diff --git a/.gitignore b/.gitignore
index 09f5889..27b89011 100644
--- a/.gitignore
+++ b/.gitignore
@@ -341,7 +341,6 @@
 /third_party/omaha/src/omaha
 /third_party/openmax_dl/
 /third_party/opus/src
-/third_party/ots
 /third_party/pdfsqueeze
 /third_party/pdfium
 /third_party/pefile
@@ -363,6 +362,7 @@
 /third_party/pywebsocket/src
 /third_party/pywebsocket/src
 /third_party/requests/src
+/third_party/robolectric/lib
 /third_party/safe_browsing/testing
 /third_party/scons-2.0.1
 /third_party/sfntly/cpp
@@ -382,6 +382,7 @@
 /third_party/v8-i18n
 /third_party/valgrind
 /third_party/v4l2capture
+/third_party/web-animations-js
 /third_party/webdriver/pylib
 /third_party/webdriver/python/selenium
 /third_party/webgl
diff --git a/.gn b/.gn
index 1429a87..a8b55e2c 100644
--- a/.gn
+++ b/.gn
@@ -15,10 +15,60 @@
 # their includes checked for proper dependencies when you run either
 # "gn check" or "gn gen --check".
 check_targets = [
-  # Requires GN binary >= 314794 and to mark the "...__generate_enum" action in
-  # build/config/android/rules.gni "java_cpp_enum" template with
-  #   check_includes = false.
-  #"//base/*",
+  #"//apps/*",  # Medium-hard.
+  #"//ash/*",  # Medium-hard.
+  #"//base/*",  # Needs GN binary changes to work on Android.
+  "//blink/*",
+  "//breakpad/*",
+  "//build/*",
   "//cc/*",
+
+  #"//chrome/*",  # Epic number of errors.
+  "//chromecast/*",
+  "//chrome_elf/*",
+  "//cloud_print/*",
+
+  #"//components/*",  # Lots of errors.
+  #"//content/*",  # A whole lot of errors.
+  "//courgette/*",
+  "//crypto/*",
+  "//data/*",
+  "//dbus/*",
+  "//device/*",
+
+  #"//extensions/*",  # Lots of errors.
+  #"//gin/*",  # Easy.
+  #"//google_apis/*",  # Easy.
+  "//google_update/*",
+
+  #"//gpu/*",  # Lots of errors.
+  #"//ios/*",
   "//ipc/*",
+
+  #"//jingle/*",
+  #"//media/*",  # Lots of errors.
+  #"//mojo/*",
+  #"//native_client/*",
+  #"//net/*",  # Needs GN binary changes to work on Android.
+
+  #"//pdf/*",  # Medium-hard.
+  #"//ppapi/*",  # Lots of errors.
+  "//printing/*",
+
+  #"//remoting/*",  # Medium-hard.
+  #"//rlz/*",  # Needs checking on Windows.
+  #"//sandbox/*",  # Medium-hard.
+  "//sdch/*",
+  "//skia/*",
+  "//sql/*",
+  "//storage/*",
+  "//sync/*",
+  "//testing/*",
+
+  #"//third_party/*",  # May not ever want this.
+  "//tools/*",
+
+  #"//ui/*",  # Just a few problems.
+  "//url/*",
+  "//v8/*",
 ]
diff --git a/AUTHORS b/AUTHORS
index ce9ba20..ca53284 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -196,6 +196,7 @@
 Ian Scott <ian.scott@arteris.com>
 Ibrar Ahmed <ibrar.ahmad@gmail.com>
 Ion Rosca <rosca@adobe.com>
+Ivan Sham <ivansham@amazon.com>
 J. Ryan Stinnett <jryans@chromium.org>
 Jacob Mandelson <jacob@mandelson.org>
 Jaehun Lim <ljaehun.lim@samsung.com>
diff --git a/BUILD.gn b/BUILD.gn
index 0aee2668..6ca1cda 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -170,7 +170,7 @@
     deps += [ "//rlz:rlz_lib" ]
   }
 
-  if (cpu_arch == "x86" || cpu_arch == "x64") {
+  if (current_cpu == "x86" || current_cpu == "x64") {
     # YASM is x86/x64 only.
     deps += [ "//third_party/yasm($host_toolchain)" ]
   }
diff --git a/DEPS b/DEPS
index 1274f5e..0d6f6a7b5 100644
--- a/DEPS
+++ b/DEPS
@@ -34,35 +34,36 @@
   'llvm_url': 'http://src.chromium.org/llvm-project',
   'llvm_git': 'https://llvm.googlesource.com',
   'webkit_trunk': 'http://src.chromium.org/blink/trunk',
-  'webkit_revision': '8ae0a300754cd217b85802270b3f02108fc8f170', # from svn revision 190364
+  'webkit_revision': 'd423b2b863c9484281c547d573ae2a6f362a5277', # from svn revision 190626
   'chromium_git': 'https://chromium.googlesource.com',
   'chromiumos_git': 'https://chromium.googlesource.com/chromiumos',
   'pdfium_git': 'https://pdfium.googlesource.com',
   'skia_git': 'https://skia.googlesource.com',
   'boringssl_git': 'https://boringssl.googlesource.com',
-  'libvpx_revision': '5cdd30205301c293be6160f778bce981300b5a28',
+  'libvpx_revision': '33bbffe8b3fa6d240ab7720f4f46854bd98d7198',
   'sfntly_revision': '1bdaae8fc788a5ac8936d68bf24f37d977a13dac',
-  'skia_revision': 'd1371a6019189820653aaf20f72ee8f5d0ee3fef',
+  'skia_revision': '792c80f5a7b66e75d42664ccb298f31962c6654c',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and V8 without interference from each other.
   'v8_branch': 'trunk',
-  'v8_revision': '055b865a326cdf6a28d2bbb0f197b36e09069fc1',
+  'v8_revision': '3dfd929ea07487f2295553df397720d8d75d227c',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling WebRTC
   # and V8 without interference from each other.
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling swarming_client
   # and whatever else without interference from each other.
-  'swarming_revision': 'bdad1183d6047cc6a1459a5be167ba0a7325146e',
+  'swarming_revision': '1b7bfeca33abce319356fd1835a5cd2f74f1916a',
   # 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': 'd2d21dcadcc5c6a4ac2bbf4edd88681390a96a78',
+  'angle_revision': '6df9b37d8e3aed3aea12058900b7932f911a152a',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling build tools
   # and whatever else without interference from each other.
-  'buildtools_revision': '5c5e924788fe40f7d6e0a3841ac572de2475e689',
+  #
+  'buildtools_revision': '93b3d0af1b30db55ee42bd2e983f7753153217db',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
@@ -74,7 +75,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling BoringSSL
   # and whatever else without interference from each other.
-  'boringssl_revision': 'd306f165a462f47c2c0e8f3a0f2b4ae6950f70ed',
+  'boringssl_revision': 'b180ee98a60149dd3fd07cce4e834494c9d5b31c',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling nss
   # and whatever else without interference from each other.
@@ -94,7 +95,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling NaCl
   # and whatever else without interference from each other.
-  'nacl_revision': '6b9d8cf7adfa83b8bb6800313604b034551bd86e',
+  'nacl_revision': '456d6e41cd963607ca2ce966d6d9a8d9895ea973',
 }
 
 # Only these hosts are allowed for dependencies in this DEPS file.
@@ -128,13 +129,13 @@
    Var('chromium_git') + '/external/colorama.git' + '@' + '799604a1041e9b3bc5d2789ecbd7e8db2e18e6b8',
 
   'src/third_party/trace-viewer':
-   Var('chromium_git') + '/external/trace-viewer.git' + '@' + 'a9802a1384185f9c7ee250ce67d3d24d07b11141',
+   Var('chromium_git') + '/external/trace-viewer.git' + '@' + 'e0c8f3b8e60999ab4fd97ad1afba869103d71193',
 
   'src/third_party/WebKit':
    Var('chromium_git') + '/chromium/blink.git' + '@' +  Var('webkit_revision'),
 
   'src/third_party/icu':
-   Var('chromium_git') + '/chromium/deps/icu.git' + '@' + '4e3266f32c62d30a3f9e2232a753c60129d1e670',
+   Var('chromium_git') + '/chromium/deps/icu.git' + '@' + '2081ee6abfa118003fd559cb72393f5df561dba7',
 
   'src/third_party/libexif/sources':
    Var('chromium_git') + '/chromium/deps/libexif/sources.git' + '@' + 'ed98343daabd7b4497f97fda972e132e6877c48a',
@@ -161,7 +162,7 @@
     Var('chromium_git') + '/external/grit-i18n.git' + '@' + 'a5890a8118c0c80cc0560e6d8d5cf65e5d725509', # from svn revision 185
 
   'src/tools/gyp':
-    Var('chromium_git') + '/external/gyp.git' + '@' + '4d7c139b1820c5fcb993868c61f170a02cda8a40', # from svn revision 2030
+    Var('chromium_git') + '/external/gyp.git' + '@' + '34640080d08ab2a37665512e52142947def3056d', # from svn revision 2034
 
   'src/tools/swarming_client':
    Var('chromium_git') + '/external/swarming.client.git' + '@' +  Var('swarming_revision'),
@@ -178,9 +179,6 @@
   'src/third_party/skia':
    Var('chromium_git') + '/skia.git' + '@' +  Var('skia_revision'),
 
-  'src/third_party/ots':
-    Var('chromium_git') + '/external/ots.git' + '@' + '98897009f3ea8a5fa3e20a4a74977da7aaa8e61a',
-
   'src/third_party/brotli/src':
    Var('chromium_git') + '/external/font-compression-reference.git' + '@' + '8c9c83426beb4a58da34be76ea1fccb4054c4703',
 
@@ -209,13 +207,13 @@
    Var('chromium_git') + '/chromium/deps/libvpx.git' + '@' +  Var('libvpx_revision'),
 
   'src/third_party/ffmpeg':
-   Var('chromium_git') + '/chromium/third_party/ffmpeg.git' + '@' + '2e38ab8c03616a4c3c121f581b67dab99b2b21d7',
+   Var('chromium_git') + '/chromium/third_party/ffmpeg.git' + '@' + 'd4b1674dcd2f742403179a3ef8e6dd8d7aaecf1a',
 
   'src/third_party/libjingle/source/talk':
-    Var('chromium_git') + '/external/webrtc/trunk/talk.git' + '@' + 'f7067cb5af70e0a2f766a68b61e8e79834185f26',
+    Var('chromium_git') + '/external/webrtc/trunk/talk.git' + '@' + '0366d0b6d9dddb3d775d7ed8919c56a729cfe82f',
 
   'src/third_party/usrsctp/usrsctplib':
-    Var('chromium_git') + '/external/usrsctplib.git' + '@' + '190c8cbfcf8fd810aa09e0fab4ca62a8ce724e14',
+    Var('chromium_git') + '/external/usrsctplib.git' + '@' + '13718c7b9fd376fde092cbd3c5347d15059ac652', # from svn revision 9167
 
   'src/third_party/libsrtp':
    Var('chromium_git') + '/chromium/deps/libsrtp.git' + '@' + '6446144c7f083552f21cc4e6768e891bcb767574',
@@ -236,7 +234,7 @@
    Var('chromium_git') + '/native_client/src/third_party/scons-2.0.1.git' + '@' + '1c1550e17fc26355d08627fbdec13d8291227067',
 
   'src/third_party/webrtc':
-    Var('chromium_git') + '/external/webrtc/trunk/webrtc.git' + '@' + '8424b76935d0d0c35c66032e1603fc36b1aa3f17',
+    Var('chromium_git') + '/external/webrtc/trunk/webrtc.git' + '@' + 'de41287e2e18ab6859359f03f8cbdeceee1d0fe2',
 
   'src/third_party/openmax_dl':
     Var('chromium_git') + '/external/webrtc/deps/third_party/openmax.git' + '@' +  Var('openmax_dl_revision'),
@@ -409,7 +407,7 @@
 
     # For Linux and Chromium OS.
     'src/third_party/cros_system_api':
-     Var('chromium_git') + '/chromiumos/platform/system_api.git' + '@' + 'd2eb139b58ab885610685b199f9f3d05ec892082',
+     Var('chromium_git') + '/chromiumos/platform/system_api.git' + '@' + 'e22c1effdfaace2f904536f8a9953644ba90398c',
 
     # Note that this is different from Android's freetype repo.
     'src/third_party/freetype2/src':
@@ -417,7 +415,7 @@
 
     # Build tools for Chrome OS.
     'src/third_party/chromite':
-     Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'c1bb274a82c13f76c3d8560808423605bfd84d76',
+     Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'fdc9440cb96f8de35202abc285ffb896e04292d3',
 
     # Dependency of chromite.git.
     'src/third_party/pyelftools':
@@ -476,6 +474,9 @@
     'src/third_party/mockito/src':
       Var('chromium_git') + '/external/mockito/mockito.git' + '@' + 'ed99a52e94a84bd7c467f2443b475a22fcc6ba8e',
 
+    'src/third_party/robolectric/lib':
+      Var('chromium_git') + '/chromium/third_party/robolectric.git' + '@' + '6b63c99a8b6967acdb42cbed0adb067c80efc810',
+
     'src/third_party/lss':
       Var('chromium_git') + '/external/linux-syscall-support/lss.git' + '@' + Var('lss_revision'),
 
@@ -540,7 +541,7 @@
         'python',
         'src/build/download_nacl_toolchains.py',
         '--mode', 'nacl_core_sdk',
-        'sync',
+        'sync', '--extract',
     ],
   },
   {
@@ -725,7 +726,7 @@
     'action': ['python',
                'src/build/get_syzygy_binaries.py',
                '--output-dir=src/third_party/kasko',
-               '--revision=f0332d538f3eb84c2a55a3d0506a4a0b58d1c09e',
+               '--revision=33788dfa4e351db209915389a97eed31499c3206',
                '--resource=kasko.zip',
                '--resource=kasko_symbols.zip',
                '--overwrite',
diff --git a/PRESUBMIT.py b/PRESUBMIT.py
index 18fe621e..c49ccd0c4 100644
--- a/PRESUBMIT.py
+++ b/PRESUBMIT.py
@@ -1305,6 +1305,36 @@
   return copyright_scanner.ScanAtPresubmit(input_api, output_api)
 
 
+def _CheckSingletonInHeaders(input_api, output_api):
+  """Checks to make sure no header files have |Singleton<|."""
+  def FileFilter(affected_file):
+    # It's ok for base/memory/singleton.h to have |Singleton<|.
+    black_list = (_EXCLUDED_PATHS +
+                  input_api.DEFAULT_BLACK_LIST +
+                  (r"^base[\\\/]memory[\\\/]singleton\.h$",))
+    return input_api.FilterSourceFile(affected_file, black_list=black_list)
+
+  pattern = input_api.re.compile(r'(?<!class\s)Singleton\s*<')
+  files = []
+  for f in input_api.AffectedSourceFiles(FileFilter):
+    if (f.LocalPath().endswith('.h') or f.LocalPath().endswith('.hxx') or
+        f.LocalPath().endswith('.hpp') or f.LocalPath().endswith('.inl')):
+      contents = input_api.ReadFile(f)
+      for line in contents.splitlines(False):
+        if (not input_api.re.match(r'//', line) and # Strip C++ comment.
+            pattern.search(line)):
+          files.append(f)
+          break
+
+  if files:
+    return [ output_api.PresubmitError(
+        'Found Singleton<T> in the following header files.\n' +
+        'Please move them to an appropriate source file so that the ' +
+        'template gets instantiated in a single compilation unit.',
+        files) ]
+  return []
+
+
 _DEPRECATED_CSS = [
   # Values
   ( "-webkit-box", "flex" ),
@@ -1422,6 +1452,7 @@
   results.extend(_CheckForIPCRules(input_api, output_api))
   results.extend(_CheckForCopyrightedCode(input_api, output_api))
   results.extend(_CheckForWindowsLineEndings(input_api, output_api))
+  results.extend(_CheckSingletonInHeaders(input_api, output_api))
 
   if any('PRESUBMIT.py' == f.LocalPath() for f in input_api.AffectedFiles()):
     results.extend(input_api.canned_checks.RunUnitTestsInDirectory(
@@ -1660,7 +1691,6 @@
   """
   # Potentially ambiguous bot names are listed explicitly.
   master_map = {
-      'win_gpu': 'tryserver.chromium.gpu',
       'chromium_presubmit': 'tryserver.chromium.linux',
       'blink_presubmit': 'tryserver.chromium.linux',
       'tools_build_presubmit': 'tryserver.chromium.linux',
diff --git a/PRESUBMIT_test.py b/PRESUBMIT_test.py
index 00997b43..5c93d98 100755
--- a/PRESUBMIT_test.py
+++ b/PRESUBMIT_test.py
@@ -12,7 +12,7 @@
 import unittest
 
 import PRESUBMIT
-from PRESUBMIT_test_mocks import MockChange, MockFile
+from PRESUBMIT_test_mocks import MockChange, MockFile, MockAffectedFile
 from PRESUBMIT_test_mocks import MockInputApi, MockOutputApi
 
 _TEST_DATA_DIR = 'base/test/data/presubmit'
@@ -384,6 +384,33 @@
     self.assertEqual({}, results)
 
 
+class CheckSingletonInHeadersTest(unittest.TestCase):
+  def testSingletonInArbitraryHeader(self):
+    diff_singleton_h = ['base::subtle::AtomicWord '
+                        'Singleton<Type, Traits, DifferentiatingType>::']
+    diff_foo_h = ['// Singleton<Foo> in comment.',
+                  'friend class Singleton<Foo>']
+    diff_bad_h = ['Foo* foo = Singleton<Foo>::get();']
+    mock_input_api = MockInputApi()
+    mock_input_api.files = [MockAffectedFile('base/memory/singleton.h',
+                                     diff_singleton_h),
+                            MockAffectedFile('foo.h', diff_foo_h),
+                            MockAffectedFile('bad.h', diff_bad_h)]
+    warnings = PRESUBMIT._CheckSingletonInHeaders(mock_input_api,
+                                                  MockOutputApi())
+    self.assertEqual(1, len(warnings))
+    self.assertEqual('error', warnings[0].type)
+    self.assertTrue('Found Singleton<T>' in warnings[0].message)
+
+  def testSingletonInCC(self):
+    diff_cc = ['Foo* foo = Singleton<Foo>::get();']
+    mock_input_api = MockInputApi()
+    mock_input_api.files = [MockAffectedFile('some/path/foo.cc', diff_cc)]
+    warnings = PRESUBMIT._CheckSingletonInHeaders(mock_input_api,
+                                                  MockOutputApi())
+    self.assertEqual(0, len(warnings))
+
+
 class InvalidOSMacroNamesTest(unittest.TestCase):
   def testInvalidOSMacroNames(self):
     lines = ['#if defined(OS_WINDOWS)',
@@ -664,10 +691,6 @@
 class TryServerMasterTest(unittest.TestCase):
   def testTryServerMasters(self):
     bots = {
-        'tryserver.chromium.gpu': [
-            'win_gpu',
-            'win_gpu_triggered_tests',
-        ],
         'tryserver.chromium.mac': [
             'ios_dbg_simulator',
             'ios_rel_device',
diff --git a/PRESUBMIT_test_mocks.py b/PRESUBMIT_test_mocks.py
index 7d286b9d..5caa1565 100644
--- a/PRESUBMIT_test_mocks.py
+++ b/PRESUBMIT_test_mocks.py
@@ -29,10 +29,15 @@
   def AffectedFiles(self, file_filter=None):
     return self.files
 
+  def AffectedSourceFiles(self, file_filter=None):
+    return self.files
+
   def PresubmitLocalPath(self):
     return os.path.dirname(__file__)
 
   def ReadFile(self, filename, mode='rU'):
+    if hasattr(filename, 'AbsoluteLocalPath'):
+       filename = filename.AbsoluteLocalPath()
     for file_ in self.files:
       if file_.LocalPath() == filename:
         return '\n'.join(file_.NewContents())
@@ -96,6 +101,11 @@
     return self._local_path
 
 
+class MockAffectedFile(MockFile):
+  def AbsoluteLocalPath(self):
+    return self._local_path
+
+
 class MockChange(object):
   """Mock class for Change class.
 
diff --git a/WATCHLISTS b/WATCHLISTS
index a19f9caf..6c3be65 100644
--- a/WATCHLISTS
+++ b/WATCHLISTS
@@ -886,7 +886,7 @@
                        'mlamouri+watch-sensors@chromium.org'],
     'developer_recommended_flags': ['scheib+watch@chromium.org'],
     'devtools': ['pfeldman@chromium.org', 'yurys@chromium.org',
-                 'vsevik@chromium.org', 'aandrey+blink@chromium.org',
+                 'aandrey+blink@chromium.org',
                  'devtools-reviews@chromium.org'],
     'disk_cache': ['gavinp+disk@chromium.org'],
     'dns': ['mmenke@chromium.org'],
diff --git a/android_webview/android_webview.gyp b/android_webview/android_webview.gyp
index 729283dc..e9f86f9 100644
--- a/android_webview/android_webview.gyp
+++ b/android_webview/android_webview.gyp
@@ -308,8 +308,8 @@
         'lib/aw_browser_dependency_factory_impl.h',
         'lib/main/aw_main_delegate.cc',
         'lib/main/aw_main_delegate.h',
-        'lib/main/webview_jni_onload_delegate.cc',
-        'lib/main/webview_jni_onload_delegate.h',
+        'lib/main/webview_jni_onload.cc',
+        'lib/main/webview_jni_onload.h',
         'public/browser/draw_gl.h',
         'renderer/aw_content_renderer_client.cc',
         'renderer/aw_content_renderer_client.h',
diff --git a/android_webview/android_webview_tests.gypi b/android_webview/android_webview_tests.gypi
index aca7475..e242a6e 100644
--- a/android_webview/android_webview_tests.gypi
+++ b/android_webview/android_webview_tests.gypi
@@ -32,6 +32,8 @@
           '<(asset_location)/full_screen_video_inside_div_test.html',
           '<(asset_location)/multiple_videos_test.html',
           '<(asset_location)/video.mp4',
+          '<(asset_location)/visual_state_during_fullscreen_test.html',
+          '<(asset_location)/visual_state_waits_for_js_test.html',
           '<@(snapshot_additional_input_paths)',
         ],
         'conditions': [
@@ -57,6 +59,8 @@
             '<(java_in_dir)/assets/full_screen_video_inside_div_test.html',
             '<(java_in_dir)/assets/multiple_videos_test.html',
             '<(java_in_dir)/assets/video.mp4',
+            '<(java_in_dir)/assets/visual_state_during_fullscreen_test.html',
+            '<(java_in_dir)/assets/visual_state_waits_for_js_test.html',
             '<@(snapshot_copy_files)',
           ],
           'conditions': [
@@ -192,6 +196,11 @@
       'include_dirs': [
         '..',
       ],
+      'variables': {
+        # This library uses native JNI exports; tell gyp so that the required
+        # symbols will be kept.
+        'use_native_jni_exports': 1,
+      },
       'sources': [
           '../android_webview/test/shell/src/draw_gl/draw_gl.cc',
       ],
diff --git a/android_webview/browser/DEPS b/android_webview/browser/DEPS
index 217bfa7..e633368 100644
--- a/android_webview/browser/DEPS
+++ b/android_webview/browser/DEPS
@@ -2,7 +2,7 @@
   "-android_webview",
   "+android_webview/browser",
   "+android_webview/common",
-  "+android_webview/native",
+  "+android_webview/native/public",
   "+android_webview/public/browser",
 
   "+cc",
diff --git a/android_webview/browser/aw_browser_context.cc b/android_webview/browser/aw_browser_context.cc
index d113d74e..30f59cb8 100644
--- a/android_webview/browser/aw_browser_context.cc
+++ b/android_webview/browser/aw_browser_context.cc
@@ -20,7 +20,9 @@
 #include "components/autofill/core/common/autofill_pref_names.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_prefs.h"
+#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_service.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.h"
+#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_statistics_prefs.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_params.h"
 #include "components/user_prefs/user_prefs.h"
 #include "components/visitedlink/browser/visitedlink_master.h"
@@ -35,11 +37,6 @@
 using base::FilePath;
 using content::BrowserThread;
 
-namespace data_reduction_proxy {
-class DataReductionProxyConfigurator;
-class DataReductionProxyStatisticsPrefs;
-}
-
 namespace android_webview {
 
 namespace {
@@ -156,24 +153,22 @@
           cache_path, cookie_store_.get(),
           make_scoped_ptr(CreateProxyConfigService()).Pass());
 
-  data_reduction_proxy_settings_.reset(
-      new data_reduction_proxy::DataReductionProxySettings(
-          scoped_ptr<data_reduction_proxy::DataReductionProxyParams>(
-              new data_reduction_proxy::DataReductionProxyParams(
-                  data_reduction_proxy::DataReductionProxyParams::kAllowed))
-              .Pass()));
   data_reduction_proxy_io_data_.reset(
       new data_reduction_proxy::DataReductionProxyIOData(
           data_reduction_proxy::Client::WEBVIEW_ANDROID,
-          scoped_ptr<data_reduction_proxy::DataReductionProxyStatisticsPrefs>(),
-          data_reduction_proxy_settings_.get(),
+          data_reduction_proxy::DataReductionProxyParams::kAllowed,
           url_request_context_getter_->GetNetLog(),
           BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO),
-          BrowserThread::GetMessageLoopProxyForThread(
-              BrowserThread::UI)));
-  data_reduction_proxy_io_data_->Init();
-  data_reduction_proxy_settings_->SetProxyConfigurator(
-      data_reduction_proxy_io_data_->configurator());
+          BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI),
+          false /* enable_quic */));
+  data_reduction_proxy_settings_.reset(
+      new data_reduction_proxy::DataReductionProxySettings());
+  data_reduction_proxy_service_.reset(
+      new data_reduction_proxy::DataReductionProxyService(
+          scoped_ptr<data_reduction_proxy::DataReductionProxyStatisticsPrefs>(),
+          data_reduction_proxy_settings_.get(), GetAwURLRequestContext()));
+  data_reduction_proxy_io_data_->SetDataReductionProxyService(
+      data_reduction_proxy_service_->GetWeakPtr());
 
   visitedlink_master_.reset(
       new visitedlink::VisitedLinkMaster(this, this, false));
@@ -233,11 +228,6 @@
   return data_reduction_proxy_io_data_.get();
 }
 
-data_reduction_proxy::DataReductionProxyConfigurator*
-AwBrowserContext::GetDataReductionProxyConfigurator() {
-  return data_reduction_proxy_io_data_->configurator();
-}
-
 AwURLRequestContextGetter* AwBrowserContext::GetAwURLRequestContext() {
   return url_request_context_getter_.get();
 }
@@ -276,11 +266,8 @@
 
   if (data_reduction_proxy_settings_) {
     data_reduction_proxy_settings_->InitDataReductionProxySettings(
-        user_pref_service_.get(),
-        scoped_ptr<data_reduction_proxy::DataReductionProxyStatisticsPrefs>(),
-        GetRequestContext(),
-        GetAwURLRequestContext()->GetNetLog(),
-        data_reduction_proxy_io_data_->event_store());
+        user_pref_service_.get(), data_reduction_proxy_io_data_.get(),
+        data_reduction_proxy_service_.Pass());
     data_reduction_proxy_settings_->MaybeActivateDataReductionProxy(true);
 
     SetDataReductionProxyEnabled(data_reduction_proxy_enabled_);
@@ -375,16 +362,18 @@
 void AwBrowserContext::CreateDataReductionProxyStatisticsIfNecessary() {
   DCHECK(user_pref_service_.get());
   DCHECK(GetDataReductionProxySettings());
-  if (GetDataReductionProxySettings()->statistics_prefs())
+  data_reduction_proxy::DataReductionProxyService*
+      data_reduction_proxy_service =
+          GetDataReductionProxySettings()->data_reduction_proxy_service();
+  DCHECK(data_reduction_proxy_service);
+  if (data_reduction_proxy_service->statistics_prefs())
     return;
   // We don't care about commit_delay for now. It is just a dummy value.
   base::TimeDelta commit_delay = base::TimeDelta::FromMinutes(60);
-  GetDataReductionProxySettings()->EnableCompressionStatisticsLogging(
+  data_reduction_proxy_service->EnableCompressionStatisticsLogging(
       user_pref_service_.get(),
       BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI),
       commit_delay);
-  GetDataReductionProxyIOData()->SetDataReductionProxyStatisticsPrefs(
-      GetDataReductionProxySettings()->statistics_prefs());
 }
 
 }  // namespace android_webview
diff --git a/android_webview/browser/aw_browser_context.h b/android_webview/browser/aw_browser_context.h
index 755a838..1e32255 100644
--- a/android_webview/browser/aw_browser_context.h
+++ b/android_webview/browser/aw_browser_context.h
@@ -32,6 +32,7 @@
 namespace data_reduction_proxy {
 class DataReductionProxyConfigurator;
 class DataReductionProxyIOData;
+class DataReductionProxyService;
 class DataReductionProxySettings;
 }
 
@@ -94,9 +95,6 @@
   data_reduction_proxy::DataReductionProxyIOData*
       GetDataReductionProxyIOData();
 
-  data_reduction_proxy::DataReductionProxyConfigurator*
-      GetDataReductionProxyConfigurator();
-
   AwURLRequestContextGetter* GetAwURLRequestContext();
 
   void CreateUserPrefServiceIfNecessary();
@@ -157,6 +155,8 @@
   scoped_ptr<AwSSLHostStateDelegate> ssl_host_state_delegate_;
   scoped_ptr<data_reduction_proxy::DataReductionProxyIOData>
       data_reduction_proxy_io_data_;
+  scoped_ptr<data_reduction_proxy::DataReductionProxyService>
+      data_reduction_proxy_service_;
 
   DISALLOW_COPY_AND_ASSIGN(AwBrowserContext);
 };
diff --git a/android_webview/browser/aw_browser_main_parts.cc b/android_webview/browser/aw_browser_main_parts.cc
index 8bbdb851..db47f02 100644
--- a/android_webview/browser/aw_browser_main_parts.cc
+++ b/android_webview/browser/aw_browser_main_parts.cc
@@ -6,7 +6,7 @@
 
 #include "android_webview/browser/aw_browser_context.h"
 #include "android_webview/browser/aw_result_codes.h"
-#include "android_webview/native/aw_assets.h"
+#include "android_webview/native/public/aw_assets.h"
 #include "base/android/build_info.h"
 #include "base/android/locale_utils.h"
 #include "base/android/memory_pressure_listener_android.h"
diff --git a/android_webview/browser/browser_view_renderer.cc b/android_webview/browser/browser_view_renderer.cc
index 486702b..c2de48b 100644
--- a/android_webview/browser/browser_view_renderer.cc
+++ b/android_webview/browser/browser_view_renderer.cc
@@ -279,8 +279,8 @@
   DidSkipCompositeInDraw();
 }
 
-void BrowserViewRenderer::InvalidateOnFunctorDestroy() {
-  client_->InvalidateOnFunctorDestroy();
+void BrowserViewRenderer::DetachFunctorFromView() {
+  client_->DetachFunctorFromView();
 }
 
 bool BrowserViewRenderer::OnDrawSoftware(SkCanvas* canvas) {
diff --git a/android_webview/browser/browser_view_renderer.h b/android_webview/browser/browser_view_renderer.h
index 784afb8..e471049 100644
--- a/android_webview/browser/browser_view_renderer.h
+++ b/android_webview/browser/browser_view_renderer.h
@@ -100,7 +100,7 @@
 
   void UpdateParentDrawConstraints();
   void DidSkipCommitFrame();
-  void InvalidateOnFunctorDestroy();
+  void DetachFunctorFromView();
 
  private:
   void SetTotalRootLayerScrollOffset(gfx::Vector2dF new_value_dip);
diff --git a/android_webview/browser/browser_view_renderer_client.h b/android_webview/browser/browser_view_renderer_client.h
index e87a27c5..ffc15b65 100644
--- a/android_webview/browser/browser_view_renderer_client.h
+++ b/android_webview/browser/browser_view_renderer_client.h
@@ -29,7 +29,7 @@
 
   // Call postInvalidateOnAnimation for invalidations. This is only used to
   // synchronize draw functor destruction.
-  virtual void InvalidateOnFunctorDestroy() = 0;
+  virtual void DetachFunctorFromView() = 0;
 
   // Called to get view's absolute location on the screen.
   virtual gfx::Point GetLocationOnScreen() = 0;
diff --git a/android_webview/browser/hardware_renderer.h b/android_webview/browser/hardware_renderer.h
index 4030452..eff1b5cf 100644
--- a/android_webview/browser/hardware_renderer.h
+++ b/android_webview/browser/hardware_renderer.h
@@ -42,6 +42,7 @@
   void WillBeginMainFrame() override {}
   void DidBeginMainFrame() override;
   void BeginMainFrame(const cc::BeginFrameArgs& args) override {}
+  void BeginMainFrameNotExpectedSoon() override {}
   void Layout() override {}
   void ApplyViewportDeltas(const gfx::Vector2dF& inner_delta,
                            const gfx::Vector2dF& outer_delta,
diff --git a/android_webview/browser/shared_renderer_state.cc b/android_webview/browser/shared_renderer_state.cc
index 5779914..119b7fd 100644
--- a/android_webview/browser/shared_renderer_state.cc
+++ b/android_webview/browser/shared_renderer_state.cc
@@ -335,7 +335,7 @@
   DCHECK(ui_loop_->BelongsToCurrentThread());
   InsideHardwareReleaseReset auto_inside_hardware_release_reset(this);
 
-  browser_view_renderer_->InvalidateOnFunctorDestroy();
+  browser_view_renderer_->DetachFunctorFromView();
   bool hardware_initialized = browser_view_renderer_->hardware_enabled();
   if (hardware_initialized) {
     bool draw_functor_succeeded = browser_view_renderer_->RequestDrawGL(true);
diff --git a/android_webview/browser/test/rendering_test.cc b/android_webview/browser/test/rendering_test.cc
index 995b602..fe69bcd 100644
--- a/android_webview/browser/test/rendering_test.cc
+++ b/android_webview/browser/test/rendering_test.cc
@@ -73,7 +73,7 @@
   window_->PostInvalidate();
 }
 
-void RenderingTest::InvalidateOnFunctorDestroy() {
+void RenderingTest::DetachFunctorFromView() {
 }
 
 gfx::Point RenderingTest::GetLocationOnScreen() {
diff --git a/android_webview/browser/test/rendering_test.h b/android_webview/browser/test/rendering_test.h
index 561d13c..d26b1392 100644
--- a/android_webview/browser/test/rendering_test.h
+++ b/android_webview/browser/test/rendering_test.h
@@ -32,7 +32,7 @@
   bool RequestDrawGL(bool wait_for_completion) override;
   void OnNewPicture() override;
   void PostInvalidate() override;
-  void InvalidateOnFunctorDestroy() override;
+  void DetachFunctorFromView() override;
   gfx::Point GetLocationOnScreen() override;
   void ScrollContainerViewTo(gfx::Vector2d new_value) override {}
   bool IsFlingActive() const override;
diff --git a/android_webview/java/src/org/chromium/android_webview/AwContents.java b/android_webview/java/src/org/chromium/android_webview/AwContents.java
index 0b4edfe..8d0d278c 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwContents.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwContents.java
@@ -2220,7 +2220,7 @@
     // Call postInvalidateOnAnimation for invalidations. This is only used to synchronize
     // draw functor destruction.
     @CalledByNative
-    private void invalidateOnFunctorDestroy() {
+    private void detachFunctorFromView() {
         mNativeGLDelegate.detachGLFunctor();
         mContainerView.invalidate();
     }
diff --git a/android_webview/java/src/org/chromium/android_webview/MessagePort.java b/android_webview/java/src/org/chromium/android_webview/MessagePort.java
index f909056d..2d0e393 100644
--- a/android_webview/java/src/org/chromium/android_webview/MessagePort.java
+++ b/android_webview/java/src/org/chromium/android_webview/MessagePort.java
@@ -37,10 +37,10 @@
 public class MessagePort implements PostMessageSender.PostMessageSenderDelegate {
 
     /**
-     * The interface for message handler for receiving messages. Called on a background thread.
+     * The message event handler for receiving messages. Called on a background thread.
      */
-    public static interface MessageHandler {
-        void onMessage(String message);
+    public abstract static class WebEventHandler {
+        public abstract void onMessage(String message);
     };
 
     /**
@@ -63,7 +63,7 @@
     private static final String TAG = "MessagePort";
     private static final int PENDING = -1;
     private int mPortId = PENDING;
-    private MessageHandler mHandler;
+    private WebEventHandler mWebEventHandler;
     private AwMessagePortService mMessagePortService;
     private boolean mClosed;
     private boolean mTransferred;
@@ -108,8 +108,8 @@
         mTransferred = true;
     }
 
-    public void setMessageHandler(MessageHandler handler) {
-        mHandler = handler;
+    public void setWebEventHandler(WebEventHandler webEventHandler) {
+        mWebEventHandler = webEventHandler;
     }
 
     public void onMessage(String message) {
@@ -117,11 +117,11 @@
             Log.w(TAG, "Port [" + mPortId + "] received message in closed state");
             return;
         }
-        if (mHandler == null) {
+        if (mWebEventHandler == null) {
             Log.w(TAG, "No handler set for port [" + mPortId + "], dropping message " + message);
             return;
         }
-        mHandler.onMessage(message);
+        mWebEventHandler.onMessage(message);
     }
 
     public void postMessage(String message, MessagePort[] msgPorts) throws IllegalStateException {
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/PostMessageTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/PostMessageTest.java
index 1e61bd9..8849ec9 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/PostMessageTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/PostMessageTest.java
@@ -322,7 +322,7 @@
                 MessagePort[] channel = mAwContents.createMessageChannel();
                 // verify communication from JS to Java.
                 channelContainer.set(channel);
-                channel[0].setMessageHandler(new MessagePort.MessageHandler() {
+                channel[0].setWebEventHandler(new MessagePort.WebEventHandler() {
                     @Override
                     public void onMessage(String message) {
                         channelContainer.setMessage(message);
@@ -404,7 +404,7 @@
         runTestOnUiThread(new Runnable() {
             @Override
             public void run() {
-                channel[0].setMessageHandler(new MessagePort.MessageHandler() {
+                channel[0].setWebEventHandler(new MessagePort.WebEventHandler() {
                     @Override
                     public void onMessage(String message) {
                         channelContainer.setMessage(message);
@@ -434,7 +434,7 @@
             @Override
             public void run() {
                 MessagePort[] channel = mAwContents.createMessageChannel();
-                channel[0].setMessageHandler(new MessagePort.MessageHandler() {
+                channel[0].setWebEventHandler(new MessagePort.WebEventHandler() {
                     @Override
                     public void onMessage(String message) {
                         channelContainer.setMessage(message);
@@ -461,7 +461,7 @@
             @Override
             public void run() {
                 MessagePort[] channel = mAwContents.createMessageChannel();
-                channel[1].setMessageHandler(new MessagePort.MessageHandler() {
+                channel[1].setWebEventHandler(new MessagePort.WebEventHandler() {
                     @Override
                     public void onMessage(String message) {
                         channelContainer.setMessage(message);
@@ -566,8 +566,8 @@
             return mPort.isClosed();
         }
         @Override
-        public void setMessageHandler(MessageHandler handler) {
-            mPort.setMessageHandler(handler);
+        public void setWebEventHandler(WebEventHandler handler) {
+            mPort.setWebEventHandler(handler);
         }
         @Override
         public void onMessage(String message) {
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/VisualStateTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/VisualStateTest.java
index 6a96cafb..3ceeb13 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/VisualStateTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/VisualStateTest.java
@@ -7,13 +7,18 @@
 import android.graphics.Bitmap;
 import android.graphics.Color;
 import android.test.suitebuilder.annotation.SmallTest;
+import android.view.View;
+import android.webkit.WebChromeClient;
 
 import org.chromium.android_webview.AwContents;
 import org.chromium.android_webview.AwContents.VisualStateCallback;
 import org.chromium.android_webview.test.util.CommonResources;
 import org.chromium.android_webview.test.util.GraphicsTestUtils;
+import org.chromium.android_webview.test.util.JavascriptEventObserver;
 import org.chromium.base.test.util.Feature;
+import org.chromium.content.browser.ContentViewCore;
 import org.chromium.content.browser.test.util.CallbackHelper;
+import org.chromium.content.browser.test.util.DOMUtils;
 import org.chromium.content_public.browser.LoadUrlParams;
 
 import java.util.concurrent.CountDownLatch;
@@ -25,6 +30,13 @@
  */
 public class VisualStateTest extends AwTestBase {
 
+    private static final String WAIT_FOR_JS_TEST_URL =
+            "file:///android_asset/visual_state_waits_for_js_test.html";
+    private static final String FULLSCREEN_TEST_URL =
+            "file:///android_asset/visual_state_during_fullscreen_test.html";
+    private static final String UPDATE_COLOR_CONTROL_ID = "updateColorControl";
+    private static final String ENTER_FULLSCREEN_CONTROL_ID = "enterFullscreenControl";
+
     private TestAwContentsClient mContentsClient = new TestAwContentsClient();
 
     @Feature({"AndroidWebView"})
@@ -117,6 +129,238 @@
         assertTrue(testFinishedSignal.await(AwTestBase.WAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS));
     }
 
+    @Feature({"AndroidWebView"})
+    @SmallTest
+    public void testVisualStateCallbackWaitsForJs() throws Throwable {
+        // This test checks that when a VisualStateCallback completes the results of executing
+        // any block of JS prior to the time at which the callback was inserted will be visible
+        // in the next draw. For that it loads a page that changes the background color of
+        // the page from JS when a button is clicked.
+        final CountDownLatch readyToUpdateColor = new CountDownLatch(1);
+        final CountDownLatch testFinishedSignal = new CountDownLatch(1);
+
+        final AtomicReference<AwContents> awContentsRef = new AtomicReference<>();
+        TestAwContentsClient awContentsClient = new TestAwContentsClient() {
+            @Override
+            public void onPageFinished(String url) {
+                super.onPageFinished(url);
+                awContentsRef.get().insertVisualStateCallback(10,
+                        new VisualStateCallback() {
+                            @Override
+                            public void onFailure(long id) {
+                                fail("onFailure received");
+                            }
+
+                            @Override
+                            public void onComplete(long id) {
+                                Bitmap blueScreenshot = GraphicsTestUtils.drawAwContents(
+                                        awContentsRef.get(), 100, 100);
+                                assertEquals(Color.BLUE, blueScreenshot.getPixel(50, 50));
+                                readyToUpdateColor.countDown();
+                            }
+                        });
+            }
+        };
+        final AwTestContainerView testView =
+                createAwTestContainerViewOnMainSync(awContentsClient);
+        final AwContents awContents = testView.getAwContents();
+        awContentsRef.set(awContents);
+        final ContentViewCore contentViewCore = testView.getContentViewCore();
+        enableJavaScriptOnUiThread(awContents);
+
+        // JS will notify this observer once it has changed the background color of the page.
+        final JavascriptEventObserver jsObserver = new JavascriptEventObserver();
+        runTestOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                jsObserver.register(contentViewCore, "jsObserver");
+            }
+        });
+
+        loadUrlSync(awContents,
+                awContentsClient.getOnPageFinishedHelper(), WAIT_FOR_JS_TEST_URL);
+
+        assertTrue(readyToUpdateColor.await(AwTestBase.WAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS));
+        DOMUtils.clickNode(
+                VisualStateTest.this, contentViewCore, UPDATE_COLOR_CONTROL_ID);
+        assertTrue(jsObserver.waitForEvent(WAIT_TIMEOUT_MS));
+
+        runTestOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                awContents.insertVisualStateCallback(20,
+                        new VisualStateCallback() {
+                            @Override
+                            public void onFailure(long id) {
+                                fail("onFailure received");
+                            }
+
+                            @Override
+                            public void onComplete(long id) {
+                                Bitmap redScreenshot = GraphicsTestUtils.drawAwContents(
+                                        awContents, 100, 100);
+                                assertEquals(Color.RED, redScreenshot.getPixel(50, 50));
+                                testFinishedSignal.countDown();
+                            }
+                        });
+
+            }
+        });
+
+        assertTrue(testFinishedSignal.await(AwTestBase.WAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS));
+    }
+
+    @Feature({"AndroidWebView"})
+    @SmallTest
+    public void testVisualStateCallbackFromJsDuringFullscreenTransitions() throws Throwable {
+        // This test checks that VisualStateCallbacks are delivered correctly during
+        // fullscreen transitions. It loads a page, clicks a button to enter fullscreen,
+        // then inserts a VisualStateCallback once notified from JS and verifies that when the
+        // callback is received the fullscreen contents are rendered correctly in the next draw.
+        final CountDownLatch readyToEnterFullscreenSignal = new CountDownLatch(1);
+        final CountDownLatch testFinishedSignal = new CountDownLatch(1);
+
+        final AtomicReference<AwContents> awContentsRef = new AtomicReference<>();
+        final FullScreenVideoTestAwContentsClient awContentsClient =
+                new FullScreenVideoTestAwContentsClient(
+                        getActivity(), isHardwareAcceleratedTest()) {
+            @Override
+            public void onPageFinished(String url) {
+                super.onPageFinished(url);
+                awContentsRef.get().insertVisualStateCallback(10, new VisualStateCallback() {
+                    @Override
+                    public void onFailure(long id) {
+                        fail("onFailure received");
+                    }
+
+                    @Override
+                    public void onComplete(long id) {
+                        Bitmap blueScreenshot =
+                                GraphicsTestUtils.drawAwContents(awContentsRef.get(), 100, 100);
+                        assertEquals(Color.BLUE, blueScreenshot.getPixel(50, 50));
+                        readyToEnterFullscreenSignal.countDown();
+                    }
+                });
+            }
+        };
+        final AwTestContainerView testView = createAwTestContainerViewOnMainSync(awContentsClient);
+        final AwContents awContents = testView.getAwContents();
+        awContentsRef.set(awContents);
+        final ContentViewCore contentViewCore = testView.getContentViewCore();
+        enableJavaScriptOnUiThread(awContents);
+        awContents.getSettings().setFullscreenSupported(true);
+
+        // JS will notify this observer once it has entered fullscreen.
+        final JavascriptEventObserver jsObserver = new JavascriptEventObserver();
+        runTestOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                jsObserver.register(contentViewCore, "jsObserver");
+            }
+        });
+
+        loadUrlSync(awContents, awContentsClient.getOnPageFinishedHelper(), FULLSCREEN_TEST_URL);
+
+        assertTrue(readyToEnterFullscreenSignal.await(
+                AwTestBase.WAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS));
+        DOMUtils.clickNode(VisualStateTest.this, contentViewCore, ENTER_FULLSCREEN_CONTROL_ID);
+        assertTrue(jsObserver.waitForEvent(WAIT_TIMEOUT_MS));
+
+        runTestOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                awContents.insertVisualStateCallback(20, new VisualStateCallback() {
+                    @Override
+                    public void onFailure(long id) {
+                        fail("onFailure received");
+                    }
+
+                    @Override
+                    public void onComplete(long id) {
+                        // NOTE: We cannot use drawAwContents here because the web contents
+                        // are rendered into the custom view while in fullscreen.
+                        Bitmap redScreenshot = GraphicsTestUtils.drawView(
+                                awContentsClient.getCustomView(), 100, 100);
+                        assertEquals(Color.RED, redScreenshot.getPixel(50, 50));
+                        testFinishedSignal.countDown();
+                    }
+                });
+            }
+        });
+
+        assertTrue(testFinishedSignal.await(AwTestBase.WAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS));
+    }
+
+    @Feature({"AndroidWebView"})
+    @SmallTest
+    public void testVisualStateCallbackFromJavaDuringFullscreenTransitions() throws Throwable {
+        // This test checks that VisualStateCallbacks are delivered correctly during
+        // fullscreen transitions. It loads a page, clicks a button to enter fullscreen,
+        // then inserts a VisualStateCallback from onShowCustomView and verifies that when the
+        // callback is received the fullscreen contents are rendered correctly in the next draw.
+        final CountDownLatch readyToEnterFullscreenSignal = new CountDownLatch(1);
+        final CountDownLatch testFinishedSignal = new CountDownLatch(1);
+
+        final AtomicReference<AwContents> awContentsRef = new AtomicReference<>();
+        final FullScreenVideoTestAwContentsClient awContentsClient =
+                new FullScreenVideoTestAwContentsClient(
+                        getActivity(), isHardwareAcceleratedTest()) {
+            @Override
+            public void onPageFinished(String url) {
+                super.onPageFinished(url);
+                awContentsRef.get().insertVisualStateCallback(10, new VisualStateCallback() {
+                    @Override
+                    public void onFailure(long id) {
+                        fail("onFailure received");
+                    }
+
+                    @Override
+                    public void onComplete(long id) {
+                        Bitmap blueScreenshot =
+                                GraphicsTestUtils.drawAwContents(awContentsRef.get(), 100, 100);
+                        assertEquals(Color.BLUE, blueScreenshot.getPixel(50, 50));
+                        readyToEnterFullscreenSignal.countDown();
+                    }
+                });
+            }
+
+            @Override
+            public void onShowCustomView(View view, WebChromeClient.CustomViewCallback callback) {
+                super.onShowCustomView(view, callback);
+                awContentsRef.get().insertVisualStateCallback(20, new VisualStateCallback() {
+                    @Override
+                    public void onFailure(long id) {
+                        fail("onFailure received");
+                    }
+
+                    @Override
+                    public void onComplete(long id) {
+                        // NOTE: We cannot use drawAwContents here because the web contents are
+                        // rendered into the custom view while in fullscreen.
+                        Bitmap redScreenshot =
+                                GraphicsTestUtils.drawView(getCustomView(), 100, 100);
+                        assertEquals(Color.RED, redScreenshot.getPixel(50, 50));
+                        testFinishedSignal.countDown();
+                    }
+                });
+            }
+        };
+        final AwTestContainerView testView = createAwTestContainerViewOnMainSync(awContentsClient);
+        final AwContents awContents = testView.getAwContents();
+        awContentsRef.set(awContents);
+        final ContentViewCore contentViewCore = testView.getContentViewCore();
+        enableJavaScriptOnUiThread(awContents);
+        awContents.getSettings().setFullscreenSupported(true);
+
+        loadUrlSync(awContents, awContentsClient.getOnPageFinishedHelper(), FULLSCREEN_TEST_URL);
+
+        assertTrue(readyToEnterFullscreenSignal.await(
+                AwTestBase.WAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS));
+        DOMUtils.clickNode(VisualStateTest.this, contentViewCore, ENTER_FULLSCREEN_CONTROL_ID);
+
+        assertTrue(testFinishedSignal.await(AwTestBase.WAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS));
+    }
+
     private static final LoadUrlParams createTestPageUrl(String backgroundColor) {
         return LoadUrlParams.createLoadDataParams(
                 "<html><body bgcolor=" + backgroundColor + "></body></html>", "text/html", false);
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/util/GraphicsTestUtils.java b/android_webview/javatests/src/org/chromium/android_webview/test/util/GraphicsTestUtils.java
index acce0fe..d41b7a38 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/util/GraphicsTestUtils.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/util/GraphicsTestUtils.java
@@ -6,6 +6,7 @@
 
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
+import android.view.View;
 
 import org.chromium.android_webview.AwContents;
 import org.chromium.android_webview.test.AwTestBase;
@@ -43,6 +44,20 @@
         return doDrawAwContents(awContents, width, height, dx, dy);
     }
 
+    /**
+     * Draws the supplied {@link View} into the returned {@link Bitmap}.
+     *
+     * @param view The view to draw
+     * @param width The width of the bitmap
+     * @param height The height of the bitmap
+     */
+    public static Bitmap drawView(View view, int width, int height) {
+        Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+        Canvas canvas = new Canvas(bitmap);
+        view.draw(canvas);
+        return bitmap;
+    }
+
     public static int sampleBackgroundColorOnUiThread(final AwContents awContents)
             throws Exception {
         return ThreadUtils.runOnUiThreadBlocking(new Callable<Integer>() {
diff --git a/android_webview/lib/main/aw_main_delegate.cc b/android_webview/lib/main/aw_main_delegate.cc
index 9acbede..b96f208 100644
--- a/android_webview/lib/main/aw_main_delegate.cc
+++ b/android_webview/lib/main/aw_main_delegate.cc
@@ -9,13 +9,13 @@
 #include "android_webview/browser/scoped_allow_wait_for_legacy_web_view_api.h"
 #include "android_webview/crash_reporter/aw_microdump_crash_reporter.h"
 #include "android_webview/lib/aw_browser_dependency_factory_impl.h"
-#include "android_webview/native/aw_assets.h"
 #include "android_webview/native/aw_media_url_interceptor.h"
 #include "android_webview/native/aw_message_port_service_impl.h"
 #include "android_webview/native/aw_quota_manager_bridge_impl.h"
 #include "android_webview/native/aw_web_contents_view_delegate.h"
 #include "android_webview/native/aw_web_preferences_populater_impl.h"
 #include "android_webview/native/external_video_surface_container_impl.h"
+#include "android_webview/native/public/aw_assets.h"
 #include "android_webview/renderer/aw_content_renderer_client.h"
 #include "base/command_line.h"
 #include "base/cpu.h"
@@ -79,8 +79,9 @@
   cl->AppendSwitch(switches::kDisableNotifications);
 
 #if defined(VIDEO_HOLE)
-  // Support EME/L1 with hole-punching.
+  // Support EME with hole-punching. For example, Widevine L1.
   cl->AppendSwitch(switches::kMediaDrmEnableNonCompositing);
+  cl->AppendSwitch(switches::kDisableEncryptedMedia);
 #endif
 
   // WebRTC hardware decoding is not supported, internal bug 15075307
diff --git a/android_webview/lib/main/webview_entry_point.cc b/android_webview/lib/main/webview_entry_point.cc
index 7de575a..f9c0940 100644
--- a/android_webview/lib/main/webview_entry_point.cc
+++ b/android_webview/lib/main/webview_entry_point.cc
@@ -2,15 +2,22 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "android_webview/lib/main/webview_jni_onload_delegate.h"
+#include "android_webview/lib/main/webview_jni_onload.h"
 #include "base/android/jni_android.h"
+#include "base/bind.h"
 #include "content/public/app/content_jni_onload.h"
 
 // This is called by the VM when the shared library is first loaded.
 // Most of the initialization is done in LibraryLoadedOnMainThread(), not here.
 JNI_EXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
-  if (!android_webview::OnJNIOnLoad(vm))
-    return -1;
+  // WebView uses native JNI exports; disable manual JNI registration to
+  // improve startup peformance.
+  base::android::DisableManualJniRegistration();
 
+  if (!content::android::OnJNIOnLoadRegisterJNI(
+          vm, base::Bind(&android_webview::RegisterJNI)) ||
+      !content::android::OnJNIOnLoadInit(base::Bind(&android_webview::Init))) {
+    return -1;
+  }
   return JNI_VERSION_1_4;
 }
diff --git a/android_webview/lib/main/webview_jni_onload.cc b/android_webview/lib/main/webview_jni_onload.cc
new file mode 100644
index 0000000..3e01190
--- /dev/null
+++ b/android_webview/lib/main/webview_jni_onload.cc
@@ -0,0 +1,52 @@
+// 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 "android_webview/lib/main/webview_jni_onload.h"
+
+#include "android_webview/lib/main/aw_main_delegate.h"
+#include "android_webview/native/android_webview_jni_registrar.h"
+#include "base/android/jni_android.h"
+#include "base/android/jni_registrar.h"
+#include "components/navigation_interception/component_jni_registrar.h"
+#include "components/web_contents_delegate_android/component_jni_registrar.h"
+#include "content/public/app/content_main.h"
+#include "url/url_util.h"
+
+namespace android_webview {
+
+namespace {
+
+static base::android::RegistrationMethod
+    kWebViewDependencyRegisteredMethods[] = {
+    { "NavigationInterception",
+        navigation_interception::RegisterNavigationInterceptionJni },
+    { "WebContentsDelegateAndroid",
+        web_contents_delegate_android::RegisterWebContentsDelegateAndroidJni },
+};
+
+}  // namespace
+
+bool RegisterJNI(JNIEnv* env) {
+  // Register JNI for components we depend on.
+  if (!RegisterNativeMethods(
+          env,
+          kWebViewDependencyRegisteredMethods,
+          arraysize(kWebViewDependencyRegisteredMethods)) ||
+      !android_webview::RegisterJni(env)) {
+    return false;
+  }
+  return true;
+}
+
+bool Init() {
+  content::SetContentMainDelegate(new android_webview::AwMainDelegate());
+
+  // Initialize url lib here while we are still single-threaded, in case we use
+  // CookieManager before initializing Chromium (which would normally have done
+  // this). It's safe to call this multiple times.
+  url::Initialize();
+  return true;
+}
+
+}  // android_webview
diff --git a/android_webview/lib/main/webview_jni_onload.h b/android_webview/lib/main/webview_jni_onload.h
new file mode 100644
index 0000000..5805dd3
--- /dev/null
+++ b/android_webview/lib/main/webview_jni_onload.h
@@ -0,0 +1,17 @@
+// 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 ANDROID_WEBVIEW_LIB_MAIN_WEBVIEW_JNI_ONLOAD__H_
+#define ANDROID_WEBVIEW_LIB_MAIN_WEBVIEW_JNI_ONLOAD__H_
+
+#include <jni.h>
+
+namespace android_webview {
+
+bool RegisterJNI(JNIEnv* env);
+bool Init();
+
+}  // android_webview
+
+#endif  // ANDROID_WEBVIEW_LIB_MAIN_WEBVIEW_JNI_ONLOAD__H_
diff --git a/android_webview/lib/main/webview_jni_onload_delegate.cc b/android_webview/lib/main/webview_jni_onload_delegate.cc
deleted file mode 100644
index 5b1d1c7..0000000
--- a/android_webview/lib/main/webview_jni_onload_delegate.cc
+++ /dev/null
@@ -1,63 +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 "android_webview/lib/main/webview_jni_onload_delegate.h"
-
-#include "android_webview/lib/main/aw_main_delegate.h"
-#include "android_webview/native/android_webview_jni_registrar.h"
-#include "base/android/jni_android.h"
-#include "base/android/jni_registrar.h"
-#include "base/android/jni_utils.h"
-#include "components/navigation_interception/component_jni_registrar.h"
-#include "components/web_contents_delegate_android/component_jni_registrar.h"
-#include "content/public/app/content_jni_onload.h"
-#include "content/public/app/content_main.h"
-#include "url/url_util.h"
-
-namespace android_webview {
-
-namespace {
-
-static base::android::RegistrationMethod
-    kWebViewDependencyRegisteredMethods[] = {
-    { "NavigationInterception",
-        navigation_interception::RegisterNavigationInterceptionJni },
-    { "WebContentsDelegateAndroid",
-        web_contents_delegate_android::RegisterWebContentsDelegateAndroidJni },
-};
-
-}  // namespace
-
-bool WebViewJNIOnLoadDelegate::RegisterJNI(JNIEnv* env) {
-  // Register JNI for components we depend on.
-  if (!RegisterNativeMethods(
-          env,
-          kWebViewDependencyRegisteredMethods,
-          arraysize(kWebViewDependencyRegisteredMethods)) ||
-      !android_webview::RegisterJni(env)) {
-    return false;
-  }
-  return true;
-}
-
-bool WebViewJNIOnLoadDelegate::Init() {
-  JNIEnv* env = base::android::AttachCurrentThread();
-  base::android::InitReplacementClassLoader(env,
-                                            base::android::GetClassLoader(env));
-
-  content::SetContentMainDelegate(new android_webview::AwMainDelegate());
-
-  // Initialize url lib here while we are still single-threaded, in case we use
-  // CookieManager before initializing Chromium (which would normally have done
-  // this). It's safe to call this multiple times.
-  url::Initialize();
-  return true;
-}
-
-bool OnJNIOnLoad(JavaVM* vm) {
-  WebViewJNIOnLoadDelegate delegate;
-  return content::android::OnJNIOnLoad(vm, &delegate);
-}
-
-}  // android_webview
diff --git a/android_webview/lib/main/webview_jni_onload_delegate.h b/android_webview/lib/main/webview_jni_onload_delegate.h
deleted file mode 100644
index 6307910..0000000
--- a/android_webview/lib/main/webview_jni_onload_delegate.h
+++ /dev/null
@@ -1,22 +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 ANDROID_WEBVIEW_LIB_MAIN_JNI_ONLOAD_DELEGATE_H_
-#define ANDROID_WEBVIEW_LIB_MAIN_JNI_ONLOAD_DELEGATE_H_
-
-#include "base/android/jni_onload_delegate.h"
-
-namespace android_webview {
-
-class WebViewJNIOnLoadDelegate : public base::android::JNIOnLoadDelegate {
- public:
-  bool RegisterJNI(JNIEnv* env) override;
-  bool Init() override;
-};
-
-bool OnJNIOnLoad(JavaVM* vm);
-
-}  // android_webview
-
-#endif  // ANDROID_WEBVIEW_LIB_MAIN_JNI_ONLOAD_DELEGATE_H_
diff --git a/android_webview/libwebviewchromium.gypi b/android_webview/libwebviewchromium.gypi
index d3ac6cf..feff124 100644
--- a/android_webview/libwebviewchromium.gypi
+++ b/android_webview/libwebviewchromium.gypi
@@ -7,6 +7,9 @@
   'dependencies': [
     'android_webview_common',
   ],
+  'variables': {
+    'use_native_jni_exports': 1,
+  },
   'conditions': [
     [ 'android_webview_build==1', {
       'dependencies': [
diff --git a/android_webview/native/android_webview_jni_registrar.cc b/android_webview/native/android_webview_jni_registrar.cc
index a9aa621e..1de8c4c 100644
--- a/android_webview/native/android_webview_jni_registrar.cc
+++ b/android_webview/native/android_webview_jni_registrar.cc
@@ -5,7 +5,6 @@
 #include "android_webview/native/android_webview_jni_registrar.h"
 
 #include "android_webview/native/android_protocol_handler.h"
-#include "android_webview/native/aw_assets.h"
 #include "android_webview/native/aw_autofill_client.h"
 #include "android_webview/native/aw_contents.h"
 #include "android_webview/native/aw_contents_client_bridge.h"
@@ -27,6 +26,7 @@
 #include "android_webview/native/input_stream_impl.h"
 #include "android_webview/native/java_browser_view_renderer_helper.h"
 #include "android_webview/native/permission/aw_permission_request.h"
+#include "android_webview/native/public/aw_assets.h"
 #include "base/android/jni_android.h"
 #include "base/android/jni_registrar.h"
 #include "base/trace_event/trace_event.h"
diff --git a/android_webview/native/aw_assets.cc b/android_webview/native/aw_assets.cc
deleted file mode 100644
index ba734ae..0000000
--- a/android_webview/native/aw_assets.cc
+++ /dev/null
@@ -1,55 +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 <jni.h>
-
-#include "android_webview/native/aw_assets.h"
-
-#include "base/android/jni_array.h"
-#include "base/android/jni_string.h"
-#include "base/android/scoped_java_ref.h"
-#include "jni/AwAssets_jni.h"
-
-namespace android_webview {
-namespace AwAssets {
-
-bool OpenAsset(const std::string& filename,
-               int* fd,
-               int64* offset,
-               int64* size) {
-  JNIEnv* env = base::android::AttachCurrentThread();
-  ScopedJavaLocalRef<jlongArray> jarr = Java_AwAssets_openAsset(
-      env,
-      base::android::GetApplicationContext(),
-      base::android::ConvertUTF8ToJavaString(env, filename).Release());
-  std::vector<jlong> results;
-  base::android::JavaLongArrayToLongVector(env, jarr.obj(), &results);
-  DCHECK_EQ(3U, results.size());
-  *fd = static_cast<int>(results[0]);
-  *offset = results[1];
-  *size = results[2];
-  return *fd != -1;
-}
-
-bool RegisterAssetWithGlobalDescriptors(base::GlobalDescriptors::Key key,
-                                        const std::string& asset_filename) {
-  int asset_fd = 0;
-  int64 asset_off = 0;
-  int64 asset_len = 0;
-  bool result =
-      AwAssets::OpenAsset(asset_filename, &asset_fd, &asset_off, &asset_len);
-  if (result) {
-    base::GlobalDescriptors::GetInstance()->Set(
-        key, asset_fd, base::MemoryMappedFile::Region(asset_off, asset_len));
-  }
-  return result;
-}
-
-}  // namespace AwAssets
-
-bool RegisterAwAssets(JNIEnv* env) {
-  return RegisterNativesImpl(env);
-}
-
-}  // namespace android_webview
diff --git a/android_webview/native/aw_assets.h b/android_webview/native/aw_assets.h
deleted file mode 100644
index 16fcafb..0000000
--- a/android_webview/native/aw_assets.h
+++ /dev/null
@@ -1,36 +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 ANDROID_WEBVIEW_NATIVE_AW_ASSETS_H_
-#define ANDROID_WEBVIEW_NATIVE_AW_ASSETS_H_
-
-#include <string>
-
-#include "base/android/jni_android.h"
-#include "base/posix/global_descriptors.h"
-
-namespace android_webview {
-namespace AwAssets {
-
-// Called by native to retrieve an asset (e.g. a .pak file) from the apk.
-// Returns: true in case of success, false otherwise.
-// Output arguments:
-// - |fd|: file descriptor to the apk. The caller takes the ownership.
-// - |offset|: offset in bytes from the start of the file
-// - |size|: size in bytes of the asset / resource.
-bool OpenAsset(const std::string& filename,
-               int* fd,
-               int64* offset,
-               int64* size);
-
-bool RegisterAssetWithGlobalDescriptors(base::GlobalDescriptors::Key key,
-                                        const std::string& asset_filename);
-
-}  // namespace AwAssets
-
-bool RegisterAwAssets(JNIEnv* env);
-
-}  // namsespace android_webview
-
-#endif  // ANDROID_WEBVIEW_NATIVE_AW_ASSETS_H_
diff --git a/android_webview/native/aw_contents.cc b/android_webview/native/aw_contents.cc
index be09dc7..c3c3110 100644
--- a/android_webview/native/aw_contents.cc
+++ b/android_webview/native/aw_contents.cc
@@ -811,11 +811,11 @@
   browser_view_renderer_.OnDetachedFromWindow();
 }
 
-void AwContents::InvalidateOnFunctorDestroy() {
+void AwContents::DetachFunctorFromView() {
   JNIEnv* env = AttachCurrentThread();
   ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
   if (!obj.is_null())
-    Java_AwContents_invalidateOnFunctorDestroy(env, obj.obj());
+    Java_AwContents_detachFunctorFromView(env, obj.obj());
 }
 
 base::android::ScopedJavaLocalRef<jbyteArray>
diff --git a/android_webview/native/aw_contents.h b/android_webview/native/aw_contents.h
index acbd21d..ce3c911f 100644
--- a/android_webview/native/aw_contents.h
+++ b/android_webview/native/aw_contents.h
@@ -192,7 +192,7 @@
   // BrowserViewRendererClient implementation.
   bool RequestDrawGL(bool wait_for_completion) override;
   void PostInvalidate() override;
-  void InvalidateOnFunctorDestroy() override;
+  void DetachFunctorFromView() override;
   void OnNewPicture() override;
   gfx::Point GetLocationOnScreen() override;
   void ScrollContainerViewTo(gfx::Vector2d new_value) override;
diff --git a/android_webview/native/aw_media_url_interceptor.cc b/android_webview/native/aw_media_url_interceptor.cc
index 1b214da..3f52d9c 100644
--- a/android_webview/native/aw_media_url_interceptor.cc
+++ b/android_webview/native/aw_media_url_interceptor.cc
@@ -5,8 +5,8 @@
 #include <string>
 
 #include "android_webview/common/url_constants.h"
-#include "android_webview/native/aw_assets.h"
 #include "android_webview/native/aw_media_url_interceptor.h"
+#include "android_webview/native/public/aw_assets.h"
 #include "base/strings/string_util.h"
 #include "content/public/common/url_constants.h"
 
diff --git a/android_webview/native/public/aw_assets.cc b/android_webview/native/public/aw_assets.cc
new file mode 100644
index 0000000..e13d04e1
--- /dev/null
+++ b/android_webview/native/public/aw_assets.cc
@@ -0,0 +1,55 @@
+// 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 <jni.h>
+
+#include "android_webview/native/public/aw_assets.h"
+
+#include "base/android/jni_array.h"
+#include "base/android/jni_string.h"
+#include "base/android/scoped_java_ref.h"
+#include "jni/AwAssets_jni.h"
+
+namespace android_webview {
+namespace AwAssets {
+
+bool OpenAsset(const std::string& filename,
+               int* fd,
+               int64* offset,
+               int64* size) {
+  JNIEnv* env = base::android::AttachCurrentThread();
+  ScopedJavaLocalRef<jlongArray> jarr = Java_AwAssets_openAsset(
+      env,
+      base::android::GetApplicationContext(),
+      base::android::ConvertUTF8ToJavaString(env, filename).Release());
+  std::vector<jlong> results;
+  base::android::JavaLongArrayToLongVector(env, jarr.obj(), &results);
+  DCHECK_EQ(3U, results.size());
+  *fd = static_cast<int>(results[0]);
+  *offset = results[1];
+  *size = results[2];
+  return *fd != -1;
+}
+
+bool RegisterAssetWithGlobalDescriptors(base::GlobalDescriptors::Key key,
+                                        const std::string& asset_filename) {
+  int asset_fd = 0;
+  int64 asset_off = 0;
+  int64 asset_len = 0;
+  bool result =
+      AwAssets::OpenAsset(asset_filename, &asset_fd, &asset_off, &asset_len);
+  if (result) {
+    base::GlobalDescriptors::GetInstance()->Set(
+        key, asset_fd, base::MemoryMappedFile::Region(asset_off, asset_len));
+  }
+  return result;
+}
+
+}  // namespace AwAssets
+
+bool RegisterAwAssets(JNIEnv* env) {
+  return RegisterNativesImpl(env);
+}
+
+}  // namespace android_webview
diff --git a/android_webview/native/public/aw_assets.h b/android_webview/native/public/aw_assets.h
new file mode 100644
index 0000000..9eacabf0
--- /dev/null
+++ b/android_webview/native/public/aw_assets.h
@@ -0,0 +1,36 @@
+// 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 ANDROID_WEBVIEW_NATIVE_PUBLIC_AW_ASSETS_H_
+#define ANDROID_WEBVIEW_NATIVE_PUBLIC_AW_ASSETS_H_
+
+#include <string>
+
+#include "base/android/jni_android.h"
+#include "base/posix/global_descriptors.h"
+
+namespace android_webview {
+namespace AwAssets {
+
+// Called by native to retrieve an asset (e.g. a .pak file) from the apk.
+// Returns: true in case of success, false otherwise.
+// Output arguments:
+// - |fd|: file descriptor to the apk. The caller takes the ownership.
+// - |offset|: offset in bytes from the start of the file
+// - |size|: size in bytes of the asset / resource.
+bool OpenAsset(const std::string& filename,
+               int* fd,
+               int64* offset,
+               int64* size);
+
+bool RegisterAssetWithGlobalDescriptors(base::GlobalDescriptors::Key key,
+                                        const std::string& asset_filename);
+
+}  // namespace AwAssets
+
+bool RegisterAwAssets(JNIEnv* env);
+
+}  // namsespace android_webview
+
+#endif  // ANDROID_WEBVIEW_NATIVE_PUBLIC_AW_ASSETS_H_
diff --git a/android_webview/native/webview_native.gyp b/android_webview/native/webview_native.gyp
index 34889e17..121a876b 100644
--- a/android_webview/native/webview_native.gyp
+++ b/android_webview/native/webview_native.gyp
@@ -36,8 +36,6 @@
         'android_protocol_handler.h',
         'android_webview_jni_registrar.cc',
         'android_webview_jni_registrar.h',
-        'aw_assets.cc',
-        'aw_assets.h',
         'aw_autofill_client.cc',
         'aw_autofill_client.h',
         'aw_browser_dependency_factory.cc',
@@ -97,6 +95,8 @@
         'permission/permission_request_handler_client.h',
         'permission/simple_permission_request.cc',
         'permission/simple_permission_request.h',
+        'public/aw_assets.cc',
+        'public/aw_assets.h',
         'state_serializer.cc',
         'state_serializer.h',
       ],
diff --git a/android_webview/test/shell/assets/visual_state_during_fullscreen_test.html b/android_webview/test/shell/assets/visual_state_during_fullscreen_test.html
new file mode 100644
index 0000000..cf1fed0
--- /dev/null
+++ b/android_webview/test/shell/assets/visual_state_during_fullscreen_test.html
@@ -0,0 +1,33 @@
+<html>
+<head>
+<script>
+  function enterFullscreen() {
+    var div = document.getElementById("mydiv");
+    div.webkitRequestFullScreen();
+    div.addEventListener("webkitfullscreenchange", function(){
+      div.style.height = "100%";
+      div.style.width = "100%";
+      div.style.backgroundColor = "red";
+      jsObserver.notifyJava();
+    });
+  }
+</script>
+<style>
+  body {
+    margin: 0px;
+    padding: 0px;
+  }
+  #enterFullscreenControl {
+    height: 100%;
+    width: 100%;
+    padding: 0px 0px;
+    background-color: blue;
+  }
+</style>
+</head>
+<body>
+<button id="enterFullscreenControl" autofocus onclick="enterFullscreen(); return false">
+<div id="mydiv"></div>
+</button>
+</body>
+</html>
diff --git a/android_webview/test/shell/assets/visual_state_waits_for_js_test.html b/android_webview/test/shell/assets/visual_state_waits_for_js_test.html
new file mode 100644
index 0000000..3455d38
--- /dev/null
+++ b/android_webview/test/shell/assets/visual_state_waits_for_js_test.html
@@ -0,0 +1,25 @@
+<html>
+<head>
+<script>
+  function updateColor() {
+    document.getElementById("updateColorControl").style.backgroundColor = "red";
+    jsObserver.notifyJava();
+  }
+</script>
+<style>
+  body {
+    margin: 0px;
+    padding: 0px;
+  }
+  #updateColorControl {
+    height: 100%;
+    width: 100%;
+    padding: 0px 0px;
+    background-color: blue;
+  }
+</style>
+</head>
+<body>
+<button id="updateColorControl" autofocus onclick="updateColor(); return false"></button>
+</body>
+</html>
diff --git a/android_webview/tools/known_issues.py b/android_webview/tools/known_issues.py
index 970fce5..ae203fd 100644
--- a/android_webview/tools/known_issues.py
+++ b/android_webview/tools/known_issues.py
@@ -45,6 +45,7 @@
         'third_party/elfutils',
         'third_party/instrumented_libraries',
         'third_party/liblouis',
+        'third_party/llvm',
         'third_party/speech-dispatcher',
         'third_party/sudden_motion_sensor',
         'third_party/swiftshader',
diff --git a/apps/BUILD.gn b/apps/BUILD.gn
index 3bffcf5..5c409c2b 100644
--- a/apps/BUILD.gn
+++ b/apps/BUILD.gn
@@ -69,8 +69,6 @@
     ]
   }
 
-  if (is_win) {
-    # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
-    cflags = [ "/wd4267" ]
-  }
+  # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+  configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
 }
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index f6f0eb9..ed4cd13 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -25,6 +25,7 @@
     "//base:i18n",
     "//base/third_party/dynamic_annotations",
     "//cc",
+    "//components/device_event_log",
     "//components/user_manager",
     "//components/wallpaper",
     "//content/public/browser",
@@ -62,7 +63,7 @@
     ]
 
     # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
-    cflags = [ "/wd4267" ]
+    configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
   }
 
   if (use_x11) {
@@ -314,9 +315,7 @@
     ]
   }
 
-  if (is_win) {
-    cflags = [ "/wd4267" ]
-  }
+  configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
 
   if (!use_x11) {
     sources -= [ "host/ash_window_tree_host_x11_unittest.cc" ]
diff --git a/ash/ash.gyp b/ash/ash.gyp
index 95547b2..c907df0 100644
--- a/ash/ash.gyp
+++ b/ash/ash.gyp
@@ -873,6 +873,7 @@
         '../base/base.gyp:base_i18n',
         '../base/third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations',
         '../cc/cc.gyp:cc',
+        '../components/components.gyp:device_event_log_component',
         '../components/components.gyp:user_manager',
         '../components/components.gyp:wallpaper',
         '../content/content.gyp:content_browser',
diff --git a/ash/ash_switches.cc b/ash/ash_switches.cc
index 05fabb2..a50a1ae 100644
--- a/ash/ash_switches.cc
+++ b/ash/ash_switches.cc
@@ -39,10 +39,6 @@
     "ash-disable-screen-orientation-lock";
 #endif
 
-// Disables gesture swipe to close windows while in Overview mode.
-const char kAshDisableSwipeToCloseInOverviewMode[] =
-    "ash-disable-swipe-to-close-in-overview-mode";
-
 // Disable the Touch Exploration Mode. Touch Exploration Mode will no longer be
 // turned on automatically when spoken feedback is enabled when this flag is
 // set.
@@ -68,6 +64,10 @@
 // Enables software based mirroring.
 const char kAshEnableSoftwareMirroring[] = "ash-enable-software-mirroring";
 
+// Enables gesture swipe to close windows while in Overview mode.
+const char kAshEnableSwipeToCloseInOverviewMode[] =
+    "ash-enable-swipe-to-close-in-overview-mode";
+
 // Enables touch view testing.
 // TODO(skuhne): Remove TOGGLE_TOUCH_VIEW_TESTING accelerator once this
 // flag is removed.
diff --git a/ash/ash_switches.h b/ash/ash_switches.h
index 50c8aca..2442aff 100644
--- a/ash/ash_switches.h
+++ b/ash/ash_switches.h
@@ -22,7 +22,6 @@
 ASH_EXPORT extern const char kAshCopyHostBackgroundAtBoot[];
 ASH_EXPORT extern const char kAshDebugShortcuts[];
 ASH_EXPORT extern const char kAshDisableLockLayoutManager[];
-ASH_EXPORT extern const char kAshDisableSwipeToCloseInOverviewMode[];
 #if defined(OS_CHROMEOS)
 ASH_EXPORT extern const char kAshDisableScreenOrientationLock[];
 #endif
@@ -34,6 +33,7 @@
 ASH_EXPORT extern const char kAshEnableMirroredScreen[];
 ASH_EXPORT extern const char kAshEnablePowerButtonQuickLock[];
 ASH_EXPORT extern const char kAshEnableSoftwareMirroring[];
+ASH_EXPORT extern const char kAshEnableSwipeToCloseInOverviewMode[];
 ASH_EXPORT extern const char kAshEnableSystemSounds[];
 ASH_EXPORT extern const char kAshEnableTouchViewTesting[];
 ASH_EXPORT extern const char kAshHideNotificationsForFactory[];
diff --git a/ash/autoclick/autoclick_controller.cc b/ash/autoclick/autoclick_controller.cc
index fff3cde..39d1b02 100644
--- a/ash/autoclick/autoclick_controller.cc
+++ b/ash/autoclick/autoclick_controller.cc
@@ -13,6 +13,7 @@
 #include "ui/events/event_constants.h"
 #include "ui/events/event_handler.h"
 #include "ui/events/event_processor.h"
+#include "ui/events/event_utils.h"
 #include "ui/gfx/geometry/point.h"
 #include "ui/gfx/geometry/vector2d.h"
 #include "ui/wm/core/coordinate_conversion.h"
@@ -190,14 +191,12 @@
   aura::WindowTreeHost* host = root_window->GetHost();
   host->ConvertPointToHost(&click_location);
 
-  ui::MouseEvent press_event(ui::ET_MOUSE_PRESSED,
-                             click_location,
-                             click_location,
+  ui::MouseEvent press_event(ui::ET_MOUSE_PRESSED, click_location,
+                             click_location, ui::EventTimeForNow(),
                              mouse_event_flags_ | ui::EF_LEFT_MOUSE_BUTTON,
                              ui::EF_LEFT_MOUSE_BUTTON);
-  ui::MouseEvent release_event(ui::ET_MOUSE_RELEASED,
-                               click_location,
-                               click_location,
+  ui::MouseEvent release_event(ui::ET_MOUSE_RELEASED, click_location,
+                               click_location, ui::EventTimeForNow(),
                                mouse_event_flags_ | ui::EF_LEFT_MOUSE_BUTTON,
                                ui::EF_LEFT_MOUSE_BUTTON);
 
diff --git a/ash/autoclick/autoclick_unittest.cc b/ash/autoclick/autoclick_unittest.cc
index 8768c0c..ff66e04 100644
--- a/ash/autoclick/autoclick_unittest.cc
+++ b/ash/autoclick/autoclick_unittest.cc
@@ -11,6 +11,7 @@
 #include "ui/events/event.h"
 #include "ui/events/event_constants.h"
 #include "ui/events/event_handler.h"
+#include "ui/events/event_utils.h"
 #include "ui/events/keycodes/keyboard_codes.h"
 #include "ui/events/test/event_generator.h"
 
@@ -33,12 +34,10 @@
     ui::EventType type = event->type();
     if (type == ui::ET_MOUSE_MOVED || type == ui::ET_MOUSE_PRESSED ||
         type == ui::ET_MOUSE_RELEASED) {
-      events_.push_back(ui::MouseEvent(
-          event->type(),
-          event->location(),
-          event->root_location(),
-          event->flags(),
-          event->changed_button_flags()));
+      events_.push_back(ui::MouseEvent(event->type(), event->location(),
+                                       event->root_location(),
+                                       ui::EventTimeForNow(), event->flags(),
+                                       event->changed_button_flags()));
       // Stop event propagation so we don't click on random stuff that
       // might break test assumptions.
       event->StopPropagation();
diff --git a/ash/display/mouse_cursor_event_filter.cc b/ash/display/mouse_cursor_event_filter.cc
index baa1466..c736901 100644
--- a/ash/display/mouse_cursor_event_filter.cc
+++ b/ash/display/mouse_cursor_event_filter.cc
@@ -110,7 +110,6 @@
 
 MouseCursorEventFilter::MouseCursorEventFilter()
     : mouse_warp_mode_(WARP_ALWAYS),
-      was_mouse_warped_(false),
       drag_source_root_(NULL),
       scale_when_drag_started_(1.0f),
       shared_display_edge_indicator_(new SharedDisplayEdgeIndicator) {
diff --git a/ash/display/mouse_cursor_event_filter.h b/ash/display/mouse_cursor_event_filter.h
index 972158c..62c02cb6 100644
--- a/ash/display/mouse_cursor_event_filter.h
+++ b/ash/display/mouse_cursor_event_filter.h
@@ -96,17 +96,11 @@
   void GetSrcAndDstRootWindows(aura::Window** src_window,
                                aura::Window** dst_window);
 
-  void reset_was_mouse_warped_for_test() { was_mouse_warped_ = false; }
-
   bool WarpMouseCursorIfNecessaryForTest(aura::Window* target_root,
                                          const gfx::Point& point_in_screen);
 
   MouseWarpMode mouse_warp_mode_;
 
-  // This flag is used to suppress the accidental mouse warp back to the
-  // original display.
-  bool was_mouse_warped_;
-
   // The bounds for warp hole windows. |dst_indicator_bounds_| is kept
   // in the instance for testing.
   gfx::Rect src_indicator_bounds_;
diff --git a/ash/display/mouse_cursor_event_filter_unittest.cc b/ash/display/mouse_cursor_event_filter_unittest.cc
index 3b4f9ee..d1b9f38 100644
--- a/ash/display/mouse_cursor_event_filter_unittest.cc
+++ b/ash/display/mouse_cursor_event_filter_unittest.cc
@@ -11,6 +11,7 @@
 #include "ash/display/display_manager.h"
 #include "ui/aura/env.h"
 #include "ui/aura/window_event_dispatcher.h"
+#include "ui/events/event_utils.h"
 #include "ui/gfx/display.h"
 #include "ui/gfx/screen.h"
 
@@ -30,7 +31,6 @@
                                   gfx::Point point_in_screen) {
     bool is_warped = event_filter()->WarpMouseCursorIfNecessaryForTest(
         target_root, point_in_screen);
-    event_filter()->reset_was_mouse_warped_for_test();
     return is_warped;
   }
 
@@ -39,16 +39,15 @@
       aura::Window* target_root,
       gfx::Point point_in_screen) {
     gfx::Point location = drag_source_root->bounds().CenterPoint();
-    ui::MouseEvent pressed(ui::ET_MOUSE_PRESSED, location,
-                           location, 0, 0);
+    ui::MouseEvent pressed(ui::ET_MOUSE_PRESSED, location, location,
+                           ui::EventTimeForNow(), 0, 0);
     ui::Event::DispatcherApi(&pressed).set_target(drag_source_root);
     event_filter()->OnMouseEvent(&pressed);
     bool is_warped = event_filter()->WarpMouseCursorIfNecessaryForTest(
         target_root, point_in_screen);
-    event_filter()->reset_was_mouse_warped_for_test();
 
-    ui::MouseEvent released(ui::ET_MOUSE_RELEASED, location,
-                            location, 0, 0);
+    ui::MouseEvent released(ui::ET_MOUSE_RELEASED, location, location,
+                            ui::EventTimeForNow(), 0, 0);
     ui::Event::DispatcherApi(&released).set_target(drag_source_root);
     event_filter()->OnMouseEvent(&released);
     return is_warped;
@@ -155,8 +154,6 @@
   EXPECT_EQ("500,123",
             aura::Env::GetInstance()->last_mouse_location().ToString());
 
-  event_filter()->reset_was_mouse_warped_for_test();
-
   // Touch the edge of 2nd display again and make sure it warps to
   // 1st dislay.
   EXPECT_TRUE(event_filter()->WarpMouseCursorIfNecessaryForTest(
diff --git a/ash/drag_drop/drag_drop_controller_unittest.cc b/ash/drag_drop/drag_drop_controller_unittest.cc
index 537b4527..6ee3a59 100644
--- a/ash/drag_drop/drag_drop_controller_unittest.cc
+++ b/ash/drag_drop/drag_drop_controller_unittest.cc
@@ -722,7 +722,7 @@
     // The DragDropController should simply ignore these events.
     gfx::Point mouse_move_location = drag_view->bounds().CenterPoint();
     ui::MouseEvent mouse_move(ui::ET_MOUSE_MOVED, mouse_move_location,
-                              mouse_move_location, 0, 0);
+                              mouse_move_location, ui::EventTimeForNow(), 0, 0);
     ui::EventDispatchDetails details = Shell::GetPrimaryRootWindow()->
         GetHost()->event_processor()->OnEventFromSource(&mouse_move);
     ASSERT_FALSE(details.dispatcher_destroyed);
@@ -1006,18 +1006,14 @@
     GetDragImageWindow()->AddObserver(&observer);
 
     {
-      ui::MouseEvent e(ui::ET_MOUSE_DRAGGED,
-                       gfx::Point(200, 0),
-                       gfx::Point(200, 0),
-                       ui::EF_NONE,
+      ui::MouseEvent e(ui::ET_MOUSE_DRAGGED, gfx::Point(200, 0),
+                       gfx::Point(200, 0), ui::EventTimeForNow(), ui::EF_NONE,
                        ui::EF_NONE);
       drag_drop_controller_->DragUpdate(window, e);
     }
     {
-      ui::MouseEvent e(ui::ET_MOUSE_DRAGGED,
-                       gfx::Point(600, 0),
-                       gfx::Point(600, 0),
-                       ui::EF_NONE,
+      ui::MouseEvent e(ui::ET_MOUSE_DRAGGED, gfx::Point(600, 0),
+                       gfx::Point(600, 0), ui::EventTimeForNow(), ui::EF_NONE,
                        ui::EF_NONE);
       drag_drop_controller_->DragUpdate(window, e);
     }
@@ -1043,18 +1039,14 @@
     GetDragImageWindow()->AddObserver(&observer);
 
     {
-      ui::MouseEvent e(ui::ET_MOUSE_DRAGGED,
-                       gfx::Point(600, 0),
-                       gfx::Point(600, 0),
-                       ui::EF_NONE,
+      ui::MouseEvent e(ui::ET_MOUSE_DRAGGED, gfx::Point(600, 0),
+                       gfx::Point(600, 0), ui::EventTimeForNow(), ui::EF_NONE,
                        ui::EF_NONE);
       drag_drop_controller_->DragUpdate(window, e);
     }
     {
-      ui::MouseEvent e(ui::ET_MOUSE_DRAGGED,
-                       gfx::Point(200, 0),
-                       gfx::Point(200, 0),
-                       ui::EF_NONE,
+      ui::MouseEvent e(ui::ET_MOUSE_DRAGGED, gfx::Point(200, 0),
+                       gfx::Point(200, 0), ui::EventTimeForNow(), ui::EF_NONE,
                        ui::EF_NONE);
       drag_drop_controller_->DragUpdate(window, e);
     }
diff --git a/ash/drag_drop/drag_drop_tracker.cc b/ash/drag_drop/drag_drop_tracker.cc
index d851098..49715143 100644
--- a/ash/drag_drop/drag_drop_tracker.cc
+++ b/ash/drag_drop/drag_drop_tracker.cc
@@ -11,6 +11,7 @@
 #include "ui/aura/window.h"
 #include "ui/aura/window_event_dispatcher.h"
 #include "ui/events/event.h"
+#include "ui/events/event_utils.h"
 #include "ui/gfx/screen.h"
 #include "ui/wm/core/coordinate_conversion.h"
 #include "ui/wm/public/activation_delegate.h"
@@ -91,12 +92,10 @@
       capture_window_->GetRootWindow(),
       ash::wm::GetRootWindowAt(location_in_screen),
       &target_root_location);
-  return new ui::MouseEvent(event.type(),
-                            target_location,
-                            target_root_location,
-                            event.flags(),
-                            static_cast<const ui::MouseEvent&>(event).
-                                changed_button_flags());
+  return new ui::MouseEvent(
+      event.type(), target_location, target_root_location,
+      ui::EventTimeForNow(), event.flags(),
+      static_cast<const ui::MouseEvent&>(event).changed_button_flags());
 }
 
 }  // namespace ash
diff --git a/ash/drag_drop/drag_drop_tracker_unittest.cc b/ash/drag_drop/drag_drop_tracker_unittest.cc
index 0e6d7f2a..0e4bfa1 100644
--- a/ash/drag_drop/drag_drop_tracker_unittest.cc
+++ b/ash/drag_drop/drag_drop_tracker_unittest.cc
@@ -11,6 +11,7 @@
 #include "ui/aura/test/test_windows.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_event_dispatcher.h"
+#include "ui/events/event_utils.h"
 
 namespace ash {
 namespace test {
@@ -30,11 +31,8 @@
   static aura::Window* GetTarget(const gfx::Point& location) {
     scoped_ptr<DragDropTracker> tracker(
         new DragDropTracker(Shell::GetPrimaryRootWindow(), NULL));
-    ui::MouseEvent e(ui::ET_MOUSE_DRAGGED,
-                     location,
-                     location,
-                     ui::EF_NONE,
-                     ui::EF_NONE);
+    ui::MouseEvent e(ui::ET_MOUSE_DRAGGED, location, location,
+                     ui::EventTimeForNow(), ui::EF_NONE, ui::EF_NONE);
     aura::Window* target = tracker->GetTarget(e);
     return target;
   }
@@ -132,11 +130,9 @@
 
   // Start tracking from the RootWindow0 and converts the mouse event into
   // |window0|'s coodinates.
-  ui::MouseEvent original00(ui::ET_MOUSE_DRAGGED,
-                            gfx::Point(50, 50),
-                            gfx::Point(50, 50),
-                            ui::EF_NONE,
-                            ui::EF_NONE);
+  ui::MouseEvent original00(ui::ET_MOUSE_DRAGGED, gfx::Point(50, 50),
+                            gfx::Point(50, 50), ui::EventTimeForNow(),
+                            ui::EF_NONE, ui::EF_NONE);
   scoped_ptr<ui::LocatedEvent> converted00(ConvertEvent(window0.get(),
                                                         original00));
   EXPECT_EQ(original00.type(), converted00->type());
@@ -146,11 +142,9 @@
 
   // Start tracking from the RootWindow0 and converts the mouse event into
   // |window1|'s coodinates.
-  ui::MouseEvent original01(ui::ET_MOUSE_DRAGGED,
-                            gfx::Point(350, 150),
-                            gfx::Point(350, 150),
-                            ui::EF_NONE,
-                            ui::EF_NONE);
+  ui::MouseEvent original01(ui::ET_MOUSE_DRAGGED, gfx::Point(350, 150),
+                            gfx::Point(350, 150), ui::EventTimeForNow(),
+                            ui::EF_NONE, ui::EF_NONE);
   scoped_ptr<ui::LocatedEvent> converted01(ConvertEvent(window1.get(),
                                                         original01));
   EXPECT_EQ(original01.type(), converted01->type());
@@ -163,11 +157,9 @@
 
   // Start tracking from the RootWindow1 and converts the mouse event into
   // |window0|'s coodinates.
-  ui::MouseEvent original10(ui::ET_MOUSE_DRAGGED,
-                            gfx::Point(-150, 50),
-                            gfx::Point(-150, 50),
-                            ui::EF_NONE,
-                            ui::EF_NONE);
+  ui::MouseEvent original10(ui::ET_MOUSE_DRAGGED, gfx::Point(-150, 50),
+                            gfx::Point(-150, 50), ui::EventTimeForNow(),
+                            ui::EF_NONE, ui::EF_NONE);
   scoped_ptr<ui::LocatedEvent> converted10(ConvertEvent(window0.get(),
                                                         original10));
   EXPECT_EQ(original10.type(), converted10->type());
@@ -177,11 +169,9 @@
 
   // Start tracking from the RootWindow1 and converts the mouse event into
   // |window1|'s coodinates.
-  ui::MouseEvent original11(ui::ET_MOUSE_DRAGGED,
-                            gfx::Point(150, 150),
-                            gfx::Point(150, 150),
-                            ui::EF_NONE,
-                            ui::EF_NONE);
+  ui::MouseEvent original11(ui::ET_MOUSE_DRAGGED, gfx::Point(150, 150),
+                            gfx::Point(150, 150), ui::EventTimeForNow(),
+                            ui::EF_NONE, ui::EF_NONE);
   scoped_ptr<ui::LocatedEvent> converted11(ConvertEvent(window1.get(),
                                                            original11));
   EXPECT_EQ(original11.type(), converted11->type());
diff --git a/ash/frame/caption_buttons/frame_caption_button.cc b/ash/frame/caption_buttons/frame_caption_button.cc
index 2eb30a3..7845efe 100644
--- a/ash/frame/caption_buttons/frame_caption_button.cc
+++ b/ash/frame/caption_buttons/frame_caption_button.cc
@@ -20,6 +20,9 @@
 // animation as a ratio of |kSwapImagesAnimationDurationMs|.
 const float kFadeOutRatio = 0.5f;
 
+// The alpha to draw inactive icons with.
+const float kInactiveIconAlpha = 0.2f;
+
 }  // namespace
 
 // static
@@ -32,7 +35,6 @@
       paint_as_active_(false),
       alpha_(255),
       icon_image_id_(-1),
-      inactive_icon_image_id_(-1),
       hovered_background_image_id_(-1),
       pressed_background_image_id_(-1),
       swap_images_animation_(new gfx::SlideAnimation(this)) {
@@ -49,7 +51,6 @@
 void FrameCaptionButton::SetImages(CaptionButtonIcon icon,
                                    Animate animate,
                                    int icon_image_id,
-                                   int inactive_icon_image_id,
                                    int hovered_background_image_id,
                                    int pressed_background_image_id) {
   // The early return is dependant on |animate| because callers use SetImages()
@@ -57,24 +58,21 @@
   if (icon == icon_ &&
       (animate == ANIMATE_YES || !swap_images_animation_->is_animating()) &&
       icon_image_id == icon_image_id_ &&
-      inactive_icon_image_id == inactive_icon_image_id_ &&
       hovered_background_image_id == hovered_background_image_id_ &&
       pressed_background_image_id == pressed_background_image_id_) {
     return;
   }
 
   if (animate == ANIMATE_YES)
-    crossfade_icon_image_ = GetIconImageToPaint();
+    crossfade_icon_image_ = icon_image_;
 
   icon_ = icon;
   icon_image_id_ = icon_image_id;
-  inactive_icon_image_id_ = inactive_icon_image_id;
   hovered_background_image_id_ = hovered_background_image_id;
   pressed_background_image_id_ = pressed_background_image_id;
 
   ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
   icon_image_ = *rb.GetImageSkiaNamed(icon_image_id);
-  inactive_icon_image_ = *rb.GetImageSkiaNamed(inactive_icon_image_id);
   hovered_background_image_ = *rb.GetImageSkiaNamed(
       hovered_background_image_id);
   pressed_background_image_ = *rb.GetImageSkiaNamed(
@@ -127,12 +125,11 @@
   if (icon_alpha < static_cast<int>(kFadeOutRatio * 255))
      crossfade_icon_alpha = static_cast<int>(255 - icon_alpha / kFadeOutRatio);
 
-  gfx::ImageSkia icon_image = GetIconImageToPaint();
   if (crossfade_icon_alpha > 0 && !crossfade_icon_image_.isNull()) {
-    gfx::Canvas icon_canvas(icon_image.size(), canvas->image_scale(), false);
+    gfx::Canvas icon_canvas(icon_image_.size(), canvas->image_scale(), false);
     SkPaint paint;
     paint.setAlpha(icon_alpha);
-    icon_canvas.DrawImageInt(icon_image, 0, 0, paint);
+    icon_canvas.DrawImageInt(icon_image_, 0, 0, paint);
 
     paint.setAlpha(crossfade_icon_alpha);
     paint.setXfermodeMode(SkXfermode::kPlus_Mode);
@@ -143,7 +140,7 @@
   } else {
     if (!swap_images_animation_->is_animating())
       icon_alpha = alpha_;
-    PaintCentered(canvas, icon_image, icon_alpha);
+    PaintCentered(canvas, icon_image_, icon_alpha);
   }
 }
 
@@ -170,13 +167,12 @@
   CustomButton::OnGestureEvent(event);
 }
 
-const gfx::ImageSkia& FrameCaptionButton::GetIconImageToPaint() const {
-  return paint_as_active_ ? icon_image_ : inactive_icon_image_;
-}
-
 void FrameCaptionButton::PaintCentered(gfx::Canvas* canvas,
                                        const gfx::ImageSkia& to_center,
                                        int alpha) {
+  if (!paint_as_active_)
+    alpha *= kInactiveIconAlpha;
+
   SkPaint paint;
   paint.setAlpha(alpha);
   canvas->DrawImageInt(to_center,
diff --git a/ash/frame/caption_buttons/frame_caption_button.h b/ash/frame/caption_buttons/frame_caption_button.h
index 05028e4..a54461d 100644
--- a/ash/frame/caption_buttons/frame_caption_button.h
+++ b/ash/frame/caption_buttons/frame_caption_button.h
@@ -38,7 +38,6 @@
   void SetImages(CaptionButtonIcon icon,
                  Animate animate,
                  int icon_image_id,
-                 int inactive_icon_image_id,
                  int hovered_background_image_id,
                  int pressed_background_image_id);
 
@@ -69,9 +68,6 @@
   void OnGestureEvent(ui::GestureEvent* event) override;
 
  private:
-  // Returns the icon image to paint based on |paint_as_active_|.
-  const gfx::ImageSkia& GetIconImageToPaint() const;
-
   // Paints |to_center| centered within the button with |alpha|.
   void PaintCentered(gfx::Canvas* canvas,
                      const gfx::ImageSkia& to_center,
@@ -88,11 +84,9 @@
 
   // The images and image ids used to paint the button.
   int icon_image_id_;
-  int inactive_icon_image_id_;
   int hovered_background_image_id_;
   int pressed_background_image_id_;
   gfx::ImageSkia icon_image_;
-  gfx::ImageSkia inactive_icon_image_;
   gfx::ImageSkia hovered_background_image_;
   gfx::ImageSkia pressed_background_image_;
 
diff --git a/ash/frame/caption_buttons/frame_caption_button_container_view.cc b/ash/frame/caption_buttons/frame_caption_button_container_view.cc
index 0890907..686fa08a 100644
--- a/ash/frame/caption_buttons/frame_caption_button_container_view.cc
+++ b/ash/frame/caption_buttons/frame_caption_button_container_view.cc
@@ -151,11 +151,9 @@
 void FrameCaptionButtonContainerView::SetButtonImages(
     CaptionButtonIcon icon,
     int icon_image_id,
-    int inactive_icon_image_id,
     int hovered_background_image_id,
     int pressed_background_image_id) {
   button_icon_id_map_[icon] = ButtonIconIds(icon_image_id,
-                                            inactive_icon_image_id,
                                             hovered_background_image_id,
                                             pressed_background_image_id);
   FrameCaptionButton* buttons[] = {
@@ -166,7 +164,6 @@
       buttons[i]->SetImages(icon,
                             FrameCaptionButton::ANIMATE_NO,
                             icon_image_id,
-                            inactive_icon_image_id,
                             hovered_background_image_id,
                             pressed_background_image_id);
     }
@@ -307,7 +304,6 @@
     button->SetImages(icon,
                       fcb_animate,
                       it->second.icon_image_id,
-                      it->second.inactive_icon_image_id,
                       it->second.hovered_background_image_id,
                       it->second.pressed_background_image_id);
   }
@@ -418,18 +414,15 @@
 
 FrameCaptionButtonContainerView::ButtonIconIds::ButtonIconIds()
     : icon_image_id(-1),
-      inactive_icon_image_id(-1),
       hovered_background_image_id(-1),
       pressed_background_image_id(-1) {
 }
 
 FrameCaptionButtonContainerView::ButtonIconIds::ButtonIconIds(
     int icon_id,
-    int inactive_icon_id,
     int hovered_background_id,
     int pressed_background_id)
     : icon_image_id(icon_id),
-      inactive_icon_image_id(inactive_icon_id),
       hovered_background_image_id(hovered_background_id),
       pressed_background_image_id(pressed_background_id) {
 }
diff --git a/ash/frame/caption_buttons/frame_caption_button_container_view.h b/ash/frame/caption_buttons/frame_caption_button_container_view.h
index 1d915943..e5620f5 100644
--- a/ash/frame/caption_buttons/frame_caption_button_container_view.h
+++ b/ash/frame/caption_buttons/frame_caption_button_container_view.h
@@ -69,7 +69,6 @@
   // |icon| even if none of the buttons currently use |icon|.
   void SetButtonImages(CaptionButtonIcon icon,
                        int icon_image_id,
-                       int inactive_icon_image_id,
                        int hovered_background_image_id,
                        int pressed_background_image_id);
 
@@ -105,13 +104,11 @@
   struct ButtonIconIds {
     ButtonIconIds();
     ButtonIconIds(int icon_id,
-                  int inactive_icon_id,
                   int hovered_background_id,
                   int pressed_background_id);
     ~ButtonIconIds();
 
     int icon_image_id;
-    int inactive_icon_image_id;
     int hovered_background_image_id;
     int pressed_background_image_id;
   };
diff --git a/ash/frame/caption_buttons/frame_caption_button_container_view_unittest.cc b/ash/frame/caption_buttons/frame_caption_button_container_view_unittest.cc
index fe538808..2524d8db 100644
--- a/ash/frame/caption_buttons/frame_caption_button_container_view_unittest.cc
+++ b/ash/frame/caption_buttons/frame_caption_button_container_view_unittest.cc
@@ -76,7 +76,6 @@
       container->SetButtonImages(
           static_cast<CaptionButtonIcon>(icon),
           IDR_AURA_WINDOW_CONTROL_ICON_CLOSE,
-          IDR_AURA_WINDOW_CONTROL_ICON_CLOSE_I,
           IDR_AURA_WINDOW_CONTROL_BACKGROUND_H,
           IDR_AURA_WINDOW_CONTROL_BACKGROUND_P);
     }
diff --git a/ash/frame/caption_buttons/frame_size_button_unittest.cc b/ash/frame/caption_buttons/frame_size_button_unittest.cc
index f02d682..3c950c1 100644
--- a/ash/frame/caption_buttons/frame_size_button_unittest.cc
+++ b/ash/frame/caption_buttons/frame_size_button_unittest.cc
@@ -63,7 +63,6 @@
         caption_button_container_->SetButtonImages(
             static_cast<CaptionButtonIcon>(icon),
             IDR_AURA_WINDOW_CONTROL_ICON_CLOSE,
-            IDR_AURA_WINDOW_CONTROL_ICON_CLOSE_I,
             IDR_AURA_WINDOW_CONTROL_BACKGROUND_H,
             IDR_AURA_WINDOW_CONTROL_BACKGROUND_P);
       }
diff --git a/ash/frame/default_header_painter.cc b/ash/frame/default_header_painter.cc
index 236919f3..72d720e 100644
--- a/ash/frame/default_header_painter.cc
+++ b/ash/frame/default_header_painter.cc
@@ -39,6 +39,8 @@
 const SkColor kDefaultFrameColor = SkColorSetRGB(242, 242, 242);
 // Duration of crossfade animation for activating and deactivating frame.
 const int kActivationCrossfadeDurationMs = 200;
+// Luminance below which to use white caption buttons.
+const int kMaxLuminanceForLightButtons = 125;
 
 // Tiles an image into an area, rounding the top corners.
 void TileRoundRect(gfx::Canvas* canvas,
@@ -99,36 +101,7 @@
   frame_ = frame;
   view_ = header_view;
   caption_button_container_ = caption_button_container;
-
-  caption_button_container_->SetButtonImages(
-      CAPTION_BUTTON_ICON_MINIMIZE,
-      IDR_AURA_WINDOW_CONTROL_ICON_MINIMIZE,
-      IDR_AURA_WINDOW_CONTROL_ICON_MINIMIZE_I,
-      IDR_AURA_WINDOW_CONTROL_BACKGROUND_H,
-      IDR_AURA_WINDOW_CONTROL_BACKGROUND_P);
-  UpdateSizeButtonImages();
-  caption_button_container_->SetButtonImages(
-      CAPTION_BUTTON_ICON_CLOSE,
-      IDR_AURA_WINDOW_CONTROL_ICON_CLOSE,
-      IDR_AURA_WINDOW_CONTROL_ICON_CLOSE_I,
-      IDR_AURA_WINDOW_CONTROL_BACKGROUND_H,
-      IDR_AURA_WINDOW_CONTROL_BACKGROUND_P);
-
-  // There is no dedicated icon for the snap-left and snap-right buttons
-  // when |frame_| is inactive because they should never be visible while
-  // |frame_| is inactive.
-  caption_button_container_->SetButtonImages(
-      CAPTION_BUTTON_ICON_LEFT_SNAPPED,
-      IDR_AURA_WINDOW_CONTROL_ICON_LEFT_SNAPPED,
-      IDR_AURA_WINDOW_CONTROL_ICON_LEFT_SNAPPED,
-      IDR_AURA_WINDOW_CONTROL_BACKGROUND_H,
-      IDR_AURA_WINDOW_CONTROL_BACKGROUND_P);
-  caption_button_container_->SetButtonImages(
-      CAPTION_BUTTON_ICON_RIGHT_SNAPPED,
-      IDR_AURA_WINDOW_CONTROL_ICON_RIGHT_SNAPPED,
-      IDR_AURA_WINDOW_CONTROL_ICON_RIGHT_SNAPPED,
-      IDR_AURA_WINDOW_CONTROL_BACKGROUND_H,
-      IDR_AURA_WINDOW_CONTROL_BACKGROUND_P);
+  UpdateAllButtonImages();
 }
 
 int DefaultHeaderPainter::GetMinimumHeaderWidth() const {
@@ -143,6 +116,7 @@
   mode_ = mode;
 
   if (mode_ != old_mode) {
+    UpdateAllButtonImages();
     if (!initial_paint_ && HeaderPainterUtil::CanAnimateActivation(frame_)) {
       activation_animation_->SetSlideDuration(kActivationCrossfadeDurationMs);
       if (mode_ == MODE_ACTIVE)
@@ -181,7 +155,7 @@
 }
 
 void DefaultHeaderPainter::LayoutHeader() {
-  UpdateSizeButtonImages();
+  UpdateSizeButtonImages(ShouldUseLightImages());
   caption_button_container_->Layout();
 
   gfx::Size caption_button_container_size =
@@ -221,6 +195,7 @@
                                           SkColor inactive_frame_color) {
   active_frame_color_ = active_frame_color;
   inactive_frame_color_ = inactive_frame_color;
+  UpdateAllButtonImages();
 }
 
 void DefaultHeaderPainter::UpdateLeftHeaderView(views::View* left_header_view) {
@@ -311,20 +286,57 @@
   }
 }
 
-void DefaultHeaderPainter::UpdateSizeButtonImages() {
+bool DefaultHeaderPainter::ShouldUseLightImages() {
+  int luminance = color_utils::GetLuminanceForColor(
+      mode_ == MODE_INACTIVE ? inactive_frame_color_ : active_frame_color_);
+  return luminance < kMaxLuminanceForLightButtons;
+}
+
+void DefaultHeaderPainter::UpdateAllButtonImages() {
+  bool use_light_images = ShouldUseLightImages();
+  caption_button_container_->SetButtonImages(
+      CAPTION_BUTTON_ICON_MINIMIZE,
+      use_light_images ? IDR_AURA_WINDOW_CONTROL_ICON_MINIMIZE_WHITE
+                       : IDR_AURA_WINDOW_CONTROL_ICON_MINIMIZE,
+      IDR_AURA_WINDOW_CONTROL_BACKGROUND_H,
+      IDR_AURA_WINDOW_CONTROL_BACKGROUND_P);
+
+  UpdateSizeButtonImages(use_light_images);
+
+  caption_button_container_->SetButtonImages(
+      CAPTION_BUTTON_ICON_CLOSE,
+      use_light_images ? IDR_AURA_WINDOW_CONTROL_ICON_CLOSE_WHITE
+                       : IDR_AURA_WINDOW_CONTROL_ICON_CLOSE,
+      IDR_AURA_WINDOW_CONTROL_BACKGROUND_H,
+      IDR_AURA_WINDOW_CONTROL_BACKGROUND_P);
+
+  caption_button_container_->SetButtonImages(
+      CAPTION_BUTTON_ICON_LEFT_SNAPPED,
+      use_light_images ? IDR_AURA_WINDOW_CONTROL_ICON_LEFT_SNAPPED_WHITE
+                       : IDR_AURA_WINDOW_CONTROL_ICON_LEFT_SNAPPED,
+      IDR_AURA_WINDOW_CONTROL_BACKGROUND_H,
+      IDR_AURA_WINDOW_CONTROL_BACKGROUND_P);
+
+  caption_button_container_->SetButtonImages(
+      CAPTION_BUTTON_ICON_RIGHT_SNAPPED,
+      use_light_images ? IDR_AURA_WINDOW_CONTROL_ICON_RIGHT_SNAPPED_WHITE
+                       : IDR_AURA_WINDOW_CONTROL_ICON_RIGHT_SNAPPED,
+      IDR_AURA_WINDOW_CONTROL_BACKGROUND_H,
+      IDR_AURA_WINDOW_CONTROL_BACKGROUND_P);
+}
+
+void DefaultHeaderPainter::UpdateSizeButtonImages(bool use_light_images) {
   int icon_id = 0;
-  int inactive_icon_id = 0;
   if (frame_->IsMaximized() || frame_->IsFullscreen()) {
-    icon_id = IDR_AURA_WINDOW_CONTROL_ICON_RESTORE;
-    inactive_icon_id = IDR_AURA_WINDOW_CONTROL_ICON_RESTORE_I;
+    icon_id = use_light_images ? IDR_AURA_WINDOW_CONTROL_ICON_RESTORE_WHITE
+                               : IDR_AURA_WINDOW_CONTROL_ICON_RESTORE;
   } else {
-    icon_id = IDR_AURA_WINDOW_CONTROL_ICON_MAXIMIZE;
-    inactive_icon_id = IDR_AURA_WINDOW_CONTROL_ICON_MAXIMIZE_I;
+    icon_id = use_light_images ? IDR_AURA_WINDOW_CONTROL_ICON_MAXIMIZE_WHITE
+                               : IDR_AURA_WINDOW_CONTROL_ICON_MAXIMIZE;
   }
   caption_button_container_->SetButtonImages(
       CAPTION_BUTTON_ICON_MAXIMIZE_RESTORE,
       icon_id,
-      inactive_icon_id,
       IDR_AURA_WINDOW_CONTROL_BACKGROUND_H,
       IDR_AURA_WINDOW_CONTROL_BACKGROUND_P);
 }
diff --git a/ash/frame/default_header_painter.h b/ash/frame/default_header_painter.h
index 1c5f979..662d4c1 100644
--- a/ash/frame/default_header_painter.h
+++ b/ash/frame/default_header_painter.h
@@ -57,6 +57,7 @@
 
  private:
   FRIEND_TEST_ALL_PREFIXES(DefaultHeaderPainterTest, TitleIconAlignment);
+  FRIEND_TEST_ALL_PREFIXES(DefaultHeaderPainterTest, LightIcons);
 
   // gfx::AnimationDelegate override:
   void AnimationProgressed(const gfx::Animation* animation) override;
@@ -74,8 +75,15 @@
   // Layout the left header view.
   void LayoutLeftHeaderView();
 
+  // Whether light caption images should be used. This is the case when the
+  // background of the frame is dark.
+  bool ShouldUseLightImages();
+
+  // Update all the images in the caption buttons.
+  void UpdateAllButtonImages();
+
   // Updates the size button's images.
-  void UpdateSizeButtonImages();
+  void UpdateSizeButtonImages(bool use_light_images);
 
   // Returns the header bounds in the coordinates of |view_|. The header is
   // assumed to be positioned at the top left corner of |view_| and to have the
diff --git a/ash/frame/default_header_painter_unittest.cc b/ash/frame/default_header_painter_unittest.cc
index c4e2106..0bf03ebc 100644
--- a/ash/frame/default_header_painter_unittest.cc
+++ b/ash/frame/default_header_painter_unittest.cc
@@ -53,4 +53,45 @@
             title_bounds.CenterPoint().y());
 }
 
+// Ensure the light icons are used when appropriate.
+TEST_F(DefaultHeaderPainterTest, LightIcons) {
+  scoped_ptr<Widget> w(CreateTestWidget());
+  ash::FrameCaptionButtonContainerView container(w.get());
+  views::StaticSizedView window_icon(gfx::Size(16, 16));
+  window_icon.SetBounds(0, 0, 16, 16);
+  w->SetBounds(gfx::Rect(0, 0, 500, 500));
+  w->Show();
+
+  DefaultHeaderPainter painter;
+  painter.Init(w.get(), w->non_client_view()->frame_view(), &container);
+
+  // Check by default light icons are not used.
+  painter.mode_ = HeaderPainter::MODE_ACTIVE;
+  EXPECT_EQ(false, painter.ShouldUseLightImages());
+  painter.mode_ = HeaderPainter::MODE_INACTIVE;
+  EXPECT_EQ(false, painter.ShouldUseLightImages());
+
+  // Check that setting dark colors should use light icons.
+  painter.SetFrameColors(SkColorSetRGB(0, 0, 0), SkColorSetRGB(0, 0, 0));
+  painter.mode_ = HeaderPainter::MODE_ACTIVE;
+  EXPECT_EQ(true, painter.ShouldUseLightImages());
+  painter.mode_ = HeaderPainter::MODE_INACTIVE;
+  EXPECT_EQ(true, painter.ShouldUseLightImages());
+
+  // Check that inactive and active colors are used properly.
+  painter.SetFrameColors(SkColorSetRGB(0, 0, 0), SkColorSetRGB(255, 255, 255));
+  painter.mode_ = HeaderPainter::MODE_ACTIVE;
+  EXPECT_EQ(true, painter.ShouldUseLightImages());
+  painter.mode_ = HeaderPainter::MODE_INACTIVE;
+  EXPECT_EQ(false, painter.ShouldUseLightImages());
+
+  // Check not so light or dark colors.
+  painter.SetFrameColors(SkColorSetRGB(70, 70, 70),
+                         SkColorSetRGB(200, 200, 200));
+  painter.mode_ = HeaderPainter::MODE_ACTIVE;
+  EXPECT_EQ(true, painter.ShouldUseLightImages());
+  painter.mode_ = HeaderPainter::MODE_INACTIVE;
+  EXPECT_EQ(false, painter.ShouldUseLightImages());
+}
+
 }  // namespace ash
diff --git a/ash/resources/ash_resources.grd b/ash/resources/ash_resources.grd
index 6b49345..dbee687 100644
--- a/ash/resources/ash_resources.grd
+++ b/ash/resources/ash_resources.grd
@@ -192,17 +192,19 @@
       <structure type="chrome_scaled_image" name="IDR_AURA_WINDOW_CONTROL_BACKGROUND_H" file="common/window_control_background_hover.png" />
       <structure type="chrome_scaled_image" name="IDR_AURA_WINDOW_CONTROL_BACKGROUND_P" file="common/window_control_background_pressed.png" />
       <structure type="chrome_scaled_image" name="IDR_AURA_WINDOW_CONTROL_ICON_BACK" file="common/window_control_icon_back.png" />
-      <structure type="chrome_scaled_image" name="IDR_AURA_WINDOW_CONTROL_ICON_BACK_I" file="common/window_control_icon_back_inactive.png" />
+      <structure type="chrome_scaled_image" name="IDR_AURA_WINDOW_CONTROL_ICON_BACK_WHITE" file="common/window_control_icon_back_white.png" />
       <structure type="chrome_scaled_image" name="IDR_AURA_WINDOW_CONTROL_ICON_CLOSE" file="common/window_control_icon_close.png" />
-      <structure type="chrome_scaled_image" name="IDR_AURA_WINDOW_CONTROL_ICON_CLOSE_I" file="common/window_control_icon_close_inactive.png" />
+      <structure type="chrome_scaled_image" name="IDR_AURA_WINDOW_CONTROL_ICON_CLOSE_WHITE" file="common/window_control_icon_close_white.png" />
       <structure type="chrome_scaled_image" name="IDR_AURA_WINDOW_CONTROL_ICON_LEFT_SNAPPED" file="common/window_control_icon_left_snapped.png" />
+      <structure type="chrome_scaled_image" name="IDR_AURA_WINDOW_CONTROL_ICON_LEFT_SNAPPED_WHITE" file="common/window_control_icon_left_snapped_white.png" />
       <structure type="chrome_scaled_image" name="IDR_AURA_WINDOW_CONTROL_ICON_MAXIMIZE" file="common/window_control_icon_maximize.png" />
-      <structure type="chrome_scaled_image" name="IDR_AURA_WINDOW_CONTROL_ICON_MAXIMIZE_I" file="common/window_control_icon_maximize_inactive.png" />
+      <structure type="chrome_scaled_image" name="IDR_AURA_WINDOW_CONTROL_ICON_MAXIMIZE_WHITE" file="common/window_control_icon_maximize_white.png" />
       <structure type="chrome_scaled_image" name="IDR_AURA_WINDOW_CONTROL_ICON_MINIMIZE" file="common/window_control_icon_minimize.png" />
-      <structure type="chrome_scaled_image" name="IDR_AURA_WINDOW_CONTROL_ICON_MINIMIZE_I" file="common/window_control_icon_minimize_inactive.png" />
+      <structure type="chrome_scaled_image" name="IDR_AURA_WINDOW_CONTROL_ICON_MINIMIZE_WHITE" file="common/window_control_icon_minimize_white.png" />
       <structure type="chrome_scaled_image" name="IDR_AURA_WINDOW_CONTROL_ICON_RESTORE" file="common/window_control_icon_restore.png" />
-      <structure type="chrome_scaled_image" name="IDR_AURA_WINDOW_CONTROL_ICON_RESTORE_I" file="common/window_control_icon_restore_inactive.png" />
+      <structure type="chrome_scaled_image" name="IDR_AURA_WINDOW_CONTROL_ICON_RESTORE_WHITE" file="common/window_control_icon_restore_white.png" />
       <structure type="chrome_scaled_image" name="IDR_AURA_WINDOW_CONTROL_ICON_RIGHT_SNAPPED" file="common/window_control_icon_right_snapped.png" />
+      <structure type="chrome_scaled_image" name="IDR_AURA_WINDOW_CONTROL_ICON_RIGHT_SNAPPED_WHITE" file="common/window_control_icon_right_snapped_white.png" />
 
       <structure type="chrome_scaled_image" name="IDR_AURA_WINDOW_HEADER_SHADE_INACTIVE_BOTTOM" file="common/window_header_shade_bottom_inactive.png" />
       <structure type="chrome_scaled_image" name="IDR_AURA_WINDOW_HEADER_SHADE_INACTIVE_LEFT" file="common/window_header_shade_left_inactive.png" />
diff --git a/ash/resources/default_100_percent/common/window_control_icon_back.png b/ash/resources/default_100_percent/common/window_control_icon_back.png
index 41a69beb..2e0723d 100644
--- a/ash/resources/default_100_percent/common/window_control_icon_back.png
+++ b/ash/resources/default_100_percent/common/window_control_icon_back.png
Binary files differ
diff --git a/ash/resources/default_100_percent/common/window_control_icon_back_inactive.png b/ash/resources/default_100_percent/common/window_control_icon_back_inactive.png
deleted file mode 100644
index 95a636aa..0000000
--- a/ash/resources/default_100_percent/common/window_control_icon_back_inactive.png
+++ /dev/null
Binary files differ
diff --git a/ash/resources/default_100_percent/common/window_control_icon_back_white.png b/ash/resources/default_100_percent/common/window_control_icon_back_white.png
new file mode 100644
index 0000000..d9b95394
--- /dev/null
+++ b/ash/resources/default_100_percent/common/window_control_icon_back_white.png
Binary files differ
diff --git a/ash/resources/default_100_percent/common/window_control_icon_close.png b/ash/resources/default_100_percent/common/window_control_icon_close.png
index 9d28237..db26524 100644
--- a/ash/resources/default_100_percent/common/window_control_icon_close.png
+++ b/ash/resources/default_100_percent/common/window_control_icon_close.png
Binary files differ
diff --git a/ash/resources/default_100_percent/common/window_control_icon_close_inactive.png b/ash/resources/default_100_percent/common/window_control_icon_close_inactive.png
deleted file mode 100644
index dba91d5..0000000
--- a/ash/resources/default_100_percent/common/window_control_icon_close_inactive.png
+++ /dev/null
Binary files differ
diff --git a/ash/resources/default_100_percent/common/window_control_icon_close_white.png b/ash/resources/default_100_percent/common/window_control_icon_close_white.png
new file mode 100644
index 0000000..1bc6a84
--- /dev/null
+++ b/ash/resources/default_100_percent/common/window_control_icon_close_white.png
Binary files differ
diff --git a/ash/resources/default_100_percent/common/window_control_icon_left_snapped.png b/ash/resources/default_100_percent/common/window_control_icon_left_snapped.png
index 67561f1..4374068dfb 100644
--- a/ash/resources/default_100_percent/common/window_control_icon_left_snapped.png
+++ b/ash/resources/default_100_percent/common/window_control_icon_left_snapped.png
Binary files differ
diff --git a/ash/resources/default_100_percent/common/window_control_icon_left_snapped_white.png b/ash/resources/default_100_percent/common/window_control_icon_left_snapped_white.png
new file mode 100644
index 0000000..58be13a
--- /dev/null
+++ b/ash/resources/default_100_percent/common/window_control_icon_left_snapped_white.png
Binary files differ
diff --git a/ash/resources/default_100_percent/common/window_control_icon_maximize.png b/ash/resources/default_100_percent/common/window_control_icon_maximize.png
index 1fca585..ce0cf13 100644
--- a/ash/resources/default_100_percent/common/window_control_icon_maximize.png
+++ b/ash/resources/default_100_percent/common/window_control_icon_maximize.png
Binary files differ
diff --git a/ash/resources/default_100_percent/common/window_control_icon_maximize_inactive.png b/ash/resources/default_100_percent/common/window_control_icon_maximize_inactive.png
deleted file mode 100644
index c1fb862..0000000
--- a/ash/resources/default_100_percent/common/window_control_icon_maximize_inactive.png
+++ /dev/null
Binary files differ
diff --git a/ash/resources/default_100_percent/common/window_control_icon_maximize_white.png b/ash/resources/default_100_percent/common/window_control_icon_maximize_white.png
new file mode 100644
index 0000000..5dc6ccb
--- /dev/null
+++ b/ash/resources/default_100_percent/common/window_control_icon_maximize_white.png
Binary files differ
diff --git a/ash/resources/default_100_percent/common/window_control_icon_minimize.png b/ash/resources/default_100_percent/common/window_control_icon_minimize.png
index f8c609e..48c262c 100644
--- a/ash/resources/default_100_percent/common/window_control_icon_minimize.png
+++ b/ash/resources/default_100_percent/common/window_control_icon_minimize.png
Binary files differ
diff --git a/ash/resources/default_100_percent/common/window_control_icon_minimize_inactive.png b/ash/resources/default_100_percent/common/window_control_icon_minimize_inactive.png
deleted file mode 100644
index 6f0837d..0000000
--- a/ash/resources/default_100_percent/common/window_control_icon_minimize_inactive.png
+++ /dev/null
Binary files differ
diff --git a/ash/resources/default_100_percent/common/window_control_icon_minimize_white.png b/ash/resources/default_100_percent/common/window_control_icon_minimize_white.png
new file mode 100644
index 0000000..b085916
--- /dev/null
+++ b/ash/resources/default_100_percent/common/window_control_icon_minimize_white.png
Binary files differ
diff --git a/ash/resources/default_100_percent/common/window_control_icon_restore.png b/ash/resources/default_100_percent/common/window_control_icon_restore.png
index 84fa1d17..565dae9c 100644
--- a/ash/resources/default_100_percent/common/window_control_icon_restore.png
+++ b/ash/resources/default_100_percent/common/window_control_icon_restore.png
Binary files differ
diff --git a/ash/resources/default_100_percent/common/window_control_icon_restore_inactive.png b/ash/resources/default_100_percent/common/window_control_icon_restore_inactive.png
deleted file mode 100644
index 34b38908..0000000
--- a/ash/resources/default_100_percent/common/window_control_icon_restore_inactive.png
+++ /dev/null
Binary files differ
diff --git a/ash/resources/default_100_percent/common/window_control_icon_restore_white.png b/ash/resources/default_100_percent/common/window_control_icon_restore_white.png
new file mode 100644
index 0000000..5b51c15
--- /dev/null
+++ b/ash/resources/default_100_percent/common/window_control_icon_restore_white.png
Binary files differ
diff --git a/ash/resources/default_100_percent/common/window_control_icon_right_snapped.png b/ash/resources/default_100_percent/common/window_control_icon_right_snapped.png
index 3a4f15e..53551d4f 100644
--- a/ash/resources/default_100_percent/common/window_control_icon_right_snapped.png
+++ b/ash/resources/default_100_percent/common/window_control_icon_right_snapped.png
Binary files differ
diff --git a/ash/resources/default_100_percent/common/window_control_icon_right_snapped_white.png b/ash/resources/default_100_percent/common/window_control_icon_right_snapped_white.png
new file mode 100644
index 0000000..b02d33c
--- /dev/null
+++ b/ash/resources/default_100_percent/common/window_control_icon_right_snapped_white.png
Binary files differ
diff --git a/ash/resources/default_200_percent/common/window_control_icon_back.png b/ash/resources/default_200_percent/common/window_control_icon_back.png
index a3e6877..34e907b 100644
--- a/ash/resources/default_200_percent/common/window_control_icon_back.png
+++ b/ash/resources/default_200_percent/common/window_control_icon_back.png
Binary files differ
diff --git a/ash/resources/default_200_percent/common/window_control_icon_back_inactive.png b/ash/resources/default_200_percent/common/window_control_icon_back_inactive.png
deleted file mode 100644
index 1d511d0..0000000
--- a/ash/resources/default_200_percent/common/window_control_icon_back_inactive.png
+++ /dev/null
Binary files differ
diff --git a/ash/resources/default_200_percent/common/window_control_icon_back_white.png b/ash/resources/default_200_percent/common/window_control_icon_back_white.png
new file mode 100644
index 0000000..11a103a6
--- /dev/null
+++ b/ash/resources/default_200_percent/common/window_control_icon_back_white.png
Binary files differ
diff --git a/ash/resources/default_200_percent/common/window_control_icon_close.png b/ash/resources/default_200_percent/common/window_control_icon_close.png
index 422b49d..5c14ecd 100644
--- a/ash/resources/default_200_percent/common/window_control_icon_close.png
+++ b/ash/resources/default_200_percent/common/window_control_icon_close.png
Binary files differ
diff --git a/ash/resources/default_200_percent/common/window_control_icon_close_inactive.png b/ash/resources/default_200_percent/common/window_control_icon_close_inactive.png
deleted file mode 100644
index 41406fab2..0000000
--- a/ash/resources/default_200_percent/common/window_control_icon_close_inactive.png
+++ /dev/null
Binary files differ
diff --git a/ash/resources/default_200_percent/common/window_control_icon_close_white.png b/ash/resources/default_200_percent/common/window_control_icon_close_white.png
new file mode 100644
index 0000000..27cef0f
--- /dev/null
+++ b/ash/resources/default_200_percent/common/window_control_icon_close_white.png
Binary files differ
diff --git a/ash/resources/default_200_percent/common/window_control_icon_left_snapped.png b/ash/resources/default_200_percent/common/window_control_icon_left_snapped.png
index 4d73305..f8db919 100644
--- a/ash/resources/default_200_percent/common/window_control_icon_left_snapped.png
+++ b/ash/resources/default_200_percent/common/window_control_icon_left_snapped.png
Binary files differ
diff --git a/ash/resources/default_200_percent/common/window_control_icon_left_snapped_white.png b/ash/resources/default_200_percent/common/window_control_icon_left_snapped_white.png
new file mode 100644
index 0000000..6e938b8
--- /dev/null
+++ b/ash/resources/default_200_percent/common/window_control_icon_left_snapped_white.png
Binary files differ
diff --git a/ash/resources/default_200_percent/common/window_control_icon_maximize.png b/ash/resources/default_200_percent/common/window_control_icon_maximize.png
index c89a8193..a5947a59 100644
--- a/ash/resources/default_200_percent/common/window_control_icon_maximize.png
+++ b/ash/resources/default_200_percent/common/window_control_icon_maximize.png
Binary files differ
diff --git a/ash/resources/default_200_percent/common/window_control_icon_maximize_inactive.png b/ash/resources/default_200_percent/common/window_control_icon_maximize_inactive.png
deleted file mode 100644
index 6d2b978e..0000000
--- a/ash/resources/default_200_percent/common/window_control_icon_maximize_inactive.png
+++ /dev/null
Binary files differ
diff --git a/ash/resources/default_200_percent/common/window_control_icon_maximize_white.png b/ash/resources/default_200_percent/common/window_control_icon_maximize_white.png
new file mode 100644
index 0000000..5e17aaef
--- /dev/null
+++ b/ash/resources/default_200_percent/common/window_control_icon_maximize_white.png
Binary files differ
diff --git a/ash/resources/default_200_percent/common/window_control_icon_minimize.png b/ash/resources/default_200_percent/common/window_control_icon_minimize.png
index bb6d102..837b433f 100644
--- a/ash/resources/default_200_percent/common/window_control_icon_minimize.png
+++ b/ash/resources/default_200_percent/common/window_control_icon_minimize.png
Binary files differ
diff --git a/ash/resources/default_200_percent/common/window_control_icon_minimize_inactive.png b/ash/resources/default_200_percent/common/window_control_icon_minimize_inactive.png
deleted file mode 100644
index 23ca6ff..0000000
--- a/ash/resources/default_200_percent/common/window_control_icon_minimize_inactive.png
+++ /dev/null
Binary files differ
diff --git a/ash/resources/default_200_percent/common/window_control_icon_minimize_white.png b/ash/resources/default_200_percent/common/window_control_icon_minimize_white.png
new file mode 100644
index 0000000..cb295d0
--- /dev/null
+++ b/ash/resources/default_200_percent/common/window_control_icon_minimize_white.png
Binary files differ
diff --git a/ash/resources/default_200_percent/common/window_control_icon_restore.png b/ash/resources/default_200_percent/common/window_control_icon_restore.png
index 1301072..5a327bb 100644
--- a/ash/resources/default_200_percent/common/window_control_icon_restore.png
+++ b/ash/resources/default_200_percent/common/window_control_icon_restore.png
Binary files differ
diff --git a/ash/resources/default_200_percent/common/window_control_icon_restore_inactive.png b/ash/resources/default_200_percent/common/window_control_icon_restore_inactive.png
deleted file mode 100644
index 1bba322..0000000
--- a/ash/resources/default_200_percent/common/window_control_icon_restore_inactive.png
+++ /dev/null
Binary files differ
diff --git a/ash/resources/default_200_percent/common/window_control_icon_restore_white.png b/ash/resources/default_200_percent/common/window_control_icon_restore_white.png
new file mode 100644
index 0000000..a2e02ed
--- /dev/null
+++ b/ash/resources/default_200_percent/common/window_control_icon_restore_white.png
Binary files differ
diff --git a/ash/resources/default_200_percent/common/window_control_icon_right_snapped.png b/ash/resources/default_200_percent/common/window_control_icon_right_snapped.png
index d44af80..8eed437a 100644
--- a/ash/resources/default_200_percent/common/window_control_icon_right_snapped.png
+++ b/ash/resources/default_200_percent/common/window_control_icon_right_snapped.png
Binary files differ
diff --git a/ash/resources/default_200_percent/common/window_control_icon_right_snapped_white.png b/ash/resources/default_200_percent/common/window_control_icon_right_snapped_white.png
new file mode 100644
index 0000000..d8d5cfd4
--- /dev/null
+++ b/ash/resources/default_200_percent/common/window_control_icon_right_snapped_white.png
Binary files differ
diff --git a/ash/shelf/shelf_layout_manager.cc b/ash/shelf/shelf_layout_manager.cc
index 0aa077a..1971f62e 100644
--- a/ash/shelf/shelf_layout_manager.cc
+++ b/ash/shelf/shelf_layout_manager.cc
@@ -209,7 +209,6 @@
   FOR_EACH_OBSERVER(ShelfLayoutManagerObserver, observers_, WillDeleteShelf());
   Shell::GetInstance()->RemoveShellObserver(this);
   Shell::GetInstance()->lock_state_controller()->RemoveObserver(this);
-  aura::client::GetActivationClient(root_window_)->RemoveObserver(this);
   Shell::GetInstance()->
       session_state_delegate()->RemoveSessionStateObserver(this);
 }
@@ -230,6 +229,9 @@
   set_workspace_controller(NULL);
   auto_hide_event_filter_.reset();
   bezel_event_filter_.reset();
+  // Stop observing window change, otherwise we can attempt to update a
+  // partially destructed shelf.
+  aura::client::GetActivationClient(root_window_)->RemoveObserver(this);
 }
 
 bool ShelfLayoutManager::IsVisible() const {
diff --git a/ash/shelf/shelf_layout_manager_unittest.cc b/ash/shelf/shelf_layout_manager_unittest.cc
index 1ad9007..ae21072 100644
--- a/ash/shelf/shelf_layout_manager_unittest.cc
+++ b/ash/shelf/shelf_layout_manager_unittest.cc
@@ -2189,4 +2189,29 @@
   EXPECT_EQ(initial_bounds, reshow_target_bounds);
 }
 
+// Tests that during shutdown, that window activation changes are properly
+// handled, and do not crash (crbug.com/458768)
+TEST_F(ShelfLayoutManagerTest, ShutdownHandlesWindowActivation) {
+  ShelfLayoutManager* shelf_manager = GetShelfLayoutManager();
+  ShelfWidget* shelf = GetShelfWidget();
+  shelf_manager->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS);
+
+  aura::Window* window1 = CreateTestWindowInShellWithId(0);
+  window1->SetBounds(gfx::Rect(0, 0, 100, 100));
+  window1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
+  window1->Show();
+  scoped_ptr<aura::Window> window2(CreateTestWindowInShellWithId(0));
+  window2->SetBounds(gfx::Rect(0, 0, 100, 100));
+  window2->Show();
+  wm::ActivateWindow(window1);
+
+  shelf->ShutdownStatusAreaWidget();
+  shelf_manager->PrepareForShutdown();
+
+  // Deleting a focused maximized window will switch focus to |window2|. This
+  // would normally cause the ShelfLayoutManager to update its state. However
+  // during shutdown we want to handle this without crashing.
+  delete window1;
+}
+
 }  // namespace ash
diff --git a/ash/shelf/shelf_tooltip_manager_unittest.cc b/ash/shelf/shelf_tooltip_manager_unittest.cc
index d13d2907..6ebb063 100644
--- a/ash/shelf/shelf_tooltip_manager_unittest.cc
+++ b/ash/shelf/shelf_tooltip_manager_unittest.cc
@@ -18,6 +18,7 @@
 #include "ui/events/event.h"
 #include "ui/events/event_constants.h"
 #include "ui/events/event_handler.h"
+#include "ui/events/event_utils.h"
 #include "ui/events/keycodes/keyboard_codes.h"
 #include "ui/events/test/events_test_utils.h"
 #include "ui/views/widget/widget.h"
@@ -231,8 +232,8 @@
 
   // Shouldn't hide if the mouse is in the tooltip.
   ui::MouseEvent mouse_event(ui::ET_MOUSE_MOVED, tooltip_rect.CenterPoint(),
-                             tooltip_rect.CenterPoint(), ui::EF_NONE,
-                             ui::EF_NONE);
+                             tooltip_rect.CenterPoint(), ui::EventTimeForNow(),
+                             ui::EF_NONE, ui::EF_NONE);
   ui::LocatedEventTestApi test_api(&mouse_event);
 
   SetEventTarget(root_window, &mouse_event);
@@ -261,8 +262,8 @@
 
   // Should hide if the mouse is pressed in the tooltip.
   ui::MouseEvent mouse_event(ui::ET_MOUSE_PRESSED, tooltip_rect.CenterPoint(),
-                             tooltip_rect.CenterPoint(), ui::EF_NONE,
-                             ui::EF_NONE);
+                             tooltip_rect.CenterPoint(), ui::EventTimeForNow(),
+                             ui::EF_NONE, ui::EF_NONE);
 
   SetEventTarget(root_window, &mouse_event);
   event_handler->OnMouseEvent(&mouse_event);
diff --git a/ash/shelf/shelf_view.cc b/ash/shelf/shelf_view.cc
index c9cee3df..40eafe58 100644
--- a/ash/shelf/shelf_view.cc
+++ b/ash/shelf/shelf_view.cc
@@ -43,6 +43,7 @@
 #include "ui/compositor/layer.h"
 #include "ui/compositor/layer_animator.h"
 #include "ui/compositor/scoped_animation_duration_scale_mode.h"
+#include "ui/events/event_utils.h"
 #include "ui/gfx/canvas.h"
 #include "ui/gfx/geometry/point.h"
 #include "ui/views/animation/bounds_animator.h"
@@ -621,7 +622,8 @@
   gfx::Point point_in_root = location_in_screen_coordinates;
   ::wm::ConvertPointFromScreen(
       ash::wm::GetRootWindowAt(location_in_screen_coordinates), &point_in_root);
-  ui::MouseEvent event(ui::ET_MOUSE_PRESSED, pt, point_in_root, 0, 0);
+  ui::MouseEvent event(ui::ET_MOUSE_PRESSED, pt, point_in_root,
+                       ui::EventTimeForNow(), 0, 0);
   PointerPressedOnButton(drag_and_drop_view,
                          ShelfButtonHost::DRAG_AND_DROP,
                          event);
@@ -643,7 +645,8 @@
   gfx::Point point_in_root = location_in_screen_coordinates;
   ::wm::ConvertPointFromScreen(
       ash::wm::GetRootWindowAt(location_in_screen_coordinates), &point_in_root);
-  ui::MouseEvent event(ui::ET_MOUSE_DRAGGED, pt, point_in_root, 0, 0);
+  ui::MouseEvent event(ui::ET_MOUSE_DRAGGED, pt, point_in_root,
+                       ui::EventTimeForNow(), 0, 0);
   PointerDraggedOnButton(drag_and_drop_view,
                          ShelfButtonHost::DRAG_AND_DROP,
                          event);
diff --git a/ash/shelf/shelf_view_unittest.cc b/ash/shelf/shelf_view_unittest.cc
index c09acb4..d18d127 100644
--- a/ash/shelf/shelf_view_unittest.cc
+++ b/ash/shelf/shelf_view_unittest.cc
@@ -40,6 +40,7 @@
 #include "ui/compositor/layer.h"
 #include "ui/events/event.h"
 #include "ui/events/event_constants.h"
+#include "ui/events/event_utils.h"
 #include "ui/events/test/event_generator.h"
 #include "ui/views/view_model.h"
 #include "ui/views/widget/widget.h"
@@ -422,9 +423,9 @@
                                      int button_index) {
     ShelfButtonHost* button_host = shelf_view_;
     ShelfButton* button = test_api_->GetButton(button_index);
-    ui::MouseEvent click_event(ui::ET_MOUSE_PRESSED,
-                               gfx::Point(),
-                               button->GetBoundsInScreen().origin(), 0, 0);
+    ui::MouseEvent click_event(ui::ET_MOUSE_PRESSED, gfx::Point(),
+                               button->GetBoundsInScreen().origin(),
+                               ui::EventTimeForNow(), 0, 0);
     button_host->PointerPressedOnButton(button, pointer, click_event);
     return button;
   }
@@ -434,11 +435,9 @@
     ShelfButtonHost* button_host = shelf_view_;
     ShelfButton* button =
         SimulateButtonPressed(ShelfButtonHost::MOUSE, button_index);
-    ui::MouseEvent release_event(ui::ET_MOUSE_RELEASED,
-                                 gfx::Point(),
+    ui::MouseEvent release_event(ui::ET_MOUSE_RELEASED, gfx::Point(),
                                  button->GetBoundsInScreen().origin(),
-                                 0,
-                                 0);
+                                 ui::EventTimeForNow(), 0, 0);
     test_api_->ButtonPressed(button, release_event);
     button_host->PointerReleasedOnButton(button, ShelfButtonHost::MOUSE, false);
   }
@@ -448,10 +447,9 @@
     ShelfButtonHost* button_host = shelf_view_;
     ShelfButton* button =
         SimulateButtonPressed(ShelfButtonHost::MOUSE, button_index);
-    ui::MouseEvent release_event(ui::ET_MOUSE_RELEASED,
-                                 gfx::Point(),
+    ui::MouseEvent release_event(ui::ET_MOUSE_RELEASED, gfx::Point(),
                                  button->GetBoundsInScreen().origin(),
-                                 ui::EF_IS_DOUBLE_CLICK,
+                                 ui::EventTimeForNow(), ui::EF_IS_DOUBLE_CLICK,
                                  0);
     test_api_->ButtonPressed(button, release_event);
     button_host->PointerReleasedOnButton(button, ShelfButtonHost::MOUSE, false);
@@ -465,10 +463,10 @@
 
     // Drag.
     views::View* destination = test_api_->GetButton(destination_index);
-    ui::MouseEvent drag_event(ui::ET_MOUSE_DRAGGED,
-                              gfx::Point(destination->x() - button->x(),
+    ui::MouseEvent drag_event(
+        ui::ET_MOUSE_DRAGGED, gfx::Point(destination->x() - button->x(),
                                          destination->y() - button->y()),
-                              destination->GetBoundsInScreen().origin(), 0, 0);
+        destination->GetBoundsInScreen().origin(), ui::EventTimeForNow(), 0, 0);
     button_host->PointerDraggedOnButton(button, pointer, drag_event);
     return button;
   }
@@ -1111,28 +1109,27 @@
   gfx::Point press_location_in_screen =
       button->GetBoundsInScreen().origin() + press_offset;
 
-  ui::MouseEvent click_event(ui::ET_MOUSE_PRESSED,
-                             press_location,
-                             press_location_in_screen,
+  ui::MouseEvent click_event(ui::ET_MOUSE_PRESSED, press_location,
+                             press_location_in_screen, ui::EventTimeForNow(),
                              ui::EF_LEFT_MOUSE_BUTTON, 0);
   button->OnMousePressed(click_event);
 
-  ui::MouseEvent drag_event1(ui::ET_MOUSE_DRAGGED,
-                             press_location + gfx::Vector2d(0, 1),
-                             press_location_in_screen + gfx::Vector2d(0, 1),
-                             ui::EF_LEFT_MOUSE_BUTTON, 0);
+  ui::MouseEvent drag_event1(
+      ui::ET_MOUSE_DRAGGED, press_location + gfx::Vector2d(0, 1),
+      press_location_in_screen + gfx::Vector2d(0, 1), ui::EventTimeForNow(),
+      ui::EF_LEFT_MOUSE_BUTTON, 0);
   button->OnMouseDragged(drag_event1);
 
-  ui::MouseEvent drag_event2(ui::ET_MOUSE_DRAGGED,
-                             press_location + gfx::Vector2d(-1, 0),
-                             press_location_in_screen + gfx::Vector2d(-1, 0),
-                             ui::EF_LEFT_MOUSE_BUTTON, 0);
+  ui::MouseEvent drag_event2(
+      ui::ET_MOUSE_DRAGGED, press_location + gfx::Vector2d(-1, 0),
+      press_location_in_screen + gfx::Vector2d(-1, 0), ui::EventTimeForNow(),
+      ui::EF_LEFT_MOUSE_BUTTON, 0);
   button->OnMouseDragged(drag_event2);
 
-  ui::MouseEvent release_event(ui::ET_MOUSE_RELEASED,
-                               press_location + gfx::Vector2d(-1, 0),
-                               press_location_in_screen + gfx::Vector2d(-1, 0),
-                               ui::EF_LEFT_MOUSE_BUTTON, 0);
+  ui::MouseEvent release_event(
+      ui::ET_MOUSE_RELEASED, press_location + gfx::Vector2d(-1, 0),
+      press_location_in_screen + gfx::Vector2d(-1, 0), ui::EventTimeForNow(),
+      ui::EF_LEFT_MOUSE_BUTTON, 0);
   button->OnMouseReleased(release_event);
 
   EXPECT_TRUE(selection_tracker->WasSelected());
diff --git a/ash/shelf/shelf_widget_unittest.cc b/ash/shelf/shelf_widget_unittest.cc
index 770bd7e..bbd6b4c1 100644
--- a/ash/shelf/shelf_widget_unittest.cc
+++ b/ash/shelf/shelf_widget_unittest.cc
@@ -209,7 +209,7 @@
     // shelf).
     gfx::Point event_location(20, shelf_bounds.y() + 1);
     ui::MouseEvent mouse(ui::ET_MOUSE_MOVED, event_location, event_location,
-                         ui::EF_NONE, ui::EF_NONE);
+                         ui::EventTimeForNow(), ui::EF_NONE, ui::EF_NONE);
     ui::EventTarget* target = targeter->FindTargetForEvent(root, &mouse);
     EXPECT_EQ(widget->GetNativeWindow(), target);
   }
@@ -232,7 +232,7 @@
     // window-target should find the shelf as the target.
     gfx::Point event_location(20, shelf_bounds.y() + 1);
     ui::MouseEvent mouse(ui::ET_MOUSE_MOVED, event_location, event_location,
-                         ui::EF_NONE, ui::EF_NONE);
+                         ui::EventTimeForNow(), ui::EF_NONE, ui::EF_NONE);
     ui::EventTarget* target = targeter->FindTargetForEvent(root, &mouse);
     EXPECT_EQ(shelf_widget->GetNativeWindow(), target);
   }
diff --git a/ash/shell.cc b/ash/shell.cc
index b51a75ae..eebf156 100644
--- a/ash/shell.cc
+++ b/ash/shell.cc
@@ -990,10 +990,8 @@
   window_selector_controller_.reset(new WindowSelectorController());
   window_cycle_controller_.reset(new WindowCycleController());
 
-  tooltip_controller_.reset(
-      new views::corewm::TooltipController(
-          scoped_ptr<views::corewm::Tooltip>(
-              new views::corewm::TooltipAura(gfx::SCREEN_TYPE_ALTERNATE))));
+  tooltip_controller_.reset(new views::corewm::TooltipController(
+      scoped_ptr<views::corewm::Tooltip>(new views::corewm::TooltipAura)));
   AddPreTargetHandler(tooltip_controller_.get());
 
   event_client_.reset(new EventClientImpl);
diff --git a/ash/shell.h b/ash/shell.h
index 155d339..b37f284 100644
--- a/ash/shell.h
+++ b/ash/shell.h
@@ -380,6 +380,9 @@
     return display_controller_.get();
   }
 #if defined(OS_CHROMEOS)
+  PowerEventObserver* power_event_observer() {
+    return power_event_observer_.get();
+  }
   TouchTransformerController* touch_transformer_controller() {
     return touch_transformer_controller_.get();
   }
diff --git a/ash/sticky_keys/sticky_keys_unittest.cc b/ash/sticky_keys/sticky_keys_unittest.cc
index 16bdd20d..e00c9dc 100644
--- a/ash/sticky_keys/sticky_keys_unittest.cc
+++ b/ash/sticky_keys/sticky_keys_unittest.cc
@@ -17,6 +17,7 @@
 #include "ui/aura/window.h"
 #include "ui/aura/window_tree_host.h"
 #include "ui/events/event_source.h"
+#include "ui/events/event_utils.h"
 #include "ui/events/test/events_test_utils_x11.h"
 
 namespace ash {
@@ -126,11 +127,9 @@
   // Creates a synthesized MouseEvent that is not backed by a native event.
   ui::MouseEvent* GenerateSynthesizedMouseEventAt(ui::EventType event_type,
                                                   const gfx::Point& location) {
-    ui::MouseEvent* event = new ui::MouseEvent(event_type,
-                                               location,
-                                               location,
-                                               ui::EF_LEFT_MOUSE_BUTTON,
-                                               ui::EF_LEFT_MOUSE_BUTTON);
+    ui::MouseEvent* event = new ui::MouseEvent(
+        event_type, location, location, ui::EventTimeForNow(),
+        ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
     ui::Event::DispatcherApi dispatcher(event);
     dispatcher.set_target(target_);
     return event;
diff --git a/ash/system/chromeos/network/network_state_list_detailed_view.cc b/ash/system/chromeos/network/network_state_list_detailed_view.cc
index 9d299e24..39d466c 100644
--- a/ash/system/chromeos/network/network_state_list_detailed_view.cc
+++ b/ash/system/chromeos/network/network_state_list_detailed_view.cc
@@ -29,9 +29,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/time/time.h"
 #include "chromeos/chromeos_switches.h"
-#include "chromeos/device_event_log.h"
 #include "chromeos/network/device_state.h"
-#include "chromeos/network/network_configuration_handler.h"
 #include "chromeos/network/network_state.h"
 #include "chromeos/network/network_state_handler.h"
 #include "grit/ash_resources.h"
diff --git a/ash/system/chromeos/network/tray_network_state_observer.cc b/ash/system/chromeos/network/tray_network_state_observer.cc
index c23279e..8d7f181e 100644
--- a/ash/system/chromeos/network/tray_network_state_observer.cc
+++ b/ash/system/chromeos/network/tray_network_state_observer.cc
@@ -9,7 +9,6 @@
 
 #include "ash/system/tray/system_tray.h"
 #include "base/location.h"
-#include "chromeos/device_event_log.h"
 #include "chromeos/network/network_state.h"
 #include "chromeos/network/network_state_handler.h"
 #include "third_party/cros_system_api/dbus/service_constants.h"
diff --git a/ash/system/chromeos/network/tray_vpn.cc b/ash/system/chromeos/network/tray_vpn.cc
index d6c71bf..1ac509c 100644
--- a/ash/system/chromeos/network/tray_vpn.cc
+++ b/ash/system/chromeos/network/tray_vpn.cc
@@ -13,7 +13,6 @@
 #include "ash/system/tray/tray_constants.h"
 #include "ash/system/tray/tray_item_more.h"
 #include "ash/system/tray/tray_popup_label_button.h"
-#include "chromeos/device_event_log.h"
 #include "chromeos/network/network_state.h"
 #include "chromeos/network/network_state_handler.h"
 #include "grit/ash_strings.h"
diff --git a/ash/system/chromeos/power/power_event_observer.cc b/ash/system/chromeos/power/power_event_observer.cc
index 65567f4..1510ddd 100644
--- a/ash/system/chromeos/power/power_event_observer.cc
+++ b/ash/system/chromeos/power/power_event_observer.cc
@@ -10,13 +10,37 @@
 #include "ash/wm/power_button_controller.h"
 #include "base/prefs/pref_service.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
+#include "ui/aura/window.h"
+#include "ui/aura/window_tree_host.h"
 #include "ui/base/user_activity/user_activity_detector.h"
+#include "ui/compositor/compositor.h"
 #include "ui/display/chromeos/display_configurator.h"
 
 namespace ash {
 
+namespace {
+
+// Tells the compositor for each of the displays to finish all pending
+// rendering requests and block any new ones.
+void StopRenderingRequests() {
+  for (aura::Window* window : Shell::GetAllRootWindows()) {
+    ui::Compositor* compositor = window->GetHost()->compositor();
+    compositor->SetVisible(false);
+    compositor->FinishAllRendering();
+  }
+}
+
+// Tells the compositor for each of the displays to resume sending rendering
+// requests to the GPU.
+void ResumeRenderingRequests() {
+  for (aura::Window* window : Shell::GetAllRootWindows())
+    window->GetHost()->compositor()->SetVisible(true);
+}
+
+}  // namespace
+
 PowerEventObserver::PowerEventObserver()
-    : screen_locked_(false) {
+    : screen_locked_(false), waiting_for_lock_screen_animations_(false) {
   chromeos::DBusThreadManager::Get()->GetPowerManagerClient()->
       AddObserver(this);
   chromeos::DBusThreadManager::Get()->GetSessionManagerClient()->
@@ -30,6 +54,18 @@
       RemoveObserver(this);
 }
 
+void PowerEventObserver::OnLockAnimationsComplete() {
+  VLOG(1) << "Screen locker animations have completed.";
+  waiting_for_lock_screen_animations_ = false;
+
+  if (!screen_lock_callback_.is_null()) {
+    StopRenderingRequests();
+
+    screen_lock_callback_.Run();
+    screen_lock_callback_.Reset();
+  }
+}
+
 void PowerEventObserver::BrightnessChanged(int level, bool user_initiated) {
   Shell::GetInstance()->power_button_controller()->OnScreenBrightnessChanged(
       static_cast<double>(level));
@@ -39,8 +75,19 @@
   Shell* shell = Shell::GetInstance();
   SessionStateDelegate* delegate = shell->session_state_delegate();
 
-  // If the lock-before-suspending pref is set, get a callback to block
-  // suspend and ask the session manager to lock the screen.
+  // This class is responsible for disabling all rendering requests at suspend
+  // time and then enabling them at resume time.  When the
+  // lock-before-suspending pref is not set this is easy to do since
+  // StopRenderingRequests() is just called directly from this function.  If the
+  // lock-before-suspending pref _is_ set, then the suspend needs to be delayed
+  // until the lock screen is fully visible.  While it is sufficient from a
+  // security perspective to block only until the lock screen is ready, which
+  // guarantees that the contents of the user's screen are no longer visible,
+  // this leads to poor UX on the first resume since neither the user pod nor
+  // the header bar will be visible for a few hundred milliseconds until the GPU
+  // process starts rendering again.  To deal with this, the suspend is delayed
+  // until all the lock screen animations have completed and the suspend request
+  // is unblocked from OnLockAnimationsComplete().
   if (!screen_locked_ && delegate->ShouldLockScreenBeforeSuspending() &&
       delegate->CanLockScreen()) {
     screen_lock_callback_ = chromeos::DBusThreadManager::Get()->
@@ -48,6 +95,21 @@
     VLOG(1) << "Requesting screen lock from PowerEventObserver";
     chromeos::DBusThreadManager::Get()->GetSessionManagerClient()->
         RequestLockScreen();
+  } else if (waiting_for_lock_screen_animations_) {
+    // The lock-before-suspending pref has been set and the lock screen is ready
+    // but the animations have not completed yet.  This can happen if a suspend
+    // request is canceled after the lock screen is ready but before the
+    // animations have completed and then another suspend request is immediately
+    // started.  In practice, it is highly unlikely that this will ever happen
+    // but it's better to be safe since the cost of not dealing with it properly
+    // is a memory leak in the GPU and weird artifacts on the screen.
+    screen_lock_callback_ = chromeos::DBusThreadManager::Get()
+                                ->GetPowerManagerClient()
+                                ->GetSuspendReadinessCallback();
+  } else {
+    // The lock-before-suspending pref is not set or the screen has already been
+    // locked and the animations have completed.  Rendering can be stopped now.
+    StopRenderingRequests();
   }
 
   ui::UserActivityDetector::Get()->OnDisplayPowerChanging();
@@ -57,19 +119,24 @@
 void PowerEventObserver::SuspendDone(const base::TimeDelta& sleep_duration) {
   Shell::GetInstance()->display_configurator()->ResumeDisplays();
   Shell::GetInstance()->system_tray_notifier()->NotifyRefreshClock();
+
+  // If the suspend request was being blocked while waiting for the lock
+  // animation to complete, clear the blocker since the suspend has already
+  // completed.  This prevents rendering requests from being blocked after a
+  // resume if the lock screen took too long to show.
+  screen_lock_callback_.Reset();
+
+  ResumeRenderingRequests();
 }
 
 void PowerEventObserver::ScreenIsLocked() {
   screen_locked_ = true;
+  waiting_for_lock_screen_animations_ = true;
 
-  // Stop blocking suspend after the screen is locked.
+  // The screen is now locked but the pending suspend, if any, will be blocked
+  // until all the animations have completed.
   if (!screen_lock_callback_.is_null()) {
     VLOG(1) << "Screen locked due to suspend";
-    // Run the callback asynchronously.  ScreenIsLocked() is currently
-    // called asynchronously after RequestLockScreen(), but this guards
-    // against it being made synchronous later.
-    base::MessageLoop::current()->PostTask(FROM_HERE, screen_lock_callback_);
-    screen_lock_callback_.Reset();
   } else {
     VLOG(1) << "Screen locked without suspend";
   }
diff --git a/ash/system/chromeos/power/power_event_observer.h b/ash/system/chromeos/power/power_event_observer.h
index 941e234..37fe5bb 100644
--- a/ash/system/chromeos/power/power_event_observer.h
+++ b/ash/system/chromeos/power/power_event_observer.h
@@ -23,6 +23,12 @@
   PowerEventObserver();
   ~PowerEventObserver() override;
 
+  // Called by the WebUIScreenLocker when all the lock screen animations have
+  // completed.  This really should be implemented via an observer but since
+  // ash/ isn't allowed to depend on chrome/ we need to have the
+  // WebUIScreenLocker reach into ash::Shell to make this call.
+  void OnLockAnimationsComplete();
+
   // chromeos::PowerManagerClient::Observer overrides:
   void BrightnessChanged(int level, bool user_initiated) override;
   void SuspendImminent() override;
@@ -35,8 +41,11 @@
   // Is the screen currently locked?
   bool screen_locked_;
 
-  // If set, called when the lock screen has been shown to confirm that the
-  // system is ready to be suspended.
+  // Have the lock screen animations completed?
+  bool waiting_for_lock_screen_animations_;
+
+  // If set, called when the lock screen animations have completed to confirm
+  // that the system is ready to be suspended.
   base::Closure screen_lock_callback_;
 
  private:
diff --git a/ash/system/chromeos/power/power_event_observer_unittest.cc b/ash/system/chromeos/power/power_event_observer_unittest.cc
index aaaf2c7..2fd1a1f 100644
--- a/ash/system/chromeos/power/power_event_observer_unittest.cc
+++ b/ash/system/chromeos/power/power_event_observer_unittest.cc
@@ -4,11 +4,15 @@
 
 #include "ash/system/chromeos/power/power_event_observer.h"
 
+#include "ash/shell.h"
 #include "ash/test/ash_test_base.h"
 #include "base/memory/scoped_ptr.h"
 #include "base/time/time.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/power_manager_client.h"
+#include "ui/aura/window.h"
+#include "ui/aura/window_tree_host.h"
+#include "ui/compositor/compositor.h"
 
 namespace ash {
 
@@ -29,6 +33,16 @@
   }
 
  protected:
+  int GetNumVisibleCompositors() {
+    int result = 0;
+    for (const auto& window : Shell::GetAllRootWindows()) {
+      if (window->GetHost()->compositor()->IsVisible())
+        ++result;
+    }
+
+    return result;
+  }
+
   scoped_ptr<PowerEventObserver> observer_;
 
  private:
@@ -47,15 +61,17 @@
   observer_->SuspendImminent();
   EXPECT_EQ(1, client->GetNumPendingSuspendReadinessCallbacks());
 
-  // It should run the callback when it hears that the screen is locked.
+  // It should run the callback when it hears that the screen is locked and the
+  // lock screen animations have completed.
   observer_->ScreenIsLocked();
-  RunAllPendingInMessageLoop();
+  observer_->OnLockAnimationsComplete();
   EXPECT_EQ(0, client->GetNumPendingSuspendReadinessCallbacks());
 
   // If the system is already locked, no callback should be requested.
   observer_->SuspendDone(base::TimeDelta());
   observer_->ScreenIsUnlocked();
   observer_->ScreenIsLocked();
+  observer_->OnLockAnimationsComplete();
   observer_->SuspendImminent();
   EXPECT_EQ(0, client->GetNumPendingSuspendReadinessCallbacks());
 
@@ -67,4 +83,79 @@
   EXPECT_EQ(0, client->GetNumPendingSuspendReadinessCallbacks());
 }
 
+TEST_F(PowerEventObserverTest, SetInvisibleBeforeSuspend) {
+  // Tests that all the Compositors are marked invisible before a suspend
+  // request when the screen is not supposed to be locked before a suspend.
+  EXPECT_EQ(1, GetNumVisibleCompositors());
+
+  observer_->SuspendImminent();
+  EXPECT_EQ(0, GetNumVisibleCompositors());
+  observer_->SuspendDone(base::TimeDelta());
+
+  // Tests that all the Compositors are marked invisible _after_ the screen lock
+  // animations have completed.
+  SetCanLockScreen(true);
+  SetShouldLockScreenBeforeSuspending(true);
+
+  observer_->SuspendImminent();
+  EXPECT_EQ(1, GetNumVisibleCompositors());
+
+  observer_->ScreenIsLocked();
+  EXPECT_EQ(1, GetNumVisibleCompositors());
+
+  observer_->OnLockAnimationsComplete();
+  EXPECT_EQ(0, GetNumVisibleCompositors());
+
+  observer_->SuspendDone(base::TimeDelta());
+  EXPECT_EQ(1, GetNumVisibleCompositors());
+}
+
+TEST_F(PowerEventObserverTest, CanceledSuspend) {
+  // Tests that the Compositors are not marked invisible if a suspend is
+  // canceled or the system resumes before the lock screen is ready.
+  SetCanLockScreen(true);
+  SetShouldLockScreenBeforeSuspending(true);
+  observer_->SuspendImminent();
+  EXPECT_EQ(1, GetNumVisibleCompositors());
+
+  observer_->SuspendDone(base::TimeDelta());
+  observer_->ScreenIsLocked();
+  observer_->OnLockAnimationsComplete();
+  EXPECT_EQ(1, GetNumVisibleCompositors());
+}
+
+TEST_F(PowerEventObserverTest, DelayResuspendForLockAnimations) {
+  // Tests that the following order of events is handled correctly:
+  //
+  // - A suspend request is started.
+  // - The screen is locked.
+  // - The suspend request is canceled.
+  // - Another suspend request is started.
+  // - The screen lock animations complete.
+  //
+  // In this case, the observer should block the second suspend request until
+  // the animations have completed.
+  SetCanLockScreen(true);
+  SetShouldLockScreenBeforeSuspending(true);
+
+  chromeos::PowerManagerClient* client =
+      chromeos::DBusThreadManager::Get()->GetPowerManagerClient();
+  observer_->SuspendImminent();
+  EXPECT_EQ(1, client->GetNumPendingSuspendReadinessCallbacks());
+
+  observer_->ScreenIsLocked();
+  observer_->SuspendDone(base::TimeDelta());
+  observer_->SuspendImminent();
+
+  // The expected number of suspend readiness callbacks is 2 because the
+  // observer has not run the callback that it got from the first suspend
+  // request.  The real PowerManagerClient would reset its internal counter in
+  // this situation but the stub client is not that smart.
+  EXPECT_EQ(2, client->GetNumPendingSuspendReadinessCallbacks());
+
+  observer_->OnLockAnimationsComplete();
+  EXPECT_EQ(1, client->GetNumPendingSuspendReadinessCallbacks());
+  EXPECT_EQ(0, GetNumVisibleCompositors());
+}
+
 }  // namespace ash
diff --git a/ash/system/chromeos/screen_security/screen_tray_item_unittest.cc b/ash/system/chromeos/screen_security/screen_tray_item_unittest.cc
index 9c68d0b8..63d6347 100644
--- a/ash/system/chromeos/screen_security/screen_tray_item_unittest.cc
+++ b/ash/system/chromeos/screen_security/screen_tray_item_unittest.cc
@@ -12,6 +12,7 @@
 #include "base/callback.h"
 #include "base/strings/utf_string_conversions.h"
 #include "ui/events/event.h"
+#include "ui/events/event_utils.h"
 #include "ui/gfx/geometry/point.h"
 #include "ui/message_center/message_center.h"
 #include "ui/views/view.h"
@@ -35,11 +36,9 @@
 void ClickViewCenter(views::View* view) {
   gfx::Point click_location_in_local =
       gfx::Point(view->width() / 2, view->height() / 2);
-  view->OnMousePressed(ui::MouseEvent(ui::ET_MOUSE_PRESSED,
-                                      click_location_in_local,
-                                      click_location_in_local,
-                                      ui::EF_NONE,
-                                      ui::EF_NONE));
+  view->OnMousePressed(ui::MouseEvent(
+      ui::ET_MOUSE_PRESSED, click_location_in_local, click_location_in_local,
+      ui::EventTimeForNow(), ui::EF_NONE, ui::EF_NONE));
 }
 
 class ScreenTrayItemTest : public ash::test::AshTestBase {
diff --git a/ash/system/ime/tray_ime_chromeos.cc b/ash/system/ime/tray_ime_chromeos.cc
index 6d93f2b..4896a59 100644
--- a/ash/system/ime/tray_ime_chromeos.cc
+++ b/ash/system/ime/tray_ime_chromeos.cc
@@ -114,11 +114,11 @@
 
     if (list.size() > 1)
       AppendIMEList(list);
-    if (property_list.size() > 1)
+    if (!property_list.empty())
       AppendIMEProperties(property_list);
 
     if (show_keyboard_toggle) {
-      if (list.size() > 1 || property_list.size() > 1)
+      if (list.size() > 1 || !property_list.empty())
         AddScrollSeparator();
       AppendKeyboardStatus();
     }
diff --git a/ash/system/tray/default_system_tray_delegate.cc b/ash/system/tray/default_system_tray_delegate.cc
index d2564d0..49386b4 100644
--- a/ash/system/tray/default_system_tray_delegate.cc
+++ b/ash/system/tray/default_system_tray_delegate.cc
@@ -15,37 +15,13 @@
 
 namespace ash {
 
-namespace {
-
-class DefaultVolumnControlDelegate : public VolumeControlDelegate {
- public:
-  DefaultVolumnControlDelegate() {}
-  ~DefaultVolumnControlDelegate() override {}
-
-  void HandleVolumeMute(const ui::Accelerator& accelerator) override {}
-  void HandleVolumeDown(const ui::Accelerator& accelerator) override {}
-  void HandleVolumeUp(const ui::Accelerator& accelerator) override {}
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(DefaultVolumnControlDelegate);
-};
-
-}  // namespace
-
 DefaultSystemTrayDelegate::DefaultSystemTrayDelegate()
-    : bluetooth_enabled_(true),
-      volume_control_delegate_(new DefaultVolumnControlDelegate) {
+    : bluetooth_enabled_(true) {
 }
 
 DefaultSystemTrayDelegate::~DefaultSystemTrayDelegate() {
 }
 
-void DefaultSystemTrayDelegate::Initialize() {
-}
-
-void DefaultSystemTrayDelegate::Shutdown() {
-}
-
 bool DefaultSystemTrayDelegate::GetTrayVisibilityOnStartup() {
   return true;
 }
@@ -54,43 +30,16 @@
   return user::LOGGED_IN_USER;
 }
 
-void DefaultSystemTrayDelegate::ChangeProfilePicture() {
-}
-
-const std::string DefaultSystemTrayDelegate::GetEnterpriseDomain() const {
-  return std::string();
-}
-
-const base::string16 DefaultSystemTrayDelegate::GetEnterpriseMessage() const {
-  return base::string16();
-}
-
-const std::string
-DefaultSystemTrayDelegate::GetSupervisedUserManager() const {
+const std::string DefaultSystemTrayDelegate::GetSupervisedUserManager() const {
   if (!IsUserSupervised())
     return std::string();
   return "manager@chrome.com";
 }
 
-const base::string16
-DefaultSystemTrayDelegate::GetSupervisedUserManagerName()
-    const {
-  return base::string16();
-}
-
-const base::string16 DefaultSystemTrayDelegate::GetSupervisedUserMessage()
-    const {
-  return base::string16();
-}
-
 bool DefaultSystemTrayDelegate::IsUserSupervised() const {
   return GetUserLoginStatus() == ash::user::LOGGED_IN_SUPERVISED;
 }
 
-bool DefaultSystemTrayDelegate::IsUserChild() const {
-  return false;
-}
-
 void DefaultSystemTrayDelegate::GetSystemUpdateInfo(UpdateInfo* info) const {
   DCHECK(info);
   info->severity = UpdateInfo::UPDATE_NORMAL;
@@ -98,106 +47,14 @@
   info->factory_reset_required = false;
 }
 
-base::HourClockType DefaultSystemTrayDelegate::GetHourClockType() const {
-  return base::k24HourClock;
-}
-
-void DefaultSystemTrayDelegate::ShowSettings() {
-}
-
 bool DefaultSystemTrayDelegate::ShouldShowSettings() {
   return true;
 }
 
-void DefaultSystemTrayDelegate::ShowDateSettings() {
-}
-
-void DefaultSystemTrayDelegate::ShowSetTimeDialog() {
-}
-
-void DefaultSystemTrayDelegate::ShowNetworkSettings(
-    const std::string& service_path) {
-}
-
-void DefaultSystemTrayDelegate::ShowBluetoothSettings() {
-}
-
-void DefaultSystemTrayDelegate::ShowDisplaySettings() {
-}
-
-void DefaultSystemTrayDelegate::ShowChromeSlow() {
-}
-
 bool DefaultSystemTrayDelegate::ShouldShowDisplayNotification() {
   return false;
 }
 
-void DefaultSystemTrayDelegate::ShowIMESettings() {
-}
-
-void DefaultSystemTrayDelegate::ShowHelp() {
-}
-
-void DefaultSystemTrayDelegate::ShowAccessibilityHelp() {
-}
-
-void DefaultSystemTrayDelegate::ShowAccessibilitySettings() {
-}
-
-void DefaultSystemTrayDelegate::ShowPublicAccountInfo() {
-}
-
-void DefaultSystemTrayDelegate::ShowEnterpriseInfo() {
-}
-
-void DefaultSystemTrayDelegate::ShowSupervisedUserInfo() {
-}
-
-void DefaultSystemTrayDelegate::ShowUserLogin() {
-}
-
-void DefaultSystemTrayDelegate::SignOut() {
-}
-
-void DefaultSystemTrayDelegate::RequestLockScreen() {
-}
-
-void DefaultSystemTrayDelegate::RequestRestartForUpdate() {
-}
-
-void DefaultSystemTrayDelegate::GetAvailableBluetoothDevices(
-    BluetoothDeviceList* list) {
-}
-
-void DefaultSystemTrayDelegate::BluetoothStartDiscovering() {
-}
-
-void DefaultSystemTrayDelegate::BluetoothStopDiscovering() {
-}
-
-void DefaultSystemTrayDelegate::ConnectToBluetoothDevice(
-    const std::string& address) {
-}
-
-void DefaultSystemTrayDelegate::GetCurrentIME(IMEInfo* info) {
-}
-
-void DefaultSystemTrayDelegate::GetAvailableIMEList(IMEInfoList* list) {
-}
-
-void DefaultSystemTrayDelegate::GetCurrentIMEProperties(
-    IMEPropertyInfoList* list) {
-}
-
-void DefaultSystemTrayDelegate::SwitchIME(const std::string& ime_id) {
-}
-
-void DefaultSystemTrayDelegate::ActivateIMEProperty(const std::string& key) {
-}
-
-void DefaultSystemTrayDelegate::ManageBluetoothDevices() {
-}
-
 void DefaultSystemTrayDelegate::ToggleBluetooth() {
   bluetooth_enabled_ = !bluetooth_enabled_;
 }
@@ -206,10 +63,6 @@
   return false;
 }
 
-void DefaultSystemTrayDelegate::ShowOtherNetworkDialog(
-    const std::string& type) {
-}
-
 bool DefaultSystemTrayDelegate::GetBluetoothAvailable() {
   return true;
 }
@@ -222,14 +75,6 @@
   return false;
 }
 
-void DefaultSystemTrayDelegate::ChangeProxySettings() {
-}
-
-NetworkingConfigDelegate*
-DefaultSystemTrayDelegate::GetNetworkingConfigDelegate() const {
-  return nullptr;
-}
-
 VolumeControlDelegate* DefaultSystemTrayDelegate::GetVolumeControlDelegate()
     const {
   return volume_control_delegate_.get();
@@ -240,51 +85,9 @@
   volume_control_delegate_ = delegate.Pass();
 }
 
-bool DefaultSystemTrayDelegate::GetSessionStartTime(
-    base::TimeTicks* session_start_time) {
-  return false;
-}
-
-bool DefaultSystemTrayDelegate::GetSessionLengthLimit(
-     base::TimeDelta* session_length_limit) {
-  return false;
-}
-
 int DefaultSystemTrayDelegate::GetSystemTrayMenuWidth() {
   // This is the default width for English languages.
   return 300;
 }
 
-void DefaultSystemTrayDelegate::ActiveUserWasChanged() {
-}
-
-bool DefaultSystemTrayDelegate::IsSearchKeyMappedToCapsLock() {
-  return false;
-}
-
-tray::UserAccountsDelegate* DefaultSystemTrayDelegate::GetUserAccountsDelegate(
-    const std::string& user_id) {
-  return NULL;
-}
-
-void DefaultSystemTrayDelegate::AddCustodianInfoTrayObserver(
-    CustodianInfoTrayObserver* observer) {
-}
-
-void DefaultSystemTrayDelegate::RemoveCustodianInfoTrayObserver(
-    CustodianInfoTrayObserver* observer) {
-}
-
-void DefaultSystemTrayDelegate::AddShutdownPolicyObserver(
-    ShutdownPolicyObserver* observer) {
-}
-
-void DefaultSystemTrayDelegate::RemoveShutdownPolicyObserver(
-    ShutdownPolicyObserver* observer) {
-}
-
-void DefaultSystemTrayDelegate::ShouldRebootOnShutdown(
-    const RebootOnShutdownCallback& callback) {
-}
-
 }  // namespace ash
diff --git a/ash/system/tray/default_system_tray_delegate.h b/ash/system/tray/default_system_tray_delegate.h
index 76584bf..ed26303 100644
--- a/ash/system/tray/default_system_tray_delegate.h
+++ b/ash/system/tray/default_system_tray_delegate.h
@@ -17,77 +17,23 @@
   DefaultSystemTrayDelegate();
   ~DefaultSystemTrayDelegate() override;
 
-  // Overridden from SystemTrayDelegate:
-  void Initialize() override;
-  void Shutdown() override;
+  // SystemTrayDelegate
   bool GetTrayVisibilityOnStartup() override;
   user::LoginStatus GetUserLoginStatus() const override;
-  void ChangeProfilePicture() override;
-  const std::string GetEnterpriseDomain() const override;
-  const base::string16 GetEnterpriseMessage() const override;
   const std::string GetSupervisedUserManager() const override;
-  const base::string16 GetSupervisedUserManagerName() const override;
-  const base::string16 GetSupervisedUserMessage() const override;
   bool IsUserSupervised() const override;
-  bool IsUserChild() const override;
   void GetSystemUpdateInfo(UpdateInfo* info) const override;
-  base::HourClockType GetHourClockType() const override;
-  void ShowSettings() override;
   bool ShouldShowSettings() override;
-  void ShowDateSettings() override;
-  void ShowSetTimeDialog() override;
-  void ShowNetworkSettings(const std::string& service_path) override;
-  void ShowBluetoothSettings() override;
-  void ShowDisplaySettings() override;
-  void ShowChromeSlow() override;
   bool ShouldShowDisplayNotification() override;
-  void ShowIMESettings() override;
-  void ShowHelp() override;
-  void ShowAccessibilityHelp() override;
-  void ShowAccessibilitySettings() override;
-  void ShowPublicAccountInfo() override;
-  void ShowEnterpriseInfo() override;
-  void ShowSupervisedUserInfo() override;
-  void ShowUserLogin() override;
-  void SignOut() override;
-  void RequestLockScreen() override;
-  void RequestRestartForUpdate() override;
-  void GetAvailableBluetoothDevices(BluetoothDeviceList* list) override;
-  void BluetoothStartDiscovering() override;
-  void BluetoothStopDiscovering() override;
-  void ConnectToBluetoothDevice(const std::string& address) override;
-  void GetCurrentIME(IMEInfo* info) override;
-  void GetAvailableIMEList(IMEInfoList* list) override;
-  void GetCurrentIMEProperties(IMEPropertyInfoList* list) override;
-  void SwitchIME(const std::string& ime_id) override;
-  void ActivateIMEProperty(const std::string& key) override;
-  void ManageBluetoothDevices() override;
   void ToggleBluetooth() override;
   bool IsBluetoothDiscovering() override;
-  void ShowOtherNetworkDialog(const std::string& type) override;
   bool GetBluetoothAvailable() override;
   bool GetBluetoothEnabled() override;
   bool GetBluetoothDiscovering() override;
-  void ChangeProxySettings() override;
-  NetworkingConfigDelegate* GetNetworkingConfigDelegate() const override;
   VolumeControlDelegate* GetVolumeControlDelegate() const override;
   void SetVolumeControlDelegate(
       scoped_ptr<VolumeControlDelegate> delegate) override;
-  bool GetSessionStartTime(base::TimeTicks* session_start_time) override;
-  bool GetSessionLengthLimit(base::TimeDelta* session_length_limit) override;
   int GetSystemTrayMenuWidth() override;
-  void ActiveUserWasChanged() override;
-  bool IsSearchKeyMappedToCapsLock() override;
-  tray::UserAccountsDelegate* GetUserAccountsDelegate(
-      const std::string& user_id) override;
-  void AddCustodianInfoTrayObserver(
-      CustodianInfoTrayObserver* observer) override;
-  void RemoveCustodianInfoTrayObserver(
-      CustodianInfoTrayObserver* observer) override;
-  void AddShutdownPolicyObserver(ShutdownPolicyObserver* observer) override;
-  void RemoveShutdownPolicyObserver(ShutdownPolicyObserver* observer) override;
-  void ShouldRebootOnShutdown(
-      const RebootOnShutdownCallback& callback) override;
 
  private:
   bool bluetooth_enabled_;
diff --git a/ash/system/tray/system_tray_delegate.cc b/ash/system/tray/system_tray_delegate.cc
index c1cf4b4..71f4e80 100644
--- a/ash/system/tray/system_tray_delegate.cc
+++ b/ash/system/tray/system_tray_delegate.cc
@@ -17,24 +17,19 @@
 }
 
 BluetoothDeviceInfo::BluetoothDeviceInfo()
-    : connected(false),
-      connecting(false),
-      paired(false) {
+    : connected(false), connecting(false), paired(false) {
 }
 
 BluetoothDeviceInfo::~BluetoothDeviceInfo() {
 }
 
-IMEInfo::IMEInfo()
-    : selected(false),
-      third_party(false) {
+IMEInfo::IMEInfo() : selected(false), third_party(false) {
 }
 
 IMEInfo::~IMEInfo() {
 }
 
-IMEPropertyInfo::IMEPropertyInfo()
-    : selected(false) {
+IMEPropertyInfo::IMEPropertyInfo() : selected(false) {
 }
 
 IMEPropertyInfo::~IMEPropertyInfo() {
@@ -49,4 +44,242 @@
 UpdateInfo::~UpdateInfo() {
 }
 
+SystemTrayDelegate::SystemTrayDelegate() {
+}
+
+SystemTrayDelegate::~SystemTrayDelegate() {
+}
+
+void SystemTrayDelegate::Initialize() {
+}
+
+void SystemTrayDelegate::Shutdown() {
+}
+
+bool SystemTrayDelegate::GetTrayVisibilityOnStartup() {
+  return false;
+}
+
+user::LoginStatus SystemTrayDelegate::GetUserLoginStatus() const {
+  return user::LOGGED_IN_NONE;
+}
+
+void SystemTrayDelegate::ChangeProfilePicture() {
+}
+
+const std::string SystemTrayDelegate::GetEnterpriseDomain() const {
+  return std::string();
+}
+
+const base::string16 SystemTrayDelegate::GetEnterpriseMessage() const {
+  return base::string16();
+}
+
+const std::string SystemTrayDelegate::GetSupervisedUserManager() const {
+  return std::string();
+}
+
+const base::string16 SystemTrayDelegate::GetSupervisedUserManagerName() const {
+  return base::string16();
+}
+
+const base::string16 SystemTrayDelegate::GetSupervisedUserMessage() const {
+  return base::string16();
+}
+
+bool SystemTrayDelegate::IsUserSupervised() const {
+  return false;
+}
+
+bool SystemTrayDelegate::IsUserChild() const {
+  return false;
+}
+
+void SystemTrayDelegate::GetSystemUpdateInfo(UpdateInfo* info) const {
+  info->severity = UpdateInfo::UPDATE_NORMAL;
+  info->update_required = false;
+  info->factory_reset_required = false;
+}
+
+base::HourClockType SystemTrayDelegate::GetHourClockType() const {
+  return base::k24HourClock;
+}
+
+void SystemTrayDelegate::ShowSettings() {
+}
+
+bool SystemTrayDelegate::ShouldShowSettings() {
+  return false;
+}
+
+void SystemTrayDelegate::ShowDateSettings() {
+}
+
+void SystemTrayDelegate::ShowSetTimeDialog() {
+}
+
+void SystemTrayDelegate::ShowNetworkSettings(const std::string& service_path) {
+}
+
+void SystemTrayDelegate::ShowBluetoothSettings() {
+}
+
+void SystemTrayDelegate::ShowDisplaySettings() {
+}
+
+void SystemTrayDelegate::ShowChromeSlow() {
+}
+
+bool SystemTrayDelegate::ShouldShowDisplayNotification() {
+  return false;
+}
+
+void SystemTrayDelegate::ShowIMESettings() {
+}
+
+void SystemTrayDelegate::ShowHelp() {
+}
+
+void SystemTrayDelegate::ShowAccessibilityHelp() {
+}
+
+void SystemTrayDelegate::ShowAccessibilitySettings() {
+}
+
+void SystemTrayDelegate::ShowPublicAccountInfo() {
+}
+
+void SystemTrayDelegate::ShowEnterpriseInfo() {
+}
+
+void SystemTrayDelegate::ShowSupervisedUserInfo() {
+}
+
+void SystemTrayDelegate::ShowUserLogin() {
+}
+
+void SystemTrayDelegate::SignOut() {
+}
+
+void SystemTrayDelegate::RequestLockScreen() {
+}
+
+void SystemTrayDelegate::RequestRestartForUpdate() {
+}
+
+void SystemTrayDelegate::GetAvailableBluetoothDevices(
+    BluetoothDeviceList* list) {
+}
+
+void SystemTrayDelegate::BluetoothStartDiscovering() {
+}
+
+void SystemTrayDelegate::BluetoothStopDiscovering() {
+}
+
+void SystemTrayDelegate::ConnectToBluetoothDevice(const std::string& address) {
+}
+
+void SystemTrayDelegate::GetCurrentIME(IMEInfo* info) {
+}
+
+void SystemTrayDelegate::GetAvailableIMEList(IMEInfoList* list) {
+}
+
+void SystemTrayDelegate::GetCurrentIMEProperties(IMEPropertyInfoList* list) {
+}
+
+void SystemTrayDelegate::SwitchIME(const std::string& ime_id) {
+}
+
+void SystemTrayDelegate::ActivateIMEProperty(const std::string& key) {
+}
+
+void SystemTrayDelegate::ManageBluetoothDevices() {
+}
+
+void SystemTrayDelegate::ToggleBluetooth() {
+}
+
+bool SystemTrayDelegate::IsBluetoothDiscovering() {
+  return false;
+}
+
+void SystemTrayDelegate::ShowOtherNetworkDialog(const std::string& type) {
+}
+
+bool SystemTrayDelegate::GetBluetoothAvailable() {
+  return false;
+}
+
+bool SystemTrayDelegate::GetBluetoothEnabled() {
+  return false;
+}
+
+bool SystemTrayDelegate::GetBluetoothDiscovering() {
+  return false;
+}
+
+void SystemTrayDelegate::ChangeProxySettings() {
+}
+
+NetworkingConfigDelegate* SystemTrayDelegate::GetNetworkingConfigDelegate()
+    const {
+  return nullptr;
+}
+
+VolumeControlDelegate* SystemTrayDelegate::GetVolumeControlDelegate() const {
+  return nullptr;
+}
+
+void SystemTrayDelegate::SetVolumeControlDelegate(
+    scoped_ptr<VolumeControlDelegate> delegate) {
+}
+
+bool SystemTrayDelegate::GetSessionStartTime(
+    base::TimeTicks* session_start_time) {
+  return false;
+}
+
+bool SystemTrayDelegate::GetSessionLengthLimit(
+    base::TimeDelta* session_length_limit) {
+  return false;
+}
+
+int SystemTrayDelegate::GetSystemTrayMenuWidth() {
+  return 0;
+}
+
+void SystemTrayDelegate::ActiveUserWasChanged() {
+}
+
+bool SystemTrayDelegate::IsSearchKeyMappedToCapsLock() {
+  return false;
+}
+
+tray::UserAccountsDelegate* SystemTrayDelegate::GetUserAccountsDelegate(
+    const std::string& user_id) {
+  return nullptr;
+}
+
+void SystemTrayDelegate::AddCustodianInfoTrayObserver(
+    CustodianInfoTrayObserver* observer) {
+}
+
+void SystemTrayDelegate::RemoveCustodianInfoTrayObserver(
+    CustodianInfoTrayObserver* observer) {
+}
+
+void SystemTrayDelegate::AddShutdownPolicyObserver(
+    ShutdownPolicyObserver* observer) {
+}
+
+void SystemTrayDelegate::RemoveShutdownPolicyObserver(
+    ShutdownPolicyObserver* observer) {
+}
+
+void SystemTrayDelegate::ShouldRebootOnShutdown(
+    const RebootOnShutdownCallback& callback) {
+}
+
 }  // namespace ash
diff --git a/ash/system/tray/system_tray_delegate.h b/ash/system/tray/system_tray_delegate.h
index ee1637f..6cfd1c5 100644
--- a/ash/system/tray/system_tray_delegate.h
+++ b/ash/system/tray/system_tray_delegate.h
@@ -10,6 +10,7 @@
 
 #include "ash/ash_export.h"
 #include "ash/system/user/login_status.h"
+#include "ash/volume_control_delegate.h"
 #include "base/callback_forward.h"
 #include "base/files/file_path.h"
 #include "base/i18n/time_formatting.h"
@@ -98,7 +99,6 @@
 using IMEInfoList = std::vector<IMEInfo>;
 
 class NetworkingConfigDelegate;
-class VolumeControlDelegate;
 
 using RebootOnShutdownCallback = base::Callback<void(bool)>;
 
@@ -106,221 +106,232 @@
 class UserAccountsDelegate;
 }  // namespace tray
 
+// SystemTrayDelegate is intended for delegating tasks in the System Tray to the
+// application (e.g. Chrome). These tasks should be limited to application
+// (browser) specific tasks. For non application specific tasks, where possible,
+// components/, chromeos/, device/, etc., code should be used directly. If more
+// than one related method is being added, consider adding an additional
+// specific delegate (e.g. VolumeControlDelegate).
+//
+// These methods should all have trivial default implementations for platforms
+// that do not implement the method (e.g. return false or nullptr). This
+// eliminates the need to propagate default implementations across the various
+// implementations of this class. Consumers of this delegate should handle the
+// default return value (e.g. nullptr).
 class ASH_EXPORT SystemTrayDelegate {
  public:
-  virtual ~SystemTrayDelegate() {}
+  SystemTrayDelegate();
+  virtual ~SystemTrayDelegate();
 
   // Called after SystemTray has been instantiated.
-  virtual void Initialize() = 0;
+  virtual void Initialize();
 
   // Called before SystemTray is destroyed.
-  virtual void Shutdown() = 0;
+  virtual void Shutdown();
 
   // Returns true if system tray should be visible on startup.
-  virtual bool GetTrayVisibilityOnStartup() = 0;
+  virtual bool GetTrayVisibilityOnStartup();
 
   // Gets information about the active user.
-  virtual user::LoginStatus GetUserLoginStatus() const = 0;
+  virtual user::LoginStatus GetUserLoginStatus() const;
 
   // Shows UI for changing user's profile picture.
-  virtual void ChangeProfilePicture() = 0;
+  virtual void ChangeProfilePicture();
 
   // Returns the domain that manages the device, if it is enterprise-enrolled.
-  virtual const std::string GetEnterpriseDomain() const = 0;
+  virtual const std::string GetEnterpriseDomain() const;
 
   // Returns notification for enterprise enrolled devices.
-  virtual const base::string16 GetEnterpriseMessage() const = 0;
+  virtual const base::string16 GetEnterpriseMessage() const;
 
   // Returns the display email of the user that manages the current supervised
   // user.
-  virtual const std::string GetSupervisedUserManager() const = 0;
+  virtual const std::string GetSupervisedUserManager() const;
 
   // Returns the name of the user that manages the current supervised user.
-  virtual const base::string16 GetSupervisedUserManagerName() const = 0;
+  virtual const base::string16 GetSupervisedUserManagerName() const;
 
   // Returns the notification for supervised users.
-  virtual const base::string16 GetSupervisedUserMessage() const = 0;
+  virtual const base::string16 GetSupervisedUserMessage() const;
 
   // Returns true if the current user is supervised: has legacy supervised
   // account or kid account.
-  virtual bool IsUserSupervised() const = 0;
+  virtual bool IsUserSupervised() const;
 
   // Returns true if the current user is child.
   // TODO(merkulova): remove on FakeUserManager componentization.
   // crbug.com/443119
-  virtual bool IsUserChild() const = 0;
+  virtual bool IsUserChild() const;
 
-  // Fills |info| structure with current update info.
-  virtual void GetSystemUpdateInfo(UpdateInfo* info) const = 0;
+  // Fills |info| structure (which must not be null) with current update info.
+  virtual void GetSystemUpdateInfo(UpdateInfo* info) const;
 
   // Returns the desired hour clock type.
-  virtual base::HourClockType GetHourClockType() const = 0;
+  virtual base::HourClockType GetHourClockType() const;
 
   // Shows settings.
-  virtual void ShowSettings() = 0;
+  virtual void ShowSettings();
 
   // Returns true if settings menu item should appear.
-  virtual bool ShouldShowSettings() = 0;
+  virtual bool ShouldShowSettings();
 
   // Shows the settings related to date, timezone etc.
-  virtual void ShowDateSettings() = 0;
+  virtual void ShowDateSettings();
 
   // Shows the dialog to set system time, date, and timezone.
-  virtual void ShowSetTimeDialog() = 0;
+  virtual void ShowSetTimeDialog();
 
   // Shows the settings related to network. If |service_path| is not empty,
   // show the settings for that network.
-  virtual void ShowNetworkSettings(const std::string& service_path) = 0;
+  virtual void ShowNetworkSettings(const std::string& service_path);
 
   // Shows the settings related to bluetooth.
-  virtual void ShowBluetoothSettings() = 0;
+  virtual void ShowBluetoothSettings();
 
   // Shows settings related to multiple displays.
-  virtual void ShowDisplaySettings() = 0;
+  virtual void ShowDisplaySettings();
 
   // Shows the page that lets you disable performance tracing.
-  virtual void ShowChromeSlow() = 0;
+  virtual void ShowChromeSlow();
 
   // Returns true if the notification for the display configuration change
   // should appear.
-  virtual bool ShouldShowDisplayNotification() = 0;
+  virtual bool ShouldShowDisplayNotification();
 
   // Shows settings related to input methods.
-  virtual void ShowIMESettings() = 0;
+  virtual void ShowIMESettings();
 
   // Shows help.
-  virtual void ShowHelp() = 0;
+  virtual void ShowHelp();
 
   // Show accessilibity help.
-  virtual void ShowAccessibilityHelp() = 0;
+  virtual void ShowAccessibilityHelp();
 
   // Show the settings related to accessilibity.
-  virtual void ShowAccessibilitySettings() = 0;
+  virtual void ShowAccessibilitySettings();
 
   // Shows more information about public account mode.
-  virtual void ShowPublicAccountInfo() = 0;
+  virtual void ShowPublicAccountInfo();
 
   // Shows information about enterprise enrolled devices.
-  virtual void ShowEnterpriseInfo() = 0;
+  virtual void ShowEnterpriseInfo();
 
   // Shows information about supervised users.
-  virtual void ShowSupervisedUserInfo() = 0;
+  virtual void ShowSupervisedUserInfo();
 
   // Shows login UI to add other users to this session.
-  virtual void ShowUserLogin() = 0;
+  virtual void ShowUserLogin();
 
   // Attempts to sign out the user.
-  virtual void SignOut() = 0;
+  virtual void SignOut();
 
   // Attempts to lock the screen.
-  virtual void RequestLockScreen() = 0;
+  virtual void RequestLockScreen();
 
   // Attempts to restart the system for update.
-  virtual void RequestRestartForUpdate() = 0;
+  virtual void RequestRestartForUpdate();
 
   // Returns a list of available bluetooth devices.
-  virtual void GetAvailableBluetoothDevices(BluetoothDeviceList* devices) = 0;
+  virtual void GetAvailableBluetoothDevices(BluetoothDeviceList* devices);
 
   // Requests bluetooth start discovering devices.
-  virtual void BluetoothStartDiscovering() = 0;
+  virtual void BluetoothStartDiscovering();
 
   // Requests bluetooth stop discovering devices.
-  virtual void BluetoothStopDiscovering() = 0;
+  virtual void BluetoothStopDiscovering();
 
   // Connect to a specific bluetooth device.
-  virtual void ConnectToBluetoothDevice(const std::string& address) = 0;
+  virtual void ConnectToBluetoothDevice(const std::string& address);
 
   // Returns true if bluetooth adapter is discovering bluetooth devices.
-  virtual bool IsBluetoothDiscovering() = 0;
+  virtual bool IsBluetoothDiscovering();
 
   // Returns the currently selected IME.
-  virtual void GetCurrentIME(IMEInfo* info) = 0;
+  virtual void GetCurrentIME(IMEInfo* info);
 
   // Returns a list of availble IMEs.
-  virtual void GetAvailableIMEList(IMEInfoList* list) = 0;
+  virtual void GetAvailableIMEList(IMEInfoList* list);
 
   // Returns a list of properties for the currently selected IME.
-  virtual void GetCurrentIMEProperties(IMEPropertyInfoList* list) = 0;
+  virtual void GetCurrentIMEProperties(IMEPropertyInfoList* list);
 
   // Switches to the selected input method.
-  virtual void SwitchIME(const std::string& ime_id) = 0;
+  virtual void SwitchIME(const std::string& ime_id);
 
   // Activates an IME property.
-  virtual void ActivateIMEProperty(const std::string& key) = 0;
+  virtual void ActivateIMEProperty(const std::string& key);
 
   // Shows UI to manage bluetooth devices.
-  virtual void ManageBluetoothDevices() = 0;
+  virtual void ManageBluetoothDevices();
 
   // Toggles bluetooth.
-  virtual void ToggleBluetooth() = 0;
+  virtual void ToggleBluetooth();
 
   // Shows UI to connect to an unlisted network of type |type|. On Chrome OS
   // |type| corresponds to a Shill network type.
-  virtual void ShowOtherNetworkDialog(const std::string& type) = 0;
+  virtual void ShowOtherNetworkDialog(const std::string& type);
 
   // Returns whether bluetooth capability is available.
-  virtual bool GetBluetoothAvailable() = 0;
+  virtual bool GetBluetoothAvailable();
 
   // Returns whether bluetooth is enabled.
-  virtual bool GetBluetoothEnabled() = 0;
+  virtual bool GetBluetoothEnabled();
 
   // Returns whether the delegate has initiated a bluetooth discovery session.
-  virtual bool GetBluetoothDiscovering() = 0;
+  virtual bool GetBluetoothDiscovering();
 
   // Shows UI for changing proxy settings.
-  virtual void ChangeProxySettings() = 0;
+  virtual void ChangeProxySettings();
 
-  // Returns NetworkingConfigDelegate.
-  virtual NetworkingConfigDelegate* GetNetworkingConfigDelegate() const = 0;
+  // Returns NetworkingConfigDelegate. May return nullptr.
+  virtual NetworkingConfigDelegate* GetNetworkingConfigDelegate() const;
 
-  // Returns VolumeControlDelegate.
-  virtual VolumeControlDelegate* GetVolumeControlDelegate() const = 0;
+  // Returns VolumeControlDelegate. May return nullptr.
+  virtual VolumeControlDelegate* GetVolumeControlDelegate() const;
 
-  // Sets VolumeControlDelegate.
+  // Sets the VolumeControlDelegate.
   virtual void SetVolumeControlDelegate(
-      scoped_ptr<VolumeControlDelegate> delegate) = 0;
+      scoped_ptr<VolumeControlDelegate> delegate);
 
   // Retrieves the session start time. Returns |false| if the time is not set.
-  virtual bool GetSessionStartTime(base::TimeTicks* session_start_time) = 0;
+  virtual bool GetSessionStartTime(base::TimeTicks* session_start_time);
 
   // Retrieves the session length limit. Returns |false| if no limit is set.
-  virtual bool GetSessionLengthLimit(base::TimeDelta* session_length_limit) = 0;
+  virtual bool GetSessionLengthLimit(base::TimeDelta* session_length_limit);
 
   // Get the system tray menu size in pixels (dependent on the language).
-  virtual int GetSystemTrayMenuWidth() = 0;
+  virtual int GetSystemTrayMenuWidth();
 
   // The active user has been changed. This will be called when the UI is ready
   // to be switched to the new user.
   // Note: This will happen after SessionStateObserver::ActiveUserChanged fires.
-  virtual void ActiveUserWasChanged() = 0;
+  virtual void ActiveUserWasChanged();
 
   // Returns true when the Search key is configured to be treated as Caps Lock.
-  virtual bool IsSearchKeyMappedToCapsLock() = 0;
+  virtual bool IsSearchKeyMappedToCapsLock();
 
-  // Returns accounts delegate for given user.
+  // Returns accounts delegate for given user. May return nullptr.
   virtual tray::UserAccountsDelegate* GetUserAccountsDelegate(
-      const std::string& user_id) = 0;
+      const std::string& user_id);
 
   // Adding observers that are notified when supervised info is being changed.
   virtual void AddCustodianInfoTrayObserver(
-      CustodianInfoTrayObserver* observer) = 0;
+      CustodianInfoTrayObserver* observer);
 
   virtual void RemoveCustodianInfoTrayObserver(
-      CustodianInfoTrayObserver* observer) = 0;
+      CustodianInfoTrayObserver* observer);
 
   // Adds an observer whose |OnShutdownPolicyChanged| function is called when
   // the |DeviceRebootOnShutdown| policy changes. If this policy is set to
   // true, a device cannot be shut down anymore but only rebooted.
-  virtual void AddShutdownPolicyObserver(ShutdownPolicyObserver* observer) = 0;
+  virtual void AddShutdownPolicyObserver(ShutdownPolicyObserver* observer);
 
-  virtual void RemoveShutdownPolicyObserver(
-      ShutdownPolicyObserver* observer) = 0;
+  virtual void RemoveShutdownPolicyObserver(ShutdownPolicyObserver* observer);
 
   // Determines whether the device is automatically rebooted when shut down as
   // specified by the device policy |DeviceRebootOnShutdown|. This function
   // asynchronously calls |callback| once a trusted policy becomes available.
-  virtual void ShouldRebootOnShutdown(
-      const RebootOnShutdownCallback& callback) = 0;
+  virtual void ShouldRebootOnShutdown(const RebootOnShutdownCallback& callback);
 };
 
 }  // namespace ash
diff --git a/ash/system/user/accounts_detailed_view.cc b/ash/system/user/accounts_detailed_view.cc
index 97f6691..fe41fd30 100644
--- a/ash/system/user/accounts_detailed_view.cc
+++ b/ash/system/user/accounts_detailed_view.cc
@@ -39,19 +39,14 @@
 }  // namespace
 
 AccountsDetailedView::AccountsDetailedView(TrayUser* owner,
-                                           user::LoginStatus login_status)
+                                           user::LoginStatus login_status,
+                                           UserAccountsDelegate* delegate)
     : TrayDetailsView(owner),
-      delegate_(NULL),
-      account_list_(NULL),
-      add_account_button_(NULL),
-      add_user_button_(NULL) {
-  std::string user_id = Shell::GetInstance()
-                            ->session_state_delegate()
-                            ->GetUserInfo(0)
-                            ->GetUserID();
-  delegate_ =
-      Shell::GetInstance()->system_tray_delegate()->GetUserAccountsDelegate(
-          user_id);
+      delegate_(delegate),
+      account_list_(nullptr),
+      add_account_button_(nullptr),
+      add_user_button_(nullptr) {
+  DCHECK(delegate_);
   delegate_->AddObserver(this);
   AddHeader(login_status);
   CreateScrollableList();
diff --git a/ash/system/user/accounts_detailed_view.h b/ash/system/user/accounts_detailed_view.h
index 7fabcc32..8b6f8f3 100644
--- a/ash/system/user/accounts_detailed_view.h
+++ b/ash/system/user/accounts_detailed_view.h
@@ -30,7 +30,9 @@
                              public views::ButtonListener,
                              public ash::tray::UserAccountsDelegate::Observer {
  public:
-  AccountsDetailedView(TrayUser* owner, user::LoginStatus login_status);
+  AccountsDetailedView(TrayUser* owner,
+                       user::LoginStatus login_status,
+                       UserAccountsDelegate* delegate);
   ~AccountsDetailedView() override;
 
  private:
@@ -52,7 +54,7 @@
 
   views::View* CreateDeleteButton();
 
-  ash::tray::UserAccountsDelegate* delegate_;
+  UserAccountsDelegate* delegate_;
   views::View* account_list_;
   views::View* add_account_button_;
   views::View* add_user_button_;
diff --git a/ash/system/user/tray_user.cc b/ash/system/user/tray_user.cc
index cdac4cf6..b3bd4ea15 100644
--- a/ash/system/user/tray_user.cc
+++ b/ash/system/user/tray_user.cc
@@ -42,10 +42,10 @@
 TrayUser::TrayUser(SystemTray* system_tray, MultiProfileIndex index)
     : SystemTrayItem(system_tray),
       multiprofile_index_(index),
-      user_(NULL),
-      layout_view_(NULL),
-      avatar_(NULL),
-      label_(NULL) {
+      user_(nullptr),
+      layout_view_(nullptr),
+      avatar_(nullptr),
+      label_(nullptr) {
   Shell::GetInstance()->system_tray_notifier()->AddUserObserver(this);
 }
 
@@ -77,7 +77,7 @@
 }
 
 views::View* TrayUser::CreateTrayView(user::LoginStatus status) {
-  CHECK(layout_view_ == NULL);
+  CHECK(layout_view_ == nullptr);
 
   layout_view_ = new views::View;
   layout_view_->SetLayoutManager(
@@ -89,7 +89,7 @@
 
 views::View* TrayUser::CreateDefaultView(user::LoginStatus status) {
   if (status == user::LOGGED_IN_NONE)
-    return NULL;
+    return nullptr;
   const SessionStateDelegate* session_state_delegate =
       Shell::GetInstance()->session_state_delegate();
 
@@ -98,32 +98,41 @@
   if (multiprofile_index_ &&
       (session_state_delegate->IsUserSessionBlocked() ||
        Shell::GetInstance()->IsSystemModalWindowOpen()))
-    return NULL;
+    return nullptr;
 
-  CHECK(user_ == NULL);
+  CHECK(user_ == nullptr);
 
   int logged_in_users = session_state_delegate->NumberOfLoggedInUsers();
 
   // Do not show more UserView's then there are logged in users.
   if (multiprofile_index_ >= logged_in_users)
-    return NULL;
+    return nullptr;
 
   user_ = new tray::UserView(this, status, multiprofile_index_, false);
   return user_;
 }
 
 views::View* TrayUser::CreateDetailedView(user::LoginStatus status) {
-  return new tray::AccountsDetailedView(this, status);
+  std::string user_id = Shell::GetInstance()
+                            ->session_state_delegate()
+                            ->GetUserInfo(0)
+                            ->GetUserID();
+  tray::UserAccountsDelegate* delegate =
+      Shell::GetInstance()->system_tray_delegate()->GetUserAccountsDelegate(
+          user_id);
+  if (!delegate)
+    return nullptr;
+  return new tray::AccountsDetailedView(this, status, delegate);
 }
 
 void TrayUser::DestroyTrayView() {
-  layout_view_ = NULL;
-  avatar_ = NULL;
-  label_ = NULL;
+  layout_view_ = nullptr;
+  avatar_ = nullptr;
+  label_ = nullptr;
 }
 
 void TrayUser::DestroyDefaultView() {
-  user_ = NULL;
+  user_ = nullptr;
 }
 
 void TrayUser::DestroyDetailedView() {
@@ -159,21 +168,21 @@
       break;
   }
 
-  if ((need_avatar != (avatar_ != NULL)) ||
-      (need_label != (label_ != NULL))) {
+  if ((need_avatar != (avatar_ != nullptr)) ||
+      (need_label != (label_ != nullptr))) {
     layout_view_->RemoveAllChildViews(true);
     if (need_label) {
       label_ = new views::Label;
       SetupLabelForTray(label_);
       layout_view_->AddChildView(label_);
     } else {
-      label_ = NULL;
+      label_ = nullptr;
     }
     if (need_avatar) {
       avatar_ = new tray::RoundedImageView(kTrayAvatarCornerRadius, true);
       layout_view_->AddChildView(avatar_);
     } else {
-      avatar_ = NULL;
+      avatar_ = nullptr;
     }
   }
 
diff --git a/ash/wm/drag_window_resizer_unittest.cc b/ash/wm/drag_window_resizer_unittest.cc
index b0044af4..cfe72ad 100644
--- a/ash/wm/drag_window_resizer_unittest.cc
+++ b/ash/wm/drag_window_resizer_unittest.cc
@@ -127,7 +127,6 @@
         Shell::GetInstance()->mouse_cursor_filter();
     bool is_warped = event_filter->WarpMouseCursorIfNecessaryForTest(
         target_root, point_in_screen);
-    event_filter->reset_was_mouse_warped_for_test();
     return is_warped;
   }
 
diff --git a/ash/wm/immersive_fullscreen_controller_unittest.cc b/ash/wm/immersive_fullscreen_controller_unittest.cc
index 7195429..3cae317 100644
--- a/ash/wm/immersive_fullscreen_controller_unittest.cc
+++ b/ash/wm/immersive_fullscreen_controller_unittest.cc
@@ -356,8 +356,8 @@
                           top_container_bounds_in_screen.y());
 
   // Mouse wheel event does nothing.
-  ui::MouseEvent wheel(
-      ui::ET_MOUSEWHEEL, top_edge_pos, top_edge_pos, ui::EF_NONE, ui::EF_NONE);
+  ui::MouseEvent wheel(ui::ET_MOUSEWHEEL, top_edge_pos, top_edge_pos,
+                       ui::EventTimeForNow(), ui::EF_NONE, ui::EF_NONE);
   event_generator.Dispatch(&wheel);
   EXPECT_FALSE(top_edge_hover_timer_running());
 
diff --git a/ash/wm/overview/window_selector_controller.cc b/ash/wm/overview/window_selector_controller.cc
index 69ad590..bd0aaf1 100644
--- a/ash/wm/overview/window_selector_controller.cc
+++ b/ash/wm/overview/window_selector_controller.cc
@@ -23,8 +23,8 @@
 namespace ash {
 
 WindowSelectorController::WindowSelectorController()
-    : swipe_to_close_disabled_(base::CommandLine::ForCurrentProcess()->
-        HasSwitch(switches::kAshDisableSwipeToCloseInOverviewMode)) {
+    : swipe_to_close_enabled_(base::CommandLine::ForCurrentProcess()->
+        HasSwitch(switches::kAshEnableSwipeToCloseInOverviewMode)) {
 }
 
 WindowSelectorController::~WindowSelectorController() {
diff --git a/ash/wm/overview/window_selector_controller.h b/ash/wm/overview/window_selector_controller.h
index 3a87904..518d05de 100644
--- a/ash/wm/overview/window_selector_controller.h
+++ b/ash/wm/overview/window_selector_controller.h
@@ -48,7 +48,7 @@
   // are visible during overview mode.
   bool IsRestoringMinimizedWindows() const;
 
-  bool swipe_to_close_disabled() const { return swipe_to_close_disabled_; }
+  bool swipe_to_close_enabled() const { return swipe_to_close_enabled_; }
 
   // WindowSelectorDelegate:
   void OnSelectionEnded() override;
@@ -62,8 +62,8 @@
   scoped_ptr<WindowSelector> window_selector_;
   base::Time last_selection_time_;
 
-  // Tracks whether the "Swipe-to-close" feature is disabled.
-  bool swipe_to_close_disabled_;
+  // Tracks whether the "Swipe-to-close" feature is enabled.
+  bool swipe_to_close_enabled_;
 
   DISALLOW_COPY_AND_ASSIGN(WindowSelectorController);
 };
diff --git a/ash/wm/overview/window_selector_item.cc b/ash/wm/overview/window_selector_item.cc
index 2559362..0ee5f7c 100644
--- a/ash/wm/overview/window_selector_item.cc
+++ b/ash/wm/overview/window_selector_item.cc
@@ -261,8 +261,8 @@
 }
 
 void WindowSelectorItem::OnGestureEvent(ui::GestureEvent* event) {
-  if (Shell::GetInstance()->window_selector_controller()->
-      swipe_to_close_disabled())
+  if (!Shell::GetInstance()->window_selector_controller()->
+      swipe_to_close_enabled())
     return;
 
   int delta_x = 0;
diff --git a/ash/wm/overview/window_selector_unittest.cc b/ash/wm/overview/window_selector_unittest.cc
index a663b5c..b013a01 100644
--- a/ash/wm/overview/window_selector_unittest.cc
+++ b/ash/wm/overview/window_selector_unittest.cc
@@ -45,6 +45,7 @@
 #include "ui/aura/window.h"
 #include "ui/aura/window_event_dispatcher.h"
 #include "ui/compositor/scoped_animation_duration_scale_mode.h"
+#include "ui/events/event_utils.h"
 #include "ui/events/test/event_generator.h"
 #include "ui/gfx/geometry/point_conversions.h"
 #include "ui/gfx/geometry/rect_conversions.h"
@@ -278,21 +279,21 @@
   DISALLOW_COPY_AND_ASSIGN(WindowSelectorTest);
 };
 
-class WindowSelectorSwipeToCloseDisabledTest : public WindowSelectorTest {
+class WindowSelectorSwipeToCloseEnabledTest : public WindowSelectorTest {
  public:
-  WindowSelectorSwipeToCloseDisabledTest() {}
-  ~WindowSelectorSwipeToCloseDisabledTest() override {}
+  WindowSelectorSwipeToCloseEnabledTest() {}
+  ~WindowSelectorSwipeToCloseEnabledTest() override {}
 
   // WindowSelectorTest:
   void SetUp() override;
 
  private:
-  DISALLOW_COPY_AND_ASSIGN(WindowSelectorSwipeToCloseDisabledTest);
+  DISALLOW_COPY_AND_ASSIGN(WindowSelectorSwipeToCloseEnabledTest);
 };
 
-void WindowSelectorSwipeToCloseDisabledTest::SetUp() {
+void WindowSelectorSwipeToCloseEnabledTest::SetUp() {
   base::CommandLine::ForCurrentProcess()->AppendSwitch(
-      switches::kAshDisableSwipeToCloseInOverviewMode);
+      switches::kAshEnableSwipeToCloseInOverviewMode);
   WindowSelectorTest::SetUp();
 }
 
@@ -434,7 +435,7 @@
   gfx::Point point1(window_bounds.x() + 10, window_bounds.y() + 10);
 
   ui::MouseEvent event1(ui::ET_MOUSE_PRESSED, point1, point1,
-                        ui::EF_NONE, ui::EF_NONE);
+                        ui::EventTimeForNow(), ui::EF_NONE, ui::EF_NONE);
 
   ui::EventTarget* root_target = root_window;
   ui::EventTargeter* targeter = root_target->GetEventTargeter();
@@ -450,7 +451,7 @@
   gfx::RectF bounds = GetTransformedBoundsInRootWindow(window.get());
   gfx::Point point2(bounds.x() + 10, bounds.y() + 10);
   ui::MouseEvent event2(ui::ET_MOUSE_PRESSED, point2, point2,
-                        ui::EF_NONE, ui::EF_NONE);
+                        ui::EventTimeForNow(), ui::EF_NONE, ui::EF_NONE);
 
   // Now the transparent window should be intercepting this event.
   EXPECT_NE(window, static_cast<aura::Window*>(
@@ -1273,8 +1274,8 @@
   EXPECT_FALSE(IsSelecting());
 }
 
-// Verify swipe to close doesn't work when swipe to close is disabled.
-TEST_F(WindowSelectorSwipeToCloseDisabledTest, WindowTapDragFarDistance) {
+// Verify swipe to close doesn't work when swipe to close is not enabled.
+TEST_F(WindowSelectorTest, WindowTapDragFarDistance) {
   scoped_ptr<views::Widget> widget =
       CreateWindowWidget(gfx::Rect(0, 0, 400, 400));
 
@@ -1299,7 +1300,8 @@
 }
 
 // Verify the window moves and fades as it is dragged.
-TEST_F(WindowSelectorTest, VerifyWindowBehaviourDuringTapDrag) {
+TEST_F(WindowSelectorSwipeToCloseEnabledTest,
+       VerifyWindowBehaviourDuringTapDrag) {
   scoped_ptr<aura::Window> window(CreateWindow(gfx::Rect(0, 0, 400, 400)));
 
   ToggleOverview();
@@ -1350,7 +1352,7 @@
 }
 
 // Test dragging a window a short distance.
-TEST_F(WindowSelectorTest, WindowTapDragShortDistance) {
+TEST_F(WindowSelectorSwipeToCloseEnabledTest, WindowTapDragShortDistance) {
   scoped_ptr<views::Widget> widget =
       CreateWindowWidget(gfx::Rect(0, 0, 400, 400));
 
@@ -1374,7 +1376,7 @@
 }
 
 // Test dragging a window a far distance.
-TEST_F(WindowSelectorTest, WindowTapDragFarDistance) {
+TEST_F(WindowSelectorSwipeToCloseEnabledTest, WindowTapDragFarDistance) {
   scoped_ptr<views::Widget> widget =
       CreateWindowWidget(gfx::Rect(0, 0, 400, 400));
 
@@ -1399,7 +1401,7 @@
 }
 
 // Test a slow velocity fling.
-TEST_F(WindowSelectorTest, SlowVelocityFling) {
+TEST_F(WindowSelectorSwipeToCloseEnabledTest, SlowVelocityFling) {
   scoped_ptr<views::Widget> widget =
       CreateWindowWidget(gfx::Rect(0, 0, 400, 400));
 
@@ -1425,7 +1427,7 @@
 }
 
 // Test a fast velocity fling.
-TEST_F(WindowSelectorTest, FastVelocityFling) {
+TEST_F(WindowSelectorSwipeToCloseEnabledTest, FastVelocityFling) {
   scoped_ptr<views::Widget> widget =
       CreateWindowWidget(gfx::Rect(0, 0, 400, 400));
 
@@ -1452,7 +1454,7 @@
 }
 
 // Test a fast velocity fling.
-TEST_F(WindowSelectorTest, SlowVelocityFlingAtAFarDistance) {
+TEST_F(WindowSelectorSwipeToCloseEnabledTest, SlowVelocityFlingAtAFarDistance) {
   scoped_ptr<views::Widget> widget =
       CreateWindowWidget(gfx::Rect(0, 0, 400, 400));
 
diff --git a/ash/wm/window_manager_unittest.cc b/ash/wm/window_manager_unittest.cc
index 61aaaf2..2d777c7 100644
--- a/ash/wm/window_manager_unittest.cc
+++ b/ash/wm/window_manager_unittest.cc
@@ -513,7 +513,8 @@
   {
     // Resize edges and corners show proper cursors.
     window_delegate.set_hittest_code(HTBOTTOM);
-    ui::MouseEvent move1(ui::ET_MOUSE_MOVED, point1, point1, 0, 0);
+    ui::MouseEvent move1(ui::ET_MOUSE_MOVED, point1, point1,
+                         ui::EventTimeForNow(), 0, 0);
     ui::EventDispatchDetails details = dispatcher->OnEventFromSource(&move1);
     ASSERT_FALSE(details.dispatcher_destroyed);
     EXPECT_EQ(ui::kCursorSouthResize, host->last_cursor().native_type());
@@ -521,7 +522,8 @@
 
   {
     window_delegate.set_hittest_code(HTBOTTOMLEFT);
-    ui::MouseEvent move2(ui::ET_MOUSE_MOVED, point2, point2, 0, 0);
+    ui::MouseEvent move2(ui::ET_MOUSE_MOVED, point2, point2,
+                         ui::EventTimeForNow(), 0, 0);
     ui::EventDispatchDetails details = dispatcher->OnEventFromSource(&move2);
     ASSERT_FALSE(details.dispatcher_destroyed);
     EXPECT_EQ(ui::kCursorSouthWestResize, host->last_cursor().native_type());
@@ -529,7 +531,8 @@
 
   {
     window_delegate.set_hittest_code(HTBOTTOMRIGHT);
-    ui::MouseEvent move1(ui::ET_MOUSE_MOVED, point1, point1, 0, 0);
+    ui::MouseEvent move1(ui::ET_MOUSE_MOVED, point1, point1,
+                         ui::EventTimeForNow(), 0, 0);
     ui::EventDispatchDetails details = dispatcher->OnEventFromSource(&move1);
     ASSERT_FALSE(details.dispatcher_destroyed);
     EXPECT_EQ(ui::kCursorSouthEastResize, host->last_cursor().native_type());
@@ -537,7 +540,8 @@
 
   {
     window_delegate.set_hittest_code(HTLEFT);
-    ui::MouseEvent move2(ui::ET_MOUSE_MOVED, point2, point2, 0, 0);
+    ui::MouseEvent move2(ui::ET_MOUSE_MOVED, point2, point2,
+                         ui::EventTimeForNow(), 0, 0);
     ui::EventDispatchDetails details = dispatcher->OnEventFromSource(&move2);
     ASSERT_FALSE(details.dispatcher_destroyed);
     EXPECT_EQ(ui::kCursorWestResize, host->last_cursor().native_type());
@@ -545,7 +549,8 @@
 
   {
     window_delegate.set_hittest_code(HTRIGHT);
-    ui::MouseEvent move1(ui::ET_MOUSE_MOVED, point1, point1, 0, 0);
+    ui::MouseEvent move1(ui::ET_MOUSE_MOVED, point1, point1,
+                         ui::EventTimeForNow(), 0, 0);
     ui::EventDispatchDetails details = dispatcher->OnEventFromSource(&move1);
     ASSERT_FALSE(details.dispatcher_destroyed);
     EXPECT_EQ(ui::kCursorEastResize, host->last_cursor().native_type());
@@ -553,7 +558,8 @@
 
   {
     window_delegate.set_hittest_code(HTTOP);
-    ui::MouseEvent move2(ui::ET_MOUSE_MOVED, point2, point2, 0, 0);
+    ui::MouseEvent move2(ui::ET_MOUSE_MOVED, point2, point2,
+                         ui::EventTimeForNow(), 0, 0);
     ui::EventDispatchDetails details = dispatcher->OnEventFromSource(&move2);
     ASSERT_FALSE(details.dispatcher_destroyed);
     EXPECT_EQ(ui::kCursorNorthResize, host->last_cursor().native_type());
@@ -561,7 +567,8 @@
 
   {
     window_delegate.set_hittest_code(HTTOPLEFT);
-    ui::MouseEvent move1(ui::ET_MOUSE_MOVED, point1, point1, 0, 0);
+    ui::MouseEvent move1(ui::ET_MOUSE_MOVED, point1, point1,
+                         ui::EventTimeForNow(), 0, 0);
     ui::EventDispatchDetails details = dispatcher->OnEventFromSource(&move1);
     ASSERT_FALSE(details.dispatcher_destroyed);
     EXPECT_EQ(ui::kCursorNorthWestResize, host->last_cursor().native_type());
@@ -569,7 +576,8 @@
 
   {
     window_delegate.set_hittest_code(HTTOPRIGHT);
-    ui::MouseEvent move2(ui::ET_MOUSE_MOVED, point2, point2, 0, 0);
+    ui::MouseEvent move2(ui::ET_MOUSE_MOVED, point2, point2,
+                         ui::EventTimeForNow(), 0, 0);
     ui::EventDispatchDetails details = dispatcher->OnEventFromSource(&move2);
     ASSERT_FALSE(details.dispatcher_destroyed);
     EXPECT_EQ(ui::kCursorNorthEastResize, host->last_cursor().native_type());
@@ -578,7 +586,8 @@
   {
     // Client area uses null cursor.
     window_delegate.set_hittest_code(HTCLIENT);
-    ui::MouseEvent move1(ui::ET_MOUSE_MOVED, point1, point1, 0, 0);
+    ui::MouseEvent move1(ui::ET_MOUSE_MOVED, point1, point1,
+                         ui::EventTimeForNow(), 0, 0);
     ui::EventDispatchDetails details = dispatcher->OnEventFromSource(&move1);
     ASSERT_FALSE(details.dispatcher_destroyed);
     EXPECT_EQ(ui::kCursorNull, host->last_cursor().native_type());
@@ -612,29 +621,23 @@
 
   gfx::Point miss_point(5, 5);
   transform.TransformPoint(&miss_point);
-  ui::MouseEvent mouseev1(ui::ET_MOUSE_PRESSED,
-                          miss_point,
-                          miss_point,
-                          ui::EF_LEFT_MOUSE_BUTTON,
+  ui::MouseEvent mouseev1(ui::ET_MOUSE_PRESSED, miss_point, miss_point,
+                          ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
                           ui::EF_LEFT_MOUSE_BUTTON);
   ui::EventProcessor* dispatcher = root_window->GetHost()->event_processor();
   ui::EventDispatchDetails details = dispatcher->OnEventFromSource(&mouseev1);
   ASSERT_FALSE(details.dispatcher_destroyed);
   EXPECT_EQ(NULL, aura::client::GetFocusClient(w1.get())->GetFocusedWindow());
-  ui::MouseEvent mouseup(ui::ET_MOUSE_RELEASED,
-                         miss_point,
-                         miss_point,
-                         ui::EF_LEFT_MOUSE_BUTTON,
+  ui::MouseEvent mouseup(ui::ET_MOUSE_RELEASED, miss_point, miss_point,
+                         ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
                          ui::EF_LEFT_MOUSE_BUTTON);
   details = dispatcher->OnEventFromSource(&mouseup);
   ASSERT_FALSE(details.dispatcher_destroyed);
 
   gfx::Point hit_point(5, 15);
   transform.TransformPoint(&hit_point);
-  ui::MouseEvent mouseev2(ui::ET_MOUSE_PRESSED,
-                          hit_point,
-                          hit_point,
-                          ui::EF_LEFT_MOUSE_BUTTON,
+  ui::MouseEvent mouseev2(ui::ET_MOUSE_PRESSED, hit_point, hit_point,
+                          ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
                           ui::EF_LEFT_MOUSE_BUTTON);
   details = dispatcher->OnEventFromSource(&mouseev2);
   ASSERT_FALSE(details.dispatcher_destroyed);
@@ -672,8 +675,8 @@
   ui::EventProcessor* dispatcher = root_window->GetHost()->event_processor();
   ui::EventDispatchDetails details = dispatcher->OnEventFromSource(&key_event);
   ASSERT_FALSE(details.dispatcher_destroyed);
-  ui::MouseEvent mouse_pressed(
-      ui::ET_MOUSE_PRESSED, gfx::Point(0, 0), gfx::Point(0, 0), 0, 0);
+  ui::MouseEvent mouse_pressed(ui::ET_MOUSE_PRESSED, gfx::Point(0, 0),
+                               gfx::Point(0, 0), ui::EventTimeForNow(), 0, 0);
   details = dispatcher->OnEventFromSource(&mouse_pressed);
   ASSERT_FALSE(details.dispatcher_destroyed);
 
@@ -693,8 +696,8 @@
   // Dispatches events.
   details = dispatcher->OnEventFromSource(&key_event);
   ASSERT_FALSE(details.dispatcher_destroyed);
-  ui::MouseEvent mouse_released(
-      ui::ET_MOUSE_RELEASED, gfx::Point(0, 0), gfx::Point(0, 0), 0, 0);
+  ui::MouseEvent mouse_released(ui::ET_MOUSE_RELEASED, gfx::Point(0, 0),
+                                gfx::Point(0, 0), ui::EventTimeForNow(), 0, 0);
   details = dispatcher->OnEventFromSource(&mouse_released);
   ASSERT_FALSE(details.dispatcher_destroyed);
 
diff --git a/ash/wm/workspace/multi_window_resize_controller.cc b/ash/wm/workspace/multi_window_resize_controller.cc
index 1b8b2cf..067275b 100644
--- a/ash/wm/workspace/multi_window_resize_controller.cc
+++ b/ash/wm/workspace/multi_window_resize_controller.cc
@@ -18,6 +18,7 @@
 #include "ui/base/hit_test.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/events/event_targeter.h"
+#include "ui/events/event_utils.h"
 #include "ui/gfx/canvas.h"
 #include "ui/gfx/image/image.h"
 #include "ui/gfx/screen.h"
@@ -549,7 +550,8 @@
   aura::Window* root = windows_.window1->GetRootWindow();
   ::wm::ConvertPointFromScreen(root, &location_in_root);
   ui::MouseEvent test_event(ui::ET_MOUSE_MOVED, location_in_root,
-                            location_in_root, ui::EF_NONE, ui::EF_NONE);
+                            location_in_root, ui::EventTimeForNow(),
+                            ui::EF_NONE, ui::EF_NONE);
   ui::EventTarget* event_handler = static_cast<ui::EventTarget*>(root)
                                        ->GetEventTargeter()
                                        ->FindTargetForEvent(root, &test_event);
diff --git a/ash/wm/workspace/workspace_event_handler_unittest.cc b/ash/wm/workspace/workspace_event_handler_unittest.cc
index dde0be8..42f7f517 100644
--- a/ash/wm/workspace/workspace_event_handler_unittest.cc
+++ b/ash/wm/workspace/workspace_event_handler_unittest.cc
@@ -18,6 +18,7 @@
 #include "ui/aura/window_tree_host.h"
 #include "ui/base/hit_test.h"
 #include "ui/events/event_processor.h"
+#include "ui/events/event_utils.h"
 #include "ui/events/test/event_generator.h"
 #include "ui/gfx/screen.h"
 #include "ui/wm/core/window_util.h"
@@ -37,10 +38,10 @@
                           int flags) {
   gfx::Point location = generator->current_location();
   ui::MouseEvent press(ui::ET_MOUSE_PRESSED, location, location,
-                       button | flags, button);
+                       ui::EventTimeForNow(), button | flags, button);
   generator->Dispatch(&press);
   ui::MouseEvent release(ui::ET_MOUSE_RELEASED, location, location,
-                         button | flags, button);
+                         ui::EventTimeForNow(), button | flags, button);
   generator->Dispatch(&release);
 }
 
diff --git a/ash/wm/workspace_controller_unittest.cc b/ash/wm/workspace_controller_unittest.cc
index df1de84..640785fb4 100644
--- a/ash/wm/workspace_controller_unittest.cc
+++ b/ash/wm/workspace_controller_unittest.cc
@@ -1478,8 +1478,8 @@
     for (int i = 0; i < kNumPoints; ++i) {
       SCOPED_TRACE(points[i].direction);
       const gfx::Point& location = points[i].location;
-      ui::MouseEvent mouse(ui::ET_MOUSE_MOVED, location, location, ui::EF_NONE,
-                           ui::EF_NONE);
+      ui::MouseEvent mouse(ui::ET_MOUSE_MOVED, location, location,
+                           ui::EventTimeForNow(), ui::EF_NONE, ui::EF_NONE);
       ui::EventTarget* target = targeter->FindTargetForEvent(root, &mouse);
       EXPECT_EQ(expected_target, target);
 
@@ -1519,8 +1519,8 @@
   for (int i = 0; i < kNumPoints; ++i) {
     SCOPED_TRACE(points[i].direction);
     const gfx::Point& location = points[i].location;
-    ui::MouseEvent mouse(ui::ET_MOUSE_MOVED, location, location, ui::EF_NONE,
-                         ui::EF_NONE);
+    ui::MouseEvent mouse(ui::ET_MOUSE_MOVED, location, location,
+                         ui::EventTimeForNow(), ui::EF_NONE, ui::EF_NONE);
     ui::EventTarget* target = targeter->FindTargetForEvent(root, &mouse);
     if (points[i].is_target_hit)
       EXPECT_EQ(window.get(), target);
@@ -1596,8 +1596,8 @@
   for (int i = 0; i < kNumPoints; ++i) {
     SCOPED_TRACE(points[i].direction);
     const gfx::Point& location = points[i].location;
-    ui::MouseEvent mouse(ui::ET_MOUSE_MOVED, location, location, ui::EF_NONE,
-                         ui::EF_NONE);
+    ui::MouseEvent mouse(ui::ET_MOUSE_MOVED, location, location,
+                         ui::EventTimeForNow(), ui::EF_NONE, ui::EF_NONE);
     ui::EventTarget* target = targeter->FindTargetForEvent(root, &mouse);
     if (points[i].is_target_hit)
       EXPECT_EQ(window.get(), target);
diff --git a/base/BUILD.gn b/base/BUILD.gn
index e4176811..c63d377 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -42,7 +42,6 @@
     "android/jni_android.h",
     "android/jni_array.cc",
     "android/jni_array.h",
-    "android/jni_onload_delegate.h",
     "android/jni_registrar.cc",
     "android/jni_registrar.h",
     "android/jni_string.cc",
@@ -676,6 +675,10 @@
     "trace_event/memory_dump_provider.h",
     "trace_event/process_memory_dump.cc",
     "trace_event/process_memory_dump.h",
+    "trace_event/process_memory_totals.cc",
+    "trace_event/process_memory_totals.h",
+    "trace_event/process_memory_totals_dump_provider.cc",
+    "trace_event/process_memory_totals_dump_provider.h",
     "trace_event/trace_event.h",
     "trace_event/trace_event_android.cc",
     "trace_event/trace_event_argument.cc",
@@ -869,7 +872,7 @@
     ]
 
     # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
-    cflags = [ "/wd4267" ]
+    configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
 
     libs = [
       "cfgmgr32.lib",
@@ -1028,10 +1031,8 @@
     configs += [ "//build/config/compiler:optimize_max" ]
   }
 
-  if (is_win) {
-    # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
-    cflags = [ "/wd4267" ]
-  }
+  # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+  configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
 }
 
 source_set("prefs") {
@@ -1335,6 +1336,7 @@
     "timer/timer_unittest.cc",
     "tools_sanity_unittest.cc",
     "trace_event/memory_dump_manager_unittest.cc",
+    "trace_event/process_memory_totals_dump_provider_unittest.cc",
     "trace_event/trace_event_argument_unittest.cc",
     "trace_event/trace_event_memory_unittest.cc",
     "trace_event/trace_event_synthetic_delay_unittest.cc",
@@ -1439,10 +1441,8 @@
     set_sources_assignment_filter(sources_assignment_filter)
   }
 
-  if (is_win) {
-    # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
-    cflags = [ "/wd4267" ]
-  }
+  # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+  configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
 }
 
 if (is_android) {
diff --git a/base/allocator/BUILD.gn b/base/allocator/BUILD.gn
index e931b1c..a07a356 100644
--- a/base/allocator/BUILD.gn
+++ b/base/allocator/BUILD.gn
@@ -36,7 +36,7 @@
     args = [
       visual_studio_path + "/vc/lib",
       rebase_path("$target_gen_dir/allocator"),
-      cpu_arch,
+      current_cpu,
     ]
   }
 }
diff --git a/base/android/base_jni_onload.cc b/base/android/base_jni_onload.cc
index ae64120..c3a65d4 100644
--- a/base/android/base_jni_onload.cc
+++ b/base/android/base_jni_onload.cc
@@ -5,59 +5,51 @@
 #include "base/android/base_jni_onload.h"
 
 #include "base/android/jni_android.h"
-#include "base/android/jni_onload_delegate.h"
+#include "base/android/jni_utils.h"
 #include "base/android/library_loader/library_loader_hooks.h"
+#include "base/bind.h"
 
 namespace base {
 namespace android {
 
 namespace {
 
-// The JNIOnLoadDelegate implementation in base.
-class BaseJNIOnLoadDelegate : public JNIOnLoadDelegate {
- public:
-  bool RegisterJNI(JNIEnv* env) override;
-  bool Init() override;
-};
-
-bool BaseJNIOnLoadDelegate::RegisterJNI(JNIEnv* env) {
+bool RegisterJNI(JNIEnv* env) {
   return RegisterLibraryLoaderEntryHook(env);
 }
 
-bool BaseJNIOnLoadDelegate::Init() {
+bool Init() {
+  JNIEnv* env = base::android::AttachCurrentThread();
+  base::android::InitReplacementClassLoader(env,
+                                            base::android::GetClassLoader(env));
   return true;
 }
 
 }  // namespace
 
 
-bool OnJNIOnLoad(JavaVM* vm,
-                 std::vector<JNIOnLoadDelegate*>* delegates) {
+bool OnJNIOnLoadRegisterJNI(JavaVM* vm,
+                            std::vector<RegisterCallback> callbacks) {
   base::android::InitVM(vm);
   JNIEnv* env = base::android::AttachCurrentThread();
 
-  BaseJNIOnLoadDelegate delegate;
-  delegates->push_back(&delegate);
-  bool ret = true;
-  for (std::vector<JNIOnLoadDelegate*>::reverse_iterator i =
-           delegates->rbegin(); i != delegates->rend(); ++i) {
-    if (!(*i)->RegisterJNI(env)) {
-      ret = false;
-      break;
-    }
+  callbacks.push_back(base::Bind(&RegisterJNI));
+  for (std::vector<RegisterCallback>::reverse_iterator i =
+           callbacks.rbegin(); i != callbacks.rend(); ++i) {
+    if (!i->Run(env))
+      return false;
   }
+  return true;
+}
 
-  if (ret) {
-    for (std::vector<JNIOnLoadDelegate*>::reverse_iterator i =
-             delegates->rbegin(); i != delegates->rend(); ++i) {
-      if (!(*i)->Init()) {
-        ret = false;
-        break;
-      }
-    }
+bool OnJNIOnLoadInit(std::vector<InitCallback> callbacks) {
+  callbacks.push_back(base::Bind(&Init));
+  for (std::vector<InitCallback>::reverse_iterator i =
+           callbacks.rbegin(); i != callbacks.rend(); ++i) {
+    if (!i->Run())
+      return false;
   }
-  delegates->pop_back();
-  return ret;
+  return true;
 }
 
 }  // namespace android
diff --git a/base/android/base_jni_onload.h b/base/android/base_jni_onload.h
index f3f05fa5..dcc7756 100644
--- a/base/android/base_jni_onload.h
+++ b/base/android/base_jni_onload.h
@@ -9,17 +9,22 @@
 #include <vector>
 
 #include "base/base_export.h"
+#include "base/callback.h"
 
 namespace base {
 namespace android {
 
-class JNIOnLoadDelegate;
+// Returns whether JNI registration succeeded. Caller shall put the
+// RegisterCallback into |callbacks| in reverse order.
+typedef base::Callback<bool(JNIEnv*)> RegisterCallback;
+BASE_EXPORT bool OnJNIOnLoadRegisterJNI(
+    JavaVM* vm,
+    std::vector<RegisterCallback> callbacks);
 
-// Returns whether JNI registration and initialization succeeded. Caller shall
-// put the JNIOnLoadDelegate into |delegates| in reverse order. Refer
-// JNIOnLoadDelegate for more information.
-BASE_EXPORT bool OnJNIOnLoad(JavaVM* vm,
-                             std::vector<JNIOnLoadDelegate*>* delegates);
+// Returns whether initialization succeeded. Caller shall put the
+// InitCallback into |callbacks| in reverse order.
+typedef base::Callback<bool(void)> InitCallback;
+BASE_EXPORT bool OnJNIOnLoadInit(std::vector<InitCallback> callbacks);
 
 }  // namespace android
 }  // namespace base
diff --git a/base/android/java/src/org/chromium/base/ResourceExtractor.java b/base/android/java/src/org/chromium/base/ResourceExtractor.java
index 9252b4dd..d44f2fc 100644
--- a/base/android/java/src/org/chromium/base/ResourceExtractor.java
+++ b/base/android/java/src/org/chromium/base/ResourceExtractor.java
@@ -12,6 +12,8 @@
 import android.content.res.AssetManager;
 import android.os.AsyncTask;
 import android.os.Build;
+import android.os.Handler;
+import android.os.Looper;
 import android.os.Trace;
 import android.preference.PreferenceManager;
 import android.util.Log;
@@ -57,6 +59,8 @@
     private class ExtractTask extends AsyncTask<Void, Void, Void> {
         private static final int BUFFER_SIZE = 16 * 1024;
 
+        private final List<Runnable> mCompletionCallbacks = new ArrayList<Runnable>();
+
         public ExtractTask() {
         }
 
@@ -211,6 +215,23 @@
             return null;
         }
 
+        private void onPostExecuteImpl() {
+            for (int i = 0; i < mCompletionCallbacks.size(); i++) {
+                mCompletionCallbacks.get(i).run();
+            }
+            mCompletionCallbacks.clear();
+        }
+
+        @Override
+        protected void onPostExecute(Void result) {
+            beginTraceSection("ResourceExtractor.ExtractTask.onPostExecute");
+            try {
+                onPostExecuteImpl();
+            } finally {
+                endTraceSection();
+            }
+        }
+
         // Looks for a timestamp file on disk that indicates the version of the APK that
         // the resource paks were extracted from. Returns null if a timestamp was found
         // and it indicates that the resources match the current APK. Otherwise returns
@@ -335,6 +356,13 @@
         mContext = context.getApplicationContext();
     }
 
+    /**
+     * Synchronously wait for the resource extraction to be completed.
+     * <p>
+     * This method is bad and you should feel bad for using it.
+     *
+     * @see #addCompletionCallback(Runnable)
+     */
     public void waitForCompletion() {
         if (shouldSkipPakExtraction()) {
             return;
@@ -355,6 +383,35 @@
     }
 
     /**
+     * Adds a callback to be notified upon the completion of resource extraction.
+     * <p>
+     * If the resource task has already completed, the callback will be posted to the UI message
+     * queue.  Otherwise, it will be executed after all the resources have been extracted.
+     * <p>
+     * This must be called on the UI thread.  The callback will also always be executed on
+     * the UI thread.
+     *
+     * @param callback The callback to be enqueued.
+     */
+    public void addCompletionCallback(Runnable callback) {
+        ThreadUtils.assertOnUiThread();
+
+        Handler handler = new Handler(Looper.getMainLooper());
+        if (shouldSkipPakExtraction()) {
+            handler.post(callback);
+            return;
+        }
+
+        assert mExtractTask != null;
+        assert !mExtractTask.isCancelled();
+        if (mExtractTask.getStatus() == AsyncTask.Status.FINISHED) {
+            handler.post(callback);
+        } else {
+            mExtractTask.mCompletionCallbacks.add(callback);
+        }
+    }
+
+    /**
      * This will extract the application pak resources in an
      * AsyncTask. Call waitForCompletion() at the point resources
      * are needed to block until the task completes.
diff --git a/base/android/java/src/org/chromium/base/library_loader/Linker.java b/base/android/java/src/org/chromium/base/library_loader/Linker.java
index 23f953c2..bbf76cb 100644
--- a/base/android/java/src/org/chromium/base/library_loader/Linker.java
+++ b/base/android/java/src/org/chromium/base/library_loader/Linker.java
@@ -1039,7 +1039,7 @@
             mLoadSize = in.readLong();
             mRelroStart = in.readLong();
             mRelroSize = in.readLong();
-            ParcelFileDescriptor fd = in.readFileDescriptor();
+            ParcelFileDescriptor fd = ParcelFileDescriptor.CREATOR.createFromParcel(in);
             // If CreateSharedRelro fails, the OS file descriptor will be -1 and |fd| will be null.
             mRelroFd = (fd == null) ? -1 : fd.detachFd();
         }
diff --git a/base/android/jni_android.cc b/base/android/jni_android.cc
index e09c2d5d..a2de00a 100644
--- a/base/android/jni_android.cc
+++ b/base/android/jni_android.cc
@@ -17,6 +17,8 @@
 using base::android::MethodID;
 using base::android::ScopedJavaLocalRef;
 
+bool g_disable_manual_jni_registration = false;
+
 JavaVM* g_jvm = NULL;
 // Leak the global app context, as it is used from a non-joinable worker thread
 // that may still be running at shutdown. There is no harm in doing this.
@@ -77,6 +79,15 @@
 namespace base {
 namespace android {
 
+bool IsManualJniRegistrationDisabled() {
+  return g_disable_manual_jni_registration;
+}
+
+void DisableManualJniRegistration() {
+  DCHECK(!g_disable_manual_jni_registration);
+  g_disable_manual_jni_registration = true;
+}
+
 JNIEnv* AttachCurrentThread() {
   DCHECK(g_jvm);
   JNIEnv* env = NULL;
diff --git a/base/android/jni_android.h b/base/android/jni_android.h
index b5e55263..504eb857 100644
--- a/base/android/jni_android.h
+++ b/base/android/jni_android.h
@@ -21,6 +21,13 @@
 // Used to mark symbols to be exported in a shared library's symbol table.
 #define JNI_EXPORT __attribute__ ((visibility("default")))
 
+// Used to disable manual JNI registration in binaries that prefer to use native
+// JNI exports for startup performance. This is not compatible with the crazy
+// linker and so defaults to off. Call DisableManualJniRegistration at the very
+// beginning of JNI_OnLoad to use this.
+BASE_EXPORT bool IsManualJniRegistrationDisabled();
+BASE_EXPORT void DisableManualJniRegistration();
+
 // Contains the registration method information for initializing JNI bindings.
 struct RegistrationMethod {
   const char* name;
diff --git a/base/android/jni_generator/jni_generator.py b/base/android/jni_generator/jni_generator.py
index 6e39c13..54fea6ba 100755
--- a/base/android/jni_generator/jni_generator.py
+++ b/base/android/jni_generator/jni_generator.py
@@ -889,7 +889,7 @@
 
   def GetJNINativeMethodsString(self):
     """Returns the implementation of the array of native methods."""
-    if self.options.native_exports:
+    if self.options.native_exports and not self.options.native_exports_optional:
       return ''
     template = Template("""\
 static const JNINativeMethod kMethods${JAVA_CLASS}[] = {
@@ -922,7 +922,7 @@
     """Returns the code for RegisterNatives."""
     template = Template("""\
 ${REGISTER_NATIVES_SIGNATURE} {
-${CLASSES}
+${EARLY_EXIT}${CLASSES}
 ${NATIVES}
 ${CALLED_BY_NATIVES}
   return true;
@@ -934,9 +934,16 @@
     else:
       signature += ')'
 
+    early_exit = ''
+    if self.options.native_exports_optional:
+      early_exit = """\
+  if (base::android::IsManualJniRegistrationDisabled()) return true;
+"""
+
     natives = self.GetRegisterNativesImplString()
     called_by_natives = self.GetRegisterCalledByNativesImplString()
     values = {'REGISTER_NATIVES_SIGNATURE': signature,
+              'EARLY_EXIT': early_exit,
               'CLASSES': self.GetFindClasses(),
               'NATIVES': natives,
               'CALLED_BY_NATIVES': called_by_natives,
@@ -945,7 +952,7 @@
 
   def GetRegisterNativesImplString(self):
     """Returns the shared implementation for RegisterNatives."""
-    if self.options.native_exports:
+    if self.options.native_exports and not self.options.native_exports_optional:
       return ''
 
     template = Template("""\
@@ -1035,6 +1042,31 @@
         param.name
         for param in called_by_native.params])
 
+  def GetStubName(self, native):
+    """Return the name of the stub function for this native method.
+
+    Args:
+      native: the native dictionary describing the method.
+
+    Returns:
+      A string with the stub function name. For native exports mode this is the
+      Java_* symbol name required by the JVM; otherwise it is just the name of
+      the native method itself.
+    """
+    if self.options.native_exports:
+      template = Template("Java_${JAVA_NAME}_native${NAME}")
+
+      java_name = JniParams.RemapClassName(self.fully_qualified_class)
+      java_name = java_name.replace('_', '_1').replace('/', '_')
+      if native.java_class_name:
+        java_name += '_00024' + native.java_class_name
+
+      values = {'NAME': native.name,
+                'JAVA_NAME': java_name}
+      return template.substitute(values)
+    else:
+      return native.name
+
   def GetForwardDeclaration(self, native):
     template_str = """
 static ${RETURN} ${NAME}(JNIEnv* env, ${PARAMS});
@@ -1042,7 +1074,7 @@
     if self.options.native_exports:
       template_str += """
 __attribute__((visibility("default"), alias("${NAME}")))
-${RETURN} Java_${JAVA_NAME}_native${NAME}(JNIEnv* env, ${PARAMS});
+${RETURN} ${STUB_NAME}(JNIEnv* env, ${PARAMS});
 """
     template = Template(template_str)
     params_in_call = []
@@ -1050,16 +1082,11 @@
       params_in_call = ['env', 'jcaller']
     params_in_call = ', '.join(params_in_call + [p.name for p in native.params])
 
-    java_name = JniParams.RemapClassName(self.fully_qualified_class)
-    java_name = java_name.replace('_', '_1').replace('/', '_')
-    if native.java_class_name:
-      java_name += '_00024' + native.java_class_name
-
     values = {'RETURN': JavaDataTypeToC(native.return_type),
               'NAME': native.name,
-              'JAVA_NAME': java_name,
               'PARAMS': self.GetParamsInDeclaration(native),
-              'PARAMS_IN_CALL': params_in_call}
+              'PARAMS_IN_CALL': params_in_call,
+              'STUB_NAME': self.GetStubName(native)}
     return template.substitute(values)
 
   def GetNativeMethodStubString(self, native):
@@ -1067,11 +1094,11 @@
     if self.options.native_exports:
       template_str = """\
 __attribute__((visibility("default")))
-${RETURN} Java_${JAVA_NAME}_native${NAME}(JNIEnv* env,
+${RETURN} ${STUB_NAME}(JNIEnv* env,
     ${PARAMS_IN_DECLARATION}) {"""
     else:
       template_str = """\
-static ${RETURN} ${NAME}(JNIEnv* env, ${PARAMS_IN_DECLARATION}) {"""
+static ${RETURN} ${STUB_NAME}(JNIEnv* env, ${PARAMS_IN_DECLARATION}) {"""
     template_str += """
   ${P0_TYPE}* native = reinterpret_cast<${P0_TYPE}*>(${PARAM0_NAME});
   CHECK_NATIVE_PTR(env, jcaller, native, "${NAME}"${OPTIONAL_ERROR_RETURN});
@@ -1093,24 +1120,16 @@
     if re.match(RE_SCOPED_JNI_RETURN_TYPES, return_type):
       post_call = '.Release()'
 
-    if self.options.native_exports:
-      java_name = JniParams.RemapClassName(self.fully_qualified_class)
-      java_name = java_name.replace('_', '_1').replace('/', '_')
-      if native.java_class_name:
-        java_name += '_00024' + native.java_class_name
-    else:
-      java_name = ''
-
     values = {
         'RETURN': return_type,
         'OPTIONAL_ERROR_RETURN': optional_error_return,
-        'JAVA_NAME': java_name,
         'NAME': native.name,
         'PARAMS_IN_DECLARATION': self.GetParamsInDeclaration(native),
         'PARAM0_NAME': native.params[0].name,
         'P0_TYPE': native.p0_type,
         'PARAMS_IN_CALL': params_in_call,
-        'POST_CALL': post_call
+        'POST_CALL': post_call,
+        'STUB_NAME': self.GetStubName(native),
     }
     return template.substitute(values)
 
@@ -1225,12 +1244,13 @@
     return template.substitute(values)
 
   def GetKMethodArrayEntry(self, native):
-    template = Template("""\
-    { "native${NAME}", ${JNI_SIGNATURE}, reinterpret_cast<void*>(${NAME}) },""")
+    template = Template('    { "native${NAME}", ${JNI_SIGNATURE}, ' +
+                        'reinterpret_cast<void*>(${STUB_NAME}) },')
     values = {'NAME': native.name,
               'JNI_SIGNATURE': JniParams.Signature(native.params,
                                                    native.return_type,
-                                                   True)}
+                                                   True),
+              'STUB_NAME': self.GetStubName(native)}
     return template.substitute(values)
 
   def GetUniqueClasses(self, origin):
@@ -1500,7 +1520,12 @@
   option_parser.add_option('--native_exports', action='store_true',
                            help='Native method registration through .so '
                            'exports.')
+  option_parser.add_option('--native_exports_optional', action='store_true',
+                           help='Support both explicit and native method'
+                           'registration.')
   options, args = option_parser.parse_args(argv)
+  if options.native_exports_optional:
+    options.native_exports = True
   if options.jar_file:
     input_file = ExtractJarInputFile(options.jar_file, options.input_file,
                                      options.output_dir)
diff --git a/base/android/jni_generator/jni_generator_tests.py b/base/android/jni_generator/jni_generator_tests.py
index 7e39cda..e29bc0c 100755
--- a/base/android/jni_generator/jni_generator_tests.py
+++ b/base/android/jni_generator/jni_generator_tests.py
@@ -43,6 +43,7 @@
     self.cpp = 'cpp'
     self.javap = 'javap'
     self.native_exports = False
+    self.native_exports_optional = False
 
 class TestGenerator(unittest.TestCase):
   def assertObjEquals(self, first, second):
@@ -1019,7 +1020,7 @@
         test_data, 'org/chromium/example/jni_generator/Test', options)
     self.assertGoldenTextEquals(jni_from_java.GetContent())
 
-  def testNativeExportsOption(self):
+  def runNativeExportsOption(self, optional):
     test_data = """
     package org.chromium.example.jni_generator;
 
@@ -1054,9 +1055,18 @@
     options = TestOptions()
     options.jni_init_native_name = 'nativeInitNativeClass'
     options.native_exports = True
+    options.native_exports_optional = optional
     jni_from_java = jni_generator.JNIFromJavaSource(
         test_data, 'org/chromium/example/jni_generator/SampleForTests', options)
-    self.assertGoldenTextEquals(jni_from_java.GetContent())
+    return jni_from_java.GetContent()
+
+  def testNativeExportsOption(self):
+    content = self.runNativeExportsOption(False)
+    self.assertGoldenTextEquals(content)
+
+  def testNativeExportsOptionalOption(self):
+    content = self.runNativeExportsOption(True)
+    self.assertGoldenTextEquals(content)
 
   def testOuterInnerRaises(self):
     test_data = """
diff --git a/base/android/jni_generator/testNativeExportsOptionalOption.golden b/base/android/jni_generator/testNativeExportsOptionalOption.golden
new file mode 100644
index 0000000..2a3b172d
--- /dev/null
+++ b/base/android/jni_generator/testNativeExportsOptionalOption.golden
@@ -0,0 +1,283 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file is autogenerated by
+//     base/android/jni_generator/jni_generator.py
+// For
+//     org/chromium/example/jni_generator/SampleForTests
+
+#ifndef org_chromium_example_jni_generator_SampleForTests_JNI
+#define org_chromium_example_jni_generator_SampleForTests_JNI
+
+#include <jni.h>
+
+#include "base/android/jni_generator/jni_generator_helper.h"
+
+#include "base/android/jni_int_wrapper.h"
+
+// Step 1: forward declarations.
+namespace {
+const char kSampleForTestsClassPath[] =
+    "org/chromium/example/jni_generator/SampleForTests";
+// Leaking this jclass as we cannot use LazyInstance from some threads.
+base::subtle::AtomicWord g_SampleForTests_clazz __attribute__((unused)) = 0;
+#define SampleForTests_clazz(env) base::android::LazyGetClass(env, kSampleForTestsClassPath, &g_SampleForTests_clazz)
+
+}  // namespace
+
+extern "C" {
+
+static jint Init(JNIEnv* env, jobject jcaller);
+
+__attribute__((visibility("default"), alias("Init")))
+jint
+    Java_org_chromium_example_jni_1generator_SampleForTests_00024MyInnerClass_nativeInit(JNIEnv*
+    env, jobject jcaller);
+
+static jint Init(JNIEnv* env, jobject jcaller);
+
+__attribute__((visibility("default"), alias("Init")))
+jint
+    Java_org_chromium_example_jni_1generator_SampleForTests_00024MyOtherInnerClass_nativeInit(JNIEnv*
+    env, jobject jcaller);
+
+};  // extern "C"
+
+// Step 2: method stubs.
+
+extern "C" {
+__attribute__((visibility("default")))
+jint
+    Java_org_chromium_example_jni_1generator_SampleForTests_nativeStaticMethod(JNIEnv*
+    env,
+    jobject jcaller,
+    jlong nativeTest,
+    jint arg1) {
+  Test* native = reinterpret_cast<Test*>(nativeTest);
+  CHECK_NATIVE_PTR(env, jcaller, native, "StaticMethod", 0);
+  return native->StaticMethod(env, jcaller, arg1);
+}
+
+__attribute__((visibility("default")))
+jint
+    Java_org_chromium_example_jni_1generator_SampleForTests_nativeMethod(JNIEnv*
+    env,
+    jobject jcaller,
+    jlong nativeTest,
+    jint arg1) {
+  Test* native = reinterpret_cast<Test*>(nativeTest);
+  CHECK_NATIVE_PTR(env, jcaller, native, "Method", 0);
+  return native->Method(env, jcaller, arg1);
+}
+
+static base::subtle::AtomicWord g_SampleForTests_testMethodWithParam = 0;
+static void Java_SampleForTests_testMethodWithParam(JNIEnv* env, jobject obj,
+    JniIntWrapper iParam) {
+  /* Must call RegisterNativesImpl()  */
+  CHECK_CLAZZ(env, obj,
+      SampleForTests_clazz(env));
+  jmethodID method_id =
+      base::android::MethodID::LazyGet<
+      base::android::MethodID::TYPE_INSTANCE>(
+      env, SampleForTests_clazz(env),
+      "testMethodWithParam",
+
+"("
+"I"
+")"
+"V",
+      &g_SampleForTests_testMethodWithParam);
+
+     env->CallVoidMethod(obj,
+          method_id, as_jint(iParam));
+  jni_generator::CheckException(env);
+
+}
+
+static base::subtle::AtomicWord g_SampleForTests_testMethodWithParamAndReturn =
+    0;
+static base::android::ScopedJavaLocalRef<jstring>
+    Java_SampleForTests_testMethodWithParamAndReturn(JNIEnv* env, jobject obj,
+    JniIntWrapper iParam) {
+  /* Must call RegisterNativesImpl()  */
+  CHECK_CLAZZ(env, obj,
+      SampleForTests_clazz(env), NULL);
+  jmethodID method_id =
+      base::android::MethodID::LazyGet<
+      base::android::MethodID::TYPE_INSTANCE>(
+      env, SampleForTests_clazz(env),
+      "testMethodWithParamAndReturn",
+
+"("
+"I"
+")"
+"Ljava/lang/String;",
+      &g_SampleForTests_testMethodWithParamAndReturn);
+
+  jstring ret =
+      static_cast<jstring>(env->CallObjectMethod(obj,
+          method_id, as_jint(iParam)));
+  jni_generator::CheckException(env);
+  return base::android::ScopedJavaLocalRef<jstring>(env, ret);
+}
+
+static base::subtle::AtomicWord g_SampleForTests_testStaticMethodWithParam = 0;
+static jint Java_SampleForTests_testStaticMethodWithParam(JNIEnv* env,
+    JniIntWrapper iParam) {
+  /* Must call RegisterNativesImpl()  */
+  CHECK_CLAZZ(env, SampleForTests_clazz(env),
+      SampleForTests_clazz(env), 0);
+  jmethodID method_id =
+      base::android::MethodID::LazyGet<
+      base::android::MethodID::TYPE_STATIC>(
+      env, SampleForTests_clazz(env),
+      "testStaticMethodWithParam",
+
+"("
+"I"
+")"
+"I",
+      &g_SampleForTests_testStaticMethodWithParam);
+
+  jint ret =
+      env->CallStaticIntMethod(SampleForTests_clazz(env),
+          method_id, as_jint(iParam));
+  jni_generator::CheckException(env);
+  return ret;
+}
+
+static base::subtle::AtomicWord g_SampleForTests_testMethodWithNoParam = 0;
+static jdouble Java_SampleForTests_testMethodWithNoParam(JNIEnv* env) {
+  /* Must call RegisterNativesImpl()  */
+  CHECK_CLAZZ(env, SampleForTests_clazz(env),
+      SampleForTests_clazz(env), 0);
+  jmethodID method_id =
+      base::android::MethodID::LazyGet<
+      base::android::MethodID::TYPE_STATIC>(
+      env, SampleForTests_clazz(env),
+      "testMethodWithNoParam",
+
+"("
+")"
+"D",
+      &g_SampleForTests_testMethodWithNoParam);
+
+  jdouble ret =
+      env->CallStaticDoubleMethod(SampleForTests_clazz(env),
+          method_id);
+  jni_generator::CheckException(env);
+  return ret;
+}
+
+static base::subtle::AtomicWord g_SampleForTests_testStaticMethodWithNoParam =
+    0;
+static base::android::ScopedJavaLocalRef<jstring>
+    Java_SampleForTests_testStaticMethodWithNoParam(JNIEnv* env) {
+  /* Must call RegisterNativesImpl()  */
+  CHECK_CLAZZ(env, SampleForTests_clazz(env),
+      SampleForTests_clazz(env), NULL);
+  jmethodID method_id =
+      base::android::MethodID::LazyGet<
+      base::android::MethodID::TYPE_STATIC>(
+      env, SampleForTests_clazz(env),
+      "testStaticMethodWithNoParam",
+
+"("
+")"
+"Ljava/lang/String;",
+      &g_SampleForTests_testStaticMethodWithNoParam);
+
+  jstring ret =
+static_cast<jstring>(env->CallStaticObjectMethod(SampleForTests_clazz(env),
+          method_id));
+  jni_generator::CheckException(env);
+  return base::android::ScopedJavaLocalRef<jstring>(env, ret);
+}
+};  // extern "C"
+
+// Step 3: RegisterNatives.
+
+static const JNINativeMethod kMethodsMyOtherInnerClass[] = {
+    { "nativeInit",
+"("
+")"
+"I",
+    reinterpret_cast<void*>(Java_org_chromium_example_jni_1generator_SampleForTests_00024MyOtherInnerClass_nativeInit)
+    },
+};
+
+static const JNINativeMethod kMethodsMyInnerClass[] = {
+    { "nativeInit",
+"("
+")"
+"I",
+    reinterpret_cast<void*>(Java_org_chromium_example_jni_1generator_SampleForTests_00024MyInnerClass_nativeInit)
+    },
+};
+
+static const JNINativeMethod kMethodsSampleForTests[] = {
+    { "nativeStaticMethod",
+"("
+"J"
+"I"
+")"
+"I",
+    reinterpret_cast<void*>(Java_org_chromium_example_jni_1generator_SampleForTests_nativeStaticMethod)
+    },
+    { "nativeMethod",
+"("
+"J"
+"I"
+")"
+"I",
+    reinterpret_cast<void*>(Java_org_chromium_example_jni_1generator_SampleForTests_nativeMethod)
+    },
+};
+
+static bool RegisterNativesImpl(JNIEnv* env, jclass clazz) {
+  if (base::android::IsManualJniRegistrationDisabled()) return true;
+    base::subtle::Release_Store(&g_SampleForTests_clazz,
+      static_cast<base::subtle::AtomicWord>(env->NewWeakGlobalRef(clazz));
+
+  const int kMethodsMyOtherInnerClassSize =
+      arraysize(kMethodsMyOtherInnerClass);
+
+  if (env->RegisterNatives(MyOtherInnerClass_clazz(env),
+                           kMethodsMyOtherInnerClass,
+                           kMethodsMyOtherInnerClassSize) < 0) {
+    jni_generator::HandleRegistrationError(
+        env, MyOtherInnerClass_clazz(env), __FILE__);
+    return false;
+  }
+
+  const int kMethodsMyInnerClassSize = arraysize(kMethodsMyInnerClass);
+
+  if (env->RegisterNatives(MyInnerClass_clazz(env),
+                           kMethodsMyInnerClass,
+                           kMethodsMyInnerClassSize) < 0) {
+    jni_generator::HandleRegistrationError(
+        env, MyInnerClass_clazz(env), __FILE__);
+    return false;
+  }
+
+  const int kMethodsSampleForTestsSize = arraysize(kMethodsSampleForTests);
+
+  if (env->RegisterNatives(SampleForTests_clazz(env),
+                           kMethodsSampleForTests,
+                           kMethodsSampleForTestsSize) < 0) {
+    jni_generator::HandleRegistrationError(
+        env, SampleForTests_clazz(env), __FILE__);
+    return false;
+  }
+
+  return true;
+}
+
+extern "C" JNIEXPORT bool JNICALL
+Java_org_chromium_example_jni_1generator_SampleForTests_nativeInitNativeClass(JNIEnv*
+    env, jclass clazz) {
+  return RegisterNativesImpl(env, clazz);
+}
+
+#endif  // org_chromium_example_jni_generator_SampleForTests_JNI
diff --git a/base/android/jni_onload_delegate.h b/base/android/jni_onload_delegate.h
deleted file mode 100644
index ef1b137..0000000
--- a/base/android/jni_onload_delegate.h
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef BASE_ANDROID_JNI_ONLOAD_DELEGATE_H_
-#define BASE_ANDROID_JNI_ONLOAD_DELEGATE_H_
-
-#include <jni.h>
-
-#include "base/base_export.h"
-
-namespace base {
-namespace android {
-
-// This delegate class is used to implement component specific JNI registration
-// and initialization. All methods are called in JNI_OnLoad().
-//
-// Both RegisterJNI() and Init() methods are called if the shared library
-// is loaded by crazy linker that can't find JNI methods without JNI
-// registration, otherwise, only Init() is invoked where dynamic lookup is
-// used to find the JNI methods.
-//
-// It is important to make sure the JNI registration code is only in
-// RegisterJNI(), so it could be stripped out when JNI registration isn't
-// needed.
-class BASE_EXPORT JNIOnLoadDelegate {
- public:
-  virtual ~JNIOnLoadDelegate() {}
-
-  // Returns whether the JNI registration succeeded.
-  virtual bool RegisterJNI(JNIEnv* env) = 0;
-
-  // Returns whether the initialization succeeded. This method is called after
-  // RegisterJNI(), all JNI methods shall ready to be used.
-  virtual bool Init() = 0;
-};
-
-}  // namespace android
-}  // namespace base
-
-#endif  // BASE_ANDROID_JNI_ONLOAD_DELEGATE_H_
diff --git a/base/base.gyp b/base/base.gyp
index 8cd5d6ae..213e62d9 100644
--- a/base/base.gyp
+++ b/base/base.gyp
@@ -645,6 +645,7 @@
         'timer/timer_unittest.cc',
         'tools_sanity_unittest.cc',
         'trace_event/memory_dump_manager_unittest.cc',
+        'trace_event/process_memory_totals_dump_provider_unittest.cc',
         'trace_event/trace_event_argument_unittest.cc',
         'trace_event/trace_event_memory_unittest.cc',
         'trace_event/trace_event_synthetic_delay_unittest.cc',
diff --git a/base/base.gypi b/base/base.gypi
index 37a8171..b7c33b83 100644
--- a/base/base.gypi
+++ b/base/base.gypi
@@ -44,7 +44,6 @@
           'android/jni_android.h',
           'android/jni_array.cc',
           'android/jni_array.h',
-          'android/jni_onload_delegate.h',
           'android/jni_registrar.cc',
           'android/jni_registrar.h',
           'android/jni_string.cc',
@@ -672,6 +671,10 @@
           'trace_event/memory_dump_provider.h',
           'trace_event/process_memory_dump.cc',
           'trace_event/process_memory_dump.h',
+          'trace_event/process_memory_totals.cc',
+          'trace_event/process_memory_totals.h',
+          'trace_event/process_memory_totals_dump_provider.cc',
+          'trace_event/process_memory_totals_dump_provider.h',
           'trace_event/trace_event.h',
           'trace_event/trace_event_android.cc',
           'trace_event/trace_event_argument.cc',
diff --git a/base/i18n/break_iterator.cc b/base/i18n/break_iterator.cc
index e3aaa2b5..e2ed667 100644
--- a/base/i18n/break_iterator.cc
+++ b/base/i18n/break_iterator.cc
@@ -74,7 +74,8 @@
                       static_cast<int32_t>(string_.size()),
                       &status);
     if (U_FAILURE(status)) {
-      NOTREACHED() << "ubrk_open failed";
+      NOTREACHED() << "ubrk_open failed for type " << break_type
+          << " with error " << status;
     }
   }
 
diff --git a/base/i18n/time_formatting_unittest.cc b/base/i18n/time_formatting_unittest.cc
index 4739b62a..df0c1ed 100644
--- a/base/i18n/time_formatting_unittest.cc
+++ b/base/i18n/time_formatting_unittest.cc
@@ -158,11 +158,11 @@
 
   EXPECT_EQ(ASCIIToUTF16("30 Apr 2011"), TimeFormatShortDate(time));
   EXPECT_EQ(ASCIIToUTF16("30/04/2011"), TimeFormatShortDateNumeric(time));
-  EXPECT_EQ(ASCIIToUTF16("30/04/2011 15:42:07"),
+  EXPECT_EQ(ASCIIToUTF16("30/04/2011, 15:42:07"),
             TimeFormatShortDateAndTime(time));
-  EXPECT_EQ(ASCIIToUTF16("30/04/2011 15:42:07 ") + GetShortTimeZone(),
+  EXPECT_EQ(ASCIIToUTF16("30/04/2011, 15:42:07 ") + GetShortTimeZone(),
             TimeFormatShortDateAndTimeWithTimeZone(time));
-  EXPECT_EQ(ASCIIToUTF16("Saturday, 30 April 2011 15:42:07"),
+  EXPECT_EQ(ASCIIToUTF16("Saturday, 30 April 2011 at 15:42:07"),
             TimeFormatFriendlyDateAndTime(time));
   EXPECT_EQ(ASCIIToUTF16("Saturday, 30 April 2011"),
             TimeFormatFriendlyDate(time));
diff --git a/base/ios/device_util.mm b/base/ios/device_util.mm
index ff7be36..1234562 100644
--- a/base/ios/device_util.mm
+++ b/base/ios/device_util.mm
@@ -13,7 +13,6 @@
 #include <sys/socket.h>
 #include <sys/sysctl.h>
 
-#include "base/ios/ios_util.h"
 #include "base/logging.h"
 #include "base/mac/scoped_cftyperef.h"
 #include "base/memory/scoped_ptr.h"
diff --git a/base/ios/device_util_unittest.mm b/base/ios/device_util_unittest.mm
index 3494e00..82d42172 100644
--- a/base/ios/device_util_unittest.mm
+++ b/base/ios/device_util_unittest.mm
@@ -5,7 +5,6 @@
 #import <UIKit/UIKit.h>
 
 #include "base/ios/device_util.h"
-#include "base/ios/ios_util.h"
 #include "base/strings/sys_string_conversions.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/gtest_mac.h"
diff --git a/base/memory/discardable_memory.h b/base/memory/discardable_memory.h
index d8b7a58f..c49a99f 100644
--- a/base/memory/discardable_memory.h
+++ b/base/memory/discardable_memory.h
@@ -89,10 +89,6 @@
   // Create a DiscardableMemory instance with preferred type and |size|.
   static scoped_ptr<DiscardableMemory> CreateLockedMemory(size_t size);
 
-  // Discardable memory implementations might use this to release memory
-  // or resources assigned to instances that have been purged.
-  static void ReleaseFreeMemory();
-
   // Discardable memory implementations might allow an elevated usage level
   // while in frequent use. Call this to have the usage reduced to the base
   // level. Returns true if there's no need to call this again until
@@ -118,12 +114,6 @@
   // Returns the memory address held by this object. The object must be locked
   // before calling this. Otherwise, this will cause a DCHECK error.
   virtual void* Memory() const = 0;
-
-  // Testing utility calls.
-
-  // Purge all discardable memory in the system. This call has global effects
-  // across all running processes, so it should only be used for testing!
-  static void PurgeForTesting();
 };
 
 }  // namespace base
diff --git a/base/memory/discardable_memory_android.cc b/base/memory/discardable_memory_android.cc
index de71124..5dcdfdc 100644
--- a/base/memory/discardable_memory_android.cc
+++ b/base/memory/discardable_memory_android.cc
@@ -44,11 +44,6 @@
 }  // namespace
 
 // static
-void DiscardableMemory::ReleaseFreeMemory() {
-  internal::DiscardableMemoryShmem::ReleaseFreeMemory();
-}
-
-// static
 bool DiscardableMemory::ReduceMemoryUsage() {
   return internal::DiscardableMemoryEmulated::ReduceMemoryUsage();
 }
@@ -58,8 +53,8 @@
     std::vector<DiscardableMemoryType>* types) {
   const DiscardableMemoryType supported_types[] = {
     DISCARDABLE_MEMORY_TYPE_ASHMEM,
-    DISCARDABLE_MEMORY_TYPE_EMULATED,
-    DISCARDABLE_MEMORY_TYPE_SHMEM
+    DISCARDABLE_MEMORY_TYPE_SHMEM,
+    DISCARDABLE_MEMORY_TYPE_EMULATED
   };
   types->assign(supported_types, supported_types + arraysize(supported_types));
 }
@@ -104,11 +99,4 @@
   return nullptr;
 }
 
-// static
-void DiscardableMemory::PurgeForTesting() {
-  g_shared_state.Pointer()->manager.PurgeAll();
-  internal::DiscardableMemoryEmulated::PurgeForTesting();
-  internal::DiscardableMemoryShmem::PurgeForTesting();
-}
-
 }  // namespace base
diff --git a/base/memory/discardable_memory_ashmem.cc b/base/memory/discardable_memory_ashmem.cc
index df0697c..a590e531 100644
--- a/base/memory/discardable_memory_ashmem.cc
+++ b/base/memory/discardable_memory_ashmem.cc
@@ -71,9 +71,5 @@
   ashmem_chunk_.reset();
 }
 
-bool DiscardableMemoryAshmem::IsMemoryResident() const {
-  return true;
-}
-
 }  // namespace internal
 }  // namespace base
diff --git a/base/memory/discardable_memory_ashmem.h b/base/memory/discardable_memory_ashmem.h
index 100655be..1269cc291 100644
--- a/base/memory/discardable_memory_ashmem.h
+++ b/base/memory/discardable_memory_ashmem.h
@@ -38,7 +38,6 @@
   bool AllocateAndAcquireLock() override;
   void ReleaseLock() override;
   void Purge() override;
-  bool IsMemoryResident() const override;
 
  private:
   const size_t bytes_;
diff --git a/base/memory/discardable_memory_emulated.cc b/base/memory/discardable_memory_emulated.cc
index 4303400..b12d7ce0 100644
--- a/base/memory/discardable_memory_emulated.cc
+++ b/base/memory/discardable_memory_emulated.cc
@@ -68,11 +68,6 @@
   g_manager.Pointer()->ReduceMemoryUsageUntilWithinLimit(bytes);
 }
 
-// static
-void DiscardableMemoryEmulated::PurgeForTesting() {
-  g_manager.Pointer()->PurgeAll();
-}
-
 bool DiscardableMemoryEmulated::Initialize() {
   return Lock() != DISCARDABLE_MEMORY_LOCK_STATUS_FAILED;
 }
@@ -113,9 +108,5 @@
   memory_.reset();
 }
 
-bool DiscardableMemoryEmulated::IsMemoryResident() const {
-  return true;
-}
-
 }  // namespace internal
 }  // namespace base
diff --git a/base/memory/discardable_memory_emulated.h b/base/memory/discardable_memory_emulated.h
index 150c1ab5..aa01a9de 100644
--- a/base/memory/discardable_memory_emulated.h
+++ b/base/memory/discardable_memory_emulated.h
@@ -26,8 +26,6 @@
   // unavailable in kernel space. crbug.com/400423
   BASE_EXPORT static void ReduceMemoryUsageUntilWithinLimit(size_t bytes);
 
-  static void PurgeForTesting();
-
   bool Initialize();
 
   // Overridden from DiscardableMemory:
@@ -39,7 +37,6 @@
   bool AllocateAndAcquireLock() override;
   void ReleaseLock() override {}
   void Purge() override;
-  bool IsMemoryResident() const override;
 
  private:
   const size_t bytes_;
diff --git a/base/memory/discardable_memory_linux.cc b/base/memory/discardable_memory_linux.cc
index 9b4e940..670ad7ef 100644
--- a/base/memory/discardable_memory_linux.cc
+++ b/base/memory/discardable_memory_linux.cc
@@ -11,11 +11,6 @@
 namespace base {
 
 // static
-void DiscardableMemory::ReleaseFreeMemory() {
-  internal::DiscardableMemoryShmem::ReleaseFreeMemory();
-}
-
-// static
 bool DiscardableMemory::ReduceMemoryUsage() {
   return internal::DiscardableMemoryEmulated::ReduceMemoryUsage();
 }
@@ -24,8 +19,8 @@
 void DiscardableMemory::GetSupportedTypes(
     std::vector<DiscardableMemoryType>* types) {
   const DiscardableMemoryType supported_types[] = {
-    DISCARDABLE_MEMORY_TYPE_EMULATED,
-    DISCARDABLE_MEMORY_TYPE_SHMEM
+    DISCARDABLE_MEMORY_TYPE_SHMEM,
+    DISCARDABLE_MEMORY_TYPE_EMULATED
   };
   types->assign(supported_types, supported_types + arraysize(supported_types));
 }
@@ -61,10 +56,4 @@
   return nullptr;
 }
 
-// static
-void DiscardableMemory::PurgeForTesting() {
-  internal::DiscardableMemoryEmulated::PurgeForTesting();
-  internal::DiscardableMemoryShmem::PurgeForTesting();
-}
-
 }  // namespace base
diff --git a/base/memory/discardable_memory_mac.cc b/base/memory/discardable_memory_mac.cc
index 18cf80a..e0096e5 100644
--- a/base/memory/discardable_memory_mac.cc
+++ b/base/memory/discardable_memory_mac.cc
@@ -14,11 +14,6 @@
 namespace base {
 
 // static
-void DiscardableMemory::ReleaseFreeMemory() {
-  internal::DiscardableMemoryShmem::ReleaseFreeMemory();
-}
-
-// static
 bool DiscardableMemory::ReduceMemoryUsage() {
   return internal::DiscardableMemoryEmulated::ReduceMemoryUsage();
 }
@@ -28,8 +23,8 @@
     std::vector<DiscardableMemoryType>* types) {
   const DiscardableMemoryType supported_types[] = {
     DISCARDABLE_MEMORY_TYPE_MACH,
-    DISCARDABLE_MEMORY_TYPE_EMULATED,
-    DISCARDABLE_MEMORY_TYPE_SHMEM
+    DISCARDABLE_MEMORY_TYPE_SHMEM,
+    DISCARDABLE_MEMORY_TYPE_EMULATED
   };
   types->assign(supported_types, supported_types + arraysize(supported_types));
 }
@@ -72,11 +67,4 @@
   return nullptr;
 }
 
-// static
-void DiscardableMemory::PurgeForTesting() {
-  internal::DiscardableMemoryMach::PurgeForTesting();
-  internal::DiscardableMemoryEmulated::PurgeForTesting();
-  internal::DiscardableMemoryShmem::PurgeForTesting();
-}
-
 }  // namespace base
diff --git a/base/memory/discardable_memory_mach.cc b/base/memory/discardable_memory_mach.cc
index 5fc43f2a..d96de791 100644
--- a/base/memory/discardable_memory_mach.cc
+++ b/base/memory/discardable_memory_mach.cc
@@ -63,12 +63,6 @@
   g_manager.Pointer()->Unregister(this);
 }
 
-// static
-void DiscardableMemoryMach::PurgeForTesting() {
-  int state = 0;
-  vm_purgable_control(mach_task_self(), 0, VM_PURGABLE_PURGE_ALL, &state);
-}
-
 bool DiscardableMemoryMach::Initialize() {
   return Lock() != DISCARDABLE_MEMORY_LOCK_STATUS_FAILED;
 }
@@ -154,9 +148,5 @@
   memory_.reset();
 }
 
-bool DiscardableMemoryMach::IsMemoryResident() const {
-  return true;
-}
-
 }  // namespace internal
 }  // namespace base
diff --git a/base/memory/discardable_memory_mach.h b/base/memory/discardable_memory_mach.h
index af2191f3..b3b4b486 100644
--- a/base/memory/discardable_memory_mach.h
+++ b/base/memory/discardable_memory_mach.h
@@ -20,8 +20,6 @@
   explicit DiscardableMemoryMach(size_t bytes);
   ~DiscardableMemoryMach() override;
 
-  static void PurgeForTesting();
-
   bool Initialize();
 
   // Overridden from DiscardableMemory:
@@ -33,7 +31,6 @@
   bool AllocateAndAcquireLock() override;
   void ReleaseLock() override;
   void Purge() override;
-  bool IsMemoryResident() const override;
 
  private:
   mac::ScopedMachVM memory_;
diff --git a/base/memory/discardable_memory_manager.cc b/base/memory/discardable_memory_manager.cc
index 09e63e35..cbbdb47 100644
--- a/base/memory/discardable_memory_manager.cc
+++ b/base/memory/discardable_memory_manager.cc
@@ -51,34 +51,6 @@
   hard_memory_limit_expiration_time_ = hard_memory_limit_expiration_time;
 }
 
-void DiscardableMemoryManager::ReleaseFreeMemory() {
-  TRACE_EVENT0("base", "DiscardableMemoryManager::ReleaseFreeMemory");
-
-  AutoLock lock(lock_);
-  size_t bytes_allocated_before_releasing_memory = bytes_allocated_;
-  for (auto& entry : allocations_) {
-    Allocation* allocation = entry.first;
-    AllocationInfo* info = &entry.second;
-
-    if (!info->purgable)
-      continue;
-
-    // Skip if memory is still resident, otherwise purge and adjust
-    // |bytes_allocated_|.
-    if (allocation->IsMemoryResident())
-      continue;
-
-    size_t bytes_purgable = info->bytes;
-    DCHECK_LE(bytes_purgable, bytes_allocated_);
-    bytes_allocated_ -= bytes_purgable;
-    info->purgable = false;
-    allocation->Purge();
-  }
-
-  if (bytes_allocated_ != bytes_allocated_before_releasing_memory)
-    BytesAllocatedChanged(bytes_allocated_);
-}
-
 bool DiscardableMemoryManager::ReduceMemoryUsage() {
   return PurgeIfNotUsedSinceHardLimitCutoffUntilWithinSoftMemoryLimit();
 }
diff --git a/base/memory/discardable_memory_manager.h b/base/memory/discardable_memory_manager.h
index 8bf9289..43737f8 100644
--- a/base/memory/discardable_memory_manager.h
+++ b/base/memory/discardable_memory_manager.h
@@ -31,10 +31,6 @@
   // is acquired on the allocation.
   virtual void Purge() = 0;
 
-  // Check if allocated memory is still resident. It is illegal to call this
-  // while a lock is acquired on the allocation.
-  virtual bool IsMemoryResident() const = 0;
-
  protected:
   virtual ~DiscardableMemoryManagerAllocation() {}
 };
@@ -72,9 +68,6 @@
   void SetHardMemoryLimitExpirationTime(
       TimeDelta hard_memory_limit_expiration_time);
 
-  // This will make sure that all purged memory is released to the OS.
-  void ReleaseFreeMemory();
-
   // This will attempt to reduce memory footprint until within soft memory
   // limit. Returns true if there's no need to call this again until allocations
   // have been used.
diff --git a/base/memory/discardable_memory_manager_unittest.cc b/base/memory/discardable_memory_manager_unittest.cc
index 9f06e37..6717f09 100644
--- a/base/memory/discardable_memory_manager_unittest.cc
+++ b/base/memory/discardable_memory_manager_unittest.cc
@@ -33,10 +33,6 @@
     DCHECK(is_allocated_);
     is_allocated_ = false;
   }
-  bool IsMemoryResident() const override {
-    DCHECK(is_allocated_);
-    return true;
-  }
 
   bool is_locked() const { return is_locked_; }
 
diff --git a/base/memory/discardable_memory_shmem.cc b/base/memory/discardable_memory_shmem.cc
index 416fcf2..9056279 100644
--- a/base/memory/discardable_memory_shmem.cc
+++ b/base/memory/discardable_memory_shmem.cc
@@ -6,62 +6,17 @@
 
 #include "base/lazy_instance.h"
 #include "base/memory/discardable_memory_shmem_allocator.h"
-#include "base/memory/discardable_shared_memory.h"
 
 namespace base {
-namespace {
-
-// Have the DiscardableMemoryManager trigger in-process eviction
-// when address space usage gets too high (e.g. 512 MBytes).
-const size_t kMemoryLimit = 512 * 1024 * 1024;
-
-// internal::DiscardableMemoryManager has an explicit constructor that takes
-// a number of memory limit parameters. The LeakyLazyInstanceTraits doesn't
-// handle the case. Thus, we need our own class here.
-struct DiscardableMemoryManagerLazyInstanceTraits {
-  // Leaky as discardable memory clients can use this after the exit handler
-  // has been called.
-  static const bool kRegisterOnExit = false;
-#ifndef NDEBUG
-  static const bool kAllowedToAccessOnNonjoinableThread = true;
-#endif
-
-  static internal::DiscardableMemoryManager* New(void* instance) {
-    return new (instance) internal::DiscardableMemoryManager(
-        kMemoryLimit, kMemoryLimit, TimeDelta::Max());
-  }
-  static void Delete(internal::DiscardableMemoryManager* instance) {
-    instance->~DiscardableMemoryManager();
-  }
-};
-
-LazyInstance<internal::DiscardableMemoryManager,
-             DiscardableMemoryManagerLazyInstanceTraits> g_manager =
-    LAZY_INSTANCE_INITIALIZER;
-
-}  // namespace
-
 namespace internal {
 
 DiscardableMemoryShmem::DiscardableMemoryShmem(size_t bytes)
     : bytes_(bytes), is_locked_(false) {
-  g_manager.Pointer()->Register(this, bytes);
 }
 
 DiscardableMemoryShmem::~DiscardableMemoryShmem() {
   if (is_locked_)
     Unlock();
-  g_manager.Pointer()->Unregister(this);
-}
-
-// static
-void DiscardableMemoryShmem::ReleaseFreeMemory() {
-  g_manager.Pointer()->ReleaseFreeMemory();
-}
-
-// static
-void DiscardableMemoryShmem::PurgeForTesting() {
-  g_manager.Pointer()->PurgeAll();
 }
 
 bool DiscardableMemoryShmem::Initialize() {
@@ -71,18 +26,24 @@
 DiscardableMemoryLockStatus DiscardableMemoryShmem::Lock() {
   DCHECK(!is_locked_);
 
-  bool purged = false;
-  if (!g_manager.Pointer()->AcquireLock(this, &purged))
+  if (chunk_ && chunk_->Lock()) {
+    is_locked_ = true;
+    return DISCARDABLE_MEMORY_LOCK_STATUS_SUCCESS;
+  }
+
+  chunk_ = DiscardableMemoryShmemAllocator::GetInstance()
+               ->AllocateLockedDiscardableMemory(bytes_);
+  if (!chunk_)
     return DISCARDABLE_MEMORY_LOCK_STATUS_FAILED;
 
   is_locked_ = true;
-  return purged ? DISCARDABLE_MEMORY_LOCK_STATUS_PURGED
-                : DISCARDABLE_MEMORY_LOCK_STATUS_SUCCESS;
+  return DISCARDABLE_MEMORY_LOCK_STATUS_PURGED;
 }
 
 void DiscardableMemoryShmem::Unlock() {
   DCHECK(is_locked_);
-  g_manager.Pointer()->ReleaseLock(this);
+  DCHECK(chunk_);
+  chunk_->Unlock();
   is_locked_ = false;
 }
 
@@ -92,28 +53,5 @@
   return chunk_->Memory();
 }
 
-bool DiscardableMemoryShmem::AllocateAndAcquireLock() {
-  if (chunk_ && chunk_->Lock())
-    return true;
-
-  chunk_ = DiscardableMemoryShmemAllocator::GetInstance()
-               ->AllocateLockedDiscardableMemory(bytes_);
-  DCHECK(chunk_);
-  return false;
-}
-
-void DiscardableMemoryShmem::ReleaseLock() {
-  chunk_->Unlock();
-}
-
-void DiscardableMemoryShmem::Purge() {
-  DCHECK(!is_locked_);
-  chunk_.reset();
-}
-
-bool DiscardableMemoryShmem::IsMemoryResident() const {
-  return chunk_->IsMemoryResident();
-}
-
 }  // namespace internal
 }  // namespace base
diff --git a/base/memory/discardable_memory_shmem.h b/base/memory/discardable_memory_shmem.h
index b04c9b1..98d3b97 100644
--- a/base/memory/discardable_memory_shmem.h
+++ b/base/memory/discardable_memory_shmem.h
@@ -7,24 +7,16 @@
 
 #include "base/memory/discardable_memory.h"
 
-#include "base/memory/discardable_memory_manager.h"
-
 namespace base {
 class DiscardableMemoryShmemChunk;
 
 namespace internal {
 
-class DiscardableMemoryShmem
-    : public DiscardableMemory,
-      public internal::DiscardableMemoryManagerAllocation {
+class DiscardableMemoryShmem : public DiscardableMemory {
  public:
   explicit DiscardableMemoryShmem(size_t bytes);
   ~DiscardableMemoryShmem() override;
 
-  static void ReleaseFreeMemory();
-
-  static void PurgeForTesting();
-
   bool Initialize();
 
   // Overridden from DiscardableMemory:
@@ -32,12 +24,6 @@
   void Unlock() override;
   void* Memory() const override;
 
-  // Overridden from internal::DiscardableMemoryManagerAllocation:
-  bool AllocateAndAcquireLock() override;
-  void ReleaseLock() override;
-  void Purge() override;
-  bool IsMemoryResident() const override;
-
  private:
   const size_t bytes_;
   scoped_ptr<DiscardableMemoryShmemChunk> chunk_;
diff --git a/base/memory/discardable_memory_shmem_allocator.cc b/base/memory/discardable_memory_shmem_allocator.cc
index 8abe4569..761204f 100644
--- a/base/memory/discardable_memory_shmem_allocator.cc
+++ b/base/memory/discardable_memory_shmem_allocator.cc
@@ -18,16 +18,12 @@
       : shared_memory_(shared_memory.Pass()) {}
 
   // Overridden from DiscardableMemoryShmemChunk:
-  bool Lock() override {
-    auto result = shared_memory_->Lock(0, 0);
-    DCHECK_NE(result, DiscardableSharedMemory::PURGED);
-    return result == DiscardableSharedMemory::SUCCESS;
+  bool Lock() override { return false; }
+  void Unlock() override {
+    shared_memory_->Unlock(0, 0);
+    shared_memory_.reset();
   }
-  void Unlock() override { shared_memory_->Unlock(0, 0); }
   void* Memory() const override { return shared_memory_->memory(); }
-  bool IsMemoryResident() const override {
-    return shared_memory_->IsMemoryResident();
-  }
 
  private:
   scoped_ptr<DiscardableSharedMemory> shared_memory_;
diff --git a/base/memory/discardable_memory_shmem_allocator.h b/base/memory/discardable_memory_shmem_allocator.h
index ed40672..ac4118e 100644
--- a/base/memory/discardable_memory_shmem_allocator.h
+++ b/base/memory/discardable_memory_shmem_allocator.h
@@ -19,7 +19,6 @@
   virtual bool Lock() = 0;
   virtual void Unlock() = 0;
   virtual void* Memory() const = 0;
-  virtual bool IsMemoryResident() const = 0;
 };
 
 class BASE_EXPORT DiscardableMemoryShmemAllocator {
diff --git a/base/memory/discardable_memory_unittest.cc b/base/memory/discardable_memory_unittest.cc
index 45104131..fb1eba6 100644
--- a/base/memory/discardable_memory_unittest.cc
+++ b/base/memory/discardable_memory_unittest.cc
@@ -82,16 +82,6 @@
   ASSERT_TRUE(memory);
 }
 
-// Test forced purging.
-TEST_P(DiscardableMemoryTest, Purge) {
-  const scoped_ptr<DiscardableMemory> memory(CreateLockedMemory(kSize));
-  ASSERT_TRUE(memory);
-  memory->Unlock();
-
-  DiscardableMemory::PurgeForTesting();
-  EXPECT_EQ(DISCARDABLE_MEMORY_LOCK_STATUS_PURGED, memory->Lock());
-}
-
 #if !defined(NDEBUG) && !defined(OS_ANDROID)
 // Death tests are not supported with Android APKs.
 TEST_P(DiscardableMemoryTest, UnlockedMemoryAccessCrashesInDebugMode) {
diff --git a/base/memory/discardable_memory_win.cc b/base/memory/discardable_memory_win.cc
index 9b4e940..670ad7ef 100644
--- a/base/memory/discardable_memory_win.cc
+++ b/base/memory/discardable_memory_win.cc
@@ -11,11 +11,6 @@
 namespace base {
 
 // static
-void DiscardableMemory::ReleaseFreeMemory() {
-  internal::DiscardableMemoryShmem::ReleaseFreeMemory();
-}
-
-// static
 bool DiscardableMemory::ReduceMemoryUsage() {
   return internal::DiscardableMemoryEmulated::ReduceMemoryUsage();
 }
@@ -24,8 +19,8 @@
 void DiscardableMemory::GetSupportedTypes(
     std::vector<DiscardableMemoryType>* types) {
   const DiscardableMemoryType supported_types[] = {
-    DISCARDABLE_MEMORY_TYPE_EMULATED,
-    DISCARDABLE_MEMORY_TYPE_SHMEM
+    DISCARDABLE_MEMORY_TYPE_SHMEM,
+    DISCARDABLE_MEMORY_TYPE_EMULATED
   };
   types->assign(supported_types, supported_types + arraysize(supported_types));
 }
@@ -61,10 +56,4 @@
   return nullptr;
 }
 
-// static
-void DiscardableMemory::PurgeForTesting() {
-  internal::DiscardableMemoryEmulated::PurgeForTesting();
-  internal::DiscardableMemoryShmem::PurgeForTesting();
-}
-
 }  // namespace base
diff --git a/base/memory/scoped_ptr_unittest.cc b/base/memory/scoped_ptr_unittest.cc
index 0887a99..766f444 100644
--- a/base/memory/scoped_ptr_unittest.cc
+++ b/base/memory/scoped_ptr_unittest.cc
@@ -9,7 +9,6 @@
 #include "base/basictypes.h"
 #include "base/bind.h"
 #include "base/callback.h"
-#include "base/strings/stringprintf.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace {
@@ -686,8 +685,11 @@
 // value first.
 TEST(ScopedPtrTest, LoggingDoesntConvertToBoolean) {
   scoped_ptr<int> x(new int);
-  std::stringstream s;
-  s << x;
-  std::string expected = base::StringPrintf("%p", x.get());
-  EXPECT_EQ(expected, s.str());
+  std::stringstream s1;
+  s1 << x;
+
+  std::stringstream s2;
+  s2 << x.get();
+
+  EXPECT_EQ(s2.str(), s1.str());
 }
diff --git a/base/memory/singleton.h b/base/memory/singleton.h
index e5e2e3e..e50bdc0 100644
--- a/base/memory/singleton.h
+++ b/base/memory/singleton.h
@@ -23,7 +23,6 @@
 #include "base/atomicops.h"
 #include "base/base_export.h"
 #include "base/memory/aligned_memory.h"
-#include "base/third_party/dynamic_annotations/dynamic_annotations.h"
 #include "base/threading/thread_restrictions.h"
 
 namespace base {
@@ -237,8 +236,6 @@
     // instance_ pointer must acquire visibility over the singleton data.
     base::subtle::AtomicWord value = base::subtle::Acquire_Load(&instance_);
     if (value != 0 && value != base::internal::kBeingCreatedMarker) {
-      // See the corresponding HAPPENS_BEFORE below.
-      ANNOTATE_HAPPENS_AFTER(&instance_);
       return reinterpret_cast<Type*>(value);
     }
 
@@ -250,10 +247,6 @@
       // stop right after we do this store.
       Type* newval = Traits::New();
 
-      // This annotation helps race detectors recognize correct lock-less
-      // synchronization between different threads calling get().
-      // See the corresponding HAPPENS_AFTER below and above.
-      ANNOTATE_HAPPENS_BEFORE(&instance_);
       // Releases the visibility over instance_ to the readers.
       base::subtle::Release_Store(
           &instance_, reinterpret_cast<base::subtle::AtomicWord>(newval));
@@ -267,8 +260,6 @@
     // We hit a race. Wait for the other thread to complete it.
     value = base::internal::WaitForInstance(&instance_);
 
-    // See the corresponding HAPPENS_BEFORE above.
-    ANNOTATE_HAPPENS_AFTER(&instance_);
     return reinterpret_cast<Type*>(value);
   }
 
diff --git a/base/process/process_win.cc b/base/process/process_win.cc
index 4e600f94..8e5360b3 100644
--- a/base/process/process_win.cc
+++ b/base/process/process_win.cc
@@ -6,6 +6,7 @@
 
 #include "base/logging.h"
 #include "base/memory/scoped_ptr.h"
+#include "base/metrics/field_trial.h"
 #include "base/process/kill.h"
 #include "base/win/windows_version.h"
 
@@ -165,7 +166,21 @@
     priority = value ? PROCESS_MODE_BACKGROUND_BEGIN :
                        PROCESS_MODE_BACKGROUND_END;
   } else {
-    priority = value ? BELOW_NORMAL_PRIORITY_CLASS : NORMAL_PRIORITY_CLASS;
+    // Experiment (http://crbug.com/458594) with using IDLE_PRIORITY_CLASS as a
+    // background priority for background renderers (this code path is
+    // technically for more than just the renderers but they're the only use
+    // case in practice and experimenting here direclty is thus easier -- plus
+    // it doesn't really hurt as above we already state our intent of using
+    // PROCESS_MODE_BACKGROUND_BEGIN if available which is essentially
+    // IDLE_PRIORITY_CLASS plus lowered IO priority). Enabled by default in the
+    // asbence of field trials to get coverage on the perf waterfall.
+    DWORD background_priority = IDLE_PRIORITY_CLASS;
+    base::FieldTrial* trial =
+        base::FieldTrialList::Find("BackgroundRendererProcesses");
+    if (trial && trial->group_name() == "AllowBelowNormalFromBrowser")
+      background_priority = BELOW_NORMAL_PRIORITY_CLASS;
+
+    priority = value ? background_priority : NORMAL_PRIORITY_CLASS;
   }
 
   return (::SetPriorityClass(Handle(), priority) != 0);
diff --git a/base/test/android/javatests/src/org/chromium/base/test/BaseInstrumentationTestRunner.java b/base/test/android/javatests/src/org/chromium/base/test/BaseInstrumentationTestRunner.java
index b6c103d..a104831d 100644
--- a/base/test/android/javatests/src/org/chromium/base/test/BaseInstrumentationTestRunner.java
+++ b/base/test/android/javatests/src/org/chromium/base/test/BaseInstrumentationTestRunner.java
@@ -4,19 +4,20 @@
 
 package org.chromium.base.test;
 
-import android.content.Context;
 import android.os.Build;
 import android.os.Bundle;
 import android.test.AndroidTestRunner;
 import android.test.InstrumentationTestRunner;
 import android.util.Log;
 
-import junit.framework.Test;
 import junit.framework.TestCase;
-import junit.framework.TestSuite;
+import junit.framework.TestResult;
 
 import org.chromium.base.test.util.MinAndroidSdkLevel;
 
+import java.util.ArrayList;
+import java.util.List;
+
 /**
  *  An Instrumentation test runner that checks SDK level for tests with specific requirements.
  */
@@ -24,40 +25,94 @@
 
     private static final String TAG = "BaseInstrumentationTestRunner";
 
-    @Override
-    protected AndroidTestRunner getAndroidTestRunner() {
-        return new BaseAndroidTestRunner(getContext());
+    /**
+     * An interface for classes that check whether a test case should be skipped.
+     */
+    public interface SkipCheck {
+        /**
+         * Checks whether the given test case should be skipped.
+         *
+         * @param testCase The test case to check.
+         * @return Whether the test case should be skipped.
+         */
+        public boolean shouldSkip(TestCase testCase);
     }
 
     /**
-     *  Skips tests that don't meet the requirements of the current device.
+     * A test result that can skip tests.
      */
-    public class BaseAndroidTestRunner extends AndroidTestRunner {
-        private final Context mContext;
+    public class SkippingTestResult extends TestResult {
 
-        public BaseAndroidTestRunner(Context context) {
-            mContext = context;
+        private final List<SkipCheck> mSkipChecks;
+
+        /**
+         * Creates an instance of SkippingTestResult.
+         */
+        public SkippingTestResult() {
+            mSkipChecks = new ArrayList<SkipCheck>();
+        }
+
+        /**
+         * Adds a check for whether a test should run.
+         *
+         * @param skipCheck The check to add.
+         */
+        public void addSkipCheck(SkipCheck skipCheck) {
+            mSkipChecks.add(skipCheck);
+        }
+
+        private boolean shouldSkip(final TestCase test) {
+            for (SkipCheck s : mSkipChecks) {
+                if (s.shouldSkip(test)) return true;
+            }
+            return false;
         }
 
         @Override
-        public void setTest(Test test) {
-            super.setTest(test);
-            TestSuite revisedTestSuite = new TestSuite();
-            for (TestCase testCase : this.getTestCases()) {
-                Class<?> testClass = testCase.getClass();
-                if (shouldSkip(testClass, testCase)) {
-                    revisedTestSuite.addTest(new SkippedTest(testCase));
-                    Bundle skipResult = new Bundle();
-                    skipResult.putBoolean("test_skipped", true);
-                    sendStatus(0, skipResult);
-                } else {
-                    revisedTestSuite.addTest(testCase);
-                }
-            }
-            super.setTest(revisedTestSuite);
-        }
+        protected void run(final TestCase test) {
+            if (shouldSkip(test)) {
+                startTest(test);
 
-        protected boolean shouldSkip(Class<?> testClass, TestCase testCase) {
+                Bundle skipResult = new Bundle();
+                skipResult.putString("class", test.getClass().getName());
+                skipResult.putString("test", test.getName());
+                skipResult.putBoolean("test_skipped", true);
+                sendStatus(0, skipResult);
+
+                endTest(test);
+            } else {
+                super.run(test);
+            }
+        }
+    }
+
+    @Override
+    protected AndroidTestRunner getAndroidTestRunner() {
+        return new AndroidTestRunner() {
+            @Override
+            protected TestResult createTestResult() {
+                SkippingTestResult r = new SkippingTestResult();
+                r.addSkipCheck(new MinAndroidSdkLevelSkipCheck());
+                return r;
+            }
+        };
+    }
+
+    /**
+     * Checks the device's SDK level against any specified minimum requirement.
+     */
+    public static class MinAndroidSdkLevelSkipCheck implements SkipCheck {
+
+        /**
+         * If {@link org.chromium.base.test.util.MinAndroidSdkLevel} is present, checks its value
+         * against the device's SDK level.
+         *
+         * @param testCase The test to check.
+         * @return true if the device's SDK level is below the specified minimum.
+         */
+        @Override
+        public boolean shouldSkip(TestCase testCase) {
+            Class<?> testClass = testCase.getClass();
             if (testClass.isAnnotationPresent(MinAndroidSdkLevel.class)) {
                 MinAndroidSdkLevel v = testClass.getAnnotation(MinAndroidSdkLevel.class);
                 if (Build.VERSION.SDK_INT < v.value()) {
@@ -69,28 +124,6 @@
             }
             return false;
         }
-
-        protected Context getContext() {
-            return mContext;
-        }
     }
 
-    /**
-     *  Replaces a TestCase that should be skipped.
-     */
-    public static class SkippedTest extends TestCase {
-
-        public SkippedTest(TestCase skipped) {
-            super(skipped.getClass().getName() + "#" + skipped.getName());
-        }
-
-        @Override
-        protected void runTest() throws Throwable {
-        }
-
-        @Override
-        public String toString() {
-            return "SKIPPED " + super.toString();
-        }
-    }
 }
diff --git a/base/test/test_suite.cc b/base/test/test_suite.cc
index 903e93e..d40dd983 100644
--- a/base/test/test_suite.cc
+++ b/base/test/test_suite.cc
@@ -35,6 +35,14 @@
 #endif  // OS_IOS
 #endif  // OS_MACOSX
 
+#if !defined(OS_WIN)
+#include "base/i18n/rtl.h"
+#if !defined(OS_IOS)
+#include "base/strings/string_util.h"
+#include "third_party/icu/source/common/unicode/uloc.h"
+#endif
+#endif
+
 #if defined(OS_ANDROID)
 #include "base/test/test_support_android.h"
 #endif
@@ -321,6 +329,22 @@
   }
 
   base::i18n::InitializeICU();
+  // On the Mac OS X command line, the default locale is *_POSIX. In Chromium,
+  // the locale is set via an OS X locale API and is never *_POSIX.
+  // Some tests (such as those involving word break iterator) will behave
+  // differently and fail if we use *POSIX locale. Setting it to en_US here
+  // does not affect tests that explicitly overrides the locale for testing.
+  // This can be an issue on all platforms other than Windows.
+  // TODO(jshin): Should we set the locale via an OS X locale API here?
+#if !defined(OS_WIN)
+#if defined(OS_IOS)
+  base::i18n::SetICUDefaultLocale("en_US");
+#else
+  std::string default_locale(uloc_getDefault());
+  if (EndsWith(default_locale, "POSIX", false))
+    base::i18n::SetICUDefaultLocale("en_US");
+#endif
+#endif
 
   CatchMaybeTests();
   ResetCommandLine();
diff --git a/base/time/time.h b/base/time/time.h
index 18de085e..6d61861 100644
--- a/base/time/time.h
+++ b/base/time/time.h
@@ -178,6 +178,14 @@
     return delta_ / a.delta_;
   }
 
+  // Multiplicative computations with floats.
+  TimeDelta multiply_by(double a) const {
+    return TimeDelta(delta_ * a);
+  }
+  TimeDelta divide_by(double a) const {
+    return TimeDelta(delta_ / a);
+  }
+
   // Defined below because it depends on the definition of the other classes.
   Time operator+(Time t) const;
   TimeTicks operator+(TimeTicks t) const;
diff --git a/base/time/time_unittest.cc b/base/time/time_unittest.cc
index fdac59d..6387ec7 100644
--- a/base/time/time_unittest.cc
+++ b/base/time/time_unittest.cc
@@ -867,6 +867,15 @@
             TimeDelta::FromMicroseconds(min_int64_plus_two).magnitude());
 }
 
+
+TEST(TimeDelta, multiply_by) {
+  double d = 0.5;
+  EXPECT_EQ(TimeDelta::FromMilliseconds(500),
+            TimeDelta::FromMilliseconds(1000).multiply_by(d));
+  EXPECT_EQ(TimeDelta::FromMilliseconds(2000),
+            TimeDelta::FromMilliseconds(1000).divide_by(d));
+}
+
 TEST(TimeDeltaLogging, DCheckEqCompiles) {
   DCHECK_EQ(TimeDelta(), TimeDelta());
 }
diff --git a/base/trace_event/memory_dump_manager.cc b/base/trace_event/memory_dump_manager.cc
index bf631b3..c8be8f89 100644
--- a/base/trace_event/memory_dump_manager.cc
+++ b/base/trace_event/memory_dump_manager.cc
@@ -9,22 +9,15 @@
 #include "base/compiler_specific.h"
 #include "base/trace_event/memory_dump_provider.h"
 #include "base/trace_event/process_memory_dump.h"
-
-// TODO(primiano): in a separate CL rename DeleteTraceLogForTesting into
-// something like base::internal::TeardownSingletonForTesting so we don't have
-// to add a new friend to singleton each time.
-class DeleteTraceLogForTesting {
- public:
-  static void Delete() {
-    Singleton<
-        base::trace_event::MemoryDumpManager,
-        LeakySingletonTraits<base::trace_event::MemoryDumpManager>>::OnExit(0);
-  }
-};
+#include "base/trace_event/trace_event_argument.h"
 
 namespace base {
 namespace trace_event {
 
+namespace {
+MemoryDumpManager* g_instance_for_testing = nullptr;
+}
+
 // TODO(primiano): this should be smarter and should do something similar to
 // trace event synthetic delays.
 const char MemoryDumpManager::kTraceCategory[] =
@@ -32,13 +25,16 @@
 
 // static
 MemoryDumpManager* MemoryDumpManager::GetInstance() {
+  if (g_instance_for_testing)
+    return g_instance_for_testing;
+
   return Singleton<MemoryDumpManager,
                    LeakySingletonTraits<MemoryDumpManager>>::get();
 }
 
 // static
-void MemoryDumpManager::DeleteForTesting() {
-  DeleteTraceLogForTesting::Delete();
+void MemoryDumpManager::SetInstanceForTesting(MemoryDumpManager* instance) {
+  g_instance_for_testing = instance;
 }
 
 MemoryDumpManager::MemoryDumpManager() : memory_tracing_enabled_(0) {
@@ -96,15 +92,14 @@
 // Creates a dump point for the current process and appends it to the trace.
 void MemoryDumpManager::CreateLocalDumpPoint() {
   AutoLock lock(lock_);
-  // TRACE_EVENT_* macros don't induce scoped_refptr type inference, hence we
-  // need the base ConvertableToTraceFormat and the upcast below. The
-  // alternative would be unnecessarily expensive (double Acquire/Release).
-  scoped_refptr<ConvertableToTraceFormat> pmd(new ProcessMemoryDump());
+  scoped_ptr<ProcessMemoryDump> pmd(new ProcessMemoryDump());
 
   for (MemoryDumpProvider* dump_provider : dump_providers_enabled_) {
-    dump_provider->DumpInto(static_cast<ProcessMemoryDump*>(pmd.get()));
+    dump_provider->DumpInto(pmd.get());
   }
 
+  scoped_refptr<TracedValue> value(new TracedValue());
+  pmd->AsValueInto(value.get());
   // TODO(primiano): add the dump point to the trace at this point.
 }
 
diff --git a/base/trace_event/memory_dump_manager.h b/base/trace_event/memory_dump_manager.h
index fbc71d5..1a22e61 100644
--- a/base/trace_event/memory_dump_manager.h
+++ b/base/trace_event/memory_dump_manager.h
@@ -50,17 +50,17 @@
   void OnTraceLogDisabled() override;
 
  private:
+  friend struct DefaultDeleter<MemoryDumpManager>;  // For the testing instance.
   friend struct DefaultSingletonTraits<MemoryDumpManager>;
   friend class MemoryDumpManagerTest;
 
   static const char kTraceCategory[];
 
+  static void SetInstanceForTesting(MemoryDumpManager* instance);
+
   MemoryDumpManager();
   virtual ~MemoryDumpManager();
 
-  // Tears down the singleton instance.
-  static void DeleteForTesting();
-
   // Broadcasts the dump requests to the other processes.
   void BroadcastDumpRequest();
 
diff --git a/base/trace_event/memory_dump_manager_unittest.cc b/base/trace_event/memory_dump_manager_unittest.cc
index b5337e9..1ba73e6 100644
--- a/base/trace_event/memory_dump_manager_unittest.cc
+++ b/base/trace_event/memory_dump_manager_unittest.cc
@@ -17,14 +17,16 @@
 class MemoryDumpManagerTest : public testing::Test {
  public:
   void SetUp() override {
+    mdm_.reset(new MemoryDumpManager());
+    MemoryDumpManager::SetInstanceForTesting(mdm_.get());
+    ASSERT_EQ(mdm_, MemoryDumpManager::GetInstance());
     MemoryDumpManager::GetInstance()->Initialize();
-    mdm_ = MemoryDumpManager::GetInstance();
   }
 
   void TearDown() override {
-    MemoryDumpManager::DeleteForTesting();
+    MemoryDumpManager::SetInstanceForTesting(nullptr);
+    mdm_.reset();
     TraceLog::DeleteForTesting();
-    mdm_ = NULL;
   }
 
  protected:
@@ -37,7 +39,7 @@
 
   void DisableTracing() { TraceLog::GetInstance()->SetDisabled(); }
 
-  MemoryDumpManager* mdm_;
+  scoped_ptr<MemoryDumpManager> mdm_;
 
  private:
   // We want our singleton torn down after each test.
diff --git a/base/trace_event/process_memory_dump.cc b/base/trace_event/process_memory_dump.cc
index 0a3e096..6da9132 100644
--- a/base/trace_event/process_memory_dump.cc
+++ b/base/trace_event/process_memory_dump.cc
@@ -4,25 +4,25 @@
 
 #include "base/trace_event/process_memory_dump.h"
 
-#include "base/json/json_writer.h"
-#include "base/values.h"
+#include "base/trace_event/process_memory_totals.h"
+#include "base/trace_event/trace_event_argument.h"
 
 namespace base {
 namespace trace_event {
 
-ProcessMemoryDump::ProcessMemoryDump() {
+ProcessMemoryDump::ProcessMemoryDump() : has_process_totals_(false) {
 }
 
 ProcessMemoryDump::~ProcessMemoryDump() {
 }
 
-void ProcessMemoryDump::AppendAsTraceFormat(std::string* out) const {
-  // Build up the [dumper name] -> [serialized snapshot] JSON dictionary.
-  DictionaryValue dict;
-  std::string json_dict;
-  // TODO(primiano): this will append here the actual dumps from the dumpers.
-  base::JSONWriter::Write(&dict, &json_dict);
-  *out += json_dict;
+void ProcessMemoryDump::AsValueInto(TracedValue* value) const {
+  // Build up the [dumper name] -> [value] dictionary.
+  if (has_process_totals_) {
+    value->BeginDictionary("process_totals");
+    process_totals_.AsValueInto(value);
+    value->EndDictionary();
+  }
 }
 
 }  // namespace trace_event
diff --git a/base/trace_event/process_memory_dump.h b/base/trace_event/process_memory_dump.h
index ae42987..f70537b4 100644
--- a/base/trace_event/process_memory_dump.h
+++ b/base/trace_event/process_memory_dump.h
@@ -6,27 +6,34 @@
 #define BASE_TRACE_EVENT_PROCESS_MEMORY_DUMP_H_
 
 #include "base/base_export.h"
-#include "base/basictypes.h"
-#include "base/trace_event/trace_event_impl.h"
+#include "base/trace_event/process_memory_totals.h"
 
 namespace base {
 namespace trace_event {
 
-// A container which holds the dumps produced by the MemoryDumpProvider(s)
-// for a specific process. ProcessMemoryDump is as a strongly typed container
-// which enforces the data model for each memory dump point.
-// At trace generation time (i.e. when AppendAsTraceFormat is called) the
-// ProcessMemoryDump will compose a key-value dictionary of the various dumps
-// obtained during at trace dump point time.
-class BASE_EXPORT ProcessMemoryDump : public ConvertableToTraceFormat {
+class ConvertableToTraceFormat;
+
+// ProcessMemoryDump is as a strongly typed container which enforces the data
+// model for each memory dump point and holds the dumps produced by the
+// MemoryDumpProvider(s) for a specific process.
+// At trace generation time (i.e. when AsValue() is called), ProcessMemoryDump
+// will compose a key-value dictionary of the various dumps obtained at trace
+// dump point time.
+class BASE_EXPORT ProcessMemoryDump {
  public:
   ProcessMemoryDump();
+  ~ProcessMemoryDump();
 
-  // ConvertableToTraceFormat implementation.
-  void AppendAsTraceFormat(std::string* out) const override;
+  // Called at trace generation time to populate the TracedValue.
+  void AsValueInto(TracedValue* value) const;
+
+  ProcessMemoryTotals* process_totals() { return &process_totals_; }
+  bool has_process_totals() const { return has_process_totals_; }
+  void set_has_process_totals() { has_process_totals_ = true; }
 
  private:
-  ~ProcessMemoryDump() override;
+  ProcessMemoryTotals process_totals_;
+  bool has_process_totals_;
 
   DISALLOW_COPY_AND_ASSIGN(ProcessMemoryDump);
 };
diff --git a/base/trace_event/process_memory_totals.cc b/base/trace_event/process_memory_totals.cc
new file mode 100644
index 0000000..41ad788e
--- /dev/null
+++ b/base/trace_event/process_memory_totals.cc
@@ -0,0 +1,17 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/trace_event/process_memory_totals.h"
+
+#include "base/trace_event/trace_event_argument.h"
+
+namespace base {
+namespace trace_event {
+
+void ProcessMemoryTotals::AsValueInto(TracedValue* value) const {
+  value->SetDouble("resident_set_bytes", resident_set_bytes_);
+}
+
+}  // namespace trace_event
+}  // namespace base
diff --git a/base/trace_event/process_memory_totals.h b/base/trace_event/process_memory_totals.h
new file mode 100644
index 0000000..1c99152f
--- /dev/null
+++ b/base/trace_event/process_memory_totals.h
@@ -0,0 +1,36 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TRACE_EVENT_PROCESS_MEMORY_TOTALS_H_
+#define BASE_TRACE_EVENT_PROCESS_MEMORY_TOTALS_H_
+
+#include "base/base_export.h"
+#include "base/basictypes.h"
+
+namespace base {
+namespace trace_event {
+
+class TracedValue;
+
+// Dump provider which collects process-wide memory stats.
+class BASE_EXPORT ProcessMemoryTotals {
+ public:
+  ProcessMemoryTotals() {}
+
+  // Called at trace generation time to populate the TracedValue.
+  void AsValueInto(TracedValue* value) const;
+
+  uint64 resident_set_bytes() const { return resident_set_bytes_; }
+  void set_resident_set_bytes(uint64 value) { resident_set_bytes_ = value; }
+
+ private:
+  uint64 resident_set_bytes_;
+
+  DISALLOW_COPY_AND_ASSIGN(ProcessMemoryTotals);
+};
+
+}  // namespace trace_event
+}  // namespace base
+
+#endif  // BASE_TRACE_EVENT_PROCESS_MEMORY_TOTALS_H_
diff --git a/base/trace_event/process_memory_totals_dump_provider.cc b/base/trace_event/process_memory_totals_dump_provider.cc
new file mode 100644
index 0000000..cda0ff1
--- /dev/null
+++ b/base/trace_event/process_memory_totals_dump_provider.cc
@@ -0,0 +1,53 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/trace_event/process_memory_totals_dump_provider.h"
+
+#include "base/process/process_metrics.h"
+#include "base/trace_event/process_memory_dump.h"
+#include "base/trace_event/process_memory_totals.h"
+
+namespace base {
+namespace trace_event {
+
+// static
+uint64 ProcessMemoryTotalsDumpProvider::rss_bytes_for_testing = 0;
+
+namespace {
+ProcessMetrics* CreateProcessMetricsForCurrentProcess() {
+#if !defined(OS_MACOSX) || defined(OS_IOS)
+  return ProcessMetrics::CreateProcessMetrics(GetCurrentProcessHandle());
+#else
+  return ProcessMetrics::CreateProcessMetrics(GetCurrentProcessHandle(), NULL);
+#endif
+}
+}  // namespace
+
+// static
+ProcessMemoryTotalsDumpProvider*
+ProcessMemoryTotalsDumpProvider::GetInstance() {
+  return Singleton<
+      ProcessMemoryTotalsDumpProvider,
+      LeakySingletonTraits<ProcessMemoryTotalsDumpProvider>>::get();
+}
+
+ProcessMemoryTotalsDumpProvider::ProcessMemoryTotalsDumpProvider()
+    : process_metrics_(CreateProcessMetricsForCurrentProcess()) {
+}
+
+ProcessMemoryTotalsDumpProvider::~ProcessMemoryTotalsDumpProvider() {
+}
+
+// Called at trace dump point time. Creates a snapshot the memory counters for
+// the current process.
+void ProcessMemoryTotalsDumpProvider::DumpInto(ProcessMemoryDump* pmd) {
+  const uint64 rss_bytes = rss_bytes_for_testing
+                               ? rss_bytes_for_testing
+                               : process_metrics_->GetWorkingSetSize();
+  pmd->process_totals()->set_resident_set_bytes(rss_bytes);
+  pmd->set_has_process_totals();
+}
+
+}  // namespace trace_event
+}  // namespace base
diff --git a/base/trace_event/process_memory_totals_dump_provider.h b/base/trace_event/process_memory_totals_dump_provider.h
new file mode 100644
index 0000000..45917a8
--- /dev/null
+++ b/base/trace_event/process_memory_totals_dump_provider.h
@@ -0,0 +1,44 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TRACE_EVENT_PROCESS_MEMORY_TOTALS_DUMP_PROVIDER_H_
+#define BASE_TRACE_EVENT_PROCESS_MEMORY_TOTALS_DUMP_PROVIDER_H_
+
+#include "base/gtest_prod_util.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/singleton.h"
+#include "base/trace_event/memory_dump_provider.h"
+
+namespace base {
+
+class ProcessMetrics;
+
+namespace trace_event {
+
+// Dump provider which collects process-wide memory stats.
+class BASE_EXPORT ProcessMemoryTotalsDumpProvider : public MemoryDumpProvider {
+ public:
+  static ProcessMemoryTotalsDumpProvider* GetInstance();
+
+  // MemoryDumpProvider implementation.
+  void DumpInto(ProcessMemoryDump* pmd) override;
+
+ private:
+  friend struct DefaultSingletonTraits<ProcessMemoryTotalsDumpProvider>;
+  FRIEND_TEST_ALL_PREFIXES(ProcessMemoryTotalsDumpProviderTest, DumpRSS);
+
+  static uint64 rss_bytes_for_testing;
+
+  ProcessMemoryTotalsDumpProvider();
+  ~ProcessMemoryTotalsDumpProvider() override;
+
+  scoped_ptr<ProcessMetrics> process_metrics_;
+
+  DISALLOW_COPY_AND_ASSIGN(ProcessMemoryTotalsDumpProvider);
+};
+
+}  // namespace trace_event
+}  // namespace base
+
+#endif  // BASE_TRACE_EVENT_PROCESS_MEMORY_TOTALS_DUMP_PROVIDER_H_
diff --git a/base/trace_event/process_memory_totals_dump_provider_unittest.cc b/base/trace_event/process_memory_totals_dump_provider_unittest.cc
new file mode 100644
index 0000000..4a6003610
--- /dev/null
+++ b/base/trace_event/process_memory_totals_dump_provider_unittest.cc
@@ -0,0 +1,43 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/trace_event/process_memory_totals_dump_provider.h"
+
+#include "base/trace_event/process_memory_dump.h"
+#include "base/trace_event/process_memory_totals.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace trace_event {
+
+TEST(ProcessMemoryTotalsDumpProviderTest, DumpRSS) {
+  auto mdptp = ProcessMemoryTotalsDumpProvider::GetInstance();
+  scoped_ptr<ProcessMemoryDump> pmd_before(new ProcessMemoryDump());
+  scoped_ptr<ProcessMemoryDump> pmd_after(new ProcessMemoryDump());
+
+  ProcessMemoryTotalsDumpProvider::rss_bytes_for_testing = 1024;
+  mdptp->DumpInto(pmd_before.get());
+
+  // Pretend that the RSS of the process increased of +1M.
+  const size_t kAllocSize = 1048576;
+  ProcessMemoryTotalsDumpProvider::rss_bytes_for_testing += kAllocSize;
+
+  mdptp->DumpInto(pmd_after.get());
+
+  ProcessMemoryTotalsDumpProvider::rss_bytes_for_testing = 0;
+
+  ASSERT_TRUE(pmd_before->has_process_totals());
+  ASSERT_TRUE(pmd_after->has_process_totals());
+
+  const uint64 rss_before = pmd_before->process_totals()->resident_set_bytes();
+  const uint64 rss_after = pmd_after->process_totals()->resident_set_bytes();
+
+  EXPECT_NE(0U, rss_before);
+  EXPECT_NE(0U, rss_after);
+
+  EXPECT_EQ(rss_after - rss_before, kAllocSize);
+}
+
+}  // namespace trace_Event
+}  // namespace base
diff --git a/base/trace_event/trace_event_synthetic_delay.cc b/base/trace_event/trace_event_synthetic_delay.cc
index 4b957c3..bad79cc 100644
--- a/base/trace_event/trace_event_synthetic_delay.cc
+++ b/base/trace_event/trace_event_synthetic_delay.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "base/memory/singleton.h"
+#include "base/third_party/dynamic_annotations/dynamic_annotations.h"
 #include "base/trace_event/trace_event_synthetic_delay.h"
 
 namespace {
diff --git a/base/version.cc b/base/version.cc
index 6318b35..933356e 100644
--- a/base/version.cc
+++ b/base/version.cc
@@ -31,6 +31,8 @@
 
   for (std::vector<std::string>::const_iterator it = numbers.begin();
        it != numbers.end(); ++it) {
+    if (StartsWithASCII(*it, "+", false))
+      return false;
     int num;
     if (!StringToInt(*it, &num))
       return false;
@@ -42,8 +44,8 @@
     if (num > max)
       return false;
 
-    // This throws out things like +3, or 032.
-    if (IntToString(num) != *it)
+    // This throws out leading zeros for the first item only.
+    if (it == numbers.begin() && IntToString(num) != *it)
       return false;
 
     parsed->push_back(static_cast<uint16>(num));
diff --git a/base/version_unittest.cc b/base/version_unittest.cc
index 3119c39..46d8255d1 100644
--- a/base/version_unittest.cc
+++ b/base/version_unittest.cc
@@ -41,16 +41,22 @@
     {".", 0, false},
     {" . ", 0, false},
     {"0", 1, true},
+    {"0.", 0, false},
     {"0.0", 2, true},
     {"65537.0", 0, false},
     {"-1.0", 0, false},
     {"1.-1.0", 0, false},
+    {"1,--1.0", 0, false},
     {"+1.0", 0, false},
     {"1.+1.0", 0, false},
+    {"1+1.0", 0, false},
+    {"++1.0", 0, false},
     {"1.0a", 0, false},
     {"1.2.3.4.5.6.7.8.9.0", 10, true},
     {"02.1", 0, false},
+    {"0.01", 2, true},
     {"f.1", 0, false},
+    {"15.007.20011", 3, true},
   };
 
   for (size_t i = 0; i < arraysize(cases); ++i) {
@@ -77,6 +83,7 @@
     {"1.1", "1.0.1", 1},
     {"1.0.0", "1.0", 0},
     {"1.0.3", "1.0.20", -1},
+    {"11.0.10", "15.007.20011", -1},
   };
   for (size_t i = 0; i < arraysize(cases); ++i) {
     Version lhs(cases[i].lhs);
diff --git a/base/win/pe_image.cc b/base/win/pe_image.cc
index 572b4d9..e226b6a6 100644
--- a/base/win/pe_image.cc
+++ b/base/win/pe_image.cc
@@ -20,6 +20,9 @@
 
 namespace {
 
+  // PdbInfo Signature
+  const DWORD kPdbInfoSignature = 'SDSR';
+
   // Compare two strings byte by byte on an unsigned basis.
   //   if s1 == s2, return 0
   //   if s1 < s2, return negative
@@ -35,6 +38,12 @@
             *reinterpret_cast<const unsigned char*>(s2));
   }
 
+  struct PdbInfo {
+    DWORD Signature;
+    GUID Guid;
+    DWORD Age;
+    char PdbFileName[1];
+  };
 }  // namespace
 
 // Callback used to enumerate imports. See EnumImportChunksFunction.
@@ -142,6 +151,36 @@
   return ret;
 }
 
+bool PEImage::GetDebugId(LPGUID guid, LPDWORD age) const {
+  if (NULL == guid || NULL == age) {
+    return false;
+  }
+
+  DWORD debug_directory_size =
+      GetImageDirectoryEntrySize(IMAGE_DIRECTORY_ENTRY_DEBUG);
+  PIMAGE_DEBUG_DIRECTORY debug_directory =
+      reinterpret_cast<PIMAGE_DEBUG_DIRECTORY>(
+      GetImageDirectoryEntryAddr(IMAGE_DIRECTORY_ENTRY_DEBUG));
+
+  size_t directory_count =
+      debug_directory_size / sizeof(IMAGE_DEBUG_DIRECTORY);
+
+  for (size_t index = 0; index < directory_count; ++index) {
+    if (debug_directory[index].Type == IMAGE_DEBUG_TYPE_CODEVIEW) {
+      PdbInfo* pdb_info = reinterpret_cast<PdbInfo*>(
+          RVAToAddr(debug_directory[index].AddressOfRawData));
+      if (pdb_info->Signature != kPdbInfoSignature) {
+        // Unsupported PdbInfo signature
+        return false;
+      }
+      *guid = pdb_info->Guid;
+      *age = pdb_info->Age;
+      return true;
+    }
+  }
+  return false;
+}
+
 PDWORD PEImage::GetExportEntry(LPCSTR name) const {
   PIMAGE_EXPORT_DIRECTORY exports = GetExportDirectory();
 
diff --git a/base/win/pe_image.h b/base/win/pe_image.h
index dde1b487..5cef537 100644
--- a/base/win/pe_image.h
+++ b/base/win/pe_image.h
@@ -132,6 +132,9 @@
   // Returns the exports directory.
   PIMAGE_EXPORT_DIRECTORY GetExportDirectory() const;
 
+  // Returns the debug id (guid+age).
+  bool GetDebugId(LPGUID guid, LPDWORD age) const;
+
   // Returns a given export entry.
   // Use: e = image.GetExportEntry(f);
   // Pre: 'f' is either a zero terminated string or ordinal
diff --git a/base/win/pe_image_unittest.cc b/base/win/pe_image_unittest.cc
index 238c924..af4209b 100644
--- a/base/win/pe_image_unittest.cc
+++ b/base/win/pe_image_unittest.cc
@@ -267,5 +267,21 @@
   FreeLibrary(module);
 }
 
+// Test that we can get debug id out of a module.
+TEST(PEImageTest, GetDebugId) {
+  HMODULE module = LoadLibrary(L"advapi32.dll");
+  ASSERT_TRUE(NULL != module);
+
+  PEImage pe(module);
+  GUID guid = {0};
+  DWORD age = 0;
+  EXPECT_TRUE(pe.GetDebugId(&guid, &age));
+
+  GUID empty_guid = {0};
+  EXPECT_TRUE(!IsEqualGUID(empty_guid, guid));
+  EXPECT_NE(0U, age);
+  FreeLibrary(module);
+}
+
 }  // namespace win
 }  // namespace base
diff --git a/breakpad/BUILD.gn b/breakpad/BUILD.gn
index c928081..66835e2b 100644
--- a/breakpad/BUILD.gn
+++ b/breakpad/BUILD.gn
@@ -511,7 +511,7 @@
     configs += [ "//build/config/compiler:no_chromium_code" ]
     public_configs = [ ":client_config" ]
 
-    if (cpu_arch == "arm" && is_chromeos) {
+    if (current_cpu == "arm" && is_chromeos) {
       # Avoid running out of registers in
       # linux_syscall_support.h:sys_clone()'s inline assembly.
       cflags = [ "-marm" ]
@@ -630,7 +630,7 @@
 
     include_dirs = [ "src" ]
 
-    if (cpu_arch == "mipsel" && is_android) {
+    if (current_cpu == "mipsel" && is_android) {
       include_dirs += [ "src/common/android/include" ]
     }
   }
diff --git a/breakpad/breakpad.gyp b/breakpad/breakpad.gyp
index 1743c730..1046c74 100644
--- a/breakpad/breakpad.gyp
+++ b/breakpad/breakpad.gyp
@@ -91,6 +91,11 @@
             'src/third_party/libdisasm/x86_operand_list.c',
             'src/third_party/libdisasm/x86_operand_list.h',
           ],
+          'conditions': [
+            ['OS=="ios"', {
+              'toolsets': ['host'],
+            }],
+          ],
         },
         {
           # GN version: //breakpad:microdump_stackwalk
@@ -104,6 +109,11 @@
             'src/processor/microdump_processor.cc',
             'src/processor/microdump_stackwalk.cc',
           ],
+          'conditions': [
+            ['OS=="ios"', {
+              'toolsets': ['host'],
+            }],
+          ],
         },
         {
           # GN version: //breakpad:minidump_stackwalk
@@ -122,6 +132,11 @@
             'src/processor/minidump_processor.cc',
             'src/processor/minidump_stackwalk.cc',
           ],
+          'conditions': [
+            ['OS=="ios"', {
+              'toolsets': ['host'],
+            }],
+          ],
         },
         {
           # GN version: //breakpad:minidump_dump
@@ -141,6 +156,11 @@
             'src/processor/pathname_stripper.cc',
             'src/processor/pathname_stripper.h',
           ],
+          'conditions': [
+            ['OS=="ios"', {
+              'toolsets': ['host'],
+            }],
+          ],
         },
       ],
     }],
diff --git a/build/all.gyp b/build/all.gyp
index 93cac8c7..8ad63b36 100644
--- a/build/all.gyp
+++ b/build/all.gyp
@@ -519,7 +519,6 @@
           'target_name': 'chromium_builder_nacl_win_integration',
           'type': 'none',
           'dependencies': [
-            'chromium_builder_qa', # needed for pyauto
             'chromium_builder_tests',
           ],
         }, # target_name: chromium_builder_nacl_win_integration
@@ -636,42 +635,10 @@
           ],
         }, # target_name: chromium_gpu_debug_builder
         {
-          'target_name': 'chromium_builder_qa',
-          'type': 'none',
-          'dependencies': [
-            '../chrome/chrome.gyp:chrome',
-            # Dependencies of pyauto_functional tests.
-            '../remoting/remoting.gyp:remoting_webapp',
-          ],
-          'conditions': [
-            ['OS=="mac"', {
-              'dependencies': [
-                '../remoting/remoting.gyp:remoting_me2me_host_archive',
-              ],
-            }],
-            ['OS=="win"', {
-              'dependencies': [
-                '../chrome/chrome.gyp:crash_service',
-              ],
-            }],
-            ['OS=="win" and target_arch=="ia32"', {
-              'dependencies': [
-                '../chrome/chrome.gyp:crash_service_win64',
-              ],
-            }],
-            ['OS=="win" and component != "shared_library" and wix_exists == "True" and sas_dll_exists == "True"', {
-              'dependencies': [
-                '../remoting/remoting.gyp:remoting_host_installation',
-              ],
-            }],
-          ],
-        }, # target_name: chromium_builder_qa
-        {
           'target_name': 'chromium_builder_perf_av',
           'type': 'none',
           'dependencies': [
             'blink_tests', # to run layout tests
-            'chromium_builder_qa',  # needed for perf pyauto tests
           ],
         },  # target_name: chromium_builder_perf_av
         {
@@ -1093,10 +1060,6 @@
           ],
         },
         {
-          'target_name': 'chromium_builder_win_cf',
-          'type': 'none',
-        },
-        {
           'target_name': 'chromium_builder_dbg_tsan_win',
           'type': 'none',
           'dependencies': [
diff --git a/build/android/android_exports.gyp b/build/android/android_exports.gyp
index c259eee3..bf3424d7 100644
--- a/build/android/android_exports.gyp
+++ b/build/android/android_exports.gyp
@@ -2,13 +2,20 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+# This target is only used when android_webview_build==1 - it implements a
+# whitelist for exported symbols to minimise the binary size and prevent us
+# accidentally exposing things we don't mean to expose.
+
 {
+  'variables': {
+    'android_linker_script%': '<(SHARED_INTERMEDIATE_DIR)/android_webview_export_whitelist.lst',
+  },
   'targets': [
     {
       'target_name': 'android_exports',
       'type': 'none',
       'inputs': [
-        '<(DEPTH)/build/android/android_exports.lst',
+        '<(DEPTH)/build/android/android_webview_export_whitelist.lst',
       ],
       'outputs': [
         '<(android_linker_script)',
@@ -28,9 +35,6 @@
               # Only export symbols that are specified in version script.
               '-Wl,--version-script=<(android_linker_script)',
             ],
-            'ldflags!': [
-              '-Wl,--exclude-libs=ALL',
-            ],
           },
         }],
       ],
diff --git a/build/android/android_exports.lst b/build/android/android_exports.lst
deleted file mode 100644
index 6eee232..0000000
--- a/build/android/android_exports.lst
+++ /dev/null
@@ -1,15 +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.
-
-# Default exports specification for chromium shared libraries on android.
-# Check ld version script manual:
-# https://sourceware.org/binutils/docs-2.24/ld/VERSION.html#VERSION
-
-{
-  global:
-    Java_*_native*;
-    JNI_OnLoad;
-    __gcov_*;
-  local: *;
-};
diff --git a/build/android/android_no_jni_exports.lst b/build/android/android_no_jni_exports.lst
new file mode 100644
index 0000000..ffc6cf70
--- /dev/null
+++ b/build/android/android_no_jni_exports.lst
@@ -0,0 +1,17 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# This script makes all JNI exported symbols local, to prevent the JVM from
+# being able to find them, enforcing use of manual JNI function registration.
+# This is used for all Android binaries by default, unless they explicitly state
+# that they want JNI exported symbols to remain visible, as we need to ensure
+# the manual registration path is correct to maintain compatibility with the
+# crazy linker.
+# Check ld version script manual:
+# https://sourceware.org/binutils/docs-2.24/ld/VERSION.html#VERSION
+
+{
+  local:
+    Java_*;
+};
diff --git a/build/android/android_webview_export_whitelist.lst b/build/android/android_webview_export_whitelist.lst
new file mode 100644
index 0000000..2a56a75b
--- /dev/null
+++ b/build/android/android_webview_export_whitelist.lst
@@ -0,0 +1,16 @@
+# 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.
+
+# Exports specification for android_webview_build==1, which uses a whitelist to
+# enforce only specific symbols being exported.
+# Check ld version script manual:
+# https://sourceware.org/binutils/docs-2.24/ld/VERSION.html#VERSION
+
+{
+  global:
+    Java_*_native*;
+    JNI_OnLoad;
+    __gcov_*;
+  local: *;
+};
diff --git a/build/android/buildbot/bb_device_status_check.py b/build/android/buildbot/bb_device_status_check.py
index 6de27233..3bec1582 100755
--- a/build/android/buildbot/bb_device_status_check.py
+++ b/build/android/buildbot/bb_device_status_check.py
@@ -337,11 +337,12 @@
         zip(*[DeviceInfo(dev, options) for dev in devices]))
 
   # Write device info to file for buildbot info display.
-  with open('/home/chrome-bot/.adb_device_info', 'w') as f:
-    for device in json_data:
-      f.write('%s %s %s %.1fC %s%%\n' % (device['serial'], device['type'],
-          device['build'], float(device['battery']['temperature']) / 10,
-          device['battery']['level']))
+  if os.path.exists('/home/chrome-bot'):
+    with open('/home/chrome-bot/.adb_device_info', 'w') as f:
+      for device in json_data:
+        f.write('%s %s %s %.1fC %s%%\n' % (device['serial'], device['type'],
+            device['build'], float(device['battery']['temperature']) / 10,
+            device['battery']['level']))
 
   err_msg = CheckForMissingDevices(options, devices) or []
 
diff --git a/build/android/findbugs_filter/findbugs_known_bugs.txt b/build/android/findbugs_filter/findbugs_known_bugs.txt
index 09c12b2..9afba513 100644
--- a/build/android/findbugs_filter/findbugs_known_bugs.txt
+++ b/build/android/findbugs_filter/findbugs_known_bugs.txt
@@ -5,3 +5,4 @@
 M D UuF: Unused public or protected field: org.chromium.chrome.browser.document.PendingDocumentData.webContents  In PendingDocumentData.java
 M D UuF: Unused public or protected field: org.chromium.chrome.browser.document.PendingDocumentData.originalIntent  In PendingDocumentData.java
 M D UuF: Unused public or protected field: org.chromium.chrome.browser.document.PendingDocumentData.url  In PendingDocumentData.java
+M D UuF: Unused public or protected field: org.chromium.chrome.browser.document.PendingDocumentData.requestId  In PendingDocumentData.java
diff --git a/build/android/provision_devices.py b/build/android/provision_devices.py
index 65054fe..824e642 100755
--- a/build/android/provision_devices.py
+++ b/build/android/provision_devices.py
@@ -178,7 +178,7 @@
     if options.disable_network:
       device_settings.ConfigureContentSettings(
           device, device_settings.NETWORK_DISABLED_SETTINGS)
-    if options.wait_for_battery:
+    if options.min_battery_level is not None:
       try:
         battery_info = device.old_interface.GetBatteryInfo()
       except Exception as e:
@@ -241,24 +241,11 @@
   logging.getLogger().addHandler(custom_handler)
   logging.getLogger().setLevel(logging.INFO)
 
-  # TODO(perezju): This script used to rely on the builder name to determine
-  # the desired device configuration for perf bots. To safely phase this out,
-  # we now:
-  # - expose these configuration settings as command line options
-  # - set default values for these options based on the builder name, thus
-  #   matching the previous behaviour of the script on all bots.
-  # - explicitly adding these options on the perf bots will also maintain the
-  #   script behaviour, namely:
-  #     --wait-for-battery --disable-network --disable-java-debug
-  # - after all perf-bot recipes are updated, we can remove the following
-  #   builder-name-sniffing code and replace |is_perf| with |False|.
-  is_perf = 'perf' in os.environ.get('BUILDBOT_BUILDERNAME', '').lower()
-
   # Recommended options on perf bots:
   # --disable-network
   #     TODO(tonyg): We eventually want network on. However, currently radios
   #     can cause perfbots to drain faster than they charge.
-  # --wait-for-battery
+  # --min-battery-level 95
   #     Some perf bots run benchmarks with USB charging disabled which leads
   #     to gradual draining of the battery. We must wait for a full charge
   #     before starting a run in order to keep the devices online.
@@ -274,20 +261,15 @@
                       help='when wiping the device, max number of seconds to'
                       ' wait after each reboot '
                       '(default: %s)' % _DEFAULT_TIMEOUTS.HELP_TEXT)
-  parser.add_argument('--wait-for-battery', action='store_true',
-                      default=is_perf,
-                      help='wait for the battery on the devices to charge')
-  parser.add_argument('--min-battery-level', default=95, type=int,
-                      metavar='NUM',
-                      help='when waiting for battery, minimum battery level'
-                      ' required to continue (default: %(default)s)')
+  parser.add_argument('--min-battery-level', type=int, metavar='NUM',
+                      help='wait for the device to reach this minimum battery'
+                      ' level before trying to continue')
   parser.add_argument('--disable-location', action='store_true',
                       help='disable Google location services on devices')
   parser.add_argument('--disable-network', action='store_true',
-                      default=is_perf,
                       help='disable network access on devices')
   parser.add_argument('--disable-java-debug', action='store_false',
-                      dest='enable_java_debug', default=not is_perf,
+                      dest='enable_java_debug', default=True,
                       help='disable Java property asserts and JNI checking')
   parser.add_argument('-t', '--target', default='Debug',
                       help='the build target (default: %(default)s)')
diff --git a/build/android/pylib/base/base_test_result.py b/build/android/pylib/base/base_test_result.py
index 508b988..58200f6 100644
--- a/build/android/pylib/base/base_test_result.py
+++ b/build/android/pylib/base/base_test_result.py
@@ -77,6 +77,10 @@
     """Get the test duration."""
     return self._duration
 
+  def SetLog(self, log):
+    """Set the test log."""
+    self._log = log
+
   def GetLog(self):
     """Get the test log."""
     return self._log
diff --git a/build/android/pylib/device/adb_wrapper.py b/build/android/pylib/device/adb_wrapper.py
index f29f5c7..c954508 100644
--- a/build/android/pylib/device/adb_wrapper.py
+++ b/build/android/pylib/device/adb_wrapper.py
@@ -299,14 +299,14 @@
           cmd, 'path does not specify an accessible directory in the device',
           device_serial=self._device_serial)
 
-  def Logcat(self, clear=False, dump=False, filter_spec=None,
+  def Logcat(self, clear=False, dump=False, filter_specs=None,
              logcat_format=None, timeout=None, retries=_DEFAULT_RETRIES):
     """Get an iterable over the logcat output.
 
     Args:
       clear: If true, clear the logcat.
       dump: If true, dump the current logcat contents.
-      filter_spec: If set, spec to filter the logcat.
+      filter_specs: If set, a list of specs to filter the logcat.
       logcat_format: If set, the format in which the logcat should be output.
         Options include "brief", "process", "tag", "thread", "raw", "time",
         "threadtime", and "long"
@@ -328,14 +328,14 @@
       use_iter = False
     if logcat_format:
       cmd.extend(['-v', logcat_format])
-    if filter_spec is not None:
-      cmd.append(filter_spec)
+    if filter_specs:
+      cmd.extend(filter_specs)
 
     if use_iter:
       return self._IterRunDeviceAdbCmd(cmd, timeout)
     else:
       timeout = timeout if timeout is not None else _DEFAULT_TIMEOUT
-      return self._RunDeviceAdbCmd(cmd, timeout, retries)
+      return self._RunDeviceAdbCmd(cmd, timeout, retries).splitlines()
 
   def Forward(self, local, remote, timeout=_DEFAULT_TIMEOUT,
               retries=_DEFAULT_RETRIES):
diff --git a/build/android/pylib/device/device_utils.py b/build/android/pylib/device/device_utils.py
index eba5e020..fb882e7 100644
--- a/build/android/pylib/device/device_utils.py
+++ b/build/android/pylib/device/device_utils.py
@@ -8,6 +8,8 @@
 """
 # pylint: disable=unused-argument
 
+import collections
+import itertools
 import logging
 import multiprocessing
 import os
@@ -1306,14 +1308,54 @@
       retries: number of retries
 
     Returns:
-      A 2-tuple containing:
-        - A dict containing the overall memory usage statistics for the PID.
-        - A dict containing memory usage statistics broken down by mapping.
+      A dict containing memory usage statistics for the PID. May include:
+        Size, Rss, Pss, Shared_Clean, Shared_Dirty, Private_Clean,
+        Private_Dirty, VmHWM
 
     Raises:
       CommandTimeoutError on timeout.
     """
-    return self.old_interface.GetMemoryUsageForPid(pid)
+    result = collections.defaultdict(int)
+
+    try:
+      result.update(self._GetMemoryUsageForPidFromSmaps(pid))
+    except device_errors.CommandFailedError:
+      logging.exception('Error getting memory usage from smaps')
+
+    try:
+      result.update(self._GetMemoryUsageForPidFromStatus(pid))
+    except device_errors.CommandFailedError:
+      logging.exception('Error getting memory usage from status')
+
+    return result
+
+  def _GetMemoryUsageForPidFromSmaps(self, pid):
+    SMAPS_COLUMNS = (
+        'Size', 'Rss', 'Pss', 'Shared_Clean', 'Shared_Dirty', 'Private_Clean',
+        'Private_Dirty')
+
+    showmap_out = self.RunShellCommand(
+        ['showmap', str(pid)], as_root=True, check_return=True)
+    if not showmap_out:
+      raise device_errors.CommandFailedError('No output from showmap')
+
+    split_totals = showmap_out[-1].split()
+    if (not split_totals
+        or len(split_totals) != 9
+        or split_totals[-1] != 'TOTAL'):
+      raise device_errors.CommandFailedError(
+          'Invalid output from showmap: %s' % '\n'.join(showmap_out))
+
+    return dict(itertools.izip(SMAPS_COLUMNS, (int(n) for n in split_totals)))
+
+  def _GetMemoryUsageForPidFromStatus(self, pid):
+    for line in self.ReadFile(
+        '/proc/%s/status' % str(pid), as_root=True).splitlines():
+      if line.startswith('VmHWM:'):
+        return {'VmHWM': int(line.split()[1])}
+    else:
+      raise device_errors.CommandFailedError(
+          'Could not find memory peak value for pid %s', str(pid))
 
   @decorators.WithTimeoutAndRetriesFromInstance()
   def GetLogcatMonitor(self, timeout=None, retries=None, *args, **kwargs):
@@ -1386,6 +1428,8 @@
     """
     if not devices:
       devices = adb_wrapper.AdbWrapper.GetDevices()
+      if not devices:
+        raise device_errors.NoDevicesError()
     devices = [d if isinstance(d, cls) else cls(d) for d in devices]
     if async:
       return parallelizer.Parallelizer(devices)
diff --git a/build/android/pylib/device/device_utils_test.py b/build/android/pylib/device/device_utils_test.py
index 8a25f250..3f4ef597 100755
--- a/build/android/pylib/device/device_utils_test.py
+++ b/build/android/pylib/device/device_utils_test.py
@@ -39,7 +39,7 @@
 import mock # pylint: disable=F0401
 
 
-class DeviceUtilsTest(unittest.TestCase):
+class DeviceUtilsInitTest(unittest.TestCase):
 
   def testInitWithStr(self):
     serial_as_str = str('0123456789abcdef')
@@ -121,79 +121,6 @@
     self.mocked = mocked
 
 
-class DeviceUtilsOldImplTest(unittest.TestCase):
-
-  class AndroidCommandsCalls(object):
-
-    def __init__(self, test_case, cmd_ret, comp):
-      self._cmds = cmd_ret
-      self._comp = comp
-      self._run_command = _PatchedFunction()
-      self._test_case = test_case
-      self._total_received = 0
-
-    def __enter__(self):
-      self._run_command.patched = mock.patch(
-          'run_command.RunCommand',
-          side_effect=lambda c, **kw: self._ret(c))
-      self._run_command.mocked = self._run_command.patched.__enter__()
-
-    def _ret(self, actual_cmd):
-      if sys.exc_info()[0] is None:
-        on_failure_fmt = ('\n'
-                          '  received command: %s\n'
-                          '  expected command: %s')
-        self._test_case.assertGreater(
-            len(self._cmds), self._total_received,
-            msg=on_failure_fmt % (actual_cmd, None))
-        expected_cmd, ret = self._cmds[self._total_received]
-        self._total_received += 1
-        self._test_case.assertTrue(
-            self._comp(expected_cmd, actual_cmd),
-            msg=on_failure_fmt % (actual_cmd, expected_cmd))
-        return ret
-      return ''
-
-    def __exit__(self, exc_type, exc_val, exc_tb):
-      self._run_command.patched.__exit__(exc_type, exc_val, exc_tb)
-      if exc_type is None:
-        on_failure = "adb commands don't match.\nExpected:%s\nActual:%s" % (
-            ''.join('\n  %s' % c for c, _ in self._cmds),
-            ''.join('\n  %s' % a[0]
-                    for _, a, kw in self._run_command.mocked.mock_calls))
-        self._test_case.assertEqual(
-          len(self._cmds), len(self._run_command.mocked.mock_calls),
-          msg=on_failure)
-        for (expected_cmd, _r), (_n, actual_args, actual_kwargs) in zip(
-            self._cmds, self._run_command.mocked.mock_calls):
-          self._test_case.assertEqual(1, len(actual_args), msg=on_failure)
-          self._test_case.assertTrue(self._comp(expected_cmd, actual_args[0]),
-                                     msg=on_failure)
-          self._test_case.assertTrue('timeout_time' in actual_kwargs,
-                                     msg=on_failure)
-          self._test_case.assertTrue('retry_count' in actual_kwargs,
-                                     msg=on_failure)
-
-  def assertNoAdbCalls(self):
-    return type(self).AndroidCommandsCalls(self, [], str.__eq__)
-
-  def assertCalls(self, cmd, ret, comp=str.__eq__):
-    return type(self).AndroidCommandsCalls(self, [(cmd, ret)], comp)
-
-  def assertCallsSequence(self, cmd_ret, comp=str.__eq__):
-    return type(self).AndroidCommandsCalls(self, cmd_ret, comp)
-
-  def setUp(self):
-    self._get_adb_path_patch = mock.patch('pylib.constants.GetAdbPath',
-                                          mock.Mock(return_value='adb'))
-    self._get_adb_path_patch.start()
-    self.device = device_utils.DeviceUtils(
-        '0123456789abcdef', default_timeout=1, default_retries=0)
-
-  def tearDown(self):
-    self._get_adb_path_patch.stop()
-
-
 def _AdbWrapperMock(test_serial):
   adb = mock.Mock(spec=adb_wrapper.AdbWrapper)
   adb.__str__ = mock.Mock(return_value=test_serial)
@@ -201,7 +128,7 @@
   return adb
 
 
-class DeviceUtilsNewImplTest(mock_calls.TestCase):
+class DeviceUtilsTest(mock_calls.TestCase):
 
   def setUp(self):
     self.adb = _AdbWrapperMock('0123456789abcdef')
@@ -230,7 +157,7 @@
         msg, str(self.device)))
 
 
-class DeviceUtilsIsOnlineTest(DeviceUtilsNewImplTest):
+class DeviceUtilsIsOnlineTest(DeviceUtilsTest):
 
   def testIsOnline_true(self):
     with self.assertCall(self.call.adb.GetState(), 'device'):
@@ -245,7 +172,7 @@
       self.assertFalse(self.device.IsOnline())
 
 
-class DeviceUtilsHasRootTest(DeviceUtilsNewImplTest):
+class DeviceUtilsHasRootTest(DeviceUtilsTest):
 
   def testHasRoot_true(self):
     with self.assertCall(self.call.adb.Shell('ls /root'), 'foo\n'):
@@ -256,7 +183,7 @@
       self.assertFalse(self.device.HasRoot())
 
 
-class DeviceUtilsEnableRootTest(DeviceUtilsNewImplTest):
+class DeviceUtilsEnableRootTest(DeviceUtilsTest):
 
   def testEnableRoot_succeeds(self):
     with self.assertCalls(
@@ -279,7 +206,7 @@
         self.device.EnableRoot()
 
 
-class DeviceUtilsIsUserBuildTest(DeviceUtilsNewImplTest):
+class DeviceUtilsIsUserBuildTest(DeviceUtilsTest):
 
   def testIsUserBuild_yes(self):
     with self.assertCall(
@@ -292,7 +219,7 @@
       self.assertFalse(self.device.IsUserBuild())
 
 
-class DeviceUtilsGetExternalStoragePathTest(DeviceUtilsNewImplTest):
+class DeviceUtilsGetExternalStoragePathTest(DeviceUtilsTest):
 
   def testGetExternalStoragePath_succeeds(self):
     with self.assertCall(
@@ -306,7 +233,7 @@
         self.device.GetExternalStoragePath()
 
 
-class DeviceUtilsGetApplicationPathTest(DeviceUtilsNewImplTest):
+class DeviceUtilsGetApplicationPathTest(DeviceUtilsTest):
 
   def testGetApplicationPath_exists(self):
     with self.assertCalls(
@@ -333,7 +260,7 @@
 
 
 @mock.patch('time.sleep', mock.Mock())
-class DeviceUtilsWaitUntilFullyBootedTest(DeviceUtilsNewImplTest):
+class DeviceUtilsWaitUntilFullyBootedTest(DeviceUtilsTest):
 
   def testWaitUntilFullyBooted_succeedsNoWifi(self):
     with self.assertCalls(
@@ -443,7 +370,7 @@
 
 
 @mock.patch('time.sleep', mock.Mock())
-class DeviceUtilsRebootTest(DeviceUtilsNewImplTest):
+class DeviceUtilsRebootTest(DeviceUtilsTest):
 
   def testReboot_nonBlocking(self):
     with self.assertCalls(
@@ -469,7 +396,7 @@
       self.device.Reboot(block=True, wifi=True)
 
 
-class DeviceUtilsInstallTest(DeviceUtilsNewImplTest):
+class DeviceUtilsInstallTest(DeviceUtilsTest):
 
   def testInstall_noPriorInstall(self):
     with self.assertCalls(
@@ -526,7 +453,7 @@
         self.device.Install('/fake/test/app.apk', retries=0)
 
 
-class DeviceUtilsRunShellCommandTest(DeviceUtilsNewImplTest):
+class DeviceUtilsRunShellCommandTest(DeviceUtilsTest):
 
   def setUp(self):
     super(DeviceUtilsRunShellCommandTest, self).setUp()
@@ -656,7 +583,7 @@
                         self.device.RunShellCommand(cmd, check_return=False))
 
 
-class DeviceUtilsGetDevicePieWrapper(DeviceUtilsNewImplTest):
+class DeviceUtilsGetDevicePieWrapper(DeviceUtilsTest):
 
   def testGetDevicePieWrapper_jb(self):
     with self.assertCall(
@@ -675,7 +602,7 @@
 
 
 @mock.patch('time.sleep', mock.Mock())
-class DeviceUtilsKillAllTest(DeviceUtilsNewImplTest):
+class DeviceUtilsKillAllTest(DeviceUtilsTest):
 
   def testKillAll_noMatchingProcesses(self):
     with self.assertCall(self.call.adb.Shell('ps'),
@@ -726,7 +653,7 @@
           self.device.KillAll('some.process', signum=signal.SIGTERM))
 
 
-class DeviceUtilsStartActivityTest(DeviceUtilsNewImplTest):
+class DeviceUtilsStartActivityTest(DeviceUtilsTest):
 
   def testStartActivity_actionOnly(self):
     test_intent = intent.Intent(action='android.intent.action.VIEW')
@@ -890,7 +817,7 @@
       self.device.StartActivity(test_intent)
 
 
-class DeviceUtilsStartInstrumentationTest(DeviceUtilsNewImplTest):
+class DeviceUtilsStartInstrumentationTest(DeviceUtilsTest):
 
   def testStartInstrumentation_nothing(self):
     with self.assertCalls(
@@ -932,7 +859,7 @@
           finish=False, raw=False, extras={'foo': 'Foo', 'bar': 'Bar'})
 
 
-class DeviceUtilsBroadcastIntentTest(DeviceUtilsNewImplTest):
+class DeviceUtilsBroadcastIntentTest(DeviceUtilsTest):
 
   def testBroadcastIntent_noExtras(self):
     test_intent = intent.Intent(action='test.package.with.an.INTENT')
@@ -960,7 +887,7 @@
       self.device.BroadcastIntent(test_intent)
 
 
-class DeviceUtilsGoHomeTest(DeviceUtilsNewImplTest):
+class DeviceUtilsGoHomeTest(DeviceUtilsTest):
 
   def testGoHome(self):
     with self.assertCall(
@@ -970,7 +897,7 @@
       self.device.GoHome()
 
 
-class DeviceUtilsForceStopTest(DeviceUtilsNewImplTest):
+class DeviceUtilsForceStopTest(DeviceUtilsTest):
 
   def testForceStop(self):
     with self.assertCall(
@@ -979,7 +906,7 @@
       self.device.ForceStop('this.is.a.test.package')
 
 
-class DeviceUtilsClearApplicationStateTest(DeviceUtilsNewImplTest):
+class DeviceUtilsClearApplicationStateTest(DeviceUtilsTest):
 
   def testClearApplicationState_packageDoesntExist(self):
     with self.assertCalls(
@@ -1012,14 +939,14 @@
       self.device.ClearApplicationState('this.package.exists')
 
 
-class DeviceUtilsSendKeyEventTest(DeviceUtilsNewImplTest):
+class DeviceUtilsSendKeyEventTest(DeviceUtilsTest):
 
   def testSendKeyEvent(self):
     with self.assertCall(self.call.adb.Shell('input keyevent 66'), ''):
       self.device.SendKeyEvent(66)
 
 
-class DeviceUtilsPushChangedFilesIndividuallyTest(DeviceUtilsNewImplTest):
+class DeviceUtilsPushChangedFilesIndividuallyTest(DeviceUtilsTest):
 
   def testPushChangedFilesIndividually_empty(self):
     test_files = []
@@ -1041,7 +968,7 @@
       self.device._PushChangedFilesIndividually(test_files)
 
 
-class DeviceUtilsPushChangedFilesZippedTest(DeviceUtilsNewImplTest):
+class DeviceUtilsPushChangedFilesZippedTest(DeviceUtilsTest):
 
   def testPushChangedFilesZipped_empty(self):
     test_files = []
@@ -1080,7 +1007,7 @@
          ('/test/host/path/file2', '/test/device/path/file2')])
 
 
-class DeviceUtilsFileExistsTest(DeviceUtilsNewImplTest):
+class DeviceUtilsFileExistsTest(DeviceUtilsTest):
 
   def testFileExists_usingTest_fileExists(self):
     with self.assertCall(
@@ -1096,7 +1023,7 @@
       self.assertFalse(self.device.FileExists('/does/not/exist'))
 
 
-class DeviceUtilsPullFileTest(DeviceUtilsNewImplTest):
+class DeviceUtilsPullFileTest(DeviceUtilsTest):
 
   def testPullFile_existsOnDevice(self):
     with mock.patch('os.path.exists', return_value=True):
@@ -1117,7 +1044,7 @@
                                '/test/file/host/path')
 
 
-class DeviceUtilsReadFileTest(DeviceUtilsNewImplTest):
+class DeviceUtilsReadFileTest(DeviceUtilsTest):
 
   def testReadFile_exists(self):
     with self.assertCall(
@@ -1145,7 +1072,7 @@
                                as_root=True))
 
 
-class DeviceUtilsWriteFileTest(DeviceUtilsNewImplTest):
+class DeviceUtilsWriteFileTest(DeviceUtilsTest):
 
   def testWriteFileWithPush_success(self):
     tmp_host = MockTempFile('/tmp/file/on.host')
@@ -1208,7 +1135,7 @@
       self.device.WriteFile('/test/file', 'contents', as_root=True)
 
 
-class DeviceUtilsLsTest(DeviceUtilsNewImplTest):
+class DeviceUtilsLsTest(DeviceUtilsTest):
 
   def testLs_directory(self):
     result = [('.', adb_wrapper.DeviceStat(16889, 4096, 1417436123)),
@@ -1226,7 +1153,7 @@
                         self.device.Ls('/data/local/tmp/testfile.txt'))
 
 
-class DeviceUtilsStatTest(DeviceUtilsNewImplTest):
+class DeviceUtilsStatTest(DeviceUtilsTest):
 
   def testStat_file(self):
     result = [('.', adb_wrapper.DeviceStat(16889, 4096, 1417436123)),
@@ -1256,7 +1183,7 @@
         self.device.Stat('/data/local/tmp/does.not.exist.txt')
 
 
-class DeviceUtilsSetJavaAssertsTest(DeviceUtilsNewImplTest):
+class DeviceUtilsSetJavaAssertsTest(DeviceUtilsTest):
 
   def testSetJavaAsserts_enable(self):
     with self.assertCalls(
@@ -1296,7 +1223,7 @@
       self.assertFalse(self.device.SetJavaAsserts(True))
 
 
-class DeviceUtilsGetPropTest(DeviceUtilsNewImplTest):
+class DeviceUtilsGetPropTest(DeviceUtilsTest):
 
   def testGetProp_exists(self):
     with self.assertCall(
@@ -1330,7 +1257,7 @@
                                            cache=True, retries=3))
 
 
-class DeviceUtilsSetPropTest(DeviceUtilsNewImplTest):
+class DeviceUtilsSetPropTest(DeviceUtilsTest):
 
   def testSetProp(self):
     with self.assertCall(
@@ -1351,7 +1278,7 @@
         self.device.SetProp('test.property', 'new_value', check=True)
 
 
-class DeviceUtilsGetPidsTest(DeviceUtilsNewImplTest):
+class DeviceUtilsGetPidsTest(DeviceUtilsTest):
 
   def testGetPids_noMatches(self):
     with self.assertCall(self.call.adb.Shell('ps'),
@@ -1387,7 +1314,7 @@
           self.device.GetPids('exact.match'))
 
 
-class DeviceUtilsTakeScreenshotTest(DeviceUtilsNewImplTest):
+class DeviceUtilsTakeScreenshotTest(DeviceUtilsTest):
 
   def testTakeScreenshot_fileNameProvided(self):
     with self.assertCalls(
@@ -1401,22 +1328,18 @@
       self.device.TakeScreenshot('/test/host/screenshot.png')
 
 
-class DeviceUtilsGetMemoryUsageForPidTest(DeviceUtilsOldImplTest):
+class DeviceUtilsGetMemoryUsageForPidTest(DeviceUtilsTest):
 
   def setUp(self):
     super(DeviceUtilsGetMemoryUsageForPidTest, self).setUp()
-    self.device.old_interface._privileged_command_runner = (
-        self.device.old_interface.RunShellCommand)
-    self.device.old_interface._protected_file_access_method_initialized = True
 
   def testGetMemoryUsageForPid_validPid(self):
-    with self.assertCallsSequence([
-        ("adb -s 0123456789abcdef shell 'showmap 1234'",
-         '100 101 102 103 104 105 106 107 TOTAL\r\n'),
-        ("adb -s 0123456789abcdef shell "
-            "'cat \"/proc/1234/status\" 2> /dev/null'",
-         'VmHWM: 1024 kB')
-        ]):
+    with self.assertCalls(
+        (self.call.device.RunShellCommand(
+            ['showmap', '1234'], as_root=True, check_return=True),
+         ['100 101 102 103 104 105 106 107 TOTAL']),
+        (self.call.device.ReadFile('/proc/1234/status', as_root=True),
+         'VmHWM: 1024 kB\n')):
       self.assertEqual(
           {
             'Size': 100,
@@ -1430,14 +1353,36 @@
           },
           self.device.GetMemoryUsageForPid(1234))
 
-  def testGetMemoryUsageForPid_invalidPid(self):
+  def testGetMemoryUsageForPid_noSmaps(self):
     with self.assertCalls(
-        "adb -s 0123456789abcdef shell 'showmap 4321'",
-        'cannot open /proc/4321/smaps: No such file or directory\r\n'):
-      self.assertEqual({}, self.device.GetMemoryUsageForPid(4321))
+        (self.call.device.RunShellCommand(
+            ['showmap', '4321'], as_root=True, check_return=True),
+         ['cannot open /proc/4321/smaps: No such file or directory']),
+        (self.call.device.ReadFile('/proc/4321/status', as_root=True),
+         'VmHWM: 1024 kb\n')):
+      self.assertEquals({'VmHWM': 1024}, self.device.GetMemoryUsageForPid(4321))
+
+  def testGetMemoryUsageForPid_noStatus(self):
+    with self.assertCalls(
+        (self.call.device.RunShellCommand(
+            ['showmap', '4321'], as_root=True, check_return=True),
+         ['100 101 102 103 104 105 106 107 TOTAL']),
+        (self.call.device.ReadFile('/proc/4321/status', as_root=True),
+         self.CommandError())):
+      self.assertEquals(
+          {
+            'Size': 100,
+            'Rss': 101,
+            'Pss': 102,
+            'Shared_Clean': 103,
+            'Shared_Dirty': 104,
+            'Private_Clean': 105,
+            'Private_Dirty': 106,
+          },
+          self.device.GetMemoryUsageForPid(4321))
 
 
-class DeviceUtilsStrTest(DeviceUtilsNewImplTest):
+class DeviceUtilsStrTest(DeviceUtilsTest):
 
   def testStr_returnsSerial(self):
     with self.assertCalls(
@@ -1459,6 +1404,12 @@
           and serial == str(device),
           'Expected a DeviceUtils object with serial %s' % serial)
 
+  def testParallel_noDevices(self):
+    with self.assertCall(
+        mock.call.pylib.device.adb_wrapper.AdbWrapper.GetDevices(), []):
+      with self.assertRaises(device_errors.NoDevicesError):
+        device_utils.DeviceUtils.parallel()
+
 
 if __name__ == '__main__':
   logging.getLogger().setLevel(logging.DEBUG)
diff --git a/build/android/pylib/device/logcat_monitor.py b/build/android/pylib/device/logcat_monitor.py
index 7ede49c5..1d4cc24fa 100644
--- a/build/android/pylib/device/logcat_monitor.py
+++ b/build/android/pylib/device/logcat_monitor.py
@@ -22,18 +22,20 @@
   # Format: <DATE> <TIME> <PID> <TID> <LEVEL> <COMPONENT>: <MESSAGE>
   _THREADTIME_RE_FORMAT = r'\S* +\S* +(%s) +(%s) +(%s) +(%s): +(%s)$'
 
-  def __init__(self, adb, clear=True):
+  def __init__(self, adb, clear=True, filter_specs=None):
     """Create a LogcatMonitor instance.
 
     Args:
       adb: An instance of adb_wrapper.AdbWrapper.
       clear: If True, clear the logcat when monitoring starts.
+      filter_specs: An optional list of '<tag>[:priority]' strings.
     """
     if isinstance(adb, adb_wrapper.AdbWrapper):
       self._adb = adb
     else:
       raise ValueError('Unsupported type passed for argument "device"')
     self._clear = clear
+    self._filter_specs = filter_specs
     self._logcat_out = None
     self._logcat_out_file = None
     self._logcat_proc = None
@@ -76,7 +78,7 @@
     #    returned.
     #  - failure_regex matches a line, in which case None is returned
     #  - the timeout is hit, in which case a CommandTimeoutError is raised.
-    for l in self._adb.Logcat():
+    for l in self._adb.Logcat(filter_specs=self._filter_specs):
       m = success_regex.search(l)
       if m:
         return m
diff --git a/build/android/pylib/gtest/setup.py b/build/android/pylib/gtest/setup.py
index 72c4b05..1b882ca 100644
--- a/build/android/pylib/gtest/setup.py
+++ b/build/android/pylib/gtest/setup.py
@@ -38,6 +38,7 @@
     'media_unittests': 'media/media_unittests.isolate',
     'net_unittests': 'net/net_unittests.isolate',
     'sql_unittests': 'sql/sql_unittests.isolate',
+    'sync_unit_tests': 'sync/sync_unit_tests.isolate',
     'ui_base_unittests': 'ui/base/ui_base_tests.isolate',
     'unit_tests': 'chrome/unit_tests.isolate',
     'webkit_unit_tests':
diff --git a/build/android/pylib/instrumentation/instrumentation_test_instance.py b/build/android/pylib/instrumentation/instrumentation_test_instance.py
index 45e6ee4..3f56e6d 100644
--- a/build/android/pylib/instrumentation/instrumentation_test_instance.py
+++ b/build/android/pylib/instrumentation/instrumentation_test_instance.py
@@ -23,6 +23,10 @@
     os.path.join(constants.DIR_SOURCE_ROOT, 'build', 'util', 'lib', 'common'))
 import unittest_util
 
+# Ref: http://developer.android.com/reference/android/app/Activity.html
+_ACTIVITY_RESULT_CANCELED = 0
+_ACTIVITY_RESULT_OK = -1
+
 _DEFAULT_ANNOTATIONS = [
     'Smoke', 'SmallTest', 'MediumTest', 'LargeTest',
     'EnormousTest', 'IntegrationTest']
@@ -54,48 +58,69 @@
   return (code, bundle, statuses)
 
 
-def GenerateTestResult(test_name, instr_statuses, start_ms, duration_ms):
-  """Generate the result of |test| from |instr_statuses|.
+def GenerateTestResults(
+    result_code, result_bundle, statuses, start_ms, duration_ms):
+  """Generate test results from |statuses|.
 
   Args:
-    test_name: The name of the test as "class#method"
-    instr_statuses: A list of 2-tuples containing:
+    result_code: The overall status code as an integer.
+    result_bundle: The summary bundle dump as a dict.
+    statuses: A list of 2-tuples containing:
       - the status code as an integer
       - the bundle dump as a dict mapping string keys to string values
       Note that this is the same as the third item in the 3-tuple returned by
       |_ParseAmInstrumentRawOutput|.
     start_ms: The start time of the test in milliseconds.
     duration_ms: The duration of the test in milliseconds.
+
   Returns:
-    An InstrumentationTestResult object.
+    A list containing an instance of InstrumentationTestResult for each test
+    parsed.
   """
-  log = ''
-  result_type = base_test_result.ResultType.UNKNOWN
 
-  for status_code, bundle in instr_statuses:
-    if status_code == instrumentation_parser.STATUS_CODE_START:
-      pass
-    elif status_code == instrumentation_parser.STATUS_CODE_OK:
-      bundle_test = '%s#%s' % (bundle.get('class', ''), bundle.get('test', ''))
-      skipped = bundle.get('test_skipped', '')
+  results = []
 
-      if (test_name == bundle_test and
-          result_type == base_test_result.ResultType.UNKNOWN):
-        result_type = base_test_result.ResultType.PASS
-      elif skipped.lower() in ('true', '1', 'yes'):
-        result_type = base_test_result.ResultType.SKIP
-        logging.info('Skipped ' + test_name)
+  current_result = None
+
+  for status_code, bundle in statuses:
+    test_class = bundle.get('class', '')
+    test_method = bundle.get('test', '')
+    if test_class and test_method:
+      test_name = '%s#%s' % (test_class, test_method)
     else:
-      if status_code not in (instrumentation_parser.STATUS_CODE_ERROR,
-                             instrumentation_parser.STATUS_CODE_FAILURE):
-        logging.error('Unrecognized status code %d. Handling as an error.',
-                      status_code)
-      result_type = base_test_result.ResultType.FAIL
-      if 'stack' in bundle:
-        log = bundle['stack']
+      continue
 
-  return test_result.InstrumentationTestResult(
-      test_name, result_type, start_ms, duration_ms, log=log)
+    if status_code == instrumentation_parser.STATUS_CODE_START:
+      if current_result:
+        results.append(current_result)
+      current_result = test_result.InstrumentationTestResult(
+          test_name, base_test_result.ResultType.UNKNOWN, start_ms, duration_ms)
+    else:
+      if status_code == instrumentation_parser.STATUS_CODE_OK:
+        if bundle.get('test_skipped', '').lower() in ('true', '1', 'yes'):
+          current_result.SetType(base_test_result.ResultType.SKIP)
+        elif current_result.GetType() == base_test_result.ResultType.UNKNOWN:
+          current_result.SetType(base_test_result.ResultType.PASS)
+      else:
+        if status_code not in (instrumentation_parser.STATUS_CODE_ERROR,
+                               instrumentation_parser.STATUS_CODE_FAILURE):
+          logging.error('Unrecognized status code %d. Handling as an error.',
+                        status_code)
+        current_result.SetType(base_test_result.ResultType.FAIL)
+        if 'stack' in bundle:
+          current_result.SetLog(bundle['stack'])
+
+  if current_result:
+    if current_result.GetType() == base_test_result.ResultType.UNKNOWN:
+      crashed = (result_code == _ACTIVITY_RESULT_CANCELED
+                 and any(_NATIVE_CRASH_RE.search(l)
+                         for l in result_bundle.itervalues()))
+      if crashed:
+        current_result.SetType(base_test_result.ResultType.CRASH)
+
+    results.append(current_result)
+
+  return results
 
 
 class InstrumentationTestInstance(test_instance.TestInstance):
@@ -421,37 +446,14 @@
     return inflated_tests
 
   @staticmethod
-  def GenerateMultiTestResult(errors, statuses):
-    results = []
-    skip_counter = 1
-    for status_code, bundle in statuses:
-      if status_code != instrumentation_parser.STATUS_CODE_START:
-        # TODO(rnephew): Make skipped tests still output test name. This is only
-        # there to give skipped tests a unique name so they are counted
-        if 'test_skipped' in bundle:
-          test_name = str(skip_counter)
-          skip_counter += 1
-        else:
-          test_name = '%s#%s' % (bundle.get('class', ''),
-                                 bundle.get('test', ''))
-
-        results.append(
-            GenerateTestResult(test_name, [(status_code, bundle)], 0, 0))
-    for error in errors.itervalues():
-      if _NATIVE_CRASH_RE.search(error):
-        results.append(
-            base_test_result.BaseTestResult(
-            'Crash detected', base_test_result.ResultType.CRASH))
-
-    return results
-
-  @staticmethod
   def ParseAmInstrumentRawOutput(raw_output):
     return ParseAmInstrumentRawOutput(raw_output)
 
   @staticmethod
-  def GenerateTestResult(test_name, instr_statuses, start_ms, duration_ms):
-    return GenerateTestResult(test_name, instr_statuses, start_ms, duration_ms)
+  def GenerateTestResults(
+      result_code, result_bundle, statuses, start_ms, duration_ms):
+    return GenerateTestResults(result_code, result_bundle, statuses,
+                               start_ms, duration_ms)
 
   #override
   def TearDown(self):
diff --git a/build/android/pylib/instrumentation/instrumentation_test_instance_test.py b/build/android/pylib/instrumentation/instrumentation_test_instance_test.py
index 693f175..752e4d3d 100755
--- a/build/android/pylib/instrumentation/instrumentation_test_instance_test.py
+++ b/build/android/pylib/instrumentation/instrumentation_test_instance_test.py
@@ -27,15 +27,12 @@
     options = mock.Mock()
     options.tool = ''
 
-  def testGenerateTestResult_noStatus(self):
-    result = instrumentation_test_instance.GenerateTestResult(
-        'test.package.TestClass#testMethod', [], 0, 1000)
-    self.assertEqual('test.package.TestClass#testMethod', result.GetName())
-    self.assertEqual(base_test_result.ResultType.UNKNOWN, result.GetType())
-    self.assertEqual('', result.GetLog())
-    self.assertEqual(1000, result.GetDuration())
+  def testGenerateTestResults_noStatus(self):
+    results = instrumentation_test_instance.GenerateTestResults(
+        None, None, [], 0, 1000)
+    self.assertEqual([], results)
 
-  def testGenerateTestResult_testPassed(self):
+  def testGenerateTestResults_testPassed(self):
     statuses = [
       (1, {
         'class': 'test.package.TestClass',
@@ -46,65 +43,52 @@
         'test': 'testMethod',
       }),
     ]
-    result = instrumentation_test_instance.GenerateTestResult(
-        'test.package.TestClass#testMethod', statuses, 0, 1000)
-    self.assertEqual(base_test_result.ResultType.PASS, result.GetType())
+    results = instrumentation_test_instance.GenerateTestResults(
+        None, None, statuses, 0, 1000)
+    self.assertEqual(1, len(results))
+    self.assertEqual(base_test_result.ResultType.PASS, results[0].GetType())
 
-  def testGenerateTestResult_testSkipped_first(self):
-    statuses = [
-      (0, {
-        'test_skipped': 'true',
-      }),
-      (1, {
-        'class': 'test.package.TestClass',
-        'test': 'testMethod',
-      }),
-      (0, {
-        'class': 'test.package.TestClass',
-        'test': 'testMethod',
-      }),
-    ]
-    result = instrumentation_test_instance.GenerateTestResult(
-        'test.package.TestClass#testMethod', statuses, 0, 1000)
-    self.assertEqual(base_test_result.ResultType.SKIP, result.GetType())
-
-  def testGenerateTestResult_testSkipped_last(self):
+  def testGenerateTestResults_testSkipped_true(self):
     statuses = [
       (1, {
         'class': 'test.package.TestClass',
         'test': 'testMethod',
       }),
       (0, {
-        'class': 'test.package.TestClass',
-        'test': 'testMethod',
-      }),
-      (0, {
         'test_skipped': 'true',
+        'class': 'test.package.TestClass',
+        'test': 'testMethod',
+      }),
+      (0, {
+        'class': 'test.package.TestClass',
+        'test': 'testMethod',
       }),
     ]
-    result = instrumentation_test_instance.GenerateTestResult(
-        'test.package.TestClass#testMethod', statuses, 0, 1000)
-    self.assertEqual(base_test_result.ResultType.SKIP, result.GetType())
+    results = instrumentation_test_instance.GenerateTestResults(
+        None, None, statuses, 0, 1000)
+    self.assertEqual(1, len(results))
+    self.assertEqual(base_test_result.ResultType.SKIP, results[0].GetType())
 
-  def testGenerateTestResult_testSkipped_false(self):
+  def testGenerateTestResults_testSkipped_false(self):
     statuses = [
+      (1, {
+        'class': 'test.package.TestClass',
+        'test': 'testMethod',
+      }),
       (0, {
         'test_skipped': 'false',
       }),
-      (1, {
-        'class': 'test.package.TestClass',
-        'test': 'testMethod',
-      }),
       (0, {
         'class': 'test.package.TestClass',
         'test': 'testMethod',
       }),
     ]
-    result = instrumentation_test_instance.GenerateTestResult(
-        'test.package.TestClass#testMethod', statuses, 0, 1000)
-    self.assertEqual(base_test_result.ResultType.PASS, result.GetType())
+    results = instrumentation_test_instance.GenerateTestResults(
+        None, None, statuses, 0, 1000)
+    self.assertEqual(1, len(results))
+    self.assertEqual(base_test_result.ResultType.PASS, results[0].GetType())
 
-  def testGenerateTestResult_testFailed(self):
+  def testGenerateTestResults_testFailed(self):
     statuses = [
       (1, {
         'class': 'test.package.TestClass',
@@ -115,9 +99,10 @@
         'test': 'testMethod',
       }),
     ]
-    result = instrumentation_test_instance.GenerateTestResult(
-        'test.package.TestClass#testMethod', statuses, 0, 1000)
-    self.assertEqual(base_test_result.ResultType.FAIL, result.GetType())
+    results = instrumentation_test_instance.GenerateTestResults(
+        None, None, statuses, 0, 1000)
+    self.assertEqual(1, len(results))
+    self.assertEqual(base_test_result.ResultType.FAIL, results[0].GetType())
 
 
 if __name__ == '__main__':
diff --git a/build/android/pylib/instrumentation/test_runner.py b/build/android/pylib/instrumentation/test_runner.py
index 5f095a5..f3983fd9 100644
--- a/build/android/pylib/instrumentation/test_runner.py
+++ b/build/android/pylib/instrumentation/test_runner.py
@@ -320,9 +320,16 @@
         '%s/%s' % (self.test_pkg.GetPackageName(), self.options.test_runner),
         raw=True, extras=extras, timeout=timeout, retries=3)
 
-  def _GenerateTestResult(self, test, instr_statuses, start_ms, duration_ms):
-    return instrumentation_test_instance.GenerateTestResult(
-        test, instr_statuses, start_ms, duration_ms)
+  def _GenerateTestResult(self, test, instr_result_code, instr_result_bundle,
+                          statuses, start_ms, duration_ms):
+    results = instrumentation_test_instance.GenerateTestResults(
+        instr_result_code, instr_result_bundle, statuses, start_ms, duration_ms)
+    for r in results:
+      if r.GetName() == test:
+        return r
+    logging.error('Could not find result for test: %s', test)
+    return test_result.InstrumentationTestResult(
+        test, base_test_result.ResultType.UNKNOWN, start_ms, duration_ms)
 
   #override
   def RunTest(self, test):
@@ -345,9 +352,10 @@
       duration_ms = time_ms() - start_ms
 
       # Parse the test output
-      _, _, statuses = (
+      result_code, result_bundle, statuses = (
           instrumentation_test_instance.ParseAmInstrumentRawOutput(raw_output))
-      result = self._GenerateTestResult(test, statuses, start_ms, duration_ms)
+      result = self._GenerateTestResult(
+          test, result_code, result_bundle, statuses, start_ms, duration_ms)
       if local_device_instrumentation_test_run.DidPackageCrashOnDevice(
           self.test_pkg.GetPackageName(), self.device):
         result.SetType(base_test_result.ResultType.CRASH)
diff --git a/build/android/pylib/linker/test_case.py b/build/android/pylib/linker/test_case.py
index 8ebf803..c7b0f50 100644
--- a/build/android/pylib/linker/test_case.py
+++ b/build/android/pylib/linker/test_case.py
@@ -123,7 +123,7 @@
   """
 
   # 1. Start recording logcat with appropriate filters.
-  with device.GetLogcatMonitor(filters=_LOGCAT_FILTERS) as logmon:
+  with device.GetLogcatMonitor(filter_specs=_LOGCAT_FILTERS) as logmon:
 
     # 2. Force-start activity.
     device.StartActivity(
diff --git a/build/android/pylib/local/device/local_device_instrumentation_test_run.py b/build/android/pylib/local/device/local_device_instrumentation_test_run.py
index 2ed16ee..ac3f5b1 100644
--- a/build/android/pylib/local/device/local_device_instrumentation_test_run.py
+++ b/build/android/pylib/local/device/local_device_instrumentation_test_run.py
@@ -124,16 +124,19 @@
 
     # TODO(jbudorick): Make instrumentation tests output a JSON so this
     # doesn't have to parse the output.
-    logging.info('output from %s:' % test_name)
+    logging.debug('output from %s:', test_name)
     for l in output:
-      logging.info('  %s' % l)
+      logging.debug('  %s', l)
 
-    _, _, statuses = self._test_instance.ParseAmInstrumentRawOutput(output)
-    result = self._test_instance.GenerateTestResult(
-        test_name, statuses, start_ms, duration_ms)
+    result_code, result_bundle, statuses = (
+        self._test_instance.ParseAmInstrumentRawOutput(output))
+    results = self._test_instance.GenerateTestResults(
+        result_code, result_bundle, statuses, start_ms, duration_ms)
     if DidPackageCrashOnDevice(self._test_instance.test_package, device):
-      result.SetType(base_test_result.ResultType.CRASH)
-    return result
+      for r in results:
+        if r.GetType() == base_test_result.ResultType.UNKNOWN:
+          r.SetType(base_test_result.ResultType.CRASH)
+    return results
 
   #override
   def _ShouldShard(self):
diff --git a/build/android/pylib/local/device/local_device_test_run.py b/build/android/pylib/local/device/local_device_test_run.py
index 8c322cb..fa24eb1 100644
--- a/build/android/pylib/local/device/local_device_test_run.py
+++ b/build/android/pylib/local/device/local_device_test_run.py
@@ -70,10 +70,11 @@
     if unknown_tests:
       results.AddResults(
           base_test_result.BaseTestResult(
-              t, base_test_result.ResultType.UNKNOWN)
-          for t in tests)
+              u, base_test_result.ResultType.UNKNOWN)
+          for u in unknown_tests)
     if failed_tests:
       results.AddResults(all_fail_results[f] for f in failed_tests)
+
     return results
 
   def GetTool(self, device):
diff --git a/build/android/pylib/remote/device/remote_device_instrumentation_test_run.py b/build/android/pylib/remote/device/remote_device_instrumentation_test_run.py
index 5138d46..709a30c 100644
--- a/build/android/pylib/remote/device/remote_device_instrumentation_test_run.py
+++ b/build/android/pylib/remote/device/remote_device_instrumentation_test_run.py
@@ -40,10 +40,11 @@
           base_test_result.ResultType.FAIL))
       return r
 
-    _, errors, parsed_output = self._test_instance.ParseAmInstrumentRawOutput(
-        self._results['results']['output'].splitlines())
-    logging.debug(errors)
-    result = self._test_instance.GenerateMultiTestResult(errors, parsed_output)
+    result_code, result_bundle, statuses = (
+        self._test_instance.ParseAmInstrumentRawOutput(
+            self._results['results']['output'].splitlines()))
+    result = self._test_instance.GenerateTestResults(
+        result_code, result_bundle, statuses, 0, 0)
 
     if isinstance(result, base_test_result.BaseTestResult):
       r.AddResult(result)
diff --git a/build/android/pylib/uiautomator/test_runner.py b/build/android/pylib/uiautomator/test_runner.py
index 02f5d6a..d7a4bdf 100644
--- a/build/android/pylib/uiautomator/test_runner.py
+++ b/build/android/pylib/uiautomator/test_runner.py
@@ -79,8 +79,11 @@
     return self.device.RunShellCommand(cmd, timeout=timeout, retries=0)
 
   #override
-  def _GenerateTestResult(self, test, instr_statuses, start_ms, duration_ms):
+  def _GenerateTestResult(self, test, _result_code, _result_bundle, statuses,
+                          start_ms, duration_ms):
     # uiautomator emits its summary status with INSTRUMENTATION_STATUS_CODE,
     # not INSTRUMENTATION_CODE, so we have to drop if off the list of statuses.
+    summary_code, summary_bundle = statuses[-1]
     return super(TestRunner, self)._GenerateTestResult(
-        test, instr_statuses[:-1], start_ms, duration_ms)
+        test, summary_code, summary_bundle, statuses[:-1], start_ms,
+        duration_ms)
diff --git a/build/common.gypi b/build/common.gypi
index 3a071c8..55dad682 100644
--- a/build/common.gypi
+++ b/build/common.gypi
@@ -584,9 +584,6 @@
 
       # If no directory is specified then a temporary directory will be used.
       'test_isolation_outdir%': '',
-      # True if isolate should fail if the isolate files refer to files
-      # that are missing.
-      'test_isolation_fail_on_missing': 1,
 
       'wix_path%': '<(DEPTH)/third_party/wix',
 
@@ -630,6 +627,9 @@
       # Enable LTO on code compiled with -O2.
       'use_lto_o2%': 0,
 
+      # Allowed level of identical code folding in the gold linker.
+      'gold_icf_level%': 'safe',
+
       # Libxkbcommon usage.
       'use_xkbcommon%': 0,
 
@@ -1157,7 +1157,6 @@
     'use_canvas_skia%': '<(use_canvas_skia)',
     'test_isolation_mode%': '<(test_isolation_mode)',
     'test_isolation_outdir%': '<(test_isolation_outdir)',
-    'test_isolation_fail_on_missing': '<(test_isolation_fail_on_missing)',
     'enable_basic_printing%': '<(enable_basic_printing)',
     'enable_print_preview%': '<(enable_print_preview)',
     'enable_spellcheck%': '<(enable_spellcheck)',
@@ -1199,6 +1198,7 @@
     'gomadir%': '<(gomadir)',
     'use_lto%': '<(use_lto)',
     'use_lto_o2%': '<(use_lto_o2)',
+    'gold_icf_level%': '<(gold_icf_level)',
     'video_hole%': '<(video_hole)',
     'support_pre_M6_history_database%': '<(support_pre_M6_history_database)',
     'v8_use_external_startup_data%': '<(v8_use_external_startup_data)',
@@ -1802,9 +1802,6 @@
 
         # Copy it out one scope.
         'android_webview_build%': '<(android_webview_build)',
-
-        # Default android linker script for shared library exports.
-        'android_linker_script%': '<(SHARED_INTERMEDIATE_DIR)/android_exports.lst',
       }],  # OS=="android"
       ['embedded==1', {
         'use_system_fontconfig%': 0,
@@ -1831,7 +1828,6 @@
         'jni_generator_jarjar_file': '../android_webview/build/jarjar-rules.txt',
       }],
       ['OS=="linux" and target_arch!="mipsel"', {
-        # TODO(thakis): This is here to measure perf for a while.
         'clang%': 1,
       }],  # OS=="mac"
       ['OS=="mac"', {
@@ -2144,23 +2140,13 @@
         'clang_chrome_plugins_flags': [
           '<!@(<(DEPTH)/tools/clang/scripts/plugin_flags.sh)'
         ],
-        'conditions': [
-          # TODO(dcheng): https://crbug.com/417463 -- work to enable this flag
-          # on all platforms is currently underway.
-          ['OS=="android" or OS=="linux" or OS=="mac" or OS=="ios"', {
-            'clang_chrome_plugins_flags': [
-              '-Xclang',
-              '-plugin-arg-find-bad-constructs',
-              '-Xclang',
-              'strict-virtual-specifiers',
-            ],
-          }],
-        ],
       }],
       ['asan==1 or msan==1 or lsan==1 or tsan==1', {
         'clang%': 1,
         'use_allocator%': 'none',
         'use_sanitizer_options%': 1,
+        # Disable ICF in the linker to avoid debug info loss.
+        'gold_icf_level%': 'none',
       }],
       ['asan==1 and OS=="linux" and chromeos==0', {
         'use_custom_libcxx%': 1,
@@ -2475,6 +2461,14 @@
         '-Wno-unnamed-type-template-args',
       ],
 
+      # By default, Android targets have their exported JNI symbols stripped,
+      # so we test the manual JNI registration code paths that are required
+      # when using the crazy linker. To allow use of native JNI exports (lazily
+      # resolved by the JVM), targets can enable this variable, which will stop
+      # the stripping from happening. Only targets which do not need to be
+      # compatible with the crazy linker are permitted to set this.
+      'use_native_jni_exports%': 0,
+
       'conditions': [
         ['OS=="win" and component=="shared_library"', {
           # See http://msdn.microsoft.com/en-us/library/aa652367.aspx
@@ -3517,7 +3511,7 @@
         ],
       },
     }],
-    # TODO(thakis): Enable this everywhere. http://crbug.com/371125
+    # -Wl,-z,-defs doesn't work with the sanitiziers, http://crbug.com/452065
     ['(OS=="linux" or OS=="android") and asan==0 and msan==0 and tsan==0 and ubsan==0 and ubsan_vptr==0', {
       'target_defaults': {
         'ldflags': [
@@ -4414,7 +4408,7 @@
                 'target_conditions': [
                   ['_toolset=="target"', {
                     'ldflags': [
-                      '-Wl,--icf=safe',
+                      '-Wl,--icf=<(gold_icf_level)',
                     ],
                   }],
                 ],
@@ -4594,9 +4588,15 @@
               '-Wl,--no-undefined',
             ],
             'conditions': [
-              ['component=="static_library"', {
-                'ldflags': [
-                  '-Wl,--exclude-libs=ALL',
+              ['component=="static_library" and android_webview_build==0', {
+                'target_conditions': [
+                  ['use_native_jni_exports==0', {
+                    # Use a linker version script to strip JNI exports from
+                    # binaries which have not specifically asked to use them.
+                    'ldflags': [
+                      '-Wl,--version-script=<!(cd <(DEPTH) && pwd -P)/build/android/android_no_jni_exports.lst',
+                    ],
+                  }],
                 ],
               }],
               ['clang==1', {
@@ -4656,6 +4656,20 @@
                 'ldflags': [
                   '--sysroot=<(android_ndk_sysroot)',
                   '-nostdlib',
+                  # Don't allow visible symbols from libgcc or stlport to be
+                  # re-exported.
+                  '-Wl,--exclude-libs=libgcc.a',
+                  '-Wl,--exclude-libs=libstlport_static.a',
+                  # Don't allow visible symbols from libraries that contain
+                  # assembly code with symbols that aren't hidden properly.
+                  # http://crbug.com/448386
+                  '-Wl,--exclude-libs=libcommon_audio.a',
+                  '-Wl,--exclude-libs=libcommon_audio_neon.a',
+                  '-Wl,--exclude-libs=libcommon_audio_sse2.a',
+                  '-Wl,--exclude-libs=libiSACFix.a',
+                  '-Wl,--exclude-libs=libisac_neon.a',
+                  '-Wl,--exclude-libs=libopus.a',
+                  '-Wl,--exclude-libs=libvpx.a',
                 ],
                 'libraries': [
                   '-l<(android_stlport_library)',
@@ -4712,7 +4726,7 @@
               ['target_arch == "arm" and order_profiling==0', {
                 'ldflags': [
                   # Enable identical code folding to reduce size.
-                  '-Wl,--icf=safe',
+                  '-Wl,--icf=<(gold_icf_level)',
                 ],
               }],
               # NOTE: The stlport header include paths below are specified in
@@ -4772,9 +4786,6 @@
                 ],
               }],
               ['_type=="shared_library" or _type=="loadable_module"', {
-                'ldflags!': [
-                  '-Wl,--exclude-libs=ALL',
-                ],
                 'ldflags': [
                   '-Wl,-shared,-Bsymbolic',
                 ],
diff --git a/build/compiled_action.gni b/build/compiled_action.gni
index e5059aa..b6d0c4d7 100644
--- a/build/compiled_action.gni
+++ b/build/compiled_action.gni
@@ -66,7 +66,7 @@
 # saves unnecessarily compiling your tool for the target platform. But if you
 # need a target build of your tool as well, just leave off the if statement.
 
-if (build_os == "win") {
+if (host_os == "win") {
   _host_executable_suffix = ".exe"
 } else {
   _host_executable_suffix = ""
diff --git a/build/config/BUILD.gn b/build/config/BUILD.gn
index 5f93dd9..49b5cfe 100644
--- a/build/config/BUILD.gn
+++ b/build/config/BUILD.gn
@@ -231,7 +231,7 @@
       # have to tell it to turn it off.
       defines += [ "_HAS_ITERATOR_DEBUGGING=0" ]
     }
-  } else if (is_linux && !is_android && cpu_arch == "x64" &&
+  } else if (is_linux && !is_android && current_cpu == "x64" &&
              !disable_iterator_debugging) {
     # Enable libstdc++ debugging facilities to help catch problems early, see
     # http://crbug.com/65151 .
diff --git a/build/config/BUILDCONFIG.gn b/build/config/BUILDCONFIG.gn
index d806a17..a554f12d 100644
--- a/build/config/BUILDCONFIG.gn
+++ b/build/config/BUILDCONFIG.gn
@@ -16,7 +16,53 @@
 # KEEP IN ALPHABETICAL ORDER and write a good description for everything.
 # Use "is_*" names for intrinsic platform descriptions and build modes, and
 # "use_*" names for optional features libraries, and configurations.
+
+# TODO(dpranke): The os and cpu_arch variables exist for backwards
+# compatibility and should be deleted once all of the build files and
+# bots have been updated to use current_cpu/target_cpu and
+# current_os/target_os instead.
+
+if (target_os == "") {
+  if (defined(os)) {
+    # If os is defined, it was set in an args file and needs to be
+    # used for backwards-compatibility.
+    target_os = os
+  } else {
+    target_os = host_os
+  }
+}
+
+if (target_cpu == "") {
+  if (defined(cpu_arch)) {
+    # If cpu_arch is defined, it was set in an args file and needs to be
+    # used for backwards-compatibility.
+    target_cpu = cpu_arch
+  } else if (target_os == "android") {
+    # If we're building for Android, we should assume that we want to
+    # build for ARM by default, not the host_cpu (which is likely x64).
+    # This allows us to not have to specify both target_os and target_cpu
+    # on the command line.
+    target_cpu = "arm"
+  } else {
+    target_cpu = host_cpu
+  }
+}
+
+if (current_cpu == "") {
+  current_cpu = target_cpu
+}
+if (current_os == "") {
+  current_os = target_os
+}
+
 declare_args() {
+  # TODO(dpranke): These values are here for backwards compatibility and
+  # should be deleted when all of the builders and configs have been updated.
+  cpu_arch = target_cpu
+  os = target_os
+  build_cpu_arch = host_cpu
+  build_os = host_os
+
   # How many symbols to include in the build. This affects the performance of
   # the build since the symbols are large and dealing with them is slow.
   #   2 means regular build with symbols.
@@ -32,11 +78,12 @@
   is_debug = true
 
   # Whether we're a traditional desktop unix.
-  is_desktop_linux = os == "linux" && os != "chromeos"
+  is_desktop_linux = current_os == "linux" && current_os != "chromeos"
 
   # Set to true when compiling with the Clang compiler. Typically this is used
   # to configure warnings.
-  is_clang = os == "mac" || os == "ios" || os == "linux" || os == "chromeos"
+  is_clang = current_os == "mac" || current_os == "ios" ||
+             current_os == "linux" || current_os == "chromeos"
 
   # Selects the desired build flavor. Official builds get additional
   # processing to prepare for release. Normally you will want to develop and
@@ -60,29 +107,23 @@
   # Compile for Thread Sanitizer to find threading bugs.
   is_tsan = false
 
-  if (os == "chromeos") {
+  if (current_os == "chromeos") {
     # Allows the target toolchain to be injected as arguments. This is needed
     # to support the CrOS build system which supports per-build-configuration
     # toolchains.
     cros_use_custom_toolchain = false
   }
 
-  # TODO(cjhopman): Make target_arch work for all platforms.
-
-  # Architecture of the target device. For Android builds, this will be equal to
-  # the cpu_arch of the default toolchain. When checking the CPU architecture
-  # for source files and build dependencies you should almost alway use cpu_arch
-  # instead. cpu_arch is the architecture of the current toolchain and allows
-  # cross-compiles (compiling the same target for multiple toolchains in the
-  # same build) to work.
-  target_arch = "arm"
-
   # TODO(brettw) remove this flag (and therefore enable linking all targets) on
   # Windows when we have sufficient bot capacity. In the meantime, you can
   # enable linking for local compiles.
   link_chrome_on_windows = true
 }
 
+# TODO(dpranke): Remove these asserts when os and cpu_arch are removed.
+assert(current_cpu == cpu_arch)
+assert(current_os == os)
+
 # =============================================================================
 # OS DEFINITIONS
 # =============================================================================
@@ -98,10 +139,10 @@
 #   generally too different despite being based on the Linux kernel).
 #
 # Do not add more is_* variants here for random lesser-used Unix systems like
-# aix or one of the BSDs. If you need to check these, just check the os value
-# directly.
+# aix or one of the BSDs. If you need to check these, just check the
+# current_os value directly.
 
-if (os == "win") {
+if (current_os == "win") {
   is_android = false
   is_chromeos = false
   is_ios = false
@@ -110,7 +151,7 @@
   is_nacl = false
   is_posix = false
   is_win = true
-} else if (os == "mac") {
+} else if (current_os == "mac") {
   is_android = false
   is_chromeos = false
   is_ios = false
@@ -119,7 +160,7 @@
   is_nacl = false
   is_posix = true
   is_win = false
-} else if (os == "android") {
+} else if (current_os == "android") {
   is_android = true
   is_chromeos = false
   is_ios = false
@@ -128,7 +169,7 @@
   is_nacl = false
   is_posix = true
   is_win = false
-} else if (os == "chromeos") {
+} else if (current_os == "chromeos") {
   is_android = false
   is_chromeos = true
   is_ios = false
@@ -137,9 +178,10 @@
   is_nacl = false
   is_posix = true
   is_win = false
-} else if (os == "nacl") {
-  # os == "nacl" will be passed by the nacl toolchain definition. It is not
-  # set by default or on the command line. We treat is as a Posix variant.
+} else if (current_os == "nacl") {
+  # current_os == "nacl" will be passed by the nacl toolchain definition.
+  # It is not set by default or on the command line. We treat is as a
+  # Posix variant.
   is_android = false
   is_chromeos = false
   is_ios = false
@@ -148,7 +190,7 @@
   is_nacl = true
   is_posix = true
   is_win = false
-} else if (os == "ios") {
+} else if (current_os == "ios") {
   is_android = false
   is_chromeos = false
   is_ios = true
@@ -157,7 +199,7 @@
   is_nacl = false
   is_posix = true
   is_win = false
-} else if (os == "linux") {
+} else if (current_os == "linux") {
   is_android = false
   is_chromeos = false
   is_ios = false
@@ -169,18 +211,6 @@
 }
 
 # =============================================================================
-# CPU ARCHITECTURE
-# =============================================================================
-
-if (is_android) {
-  # TODO(cjhopman): enable this assert once bots are updated to not set
-  # cpu_arch.
-  #assert(cpu_arch == build_cpu_arch, "Android device target architecture should
-  #    be set with 'target_arch', not 'cpu_arch'")
-  cpu_arch = target_arch
-}
-
-# =============================================================================
 # SOURCES FILTERS
 # =============================================================================
 #
@@ -441,6 +471,11 @@
   _shared_library_configs += _windows_linker_configs
 } else if (is_mac) {
   _shared_library_configs += [ "//build/config/mac:mac_dynamic_flags" ]
+} else if (is_android) {
+  # Strip native JNI exports from shared libraries by default. Binaries that
+  # want this can remove this config.
+  _shared_library_configs +=
+      [ "//build/config/android:hide_native_jni_exports" ]
 }
 set_defaults("shared_library") {
   configs = _shared_library_configs
@@ -481,28 +516,28 @@
 
 if (is_win) {
   # On windows we use the same toolchain for host and target by default.
-  # TODO(dpranke): rename the toolchains to x64 and x86 to match cpu_arch.
-  if (cpu_arch == "x64") {
+  # TODO(dpranke): rename the toolchains to x64 and x86 to match current_cpu.
+  if (current_cpu == "x64") {
     host_toolchain = "//build/toolchain/win:64"
-  } else if (cpu_arch == "x86") {
+  } else if (current_cpu == "x86") {
     host_toolchain = "//build/toolchain/win:32"
   }
   set_default_toolchain("$host_toolchain")
 } else if (is_android) {
   # Use clang for the x86/64 Linux host builds.
-  if (build_cpu_arch == "x86" || build_cpu_arch == "x64") {
-    host_toolchain = "//build/toolchain/linux:clang_$build_cpu_arch"
+  if (host_cpu == "x86" || host_cpu == "x64") {
+    host_toolchain = "//build/toolchain/linux:clang_$host_cpu"
   } else {
-    host_toolchain = "//build/toolchain/linux:$build_cpu_arch"
+    host_toolchain = "//build/toolchain/linux:$host_cpu"
   }
-  set_default_toolchain("//build/toolchain/android:$cpu_arch")
+  set_default_toolchain("//build/toolchain/android:$current_cpu")
 } else if (is_linux) {
   if (is_clang) {
-    host_toolchain = "//build/toolchain/linux:clang_$build_cpu_arch"
-    set_default_toolchain("//build/toolchain/linux:clang_$cpu_arch")
+    host_toolchain = "//build/toolchain/linux:clang_$host_cpu"
+    set_default_toolchain("//build/toolchain/linux:clang_$current_cpu")
   } else {
-    host_toolchain = "//build/toolchain/linux:$build_cpu_arch"
-    set_default_toolchain("//build/toolchain/linux:$cpu_arch")
+    host_toolchain = "//build/toolchain/linux:$host_cpu"
+    set_default_toolchain("//build/toolchain/linux:$current_cpu")
   }
   if (is_chromeos && cros_use_custom_toolchain) {
     set_default_toolchain("//build/toolchain/cros:target")
diff --git a/build/config/allocator.gni b/build/config/allocator.gni
index 9fcfe49..4c9ae67a 100644
--- a/build/config/allocator.gni
+++ b/build/config/allocator.gni
@@ -3,7 +3,7 @@
 # found in the LICENSE file.
 
 # TODO(GYP): Make tcmalloc work on win.
-if (is_android || cpu_arch == "mipsel" || is_mac || is_asan || is_win) {
+if (is_android || current_cpu == "mipsel" || is_mac || is_asan || is_win) {
   _default_allocator = "none"
 } else {
   _default_allocator = "tcmalloc"
diff --git a/build/config/android/BUILD.gn b/build/config/android/BUILD.gn
index 0cc38b7e..5492693f 100644
--- a/build/config/android/BUILD.gn
+++ b/build/config/android/BUILD.gn
@@ -25,3 +25,8 @@
   cflags = [ "-fPIE" ]
   ldflags = [ "-pie" ]
 }
+
+config("hide_native_jni_exports") {
+  ldflags = [ "-Wl,--version-script=" +
+              rebase_path("//build/android/android_no_jni_exports.lst") ]
+}
diff --git a/build/config/android/config.gni b/build/config/android/config.gni
index 0105a64..cebf4de6 100644
--- a/build/config/android/config.gni
+++ b/build/config/android/config.gni
@@ -50,9 +50,9 @@
 
   # Defines the name the Android build gives to the current host CPU
   # architecture, which is different than the names GN uses.
-  if (build_cpu_arch == "x64") {
+  if (host_cpu == "x64") {
     android_host_arch = "x86_64"
-  } else if (build_cpu_arch == "x86") {
+  } else if (host_cpu == "x86") {
     android_host_arch = "x86"
   } else {
     assert(false, "Need Android toolchain support for your build CPU arch.")
@@ -60,7 +60,7 @@
 
   # Defines the name the Android build gives to the current host CPU
   # architecture, which is different than the names GN uses.
-  if (build_os == "linux") {
+  if (host_os == "linux") {
     android_host_os = "linux"
   } else {
     assert(false, "Need Android toolchain support for your build OS.")
@@ -119,32 +119,32 @@
   # Location of libgcc. This is only needed for the current GN toolchain, so we
   # only need to define the current one, rather than one for every platform
   # like the toolchain roots.
-  if (cpu_arch == "x86") {
+  if (current_cpu == "x86") {
     android_prebuilt_arch = "android-x86"
     _binary_prefix = "i686-linux-android"
     android_toolchain_root = "$x86_android_toolchain_root"
     android_libgcc_file = "$android_toolchain_root/lib/gcc/i686-linux-android/${_android_toolchain_version}/libgcc.a"
-  } else if (cpu_arch == "arm") {
+  } else if (current_cpu == "arm") {
     android_prebuilt_arch = "android-arm"
     _binary_prefix = "arm-linux-androideabi"
     android_toolchain_root = "$arm_android_toolchain_root"
     android_libgcc_file = "$android_toolchain_root/lib/gcc/arm-linux-androideabi/${_android_toolchain_version}/libgcc.a"
-  } else if (cpu_arch == "mipsel") {
+  } else if (current_cpu == "mipsel") {
     android_prebuilt_arch = "android-mips"
     _binary_prefix = "mipsel-linux-android"
     android_toolchain_root = "$mips_android_toolchain_root"
     android_libgcc_file = "$android_toolchain_root/lib/gcc/mipsel-linux-android/${_android_toolchain_version}/libgcc.a"
-  } else if (cpu_arch == "x64") {
+  } else if (current_cpu == "x64") {
     android_prebuilt_arch = "android-x86_64"
     _binary_prefix = "x86_64-linux-android"
     android_toolchain_root = "$x86_64_android_toolchain_root"
     android_libgcc_file = "$android_toolchain_root/lib/gcc/x86_64-linux-android/${_android_toolchain_version}/libgcc.a"
-  } else if (cpu_arch == "arm64") {
+  } else if (current_cpu == "arm64") {
     android_prebuilt_arch = "android-arm64"
     _binary_prefix = "aarch64-linux-android"
     android_toolchain_root = "$arm64_android_toolchain_root"
     android_libgcc_file = "$android_toolchain_root/lib/gcc/aarch64-linux-android/${_android_toolchain_version}/libgcc.a"
-  } else if (cpu_arch == "mips64el") {
+  } else if (current_cpu == "mips64el") {
     android_prebuilt_arch = "android-mips64"
     _binary_prefix = "mips64el-linux-android"
     android_toolchain_root = "$mips64_android_toolchain_root"
@@ -169,25 +169,25 @@
 
   # ABI ------------------------------------------------------------------------
 
-  if (cpu_arch == "x86") {
+  if (current_cpu == "x86") {
     android_app_abi = "x86"
-  } else if (cpu_arch == "arm") {
+  } else if (current_cpu == "arm") {
     import("//build/config/arm.gni")
     if (arm_version < 7) {
       android_app_abi = "armeabi"
     } else {
       android_app_abi = "armeabi-v7a"
     }
-  } else if (cpu_arch == "mipsel") {
+  } else if (current_cpu == "mipsel") {
     android_app_abi = "mips"
-  } else if (cpu_arch == "x64") {
+  } else if (current_cpu == "x64") {
     android_app_abi = "x86_64"
-  } else if (cpu_arch == "arm64") {
+  } else if (current_cpu == "arm64") {
     android_app_abi = "arm64-v8a"
-  } else if (cpu_arch == "mips64el") {
+  } else if (current_cpu == "mips64el") {
     android_app_abi = "mips64"
   } else {
-    assert(false, "Unknown Android ABI: " + cpu_arch)
+    assert(false, "Unknown Android ABI: " + current_cpu)
   }
 } else {
   if (!defined(is_android_webview_build)) {
diff --git a/build/config/android/rules.gni b/build/config/android/rules.gni
index 33dfa37..8ca06424 100644
--- a/build/config/android/rules.gni
+++ b/build/config/android/rules.gni
@@ -74,6 +74,12 @@
         rebase_path(jni_generator_jarjar_file, root_build_dir),
       ]
     }
+    if (!is_clang) {
+      # Clang builds currently fail with --native_exports_optional due to
+      # http://llvm.org/bugs/show_bug.cgi?id=22602 - only enable for gcc.
+      # http://crbug.com/442327
+      args += [ "--native_exports_optional" ]
+    }
   }
 
   config("jni_includes_${target_name}") {
@@ -184,6 +190,12 @@
         "--includes",
         rebase_path(jni_generator_include, root_build_dir),
       ]
+      if (!is_clang) {
+        # Clang builds currently fail with --native_exports_optional due to
+        # http://llvm.org/bugs/show_bug.cgi?id=22602 - only enable for gcc.
+        # http://crbug.com/442327
+        args += [ "--native_exports_optional" ]
+      }
     }
   }
 
@@ -346,9 +358,7 @@
 
   action("${target_name}__generate_enum") {
     # The sources aren't compiled so don't check their dependencies.
-    # TODO(brettw) uncomment after GN binary rolled pas 314974 (which added
-    # support for this value on actions).
-    #check_includes = false
+    check_includes = false
 
     sources = invoker.sources
     script = "//build/android/gyp/java_cpp_enum.py"
diff --git a/build/config/arm.gni b/build/config/arm.gni
index d39c6e9..778ecc1 100644
--- a/build/config/arm.gni
+++ b/build/config/arm.gni
@@ -2,7 +2,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-if (cpu_arch == "arm") {
+if (current_cpu == "arm") {
   declare_args() {
     # Version of the ARM processor when compiling on ARM. Ignored on non-ARM
     # platforms.
diff --git a/build/config/compiler/BUILD.gn b/build/config/compiler/BUILD.gn
index 164b97d..8cb4088 100644
--- a/build/config/compiler/BUILD.gn
+++ b/build/config/compiler/BUILD.gn
@@ -3,10 +3,10 @@
 # found in the LICENSE file.
 
 import("//build/config/android/config.gni")
-if (cpu_arch == "arm") {
+if (current_cpu == "arm") {
   import("//build/config/arm.gni")
 }
-if (cpu_arch == "mipsel" || cpu_arch == "mips64el") {
+if (current_cpu == "mipsel" || current_cpu == "mips64el") {
   import("//build/config/mips.gni")
 }
 if (is_posix) {
@@ -24,7 +24,7 @@
   # These are not multi-arch so cannot be used except on x86 and x86-64 (the
   # only two architectures that are currently checked in). Turn this off when
   # you are using a custom toolchain and need to control -B in cflags.
-  linux_use_bundled_binutils = is_linux && cpu_arch == "x64"
+  linux_use_bundled_binutils = is_linux && current_cpu == "x64"
 
   # Compile in such a way as to enable profiling of the generated code. For
   # example, don't omit the frame pointer and leave in symbols.
@@ -39,7 +39,7 @@
 
   # Use gold for linking on 64-bit Linux only (on 32-bit it runs out of
   # address space, and it doesn't support cross-compiling).
-  use_gold = is_linux && cpu_arch == "x64"
+  use_gold = is_linux && current_cpu == "x64"
 
   # use_debug_fission: whether to use split DWARF debug info
   # files. This can reduce link time significantly, but is incompatible
@@ -121,7 +121,7 @@
     }
 
     # Linker warnings.
-    if (!(is_chromeos && cpu_arch == "arm") && !is_mac) {
+    if (!(is_chromeos && current_cpu == "arm") && !is_mac) {
       # TODO(jochen): Enable this on chromeos on arm. http://crbug.com/356580
       ldflags += [ "-Wl,--fatal-warnings" ]
     }
@@ -172,12 +172,12 @@
     common_mac_flags = []
 
     # CPU architecture.
-    if (cpu_arch == "x64") {
+    if (current_cpu == "x64") {
       common_mac_flags += [
         "-arch",
         "x86_64",
       ]
-    } else if (cpu_arch == "x86") {
+    } else if (current_cpu == "x86") {
       common_mac_flags += [
         "-arch",
         "i386",
@@ -222,13 +222,13 @@
 
     # CPU architecture. We may or may not be doing a cross compile now, so for
     # simplicity we always explicitly set the architecture.
-    if (cpu_arch == "x64") {
+    if (current_cpu == "x64") {
       cflags += [
         "-m64",
         "-march=x86-64",
       ]
       ldflags += [ "-m64" ]
-    } else if (cpu_arch == "x86") {
+    } else if (current_cpu == "x86") {
       cflags += [ "-m32" ]
       ldflags += [ "-m32" ]
       if (is_clang) {
@@ -242,7 +242,7 @@
           "-mstackrealign",
         ]
       }
-    } else if (cpu_arch == "arm") {
+    } else if (current_cpu == "arm") {
       # Don't set the compiler flags for the WebView build. These will come
       # from the Android build system.
       if (!is_android_webview_build) {
@@ -275,7 +275,7 @@
           ]
         }
       }
-    } else if (cpu_arch == "mipsel") {
+    } else if (current_cpu == "mipsel") {
       # Don't set the compiler flags for the WebView build. These will come
       # from the Android build system.
       if (!is_android_webview_build) {
@@ -302,7 +302,7 @@
           ]
         }
       }
-    } else if (cpu_arch == "mips64el") {
+    } else if (current_cpu == "mips64el") {
       # Don't set the compiler flags for the WebView build. These will come
       # from the Android build system.
       if (!is_android_webview_build) {
@@ -437,7 +437,7 @@
     }
 
     # Use gold for Android for most CPU architectures.
-    if (cpu_arch == "x86" || cpu_arch == "x64" || cpu_arch == "arm") {
+    if (current_cpu == "x86" || current_cpu == "x64" || current_cpu == "arm") {
       ldflags += [ "-fuse-ld=gold" ]
       if (is_clang) {
         # Let clang find the ld.gold in the NDK.
@@ -449,10 +449,17 @@
     ldflags += [
       "-Wl,--no-undefined",
 
-      # Don't export symbols from statically linked libraries.
-      "-Wl,--exclude-libs=ALL",
+      # Don't allow visible symbols from libgcc or stlport to be
+      # re-exported.
+      "-Wl,--exclude-libs=libgcc.a",
+      "-Wl,--exclude-libs=libstlport_static.a",
+
+      # Don't allow visible symbols from libraries that contain
+      # assembly code with symbols that aren't hidden properly.
+      # http://crbug.com/448386
+      "-Wl,--exclude-libs=libvpx_assembly_arm.a",
     ]
-    if (cpu_arch == "arm") {
+    if (current_cpu == "arm") {
       ldflags += [
         # Enable identical code folding to reduce size.
         "-Wl,--icf=safe",
@@ -460,10 +467,10 @@
     }
 
     if (is_clang) {
-      if (cpu_arch == "arm") {
+      if (current_cpu == "arm") {
         cflags += [ "-target arm-linux-androideabi" ]
         ldflags += [ "-target arm-linux-androideabi" ]
-      } else if (cpu_arch == "x86") {
+      } else if (current_cpu == "x86") {
         cflags += [ "-target x86-linux-androideabi" ]
         ldflags += [ "-target x86-linux-androideabi" ]
       }
@@ -472,7 +479,7 @@
 }
 
 config("compiler_arm_fpu") {
-  if (cpu_arch == "arm" && !is_android_webview_build) {
+  if (current_cpu == "arm" && !is_android_webview_build) {
     cflags = [ "-mfpu=$arm_fpu" ]
   }
 }
@@ -570,7 +577,7 @@
       libs += [ "stlport_static" ]
     }
 
-    if (cpu_arch == "mipsel") {
+    if (current_cpu == "mipsel") {
       libs += [
         # ld linker is used for mips Android, and ld does not accept library
         # absolute path prefixed by "-l"; Since libgcc does not exist in mips
@@ -837,7 +844,7 @@
 
     # Suppress warnings about ABI changes on ARM (Clang doesn't give this
     # warning).
-    if (cpu_arch == "arm" && !is_clang) {
+    if (current_cpu == "arm" && !is_clang) {
       cflags += [ "-Wno-psabi" ]
     }
 
@@ -881,6 +888,20 @@
   }
 }
 
+# On Windows compiling on x64, VC will issue a warning when converting
+# size_t to int because it will truncate the value. Our code should not have
+# these warnings and one should use a static_cast or a checked_cast for the
+# conversion depending on the case. However, a lot of code still needs to be
+# fixed. Apply this config to such targets to disable the warning.
+#
+# Note that this can be applied regardless of platform and architecture to
+# clean up the call sites. This will only apply the flag when necessary.
+config("no_size_t_to_int_warning") {
+  if (is_win && current_cpu == "x64") {
+    cflags = [ "/wd4267" ]
+  }
+}
+
 # Optimization -----------------------------------------------------------------
 #
 # Note that BUILDCONFIG.gn sets up a variable "default_optimization_config"
diff --git a/build/config/features.gni b/build/config/features.gni
index 3811e27..ba69371 100644
--- a/build/config/features.gni
+++ b/build/config/features.gni
@@ -74,9 +74,9 @@
 # currently.
 # Do not disable seccomp_bpf anywhere without talking to
 # security@chromium.org!
-use_seccomp_bpf =
-    (is_linux || is_android) && (cpu_arch == "x86" || cpu_arch == "x64" ||
-                                 cpu_arch == "arm" || cpu_arch == "mipsel")
+use_seccomp_bpf = (is_linux || is_android) &&
+                  (current_cpu == "x86" || current_cpu == "x64" ||
+                   current_cpu == "arm" || current_cpu == "mipsel")
 
 # Enable notifications everywhere except iOS.
 enable_notifications = !is_ios
diff --git a/build/config/linux/BUILD.gn b/build/config/linux/BUILD.gn
index 0453c84..202fd731 100644
--- a/build/config/linux/BUILD.gn
+++ b/build/config/linux/BUILD.gn
@@ -180,15 +180,17 @@
   #ignore_libs = true  # Loader generated below.
 }
 
-# This generates a target named "gio".
-generate_library_loader("gio") {
-  name = "LibGioLoader"
-  output_h = "libgio.h"
-  output_cc = "libgio_loader.cc"
-  header = "<gio/gio.h>"
-  config = ":gio_config"
+if (is_desktop_linux) {
+  # This generates a target named "gio".
+  generate_library_loader("gio") {
+    name = "LibGioLoader"
+    output_h = "libgio.h"
+    output_cc = "libgio_loader.cc"
+    header = "<gio/gio.h>"
+    config = ":gio_config"
 
-  functions = gypi_values.libgio_functions
+    functions = gypi_values.libgio_functions
+  }
 }
 
 # This generates a target named "libpci".
diff --git a/build/config/linux/pkg_config.gni b/build/config/linux/pkg_config.gni
index 631d60aa..34ed1af 100644
--- a/build/config/linux/pkg_config.gni
+++ b/build/config/linux/pkg_config.gni
@@ -43,7 +43,7 @@
     "-s",
     sysroot,
     "-a",
-    cpu_arch,
+    current_cpu,
   ]
 } else if (pkg_config != "") {
   pkg_config_args = [
diff --git a/build/config/mips.gni b/build/config/mips.gni
index f544d94..512552d 100644
--- a/build/config/mips.gni
+++ b/build/config/mips.gni
@@ -3,11 +3,11 @@
 # found in the LICENSE file.
 
 # MIPS arch variant.
-if (cpu_arch == "mipsel") {
+if (current_cpu == "mipsel") {
   declare_args() {
     mips_arch_variant = "r1"
   }
-} else if (cpu_arch == "mips64el") {
+} else if (current_cpu == "mips64el") {
   if (is_android) {
     declare_args() {
       mips_arch_variant = "r6"
diff --git a/build/config/sysroot.gni b/build/config/sysroot.gni
index a9b250c..941c77a 100644
--- a/build/config/sysroot.gni
+++ b/build/config/sysroot.gni
@@ -16,17 +16,17 @@
 } else if (is_android) {
   import("//build/config/android/config.gni")
   if (!is_android_webview_build) {
-    if (cpu_arch == "x86") {
+    if (current_cpu == "x86") {
       sysroot = rebase_path("$android_ndk_root/$x86_android_sysroot_subdir")
-    } else if (cpu_arch == "arm") {
+    } else if (current_cpu == "arm") {
       sysroot = rebase_path("$android_ndk_root/$arm_android_sysroot_subdir")
-    } else if (cpu_arch == "mipsel") {
+    } else if (current_cpu == "mipsel") {
       sysroot = rebase_path("$android_ndk_root/$mips_android_sysroot_subdir")
-    } else if (cpu_arch == "x64") {
+    } else if (current_cpu == "x64") {
       sysroot = rebase_path("$android_ndk_root/$x86_64_android_sysroot_subdir")
-    } else if (cpu_arch == "arm64") {
+    } else if (current_cpu == "arm64") {
       sysroot = rebase_path("$android_ndk_root/$arm64_android_sysroot_subdir")
-    } else if (cpu_arch == "mips64") {
+    } else if (current_cpu == "mips64") {
       sysroot = rebase_path("$android_ndk_root/$mips64_android_sysroot_subdir")
     } else {
       sysroot = ""
@@ -37,17 +37,17 @@
 } else if (is_linux && is_chrome_branded && is_official_build && !is_chromeos) {
   # For official builds, use the sysroot checked into the internal source repo
   # so that the builds work on older versions of Linux.
-  if (cpu_arch == "x64") {
+  if (current_cpu == "x64") {
     sysroot =
         rebase_path("//chrome/installer/linux/debian_wheezy_amd64-sysroot")
-  } else if (cpu_arch == "x86") {
+  } else if (current_cpu == "x86") {
     sysroot = rebase_path("//chrome/installer/linux/debian_wheezy_i386-sysroot")
   } else {
     # Any other builds don't use a sysroot.
     sysroot = ""
   }
 } else if (is_linux && !is_chromeos) {
-  if (cpu_arch == "mipsel") {
+  if (current_cpu == "mipsel") {
     sysroot = rebase_path("//mipsel-sysroot/sysroot")
   } else {
     sysroot = ""
diff --git a/build/config/win/BUILD.gn b/build/config/win/BUILD.gn
index 3f05108..201d45b 100644
--- a/build/config/win/BUILD.gn
+++ b/build/config/win/BUILD.gn
@@ -31,7 +31,7 @@
 
 # Linker flags for Windows SDK setup, this is applied only to EXEs and DLLs.
 config("sdk_link") {
-  if (cpu_arch == "x64") {
+  if (current_cpu == "x64") {
     ldflags = [ "/MACHINE:X64" ]
     lib_dirs = [
       "$windows_sdk_path\Lib\winv6.3\um\x64",
diff --git a/build/download_sdk_extras.py b/build/download_sdk_extras.py
index 45e71996..d22d885 100755
--- a/build/download_sdk_extras.py
+++ b/build/download_sdk_extras.py
@@ -9,6 +9,8 @@
 bucket named: <dir in SDK extras>_<package name>_<version>.zip. The file will
 be extracted in the android_tools/sdk/extras directory on the test bots. This
 script will not do anything for developers.
+
+TODO(navabi): Move this script (crbug.com/459819).
 """
 
 import json
@@ -54,8 +56,13 @@
     local_zip = '%s/%s' % (SDK_EXTRAS_PATH, package['zip'])
     if not os.path.exists(local_zip):
       package_zip = '%s/%s' % (SDK_EXTRAS_BUCKET, package['zip'])
-      subprocess.check_call(['python', GSUTIL_PATH, '--force-version', '4.7',
-                             'cp', package_zip, local_zip])
+      try:
+        subprocess.check_call(['python', GSUTIL_PATH, '--force-version', '4.7',
+                               'cp', package_zip, local_zip])
+      except AccessDeniedException:
+        print ('WARNING: Bot does not have permission to download SDK packages.'
+               ' If this bot compiles for Android, it may have errors.')
+        return 0
     # Always clean dir and extract zip to ensure correct contents.
     clean_and_extract(package['dir_name'], package['package'], package['zip'])
 
diff --git a/build/get_landmines.py b/build/get_landmines.py
index 7a918c86..d7c98a5f 100755
--- a/build/get_landmines.py
+++ b/build/get_landmines.py
@@ -62,6 +62,7 @@
   print 'Clobber to fix missing NaCl gyp dependencies (crbug.com/427427).'
   print 'Another clobber for missing NaCl gyp deps (crbug.com/427427).'
   print 'Clobber to fix GN not picking up increased ID range (crbug.com/444902)'
+  print 'Remove NaCl toolchains from the output dir (crbug.com/456902)'
 
 
 def main():
diff --git a/build/ios/OWNERS b/build/ios/OWNERS
index 1c3e6c87..4caf405d 100644
--- a/build/ios/OWNERS
+++ b/build/ios/OWNERS
@@ -1,3 +1,4 @@
 rohitrao@chromium.org
 stuartmorgan@chromium.org
 
+per-file grit_whitelist.txt=*
diff --git a/build/ios/grit_whitelist.txt b/build/ios/grit_whitelist.txt
index c1edfdf..484f8069 100644
--- a/build/ios/grit_whitelist.txt
+++ b/build/ios/grit_whitelist.txt
@@ -130,6 +130,7 @@
 IDS_AUTOFILL_ADDRESS_LINE_SEPARATOR
 IDS_AUTOFILL_ADDRESS_SUMMARY_SEPARATOR
 IDS_AUTOFILL_CC_AMEX
+IDS_AUTOFILL_CC_AMEX_SHORT
 IDS_AUTOFILL_CC_DINERS
 IDS_AUTOFILL_CC_DISCOVER
 IDS_AUTOFILL_CC_GENERIC
@@ -719,6 +720,7 @@
 IDS_LIBADDRESSINPUT_UNRECOGNIZED_FORMAT_ZIP_CODE_EXAMPLE_AND_URL
 IDS_LIBADDRESSINPUT_VILLAGE_TOWNSHIP
 IDS_LIBADDRESSINPUT_ZIP_CODE_LABEL
+IDS_LINK_FROM_CLIPBOARD
 IDS_LOGIN_DIALOG_OK_BUTTON_LABEL
 IDS_LOGIN_DIALOG_PASSWORD_FIELD
 IDS_LOGIN_DIALOG_TITLE
diff --git a/build/isolate.gypi b/build/isolate.gypi
index d7070fe..fbb6d3cf 100644
--- a/build/isolate.gypi
+++ b/build/isolate.gypi
@@ -109,9 +109,6 @@
         ["test_isolation_outdir!=''", {
           'action': [ '--isolate-server', '<(test_isolation_outdir)' ],
         }],
-        ['test_isolation_fail_on_missing == 0', {
-          'action': ['--ignore_broken_items'],
-        }],
         ["test_isolation_mode == 'prepare'", {
           'outputs': [
             '<(PRODUCT_DIR)/<(RULE_INPUT_ROOT).isolated.gen.json',
diff --git a/build/jar_file_jni_generator.gypi b/build/jar_file_jni_generator.gypi
index 4c01c8a0..9472c10 100644
--- a/build/jar_file_jni_generator.gypi
+++ b/build/jar_file_jni_generator.gypi
@@ -73,5 +73,13 @@
         '<(DEPTH)/build/android/android_exports.gyp:android_exports',
       ],
     }],
+    ['clang==0', {
+      # Clang builds currently fail with --native_exports_optional due to
+      # http://llvm.org/bugs/show_bug.cgi?id=22602 - only enable for gcc.
+      # http://crbug.com/442327
+      'variables': {
+        'native_exports%': '--native_exports_optional',
+      },
+    }],
   ],
 }
diff --git a/build/jni_generator.gypi b/build/jni_generator.gypi
index 6edc512..853b5f6 100644
--- a/build/jni_generator.gypi
+++ b/build/jni_generator.gypi
@@ -92,6 +92,14 @@
         '<(DEPTH)/build/android/android_exports.gyp:android_exports',
       ],
     }],
+    ['clang==0', {
+      # Clang builds currently fail with --native_exports_optional due to
+      # http://llvm.org/bugs/show_bug.cgi?id=22602 - only enable for gcc.
+      # http://crbug.com/442327
+      'variables': {
+        'native_exports%': '--native_exports_optional',
+      },
+    }],
   ],
 }
 
diff --git a/build/json_schema_api.gni b/build/json_schema_api.gni
index 83846a2..68a9fdd 100644
--- a/build/json_schema_api.gni
+++ b/build/json_schema_api.gni
@@ -183,6 +183,7 @@
       sources += get_target_outputs(":$schema_generator_name")
       public_deps += [ ":$schema_generator_name" ]
       deps += [ "//tools/json_schema_compiler:generated_api_util" ]
+      configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
     }
 
     if (bundle) {
diff --git a/build/module_args/mojo.gni b/build/module_args/mojo.gni
index 0454f0d..21e5e9c 100644
--- a/build/module_args/mojo.gni
+++ b/build/module_args/mojo.gni
@@ -7,4 +7,8 @@
 
 # Chromium builds the network service from source, as it is the
 # producer of the network service.
+build_network_service_from_source = true
+
+# TODO(blundell): Remove the below line once
+# https://codereview.chromium.org/939753003/ rolls into Chromium.
 use_prebuilt_network_service = false
diff --git a/build/sanitizers/tsan_suppressions.cc b/build/sanitizers/tsan_suppressions.cc
index 5704f2d..24c24d6 100644
--- a/build/sanitizers/tsan_suppressions.cc
+++ b/build/sanitizers/tsan_suppressions.cc
@@ -69,8 +69,8 @@
 "race:v8::Locker::Initialize\n"
 
 // http://crbug.com/223352
-"race:uprv_malloc_52\n"
-"race:uprv_realloc_52\n"
+"race:uprv_malloc_54\n"
+"race:uprv_realloc_54\n"
 
 // http://crbug.com/239359
 "race:media::TestInputCallback::OnData\n"
@@ -325,6 +325,15 @@
 // https://crbug.com/455665
 "race:mojo::common::*::tick_clock\n"
 
+// https://crbug.com/459429
+"race:randomnessPid\n"
+
+// https://crbug.com/460243
+"race:IPC::ChannelMojoHost::OnClientLaunched\n"
+
+// https://crbug.com/454655
+"race:content::BrowserTestBase::PostTaskToInProcessRendererAndWait\n"
+
 // End of suppressions.
 ;  // Please keep this semicolon.
 
diff --git a/build/secondary/third_party/cacheinvalidation/BUILD.gn b/build/secondary/third_party/cacheinvalidation/BUILD.gn
index 088f89a..feb93d74 100644
--- a/build/secondary/third_party/cacheinvalidation/BUILD.gn
+++ b/build/secondary/third_party/cacheinvalidation/BUILD.gn
@@ -77,17 +77,14 @@
     "src/google/cacheinvalidation/include/types.h",
   ]
 
+  # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+  configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
   public_configs = [ ":cacheinvalidation_config" ]
 
   deps = [
     "src/google/cacheinvalidation:cacheinvalidation_proto_cpp",
     "//base",
   ]
-
-  if (is_win) {
-    # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
-    cflags = [ "/wd4267" ]
-  }
 }
 
 test("cacheinvalidation_unittests") {
diff --git a/build/secondary/third_party/icu/BUILD.gn b/build/secondary/third_party/icu/BUILD.gn
index 8b64f70..53818c9 100644
--- a/build/secondary/third_party/icu/BUILD.gn
+++ b/build/secondary/third_party/icu/BUILD.gn
@@ -74,6 +74,26 @@
     "source/i18n/chnsecal.cpp",
     "source/i18n/choicfmt.cpp",
     "source/i18n/coleitr.cpp",
+    "source/i18n/collationbasedatabuilder.cpp",
+    "source/i18n/collationbuilder.cpp",
+    "source/i18n/collationcompare.cpp",
+    "source/i18n/collation.cpp",
+    "source/i18n/collationdatabuilder.cpp",
+    "source/i18n/collationdata.cpp",
+    "source/i18n/collationdatareader.cpp",
+    "source/i18n/collationdatawriter.cpp",
+    "source/i18n/collationfastlatinbuilder.cpp",
+    "source/i18n/collationfastlatin.cpp",
+    "source/i18n/collationfcd.cpp",
+    "source/i18n/collationiterator.cpp",
+    "source/i18n/collationkeys.cpp",
+    "source/i18n/collationroot.cpp",
+    "source/i18n/collationrootelements.cpp",
+    "source/i18n/collationruleparser.cpp",
+    "source/i18n/collationsets.cpp",
+    "source/i18n/collationsettings.cpp",
+    "source/i18n/collationtailoring.cpp",
+    "source/i18n/collationweights.cpp",
     "source/i18n/coll.cpp",
     "source/i18n/compactdecimalformat.cpp",
     "source/i18n/coptccal.cpp",
@@ -95,6 +115,7 @@
     "source/i18n/dcfmtsym.cpp",
     "source/i18n/decContext.c",
     "source/i18n/decfmtst.cpp",
+    "source/i18n/decimalformatpattern.cpp",
     "source/i18n/decimfmt.cpp",
     "source/i18n/decNumber.c",
     "source/i18n/digitlst.cpp",
@@ -105,6 +126,7 @@
     "source/i18n/dtrule.cpp",
     "source/i18n/esctrn.cpp",
     "source/i18n/ethpccal.cpp",
+    "source/i18n/filteredbrk.cpp",
     "source/i18n/fmtable_cnv.cpp",
     "source/i18n/fmtable.cpp",
     "source/i18n/format.cpp",
@@ -122,6 +144,7 @@
     "source/i18n/japancal.cpp",
     "source/i18n/locdspnm.cpp",
     "source/i18n/measfmt.cpp",
+    "source/i18n/measunit.cpp",
     "source/i18n/measure.cpp",
     "source/i18n/msgfmt.cpp",
     "source/i18n/name2uni.cpp",
@@ -137,6 +160,7 @@
     "source/i18n/plurfmt.cpp",
     "source/i18n/plurrule.cpp",
     "source/i18n/quant.cpp",
+    "source/i18n/quantityformatter.cpp",
     "source/i18n/rbnf.cpp",
     "source/i18n/rbt.cpp",
     "source/i18n/rbt_data.cpp",
@@ -149,13 +173,17 @@
     "source/i18n/regexst.cpp",
     "source/i18n/regextxt.cpp",
     "source/i18n/region.cpp",
+    "source/i18n/reldatefmt.cpp",
     "source/i18n/reldtfmt.cpp",
     "source/i18n/rematch.cpp",
     "source/i18n/remtrans.cpp",
     "source/i18n/repattrn.cpp",
+    "source/i18n/rulebasedcollator.cpp",
+    "source/i18n/scientificformathelper.cpp",
     "source/i18n/scriptset.cpp",
     "source/i18n/search.cpp",
     "source/i18n/selfmt.cpp",
+    "source/i18n/sharedbreakiterator.cpp",
     "source/i18n/simpletz.cpp",
     "source/i18n/smpdtfmt.cpp",
     "source/i18n/smpdtfst.cpp",
@@ -164,7 +192,6 @@
     "source/i18n/strrepl.cpp",
     "source/i18n/stsearch.cpp",
     "source/i18n/taiwncal.cpp",
-    "source/i18n/tblcoll.cpp",
     "source/i18n/timezone.cpp",
     "source/i18n/titletrn.cpp",
     "source/i18n/tmunit.cpp",
@@ -182,21 +209,17 @@
     "source/i18n/tzrule.cpp",
     "source/i18n/tztrans.cpp",
     "source/i18n/ucal.cpp",
-    "source/i18n/ucln_in.c",
-    "source/i18n/ucol_bld.cpp",
-    "source/i18n/ucol_cnt.cpp",
+    "source/i18n/ucln_in.cpp",
     "source/i18n/ucol.cpp",
     "source/i18n/ucoleitr.cpp",
-    "source/i18n/ucol_elm.cpp",
     "source/i18n/ucol_res.cpp",
     "source/i18n/ucol_sit.cpp",
-    "source/i18n/ucol_tok.cpp",
-    "source/i18n/ucol_wgt.cpp",
     "source/i18n/ucsdet.cpp",
     "source/i18n/ucurr.cpp",
     "source/i18n/udat.cpp",
     "source/i18n/udateintervalformat.cpp",
     "source/i18n/udatpg.cpp",
+    "source/i18n/uitercollationiterator.cpp",
     "source/i18n/ulocdata.c",
     "source/i18n/umsg.cpp",
     "source/i18n/unesctrn.cpp",
@@ -213,6 +236,8 @@
     "source/i18n/uspoof.cpp",
     "source/i18n/uspoof_impl.cpp",
     "source/i18n/uspoof_wsconf.cpp",
+    "source/i18n/utf16collationiterator.cpp",
+    "source/i18n/utf8collationiterator.cpp",
     "source/i18n/utmscale.c",
     "source/i18n/utrans.cpp",
     "source/i18n/vtzone.cpp",
@@ -286,8 +311,9 @@
     "source/common/errorcode.cpp",
     "source/common/filterednormalizer2.cpp",
     "source/common/icudataver.c",
-    "source/common/icuplug.c",
+    "source/common/icuplug.cpp",
     "source/common/listformatter.cpp",
+    "source/common/loadednormalizer2impl.cpp",
     "source/common/locavailable.cpp",
     "source/common/locbased.cpp",
     "source/common/locdispnames.cpp",
@@ -325,6 +351,8 @@
     "source/common/servnotf.cpp",
     "source/common/servrbf.cpp",
     "source/common/servslkf.cpp",
+    "source/common/sharedobject.cpp",
+    "source/common/simplepatternformatter.cpp",
     "source/common/stringpiece.cpp",
     "source/common/stringtriebuilder.cpp",
     "source/common/uarrsort.c",
@@ -342,7 +370,7 @@
     "source/common/ucharstrie.cpp",
     "source/common/ucharstrieiterator.cpp",
     "source/common/uchriter.cpp",
-    "source/common/ucln_cmn.c",
+    "source/common/ucln_cmn.cpp",
     "source/common/ucmndata.c",
     "source/common/ucnv2022.cpp",
     "source/common/ucnv_bld.cpp",
@@ -359,7 +387,7 @@
     "source/common/ucnvisci.c",
     "source/common/ucnvlat1.c",
     "source/common/ucnv_lmb.c",
-    "source/common/ucnvmbcs.c",
+    "source/common/ucnvmbcs.cpp",
     "source/common/ucnvscsu.c",
     "source/common/ucnvsel.cpp",
     "source/common/ucnv_set.c",
@@ -380,11 +408,13 @@
     "source/common/uiter.cpp",
     "source/common/ulist.c",
     "source/common/uloc.cpp",
+    "source/common/uloc_keytype.cpp",
     "source/common/uloc_tag.c",
     "source/common/umapfile.c",
     "source/common/umath.c",
     "source/common/umutex.cpp",
     "source/common/unames.cpp",
+    "source/common/unifiedcache.cpp",
     "source/common/unifilt.cpp",
     "source/common/unifunct.cpp",
     "source/common/uniset_closure.cpp",
@@ -399,7 +429,6 @@
     "source/common/unistr_titlecase_brkiter.cpp",
     "source/common/unormcmp.cpp",
     "source/common/unorm.cpp",
-    "source/common/unorm_it.c",
     "source/common/uobject.cpp",
     "source/common/uprops.cpp",
     "source/common/uresbund.cpp",
@@ -416,7 +445,7 @@
     "source/common/ustack.cpp",
     "source/common/ustrcase.cpp",
     "source/common/ustrcase_locale.cpp",
-    "source/common/ustr_cnv.c",
+    "source/common/ustr_cnv.cpp",
     "source/common/ustrenum.cpp",
     "source/common/ustrfmt.c",
     "source/common/ustring.cpp",
@@ -457,6 +486,7 @@
 
   if (is_win || icu_use_data_file) {
     sources += [ "source/stubdata/stubdata.c" ]
+    defines += [ "U_ICUDATAENTRY_IN_COMMON" ]
   }
 }
 
diff --git a/build/secondary/third_party/libjpeg_turbo/BUILD.gn b/build/secondary/third_party/libjpeg_turbo/BUILD.gn
index be16302..bf35d07e 100644
--- a/build/secondary/third_party/libjpeg_turbo/BUILD.gn
+++ b/build/secondary/third_party/libjpeg_turbo/BUILD.gn
@@ -5,17 +5,17 @@
 # Do not use the targets in this file unless you need a certain libjpeg
 # implementation. Use the meta target //third_party:jpeg instead.
 
-if (cpu_arch == "arm") {
+if (current_cpu == "arm") {
   import("//build/config/arm.gni")
 }
 
-if (cpu_arch == "x86" || cpu_arch == "x64") {
+if (current_cpu == "x86" || current_cpu == "x64") {
   import("//third_party/yasm/yasm_assemble.gni")
 
   yasm_assemble("simd_asm") {
     defines = []
 
-    if (cpu_arch == "x86") {
+    if (current_cpu == "x86") {
       sources = [
         "simd/jccolmmx.asm",
         "simd/jccolss2.asm",
@@ -52,7 +52,7 @@
         "simd/jsimdcpu.asm",
       ]
       defines += [ "__x86__" ]
-    } else if (cpu_arch == "x64") {
+    } else if (current_cpu == "x64") {
       sources = [
         "simd/jccolss2-64.asm",
         "simd/jcgrass2-64.asm",
@@ -76,7 +76,7 @@
     if (is_win) {
       defines += [ "MSVC" ]
       include_dirs = [ "win" ]
-      if (cpu_arch == "x86") {
+      if (current_cpu == "x86") {
         defines += [ "WIN32" ]
       } else {
         defines += [ "WIN64" ]
@@ -92,7 +92,7 @@
 }
 
 source_set("simd") {
-  if (cpu_arch == "x86") {
+  if (current_cpu == "x86") {
     deps = [
       ":simd_asm",
     ]
@@ -102,14 +102,14 @@
     if (is_win) {
       cflags = [ "/wd4245" ]
     }
-  } else if (cpu_arch == "x64") {
+  } else if (current_cpu == "x64") {
     deps = [
       ":simd_asm",
     ]
     sources = [
       "simd/jsimd_x86_64.c",
     ]
-  } else if (cpu_arch == "arm" && arm_version >= 7 &&
+  } else if (current_cpu == "arm" && arm_version >= 7 &&
              (arm_use_neon || arm_optionally_use_neon)) {
     sources = [
       "simd/jsimd_arm.c",
diff --git a/build/secondary/third_party/libsrtp/BUILD.gn b/build/secondary/third_party/libsrtp/BUILD.gn
index 506d19f..d9b658e7 100644
--- a/build/secondary/third_party/libsrtp/BUILD.gn
+++ b/build/secondary/third_party/libsrtp/BUILD.gn
@@ -45,7 +45,7 @@
     ]
   }
 
-  if (cpu_arch == "x64" || cpu_arch == "x86" || cpu_arch == "arm") {
+  if (current_cpu == "x64" || current_cpu == "x86" || current_cpu == "arm") {
     defines += [
       # TODO(leozwang): CPU_RISC doesn"t work properly on android/arm
       # platform for unknown reasons, need to investigate the root cause
@@ -56,7 +56,7 @@
     ]
   }
 
-  if (cpu_arch == "mipsel") {
+  if (current_cpu == "mipsel") {
     defines += [ "CPU_RISC" ]
   }
 }
diff --git a/build/secondary/third_party/nss/BUILD.gn b/build/secondary/third_party/nss/BUILD.gn
index 768a85d..ee5f112 100644
--- a/build/secondary/third_party/nss/BUILD.gn
+++ b/build/secondary/third_party/nss/BUILD.gn
@@ -217,7 +217,10 @@
         "//build/config/win:lean_and_mean",  # Won"t compile with lean and mean.
       ]
     }
-    configs += [ "//build/config/compiler:no_chromium_code" ]
+    configs += [
+      "//build/config/compiler:no_chromium_code",
+      "//build/config/compiler:no_size_t_to_int_warning",
+    ]
 
     cflags = []
     defines = [
@@ -228,10 +231,7 @@
     include_dirs = [ "nspr/pr/include/private" ]
 
     if (is_win) {
-      cflags = [
-        "/wd4554",  # Check precidence.
-        "/wd4267",  # Conversion from size_t to "type".
-      ]
+      cflags = [ "/wd4554" ]  # Check precidence.
       defines += [
         "XP_PC",
         "WIN32",
@@ -281,9 +281,9 @@
       ]
     }
 
-    if (cpu_arch == "x86") {
+    if (current_cpu == "x86") {
       defines += [ "_X86_" ]
-    } else if (cpu_arch == "x64") {
+    } else if (current_cpu == "x64") {
       defines += [ "_AMD64_" ]
     }
 
@@ -474,7 +474,7 @@
     ]
   }
 
-  if (is_win && cpu_arch == "x86") {
+  if (is_win && current_cpu == "x86") {
     source_set("nss_static_avx") {
       sources = [
         "nss/lib/freebl/intel-gcm-wrap.c",
@@ -882,7 +882,10 @@
     if (is_win) {
       configs -= [ "//build/config/win:unicode" ]  # Requires 8-bit mode.
     }
-    configs += [ "//build/config/compiler:no_chromium_code" ]
+    configs += [
+      "//build/config/compiler:no_chromium_code",
+      "//build/config/compiler:no_size_t_to_int_warning",
+    ]
     public_configs = [ ":nss_static_config" ]
 
     cflags = []
@@ -901,10 +904,7 @@
     ]
 
     if (is_win) {
-      cflags += [
-        "/wd4101",  # Unreferenced local variable.
-        "/wd4267",  # Conversion from size_t to "type".
-      ]
+      cflags += [ "/wd4101" ]  # Unreferenced local variable.
     }
 
     if (include_nss_libpkix) {
@@ -1094,7 +1094,7 @@
       defines += [ "NSS_DISABLE_ROOT_CERTS" ]
     }
 
-    if (cpu_arch == "x64" && !is_win) {
+    if (current_cpu == "x64" && !is_win) {
       sources -= [
         "nss/lib/freebl/chacha20/chacha20.c",
         "nss/lib/freebl/poly1305/poly1305.c",
@@ -1139,7 +1139,7 @@
         "WIN95",
       ]
 
-      if (cpu_arch == "x86") {
+      if (current_cpu == "x86") {
         defines += [
           "NSS_X86_OR_X64",
           "NSS_X86",
@@ -1153,7 +1153,7 @@
           "INTEL_GCM",
         ]
         sources -= [ "nss/lib/freebl/mpi/mpi_amd64.c" ]
-      } else if (cpu_arch == "x64") {
+      } else if (current_cpu == "x64") {
         sources -= [
           "nss/lib/freebl/intel-aes-x86-masm.asm",
           "nss/lib/freebl/mpi/mpi_amd64.c",
@@ -1204,7 +1204,7 @@
       "//third_party/sqlite",
     ]
 
-    if (is_win && cpu_arch == "x86") {
+    if (is_win && current_cpu == "x86") {
       deps += [ ":nss_static_avx" ]
     }
   }
diff --git a/build/toolchain/android/BUILD.gn b/build/toolchain/android/BUILD.gn
index e3d950a..53ad506 100644
--- a/build/toolchain/android/BUILD.gn
+++ b/build/toolchain/android/BUILD.gn
@@ -18,7 +18,7 @@
 #      Subdirectory inside of android_ndk_sysroot where libs go.
 #  - tool_prefix
 #      Prefix to be added to the tool names.
-#  - toolchain_cpu_arch
+#  - toolchain_cpu
 #      Same as gcc_toolchain
 template("android_gcc_toolchain") {
   gcc_toolchain(target_name) {
@@ -51,7 +51,7 @@
     ld = cxx
 
     toolchain_os = "android"
-    toolchain_cpu_arch = invoker.toolchain_cpu_arch
+    toolchain_cpu = invoker.toolchain_cpu
 
     # We make the assumption that the gcc_toolchain will produce a soname with
     # the following definition.
@@ -85,7 +85,7 @@
   android_ndk_lib_dir = "usr/lib"
 
   tool_prefix = "$x86_android_toolchain_root/bin/i686-linux-android-"
-  toolchain_cpu_arch = "x86"
+  toolchain_cpu = "x86"
 }
 
 android_gcc_toolchain("arm") {
@@ -93,7 +93,7 @@
   android_ndk_lib_dir = "usr/lib"
 
   tool_prefix = "$arm_android_toolchain_root/bin/arm-linux-androideabi-"
-  toolchain_cpu_arch = "arm"
+  toolchain_cpu = "arm"
 }
 
 android_gcc_toolchain("mipsel") {
@@ -101,7 +101,7 @@
   android_ndk_lib_dir = "usr/lib"
 
   tool_prefix = "$mips_android_toolchain_root/bin/mipsel-linux-android-"
-  toolchain_cpu_arch = "mipsel"
+  toolchain_cpu = "mipsel"
 }
 
 android_gcc_toolchain("x64") {
@@ -109,7 +109,7 @@
   android_ndk_lib_dir = "usr/lib64"
 
   tool_prefix = "$x86_64_android_toolchain_root/bin/x86_64-linux-android-"
-  toolchain_cpu_arch = "x86_64"
+  toolchain_cpu = "x86_64"
 }
 
 android_gcc_toolchain("arm64") {
@@ -117,7 +117,7 @@
   android_ndk_lib_dir = "usr/lib"
 
   tool_prefix = "$arm64_android_toolchain_root/bin/arm-linux-androideabi-"
-  toolchain_cpu_arch = "aarch64"
+  toolchain_cpu = "aarch64"
 }
 
 android_gcc_toolchain("mips64el") {
@@ -125,5 +125,5 @@
   android_ndk_lib_dir = "usr/lib64"
 
   tool_prefix = "$mips64_android_toolchain_root/bin/mipsel-linux-android-"
-  toolchain_cpu_arch = "mipsel64el"
+  toolchain_cpu = "mipsel64el"
 }
diff --git a/build/toolchain/cros/BUILD.gn b/build/toolchain/cros/BUILD.gn
index d360f72..140958b5b 100644
--- a/build/toolchain/cros/BUILD.gn
+++ b/build/toolchain/cros/BUILD.gn
@@ -29,7 +29,7 @@
   ar = "${cros_target_ar}"
   ld = cxx
 
-  toolchain_cpu_arch = "${cpu_arch}"
+  toolchain_cpu = "${target_cpu}"
   toolchain_os = "linux"
   is_clang = is_clang
 }
diff --git a/build/toolchain/gcc_toolchain.gni b/build/toolchain/gcc_toolchain.gni
index 5cca299..5f49f683 100644
--- a/build/toolchain/gcc_toolchain.gni
+++ b/build/toolchain/gcc_toolchain.gni
@@ -14,10 +14,10 @@
 #  - ar
 #  - ld
 # and the following which is used in the toolchain_args
-#  - toolchain_cpu_arch  (What "cpu_arch" should be set to when invoking a
-#                         build using this toolchain.)
-#  - toolchain_os  (What "os" should be set to when invoking a build using this
-#                   toolchain.)
+#  - toolchain_cpu  (What "current_cpu" should be set to when invoking a
+#                    build using this toolchain.)
+#  - toolchain_os  (What "current_os" should be set to when invoking a
+#                   build using this toolchain.)
 #
 # Optional parameters:
 #  - libs_section_prefix
@@ -40,8 +40,8 @@
     assert(defined(invoker.cxx), "gcc_toolchain() must specify a \"cxx\" value")
     assert(defined(invoker.ar), "gcc_toolchain() must specify a \"ar\" value")
     assert(defined(invoker.ld), "gcc_toolchain() must specify a \"ld\" value")
-    assert(defined(invoker.toolchain_cpu_arch),
-           "gcc_toolchain() must specify a \"toolchain_cpu_arch\"")
+    assert(defined(invoker.toolchain_cpu),
+           "gcc_toolchain() must specify a \"toolchain_cpu\"")
     assert(defined(invoker.toolchain_os),
            "gcc_toolchain() must specify a \"toolchain_os\"")
 
@@ -204,8 +204,19 @@
     # When invoking this toolchain not as the default one, these args will be
     # passed to the build. They are ignored when this is the default toolchain.
     toolchain_args() {
-      cpu_arch = invoker.toolchain_cpu_arch
-      os = invoker.toolchain_os
+      current_cpu = invoker.toolchain_cpu
+      current_os = invoker.toolchain_os
+
+      # These values need to be passed through unchanged.
+      target_os = target_os
+      target_cpu = target_cpu
+
+      # TODO(dpranke): These values are here for backwards compatibility and
+      # should be deleted when all of the builders and configs have been
+      # updated.
+      cpu_arch = current_cpu
+      os = current_os
+
       if (defined(invoker.is_clang)) {
         is_clang = invoker.is_clang
       }
diff --git a/build/toolchain/linux/BUILD.gn b/build/toolchain/linux/BUILD.gn
index 1d17772..7bcd9d01 100644
--- a/build/toolchain/linux/BUILD.gn
+++ b/build/toolchain/linux/BUILD.gn
@@ -24,7 +24,7 @@
   ar = "arm-linux-gnueabi-ar"
   ld = cxx
 
-  toolchain_cpu_arch = "arm"
+  toolchain_cpu = "arm"
   toolchain_os = "linux"
   is_clang = false
 }
@@ -43,7 +43,7 @@
   ar = "ar"
   ld = cxx
 
-  toolchain_cpu_arch = "x86"
+  toolchain_cpu = "x86"
   toolchain_os = "linux"
   is_clang = true
 }
@@ -55,7 +55,7 @@
   ar = "ar"
   ld = cxx
 
-  toolchain_cpu_arch = "x86"
+  toolchain_cpu = "x86"
   toolchain_os = "linux"
   is_clang = false
 }
@@ -74,7 +74,7 @@
   ar = "ar"
   ld = cxx
 
-  toolchain_cpu_arch = "x64"
+  toolchain_cpu = "x64"
   toolchain_os = "linux"
   is_clang = true
 }
@@ -86,7 +86,7 @@
   ar = "ar"
   ld = cxx
 
-  toolchain_cpu_arch = "x64"
+  toolchain_cpu = "x64"
   toolchain_os = "linux"
   is_clang = false
 }
@@ -97,7 +97,7 @@
   ar = "mipsel-linux-gnu-ar"
   ld = cxx
 
-  toolchain_cpu_arch = "mipsel"
+  toolchain_cpu = "mipsel"
   toolchain_os = "linux"
   is_clang = false
 }
diff --git a/build/toolchain/mac/BUILD.gn b/build/toolchain/mac/BUILD.gn
index 26ad865..18ee8b8 100644
--- a/build/toolchain/mac/BUILD.gn
+++ b/build/toolchain/mac/BUILD.gn
@@ -14,6 +14,12 @@
 import("//build/toolchain/clang.gni")
 import("//build/toolchain/goma.gni")
 
+if (use_goma) {
+  goma_prefix = "$goma_dir/gomacc "
+} else {
+  goma_prefix = ""
+}
+
 if (is_clang) {
   cc = rebase_path("//third_party/llvm-build/Release+Asserts/bin/clang",
                    root_build_dir)
@@ -23,6 +29,8 @@
   cc = "gcc"
   cxx = "g++"
 }
+cc = goma_prefix + cc
+cxx = goma_prefix + cxx
 ld = cxx
 
 # This will copy the gyp-mac-tool to the build directory. We pass in the source
@@ -186,14 +194,17 @@
     }
 
     toolchain_args() {
-      os = invoker.toolchain_os
+      current_os = invoker.toolchain_os
+
+      # TODO(dpranke): os is here for backwards compatibility.
+      os = current_os
     }
   }
 }
 
 # Toolchain representing the target build (either mac or iOS).
 mac_clang_toolchain("clang") {
-  toolchain_os = os
+  toolchain_os = current_os
 }
 
 # This toolchain provides a way for iOS target compiles to reference targets
diff --git a/build/toolchain/nacl/BUILD.gn b/build/toolchain/nacl/BUILD.gn
index b923608..5fa637c 100644
--- a/build/toolchain/nacl/BUILD.gn
+++ b/build/toolchain/nacl/BUILD.gn
@@ -54,7 +54,7 @@
   toolchain_args() {
     # Override the default OS detection. The build config will set the is_*
     # flags accordingly.
-    os = "nacl"
+    current_os = "nacl"
 
     # Component build not supported in NaCl, since it does not support shared
     # libraries.
diff --git a/build/toolchain/win/BUILD.gn b/build/toolchain/win/BUILD.gn
index b8f87e7..e9461a4 100644
--- a/build/toolchain/win/BUILD.gn
+++ b/build/toolchain/win/BUILD.gn
@@ -31,7 +31,7 @@
                                gyp_win_tool_path,
                                windows_sdk_path,
                                visual_studio_runtime_dirs,
-                               cpu_arch,
+                               current_cpu,
                              ],
                              "scope")
 
@@ -43,7 +43,7 @@
 concurrent_links = exec_script("../get_concurrent_links.py", [], "value")
 
 # Parameters:
-#  cpu_arch: cpu_arch to pass as a build arg
+#  current_cpu: current_cpu to pass as a build arg
 #  environment: File name of environment file.
 template("msvc_toolchain") {
   if (defined(invoker.concurrent_links)) {
@@ -62,7 +62,7 @@
                 "copy_dlls",
                 rebase_path(root_build_dir),
                 configuration,
-                invoker.cpu_arch,
+                invoker.current_cpu,
               ])
 
   if (use_goma) {
@@ -200,7 +200,10 @@
     # When invoking this toolchain not as the default one, these args will be
     # passed to the build. They are ignored when this is the default toolchain.
     toolchain_args() {
-      cpu_arch = invoker.cpu_arch
+      current_cpu = invoker.current_cpu
+
+      # TODO(dpranke): cpu_arch is here for backwards compatibility.
+      cpu_arch = current_cpu
     }
   }
 }
@@ -209,16 +212,18 @@
 # get it sorted out how we want to support them both in a single build.
 # Right now only one of these can be enabled at a time because the
 # runtime libraries get copied to root_build_dir and would collide.
-if (cpu_arch == "x86") {
+if (current_cpu == "x86") {
   msvc_toolchain("32") {
     environment = "environment.x86"
-    cpu_arch = "x86"
+
+    current_cpu = "x86"
   }
 }
 
-if (cpu_arch == "x64") {
+if (current_cpu == "x64") {
   msvc_toolchain("64") {
     environment = "environment.x64"
-    cpu_arch = "x64"
+
+    current_cpu = "x64"
   }
 }
diff --git a/build/toolchain/win/midl.gni b/build/toolchain/win/midl.gni
index ce310d11..30bcbf77 100644
--- a/build/toolchain/win/midl.gni
+++ b/build/toolchain/win/midl.gni
@@ -56,10 +56,10 @@
       "$out_dir/$proxy_file",
     ]
 
-    if (cpu_arch == "x86") {
+    if (current_cpu == "x86") {
       win_tool_arch = "environment.x86"
       idl_target_platform = "win32"
-    } else if (cpu_arch == "x64") {
+    } else if (current_cpu == "x64") {
       win_tool_arch = "environment.x64"
       idl_target_platform = "x64"
     } else {
diff --git a/build/toolchain/win/setup_toolchain.py b/build/toolchain/win/setup_toolchain.py
index cc89638e..bc9bd1e 100644
--- a/build/toolchain/win/setup_toolchain.py
+++ b/build/toolchain/win/setup_toolchain.py
@@ -51,23 +51,23 @@
   return env
 
 
-def _SetupScript(target_arch, sdk_dir):
+def _SetupScript(target_cpu, sdk_dir):
   """Returns a command (with arguments) to be used to set up the
   environment."""
   # Check if we are running in the SDK command line environment and use
-  # the setup script from the SDK if so. |target_arch| should be either
+  # the setup script from the SDK if so. |target_cpu| should be either
   # 'x86' or 'x64'.
-  assert target_arch in ('x86', 'x64')
+  assert target_cpu in ('x86', 'x64')
   if bool(int(os.environ.get('DEPOT_TOOLS_WIN_TOOLCHAIN', 1))) and sdk_dir:
     return [os.path.normpath(os.path.join(sdk_dir, 'Bin/SetEnv.Cmd')),
-            '/' + target_arch]
+            '/' + target_cpu]
   else:
     # We only support x64-hosted tools.
     # TODO(scottmg|dpranke): Non-depot_tools toolchain: need to get Visual
     # Studio install location from registry.
     return [os.path.normpath(os.path.join(os.environ['GYP_MSVS_OVERRIDE_PATH'],
                                           'VC/vcvarsall.bat')),
-            'amd64_x86' if target_arch == 'x86' else 'amd64']
+            'amd64_x86' if target_cpu == 'x86' else 'amd64']
 
 
 def _FormatAsEnvironmentBlock(envvar_dict):
@@ -100,25 +100,25 @@
   if len(sys.argv) != 6:
     print('Usage setup_toolchain.py '
           '<visual studio path> <win tool path> <win sdk path> '
-          '<runtime dirs> <cpu_arch>')
+          '<runtime dirs> <target_cpu>')
     sys.exit(2)
   tool_source = sys.argv[2]
   win_sdk_path = sys.argv[3]
   runtime_dirs = sys.argv[4]
-  cpu_arch = sys.argv[5]
+  target_cpu = sys.argv[5]
 
   _CopyTool(tool_source)
 
-  archs = ('x86', 'x64')
-  assert cpu_arch in archs
+  cpus = ('x86', 'x64')
+  assert target_cpu in cpus
   vc_bin_dir = ''
 
   # TODO(scottmg|goma): Do we need an equivalent of
   # ninja_use_custom_environment_files?
 
-  for arch in archs:
+  for cpu in cpus:
     # Extract environment variables for subprocesses.
-    args = _SetupScript(arch, win_sdk_path)
+    args = _SetupScript(cpu, win_sdk_path)
     args.extend(('&&', 'set'))
     popen = subprocess.Popen(
         args, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
@@ -126,7 +126,7 @@
     env = _ExtractImportantEnvironment(variables)
     env['PATH'] = runtime_dirs + ';' + env['PATH']
 
-    if arch == cpu_arch:
+    if cpu == target_cpu:
       for path in env['PATH'].split(os.pathsep):
         if os.path.exists(os.path.join(path, 'cl.exe')):
           vc_bin_dir = os.path.realpath(path)
@@ -143,7 +143,7 @@
                                   sdk_dir=win_sdk_path)
       env['INCLUDE'] = additional_includes + env['INCLUDE']
     env_block = _FormatAsEnvironmentBlock(env)
-    with open('environment.' + arch, 'wb') as f:
+    with open('environment.' + cpu, 'wb') as f:
       f.write(env_block)
 
   assert vc_bin_dir
diff --git a/build/vs_toolchain.py b/build/vs_toolchain.py
index 6f49e7c..5b175eb1 100644
--- a/build/vs_toolchain.py
+++ b/build/vs_toolchain.py
@@ -129,11 +129,11 @@
                        source_x64)
 
 
-def CopyDlls(target_dir, configuration, cpu_arch):
+def CopyDlls(target_dir, configuration, target_cpu):
   """Copy the VS runtime DLLs into the requested directory as needed.
 
   configuration is one of 'Debug' or 'Release'.
-  cpu_arch is one of 'x86' or 'x64'.
+  target_cpu is one of 'x86' or 'x64'.
 
   The debug configuration gets both the debug and release DLLs; the
   release config only the latter.
@@ -143,7 +143,7 @@
     return
 
   x64_runtime, x86_runtime = vs2013_runtime_dll_dirs
-  runtime_dir = x64_runtime if cpu_arch == 'x64' else x86_runtime
+  runtime_dir = x64_runtime if target_cpu == 'x64' else x86_runtime
   _CopyRuntime(target_dir, runtime_dir, 'msvc%s120.dll')
   if configuration == 'Debug':
     _CopyRuntime(target_dir, runtime_dir, 'msvc%s120d.dll')
diff --git a/build/win/install-build-deps.py b/build/win/install-build-deps.py
deleted file mode 100755
index d9e50b6e..0000000
--- a/build/win/install-build-deps.py
+++ /dev/null
@@ -1,47 +0,0 @@
-#!/usr/bin/env python
-# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import shutil
-import sys
-import os
-
-def patch_msbuild():
-  """VS2010 MSBuild has a ULDI bug that we patch here. See http://goo.gl/Pn8tj.
-  """
-  source_path = os.path.join(os.environ['ProgramFiles(x86)'],
-                             "MSBuild",
-                             "Microsoft.Cpp",
-                             "v4.0",
-                             "Microsoft.CppBuild.targets")
-  backup_path = source_path + ".backup"
-  if not os.path.exists(backup_path):
-    try:
-      print "Backing up %s..." % source_path
-      shutil.copyfile(source_path, backup_path)
-    except IOError:
-      print "Could not back up %s to %s. Run as Administrator?" % (
-          source_path, backup_path)
-      return 1
-
-  source = open(source_path).read()
-  base = ('''<Target Name="GetResolvedLinkObjs" Returns="@(ObjFullPath)" '''
-          '''DependsOnTargets="$(CommonBuildOnlyTargets);ComputeCLOutputs;'''
-          '''ResolvedLinkObjs"''')
-  find = base + '>'
-  replace = base + ''' Condition="'$(ConfigurationType)'=='StaticLibrary'">'''
-  result = source.replace(find, replace)
-
-  if result != source:
-    open(source_path, "w").write(result)
-    print "Patched %s." % source_path
-  return 0
-
-
-def main():
-  return patch_msbuild()
-
-
-if __name__ == "__main__":
-  sys.exit(main())
diff --git a/cc/BUILD.gn b/cc/BUILD.gn
index dafa89a..70a4092 100644
--- a/cc/BUILD.gn
+++ b/cc/BUILD.gn
@@ -26,8 +26,8 @@
     "animation/layer_animation_value_provider.h",
     "animation/scroll_offset_animation_curve.cc",
     "animation/scroll_offset_animation_curve.h",
-    "animation/scrollbar_animation_controller.h",
     "animation/scrollbar_animation_controller.cc",
+    "animation/scrollbar_animation_controller.h",
     "animation/scrollbar_animation_controller_linear_fade.cc",
     "animation/scrollbar_animation_controller_linear_fade.h",
     "animation/scrollbar_animation_controller_thinning.cc",
@@ -94,12 +94,12 @@
     "debug/layer_tree_debug_state.h",
     "debug/micro_benchmark.cc",
     "debug/micro_benchmark.h",
-    "debug/micro_benchmark_impl.cc",
-    "debug/micro_benchmark_impl.h",
     "debug/micro_benchmark_controller.cc",
     "debug/micro_benchmark_controller.h",
     "debug/micro_benchmark_controller_impl.cc",
     "debug/micro_benchmark_controller_impl.h",
+    "debug/micro_benchmark_impl.cc",
+    "debug/micro_benchmark_impl.h",
     "debug/paint_time_counter.cc",
     "debug/paint_time_counter.h",
     "debug/picture_debug_util.cc",
@@ -125,10 +125,10 @@
     "debug/unittest_only_benchmark_impl.h",
     "input/input_handler.cc",
     "input/input_handler.h",
-    "input/page_scale_animation.cc",
-    "input/page_scale_animation.h",
     "input/layer_selection_bound.cc",
     "input/layer_selection_bound.h",
+    "input/page_scale_animation.cc",
+    "input/page_scale_animation.h",
     "input/scroll_elasticity_helper.cc",
     "input/scroll_elasticity_helper.h",
     "input/selection_bound_type.h",
@@ -308,8 +308,8 @@
     "quads/draw_quad.h",
     "quads/io_surface_draw_quad.cc",
     "quads/io_surface_draw_quad.h",
-    "quads/largest_draw_quad.h",
     "quads/largest_draw_quad.cc",
+    "quads/largest_draw_quad.h",
     "quads/list_container.cc",
     "quads/list_container.h",
     "quads/picture_draw_quad.cc",
@@ -336,10 +336,10 @@
     "quads/yuv_video_draw_quad.h",
     "resources/bitmap_content_layer_updater.cc",
     "resources/bitmap_content_layer_updater.h",
-    "resources/bitmap_tile_task_worker_pool.cc",
-    "resources/bitmap_tile_task_worker_pool.h",
     "resources/bitmap_skpicture_content_layer_updater.cc",
     "resources/bitmap_skpicture_content_layer_updater.h",
+    "resources/bitmap_tile_task_worker_pool.cc",
+    "resources/bitmap_tile_task_worker_pool.h",
     "resources/clip_display_item.cc",
     "resources/clip_display_item.h",
     "resources/clip_path_display_item.cc",
@@ -403,19 +403,13 @@
     "resources/raster_source.h",
     "resources/raster_source_helper.cc",
     "resources/raster_source_helper.h",
+    "resources/raster_tile_priority_queue.cc",
+    "resources/raster_tile_priority_queue.h",
     "resources/raster_tile_priority_queue_all.cc",
     "resources/raster_tile_priority_queue_all.h",
     "resources/raster_tile_priority_queue_required.cc",
     "resources/raster_tile_priority_queue_required.h",
-    "resources/raster_tile_priority_queue.cc",
-    "resources/raster_tile_priority_queue.h",
     "resources/rasterizer.h",
-    "resources/software_rasterizer.cc",
-    "resources/software_rasterizer.h",
-    "resources/tile_task_worker_pool.cc",
-    "resources/tile_task_worker_pool.h",
-    "resources/tile_task_runner.cc",
-    "resources/tile_task_runner.h",
     "resources/release_callback.h",
     "resources/resource.cc",
     "resources/resource.h",
@@ -447,6 +441,8 @@
     "resources/single_release_callback_impl.h",
     "resources/skpicture_content_layer_updater.cc",
     "resources/skpicture_content_layer_updater.h",
+    "resources/software_rasterizer.cc",
+    "resources/software_rasterizer.h",
     "resources/task_graph_runner.cc",
     "resources/task_graph_runner.h",
     "resources/texture_mailbox.cc",
@@ -463,6 +459,10 @@
     "resources/tile_manager.h",
     "resources/tile_priority.cc",
     "resources/tile_priority.h",
+    "resources/tile_task_runner.cc",
+    "resources/tile_task_runner.h",
+    "resources/tile_task_worker_pool.cc",
+    "resources/tile_task_worker_pool.h",
     "resources/tiling_set_eviction_queue.cc",
     "resources/tiling_set_eviction_queue.h",
     "resources/tiling_set_raster_queue_all.cc",
@@ -536,10 +536,8 @@
     "trees/tree_synchronizer.h",
   ]
 
-  if (is_win) {
-    # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
-    cflags = [ "/wd4267" ]  # size_t -> int
-  }
+  # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+  configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
 
   public_deps = [
     "//skia",
@@ -572,6 +570,8 @@
     "test/animation_test_common.h",
     "test/begin_frame_args_test.cc",
     "test/begin_frame_args_test.h",
+    "test/failure_output_surface.cc",
+    "test/failure_output_surface.h",
     "test/fake_content_layer.cc",
     "test/fake_content_layer.h",
     "test/fake_content_layer_client.cc",
@@ -626,12 +626,8 @@
     "test/fake_ui_resource_layer_tree_host_impl.h",
     "test/fake_video_frame_provider.cc",
     "test/fake_video_frame_provider.h",
-    "test/failure_output_surface.cc",
-    "test/failure_output_surface.h",
     "test/geometry_test_utils.cc",
     "test/geometry_test_utils.h",
-    "test/test_in_process_context_provider.cc",
-    "test/test_in_process_context_provider.h",
     "test/impl_side_painting_settings.h",
     "test/layer_test_common.cc",
     "test/layer_test_common.h",
@@ -682,6 +678,8 @@
     "test/test_gpu_memory_buffer_manager.h",
     "test/test_image_factory.cc",
     "test/test_image_factory.h",
+    "test/test_in_process_context_provider.cc",
+    "test/test_in_process_context_provider.h",
     "test/test_now_source.cc",
     "test/test_now_source.h",
     "test/test_occlusion_tracker.h",
@@ -697,6 +695,8 @@
     "test/tiled_layer_test_common.h",
   ]
 
+  configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
+
   include_dirs = [
     ".",
     "test",
@@ -757,8 +757,8 @@
       "layers/delegated_frame_resource_collection_unittest.cc",
       "layers/delegated_renderer_layer_impl_unittest.cc",
       "layers/delegated_renderer_layer_unittest.cc",
-      "layers/heads_up_display_unittest.cc",
       "layers/heads_up_display_layer_impl_unittest.cc",
+      "layers/heads_up_display_unittest.cc",
       "layers/io_surface_layer_impl_unittest.cc",
       "layers/layer_impl_unittest.cc",
       "layers/layer_iterator_unittest.cc",
@@ -772,15 +772,15 @@
       "layers/picture_image_layer_unittest.cc",
       "layers/picture_layer_impl_unittest.cc",
       "layers/picture_layer_unittest.cc",
-      "layers/render_surface_unittest.cc",
       "layers/render_surface_impl_unittest.cc",
+      "layers/render_surface_unittest.cc",
       "layers/scrollbar_layer_unittest.cc",
       "layers/solid_color_layer_impl_unittest.cc",
       "layers/solid_color_scrollbar_layer_impl_unittest.cc",
-      "layers/surface_layer_unittest.cc",
       "layers/surface_layer_impl_unittest.cc",
-      "layers/texture_layer_unittest.cc",
+      "layers/surface_layer_unittest.cc",
       "layers/texture_layer_impl_unittest.cc",
+      "layers/texture_layer_unittest.cc",
       "layers/tiled_layer_impl_unittest.cc",
       "layers/tiled_layer_unittest.cc",
       "layers/ui_resource_layer_impl_unittest.cc",
@@ -806,8 +806,8 @@
       "resources/picture_pile_impl_unittest.cc",
       "resources/picture_pile_unittest.cc",
       "resources/picture_unittest.cc",
+      "resources/platform_color_unittest.cc",
       "resources/prioritized_resource_unittest.cc",
-      "resources/tile_task_worker_pool_unittest.cc",
       "resources/resource_provider_unittest.cc",
       "resources/resource_update_controller_unittest.cc",
       "resources/scoped_gpu_raster_unittest.cc",
@@ -817,6 +817,7 @@
       "resources/texture_uploader_unittest.cc",
       "resources/tile_manager_unittest.cc",
       "resources/tile_priority_unittest.cc",
+      "resources/tile_task_worker_pool_unittest.cc",
       "resources/video_resource_updater_unittest.cc",
       "scheduler/begin_frame_source_unittest.cc",
       "scheduler/delay_based_time_source_unittest.cc",
@@ -832,7 +833,6 @@
       "trees/layer_tree_host_pixeltest_blending.cc",
       "trees/layer_tree_host_pixeltest_filters.cc",
       "trees/layer_tree_host_pixeltest_masks.cc",
-      "trees/layer_tree_host_pixeltest_on_demand_raster.cc",
       "trees/layer_tree_host_pixeltest_readback.cc",
       "trees/layer_tree_host_pixeltest_synchronous.cc",
       "trees/layer_tree_host_unittest.cc",
@@ -841,8 +841,8 @@
       "trees/layer_tree_host_unittest_copyrequest.cc",
       "trees/layer_tree_host_unittest_damage.cc",
       "trees/layer_tree_host_unittest_delegated.cc",
-      "trees/layer_tree_host_unittest_occlusion.cc",
       "trees/layer_tree_host_unittest_no_message_loop.cc",
+      "trees/layer_tree_host_unittest_occlusion.cc",
       "trees/layer_tree_host_unittest_picture.cc",
       "trees/layer_tree_host_unittest_proxy.cc",
       "trees/layer_tree_host_unittest_scroll.cc",
@@ -861,10 +861,12 @@
       "surfaces/surfaces_pixeltest.cc",
 
       # Setup.
-      "test/run_all_unittests.cc",
       "test/cc_test_suite.cc",
+      "test/run_all_unittests.cc",
     ]
 
+    configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
+
     deps = [
       ":cc",
       ":test_support",
@@ -893,9 +895,9 @@
     "layers/picture_layer_impl_perftest.cc",
     "resources/picture_layer_tiling_perftest.cc",
     "resources/picture_pile_impl_perftest.cc",
-    "resources/tile_task_worker_pool_perftest.cc",
     "resources/task_graph_runner_perftest.cc",
     "resources/tile_manager_perftest.cc",
+    "resources/tile_task_worker_pool_perftest.cc",
     "test/cc_test_suite.cc",
     "test/run_all_perftests.cc",
     "trees/layer_tree_host_common_perftest.cc",
@@ -903,6 +905,8 @@
     "trees/occlusion_tracker_perftest.cc",
   ]
 
+  configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
+
   deps = [
     ":cc",
     ":test_support",
diff --git a/cc/animation/animation.cc b/cc/animation/animation.cc
index 48ebc74b..96cb40ae 100644
--- a/cc/animation/animation.cc
+++ b/cc/animation/animation.cc
@@ -14,32 +14,28 @@
 namespace {
 
 // This should match the RunState enum.
-static const char* const s_runStateNames[] = {
-  "WaitingForTargetAvailability",
-  "WaitingForDeletion",
-  "Starting",
-  "Running",
-  "Paused",
-  "Finished",
-  "Aborted"
-};
+static const char* const s_runStateNames[] = {"WAITING_FOR_TARGET_AVAILABILITY",
+                                              "WAITING_FOR_DELETION",
+                                              "STARTING",
+                                              "RUNNING",
+                                              "PAUSED",
+                                              "FINISHED",
+                                              "ABORTED"};
 
-static_assert(static_cast<int>(cc::Animation::RunStateEnumSize) ==
-              arraysize(s_runStateNames),
+static_assert(static_cast<int>(cc::Animation::LAST_RUN_STATE) + 1 ==
+                  arraysize(s_runStateNames),
               "RunStateEnumSize should equal the number of elements in "
               "s_runStateNames");
 
 // This should match the TargetProperty enum.
-static const char* const s_targetPropertyNames[] = {
-  "Transform",
-  "Opacity",
-  "Filter",
-  "ScrollOffset",
-  "BackgroundColor"
-};
+static const char* const s_targetPropertyNames[] = {"TRANSFORM",
+                                                    "OPACITY",
+                                                    "FILTER",
+                                                    "SCROLL_OFFSET",
+                                                    "BACKGROUND_COLOR"};
 
-static_assert(static_cast<int>(cc::Animation::TargetPropertyEnumSize) ==
-              arraysize(s_targetPropertyNames),
+static_assert(static_cast<int>(cc::Animation::LAST_TARGET_PROPERTY) + 1 ==
+                  arraysize(s_targetPropertyNames),
               "TargetPropertyEnumSize should equal the number of elements in "
               "s_targetPropertyNames");
 
@@ -65,12 +61,12 @@
       id_(animation_id),
       group_(group_id),
       target_property_(target_property),
-      run_state_(WaitingForTargetAvailability),
+      run_state_(WAITING_FOR_TARGET_AVAILABILITY),
       iterations_(1),
       iteration_start_(0),
-      direction_(Normal),
+      direction_(DIRECTION_NORMAL),
       playback_rate_(1),
-      fill_mode_(FillModeBoth),
+      fill_mode_(FILL_MODE_BOTH),
       needs_synchronized_start_time_(false),
       received_finished_event_(false),
       suspended_(false),
@@ -81,8 +77,8 @@
 }
 
 Animation::~Animation() {
-  if (run_state_ == Running || run_state_ == Paused)
-    SetRunState(Aborted, base::TimeTicks());
+  if (run_state_ == RUNNING || run_state_ == PAUSED)
+    SetRunState(ABORTED, base::TimeTicks());
 }
 
 void Animation::SetRunState(RunState run_state,
@@ -97,10 +93,10 @@
                  s_targetPropertyNames[target_property_],
                  group_);
 
-  bool is_waiting_to_start = run_state_ == WaitingForTargetAvailability ||
-                             run_state_ == Starting;
+  bool is_waiting_to_start =
+      run_state_ == WAITING_FOR_TARGET_AVAILABILITY || run_state_ == STARTING;
 
-  if (is_controlling_instance_ && is_waiting_to_start && run_state == Running) {
+  if (is_controlling_instance_ && is_waiting_to_start && run_state == RUNNING) {
     TRACE_EVENT_ASYNC_BEGIN1(
         "cc", "Animation", this, "Name", TRACE_STR_COPY(name_buffer));
   }
@@ -109,9 +105,9 @@
 
   const char* old_run_state_name = s_runStateNames[run_state_];
 
-  if (run_state == Running && run_state_ == Paused)
+  if (run_state == RUNNING && run_state_ == PAUSED)
     total_paused_time_ += (monotonic_time - pause_time_);
-  else if (run_state == Paused)
+  else if (run_state == PAUSED)
     pause_time_ = monotonic_time;
   run_state_ = run_state;
 
@@ -137,13 +133,13 @@
 }
 
 void Animation::Suspend(base::TimeTicks monotonic_time) {
-  SetRunState(Paused, monotonic_time);
+  SetRunState(PAUSED, monotonic_time);
   suspended_ = true;
 }
 
 void Animation::Resume(base::TimeTicks monotonic_time) {
   suspended_ = false;
-  SetRunState(Running, monotonic_time);
+  SetRunState(RUNNING, monotonic_time);
 }
 
 bool Animation::IsFinishedAt(base::TimeTicks monotonic_time) const {
@@ -156,7 +152,7 @@
   if (playback_rate_ == 0)
     return false;
 
-  return run_state_ == Running && iterations_ >= 0 &&
+  return run_state_ == RUNNING && iterations_ >= 0 &&
          TimeUtil::Scale(curve_->Duration(),
                          iterations_ / std::abs(playback_rate_)) <=
              (monotonic_time + time_offset_ - start_time_ - total_paused_time_);
@@ -164,7 +160,7 @@
 
 bool Animation::InEffect(base::TimeTicks monotonic_time) const {
   return ConvertToActiveTime(monotonic_time) >= base::TimeDelta() ||
-         (fill_mode_ == FillModeBoth || fill_mode_ == FillModeBackwards);
+         (fill_mode_ == FILL_MODE_BOTH || fill_mode_ == FILL_MODE_BACKWARDS);
 }
 
 base::TimeDelta Animation::ConvertToActiveTime(
@@ -172,7 +168,7 @@
   base::TimeTicks trimmed = monotonic_time + time_offset_;
 
   // If we're paused, time is 'stuck' at the pause time.
-  if (run_state_ == Paused)
+  if (run_state_ == PAUSED)
     trimmed = pause_time_;
 
   // Returned time should always be relative to the start time and should
@@ -181,7 +177,7 @@
 
   // If we're just starting or we're waiting on receiving a start time,
   // time is 'stuck' at the initial state.
-  if ((run_state_ == Starting && !has_set_start_time()) ||
+  if ((run_state_ == STARTING && !has_set_start_time()) ||
       needs_synchronized_start_time())
     trimmed = base::TimeTicks() + time_offset_;
 
@@ -247,9 +243,10 @@
 
   // Check if we are running the animation in reverse direction for the current
   // iteration
-  bool reverse = (direction_ == Reverse) ||
-                 (direction_ == Alternate && iteration % 2 == 1) ||
-                 (direction_ == AlternateReverse && iteration % 2 == 0);
+  bool reverse =
+      (direction_ == DIRECTION_REVERSE) ||
+      (direction_ == DIRECTION_ALTERNATE && iteration % 2 == 1) ||
+      (direction_ == DIRECTION_ALTERNATE_REVERSE && iteration % 2 == 0);
 
   // If we are running the animation in reverse direction, reverse the result
   if (reverse)
@@ -280,8 +277,8 @@
 void Animation::PushPropertiesTo(Animation* other) const {
   // Currently, we only push changes due to pausing and resuming animations on
   // the main thread.
-  if (run_state_ == Animation::Paused ||
-      other->run_state_ == Animation::Paused) {
+  if (run_state_ == Animation::PAUSED ||
+      other->run_state_ == Animation::PAUSED) {
     other->run_state_ = run_state_;
     other->pause_time_ = pause_time_;
     other->total_paused_time_ = total_paused_time_;
diff --git a/cc/animation/animation.h b/cc/animation/animation.h
index 9342f1a..2677fde 100644
--- a/cc/animation/animation.h
+++ b/cc/animation/animation.h
@@ -19,43 +19,48 @@
 // loop count, last pause time, and the total time spent paused.
 class CC_EXPORT Animation {
  public:
-  // Animations begin in the 'WaitingForTargetAvailability' state. An Animation
-  // waiting for target availibility will run as soon as its target property
-  // is free (and all the animations animating with it are also able to run).
-  // When this time arrives, the controller will move the animation into the
-  // Starting state, and then into the Running state. Running animations may
-  // toggle between Running and Paused, and may be stopped by moving into either
-  // the Aborted or Finished states. A Finished animation was allowed to run to
-  // completion, but an Aborted animation was not.
+  // Animations begin in the 'WAITING_FOR_TARGET_AVAILABILITY' state. An
+  // Animation waiting for target availibility will run as soon as its target
+  // property is free (and all the animations animating with it are also able to
+  // run). When this time arrives, the controller will move the animation into
+  // the STARTING state, and then into the RUNNING state. RUNNING animations may
+  // toggle between RUNNING and PAUSED, and may be stopped by moving into either
+  // the ABORTED or FINISHED states. A FINISHED animation was allowed to run to
+  // completion, but an ABORTED animation was not.
   enum RunState {
-    WaitingForTargetAvailability = 0,
-    WaitingForDeletion,
-    Starting,
-    Running,
-    Paused,
-    Finished,
-    Aborted,
+    WAITING_FOR_TARGET_AVAILABILITY = 0,
+    WAITING_FOR_DELETION,
+    STARTING,
+    RUNNING,
+    PAUSED,
+    FINISHED,
+    ABORTED,
     // This sentinel must be last.
-    RunStateEnumSize
+    LAST_RUN_STATE = ABORTED
   };
 
   enum TargetProperty {
-    Transform = 0,
-    Opacity,
-    Filter,
-    ScrollOffset,
-    BackgroundColor,
+    TRANSFORM = 0,
+    OPACITY,
+    FILTER,
+    SCROLL_OFFSET,
+    BACKGROUND_COLOR,
     // This sentinel must be last.
-    TargetPropertyEnumSize
+    LAST_TARGET_PROPERTY = BACKGROUND_COLOR
   };
 
-  enum Direction { Normal, Reverse, Alternate, AlternateReverse };
+  enum Direction {
+    DIRECTION_NORMAL,
+    DIRECTION_REVERSE,
+    DIRECTION_ALTERNATE,
+    DIRECTION_ALTERNATE_REVERSE
+  };
 
   enum FillMode {
-    FillModeNone,
-    FillModeForwards,
-    FillModeBackwards,
-    FillModeBoth
+    FILL_MODE_NONE,
+    FILL_MODE_FORWARDS,
+    FILL_MODE_BACKWARDS,
+    FILL_MODE_BOTH
   };
 
   static scoped_ptr<Animation> Create(scoped_ptr<AnimationCurve> curve,
@@ -111,9 +116,8 @@
 
   bool IsFinishedAt(base::TimeTicks monotonic_time) const;
   bool is_finished() const {
-    return run_state_ == Finished ||
-        run_state_ == Aborted ||
-        run_state_ == WaitingForDeletion;
+    return run_state_ == FINISHED || run_state_ == ABORTED ||
+           run_state_ == WAITING_FOR_DELETION;
   }
 
   bool InEffect(base::TimeTicks monotonic_time) const;
@@ -131,7 +135,7 @@
     needs_synchronized_start_time_ = needs_synchronized_start_time;
   }
 
-  // This is true for animations running on the main thread when the Finished
+  // This is true for animations running on the main thread when the FINISHED
   // event sent by the corresponding impl animation has been received.
   bool received_finished_event() const {
     return received_finished_event_;
@@ -227,10 +231,10 @@
   // When pushed from a main-thread controller to a compositor-thread
   // controller, an animation will initially only affect pending observers
   // (corresponding to layers in the pending tree). Animations that only
-  // affect pending observers are able to reach the Starting state and tick
+  // affect pending observers are able to reach the STARTING state and tick
   // pending observers, but cannot proceed any further and do not tick active
   // observers. After activation, such animations affect both kinds of observers
-  // and are able to proceed past the Starting state. When the removal of
+  // and are able to proceed past the STARTING state. When the removal of
   // an animation is pushed from a main-thread controller to a
   // compositor-thread controller, this initially only makes the animation
   // stop affecting pending observers. After activation, such animations no
diff --git a/cc/animation/animation_curve.cc b/cc/animation/animation_curve.cc
index 3acff5d1..9e8cae9 100644
--- a/cc/animation/animation_curve.cc
+++ b/cc/animation/animation_curve.cc
@@ -10,48 +10,50 @@
 namespace cc {
 
 const ColorAnimationCurve* AnimationCurve::ToColorAnimationCurve() const {
-  DCHECK(Type() == AnimationCurve::Color);
+  DCHECK(Type() == AnimationCurve::COLOR);
   return static_cast<const ColorAnimationCurve*>(this);
 }
 
-AnimationCurve::CurveType ColorAnimationCurve::Type() const { return Color; }
+AnimationCurve::CurveType ColorAnimationCurve::Type() const {
+  return COLOR;
+}
 
 const FloatAnimationCurve* AnimationCurve::ToFloatAnimationCurve() const {
-  DCHECK(Type() == AnimationCurve::Float);
+  DCHECK(Type() == AnimationCurve::FLOAT);
   return static_cast<const FloatAnimationCurve*>(this);
 }
 
 AnimationCurve::CurveType FloatAnimationCurve::Type() const {
-  return Float;
+  return FLOAT;
 }
 
 const TransformAnimationCurve* AnimationCurve::ToTransformAnimationCurve()
     const {
-  DCHECK(Type() == AnimationCurve::Transform);
+  DCHECK(Type() == AnimationCurve::TRANSFORM);
   return static_cast<const TransformAnimationCurve*>(this);
 }
 
 AnimationCurve::CurveType TransformAnimationCurve::Type() const {
-  return Transform;
+  return TRANSFORM;
 }
 
 const FilterAnimationCurve* AnimationCurve::ToFilterAnimationCurve() const {
-  DCHECK(Type() == AnimationCurve::Filter);
+  DCHECK(Type() == AnimationCurve::FILTER);
   return static_cast<const FilterAnimationCurve*>(this);
 }
 
 AnimationCurve::CurveType FilterAnimationCurve::Type() const {
-  return Filter;
+  return FILTER;
 }
 
 const ScrollOffsetAnimationCurve* AnimationCurve::ToScrollOffsetAnimationCurve()
     const {
-  DCHECK(Type() == AnimationCurve::ScrollOffset);
+  DCHECK(Type() == AnimationCurve::SCROLL_OFFSET);
   return static_cast<const ScrollOffsetAnimationCurve*>(this);
 }
 
 ScrollOffsetAnimationCurve* AnimationCurve::ToScrollOffsetAnimationCurve() {
-  DCHECK(Type() == AnimationCurve::ScrollOffset);
+  DCHECK(Type() == AnimationCurve::SCROLL_OFFSET);
   return static_cast<ScrollOffsetAnimationCurve*>(this);
 }
 
diff --git a/cc/animation/animation_curve.h b/cc/animation/animation_curve.h
index 74ce11b6..1074203f 100644
--- a/cc/animation/animation_curve.h
+++ b/cc/animation/animation_curve.h
@@ -27,7 +27,7 @@
 // An animation curve is a function that returns a value given a time.
 class CC_EXPORT AnimationCurve {
  public:
-  enum CurveType { Color, Float, Transform, Filter, ScrollOffset };
+  enum CurveType { COLOR, FLOAT, TRANSFORM, FILTER, SCROLL_OFFSET };
 
   virtual ~AnimationCurve() {}
 
diff --git a/cc/animation/animation_events.h b/cc/animation/animation_events.h
index e40ca3f..d268162 100644
--- a/cc/animation/animation_events.h
+++ b/cc/animation/animation_events.h
@@ -15,7 +15,7 @@
 namespace cc {
 
 struct CC_EXPORT AnimationEvent {
-  enum Type { Started, Finished, Aborted, PropertyUpdate };
+  enum Type { STARTED, FINISHED, ABORTED, PROPERTY_UPDATE };
 
   AnimationEvent(Type type,
                  int layer_id,
diff --git a/cc/animation/animation_unittest.cc b/cc/animation/animation_unittest.cc
index 2522f19..9345c6b1 100644
--- a/cc/animation/animation_unittest.cc
+++ b/cc/animation/animation_unittest.cc
@@ -23,9 +23,7 @@
                                       double playback_rate) {
   scoped_ptr<Animation> to_return(
       Animation::Create(make_scoped_ptr(new FakeFloatAnimationCurve(duration)),
-                        0,
-                        1,
-                        Animation::Opacity));
+                        0, 1, Animation::OPACITY));
   to_return->set_iterations(iterations);
   to_return->set_playback_rate(playback_rate);
   return to_return.Pass();
@@ -93,7 +91,7 @@
 
 TEST(AnimationTest, TrimTimeReverse) {
   scoped_ptr<Animation> anim(CreateAnimation(-1));
-  anim->set_direction(Animation::Reverse);
+  anim->set_direction(Animation::DIRECTION_REVERSE);
   EXPECT_EQ(
       1.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0)).InSecondsF());
   EXPECT_EQ(0.75, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.25))
@@ -110,7 +108,7 @@
 
 TEST(AnimationTest, TrimTimeAlternateInfiniteIterations) {
   scoped_ptr<Animation> anim(CreateAnimation(-1));
-  anim->set_direction(Animation::Alternate);
+  anim->set_direction(Animation::DIRECTION_ALTERNATE);
   EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0))
                      .InSecondsF());
   EXPECT_EQ(0.25, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.25))
@@ -127,7 +125,7 @@
 
 TEST(AnimationTest, TrimTimeAlternateOneIteration) {
   scoped_ptr<Animation> anim(CreateAnimation(1));
-  anim->set_direction(Animation::Alternate);
+  anim->set_direction(Animation::DIRECTION_ALTERNATE);
   EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0))
                      .InSecondsF());
   EXPECT_EQ(0.25, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.25))
@@ -144,7 +142,7 @@
 
 TEST(AnimationTest, TrimTimeAlternateTwoIterations) {
   scoped_ptr<Animation> anim(CreateAnimation(2));
-  anim->set_direction(Animation::Alternate);
+  anim->set_direction(Animation::DIRECTION_ALTERNATE);
   EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0))
                      .InSecondsF());
   EXPECT_EQ(0.25, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.25))
@@ -167,7 +165,7 @@
 
 TEST(AnimationTest, TrimTimeAlternateTwoHalfIterations) {
   scoped_ptr<Animation> anim(CreateAnimation(2.5));
-  anim->set_direction(Animation::Alternate);
+  anim->set_direction(Animation::DIRECTION_ALTERNATE);
   EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0))
                      .InSecondsF());
   EXPECT_EQ(0.25, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.25))
@@ -194,7 +192,7 @@
 
 TEST(AnimationTest, TrimTimeAlternateReverseInfiniteIterations) {
   scoped_ptr<Animation> anim(CreateAnimation(-1));
-  anim->set_direction(Animation::AlternateReverse);
+  anim->set_direction(Animation::DIRECTION_ALTERNATE_REVERSE);
   EXPECT_EQ(1.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0))
                      .InSecondsF());
   EXPECT_EQ(0.75, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.25))
@@ -211,7 +209,7 @@
 
 TEST(AnimationTest, TrimTimeAlternateReverseOneIteration) {
   scoped_ptr<Animation> anim(CreateAnimation(1));
-  anim->set_direction(Animation::AlternateReverse);
+  anim->set_direction(Animation::DIRECTION_ALTERNATE_REVERSE);
   EXPECT_EQ(1.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0))
                      .InSecondsF());
   EXPECT_EQ(0.75, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.25))
@@ -228,7 +226,7 @@
 
 TEST(AnimationTest, TrimTimeAlternateReverseTwoIterations) {
   scoped_ptr<Animation> anim(CreateAnimation(2));
-  anim->set_direction(Animation::AlternateReverse);
+  anim->set_direction(Animation::DIRECTION_ALTERNATE_REVERSE);
   EXPECT_EQ(1.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0))
                      .InSecondsF());
   EXPECT_EQ(0.75, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.25))
@@ -267,7 +265,7 @@
 TEST(AnimationTest, TrimTimeStartTimeReverse) {
   scoped_ptr<Animation> anim(CreateAnimation(1));
   anim->set_start_time(TicksFromSecondsF(4));
-  anim->set_direction(Animation::Reverse);
+  anim->set_direction(Animation::DIRECTION_REVERSE);
   EXPECT_EQ(
       0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0)).InSecondsF());
   EXPECT_EQ(1.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(4.0))
@@ -298,7 +296,7 @@
   scoped_ptr<Animation> anim(CreateAnimation(1));
   anim->set_time_offset(TimeDelta::FromMilliseconds(4000));
   anim->set_start_time(TicksFromSecondsF(4));
-  anim->set_direction(Animation::Reverse);
+  anim->set_direction(Animation::DIRECTION_REVERSE);
   EXPECT_EQ(1.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0))
                      .InSecondsF());
   EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.5))
@@ -326,7 +324,7 @@
 TEST(AnimationTest, TrimTimeNegativeTimeOffsetReverse) {
   scoped_ptr<Animation> anim(CreateAnimation(1));
   anim->set_time_offset(TimeDelta::FromMilliseconds(-4000));
-  anim->set_direction(Animation::Reverse);
+  anim->set_direction(Animation::DIRECTION_REVERSE);
 
   EXPECT_EQ(
       0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0)).InSecondsF());
@@ -340,15 +338,15 @@
 
 TEST(AnimationTest, TrimTimePauseResume) {
   scoped_ptr<Animation> anim(CreateAnimation(1));
-  anim->SetRunState(Animation::Running, TicksFromSecondsF(0.0));
+  anim->SetRunState(Animation::RUNNING, TicksFromSecondsF(0.0));
   EXPECT_EQ(
       0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0)).InSecondsF());
   EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.5))
                      .InSecondsF());
-  anim->SetRunState(Animation::Paused, TicksFromSecondsF(0.5));
+  anim->SetRunState(Animation::PAUSED, TicksFromSecondsF(0.5));
   EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1024.0))
                      .InSecondsF());
-  anim->SetRunState(Animation::Running, TicksFromSecondsF(1024.0));
+  anim->SetRunState(Animation::RUNNING, TicksFromSecondsF(1024.0));
   EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1024.0))
                      .InSecondsF());
   EXPECT_EQ(1, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1024.5))
@@ -357,16 +355,16 @@
 
 TEST(AnimationTest, TrimTimePauseResumeReverse) {
   scoped_ptr<Animation> anim(CreateAnimation(1));
-  anim->set_direction(Animation::Reverse);
-  anim->SetRunState(Animation::Running, TicksFromSecondsF(0.0));
+  anim->set_direction(Animation::DIRECTION_REVERSE);
+  anim->SetRunState(Animation::RUNNING, TicksFromSecondsF(0.0));
   EXPECT_EQ(1.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0))
                      .InSecondsF());
   EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.5))
                      .InSecondsF());
-  anim->SetRunState(Animation::Paused, TicksFromSecondsF(0.25));
+  anim->SetRunState(Animation::PAUSED, TicksFromSecondsF(0.25));
   EXPECT_EQ(0.75, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1024.0))
                       .InSecondsF());
-  anim->SetRunState(Animation::Running, TicksFromSecondsF(1024.0));
+  anim->SetRunState(Animation::RUNNING, TicksFromSecondsF(1024.0));
   EXPECT_EQ(0.75, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1024.0))
                       .InSecondsF());
   EXPECT_EQ(0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1024.75))
@@ -375,7 +373,7 @@
 
 TEST(AnimationTest, TrimTimeSuspendResume) {
   scoped_ptr<Animation> anim(CreateAnimation(1));
-  anim->SetRunState(Animation::Running, TicksFromSecondsF(0.0));
+  anim->SetRunState(Animation::RUNNING, TicksFromSecondsF(0.0));
   EXPECT_EQ(
       0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0)).InSecondsF());
   EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.5))
@@ -392,8 +390,8 @@
 
 TEST(AnimationTest, TrimTimeSuspendResumeReverse) {
   scoped_ptr<Animation> anim(CreateAnimation(1));
-  anim->set_direction(Animation::Reverse);
-  anim->SetRunState(Animation::Running, TicksFromSecondsF(0.0));
+  anim->set_direction(Animation::DIRECTION_REVERSE);
+  anim->SetRunState(Animation::RUNNING, TicksFromSecondsF(0.0));
   EXPECT_EQ(1.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0))
                      .InSecondsF());
   EXPECT_EQ(0.75, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.25))
@@ -410,7 +408,7 @@
 
 TEST(AnimationTest, TrimTimeZeroDuration) {
   scoped_ptr<Animation> anim(CreateAnimation(0, 0));
-  anim->SetRunState(Animation::Running, TicksFromSecondsF(0.0));
+  anim->SetRunState(Animation::RUNNING, TicksFromSecondsF(0.0));
   EXPECT_EQ(0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(-1.0))
                    .InSecondsF());
   EXPECT_EQ(
@@ -421,7 +419,7 @@
 
 TEST(AnimationTest, TrimTimeStarting) {
   scoped_ptr<Animation> anim(CreateAnimation(1, 5.0));
-  anim->SetRunState(Animation::Starting, TicksFromSecondsF(0.0));
+  anim->SetRunState(Animation::STARTING, TicksFromSecondsF(0.0));
   EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(-1.0))
                      .InSecondsF());
   EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0))
@@ -448,7 +446,7 @@
 
 TEST(AnimationTest, TrimTimeNeedsSynchronizedStartTime) {
   scoped_ptr<Animation> anim(CreateAnimation(1, 5.0));
-  anim->SetRunState(Animation::Running, TicksFromSecondsF(0.0));
+  anim->SetRunState(Animation::RUNNING, TicksFromSecondsF(0.0));
   anim->set_needs_synchronized_start_time(true);
   EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(-1.0))
                      .InSecondsF());
@@ -475,7 +473,7 @@
 
 TEST(AnimationTest, IsFinishedAtZeroIterations) {
   scoped_ptr<Animation> anim(CreateAnimation(0));
-  anim->SetRunState(Animation::Running, TicksFromSecondsF(0.0));
+  anim->SetRunState(Animation::RUNNING, TicksFromSecondsF(0.0));
   EXPECT_FALSE(anim->IsFinishedAt(TicksFromSecondsF(-1.0)));
   EXPECT_TRUE(anim->IsFinishedAt(TicksFromSecondsF(0.0)));
   EXPECT_TRUE(anim->IsFinishedAt(TicksFromSecondsF(1.0)));
@@ -483,7 +481,7 @@
 
 TEST(AnimationTest, IsFinishedAtOneIteration) {
   scoped_ptr<Animation> anim(CreateAnimation(1));
-  anim->SetRunState(Animation::Running, TicksFromSecondsF(0.0));
+  anim->SetRunState(Animation::RUNNING, TicksFromSecondsF(0.0));
   EXPECT_FALSE(anim->IsFinishedAt(TicksFromSecondsF(-1.0)));
   EXPECT_FALSE(anim->IsFinishedAt(TicksFromSecondsF(0.0)));
   EXPECT_TRUE(anim->IsFinishedAt(TicksFromSecondsF(1.0)));
@@ -492,7 +490,7 @@
 
 TEST(AnimationTest, IsFinishedAtInfiniteIterations) {
   scoped_ptr<Animation> anim(CreateAnimation(-1));
-  anim->SetRunState(Animation::Running, TicksFromSecondsF(0.0));
+  anim->SetRunState(Animation::RUNNING, TicksFromSecondsF(0.0));
   EXPECT_FALSE(anim->IsFinishedAt(TicksFromSecondsF(0.0)));
   EXPECT_FALSE(anim->IsFinishedAt(TicksFromSecondsF(0.5)));
   EXPECT_FALSE(anim->IsFinishedAt(TicksFromSecondsF(1.0)));
@@ -502,7 +500,7 @@
 TEST(AnimationTest, IsFinishedNegativeTimeOffset) {
   scoped_ptr<Animation> anim(CreateAnimation(1));
   anim->set_time_offset(TimeDelta::FromMilliseconds(-500));
-  anim->SetRunState(Animation::Running, TicksFromSecondsF(0.0));
+  anim->SetRunState(Animation::RUNNING, TicksFromSecondsF(0.0));
 
   EXPECT_FALSE(anim->IsFinishedAt(TicksFromSecondsF(-1.0)));
   EXPECT_FALSE(anim->IsFinishedAt(TicksFromSecondsF(0.0)));
@@ -516,7 +514,7 @@
 TEST(AnimationTest, IsFinishedPositiveTimeOffset) {
   scoped_ptr<Animation> anim(CreateAnimation(1));
   anim->set_time_offset(TimeDelta::FromMilliseconds(500));
-  anim->SetRunState(Animation::Running, TicksFromSecondsF(0.0));
+  anim->SetRunState(Animation::RUNNING, TicksFromSecondsF(0.0));
 
   EXPECT_FALSE(anim->IsFinishedAt(TicksFromSecondsF(-1.0)));
   EXPECT_FALSE(anim->IsFinishedAt(TicksFromSecondsF(0.0)));
@@ -526,58 +524,58 @@
 
 TEST(AnimationTest, IsFinishedAtNotRunning) {
   scoped_ptr<Animation> anim(CreateAnimation(0));
-  anim->SetRunState(Animation::Running, TicksFromSecondsF(0.0));
+  anim->SetRunState(Animation::RUNNING, TicksFromSecondsF(0.0));
   EXPECT_TRUE(anim->IsFinishedAt(TicksFromSecondsF(0.0)));
-  anim->SetRunState(Animation::Paused, TicksFromSecondsF(0.0));
+  anim->SetRunState(Animation::PAUSED, TicksFromSecondsF(0.0));
   EXPECT_FALSE(anim->IsFinishedAt(TicksFromSecondsF(0.0)));
-  anim->SetRunState(Animation::WaitingForTargetAvailability,
+  anim->SetRunState(Animation::WAITING_FOR_TARGET_AVAILABILITY,
                     TicksFromSecondsF(0.0));
   EXPECT_FALSE(anim->IsFinishedAt(TicksFromSecondsF(0.0)));
-  anim->SetRunState(Animation::Finished, TicksFromSecondsF(0.0));
+  anim->SetRunState(Animation::FINISHED, TicksFromSecondsF(0.0));
   EXPECT_TRUE(anim->IsFinishedAt(TicksFromSecondsF(0.0)));
-  anim->SetRunState(Animation::Aborted, TicksFromSecondsF(0.0));
+  anim->SetRunState(Animation::ABORTED, TicksFromSecondsF(0.0));
   EXPECT_TRUE(anim->IsFinishedAt(TicksFromSecondsF(0.0)));
 }
 
 TEST(AnimationTest, IsFinished) {
   scoped_ptr<Animation> anim(CreateAnimation(1));
-  anim->SetRunState(Animation::Running, TicksFromSecondsF(0.0));
+  anim->SetRunState(Animation::RUNNING, TicksFromSecondsF(0.0));
   EXPECT_FALSE(anim->is_finished());
-  anim->SetRunState(Animation::Paused, TicksFromSecondsF(0.0));
+  anim->SetRunState(Animation::PAUSED, TicksFromSecondsF(0.0));
   EXPECT_FALSE(anim->is_finished());
-  anim->SetRunState(Animation::WaitingForTargetAvailability,
+  anim->SetRunState(Animation::WAITING_FOR_TARGET_AVAILABILITY,
                     TicksFromSecondsF(0.0));
   EXPECT_FALSE(anim->is_finished());
-  anim->SetRunState(Animation::Finished, TicksFromSecondsF(0.0));
+  anim->SetRunState(Animation::FINISHED, TicksFromSecondsF(0.0));
   EXPECT_TRUE(anim->is_finished());
-  anim->SetRunState(Animation::Aborted, TicksFromSecondsF(0.0));
+  anim->SetRunState(Animation::ABORTED, TicksFromSecondsF(0.0));
   EXPECT_TRUE(anim->is_finished());
 }
 
 TEST(AnimationTest, IsFinishedNeedsSynchronizedStartTime) {
   scoped_ptr<Animation> anim(CreateAnimation(1));
-  anim->SetRunState(Animation::Running, TicksFromSecondsF(2.0));
+  anim->SetRunState(Animation::RUNNING, TicksFromSecondsF(2.0));
   EXPECT_FALSE(anim->is_finished());
-  anim->SetRunState(Animation::Paused, TicksFromSecondsF(2.0));
+  anim->SetRunState(Animation::PAUSED, TicksFromSecondsF(2.0));
   EXPECT_FALSE(anim->is_finished());
-  anim->SetRunState(Animation::WaitingForTargetAvailability,
+  anim->SetRunState(Animation::WAITING_FOR_TARGET_AVAILABILITY,
                     TicksFromSecondsF(2.0));
   EXPECT_FALSE(anim->is_finished());
-  anim->SetRunState(Animation::Finished, TicksFromSecondsF(0.0));
+  anim->SetRunState(Animation::FINISHED, TicksFromSecondsF(0.0));
   EXPECT_TRUE(anim->is_finished());
-  anim->SetRunState(Animation::Aborted, TicksFromSecondsF(0.0));
+  anim->SetRunState(Animation::ABORTED, TicksFromSecondsF(0.0));
   EXPECT_TRUE(anim->is_finished());
 }
 
 TEST(AnimationTest, RunStateChangesIgnoredWhileSuspended) {
   scoped_ptr<Animation> anim(CreateAnimation(1));
   anim->Suspend(TicksFromSecondsF(0));
-  EXPECT_EQ(Animation::Paused, anim->run_state());
-  anim->SetRunState(Animation::Running, TicksFromSecondsF(0.0));
-  EXPECT_EQ(Animation::Paused, anim->run_state());
+  EXPECT_EQ(Animation::PAUSED, anim->run_state());
+  anim->SetRunState(Animation::RUNNING, TicksFromSecondsF(0.0));
+  EXPECT_EQ(Animation::PAUSED, anim->run_state());
   anim->Resume(TicksFromSecondsF(0));
-  anim->SetRunState(Animation::Running, TicksFromSecondsF(0.0));
-  EXPECT_EQ(Animation::Running, anim->run_state());
+  anim->SetRunState(Animation::RUNNING, TicksFromSecondsF(0.0));
+  EXPECT_EQ(Animation::RUNNING, anim->run_state());
 }
 
 TEST(AnimationTest, TrimTimePlaybackNormal) {
@@ -708,7 +706,7 @@
 
 TEST(AnimationTest, TrimTimePlaybackNormalDoubleReverse) {
   scoped_ptr<Animation> anim(CreateAnimation(1, 1, -1));
-  anim->set_direction(Animation::Reverse);
+  anim->set_direction(Animation::DIRECTION_REVERSE);
   EXPECT_EQ(0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(-1.0))
                    .InSecondsF());
   EXPECT_EQ(
@@ -723,7 +721,7 @@
 
 TEST(AnimationTest, TrimTimePlaybackFastDoubleReverse) {
   scoped_ptr<Animation> anim(CreateAnimation(1, 4, -2));
-  anim->set_direction(Animation::Reverse);
+  anim->set_direction(Animation::DIRECTION_REVERSE);
   EXPECT_EQ(0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(-1.0))
                    .InSecondsF());
   EXPECT_EQ(
@@ -742,7 +740,7 @@
 
 TEST(AnimationTest, TrimTimeAlternateTwoIterationsPlaybackFast) {
   scoped_ptr<Animation> anim(CreateAnimation(2, 2, 2));
-  anim->set_direction(Animation::Alternate);
+  anim->set_direction(Animation::DIRECTION_ALTERNATE);
   EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0))
                      .InSecondsF());
   EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.25))
@@ -767,7 +765,7 @@
 
 TEST(AnimationTest, TrimTimeAlternateTwoIterationsPlaybackFastReverse) {
   scoped_ptr<Animation> anim(CreateAnimation(2, 2, 2));
-  anim->set_direction(Animation::AlternateReverse);
+  anim->set_direction(Animation::DIRECTION_ALTERNATE_REVERSE);
   EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(-1.0))
                      .InSecondsF());
   EXPECT_EQ(2.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0))
@@ -794,7 +792,7 @@
 
 TEST(AnimationTest, TrimTimeAlternateTwoIterationsPlaybackFastDoubleReverse) {
   scoped_ptr<Animation> anim(CreateAnimation(2, 2, -2));
-  anim->set_direction(Animation::AlternateReverse);
+  anim->set_direction(Animation::DIRECTION_ALTERNATE_REVERSE);
   EXPECT_EQ(2.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0))
                      .InSecondsF());
   EXPECT_EQ(1.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.25))
@@ -820,7 +818,7 @@
 TEST(AnimationTest,
      TrimTimeAlternateReverseThreeIterationsPlaybackFastAlternateReverse) {
   scoped_ptr<Animation> anim(CreateAnimation(3, 2, -2));
-  anim->set_direction(Animation::AlternateReverse);
+  anim->set_direction(Animation::DIRECTION_ALTERNATE_REVERSE);
   EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0))
                      .InSecondsF());
   EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.25))
@@ -854,7 +852,7 @@
 TEST(AnimationTest,
      TrimTimeAlternateReverseTwoIterationsPlaybackNormalAlternate) {
   scoped_ptr<Animation> anim(CreateAnimation(2, 2, -1));
-  anim->set_direction(Animation::Alternate);
+  anim->set_direction(Animation::DIRECTION_ALTERNATE);
   EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0))
                      .InSecondsF());
   EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.5))
@@ -898,7 +896,7 @@
 
 TEST(AnimationTest, TrimTimeIterationStartAlternate) {
   scoped_ptr<Animation> anim(CreateAnimation(2, 1, 1));
-  anim->set_direction(Animation::Alternate);
+  anim->set_direction(Animation::DIRECTION_ALTERNATE);
   anim->set_iteration_start(0.3);
   EXPECT_EQ(0.3, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(-1.0))
                      .InSecondsF());
@@ -918,7 +916,7 @@
 
 TEST(AnimationTest, TrimTimeIterationStartAlternateThreeIterations) {
   scoped_ptr<Animation> anim(CreateAnimation(3, 1, 1));
-  anim->set_direction(Animation::Alternate);
+  anim->set_direction(Animation::DIRECTION_ALTERNATE);
   anim->set_iteration_start(1);
   EXPECT_EQ(1.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(-1.0))
                      .InSecondsF());
@@ -943,7 +941,7 @@
 TEST(AnimationTest,
      TrimTimeIterationStartAlternateThreeIterationsPlaybackReverse) {
   scoped_ptr<Animation> anim(CreateAnimation(3, 1, -1));
-  anim->set_direction(Animation::Alternate);
+  anim->set_direction(Animation::DIRECTION_ALTERNATE);
   anim->set_iteration_start(1);
   EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0))
                      .InSecondsF());
@@ -959,22 +957,22 @@
 
 TEST(AnimationTest, InEffectFillMode) {
   scoped_ptr<Animation> anim(CreateAnimation(1));
-  anim->set_fill_mode(Animation::FillModeNone);
+  anim->set_fill_mode(Animation::FILL_MODE_NONE);
   EXPECT_FALSE(anim->InEffect(TicksFromSecondsF(-1.0)));
   EXPECT_TRUE(anim->InEffect(TicksFromSecondsF(0.0)));
   EXPECT_TRUE(anim->InEffect(TicksFromSecondsF(1.0)));
 
-  anim->set_fill_mode(Animation::FillModeForwards);
+  anim->set_fill_mode(Animation::FILL_MODE_FORWARDS);
   EXPECT_FALSE(anim->InEffect(TicksFromSecondsF(-1.0)));
   EXPECT_TRUE(anim->InEffect(TicksFromSecondsF(0.0)));
   EXPECT_TRUE(anim->InEffect(TicksFromSecondsF(1.0)));
 
-  anim->set_fill_mode(Animation::FillModeBackwards);
+  anim->set_fill_mode(Animation::FILL_MODE_BACKWARDS);
   EXPECT_TRUE(anim->InEffect(TicksFromSecondsF(-1.0)));
   EXPECT_TRUE(anim->InEffect(TicksFromSecondsF(0.0)));
   EXPECT_TRUE(anim->InEffect(TicksFromSecondsF(1.0)));
 
-  anim->set_fill_mode(Animation::FillModeBoth);
+  anim->set_fill_mode(Animation::FILL_MODE_BOTH);
   EXPECT_TRUE(anim->InEffect(TicksFromSecondsF(-1.0)));
   EXPECT_TRUE(anim->InEffect(TicksFromSecondsF(0.0)));
   EXPECT_TRUE(anim->InEffect(TicksFromSecondsF(1.0)));
@@ -982,22 +980,22 @@
 
 TEST(AnimationTest, InEffectFillModePlayback) {
   scoped_ptr<Animation> anim(CreateAnimation(1, 1, -1));
-  anim->set_fill_mode(Animation::FillModeNone);
+  anim->set_fill_mode(Animation::FILL_MODE_NONE);
   EXPECT_FALSE(anim->InEffect(TicksFromSecondsF(-1.0)));
   EXPECT_TRUE(anim->InEffect(TicksFromSecondsF(0.0)));
   EXPECT_TRUE(anim->InEffect(TicksFromSecondsF(1.0)));
 
-  anim->set_fill_mode(Animation::FillModeForwards);
+  anim->set_fill_mode(Animation::FILL_MODE_FORWARDS);
   EXPECT_FALSE(anim->InEffect(TicksFromSecondsF(-1.0)));
   EXPECT_TRUE(anim->InEffect(TicksFromSecondsF(0.0)));
   EXPECT_TRUE(anim->InEffect(TicksFromSecondsF(1.0)));
 
-  anim->set_fill_mode(Animation::FillModeBackwards);
+  anim->set_fill_mode(Animation::FILL_MODE_BACKWARDS);
   EXPECT_TRUE(anim->InEffect(TicksFromSecondsF(-1.0)));
   EXPECT_TRUE(anim->InEffect(TicksFromSecondsF(0.0)));
   EXPECT_TRUE(anim->InEffect(TicksFromSecondsF(1.0)));
 
-  anim->set_fill_mode(Animation::FillModeBoth);
+  anim->set_fill_mode(Animation::FILL_MODE_BOTH);
   EXPECT_TRUE(anim->InEffect(TicksFromSecondsF(-1.0)));
   EXPECT_TRUE(anim->InEffect(TicksFromSecondsF(0.0)));
   EXPECT_TRUE(anim->InEffect(TicksFromSecondsF(1.0)));
diff --git a/cc/animation/layer_animation_controller.cc b/cc/animation/layer_animation_controller.cc
index 1892dd4..c253dae 100644
--- a/cc/animation/layer_animation_controller.cc
+++ b/cc/animation/layer_animation_controller.cc
@@ -45,7 +45,7 @@
                                               base::TimeDelta time_offset) {
   for (size_t i = 0; i < animations_.size(); ++i) {
     if (animations_[i]->id() == animation_id) {
-      animations_[i]->SetRunState(Animation::Paused,
+      animations_[i]->SetRunState(Animation::PAUSED,
                                   time_offset + animations_[i]->start_time());
     }
   }
@@ -65,13 +65,13 @@
   auto animations_to_remove =
       animations_.remove_if(HasAnimationId(animation_id));
   for (auto it = animations_to_remove; it != animations_.end(); ++it) {
-    if ((*it)->target_property() == Animation::ScrollOffset) {
+    if ((*it)->target_property() == Animation::SCROLL_OFFSET) {
       scroll_offset_animation_was_interrupted_ = true;
       break;
     }
   }
   animations_.erase(animations_to_remove, animations_.end());
-  UpdateActivation(NormalActivation);
+  UpdateActivation(NORMAL_ACTIVATION);
 }
 
 struct HasAnimationIdAndProperty {
@@ -92,12 +92,12 @@
     Animation::TargetProperty target_property) {
   auto animations_to_remove = animations_.remove_if(
       HasAnimationIdAndProperty(animation_id, target_property));
-  if (target_property == Animation::ScrollOffset &&
+  if (target_property == Animation::SCROLL_OFFSET &&
       animations_to_remove != animations_.end())
     scroll_offset_animation_was_interrupted_ = true;
 
   animations_.erase(animations_to_remove, animations_.end());
-  UpdateActivation(NormalActivation);
+  UpdateActivation(NORMAL_ACTIVATION);
 }
 
 void LayerAnimationController::AbortAnimations(
@@ -105,7 +105,7 @@
   for (size_t i = 0; i < animations_.size(); ++i) {
     if (animations_[i]->target_property() == target_property &&
         !animations_[i]->is_finished())
-      animations_[i]->SetRunState(Animation::Aborted, last_tick_time_);
+      animations_[i]->SetRunState(Animation::ABORTED, last_tick_time_);
   }
 }
 
@@ -125,8 +125,8 @@
   RemoveAnimationsCompletedOnMainThread(controller_impl);
 
   PushPropertiesToImplThread(controller_impl);
-  controller_impl->UpdateActivation(NormalActivation);
-  UpdateActivation(NormalActivation);
+  controller_impl->UpdateActivation(NORMAL_ACTIVATION);
+  UpdateActivation(NORMAL_ACTIVATION);
 }
 
 void LayerAnimationController::Animate(base::TimeTicks monotonic_time) {
@@ -157,11 +157,9 @@
     base::TimeDelta trimmed =
         animation->TrimTimeToCurrentIteration(monotonic_time);
     switch (animation->target_property()) {
-      case Animation::Opacity: {
-        AnimationEvent event(AnimationEvent::PropertyUpdate,
-                             id_,
-                             animation->group(),
-                             Animation::Opacity,
+      case Animation::OPACITY: {
+        AnimationEvent event(AnimationEvent::PROPERTY_UPDATE, id_,
+                             animation->group(), Animation::OPACITY,
                              monotonic_time);
         const FloatAnimationCurve* float_animation_curve =
             animation->curve()->ToFloatAnimationCurve();
@@ -171,11 +169,9 @@
         break;
       }
 
-      case Animation::Transform: {
-        AnimationEvent event(AnimationEvent::PropertyUpdate,
-                             id_,
-                             animation->group(),
-                             Animation::Transform,
+      case Animation::TRANSFORM: {
+        AnimationEvent event(AnimationEvent::PROPERTY_UPDATE, id_,
+                             animation->group(), Animation::TRANSFORM,
                              monotonic_time);
         const TransformAnimationCurve* transform_animation_curve =
             animation->curve()->ToTransformAnimationCurve();
@@ -185,11 +181,9 @@
         break;
       }
 
-      case Animation::Filter: {
-        AnimationEvent event(AnimationEvent::PropertyUpdate,
-                             id_,
-                             animation->group(),
-                             Animation::Filter,
+      case Animation::FILTER: {
+        AnimationEvent event(AnimationEvent::PROPERTY_UPDATE, id_,
+                             animation->group(), Animation::FILTER,
                              monotonic_time);
         const FilterAnimationCurve* filter_animation_curve =
             animation->curve()->ToFilterAnimationCurve();
@@ -199,17 +193,16 @@
         break;
       }
 
-      case Animation::BackgroundColor: { break; }
-
-      case Animation::ScrollOffset: {
-        // Impl-side changes to scroll offset are already sent back to the
-        // main thread (e.g. for user-driven scrolling), so a PropertyUpdate
-        // isn't needed.
+      case Animation::BACKGROUND_COLOR: {
         break;
       }
 
-      case Animation::TargetPropertyEnumSize:
-        NOTREACHED();
+      case Animation::SCROLL_OFFSET: {
+        // Impl-side changes to scroll offset are already sent back to the
+        // main thread (e.g. for user-driven scrolling), so a PROPERTY_UPDATE
+        // isn't needed.
+        break;
+      }
     }
   }
 }
@@ -237,7 +230,7 @@
 
   AccumulatePropertyUpdates(last_tick_time_, events);
 
-  UpdateActivation(NormalActivation);
+  UpdateActivation(NORMAL_ACTIVATION);
 }
 
 struct AffectsNoObservers {
@@ -258,13 +251,13 @@
                                   AffectsNoObservers()),
                     animations_.end());
   scroll_offset_animation_was_interrupted_ = false;
-  UpdateActivation(NormalActivation);
+  UpdateActivation(NORMAL_ACTIVATION);
 }
 
 void LayerAnimationController::AddAnimation(scoped_ptr<Animation> animation) {
   animations_.push_back(animation.Pass());
   needs_to_start_animations_ = true;
-  UpdateActivation(NormalActivation);
+  UpdateActivation(NORMAL_ACTIVATION);
 }
 
 Animation* LayerAnimationController::GetAnimation(
@@ -315,7 +308,7 @@
   if (registrar_)
     registrar_->RegisterAnimationController(this);
 
-  UpdateActivation(ForceActivation);
+  UpdateActivation(FORCE_ACTIVATION);
 }
 
 void LayerAnimationController::NotifyAnimationStarted(
@@ -375,7 +368,7 @@
   for (size_t i = 0; i < animations_.size(); ++i) {
     if (animations_[i]->group() == event.group_id &&
         animations_[i]->target_property() == event.target_property) {
-      animations_[i]->SetRunState(Animation::Aborted, event.monotonic_time);
+      animations_[i]->SetRunState(Animation::ABORTED, event.monotonic_time);
     }
   }
 }
@@ -385,11 +378,11 @@
   bool notify_active_observers = true;
   bool notify_pending_observers = true;
   switch (event.target_property) {
-    case Animation::Opacity:
+    case Animation::OPACITY:
       NotifyObserversOpacityAnimated(
           event.opacity, notify_active_observers, notify_pending_observers);
       break;
-    case Animation::Transform:
+    case Animation::TRANSFORM:
       NotifyObserversTransformAnimated(
           event.transform, notify_active_observers, notify_pending_observers);
       break;
@@ -423,7 +416,7 @@
 bool LayerAnimationController::HasFilterAnimationThatInflatesBounds() const {
   for (size_t i = 0; i < animations_.size(); ++i) {
     if (!animations_[i]->is_finished() &&
-        animations_[i]->target_property() == Animation::Filter &&
+        animations_[i]->target_property() == Animation::FILTER &&
         animations_[i]
             ->curve()
             ->ToFilterAnimationCurve()
@@ -435,7 +428,7 @@
 }
 
 bool LayerAnimationController::HasTransformAnimationThatInflatesBounds() const {
-  return IsAnimatingProperty(Animation::Transform);
+  return IsAnimatingProperty(Animation::TRANSFORM);
 }
 
 bool LayerAnimationController::FilterAnimationBoundsForBox(
@@ -459,7 +452,7 @@
   *bounds = gfx::BoxF();
   for (size_t i = 0; i < animations_.size(); ++i) {
     if (animations_[i]->is_finished() ||
-        animations_[i]->target_property() != Animation::Transform)
+        animations_[i]->target_property() != Animation::TRANSFORM)
       continue;
 
     const TransformAnimationCurve* transform_animation_curve =
@@ -478,7 +471,7 @@
 bool LayerAnimationController::HasAnimationThatAffectsScale() const {
   for (size_t i = 0; i < animations_.size(); ++i) {
     if (animations_[i]->is_finished() ||
-        animations_[i]->target_property() != Animation::Transform)
+        animations_[i]->target_property() != Animation::TRANSFORM)
       continue;
 
     const TransformAnimationCurve* transform_animation_curve =
@@ -493,7 +486,7 @@
 bool LayerAnimationController::HasOnlyTranslationTransforms() const {
   for (size_t i = 0; i < animations_.size(); ++i) {
     if (animations_[i]->is_finished() ||
-        animations_[i]->target_property() != Animation::Transform)
+        animations_[i]->target_property() != Animation::TRANSFORM)
       continue;
 
     const TransformAnimationCurve* transform_animation_curve =
@@ -508,7 +501,7 @@
 bool LayerAnimationController::AnimationsPreserveAxisAlignment() const {
   for (size_t i = 0; i < animations_.size(); ++i) {
     if (animations_[i]->is_finished() ||
-        animations_[i]->target_property() != Animation::Transform)
+        animations_[i]->target_property() != Animation::TRANSFORM)
       continue;
 
     const TransformAnimationCurve* transform_animation_curve =
@@ -524,17 +517,17 @@
   *max_scale = 0.f;
   for (size_t i = 0; i < animations_.size(); ++i) {
     if (animations_[i]->is_finished() ||
-        animations_[i]->target_property() != Animation::Transform)
+        animations_[i]->target_property() != Animation::TRANSFORM)
       continue;
 
     bool forward_direction = true;
     switch (animations_[i]->direction()) {
-      case Animation::Normal:
-      case Animation::Alternate:
+      case Animation::DIRECTION_NORMAL:
+      case Animation::DIRECTION_ALTERNATE:
         forward_direction = animations_[i]->playback_rate() >= 0.0;
         break;
-      case Animation::Reverse:
-      case Animation::AlternateReverse:
+      case Animation::DIRECTION_REVERSE:
+      case Animation::DIRECTION_ALTERNATE_REVERSE:
         forward_direction = animations_[i]->playback_rate() < 0.0;
         break;
     }
@@ -571,7 +564,7 @@
       continue;
 
     // Scroll animations always start at the current scroll offset.
-    if (animations_[i]->target_property() == Animation::ScrollOffset) {
+    if (animations_[i]->target_property() == Animation::SCROLL_OFFSET) {
       gfx::ScrollOffset current_scroll_offset;
       if (controller_impl->value_provider_) {
         current_scroll_offset =
@@ -587,7 +580,7 @@
 
     // The new animation should be set to run as soon as possible.
     Animation::RunState initial_run_state =
-        Animation::WaitingForTargetAvailability;
+        Animation::WAITING_FOR_TARGET_AVAILABILITY;
     scoped_ptr<Animation> to_add(
         animations_[i]->CloneAndInitialize(initial_run_state));
     DCHECK(!to_add->needs_synchronized_start_time());
@@ -600,14 +593,14 @@
     Animation* animation,
     const LayerAnimationController* main_thread_controller) {
   if (animation->is_impl_only()) {
-    return (animation->run_state() == Animation::WaitingForDeletion);
+    return (animation->run_state() == Animation::WAITING_FOR_DELETION);
   } else {
     return !main_thread_controller->GetAnimationById(animation->id());
   }
 }
 
 static bool AffectsActiveOnlyAndIsWaitingForDeletion(Animation* animation) {
-  return animation->run_state() == Animation::WaitingForDeletion &&
+  return animation->run_state() == Animation::WAITING_FOR_DELETION &&
          !animation->affects_pending_observers();
 }
 
@@ -615,7 +608,7 @@
     LayerAnimationController* controller_impl) const {
   // Animations removed on the main thread should no longer affect pending
   // observers, and should stop affecting active observers after the next call
-  // to ActivateAnimations. If already WaitingForDeletion, they can be removed
+  // to ActivateAnimations. If already WAITING_FOR_DELETION, they can be removed
   // immediately.
   ScopedPtrVector<Animation>& animations = controller_impl->animations_;
   for (size_t i = 0; i < animations.size(); ++i) {
@@ -652,8 +645,8 @@
 
   animations_waiting_for_target.reserve(animations_.size());
   for (size_t i = 0; i < animations_.size(); ++i) {
-    if (animations_[i]->run_state() == Animation::Starting ||
-        animations_[i]->run_state() == Animation::Running) {
+    if (animations_[i]->run_state() == Animation::STARTING ||
+        animations_[i]->run_state() == Animation::RUNNING) {
       if (animations_[i]->affects_active_observers()) {
         blocked_properties_for_active_observers.insert(
             animations_[i]->target_property());
@@ -663,7 +656,7 @@
             animations_[i]->target_property());
       }
     } else if (animations_[i]->run_state() ==
-               Animation::WaitingForTargetAvailability) {
+               Animation::WAITING_FOR_TARGET_AVAILABILITY) {
       animations_waiting_for_target.push_back(i);
     }
   }
@@ -677,7 +670,7 @@
     // for target because it might have changed the run state while handling
     // previous animation in this loop (if they belong to same group).
     if (animation_waiting_for_target->run_state() ==
-        Animation::WaitingForTargetAvailability) {
+        Animation::WAITING_FOR_TARGET_AVAILABILITY) {
       TargetProperties enqueued_properties;
       bool affects_active_observers =
           animation_waiting_for_target->affects_active_observers();
@@ -715,12 +708,12 @@
       // If the intersection is null, then we are free to start the animations
       // in the group.
       if (null_intersection) {
-        animation_waiting_for_target->SetRunState(Animation::Starting,
+        animation_waiting_for_target->SetRunState(Animation::STARTING,
                                                   monotonic_time);
         for (size_t j = animation_index + 1; j < animations_.size(); ++j) {
           if (animation_waiting_for_target->group() ==
               animations_[j]->group()) {
-            animations_[j]->SetRunState(Animation::Starting, monotonic_time);
+            animations_[j]->SetRunState(Animation::STARTING, monotonic_time);
           }
         }
       } else {
@@ -734,18 +727,16 @@
     base::TimeTicks monotonic_time,
     AnimationEventsVector* events) {
   for (size_t i = 0; i < animations_.size(); ++i) {
-    if (animations_[i]->run_state() == Animation::Starting &&
+    if (animations_[i]->run_state() == Animation::STARTING &&
         animations_[i]->affects_active_observers()) {
-      animations_[i]->SetRunState(Animation::Running, monotonic_time);
+      animations_[i]->SetRunState(Animation::RUNNING, monotonic_time);
       if (!animations_[i]->has_set_start_time() &&
           !animations_[i]->needs_synchronized_start_time())
         animations_[i]->set_start_time(monotonic_time);
       if (events) {
-        AnimationEvent started_event(AnimationEvent::Started,
-                                     id_,
-                                     animations_[i]->group(),
-                                     animations_[i]->target_property(),
-                                     monotonic_time);
+        AnimationEvent started_event(
+            AnimationEvent::STARTED, id_, animations_[i]->group(),
+            animations_[i]->target_property(), monotonic_time);
         started_event.is_impl_only = animations_[i]->is_impl_only();
         if (started_event.is_impl_only)
           NotifyAnimationStarted(started_event);
@@ -760,9 +751,9 @@
     base::TimeTicks monotonic_time) {
   for (size_t i = 0; i < animations_.size(); ++i) {
     if (animations_[i]->IsFinishedAt(monotonic_time) &&
-        animations_[i]->run_state() != Animation::Aborted &&
-        animations_[i]->run_state() != Animation::WaitingForDeletion)
-      animations_[i]->SetRunState(Animation::Finished, monotonic_time);
+        animations_[i]->run_state() != Animation::ABORTED &&
+        animations_[i]->run_state() != Animation::WAITING_FOR_DELETION)
+      animations_[i]->SetRunState(Animation::FINISHED, monotonic_time);
   }
 }
 
@@ -774,21 +765,19 @@
 
   animations_with_same_group_id.reserve(animations_.size());
   // Non-aborted animations are marked for deletion after a corresponding
-  // AnimationEvent::Finished event is sent or received. This means that if
+  // AnimationEvent::FINISHED event is sent or received. This means that if
   // we don't have an events vector, we must ensure that non-aborted animations
   // have received a finished event before marking them for deletion.
   for (size_t i = 0; i < animations_.size(); i++) {
     int group_id = animations_[i]->group();
-    if (animations_[i]->run_state() == Animation::Aborted) {
+    if (animations_[i]->run_state() == Animation::ABORTED) {
       if (events && !animations_[i]->is_impl_only()) {
-        AnimationEvent aborted_event(AnimationEvent::Aborted,
-                                     id_,
-                                     group_id,
+        AnimationEvent aborted_event(AnimationEvent::ABORTED, id_, group_id,
                                      animations_[i]->target_property(),
                                      monotonic_time);
         events->push_back(aborted_event);
       }
-      animations_[i]->SetRunState(Animation::WaitingForDeletion,
+      animations_[i]->SetRunState(Animation::WAITING_FOR_DELETION,
                                   monotonic_time);
       marked_animations_for_deletions = true;
       continue;
@@ -797,13 +786,13 @@
     bool all_anims_with_same_id_are_finished = false;
 
     // Since deleting an animation on the main thread leads to its deletion
-    // on the impl thread, we only mark a Finished main thread animation for
-    // deletion once it has received a Finished event from the impl thread.
+    // on the impl thread, we only mark a FINISHED main thread animation for
+    // deletion once it has received a FINISHED event from the impl thread.
     bool animation_i_will_send_or_has_received_finish_event =
         events || animations_[i]->received_finished_event();
     // If an animation is finished, and not already marked for deletion,
     // find out if all other animations in the same group are also finished.
-    if (animations_[i]->run_state() == Animation::Finished &&
+    if (animations_[i]->run_state() == Animation::FINISHED &&
         animation_i_will_send_or_has_received_finish_event) {
       // Clear the animations_with_same_group_id if it was added for
       // the previous animation's iteration.
@@ -815,16 +804,16 @@
             events || animations_[j]->received_finished_event();
         if (group_id == animations_[j]->group()) {
           if (!animations_[j]->is_finished() ||
-              (animations_[j]->run_state() == Animation::Finished &&
+              (animations_[j]->run_state() == Animation::FINISHED &&
                !animation_j_will_send_or_has_received_finish_event)) {
             all_anims_with_same_id_are_finished = false;
             break;
           } else if (j >= i &&
-                     animations_[j]->run_state() != Animation::Aborted) {
+                     animations_[j]->run_state() != Animation::ABORTED) {
             // Mark down the animations which belong to the same group
             // and is not yet aborted. If this current iteration finds that all
             // animations with same ID are finished, then the marked
-            // animations below will be set to WaitingForDeletion in next
+            // animations below will be set to WAITING_FOR_DELETION in next
             // iteration.
             animations_with_same_group_id.push_back(j);
           }
@@ -839,8 +828,7 @@
         size_t animation_index = animations_with_same_group_id[j];
           if (events) {
             AnimationEvent finished_event(
-                AnimationEvent::Finished,
-                id_,
+                AnimationEvent::FINISHED, id_,
                 animations_[animation_index]->group(),
                 animations_[animation_index]->target_property(),
                 monotonic_time);
@@ -852,7 +840,7 @@
               events->push_back(finished_event);
           }
           animations_[animation_index]->SetRunState(
-              Animation::WaitingForDeletion, monotonic_time);
+              Animation::WAITING_FOR_DELETION, monotonic_time);
       }
       marked_animations_for_deletions = true;
     }
@@ -862,7 +850,7 @@
 }
 
 static bool IsWaitingForDeletion(Animation* animation) {
-  return animation->run_state() == Animation::WaitingForDeletion;
+  return animation->run_state() == Animation::WAITING_FOR_DELETION;
 }
 
 void LayerAnimationController::PurgeAnimationsMarkedForDeletion() {
@@ -875,9 +863,9 @@
 
 void LayerAnimationController::TickAnimations(base::TimeTicks monotonic_time) {
   for (size_t i = 0; i < animations_.size(); ++i) {
-    if (animations_[i]->run_state() == Animation::Starting ||
-        animations_[i]->run_state() == Animation::Running ||
-        animations_[i]->run_state() == Animation::Paused) {
+    if (animations_[i]->run_state() == Animation::STARTING ||
+        animations_[i]->run_state() == Animation::RUNNING ||
+        animations_[i]->run_state() == Animation::PAUSED) {
       if (!animations_[i]->InEffect(monotonic_time))
         continue;
 
@@ -885,7 +873,7 @@
           animations_[i]->TrimTimeToCurrentIteration(monotonic_time);
 
       switch (animations_[i]->target_property()) {
-        case Animation::Transform: {
+        case Animation::TRANSFORM: {
           const TransformAnimationCurve* transform_animation_curve =
               animations_[i]->curve()->ToTransformAnimationCurve();
           const gfx::Transform transform =
@@ -897,7 +885,7 @@
           break;
         }
 
-        case Animation::Opacity: {
+        case Animation::OPACITY: {
           const FloatAnimationCurve* float_animation_curve =
               animations_[i]->curve()->ToFloatAnimationCurve();
           const float opacity = std::max(
@@ -909,7 +897,7 @@
           break;
         }
 
-        case Animation::Filter: {
+        case Animation::FILTER: {
           const FilterAnimationCurve* filter_animation_curve =
               animations_[i]->curve()->ToFilterAnimationCurve();
           const FilterOperations filter =
@@ -921,12 +909,12 @@
           break;
         }
 
-        case Animation::BackgroundColor: {
+        case Animation::BACKGROUND_COLOR: {
           // Not yet implemented.
           break;
         }
 
-        case Animation::ScrollOffset: {
+        case Animation::SCROLL_OFFSET: {
           const ScrollOffsetAnimationCurve* scroll_offset_animation_curve =
               animations_[i]->curve()->ToScrollOffsetAnimationCurve();
           const gfx::ScrollOffset scroll_offset =
@@ -937,22 +925,18 @@
               animations_[i]->affects_pending_observers());
           break;
         }
-
-        // Do nothing for sentinel value.
-        case Animation::TargetPropertyEnumSize:
-          NOTREACHED();
       }
     }
   }
 }
 
 void LayerAnimationController::UpdateActivation(UpdateActivationType type) {
-  bool force = type == ForceActivation;
+  bool force = type == FORCE_ACTIVATION;
   if (registrar_) {
     bool was_active = is_active_;
     is_active_ = false;
     for (size_t i = 0; i < animations_.size(); ++i) {
-      if (animations_[i]->run_state() != Animation::WaitingForDeletion) {
+      if (animations_[i]->run_state() != Animation::WAITING_FOR_DELETION) {
         is_active_ = true;
         break;
       }
diff --git a/cc/animation/layer_animation_controller.h b/cc/animation/layer_animation_controller.h
index 48c3bdca..d459515f 100644
--- a/cc/animation/layer_animation_controller.h
+++ b/cc/animation/layer_animation_controller.h
@@ -179,10 +179,7 @@
 
   void TickAnimations(base::TimeTicks monotonic_time);
 
-  enum UpdateActivationType {
-    NormalActivation,
-    ForceActivation
-  };
+  enum UpdateActivationType { NORMAL_ACTIVATION, FORCE_ACTIVATION };
   void UpdateActivation(UpdateActivationType type);
 
   void NotifyObserversOpacityAnimated(float opacity,
diff --git a/cc/animation/layer_animation_controller_unittest.cc b/cc/animation/layer_animation_controller_unittest.cc
index ba639b3..d8d0b056 100644
--- a/cc/animation/layer_animation_controller_unittest.cc
+++ b/cc/animation/layer_animation_controller_unittest.cc
@@ -49,7 +49,7 @@
       LayerAnimationController::Create(0));
   controller->AddValueObserver(&dummy);
 
-  EXPECT_FALSE(controller_impl->GetAnimation(Animation::Opacity));
+  EXPECT_FALSE(controller_impl->GetAnimation(Animation::OPACITY));
 
   EXPECT_FALSE(controller->needs_to_start_animations_for_testing());
   EXPECT_FALSE(controller_impl->needs_to_start_animations_for_testing());
@@ -63,7 +63,7 @@
   controller_impl->ActivateAnimations();
 
   EXPECT_TRUE(controller_impl->GetAnimationById(animation_id));
-  EXPECT_EQ(Animation::WaitingForTargetAvailability,
+  EXPECT_EQ(Animation::WAITING_FOR_TARGET_AVAILABILITY,
             controller_impl->GetAnimationById(animation_id)->run_state());
 }
 
@@ -79,7 +79,7 @@
       LayerAnimationController::Create(0));
   controller->AddValueObserver(&dummy);
 
-  EXPECT_FALSE(controller_impl->GetAnimation(Animation::Opacity));
+  EXPECT_FALSE(controller_impl->GetAnimation(Animation::OPACITY));
 
   int animation_id =
       AddOpacityTransitionToController(controller.get(), 1, 0, 1, false);
@@ -88,7 +88,7 @@
   controller_impl->ActivateAnimations();
 
   EXPECT_TRUE(controller_impl->GetAnimationById(animation_id));
-  EXPECT_EQ(Animation::WaitingForTargetAvailability,
+  EXPECT_EQ(Animation::WAITING_FOR_TARGET_AVAILABILITY,
             controller_impl->GetAnimationById(animation_id)->run_state());
 
   AnimationEventsVector events;
@@ -122,13 +122,13 @@
       AddOpacityTransitionToController(controller.get(), 1, 0, 1, false);
 
   const TimeTicks start_time = TicksFromSecondsF(123);
-  controller->GetAnimation(Animation::Opacity)->set_start_time(start_time);
+  controller->GetAnimation(Animation::OPACITY)->set_start_time(start_time);
 
   controller->PushAnimationUpdatesTo(controller_impl.get());
   controller_impl->ActivateAnimations();
 
   EXPECT_TRUE(controller_impl->GetAnimationById(animation_id));
-  EXPECT_EQ(Animation::WaitingForTargetAvailability,
+  EXPECT_EQ(Animation::WAITING_FOR_TARGET_AVAILABILITY,
             controller_impl->GetAnimationById(animation_id)->run_state());
 
   AnimationEventsVector events;
@@ -202,8 +202,8 @@
 
   controller->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(1000));
   controller->UpdateState(true, nullptr);
-  EXPECT_EQ(Animation::Finished,
-            controller->GetAnimation(Animation::Opacity)->run_state());
+  EXPECT_EQ(Animation::FINISHED,
+            controller->GetAnimation(Animation::OPACITY)->run_state());
   EXPECT_EQ(1u, registrar->active_animation_controllers().size());
 
   events.reset(new AnimationEventsVector);
@@ -211,8 +211,8 @@
                            TimeDelta::FromMilliseconds(1500));
   controller_impl->UpdateState(true, events.get());
 
-  EXPECT_EQ(Animation::WaitingForDeletion,
-            controller_impl->GetAnimation(Animation::Opacity)->run_state());
+  EXPECT_EQ(Animation::WAITING_FOR_DELETION,
+            controller_impl->GetAnimation(Animation::OPACITY)->run_state());
   // The impl thread controller should have de-activated.
   EXPECT_EQ(0u, registrar_impl->active_animation_controllers().size());
 
@@ -221,8 +221,8 @@
   controller->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(1500));
   controller->UpdateState(true, nullptr);
 
-  EXPECT_EQ(Animation::WaitingForDeletion,
-            controller->GetAnimation(Animation::Opacity)->run_state());
+  EXPECT_EQ(Animation::WAITING_FOR_DELETION,
+            controller->GetAnimation(Animation::OPACITY)->run_state());
   // The main thread controller should have de-activated.
   EXPECT_EQ(0u, registrar->active_animation_controllers().size());
 
@@ -247,7 +247,7 @@
       LayerAnimationController::Create(0));
   controller->AddValueObserver(&dummy);
 
-  EXPECT_FALSE(controller_impl->GetAnimation(Animation::Opacity));
+  EXPECT_FALSE(controller_impl->GetAnimation(Animation::OPACITY));
 
   int animation_id =
       AddOpacityTransitionToController(controller.get(), 1, 0, 1, false);
@@ -256,7 +256,7 @@
   controller_impl->ActivateAnimations();
 
   EXPECT_TRUE(controller_impl->GetAnimationById(animation_id));
-  EXPECT_EQ(Animation::WaitingForTargetAvailability,
+  EXPECT_EQ(Animation::WAITING_FOR_TARGET_AVAILABILITY,
             controller_impl->GetAnimationById(animation_id)->run_state());
 
   // Start the animations on each controller.
@@ -265,22 +265,22 @@
   controller_impl->UpdateState(true, &events);
   controller->Animate(kInitialTickTime);
   controller->UpdateState(true, nullptr);
-  EXPECT_EQ(Animation::Running,
+  EXPECT_EQ(Animation::RUNNING,
             controller_impl->GetAnimationById(animation_id)->run_state());
-  EXPECT_EQ(Animation::Running,
+  EXPECT_EQ(Animation::RUNNING,
             controller->GetAnimationById(animation_id)->run_state());
 
   // Pause the main-thread animation.
   controller->PauseAnimation(
       animation_id,
       TimeDelta::FromMilliseconds(1000) + TimeDelta::FromMilliseconds(1000));
-  EXPECT_EQ(Animation::Paused,
+  EXPECT_EQ(Animation::PAUSED,
             controller->GetAnimationById(animation_id)->run_state());
 
   // The pause run state change should make it to the impl thread controller.
   controller->PushAnimationUpdatesTo(controller_impl.get());
   controller_impl->ActivateAnimations();
-  EXPECT_EQ(Animation::Paused,
+  EXPECT_EQ(Animation::PAUSED,
             controller_impl->GetAnimationById(animation_id)->run_state());
 }
 
@@ -294,7 +294,7 @@
       LayerAnimationController::Create(0));
   controller->AddValueObserver(&dummy);
 
-  EXPECT_FALSE(controller_impl->GetAnimation(Animation::Opacity));
+  EXPECT_FALSE(controller_impl->GetAnimation(Animation::OPACITY));
 
   int animation_id =
       AddOpacityTransitionToController(controller.get(), 1, 0, 1, false);
@@ -304,15 +304,12 @@
   controller_impl->ActivateAnimations();
 
   EXPECT_TRUE(controller_impl->GetAnimationById(animation_id));
-  EXPECT_EQ(Animation::WaitingForTargetAvailability,
+  EXPECT_EQ(Animation::WAITING_FOR_TARGET_AVAILABILITY,
             controller_impl->GetAnimationById(animation_id)->run_state());
 
   // Notify main thread controller that the animation has started.
-  AnimationEvent animation_started_event(AnimationEvent::Started,
-                                         0,
-                                         group_id,
-                                         Animation::Opacity,
-                                         kInitialTickTime);
+  AnimationEvent animation_started_event(AnimationEvent::STARTED, 0, group_id,
+                                         Animation::OPACITY, kInitialTickTime);
   controller->NotifyAnimationStarted(animation_started_event);
 
   // Force animation to complete on impl thread.
@@ -351,9 +348,9 @@
   controller_impl->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(500));
   controller_impl->UpdateState(true, events.get());
 
-  // There should be a Started event for the animation.
+  // There should be a STARTED event for the animation.
   EXPECT_EQ(1u, events->size());
-  EXPECT_EQ(AnimationEvent::Started, (*events)[0].type);
+  EXPECT_EQ(AnimationEvent::STARTED, (*events)[0].type);
   controller->NotifyAnimationStarted((*events)[0]);
 
   controller->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(1000));
@@ -369,13 +366,13 @@
 
   EXPECT_TRUE(dummy_impl.animation_waiting_for_deletion());
 
-  // There should be a Finished event for the animation.
+  // There should be a FINISHED event for the animation.
   EXPECT_EQ(1u, events->size());
-  EXPECT_EQ(AnimationEvent::Finished, (*events)[0].type);
+  EXPECT_EQ(AnimationEvent::FINISHED, (*events)[0].type);
 
   // Neither controller should have deleted the animation yet.
-  EXPECT_TRUE(controller->GetAnimation(Animation::Opacity));
-  EXPECT_TRUE(controller_impl->GetAnimation(Animation::Opacity));
+  EXPECT_TRUE(controller->GetAnimation(Animation::OPACITY));
+  EXPECT_TRUE(controller_impl->GetAnimation(Animation::OPACITY));
 
   controller->NotifyAnimationFinished((*events)[0]);
 
@@ -399,7 +396,7 @@
     const AnimationEventsVector* events) {
   const AnimationEvent* event = 0;
   for (size_t i = 0; i < events->size(); ++i)
-    if ((*events)[i].type == AnimationEvent::PropertyUpdate)
+    if ((*events)[i].type == AnimationEvent::PROPERTY_UPDATE)
       event = &(*events)[i];
 
   return event;
@@ -415,8 +412,7 @@
 
   scoped_ptr<Animation> to_add(CreateAnimation(
       scoped_ptr<AnimationCurve>(new FakeFloatTransition(1.0, 0.f, 1.f)).Pass(),
-      1,
-      Animation::Opacity));
+      1, Animation::OPACITY));
 
   EXPECT_FALSE(controller->needs_to_start_animations_for_testing());
   controller->AddAnimation(to_add.Pass());
@@ -447,8 +443,7 @@
 
   scoped_ptr<Animation> to_add(CreateAnimation(
       scoped_ptr<AnimationCurve>(new FakeFloatTransition(1.0, 0.f, 1.f)).Pass(),
-      1,
-      Animation::Opacity));
+      1, Animation::OPACITY));
   to_add->set_is_impl_only(true);
 
   controller_impl->AddAnimation(to_add.Pass());
@@ -488,7 +483,7 @@
   scoped_ptr<KeyframedTransformAnimationCurve> curve(
       KeyframedTransformAnimationCurve::Create());
 
-  // Create simple Transform animation.
+  // Create simple TRANSFORM animation.
   TransformOperations operations;
   curve->AddKeyframe(
       TransformKeyframe::Create(base::TimeDelta(), operations, nullptr));
@@ -497,7 +492,7 @@
       base::TimeDelta::FromSecondsD(1.0), operations, nullptr));
 
   scoped_ptr<Animation> animation(
-      Animation::Create(curve.Pass(), 1, 0, Animation::Transform));
+      Animation::Create(curve.Pass(), 1, 0, Animation::TRANSFORM));
   animation->set_is_impl_only(true);
   controller_impl->AddAnimation(animation.Pass());
 
@@ -549,7 +544,7 @@
                                             end_filters, nullptr));
 
   scoped_ptr<Animation> animation(
-      Animation::Create(curve.Pass(), 1, 0, Animation::Filter));
+      Animation::Create(curve.Pass(), 1, 0, Animation::FILTER));
   controller->AddAnimation(animation.Pass());
 
   controller->Animate(kInitialTickTime);
@@ -587,7 +582,7 @@
   scoped_ptr<KeyframedFilterAnimationCurve> curve(
       KeyframedFilterAnimationCurve::Create());
 
-  // Create simple Filter animation.
+  // Create simple FILTER animation.
   FilterOperations start_filters;
   start_filters.Append(FilterOperation::CreateBrightnessFilter(1.f));
   curve->AddKeyframe(
@@ -598,7 +593,7 @@
                                             end_filters, nullptr));
 
   scoped_ptr<Animation> animation(
-      Animation::Create(curve.Pass(), 1, 0, Animation::Filter));
+      Animation::Create(curve.Pass(), 1, 0, Animation::FILTER));
   animation->set_is_impl_only(true);
   controller_impl->AddAnimation(animation.Pass());
 
@@ -651,20 +646,20 @@
           EaseInOutTimingFunction::Create().Pass()));
 
   scoped_ptr<Animation> animation(
-      Animation::Create(curve.Pass(), 1, 0, Animation::ScrollOffset));
+      Animation::Create(curve.Pass(), 1, 0, Animation::SCROLL_OFFSET));
   animation->set_needs_synchronized_start_time(true);
   controller->AddAnimation(animation.Pass());
 
   dummy_provider_impl.set_scroll_offset(initial_value);
   controller->PushAnimationUpdatesTo(controller_impl.get());
   controller_impl->ActivateAnimations();
-  EXPECT_TRUE(controller_impl->GetAnimation(Animation::ScrollOffset));
-  TimeDelta duration = controller_impl->GetAnimation(Animation::ScrollOffset)
+  EXPECT_TRUE(controller_impl->GetAnimation(Animation::SCROLL_OFFSET));
+  TimeDelta duration = controller_impl->GetAnimation(Animation::SCROLL_OFFSET)
                            ->curve()
                            ->Duration();
   EXPECT_EQ(
       duration,
-      controller->GetAnimation(Animation::ScrollOffset)->curve()->Duration());
+      controller->GetAnimation(Animation::SCROLL_OFFSET)->curve()->Duration());
 
   controller->Animate(kInitialTickTime);
   controller->UpdateState(true, nullptr);
@@ -730,20 +725,20 @@
           EaseInOutTimingFunction::Create().Pass()));
 
   scoped_ptr<Animation> animation(
-      Animation::Create(curve.Pass(), 1, 0, Animation::ScrollOffset));
+      Animation::Create(curve.Pass(), 1, 0, Animation::SCROLL_OFFSET));
   animation->set_needs_synchronized_start_time(true);
   controller->AddAnimation(animation.Pass());
 
   dummy_provider.set_scroll_offset(initial_value);
   controller->PushAnimationUpdatesTo(controller_impl.get());
   controller_impl->ActivateAnimations();
-  EXPECT_TRUE(controller_impl->GetAnimation(Animation::ScrollOffset));
-  TimeDelta duration = controller_impl->GetAnimation(Animation::ScrollOffset)
+  EXPECT_TRUE(controller_impl->GetAnimation(Animation::SCROLL_OFFSET));
+  TimeDelta duration = controller_impl->GetAnimation(Animation::SCROLL_OFFSET)
                            ->curve()
                            ->Duration();
   EXPECT_EQ(
       duration,
-      controller->GetAnimation(Animation::ScrollOffset)->curve()->Duration());
+      controller->GetAnimation(Animation::SCROLL_OFFSET)->curve()->Duration());
 
   controller->Animate(kInitialTickTime);
   controller->UpdateState(true, nullptr);
@@ -803,7 +798,7 @@
   double duration_in_seconds = curve->Duration().InSecondsF();
 
   scoped_ptr<Animation> animation(
-      Animation::Create(curve.Pass(), 1, 0, Animation::ScrollOffset));
+      Animation::Create(curve.Pass(), 1, 0, Animation::SCROLL_OFFSET));
   animation->set_is_impl_only(true);
   controller_impl->AddAnimation(animation.Pass());
 
@@ -857,7 +852,7 @@
 
   int animation_id = 1;
   scoped_ptr<Animation> animation(Animation::Create(
-      curve.Pass(), animation_id, 0, Animation::ScrollOffset));
+      curve.Pass(), animation_id, 0, Animation::SCROLL_OFFSET));
   animation->set_needs_synchronized_start_time(true);
   controller->AddAnimation(animation.Pass());
   controller->PushAnimationUpdatesTo(controller_impl.get());
@@ -878,8 +873,8 @@
   // Now, test the 2-argument version of RemoveAnimation.
   curve = ScrollOffsetAnimationCurve::Create(
       target_value, EaseInOutTimingFunction::Create().Pass());
-  animation =
-      Animation::Create(curve.Pass(), animation_id, 0, Animation::ScrollOffset);
+  animation = Animation::Create(curve.Pass(), animation_id, 0,
+                                Animation::SCROLL_OFFSET);
   animation->set_needs_synchronized_start_time(true);
   controller->AddAnimation(animation.Pass());
   controller->PushAnimationUpdatesTo(controller_impl.get());
@@ -975,8 +970,7 @@
 
   scoped_ptr<Animation> to_add(CreateAnimation(
       scoped_ptr<AnimationCurve>(new FakeFloatTransition(1.0, 0.f, 1.f)).Pass(),
-      1,
-      Animation::Opacity));
+      1, Animation::OPACITY));
   to_add->set_is_impl_only(true);
   controller_impl->AddAnimation(to_add.Pass());
 
@@ -1011,8 +1005,7 @@
 
   scoped_ptr<Animation> to_add(CreateAnimation(
       scoped_ptr<AnimationCurve>(new FakeFloatTransition(1.0, 0.f, 1.f)).Pass(),
-      1,
-      Animation::Opacity));
+      1, Animation::OPACITY));
   to_add->set_needs_synchronized_start_time(true);
 
   // We should pause at the first keyframe indefinitely waiting for that
@@ -1033,10 +1026,7 @@
 
   // Send the synchronized start time.
   controller->NotifyAnimationStarted(
-      AnimationEvent(AnimationEvent::Started,
-                     0,
-                     1,
-                     Animation::Opacity,
+      AnimationEvent(AnimationEvent::STARTED, 0, 1, Animation::OPACITY,
                      kInitialTickTime + TimeDelta::FromMilliseconds(2000)));
   controller->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(5000));
   controller->UpdateState(true, events.get());
@@ -1057,13 +1047,11 @@
 
   controller->AddAnimation(CreateAnimation(
       scoped_ptr<AnimationCurve>(new FakeFloatTransition(1.0, 0.f, 1.f)).Pass(),
-      1,
-      Animation::Opacity));
+      1, Animation::OPACITY));
   controller->AddAnimation(CreateAnimation(
-      scoped_ptr<AnimationCurve>(
-          new FakeFloatTransition(1.0, 1.f, 0.5f)).Pass(),
-      2,
-      Animation::Opacity));
+      scoped_ptr<AnimationCurve>(new FakeFloatTransition(1.0, 1.f, 0.5f))
+          .Pass(),
+      2, Animation::OPACITY));
 
   EXPECT_TRUE(controller->needs_to_start_animations_for_testing());
 
@@ -1099,19 +1087,17 @@
   controller->AddValueObserver(&dummy);
   controller->AddAnimation(CreateAnimation(
       scoped_ptr<AnimationCurve>(new FakeFloatTransition(1.0, 0.f, 1.f)).Pass(),
-      1,
-      Animation::Opacity));
+      1, Animation::OPACITY));
   controller->Animate(kInitialTickTime);
   controller->UpdateState(true, events.get());
   EXPECT_TRUE(controller->HasActiveAnimation());
   EXPECT_EQ(0.f, dummy.opacity());
 
   scoped_ptr<Animation> to_add(CreateAnimation(
-      scoped_ptr<AnimationCurve>(
-          new FakeFloatTransition(1.0, 1.f, 0.5f)).Pass(),
-      2,
-      Animation::Opacity));
-  controller->AbortAnimations(Animation::Opacity);
+      scoped_ptr<AnimationCurve>(new FakeFloatTransition(1.0, 1.f, 0.5f))
+          .Pass(),
+      2, Animation::OPACITY));
+  controller->AbortAnimations(Animation::OPACITY);
   controller->AddAnimation(to_add.Pass());
 
   // Since the previous animation was aborted, the new animation should start
@@ -1137,17 +1123,14 @@
   controller->AddValueObserver(&dummy);
 
   controller->AddAnimation(CreateAnimation(
-      scoped_ptr<AnimationCurve>(new FakeTransformTransition(1)).Pass(),
-      1,
-      Animation::Transform));
+      scoped_ptr<AnimationCurve>(new FakeTransformTransition(1)).Pass(), 1,
+      Animation::TRANSFORM));
   controller->AddAnimation(CreateAnimation(
-      scoped_ptr<AnimationCurve>(new FakeTransformTransition(1)).Pass(),
-      2,
-      Animation::Transform));
+      scoped_ptr<AnimationCurve>(new FakeTransformTransition(1)).Pass(), 2,
+      Animation::TRANSFORM));
   controller->AddAnimation(CreateAnimation(
       scoped_ptr<AnimationCurve>(new FakeFloatTransition(1.0, 0.f, 1.f)).Pass(),
-      2,
-      Animation::Opacity));
+      2, Animation::OPACITY));
 
   controller->Animate(kInitialTickTime);
   controller->UpdateState(true, events.get());
@@ -1177,18 +1160,15 @@
   controller->AddValueObserver(&dummy);
 
   controller->AddAnimation(CreateAnimation(
-      scoped_ptr<AnimationCurve>(new FakeTransformTransition(2)).Pass(),
-      1,
-      Animation::Transform));
+      scoped_ptr<AnimationCurve>(new FakeTransformTransition(2)).Pass(), 1,
+      Animation::TRANSFORM));
   controller->AddAnimation(CreateAnimation(
       scoped_ptr<AnimationCurve>(new FakeFloatTransition(1.0, 0.f, 1.f)).Pass(),
-      1,
-      Animation::Opacity));
+      1, Animation::OPACITY));
   controller->AddAnimation(CreateAnimation(
-      scoped_ptr<AnimationCurve>(
-          new FakeFloatTransition(1.0, 1.f, 0.5f)).Pass(),
-      2,
-      Animation::Opacity));
+      scoped_ptr<AnimationCurve>(new FakeFloatTransition(1.0, 1.f, 0.5f))
+          .Pass(),
+      2, Animation::OPACITY));
 
   // Animations with id 1 should both start now.
   controller->Animate(kInitialTickTime);
@@ -1223,8 +1203,7 @@
 
   scoped_ptr<Animation> to_add(CreateAnimation(
       scoped_ptr<AnimationCurve>(new FakeFloatTransition(1.0, 0.f, 1.f)).Pass(),
-      1,
-      Animation::Opacity));
+      1, Animation::OPACITY));
   to_add->set_iterations(3);
   controller->AddAnimation(to_add.Pass());
 
@@ -1270,7 +1249,7 @@
 
   scoped_ptr<Animation> to_add(CreateAnimation(
       scoped_ptr<AnimationCurve>(new FakeFloatTransition(1.0, 0.f, 1.f)).Pass(),
-      1, Animation::Opacity));
+      1, Animation::OPACITY));
   to_add->set_iterations(-1);
   controller->AddAnimation(to_add.Pass());
 
@@ -1298,9 +1277,9 @@
   EXPECT_TRUE(controller->HasActiveAnimation());
   EXPECT_EQ(0.75f, dummy.opacity());
 
-  EXPECT_TRUE(controller->GetAnimation(Animation::Opacity));
-  controller->GetAnimation(Animation::Opacity)
-      ->SetRunState(Animation::Aborted,
+  EXPECT_TRUE(controller->GetAnimation(Animation::OPACITY));
+  controller->GetAnimation(Animation::OPACITY)
+      ->SetRunState(Animation::ABORTED,
                     kInitialTickTime + TimeDelta::FromMilliseconds(750));
   EXPECT_FALSE(controller->HasActiveAnimation());
   EXPECT_EQ(0.75f, dummy.opacity());
@@ -1317,7 +1296,7 @@
 
   controller->AddAnimation(CreateAnimation(
       scoped_ptr<AnimationCurve>(new FakeFloatTransition(1.0, 0.f, 1.f)).Pass(),
-      1, Animation::Opacity));
+      1, Animation::OPACITY));
 
   controller->Animate(kInitialTickTime);
   controller->UpdateState(true, events.get());
@@ -1328,9 +1307,9 @@
   EXPECT_TRUE(controller->HasActiveAnimation());
   EXPECT_EQ(0.5f, dummy.opacity());
 
-  EXPECT_TRUE(controller->GetAnimation(Animation::Opacity));
-  controller->GetAnimation(Animation::Opacity)
-      ->SetRunState(Animation::Paused,
+  EXPECT_TRUE(controller->GetAnimation(Animation::OPACITY));
+  controller->GetAnimation(Animation::OPACITY)
+      ->SetRunState(Animation::PAUSED,
                     kInitialTickTime + TimeDelta::FromMilliseconds(500));
 
   controller->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(1024000));
@@ -1338,9 +1317,9 @@
   EXPECT_TRUE(controller->HasActiveAnimation());
   EXPECT_EQ(0.5f, dummy.opacity());
 
-  EXPECT_TRUE(controller->GetAnimation(Animation::Opacity));
-  controller->GetAnimation(Animation::Opacity)
-      ->SetRunState(Animation::Running,
+  EXPECT_TRUE(controller->GetAnimation(Animation::OPACITY));
+  controller->GetAnimation(Animation::OPACITY)
+      ->SetRunState(Animation::RUNNING,
                     kInitialTickTime + TimeDelta::FromMilliseconds(1024000));
   controller->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(1024250));
   controller->UpdateState(true, events.get());
@@ -1364,14 +1343,14 @@
   const int animation_id = 2;
   controller->AddAnimation(Animation::Create(
       scoped_ptr<AnimationCurve>(new FakeTransformTransition(1)).Pass(), 1, 1,
-      Animation::Transform));
+      Animation::TRANSFORM));
   controller->AddAnimation(Animation::Create(
       scoped_ptr<AnimationCurve>(new FakeFloatTransition(2.0, 0.f, 1.f)).Pass(),
-      animation_id, 1, Animation::Opacity));
+      animation_id, 1, Animation::OPACITY));
   controller->AddAnimation(Animation::Create(
       scoped_ptr<AnimationCurve>(new FakeFloatTransition(1.0, 1.f, 0.75f))
           .Pass(),
-      3, 2, Animation::Opacity));
+      3, 2, Animation::OPACITY));
 
   controller->Animate(kInitialTickTime);
   controller->UpdateState(true, events.get());
@@ -1384,7 +1363,7 @@
 
   EXPECT_TRUE(controller->GetAnimationById(animation_id));
   controller->GetAnimationById(animation_id)
-      ->SetRunState(Animation::Aborted,
+      ->SetRunState(Animation::ABORTED,
                     kInitialTickTime + TimeDelta::FromMilliseconds(1000));
   controller->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(1000));
   controller->UpdateState(true, events.get());
@@ -1410,24 +1389,23 @@
 
   scoped_ptr<Animation> to_add(CreateAnimation(
       scoped_ptr<AnimationCurve>(new FakeFloatTransition(2.0, 0.f, 1.f)).Pass(),
-      0,
-      Animation::Opacity));
+      0, Animation::OPACITY));
   to_add->set_needs_synchronized_start_time(true);
   controller->AddAnimation(to_add.Pass());
 
   controller->Animate(kInitialTickTime);
   controller->UpdateState(true, events.get());
   EXPECT_TRUE(controller->HasActiveAnimation());
-  Animation* active_animation = controller->GetAnimation(Animation::Opacity);
+  Animation* active_animation = controller->GetAnimation(Animation::OPACITY);
   EXPECT_TRUE(active_animation);
   EXPECT_TRUE(active_animation->needs_synchronized_start_time());
 
   controller->PushAnimationUpdatesTo(controller_impl.get());
   controller_impl->ActivateAnimations();
 
-  active_animation = controller_impl->GetAnimation(Animation::Opacity);
+  active_animation = controller_impl->GetAnimation(Animation::OPACITY);
   EXPECT_TRUE(active_animation);
-  EXPECT_EQ(Animation::WaitingForTargetAvailability,
+  EXPECT_EQ(Animation::WAITING_FOR_TARGET_AVAILABILITY,
             active_animation->run_state());
 }
 
@@ -1441,17 +1419,15 @@
   controller->AddValueObserver(&dummy);
 
   controller->AddAnimation(CreateAnimation(
-      scoped_ptr<AnimationCurve>(new FakeTransformTransition(1)).Pass(),
-      1,
-      Animation::Transform));
+      scoped_ptr<AnimationCurve>(new FakeTransformTransition(1)).Pass(), 1,
+      Animation::TRANSFORM));
 
   controller->Animate(kInitialTickTime);
   controller->UpdateState(true, events.get());
 
   controller->AddAnimation(CreateAnimation(
       scoped_ptr<AnimationCurve>(new FakeFloatTransition(1.0, 0.f, 1.f)).Pass(),
-      2,
-      Animation::Opacity));
+      2, Animation::OPACITY));
 
   // Animate but don't UpdateState.
   controller->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(1000));
@@ -1460,7 +1436,7 @@
   events.reset(new AnimationEventsVector);
   controller->UpdateState(true, events.get());
 
-  // Should have one Started event and one Finished event.
+  // Should have one STARTED event and one FINISHED event.
   EXPECT_EQ(2u, events->size());
   EXPECT_NE((*events)[0].type, (*events)[1].type);
 
@@ -1477,7 +1453,7 @@
 }
 
 // Tests that an animation controller with only a pending observer gets ticked
-// but doesn't progress animations past the Starting state.
+// but doesn't progress animations past the STARTING state.
 TEST(LayerAnimationControllerTest, InactiveObserverGetsTicked) {
   scoped_ptr<AnimationEventsVector> events(
       make_scoped_ptr(new AnimationEventsVector));
@@ -1487,49 +1463,49 @@
       LayerAnimationController::Create(0));
 
   const int id = 1;
-  controller->AddAnimation(CreateAnimation(scoped_ptr<AnimationCurve>(
-      new FakeFloatTransition(1.0, 0.5f, 1.f)).Pass(),
-      id,
-      Animation::Opacity));
+  controller->AddAnimation(CreateAnimation(
+      scoped_ptr<AnimationCurve>(new FakeFloatTransition(1.0, 0.5f, 1.f))
+          .Pass(),
+      id, Animation::OPACITY));
 
-  // Without an observer, the animation shouldn't progress to the Starting
+  // Without an observer, the animation shouldn't progress to the STARTING
   // state.
   controller->Animate(kInitialTickTime);
   controller->UpdateState(true, events.get());
   EXPECT_EQ(0u, events->size());
-  EXPECT_EQ(Animation::WaitingForTargetAvailability,
-            controller->GetAnimation(Animation::Opacity)->run_state());
+  EXPECT_EQ(Animation::WAITING_FOR_TARGET_AVAILABILITY,
+            controller->GetAnimation(Animation::OPACITY)->run_state());
 
   controller->AddValueObserver(&pending_dummy);
 
   // With only a pending observer, the animation should progress to the
-  // Starting state and get ticked at its starting point, but should not
-  // progress to Running.
+  // STARTING state and get ticked at its starting point, but should not
+  // progress to RUNNING.
   controller->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(1000));
   controller->UpdateState(true, events.get());
   EXPECT_EQ(0u, events->size());
-  EXPECT_EQ(Animation::Starting,
-            controller->GetAnimation(Animation::Opacity)->run_state());
+  EXPECT_EQ(Animation::STARTING,
+            controller->GetAnimation(Animation::OPACITY)->run_state());
   EXPECT_EQ(0.5f, pending_dummy.opacity());
 
-  // Even when already in the Starting state, the animation should stay
+  // Even when already in the STARTING state, the animation should stay
   // there, and shouldn't be ticked past its starting point.
   controller->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(2000));
   controller->UpdateState(true, events.get());
   EXPECT_EQ(0u, events->size());
-  EXPECT_EQ(Animation::Starting,
-            controller->GetAnimation(Animation::Opacity)->run_state());
+  EXPECT_EQ(Animation::STARTING,
+            controller->GetAnimation(Animation::OPACITY)->run_state());
   EXPECT_EQ(0.5f, pending_dummy.opacity());
 
   controller->AddValueObserver(&dummy);
 
   // Now that an active observer has been added, the animation should still
-  // initially tick at its starting point, but should now progress to Running.
+  // initially tick at its starting point, but should now progress to RUNNING.
   controller->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(3000));
   controller->UpdateState(true, events.get());
   EXPECT_EQ(1u, events->size());
-  EXPECT_EQ(Animation::Running,
-            controller->GetAnimation(Animation::Opacity)->run_state());
+  EXPECT_EQ(Animation::RUNNING,
+            controller->GetAnimation(Animation::OPACITY)->run_state());
   EXPECT_EQ(0.5f, pending_dummy.opacity());
   EXPECT_EQ(0.5f, dummy.opacity());
 
@@ -1554,7 +1530,7 @@
       base::TimeDelta::FromSecondsD(1.0), operations1, nullptr));
 
   scoped_ptr<Animation> animation(
-      Animation::Create(curve1.Pass(), 1, 1, Animation::Transform));
+      Animation::Create(curve1.Pass(), 1, 1, Animation::TRANSFORM));
   controller_impl->AddAnimation(animation.Pass());
 
   scoped_ptr<KeyframedTransformAnimationCurve> curve2(
@@ -1567,7 +1543,7 @@
   curve2->AddKeyframe(TransformKeyframe::Create(
       base::TimeDelta::FromSecondsD(1.0), operations2, nullptr));
 
-  animation = Animation::Create(curve2.Pass(), 2, 2, Animation::Transform);
+  animation = Animation::Create(curve2.Pass(), 2, 2, Animation::TRANSFORM);
   controller_impl->AddAnimation(animation.Pass());
 
   gfx::BoxF box(1.f, 2.f, -1.f, 3.f, 4.f, 5.f);
@@ -1578,7 +1554,7 @@
             bounds.ToString());
 
   controller_impl->GetAnimationById(1)
-      ->SetRunState(Animation::Finished, TicksFromSecondsF(0.0));
+      ->SetRunState(Animation::FINISHED, TicksFromSecondsF(0.0));
 
   // Only the unfinished animation should affect the animated bounds.
   EXPECT_TRUE(controller_impl->TransformAnimationBoundsForBox(box, &bounds));
@@ -1586,7 +1562,7 @@
             bounds.ToString());
 
   controller_impl->GetAnimationById(2)
-      ->SetRunState(Animation::Finished, TicksFromSecondsF(0.0));
+      ->SetRunState(Animation::FINISHED, TicksFromSecondsF(0.0));
 
   // There are no longer any running animations.
   EXPECT_FALSE(controller_impl->HasTransformAnimationThatInflatesBounds());
@@ -1602,7 +1578,7 @@
   operations3.AppendMatrix(transform3);
   curve3->AddKeyframe(TransformKeyframe::Create(
       base::TimeDelta::FromSecondsD(1.0), operations3, nullptr));
-  animation = Animation::Create(curve3.Pass(), 3, 3, Animation::Transform);
+  animation = Animation::Create(curve3.Pass(), 3, 3, Animation::TRANSFORM);
   controller_impl->AddAnimation(animation.Pass());
   EXPECT_FALSE(controller_impl->TransformAnimationBoundsForBox(box, &bounds));
 }
@@ -1619,40 +1595,40 @@
   // state.
   controller->AddAnimation(Animation::Create(
       scoped_ptr<AnimationCurve>(new FakeTransformTransition(1.0)).Pass(), 1, 1,
-      Animation::Transform));
+      Animation::TRANSFORM));
   controller->AddAnimation(Animation::Create(
       scoped_ptr<AnimationCurve>(new FakeFloatTransition(1.0, 0.f, 1.f)).Pass(),
-      2, 2, Animation::Opacity));
+      2, 2, Animation::OPACITY));
   controller->AddAnimation(Animation::Create(
       scoped_ptr<AnimationCurve>(new FakeTransformTransition(1.0)).Pass(), 3, 3,
-      Animation::Transform));
+      Animation::TRANSFORM));
   controller->AddAnimation(Animation::Create(
       scoped_ptr<AnimationCurve>(new FakeTransformTransition(2.0)).Pass(), 4, 4,
-      Animation::Transform));
+      Animation::TRANSFORM));
   controller->AddAnimation(Animation::Create(
       scoped_ptr<AnimationCurve>(new FakeFloatTransition(1.0, 0.f, 1.f)).Pass(),
-      5, 5, Animation::Opacity));
+      5, 5, Animation::OPACITY));
 
   controller->Animate(kInitialTickTime);
   controller->UpdateState(true, nullptr);
   controller->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(1000));
   controller->UpdateState(true, nullptr);
 
-  EXPECT_EQ(Animation::Finished, controller->GetAnimationById(1)->run_state());
-  EXPECT_EQ(Animation::Finished, controller->GetAnimationById(2)->run_state());
-  EXPECT_EQ(Animation::Running, controller->GetAnimationById(3)->run_state());
-  EXPECT_EQ(Animation::WaitingForTargetAvailability,
+  EXPECT_EQ(Animation::FINISHED, controller->GetAnimationById(1)->run_state());
+  EXPECT_EQ(Animation::FINISHED, controller->GetAnimationById(2)->run_state());
+  EXPECT_EQ(Animation::RUNNING, controller->GetAnimationById(3)->run_state());
+  EXPECT_EQ(Animation::WAITING_FOR_TARGET_AVAILABILITY,
             controller->GetAnimationById(4)->run_state());
-  EXPECT_EQ(Animation::Running, controller->GetAnimationById(5)->run_state());
+  EXPECT_EQ(Animation::RUNNING, controller->GetAnimationById(5)->run_state());
 
-  controller->AbortAnimations(Animation::Transform);
+  controller->AbortAnimations(Animation::TRANSFORM);
 
-  // Only un-finished Transform animations should have been aborted.
-  EXPECT_EQ(Animation::Finished, controller->GetAnimationById(1)->run_state());
-  EXPECT_EQ(Animation::Finished, controller->GetAnimationById(2)->run_state());
-  EXPECT_EQ(Animation::Aborted, controller->GetAnimationById(3)->run_state());
-  EXPECT_EQ(Animation::Aborted, controller->GetAnimationById(4)->run_state());
-  EXPECT_EQ(Animation::Running, controller->GetAnimationById(5)->run_state());
+  // Only un-finished TRANSFORM animations should have been aborted.
+  EXPECT_EQ(Animation::FINISHED, controller->GetAnimationById(1)->run_state());
+  EXPECT_EQ(Animation::FINISHED, controller->GetAnimationById(2)->run_state());
+  EXPECT_EQ(Animation::ABORTED, controller->GetAnimationById(3)->run_state());
+  EXPECT_EQ(Animation::ABORTED, controller->GetAnimationById(4)->run_state());
+  EXPECT_EQ(Animation::RUNNING, controller->GetAnimationById(5)->run_state());
 }
 
 // An animation aborted on the main thread should get deleted on both threads.
@@ -1673,17 +1649,17 @@
   controller_impl->ActivateAnimations();
   EXPECT_TRUE(controller_impl->GetAnimationById(animation_id));
 
-  controller->AbortAnimations(Animation::Opacity);
-  EXPECT_EQ(Animation::Aborted,
-            controller->GetAnimation(Animation::Opacity)->run_state());
+  controller->AbortAnimations(Animation::OPACITY);
+  EXPECT_EQ(Animation::ABORTED,
+            controller->GetAnimation(Animation::OPACITY)->run_state());
   EXPECT_FALSE(dummy.animation_waiting_for_deletion());
   EXPECT_FALSE(dummy_impl.animation_waiting_for_deletion());
 
   controller->Animate(kInitialTickTime);
   controller->UpdateState(true, nullptr);
   EXPECT_TRUE(dummy.animation_waiting_for_deletion());
-  EXPECT_EQ(Animation::WaitingForDeletion,
-            controller->GetAnimation(Animation::Opacity)->run_state());
+  EXPECT_EQ(Animation::WAITING_FOR_DELETION,
+            controller->GetAnimation(Animation::OPACITY)->run_state());
 
   controller->PushAnimationUpdatesTo(controller_impl.get());
   controller_impl->ActivateAnimations();
@@ -1709,9 +1685,9 @@
   controller_impl->ActivateAnimations();
   EXPECT_TRUE(controller_impl->GetAnimationById(animation_id));
 
-  controller_impl->AbortAnimations(Animation::Opacity);
-  EXPECT_EQ(Animation::Aborted,
-            controller_impl->GetAnimation(Animation::Opacity)->run_state());
+  controller_impl->AbortAnimations(Animation::OPACITY);
+  EXPECT_EQ(Animation::ABORTED,
+            controller_impl->GetAnimation(Animation::OPACITY)->run_state());
   EXPECT_FALSE(dummy.animation_waiting_for_deletion());
   EXPECT_FALSE(dummy_impl.animation_waiting_for_deletion());
 
@@ -1720,19 +1696,19 @@
   controller_impl->UpdateState(true, &events);
   EXPECT_TRUE(dummy_impl.animation_waiting_for_deletion());
   EXPECT_EQ(1u, events.size());
-  EXPECT_EQ(AnimationEvent::Aborted, events[0].type);
-  EXPECT_EQ(Animation::WaitingForDeletion,
-            controller_impl->GetAnimation(Animation::Opacity)->run_state());
+  EXPECT_EQ(AnimationEvent::ABORTED, events[0].type);
+  EXPECT_EQ(Animation::WAITING_FOR_DELETION,
+            controller_impl->GetAnimation(Animation::OPACITY)->run_state());
 
   controller->NotifyAnimationAborted(events[0]);
-  EXPECT_EQ(Animation::Aborted,
-            controller->GetAnimation(Animation::Opacity)->run_state());
+  EXPECT_EQ(Animation::ABORTED,
+            controller->GetAnimation(Animation::OPACITY)->run_state());
 
   controller->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(500));
   controller->UpdateState(true, nullptr);
   EXPECT_TRUE(dummy.animation_waiting_for_deletion());
-  EXPECT_EQ(Animation::WaitingForDeletion,
-            controller->GetAnimation(Animation::Opacity)->run_state());
+  EXPECT_EQ(Animation::WAITING_FOR_DELETION,
+            controller->GetAnimation(Animation::OPACITY)->run_state());
 
   controller->PushAnimationUpdatesTo(controller_impl.get());
   controller_impl->ActivateAnimations();
@@ -1740,7 +1716,7 @@
   EXPECT_FALSE(controller_impl->GetAnimationById(animation_id));
 }
 
-// Ensure that we only generate Finished events for animations in a group
+// Ensure that we only generate FINISHED events for animations in a group
 // once all animations in that group are finished.
 TEST(LayerAnimationControllerTest, FinishedEventsForGroup) {
   scoped_ptr<AnimationEventsVector> events(
@@ -1755,18 +1731,18 @@
   // Add two animations with the same group id but different durations.
   controller_impl->AddAnimation(Animation::Create(
       scoped_ptr<AnimationCurve>(new FakeTransformTransition(2.0)).Pass(), 1,
-      group_id, Animation::Transform));
+      group_id, Animation::TRANSFORM));
   controller_impl->AddAnimation(Animation::Create(
       scoped_ptr<AnimationCurve>(new FakeFloatTransition(1.0, 0.f, 1.f)).Pass(),
-      2, group_id, Animation::Opacity));
+      2, group_id, Animation::OPACITY));
 
   controller_impl->Animate(kInitialTickTime);
   controller_impl->UpdateState(true, events.get());
 
   // Both animations should have started.
   EXPECT_EQ(2u, events->size());
-  EXPECT_EQ(AnimationEvent::Started, (*events)[0].type);
-  EXPECT_EQ(AnimationEvent::Started, (*events)[1].type);
+  EXPECT_EQ(AnimationEvent::STARTED, (*events)[0].type);
+  EXPECT_EQ(AnimationEvent::STARTED, (*events)[1].type);
 
   events.reset(new AnimationEventsVector);
   controller_impl->Animate(kInitialTickTime +
@@ -1774,25 +1750,25 @@
   controller_impl->UpdateState(true, events.get());
 
   // The opacity animation should be finished, but should not have generated
-  // a Finished event yet.
+  // a FINISHED event yet.
   EXPECT_EQ(0u, events->size());
-  EXPECT_EQ(Animation::Finished,
+  EXPECT_EQ(Animation::FINISHED,
             controller_impl->GetAnimationById(2)->run_state());
-  EXPECT_EQ(Animation::Running,
+  EXPECT_EQ(Animation::RUNNING,
             controller_impl->GetAnimationById(1)->run_state());
 
   controller_impl->Animate(kInitialTickTime +
                            TimeDelta::FromMilliseconds(2000));
   controller_impl->UpdateState(true, events.get());
 
-  // Both animations should have generated Finished events.
+  // Both animations should have generated FINISHED events.
   EXPECT_EQ(2u, events->size());
-  EXPECT_EQ(AnimationEvent::Finished, (*events)[0].type);
-  EXPECT_EQ(AnimationEvent::Finished, (*events)[1].type);
+  EXPECT_EQ(AnimationEvent::FINISHED, (*events)[0].type);
+  EXPECT_EQ(AnimationEvent::FINISHED, (*events)[1].type);
 }
 
 // Ensure that when a group has a mix of aborted and finished animations,
-// we generate a Finished event for the finished animation and an Aborted
+// we generate a FINISHED event for the finished animation and an ABORTED
 // event for the aborted animation.
 TEST(LayerAnimationControllerTest, FinishedAndAbortedEventsForGroup) {
   scoped_ptr<AnimationEventsVector> events(
@@ -1804,36 +1780,34 @@
 
   // Add two animations with the same group id.
   controller_impl->AddAnimation(CreateAnimation(
-      scoped_ptr<AnimationCurve>(new FakeTransformTransition(1.0)).Pass(),
-      1,
-      Animation::Transform));
+      scoped_ptr<AnimationCurve>(new FakeTransformTransition(1.0)).Pass(), 1,
+      Animation::TRANSFORM));
   controller_impl->AddAnimation(CreateAnimation(
       scoped_ptr<AnimationCurve>(new FakeFloatTransition(1.0, 0.f, 1.f)).Pass(),
-      1,
-      Animation::Opacity));
+      1, Animation::OPACITY));
 
   controller_impl->Animate(kInitialTickTime);
   controller_impl->UpdateState(true, events.get());
 
   // Both animations should have started.
   EXPECT_EQ(2u, events->size());
-  EXPECT_EQ(AnimationEvent::Started, (*events)[0].type);
-  EXPECT_EQ(AnimationEvent::Started, (*events)[1].type);
+  EXPECT_EQ(AnimationEvent::STARTED, (*events)[0].type);
+  EXPECT_EQ(AnimationEvent::STARTED, (*events)[1].type);
 
-  controller_impl->AbortAnimations(Animation::Opacity);
+  controller_impl->AbortAnimations(Animation::OPACITY);
 
   events.reset(new AnimationEventsVector);
   controller_impl->Animate(kInitialTickTime +
                            TimeDelta::FromMilliseconds(1000));
   controller_impl->UpdateState(true, events.get());
 
-  // We should have exactly 2 events: a Finished event for the tranform
-  // animation, and an Aborted event for the opacity animation.
+  // We should have exactly 2 events: a FINISHED event for the tranform
+  // animation, and an ABORTED event for the opacity animation.
   EXPECT_EQ(2u, events->size());
-  EXPECT_EQ(AnimationEvent::Finished, (*events)[0].type);
-  EXPECT_EQ(Animation::Transform, (*events)[0].target_property);
-  EXPECT_EQ(AnimationEvent::Aborted, (*events)[1].type);
-  EXPECT_EQ(Animation::Opacity, (*events)[1].target_property);
+  EXPECT_EQ(AnimationEvent::FINISHED, (*events)[0].type);
+  EXPECT_EQ(Animation::TRANSFORM, (*events)[0].target_property);
+  EXPECT_EQ(AnimationEvent::ABORTED, (*events)[1].type);
+  EXPECT_EQ(Animation::OPACITY, (*events)[1].target_property);
 }
 
 TEST(LayerAnimationControllerTest, HasAnimationThatAffectsScale) {
@@ -1844,8 +1818,7 @@
 
   controller_impl->AddAnimation(CreateAnimation(
       scoped_ptr<AnimationCurve>(new FakeFloatTransition(1.0, 0.f, 1.f)).Pass(),
-      1,
-      Animation::Opacity));
+      1, Animation::OPACITY));
 
   // Opacity animations don't affect scale.
   EXPECT_FALSE(controller_impl->HasAnimationThatAffectsScale());
@@ -1861,7 +1834,7 @@
       base::TimeDelta::FromSecondsD(1.0), operations1, nullptr));
 
   scoped_ptr<Animation> animation(
-      Animation::Create(curve1.Pass(), 2, 2, Animation::Transform));
+      Animation::Create(curve1.Pass(), 2, 2, Animation::TRANSFORM));
   controller_impl->AddAnimation(animation.Pass());
 
   // Translations don't affect scale.
@@ -1877,13 +1850,13 @@
   curve2->AddKeyframe(TransformKeyframe::Create(
       base::TimeDelta::FromSecondsD(1.0), operations2, nullptr));
 
-  animation = Animation::Create(curve2.Pass(), 3, 3, Animation::Transform);
+  animation = Animation::Create(curve2.Pass(), 3, 3, Animation::TRANSFORM);
   controller_impl->AddAnimation(animation.Pass());
 
   EXPECT_TRUE(controller_impl->HasAnimationThatAffectsScale());
 
   controller_impl->GetAnimationById(3)
-      ->SetRunState(Animation::Finished, TicksFromSecondsF(0.0));
+      ->SetRunState(Animation::FINISHED, TicksFromSecondsF(0.0));
 
   // Only unfinished animations should be considered by
   // HasAnimationThatAffectsScale.
@@ -1898,8 +1871,7 @@
 
   controller_impl->AddAnimation(CreateAnimation(
       scoped_ptr<AnimationCurve>(new FakeFloatTransition(1.0, 0.f, 1.f)).Pass(),
-      1,
-      Animation::Opacity));
+      1, Animation::OPACITY));
 
   // Opacity animations aren't non-translation transforms.
   EXPECT_TRUE(controller_impl->HasOnlyTranslationTransforms());
@@ -1915,7 +1887,7 @@
       base::TimeDelta::FromSecondsD(1.0), operations1, nullptr));
 
   scoped_ptr<Animation> animation(
-      Animation::Create(curve1.Pass(), 2, 2, Animation::Transform));
+      Animation::Create(curve1.Pass(), 2, 2, Animation::TRANSFORM));
   controller_impl->AddAnimation(animation.Pass());
 
   // The only transform animation we've added is a translation.
@@ -1931,14 +1903,14 @@
   curve2->AddKeyframe(TransformKeyframe::Create(
       base::TimeDelta::FromSecondsD(1.0), operations2, nullptr));
 
-  animation = Animation::Create(curve2.Pass(), 3, 3, Animation::Transform);
+  animation = Animation::Create(curve2.Pass(), 3, 3, Animation::TRANSFORM);
   controller_impl->AddAnimation(animation.Pass());
 
   // A scale animation is not a translation.
   EXPECT_FALSE(controller_impl->HasOnlyTranslationTransforms());
 
   controller_impl->GetAnimationById(3)
-      ->SetRunState(Animation::Finished, TicksFromSecondsF(0.0));
+      ->SetRunState(Animation::FINISHED, TicksFromSecondsF(0.0));
 
   // Only unfinished animations should be considered by
   // HasOnlyTranslationTransforms.
@@ -1964,7 +1936,7 @@
       base::TimeDelta::FromSecondsD(1.0), operations1, nullptr));
 
   scoped_ptr<Animation> animation(
-      Animation::Create(curve1.Pass(), 1, 1, Animation::Transform));
+      Animation::Create(curve1.Pass(), 1, 1, Animation::TRANSFORM));
   controller_impl->AddAnimation(animation.Pass());
 
   EXPECT_TRUE(controller_impl->MaximumTargetScale(&max_scale));
@@ -1980,7 +1952,7 @@
   curve2->AddKeyframe(TransformKeyframe::Create(
       base::TimeDelta::FromSecondsD(1.0), operations2, nullptr));
 
-  animation = Animation::Create(curve2.Pass(), 2, 2, Animation::Transform);
+  animation = Animation::Create(curve2.Pass(), 2, 2, Animation::TRANSFORM);
   controller_impl->AddAnimation(animation.Pass());
 
   EXPECT_TRUE(controller_impl->MaximumTargetScale(&max_scale));
@@ -1996,15 +1968,15 @@
   curve3->AddKeyframe(TransformKeyframe::Create(
       base::TimeDelta::FromSecondsD(1.0), operations3, nullptr));
 
-  animation = Animation::Create(curve3.Pass(), 3, 3, Animation::Transform);
+  animation = Animation::Create(curve3.Pass(), 3, 3, Animation::TRANSFORM);
   controller_impl->AddAnimation(animation.Pass());
 
   EXPECT_FALSE(controller_impl->MaximumTargetScale(&max_scale));
 
   controller_impl->GetAnimationById(3)
-      ->SetRunState(Animation::Finished, TicksFromSecondsF(0.0));
+      ->SetRunState(Animation::FINISHED, TicksFromSecondsF(0.0));
   controller_impl->GetAnimationById(2)
-      ->SetRunState(Animation::Finished, TicksFromSecondsF(0.0));
+      ->SetRunState(Animation::FINISHED, TicksFromSecondsF(0.0));
 
   // Only unfinished animations should be considered by
   // MaximumTargetScale.
@@ -2028,7 +2000,7 @@
       base::TimeDelta::FromSecondsD(1.0), operations2, nullptr));
 
   scoped_ptr<Animation> animation_owned(
-      Animation::Create(curve1.Pass(), 1, 1, Animation::Transform));
+      Animation::Create(curve1.Pass(), 1, 1, Animation::TRANSFORM));
   Animation* animation = animation_owned.get();
   controller_impl->AddAnimation(animation_owned.Pass());
 
@@ -2036,45 +2008,45 @@
 
   EXPECT_GT(animation->playback_rate(), 0.0);
 
-  // Normal direction with positive playback rate.
-  animation->set_direction(Animation::Normal);
+  // NORMAL direction with positive playback rate.
+  animation->set_direction(Animation::DIRECTION_NORMAL);
   EXPECT_TRUE(controller_impl->MaximumTargetScale(&max_scale));
   EXPECT_EQ(6.f, max_scale);
 
-  // Alternate direction with positive playback rate.
-  animation->set_direction(Animation::Alternate);
+  // ALTERNATE direction with positive playback rate.
+  animation->set_direction(Animation::DIRECTION_ALTERNATE);
   EXPECT_TRUE(controller_impl->MaximumTargetScale(&max_scale));
   EXPECT_EQ(6.f, max_scale);
 
-  // Reverse direction with positive playback rate.
-  animation->set_direction(Animation::Reverse);
+  // REVERSE direction with positive playback rate.
+  animation->set_direction(Animation::DIRECTION_REVERSE);
   EXPECT_TRUE(controller_impl->MaximumTargetScale(&max_scale));
   EXPECT_EQ(3.f, max_scale);
 
-  // Alternate reverse direction.
-  animation->set_direction(Animation::Reverse);
+  // ALTERNATE reverse direction.
+  animation->set_direction(Animation::DIRECTION_REVERSE);
   EXPECT_TRUE(controller_impl->MaximumTargetScale(&max_scale));
   EXPECT_EQ(3.f, max_scale);
 
   animation->set_playback_rate(-1.0);
 
-  // Normal direction with negative playback rate.
-  animation->set_direction(Animation::Normal);
+  // NORMAL direction with negative playback rate.
+  animation->set_direction(Animation::DIRECTION_NORMAL);
   EXPECT_TRUE(controller_impl->MaximumTargetScale(&max_scale));
   EXPECT_EQ(3.f, max_scale);
 
-  // Alternate direction with negative playback rate.
-  animation->set_direction(Animation::Alternate);
+  // ALTERNATE direction with negative playback rate.
+  animation->set_direction(Animation::DIRECTION_ALTERNATE);
   EXPECT_TRUE(controller_impl->MaximumTargetScale(&max_scale));
   EXPECT_EQ(3.f, max_scale);
 
-  // Reverse direction with negative playback rate.
-  animation->set_direction(Animation::Reverse);
+  // REVERSE direction with negative playback rate.
+  animation->set_direction(Animation::DIRECTION_REVERSE);
   EXPECT_TRUE(controller_impl->MaximumTargetScale(&max_scale));
   EXPECT_EQ(6.f, max_scale);
 
-  // Alternate reverse direction with negative playback rate.
-  animation->set_direction(Animation::Reverse);
+  // ALTERNATE reverse direction with negative playback rate.
+  animation->set_direction(Animation::DIRECTION_REVERSE);
   EXPECT_TRUE(controller_impl->MaximumTargetScale(&max_scale));
   EXPECT_EQ(6.f, max_scale);
 }
@@ -2103,7 +2075,7 @@
   EXPECT_TRUE(controller_impl->needs_to_start_animations_for_testing());
 
   EXPECT_TRUE(controller_impl->GetAnimationById(animation_id));
-  EXPECT_EQ(Animation::WaitingForTargetAvailability,
+  EXPECT_EQ(Animation::WAITING_FOR_TARGET_AVAILABILITY,
             controller_impl->GetAnimationById(animation_id)->run_state());
   EXPECT_TRUE(controller_impl->GetAnimationById(animation_id)
                   ->affects_pending_observers());
@@ -2114,9 +2086,9 @@
   EXPECT_FALSE(controller_impl->needs_to_start_animations_for_testing());
   controller_impl->UpdateState(true, events.get());
 
-  // Since the animation hasn't been activated, it should still be Starting
-  // rather than Running.
-  EXPECT_EQ(Animation::Starting,
+  // Since the animation hasn't been activated, it should still be STARTING
+  // rather than RUNNING.
+  EXPECT_EQ(Animation::STARTING,
             controller_impl->GetAnimationById(animation_id)->run_state());
 
   // Since the animation hasn't been activated, only the pending observer
@@ -2135,8 +2107,8 @@
   controller_impl->UpdateState(true, events.get());
 
   // Since the animation has been activated, it should have reached the
-  // Running state and the active observer should start to get ticked.
-  EXPECT_EQ(Animation::Running,
+  // RUNNING state and the active observer should start to get ticked.
+  EXPECT_EQ(Animation::RUNNING,
             controller_impl->GetAnimationById(animation_id)->run_state());
   EXPECT_EQ(0.5f, pending_dummy_impl.opacity());
   EXPECT_EQ(0.5f, dummy_impl.opacity());
@@ -2162,7 +2134,7 @@
   controller->PushAnimationUpdatesTo(controller_impl.get());
 
   EXPECT_TRUE(controller_impl->GetAnimationById(animation_id));
-  EXPECT_EQ(Animation::WaitingForTargetAvailability,
+  EXPECT_EQ(Animation::WAITING_FOR_TARGET_AVAILABILITY,
             controller_impl->GetAnimationById(animation_id)->run_state());
   EXPECT_TRUE(controller_impl->GetAnimationById(animation_id)
                   ->affects_pending_observers());
@@ -2185,8 +2157,8 @@
   controller_impl->UpdateState(true, events.get());
 
   // Since the animation has been activated, it should have reached the
-  // Running state.
-  EXPECT_EQ(Animation::Running,
+  // RUNNING state.
+  EXPECT_EQ(Animation::RUNNING,
             controller_impl->GetAnimationById(animation_id)->run_state());
 
   controller_impl->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(500));
@@ -2249,7 +2221,7 @@
   controller_impl->ActivateAnimations();
   controller_impl->Animate(kInitialTickTime);
   controller_impl->UpdateState(true, events.get());
-  EXPECT_EQ(Animation::Running,
+  EXPECT_EQ(Animation::RUNNING,
             controller_impl->GetAnimationById(animation_id)->run_state());
   EXPECT_EQ(0.5f, pending_dummy_impl.opacity());
   EXPECT_EQ(0.5f, dummy_impl.opacity());
@@ -2261,7 +2233,7 @@
 
   // Delete the animation on the main-thread controller.
   controller->RemoveAnimation(
-      controller->GetAnimation(Animation::Opacity)->id());
+      controller->GetAnimation(Animation::OPACITY)->id());
   controller->PushAnimationUpdatesTo(controller_impl.get());
 
   // The animation should no longer affect pending observers.
@@ -2310,7 +2282,7 @@
   // Remove the first animation from the main-thread controller, and add a
   // new animation affecting the same property.
   controller->RemoveAnimation(
-      controller->GetAnimation(Animation::Opacity)->id());
+      controller->GetAnimation(Animation::OPACITY)->id());
   int second_animation_id =
       AddOpacityTransitionToController(controller.get(), 1, 1.f, 0.5f, true);
   controller->PushAnimationUpdatesTo(controller_impl.get());
@@ -2331,10 +2303,10 @@
 
   // The original animation should still be running, and the new animation
   // should be starting.
-  EXPECT_EQ(Animation::Running,
+  EXPECT_EQ(Animation::RUNNING,
             controller_impl->GetAnimationById(first_animation_id)->run_state());
   EXPECT_EQ(
-      Animation::Starting,
+      Animation::STARTING,
       controller_impl->GetAnimationById(second_animation_id)->run_state());
 
   // The active observer should have been ticked by the original animation,
@@ -2359,7 +2331,7 @@
   // The new animation should be running, and the active observer should have
   // been ticked at the new animation's starting point.
   EXPECT_EQ(
-      Animation::Running,
+      Animation::RUNNING,
       controller_impl->GetAnimationById(second_animation_id)->run_state());
   EXPECT_EQ(1.f, pending_dummy_impl.opacity());
   EXPECT_EQ(1.f, dummy_impl.opacity());
@@ -2373,15 +2345,14 @@
 
   scoped_ptr<Animation> animation(CreateAnimation(
       scoped_ptr<AnimationCurve>(new FakeFloatTransition(1.0, 0.f, 1.f)).Pass(),
-      1,
-      Animation::Opacity));
+      1, Animation::OPACITY));
   controller->AddAnimation(animation.Pass());
   controller->Animate(kInitialTickTime);
-  EXPECT_TRUE(controller->IsAnimatingProperty(Animation::Opacity));
+  EXPECT_TRUE(controller->IsAnimatingProperty(Animation::OPACITY));
   controller->UpdateState(true, nullptr);
   EXPECT_TRUE(controller->HasActiveAnimation());
-  EXPECT_TRUE(controller->IsAnimatingProperty(Animation::Opacity));
-  EXPECT_FALSE(controller->IsAnimatingProperty(Animation::Filter));
+  EXPECT_TRUE(controller->IsAnimatingProperty(Animation::OPACITY));
+  EXPECT_FALSE(controller->IsAnimatingProperty(Animation::FILTER));
   EXPECT_EQ(0.f, dummy.opacity());
 }
 
@@ -2393,22 +2364,21 @@
 
   scoped_ptr<Animation> animation(CreateAnimation(
       scoped_ptr<AnimationCurve>(new FakeFloatTransition(1.0, 0.f, 1.f)).Pass(),
-      1,
-      Animation::Opacity));
-  animation->set_fill_mode(Animation::FillModeNone);
+      1, Animation::OPACITY));
+  animation->set_fill_mode(Animation::FILL_MODE_NONE);
   animation->set_time_offset(TimeDelta::FromMilliseconds(-2000));
   controller->AddAnimation(animation.Pass());
 
   controller->Animate(kInitialTickTime);
   controller->UpdateState(true, nullptr);
-  EXPECT_FALSE(controller->IsAnimatingProperty(Animation::Opacity));
+  EXPECT_FALSE(controller->IsAnimatingProperty(Animation::OPACITY));
   EXPECT_TRUE(controller->HasActiveAnimation());
-  EXPECT_FALSE(controller->IsAnimatingProperty(Animation::Opacity));
-  EXPECT_FALSE(controller->IsAnimatingProperty(Animation::Filter));
+  EXPECT_FALSE(controller->IsAnimatingProperty(Animation::OPACITY));
+  EXPECT_FALSE(controller->IsAnimatingProperty(Animation::FILTER));
 
   controller->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(2000));
   controller->UpdateState(true, nullptr);
-  EXPECT_TRUE(controller->IsAnimatingProperty(Animation::Opacity));
+  EXPECT_TRUE(controller->IsAnimatingProperty(Animation::OPACITY));
 }
 
 }  // namespace
diff --git a/cc/animation/scroll_offset_animation_curve.cc b/cc/animation/scroll_offset_animation_curve.cc
index 9567263..87a29c1e 100644
--- a/cc/animation/scroll_offset_animation_curve.cc
+++ b/cc/animation/scroll_offset_animation_curve.cc
@@ -90,7 +90,7 @@
 }
 
 AnimationCurve::CurveType ScrollOffsetAnimationCurve::Type() const {
-  return ScrollOffset;
+  return SCROLL_OFFSET;
 }
 
 scoped_ptr<AnimationCurve> ScrollOffsetAnimationCurve::Clone() const {
diff --git a/cc/animation/scroll_offset_animation_curve_unittest.cc b/cc/animation/scroll_offset_animation_curve_unittest.cc
index cb3e914b..8aea905 100644
--- a/cc/animation/scroll_offset_animation_curve_unittest.cc
+++ b/cc/animation/scroll_offset_animation_curve_unittest.cc
@@ -68,7 +68,7 @@
   EXPECT_GT(curve->Duration().InSecondsF(), 0);
   EXPECT_LT(curve->Duration().InSecondsF(), 0.1);
 
-  EXPECT_EQ(AnimationCurve::ScrollOffset, curve->Type());
+  EXPECT_EQ(AnimationCurve::SCROLL_OFFSET, curve->Type());
   EXPECT_EQ(duration, curve->Duration());
 
   EXPECT_VECTOR2DF_EQ(initial_value,
@@ -101,7 +101,7 @@
 
   scoped_ptr<AnimationCurve> clone(curve->Clone().Pass());
 
-  EXPECT_EQ(AnimationCurve::ScrollOffset, clone->Type());
+  EXPECT_EQ(AnimationCurve::SCROLL_OFFSET, clone->Type());
   EXPECT_EQ(duration, clone->Duration());
 
   EXPECT_VECTOR2DF_EQ(initial_value,
diff --git a/cc/animation/transform_operation.cc b/cc/animation/transform_operation.cc
index e9ae86d..7421924 100644
--- a/cc/animation/transform_operation.cc
+++ b/cc/animation/transform_operation.cc
@@ -99,14 +99,14 @@
     return true;
 
   TransformOperation::Type interpolation_type =
-      TransformOperation::TransformOperationIdentity;
+      TransformOperation::TRANSFORM_OPERATION_IDENTITY;
   if (IsOperationIdentity(to))
     interpolation_type = from->type;
   else
     interpolation_type = to->type;
 
   switch (interpolation_type) {
-  case TransformOperation::TransformOperationTranslate: {
+    case TransformOperation::TRANSFORM_OPERATION_TRANSLATE: {
     SkMScalar from_x = IsOperationIdentity(from) ? 0 : from->translate.x;
     SkMScalar from_y = IsOperationIdentity(from) ? 0 : from->translate.y;
     SkMScalar from_z = IsOperationIdentity(from) ? 0 : from->translate.z;
@@ -118,7 +118,7 @@
                         BlendSkMScalars(from_z, to_z, progress));
     break;
   }
-  case TransformOperation::TransformOperationRotate: {
+  case TransformOperation::TRANSFORM_OPERATION_ROTATE: {
     SkMScalar axis_x = 0;
     SkMScalar axis_y = 0;
     SkMScalar axis_z = 1;
@@ -140,7 +140,7 @@
     }
     break;
   }
-  case TransformOperation::TransformOperationScale: {
+  case TransformOperation::TRANSFORM_OPERATION_SCALE: {
     SkMScalar from_x = IsOperationIdentity(from) ? 1 : from->scale.x;
     SkMScalar from_y = IsOperationIdentity(from) ? 1 : from->scale.y;
     SkMScalar from_z = IsOperationIdentity(from) ? 1 : from->scale.z;
@@ -152,7 +152,7 @@
                     BlendSkMScalars(from_z, to_z, progress));
     break;
   }
-  case TransformOperation::TransformOperationSkew: {
+  case TransformOperation::TRANSFORM_OPERATION_SKEW: {
     SkMScalar from_x = IsOperationIdentity(from) ? 0 : from->skew.x;
     SkMScalar from_y = IsOperationIdentity(from) ? 0 : from->skew.y;
     SkMScalar to_x = IsOperationIdentity(to) ? 0 : to->skew.x;
@@ -161,7 +161,7 @@
     result->SkewY(BlendSkMScalars(from_y, to_y, progress));
     break;
   }
-  case TransformOperation::TransformOperationPerspective: {
+  case TransformOperation::TRANSFORM_OPERATION_PERSPECTIVE: {
     SkMScalar from_perspective_depth =
         IsOperationIdentity(from) ? std::numeric_limits<SkMScalar>::max()
                                   : from->perspective_depth;
@@ -180,7 +180,7 @@
     result->ApplyPerspectiveDepth(1.f / blended_perspective_depth);
     break;
   }
-  case TransformOperation::TransformOperationMatrix: {
+  case TransformOperation::TRANSFORM_OPERATION_MATRIX: {
     gfx::Transform to_matrix;
     if (!IsOperationIdentity(to))
       to_matrix = to->matrix;
@@ -192,7 +192,7 @@
       return false;
     break;
   }
-  case TransformOperation::TransformOperationIdentity:
+  case TransformOperation::TRANSFORM_OPERATION_IDENTITY:
     // Do nothing.
     break;
   }
@@ -375,20 +375,20 @@
   }
 
   TransformOperation::Type interpolation_type =
-      TransformOperation::TransformOperationIdentity;
+      TransformOperation::TRANSFORM_OPERATION_IDENTITY;
   if (is_identity_to)
     interpolation_type = from->type;
   else
     interpolation_type = to->type;
 
   switch (interpolation_type) {
-    case TransformOperation::TransformOperationIdentity:
+    case TransformOperation::TRANSFORM_OPERATION_IDENTITY:
       *bounds = box;
       return true;
-    case TransformOperation::TransformOperationTranslate:
-    case TransformOperation::TransformOperationSkew:
-    case TransformOperation::TransformOperationPerspective:
-    case TransformOperation::TransformOperationScale: {
+    case TransformOperation::TRANSFORM_OPERATION_TRANSLATE:
+    case TransformOperation::TRANSFORM_OPERATION_SKEW:
+    case TransformOperation::TRANSFORM_OPERATION_PERSPECTIVE:
+    case TransformOperation::TRANSFORM_OPERATION_SCALE: {
       gfx::Transform from_transform;
       gfx::Transform to_transform;
       if (!BlendTransformOperations(from, to, min_progress, &from_transform) ||
@@ -404,7 +404,7 @@
 
       return true;
     }
-    case TransformOperation::TransformOperationRotate: {
+    case TransformOperation::TRANSFORM_OPERATION_ROTATE: {
       SkMScalar axis_x = 0;
       SkMScalar axis_y = 0;
       SkMScalar axis_z = 1;
@@ -429,7 +429,7 @@
       }
       return true;
     }
-    case TransformOperation::TransformOperationMatrix:
+    case TransformOperation::TRANSFORM_OPERATION_MATRIX:
       return false;
   }
   NOTREACHED();
diff --git a/cc/animation/transform_operation.h b/cc/animation/transform_operation.h
index 345ff295e..3ea5fc2 100644
--- a/cc/animation/transform_operation.h
+++ b/cc/animation/transform_operation.h
@@ -15,18 +15,16 @@
 
 struct TransformOperation {
   enum Type {
-    TransformOperationTranslate,
-    TransformOperationRotate,
-    TransformOperationScale,
-    TransformOperationSkew,
-    TransformOperationPerspective,
-    TransformOperationMatrix,
-    TransformOperationIdentity
+    TRANSFORM_OPERATION_TRANSLATE,
+    TRANSFORM_OPERATION_ROTATE,
+    TRANSFORM_OPERATION_SCALE,
+    TRANSFORM_OPERATION_SKEW,
+    TRANSFORM_OPERATION_PERSPECTIVE,
+    TRANSFORM_OPERATION_MATRIX,
+    TRANSFORM_OPERATION_IDENTITY
   };
 
-  TransformOperation()
-      : type(TransformOperationIdentity) {
-  }
+  TransformOperation() : type(TRANSFORM_OPERATION_IDENTITY) {}
 
   Type type;
   gfx::Transform matrix;
diff --git a/cc/animation/transform_operations.cc b/cc/animation/transform_operations.cc
index d9d58f2..20e8c6a 100644
--- a/cc/animation/transform_operations.cc
+++ b/cc/animation/transform_operations.cc
@@ -85,9 +85,9 @@
 
 bool TransformOperations::AffectsScale() const {
   for (size_t i = 0; i < operations_.size(); ++i) {
-    if (operations_[i].type == TransformOperation::TransformOperationScale)
+    if (operations_[i].type == TransformOperation::TRANSFORM_OPERATION_SCALE)
       return true;
-    if (operations_[i].type == TransformOperation::TransformOperationMatrix &&
+    if (operations_[i].type == TransformOperation::TRANSFORM_OPERATION_MATRIX &&
         !operations_[i].matrix.IsIdentityOrTranslation())
       return true;
   }
@@ -97,18 +97,18 @@
 bool TransformOperations::PreservesAxisAlignment() const {
   for (size_t i = 0; i < operations_.size(); ++i) {
     switch (operations_[i].type) {
-      case TransformOperation::TransformOperationIdentity:
-      case TransformOperation::TransformOperationTranslate:
-      case TransformOperation::TransformOperationScale:
+      case TransformOperation::TRANSFORM_OPERATION_IDENTITY:
+      case TransformOperation::TRANSFORM_OPERATION_TRANSLATE:
+      case TransformOperation::TRANSFORM_OPERATION_SCALE:
         continue;
-      case TransformOperation::TransformOperationMatrix:
+      case TransformOperation::TRANSFORM_OPERATION_MATRIX:
         if (!operations_[i].matrix.IsIdentity() &&
             !operations_[i].matrix.IsScaleOrTranslation())
           return false;
         continue;
-      case TransformOperation::TransformOperationRotate:
-      case TransformOperation::TransformOperationSkew:
-      case TransformOperation::TransformOperationPerspective:
+      case TransformOperation::TRANSFORM_OPERATION_ROTATE:
+      case TransformOperation::TRANSFORM_OPERATION_SKEW:
+      case TransformOperation::TRANSFORM_OPERATION_PERSPECTIVE:
         return false;
     }
   }
@@ -118,17 +118,17 @@
 bool TransformOperations::IsTranslation() const {
   for (size_t i = 0; i < operations_.size(); ++i) {
     switch (operations_[i].type) {
-      case TransformOperation::TransformOperationIdentity:
-      case TransformOperation::TransformOperationTranslate:
+      case TransformOperation::TRANSFORM_OPERATION_IDENTITY:
+      case TransformOperation::TRANSFORM_OPERATION_TRANSLATE:
         continue;
-      case TransformOperation::TransformOperationMatrix:
+      case TransformOperation::TRANSFORM_OPERATION_MATRIX:
         if (!operations_[i].matrix.IsIdentityOrTranslation())
           return false;
         continue;
-      case TransformOperation::TransformOperationRotate:
-      case TransformOperation::TransformOperationScale:
-      case TransformOperation::TransformOperationSkew:
-      case TransformOperation::TransformOperationPerspective:
+      case TransformOperation::TRANSFORM_OPERATION_ROTATE:
+      case TransformOperation::TRANSFORM_OPERATION_SCALE:
+      case TransformOperation::TRANSFORM_OPERATION_SKEW:
+      case TransformOperation::TRANSFORM_OPERATION_PERSPECTIVE:
         return false;
     }
   }
@@ -140,18 +140,18 @@
   bool has_scale_component = false;
   for (size_t i = 0; i < operations_.size(); ++i) {
     switch (operations_[i].type) {
-      case TransformOperation::TransformOperationIdentity:
-      case TransformOperation::TransformOperationTranslate:
+      case TransformOperation::TRANSFORM_OPERATION_IDENTITY:
+      case TransformOperation::TRANSFORM_OPERATION_TRANSLATE:
         continue;
-      case TransformOperation::TransformOperationMatrix:
+      case TransformOperation::TRANSFORM_OPERATION_MATRIX:
         if (!operations_[i].matrix.IsIdentityOrTranslation())
           return false;
         continue;
-      case TransformOperation::TransformOperationRotate:
-      case TransformOperation::TransformOperationSkew:
-      case TransformOperation::TransformOperationPerspective:
+      case TransformOperation::TRANSFORM_OPERATION_ROTATE:
+      case TransformOperation::TRANSFORM_OPERATION_SKEW:
+      case TransformOperation::TRANSFORM_OPERATION_PERSPECTIVE:
         return false;
-      case TransformOperation::TransformOperationScale:
+      case TransformOperation::TRANSFORM_OPERATION_SCALE:
         if (has_scale_component)
           return false;
         has_scale_component = true;
@@ -191,7 +191,7 @@
                                           SkMScalar z) {
   TransformOperation to_add;
   to_add.matrix.Translate3d(x, y, z);
-  to_add.type = TransformOperation::TransformOperationTranslate;
+  to_add.type = TransformOperation::TRANSFORM_OPERATION_TRANSLATE;
   to_add.translate.x = x;
   to_add.translate.y = y;
   to_add.translate.z = z;
@@ -205,7 +205,7 @@
                                        SkMScalar degrees) {
   TransformOperation to_add;
   to_add.matrix.RotateAbout(gfx::Vector3dF(x, y, z), degrees);
-  to_add.type = TransformOperation::TransformOperationRotate;
+  to_add.type = TransformOperation::TRANSFORM_OPERATION_ROTATE;
   to_add.rotate.axis.x = x;
   to_add.rotate.axis.y = y;
   to_add.rotate.axis.z = z;
@@ -217,7 +217,7 @@
 void TransformOperations::AppendScale(SkMScalar x, SkMScalar y, SkMScalar z) {
   TransformOperation to_add;
   to_add.matrix.Scale3d(x, y, z);
-  to_add.type = TransformOperation::TransformOperationScale;
+  to_add.type = TransformOperation::TRANSFORM_OPERATION_SCALE;
   to_add.scale.x = x;
   to_add.scale.y = y;
   to_add.scale.z = z;
@@ -229,7 +229,7 @@
   TransformOperation to_add;
   to_add.matrix.SkewX(x);
   to_add.matrix.SkewY(y);
-  to_add.type = TransformOperation::TransformOperationSkew;
+  to_add.type = TransformOperation::TRANSFORM_OPERATION_SKEW;
   to_add.skew.x = x;
   to_add.skew.y = y;
   operations_.push_back(to_add);
@@ -239,7 +239,7 @@
 void TransformOperations::AppendPerspective(SkMScalar depth) {
   TransformOperation to_add;
   to_add.matrix.ApplyPerspectiveDepth(depth);
-  to_add.type = TransformOperation::TransformOperationPerspective;
+  to_add.type = TransformOperation::TRANSFORM_OPERATION_PERSPECTIVE;
   to_add.perspective_depth = depth;
   operations_.push_back(to_add);
   decomposed_transform_dirty_ = true;
@@ -248,7 +248,7 @@
 void TransformOperations::AppendMatrix(const gfx::Transform& matrix) {
   TransformOperation to_add;
   to_add.matrix = matrix;
-  to_add.type = TransformOperation::TransformOperationMatrix;
+  to_add.type = TransformOperation::TRANSFORM_OPERATION_MATRIX;
   operations_.push_back(to_add);
   decomposed_transform_dirty_ = true;
 }
diff --git a/cc/base/latency_info_swap_promise_monitor.cc b/cc/base/latency_info_swap_promise_monitor.cc
index dc287398..de065d1 100644
--- a/cc/base/latency_info_swap_promise_monitor.cc
+++ b/cc/base/latency_info_swap_promise_monitor.cc
@@ -12,12 +12,14 @@
 
 namespace {
 
-bool AddRenderingScheduledComponent(ui::LatencyInfo* latency_info) {
-  if (latency_info->FindLatency(
-          ui::INPUT_EVENT_LATENCY_RENDERING_SCHEDULED_COMPONENT, 0, nullptr))
+bool AddRenderingScheduledComponent(ui::LatencyInfo* latency_info,
+                                    bool on_main) {
+  ui::LatencyComponentType type = on_main ?
+      ui::INPUT_EVENT_LATENCY_RENDERING_SCHEDULED_MAIN_COMPONENT :
+      ui::INPUT_EVENT_LATENCY_RENDERING_SCHEDULED_IMPL_COMPONENT;
+  if (latency_info->FindLatency(type, 0, nullptr))
     return false;
-  latency_info->AddLatencyNumber(
-      ui::INPUT_EVENT_LATENCY_RENDERING_SCHEDULED_COMPONENT, 0, 0);
+  latency_info->AddLatencyNumber(type, 0, 0);
   return true;
 }
 
@@ -48,14 +50,14 @@
 LatencyInfoSwapPromiseMonitor::~LatencyInfoSwapPromiseMonitor() {}
 
 void LatencyInfoSwapPromiseMonitor::OnSetNeedsCommitOnMain() {
-  if (AddRenderingScheduledComponent(latency_)) {
+  if (AddRenderingScheduledComponent(latency_, true /* on_main */)) {
     scoped_ptr<SwapPromise> swap_promise(new LatencyInfoSwapPromise(*latency_));
     layer_tree_host_->QueueSwapPromise(swap_promise.Pass());
   }
 }
 
 void LatencyInfoSwapPromiseMonitor::OnSetNeedsRedrawOnImpl() {
-  if (AddRenderingScheduledComponent(latency_)) {
+  if (AddRenderingScheduledComponent(latency_, false /* on_main */)) {
     scoped_ptr<SwapPromise> swap_promise(new LatencyInfoSwapPromise(*latency_));
     layer_tree_host_impl_->active_tree()->QueueSwapPromise(swap_promise.Pass());
   }
diff --git a/cc/blink/cc_blink_tests.gyp b/cc/blink/cc_blink_tests.gyp
index 003d126..2e3fef5 100644
--- a/cc/blink/cc_blink_tests.gyp
+++ b/cc/blink/cc_blink_tests.gyp
@@ -20,10 +20,10 @@
         'cc_blink.gyp:cc_blink',
       ],
       'sources': [
+        '../../base/test/run_all_unittests.cc',
         'web_animation_unittest.cc',
         'web_float_animation_curve_unittest.cc',
         'web_layer_impl_fixed_bounds_unittest.cc',
-        '../../base/test/run_all_unittests.cc',
       ],
     }
   ],
diff --git a/cc/blink/web_animation_impl.cc b/cc/blink/web_animation_impl.cc
index 24fa4ec8..705c33a9 100644
--- a/cc/blink/web_animation_impl.cc
+++ b/cc/blink/web_animation_impl.cc
@@ -119,13 +119,13 @@
 blink::WebCompositorAnimation::Direction WebCompositorAnimationImpl::direction()
     const {
   switch (animation_->direction()) {
-    case cc::Animation::Normal:
+    case cc::Animation::DIRECTION_NORMAL:
       return DirectionNormal;
-    case cc::Animation::Reverse:
+    case cc::Animation::DIRECTION_REVERSE:
       return DirectionReverse;
-    case cc::Animation::Alternate:
+    case cc::Animation::DIRECTION_ALTERNATE:
       return DirectionAlternate;
-    case cc::Animation::AlternateReverse:
+    case cc::Animation::DIRECTION_ALTERNATE_REVERSE:
       return DirectionAlternateReverse;
     default:
       NOTREACHED();
@@ -136,16 +136,16 @@
 void WebCompositorAnimationImpl::setDirection(Direction direction) {
   switch (direction) {
     case DirectionNormal:
-      animation_->set_direction(cc::Animation::Normal);
+      animation_->set_direction(cc::Animation::DIRECTION_NORMAL);
       break;
     case DirectionReverse:
-      animation_->set_direction(cc::Animation::Reverse);
+      animation_->set_direction(cc::Animation::DIRECTION_REVERSE);
       break;
     case DirectionAlternate:
-      animation_->set_direction(cc::Animation::Alternate);
+      animation_->set_direction(cc::Animation::DIRECTION_ALTERNATE);
       break;
     case DirectionAlternateReverse:
-      animation_->set_direction(cc::Animation::AlternateReverse);
+      animation_->set_direction(cc::Animation::DIRECTION_ALTERNATE_REVERSE);
       break;
   }
 }
@@ -161,13 +161,13 @@
 blink::WebCompositorAnimation::FillMode WebCompositorAnimationImpl::fillMode()
     const {
   switch (animation_->fill_mode()) {
-    case cc::Animation::FillModeNone:
+    case cc::Animation::FILL_MODE_NONE:
       return FillModeNone;
-    case cc::Animation::FillModeForwards:
+    case cc::Animation::FILL_MODE_FORWARDS:
       return FillModeForwards;
-    case cc::Animation::FillModeBackwards:
+    case cc::Animation::FILL_MODE_BACKWARDS:
       return FillModeBackwards;
-    case cc::Animation::FillModeBoth:
+    case cc::Animation::FILL_MODE_BOTH:
       return FillModeBoth;
     default:
       NOTREACHED();
@@ -178,16 +178,16 @@
 void WebCompositorAnimationImpl::setFillMode(FillMode fill_mode) {
   switch (fill_mode) {
     case FillModeNone:
-      animation_->set_fill_mode(cc::Animation::FillModeNone);
+      animation_->set_fill_mode(cc::Animation::FILL_MODE_NONE);
       break;
     case FillModeForwards:
-      animation_->set_fill_mode(cc::Animation::FillModeForwards);
+      animation_->set_fill_mode(cc::Animation::FILL_MODE_FORWARDS);
       break;
     case FillModeBackwards:
-      animation_->set_fill_mode(cc::Animation::FillModeBackwards);
+      animation_->set_fill_mode(cc::Animation::FILL_MODE_BACKWARDS);
       break;
     case FillModeBoth:
-      animation_->set_fill_mode(cc::Animation::FillModeBoth);
+      animation_->set_fill_mode(cc::Animation::FILL_MODE_BOTH);
       break;
   }
 }
diff --git a/cc/blink/web_display_item_list_impl.cc b/cc/blink/web_display_item_list_impl.cc
index dc1d430..4941afe 100644
--- a/cc/blink/web_display_item_list_impl.cc
+++ b/cc/blink/web_display_item_list_impl.cc
@@ -35,14 +35,7 @@
 
 void WebDisplayItemListImpl::appendDrawingItem(const SkPicture* picture) {
   display_item_list_->AppendItem(cc::DrawingDisplayItem::Create(
-      skia::SharePtr(const_cast<SkPicture*>(picture)), gfx::PointF(0, 0)));
-}
-
-void WebDisplayItemListImpl::appendDrawingItem(
-    SkPicture* picture,
-    const blink::WebFloatPoint& location) {
-  display_item_list_->AppendItem(
-      cc::DrawingDisplayItem::Create(skia::SharePtr(picture), location));
+      skia::SharePtr(const_cast<SkPicture*>(picture))));
 }
 
 void WebDisplayItemListImpl::appendClipItem(
@@ -101,7 +94,6 @@
   display_item_list_->AppendItem(cc::EndTransparencyDisplayItem::Create());
 }
 
-#if FILTER_DISPLAY_ITEM_USES_FILTER_OPERATIONS
 void WebDisplayItemListImpl::appendFilterItem(
     const blink::WebFilterOperations& filters,
     const blink::WebFloatRect& bounds) {
@@ -110,22 +102,23 @@
   display_item_list_->AppendItem(
       cc::FilterDisplayItem::Create(filters_impl.AsFilterOperations(), bounds));
 }
-#else
-void WebDisplayItemListImpl::appendFilterItem(
-    SkImageFilter* filter,
-    const blink::WebFloatRect& bounds) {
-  cc::FilterOperations filter_operations;
-  filter_operations.Append(
-      cc::FilterOperation::CreateReferenceFilter(skia::SharePtr(filter)));
-  display_item_list_->AppendItem(
-      cc::FilterDisplayItem::Create(filter_operations, bounds));
-}
-#endif
 
 void WebDisplayItemListImpl::appendEndFilterItem() {
   display_item_list_->AppendItem(cc::EndFilterDisplayItem::Create());
 }
 
+void WebDisplayItemListImpl::appendScrollItem(
+    const blink::WebSize& scrollOffset,
+    ScrollContainerId) {
+  SkMatrix44 matrix;
+  matrix.setTranslate(-scrollOffset.width, -scrollOffset.height, 0);
+  appendTransformItem(matrix);
+}
+
+void WebDisplayItemListImpl::appendEndScrollItem() {
+  appendEndTransformItem();
+}
+
 WebDisplayItemListImpl::~WebDisplayItemListImpl() {
 }
 
diff --git a/cc/blink/web_display_item_list_impl.h b/cc/blink/web_display_item_list_impl.h
index f2ff8c67..8ec7a152 100644
--- a/cc/blink/web_display_item_list_impl.h
+++ b/cc/blink/web_display_item_list_impl.h
@@ -33,8 +33,6 @@
 
   // blink::WebDisplayItemList implementation.
   virtual void appendDrawingItem(const SkPicture*);
-  virtual void appendDrawingItem(SkPicture*,
-                                 const blink::WebFloatPoint& location);
   virtual void appendClipItem(
       const blink::WebRect& clip_rect,
       const blink::WebVector<SkRRect>& rounded_clip_rects);
@@ -50,14 +48,12 @@
   virtual void appendTransparencyItem(float opacity,
                                       blink::WebBlendMode blend_mode);
   virtual void appendEndTransparencyItem();
-#if FILTER_DISPLAY_ITEM_USES_FILTER_OPERATIONS
   virtual void appendFilterItem(const blink::WebFilterOperations& filters,
                                 const blink::WebFloatRect& bounds);
-#else
-  virtual void appendFilterItem(SkImageFilter* filter,
-                                const blink::WebFloatRect& bounds);
-#endif
   virtual void appendEndFilterItem();
+  virtual void appendScrollItem(const blink::WebSize& scrollOffset,
+                                ScrollContainerId);
+  virtual void appendEndScrollItem();
 
  private:
   scoped_refptr<cc::DisplayItemList> display_item_list_;
diff --git a/cc/blink/web_layer_impl.cc b/cc/blink/web_layer_impl.cc
index 2d5e459..0ef5ae77 100644
--- a/cc/blink/web_layer_impl.cc
+++ b/cc/blink/web_layer_impl.cc
@@ -397,17 +397,17 @@
 }
 
 static_assert(static_cast<ScrollBlocksOn>(blink::WebScrollBlocksOnNone) ==
-                  ScrollBlocksOnNone,
+                  SCROLL_BLOCKS_ON_NONE,
               "ScrollBlocksOn and WebScrollBlocksOn enums must match");
 static_assert(static_cast<ScrollBlocksOn>(blink::WebScrollBlocksOnStartTouch) ==
-                  ScrollBlocksOnStartTouch,
+                  SCROLL_BLOCKS_ON_START_TOUCH,
               "ScrollBlocksOn and WebScrollBlocksOn enums must match");
 static_assert(static_cast<ScrollBlocksOn>(blink::WebScrollBlocksOnWheelEvent) ==
-                  ScrollBlocksOnWheelEvent,
+                  SCROLL_BLOCKS_ON_WHEEL_EVENT,
               "ScrollBlocksOn and WebScrollBlocksOn enums must match");
 static_assert(
     static_cast<ScrollBlocksOn>(blink::WebScrollBlocksOnScrollEvent) ==
-        ScrollBlocksOnScrollEvent,
+        SCROLL_BLOCKS_ON_SCROLL_EVENT,
     "ScrollBlocksOn and WebScrollBlocksOn enums must match");
 
 void WebLayerImpl::setScrollBlocksOn(blink::WebScrollBlocksOn blocks) {
diff --git a/cc/cc.gyp b/cc/cc.gyp
index d04b51c..84a0930c 100644
--- a/cc/cc.gyp
+++ b/cc/cc.gyp
@@ -52,8 +52,8 @@
         'animation/layer_animation_value_provider.h',
         'animation/scroll_offset_animation_curve.cc',
         'animation/scroll_offset_animation_curve.h',
-        'animation/scrollbar_animation_controller.h',
         'animation/scrollbar_animation_controller.cc',
+        'animation/scrollbar_animation_controller.h',
         'animation/scrollbar_animation_controller_linear_fade.cc',
         'animation/scrollbar_animation_controller_linear_fade.h',
         'animation/scrollbar_animation_controller_thinning.cc',
@@ -88,9 +88,9 @@
         'base/swap_promise.h',
         'base/swap_promise_monitor.cc',
         'base/swap_promise_monitor.h',
-        'base/synced_property.h',
         'base/switches.cc',
         'base/switches.h',
+        'base/synced_property.h',
         'base/tiling_data.cc',
         'base/tiling_data.h',
         'base/time_util.h',
@@ -120,12 +120,12 @@
         'debug/layer_tree_debug_state.h',
         'debug/micro_benchmark.cc',
         'debug/micro_benchmark.h',
-        'debug/micro_benchmark_impl.cc',
-        'debug/micro_benchmark_impl.h',
         'debug/micro_benchmark_controller.cc',
         'debug/micro_benchmark_controller.h',
         'debug/micro_benchmark_controller_impl.cc',
         'debug/micro_benchmark_controller_impl.h',
+        'debug/micro_benchmark_impl.cc',
+        'debug/micro_benchmark_impl.h',
         'debug/paint_time_counter.cc',
         'debug/paint_time_counter.h',
         'debug/picture_debug_util.cc',
@@ -151,10 +151,10 @@
         'debug/unittest_only_benchmark_impl.h',
         'input/input_handler.cc',
         'input/input_handler.h',
-        'input/page_scale_animation.cc',
-        'input/page_scale_animation.h',
         'input/layer_selection_bound.cc',
         'input/layer_selection_bound.h',
+        'input/page_scale_animation.cc',
+        'input/page_scale_animation.h',
         'input/scroll_elasticity_helper.cc',
         'input/scroll_elasticity_helper.h',
         'input/selection_bound_type.h',
@@ -273,8 +273,8 @@
         'output/copy_output_request.h',
         'output/copy_output_result.cc',
         'output/copy_output_result.h',
-        'output/delegated_frame_data.h',
         'output/delegated_frame_data.cc',
+        'output/delegated_frame_data.h',
         'output/delegating_renderer.cc',
         'output/delegating_renderer.h',
         'output/direct_renderer.cc',
@@ -285,8 +285,8 @@
         'output/filter_operations.h',
         'output/geometry_binding.cc',
         'output/geometry_binding.h',
-        'output/gl_frame_data.h',
         'output/gl_frame_data.cc',
+        'output/gl_frame_data.h',
         'output/gl_renderer.cc',
         'output/gl_renderer.h',
         'output/gl_renderer_draw_cache.cc',
@@ -334,8 +334,8 @@
         'quads/draw_quad.h',
         'quads/io_surface_draw_quad.cc',
         'quads/io_surface_draw_quad.h',
-        'quads/largest_draw_quad.h',
         'quads/largest_draw_quad.cc',
+        'quads/largest_draw_quad.h',
         'quads/list_container.cc',
         'quads/list_container.h',
         'quads/picture_draw_quad.cc',
@@ -362,10 +362,10 @@
         'quads/yuv_video_draw_quad.h',
         'resources/bitmap_content_layer_updater.cc',
         'resources/bitmap_content_layer_updater.h',
-        'resources/bitmap_tile_task_worker_pool.cc',
-        'resources/bitmap_tile_task_worker_pool.h',
         'resources/bitmap_skpicture_content_layer_updater.cc',
         'resources/bitmap_skpicture_content_layer_updater.h',
+        'resources/bitmap_tile_task_worker_pool.cc',
+        'resources/bitmap_tile_task_worker_pool.h',
         'resources/clip_display_item.cc',
         'resources/clip_display_item.h',
         'resources/clip_path_display_item.cc',
@@ -429,19 +429,13 @@
         'resources/raster_source.h',
         'resources/raster_source_helper.cc',
         'resources/raster_source_helper.h',
+        'resources/raster_tile_priority_queue.cc',
+        'resources/raster_tile_priority_queue.h',
         'resources/raster_tile_priority_queue_all.cc',
         'resources/raster_tile_priority_queue_all.h',
         'resources/raster_tile_priority_queue_required.cc',
         'resources/raster_tile_priority_queue_required.h',
-        'resources/raster_tile_priority_queue.cc',
-        'resources/raster_tile_priority_queue.h',
         'resources/rasterizer.h',
-        'resources/software_rasterizer.cc',
-        'resources/software_rasterizer.h',
-        'resources/tile_task_worker_pool.cc',
-        'resources/tile_task_worker_pool.h',
-        'resources/tile_task_runner.cc',
-        'resources/tile_task_runner.h',
         'resources/recording_source.h',
         'resources/release_callback.h',
         'resources/resource.cc',
@@ -474,6 +468,8 @@
         'resources/single_release_callback_impl.h',
         'resources/skpicture_content_layer_updater.cc',
         'resources/skpicture_content_layer_updater.h',
+        'resources/software_rasterizer.cc',
+        'resources/software_rasterizer.h',
         'resources/task_graph_runner.cc',
         'resources/task_graph_runner.h',
         'resources/texture_mailbox.cc',
@@ -490,6 +486,10 @@
         'resources/tile_manager.h',
         'resources/tile_priority.cc',
         'resources/tile_priority.h',
+        'resources/tile_task_runner.cc',
+        'resources/tile_task_runner.h',
+        'resources/tile_task_worker_pool.cc',
+        'resources/tile_task_worker_pool.h',
         'resources/tiling_set_eviction_queue.cc',
         'resources/tiling_set_eviction_queue.h',
         'resources/tiling_set_raster_queue_all.cc',
@@ -595,14 +595,14 @@
         'surfaces/surface.h',
         'surfaces/surface_aggregator.cc',
         'surfaces/surface_aggregator.h',
+        'surfaces/surface_display_output_surface.cc',
+        'surfaces/surface_display_output_surface.h',
         'surfaces/surface_factory.cc',
         'surfaces/surface_factory.h',
         'surfaces/surface_factory_client.h',
         'surfaces/surface_id.h',
         'surfaces/surface_id_allocator.cc',
         'surfaces/surface_id_allocator.h',
-        'surfaces/surface_display_output_surface.cc',
-        'surfaces/surface_display_output_surface.h',
         'surfaces/surface_manager.cc',
         'surfaces/surface_manager.h',
         'surfaces/surface_resource_holder.cc',
diff --git a/cc/cc_tests.gyp b/cc/cc_tests.gyp
index 49337bc..4d29a60 100644
--- a/cc/cc_tests.gyp
+++ b/cc/cc_tests.gyp
@@ -32,8 +32,8 @@
       'layers/delegated_frame_resource_collection_unittest.cc',
       'layers/delegated_renderer_layer_impl_unittest.cc',
       'layers/delegated_renderer_layer_unittest.cc',
-      'layers/heads_up_display_unittest.cc',
       'layers/heads_up_display_layer_impl_unittest.cc',
+      'layers/heads_up_display_unittest.cc',
       'layers/io_surface_layer_impl_unittest.cc',
       'layers/layer_impl_unittest.cc',
       'layers/layer_iterator_unittest.cc',
@@ -47,15 +47,15 @@
       'layers/picture_image_layer_unittest.cc',
       'layers/picture_layer_impl_unittest.cc',
       'layers/picture_layer_unittest.cc',
-      'layers/render_surface_unittest.cc',
       'layers/render_surface_impl_unittest.cc',
+      'layers/render_surface_unittest.cc',
       'layers/scrollbar_layer_unittest.cc',
       'layers/solid_color_layer_impl_unittest.cc',
       'layers/solid_color_scrollbar_layer_impl_unittest.cc',
-      'layers/surface_layer_unittest.cc',
       'layers/surface_layer_impl_unittest.cc',
-      'layers/texture_layer_unittest.cc',
+      'layers/surface_layer_unittest.cc',
       'layers/texture_layer_impl_unittest.cc',
+      'layers/texture_layer_unittest.cc',
       'layers/tiled_layer_impl_unittest.cc',
       'layers/tiled_layer_unittest.cc',
       'layers/ui_resource_layer_impl_unittest.cc',
@@ -83,8 +83,8 @@
       'resources/picture_pile_impl_unittest.cc',
       'resources/picture_pile_unittest.cc',
       'resources/picture_unittest.cc',
+      'resources/platform_color_unittest.cc',
       'resources/prioritized_resource_unittest.cc',
-      'resources/tile_task_worker_pool_unittest.cc',
       'resources/resource_provider_unittest.cc',
       'resources/resource_update_controller_unittest.cc',
       'resources/scoped_gpu_raster_unittest.cc',
@@ -94,6 +94,7 @@
       'resources/texture_uploader_unittest.cc',
       'resources/tile_manager_unittest.cc',
       'resources/tile_priority_unittest.cc',
+      'resources/tile_task_worker_pool_unittest.cc',
       'resources/video_resource_updater_unittest.cc',
       'scheduler/begin_frame_source_unittest.cc',
       'scheduler/delay_based_time_source_unittest.cc',
@@ -110,7 +111,6 @@
       'trees/layer_tree_host_pixeltest_blending.cc',
       'trees/layer_tree_host_pixeltest_filters.cc',
       'trees/layer_tree_host_pixeltest_masks.cc',
-      'trees/layer_tree_host_pixeltest_on_demand_raster.cc',
       'trees/layer_tree_host_pixeltest_readback.cc',
       'trees/layer_tree_host_pixeltest_synchronous.cc',
       'trees/layer_tree_host_unittest.cc',
@@ -119,8 +119,8 @@
       'trees/layer_tree_host_unittest_copyrequest.cc',
       'trees/layer_tree_host_unittest_damage.cc',
       'trees/layer_tree_host_unittest_delegated.cc',
-      'trees/layer_tree_host_unittest_occlusion.cc',
       'trees/layer_tree_host_unittest_no_message_loop.cc',
+      'trees/layer_tree_host_unittest_occlusion.cc',
       'trees/layer_tree_host_unittest_picture.cc',
       'trees/layer_tree_host_unittest_proxy.cc',
       'trees/layer_tree_host_unittest_scroll.cc',
@@ -132,6 +132,7 @@
       'trees/tree_synchronizer_unittest.cc',
     ],
     'cc_surfaces_unit_tests_source_files': [
+      'surfaces/display_unittest.cc',
       'surfaces/surface_aggregator_test_helpers.cc',
       'surfaces/surface_aggregator_test_helpers.h',
       'surfaces/surface_aggregator_unittest.cc',
@@ -144,6 +145,8 @@
       'test/animation_test_common.h',
       'test/begin_frame_args_test.cc',
       'test/begin_frame_args_test.h',
+      'test/failure_output_surface.cc',
+      'test/failure_output_surface.h',
       'test/fake_content_layer.cc',
       'test/fake_content_layer.h',
       'test/fake_content_layer_client.cc',
@@ -198,12 +201,8 @@
       'test/fake_ui_resource_layer_tree_host_impl.h',
       'test/fake_video_frame_provider.cc',
       'test/fake_video_frame_provider.h',
-      'test/failure_output_surface.cc',
-      'test/failure_output_surface.h',
       'test/geometry_test_utils.cc',
       'test/geometry_test_utils.h',
-      'test/test_in_process_context_provider.cc',
-      'test/test_in_process_context_provider.h',
       'test/impl_side_painting_settings.h',
       'test/layer_test_common.cc',
       'test/layer_test_common.h',
@@ -254,6 +253,8 @@
       'test/test_gpu_memory_buffer_manager.h',
       'test/test_image_factory.cc',
       'test/test_image_factory.h',
+      'test/test_in_process_context_provider.cc',
+      'test/test_in_process_context_provider.h',
       'test/test_now_source.cc',
       'test/test_now_source.h',
       'test/test_occlusion_tracker.h',
@@ -290,8 +291,8 @@
         'cc_test_support',
       ],
       'sources': [
-        'test/run_all_unittests.cc',
         'test/cc_test_suite.cc',
+        'test/run_all_unittests.cc',
         '<@(cc_unit_tests_source_files)',
         '<@(cc_surfaces_unit_tests_source_files)',
       ],
@@ -349,9 +350,9 @@
         'layers/picture_layer_impl_perftest.cc',
         'resources/picture_layer_tiling_perftest.cc',
         'resources/picture_pile_impl_perftest.cc',
-        'resources/tile_task_worker_pool_perftest.cc',
         'resources/task_graph_runner_perftest.cc',
         'resources/tile_manager_perftest.cc',
+        'resources/tile_task_worker_pool_perftest.cc',
         'test/cc_test_suite.cc',
         'test/run_all_perftests.cc',
         'trees/layer_tree_host_common_perftest.cc',
diff --git a/cc/debug/debug_colors.cc b/cc/debug/debug_colors.cc
index daec5cc2..38ecf54 100644
--- a/cc/debug/debug_colors.cc
+++ b/cc/debug/debug_colors.cc
@@ -114,9 +114,9 @@
   return Scale(2, tree_impl);
 }
 
-// Missing tile borders are red.
+// Missing tile borders are dark grey.
 SkColor DebugColors::MissingTileBorderColor() {
-  return SkColorSetARGB(100, 255, 0, 0);
+  return SkColorSetARGB(64, 64, 64, 0);
 }
 int DebugColors::MissingTileBorderWidth(const LayerTreeImpl* tree_impl) {
   return Scale(1, tree_impl);
@@ -130,11 +130,11 @@
   return Scale(1, tree_impl);
 }
 
-// Picture tile borders are dark grey.
-SkColor DebugColors::PictureTileBorderColor() {
-  return SkColorSetARGB(64, 64, 64, 0);
+// OOM tile borders are red.
+SkColor DebugColors::OOMTileBorderColor() {
+  return SkColorSetARGB(100, 255, 0, 0);
 }
-int DebugColors::PictureTileBorderWidth(const LayerTreeImpl* tree_impl) {
+int DebugColors::OOMTileBorderWidth(const LayerTreeImpl* tree_impl) {
   return Scale(1, tree_impl);
 }
 
diff --git a/cc/debug/debug_colors.h b/cc/debug/debug_colors.h
index 52dab85..8e4ec8d 100644
--- a/cc/debug/debug_colors.h
+++ b/cc/debug/debug_colors.h
@@ -56,8 +56,8 @@
   static SkColor SolidColorTileBorderColor();
   static int SolidColorTileBorderWidth(const LayerTreeImpl* tree_impl);
 
-  static SkColor PictureTileBorderColor();
-  static int PictureTileBorderWidth(const LayerTreeImpl* tree_impl);
+  static SkColor OOMTileBorderColor();
+  static int OOMTileBorderWidth(const LayerTreeImpl* tree_impl);
 
   static SkColor DirectPictureBorderColor();
   static int DirectPictureBorderWidth(const LayerTreeImpl* tree_impl);
diff --git a/cc/input/input_handler.h b/cc/input/input_handler.h
index d899a85..954f8d73 100644
--- a/cc/input/input_handler.h
+++ b/cc/input/input_handler.h
@@ -66,14 +66,14 @@
   // Note these are used in a histogram. Do not reorder or delete existing
   // entries.
   enum ScrollStatus {
-    ScrollOnMainThread = 0,
-    ScrollStarted,
-    ScrollIgnored,
-    ScrollUnknown,
+    SCROLL_ON_MAIN_THREAD = 0,
+    SCROLL_STARTED,
+    SCROLL_IGNORED,
+    SCROLL_UNKNOWN,
     // This must be the last entry.
     ScrollStatusCount
   };
-  enum ScrollInputType { Gesture, Wheel, NonBubblingGesture };
+  enum ScrollInputType { GESTURE, WHEEL, NON_BUBBLING_GESTURE };
 
   // Binds a client to this handler to receive notifications. Only one client
   // can be bound to an InputHandler. The client must live at least until the
@@ -81,10 +81,10 @@
   virtual void BindToClient(InputHandlerClient* client) = 0;
 
   // Selects a layer to be scrolled at a given point in viewport (logical
-  // pixel) coordinates. Returns ScrollStarted if the layer at the coordinates
-  // can be scrolled, ScrollOnMainThread if the scroll event should instead be
-  // delegated to the main thread, or ScrollIgnored if there is nothing to be
-  // scrolled at the given coordinates.
+  // pixel) coordinates. Returns SCROLL_STARTED if the layer at the coordinates
+  // can be scrolled, SCROLL_ON_MAIN_THREAD if the scroll event should instead
+  // be delegated to the main thread, or SCROLL_IGNORED if there is nothing to
+  // be scrolled at the given coordinates.
   virtual ScrollStatus ScrollBegin(const gfx::Point& viewport_point,
                                    ScrollInputType type) = 0;
 
@@ -102,7 +102,7 @@
   // If the scroll delta hits the root layer, and the layer can no longer move,
   // the root overscroll accumulated within this ScrollBegin() scope is reported
   // in the return value's |accumulated_overscroll| field.
-  // Should only be called if ScrollBegin() returned ScrollStarted.
+  // Should only be called if ScrollBegin() returned SCROLL_STARTED.
   virtual InputHandlerScrollResult ScrollBy(
       const gfx::Point& viewport_point,
       const gfx::Vector2dF& scroll_delta) = 0;
@@ -110,14 +110,14 @@
   virtual bool ScrollVerticallyByPage(const gfx::Point& viewport_point,
                                       ScrollDirection direction) = 0;
 
-  // Returns ScrollStarted if a layer was being actively being scrolled,
-  // ScrollIgnored if not.
+  // Returns SCROLL_STARTED if a layer was being actively being scrolled,
+  // SCROLL_IGNORED if not.
   virtual ScrollStatus FlingScrollBegin() = 0;
 
   virtual void MouseMoveAt(const gfx::Point& mouse_position) = 0;
 
   // Stop scrolling the selected layer. Should only be called if ScrollBegin()
-  // returned ScrollStarted.
+  // returned SCROLL_STARTED.
   virtual void ScrollEnd() = 0;
 
   virtual void SetRootLayerScrollOffsetDelegate(
diff --git a/cc/input/scroll_elasticity_helper.cc b/cc/input/scroll_elasticity_helper.cc
index 1e9fe7d..1c496d9 100644
--- a/cc/input/scroll_elasticity_helper.cc
+++ b/cc/input/scroll_elasticity_helper.cc
@@ -15,6 +15,7 @@
   explicit ScrollElasticityHelperImpl(LayerTreeHostImpl* layer_tree_host_impl);
   ~ScrollElasticityHelperImpl() override;
 
+  bool IsUserScrollable() const override;
   gfx::Vector2dF StretchAmount() const override;
   void SetStretchAmount(const gfx::Vector2dF& stretch_amount) override;
   gfx::ScrollOffset ScrollOffset() const override;
@@ -34,6 +35,14 @@
 ScrollElasticityHelperImpl::~ScrollElasticityHelperImpl() {
 }
 
+bool ScrollElasticityHelperImpl::IsUserScrollable() const {
+  LayerImpl* layer = layer_tree_host_impl_->OuterViewportScrollLayer();
+  if (!layer)
+    return false;
+  return layer->user_scrollable_horizontal() ||
+         layer->user_scrollable_vertical();
+}
+
 gfx::Vector2dF ScrollElasticityHelperImpl::StretchAmount() const {
   return layer_tree_host_impl_->active_tree()->elastic_overscroll()->Current(
       true);
diff --git a/cc/input/scroll_elasticity_helper.h b/cc/input/scroll_elasticity_helper.h
index bf444ee2..a2bdbfe 100644
--- a/cc/input/scroll_elasticity_helper.h
+++ b/cc/input/scroll_elasticity_helper.h
@@ -52,6 +52,8 @@
 
   virtual ~ScrollElasticityHelper() {}
 
+  virtual bool IsUserScrollable() const = 0;
+
   // The amount that the view is stretched past the normal allowable bounds.
   virtual gfx::Vector2dF StretchAmount() const = 0;
   virtual void SetStretchAmount(const gfx::Vector2dF& stretch_amount) = 0;
diff --git a/cc/layers/content_layer.cc b/cc/layers/content_layer.cc
index 6af92ec..d1ff842 100644
--- a/cc/layers/content_layer.cc
+++ b/cc/layers/content_layer.cc
@@ -114,10 +114,6 @@
     updater_->SetOpaque(opaque);
 }
 
-bool ContentLayer::SupportsLCDText() const {
-  return true;
-}
-
 skia::RefPtr<SkPicture> ContentLayer::GetPicture() const {
   if (!DrawsContent())
     return skia::RefPtr<SkPicture>();
diff --git a/cc/layers/content_layer.h b/cc/layers/content_layer.h
index 22dd8fd..5d6ac15 100644
--- a/cc/layers/content_layer.h
+++ b/cc/layers/content_layer.h
@@ -46,8 +46,6 @@
 
   void SetContentsOpaque(bool contents_opaque) override;
 
-  bool SupportsLCDText() const override;
-
   skia::RefPtr<SkPicture> GetPicture() const override;
 
   void OnOutputSurfaceCreated() override;
diff --git a/cc/layers/heads_up_display_layer_impl.cc b/cc/layers/heads_up_display_layer_impl.cc
index 97fddae..19217c9 100644
--- a/cc/layers/heads_up_display_layer_impl.cc
+++ b/cc/layers/heads_up_display_layer_impl.cc
@@ -97,7 +97,7 @@
   scoped_ptr<ScopedResource> resource =
       ScopedResource::Create(resource_provider);
   resource->Allocate(internal_content_bounds_,
-                     ResourceProvider::TextureHintImmutable, RGBA_8888);
+                     ResourceProvider::TEXTURE_HINT_IMMUTABLE, RGBA_8888);
   resources_.push_back(resource.Pass());
 }
 
@@ -210,13 +210,10 @@
   size_t row_bytes = 0;
   const void* pixels = hud_surface_->getCanvas()->peekPixels(&info, &row_bytes);
   DCHECK(pixels);
-  gfx::Rect content_rect(internal_content_bounds_);
   DCHECK(info.colorType() == kN32_SkColorType);
-  resource_provider->SetPixels(resources_.back()->id(),
-                               static_cast<const uint8_t*>(pixels),
-                               content_rect,
-                               content_rect,
-                               gfx::Vector2d());
+  resource_provider->CopyToResource(resources_.back()->id(),
+                                    static_cast<const uint8_t*>(pixels),
+                                    internal_content_bounds_);
 }
 
 void HeadsUpDisplayLayerImpl::ReleaseResources() {
diff --git a/cc/layers/layer.cc b/cc/layers/layer.cc
index 19b505b..ca0e3b8e 100644
--- a/cc/layers/layer.cc
+++ b/cc/layers/layer.cc
@@ -71,7 +71,7 @@
       force_render_surface_(false),
       transform_is_invertible_(true),
       has_render_surface_(false),
-      scroll_blocks_on_(ScrollBlocksOnNone),
+      scroll_blocks_on_(SCROLL_BLOCKS_ON_NONE),
       background_color_(0),
       opacity_(1.f),
       blend_mode_(SkXfermode::kSrcOver_Mode),
@@ -471,7 +471,7 @@
 }
 
 bool Layer::FilterIsAnimating() const {
-  return layer_animation_controller_->IsAnimatingProperty(Animation::Filter);
+  return layer_animation_controller_->IsAnimatingProperty(Animation::FILTER);
 }
 
 void Layer::SetBackgroundFilters(const FilterOperations& filters) {
@@ -491,7 +491,7 @@
 }
 
 bool Layer::OpacityIsAnimating() const {
-  return layer_animation_controller_->IsAnimatingProperty(Animation::Opacity);
+  return layer_animation_controller_->IsAnimatingProperty(Animation::OPACITY);
 }
 
 bool Layer::OpacityCanAnimateOnImplThread() const {
@@ -601,7 +601,7 @@
 }
 
 bool Layer::TransformIsAnimating() const {
-  return layer_animation_controller_->IsAnimatingProperty(Animation::Transform);
+  return layer_animation_controller_->IsAnimatingProperty(Animation::TRANSFORM);
 }
 
 void Layer::SetScrollParent(Layer* parent) {
@@ -909,7 +909,7 @@
       draw_checkerboard_for_missing_tiles_);
   layer->SetDrawsContent(DrawsContent());
   layer->SetHideLayerAndSubtree(hide_layer_and_subtree_);
-  layer->SetHasRenderSurface(has_render_surface_);
+  layer->SetHasRenderSurface(has_render_surface_ || layer->HasCopyRequest());
   if (!layer->FilterIsAnimatingOnImplOnly() && !FilterIsAnimating())
     layer->SetFilters(filters_);
   DCHECK(!(FilterIsAnimating() && layer->FilterIsAnimatingOnImplOnly()));
@@ -1181,7 +1181,7 @@
   if (!layer_animation_controller_->animation_registrar())
     return false;
 
-  if (animation->target_property() == Animation::ScrollOffset &&
+  if (animation->target_property() == Animation::SCROLL_OFFSET &&
       !layer_animation_controller_->animation_registrar()
            ->supports_scroll_animations())
     return false;
@@ -1240,10 +1240,6 @@
   return layer_tree_host_->rendering_stats_instrumentation();
 }
 
-bool Layer::SupportsLCDText() const {
-  return false;
-}
-
 void Layer::RemoveFromScrollTree() {
   if (scroll_children_.get()) {
     std::set<Layer*> copy = *scroll_children_;
diff --git a/cc/layers/layer.h b/cc/layers/layer.h
index 873f360a..8eb7c3d 100644
--- a/cc/layers/layer.h
+++ b/cc/layers/layer.h
@@ -242,7 +242,6 @@
   bool screen_space_opacity_is_animating() const {
     return draw_properties_.screen_space_opacity_is_animating;
   }
-  bool can_use_lcd_text() const { return draw_properties_.can_use_lcd_text; }
   bool is_clipped() const { return draw_properties_.is_clipped; }
   gfx::Rect clip_rect() const { return draw_properties_.clip_rect; }
   gfx::Rect drawable_content_rect() const {
@@ -457,8 +456,6 @@
   float raster_scale() const { return raster_scale_; }
   bool raster_scale_is_unknown() const { return raster_scale_ == 0.f; }
 
-  virtual bool SupportsLCDText() const;
-
   void SetNeedsPushProperties();
   bool needs_push_properties() const { return needs_push_properties_; }
   bool descendant_needs_push_properties() const {
diff --git a/cc/layers/layer_impl.cc b/cc/layers/layer_impl.cc
index 6e11644..63fd4dc 100644
--- a/cc/layers/layer_impl.cc
+++ b/cc/layers/layer_impl.cc
@@ -54,7 +54,7 @@
       should_scroll_on_main_thread_(false),
       have_wheel_event_handlers_(false),
       have_scroll_event_handlers_(false),
-      scroll_blocks_on_(ScrollBlocksOnNone),
+      scroll_blocks_on_(SCROLL_BLOCKS_ON_NONE),
       user_scrollable_horizontal_(true),
       user_scrollable_vertical_(true),
       stacking_order_changed_(false),
@@ -425,12 +425,12 @@
     ScrollBlocksOn effective_block_mode) const {
   if (should_scroll_on_main_thread()) {
     TRACE_EVENT0("cc", "LayerImpl::TryScroll: Failed ShouldScrollOnMainThread");
-    return InputHandler::ScrollOnMainThread;
+    return InputHandler::SCROLL_ON_MAIN_THREAD;
   }
 
   if (!screen_space_transform().IsInvertible()) {
     TRACE_EVENT0("cc", "LayerImpl::TryScroll: Ignored NonInvertibleTransform");
-    return InputHandler::ScrollIgnored;
+    return InputHandler::SCROLL_IGNORED;
   }
 
   if (!non_fast_scrollable_region().IsEmpty()) {
@@ -440,7 +440,7 @@
     if (!screen_space_transform().GetInverse(&inverse_screen_space_transform)) {
       // TODO(shawnsingh): We shouldn't be applying a projection if screen space
       // transform is uninvertible here. Perhaps we should be returning
-      // ScrollOnMainThread in this case?
+      // SCROLL_ON_MAIN_THREAD in this case?
     }
 
     gfx::PointF hit_test_point_in_content_space =
@@ -456,25 +456,25 @@
             gfx::ToRoundedPoint(hit_test_point_in_layer_space))) {
       TRACE_EVENT0("cc",
                    "LayerImpl::tryScroll: Failed NonFastScrollableRegion");
-      return InputHandler::ScrollOnMainThread;
+      return InputHandler::SCROLL_ON_MAIN_THREAD;
     }
   }
 
   if (have_scroll_event_handlers() &&
-      effective_block_mode & ScrollBlocksOnScrollEvent) {
+      effective_block_mode & SCROLL_BLOCKS_ON_SCROLL_EVENT) {
     TRACE_EVENT0("cc", "LayerImpl::tryScroll: Failed ScrollEventHandlers");
-    return InputHandler::ScrollOnMainThread;
+    return InputHandler::SCROLL_ON_MAIN_THREAD;
   }
 
-  if (type == InputHandler::Wheel && have_wheel_event_handlers() &&
-      effective_block_mode & ScrollBlocksOnWheelEvent) {
+  if (type == InputHandler::WHEEL && have_wheel_event_handlers() &&
+      effective_block_mode & SCROLL_BLOCKS_ON_WHEEL_EVENT) {
     TRACE_EVENT0("cc", "LayerImpl::tryScroll: Failed WheelEventHandlers");
-    return InputHandler::ScrollOnMainThread;
+    return InputHandler::SCROLL_ON_MAIN_THREAD;
   }
 
   if (!scrollable()) {
     TRACE_EVENT0("cc", "LayerImpl::tryScroll: Ignored not scrollable");
-    return InputHandler::ScrollIgnored;
+    return InputHandler::SCROLL_IGNORED;
   }
 
   gfx::ScrollOffset max_scroll_offset = MaxScrollOffset();
@@ -482,10 +482,10 @@
     TRACE_EVENT0("cc",
                  "LayerImpl::tryScroll: Ignored. Technically scrollable,"
                  " but has no affordance in either direction.");
-    return InputHandler::ScrollIgnored;
+    return InputHandler::SCROLL_IGNORED;
   }
 
-  return InputHandler::ScrollStarted;
+  return InputHandler::SCROLL_STARTED;
 }
 
 gfx::Rect LayerImpl::LayerRectToContentRect(
@@ -517,7 +517,7 @@
       draw_checkerboard_for_missing_tiles_);
   layer->SetDrawsContent(DrawsContent());
   layer->SetHideLayerAndSubtree(hide_layer_and_subtree_);
-  layer->SetHasRenderSurface(!!render_surface());
+  layer->SetHasRenderSurface(!!render_surface() || layer->HasCopyRequest());
   layer->SetFilters(filters());
   layer->SetBackgroundFilters(background_filters());
   layer->SetMasksToBounds(masks_to_bounds_);
@@ -672,7 +672,7 @@
 
   result->SetBoolean("DrawsContent", draws_content_);
   result->SetBoolean("Is3dSorted", Is3dSorted());
-  result->SetDouble("Opacity", opacity());
+  result->SetDouble("OPACITY", opacity());
   result->SetBoolean("ContentsOpaque", contents_opaque_);
 
   if (scrollable())
@@ -689,11 +689,11 @@
 
   if (scroll_blocks_on_) {
     list = new base::ListValue;
-    if (scroll_blocks_on_ & ScrollBlocksOnStartTouch)
+    if (scroll_blocks_on_ & SCROLL_BLOCKS_ON_START_TOUCH)
       list->AppendString("StartTouch");
-    if (scroll_blocks_on_ & ScrollBlocksOnWheelEvent)
+    if (scroll_blocks_on_ & SCROLL_BLOCKS_ON_WHEEL_EVENT)
       list->AppendString("WheelEvent");
-    if (scroll_blocks_on_ & ScrollBlocksOnScrollEvent)
+    if (scroll_blocks_on_ & SCROLL_BLOCKS_ON_SCROLL_EVENT)
       list->AppendString("ScrollEvent");
     result->Set("ScrollBlocksOn", list);
   }
@@ -946,12 +946,12 @@
 }
 
 bool LayerImpl::FilterIsAnimating() const {
-  return layer_animation_controller_->IsAnimatingProperty(Animation::Filter);
+  return layer_animation_controller_->IsAnimatingProperty(Animation::FILTER);
 }
 
 bool LayerImpl::FilterIsAnimatingOnImplOnly() const {
   Animation* filter_animation =
-      layer_animation_controller_->GetAnimation(Animation::Filter);
+      layer_animation_controller_->GetAnimation(Animation::FILTER);
   return filter_animation && filter_animation->is_impl_only();
 }
 
@@ -989,12 +989,12 @@
 }
 
 bool LayerImpl::OpacityIsAnimating() const {
-  return layer_animation_controller_->IsAnimatingProperty(Animation::Opacity);
+  return layer_animation_controller_->IsAnimatingProperty(Animation::OPACITY);
 }
 
 bool LayerImpl::OpacityIsAnimatingOnImplOnly() const {
   Animation* opacity_animation =
-      layer_animation_controller_->GetAnimation(Animation::Opacity);
+      layer_animation_controller_->GetAnimation(Animation::OPACITY);
   return opacity_animation && opacity_animation->is_impl_only();
 }
 
@@ -1066,12 +1066,12 @@
 }
 
 bool LayerImpl::TransformIsAnimating() const {
-  return layer_animation_controller_->IsAnimatingProperty(Animation::Transform);
+  return layer_animation_controller_->IsAnimatingProperty(Animation::TRANSFORM);
 }
 
 bool LayerImpl::TransformIsAnimatingOnImplOnly() const {
   Animation* transform_animation =
-      layer_animation_controller_->GetAnimation(Animation::Transform);
+      layer_animation_controller_->GetAnimation(Animation::TRANSFORM);
   return transform_animation && transform_animation->is_impl_only();
 }
 
@@ -1332,7 +1332,7 @@
 
 void LayerImpl::DidBecomeActive() {
   if (layer_tree_impl_->settings().scrollbar_animator ==
-      LayerTreeSettings::NoAnimator) {
+      LayerTreeSettings::NO_ANIMATOR) {
     return;
   }
 
@@ -1573,7 +1573,7 @@
     base::TimeTicks monotonic_time,
     Animation::TargetProperty target_property,
     int group) {
-  if (target_property == Animation::ScrollOffset)
+  if (target_property == Animation::SCROLL_OFFSET)
     layer_tree_impl_->InputScrollAnimationFinished();
 }
 
diff --git a/cc/layers/layer_impl.h b/cc/layers/layer_impl.h
index 97fd205..5de0d9a 100644
--- a/cc/layers/layer_impl.h
+++ b/cc/layers/layer_impl.h
@@ -667,7 +667,7 @@
   bool have_wheel_event_handlers_ : 1;
   bool have_scroll_event_handlers_ : 1;
 
-  static_assert(ScrollBlocksOnMax < (1 << 3), "ScrollBlocksOn too big");
+  static_assert(SCROLL_BLOCKS_ON_MAX < (1 << 3), "ScrollBlocksOn too big");
   ScrollBlocksOn scroll_blocks_on_ : 3;
 
   bool user_scrollable_horizontal_ : 1;
diff --git a/cc/layers/layer_unittest.cc b/cc/layers/layer_unittest.cc
index 460726f..8a61510 100644
--- a/cc/layers/layer_unittest.cc
+++ b/cc/layers/layer_unittest.cc
@@ -738,8 +738,9 @@
                                    1.0,
                                    0,
                                    100);
-  impl_layer->layer_animation_controller()->GetAnimation(Animation::Transform)->
-      set_is_impl_only(true);
+  impl_layer->layer_animation_controller()
+      ->GetAnimation(Animation::TRANSFORM)
+      ->set_is_impl_only(true);
   transform.Rotate(45.0);
   EXPECT_SET_NEEDS_COMMIT(1, test_layer->SetTransform(transform));
 
@@ -779,8 +780,9 @@
                                    0.3f,
                                    0.7f,
                                    false);
-  impl_layer->layer_animation_controller()->GetAnimation(Animation::Opacity)->
-      set_is_impl_only(true);
+  impl_layer->layer_animation_controller()
+      ->GetAnimation(Animation::OPACITY)
+      ->set_is_impl_only(true);
   EXPECT_SET_NEEDS_COMMIT(1, test_layer->SetOpacity(0.75f));
 
   EXPECT_FALSE(impl_layer->LayerPropertyChanged());
@@ -815,8 +817,9 @@
   impl_layer->ResetAllChangeTrackingForSubtree();
   AddAnimatedFilterToController(
       impl_layer->layer_animation_controller(), 1.0, 1.f, 2.f);
-  impl_layer->layer_animation_controller()->GetAnimation(Animation::Filter)->
-      set_is_impl_only(true);
+  impl_layer->layer_animation_controller()
+      ->GetAnimation(Animation::FILTER)
+      ->set_is_impl_only(true);
   filters.Append(FilterOperation::CreateSepiaFilter(0.5f));
   EXPECT_SET_NEEDS_COMMIT(1, test_layer->SetFilters(filters));
 
@@ -1148,7 +1151,7 @@
   curve->AddKeyframe(
       FloatKeyframe::Create(base::TimeDelta::FromSecondsD(1.0), 0.7f, nullptr));
   scoped_ptr<Animation> animation =
-      Animation::Create(curve.Pass(), 0, 0, Animation::Opacity);
+      Animation::Create(curve.Pass(), 0, 0, Animation::OPACITY);
 
   return layer->AddAnimation(animation.Pass());
 }
diff --git a/cc/layers/picture_image_layer.cc b/cc/layers/picture_image_layer.cc
index 9f35d6e..6886612 100644
--- a/cc/layers/picture_image_layer.cc
+++ b/cc/layers/picture_image_layer.cc
@@ -73,8 +73,7 @@
   PaintContents(canvas, clip, painting_control);
 
   skia::RefPtr<SkPicture> picture = skia::AdoptRef(recorder.endRecording());
-  display_item_list->AppendItem(
-      DrawingDisplayItem::Create(picture, gfx::Point()));
+  display_item_list->AppendItem(DrawingDisplayItem::Create(picture));
   return display_item_list;
 }
 
diff --git a/cc/layers/picture_image_layer_impl_unittest.cc b/cc/layers/picture_image_layer_impl_unittest.cc
index d40f555..3d2d38e 100644
--- a/cc/layers/picture_image_layer_impl_unittest.cc
+++ b/cc/layers/picture_image_layer_impl_unittest.cc
@@ -52,9 +52,6 @@
       case PENDING_TREE:
         tree = host_impl_.pending_tree();
         break;
-      case NUM_TREES:
-        NOTREACHED();
-        break;
     }
     TestablePictureImageLayerImpl* layer =
         new TestablePictureImageLayerImpl(tree, id);
diff --git a/cc/layers/picture_layer.cc b/cc/layers/picture_layer.cc
index 0ca2ae7..e7918e2 100644
--- a/cc/layers/picture_layer.cc
+++ b/cc/layers/picture_layer.cc
@@ -23,7 +23,6 @@
     : client_(client),
       instrumentation_object_tracker_(id()),
       update_source_frame_number_(-1),
-      can_use_lcd_text_for_update_(true),
       is_mask_(false),
       nearest_neighbor_(false) {
 }
@@ -69,8 +68,10 @@
 
   layer_impl->SetNearestNeighbor(nearest_neighbor_);
 
+  // Preserve lcd text settings from the current raster source.
+  bool can_use_lcd_text = layer_impl->RasterSourceUsesLCDText();
   scoped_refptr<RasterSource> raster_source =
-      recording_source_->CreateRasterSource();
+      recording_source_->CreateRasterSource(can_use_lcd_text);
   layer_impl->UpdateRasterSource(raster_source, &recording_invalidation_,
                                  nullptr);
   DCHECK(recording_invalidation_.IsEmpty());
@@ -107,14 +108,12 @@
   update_source_frame_number_ = layer_tree_host()->source_frame_number();
   bool updated = Layer::Update(queue, occlusion);
 
-  bool can_use_lcd_text_changed = UpdateCanUseLCDText();
-
   gfx::Rect visible_layer_rect = gfx::ScaleToEnclosingRect(
       visible_content_rect(), 1.f / contents_scale_x());
   gfx::Size layer_size = paint_properties().bounds;
 
   if (last_updated_visible_content_rect_ == visible_content_rect() &&
-      recording_source_->GetSize() == layer_size && !can_use_lcd_text_changed &&
+      recording_source_->GetSize() == layer_size &&
       pending_invalidation_.IsEmpty()) {
     // Only early out if the visible content rect of this layer hasn't changed.
     return updated;
@@ -147,9 +146,8 @@
   // for them.
   DCHECK(client_);
   updated |= recording_source_->UpdateAndExpandInvalidation(
-      client_, &recording_invalidation_, can_use_lcd_text_for_update_,
-      layer_size, visible_layer_rect, update_source_frame_number_,
-      RecordingSource::RECORD_NORMALLY);
+      client_, &recording_invalidation_, layer_size, visible_layer_rect,
+      update_source_frame_number_, RecordingSource::RECORD_NORMALLY);
   last_updated_visible_content_rect_ = visible_content_rect();
 
   if (updated) {
@@ -167,20 +165,6 @@
   is_mask_ = is_mask;
 }
 
-bool PictureLayer::SupportsLCDText() const {
-  return true;
-}
-
-bool PictureLayer::UpdateCanUseLCDText() {
-  if (!can_use_lcd_text_for_update_)
-    return false;  // Don't allow the LCD text state to change once disabled.
-  if (can_use_lcd_text_for_update_ == can_use_lcd_text())
-    return false;
-
-  can_use_lcd_text_for_update_ = can_use_lcd_text();
-  return true;
-}
-
 skia::RefPtr<SkPicture> PictureLayer::GetPicture() const {
   // We could either flatten the RecordingSource into a single SkPicture,
   // or paint a fresh one depending on what we intend to do with the
diff --git a/cc/layers/picture_layer.h b/cc/layers/picture_layer.h
index 648d1c93..767de273 100644
--- a/cc/layers/picture_layer.h
+++ b/cc/layers/picture_layer.h
@@ -33,7 +33,6 @@
   bool Update(ResourceUpdateQueue* queue,
               const OcclusionTracker<Layer>* occlusion) override;
   void SetIsMask(bool is_mask) override;
-  bool SupportsLCDText() const override;
   skia::RefPtr<SkPicture> GetPicture() const override;
   bool IsSuitableForGpuRasterization() const override;
 
@@ -56,8 +55,6 @@
   bool is_mask() const { return is_mask_; }
 
  private:
-  bool UpdateCanUseLCDText();
-
   ContentLayerClient* client_;
   scoped_ptr<RecordingSource> recording_source_;
   devtools_instrumentation::
@@ -69,7 +66,6 @@
   gfx::Rect last_updated_visible_content_rect_;
 
   int update_source_frame_number_;
-  bool can_use_lcd_text_for_update_;
   bool is_mask_;
   bool nearest_neighbor_;
 
diff --git a/cc/layers/picture_layer_impl.cc b/cc/layers/picture_layer_impl.cc
index f62a866..e819afd 100644
--- a/cc/layers/picture_layer_impl.cc
+++ b/cc/layers/picture_layer_impl.cc
@@ -225,9 +225,9 @@
         if (mode == TileDrawInfo::SOLID_COLOR_MODE) {
           color = DebugColors::SolidColorTileBorderColor();
           width = DebugColors::SolidColorTileBorderWidth(layer_tree_impl());
-        } else if (mode == TileDrawInfo::PICTURE_PILE_MODE) {
-          color = DebugColors::PictureTileBorderColor();
-          width = DebugColors::PictureTileBorderWidth(layer_tree_impl());
+        } else if (mode == TileDrawInfo::OOM_MODE) {
+          color = DebugColors::OOMTileBorderColor();
+          width = DebugColors::OOMTileBorderWidth(layer_tree_impl());
         } else if (iter.resolution() == HIGH_RESOLUTION) {
           color = DebugColors::HighResTileBorderColor();
           width = DebugColors::HighResTileBorderWidth(layer_tree_impl());
@@ -313,30 +313,6 @@
           has_draw_quad = true;
           break;
         }
-        case TileDrawInfo::PICTURE_PILE_MODE: {
-          if (!layer_tree_impl()
-                   ->GetRendererCapabilities()
-                   .allow_rasterize_on_demand) {
-            ++on_demand_missing_tile_count;
-            break;
-          }
-
-          gfx::RectF texture_rect = iter.texture_rect();
-
-          ResourceProvider* resource_provider =
-              layer_tree_impl()->resource_provider();
-          ResourceFormat format =
-              resource_provider->memory_efficient_texture_format();
-          PictureDrawQuad* quad =
-              render_pass->CreateAndAppendDrawQuad<PictureDrawQuad>();
-          quad->SetNew(shared_quad_state, geometry_rect, opaque_rect,
-                       visible_geometry_rect, texture_rect,
-                       iter->desired_texture_size(), nearest_neighbor_, format,
-                       iter->content_rect(), iter->contents_scale(),
-                       raster_source_);
-          has_draw_quad = true;
-          break;
-        }
         case TileDrawInfo::SOLID_COLOR_MODE: {
           SolidColorDrawQuad* quad =
               render_pass->CreateAndAppendDrawQuad<SolidColorDrawQuad>();
@@ -345,6 +321,8 @@
           has_draw_quad = true;
           break;
         }
+        case TileDrawInfo::OOM_MODE:
+          break;  // Checkerboard.
       }
     }
 
@@ -565,6 +543,36 @@
       MaximumContentsScale());
 }
 
+void PictureLayerImpl::UpdateCanUseLCDTextAfterCommit() {
+  // This function is only allowed to be called after commit, due to it not
+  // being smart about sharing tiles and because otherwise it would cause
+  // flashes by switching out tiles in place that may be currently on screen.
+  DCHECK(layer_tree_impl()->IsSyncTree());
+
+  // Don't allow the LCD text state to change once disabled.
+  if (!RasterSourceUsesLCDText())
+    return;
+  if (can_use_lcd_text() == RasterSourceUsesLCDText())
+    return;
+
+  // Raster sources are considered const, so in order to update the state
+  // a new one must be created and all tiles recreated.
+  scoped_refptr<RasterSource> new_raster_source =
+      raster_source_->CreateCloneWithoutLCDText();
+  // Synthetically invalidate everything.
+  gfx::Rect bounds_rect(bounds());
+  Region invalidation(bounds_rect);
+  UpdateRasterSource(new_raster_source, &invalidation, nullptr);
+  SetUpdateRect(bounds_rect);
+
+  DCHECK(!RasterSourceUsesLCDText());
+}
+
+bool PictureLayerImpl::RasterSourceUsesLCDText() const {
+  return raster_source_ ? raster_source_->CanUseLCDText()
+                        : layer_tree_impl()->settings().can_use_lcd_text;
+}
+
 void PictureLayerImpl::NotifyTileStateChanged(const Tile* tile) {
   if (layer_tree_impl()->IsActiveTree()) {
     gfx::RectF layer_damage_rect =
diff --git a/cc/layers/picture_layer_impl.h b/cc/layers/picture_layer_impl.h
index f32e56fa..9500e22 100644
--- a/cc/layers/picture_layer_impl.h
+++ b/cc/layers/picture_layer_impl.h
@@ -79,6 +79,8 @@
                           Region* new_invalidation,
                           const PictureLayerTilingSet* pending_set);
   bool UpdateTiles(bool resourceless_software_draw);
+  void UpdateCanUseLCDTextAfterCommit();
+  bool RasterSourceUsesLCDText() const;
 
   // Mask-related functions.
   void GetContentsResourceId(ResourceProvider::ResourceId* resource_id,
diff --git a/cc/layers/picture_layer_impl_perftest.cc b/cc/layers/picture_layer_impl_perftest.cc
index 62f4129..ea73cb2 100644
--- a/cc/layers/picture_layer_impl_perftest.cc
+++ b/cc/layers/picture_layer_impl_perftest.cc
@@ -72,7 +72,8 @@
                                              int num_tiles,
                                              const gfx::Size& viewport_size) {
     host_impl_.SetViewportSize(viewport_size);
-    host_impl_.pending_tree()->UpdateDrawProperties();
+    bool update_lcd_text = false;
+    host_impl_.pending_tree()->UpdateDrawProperties(update_lcd_text);
 
     timer_.Reset();
     do {
@@ -96,7 +97,8 @@
     host_impl_.SetViewportSize(viewport.size());
     pending_layer_->PushScrollOffsetFromMainThread(
         gfx::ScrollOffset(viewport.x(), viewport.y()));
-    host_impl_.pending_tree()->UpdateDrawProperties();
+    bool update_lcd_text = false;
+    host_impl_.pending_tree()->UpdateDrawProperties(update_lcd_text);
 
     timer_.Reset();
     do {
@@ -114,7 +116,8 @@
       int num_tiles,
       const gfx::Size& viewport_size) {
     host_impl_.SetViewportSize(viewport_size);
-    host_impl_.pending_tree()->UpdateDrawProperties();
+    bool update_lcd_text = false;
+    host_impl_.pending_tree()->UpdateDrawProperties(update_lcd_text);
 
     TreePriority priorities[] = {SAME_PRIORITY_FOR_BOTH_TREES,
                                  SMOOTHNESS_TAKES_PRIORITY,
@@ -145,7 +148,8 @@
     host_impl_.SetViewportSize(viewport.size());
     pending_layer_->PushScrollOffsetFromMainThread(
         gfx::ScrollOffset(viewport.x(), viewport.y()));
-    host_impl_.pending_tree()->UpdateDrawProperties();
+    bool update_lcd_text = false;
+    host_impl_.pending_tree()->UpdateDrawProperties(update_lcd_text);
 
     TreePriority priorities[] = {SAME_PRIORITY_FOR_BOTH_TREES,
                                  SMOOTHNESS_TAKES_PRIORITY,
diff --git a/cc/layers/picture_layer_impl_unittest.cc b/cc/layers/picture_layer_impl_unittest.cc
index f00b2e45..26f3be56 100644
--- a/cc/layers/picture_layer_impl_unittest.cc
+++ b/cc/layers/picture_layer_impl_unittest.cc
@@ -128,7 +128,8 @@
     active_layer_ = static_cast<FakePictureLayerImpl*>(
         host_impl_.active_tree()->LayerById(id_));
 
-    host_impl_.active_tree()->UpdateDrawProperties();
+    bool update_lcd_text = false;
+    host_impl_.active_tree()->UpdateDrawProperties(update_lcd_text);
   }
 
   void SetupDefaultTreesWithFixedTileSize(const gfx::Size& layer_bounds,
@@ -223,7 +224,8 @@
         host_impl_.pending_tree()->LayerById(id_));
 
     // Add tilings/tiles for the layer.
-    host_impl_.pending_tree()->UpdateDrawProperties();
+    bool update_lcd_text = false;
+    host_impl_.pending_tree()->UpdateDrawProperties(update_lcd_text);
   }
 
   void SetupDrawPropertiesAndUpdateTiles(FakePictureLayerImpl* layer,
@@ -431,7 +433,8 @@
                                         viewport_rect_for_tile_priority,
                                         transform_for_tile_priority,
                                         resourceless_software_draw);
-  host_impl_.active_tree()->UpdateDrawProperties();
+  bool update_lcd_text = false;
+  host_impl_.active_tree()->UpdateDrawProperties(update_lcd_text);
 
   gfx::Rect viewport_rect_for_tile_priority_in_view_space =
       viewport_rect_for_tile_priority;
@@ -465,7 +468,7 @@
                                         viewport_rect_for_tile_priority,
                                         transform_for_tile_priority,
                                         resourceless_software_draw);
-  host_impl_.active_tree()->UpdateDrawProperties();
+  host_impl_.active_tree()->UpdateDrawProperties(update_lcd_text);
 
   gfx::Transform screen_to_view(gfx::Transform::kSkipInitialization);
   bool success = transform_for_tile_priority.GetInverse(&screen_to_view);
@@ -603,7 +606,8 @@
   host_impl_.SetExternalDrawConstraints(
       transform, viewport, viewport, viewport_rect_for_tile_priority,
       transform_for_tile_priority, resourceless_software_draw);
-  host_impl_.active_tree()->UpdateDrawProperties();
+  bool update_lcd_text = false;
+  host_impl_.active_tree()->UpdateDrawProperties(update_lcd_text);
 
   EXPECT_EQ(viewport_rect_for_tile_priority,
             active_layer_->viewport_rect_for_tile_priority_in_content_space());
@@ -621,7 +625,7 @@
   // should remain to be the previously cached value.
   EXPECT_EQ(viewport_rect_for_tile_priority,
             active_layer_->viewport_rect_for_tile_priority_in_content_space());
-  host_impl_.active_tree()->UpdateDrawProperties();
+  host_impl_.active_tree()->UpdateDrawProperties(update_lcd_text);
 
   // Now the UpdateDrawProperties is called. The viewport rect for tile
   // priority should be the latest value.
@@ -1312,7 +1316,8 @@
   time_ticks += base::TimeDelta::FromMilliseconds(1);
   host_impl_.SetCurrentBeginFrameArgs(
       CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, time_ticks));
-  host_impl_.pending_tree()->UpdateDrawProperties();
+  bool update_lcd_text = false;
+  host_impl_.pending_tree()->UpdateDrawProperties(update_lcd_text);
 
   FakePictureLayerImpl* pending_mask =
       static_cast<FakePictureLayerImpl*>(pending_layer_->mask_layer());
@@ -1362,7 +1367,7 @@
   time_ticks += base::TimeDelta::FromMilliseconds(1);
   host_impl_.SetCurrentBeginFrameArgs(
       CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, time_ticks));
-  host_impl_.pending_tree()->UpdateDrawProperties();
+  host_impl_.pending_tree()->UpdateDrawProperties(update_lcd_text);
 
   // The mask tiling gets scaled down.
   EXPECT_LT(pending_mask->HighResTiling()->contents_scale(), 1.f);
@@ -1418,7 +1423,7 @@
   time_ticks += base::TimeDelta::FromMilliseconds(1);
   host_impl_.SetCurrentBeginFrameArgs(
       CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, time_ticks));
-  host_impl_.pending_tree()->UpdateDrawProperties();
+  host_impl_.pending_tree()->UpdateDrawProperties(update_lcd_text);
 
   EXPECT_EQ(0u, pending_mask->num_tilings());
 }
@@ -1450,7 +1455,8 @@
   time_ticks += base::TimeDelta::FromMilliseconds(1);
   host_impl_.SetCurrentBeginFrameArgs(
       CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, time_ticks));
-  host_impl_.pending_tree()->UpdateDrawProperties();
+  bool update_lcd_text = false;
+  host_impl_.pending_tree()->UpdateDrawProperties(update_lcd_text);
 
   FakePictureLayerImpl* pending_mask =
       static_cast<FakePictureLayerImpl*>(pending_layer_->mask_layer());
@@ -1771,7 +1777,8 @@
   time_ticks += base::TimeDelta::FromMilliseconds(1);
   host_impl_.SetCurrentBeginFrameArgs(
       CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, time_ticks));
-  host_impl_.pending_tree()->UpdateDrawProperties();
+  bool update_lcd_text = false;
+  host_impl_.pending_tree()->UpdateDrawProperties(update_lcd_text);
 
   // Set visible content rect that is different from
   // external_viewport_for_tile_priority.
@@ -1811,7 +1818,7 @@
 
   // Activate and draw active layer.
   host_impl_.ActivateSyncTree();
-  host_impl_.active_tree()->UpdateDrawProperties();
+  host_impl_.active_tree()->UpdateDrawProperties(update_lcd_text);
   active_layer_->draw_properties().visible_content_rect = visible_content_rect;
 
   scoped_ptr<RenderPass> render_pass = RenderPass::Create();
@@ -2415,7 +2422,8 @@
   time_ticks += base::TimeDelta::FromMilliseconds(1);
   host_impl_.SetCurrentBeginFrameArgs(
       CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, time_ticks));
-  host_impl_.pending_tree()->UpdateDrawProperties();
+  bool update_lcd_text = false;
+  host_impl_.pending_tree()->UpdateDrawProperties(update_lcd_text);
   EXPECT_TRUE(pending_layer_->tilings()->FindTilingWithScale(1.f));
 
   ActivateTree();
@@ -2596,8 +2604,8 @@
 // However, this is also a regression test for PictureLayerImpl in that
 // not having this update will cause a crash.
 TEST_F(DeferredInitPictureLayerImplTest, PreventUpdateTilesDuringLostContext) {
-  host_impl_.pending_tree()->UpdateDrawProperties();
-  host_impl_.active_tree()->UpdateDrawProperties();
+  host_impl_.pending_tree()->UpdateDrawProperties(true);
+  host_impl_.active_tree()->UpdateDrawProperties(false);
   EXPECT_FALSE(host_impl_.pending_tree()->needs_update_draw_properties());
   EXPECT_FALSE(host_impl_.active_tree()->needs_update_draw_properties());
 
@@ -2609,7 +2617,7 @@
   // These will crash PictureLayerImpl if this is not true.
   ASSERT_TRUE(host_impl_.pending_tree()->needs_update_draw_properties());
   ASSERT_TRUE(host_impl_.active_tree()->needs_update_draw_properties());
-  host_impl_.active_tree()->UpdateDrawProperties();
+  host_impl_.active_tree()->UpdateDrawProperties(false);
 }
 
 TEST_F(PictureLayerImplTest, HighResTilingDuringAnimationForCpuRasterization) {
@@ -3855,7 +3863,8 @@
   SetupPendingTree(pending_pile);
   pending_layer_->SetBounds(layer_bounds);
   ActivateTree();
-  host_impl_.active_tree()->UpdateDrawProperties();
+  bool update_lcd_text = false;
+  host_impl_.active_tree()->UpdateDrawProperties(update_lcd_text);
   std::vector<Tile*> tiles =
       active_layer_->HighResTiling()->AllTilesForTesting();
   host_impl_.tile_manager()->InitializeTilesWithResourcesForTesting(tiles);
@@ -3905,7 +3914,7 @@
                                         WhichTree tree,
                                         size_t expected_occluded_tile_count) {
     WhichTree twin_tree = tree == ACTIVE_TREE ? PENDING_TREE : ACTIVE_TREE;
-    for (int priority_count = 0; priority_count < NUM_TREE_PRIORITIES;
+    for (int priority_count = 0; priority_count <= LAST_TREE_PRIORITY;
          ++priority_count) {
       TreePriority tree_priority = static_cast<TreePriority>(priority_count);
       size_t occluded_tile_count = 0u;
@@ -4006,9 +4015,6 @@
               // eviction queue.
               EXPECT_EQ(ACTIVE_TREE, tree);
               break;
-            case NUM_TREE_PRIORITIES:
-              NOTREACHED();
-              break;
           }
         }
         queue->Pop();
@@ -4068,7 +4074,8 @@
   time_ticks += base::TimeDelta::FromMilliseconds(200);
   host_impl_.SetCurrentBeginFrameArgs(
       CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, time_ticks));
-  host_impl_.pending_tree()->UpdateDrawProperties();
+  bool update_lcd_text = false;
+  host_impl_.pending_tree()->UpdateDrawProperties(update_lcd_text);
 
   unoccluded_tile_count = 0;
   queue.reset(new TilingSetRasterQueueAll(
@@ -4092,7 +4099,7 @@
   time_ticks += base::TimeDelta::FromMilliseconds(200);
   host_impl_.SetCurrentBeginFrameArgs(
       CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, time_ticks));
-  host_impl_.pending_tree()->UpdateDrawProperties();
+  host_impl_.pending_tree()->UpdateDrawProperties(update_lcd_text);
 
   unoccluded_tile_count = 0;
   queue.reset(new TilingSetRasterQueueAll(
@@ -4166,7 +4173,8 @@
   time_ticks += base::TimeDelta::FromMilliseconds(200);
   host_impl_.SetCurrentBeginFrameArgs(
       CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, time_ticks));
-  host_impl_.pending_tree()->UpdateDrawProperties();
+  bool update_lcd_text = false;
+  host_impl_.pending_tree()->UpdateDrawProperties(update_lcd_text);
 
   for (size_t i = 0; i < pending_layer_->num_tilings(); ++i) {
     PictureLayerTiling* tiling = pending_layer_->tilings()->tiling_at(i);
@@ -4206,7 +4214,7 @@
   time_ticks += base::TimeDelta::FromMilliseconds(200);
   host_impl_.SetCurrentBeginFrameArgs(
       CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, time_ticks));
-  host_impl_.pending_tree()->UpdateDrawProperties();
+  host_impl_.pending_tree()->UpdateDrawProperties(update_lcd_text);
 
   for (size_t i = 0; i < pending_layer_->num_tilings(); ++i) {
     PictureLayerTiling* tiling = pending_layer_->tilings()->tiling_at(i);
@@ -4280,7 +4288,8 @@
   host_impl_.SetCurrentBeginFrameArgs(
       CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, time_ticks));
   // UpdateDrawProperties with the occluding layer.
-  host_impl_.pending_tree()->UpdateDrawProperties();
+  bool update_lcd_text = false;
+  host_impl_.pending_tree()->UpdateDrawProperties(update_lcd_text);
 
   EXPECT_EQ(5u, pending_layer_->num_tilings());
 
@@ -4479,7 +4488,8 @@
   host_impl_.SetCurrentBeginFrameArgs(
       CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, time_ticks));
   // UpdateDrawProperties with the occluding layer.
-  host_impl_.pending_tree()->UpdateDrawProperties();
+  bool update_lcd_text = false;
+  host_impl_.pending_tree()->UpdateDrawProperties(update_lcd_text);
 
   // The expected number of occluded tiles on each of the 2 tilings for each of
   // the 3 tree priorities.
@@ -4670,11 +4680,11 @@
 
   Region invalidation(layer_rect);
   recording_source->UpdateAndExpandInvalidation(
-      &client, &invalidation, false, layer_bounds, layer_rect, frame_number++,
+      &client, &invalidation, layer_bounds, layer_rect, frame_number++,
       RecordingSource::RECORD_NORMALLY);
 
   scoped_refptr<RasterSource> pending_raster_source =
-      recording_source->CreateRasterSource();
+      recording_source->CreateRasterSource(true);
 
   SetupPendingTreeWithFixedTileSize(pending_raster_source, tile_size, Region());
   ActivateTree();
@@ -4733,15 +4743,16 @@
 
   Region invalidation1(layer_rect);
   recording_source->UpdateAndExpandInvalidation(
-      &client, &invalidation1, false, layer_bounds, layer_rect, frame_number++,
+      &client, &invalidation1, layer_bounds, layer_rect, frame_number++,
       RecordingSource::RECORD_NORMALLY);
 
   scoped_refptr<RasterSource> raster_source1 =
-      recording_source->CreateRasterSource();
+      recording_source->CreateRasterSource(true);
 
   SetupPendingTree(raster_source1);
   ActivateTree();
-  host_impl_.active_tree()->UpdateDrawProperties();
+  bool update_lcd_text = false;
+  host_impl_.active_tree()->UpdateDrawProperties(update_lcd_text);
 
   // We've started with a solid layer that contains some tilings.
   ASSERT_TRUE(active_layer_->tilings());
@@ -4751,11 +4762,11 @@
 
   Region invalidation2(layer_rect);
   recording_source->UpdateAndExpandInvalidation(
-      &client, &invalidation2, false, layer_bounds, layer_rect, frame_number++,
+      &client, &invalidation2, layer_bounds, layer_rect, frame_number++,
       RecordingSource::RECORD_NORMALLY);
 
   scoped_refptr<RasterSource> raster_source2 =
-      recording_source->CreateRasterSource();
+      recording_source->CreateRasterSource(true);
 
   SetupPendingTree(raster_source2);
   ActivateTree();
diff --git a/cc/layers/scroll_blocks_on.h b/cc/layers/scroll_blocks_on.h
index 2447298..f41e7fd 100644
--- a/cc/layers/scroll_blocks_on.h
+++ b/cc/layers/scroll_blocks_on.h
@@ -6,12 +6,13 @@
 #define CC_LAYERS_SCROLL_BLOCKS_ON_H_
 
 enum ScrollBlocksOn {
-  ScrollBlocksOnNone = 0x0,
-  ScrollBlocksOnStartTouch = 0x1,
-  ScrollBlocksOnWheelEvent = 0x2,
-  ScrollBlocksOnScrollEvent = 0x4,
-  ScrollBlocksOnMax = ScrollBlocksOnStartTouch | ScrollBlocksOnWheelEvent |
-                      ScrollBlocksOnScrollEvent
+  SCROLL_BLOCKS_ON_NONE = 0x0,
+  SCROLL_BLOCKS_ON_START_TOUCH = 0x1,
+  SCROLL_BLOCKS_ON_WHEEL_EVENT = 0x2,
+  SCROLL_BLOCKS_ON_SCROLL_EVENT = 0x4,
+  SCROLL_BLOCKS_ON_MAX = SCROLL_BLOCKS_ON_START_TOUCH |
+                         SCROLL_BLOCKS_ON_WHEEL_EVENT |
+                         SCROLL_BLOCKS_ON_SCROLL_EVENT
 };
 
 inline ScrollBlocksOn operator|(ScrollBlocksOn a, ScrollBlocksOn b) {
diff --git a/cc/layers/scrollbar_layer_unittest.cc b/cc/layers/scrollbar_layer_unittest.cc
index c70b93a..36c37e9 100644
--- a/cc/layers/scrollbar_layer_unittest.cc
+++ b/cc/layers/scrollbar_layer_unittest.cc
@@ -173,9 +173,10 @@
   // When the scrollbar is not an overlay scrollbar, the scroll should be
   // responded to on the main thread as the compositor does not yet implement
   // scrollbar scrolling.
-  EXPECT_EQ(InputHandler::ScrollOnMainThread,
-            scrollbar_layer_impl->TryScroll(
-                gfx::Point(0, 0), InputHandler::Gesture, ScrollBlocksOnNone));
+  EXPECT_EQ(
+      InputHandler::SCROLL_ON_MAIN_THREAD,
+      scrollbar_layer_impl->TryScroll(gfx::Point(0, 0), InputHandler::GESTURE,
+                                      SCROLL_BLOCKS_ON_NONE));
 
   // Create and attach an overlay scrollbar.
   scrollbar.reset(new FakeScrollbar(false, false, true));
@@ -187,9 +188,10 @@
 
   // The user shouldn't be able to drag an overlay scrollbar and the scroll
   // may be handled in the compositor.
-  EXPECT_EQ(InputHandler::ScrollIgnored,
-            scrollbar_layer_impl->TryScroll(
-                gfx::Point(0, 0), InputHandler::Gesture, ScrollBlocksOnNone));
+  EXPECT_EQ(
+      InputHandler::SCROLL_IGNORED,
+      scrollbar_layer_impl->TryScroll(gfx::Point(0, 0), InputHandler::GESTURE,
+                                      SCROLL_BLOCKS_ON_NONE));
 }
 
 TEST_F(ScrollbarLayerTest, ScrollOffsetSynchronization) {
diff --git a/cc/layers/texture_layer_impl.cc b/cc/layers/texture_layer_impl.cc
index d9ddee7d..4f4b12ed 100644
--- a/cc/layers/texture_layer_impl.cc
+++ b/cc/layers/texture_layer_impl.cc
@@ -105,7 +105,7 @@
 
     if (!texture_copy_->id()) {
       texture_copy_->Allocate(texture_mailbox_.shared_memory_size(),
-                              ResourceProvider::TextureHintImmutable,
+                              ResourceProvider::TEXTURE_HINT_IMMUTABLE,
                               resource_provider->best_texture_format());
     }
 
diff --git a/cc/layers/video_layer_impl.cc b/cc/layers/video_layer_impl.cc
index 559e0ac..228f2cc 100644
--- a/cc/layers/video_layer_impl.cc
+++ b/cc/layers/video_layer_impl.cc
@@ -222,10 +222,13 @@
       DCHECK_GE(frame_resources_.size(), 3u);
       if (frame_resources_.size() < 3u)
         break;
-      YUVVideoDrawQuad::ColorSpace color_space =
-          frame_->format() == media::VideoFrame::YV12J
-              ? YUVVideoDrawQuad::REC_601_JPEG
-              : YUVVideoDrawQuad::REC_601;
+      YUVVideoDrawQuad::ColorSpace color_space = YUVVideoDrawQuad::REC_601;
+      if (frame_->format() == media::VideoFrame::YV12J) {
+        color_space = YUVVideoDrawQuad::JPEG;
+      } else if (frame_->format() == media::VideoFrame::YV12HD) {
+        color_space = YUVVideoDrawQuad::REC_709;
+      }
+
       gfx::RectF tex_coord_rect(
           tex_x_offset, tex_y_offset, tex_width_scale, tex_height_scale);
       YUVVideoDrawQuad* yuv_video_quad =
diff --git a/cc/output/direct_renderer.cc b/cc/output/direct_renderer.cc
index 9fbd4a5..5867fce 100644
--- a/cc/output/direct_renderer.cc
+++ b/cc/output/direct_renderer.cc
@@ -409,7 +409,7 @@
                enlarge_pass_texture_amount_.y());
   if (!texture->id())
     texture->Allocate(
-        size, ResourceProvider::TextureHintImmutableFramebuffer, RGBA_8888);
+        size, ResourceProvider::TEXTURE_HINT_IMMUTABLE_FRAMEBUFFER, RGBA_8888);
   DCHECK(texture->id());
 
   return BindFramebufferToTexture(frame, texture, render_pass->output_rect);
diff --git a/cc/output/gl_renderer.cc b/cc/output/gl_renderer.cc
index a394a1ad..6142d964 100644
--- a/cc/output/gl_renderer.cc
+++ b/cc/output/gl_renderer.cc
@@ -85,54 +85,54 @@
 SamplerType SamplerTypeFromTextureTarget(GLenum target) {
   switch (target) {
     case GL_TEXTURE_2D:
-      return SamplerType2D;
+      return SAMPLER_TYPE_2D;
     case GL_TEXTURE_RECTANGLE_ARB:
-      return SamplerType2DRect;
+      return SAMPLER_TYPE_2D_RECT;
     case GL_TEXTURE_EXTERNAL_OES:
-      return SamplerTypeExternalOES;
+      return SAMPLER_TYPE_EXTERNAL_OES;
     default:
       NOTREACHED();
-      return SamplerType2D;
+      return SAMPLER_TYPE_2D;
   }
 }
 
 BlendMode BlendModeFromSkXfermode(SkXfermode::Mode mode) {
   switch (mode) {
     case SkXfermode::kSrcOver_Mode:
-      return BlendModeNormal;
+      return BLEND_MODE_NORMAL;
     case SkXfermode::kScreen_Mode:
-      return BlendModeScreen;
+      return BLEND_MODE_SCREEN;
     case SkXfermode::kOverlay_Mode:
-      return BlendModeOverlay;
+      return BLEND_MODE_OVERLAY;
     case SkXfermode::kDarken_Mode:
-      return BlendModeDarken;
+      return BLEND_MODE_DARKEN;
     case SkXfermode::kLighten_Mode:
-      return BlendModeLighten;
+      return BLEND_MODE_LIGHTEN;
     case SkXfermode::kColorDodge_Mode:
-      return BlendModeColorDodge;
+      return BLEND_MODE_COLOR_DODGE;
     case SkXfermode::kColorBurn_Mode:
-      return BlendModeColorBurn;
+      return BLEND_MODE_COLOR_BURN;
     case SkXfermode::kHardLight_Mode:
-      return BlendModeHardLight;
+      return BLEND_MODE_HARD_LIGHT;
     case SkXfermode::kSoftLight_Mode:
-      return BlendModeSoftLight;
+      return BLEND_MODE_SOFT_LIGHT;
     case SkXfermode::kDifference_Mode:
-      return BlendModeDifference;
+      return BLEND_MODE_DIFFERENCE;
     case SkXfermode::kExclusion_Mode:
-      return BlendModeExclusion;
+      return BLEND_MODE_EXCLUSION;
     case SkXfermode::kMultiply_Mode:
-      return BlendModeMultiply;
+      return BLEND_MODE_MULTIPLY;
     case SkXfermode::kHue_Mode:
-      return BlendModeHue;
+      return BLEND_MODE_HUE;
     case SkXfermode::kSaturation_Mode:
-      return BlendModeSaturation;
+      return BLEND_MODE_SATURATION;
     case SkXfermode::kColor_Mode:
-      return BlendModeColor;
+      return BLEND_MODE_COLOR;
     case SkXfermode::kLuminosity_Mode:
-      return BlendModeLuminosity;
+      return BLEND_MODE_LUMINOSITY;
     default:
       NOTREACHED();
-      return BlendModeNone;
+      return BLEND_MODE_NONE;
   }
 }
 
@@ -514,7 +514,8 @@
       DrawIOSurfaceQuad(frame, IOSurfaceDrawQuad::MaterialCast(quad));
       break;
     case DrawQuad::PICTURE_CONTENT:
-      DrawPictureQuad(frame, PictureDrawQuad::MaterialCast(quad));
+      // PictureDrawQuad should only be used for resourceless software draws.
+      NOTREACHED();
       break;
     case DrawQuad::RENDER_PASS:
       DrawRenderPassQuad(frame, RenderPassDrawQuad::MaterialCast(quad));
@@ -846,7 +847,7 @@
       ScopedResource::Create(resource_provider_);
   // CopyTexImage2D fails when called on a texture having immutable storage.
   device_background_texture->Allocate(
-      bounding_rect.size(), ResourceProvider::TextureHintDefault, RGBA_8888);
+      bounding_rect.size(), ResourceProvider::TEXTURE_HINT_DEFAULT, RGBA_8888);
   {
     ResourceProvider::ScopedWriteLockGL lock(resource_provider_,
                                              device_background_texture->id());
@@ -981,7 +982,7 @@
 
   scoped_ptr<ResourceProvider::ScopedSamplerGL> mask_resource_lock;
   unsigned mask_texture_id = 0;
-  SamplerType mask_sampler = SamplerTypeNA;
+  SamplerType mask_sampler = SAMPLER_TYPE_NA;
   if (quad->mask_resource_id) {
     mask_resource_lock.reset(new ResourceProvider::ScopedSamplerGL(
         resource_provider_, quad->mask_resource_id, GL_TEXTURE1, GL_LINEAR));
@@ -1032,7 +1033,7 @@
   DCHECK_EQ(background_texture || background_image, use_shaders_for_blending);
   BlendMode shader_blend_mode = use_shaders_for_blending
                                     ? BlendModeFromSkXfermode(blend_mode)
-                                    : BlendModeNone;
+                                    : BLEND_MODE_NONE;
 
   if (use_aa && mask_texture_id && !use_color_matrix) {
     const RenderPassMaskProgramAA* program = GetRenderPassMaskProgramAA(
@@ -1219,7 +1220,7 @@
     GLC(gl_, gl_->Uniform1i(shader_mask_sampler_location, 1));
 
     gfx::RectF mask_uv_rect = quad->MaskUVRect();
-    if (mask_sampler != SamplerType2D) {
+    if (mask_sampler != SAMPLER_TYPE_2D) {
       mask_uv_rect.Scale(quad->mask_texture_size.width(),
                          quad->mask_texture_size.height());
     }
@@ -1638,7 +1639,7 @@
   float fragment_tex_scale_y = clamp_tex_rect.height();
 
   // Map to normalized texture coordinates.
-  if (sampler != SamplerType2DRect) {
+  if (sampler != SAMPLER_TYPE_2D_RECT) {
     gfx::Size texture_size = quad->texture_size;
     DCHECK(!texture_size.IsEmpty());
     fragment_tex_translate_x /= texture_size.width();
@@ -1728,7 +1729,7 @@
   float vertex_tex_scale_y = tex_coord_rect.height();
 
   // Map to normalized texture coordinates.
-  if (sampler != SamplerType2DRect) {
+  if (sampler != SAMPLER_TYPE_2D_RECT) {
     gfx::Size texture_size = quad->texture_size;
     DCHECK(!texture_size.IsEmpty());
     vertex_tex_translate_x /= texture_size.width();
@@ -1900,9 +1901,12 @@
   float yuv_to_rgb_rec601[9] = {
       1.164f, 1.164f, 1.164f, 0.0f, -.391f, 2.018f, 1.596f, -.813f, 0.0f,
   };
-  float yuv_to_rgb_rec601_jpeg[9] = {
+  float yuv_to_rgb_jpeg[9] = {
       1.f, 1.f, 1.f, 0.0f, -.34414f, 1.772f, 1.402f, -.71414f, 0.0f,
   };
+  float yuv_to_rgb_rec709[9] = {
+      1.164f, 1.164f, 1.164f, 0.0f, -0.213f, 2.112f, 1.793f, -0.533f, 0.0f,
+  };
 
   // These values map to 16, 128, and 128 respectively, and are computed
   // as a fraction over 256 (e.g. 16 / 256 = 0.0625).
@@ -1910,12 +1914,12 @@
   //   Y - 16   : Gives 16 values of head and footroom for overshooting
   //   U - 128  : Turns unsigned U into signed U [-128,127]
   //   V - 128  : Turns unsigned V into signed V [-128,127]
-  float yuv_adjust_rec601[3] = {
+  float yuv_adjust_constrained[3] = {
       -0.0625f, -0.5f, -0.5f,
   };
 
   // Same as above, but without the head and footroom.
-  float yuv_adjust_rec601_jpeg[3] = {
+  float yuv_adjust_full[3] = {
       0.0f, -0.5f, -0.5f,
   };
 
@@ -1925,11 +1929,15 @@
   switch (quad->color_space) {
     case YUVVideoDrawQuad::REC_601:
       yuv_to_rgb = yuv_to_rgb_rec601;
-      yuv_adjust = yuv_adjust_rec601;
+      yuv_adjust = yuv_adjust_constrained;
       break;
-    case YUVVideoDrawQuad::REC_601_JPEG:
-      yuv_to_rgb = yuv_to_rgb_rec601_jpeg;
-      yuv_adjust = yuv_adjust_rec601_jpeg;
+    case YUVVideoDrawQuad::REC_709:
+      yuv_to_rgb = yuv_to_rgb_rec709;
+      yuv_adjust = yuv_adjust_constrained;
+      break;
+    case YUVVideoDrawQuad::JPEG:
+      yuv_to_rgb = yuv_to_rgb_jpeg;
+      yuv_adjust = yuv_adjust_full;
       break;
   }
 
@@ -1978,54 +1986,6 @@
                    program->vertex_shader().matrix_location());
 }
 
-void GLRenderer::DrawPictureQuad(const DrawingFrame* frame,
-                                 const PictureDrawQuad* quad) {
-  if (on_demand_tile_raster_bitmap_.width() != quad->texture_size.width() ||
-      on_demand_tile_raster_bitmap_.height() != quad->texture_size.height()) {
-    on_demand_tile_raster_bitmap_.allocN32Pixels(quad->texture_size.width(),
-                                                 quad->texture_size.height());
-
-    if (on_demand_tile_raster_resource_id_)
-      resource_provider_->DeleteResource(on_demand_tile_raster_resource_id_);
-
-    on_demand_tile_raster_resource_id_ = resource_provider_->CreateGLTexture(
-        quad->texture_size,
-        GL_TEXTURE_2D,
-        GL_TEXTURE_POOL_UNMANAGED_CHROMIUM,
-        GL_CLAMP_TO_EDGE,
-        ResourceProvider::TextureHintImmutable,
-        quad->texture_format);
-  }
-
-  SkCanvas canvas(on_demand_tile_raster_bitmap_);
-  quad->raster_source->PlaybackToCanvas(&canvas, quad->content_rect,
-                                        quad->contents_scale);
-
-  uint8_t* bitmap_pixels = NULL;
-  SkBitmap on_demand_tile_raster_bitmap_dest;
-  SkColorType colorType = ResourceFormatToSkColorType(quad->texture_format);
-  if (on_demand_tile_raster_bitmap_.colorType() != colorType) {
-    on_demand_tile_raster_bitmap_.copyTo(&on_demand_tile_raster_bitmap_dest,
-                                         colorType);
-    // TODO(kaanb): The GL pipeline assumes a 4-byte alignment for the
-    // bitmap data. This check will be removed once crbug.com/293728 is fixed.
-    CHECK_EQ(0u, on_demand_tile_raster_bitmap_dest.rowBytes() % 4);
-    bitmap_pixels = reinterpret_cast<uint8_t*>(
-        on_demand_tile_raster_bitmap_dest.getPixels());
-  } else {
-    bitmap_pixels =
-        reinterpret_cast<uint8_t*>(on_demand_tile_raster_bitmap_.getPixels());
-  }
-
-  resource_provider_->SetPixels(on_demand_tile_raster_resource_id_,
-                                bitmap_pixels,
-                                gfx::Rect(quad->texture_size),
-                                gfx::Rect(quad->texture_size),
-                                gfx::Vector2d());
-
-  DrawContentQuad(frame, quad, on_demand_tile_raster_resource_id_);
-}
-
 struct TextureProgramBinding {
   template <class Program>
   void Set(Program* program) {
@@ -2776,8 +2736,8 @@
   if (!tile_checkerboard_program_.initialized()) {
     TRACE_EVENT0("cc", "GLRenderer::checkerboardProgram::initalize");
     tile_checkerboard_program_.Initialize(output_surface_->context_provider(),
-                                          TexCoordPrecisionNA,
-                                          SamplerTypeNA);
+                                          TEX_COORD_PRECISION_NA,
+                                          SAMPLER_TYPE_NA);
   }
   return &tile_checkerboard_program_;
 }
@@ -2786,8 +2746,7 @@
   if (!debug_border_program_.initialized()) {
     TRACE_EVENT0("cc", "GLRenderer::debugBorderProgram::initialize");
     debug_border_program_.Initialize(output_surface_->context_provider(),
-                                     TexCoordPrecisionNA,
-                                     SamplerTypeNA);
+                                     TEX_COORD_PRECISION_NA, SAMPLER_TYPE_NA);
   }
   return &debug_border_program_;
 }
@@ -2796,8 +2755,7 @@
   if (!solid_color_program_.initialized()) {
     TRACE_EVENT0("cc", "GLRenderer::solidColorProgram::initialize");
     solid_color_program_.Initialize(output_surface_->context_provider(),
-                                    TexCoordPrecisionNA,
-                                    SamplerTypeNA);
+                                    TEX_COORD_PRECISION_NA, SAMPLER_TYPE_NA);
   }
   return &solid_color_program_;
 }
@@ -2806,8 +2764,7 @@
   if (!solid_color_program_aa_.initialized()) {
     TRACE_EVENT0("cc", "GLRenderer::solidColorProgramAA::initialize");
     solid_color_program_aa_.Initialize(output_surface_->context_provider(),
-                                       TexCoordPrecisionNA,
-                                       SamplerTypeNA);
+                                       TEX_COORD_PRECISION_NA, SAMPLER_TYPE_NA);
   }
   return &solid_color_program_aa_;
 }
@@ -2816,16 +2773,14 @@
     TexCoordPrecision precision,
     BlendMode blend_mode) {
   DCHECK_GE(precision, 0);
-  DCHECK_LT(precision, NumTexCoordPrecisions);
+  DCHECK_LE(precision, LAST_TEX_COORD_PRECISION);
   DCHECK_GE(blend_mode, 0);
-  DCHECK_LT(blend_mode, NumBlendModes);
+  DCHECK_LE(blend_mode, LAST_BLEND_MODE);
   RenderPassProgram* program = &render_pass_program_[precision][blend_mode];
   if (!program->initialized()) {
     TRACE_EVENT0("cc", "GLRenderer::renderPassProgram::initialize");
-    program->Initialize(output_surface_->context_provider(),
-                        precision,
-                        SamplerType2D,
-                        blend_mode);
+    program->Initialize(output_surface_->context_provider(), precision,
+                        SAMPLER_TYPE_2D, blend_mode);
   }
   return program;
 }
@@ -2834,17 +2789,15 @@
     TexCoordPrecision precision,
     BlendMode blend_mode) {
   DCHECK_GE(precision, 0);
-  DCHECK_LT(precision, NumTexCoordPrecisions);
+  DCHECK_LE(precision, LAST_TEX_COORD_PRECISION);
   DCHECK_GE(blend_mode, 0);
-  DCHECK_LT(blend_mode, NumBlendModes);
+  DCHECK_LE(blend_mode, LAST_BLEND_MODE);
   RenderPassProgramAA* program =
       &render_pass_program_aa_[precision][blend_mode];
   if (!program->initialized()) {
     TRACE_EVENT0("cc", "GLRenderer::renderPassProgramAA::initialize");
-    program->Initialize(output_surface_->context_provider(),
-                        precision,
-                        SamplerType2D,
-                        blend_mode);
+    program->Initialize(output_surface_->context_provider(), precision,
+                        SAMPLER_TYPE_2D, blend_mode);
   }
   return program;
 }
@@ -2854,11 +2807,11 @@
     SamplerType sampler,
     BlendMode blend_mode) {
   DCHECK_GE(precision, 0);
-  DCHECK_LT(precision, NumTexCoordPrecisions);
+  DCHECK_LE(precision, LAST_TEX_COORD_PRECISION);
   DCHECK_GE(sampler, 0);
-  DCHECK_LT(sampler, NumSamplerTypes);
+  DCHECK_LE(sampler, LAST_SAMPLER_TYPE);
   DCHECK_GE(blend_mode, 0);
-  DCHECK_LT(blend_mode, NumBlendModes);
+  DCHECK_LE(blend_mode, LAST_BLEND_MODE);
   RenderPassMaskProgram* program =
       &render_pass_mask_program_[precision][sampler][blend_mode];
   if (!program->initialized()) {
@@ -2874,11 +2827,11 @@
                                        SamplerType sampler,
                                        BlendMode blend_mode) {
   DCHECK_GE(precision, 0);
-  DCHECK_LT(precision, NumTexCoordPrecisions);
+  DCHECK_LE(precision, LAST_TEX_COORD_PRECISION);
   DCHECK_GE(sampler, 0);
-  DCHECK_LT(sampler, NumSamplerTypes);
+  DCHECK_LE(sampler, LAST_SAMPLER_TYPE);
   DCHECK_GE(blend_mode, 0);
-  DCHECK_LT(blend_mode, NumBlendModes);
+  DCHECK_LE(blend_mode, LAST_BLEND_MODE);
   RenderPassMaskProgramAA* program =
       &render_pass_mask_program_aa_[precision][sampler][blend_mode];
   if (!program->initialized()) {
@@ -2893,17 +2846,15 @@
 GLRenderer::GetRenderPassColorMatrixProgram(TexCoordPrecision precision,
                                             BlendMode blend_mode) {
   DCHECK_GE(precision, 0);
-  DCHECK_LT(precision, NumTexCoordPrecisions);
+  DCHECK_LE(precision, LAST_TEX_COORD_PRECISION);
   DCHECK_GE(blend_mode, 0);
-  DCHECK_LT(blend_mode, NumBlendModes);
+  DCHECK_LE(blend_mode, LAST_BLEND_MODE);
   RenderPassColorMatrixProgram* program =
       &render_pass_color_matrix_program_[precision][blend_mode];
   if (!program->initialized()) {
     TRACE_EVENT0("cc", "GLRenderer::renderPassColorMatrixProgram::initialize");
-    program->Initialize(output_surface_->context_provider(),
-                        precision,
-                        SamplerType2D,
-                        blend_mode);
+    program->Initialize(output_surface_->context_provider(), precision,
+                        SAMPLER_TYPE_2D, blend_mode);
   }
   return program;
 }
@@ -2912,18 +2863,16 @@
 GLRenderer::GetRenderPassColorMatrixProgramAA(TexCoordPrecision precision,
                                               BlendMode blend_mode) {
   DCHECK_GE(precision, 0);
-  DCHECK_LT(precision, NumTexCoordPrecisions);
+  DCHECK_LE(precision, LAST_TEX_COORD_PRECISION);
   DCHECK_GE(blend_mode, 0);
-  DCHECK_LT(blend_mode, NumBlendModes);
+  DCHECK_LE(blend_mode, LAST_BLEND_MODE);
   RenderPassColorMatrixProgramAA* program =
       &render_pass_color_matrix_program_aa_[precision][blend_mode];
   if (!program->initialized()) {
     TRACE_EVENT0("cc",
                  "GLRenderer::renderPassColorMatrixProgramAA::initialize");
-    program->Initialize(output_surface_->context_provider(),
-                        precision,
-                        SamplerType2D,
-                        blend_mode);
+    program->Initialize(output_surface_->context_provider(), precision,
+                        SAMPLER_TYPE_2D, blend_mode);
   }
   return program;
 }
@@ -2933,11 +2882,11 @@
                                                 SamplerType sampler,
                                                 BlendMode blend_mode) {
   DCHECK_GE(precision, 0);
-  DCHECK_LT(precision, NumTexCoordPrecisions);
+  DCHECK_LE(precision, LAST_TEX_COORD_PRECISION);
   DCHECK_GE(sampler, 0);
-  DCHECK_LT(sampler, NumSamplerTypes);
+  DCHECK_LE(sampler, LAST_SAMPLER_TYPE);
   DCHECK_GE(blend_mode, 0);
-  DCHECK_LT(blend_mode, NumBlendModes);
+  DCHECK_LE(blend_mode, LAST_BLEND_MODE);
   RenderPassMaskColorMatrixProgram* program =
       &render_pass_mask_color_matrix_program_[precision][sampler][blend_mode];
   if (!program->initialized()) {
@@ -2954,11 +2903,11 @@
                                                   SamplerType sampler,
                                                   BlendMode blend_mode) {
   DCHECK_GE(precision, 0);
-  DCHECK_LT(precision, NumTexCoordPrecisions);
+  DCHECK_LE(precision, LAST_TEX_COORD_PRECISION);
   DCHECK_GE(sampler, 0);
-  DCHECK_LT(sampler, NumSamplerTypes);
+  DCHECK_LE(sampler, LAST_SAMPLER_TYPE);
   DCHECK_GE(blend_mode, 0);
-  DCHECK_LT(blend_mode, NumBlendModes);
+  DCHECK_LE(blend_mode, LAST_BLEND_MODE);
   RenderPassMaskColorMatrixProgramAA* program =
       &render_pass_mask_color_matrix_program_aa_[precision][sampler]
                                                 [blend_mode];
@@ -2975,9 +2924,9 @@
     TexCoordPrecision precision,
     SamplerType sampler) {
   DCHECK_GE(precision, 0);
-  DCHECK_LT(precision, NumTexCoordPrecisions);
+  DCHECK_LE(precision, LAST_TEX_COORD_PRECISION);
   DCHECK_GE(sampler, 0);
-  DCHECK_LT(sampler, NumSamplerTypes);
+  DCHECK_LE(sampler, LAST_SAMPLER_TYPE);
   TileProgram* program = &tile_program_[precision][sampler];
   if (!program->initialized()) {
     TRACE_EVENT0("cc", "GLRenderer::tileProgram::initialize");
@@ -2991,9 +2940,9 @@
     TexCoordPrecision precision,
     SamplerType sampler) {
   DCHECK_GE(precision, 0);
-  DCHECK_LT(precision, NumTexCoordPrecisions);
+  DCHECK_LE(precision, LAST_TEX_COORD_PRECISION);
   DCHECK_GE(sampler, 0);
-  DCHECK_LT(sampler, NumSamplerTypes);
+  DCHECK_LE(sampler, LAST_SAMPLER_TYPE);
   TileProgramOpaque* program = &tile_program_opaque_[precision][sampler];
   if (!program->initialized()) {
     TRACE_EVENT0("cc", "GLRenderer::tileProgramOpaque::initialize");
@@ -3007,9 +2956,9 @@
     TexCoordPrecision precision,
     SamplerType sampler) {
   DCHECK_GE(precision, 0);
-  DCHECK_LT(precision, NumTexCoordPrecisions);
+  DCHECK_LE(precision, LAST_TEX_COORD_PRECISION);
   DCHECK_GE(sampler, 0);
-  DCHECK_LT(sampler, NumSamplerTypes);
+  DCHECK_LE(sampler, LAST_SAMPLER_TYPE);
   TileProgramAA* program = &tile_program_aa_[precision][sampler];
   if (!program->initialized()) {
     TRACE_EVENT0("cc", "GLRenderer::tileProgramAA::initialize");
@@ -3023,9 +2972,9 @@
     TexCoordPrecision precision,
     SamplerType sampler) {
   DCHECK_GE(precision, 0);
-  DCHECK_LT(precision, NumTexCoordPrecisions);
+  DCHECK_LE(precision, LAST_TEX_COORD_PRECISION);
   DCHECK_GE(sampler, 0);
-  DCHECK_LT(sampler, NumSamplerTypes);
+  DCHECK_LE(sampler, LAST_SAMPLER_TYPE);
   TileProgramSwizzle* program = &tile_program_swizzle_[precision][sampler];
   if (!program->initialized()) {
     TRACE_EVENT0("cc", "GLRenderer::tileProgramSwizzle::initialize");
@@ -3039,9 +2988,9 @@
 GLRenderer::GetTileProgramSwizzleOpaque(TexCoordPrecision precision,
                                         SamplerType sampler) {
   DCHECK_GE(precision, 0);
-  DCHECK_LT(precision, NumTexCoordPrecisions);
+  DCHECK_LE(precision, LAST_TEX_COORD_PRECISION);
   DCHECK_GE(sampler, 0);
-  DCHECK_LT(sampler, NumSamplerTypes);
+  DCHECK_LE(sampler, LAST_SAMPLER_TYPE);
   TileProgramSwizzleOpaque* program =
       &tile_program_swizzle_opaque_[precision][sampler];
   if (!program->initialized()) {
@@ -3056,9 +3005,9 @@
     TexCoordPrecision precision,
     SamplerType sampler) {
   DCHECK_GE(precision, 0);
-  DCHECK_LT(precision, NumTexCoordPrecisions);
+  DCHECK_LE(precision, LAST_TEX_COORD_PRECISION);
   DCHECK_GE(sampler, 0);
-  DCHECK_LT(sampler, NumSamplerTypes);
+  DCHECK_LE(sampler, LAST_SAMPLER_TYPE);
   TileProgramSwizzleAA* program = &tile_program_swizzle_aa_[precision][sampler];
   if (!program->initialized()) {
     TRACE_EVENT0("cc", "GLRenderer::tileProgramSwizzleAA::initialize");
@@ -3071,12 +3020,12 @@
 const GLRenderer::TextureProgram* GLRenderer::GetTextureProgram(
     TexCoordPrecision precision) {
   DCHECK_GE(precision, 0);
-  DCHECK_LT(precision, NumTexCoordPrecisions);
+  DCHECK_LE(precision, LAST_TEX_COORD_PRECISION);
   TextureProgram* program = &texture_program_[precision];
   if (!program->initialized()) {
     TRACE_EVENT0("cc", "GLRenderer::textureProgram::initialize");
-    program->Initialize(
-        output_surface_->context_provider(), precision, SamplerType2D);
+    program->Initialize(output_surface_->context_provider(), precision,
+                        SAMPLER_TYPE_2D);
   }
   return program;
 }
@@ -3084,14 +3033,14 @@
 const GLRenderer::NonPremultipliedTextureProgram*
 GLRenderer::GetNonPremultipliedTextureProgram(TexCoordPrecision precision) {
   DCHECK_GE(precision, 0);
-  DCHECK_LT(precision, NumTexCoordPrecisions);
+  DCHECK_LE(precision, LAST_TEX_COORD_PRECISION);
   NonPremultipliedTextureProgram* program =
       &nonpremultiplied_texture_program_[precision];
   if (!program->initialized()) {
     TRACE_EVENT0("cc",
                  "GLRenderer::NonPremultipliedTextureProgram::Initialize");
-    program->Initialize(
-        output_surface_->context_provider(), precision, SamplerType2D);
+    program->Initialize(output_surface_->context_provider(), precision,
+                        SAMPLER_TYPE_2D);
   }
   return program;
 }
@@ -3099,12 +3048,12 @@
 const GLRenderer::TextureBackgroundProgram*
 GLRenderer::GetTextureBackgroundProgram(TexCoordPrecision precision) {
   DCHECK_GE(precision, 0);
-  DCHECK_LT(precision, NumTexCoordPrecisions);
+  DCHECK_LE(precision, LAST_TEX_COORD_PRECISION);
   TextureBackgroundProgram* program = &texture_background_program_[precision];
   if (!program->initialized()) {
     TRACE_EVENT0("cc", "GLRenderer::textureProgram::initialize");
-    program->Initialize(
-        output_surface_->context_provider(), precision, SamplerType2D);
+    program->Initialize(output_surface_->context_provider(), precision,
+                        SAMPLER_TYPE_2D);
   }
   return program;
 }
@@ -3113,14 +3062,14 @@
 GLRenderer::GetNonPremultipliedTextureBackgroundProgram(
     TexCoordPrecision precision) {
   DCHECK_GE(precision, 0);
-  DCHECK_LT(precision, NumTexCoordPrecisions);
+  DCHECK_LE(precision, LAST_TEX_COORD_PRECISION);
   NonPremultipliedTextureBackgroundProgram* program =
       &nonpremultiplied_texture_background_program_[precision];
   if (!program->initialized()) {
     TRACE_EVENT0("cc",
                  "GLRenderer::NonPremultipliedTextureProgram::Initialize");
-    program->Initialize(
-        output_surface_->context_provider(), precision, SamplerType2D);
+    program->Initialize(output_surface_->context_provider(), precision,
+                        SAMPLER_TYPE_2D);
   }
   return program;
 }
@@ -3128,12 +3077,12 @@
 const GLRenderer::TextureProgram* GLRenderer::GetTextureIOSurfaceProgram(
     TexCoordPrecision precision) {
   DCHECK_GE(precision, 0);
-  DCHECK_LT(precision, NumTexCoordPrecisions);
+  DCHECK_LE(precision, LAST_TEX_COORD_PRECISION);
   TextureProgram* program = &texture_io_surface_program_[precision];
   if (!program->initialized()) {
     TRACE_EVENT0("cc", "GLRenderer::textureIOSurfaceProgram::initialize");
-    program->Initialize(
-        output_surface_->context_provider(), precision, SamplerType2DRect);
+    program->Initialize(output_surface_->context_provider(), precision,
+                        SAMPLER_TYPE_2D_RECT);
   }
   return program;
 }
@@ -3141,12 +3090,12 @@
 const GLRenderer::VideoYUVProgram* GLRenderer::GetVideoYUVProgram(
     TexCoordPrecision precision) {
   DCHECK_GE(precision, 0);
-  DCHECK_LT(precision, NumTexCoordPrecisions);
+  DCHECK_LE(precision, LAST_TEX_COORD_PRECISION);
   VideoYUVProgram* program = &video_yuv_program_[precision];
   if (!program->initialized()) {
     TRACE_EVENT0("cc", "GLRenderer::videoYUVProgram::initialize");
-    program->Initialize(
-        output_surface_->context_provider(), precision, SamplerType2D);
+    program->Initialize(output_surface_->context_provider(), precision,
+                        SAMPLER_TYPE_2D);
   }
   return program;
 }
@@ -3154,12 +3103,12 @@
 const GLRenderer::VideoYUVAProgram* GLRenderer::GetVideoYUVAProgram(
     TexCoordPrecision precision) {
   DCHECK_GE(precision, 0);
-  DCHECK_LT(precision, NumTexCoordPrecisions);
+  DCHECK_LE(precision, LAST_TEX_COORD_PRECISION);
   VideoYUVAProgram* program = &video_yuva_program_[precision];
   if (!program->initialized()) {
     TRACE_EVENT0("cc", "GLRenderer::videoYUVAProgram::initialize");
-    program->Initialize(
-        output_surface_->context_provider(), precision, SamplerType2D);
+    program->Initialize(output_surface_->context_provider(), precision,
+                        SAMPLER_TYPE_2D);
   }
   return program;
 }
@@ -3169,13 +3118,13 @@
   if (!Capabilities().using_egl_image)
     return NULL;
   DCHECK_GE(precision, 0);
-  DCHECK_LT(precision, NumTexCoordPrecisions);
+  DCHECK_LE(precision, LAST_TEX_COORD_PRECISION);
   VideoStreamTextureProgram* program =
       &video_stream_texture_program_[precision];
   if (!program->initialized()) {
     TRACE_EVENT0("cc", "GLRenderer::streamTextureProgram::initialize");
-    program->Initialize(
-        output_surface_->context_provider(), precision, SamplerTypeExternalOES);
+    program->Initialize(output_surface_->context_provider(), precision,
+                        SAMPLER_TYPE_EXTERNAL_OES);
   }
   return program;
 }
@@ -3183,8 +3132,8 @@
 void GLRenderer::CleanupSharedObjects() {
   shared_geometry_ = nullptr;
 
-  for (int i = 0; i < NumTexCoordPrecisions; ++i) {
-    for (int j = 0; j < NumSamplerTypes; ++j) {
+  for (int i = 0; i <= LAST_TEX_COORD_PRECISION; ++i) {
+    for (int j = 0; j <= LAST_SAMPLER_TYPE; ++j) {
       tile_program_[i][j].Cleanup(gl_);
       tile_program_opaque_[i][j].Cleanup(gl_);
       tile_program_swizzle_[i][j].Cleanup(gl_);
@@ -3192,14 +3141,14 @@
       tile_program_aa_[i][j].Cleanup(gl_);
       tile_program_swizzle_aa_[i][j].Cleanup(gl_);
 
-      for (int k = 0; k < NumBlendModes; k++) {
+      for (int k = 0; k <= LAST_BLEND_MODE; k++) {
         render_pass_mask_program_[i][j][k].Cleanup(gl_);
         render_pass_mask_program_aa_[i][j][k].Cleanup(gl_);
         render_pass_mask_color_matrix_program_aa_[i][j][k].Cleanup(gl_);
         render_pass_mask_color_matrix_program_[i][j][k].Cleanup(gl_);
       }
     }
-    for (int j = 0; j < NumBlendModes; j++) {
+    for (int j = 0; j <= LAST_BLEND_MODE; j++) {
       render_pass_program_[i][j].Cleanup(gl_);
       render_pass_program_aa_[i][j].Cleanup(gl_);
       render_pass_color_matrix_program_[i][j].Cleanup(gl_);
diff --git a/cc/output/gl_renderer.h b/cc/output/gl_renderer.h
index 22e7706..25c7096 100644
--- a/cc/output/gl_renderer.h
+++ b/cc/output/gl_renderer.h
@@ -376,47 +376,62 @@
   const SolidColorProgram* GetSolidColorProgram();
   const SolidColorProgramAA* GetSolidColorProgramAA();
 
-  TileProgram tile_program_[NumTexCoordPrecisions][NumSamplerTypes];
+  TileProgram
+      tile_program_[LAST_TEX_COORD_PRECISION + 1][LAST_SAMPLER_TYPE + 1];
   TileProgramOpaque
-      tile_program_opaque_[NumTexCoordPrecisions][NumSamplerTypes];
-  TileProgramAA tile_program_aa_[NumTexCoordPrecisions][NumSamplerTypes];
-  TileProgramSwizzle
-      tile_program_swizzle_[NumTexCoordPrecisions][NumSamplerTypes];
+      tile_program_opaque_[LAST_TEX_COORD_PRECISION + 1][LAST_SAMPLER_TYPE + 1];
+  TileProgramAA
+      tile_program_aa_[LAST_TEX_COORD_PRECISION + 1][LAST_SAMPLER_TYPE + 1];
+  TileProgramSwizzle tile_program_swizzle_[LAST_TEX_COORD_PRECISION +
+                                           1][LAST_SAMPLER_TYPE + 1];
   TileProgramSwizzleOpaque
-      tile_program_swizzle_opaque_[NumTexCoordPrecisions][NumSamplerTypes];
-  TileProgramSwizzleAA
-      tile_program_swizzle_aa_[NumTexCoordPrecisions][NumSamplerTypes];
+      tile_program_swizzle_opaque_[LAST_TEX_COORD_PRECISION +
+                                   1][LAST_SAMPLER_TYPE + 1];
+  TileProgramSwizzleAA tile_program_swizzle_aa_[LAST_TEX_COORD_PRECISION +
+                                                1][LAST_SAMPLER_TYPE + 1];
 
   TileCheckerboardProgram tile_checkerboard_program_;
 
-  TextureProgram texture_program_[NumTexCoordPrecisions];
+  TextureProgram texture_program_[LAST_TEX_COORD_PRECISION + 1];
   NonPremultipliedTextureProgram
-      nonpremultiplied_texture_program_[NumTexCoordPrecisions];
-  TextureBackgroundProgram texture_background_program_[NumTexCoordPrecisions];
+      nonpremultiplied_texture_program_[LAST_TEX_COORD_PRECISION + 1];
+  TextureBackgroundProgram
+      texture_background_program_[LAST_TEX_COORD_PRECISION + 1];
   NonPremultipliedTextureBackgroundProgram
-      nonpremultiplied_texture_background_program_[NumTexCoordPrecisions];
-  TextureProgram texture_io_surface_program_[NumTexCoordPrecisions];
+      nonpremultiplied_texture_background_program_[LAST_TEX_COORD_PRECISION +
+                                                   1];
+  TextureProgram texture_io_surface_program_[LAST_TEX_COORD_PRECISION + 1];
 
-  RenderPassProgram render_pass_program_[NumTexCoordPrecisions][NumBlendModes];
-  RenderPassProgramAA
-      render_pass_program_aa_[NumTexCoordPrecisions][NumBlendModes];
-  RenderPassMaskProgram render_pass_mask_program_
-      [NumTexCoordPrecisions][NumSamplerTypes][NumBlendModes];
-  RenderPassMaskProgramAA render_pass_mask_program_aa_
-      [NumTexCoordPrecisions][NumSamplerTypes][NumBlendModes];
+  RenderPassProgram
+      render_pass_program_[LAST_TEX_COORD_PRECISION + 1][LAST_BLEND_MODE + 1];
+  RenderPassProgramAA render_pass_program_aa_[LAST_TEX_COORD_PRECISION +
+                                              1][LAST_BLEND_MODE + 1];
+  RenderPassMaskProgram
+      render_pass_mask_program_[LAST_TEX_COORD_PRECISION +
+                                1][LAST_SAMPLER_TYPE + 1][LAST_BLEND_MODE + 1];
+  RenderPassMaskProgramAA
+      render_pass_mask_program_aa_[LAST_TEX_COORD_PRECISION +
+                                   1][LAST_SAMPLER_TYPE + 1][LAST_BLEND_MODE +
+                                                             1];
   RenderPassColorMatrixProgram
-      render_pass_color_matrix_program_[NumTexCoordPrecisions][NumBlendModes];
-  RenderPassColorMatrixProgramAA render_pass_color_matrix_program_aa_
-      [NumTexCoordPrecisions][NumBlendModes];
-  RenderPassMaskColorMatrixProgram render_pass_mask_color_matrix_program_
-      [NumTexCoordPrecisions][NumSamplerTypes][NumBlendModes];
-  RenderPassMaskColorMatrixProgramAA render_pass_mask_color_matrix_program_aa_
-      [NumTexCoordPrecisions][NumSamplerTypes][NumBlendModes];
+      render_pass_color_matrix_program_[LAST_TEX_COORD_PRECISION +
+                                        1][LAST_BLEND_MODE + 1];
+  RenderPassColorMatrixProgramAA
+      render_pass_color_matrix_program_aa_[LAST_TEX_COORD_PRECISION +
+                                           1][LAST_BLEND_MODE + 1];
+  RenderPassMaskColorMatrixProgram
+      render_pass_mask_color_matrix_program_[LAST_TEX_COORD_PRECISION +
+                                             1][LAST_SAMPLER_TYPE +
+                                                1][LAST_BLEND_MODE + 1];
+  RenderPassMaskColorMatrixProgramAA
+      render_pass_mask_color_matrix_program_aa_[LAST_TEX_COORD_PRECISION +
+                                                1][LAST_SAMPLER_TYPE +
+                                                   1][LAST_BLEND_MODE + 1];
 
-  VideoYUVProgram video_yuv_program_[NumTexCoordPrecisions];
-  VideoYUVAProgram video_yuva_program_[NumTexCoordPrecisions];
+  VideoYUVProgram video_yuv_program_[LAST_TEX_COORD_PRECISION + 1];
+  VideoYUVAProgram video_yuva_program_[LAST_TEX_COORD_PRECISION + 1];
   VideoStreamTextureProgram
-      video_stream_texture_program_[NumTexCoordPrecisions];
+      video_stream_texture_program_[LAST_TEX_COORD_PRECISION + 1];
 
   DebugBorderProgram debug_border_program_;
   SolidColorProgram solid_color_program_;
diff --git a/cc/output/gl_renderer_unittest.cc b/cc/output/gl_renderer_unittest.cc
index c5c1e4d..fc709008 100644
--- a/cc/output/gl_renderer_unittest.cc
+++ b/cc/output/gl_renderer_unittest.cc
@@ -57,41 +57,39 @@
 
 static inline SkXfermode::Mode BlendModeToSkXfermode(BlendMode blend_mode) {
   switch (blend_mode) {
-    case BlendModeNone:
-    case BlendModeNormal:
+    case BLEND_MODE_NONE:
+    case BLEND_MODE_NORMAL:
       return SkXfermode::kSrcOver_Mode;
-    case BlendModeScreen:
+    case BLEND_MODE_SCREEN:
       return SkXfermode::kScreen_Mode;
-    case BlendModeOverlay:
+    case BLEND_MODE_OVERLAY:
       return SkXfermode::kOverlay_Mode;
-    case BlendModeDarken:
+    case BLEND_MODE_DARKEN:
       return SkXfermode::kDarken_Mode;
-    case BlendModeLighten:
+    case BLEND_MODE_LIGHTEN:
       return SkXfermode::kLighten_Mode;
-    case BlendModeColorDodge:
+    case BLEND_MODE_COLOR_DODGE:
       return SkXfermode::kColorDodge_Mode;
-    case BlendModeColorBurn:
+    case BLEND_MODE_COLOR_BURN:
       return SkXfermode::kColorBurn_Mode;
-    case BlendModeHardLight:
+    case BLEND_MODE_HARD_LIGHT:
       return SkXfermode::kHardLight_Mode;
-    case BlendModeSoftLight:
+    case BLEND_MODE_SOFT_LIGHT:
       return SkXfermode::kSoftLight_Mode;
-    case BlendModeDifference:
+    case BLEND_MODE_DIFFERENCE:
       return SkXfermode::kDifference_Mode;
-    case BlendModeExclusion:
+    case BLEND_MODE_EXCLUSION:
       return SkXfermode::kExclusion_Mode;
-    case BlendModeMultiply:
+    case BLEND_MODE_MULTIPLY:
       return SkXfermode::kMultiply_Mode;
-    case BlendModeHue:
+    case BLEND_MODE_HUE:
       return SkXfermode::kHue_Mode;
-    case BlendModeSaturation:
+    case BLEND_MODE_SATURATION:
       return SkXfermode::kSaturation_Mode;
-    case BlendModeColor:
+    case BLEND_MODE_COLOR:
       return SkXfermode::kColor_Mode;
-    case BlendModeLuminosity:
+    case BLEND_MODE_LUMINOSITY:
       return SkXfermode::kLuminosity_Mode;
-    case NumBlendModes:
-      NOTREACHED();
   }
   return SkXfermode::kSrcOver_Mode;
 }
@@ -105,13 +103,13 @@
     EXPECT_PROGRAM_VALID(renderer()->GetDebugBorderProgram());
     EXPECT_PROGRAM_VALID(renderer()->GetSolidColorProgram());
     EXPECT_PROGRAM_VALID(renderer()->GetSolidColorProgramAA());
-    TestShadersWithTexCoordPrecision(TexCoordPrecisionMedium);
-    TestShadersWithTexCoordPrecision(TexCoordPrecisionHigh);
+    TestShadersWithTexCoordPrecision(TEX_COORD_PRECISION_MEDIUM);
+    TestShadersWithTexCoordPrecision(TEX_COORD_PRECISION_HIGH);
     ASSERT_FALSE(renderer()->IsContextLost());
   }
 
   void TestShadersWithTexCoordPrecision(TexCoordPrecision precision) {
-    for (int i = 0; i < NumBlendModes; ++i) {
+    for (int i = 0; i <= LAST_BLEND_MODE; ++i) {
       BlendMode blend_mode = static_cast<BlendMode>(i);
       EXPECT_PROGRAM_VALID(
           renderer()->GetRenderPassProgram(precision, blend_mode));
@@ -132,11 +130,11 @@
       EXPECT_PROGRAM_VALID(renderer()->GetVideoStreamTextureProgram(precision));
     else
       EXPECT_FALSE(renderer()->GetVideoStreamTextureProgram(precision));
-    TestShadersWithSamplerType(precision, SamplerType2D);
-    TestShadersWithSamplerType(precision, SamplerType2DRect);
+    TestShadersWithSamplerType(precision, SAMPLER_TYPE_2D);
+    TestShadersWithSamplerType(precision, SAMPLER_TYPE_2D_RECT);
     // This is unlikely to be ever true in tests due to usage of osmesa.
     if (renderer()->Capabilities().using_egl_image)
-      TestShadersWithSamplerType(precision, SamplerTypeExternalOES);
+      TestShadersWithSamplerType(precision, SAMPLER_TYPE_EXTERNAL_OES);
   }
 
   void TestShadersWithSamplerType(TexCoordPrecision precision,
@@ -149,7 +147,7 @@
         renderer()->GetTileProgramSwizzleOpaque(precision, sampler));
     EXPECT_PROGRAM_VALID(
         renderer()->GetTileProgramSwizzleAA(precision, sampler));
-    for (int i = 0; i < NumBlendModes; ++i) {
+    for (int i = 0; i <= LAST_BLEND_MODE; ++i) {
       BlendMode blend_mode = static_cast<BlendMode>(i);
       EXPECT_PROGRAM_VALID(
           renderer()->GetRenderPassMaskProgram(precision, sampler, blend_mode));
@@ -1437,9 +1435,8 @@
   TestRenderPass* root_pass;
 
   ResourceProvider::ResourceId mask = resource_provider_->CreateResource(
-      gfx::Size(20, 12),
-      GL_CLAMP_TO_EDGE,
-      ResourceProvider::TextureHintImmutable,
+      gfx::Size(20, 12), GL_CLAMP_TO_EDGE,
+      ResourceProvider::TEXTURE_HINT_IMMUTABLE,
       resource_provider_->best_texture_format());
   resource_provider_->AllocateForTesting(mask);
 
@@ -1469,10 +1466,10 @@
   gfx::Transform transform_causing_aa;
   transform_causing_aa.Rotate(20.0);
 
-  for (int i = 0; i < NumBlendModes; ++i) {
+  for (int i = 0; i <= LAST_BLEND_MODE; ++i) {
     BlendMode blend_mode = static_cast<BlendMode>(i);
     SkXfermode::Mode xfer_mode = BlendModeToSkXfermode(blend_mode);
-    settings_.force_blending_with_shaders = (blend_mode != BlendModeNone);
+    settings_.force_blending_with_shaders = (blend_mode != BLEND_MODE_NONE);
     // RenderPassProgram
     render_passes_in_draw_order_.clear();
     child_pass = AddRenderPass(&render_passes_in_draw_order_,
@@ -1499,7 +1496,7 @@
                          viewport_rect,
                          viewport_rect,
                          false);
-    TestRenderPassProgram(TexCoordPrecisionMedium, blend_mode);
+    TestRenderPassProgram(TEX_COORD_PRECISION_MEDIUM, blend_mode);
 
     // RenderPassColorMatrixProgram
     render_passes_in_draw_order_.clear();
@@ -1524,7 +1521,7 @@
                          viewport_rect,
                          viewport_rect,
                          false);
-    TestRenderPassColorMatrixProgram(TexCoordPrecisionMedium, blend_mode);
+    TestRenderPassColorMatrixProgram(TEX_COORD_PRECISION_MEDIUM, blend_mode);
 
     // RenderPassMaskProgram
     render_passes_in_draw_order_.clear();
@@ -1553,8 +1550,8 @@
                          viewport_rect,
                          viewport_rect,
                          false);
-    TestRenderPassMaskProgram(
-        TexCoordPrecisionMedium, SamplerType2D, blend_mode);
+    TestRenderPassMaskProgram(TEX_COORD_PRECISION_MEDIUM, SAMPLER_TYPE_2D,
+                              blend_mode);
 
     // RenderPassMaskColorMatrixProgram
     render_passes_in_draw_order_.clear();
@@ -1579,8 +1576,8 @@
                          viewport_rect,
                          viewport_rect,
                          false);
-    TestRenderPassMaskColorMatrixProgram(
-        TexCoordPrecisionMedium, SamplerType2D, blend_mode);
+    TestRenderPassMaskColorMatrixProgram(TEX_COORD_PRECISION_MEDIUM,
+                                         SAMPLER_TYPE_2D, blend_mode);
 
     // RenderPassProgramAA
     render_passes_in_draw_order_.clear();
@@ -1609,7 +1606,7 @@
                          viewport_rect,
                          viewport_rect,
                          false);
-    TestRenderPassProgramAA(TexCoordPrecisionMedium, blend_mode);
+    TestRenderPassProgramAA(TEX_COORD_PRECISION_MEDIUM, blend_mode);
 
     // RenderPassColorMatrixProgramAA
     render_passes_in_draw_order_.clear();
@@ -1634,7 +1631,7 @@
                          viewport_rect,
                          viewport_rect,
                          false);
-    TestRenderPassColorMatrixProgramAA(TexCoordPrecisionMedium, blend_mode);
+    TestRenderPassColorMatrixProgramAA(TEX_COORD_PRECISION_MEDIUM, blend_mode);
 
     // RenderPassMaskProgramAA
     render_passes_in_draw_order_.clear();
@@ -1663,8 +1660,8 @@
                          viewport_rect,
                          viewport_rect,
                          false);
-    TestRenderPassMaskProgramAA(
-        TexCoordPrecisionMedium, SamplerType2D, blend_mode);
+    TestRenderPassMaskProgramAA(TEX_COORD_PRECISION_MEDIUM, SAMPLER_TYPE_2D,
+                                blend_mode);
 
     // RenderPassMaskColorMatrixProgramAA
     render_passes_in_draw_order_.clear();
@@ -1689,8 +1686,8 @@
                          viewport_rect,
                          viewport_rect,
                          false);
-    TestRenderPassMaskColorMatrixProgramAA(
-        TexCoordPrecisionMedium, SamplerType2D, blend_mode);
+    TestRenderPassMaskColorMatrixProgramAA(TEX_COORD_PRECISION_MEDIUM,
+                                           SAMPLER_TYPE_2D, blend_mode);
   }
 }
 
@@ -1742,7 +1739,7 @@
 
   // If use_aa incorrectly ignores clipping, it will use the
   // RenderPassProgramAA shader instead of the RenderPassProgram.
-  TestRenderPassProgram(TexCoordPrecisionMedium, BlendModeNone);
+  TestRenderPassProgram(TEX_COORD_PRECISION_MEDIUM, BLEND_MODE_NONE);
 }
 
 TEST_F(GLRendererShaderTest, DrawSolidColorShader) {
diff --git a/cc/output/program_binding.h b/cc/output/program_binding.h
index a10d0ab..9c3244ca5 100644
--- a/cc/output/program_binding.h
+++ b/cc/output/program_binding.h
@@ -59,7 +59,7 @@
   void Initialize(ContextProvider* context_provider,
                   TexCoordPrecision precision,
                   SamplerType sampler,
-                  BlendMode blend_mode = BlendModeNone) {
+                  BlendMode blend_mode = BLEND_MODE_NONE) {
     DCHECK(context_provider);
     DCHECK(!initialized_);
 
diff --git a/cc/output/renderer_pixeltest.cc b/cc/output/renderer_pixeltest.cc
index 36765ce..743e1cf 100644
--- a/cc/output/renderer_pixeltest.cc
+++ b/cc/output/renderer_pixeltest.cc
@@ -124,19 +124,14 @@
                           SkColorGetR(texel_color),
                           SkColorGetG(texel_color),
                           SkColorGetB(texel_color));
-  std::vector<uint32_t> pixels(rect.size().GetArea(), pixel_color);
+  size_t num_pixels = static_cast<size_t>(rect.width()) * rect.height();
+  std::vector<uint32_t> pixels(num_pixels, pixel_color);
 
-  ResourceProvider::ResourceId resource =
-      resource_provider->CreateResource(rect.size(),
-                                        GL_CLAMP_TO_EDGE,
-                                        ResourceProvider::TextureHintImmutable,
-                                        RGBA_8888);
-  resource_provider->SetPixels(
-      resource,
-      reinterpret_cast<uint8_t*>(&pixels.front()),
-      rect,
-      rect,
-      gfx::Vector2d());
+  ResourceProvider::ResourceId resource = resource_provider->CreateResource(
+      rect.size(), GL_CLAMP_TO_EDGE, ResourceProvider::TEXTURE_HINT_IMMUTABLE,
+      RGBA_8888);
+  resource_provider->CopyToResource(
+      resource, reinterpret_cast<uint8_t*>(&pixels.front()), rect.size());
 
   float vertex_opacity[4] = {1.0f, 1.0f, 1.0f, 1.0f};
 
@@ -163,6 +158,13 @@
 TYPED_TEST_CASE(RendererPixelTest, RendererTypes);
 
 template <typename RendererType>
+class SoftwareRendererPixelTest : public RendererPixelTest<RendererType> {};
+
+typedef ::testing::Types<SoftwareRenderer, SoftwareRendererWithExpandedViewport>
+    SoftwareRendererTypes;
+TYPED_TEST_CASE(SoftwareRendererPixelTest, SoftwareRendererTypes);
+
+template <typename RendererType>
 class FuzzyForSoftwareOnlyPixelComparator : public PixelComparator {
  public:
   explicit FuzzyForSoftwareOnlyPixelComparator(bool discard_alpha)
@@ -566,7 +568,7 @@
     const bool with_alpha = (video_frame->format() == media::VideoFrame::YV12A);
     const YUVVideoDrawQuad::ColorSpace color_space =
         (video_frame->format() == media::VideoFrame::YV12J
-             ? YUVVideoDrawQuad::REC_601_JPEG
+             ? YUVVideoDrawQuad::JPEG
              : YUVVideoDrawQuad::REC_601);
     const gfx::Rect rect(shared_state->content_bounds);
     const gfx::Rect opaque_rect(0, 0, 0, 0);
@@ -1320,18 +1322,13 @@
 
   ResourceProvider::ResourceId mask_resource_id =
       this->resource_provider_->CreateResource(
-          mask_rect.size(),
-          GL_CLAMP_TO_EDGE,
-          ResourceProvider::TextureHintImmutable,
-          RGBA_8888);
+          mask_rect.size(), GL_CLAMP_TO_EDGE,
+          ResourceProvider::TEXTURE_HINT_IMMUTABLE, RGBA_8888);
   {
     SkAutoLockPixels lock(bitmap);
-    this->resource_provider_->SetPixels(
-        mask_resource_id,
-        reinterpret_cast<uint8_t*>(bitmap.getPixels()),
-        mask_rect,
-        mask_rect,
-        gfx::Vector2d());
+    this->resource_provider_->CopyToResource(
+        mask_resource_id, reinterpret_cast<uint8_t*>(bitmap.getPixels()),
+        mask_rect.size());
   }
 
   // This RenderPassDrawQuad does not include the full |viewport_rect| which is
@@ -1827,7 +1824,7 @@
       FuzzyPixelOffByOneComparator(true)));
 }
 
-TYPED_TEST(RendererPixelTest, PictureDrawQuadIdentityScale) {
+TYPED_TEST(SoftwareRendererPixelTest, PictureDrawQuadIdentityScale) {
   gfx::Size pile_tile_size(1000, 1000);
   gfx::Rect viewport(this->device_viewport_size_);
   // TODO(enne): the renderer should figure this out on its own.
@@ -1907,7 +1904,7 @@
 }
 
 // Not WithSkiaGPUBackend since that path currently requires tiles for opacity.
-TYPED_TEST(RendererPixelTest, PictureDrawQuadOpacity) {
+TYPED_TEST(SoftwareRendererPixelTest, PictureDrawQuadOpacity) {
   gfx::Size pile_tile_size(1000, 1000);
   gfx::Rect viewport(this->device_viewport_size_);
   ResourceFormat texture_format = RGBA_8888;
@@ -1984,7 +1981,7 @@
 
 // If we disable image filtering, then a 2x2 bitmap should appear as four
 // huge sharp squares.
-TYPED_TEST(RendererPixelTest, PictureDrawQuadDisableImageFiltering) {
+TYPED_TEST(SoftwareRendererPixelTest, PictureDrawQuadDisableImageFiltering) {
   // We only care about this in software mode since bilinear filtering is
   // cheap in hardware.
   if (!IsSoftwareRenderer<TypeParam>())
@@ -2041,7 +2038,7 @@
 }
 
 // This disables filtering by setting |nearest_neighbor| on the PictureDrawQuad.
-TYPED_TEST(RendererPixelTest, PictureDrawQuadNearestNeighbor) {
+TYPED_TEST(SoftwareRendererPixelTest, PictureDrawQuadNearestNeighbor) {
   gfx::Size pile_tile_size(1000, 1000);
   gfx::Rect viewport(this->device_viewport_size_);
   ResourceFormat texture_format = RGBA_8888;
@@ -2110,19 +2107,13 @@
   gfx::Size tile_size(2, 2);
   ResourceProvider::ResourceId resource =
       this->resource_provider_->CreateResource(
-          tile_size,
-          GL_CLAMP_TO_EDGE,
-          ResourceProvider::TextureHintImmutable,
+          tile_size, GL_CLAMP_TO_EDGE, ResourceProvider::TEXTURE_HINT_IMMUTABLE,
           RGBA_8888);
 
   {
     SkAutoLockPixels lock(bitmap);
-    this->resource_provider_->SetPixels(
-        resource,
-        static_cast<uint8_t*>(bitmap.getPixels()),
-        gfx::Rect(tile_size),
-        gfx::Rect(tile_size),
-        gfx::Vector2d());
+    this->resource_provider_->CopyToResource(
+        resource, static_cast<uint8_t*>(bitmap.getPixels()), tile_size);
   }
 
   RenderPassId id(1, 1);
@@ -2148,7 +2139,7 @@
       ExactPixelComparator(true)));
 }
 
-TYPED_TEST(RendererPixelTest, PictureDrawQuadNonIdentityScale) {
+TYPED_TEST(SoftwareRendererPixelTest, PictureDrawQuadNonIdentityScale) {
   gfx::Size pile_tile_size(1000, 1000);
   gfx::Rect viewport(this->device_viewport_size_);
   // TODO(enne): the renderer should figure this out on its own.
@@ -2449,45 +2440,6 @@
       &capture_rect));
 }
 
-TEST_F(GLRendererPixelTest, PictureDrawQuadTexture4444) {
-  gfx::Size pile_tile_size(1000, 1000);
-  gfx::Rect viewport(this->device_viewport_size_);
-  ResourceFormat texture_format = RGBA_4444;
-  bool nearest_neighbor = false;
-
-  RenderPassId id(1, 1);
-  gfx::Transform transform_to_root;
-  scoped_ptr<RenderPass> pass =
-      CreateTestRenderPass(id, viewport, transform_to_root);
-
-  // One viewport-filling blue quad
-  scoped_ptr<FakePicturePile> blue_recording =
-      FakePicturePile::CreateFilledPile(pile_tile_size, viewport.size());
-  SkPaint blue_paint;
-  blue_paint.setColor(SK_ColorBLUE);
-  blue_recording->add_draw_rect_with_paint(viewport, blue_paint);
-  blue_recording->RerecordPile();
-  scoped_refptr<FakePicturePileImpl> blue_pile =
-      FakePicturePileImpl::CreateFromPile(blue_recording.get(), nullptr);
-
-  gfx::Transform blue_content_to_target_transform;
-  SharedQuadState* blue_shared_state = CreateTestSharedQuadState(
-      blue_content_to_target_transform, viewport, pass.get());
-
-  PictureDrawQuad* blue_quad = pass->CreateAndAppendDrawQuad<PictureDrawQuad>();
-  blue_quad->SetNew(blue_shared_state, viewport, gfx::Rect(), viewport,
-                    gfx::RectF(0.f, 0.f, 1.f, 1.f), viewport.size(),
-                    nearest_neighbor, texture_format, viewport, 1.f,
-                    blue_pile.get());
-
-  RenderPassList pass_list;
-  pass_list.push_back(pass.Pass());
-
-  EXPECT_TRUE(this->RunPixelTest(&pass_list,
-                                 base::FilePath(FILE_PATH_LITERAL("blue.png")),
-                                 ExactPixelComparator(true)));
-}
-
 TYPED_TEST(RendererPixelTest, WrapModeRepeat) {
   gfx::Rect rect(this->device_viewport_size_);
 
@@ -2497,7 +2449,7 @@
   SharedQuadState* shared_state =
       CreateTestSharedQuadState(gfx::Transform(), rect, pass.get());
 
-  gfx::Rect texture_rect(4, 4);
+  gfx::Size texture_size(4, 4);
   SkPMColor colors[4] = {
     SkPreMultiplyColor(SkColorSetARGB(255, 0, 255, 0)),
     SkPreMultiplyColor(SkColorSetARGB(255, 0, 128, 0)),
@@ -2512,33 +2464,23 @@
   };
   ResourceProvider::ResourceId resource =
       this->resource_provider_->CreateResource(
-          texture_rect.size(),
-          GL_REPEAT,
-          ResourceProvider::TextureHintImmutable,
+          texture_size, GL_REPEAT, ResourceProvider::TEXTURE_HINT_IMMUTABLE,
           RGBA_8888);
-  this->resource_provider_->SetPixels(
-      resource,
-      reinterpret_cast<uint8_t*>(pixels),
-      texture_rect,
-      texture_rect,
-      gfx::Vector2d());
+  this->resource_provider_->CopyToResource(
+      resource, reinterpret_cast<uint8_t*>(pixels), texture_size);
 
   float vertex_opacity[4] = {1.0f, 1.0f, 1.0f, 1.0f};
   TextureDrawQuad* texture_quad =
       pass->CreateAndAppendDrawQuad<TextureDrawQuad>();
   texture_quad->SetNew(
-      shared_state,
-      gfx::Rect(this->device_viewport_size_),
-      gfx::Rect(),
-      gfx::Rect(this->device_viewport_size_),
-      resource,
+      shared_state, gfx::Rect(this->device_viewport_size_), gfx::Rect(),
+      gfx::Rect(this->device_viewport_size_), resource,
       true,                     // premultiplied_alpha
       gfx::PointF(0.0f, 0.0f),  // uv_top_left
       gfx::PointF(              // uv_bottom_right
-          this->device_viewport_size_.width() / texture_rect.width(),
-          this->device_viewport_size_.height() / texture_rect.height()),
-      SK_ColorWHITE,
-      vertex_opacity,
+          this->device_viewport_size_.width() / texture_size.width(),
+          this->device_viewport_size_.height() / texture_size.height()),
+      SK_ColorWHITE, vertex_opacity,
       false,   // flipped
       false);  // nearest_neighbor
 
diff --git a/cc/output/shader.cc b/cc/output/shader.cc
index 0a33e53..1891546 100644
--- a/cc/output/shader.cc
+++ b/cc/output/shader.cc
@@ -52,7 +52,7 @@
     TexCoordPrecision requested_precision,
     std::string shader_string) {
   switch (requested_precision) {
-    case TexCoordPrecisionHigh:
+    case TEX_COORD_PRECISION_HIGH:
       DCHECK_NE(shader_string.find("TexCoordPrecision"), std::string::npos);
       return "#ifdef GL_FRAGMENT_PRECISION_HIGH\n"
              "  #define TexCoordPrecision highp\n"
@@ -60,10 +60,10 @@
              "  #define TexCoordPrecision mediump\n"
              "#endif\n" +
              shader_string;
-    case TexCoordPrecisionMedium:
+    case TEX_COORD_PRECISION_MEDIUM:
       DCHECK_NE(shader_string.find("TexCoordPrecision"), std::string::npos);
       return "#define TexCoordPrecision mediump\n" + shader_string;
-    case TexCoordPrecisionNA:
+    case TEX_COORD_PRECISION_NA:
       DCHECK_EQ(shader_string.find("TexCoordPrecision"), std::string::npos);
       DCHECK_EQ(shader_string.find("texture2D"), std::string::npos);
       DCHECK_EQ(shader_string.find("texture2DRect"), std::string::npos);
@@ -104,34 +104,34 @@
 
   int highp_threshold = std::max(*highp_threshold_cache, highp_threshold_min);
   if (x > highp_threshold || y > highp_threshold)
-    return TexCoordPrecisionHigh;
-  return TexCoordPrecisionMedium;
+    return TEX_COORD_PRECISION_HIGH;
+  return TEX_COORD_PRECISION_MEDIUM;
 }
 
 static std::string SetFragmentSamplerType(SamplerType requested_type,
                                           std::string shader_string) {
   switch (requested_type) {
-    case SamplerType2D:
+    case SAMPLER_TYPE_2D:
       DCHECK_NE(shader_string.find("SamplerType"), std::string::npos);
       DCHECK_NE(shader_string.find("TextureLookup"), std::string::npos);
       return "#define SamplerType sampler2D\n"
              "#define TextureLookup texture2D\n" +
              shader_string;
-    case SamplerType2DRect:
+    case SAMPLER_TYPE_2D_RECT:
       DCHECK_NE(shader_string.find("SamplerType"), std::string::npos);
       DCHECK_NE(shader_string.find("TextureLookup"), std::string::npos);
       return "#extension GL_ARB_texture_rectangle : require\n"
              "#define SamplerType sampler2DRect\n"
              "#define TextureLookup texture2DRect\n" +
              shader_string;
-    case SamplerTypeExternalOES:
+    case SAMPLER_TYPE_EXTERNAL_OES:
       DCHECK_NE(shader_string.find("SamplerType"), std::string::npos);
       DCHECK_NE(shader_string.find("TextureLookup"), std::string::npos);
       return "#extension GL_OES_EGL_image_external : require\n"
              "#define SamplerType samplerExternalOES\n"
              "#define TextureLookup texture2D\n" +
              shader_string;
-    case SamplerTypeNA:
+    case SAMPLER_TYPE_NA:
       DCHECK_EQ(shader_string.find("SamplerType"), std::string::npos);
       DCHECK_EQ(shader_string.find("TextureLookup"), std::string::npos);
       return shader_string;
@@ -734,7 +734,7 @@
 FragmentTexBlendMode::FragmentTexBlendMode()
     : backdrop_location_(-1),
       backdrop_rect_location_(-1),
-      blend_mode_(BlendModeNone) {
+      blend_mode_(BLEND_MODE_NONE) {
 }
 
 std::string FragmentTexBlendMode::SetBlendModeFunctions(
@@ -908,20 +908,20 @@
   });
 
   switch (blend_mode_) {
-    case BlendModeOverlay:
-    case BlendModeHardLight:
+    case BLEND_MODE_OVERLAY:
+    case BLEND_MODE_HARD_LIGHT:
       return kFunctionHardLight;
-    case BlendModeColorDodge:
+    case BLEND_MODE_COLOR_DODGE:
       return kFunctionColorDodgeComponent;
-    case BlendModeColorBurn:
+    case BLEND_MODE_COLOR_BURN:
       return kFunctionColorBurnComponent;
-    case BlendModeSoftLight:
+    case BLEND_MODE_SOFT_LIGHT:
       return kFunctionSoftLightComponentPosDstAlpha;
-    case BlendModeHue:
-    case BlendModeSaturation:
+    case BLEND_MODE_HUE:
+    case BLEND_MODE_SATURATION:
       return kFunctionLum + kFunctionSat;
-    case BlendModeColor:
-    case BlendModeLuminosity:
+    case BLEND_MODE_COLOR:
+    case BLEND_MODE_LUMINOSITY:
       return kFunctionLum;
     default:
       return std::string();
@@ -939,29 +939,29 @@
 
 std::string FragmentTexBlendMode::GetBlendFunctionBodyForRGB() const {
   switch (blend_mode_) {
-    case BlendModeNormal:
+    case BLEND_MODE_NORMAL:
       return "result.rgb = src.rgb + dst.rgb * (1.0 - src.a);";
-    case BlendModeScreen:
+    case BLEND_MODE_SCREEN:
       return "result.rgb = src.rgb + (1.0 - src.rgb) * dst.rgb;";
-    case BlendModeLighten:
+    case BLEND_MODE_LIGHTEN:
       return "result.rgb = max((1.0 - src.a) * dst.rgb + src.rgb,"
              "                 (1.0 - dst.a) * src.rgb + dst.rgb);";
-    case BlendModeOverlay:
+    case BLEND_MODE_OVERLAY:
       return "result.rgb = hardLight(dst, src);";
-    case BlendModeDarken:
+    case BLEND_MODE_DARKEN:
       return "result.rgb = min((1.0 - src.a) * dst.rgb + src.rgb,"
              "                 (1.0 - dst.a) * src.rgb + dst.rgb);";
-    case BlendModeColorDodge:
+    case BLEND_MODE_COLOR_DODGE:
       return "result.r = getColorDodgeComponent(src.r, src.a, dst.r, dst.a);"
              "result.g = getColorDodgeComponent(src.g, src.a, dst.g, dst.a);"
              "result.b = getColorDodgeComponent(src.b, src.a, dst.b, dst.a);";
-    case BlendModeColorBurn:
+    case BLEND_MODE_COLOR_BURN:
       return "result.r = getColorBurnComponent(src.r, src.a, dst.r, dst.a);"
              "result.g = getColorBurnComponent(src.g, src.a, dst.g, dst.a);"
              "result.b = getColorBurnComponent(src.b, src.a, dst.b, dst.a);";
-    case BlendModeHardLight:
+    case BLEND_MODE_HARD_LIGHT:
       return "result.rgb = hardLight(src, dst);";
-    case BlendModeSoftLight:
+    case BLEND_MODE_SOFT_LIGHT:
       return "if (0.0 == dst.a) {"
              "  result.rgb = src.rgb;"
              "} else {"
@@ -969,15 +969,15 @@
              "  result.g = getSoftLightComponent(src.g, src.a, dst.g, dst.a);"
              "  result.b = getSoftLightComponent(src.b, src.a, dst.b, dst.a);"
              "}";
-    case BlendModeDifference:
+    case BLEND_MODE_DIFFERENCE:
       return "result.rgb = src.rgb + dst.rgb -"
              "    2.0 * min(src.rgb * dst.a, dst.rgb * src.a);";
-    case BlendModeExclusion:
+    case BLEND_MODE_EXCLUSION:
       return "result.rgb = dst.rgb + src.rgb - 2.0 * dst.rgb * src.rgb;";
-    case BlendModeMultiply:
+    case BLEND_MODE_MULTIPLY:
       return "result.rgb = (1.0 - src.a) * dst.rgb +"
              "    (1.0 - dst.a) * src.rgb + src.rgb * dst.rgb;";
-    case BlendModeHue:
+    case BLEND_MODE_HUE:
       return "vec4 dstSrcAlpha = dst * src.a;"
              "result.rgb ="
              "    set_luminance(set_saturation(src.rgb * dst.a,"
@@ -985,27 +985,26 @@
              "                  dstSrcAlpha.a,"
              "                  dstSrcAlpha.rgb);"
              "result.rgb += (1.0 - src.a) * dst.rgb + (1.0 - dst.a) * src.rgb;";
-    case BlendModeSaturation:
+    case BLEND_MODE_SATURATION:
       return "vec4 dstSrcAlpha = dst * src.a;"
              "result.rgb = set_luminance(set_saturation(dstSrcAlpha.rgb,"
              "                                          src.rgb * dst.a),"
              "                           dstSrcAlpha.a,"
              "                           dstSrcAlpha.rgb);"
              "result.rgb += (1.0 - src.a) * dst.rgb + (1.0 - dst.a) * src.rgb;";
-    case BlendModeColor:
+    case BLEND_MODE_COLOR:
       return "vec4 srcDstAlpha = src * dst.a;"
              "result.rgb = set_luminance(srcDstAlpha.rgb,"
              "                           srcDstAlpha.a,"
              "                           dst.rgb * src.a);"
              "result.rgb += (1.0 - src.a) * dst.rgb + (1.0 - dst.a) * src.rgb;";
-    case BlendModeLuminosity:
+    case BLEND_MODE_LUMINOSITY:
       return "vec4 srcDstAlpha = src * dst.a;"
              "result.rgb = set_luminance(dst.rgb * src.a,"
              "                           srcDstAlpha.a,"
              "                           srcDstAlpha.rgb);"
              "result.rgb += (1.0 - src.a) * dst.rgb + (1.0 - dst.a) * src.rgb;";
-    case BlendModeNone:
-    case NumBlendModes:
+    case BLEND_MODE_NONE:
       NOTREACHED();
   }
   return "result = vec4(1.0, 0.0, 0.0, 1.0);";
diff --git a/cc/output/shader.h b/cc/output/shader.h
index 3c8102e..93407d4 100644
--- a/cc/output/shader.h
+++ b/cc/output/shader.h
@@ -24,39 +24,39 @@
 namespace cc {
 
 enum TexCoordPrecision {
-  TexCoordPrecisionNA = 0,
-  TexCoordPrecisionMedium = 1,
-  TexCoordPrecisionHigh = 2,
-  NumTexCoordPrecisions = 3
+  TEX_COORD_PRECISION_NA = 0,
+  TEX_COORD_PRECISION_MEDIUM = 1,
+  TEX_COORD_PRECISION_HIGH = 2,
+  LAST_TEX_COORD_PRECISION = 2
 };
 
 enum SamplerType {
-  SamplerTypeNA = 0,
-  SamplerType2D = 1,
-  SamplerType2DRect = 2,
-  SamplerTypeExternalOES = 3,
-  NumSamplerTypes = 4
+  SAMPLER_TYPE_NA = 0,
+  SAMPLER_TYPE_2D = 1,
+  SAMPLER_TYPE_2D_RECT = 2,
+  SAMPLER_TYPE_EXTERNAL_OES = 3,
+  LAST_SAMPLER_TYPE = 3
 };
 
 enum BlendMode {
-  BlendModeNone,
-  BlendModeNormal,
-  BlendModeScreen,
-  BlendModeOverlay,
-  BlendModeDarken,
-  BlendModeLighten,
-  BlendModeColorDodge,
-  BlendModeColorBurn,
-  BlendModeHardLight,
-  BlendModeSoftLight,
-  BlendModeDifference,
-  BlendModeExclusion,
-  BlendModeMultiply,
-  BlendModeHue,
-  BlendModeSaturation,
-  BlendModeColor,
-  BlendModeLuminosity,
-  NumBlendModes
+  BLEND_MODE_NONE,
+  BLEND_MODE_NORMAL,
+  BLEND_MODE_SCREEN,
+  BLEND_MODE_OVERLAY,
+  BLEND_MODE_DARKEN,
+  BLEND_MODE_LIGHTEN,
+  BLEND_MODE_COLOR_DODGE,
+  BLEND_MODE_COLOR_BURN,
+  BLEND_MODE_HARD_LIGHT,
+  BLEND_MODE_SOFT_LIGHT,
+  BLEND_MODE_DIFFERENCE,
+  BLEND_MODE_EXCLUSION,
+  BLEND_MODE_MULTIPLY,
+  BLEND_MODE_HUE,
+  BLEND_MODE_SATURATION,
+  BLEND_MODE_COLOR,
+  BLEND_MODE_LUMINOSITY,
+  LAST_BLEND_MODE = BLEND_MODE_LUMINOSITY
 };
 
 // Note: The highp_threshold_cache must be provided by the caller to make
@@ -329,7 +329,7 @@
 
   BlendMode blend_mode() const { return blend_mode_; }
   void set_blend_mode(BlendMode blend_mode) { blend_mode_ = blend_mode; }
-  bool has_blend_mode() const { return blend_mode_ != BlendModeNone; }
+  bool has_blend_mode() const { return blend_mode_ != BLEND_MODE_NONE; }
 
  protected:
   FragmentTexBlendMode();
diff --git a/cc/output/shader_unittest.cc b/cc/output/shader_unittest.cc
index 4fd696f..675e47fb 100644
--- a/cc/output/shader_unittest.cc
+++ b/cc/output/shader_unittest.cc
@@ -27,24 +27,32 @@
   gfx::Size bigSize(2560, 2560);
 
   threshold_min = 0;
-  EXPECT_EQ(TexCoordPrecisionMedium, TexCoordPrecisionRequired(
-      &stub_gl, &threshold_cache, threshold_min, closePoint));
-  EXPECT_EQ(TexCoordPrecisionMedium, TexCoordPrecisionRequired(
-      &stub_gl, &threshold_cache, threshold_min, smallSize));
-  EXPECT_EQ(TexCoordPrecisionHigh, TexCoordPrecisionRequired(
-      &stub_gl, &threshold_cache, threshold_min, farPoint));
-  EXPECT_EQ(TexCoordPrecisionHigh, TexCoordPrecisionRequired(
-      &stub_gl, &threshold_cache, threshold_min, bigSize));
+  EXPECT_EQ(TEX_COORD_PRECISION_MEDIUM,
+            TexCoordPrecisionRequired(&stub_gl, &threshold_cache, threshold_min,
+                                      closePoint));
+  EXPECT_EQ(TEX_COORD_PRECISION_MEDIUM,
+            TexCoordPrecisionRequired(&stub_gl, &threshold_cache, threshold_min,
+                                      smallSize));
+  EXPECT_EQ(TEX_COORD_PRECISION_HIGH,
+            TexCoordPrecisionRequired(&stub_gl, &threshold_cache, threshold_min,
+                                      farPoint));
+  EXPECT_EQ(TEX_COORD_PRECISION_HIGH,
+            TexCoordPrecisionRequired(&stub_gl, &threshold_cache, threshold_min,
+                                      bigSize));
 
   threshold_min = 3000;
-  EXPECT_EQ(TexCoordPrecisionMedium, TexCoordPrecisionRequired(
-      &stub_gl, &threshold_cache, threshold_min, closePoint));
-  EXPECT_EQ(TexCoordPrecisionMedium, TexCoordPrecisionRequired(
-      &stub_gl, &threshold_cache, threshold_min, smallSize));
-  EXPECT_EQ(TexCoordPrecisionMedium, TexCoordPrecisionRequired(
-      &stub_gl, &threshold_cache, threshold_min, farPoint));
-  EXPECT_EQ(TexCoordPrecisionMedium, TexCoordPrecisionRequired(
-      &stub_gl, &threshold_cache, threshold_min, bigSize));
+  EXPECT_EQ(TEX_COORD_PRECISION_MEDIUM,
+            TexCoordPrecisionRequired(&stub_gl, &threshold_cache, threshold_min,
+                                      closePoint));
+  EXPECT_EQ(TEX_COORD_PRECISION_MEDIUM,
+            TexCoordPrecisionRequired(&stub_gl, &threshold_cache, threshold_min,
+                                      smallSize));
+  EXPECT_EQ(TEX_COORD_PRECISION_MEDIUM,
+            TexCoordPrecisionRequired(&stub_gl, &threshold_cache, threshold_min,
+                                      farPoint));
+  EXPECT_EQ(TEX_COORD_PRECISION_MEDIUM,
+            TexCoordPrecisionRequired(&stub_gl, &threshold_cache, threshold_min,
+                                      bigSize));
 }
 
 }  // namespace cc
diff --git a/cc/output/software_renderer.cc b/cc/output/software_renderer.cc
index 488ca09..5360d594bf 100644
--- a/cc/output/software_renderer.cc
+++ b/cc/output/software_renderer.cc
@@ -223,11 +223,11 @@
 bool SoftwareRenderer::IsSoftwareResource(
     ResourceProvider::ResourceId resource_id) const {
   switch (resource_provider_->GetResourceType(resource_id)) {
-    case ResourceProvider::GLTexture:
+    case ResourceProvider::RESOURCE_TYPE_GL_TEXTURE:
       return false;
-    case ResourceProvider::Bitmap:
+    case ResourceProvider::RESOURCE_TYPE_BITMAP:
       return true;
-    case ResourceProvider::InvalidType:
+    case ResourceProvider::RESOURCE_TYPE_INVALID:
       break;
   }
 
diff --git a/cc/output/software_renderer_unittest.cc b/cc/output/software_renderer_unittest.cc
index fa25200..3b2c4bc 100644
--- a/cc/output/software_renderer_unittest.cc
+++ b/cc/output/software_renderer_unittest.cc
@@ -155,16 +155,12 @@
 
   ResourceProvider::ResourceId resource_yellow =
       resource_provider()->CreateResource(
-          outer_size,
-          GL_CLAMP_TO_EDGE,
-          ResourceProvider::TextureHintImmutable,
-          RGBA_8888);
+          outer_size, GL_CLAMP_TO_EDGE,
+          ResourceProvider::TEXTURE_HINT_IMMUTABLE, RGBA_8888);
   ResourceProvider::ResourceId resource_cyan =
       resource_provider()->CreateResource(
-          inner_size,
-          GL_CLAMP_TO_EDGE,
-          ResourceProvider::TextureHintImmutable,
-          RGBA_8888);
+          inner_size, GL_CLAMP_TO_EDGE,
+          ResourceProvider::TEXTURE_HINT_IMMUTABLE, RGBA_8888);
 
   SkBitmap yellow_tile;
   yellow_tile.allocN32Pixels(outer_size.width(), outer_size.height());
@@ -174,17 +170,11 @@
   cyan_tile.allocN32Pixels(inner_size.width(), inner_size.height());
   cyan_tile.eraseColor(SK_ColorCYAN);
 
-  resource_provider()->SetPixels(
-      resource_yellow,
-      static_cast<uint8_t*>(yellow_tile.getPixels()),
-      gfx::Rect(outer_size),
-      gfx::Rect(outer_size),
-      gfx::Vector2d());
-  resource_provider()->SetPixels(resource_cyan,
-                                 static_cast<uint8_t*>(cyan_tile.getPixels()),
-                                 gfx::Rect(inner_size),
-                                 gfx::Rect(inner_size),
-                                 gfx::Vector2d());
+  resource_provider()->CopyToResource(
+      resource_yellow, static_cast<uint8_t*>(yellow_tile.getPixels()),
+      outer_size);
+  resource_provider()->CopyToResource(
+      resource_cyan, static_cast<uint8_t*>(cyan_tile.getPixels()), inner_size);
 
   gfx::Rect root_rect = outer_rect;
 
@@ -252,9 +242,7 @@
 
   ResourceProvider::ResourceId resource_cyan =
       resource_provider()->CreateResource(
-          tile_size,
-          GL_CLAMP_TO_EDGE,
-          ResourceProvider::TextureHintImmutable,
+          tile_size, GL_CLAMP_TO_EDGE, ResourceProvider::TEXTURE_HINT_IMMUTABLE,
           RGBA_8888);
 
   SkBitmap cyan_tile;  // The lowest five rows are yellow.
@@ -265,11 +253,8 @@
           0, visible_rect.bottom() - 1, tile_rect.width(), tile_rect.bottom()),
       SK_ColorYELLOW);
 
-  resource_provider()->SetPixels(resource_cyan,
-                                 static_cast<uint8_t*>(cyan_tile.getPixels()),
-                                 gfx::Rect(tile_size),
-                                 gfx::Rect(tile_size),
-                                 gfx::Vector2d());
+  resource_provider()->CopyToResource(
+      resource_cyan, static_cast<uint8_t*>(cyan_tile.getPixels()), tile_size);
 
   gfx::Rect root_rect(tile_size);
 
diff --git a/cc/quads/draw_polygon.cc b/cc/quads/draw_polygon.cc
index 71dbf36..026be2c 100644
--- a/cc/quads/draw_polygon.cc
+++ b/cc/quads/draw_polygon.cc
@@ -93,7 +93,7 @@
 
 // Checks whether or not shape a lies on the front or back side of b, or
 // whether they should be considered coplanar. If on the back side, we
-// say ABeforeB because it should be drawn in that order.
+// say A_BEFORE_B because it should be drawn in that order.
 // Assumes that layers are split and there are no intersecting planes.
 BspCompareResult DrawPolygon::SideCompare(const DrawPolygon& a,
                                           const DrawPolygon& b) {
diff --git a/cc/quads/draw_quad_unittest.cc b/cc/quads/draw_quad_unittest.cc
index 6b51398a2..b7c1bdfe 100644
--- a/cc/quads/draw_quad_unittest.cc
+++ b/cc/quads/draw_quad_unittest.cc
@@ -643,7 +643,7 @@
   ResourceProvider::ResourceId u_plane_resource_id = 532;
   ResourceProvider::ResourceId v_plane_resource_id = 4;
   ResourceProvider::ResourceId a_plane_resource_id = 63;
-  YUVVideoDrawQuad::ColorSpace color_space = YUVVideoDrawQuad::REC_601_JPEG;
+  YUVVideoDrawQuad::ColorSpace color_space = YUVVideoDrawQuad::JPEG;
   CREATE_SHARED_STATE();
 
   CREATE_QUAD_9_NEW(YUVVideoDrawQuad, opaque_rect, visible_rect, tex_coord_rect,
@@ -899,7 +899,7 @@
   ResourceProvider::ResourceId u_plane_resource_id = 532;
   ResourceProvider::ResourceId v_plane_resource_id = 4;
   ResourceProvider::ResourceId a_plane_resource_id = 63;
-  YUVVideoDrawQuad::ColorSpace color_space = YUVVideoDrawQuad::REC_601_JPEG;
+  YUVVideoDrawQuad::ColorSpace color_space = YUVVideoDrawQuad::JPEG;
 
   CREATE_SHARED_STATE();
   CREATE_QUAD_9_NEW(YUVVideoDrawQuad, opaque_rect, visible_rect, tex_coord_rect,
diff --git a/cc/quads/yuv_video_draw_quad.h b/cc/quads/yuv_video_draw_quad.h
index 99ed7e23..358929e 100644
--- a/cc/quads/yuv_video_draw_quad.h
+++ b/cc/quads/yuv_video_draw_quad.h
@@ -16,9 +16,10 @@
 class CC_EXPORT YUVVideoDrawQuad : public DrawQuad {
  public:
   enum ColorSpace {
-    REC_601,       // SDTV standard with restricted "studio swing" color range.
-    REC_601_JPEG,  // Full color range [0, 255] variant of the above.
-    COLOR_SPACE_LAST = REC_601_JPEG
+    REC_601,  // SDTV standard with restricted "studio swing" color range.
+    REC_709,  // HDTV standard with restricted "studio swing" color range.
+    JPEG,     // Full color range [0, 255] JPEG color space.
+    COLOR_SPACE_LAST = JPEG
   };
 
   ~YUVVideoDrawQuad() override;
diff --git a/cc/resources/display_item_list_unittest.cc b/cc/resources/display_item_list_unittest.cc
index ddf2cb70..c9b9558 100644
--- a/cc/resources/display_item_list_unittest.cc
+++ b/cc/resources/display_item_list_unittest.cc
@@ -37,13 +37,15 @@
   unsigned char pixels[4 * 100 * 100] = {0};
   scoped_refptr<DisplayItemList> list = DisplayItemList::Create();
 
+  gfx::PointF offset(8.f, 9.f);
+  gfx::RectF recording_rect(offset, layer_rect.size());
   canvas = skia::SharePtr(
-      recorder.beginRecording(layer_rect.width(), layer_rect.height()));
+      recorder.beginRecording(gfx::RectFToSkRect(recording_rect)));
+  canvas->translate(offset.x(), offset.y());
   canvas->drawRectCoords(0.f, 0.f, 60.f, 60.f, red_paint);
   canvas->drawRectCoords(50.f, 50.f, 75.f, 75.f, blue_paint);
   picture = skia::AdoptRef(recorder.endRecording());
-  gfx::PointF offset(8.f, 9.f);
-  list->AppendItem(DrawingDisplayItem::Create(picture, offset));
+  list->AppendItem(DrawingDisplayItem::Create(picture));
   DrawDisplayList(pixels, layer_rect, list);
 
   SkBitmap expected_bitmap;
@@ -75,22 +77,26 @@
   unsigned char pixels[4 * 100 * 100] = {0};
   scoped_refptr<DisplayItemList> list = DisplayItemList::Create();
 
+  gfx::PointF first_offset(8.f, 9.f);
+  gfx::RectF first_recording_rect(first_offset, layer_rect.size());
   canvas = skia::SharePtr(
-      recorder.beginRecording(layer_rect.width(), layer_rect.height()));
+      recorder.beginRecording(gfx::RectFToSkRect(first_recording_rect)));
+  canvas->translate(first_offset.x(), first_offset.y());
   canvas->drawRectCoords(0.f, 0.f, 60.f, 60.f, red_paint);
   picture = skia::AdoptRef(recorder.endRecording());
-  gfx::PointF first_offset(8.f, 9.f);
-  list->AppendItem(DrawingDisplayItem::Create(picture, first_offset));
+  list->AppendItem(DrawingDisplayItem::Create(picture));
 
   gfx::Rect clip_rect(60, 60, 10, 10);
   list->AppendItem(ClipDisplayItem::Create(clip_rect, std::vector<SkRRect>()));
 
+  gfx::PointF second_offset(2.f, 3.f);
+  gfx::RectF second_recording_rect(second_offset, layer_rect.size());
   canvas = skia::SharePtr(
-      recorder.beginRecording(layer_rect.width(), layer_rect.height()));
+      recorder.beginRecording(gfx::RectFToSkRect(second_recording_rect)));
+  canvas->translate(second_offset.x(), second_offset.y());
   canvas->drawRectCoords(50.f, 50.f, 75.f, 75.f, blue_paint);
   picture = skia::AdoptRef(recorder.endRecording());
-  gfx::PointF second_offset(2.f, 3.f);
-  list->AppendItem(DrawingDisplayItem::Create(picture, second_offset));
+  list->AppendItem(DrawingDisplayItem::Create(picture));
 
   list->AppendItem(EndClipDisplayItem::Create());
 
@@ -126,23 +132,27 @@
   unsigned char pixels[4 * 100 * 100] = {0};
   scoped_refptr<DisplayItemList> list = DisplayItemList::Create();
 
+  gfx::PointF first_offset(8.f, 9.f);
+  gfx::RectF first_recording_rect(first_offset, layer_rect.size());
   canvas = skia::SharePtr(
-      recorder.beginRecording(layer_rect.width(), layer_rect.height()));
+      recorder.beginRecording(gfx::RectFToSkRect(first_recording_rect)));
+  canvas->translate(first_offset.x(), first_offset.y());
   canvas->drawRectCoords(0.f, 0.f, 60.f, 60.f, red_paint);
   picture = skia::AdoptRef(recorder.endRecording());
-  gfx::PointF first_offset(8.f, 9.f);
-  list->AppendItem(DrawingDisplayItem::Create(picture, first_offset));
+  list->AppendItem(DrawingDisplayItem::Create(picture));
 
   gfx::Transform transform;
   transform.Rotate(45.0);
   list->AppendItem(TransformDisplayItem::Create(transform));
 
+  gfx::PointF second_offset(2.f, 3.f);
+  gfx::RectF second_recording_rect(second_offset, layer_rect.size());
   canvas = skia::SharePtr(
-      recorder.beginRecording(layer_rect.width(), layer_rect.height()));
+      recorder.beginRecording(gfx::RectFToSkRect(second_recording_rect)));
+  canvas->translate(second_offset.x(), second_offset.y());
   canvas->drawRectCoords(50.f, 50.f, 75.f, 75.f, blue_paint);
   picture = skia::AdoptRef(recorder.endRecording());
-  gfx::PointF second_offset(2.f, 3.f);
-  list->AppendItem(DrawingDisplayItem::Create(picture, second_offset));
+  list->AppendItem(DrawingDisplayItem::Create(picture));
 
   list->AppendItem(EndTransformDisplayItem::Create());
 
diff --git a/cc/resources/display_list_raster_source.cc b/cc/resources/display_list_raster_source.cc
index 4479a16..ab0292e 100644
--- a/cc/resources/display_list_raster_source.cc
+++ b/cc/resources/display_list_raster_source.cc
@@ -26,14 +26,12 @@
 
 namespace cc {
 
-scoped_refptr<DisplayListRasterSource> DisplayListRasterSource::Create() {
-  return make_scoped_refptr(new DisplayListRasterSource);
-}
-
 scoped_refptr<DisplayListRasterSource>
 DisplayListRasterSource::CreateFromDisplayListRecordingSource(
-    const DisplayListRecordingSource* other) {
-  return make_scoped_refptr(new DisplayListRasterSource(other));
+    const DisplayListRecordingSource* other,
+    bool can_use_lcd_text) {
+  return make_scoped_refptr(
+      new DisplayListRasterSource(other, can_use_lcd_text));
 }
 
 DisplayListRasterSource::DisplayListRasterSource()
@@ -48,11 +46,12 @@
 }
 
 DisplayListRasterSource::DisplayListRasterSource(
-    const DisplayListRecordingSource* other)
+    const DisplayListRecordingSource* other,
+    bool can_use_lcd_text)
     : display_list_(other->display_list_),
       background_color_(other->background_color_),
       requires_clear_(other->requires_clear_),
-      can_use_lcd_text_(other->can_use_lcd_text_),
+      can_use_lcd_text_(can_use_lcd_text),
       is_solid_color_(other->is_solid_color_),
       solid_color_(other->solid_color_),
       recorded_viewport_(other->recorded_viewport_),
@@ -63,6 +62,24 @@
       should_attempt_to_use_distance_field_text_(false) {
 }
 
+DisplayListRasterSource::DisplayListRasterSource(
+    const DisplayListRasterSource* other,
+    bool can_use_lcd_text)
+    : display_list_(other->display_list_),
+      background_color_(other->background_color_),
+      requires_clear_(other->requires_clear_),
+      can_use_lcd_text_(can_use_lcd_text),
+      is_solid_color_(other->is_solid_color_),
+      solid_color_(other->solid_color_),
+      recorded_viewport_(other->recorded_viewport_),
+      size_(other->size_),
+      clear_canvas_with_debug_color_(kDefaultClearCanvasSetting),
+      slow_down_raster_scale_factor_for_debug_(
+          other->slow_down_raster_scale_factor_for_debug_),
+      should_attempt_to_use_distance_field_text_(
+          other->should_attempt_to_use_distance_field_text_) {
+}
+
 DisplayListRasterSource::~DisplayListRasterSource() {
 }
 
@@ -197,4 +214,11 @@
   return can_use_lcd_text_;
 }
 
+scoped_refptr<RasterSource> DisplayListRasterSource::CreateCloneWithoutLCDText()
+    const {
+  bool can_use_lcd_text = false;
+  return scoped_refptr<RasterSource>(
+      new DisplayListRasterSource(this, can_use_lcd_text));
+}
+
 }  // namespace cc
diff --git a/cc/resources/display_list_raster_source.h b/cc/resources/display_list_raster_source.h
index f095231..44177fba 100644
--- a/cc/resources/display_list_raster_source.h
+++ b/cc/resources/display_list_raster_source.h
@@ -21,9 +21,9 @@
 
 class CC_EXPORT DisplayListRasterSource : public RasterSource {
  public:
-  static scoped_refptr<DisplayListRasterSource> Create();
   static scoped_refptr<DisplayListRasterSource>
-  CreateFromDisplayListRecordingSource(const DisplayListRecordingSource* other);
+  CreateFromDisplayListRecordingSource(const DisplayListRecordingSource* other,
+                                       bool can_use_lcd_text);
 
   // RasterSource overrides.
   void PlaybackToCanvas(SkCanvas* canvas,
@@ -52,10 +52,14 @@
   skia::RefPtr<SkPicture> GetFlattenedPicture() override;
   size_t GetPictureMemoryUsage() const override;
   bool CanUseLCDText() const override;
+  scoped_refptr<RasterSource> CreateCloneWithoutLCDText() const override;
 
  protected:
   DisplayListRasterSource();
-  explicit DisplayListRasterSource(const DisplayListRecordingSource* other);
+  DisplayListRasterSource(const DisplayListRecordingSource* other,
+                          bool can_use_lcd_text);
+  DisplayListRasterSource(const DisplayListRasterSource* other,
+                          bool can_use_lcd_text);
   ~DisplayListRasterSource() override;
 
   // These members are const as this raster source may be in use on another
diff --git a/cc/resources/display_list_recording_source.cc b/cc/resources/display_list_recording_source.cc
index 01a3bce..517f49f 100644
--- a/cc/resources/display_list_recording_source.cc
+++ b/cc/resources/display_list_recording_source.cc
@@ -28,7 +28,6 @@
 
 DisplayListRecordingSource::DisplayListRecordingSource()
     : slow_down_raster_scale_factor_for_debug_(0),
-      can_use_lcd_text_(true),
       requires_clear_(false),
       is_solid_color_(false),
       solid_color_(SK_ColorTRANSPARENT),
@@ -43,7 +42,6 @@
 bool DisplayListRecordingSource::UpdateAndExpandInvalidation(
     ContentLayerClient* painter,
     Region* invalidation,
-    bool can_use_lcd_text,
     const gfx::Size& layer_size,
     const gfx::Rect& visible_layer_rect,
     int frame_number,
@@ -55,12 +53,6 @@
     updated = true;
   }
 
-  if (can_use_lcd_text_ != can_use_lcd_text) {
-    can_use_lcd_text_ = can_use_lcd_text;
-    invalidation->Union(gfx::Rect(GetSize()));
-    updated = true;
-  }
-
   gfx::Rect old_recorded_viewport = recorded_viewport_;
   recorded_viewport_ = visible_layer_rect;
   recorded_viewport_.Inset(-pixel_record_distance_, -pixel_record_distance_);
@@ -152,10 +144,11 @@
   return is_suitable_for_gpu_rasterization_;
 }
 
-scoped_refptr<RasterSource> DisplayListRecordingSource::CreateRasterSource()
-    const {
+scoped_refptr<RasterSource> DisplayListRecordingSource::CreateRasterSource(
+    bool can_use_lcd_text) const {
   return scoped_refptr<RasterSource>(
-      DisplayListRasterSource::CreateFromDisplayListRecordingSource(this));
+      DisplayListRasterSource::CreateFromDisplayListRecordingSource(
+          this, can_use_lcd_text));
 }
 
 gfx::Size DisplayListRecordingSource::GetTileGridSizeForTesting() const {
diff --git a/cc/resources/display_list_recording_source.h b/cc/resources/display_list_recording_source.h
index 21a24fb..1e713e1 100644
--- a/cc/resources/display_list_recording_source.h
+++ b/cc/resources/display_list_recording_source.h
@@ -20,12 +20,12 @@
   // RecordingSource overrides.
   bool UpdateAndExpandInvalidation(ContentLayerClient* painter,
                                    Region* invalidation,
-                                   bool can_use_lcd_text,
                                    const gfx::Size& layer_size,
                                    const gfx::Rect& visible_layer_rect,
                                    int frame_number,
                                    RecordingMode recording_mode) override;
-  scoped_refptr<RasterSource> CreateRasterSource() const override;
+  scoped_refptr<RasterSource> CreateRasterSource(
+      bool can_use_lcd_text) const override;
   gfx::Size GetSize() const final;
   void SetEmptyBounds() override;
   void SetSlowdownRasterScaleFactor(int factor) override;
@@ -41,7 +41,6 @@
   gfx::Rect recorded_viewport_;
   gfx::Size size_;
   int slow_down_raster_scale_factor_for_debug_;
-  bool can_use_lcd_text_;
   bool requires_clear_;
   bool is_solid_color_;
   SkColor solid_color_;
diff --git a/cc/resources/drawing_display_item.cc b/cc/resources/drawing_display_item.cc
index 0fe86f4..648f9de 100644
--- a/cc/resources/drawing_display_item.cc
+++ b/cc/resources/drawing_display_item.cc
@@ -17,9 +17,8 @@
 
 namespace cc {
 
-DrawingDisplayItem::DrawingDisplayItem(skia::RefPtr<SkPicture> picture,
-                                       gfx::PointF location)
-    : picture_(picture), location_(location) {
+DrawingDisplayItem::DrawingDisplayItem(skia::RefPtr<SkPicture> picture)
+    : picture_(picture) {
 }
 
 DrawingDisplayItem::~DrawingDisplayItem() {
@@ -28,7 +27,6 @@
 void DrawingDisplayItem::Raster(SkCanvas* canvas,
                                 SkDrawPictureCallback* callback) const {
   canvas->save();
-  canvas->translate(location_.x(), location_.y());
   if (callback)
     picture_->playback(canvas, callback);
   else
@@ -38,7 +36,6 @@
 
 void DrawingDisplayItem::RasterForTracing(SkCanvas* canvas) const {
   canvas->save();
-  canvas->translate(location_.x(), location_.y());
   // The picture debugger in about:tracing doesn't drill down into |drawPicture|
   // operations. Calling |playback()| rather than |drawPicture()| causes the
   // skia operations in |picture_| to appear individually in the picture
@@ -53,7 +50,7 @@
 }
 
 int DrawingDisplayItem::ApproximateOpCount() const {
-  return picture_->approximateOpCount() + sizeof(gfx::PointF);
+  return picture_->approximateOpCount();
 }
 
 size_t DrawingDisplayItem::PictureMemoryUsage() const {
@@ -65,9 +62,11 @@
     base::trace_event::TracedValue* array) const {
   array->BeginDictionary();
   array->SetString("name", "DrawingDisplayItem");
-  array->SetString("location",
-                   base::StringPrintf("[%f,%f]", picture_->cullRect().x(),
-                                      picture_->cullRect().y()));
+  array->SetString(
+      "cullRect",
+      base::StringPrintf("[%f,%f,%f,%f]", picture_->cullRect().x(),
+                         picture_->cullRect().y(), picture_->cullRect().width(),
+                         picture_->cullRect().height()));
   std::string b64_picture;
   PictureDebugUtil::SerializeAsBase64(picture_.get(), &b64_picture);
   array->SetString("skp64", b64_picture);
diff --git a/cc/resources/drawing_display_item.h b/cc/resources/drawing_display_item.h
index 1f63ed34..b45a0392 100644
--- a/cc/resources/drawing_display_item.h
+++ b/cc/resources/drawing_display_item.h
@@ -21,9 +21,9 @@
  public:
   ~DrawingDisplayItem() override;
 
-  static scoped_ptr<DrawingDisplayItem> Create(skia::RefPtr<SkPicture> picture,
-                                               gfx::PointF location) {
-    return make_scoped_ptr(new DrawingDisplayItem(picture, location));
+  static scoped_ptr<DrawingDisplayItem> Create(
+      skia::RefPtr<SkPicture> picture) {
+    return make_scoped_ptr(new DrawingDisplayItem(picture));
   }
 
   void Raster(SkCanvas* canvas, SkDrawPictureCallback* callback) const override;
@@ -35,11 +35,10 @@
   void AsValueInto(base::trace_event::TracedValue* array) const override;
 
  protected:
-  DrawingDisplayItem(skia::RefPtr<SkPicture> picture, gfx::PointF location);
+  explicit DrawingDisplayItem(skia::RefPtr<SkPicture> picture);
 
  private:
   skia::RefPtr<SkPicture> picture_;
-  gfx::PointF location_;
 };
 
 }  // namespace cc
diff --git a/cc/resources/gpu_rasterizer.cc b/cc/resources/gpu_rasterizer.cc
index e0097f87..6e318eda 100644
--- a/cc/resources/gpu_rasterizer.cc
+++ b/cc/resources/gpu_rasterizer.cc
@@ -123,7 +123,7 @@
     SkMultiPictureDraw multi_picture_draw;
     multi_picture_draw.add(write_lock->sk_surface()->getCanvas(),
                            picture.get());
-    multi_picture_draw.draw(false);
+    multi_picture_draw.draw(msaa_sample_count_ > 0);
     write_lock->ReleaseSkSurface();
   }
 }
diff --git a/cc/resources/one_copy_tile_task_worker_pool.cc b/cc/resources/one_copy_tile_task_worker_pool.cc
index cee568a..e6a367a 100644
--- a/cc/resources/one_copy_tile_task_worker_pool.cc
+++ b/cc/resources/one_copy_tile_task_worker_pool.cc
@@ -167,6 +167,13 @@
 void OneCopyTileTaskWorkerPool::ScheduleTasks(TileTaskQueue* queue) {
   TRACE_EVENT0("cc", "OneCopyTileTaskWorkerPool::ScheduleTasks");
 
+#if DCHECK_IS_ON()
+  {
+    base::AutoLock lock(lock_);
+    DCHECK(!shutdown_);
+  }
+#endif
+
   if (tasks_pending_.none())
     TRACE_EVENT_ASYNC_BEGIN0("cc", "ScheduledTasks", this);
 
diff --git a/cc/resources/picture_pile.cc b/cc/resources/picture_pile.cc
index 7e94e18..2b15aea 100644
--- a/cc/resources/picture_pile.cc
+++ b/cc/resources/picture_pile.cc
@@ -166,7 +166,6 @@
                          const gfx::Size& tile_grid_size)
     : min_contents_scale_(0),
       slow_down_raster_scale_factor_for_debug_(0),
-      can_use_lcd_text_(true),
       has_any_recordings_(false),
       clear_canvas_with_debug_color_(kDefaultClearCanvasSetting),
       requires_clear_(true),
@@ -186,22 +185,17 @@
 bool PicturePile::UpdateAndExpandInvalidation(
     ContentLayerClient* painter,
     Region* invalidation,
-    bool can_use_lcd_text,
     const gfx::Size& layer_size,
     const gfx::Rect& visible_layer_rect,
     int frame_number,
     RecordingSource::RecordingMode recording_mode) {
-  bool can_use_lcd_text_changed = can_use_lcd_text_ != can_use_lcd_text;
-  can_use_lcd_text_ = can_use_lcd_text;
-
   gfx::Rect interest_rect = visible_layer_rect;
   interest_rect.Inset(-pixel_record_distance_, -pixel_record_distance_);
   recorded_viewport_ = interest_rect;
   recorded_viewport_.Intersect(gfx::Rect(layer_size));
 
-  bool updated =
-      ApplyInvalidationAndResize(interest_rect, invalidation, layer_size,
-                                 frame_number, can_use_lcd_text_changed);
+  bool updated = ApplyInvalidationAndResize(interest_rect, invalidation,
+                                            layer_size, frame_number);
   std::vector<gfx::Rect> invalid_tiles;
   GetInvalidTileRects(interest_rect, invalidation, visible_layer_rect,
                       frame_number, &invalid_tiles);
@@ -223,8 +217,7 @@
 bool PicturePile::ApplyInvalidationAndResize(const gfx::Rect& interest_rect,
                                              Region* invalidation,
                                              const gfx::Size& layer_size,
-                                             int frame_number,
-                                             bool can_use_lcd_text_changed) {
+                                             int frame_number) {
   bool updated = false;
 
   Region synthetic_invalidation;
@@ -233,14 +226,6 @@
     tiling_.SetTilingSize(layer_size);
     updated = true;
   }
-  if (can_use_lcd_text_changed) {
-    // When LCD text is enabled/disabled, we must drop any raster tiles for
-    // the pile, so they can be recreated in a manner consistent with the new
-    // setting. We do this with |synthetic_invalidation| since we don't need to
-    // do a new recording, just invalidate rastered content.
-    synthetic_invalidation.Union(gfx::Rect(GetSize()));
-    updated = true;
-  }
 
   gfx::Rect interest_rect_over_tiles =
       tiling_.ExpandRectToTileBounds(interest_rect);
@@ -593,9 +578,10 @@
   }
 }
 
-scoped_refptr<RasterSource> PicturePile::CreateRasterSource() const {
+scoped_refptr<RasterSource> PicturePile::CreateRasterSource(
+    bool can_use_lcd_text) const {
   return scoped_refptr<RasterSource>(
-      PicturePileImpl::CreateFromPicturePile(this));
+      PicturePileImpl::CreateFromPicturePile(this, can_use_lcd_text));
 }
 
 gfx::Size PicturePile::GetSize() const {
diff --git a/cc/resources/picture_pile.h b/cc/resources/picture_pile.h
index 2a9a3ce6..bab06c8 100644
--- a/cc/resources/picture_pile.h
+++ b/cc/resources/picture_pile.h
@@ -25,12 +25,12 @@
   // RecordingSource overrides.
   bool UpdateAndExpandInvalidation(ContentLayerClient* painter,
                                    Region* invalidation,
-                                   bool can_use_lcd_text,
                                    const gfx::Size& layer_size,
                                    const gfx::Rect& visible_layer_rect,
                                    int frame_number,
                                    RecordingMode recording_mode) override;
-  scoped_refptr<RasterSource> CreateRasterSource() const override;
+  scoped_refptr<RasterSource> CreateRasterSource(
+      bool can_use_lcd_text) const override;
   gfx::Size GetSize() const final;
   void SetEmptyBounds() override;
   void SetSlowdownRasterScaleFactor(int factor) override;
@@ -95,7 +95,6 @@
   float min_contents_scale_;
   gfx::Size tile_grid_size_;
   int slow_down_raster_scale_factor_for_debug_;
-  bool can_use_lcd_text_;
   // A hint about whether there are any recordings. This may be a false
   // positive.
   bool has_any_recordings_;
@@ -120,8 +119,7 @@
   bool ApplyInvalidationAndResize(const gfx::Rect& interest_rect,
                                   Region* invalidation,
                                   const gfx::Size& layer_size,
-                                  int frame_number,
-                                  bool can_use_lcd_text_changed);
+                                  int frame_number);
   void DetermineIfSolidColor();
   void SetBufferPixels(int buffer_pixels);
 
diff --git a/cc/resources/picture_pile_impl.cc b/cc/resources/picture_pile_impl.cc
index d629888b87..a9bad90a 100644
--- a/cc/resources/picture_pile_impl.cc
+++ b/cc/resources/picture_pile_impl.cc
@@ -19,14 +19,15 @@
 namespace cc {
 
 scoped_refptr<PicturePileImpl> PicturePileImpl::CreateFromPicturePile(
-    const PicturePile* other) {
-  return make_scoped_refptr(new PicturePileImpl(other));
+    const PicturePile* other,
+    bool can_use_lcd_text) {
+  return make_scoped_refptr(new PicturePileImpl(other, can_use_lcd_text));
 }
 
 PicturePileImpl::PicturePileImpl()
     : background_color_(SK_ColorTRANSPARENT),
       requires_clear_(true),
-      can_use_lcd_text_(false),
+      can_use_lcd_text_(true),
       is_solid_color_(false),
       solid_color_(SK_ColorTRANSPARENT),
       has_any_recordings_(false),
@@ -36,12 +37,13 @@
       should_attempt_to_use_distance_field_text_(false) {
 }
 
-PicturePileImpl::PicturePileImpl(const PicturePile* other)
+PicturePileImpl::PicturePileImpl(const PicturePile* other,
+                                 bool can_use_lcd_text)
     : picture_map_(other->picture_map_),
       tiling_(other->tiling_),
       background_color_(other->background_color_),
       requires_clear_(other->requires_clear_),
-      can_use_lcd_text_(other->can_use_lcd_text_),
+      can_use_lcd_text_(can_use_lcd_text),
       is_solid_color_(other->is_solid_color_),
       solid_color_(other->solid_color_),
       recorded_viewport_(other->recorded_viewport_),
@@ -53,6 +55,25 @@
       should_attempt_to_use_distance_field_text_(false) {
 }
 
+PicturePileImpl::PicturePileImpl(const PicturePileImpl* other,
+                                 bool can_use_lcd_text)
+    : picture_map_(other->picture_map_),
+      tiling_(other->tiling_),
+      background_color_(other->background_color_),
+      requires_clear_(other->requires_clear_),
+      can_use_lcd_text_(can_use_lcd_text),
+      is_solid_color_(other->is_solid_color_),
+      solid_color_(other->solid_color_),
+      recorded_viewport_(other->recorded_viewport_),
+      has_any_recordings_(other->has_any_recordings_),
+      clear_canvas_with_debug_color_(other->clear_canvas_with_debug_color_),
+      min_contents_scale_(other->min_contents_scale_),
+      slow_down_raster_scale_factor_for_debug_(
+          other->slow_down_raster_scale_factor_for_debug_),
+      should_attempt_to_use_distance_field_text_(
+          other->should_attempt_to_use_distance_field_text_) {
+}
+
 PicturePileImpl::~PicturePileImpl() {
 }
 
@@ -383,6 +404,13 @@
   return can_use_lcd_text_;
 }
 
+scoped_refptr<RasterSource> PicturePileImpl::CreateCloneWithoutLCDText() const {
+  DCHECK(CanUseLCDText());
+  bool can_use_lcd_text = false;
+  return scoped_refptr<RasterSource>(
+      new PicturePileImpl(this, can_use_lcd_text));
+}
+
 PicturePileImpl::PixelRefIterator::PixelRefIterator(
     const gfx::Rect& content_rect,
     float contents_scale,
diff --git a/cc/resources/picture_pile_impl.h b/cc/resources/picture_pile_impl.h
index 1f94ceca..c35dd0f 100644
--- a/cc/resources/picture_pile_impl.h
+++ b/cc/resources/picture_pile_impl.h
@@ -30,7 +30,8 @@
 class CC_EXPORT PicturePileImpl : public RasterSource {
  public:
   static scoped_refptr<PicturePileImpl> CreateFromPicturePile(
-      const PicturePile* other);
+      const PicturePile* other,
+      bool can_use_lcd_text);
 
   // RasterSource overrides. See RasterSource header for full description.
   // When slow-down-raster-scale-factor is set to a value greater than 1, the
@@ -58,6 +59,7 @@
   SkColor GetSolidColor() const override;
   bool HasRecordings() const override;
   bool CanUseLCDText() const override;
+  scoped_refptr<RasterSource> CreateCloneWithoutLCDText() const override;
 
   // Tracing functionality.
   void DidBeginTracing() override;
@@ -99,7 +101,8 @@
   using PictureInfo = PicturePile::PictureInfo;
 
   PicturePileImpl();
-  explicit PicturePileImpl(const PicturePile* other);
+  explicit PicturePileImpl(const PicturePile* other, bool can_use_lcd_text);
+  explicit PicturePileImpl(const PicturePileImpl* other, bool can_use_lcd_text);
   ~PicturePileImpl() override;
 
   int buffer_pixels() const { return tiling_.border_texels(); }
diff --git a/cc/resources/picture_pile_unittest.cc b/cc/resources/picture_pile_unittest.cc
index b834979..91ad7ce5 100644
--- a/cc/resources/picture_pile_unittest.cc
+++ b/cc/resources/picture_pile_unittest.cc
@@ -42,9 +42,9 @@
                                    const gfx::Size& layer_size,
                                    const gfx::Rect& visible_layer_rect) {
     frame_number_++;
-    return pile_.UpdateAndExpandInvalidation(
-        &client_, invalidation, false, layer_size, visible_layer_rect,
-        frame_number_, RecordingSource::RECORD_NORMALLY);
+    return pile_.UpdateAndExpandInvalidation(&client_, invalidation, layer_size,
+                                             visible_layer_rect, frame_number_,
+                                             RecordingSource::RECORD_NORMALLY);
   }
 
   bool UpdateWholePile() {
diff --git a/cc/resources/platform_color_unittest.cc b/cc/resources/platform_color_unittest.cc
new file mode 100644
index 0000000..83b2a1e
--- /dev/null
+++ b/cc/resources/platform_color_unittest.cc
@@ -0,0 +1,42 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cc/resources/platform_color.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace cc {
+namespace {
+
+// Verify SameComponentOrder on this platform.
+TEST(PlatformColorTest, SameComponentOrder) {
+  bool rgba = !!SK_B32_SHIFT;
+
+  for (size_t i = 0; i <= RESOURCE_FORMAT_MAX; ++i) {
+    ResourceFormat format = static_cast<ResourceFormat>(i);
+    switch (format) {
+      case RGBA_8888:
+        EXPECT_EQ(rgba, PlatformColor::SameComponentOrder(format));
+        break;
+      case RGBA_4444:
+        // RGBA_4444 indicates the number of bytes per pixel but the format
+        // doesn't actually imply RGBA ordering. It uses the native ordering.
+        EXPECT_EQ(true, PlatformColor::SameComponentOrder(format));
+        break;
+      case BGRA_8888:
+        EXPECT_NE(rgba, PlatformColor::SameComponentOrder(format));
+        break;
+      case ALPHA_8:
+      case LUMINANCE_8:
+      case RGB_565:
+      case ETC1:
+      case RED_8:
+        EXPECT_FALSE(PlatformColor::SameComponentOrder(format));
+        break;
+    }
+  }
+}
+
+}  // namespace
+}  // namespace cc
diff --git a/cc/resources/prioritized_resource_manager.cc b/cc/resources/prioritized_resource_manager.cc
index 48177e3..236377b 100644
--- a/cc/resources/prioritized_resource_manager.cc
+++ b/cc/resources/prioritized_resource_manager.cc
@@ -451,11 +451,8 @@
   DCHECK(resource_provider);
   ResourceProvider::ResourceId resource_id =
       resource_provider->CreateManagedResource(
-          size,
-          GL_TEXTURE_2D,
-          GL_CLAMP_TO_EDGE,
-          ResourceProvider::TextureHintImmutable,
-          format);
+          size, GL_TEXTURE_2D, GL_CLAMP_TO_EDGE,
+          ResourceProvider::TEXTURE_HINT_IMMUTABLE, format);
   PrioritizedResource::Backing* backing = new PrioritizedResource::Backing(
       resource_id, resource_provider, size, format);
   memory_use_bytes_ += backing->bytes();
diff --git a/cc/resources/raster_source.h b/cc/resources/raster_source.h
index 929e49d7a..171d2a55 100644
--- a/cc/resources/raster_source.h
+++ b/cc/resources/raster_source.h
@@ -99,6 +99,8 @@
   // Return true if LCD anti-aliasing may be used when rastering text.
   virtual bool CanUseLCDText() const = 0;
 
+  virtual scoped_refptr<RasterSource> CreateCloneWithoutLCDText() const = 0;
+
  protected:
   friend class base::RefCountedThreadSafe<RasterSource>;
 
diff --git a/cc/resources/recording_source.h b/cc/resources/recording_source.h
index 8915361..fdd8eb4 100644
--- a/cc/resources/recording_source.h
+++ b/cc/resources/recording_source.h
@@ -34,13 +34,13 @@
   // Return true iff the pile was modified.
   virtual bool UpdateAndExpandInvalidation(ContentLayerClient* painter,
                                            Region* invalidation,
-                                           bool can_use_lcd_text,
                                            const gfx::Size& layer_size,
                                            const gfx::Rect& visible_layer_rect,
                                            int frame_number,
                                            RecordingMode recording_mode) = 0;
 
-  virtual scoped_refptr<RasterSource> CreateRasterSource() const = 0;
+  virtual scoped_refptr<RasterSource> CreateRasterSource(
+      bool can_use_lcd_text) const = 0;
 
   virtual gfx::Size GetSize() const = 0;
   virtual void SetEmptyBounds() = 0;
diff --git a/cc/resources/resource_provider.cc b/cc/resources/resource_provider.cc
index 42ef4b3..115238f 100644
--- a/cc/resources/resource_provider.cc
+++ b/cc/resources/resource_provider.cc
@@ -266,7 +266,7 @@
       allow_overlay(false),
       read_lock_fence(NULL),
       size(),
-      origin(Internal),
+      origin(INTERNAL),
       target(0),
       original_filter(0),
       filter(0),
@@ -274,8 +274,8 @@
       bound_image_id(0),
       texture_pool(0),
       wrap_mode(0),
-      hint(TextureHintImmutable),
-      type(InvalidType),
+      hint(TEXTURE_HINT_IMMUTABLE),
+      type(RESOURCE_TYPE_INVALID),
       format(RGBA_8888),
       shared_bitmap(NULL),
       gpu_memory_buffer(NULL) {
@@ -322,12 +322,12 @@
       texture_pool(texture_pool),
       wrap_mode(wrap_mode),
       hint(hint),
-      type(GLTexture),
+      type(RESOURCE_TYPE_GL_TEXTURE),
       format(format),
       shared_bitmap(NULL),
       gpu_memory_buffer(NULL) {
   DCHECK(wrap_mode == GL_CLAMP_TO_EDGE || wrap_mode == GL_REPEAT);
-  DCHECK_EQ(origin == Internal, !!texture_pool);
+  DCHECK_EQ(origin == INTERNAL, !!texture_pool);
 }
 
 ResourceProvider::Resource::Resource(uint8_t* pixels,
@@ -365,13 +365,13 @@
       bound_image_id(0),
       texture_pool(0),
       wrap_mode(wrap_mode),
-      hint(TextureHintImmutable),
-      type(Bitmap),
+      hint(TEXTURE_HINT_IMMUTABLE),
+      type(RESOURCE_TYPE_BITMAP),
       format(RGBA_8888),
       shared_bitmap(bitmap),
       gpu_memory_buffer(NULL) {
   DCHECK(wrap_mode == GL_CLAMP_TO_EDGE || wrap_mode == GL_REPEAT);
-  DCHECK(origin == Delegated || pixels);
+  DCHECK(origin == DELEGATED || pixels);
   if (bitmap)
     shared_bitmap_id = bitmap->id();
 }
@@ -410,8 +410,8 @@
       bound_image_id(0),
       texture_pool(0),
       wrap_mode(wrap_mode),
-      hint(TextureHintImmutable),
-      type(Bitmap),
+      hint(TEXTURE_HINT_IMMUTABLE),
+      type(RESOURCE_TYPE_BITMAP),
       format(RGBA_8888),
       shared_bitmap_id(bitmap_id),
       shared_bitmap(NULL),
@@ -445,15 +445,15 @@
   else
     resource_provider->InitializeSoftware();
 
-  DCHECK_NE(InvalidType, resource_provider->default_resource_type());
+  DCHECK_NE(RESOURCE_TYPE_INVALID, resource_provider->default_resource_type());
   return resource_provider.Pass();
 }
 
 ResourceProvider::~ResourceProvider() {
   while (!children_.empty())
-    DestroyChildInternal(children_.begin(), ForShutdown);
+    DestroyChildInternal(children_.begin(), FOR_SHUTDOWN);
   while (!resources_.empty())
-    DeleteResourceInternal(resources_.begin(), ForShutdown);
+    DeleteResourceInternal(resources_.begin(), FOR_SHUTDOWN);
 
   CleanUpGLIfNeeded();
 }
@@ -481,17 +481,17 @@
     ResourceFormat format) {
   DCHECK(!size.IsEmpty());
   switch (default_resource_type_) {
-    case GLTexture:
+    case RESOURCE_TYPE_GL_TEXTURE:
       return CreateGLTexture(size,
                              GL_TEXTURE_2D,
                              GL_TEXTURE_POOL_UNMANAGED_CHROMIUM,
                              wrap_mode,
                              hint,
                              format);
-    case Bitmap:
+    case RESOURCE_TYPE_BITMAP:
       DCHECK_EQ(RGBA_8888, format);
       return CreateBitmap(size, wrap_mode);
-    case InvalidType:
+    case RESOURCE_TYPE_INVALID:
       break;
   }
 
@@ -507,17 +507,17 @@
     ResourceFormat format) {
   DCHECK(!size.IsEmpty());
   switch (default_resource_type_) {
-    case GLTexture:
+    case RESOURCE_TYPE_GL_TEXTURE:
       return CreateGLTexture(size,
                              target,
                              GL_TEXTURE_POOL_MANAGED_CHROMIUM,
                              wrap_mode,
                              hint,
                              format);
-    case Bitmap:
+    case RESOURCE_TYPE_BITMAP:
       DCHECK_EQ(RGBA_8888, format);
       return CreateBitmap(size, wrap_mode);
-    case InvalidType:
+    case RESOURCE_TYPE_INVALID:
       break;
   }
 
@@ -537,15 +537,8 @@
   DCHECK(thread_checker_.CalledOnValidThread());
 
   ResourceId id = next_id_++;
-  Resource resource(0,
-                    size,
-                    Resource::Internal,
-                    target,
-                    GL_LINEAR,
-                    texture_pool,
-                    wrap_mode,
-                    hint,
-                    format);
+  Resource resource(0, size, Resource::INTERNAL, target, GL_LINEAR,
+                    texture_pool, wrap_mode, hint, format);
   resource.allocated = false;
   resources_[id] = resource;
   return id;
@@ -561,8 +554,8 @@
   DCHECK(pixels);
 
   ResourceId id = next_id_++;
-  Resource resource(
-      pixels, bitmap.release(), size, Resource::Internal, GL_LINEAR, wrap_mode);
+  Resource resource(pixels, bitmap.release(), size, Resource::INTERNAL,
+                    GL_LINEAR, wrap_mode);
   resource.allocated = true;
   resources_[id] = resource;
   return id;
@@ -574,15 +567,10 @@
   DCHECK(thread_checker_.CalledOnValidThread());
 
   ResourceId id = next_id_++;
-  Resource resource(0,
-                    gfx::Size(),
-                    Resource::Internal,
-                    GL_TEXTURE_RECTANGLE_ARB,
-                    GL_LINEAR,
-                    GL_TEXTURE_POOL_UNMANAGED_CHROMIUM,
-                    GL_CLAMP_TO_EDGE,
-                    TextureHintImmutable,
-                    RGBA_8888);
+  Resource resource(0, gfx::Size(), Resource::INTERNAL,
+                    GL_TEXTURE_RECTANGLE_ARB, GL_LINEAR,
+                    GL_TEXTURE_POOL_UNMANAGED_CHROMIUM, GL_CLAMP_TO_EDGE,
+                    TEXTURE_HINT_IMMUTABLE, RGBA_8888);
   LazyCreate(&resource);
   GLES2Interface* gl = ContextGL();
   DCHECK(gl);
@@ -603,22 +591,16 @@
   DCHECK(mailbox.IsValid());
   Resource& resource = resources_[id];
   if (mailbox.IsTexture()) {
-    resource = Resource(0,
-                        gfx::Size(),
-                        Resource::External,
-                        mailbox.target(),
-                        mailbox.nearest_neighbor() ? GL_NEAREST : GL_LINEAR,
-                        0,
-                        GL_CLAMP_TO_EDGE,
-                        TextureHintImmutable,
-                        RGBA_8888);
+    resource = Resource(0, gfx::Size(), Resource::EXTERNAL, mailbox.target(),
+                        mailbox.nearest_neighbor() ? GL_NEAREST : GL_LINEAR, 0,
+                        GL_CLAMP_TO_EDGE, TEXTURE_HINT_IMMUTABLE, RGBA_8888);
   } else {
     DCHECK(mailbox.IsSharedMemory());
     SharedBitmap* shared_bitmap = mailbox.shared_bitmap();
     uint8_t* pixels = shared_bitmap->pixels();
     DCHECK(pixels);
     resource = Resource(pixels, shared_bitmap, mailbox.shared_memory_size(),
-                        Resource::External, GL_LINEAR, GL_CLAMP_TO_EDGE);
+                        Resource::EXTERNAL, GL_LINEAR, GL_CLAMP_TO_EDGE);
   }
   resource.allocated = true;
   resource.mailbox = mailbox;
@@ -642,7 +624,7 @@
     resource->marked_for_deletion = true;
     return;
   } else {
-    DeleteResourceInternal(it, Normal);
+    DeleteResourceInternal(it, NORMAL);
   }
 }
 
@@ -652,38 +634,38 @@
   Resource* resource = &it->second;
   bool lost_resource = resource->lost;
 
-  DCHECK(resource->exported_count == 0 || style != Normal);
-  if (style == ForShutdown && resource->exported_count > 0)
+  DCHECK(resource->exported_count == 0 || style != NORMAL);
+  if (style == FOR_SHUTDOWN && resource->exported_count > 0)
     lost_resource = true;
 
   if (resource->image_id) {
-    DCHECK(resource->origin == Resource::Internal);
+    DCHECK(resource->origin == Resource::INTERNAL);
     GLES2Interface* gl = ContextGL();
     DCHECK(gl);
     GLC(gl, gl->DestroyImageCHROMIUM(resource->image_id));
   }
   if (resource->gl_upload_query_id) {
-    DCHECK(resource->origin == Resource::Internal);
+    DCHECK(resource->origin == Resource::INTERNAL);
     GLES2Interface* gl = ContextGL();
     DCHECK(gl);
     GLC(gl, gl->DeleteQueriesEXT(1, &resource->gl_upload_query_id));
   }
   if (resource->gl_read_lock_query_id) {
-    DCHECK(resource->origin == Resource::Internal);
+    DCHECK(resource->origin == Resource::INTERNAL);
     GLES2Interface* gl = ContextGL();
     DCHECK(gl);
     GLC(gl, gl->DeleteQueriesEXT(1, &resource->gl_read_lock_query_id));
   }
   if (resource->gl_pixel_buffer_id) {
-    DCHECK(resource->origin == Resource::Internal);
+    DCHECK(resource->origin == Resource::INTERNAL);
     GLES2Interface* gl = ContextGL();
     DCHECK(gl);
     GLC(gl, gl->DeleteBuffers(1, &resource->gl_pixel_buffer_id));
   }
-  if (resource->origin == Resource::External) {
+  if (resource->origin == Resource::EXTERNAL) {
     DCHECK(resource->mailbox.IsValid());
     GLuint sync_point = resource->mailbox.sync_point();
-    if (resource->type == GLTexture) {
+    if (resource->type == RESOURCE_TYPE_GL_TEXTURE) {
       DCHECK(resource->mailbox.IsTexture());
       lost_resource |= lost_output_surface_;
       GLES2Interface* gl = ContextGL();
@@ -709,18 +691,18 @@
     resource->gl_id = 0;
   }
   if (resource->shared_bitmap) {
-    DCHECK(resource->origin != Resource::External);
-    DCHECK_EQ(Bitmap, resource->type);
+    DCHECK(resource->origin != Resource::EXTERNAL);
+    DCHECK_EQ(RESOURCE_TYPE_BITMAP, resource->type);
     delete resource->shared_bitmap;
     resource->pixels = NULL;
   }
   if (resource->pixels) {
-    DCHECK(resource->origin == Resource::Internal);
+    DCHECK(resource->origin == Resource::INTERNAL);
     delete[] resource->pixels;
     resource->pixels = NULL;
   }
   if (resource->gpu_memory_buffer) {
-    DCHECK(resource->origin == Resource::Internal);
+    DCHECK(resource->origin == Resource::INTERNAL);
     delete resource->gpu_memory_buffer;
     resource->gpu_memory_buffer = NULL;
   }
@@ -740,12 +722,12 @@
   Resource* resource = GetResource(id);
   DCHECK(!resource->locked_for_write);
   DCHECK(!resource->lock_for_read_count);
-  DCHECK(resource->origin == Resource::Internal);
+  DCHECK(resource->origin == Resource::INTERNAL);
   DCHECK_EQ(resource->exported_count, 0);
   DCHECK(ReadLockFenceHasPassed(resource));
   LazyAllocate(resource);
 
-  if (resource->type == GLTexture) {
+  if (resource->type == RESOURCE_TYPE_GL_TEXTURE) {
     DCHECK(resource->gl_id);
     DCHECK(!resource->pending_set_pixels);
     DCHECK_EQ(resource->target, static_cast<GLenum>(GL_TEXTURE_2D));
@@ -760,7 +742,7 @@
                               resource->format,
                               resource->size);
   } else {
-    DCHECK_EQ(Bitmap, resource->type);
+    DCHECK_EQ(RESOURCE_TYPE_BITMAP, resource->type);
     DCHECK(resource->allocated);
     DCHECK_EQ(RGBA_8888, resource->format);
     DCHECK(source_rect.x() >= image_rect.x());
@@ -780,6 +762,54 @@
   }
 }
 
+void ResourceProvider::CopyToResource(ResourceId id,
+                                      const uint8_t* image,
+                                      const gfx::Size& image_size) {
+  Resource* resource = GetResource(id);
+  DCHECK(!resource->locked_for_write);
+  DCHECK(!resource->lock_for_read_count);
+  DCHECK(resource->origin == Resource::INTERNAL);
+  DCHECK_EQ(resource->exported_count, 0);
+  DCHECK(ReadLockFenceHasPassed(resource));
+  LazyAllocate(resource);
+
+  DCHECK_EQ(image_size.width(), resource->size.width());
+  DCHECK_EQ(image_size.height(), resource->size.height());
+
+  if (resource->type == RESOURCE_TYPE_BITMAP) {
+    DCHECK_EQ(RESOURCE_TYPE_BITMAP, resource->type);
+    DCHECK(resource->allocated);
+    DCHECK_EQ(RGBA_8888, resource->format);
+    SkImageInfo source_info =
+        SkImageInfo::MakeN32Premul(image_size.width(), image_size.height());
+    size_t image_stride = image_size.width() * 4;
+
+    ScopedWriteLockSoftware lock(this, id);
+    SkCanvas dest(lock.sk_bitmap());
+    dest.writePixels(source_info, image, image_stride, 0, 0);
+  } else {
+    DCHECK(resource->gl_id);
+    DCHECK(!resource->pending_set_pixels);
+    DCHECK_EQ(resource->target, static_cast<GLenum>(GL_TEXTURE_2D));
+    GLES2Interface* gl = ContextGL();
+    DCHECK(gl);
+    DCHECK(texture_uploader_.get());
+    gl->BindTexture(GL_TEXTURE_2D, resource->gl_id);
+
+    if (resource->format == ETC1) {
+      size_t num_bytes = static_cast<size_t>(image_size.width()) *
+                         image_size.height() * BitsPerPixel(ETC1) / 8;
+      gl->CompressedTexImage2D(GL_TEXTURE_2D, 0, GLInternalFormat(ETC1),
+                               image_size.width(), image_size.height(), 0,
+                               num_bytes, image);
+    } else {
+      gl->TexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, image_size.width(),
+                        image_size.height(), GLDataFormat(resource->format),
+                        GLDataType(resource->format), image);
+    }
+  }
+}
+
 size_t ResourceProvider::NumBlockingUploads() {
   if (!texture_uploader_)
     return 0;
@@ -858,8 +888,8 @@
 
   LazyCreate(resource);
 
-  if (resource->type == GLTexture && !resource->gl_id) {
-    DCHECK(resource->origin != Resource::Internal);
+  if (resource->type == RESOURCE_TYPE_GL_TEXTURE && !resource->gl_id) {
+    DCHECK(resource->origin != Resource::INTERNAL);
     DCHECK(resource->mailbox.IsTexture());
 
     // Mailbox sync_points must be processed by a call to
@@ -908,12 +938,12 @@
   if (resource->marked_for_deletion && !resource->lock_for_read_count) {
     if (!resource->child_id) {
       // The resource belongs to this ResourceProvider, so it can be destroyed.
-      DeleteResourceInternal(it, Normal);
+      DeleteResourceInternal(it, NORMAL);
     } else {
       ChildMap::iterator child_it = children_.find(resource->child_id);
       ResourceIdArray unused;
       unused.push_back(id);
-      DeleteAndReturnUnusedResourcesToChild(child_it, Normal, unused);
+      DeleteAndReturnUnusedResourcesToChild(child_it, NORMAL, unused);
     }
   }
 }
@@ -929,14 +959,14 @@
 bool ResourceProvider::CanLockForWrite(ResourceId id) {
   Resource* resource = GetResource(id);
   return !resource->locked_for_write && !resource->lock_for_read_count &&
-         !resource->exported_count && resource->origin == Resource::Internal &&
+         !resource->exported_count && resource->origin == Resource::INTERNAL &&
          !resource->lost && ReadLockFenceHasPassed(resource);
 }
 
 void ResourceProvider::UnlockForWrite(ResourceProvider::Resource* resource) {
   DCHECK(resource->locked_for_write);
   DCHECK_EQ(resource->exported_count, 0);
-  DCHECK(resource->origin == Resource::Internal);
+  DCHECK(resource->origin == Resource::INTERNAL);
   resource->locked_for_write = false;
 }
 
@@ -1034,7 +1064,7 @@
       gpu_memory_buffer_(nullptr),
       size_(resource_->size),
       format_(resource_->format) {
-  DCHECK_EQ(GLTexture, resource_->type);
+  DCHECK_EQ(RESOURCE_TYPE_GL_TEXTURE, resource_->type);
   std::swap(gpu_memory_buffer_, resource_->gpu_memory_buffer);
 }
 
@@ -1187,7 +1217,7 @@
       highp_threshold_min_(highp_threshold_min),
       next_id_(1),
       next_child_(1),
-      default_resource_type_(InvalidType),
+      default_resource_type_(RESOURCE_TYPE_INVALID),
       use_texture_storage_ext_(false),
       use_texture_format_bgra_(false),
       use_texture_usage_hint_(false),
@@ -1204,11 +1234,11 @@
 
 void ResourceProvider::InitializeSoftware() {
   DCHECK(thread_checker_.CalledOnValidThread());
-  DCHECK_NE(Bitmap, default_resource_type_);
+  DCHECK_NE(RESOURCE_TYPE_BITMAP, default_resource_type_);
 
   CleanUpGLIfNeeded();
 
-  default_resource_type_ = Bitmap;
+  default_resource_type_ = RESOURCE_TYPE_BITMAP;
   // Pick an arbitrary limit here similar to what hardware might.
   max_texture_size_ = 16 * 1024;
   best_texture_format_ = RGBA_8888;
@@ -1217,11 +1247,11 @@
 void ResourceProvider::InitializeGL() {
   DCHECK(thread_checker_.CalledOnValidThread());
   DCHECK(!texture_uploader_);
-  DCHECK_NE(GLTexture, default_resource_type_);
+  DCHECK_NE(RESOURCE_TYPE_GL_TEXTURE, default_resource_type_);
   DCHECK(!texture_id_allocator_);
   DCHECK(!buffer_id_allocator_);
 
-  default_resource_type_ = GLTexture;
+  default_resource_type_ = RESOURCE_TYPE_GL_TEXTURE;
 
   const ContextProvider::Capabilities& caps =
       output_surface_->context_provider()->ContextCapabilities();
@@ -1250,7 +1280,7 @@
 
 void ResourceProvider::CleanUpGLIfNeeded() {
   GLES2Interface* gl = ContextGL();
-  if (default_resource_type_ != GLTexture) {
+  if (default_resource_type_ != RESOURCE_TYPE_GL_TEXTURE) {
     // We are not in GL mode, but double check before returning.
     DCHECK(!gl);
     DCHECK(!texture_uploader_);
@@ -1263,7 +1293,7 @@
   for (ResourceMap::const_iterator itr = resources_.begin();
        itr != resources_.end();
        ++itr) {
-    DCHECK_NE(GLTexture, itr->second.type);
+    DCHECK_NE(RESOURCE_TYPE_GL_TEXTURE, itr->second.type);
   }
 #endif  // DCHECK_IS_ON()
 
@@ -1287,7 +1317,7 @@
 void ResourceProvider::DestroyChild(int child_id) {
   ChildMap::iterator it = children_.find(child_id);
   DCHECK(it != children_.end());
-  DestroyChildInternal(it, Normal);
+  DestroyChildInternal(it, NORMAL);
 }
 
 void ResourceProvider::DestroyChildInternal(ChildMap::iterator it,
@@ -1295,7 +1325,7 @@
   DCHECK(thread_checker_.CalledOnValidThread());
 
   Child& child = it->second;
-  DCHECK(style == ForShutdown || !child.marked_for_deletion);
+  DCHECK(style == FOR_SHUTDOWN || !child.marked_for_deletion);
 
   ResourceIdArray resources_for_child;
 
@@ -1379,21 +1409,14 @@
     ResourceId local_id = next_id_++;
     Resource& resource = resources_[local_id];
     if (it->is_software) {
-      resource = Resource(it->mailbox_holder.mailbox,
-                          it->size,
-                          Resource::Delegated,
-                          GL_LINEAR,
-                          it->is_repeated ? GL_REPEAT : GL_CLAMP_TO_EDGE);
+      resource =
+          Resource(it->mailbox_holder.mailbox, it->size, Resource::DELEGATED,
+                   GL_LINEAR, it->is_repeated ? GL_REPEAT : GL_CLAMP_TO_EDGE);
     } else {
-      resource = Resource(0,
-                          it->size,
-                          Resource::Delegated,
-                          it->mailbox_holder.texture_target,
-                          it->filter,
-                          0,
+      resource = Resource(0, it->size, Resource::DELEGATED,
+                          it->mailbox_holder.texture_target, it->filter, 0,
                           it->is_repeated ? GL_REPEAT : GL_CLAMP_TO_EDGE,
-                          TextureHintImmutable,
-                          it->format);
+                          TEXTURE_HINT_IMMUTABLE, it->format);
       resource.mailbox = TextureMailbox(it->mailbox_holder.mailbox,
                                         it->mailbox_holder.texture_target,
                                         it->mailbox_holder.sync_point);
@@ -1438,7 +1461,7 @@
     if (!resource_is_in_use)
       unused.push_back(local_id);
   }
-  DeleteAndReturnUnusedResourcesToChild(child_it, Normal, unused);
+  DeleteAndReturnUnusedResourcesToChild(child_it, NORMAL, unused);
 }
 
 // static
@@ -1505,7 +1528,7 @@
 
     if (returned.sync_point) {
       DCHECK(!resource->has_shared_bitmap_id);
-      if (resource->origin == Resource::Internal) {
+      if (resource->origin == Resource::INTERNAL) {
         DCHECK(resource->gl_id);
         GLC(gl, gl->WaitSyncPointCHROMIUM(returned.sync_point));
       } else {
@@ -1519,18 +1542,18 @@
 
     if (!resource->child_id) {
       // The resource belongs to this ResourceProvider, so it can be destroyed.
-      DeleteResourceInternal(map_iterator, Normal);
+      DeleteResourceInternal(map_iterator, NORMAL);
       continue;
     }
 
-    DCHECK(resource->origin == Resource::Delegated);
+    DCHECK(resource->origin == Resource::DELEGATED);
     // Delete the resource and return it to the child it came from one.
     if (resource->child_id != child_id) {
       if (child_id) {
         DCHECK_NE(resources_for_child.size(), 0u);
         DCHECK(child_it != children_.end());
-        DeleteAndReturnUnusedResourcesToChild(
-            child_it, Normal, resources_for_child);
+        DeleteAndReturnUnusedResourcesToChild(child_it, NORMAL,
+                                              resources_for_child);
         resources_for_child.clear();
       }
 
@@ -1544,8 +1567,8 @@
   if (child_id) {
     DCHECK_NE(resources_for_child.size(), 0u);
     DCHECK(child_it != children_.end());
-    DeleteAndReturnUnusedResourcesToChild(
-        child_it, Normal, resources_for_child);
+    DeleteAndReturnUnusedResourcesToChild(child_it, NORMAL,
+                                          resources_for_child);
   }
 }
 
@@ -1555,7 +1578,7 @@
   Resource* source = GetResource(id);
   DCHECK(!source->locked_for_write);
   DCHECK(!source->lock_for_read_count);
-  DCHECK(source->origin != Resource::External || source->mailbox.IsValid());
+  DCHECK(source->origin != Resource::EXTERNAL || source->mailbox.IsValid());
   DCHECK(source->allocated);
   resource->id = id;
   resource->format = source->format;
@@ -1565,13 +1588,13 @@
   resource->is_repeated = (source->wrap_mode == GL_REPEAT);
   resource->allow_overlay = source->allow_overlay;
 
-  if (source->type == Bitmap) {
+  if (source->type == RESOURCE_TYPE_BITMAP) {
     resource->mailbox_holder.mailbox = source->shared_bitmap_id;
     resource->is_software = true;
   } else if (!source->mailbox.IsValid()) {
     LazyCreate(source);
     DCHECK(source->gl_id);
-    DCHECK(source->origin == Resource::Internal);
+    DCHECK(source->origin == Resource::INTERNAL);
     GLC(gl,
         gl->BindTexture(resource->mailbox_holder.texture_target,
                         source->gl_id));
@@ -1590,7 +1613,7 @@
     DCHECK(source->mailbox.IsTexture());
     if (source->image_id && source->dirty_image) {
       DCHECK(source->gl_id);
-      DCHECK(source->origin == Resource::Internal);
+      DCHECK(source->origin == Resource::INTERNAL);
       GLC(gl,
           gl->BindTexture(resource->mailbox_holder.texture_target,
                           source->gl_id));
@@ -1635,9 +1658,10 @@
     DCHECK(child_info->child_to_parent_map.count(child_id));
 
     bool is_lost =
-        resource.lost || (resource.type == GLTexture && lost_output_surface_);
+        resource.lost ||
+        (resource.type == RESOURCE_TYPE_GL_TEXTURE && lost_output_surface_);
     if (resource.exported_count > 0 || resource.lock_for_read_count > 0) {
-      if (style != ForShutdown) {
+      if (style != FOR_SHUTDOWN) {
         // Defer this until we receive the resource back from the parent or
         // the read lock is released.
         resource.marked_for_deletion = true;
@@ -1666,7 +1690,7 @@
     ReturnedResource returned;
     returned.id = child_id;
     returned.sync_point = resource.mailbox.sync_point();
-    if (!returned.sync_point && resource.type == GLTexture)
+    if (!returned.sync_point && resource.type == RESOURCE_TYPE_GL_TEXTURE)
       need_sync_point = true;
     returned.count = resource.imported_count;
     returned.lost = is_lost;
@@ -1702,12 +1726,12 @@
                "ResourceProvider::AcquirePixelBuffer");
 
   Resource* resource = GetResource(id);
-  DCHECK(resource->origin == Resource::Internal);
+  DCHECK(resource->origin == Resource::INTERNAL);
   DCHECK_EQ(resource->exported_count, 0);
   DCHECK(!resource->image_id);
   DCHECK_NE(ETC1, resource->format);
 
-  DCHECK_EQ(GLTexture, resource->type);
+  DCHECK_EQ(RESOURCE_TYPE_GL_TEXTURE, resource->type);
   GLES2Interface* gl = ContextGL();
   DCHECK(gl);
   if (!resource->gl_pixel_buffer_id)
@@ -1728,7 +1752,7 @@
                "ResourceProvider::ReleasePixelBuffer");
 
   Resource* resource = GetResource(id);
-  DCHECK(resource->origin == Resource::Internal);
+  DCHECK(resource->origin == Resource::INTERNAL);
   DCHECK_EQ(resource->exported_count, 0);
   DCHECK(!resource->image_id);
 
@@ -1744,7 +1768,7 @@
     resource->locked_for_write = false;
   }
 
-  DCHECK_EQ(GLTexture, resource->type);
+  DCHECK_EQ(RESOURCE_TYPE_GL_TEXTURE, resource->type);
   if (!resource->gl_pixel_buffer_id)
     return;
   GLES2Interface* gl = ContextGL();
@@ -1761,12 +1785,12 @@
                "ResourceProvider::MapPixelBuffer");
 
   Resource* resource = GetResource(id);
-  DCHECK(resource->origin == Resource::Internal);
+  DCHECK(resource->origin == Resource::INTERNAL);
   DCHECK_EQ(resource->exported_count, 0);
   DCHECK(!resource->image_id);
 
   *stride = 0;
-  DCHECK_EQ(GLTexture, resource->type);
+  DCHECK_EQ(RESOURCE_TYPE_GL_TEXTURE, resource->type);
   GLES2Interface* gl = ContextGL();
   DCHECK(gl);
   DCHECK(resource->gl_pixel_buffer_id);
@@ -1785,11 +1809,11 @@
                "ResourceProvider::UnmapPixelBuffer");
 
   Resource* resource = GetResource(id);
-  DCHECK(resource->origin == Resource::Internal);
+  DCHECK(resource->origin == Resource::INTERNAL);
   DCHECK_EQ(resource->exported_count, 0);
   DCHECK(!resource->image_id);
 
-  DCHECK_EQ(GLTexture, resource->type);
+  DCHECK_EQ(RESOURCE_TYPE_GL_TEXTURE, resource->type);
   GLES2Interface* gl = ContextGL();
   DCHECK(gl);
   DCHECK(resource->gl_pixel_buffer_id);
@@ -1833,7 +1857,7 @@
   DCHECK(!resource->pending_set_pixels);
 
   LazyCreate(resource);
-  DCHECK(resource->origin == Resource::Internal);
+  DCHECK(resource->origin == Resource::INTERNAL);
   DCHECK(resource->gl_id || resource->allocated);
   DCHECK(ReadLockFenceHasPassed(resource));
   DCHECK(!resource->image_id);
@@ -1842,7 +1866,7 @@
   resource->allocated = true;
   LockForWrite(id);
 
-  DCHECK_EQ(GLTexture, resource->type);
+  DCHECK_EQ(RESOURCE_TYPE_GL_TEXTURE, resource->type);
   DCHECK(resource->gl_id);
   GLES2Interface* gl = ContextGL();
   DCHECK(gl);
@@ -1944,14 +1968,15 @@
 }
 
 void ResourceProvider::LazyCreate(Resource* resource) {
-  if (resource->type != GLTexture || resource->origin != Resource::Internal)
+  if (resource->type != RESOURCE_TYPE_GL_TEXTURE ||
+      resource->origin != Resource::INTERNAL)
     return;
 
   if (resource->gl_id)
     return;
 
   DCHECK(resource->texture_pool);
-  DCHECK(resource->origin == Resource::Internal);
+  DCHECK(resource->origin == Resource::INTERNAL);
   DCHECK(!resource->mailbox.IsValid());
   resource->gl_id = texture_id_allocator_->NextId();
 
@@ -1975,7 +2000,7 @@
   GLC(gl,
       gl->TexParameteri(
           resource->target, GL_TEXTURE_POOL_CHROMIUM, resource->texture_pool));
-  if (use_texture_usage_hint_ && (resource->hint & TextureHintFramebuffer)) {
+  if (use_texture_usage_hint_ && (resource->hint & TEXTURE_HINT_FRAMEBUFFER)) {
     GLC(gl,
         gl->TexParameteri(resource->target,
                           GL_TEXTURE_USAGE_ANGLE,
@@ -2002,7 +2027,7 @@
   GLC(gl, gl->BindTexture(GL_TEXTURE_2D, resource->gl_id));
   if (use_texture_storage_ext_ &&
       IsFormatSupportedForStorage(format, use_texture_format_bgra_) &&
-      (resource->hint & TextureHintImmutable)) {
+      (resource->hint & TEXTURE_HINT_IMMUTABLE)) {
     GLenum storage_format = TextureToStorageFormat(format);
     GLC(gl,
         gl->TexStorage2DEXT(
@@ -2042,18 +2067,18 @@
 
   Resource* source_resource = GetResource(source_id);
   DCHECK(!source_resource->lock_for_read_count);
-  DCHECK(source_resource->origin == Resource::Internal);
+  DCHECK(source_resource->origin == Resource::INTERNAL);
   DCHECK_EQ(source_resource->exported_count, 0);
-  DCHECK_EQ(GLTexture, source_resource->type);
+  DCHECK_EQ(RESOURCE_TYPE_GL_TEXTURE, source_resource->type);
   DCHECK(source_resource->allocated);
   LazyCreate(source_resource);
 
   Resource* dest_resource = GetResource(dest_id);
   DCHECK(!dest_resource->locked_for_write);
   DCHECK(!dest_resource->lock_for_read_count);
-  DCHECK(dest_resource->origin == Resource::Internal);
+  DCHECK(dest_resource->origin == Resource::INTERNAL);
   DCHECK_EQ(dest_resource->exported_count, 0);
-  DCHECK_EQ(GLTexture, dest_resource->type);
+  DCHECK_EQ(RESOURCE_TYPE_GL_TEXTURE, dest_resource->type);
   LazyAllocate(dest_resource);
 
   DCHECK_EQ(source_resource->type, dest_resource->type);
@@ -2114,7 +2139,7 @@
   Resource* resource = GetResource(id);
   DCHECK_EQ(resource->exported_count, 0);
   DCHECK(resource->allocated);
-  if (resource->type != GLTexture || resource->gl_id)
+  if (resource->type != RESOURCE_TYPE_GL_TEXTURE || resource->gl_id)
     return;
   if (!resource->mailbox.sync_point())
     return;
diff --git a/cc/resources/resource_provider.h b/cc/resources/resource_provider.h
index 167ef201..95e12944 100644
--- a/cc/resources/resource_provider.h
+++ b/cc/resources/resource_provider.h
@@ -67,16 +67,16 @@
   typedef std::set<ResourceId> ResourceIdSet;
   typedef base::hash_map<ResourceId, ResourceId> ResourceIdMap;
   enum TextureHint {
-    TextureHintDefault = 0x0,
-    TextureHintImmutable = 0x1,
-    TextureHintFramebuffer = 0x2,
-    TextureHintImmutableFramebuffer =
-        TextureHintImmutable | TextureHintFramebuffer
+    TEXTURE_HINT_DEFAULT = 0x0,
+    TEXTURE_HINT_IMMUTABLE = 0x1,
+    TEXTURE_HINT_FRAMEBUFFER = 0x2,
+    TEXTURE_HINT_IMMUTABLE_FRAMEBUFFER =
+        TEXTURE_HINT_IMMUTABLE | TEXTURE_HINT_FRAMEBUFFER
   };
   enum ResourceType {
-    InvalidType = 0,
-    GLTexture = 1,
-    Bitmap,
+    RESOURCE_TYPE_INVALID = 0,
+    RESOURCE_TYPE_GL_TEXTURE = 1,
+    RESOURCE_TYPE_BITMAP,
   };
 
   static scoped_ptr<ResourceProvider> Create(
@@ -150,11 +150,15 @@
 
   // Update pixels from image, copying source_rect (in image) to dest_offset (in
   // the resource).
+  // NOTE: DEPRECATED. Use CopyToResource() instead.
   void SetPixels(ResourceId id,
                  const uint8_t* image,
                  const gfx::Rect& image_rect,
                  const gfx::Rect& source_rect,
                  const gfx::Vector2d& dest_offset);
+  void CopyToResource(ResourceId id,
+                      const uint8_t* image,
+                      const gfx::Size& image_size);
 
   // Check upload status.
   size_t NumBlockingUploads();
@@ -434,7 +438,7 @@
 
  private:
   struct Resource {
-    enum Origin { Internal, External, Delegated };
+    enum Origin { INTERNAL, EXTERNAL, DELEGATED };
 
     Resource();
     ~Resource();
@@ -547,8 +551,8 @@
                         ResourceId id,
                         TransferableResource* resource);
   enum DeleteStyle {
-    Normal,
-    ForShutdown,
+    NORMAL,
+    FOR_SHUTDOWN,
   };
   void DeleteResourceInternal(ResourceMap::iterator it, DeleteStyle style);
   void DeleteAndReturnUnusedResourcesToChild(ChildMap::iterator child_it,
diff --git a/cc/resources/resource_provider_unittest.cc b/cc/resources/resource_provider_unittest.cc
index 2bba801..fdd7fc0 100644
--- a/cc/resources/resource_provider_unittest.cc
+++ b/cc/resources/resource_provider_unittest.cc
@@ -354,14 +354,14 @@
                        uint8_t* pixels) {
   resource_provider->WaitSyncPointIfNeeded(id);
   switch (resource_provider->default_resource_type()) {
-    case ResourceProvider::GLTexture: {
+    case ResourceProvider::RESOURCE_TYPE_GL_TEXTURE: {
       ResourceProvider::ScopedReadLockGL lock_gl(resource_provider, id);
       ASSERT_NE(0U, lock_gl.texture_id());
       context->bindTexture(GL_TEXTURE_2D, lock_gl.texture_id());
       context->GetPixels(size, format, pixels);
       break;
     }
-    case ResourceProvider::Bitmap: {
+    case ResourceProvider::RESOURCE_TYPE_BITMAP: {
       ResourceProvider::ScopedReadLockSoftware lock_software(resource_provider,
                                                              id);
       memcpy(pixels,
@@ -369,7 +369,7 @@
              lock_software.sk_bitmap()->getSize());
       break;
     }
-    case ResourceProvider::InvalidType:
+    case ResourceProvider::RESOURCE_TYPE_INVALID:
       NOTREACHED();
       break;
   }
@@ -384,7 +384,7 @@
         child_context_(NULL),
         main_thread_task_runner_(BlockingTaskRunner::Create(NULL)) {
     switch (GetParam()) {
-      case ResourceProvider::GLTexture: {
+      case ResourceProvider::RESOURCE_TYPE_GL_TEXTURE: {
         scoped_ptr<ResourceProviderContext> context3d(
             ResourceProviderContext::Create(shared_data_.get()));
         context3d_ = context3d.get();
@@ -401,13 +401,13 @@
             FakeOutputSurface::Create3d(child_context_owned.Pass());
         break;
       }
-      case ResourceProvider::Bitmap:
+      case ResourceProvider::RESOURCE_TYPE_BITMAP:
         output_surface_ = FakeOutputSurface::CreateSoftware(
             make_scoped_ptr(new SoftwareOutputDevice));
         child_output_surface_ = FakeOutputSurface::CreateSoftware(
             make_scoped_ptr(new SoftwareOutputDevice));
         break;
-      case ResourceProvider::InvalidType:
+      case ResourceProvider::RESOURCE_TYPE_INVALID:
         NOTREACHED();
         break;
     }
@@ -458,7 +458,7 @@
                                                   bool* lost_resource,
                                                   bool* release_called,
                                                   uint32* sync_point) {
-    if (GetParam() == ResourceProvider::GLTexture) {
+    if (GetParam() == ResourceProvider::RESOURCE_TYPE_GL_TEXTURE) {
       unsigned texture = child_context_->createTexture();
       gpu::Mailbox gpu_mailbox;
       child_context_->bindTexture(GL_TEXTURE_2D, texture);
@@ -516,15 +516,14 @@
   ASSERT_EQ(4U, pixel_size);
 
   ResourceProvider::ResourceId id = resource_provider->CreateResource(
-      size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureHintImmutable, format);
+      size, GL_CLAMP_TO_EDGE, ResourceProvider::TEXTURE_HINT_IMMUTABLE, format);
   EXPECT_EQ(1, static_cast<int>(resource_provider->num_resources()));
-  if (expected_default_type == ResourceProvider::GLTexture)
+  if (expected_default_type == ResourceProvider::RESOURCE_TYPE_GL_TEXTURE)
     EXPECT_EQ(0u, context->NumTextures());
 
   uint8_t data[4] = { 1, 2, 3, 4 };
-  gfx::Rect rect(size);
-  resource_provider->SetPixels(id, data, rect, rect, gfx::Vector2d());
-  if (expected_default_type == ResourceProvider::GLTexture)
+  resource_provider->CopyToResource(id, data, size);
+  if (expected_default_type == ResourceProvider::RESOURCE_TYPE_GL_TEXTURE)
     EXPECT_EQ(1u, context->NumTextures());
 
   uint8_t result[4] = { 0 };
@@ -533,7 +532,7 @@
 
   resource_provider->DeleteResource(id);
   EXPECT_EQ(0, static_cast<int>(resource_provider->num_resources()));
-  if (expected_default_type == ResourceProvider::GLTexture)
+  if (expected_default_type == ResourceProvider::RESOURCE_TYPE_GL_TEXTURE)
     EXPECT_EQ(0u, context->NumTextures());
 }
 
@@ -548,7 +547,7 @@
   ASSERT_EQ(16U, pixel_size);
 
   ResourceProvider::ResourceId id = resource_provider_->CreateResource(
-      size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureHintImmutable, format);
+      size, GL_CLAMP_TO_EDGE, ResourceProvider::TEXTURE_HINT_IMMUTABLE, format);
 
   uint8_t image[16] = { 0 };
   gfx::Rect image_rect(size);
@@ -608,8 +607,40 @@
   resource_provider_->DeleteResource(id);
 }
 
+TEST_P(ResourceProviderTest, SimpleUpload) {
+  gfx::Size size(2, 2);
+  ResourceFormat format = RGBA_8888;
+  size_t pixel_size = TextureSizeBytes(size, format);
+  ASSERT_EQ(16U, pixel_size);
+
+  ResourceProvider::ResourceId id = resource_provider_->CreateResource(
+      size, GL_CLAMP_TO_EDGE, ResourceProvider::TEXTURE_HINT_IMMUTABLE, format);
+
+  uint8_t image[16] = {0};
+  resource_provider_->CopyToResource(id, image, size);
+  {
+    uint8_t result[16] = {0};
+    uint8_t expected[16] = {0};
+    GetResourcePixels(resource_provider_.get(), context(), id, size, format,
+                      result);
+    EXPECT_EQ(0, memcmp(expected, result, pixel_size));
+  }
+
+  for (uint8_t i = 0; i < pixel_size; ++i)
+    image[i] = i;
+  resource_provider_->CopyToResource(id, image, size);
+  {
+    uint8_t result[16] = {0};
+    uint8_t expected[16] = {
+        0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
+    GetResourcePixels(resource_provider_.get(), context(), id, size, format,
+                      result);
+    EXPECT_EQ(0, memcmp(expected, result, pixel_size));
+  }
+}
+
 TEST_P(ResourceProviderTest, TransferGLResources) {
-  if (GetParam() != ResourceProvider::GLTexture)
+  if (GetParam() != ResourceProvider::RESOURCE_TYPE_GL_TEXTURE)
     return;
   gfx::Size size(1, 1);
   ResourceFormat format = RGBA_8888;
@@ -617,18 +648,17 @@
   ASSERT_EQ(4U, pixel_size);
 
   ResourceProvider::ResourceId id1 = child_resource_provider_->CreateResource(
-      size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureHintImmutable, format);
+      size, GL_CLAMP_TO_EDGE, ResourceProvider::TEXTURE_HINT_IMMUTABLE, format);
   uint8_t data1[4] = { 1, 2, 3, 4 };
-  gfx::Rect rect(size);
-  child_resource_provider_->SetPixels(id1, data1, rect, rect, gfx::Vector2d());
+  child_resource_provider_->CopyToResource(id1, data1, size);
 
   ResourceProvider::ResourceId id2 = child_resource_provider_->CreateResource(
-      size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureHintImmutable, format);
+      size, GL_CLAMP_TO_EDGE, ResourceProvider::TEXTURE_HINT_IMMUTABLE, format);
   uint8_t data2[4] = { 5, 5, 5, 5 };
-  child_resource_provider_->SetPixels(id2, data2, rect, rect, gfx::Vector2d());
+  child_resource_provider_->CopyToResource(id2, data2, size);
 
   ResourceProvider::ResourceId id3 = child_resource_provider_->CreateResource(
-      size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureHintImmutable, format);
+      size, GL_CLAMP_TO_EDGE, ResourceProvider::TEXTURE_HINT_IMMUTABLE, format);
   {
     ResourceProvider::ScopedWriteLockGpuMemoryBuffer lock(
         child_resource_provider_.get(), id3);
@@ -855,16 +885,15 @@
 }
 
 TEST_P(ResourceProviderTest, ReadLockCountStopsReturnToChildOrDelete) {
-  if (GetParam() != ResourceProvider::GLTexture)
+  if (GetParam() != ResourceProvider::RESOURCE_TYPE_GL_TEXTURE)
     return;
   gfx::Size size(1, 1);
   ResourceFormat format = RGBA_8888;
 
   ResourceProvider::ResourceId id1 = child_resource_provider_->CreateResource(
-      size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureHintImmutable, format);
+      size, GL_CLAMP_TO_EDGE, ResourceProvider::TEXTURE_HINT_IMMUTABLE, format);
   uint8_t data1[4] = {1, 2, 3, 4};
-  gfx::Rect rect(size);
-  child_resource_provider_->SetPixels(id1, data1, rect, rect, gfx::Vector2d());
+  child_resource_provider_->CopyToResource(id1, data1, size);
 
   ReturnedResourceArray returned_to_child;
   int child_id =
@@ -908,7 +937,7 @@
 
 TEST_P(ResourceProviderTest, AllowOverlayTransfersToParent) {
   // Overlays only supported on the GL path.
-  if (GetParam() != ResourceProvider::GLTexture)
+  if (GetParam() != ResourceProvider::RESOURCE_TYPE_GL_TEXTURE)
     return;
 
   uint32 sync_point = 0;
@@ -958,7 +987,7 @@
 }
 
 TEST_P(ResourceProviderTest, TransferSoftwareResources) {
-  if (GetParam() != ResourceProvider::Bitmap)
+  if (GetParam() != ResourceProvider::RESOURCE_TYPE_BITMAP)
     return;
 
   gfx::Size size(1, 1);
@@ -967,15 +996,14 @@
   ASSERT_EQ(4U, pixel_size);
 
   ResourceProvider::ResourceId id1 = child_resource_provider_->CreateResource(
-      size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureHintImmutable, format);
+      size, GL_CLAMP_TO_EDGE, ResourceProvider::TEXTURE_HINT_IMMUTABLE, format);
   uint8_t data1[4] = { 1, 2, 3, 4 };
-  gfx::Rect rect(size);
-  child_resource_provider_->SetPixels(id1, data1, rect, rect, gfx::Vector2d());
+  child_resource_provider_->CopyToResource(id1, data1, size);
 
   ResourceProvider::ResourceId id2 = child_resource_provider_->CreateResource(
-      size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureHintImmutable, format);
+      size, GL_CLAMP_TO_EDGE, ResourceProvider::TEXTURE_HINT_IMMUTABLE, format);
   uint8_t data2[4] = { 5, 5, 5, 5 };
-  child_resource_provider_->SetPixels(id2, data2, rect, rect, gfx::Vector2d());
+  child_resource_provider_->CopyToResource(id2, data2, size);
 
   scoped_ptr<SharedBitmap> shared_bitmap(CreateAndFillSharedBitmap(
       shared_bitmap_manager_.get(), gfx::Size(1, 1), 0));
@@ -1143,7 +1171,7 @@
 }
 
 TEST_P(ResourceProviderTest, TransferGLToSoftware) {
-  if (GetParam() != ResourceProvider::Bitmap)
+  if (GetParam() != ResourceProvider::RESOURCE_TYPE_BITMAP)
     return;
 
   scoped_ptr<ResourceProviderContext> child_context_owned(
@@ -1169,10 +1197,9 @@
   ASSERT_EQ(4U, pixel_size);
 
   ResourceProvider::ResourceId id1 = child_resource_provider->CreateResource(
-      size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureHintImmutable, format);
+      size, GL_CLAMP_TO_EDGE, ResourceProvider::TEXTURE_HINT_IMMUTABLE, format);
   uint8_t data1[4] = { 1, 2, 3, 4 };
-  gfx::Rect rect(size);
-  child_resource_provider->SetPixels(id1, data1, rect, rect, gfx::Vector2d());
+  child_resource_provider->CopyToResource(id1, data1, size);
 
   ReturnedResourceArray returned_to_child;
   int child_id =
@@ -1207,7 +1234,7 @@
 }
 
 TEST_P(ResourceProviderTest, TransferInvalidSoftware) {
-  if (GetParam() != ResourceProvider::Bitmap)
+  if (GetParam() != ResourceProvider::RESOURCE_TYPE_BITMAP)
     return;
 
   gfx::Size size(1, 1);
@@ -1216,10 +1243,9 @@
   ASSERT_EQ(4U, pixel_size);
 
   ResourceProvider::ResourceId id1 = child_resource_provider_->CreateResource(
-      size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureHintImmutable, format);
+      size, GL_CLAMP_TO_EDGE, ResourceProvider::TEXTURE_HINT_IMMUTABLE, format);
   uint8_t data1[4] = { 1, 2, 3, 4 };
-  gfx::Rect rect(size);
-  child_resource_provider_->SetPixels(id1, data1, rect, rect, gfx::Vector2d());
+  child_resource_provider_->CopyToResource(id1, data1, size);
 
   ReturnedResourceArray returned_to_child;
   int child_id =
@@ -1264,15 +1290,14 @@
   ASSERT_EQ(4U, pixel_size);
 
   ResourceProvider::ResourceId id1 = child_resource_provider_->CreateResource(
-      size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureHintImmutable, format);
+      size, GL_CLAMP_TO_EDGE, ResourceProvider::TEXTURE_HINT_IMMUTABLE, format);
   uint8_t data1[4] = { 1, 2, 3, 4 };
-  gfx::Rect rect(size);
-  child_resource_provider_->SetPixels(id1, data1, rect, rect, gfx::Vector2d());
+  child_resource_provider_->CopyToResource(id1, data1, size);
 
   ResourceProvider::ResourceId id2 = child_resource_provider_->CreateResource(
-      size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureHintImmutable, format);
+      size, GL_CLAMP_TO_EDGE, ResourceProvider::TEXTURE_HINT_IMMUTABLE, format);
   uint8_t data2[4] = {5, 5, 5, 5};
-  child_resource_provider_->SetPixels(id2, data2, rect, rect, gfx::Vector2d());
+  child_resource_provider_->CopyToResource(id2, data2, size);
 
   ReturnedResourceArray returned_to_child;
   int child_id =
@@ -1286,7 +1311,7 @@
     child_resource_provider_->PrepareSendToParent(resource_ids_to_transfer,
                                                   &list);
     ASSERT_EQ(2u, list.size());
-    if (GetParam() == ResourceProvider::GLTexture) {
+    if (GetParam() == ResourceProvider::RESOURCE_TYPE_GL_TEXTURE) {
       EXPECT_NE(0u, list[0].mailbox_holder.sync_point);
       EXPECT_NE(0u, list[1].mailbox_holder.sync_point);
     }
@@ -1316,7 +1341,7 @@
     resource_provider_->PrepareSendToParent(resource_ids_to_transfer, &list);
 
     ASSERT_EQ(2u, list.size());
-    if (GetParam() == ResourceProvider::GLTexture) {
+    if (GetParam() == ResourceProvider::RESOURCE_TYPE_GL_TEXTURE) {
       EXPECT_NE(0u, list[0].mailbox_holder.sync_point);
       EXPECT_NE(0u, list[1].mailbox_holder.sync_point);
     }
@@ -1342,7 +1367,7 @@
 
     EXPECT_EQ(0u, resource_provider_->num_resources());
     ASSERT_EQ(2u, returned_to_child.size());
-    if (GetParam() == ResourceProvider::GLTexture) {
+    if (GetParam() == ResourceProvider::RESOURCE_TYPE_GL_TEXTURE) {
       EXPECT_NE(0u, returned_to_child[0].sync_point);
       EXPECT_NE(0u, returned_to_child[1].sync_point);
     }
@@ -1358,15 +1383,14 @@
   ASSERT_EQ(4U, pixel_size);
 
   ResourceProvider::ResourceId id1 = child_resource_provider_->CreateResource(
-      size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureHintImmutable, format);
+      size, GL_CLAMP_TO_EDGE, ResourceProvider::TEXTURE_HINT_IMMUTABLE, format);
   uint8_t data1[4] = {1, 2, 3, 4};
-  gfx::Rect rect(size);
-  child_resource_provider_->SetPixels(id1, data1, rect, rect, gfx::Vector2d());
+  child_resource_provider_->CopyToResource(id1, data1, size);
 
   ResourceProvider::ResourceId id2 = child_resource_provider_->CreateResource(
-      size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureHintImmutable, format);
+      size, GL_CLAMP_TO_EDGE, ResourceProvider::TEXTURE_HINT_IMMUTABLE, format);
   uint8_t data2[4] = {5, 5, 5, 5};
-  child_resource_provider_->SetPixels(id2, data2, rect, rect, gfx::Vector2d());
+  child_resource_provider_->CopyToResource(id2, data2, size);
 
   ReturnedResourceArray returned_to_child;
   int child_id =
@@ -1380,7 +1404,7 @@
     child_resource_provider_->PrepareSendToParent(resource_ids_to_transfer,
                                                   &list);
     ASSERT_EQ(2u, list.size());
-    if (GetParam() == ResourceProvider::GLTexture) {
+    if (GetParam() == ResourceProvider::RESOURCE_TYPE_GL_TEXTURE) {
       EXPECT_NE(0u, list[0].mailbox_holder.sync_point);
       EXPECT_NE(0u, list[1].mailbox_holder.sync_point);
     }
@@ -1410,7 +1434,7 @@
     resource_provider_->PrepareSendToParent(resource_ids_to_transfer, &list);
 
     ASSERT_EQ(2u, list.size());
-    if (GetParam() == ResourceProvider::GLTexture) {
+    if (GetParam() == ResourceProvider::RESOURCE_TYPE_GL_TEXTURE) {
       EXPECT_NE(0u, list[0].mailbox_holder.sync_point);
       EXPECT_NE(0u, list[1].mailbox_holder.sync_point);
     }
@@ -1445,7 +1469,7 @@
 
     EXPECT_EQ(1u, resource_provider_->num_resources());
     ASSERT_EQ(1u, returned_to_child.size());
-    if (GetParam() == ResourceProvider::GLTexture) {
+    if (GetParam() == ResourceProvider::RESOURCE_TYPE_GL_TEXTURE) {
       EXPECT_NE(0u, returned_to_child[0].sync_point);
     }
     EXPECT_FALSE(returned_to_child[0].lost);
@@ -1455,7 +1479,7 @@
     // lost at this point, and returned.
     resource_provider_ = nullptr;
     ASSERT_EQ(1u, returned_to_child.size());
-    if (GetParam() == ResourceProvider::GLTexture) {
+    if (GetParam() == ResourceProvider::RESOURCE_TYPE_GL_TEXTURE) {
       EXPECT_NE(0u, returned_to_child[0].sync_point);
     }
     EXPECT_TRUE(returned_to_child[0].lost);
@@ -1469,10 +1493,9 @@
   ASSERT_EQ(4U, pixel_size);
 
   ResourceProvider::ResourceId id = child_resource_provider_->CreateResource(
-      size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureHintImmutable, format);
+      size, GL_CLAMP_TO_EDGE, ResourceProvider::TEXTURE_HINT_IMMUTABLE, format);
   uint8_t data[4] = { 1, 2, 3, 4 };
-  gfx::Rect rect(size);
-  child_resource_provider_->SetPixels(id, data, rect, rect, gfx::Vector2d());
+  child_resource_provider_->CopyToResource(id, data, size);
 
   ReturnedResourceArray returned_to_child;
   int child_id =
@@ -1485,7 +1508,7 @@
     child_resource_provider_->PrepareSendToParent(resource_ids_to_transfer,
                                                   &list);
     ASSERT_EQ(1u, list.size());
-    if (GetParam() == ResourceProvider::GLTexture)
+    if (GetParam() == ResourceProvider::RESOURCE_TYPE_GL_TEXTURE)
       EXPECT_NE(0u, list[0].mailbox_holder.sync_point);
     EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id));
     resource_provider_->ReceiveFromChild(child_id, list);
@@ -1505,7 +1528,7 @@
     resource_provider_->DeclareUsedResourcesFromChild(child_id, no_resources);
 
     ASSERT_EQ(1u, returned_to_child.size());
-    if (GetParam() == ResourceProvider::GLTexture)
+    if (GetParam() == ResourceProvider::RESOURCE_TYPE_GL_TEXTURE)
       EXPECT_NE(0u, returned_to_child[0].sync_point);
     child_resource_provider_->ReceiveReturnsFromParent(returned_to_child);
   }
@@ -1519,10 +1542,9 @@
   ASSERT_EQ(4U, pixel_size);
 
   ResourceProvider::ResourceId id = child_resource_provider_->CreateResource(
-      size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureHintImmutable, format);
+      size, GL_CLAMP_TO_EDGE, ResourceProvider::TEXTURE_HINT_IMMUTABLE, format);
   uint8_t data[4] = {1, 2, 3, 4};
-  gfx::Rect rect(size);
-  child_resource_provider_->SetPixels(id, data, rect, rect, gfx::Vector2d());
+  child_resource_provider_->CopyToResource(id, data, size);
 
   ReturnedResourceArray returned_to_child;
   int child_id =
@@ -1672,7 +1694,8 @@
     ASSERT_EQ(4U, pixel_size);
 
     ResourceProvider::ResourceId id = child_resource_provider->CreateResource(
-        size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureHintImmutable, format);
+        size, GL_CLAMP_TO_EDGE, ResourceProvider::TEXTURE_HINT_IMMUTABLE,
+        format);
 
     // The new texture is created with GL_LINEAR.
     EXPECT_CALL(*child_context, bindTexture(GL_TEXTURE_2D, child_texture_id))
@@ -1695,10 +1718,9 @@
     Mock::VerifyAndClearExpectations(child_context);
 
     uint8_t data[4] = { 1, 2, 3, 4 };
-    gfx::Rect rect(size);
 
     EXPECT_CALL(*child_context, bindTexture(GL_TEXTURE_2D, child_texture_id));
-    child_resource_provider->SetPixels(id, data, rect, rect, gfx::Vector2d());
+    child_resource_provider->CopyToResource(id, data, size);
     Mock::VerifyAndClearExpectations(child_context);
 
     // The texture is set to |child_filter| in the child.
@@ -1798,20 +1820,20 @@
 };
 
 TEST_P(ResourceProviderTest, TextureFilters_ChildNearestParentLinear) {
-  if (GetParam() != ResourceProvider::GLTexture)
+  if (GetParam() != ResourceProvider::RESOURCE_TYPE_GL_TEXTURE)
     return;
   ResourceProviderTestTextureFilters::RunTest(GL_NEAREST, GL_LINEAR);
 }
 
 TEST_P(ResourceProviderTest, TextureFilters_ChildLinearParentNearest) {
-  if (GetParam() != ResourceProvider::GLTexture)
+  if (GetParam() != ResourceProvider::RESOURCE_TYPE_GL_TEXTURE)
     return;
   ResourceProviderTestTextureFilters::RunTest(GL_LINEAR, GL_NEAREST);
 }
 
 TEST_P(ResourceProviderTest, TransferMailboxResources) {
   // Other mailbox transfers tested elsewhere.
-  if (GetParam() != ResourceProvider::GLTexture)
+  if (GetParam() != ResourceProvider::RESOURCE_TYPE_GL_TEXTURE)
     return;
   unsigned texture = context()->createTexture();
   context()->bindTexture(GL_TEXTURE_2D, texture);
@@ -1943,13 +1965,12 @@
   ResourceFormat format = RGBA_8888;
   ResourceProvider::ResourceId resource =
       child_resource_provider_->CreateResource(
-          size,
-          GL_CLAMP_TO_EDGE,
-          ResourceProvider::TextureHintImmutable,
+          size, GL_CLAMP_TO_EDGE, ResourceProvider::TEXTURE_HINT_IMMUTABLE,
           format);
   child_resource_provider_->AllocateForTesting(resource);
   // Expect a GL resource to be lost.
-  bool should_lose_resource = GetParam() == ResourceProvider::GLTexture;
+  bool should_lose_resource =
+      GetParam() == ResourceProvider::RESOURCE_TYPE_GL_TEXTURE;
 
   ReturnedResourceArray returned_to_child;
   int child_id =
@@ -1999,9 +2020,7 @@
   ResourceFormat format = RGBA_8888;
   ResourceProvider::ResourceId resource =
       child_resource_provider_->CreateResource(
-          size,
-          GL_CLAMP_TO_EDGE,
-          ResourceProvider::TextureHintImmutable,
+          size, GL_CLAMP_TO_EDGE, ResourceProvider::TEXTURE_HINT_IMMUTABLE,
           format);
   child_resource_provider_->AllocateForTesting(resource);
 
@@ -2112,7 +2131,7 @@
     ASSERT_EQ(1u, returned_to_child.size());
     // Losing an output surface only loses hardware resources.
     EXPECT_EQ(returned_to_child[0].lost,
-              GetParam() == ResourceProvider::GLTexture);
+              GetParam() == ResourceProvider::RESOURCE_TYPE_GL_TEXTURE);
     child_resource_provider_->ReceiveReturnsFromParent(returned_to_child);
     returned_to_child.clear();
   }
@@ -2120,7 +2139,8 @@
   // Delete the resource in the child. Expect the resource to be lost if it's
   // a GL texture.
   child_resource_provider_->DeleteResource(resource);
-  EXPECT_EQ(lost_resource, GetParam() == ResourceProvider::GLTexture);
+  EXPECT_EQ(lost_resource,
+            GetParam() == ResourceProvider::RESOURCE_TYPE_GL_TEXTURE);
 }
 
 TEST_P(ResourceProviderTest, LostMailboxInGrandParent) {
@@ -2204,7 +2224,7 @@
 
   child_resource_provider_ = nullptr;
 
-  if (GetParam() == ResourceProvider::GLTexture) {
+  if (GetParam() == ResourceProvider::RESOURCE_TYPE_GL_TEXTURE) {
     EXPECT_LE(sync_point, release_sync_point);
   }
   EXPECT_TRUE(release_called);
@@ -2238,7 +2258,7 @@
 
 TEST_P(ResourceProviderTest, LostContext) {
   // TextureMailbox callbacks only exist for GL textures for now.
-  if (GetParam() != ResourceProvider::GLTexture)
+  if (GetParam() != ResourceProvider::RESOURCE_TYPE_GL_TEXTURE)
     return;
   unsigned texture = context()->createTexture();
   context()->bindTexture(GL_TEXTURE_2D, texture);
@@ -2274,7 +2294,7 @@
 
 TEST_P(ResourceProviderTest, ScopedSampler) {
   // Sampling is only supported for GL textures.
-  if (GetParam() != ResourceProvider::GLTexture)
+  if (GetParam() != ResourceProvider::RESOURCE_TYPE_GL_TEXTURE)
     return;
 
   scoped_ptr<TextureStateTrackingContext> context_owned(
@@ -2300,7 +2320,7 @@
   int texture_id = 1;
 
   ResourceProvider::ResourceId id = resource_provider->CreateResource(
-      size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureHintImmutable, format);
+      size, GL_CLAMP_TO_EDGE, ResourceProvider::TEXTURE_HINT_IMMUTABLE, format);
 
   // Check that the texture gets created with the right sampler settings.
   EXPECT_CALL(*context, bindTexture(GL_TEXTURE_2D, texture_id))
@@ -2361,7 +2381,7 @@
 
 TEST_P(ResourceProviderTest, ManagedResource) {
   // Sampling is only supported for GL textures.
-  if (GetParam() != ResourceProvider::GLTexture)
+  if (GetParam() != ResourceProvider::RESOURCE_TYPE_GL_TEXTURE)
     return;
 
   scoped_ptr<TextureStateTrackingContext> context_owned(
@@ -2388,11 +2408,8 @@
 
   // Check that the texture gets created with the right sampler settings.
   ResourceProvider::ResourceId id = resource_provider->CreateManagedResource(
-      size,
-      GL_TEXTURE_2D,
-      GL_CLAMP_TO_EDGE,
-      ResourceProvider::TextureHintImmutable,
-      format);
+      size, GL_TEXTURE_2D, GL_CLAMP_TO_EDGE,
+      ResourceProvider::TEXTURE_HINT_IMMUTABLE, format);
   EXPECT_CALL(*context, bindTexture(GL_TEXTURE_2D, texture_id));
   EXPECT_CALL(*context,
               texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
@@ -2416,7 +2433,7 @@
 
 TEST_P(ResourceProviderTest, TextureWrapMode) {
   // Sampling is only supported for GL textures.
-  if (GetParam() != ResourceProvider::GLTexture)
+  if (GetParam() != ResourceProvider::RESOURCE_TYPE_GL_TEXTURE)
     return;
 
   scoped_ptr<TextureStateTrackingContext> context_owned(
@@ -2445,12 +2462,8 @@
     GLint wrap_mode = texture_id == 1 ? GL_CLAMP_TO_EDGE : GL_REPEAT;
     // Check that the texture gets created with the right sampler settings.
     ResourceProvider::ResourceId id = resource_provider->CreateGLTexture(
-        size,
-        GL_TEXTURE_2D,
-        texture_pool,
-        wrap_mode,
-        ResourceProvider::TextureHintImmutable,
-        format);
+        size, GL_TEXTURE_2D, texture_pool, wrap_mode,
+        ResourceProvider::TEXTURE_HINT_IMMUTABLE, format);
     EXPECT_CALL(*context, bindTexture(GL_TEXTURE_2D, texture_id));
     EXPECT_CALL(*context,
                 texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
@@ -2473,7 +2486,7 @@
 
 TEST_P(ResourceProviderTest, TextureHint) {
   // Sampling is only supported for GL textures.
-  if (GetParam() != ResourceProvider::GLTexture)
+  if (GetParam() != ResourceProvider::RESOURCE_TYPE_GL_TEXTURE)
     return;
 
   scoped_ptr<TextureStateTrackingContext> context_owned(
@@ -2501,10 +2514,10 @@
   GLenum texture_pool = GL_TEXTURE_POOL_UNMANAGED_CHROMIUM;
 
   const ResourceProvider::TextureHint hints[4] = {
-      ResourceProvider::TextureHintDefault,
-      ResourceProvider::TextureHintImmutable,
-      ResourceProvider::TextureHintFramebuffer,
-      ResourceProvider::TextureHintImmutableFramebuffer,
+      ResourceProvider::TEXTURE_HINT_DEFAULT,
+      ResourceProvider::TEXTURE_HINT_IMMUTABLE,
+      ResourceProvider::TEXTURE_HINT_FRAMEBUFFER,
+      ResourceProvider::TEXTURE_HINT_IMMUTABLE_FRAMEBUFFER,
   };
   for (GLuint texture_id = 1; texture_id <= arraysize(hints); ++texture_id) {
     // Check that the texture gets created with the right sampler settings.
@@ -2530,9 +2543,9 @@
                 texParameteri(GL_TEXTURE_2D,
                               GL_TEXTURE_POOL_CHROMIUM,
                               GL_TEXTURE_POOL_UNMANAGED_CHROMIUM));
-    // Check only TextureHintFramebuffer set GL_TEXTURE_USAGE_ANGLE.
+    // Check only TEXTURE_HINT_FRAMEBUFFER set GL_TEXTURE_USAGE_ANGLE.
     bool is_framebuffer_hint =
-        hints[texture_id - 1] & ResourceProvider::TextureHintFramebuffer;
+        hints[texture_id - 1] & ResourceProvider::TEXTURE_HINT_FRAMEBUFFER;
     EXPECT_CALL(*context,
                 texParameteri(GL_TEXTURE_2D,
                               GL_TEXTURE_USAGE_ANGLE,
@@ -2546,7 +2559,7 @@
 }
 
 TEST_P(ResourceProviderTest, TextureMailbox_SharedMemory) {
-  if (GetParam() != ResourceProvider::Bitmap)
+  if (GetParam() != ResourceProvider::RESOURCE_TYPE_BITMAP)
     return;
 
   gfx::Size size(64, 64);
@@ -2700,7 +2713,7 @@
 
 TEST_P(ResourceProviderTest, TextureMailbox_GLTexture2D_LinearToLinear) {
   // Mailboxing is only supported for GL textures.
-  if (GetParam() != ResourceProvider::GLTexture)
+  if (GetParam() != ResourceProvider::RESOURCE_TYPE_GL_TEXTURE)
     return;
 
   ResourceProviderTestTextureMailboxGLFilters::RunTest(
@@ -2713,7 +2726,7 @@
 
 TEST_P(ResourceProviderTest, TextureMailbox_GLTexture2D_NearestToNearest) {
   // Mailboxing is only supported for GL textures.
-  if (GetParam() != ResourceProvider::GLTexture)
+  if (GetParam() != ResourceProvider::RESOURCE_TYPE_GL_TEXTURE)
     return;
 
   ResourceProviderTestTextureMailboxGLFilters::RunTest(
@@ -2726,7 +2739,7 @@
 
 TEST_P(ResourceProviderTest, TextureMailbox_GLTexture2D_NearestToLinear) {
   // Mailboxing is only supported for GL textures.
-  if (GetParam() != ResourceProvider::GLTexture)
+  if (GetParam() != ResourceProvider::RESOURCE_TYPE_GL_TEXTURE)
     return;
 
   ResourceProviderTestTextureMailboxGLFilters::RunTest(
@@ -2739,7 +2752,7 @@
 
 TEST_P(ResourceProviderTest, TextureMailbox_GLTexture2D_LinearToNearest) {
   // Mailboxing is only supported for GL textures.
-  if (GetParam() != ResourceProvider::GLTexture)
+  if (GetParam() != ResourceProvider::RESOURCE_TYPE_GL_TEXTURE)
     return;
 
   ResourceProviderTestTextureMailboxGLFilters::RunTest(
@@ -2752,7 +2765,7 @@
 
 TEST_P(ResourceProviderTest, TextureMailbox_GLTextureExternalOES) {
   // Mailboxing is only supported for GL textures.
-  if (GetParam() != ResourceProvider::GLTexture)
+  if (GetParam() != ResourceProvider::RESOURCE_TYPE_GL_TEXTURE)
     return;
 
   scoped_ptr<TextureStateTrackingContext> context_owned(
@@ -2827,7 +2840,7 @@
 TEST_P(ResourceProviderTest,
        TextureMailbox_WaitSyncPointIfNeeded_WithSyncPoint) {
   // Mailboxing is only supported for GL textures.
-  if (GetParam() != ResourceProvider::GLTexture)
+  if (GetParam() != ResourceProvider::RESOURCE_TYPE_GL_TEXTURE)
     return;
 
   scoped_ptr<TextureStateTrackingContext> context_owned(
@@ -2886,7 +2899,7 @@
 
 TEST_P(ResourceProviderTest, TextureMailbox_WaitSyncPointIfNeeded_NoSyncPoint) {
   // Mailboxing is only supported for GL textures.
-  if (GetParam() != ResourceProvider::GLTexture)
+  if (GetParam() != ResourceProvider::RESOURCE_TYPE_GL_TEXTURE)
     return;
 
   scoped_ptr<TextureStateTrackingContext> context_owned(
@@ -3013,7 +3026,7 @@
 
 TEST_P(ResourceProviderTest, TextureAllocation) {
   // Only for GL textures.
-  if (GetParam() != ResourceProvider::GLTexture)
+  if (GetParam() != ResourceProvider::RESOURCE_TYPE_GL_TEXTURE)
     return;
   scoped_ptr<AllocationTrackingContext3D> context_owned(
       new StrictMock<AllocationTrackingContext3D>);
@@ -3035,7 +3048,6 @@
 
   gfx::Size size(2, 2);
   gfx::Vector2d offset(0, 0);
-  gfx::Rect rect(0, 0, 2, 2);
   ResourceFormat format = RGBA_8888;
   ResourceProvider::ResourceId id = 0;
   uint8_t pixels[16] = { 0 };
@@ -3043,7 +3055,7 @@
 
   // Lazy allocation. Don't allocate when creating the resource.
   id = resource_provider->CreateResource(
-      size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureHintImmutable, format);
+      size, GL_CLAMP_TO_EDGE, ResourceProvider::TEXTURE_HINT_IMMUTABLE, format);
 
   EXPECT_CALL(*context, NextTextureId()).WillOnce(Return(texture_id));
   EXPECT_CALL(*context, bindTexture(GL_TEXTURE_2D, texture_id)).Times(1);
@@ -3056,13 +3068,13 @@
 
   // Do allocate when we set the pixels.
   id = resource_provider->CreateResource(
-      size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureHintImmutable, format);
+      size, GL_CLAMP_TO_EDGE, ResourceProvider::TEXTURE_HINT_IMMUTABLE, format);
 
   EXPECT_CALL(*context, NextTextureId()).WillOnce(Return(texture_id));
   EXPECT_CALL(*context, bindTexture(GL_TEXTURE_2D, texture_id)).Times(3);
   EXPECT_CALL(*context, texImage2D(_, _, _, 2, 2, _, _, _, _)).Times(1);
   EXPECT_CALL(*context, texSubImage2D(_, _, _, _, 2, 2, _, _, _)).Times(1);
-  resource_provider->SetPixels(id, pixels, rect, rect, offset);
+  resource_provider->CopyToResource(id, pixels, size);
 
   EXPECT_CALL(*context, RetireTextureId(texture_id)).Times(1);
   resource_provider->DeleteResource(id);
@@ -3071,7 +3083,7 @@
 
   // Same for async version.
   id = resource_provider->CreateResource(
-      size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureHintImmutable, format);
+      size, GL_CLAMP_TO_EDGE, ResourceProvider::TEXTURE_HINT_IMMUTABLE, format);
   resource_provider->AcquirePixelBuffer(id);
 
   EXPECT_CALL(*context, NextTextureId()).WillOnce(Return(texture_id));
@@ -3091,7 +3103,7 @@
 
 TEST_P(ResourceProviderTest, TextureAllocationHint) {
   // Only for GL textures.
-  if (GetParam() != ResourceProvider::GLTexture)
+  if (GetParam() != ResourceProvider::RESOURCE_TYPE_GL_TEXTURE)
     return;
   scoped_ptr<AllocationTrackingContext3D> context_owned(
       new StrictMock<AllocationTrackingContext3D>);
@@ -3117,10 +3129,10 @@
 
   const ResourceFormat formats[2] = {RGBA_8888, BGRA_8888};
   const ResourceProvider::TextureHint hints[4] = {
-      ResourceProvider::TextureHintDefault,
-      ResourceProvider::TextureHintImmutable,
-      ResourceProvider::TextureHintFramebuffer,
-      ResourceProvider::TextureHintImmutableFramebuffer,
+      ResourceProvider::TEXTURE_HINT_DEFAULT,
+      ResourceProvider::TEXTURE_HINT_IMMUTABLE,
+      ResourceProvider::TEXTURE_HINT_FRAMEBUFFER,
+      ResourceProvider::TEXTURE_HINT_IMMUTABLE_FRAMEBUFFER,
   };
   for (size_t i = 0; i < arraysize(formats); ++i) {
     for (GLuint texture_id = 1; texture_id <= arraysize(hints); ++texture_id) {
@@ -3131,7 +3143,7 @@
       EXPECT_CALL(*context, NextTextureId()).WillOnce(Return(texture_id));
       EXPECT_CALL(*context, bindTexture(GL_TEXTURE_2D, texture_id)).Times(2);
       bool is_immutable_hint =
-          hints[texture_id - 1] & ResourceProvider::TextureHintImmutable;
+          hints[texture_id - 1] & ResourceProvider::TEXTURE_HINT_IMMUTABLE;
       bool support_immutable_texture =
           is_immutable_hint && formats[i] == RGBA_8888;
       EXPECT_CALL(*context, texStorage2DEXT(_, _, _, 2, 2))
@@ -3150,7 +3162,7 @@
 
 TEST_P(ResourceProviderTest, TextureAllocationHint_BGRA) {
   // Only for GL textures.
-  if (GetParam() != ResourceProvider::GLTexture)
+  if (GetParam() != ResourceProvider::RESOURCE_TYPE_GL_TEXTURE)
     return;
   scoped_ptr<AllocationTrackingContext3D> context_owned(
       new StrictMock<AllocationTrackingContext3D>);
@@ -3177,10 +3189,10 @@
   const ResourceFormat formats[2] = {RGBA_8888, BGRA_8888};
 
   const ResourceProvider::TextureHint hints[4] = {
-      ResourceProvider::TextureHintDefault,
-      ResourceProvider::TextureHintImmutable,
-      ResourceProvider::TextureHintFramebuffer,
-      ResourceProvider::TextureHintImmutableFramebuffer,
+      ResourceProvider::TEXTURE_HINT_DEFAULT,
+      ResourceProvider::TEXTURE_HINT_IMMUTABLE,
+      ResourceProvider::TEXTURE_HINT_FRAMEBUFFER,
+      ResourceProvider::TEXTURE_HINT_IMMUTABLE_FRAMEBUFFER,
   };
   for (size_t i = 0; i < arraysize(formats); ++i) {
     for (GLuint texture_id = 1; texture_id <= arraysize(hints); ++texture_id) {
@@ -3191,7 +3203,7 @@
       EXPECT_CALL(*context, NextTextureId()).WillOnce(Return(texture_id));
       EXPECT_CALL(*context, bindTexture(GL_TEXTURE_2D, texture_id)).Times(2);
       bool is_immutable_hint =
-          hints[texture_id - 1] & ResourceProvider::TextureHintImmutable;
+          hints[texture_id - 1] & ResourceProvider::TEXTURE_HINT_IMMUTABLE;
       EXPECT_CALL(*context, texStorage2DEXT(_, _, _, 2, 2))
           .Times(is_immutable_hint ? 1 : 0);
       EXPECT_CALL(*context, texImage2D(_, _, _, 2, 2, _, _, _, _))
@@ -3207,7 +3219,7 @@
 }
 
 TEST_P(ResourceProviderTest, PixelBuffer_GLTexture) {
-  if (GetParam() != ResourceProvider::GLTexture)
+  if (GetParam() != ResourceProvider::RESOURCE_TYPE_GL_TEXTURE)
     return;
   scoped_ptr<AllocationTrackingContext3D> context_owned(
       new StrictMock<AllocationTrackingContext3D>);
@@ -3233,7 +3245,7 @@
                                1));
 
   id = resource_provider->CreateResource(
-      size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureHintImmutable, format);
+      size, GL_CLAMP_TO_EDGE, ResourceProvider::TEXTURE_HINT_IMMUTABLE, format);
   resource_provider->AcquirePixelBuffer(id);
 
   EXPECT_CALL(*context, NextTextureId()).WillOnce(Return(texture_id));
@@ -3254,7 +3266,7 @@
 
 TEST_P(ResourceProviderTest, ForcingAsyncUploadToComplete) {
   // Only for GL textures.
-  if (GetParam() != ResourceProvider::GLTexture)
+  if (GetParam() != ResourceProvider::RESOURCE_TYPE_GL_TEXTURE)
     return;
   scoped_ptr<AllocationTrackingContext3D> context_owned(
       new StrictMock<AllocationTrackingContext3D>);
@@ -3280,7 +3292,7 @@
                                1));
 
   id = resource_provider->CreateResource(
-      size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureHintImmutable, format);
+      size, GL_CLAMP_TO_EDGE, ResourceProvider::TEXTURE_HINT_IMMUTABLE, format);
   resource_provider->AcquirePixelBuffer(id);
 
   EXPECT_CALL(*context, NextTextureId()).WillOnce(Return(texture_id));
@@ -3329,7 +3341,7 @@
   EXPECT_CALL(*context, NextTextureId()).WillRepeatedly(Return(texture_id));
 
   id = resource_provider->CreateResource(
-      size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureHintImmutable, format);
+      size, GL_CLAMP_TO_EDGE, ResourceProvider::TEXTURE_HINT_IMMUTABLE, format);
   context->loseContextCHROMIUM(GL_GUILTY_CONTEXT_RESET_ARB,
                                GL_INNOCENT_CONTEXT_RESET_ARB);
 
@@ -3343,7 +3355,7 @@
 
 TEST_P(ResourceProviderTest, Image_GLTexture) {
   // Only for GL textures.
-  if (GetParam() != ResourceProvider::GLTexture)
+  if (GetParam() != ResourceProvider::RESOURCE_TYPE_GL_TEXTURE)
     return;
   scoped_ptr<AllocationTrackingContext3D> context_owned(
       new StrictMock<AllocationTrackingContext3D>);
@@ -3372,7 +3384,7 @@
                                1));
 
   id = resource_provider->CreateResource(
-      size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureHintImmutable, format);
+      size, GL_CLAMP_TO_EDGE, ResourceProvider::TEXTURE_HINT_IMMUTABLE, format);
 
   EXPECT_CALL(*context, createImageCHROMIUM(_, kWidth, kHeight, GL_RGBA))
       .WillOnce(Return(kImageId))
@@ -3427,7 +3439,7 @@
 }
 
 TEST_P(ResourceProviderTest, CopyResource_GLTexture) {
-  if (GetParam() != ResourceProvider::GLTexture)
+  if (GetParam() != ResourceProvider::RESOURCE_TYPE_GL_TEXTURE)
     return;
   scoped_ptr<AllocationTrackingContext3D> context_owned(
       new StrictMock<AllocationTrackingContext3D>);
@@ -3459,7 +3471,7 @@
                                1));
 
   source_id = resource_provider->CreateResource(
-      size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureHintImmutable, format);
+      size, GL_CLAMP_TO_EDGE, ResourceProvider::TEXTURE_HINT_IMMUTABLE, format);
 
   EXPECT_CALL(*context, createImageCHROMIUM(_, kWidth, kHeight, GL_RGBA))
       .WillOnce(Return(kImageId))
@@ -3472,7 +3484,7 @@
   Mock::VerifyAndClearExpectations(context);
 
   dest_id = resource_provider->CreateResource(
-      size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureHintImmutable, format);
+      size, GL_CLAMP_TO_EDGE, ResourceProvider::TEXTURE_HINT_IMMUTABLE, format);
 
   EXPECT_CALL(*context, NextTextureId())
       .WillOnce(Return(kDestTextureId))
@@ -3521,7 +3533,8 @@
   output_surface->InitializeAndSetContext3d(context_provider, nullptr);
   resource_provider->InitializeGL();
 
-  CheckCreateResource(ResourceProvider::GLTexture, resource_provider, context);
+  CheckCreateResource(ResourceProvider::RESOURCE_TYPE_GL_TEXTURE,
+                      resource_provider, context);
 }
 
 TEST(ResourceProviderTest, BasicInitializeGLSoftware) {
@@ -3544,7 +3557,8 @@
                                false,
                                1));
 
-  CheckCreateResource(ResourceProvider::Bitmap, resource_provider.get(), NULL);
+  CheckCreateResource(ResourceProvider::RESOURCE_TYPE_BITMAP,
+                      resource_provider.get(), NULL);
 
   InitializeGLAndCheck(shared_data.get(),
                        resource_provider.get(),
@@ -3552,7 +3566,8 @@
 
   resource_provider->InitializeSoftware();
   output_surface->ReleaseGL();
-  CheckCreateResource(ResourceProvider::Bitmap, resource_provider.get(), NULL);
+  CheckCreateResource(ResourceProvider::RESOURCE_TYPE_BITMAP,
+                      resource_provider.get(), NULL);
 
   InitializeGLAndCheck(shared_data.get(),
                        resource_provider.get(),
@@ -3560,7 +3575,7 @@
 }
 
 TEST_P(ResourceProviderTest, CompressedTextureETC1Allocate) {
-  if (GetParam() != ResourceProvider::GLTexture)
+  if (GetParam() != ResourceProvider::RESOURCE_TYPE_GL_TEXTURE)
     return;
 
   scoped_ptr<AllocationTrackingContext3D> context_owned(
@@ -3585,7 +3600,7 @@
   int texture_id = 123;
 
   ResourceProvider::ResourceId id = resource_provider->CreateResource(
-      size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureHintImmutable, ETC1);
+      size, GL_CLAMP_TO_EDGE, ResourceProvider::TEXTURE_HINT_IMMUTABLE, ETC1);
   EXPECT_NE(0u, id);
   EXPECT_CALL(*context, NextTextureId()).WillOnce(Return(texture_id));
   EXPECT_CALL(*context, bindTexture(GL_TEXTURE_2D, texture_id)).Times(2);
@@ -3595,8 +3610,8 @@
   resource_provider->DeleteResource(id);
 }
 
-TEST_P(ResourceProviderTest, CompressedTextureETC1SetPixels) {
-  if (GetParam() != ResourceProvider::GLTexture)
+TEST_P(ResourceProviderTest, CompressedTextureETC1Upload) {
+  if (GetParam() != ResourceProvider::RESOURCE_TYPE_GL_TEXTURE)
     return;
 
   scoped_ptr<AllocationTrackingContext3D> context_owned(
@@ -3622,15 +3637,14 @@
   uint8_t pixels[8];
 
   ResourceProvider::ResourceId id = resource_provider->CreateResource(
-      size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureHintImmutable, ETC1);
+      size, GL_CLAMP_TO_EDGE, ResourceProvider::TEXTURE_HINT_IMMUTABLE, ETC1);
   EXPECT_NE(0u, id);
   EXPECT_CALL(*context, NextTextureId()).WillOnce(Return(texture_id));
   EXPECT_CALL(*context, bindTexture(GL_TEXTURE_2D, texture_id)).Times(3);
   EXPECT_CALL(*context,
               compressedTexImage2D(
                   _, 0, _, size.width(), size.height(), _, _, _)).Times(1);
-  resource_provider->SetPixels(
-      id, pixels, gfx::Rect(size), gfx::Rect(size), gfx::Vector2d(0, 0));
+  resource_provider->CopyToResource(id, pixels, size);
 
   EXPECT_CALL(*context, RetireTextureId(texture_id)).Times(1);
   resource_provider->DeleteResource(id);
@@ -3639,7 +3653,8 @@
 INSTANTIATE_TEST_CASE_P(
     ResourceProviderTests,
     ResourceProviderTest,
-    ::testing::Values(ResourceProvider::GLTexture, ResourceProvider::Bitmap));
+    ::testing::Values(ResourceProvider::RESOURCE_TYPE_GL_TEXTURE,
+                      ResourceProvider::RESOURCE_TYPE_BITMAP));
 
 class TextureIdAllocationTrackingContext : public TestWebGraphicsContext3D {
  public:
@@ -3681,7 +3696,8 @@
                                  kTextureAllocationChunkSize));
 
     ResourceProvider::ResourceId id = resource_provider->CreateResource(
-        size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureHintImmutable, format);
+        size, GL_CLAMP_TO_EDGE, ResourceProvider::TEXTURE_HINT_IMMUTABLE,
+        format);
     resource_provider->AllocateForTesting(id);
     Mock::VerifyAndClearExpectations(context);
 
@@ -3701,7 +3717,8 @@
                                  kTextureAllocationChunkSize));
 
     ResourceProvider::ResourceId id = resource_provider->CreateResource(
-        size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureHintImmutable, format);
+        size, GL_CLAMP_TO_EDGE, ResourceProvider::TEXTURE_HINT_IMMUTABLE,
+        format);
     resource_provider->AllocateForTesting(id);
     Mock::VerifyAndClearExpectations(context);
 
diff --git a/cc/resources/scoped_resource.cc b/cc/resources/scoped_resource.cc
index 3b135590f..e701182 100644
--- a/cc/resources/scoped_resource.cc
+++ b/cc/resources/scoped_resource.cc
@@ -38,10 +38,7 @@
 
   set_dimensions(size, format);
   set_id(resource_provider_->CreateManagedResource(
-      size,
-      target,
-      GL_CLAMP_TO_EDGE,
-      ResourceProvider::TextureHintImmutable,
+      size, target, GL_CLAMP_TO_EDGE, ResourceProvider::TEXTURE_HINT_IMMUTABLE,
       format));
 
 #if DCHECK_IS_ON()
diff --git a/cc/resources/scoped_resource_unittest.cc b/cc/resources/scoped_resource_unittest.cc
index a141218..b95d814 100644
--- a/cc/resources/scoped_resource_unittest.cc
+++ b/cc/resources/scoped_resource_unittest.cc
@@ -57,8 +57,8 @@
                                1));
   scoped_ptr<ScopedResource> texture =
       ScopedResource::Create(resource_provider.get());
-  texture->Allocate(
-      gfx::Size(30, 30), ResourceProvider::TextureHintImmutable, RGBA_8888);
+  texture->Allocate(gfx::Size(30, 30), ResourceProvider::TEXTURE_HINT_IMMUTABLE,
+                    RGBA_8888);
 
   // The texture has an allocated byte-size now.
   size_t expected_bytes = 30 * 30 * 4;
@@ -89,8 +89,8 @@
         ScopedResource::Create(resource_provider.get());
 
     EXPECT_EQ(0u, resource_provider->num_resources());
-    texture->Allocate(
-        gfx::Size(30, 30), ResourceProvider::TextureHintImmutable, RGBA_8888);
+    texture->Allocate(gfx::Size(30, 30),
+                      ResourceProvider::TEXTURE_HINT_IMMUTABLE, RGBA_8888);
     EXPECT_LT(0u, texture->id());
     EXPECT_EQ(1u, resource_provider->num_resources());
   }
@@ -100,8 +100,8 @@
     scoped_ptr<ScopedResource> texture =
         ScopedResource::Create(resource_provider.get());
     EXPECT_EQ(0u, resource_provider->num_resources());
-    texture->Allocate(
-        gfx::Size(30, 30), ResourceProvider::TextureHintImmutable, RGBA_8888);
+    texture->Allocate(gfx::Size(30, 30),
+                      ResourceProvider::TEXTURE_HINT_IMMUTABLE, RGBA_8888);
     EXPECT_LT(0u, texture->id());
     EXPECT_EQ(1u, resource_provider->num_resources());
     texture->Free();
diff --git a/cc/resources/texture_uploader.cc b/cc/resources/texture_uploader.cc
index 3d0f6816..601fa8f 100644
--- a/cc/resources/texture_uploader.cc
+++ b/cc/resources/texture_uploader.cc
@@ -146,14 +146,7 @@
   if (is_full_upload)
     BeginQuery();
 
-  if (format == ETC1) {
-    // ETC1 does not support subimage uploads.
-    DCHECK(is_full_upload);
-    UploadWithTexImageETC1(image, size);
-  } else {
-    UploadWithMapTexSubImage(
-        image, image_rect, source_rect, dest_offset, format);
-  }
+  UploadWithMapTexSubImage(image, image_rect, source_rect, dest_offset, format);
 
   if (is_full_upload)
     EndQuery();
@@ -291,22 +284,6 @@
   gl_->UnmapTexSubImage2DCHROMIUM(pixel_dest);
 }
 
-void TextureUploader::UploadWithTexImageETC1(const uint8* image,
-                                             const gfx::Size& size) {
-  TRACE_EVENT0("cc", "TextureUploader::UploadWithTexImageETC1");
-  DCHECK_EQ(0, size.width() % 4);
-  DCHECK_EQ(0, size.height() % 4);
-
-  gl_->CompressedTexImage2D(GL_TEXTURE_2D,
-                            0,
-                            GLInternalFormat(ETC1),
-                            size.width(),
-                            size.height(),
-                            0,
-                            Resource::MemorySizeBytes(size, ETC1),
-                            image);
-}
-
 void TextureUploader::ProcessQueries() {
   while (!pending_queries_.empty()) {
     if (pending_queries_.front()->IsPending())
diff --git a/cc/resources/tile.cc b/cc/resources/tile.cc
index 39d48a8..1576047 100644
--- a/cc/resources/tile.cc
+++ b/cc/resources/tile.cc
@@ -39,7 +39,7 @@
       id_(s_next_id_++),
       scheduled_priority_(0) {
   set_raster_source(raster_source);
-  for (int i = 0; i < NUM_TREES; i++)
+  for (int i = 0; i <= LAST_TREE; i++)
     is_occluded_[i] = false;
 }
 
diff --git a/cc/resources/tile.h b/cc/resources/tile.h
index 698f43d..c3ca623a 100644
--- a/cc/resources/tile.h
+++ b/cc/resources/tile.h
@@ -88,7 +88,7 @@
 
   bool HasResource() const { return draw_info_.has_resource(); }
   bool NeedsRaster() const {
-    return draw_info_.mode() == TileDrawInfo::PICTURE_PILE_MODE ||
+    return draw_info_.mode() == TileDrawInfo::OOM_MODE ||
            !draw_info_.IsReadyToDraw();
   }
 
@@ -150,9 +150,9 @@
   gfx::Size desired_texture_size_;
   gfx::Rect content_rect_;
   float contents_scale_;
-  bool is_occluded_[NUM_TREES];
+  bool is_occluded_[LAST_TREE + 1];
 
-  TilePriority priority_[NUM_TREES];
+  TilePriority priority_[LAST_TREE + 1];
   TileDrawInfo draw_info_;
 
   int layer_id_;
diff --git a/cc/resources/tile_draw_info.cc b/cc/resources/tile_draw_info.cc
index 7761d72..e75ea59 100644
--- a/cc/resources/tile_draw_info.cc
+++ b/cc/resources/tile_draw_info.cc
@@ -21,7 +21,7 @@
     case RESOURCE_MODE:
       return !!resource_;
     case SOLID_COLOR_MODE:
-    case PICTURE_PILE_MODE:
+    case OOM_MODE:
       return true;
   }
   NOTREACHED();
diff --git a/cc/resources/tile_draw_info.h b/cc/resources/tile_draw_info.h
index 54ca946..0c6bbf61 100644
--- a/cc/resources/tile_draw_info.h
+++ b/cc/resources/tile_draw_info.h
@@ -17,7 +17,7 @@
 // This class holds all the state relevant to drawing a tile.
 class CC_EXPORT TileDrawInfo {
  public:
-  enum Mode { RESOURCE_MODE, SOLID_COLOR_MODE, PICTURE_PILE_MODE };
+  enum Mode { RESOURCE_MODE, SOLID_COLOR_MODE, OOM_MODE };
 
   TileDrawInfo();
   ~TileDrawInfo();
@@ -49,7 +49,7 @@
   }
 
   bool requires_resource() const {
-    return mode_ == RESOURCE_MODE || mode_ == PICTURE_PILE_MODE;
+    return mode_ == RESOURCE_MODE || mode_ == OOM_MODE;
   }
 
   inline bool has_resource() const { return !!resource_; }
@@ -72,7 +72,7 @@
     solid_color_ = color;
   }
 
-  void set_rasterize_on_demand() { mode_ = PICTURE_PILE_MODE; }
+  void set_oom() { mode_ = OOM_MODE; }
 
   Mode mode_;
   SkColor solid_color_;
diff --git a/cc/resources/tile_manager.cc b/cc/resources/tile_manager.cc
index 94b6332..00ae3da 100644
--- a/cc/resources/tile_manager.cc
+++ b/cc/resources/tile_manager.cc
@@ -439,11 +439,11 @@
       global_state_.tree_priority,
       RasterTilePriorityQueue::Type::REQUIRED_FOR_DRAW);
 
-  // Use on-demand raster for any tiles that have not been been assigned
-  // memory. This ensures that we draw even when OOM.
+  // Change to OOM mode for any tiles that have not been been assigned memory.
+  // This ensures that we draw even when OOM.
   for (; !required_for_draw_queue->IsEmpty(); required_for_draw_queue->Pop()) {
     Tile* tile = required_for_draw_queue->Top();
-    tile->draw_info().set_rasterize_on_demand();
+    tile->draw_info().set_oom();
     client_->NotifyTileStateChanged(tile);
   }
 
@@ -604,8 +604,8 @@
     TileDrawInfo& draw_info = tile->draw_info();
     tile->scheduled_priority_ = schedule_priority++;
 
-    DCHECK(draw_info.mode() == TileDrawInfo::PICTURE_PILE_MODE ||
-           !draw_info.IsReadyToDraw());
+    DCHECK_IMPLIES(draw_info.mode() != TileDrawInfo::OOM_MODE,
+                   !draw_info.IsReadyToDraw());
 
     // If the tile already has a raster_task, then the memory used by it is
     // already accounted for in memory_usage. Otherwise, we'll have to acquire
@@ -973,31 +973,30 @@
   // Likewise if we don't allow any tiles (as is the case when we're
   // invisible), if we have tiles that aren't ready, then we shouldn't
   // activate as activation can cause checkerboards.
-  bool allow_rasterize_on_demand =
-      global_state_.tree_priority != SMOOTHNESS_TAKES_PRIORITY &&
-      global_state_.memory_limit_policy != ALLOW_NOTHING;
+  bool wait_for_all_required_tiles =
+      global_state_.tree_priority == SMOOTHNESS_TAKES_PRIORITY ||
+      global_state_.memory_limit_policy == ALLOW_NOTHING;
 
-  // Use on-demand raster for any required-for-activation tiles that have
-  // not been been assigned memory after reaching a steady memory state. This
-  // ensures that we activate even when OOM. Note that we can't reuse the queue
-  // we used for AssignGpuMemoryToTiles, since the AssignGpuMemoryToTiles call
-  // could have evicted some tiles that would not be picked up by the old raster
-  // queue.
+  // Mark any required-for-activation tiles that have not been been assigned
+  // memory after reaching a steady memory state as OOM. This ensures that we
+  // activate even when OOM. Note that we can't reuse the queue we used for
+  // AssignGpuMemoryToTiles, since the AssignGpuMemoryToTiles call could have
+  // evicted some tiles that would not be picked up by the old raster queue.
   scoped_ptr<RasterTilePriorityQueue> required_for_activation_queue(
       client_->BuildRasterQueue(
           global_state_.tree_priority,
           RasterTilePriorityQueue::Type::REQUIRED_FOR_ACTIVATION));
 
-  // If we have tiles to mark as rasterize on demand, but we don't allow
-  // rasterize on demand, then skip activation and return early.
-  if (!required_for_activation_queue->IsEmpty() && !allow_rasterize_on_demand)
+  // If we have tiles left to raster for activation, and we don't allow
+  // activating without them, then skip activation and return early.
+  if (!required_for_activation_queue->IsEmpty() && wait_for_all_required_tiles)
     return;
 
-  // Mark required tiles as rasterize on demand.
+  // Mark required tiles as OOM so that we can activate without them.
   for (; !required_for_activation_queue->IsEmpty();
        required_for_activation_queue->Pop()) {
     Tile* tile = required_for_activation_queue->Top();
-    tile->draw_info().set_rasterize_on_demand();
+    tile->draw_info().set_oom();
     client_->NotifyTileStateChanged(tile);
   }
 
diff --git a/cc/resources/tile_manager_perftest.cc b/cc/resources/tile_manager_perftest.cc
index 7c316acb..87d1837c 100644
--- a/cc/resources/tile_manager_perftest.cc
+++ b/cc/resources/tile_manager_perftest.cc
@@ -355,7 +355,8 @@
       ++next_id;
     }
 
-    host_impl_.pending_tree()->UpdateDrawProperties();
+    bool update_lcd_text = false;
+    host_impl_.pending_tree()->UpdateDrawProperties(update_lcd_text);
     for (FakePictureLayerImpl* layer : layers)
       layer->CreateAllTiles();
 
diff --git a/cc/resources/tile_manager_unittest.cc b/cc/resources/tile_manager_unittest.cc
index 4f441b3..19ccf24 100644
--- a/cc/resources/tile_manager_unittest.cc
+++ b/cc/resources/tile_manager_unittest.cc
@@ -127,7 +127,8 @@
         host_impl_.pending_tree()->LayerById(id_));
 
     // Add tilings/tiles for the layer.
-    host_impl_.pending_tree()->UpdateDrawProperties();
+    bool update_lcd_text = false;
+    host_impl_.pending_tree()->UpdateDrawProperties(update_lcd_text);
   }
 
   TileManager* tile_manager() { return host_impl_.tile_manager(); }
@@ -541,7 +542,8 @@
   time_ticks += base::TimeDelta::FromMilliseconds(1);
   host_impl_.SetCurrentBeginFrameArgs(
       CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, time_ticks));
-  host_impl_.pending_tree()->UpdateDrawProperties();
+  bool update_lcd_text = false;
+  host_impl_.pending_tree()->UpdateDrawProperties(update_lcd_text);
 
   host_impl_.SetRequiresHighResToDraw();
   scoped_ptr<RasterTilePriorityQueue> queue(host_impl_.BuildRasterQueue(
@@ -771,7 +773,8 @@
   time_ticks += base::TimeDelta::FromMilliseconds(1);
   host_impl_.SetCurrentBeginFrameArgs(
       CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, time_ticks));
-  host_impl_.pending_tree()->UpdateDrawProperties();
+  bool update_lcd_text = false;
+  host_impl_.pending_tree()->UpdateDrawProperties(update_lcd_text);
 
   ActivateTree();
   SetupPendingTree(pending_pile);
@@ -909,14 +912,15 @@
   time_ticks += base::TimeDelta::FromMilliseconds(1);
   host_impl_.SetCurrentBeginFrameArgs(
       CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, time_ticks));
-  host_impl_.pending_tree()->UpdateDrawProperties();
+  bool update_lcd_text = false;
+  host_impl_.pending_tree()->UpdateDrawProperties(update_lcd_text);
 
   pending_child_layer->SetOpacity(0.0);
 
   time_ticks += base::TimeDelta::FromMilliseconds(1);
   host_impl_.SetCurrentBeginFrameArgs(
       CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, time_ticks));
-  host_impl_.pending_tree()->UpdateDrawProperties();
+  host_impl_.pending_tree()->UpdateDrawProperties(update_lcd_text);
 
   // Renew all of the tile priorities.
   gfx::Rect viewport(layer_bounds);
diff --git a/cc/resources/tile_priority.h b/cc/resources/tile_priority.h
index 5067aa6..5247b3c 100644
--- a/cc/resources/tile_priority.h
+++ b/cc/resources/tile_priority.h
@@ -24,7 +24,7 @@
   // e.g. in Tile::priority_.
   ACTIVE_TREE = 0,
   PENDING_TREE = 1,
-  NUM_TREES = 2
+  LAST_TREE = 1
   // Be sure to update WhichTreeAsValue when adding new fields.
 };
 scoped_ptr<base::Value> WhichTreeAsValue(WhichTree tree);
@@ -118,7 +118,7 @@
   SAME_PRIORITY_FOR_BOTH_TREES,
   SMOOTHNESS_TAKES_PRIORITY,
   NEW_CONTENT_TAKES_PRIORITY,
-  NUM_TREE_PRIORITIES
+  LAST_TREE_PRIORITY = NEW_CONTENT_TAKES_PRIORITY
   // Be sure to update TreePriorityAsValue when adding new fields.
 };
 std::string TreePriorityToString(TreePriority prio);
diff --git a/cc/resources/tile_task_worker_pool_perftest.cc b/cc/resources/tile_task_worker_pool_perftest.cc
index 450bcbe..71da7b1 100644
--- a/cc/resources/tile_task_worker_pool_perftest.cc
+++ b/cc/resources/tile_task_worker_pool_perftest.cc
@@ -206,7 +206,7 @@
     for (unsigned i = 0; i < num_raster_tasks; ++i) {
       scoped_ptr<ScopedResource> resource(
           ScopedResource::Create(resource_provider_.get()));
-      resource->Allocate(size, ResourceProvider::TextureHintImmutable,
+      resource->Allocate(size, ResourceProvider::TEXTURE_HINT_IMMUTABLE,
                          RGBA_8888);
 
       ImageDecodeTask::Vector dependencies = image_decode_tasks;
@@ -270,6 +270,8 @@
         break;
       case TILE_TASK_WORKER_POOL_TYPE_GPU:
         Create3dOutputSurfaceAndResourceProvider();
+        rasterizer_ = GpuRasterizer::Create(
+            context_provider_.get(), resource_provider_.get(), false, false, 0);
         tile_task_worker_pool_ = GpuTileTaskWorkerPool::Create(
             task_runner_.get(), task_graph_runner_.get(),
             static_cast<GpuRasterizer*>(rasterizer_.get()));
diff --git a/cc/resources/tile_task_worker_pool_unittest.cc b/cc/resources/tile_task_worker_pool_unittest.cc
index 1e84674..d7cb155e 100644
--- a/cc/resources/tile_task_worker_pool_unittest.cc
+++ b/cc/resources/tile_task_worker_pool_unittest.cc
@@ -240,7 +240,8 @@
   void AppendTask(unsigned id, const gfx::Size& size) {
     scoped_ptr<ScopedResource> resource(
         ScopedResource::Create(resource_provider_.get()));
-    resource->Allocate(size, ResourceProvider::TextureHintImmutable, RGBA_8888);
+    resource->Allocate(size, ResourceProvider::TEXTURE_HINT_IMMUTABLE,
+                       RGBA_8888);
     const Resource* const_resource = resource.get();
 
     ImageDecodeTask::Vector empty;
@@ -258,7 +259,8 @@
 
     scoped_ptr<ScopedResource> resource(
         ScopedResource::Create(resource_provider_.get()));
-    resource->Allocate(size, ResourceProvider::TextureHintImmutable, RGBA_8888);
+    resource->Allocate(size, ResourceProvider::TEXTURE_HINT_IMMUTABLE,
+                       RGBA_8888);
     const Resource* const_resource = resource.get();
 
     ImageDecodeTask::Vector empty;
@@ -387,7 +389,8 @@
     // Verify a resource of this size is larger than the transfer buffer.
     scoped_ptr<ScopedResource> resource(
         ScopedResource::Create(resource_provider_.get()));
-    resource->Allocate(size, ResourceProvider::TextureHintImmutable, RGBA_8888);
+    resource->Allocate(size, ResourceProvider::TEXTURE_HINT_IMMUTABLE,
+                       RGBA_8888);
     EXPECT_GE(resource->bytes(), kMaxTransferBufferUsageBytes);
   }
 
diff --git a/cc/resources/tiling_set_eviction_queue.cc b/cc/resources/tiling_set_eviction_queue.cc
index f13c915..1f0e213 100644
--- a/cc/resources/tiling_set_eviction_queue.cc
+++ b/cc/resources/tiling_set_eviction_queue.cc
@@ -330,9 +330,6 @@
       return false;
     case SAME_PRIORITY_FOR_BOTH_TREES:
       break;
-    case NUM_TREE_PRIORITIES:
-      NOTREACHED();
-      break;
   }
 
   // The priority for tile priority of a shared tile will be a combined
diff --git a/cc/resources/ui_resource_request.h b/cc/resources/ui_resource_request.h
index 6d89761..b89e567 100644
--- a/cc/resources/ui_resource_request.h
+++ b/cc/resources/ui_resource_request.h
@@ -16,9 +16,9 @@
 class CC_EXPORT UIResourceRequest {
  public:
   enum UIResourceRequestType {
-    UIResourceCreate,
-    UIResourceDelete,
-    UIResourceInvalidRequest
+    UI_RESOURCE_CREATE,
+    UI_RESOURCE_DELETE,
+    UI_RESOURCE_INVALID_REQUEST
   };
 
   UIResourceRequest(UIResourceRequestType type, UIResourceId id);
diff --git a/cc/resources/video_resource_updater.cc b/cc/resources/video_resource_updater.cc
index 0e772eb..1b089df 100644
--- a/cc/resources/video_resource_updater.cc
+++ b/cc/resources/video_resource_updater.cc
@@ -8,6 +8,7 @@
 
 #include "base/bind.h"
 #include "base/trace_event/trace_event.h"
+#include "cc/base/util.h"
 #include "cc/output/gl_renderer.h"
 #include "cc/resources/resource_provider.h"
 #include "gpu/GLES2/gl2extchromium.h"
@@ -95,9 +96,9 @@
   // TODO(danakj): Abstract out hw/sw resource create/delete from
   // ResourceProvider and stop using ResourceProvider in this class.
   const ResourceProvider::ResourceId resource_id =
-      resource_provider_->CreateResource(plane_size, GL_CLAMP_TO_EDGE,
-                                         ResourceProvider::TextureHintImmutable,
-                                         format);
+      resource_provider_->CreateResource(
+          plane_size, GL_CLAMP_TO_EDGE,
+          ResourceProvider::TEXTURE_HINT_IMMUTABLE, format);
   if (resource_id == 0)
     return all_resources_.end();
 
@@ -317,14 +318,41 @@
 
     if (!PlaneResourceMatchesUniqueID(plane_resource, video_frame.get(), i)) {
       // We need to transfer data from |video_frame| to the plane resource.
-      const uint8_t* input_plane_pixels = video_frame->data(i);
+      // TODO(reveman): Can use GpuMemoryBuffers here to improve performance.
 
-      gfx::Rect image_rect(0, 0, video_frame->stride(i),
-                           plane_resource.resource_size.height());
-      gfx::Rect source_rect(plane_resource.resource_size);
-      resource_provider_->SetPixels(plane_resource.resource_id,
-                                    input_plane_pixels, image_rect, source_rect,
-                                    gfx::Vector2d());
+      // The |resource_size_pixels| is the size of the resource we want to
+      // upload to.
+      gfx::Size resource_size_pixels = plane_resource.resource_size;
+      // The |video_stride_pixels| is the width of the video frame we are
+      // uploading (including non-frame data to fill in the stride).
+      size_t video_stride_pixels = video_frame->stride(i);
+
+      size_t bytes_per_pixel = BitsPerPixel(plane_resource.resource_format) / 8;
+      // Use 4-byte row alignment (OpenGL default) for upload performance.
+      // Assuming that GL_UNPACK_ALIGNMENT has not changed from default.
+      size_t upload_image_stride =
+          RoundUp<size_t>(bytes_per_pixel * resource_size_pixels.width(), 4u);
+
+      const uint8_t* pixels;
+      if (upload_image_stride == video_stride_pixels * bytes_per_pixel) {
+        pixels = video_frame->data(i);
+      } else {
+        // Avoid malloc for each frame/plane if possible.
+        size_t needed_size =
+            upload_image_stride * resource_size_pixels.height();
+        if (upload_pixels_.size() < needed_size)
+          upload_pixels_.resize(needed_size);
+        for (int row = 0; row < resource_size_pixels.height(); ++row) {
+          uint8_t* dst = &upload_pixels_[upload_image_stride * row];
+          const uint8_t* src = video_frame->data(i) +
+                               bytes_per_pixel * video_stride_pixels * row;
+          memcpy(dst, src, resource_size_pixels.width() * bytes_per_pixel);
+        }
+        pixels = &upload_pixels_[0];
+      }
+
+      resource_provider_->CopyToResource(plane_resource.resource_id, pixels,
+                                         resource_size_pixels);
       SetPlaneResourceUniqueId(video_frame.get(), i, &plane_resource);
     }
 
diff --git a/cc/resources/video_resource_updater.h b/cc/resources/video_resource_updater.h
index 622fe68d..1bccf79 100644
--- a/cc/resources/video_resource_updater.h
+++ b/cc/resources/video_resource_updater.h
@@ -131,6 +131,7 @@
   ContextProvider* context_provider_;
   ResourceProvider* resource_provider_;
   scoped_ptr<media::SkCanvasVideoRenderer> video_renderer_;
+  std::vector<uint8_t> upload_pixels_;
 
   // Recycle resources so that we can reduce the number of allocations and
   // data transfers.
diff --git a/cc/resources/video_resource_updater_unittest.cc b/cc/resources/video_resource_updater_unittest.cc
index 5433bcf7..2098357 100644
--- a/cc/resources/video_resource_updater_unittest.cc
+++ b/cc/resources/video_resource_updater_unittest.cc
@@ -38,6 +38,21 @@
   int upload_count_;
 };
 
+class SharedBitmapManagerAllocationCounter : public TestSharedBitmapManager {
+ public:
+  scoped_ptr<SharedBitmap> AllocateSharedBitmap(
+      const gfx::Size& size) override {
+    ++allocation_count_;
+    return TestSharedBitmapManager::AllocateSharedBitmap(size);
+  }
+
+  int AllocationCount() { return allocation_count_; }
+  void ResetAllocationCount() { allocation_count_ = 0; }
+
+ private:
+  int allocation_count_;
+};
+
 class VideoResourceUpdaterTest : public testing::Test {
  protected:
   VideoResourceUpdaterTest() {
@@ -49,7 +64,12 @@
     output_surface3d_ =
         FakeOutputSurface::Create3d(context3d.Pass());
     CHECK(output_surface3d_->BindToClient(&client_));
-    shared_bitmap_manager_.reset(new TestSharedBitmapManager());
+
+    output_surface_software_ = FakeOutputSurface::CreateSoftware(
+        make_scoped_ptr(new SoftwareOutputDevice));
+    CHECK(output_surface_software_->BindToClient(&client_));
+
+    shared_bitmap_manager_.reset(new SharedBitmapManagerAllocationCounter());
     resource_provider3d_ =
         ResourceProvider::Create(output_surface3d_.get(),
                                  shared_bitmap_manager_.get(),
@@ -58,6 +78,10 @@
                                  0,
                                  false,
                                  1);
+
+    resource_provider_software_ = ResourceProvider::Create(
+        output_surface_software_.get(), shared_bitmap_manager_.get(), NULL,
+        NULL, 0, false, 1);
   }
 
   scoped_refptr<media::VideoFrame> CreateTestYUVVideoFrame() {
@@ -85,8 +109,10 @@
   WebGraphicsContext3DUploadCounter* context3d_;
   FakeOutputSurfaceClient client_;
   scoped_ptr<FakeOutputSurface> output_surface3d_;
-  scoped_ptr<TestSharedBitmapManager> shared_bitmap_manager_;
+  scoped_ptr<FakeOutputSurface> output_surface_software_;
+  scoped_ptr<SharedBitmapManagerAllocationCounter> shared_bitmap_manager_;
   scoped_ptr<ResourceProvider> resource_provider3d_;
+  scoped_ptr<ResourceProvider> resource_provider_software_;
 };
 
 TEST_F(VideoResourceUpdaterTest, SoftwareFrame) {
@@ -112,6 +138,7 @@
   EXPECT_EQ(VideoFrameExternalResources::YUV_RESOURCE, resources.type);
   EXPECT_EQ(size_t(3), resources.mailboxes.size());
   EXPECT_EQ(size_t(3), resources.release_callbacks.size());
+  EXPECT_EQ(size_t(0), resources.software_resources.size());
   // Expect exactly three texture uploads, one for each plane.
   EXPECT_EQ(3, context3d_->UploadCount());
 
@@ -143,6 +170,7 @@
   EXPECT_EQ(VideoFrameExternalResources::YUV_RESOURCE, resources.type);
   EXPECT_EQ(size_t(3), resources.mailboxes.size());
   EXPECT_EQ(size_t(3), resources.release_callbacks.size());
+  EXPECT_EQ(size_t(0), resources.software_resources.size());
   // Expect exactly three texture uploads, one for each plane.
   EXPECT_EQ(3, context3d_->UploadCount());
 
@@ -156,5 +184,72 @@
   EXPECT_EQ(0, context3d_->UploadCount());
 }
 
+TEST_F(VideoResourceUpdaterTest, SoftwareFrameSoftwareCompositor) {
+  VideoResourceUpdater updater(nullptr, resource_provider_software_.get());
+  scoped_refptr<media::VideoFrame> video_frame = CreateTestYUVVideoFrame();
+
+  VideoFrameExternalResources resources =
+      updater.CreateExternalResourcesFromVideoFrame(video_frame);
+  EXPECT_EQ(VideoFrameExternalResources::SOFTWARE_RESOURCE, resources.type);
+}
+
+TEST_F(VideoResourceUpdaterTest, ReuseResourceSoftwareCompositor) {
+  VideoResourceUpdater updater(nullptr, resource_provider_software_.get());
+  scoped_refptr<media::VideoFrame> video_frame = CreateTestYUVVideoFrame();
+  video_frame->set_timestamp(base::TimeDelta::FromSeconds(1234));
+
+  // Allocate the resources for a software video frame.
+  shared_bitmap_manager_->ResetAllocationCount();
+  VideoFrameExternalResources resources =
+      updater.CreateExternalResourcesFromVideoFrame(video_frame);
+  EXPECT_EQ(VideoFrameExternalResources::SOFTWARE_RESOURCE, resources.type);
+  EXPECT_EQ(size_t(0), resources.mailboxes.size());
+  EXPECT_EQ(size_t(0), resources.release_callbacks.size());
+  EXPECT_EQ(size_t(1), resources.software_resources.size());
+  // Expect exactly one allocated shared bitmap.
+  EXPECT_EQ(1, shared_bitmap_manager_->AllocationCount());
+
+  // Simulate the ResourceProvider releasing the resource back to the video
+  // updater.
+  resources.software_release_callback.Run(0, false, nullptr);
+
+  // Allocate resources for the same frame.
+  shared_bitmap_manager_->ResetAllocationCount();
+  resources = updater.CreateExternalResourcesFromVideoFrame(video_frame);
+  EXPECT_EQ(VideoFrameExternalResources::SOFTWARE_RESOURCE, resources.type);
+  EXPECT_EQ(size_t(0), resources.mailboxes.size());
+  EXPECT_EQ(size_t(0), resources.release_callbacks.size());
+  EXPECT_EQ(size_t(1), resources.software_resources.size());
+  // The data should be reused so expect no new allocations.
+  EXPECT_EQ(0, shared_bitmap_manager_->AllocationCount());
+}
+
+TEST_F(VideoResourceUpdaterTest, ReuseResourceNoDeleteSoftwareCompositor) {
+  VideoResourceUpdater updater(nullptr, resource_provider_software_.get());
+  scoped_refptr<media::VideoFrame> video_frame = CreateTestYUVVideoFrame();
+  video_frame->set_timestamp(base::TimeDelta::FromSeconds(1234));
+
+  // Allocate the resources for a software video frame.
+  shared_bitmap_manager_->ResetAllocationCount();
+  VideoFrameExternalResources resources =
+      updater.CreateExternalResourcesFromVideoFrame(video_frame);
+  EXPECT_EQ(VideoFrameExternalResources::SOFTWARE_RESOURCE, resources.type);
+  EXPECT_EQ(size_t(0), resources.mailboxes.size());
+  EXPECT_EQ(size_t(0), resources.release_callbacks.size());
+  EXPECT_EQ(size_t(1), resources.software_resources.size());
+  // Expect exactly one allocated shared bitmap.
+  EXPECT_EQ(1, shared_bitmap_manager_->AllocationCount());
+
+  // Allocate resources for the same frame.
+  shared_bitmap_manager_->ResetAllocationCount();
+  resources = updater.CreateExternalResourcesFromVideoFrame(video_frame);
+  EXPECT_EQ(VideoFrameExternalResources::SOFTWARE_RESOURCE, resources.type);
+  EXPECT_EQ(size_t(0), resources.mailboxes.size());
+  EXPECT_EQ(size_t(0), resources.release_callbacks.size());
+  EXPECT_EQ(size_t(1), resources.software_resources.size());
+  // The data should be reused so expect no new allocations.
+  EXPECT_EQ(0, shared_bitmap_manager_->AllocationCount());
+}
+
 }  // namespace
 }  // namespace cc
diff --git a/cc/scheduler/scheduler.cc b/cc/scheduler/scheduler.cc
index ed8c0a2..904a057 100644
--- a/cc/scheduler/scheduler.cc
+++ b/cc/scheduler/scheduler.cc
@@ -335,6 +335,9 @@
   if (state_machine_.ShouldSetNeedsBeginFrames(
           frame_source_->NeedsBeginFrames())) {
     frame_source_->SetNeedsBeginFrames(state_machine_.BeginFrameNeeded());
+    if (!frame_source_->NeedsBeginFrames()) {
+      client_->SendBeginMainFrameNotExpectedSoon();
+    }
   }
 
   if (state_machine_.begin_impl_frame_state() ==
diff --git a/cc/scheduler/scheduler.h b/cc/scheduler/scheduler.h
index 82f305a..370ac25 100644
--- a/cc/scheduler/scheduler.h
+++ b/cc/scheduler/scheduler.h
@@ -49,6 +49,7 @@
   virtual base::TimeDelta CommitToActivateDurationEstimate() = 0;
   virtual void DidBeginImplFrameDeadline() = 0;
   virtual void SendBeginFramesToChildren(const BeginFrameArgs& args) = 0;
+  virtual void SendBeginMainFrameNotExpectedSoon() = 0;
 
  protected:
   virtual ~SchedulerClient() {}
diff --git a/cc/scheduler/scheduler_unittest.cc b/cc/scheduler/scheduler_unittest.cc
index ac52271..50cc846c 100644
--- a/cc/scheduler/scheduler_unittest.cc
+++ b/cc/scheduler/scheduler_unittest.cc
@@ -161,6 +161,10 @@
     begin_frame_is_sent_to_children_ = true;
   }
 
+  void SendBeginMainFrameNotExpectedSoon() override {
+    PushAction("SendBeginMainFrameNotExpectedSoon");
+  }
+
   base::Callback<bool(void)> ImplFrameDeadlinePending(bool state) {
     return base::Bind(&FakeSchedulerClient::ImplFrameDeadlinePendingCallback,
                       base::Unretained(this),
@@ -517,7 +521,8 @@
   client_->Reset();
 
   task_runner().RunPendingTasks();  // Run posted deadline.
-  EXPECT_SINGLE_ACTION("SetNeedsBeginFrames(false)", client_);
+  EXPECT_ACTION("SetNeedsBeginFrames(false)", client_, 0, 2);
+  EXPECT_ACTION("SendBeginMainFrameNotExpectedSoon", client_, 1, 2);
   client_->Reset();
 }
 
@@ -1001,7 +1006,8 @@
   EXPECT_TRUE(scheduler_->BeginImplFrameDeadlinePending());
   client->Reset();
   task_runner().RunPendingTasks();  // Run posted deadline.
-  EXPECT_SINGLE_ACTION("SetNeedsBeginFrames(false)", client);
+  EXPECT_ACTION("SetNeedsBeginFrames(false)", client_, 0, 2);
+  EXPECT_ACTION("SendBeginMainFrameNotExpectedSoon", client_, 1, 2);
   EXPECT_FALSE(scheduler_->BeginImplFrameDeadlinePending());
   EXPECT_EQ(0, client->num_draws());
 
@@ -1376,7 +1382,8 @@
   client_->Reset();
 
   task_runner().RunPendingTasks();  // Run posted deadline.
-  EXPECT_SINGLE_ACTION("SetNeedsBeginFrames(false)", client_);
+  EXPECT_ACTION("SetNeedsBeginFrames(false)", client_, 0, 2);
+  EXPECT_ACTION("SendBeginMainFrameNotExpectedSoon", client_, 1, 2);
   client_->Reset();
 }
 
@@ -1627,7 +1634,7 @@
   // Make sure SetNeedsBeginFrame isn't called on the client
   // when the BeginFrame is no longer needed.
   task_runner().RunPendingTasks();  // Run posted deadline.
-  EXPECT_NO_ACTION(client_);
+  EXPECT_SINGLE_ACTION("SendBeginMainFrameNotExpectedSoon", client_);
   client_->Reset();
 }
 
@@ -1783,7 +1790,8 @@
   client_->Reset();
   scheduler_->DidLoseOutputSurface();
   // Do nothing when impl frame is in deadine pending state.
-  EXPECT_SINGLE_ACTION("SetNeedsBeginFrames(false)", client_);
+  EXPECT_ACTION("SetNeedsBeginFrames(false)", client_, 0, 2);
+  EXPECT_ACTION("SendBeginMainFrameNotExpectedSoon", client_, 1, 2);
 
   client_->Reset();
   scheduler_->NotifyBeginMainFrameStarted();
@@ -1814,7 +1822,8 @@
   client_->Reset();
   scheduler_->DidLoseOutputSurface();
   // Do nothing when impl frame is in deadine pending state.
-  EXPECT_SINGLE_ACTION("SetNeedsBeginFrames(false)", client_);
+  EXPECT_ACTION("SetNeedsBeginFrames(false)", client_, 0, 2);
+  EXPECT_ACTION("SendBeginMainFrameNotExpectedSoon", client_, 1, 2);
 
   client_->Reset();
   // Run posted deadline.
@@ -1881,10 +1890,12 @@
   scheduler_->DidLoseOutputSurface();
   if (impl_side_painting) {
     // Sync tree should be forced to activate.
-    EXPECT_ACTION("ScheduledActionActivateSyncTree", client_, 0, 2);
-    EXPECT_ACTION("SetNeedsBeginFrames(false)", client_, 1, 2);
+    EXPECT_ACTION("ScheduledActionActivateSyncTree", client_, 0, 3);
+    EXPECT_ACTION("SetNeedsBeginFrames(false)", client_, 1, 3);
+    EXPECT_ACTION("SendBeginMainFrameNotExpectedSoon", client_, 2, 3);
   } else {
-    EXPECT_SINGLE_ACTION("SetNeedsBeginFrames(false)", client_);
+    EXPECT_ACTION("SetNeedsBeginFrames(false)", client_, 0, 2);
+    EXPECT_ACTION("SendBeginMainFrameNotExpectedSoon", client_, 1, 2);
   }
 
   client_->Reset();
@@ -1916,7 +1927,8 @@
 
   client_->Reset();
   scheduler_->DidLoseOutputSurface();
-  EXPECT_SINGLE_ACTION("SetNeedsBeginFrames(false)", client_);
+  EXPECT_ACTION("SetNeedsBeginFrames(false)", client_, 0, 2);
+  EXPECT_ACTION("SendBeginMainFrameNotExpectedSoon", client_, 1, 2);
 
   client_->Reset();
   task_runner().RunPendingTasks();  // Run posted deadline.
@@ -1967,8 +1979,9 @@
   client_->Reset();
   EXPECT_FALSE(scheduler_->IsBeginRetroFrameArgsEmpty());
   scheduler_->DidLoseOutputSurface();
-  EXPECT_ACTION("ScheduledActionBeginOutputSurfaceCreation", client_, 0, 2);
-  EXPECT_ACTION("SetNeedsBeginFrames(false)", client_, 1, 2);
+  EXPECT_ACTION("ScheduledActionBeginOutputSurfaceCreation", client_, 0, 3);
+  EXPECT_ACTION("SetNeedsBeginFrames(false)", client_, 1, 3);
+  EXPECT_ACTION("SendBeginMainFrameNotExpectedSoon", client_, 2, 3);
   EXPECT_TRUE(scheduler_->IsBeginRetroFrameArgsEmpty());
 
   // Posted BeginRetroFrame is aborted.
@@ -2028,7 +2041,8 @@
   client_->Reset();
   EXPECT_FALSE(scheduler_->IsBeginRetroFrameArgsEmpty());
   scheduler_->DidLoseOutputSurface();
-  EXPECT_SINGLE_ACTION("SetNeedsBeginFrames(false)", client_);
+  EXPECT_ACTION("SetNeedsBeginFrames(false)", client_, 0, 2);
+  EXPECT_ACTION("SendBeginMainFrameNotExpectedSoon", client_, 1, 2);
   EXPECT_TRUE(scheduler_->IsBeginRetroFrameArgsEmpty());
 
   // BeginImplFrame deadline should abort drawing.
@@ -2069,7 +2083,7 @@
 
   client_->Reset();
   scheduler_->DidLoseOutputSurface();
-  EXPECT_NO_ACTION(client_);
+  EXPECT_SINGLE_ACTION("SendBeginMainFrameNotExpectedSoon", client_);
   EXPECT_FALSE(scheduler_->frame_source().NeedsBeginFrames());
 
   client_->Reset();
@@ -2347,5 +2361,40 @@
   EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client_, 0, 1);
 }
 
+// Tests to ensure that we send a BeginMainFrameNotExpectedSoon when expected.
+TEST_F(SchedulerTest, SendBeginMainFrameNotExpectedSoon) {
+  scheduler_settings_.use_external_begin_frame_source = true;
+  SetUpScheduler(true);
+
+  // SetNeedsCommit should begin the frame on the next BeginImplFrame.
+  scheduler_->SetNeedsCommit();
+  EXPECT_SINGLE_ACTION("SetNeedsBeginFrames(true)", client_);
+  client_->Reset();
+
+  // Trigger a frame draw.
+  EXPECT_SCOPED(AdvanceFrame());
+  scheduler_->NotifyBeginMainFrameStarted();
+  scheduler_->NotifyReadyToCommit();
+  task_runner().RunPendingTasks();
+  EXPECT_ACTION("WillBeginImplFrame", client_, 0, 5);
+  EXPECT_ACTION("ScheduledActionSendBeginMainFrame", client_, 1, 5);
+  EXPECT_ACTION("ScheduledActionCommit", client_, 2, 5);
+  EXPECT_ACTION("ScheduledActionAnimate", client_, 3, 5);
+  EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client_, 4, 5);
+  client_->Reset();
+
+  // The following BeginImplFrame deadline should SetNeedsBeginFrame(false)
+  // and send a SendBeginMainFrameNotExpectedSoon.
+  EXPECT_SCOPED(AdvanceFrame());
+  EXPECT_SINGLE_ACTION("WillBeginImplFrame", client_);
+  EXPECT_TRUE(scheduler_->BeginImplFrameDeadlinePending());
+  client_->Reset();
+
+  task_runner().RunPendingTasks();  // Run posted deadline.
+  EXPECT_ACTION("SetNeedsBeginFrames(false)", client_, 0, 2);
+  EXPECT_ACTION("SendBeginMainFrameNotExpectedSoon", client_, 1, 2);
+  client_->Reset();
+}
+
 }  // namespace
 }  // namespace cc
diff --git a/cc/surfaces/display.cc b/cc/surfaces/display.cc
index ab1f0af..7ec4672 100644
--- a/cc/surfaces/display.cc
+++ b/cc/surfaces/display.cc
@@ -134,25 +134,46 @@
   }
   DelegatedFrameData* frame_data = frame->delegated_frame_data.get();
 
-  gfx::Size surface_size =
-      frame_data->render_pass_list.back()->output_rect.size();
+  frame->metadata.latency_info.insert(frame->metadata.latency_info.end(),
+                                      stored_latency_info_.begin(),
+                                      stored_latency_info_.end());
+  stored_latency_info_.clear();
+  bool have_copy_requests = false;
+  for (const auto* pass : frame_data->render_pass_list) {
+    have_copy_requests |= !pass->copy_requests.empty();
+  }
 
-  gfx::Rect device_viewport_rect = gfx::Rect(current_surface_size_);
-  gfx::Rect device_clip_rect = device_viewport_rect;
-  bool disable_picture_quad_image_filtering = false;
+  gfx::Size surface_size;
+  bool have_damage = false;
+  if (!frame_data->render_pass_list.empty()) {
+    surface_size = frame_data->render_pass_list.back()->output_rect.size();
+    have_damage =
+        !frame_data->render_pass_list.back()->damage_rect.size().IsEmpty();
+  }
+  bool avoid_swap = surface_size != current_surface_size_;
+  bool should_draw = !frame->metadata.latency_info.empty() ||
+                     have_copy_requests || (have_damage && !avoid_swap);
 
-  renderer_->DecideRenderPassAllocationsForFrame(frame_data->render_pass_list);
-  renderer_->DrawFrame(&frame_data->render_pass_list,
-                       device_scale_factor_,
-                       device_viewport_rect,
-                       device_clip_rect,
-                       disable_picture_quad_image_filtering);
+  if (should_draw) {
+    gfx::Rect device_viewport_rect = gfx::Rect(current_surface_size_);
+    gfx::Rect device_clip_rect = device_viewport_rect;
+    bool disable_picture_quad_image_filtering = false;
 
-  if (surface_size != current_surface_size_) {
+    renderer_->DecideRenderPassAllocationsForFrame(
+        frame_data->render_pass_list);
+    renderer_->DrawFrame(&frame_data->render_pass_list, device_scale_factor_,
+                         device_viewport_rect, device_clip_rect,
+                         disable_picture_quad_image_filtering);
+  }
+
+  if (should_draw && !avoid_swap) {
+    renderer_->SwapBuffers(frame->metadata);
+  } else {
+    stored_latency_info_.insert(stored_latency_info_.end(),
+                                frame->metadata.latency_info.begin(),
+                                frame->metadata.latency_info.end());
     DidSwapBuffers();
     DidSwapBuffersComplete();
-  } else {
-    renderer_->SwapBuffers(frame->metadata);
   }
 
   return true;
diff --git a/cc/surfaces/display.h b/cc/surfaces/display.h
index 99e66fb..ed96d07 100644
--- a/cc/surfaces/display.h
+++ b/cc/surfaces/display.h
@@ -5,6 +5,8 @@
 #ifndef CC_SURFACES_DISPLAY_H_
 #define CC_SURFACES_DISPLAY_H_
 
+#include <vector>
+
 #include "base/memory/scoped_ptr.h"
 #include "cc/output/output_surface_client.h"
 #include "cc/output/renderer.h"
@@ -101,6 +103,7 @@
   scoped_ptr<DirectRenderer> renderer_;
   scoped_ptr<BlockingTaskRunner> blocking_main_thread_task_runner_;
   scoped_ptr<TextureMailboxDeleter> texture_mailbox_deleter_;
+  std::vector<ui::LatencyInfo> stored_latency_info_;
 
   DISALLOW_COPY_AND_ASSIGN(Display);
 };
diff --git a/cc/surfaces/display_unittest.cc b/cc/surfaces/display_unittest.cc
new file mode 100644
index 0000000..7b012440
--- /dev/null
+++ b/cc/surfaces/display_unittest.cc
@@ -0,0 +1,239 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cc/output/compositor_frame.h"
+#include "cc/output/copy_output_result.h"
+#include "cc/output/delegated_frame_data.h"
+#include "cc/quads/render_pass.h"
+#include "cc/resources/shared_bitmap_manager.h"
+#include "cc/surfaces/display.h"
+#include "cc/surfaces/display_client.h"
+#include "cc/surfaces/surface.h"
+#include "cc/surfaces/surface_factory.h"
+#include "cc/surfaces/surface_factory_client.h"
+#include "cc/surfaces/surface_id_allocator.h"
+#include "cc/surfaces/surface_manager.h"
+#include "cc/test/fake_output_surface.h"
+#include "cc/test/test_shared_bitmap_manager.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace cc {
+namespace {
+
+class EmptySurfaceFactoryClient : public SurfaceFactoryClient {
+ public:
+  void ReturnResources(const ReturnedResourceArray& resources) override {}
+};
+
+class DisplayTest : public testing::Test {
+ public:
+  DisplayTest() : factory_(&manager_, &empty_client_) {}
+
+  void SetUp() override {
+    output_surface_ = FakeOutputSurface::CreateSoftware(
+        make_scoped_ptr(new SoftwareOutputDevice));
+    shared_bitmap_manager_.reset(new TestSharedBitmapManager);
+    output_surface_ptr_ = output_surface_.get();
+  }
+
+ protected:
+  void SubmitFrame(RenderPassList* pass_list, SurfaceId surface_id) {
+    scoped_ptr<DelegatedFrameData> frame_data(new DelegatedFrameData);
+    pass_list->swap(frame_data->render_pass_list);
+
+    scoped_ptr<CompositorFrame> frame(new CompositorFrame);
+    frame->delegated_frame_data = frame_data.Pass();
+
+    factory_.SubmitFrame(surface_id, frame.Pass(),
+                         SurfaceFactory::DrawCallback());
+  }
+
+  SurfaceManager manager_;
+  EmptySurfaceFactoryClient empty_client_;
+  SurfaceFactory factory_;
+  scoped_ptr<FakeOutputSurface> output_surface_;
+  FakeOutputSurface* output_surface_ptr_;
+  scoped_ptr<SharedBitmapManager> shared_bitmap_manager_;
+};
+
+class TestDisplayClient : public DisplayClient {
+ public:
+  TestDisplayClient() : damaged(false), swapped(false) {}
+  ~TestDisplayClient() override {}
+
+  void DisplayDamaged() override { damaged = true; }
+  void DidSwapBuffers() override { swapped = true; }
+  void DidSwapBuffersComplete() override {}
+  void CommitVSyncParameters(base::TimeTicks timebase,
+                             base::TimeDelta interval) override {}
+  void OutputSurfaceLost() override {}
+  void SetMemoryPolicy(const ManagedMemoryPolicy& policy) override {}
+
+  bool damaged;
+  bool swapped;
+};
+
+void CopyCallback(bool* called, scoped_ptr<CopyOutputResult> result) {
+  *called = true;
+}
+
+// Check that frame is damaged and swapped only under correct conditions.
+TEST_F(DisplayTest, DisplayDamaged) {
+  TestDisplayClient client;
+  RendererSettings settings;
+  settings.partial_swap_enabled = true;
+  Display display(&client, &manager_, shared_bitmap_manager_.get(), nullptr,
+                  settings);
+
+  display.Initialize(output_surface_.Pass());
+
+  SurfaceId surface_id(7u);
+  EXPECT_FALSE(client.damaged);
+  display.SetSurfaceId(surface_id, 1.f);
+  EXPECT_TRUE(client.damaged);
+
+  client.damaged = false;
+  display.Resize(gfx::Size(100, 100));
+  EXPECT_TRUE(client.damaged);
+
+  factory_.Create(surface_id);
+
+  // First draw from surface should have full damage.
+  RenderPassList pass_list;
+  scoped_ptr<RenderPass> pass = RenderPass::Create();
+  pass->output_rect = gfx::Rect(0, 0, 100, 100);
+  pass->damage_rect = gfx::Rect(10, 10, 1, 1);
+  pass->id = RenderPassId(1, 1);
+  pass_list.push_back(pass.Pass());
+
+  client.damaged = false;
+  SubmitFrame(&pass_list, surface_id);
+  EXPECT_TRUE(client.damaged);
+
+  EXPECT_FALSE(client.swapped);
+  EXPECT_EQ(0u, output_surface_ptr_->num_sent_frames());
+  display.Draw();
+  EXPECT_TRUE(client.swapped);
+  EXPECT_EQ(1u, output_surface_ptr_->num_sent_frames());
+  SoftwareFrameData* software_data =
+      output_surface_ptr_->last_sent_frame().software_frame_data.get();
+  ASSERT_NE(nullptr, software_data);
+  EXPECT_EQ(gfx::Size(100, 100).ToString(), software_data->size.ToString());
+  EXPECT_EQ(gfx::Rect(0, 0, 100, 100).ToString(),
+            software_data->damage_rect.ToString());
+
+  {
+    // Only damaged portion should be swapped.
+    pass = RenderPass::Create();
+    pass->output_rect = gfx::Rect(0, 0, 100, 100);
+    pass->damage_rect = gfx::Rect(10, 10, 1, 1);
+    pass->id = RenderPassId(1, 1);
+
+    pass_list.push_back(pass.Pass());
+    client.damaged = false;
+    SubmitFrame(&pass_list, surface_id);
+    EXPECT_TRUE(client.damaged);
+
+    client.swapped = false;
+    display.Draw();
+    EXPECT_TRUE(client.swapped);
+    EXPECT_EQ(2u, output_surface_ptr_->num_sent_frames());
+    software_data =
+        output_surface_ptr_->last_sent_frame().software_frame_data.get();
+    ASSERT_NE(nullptr, software_data);
+    EXPECT_EQ(gfx::Size(100, 100).ToString(), software_data->size.ToString());
+    EXPECT_EQ(gfx::Rect(10, 10, 1, 1).ToString(),
+              software_data->damage_rect.ToString());
+  }
+
+  {
+    // Pass has no damage so shouldn't be swapped.
+    pass = RenderPass::Create();
+    pass->output_rect = gfx::Rect(0, 0, 100, 100);
+    pass->damage_rect = gfx::Rect(10, 10, 0, 0);
+    pass->id = RenderPassId(1, 1);
+
+    pass_list.push_back(pass.Pass());
+    client.damaged = false;
+    SubmitFrame(&pass_list, surface_id);
+    EXPECT_TRUE(client.damaged);
+
+    client.swapped = false;
+    display.Draw();
+    EXPECT_TRUE(client.swapped);
+    EXPECT_EQ(2u, output_surface_ptr_->num_sent_frames());
+  }
+
+  {
+    // Pass is wrong size so shouldn't be swapped.
+    pass = RenderPass::Create();
+    pass->output_rect = gfx::Rect(0, 0, 99, 99);
+    pass->damage_rect = gfx::Rect(10, 10, 10, 10);
+    pass->id = RenderPassId(1, 1);
+
+    pass_list.push_back(pass.Pass());
+    client.damaged = false;
+    SubmitFrame(&pass_list, surface_id);
+    EXPECT_TRUE(client.damaged);
+
+    client.swapped = false;
+    display.Draw();
+    EXPECT_TRUE(client.swapped);
+    EXPECT_EQ(2u, output_surface_ptr_->num_sent_frames());
+  }
+
+  {
+    // Pass has copy output request so should be swapped.
+    pass = RenderPass::Create();
+    pass->output_rect = gfx::Rect(0, 0, 100, 100);
+    pass->damage_rect = gfx::Rect(10, 10, 0, 0);
+    bool copy_called = false;
+    pass->copy_requests.push_back(CopyOutputRequest::CreateRequest(
+        base::Bind(&CopyCallback, &copy_called)));
+    pass->id = RenderPassId(1, 1);
+
+    pass_list.push_back(pass.Pass());
+    client.damaged = false;
+    SubmitFrame(&pass_list, surface_id);
+    EXPECT_TRUE(client.damaged);
+
+    client.swapped = false;
+    display.Draw();
+    EXPECT_TRUE(client.swapped);
+    EXPECT_EQ(3u, output_surface_ptr_->num_sent_frames());
+    EXPECT_TRUE(copy_called);
+  }
+
+  // Pass has latency info so should be swapped.
+  {
+    pass = RenderPass::Create();
+    pass->output_rect = gfx::Rect(0, 0, 100, 100);
+    pass->damage_rect = gfx::Rect(10, 10, 0, 0);
+    pass->id = RenderPassId(1, 1);
+
+    pass_list.push_back(pass.Pass());
+    client.damaged = false;
+    scoped_ptr<DelegatedFrameData> frame_data(new DelegatedFrameData);
+    pass_list.swap(frame_data->render_pass_list);
+
+    scoped_ptr<CompositorFrame> frame(new CompositorFrame);
+    frame->delegated_frame_data = frame_data.Pass();
+    frame->metadata.latency_info.push_back(ui::LatencyInfo());
+
+    factory_.SubmitFrame(surface_id, frame.Pass(),
+                         SurfaceFactory::DrawCallback());
+    EXPECT_TRUE(client.damaged);
+
+    client.swapped = false;
+    display.Draw();
+    EXPECT_TRUE(client.swapped);
+    EXPECT_EQ(4u, output_surface_ptr_->num_sent_frames());
+  }
+
+  factory_.Destroy(surface_id);
+}
+
+}  // namespace
+}  // namespace cc
diff --git a/cc/test/animation_test_common.cc b/cc/test/animation_test_common.cc
index f6179cc9..d04c5d3 100644
--- a/cc/test/animation_test_common.cc
+++ b/cc/test/animation_test_common.cc
@@ -44,10 +44,8 @@
   int id = AnimationIdProvider::NextAnimationId();
 
   scoped_ptr<Animation> animation(
-      Animation::Create(curve.Pass(),
-                        id,
-                        AnimationIdProvider::NextGroupId(),
-                        Animation::Opacity));
+      Animation::Create(curve.Pass(), id, AnimationIdProvider::NextGroupId(),
+                        Animation::OPACITY));
   animation->set_needs_synchronized_start_time(true);
 
   target->AddAnimation(animation.Pass());
@@ -73,10 +71,8 @@
   int id = AnimationIdProvider::NextAnimationId();
 
   scoped_ptr<Animation> animation(
-      Animation::Create(curve.Pass(),
-                        id,
-                        AnimationIdProvider::NextGroupId(),
-                        Animation::Transform));
+      Animation::Create(curve.Pass(), id, AnimationIdProvider::NextGroupId(),
+                        Animation::TRANSFORM));
   animation->set_needs_synchronized_start_time(true);
 
   target->AddAnimation(animation.Pass());
@@ -122,7 +118,7 @@
   int id = AnimationIdProvider::NextAnimationId();
 
   scoped_ptr<Animation> animation(Animation::Create(
-      curve.Pass(), id, AnimationIdProvider::NextGroupId(), Animation::Filter));
+      curve.Pass(), id, AnimationIdProvider::NextGroupId(), Animation::FILTER));
   animation->set_needs_synchronized_start_time(true);
 
   target->AddAnimation(animation.Pass());
diff --git a/cc/test/data/blue.png b/cc/test/data/blue.png
deleted file mode 100644
index 48a2680..0000000
--- a/cc/test/data/blue.png
+++ /dev/null
Binary files differ
diff --git a/cc/test/fake_content_layer_client.cc b/cc/test/fake_content_layer_client.cc
index 23b73f5f..ab958e97 100644
--- a/cc/test/fake_content_layer_client.cc
+++ b/cc/test/fake_content_layer_client.cc
@@ -69,13 +69,12 @@
        it != draw_rects_.end(); ++it) {
     const gfx::RectF& draw_rect = it->first;
     const SkPaint& paint = it->second;
-    canvas = skia::SharePtr(
-        recorder.beginRecording(draw_rect.width(), draw_rect.height()));
+    canvas =
+        skia::SharePtr(recorder.beginRecording(gfx::RectFToSkRect(draw_rect)));
     canvas->drawRectCoords(0.f, 0.f, draw_rect.width(), draw_rect.height(),
                            paint);
     picture = skia::AdoptRef(recorder.endRecording());
-    list->AppendItem(DrawingDisplayItem::Create(
-        picture, gfx::PointF(draw_rect.x(), draw_rect.y())));
+    list->AppendItem(DrawingDisplayItem::Create(picture));
   }
 
   for (BitmapVector::const_iterator it = draw_bitmaps_.begin();
@@ -84,8 +83,7 @@
         recorder.beginRecording(it->bitmap.width(), it->bitmap.height()));
     canvas->drawBitmap(it->bitmap, 0.f, 0.f, &it->paint);
     picture = skia::AdoptRef(recorder.endRecording());
-    list->AppendItem(DrawingDisplayItem::Create(
-        picture, gfx::PointF(it->point.x(), it->point.y())));
+    list->AppendItem(DrawingDisplayItem::Create(picture));
   }
 
   if (fill_with_nonsolid_color_) {
@@ -94,11 +92,11 @@
     while (!draw_rect.IsEmpty()) {
       SkPaint paint;
       paint.setColor(red ? SK_ColorRED : SK_ColorBLUE);
-      canvas =
-          skia::SharePtr(recorder.beginRecording(clip.width(), clip.height()));
+      canvas = skia::SharePtr(
+          recorder.beginRecording(gfx::RectFToSkRect(draw_rect)));
       canvas->drawRect(gfx::RectFToSkRect(draw_rect), paint);
       picture = skia::AdoptRef(recorder.endRecording());
-      list->AppendItem(DrawingDisplayItem::Create(picture, gfx::PointF()));
+      list->AppendItem(DrawingDisplayItem::Create(picture));
       draw_rect.Inset(1, 1);
     }
   }
diff --git a/cc/test/fake_layer_tree_host_client.h b/cc/test/fake_layer_tree_host_client.h
index f5f3504..b5093ad 100644
--- a/cc/test/fake_layer_tree_host_client.h
+++ b/cc/test/fake_layer_tree_host_client.h
@@ -33,6 +33,7 @@
   void WillBeginMainFrame() override {}
   void DidBeginMainFrame() override {}
   void BeginMainFrame(const BeginFrameArgs& args) override {}
+  void BeginMainFrameNotExpectedSoon() override {}
   void Layout() override {}
   void ApplyViewportDeltas(const gfx::Vector2dF& inner_delta,
                            const gfx::Vector2dF& outer_delta,
diff --git a/cc/test/fake_layer_tree_host_impl.cc b/cc/test/fake_layer_tree_host_impl.cc
index bfac501..ccfb203 100644
--- a/cc/test/fake_layer_tree_host_impl.cc
+++ b/cc/test/fake_layer_tree_host_impl.cc
@@ -86,7 +86,8 @@
 void FakeLayerTreeHostImpl::UpdateNumChildrenAndDrawProperties(
     LayerTreeImpl* layerTree) {
   RecursiveUpdateNumChildren(layerTree->root_layer());
-  layerTree->UpdateDrawProperties();
+  bool update_lcd_text = false;
+  layerTree->UpdateDrawProperties(update_lcd_text);
 }
 
 }  // namespace cc
diff --git a/cc/test/fake_picture_pile.cc b/cc/test/fake_picture_pile.cc
index cc0a7ba9f..4db6c58 100644
--- a/cc/test/fake_picture_pile.cc
+++ b/cc/test/fake_picture_pile.cc
@@ -49,7 +49,8 @@
   return CreatePile(tile_size, layer_bounds, is_filled);
 }
 
-scoped_refptr<RasterSource> FakePicturePile::CreateRasterSource() const {
+scoped_refptr<RasterSource> FakePicturePile::CreateRasterSource(
+    bool can_use_lcd_text) const {
   return FakePicturePileImpl::CreateFromPile(this, playback_allowed_event_);
 }
 
diff --git a/cc/test/fake_picture_pile.h b/cc/test/fake_picture_pile.h
index 054eaa2f..48b8914 100644
--- a/cc/test/fake_picture_pile.h
+++ b/cc/test/fake_picture_pile.h
@@ -34,7 +34,8 @@
       const gfx::Size& layer_bounds);
 
   // PicturePile overrides.
-  scoped_refptr<RasterSource> CreateRasterSource() const override;
+  scoped_refptr<RasterSource> CreateRasterSource(
+      bool can_use_lcd_text) const override;
 
   using PicturePile::buffer_pixels;
   using PicturePile::CanRasterSlowTileCheck;
diff --git a/cc/test/fake_picture_pile_impl.cc b/cc/test/fake_picture_pile_impl.cc
index 8353d271e..ab255cc2 100644
--- a/cc/test/fake_picture_pile_impl.cc
+++ b/cc/test/fake_picture_pile_impl.cc
@@ -21,7 +21,7 @@
 FakePicturePileImpl::FakePicturePileImpl(
     const PicturePile* other,
     base::WaitableEvent* playback_allowed_event)
-    : PicturePileImpl(other),
+    : PicturePileImpl(other, true),
       playback_allowed_event_(playback_allowed_event),
       tile_grid_size_(other->GetTileGridSizeForTesting()) {
 }
diff --git a/cc/test/layer_tree_json_parser.cc b/cc/test/layer_tree_json_parser.cc
index efe70a0..b0b81f7e 100644
--- a/cc/test/layer_tree_json_parser.cc
+++ b/cc/test/layer_tree_json_parser.cc
@@ -156,11 +156,11 @@
     for (size_t i = 0; i < list->GetSize(); i++) {
       success &= list->GetString(i, &str);
       if (str == "StartTouch")
-        blocks |= ScrollBlocksOnStartTouch;
+        blocks |= SCROLL_BLOCKS_ON_START_TOUCH;
       else if (str == "WheelEvent")
-        blocks |= ScrollBlocksOnWheelEvent;
+        blocks |= SCROLL_BLOCKS_ON_WHEEL_EVENT;
       else if (str == "ScrollEvent")
-        blocks |= ScrollBlocksOnScrollEvent;
+        blocks |= SCROLL_BLOCKS_ON_SCROLL_EVENT;
       else
         success = false;
     }
diff --git a/cc/test/layer_tree_pixel_test.cc b/cc/test/layer_tree_pixel_test.cc
index f7ab9c27..dbb9ba5c 100644
--- a/cc/test/layer_tree_pixel_test.cc
+++ b/cc/test/layer_tree_pixel_test.cc
@@ -63,10 +63,8 @@
   return output_surface.Pass();
 }
 
-void LayerTreePixelTest::CommitCompleteOnThread(LayerTreeHostImpl* impl) {
-  LayerTreeImpl* commit_tree =
-      impl->pending_tree() ? impl->pending_tree() : impl->active_tree();
-  if (commit_tree->source_frame_number() != 0)
+void LayerTreePixelTest::WillActivateTreeOnThread(LayerTreeHostImpl* impl) {
+  if (impl->sync_tree()->source_frame_number() != 0)
     return;
 
   DirectRenderer* renderer = static_cast<DirectRenderer*>(impl->renderer());
diff --git a/cc/test/layer_tree_pixel_test.h b/cc/test/layer_tree_pixel_test.h
index 8d0228085..da030d1 100644
--- a/cc/test/layer_tree_pixel_test.h
+++ b/cc/test/layer_tree_pixel_test.h
@@ -41,7 +41,7 @@
   ~LayerTreePixelTest() override;
 
   scoped_ptr<OutputSurface> CreateOutputSurface() override;
-  void CommitCompleteOnThread(LayerTreeHostImpl* impl) override;
+  void WillActivateTreeOnThread(LayerTreeHostImpl* impl) override;
 
   virtual scoped_ptr<CopyOutputRequest> CreateCopyOutputRequest();
 
diff --git a/cc/test/layer_tree_test.cc b/cc/test/layer_tree_test.cc
index ce69079d..129a27d 100644
--- a/cc/test/layer_tree_test.cc
+++ b/cc/test/layer_tree_test.cc
@@ -441,6 +441,7 @@
   void DidAbortSwapBuffers() override {}
   void ScheduleComposite() override { test_hooks_->ScheduleComposite(); }
   void DidCompletePageScaleAnimation() override {}
+  void BeginMainFrameNotExpectedSoon() override {}
 
  private:
   explicit LayerTreeHostClientForTesting(TestHooks* test_hooks)
diff --git a/cc/test/render_pass_test_common.cc b/cc/test/render_pass_test_common.cc
index 7c91f0d..0e3fe22e 100644
--- a/cc/test/render_pass_test_common.cc
+++ b/cc/test/render_pass_test_common.cc
@@ -35,46 +35,39 @@
   const float vertex_opacity[] = {1.0f, 1.0f, 1.0f, 1.0f};
 
   ResourceProvider::ResourceId resource1 = resource_provider->CreateResource(
-      gfx::Size(45, 5),
-      GL_CLAMP_TO_EDGE,
-      ResourceProvider::TextureHintImmutable,
+      gfx::Size(45, 5), GL_CLAMP_TO_EDGE,
+      ResourceProvider::TEXTURE_HINT_IMMUTABLE,
       resource_provider->best_texture_format());
   resource_provider->AllocateForTesting(resource1);
   ResourceProvider::ResourceId resource2 = resource_provider->CreateResource(
-      gfx::Size(346, 61),
-      GL_CLAMP_TO_EDGE,
-      ResourceProvider::TextureHintImmutable,
+      gfx::Size(346, 61), GL_CLAMP_TO_EDGE,
+      ResourceProvider::TEXTURE_HINT_IMMUTABLE,
       resource_provider->best_texture_format());
   resource_provider->AllocateForTesting(resource2);
   ResourceProvider::ResourceId resource3 = resource_provider->CreateResource(
-      gfx::Size(12, 134),
-      GL_CLAMP_TO_EDGE,
-      ResourceProvider::TextureHintImmutable,
+      gfx::Size(12, 134), GL_CLAMP_TO_EDGE,
+      ResourceProvider::TEXTURE_HINT_IMMUTABLE,
       resource_provider->best_texture_format());
   resource_provider->AllocateForTesting(resource3);
   ResourceProvider::ResourceId resource4 = resource_provider->CreateResource(
-      gfx::Size(56, 12),
-      GL_CLAMP_TO_EDGE,
-      ResourceProvider::TextureHintImmutable,
+      gfx::Size(56, 12), GL_CLAMP_TO_EDGE,
+      ResourceProvider::TEXTURE_HINT_IMMUTABLE,
       resource_provider->best_texture_format());
   resource_provider->AllocateForTesting(resource4);
   gfx::Size resource5_size(73, 26);
   ResourceProvider::ResourceId resource5 = resource_provider->CreateResource(
-      resource5_size,
-      GL_CLAMP_TO_EDGE,
-      ResourceProvider::TextureHintImmutable,
+      resource5_size, GL_CLAMP_TO_EDGE,
+      ResourceProvider::TEXTURE_HINT_IMMUTABLE,
       resource_provider->best_texture_format());
   resource_provider->AllocateForTesting(resource5);
   ResourceProvider::ResourceId resource6 = resource_provider->CreateResource(
-      gfx::Size(64, 92),
-      GL_CLAMP_TO_EDGE,
-      ResourceProvider::TextureHintImmutable,
+      gfx::Size(64, 92), GL_CLAMP_TO_EDGE,
+      ResourceProvider::TEXTURE_HINT_IMMUTABLE,
       resource_provider->best_texture_format());
   resource_provider->AllocateForTesting(resource6);
   ResourceProvider::ResourceId resource7 = resource_provider->CreateResource(
-      gfx::Size(9, 14),
-      GL_CLAMP_TO_EDGE,
-      ResourceProvider::TextureHintImmutable,
+      gfx::Size(9, 14), GL_CLAMP_TO_EDGE,
+      ResourceProvider::TEXTURE_HINT_IMMUTABLE,
       resource_provider->best_texture_format());
   resource_provider->AllocateForTesting(resource7);
 
@@ -243,9 +236,8 @@
   ResourceProvider::ResourceId plane_resources[4];
   for (int i = 0; i < 4; ++i) {
     plane_resources[i] = resource_provider->CreateResource(
-        gfx::Size(20, 12),
-        GL_CLAMP_TO_EDGE,
-        ResourceProvider::TextureHintImmutable,
+        gfx::Size(20, 12), GL_CLAMP_TO_EDGE,
+        ResourceProvider::TEXTURE_HINT_IMMUTABLE,
         resource_provider->best_texture_format());
     resource_provider->AllocateForTesting(plane_resources[i]);
   }
diff --git a/cc/test/test_web_graphics_context_3d.cc b/cc/test/test_web_graphics_context_3d.cc
index 8aa392d..28b16c84 100644
--- a/cc/test/test_web_graphics_context_3d.cc
+++ b/cc/test/test_web_graphics_context_3d.cc
@@ -64,7 +64,7 @@
       height_(0),
       scale_factor_(-1.f),
       test_support_(NULL),
-      last_update_type_(NoUpdate),
+      last_update_type_(NO_UPDATE),
       next_insert_sync_point_(1),
       last_waited_sync_point_(0),
       unpack_alignment_(4),
diff --git a/cc/test/test_web_graphics_context_3d.h b/cc/test/test_web_graphics_context_3d.h
index 31182f81..c2dee45 100644
--- a/cc/test/test_web_graphics_context_3d.h
+++ b/cc/test/test_web_graphics_context_3d.h
@@ -373,11 +373,7 @@
   void clear_reshape_called() { reshape_called_ = false; }
   float scale_factor() const { return scale_factor_; }
 
-  enum UpdateType {
-    NoUpdate = 0,
-    PrepareTexture,
-    PostSubBuffer
-  };
+  enum UpdateType { NO_UPDATE = 0, PREPARE_TEXTURE, POST_SUB_BUFFER };
 
   gfx::Rect update_rect() const { return update_rect_; }
 
diff --git a/cc/trees/layer_sorter.cc b/cc/trees/layer_sorter.cc
index e171fe0..bde4920 100644
--- a/cc/trees/layer_sorter.cc
+++ b/cc/trees/layer_sorter.cc
@@ -90,7 +90,7 @@
 
   // Early out if the projected bounds don't overlap.
   if (!a->projected_bounds.Intersects(b->projected_bounds))
-    return None;
+    return NONE;
 
   gfx::PointF aPoints[4] = { a->projected_quad.p1(),
                              a->projected_quad.p2(),
@@ -123,7 +123,7 @@
         overlap_points.push_back(r);
 
   if (overlap_points.empty())
-    return None;
+    return NONE;
 
   // Check the corresponding layer depth value for all overlap points to
   // determine which layer is in front.
@@ -157,7 +157,7 @@
 
   // If we can't tell which should come first, we use document order.
   if (!accurate)
-    return ABeforeB;
+    return A_BEFORE_B;
 
   float max_diff =
       std::abs(max_positive) > std::abs(max_negative) ?
@@ -176,9 +176,9 @@
   // Maintain relative order if the layers have the same depth at all
   // intersection points.
   if (max_diff <= 0.f)
-    return ABeforeB;
+    return A_BEFORE_B;
 
-  return BBeforeA;
+  return B_BEFORE_A;
 }
 
 LayerShape::LayerShape() {}
@@ -320,10 +320,10 @@
                                                     &weight);
       GraphNode* start_node = NULL;
       GraphNode* end_node = NULL;
-      if (overlap_result == ABeforeB) {
+      if (overlap_result == A_BEFORE_B) {
         start_node = &node_a;
         end_node = &node_b;
-      } else if (overlap_result == BBeforeA) {
+      } else if (overlap_result == B_BEFORE_A) {
         start_node = &node_b;
         end_node = &node_a;
       }
diff --git a/cc/trees/layer_sorter.h b/cc/trees/layer_sorter.h
index b783ded0..4cfa8fed 100644
--- a/cc/trees/layer_sorter.h
+++ b/cc/trees/layer_sorter.h
@@ -68,11 +68,7 @@
 
   void Sort(LayerImplList::iterator first, LayerImplList::iterator last);
 
-  enum ABCompareResult {
-    ABeforeB,
-    BBeforeA,
-    None
-  };
+  enum ABCompareResult { A_BEFORE_B, B_BEFORE_A, NONE };
 
   static ABCompareResult CheckOverlap(LayerShape* a,
                                       LayerShape* b,
diff --git a/cc/trees/layer_sorter_unittest.cc b/cc/trees/layer_sorter_unittest.cc
index ba7765b..74d3cf5 100644
--- a/cc/trees/layer_sorter_unittest.cc
+++ b/cc/trees/layer_sorter_unittest.cc
@@ -37,12 +37,12 @@
 
   overlap_result =
       LayerSorter::CheckOverlap(&front, &back, z_threshold, &weight);
-  EXPECT_EQ(LayerSorter::BBeforeA, overlap_result);
+  EXPECT_EQ(LayerSorter::B_BEFORE_A, overlap_result);
   EXPECT_EQ(1.f, weight);
 
   overlap_result =
       LayerSorter::CheckOverlap(&back, &front, z_threshold, &weight);
-  EXPECT_EQ(LayerSorter::ABeforeB, overlap_result);
+  EXPECT_EQ(LayerSorter::A_BEFORE_B, overlap_result);
   EXPECT_EQ(1.f, weight);
 
   // One layer translated off to the right. No overlap should be detected.
@@ -51,7 +51,7 @@
   LayerShape back_right(2.f, 2.f, right_translate);
   overlap_result =
       LayerSorter::CheckOverlap(&front, &back_right, z_threshold, &weight);
-  EXPECT_EQ(LayerSorter::None, overlap_result);
+  EXPECT_EQ(LayerSorter::NONE, overlap_result);
 
   // When comparing a layer with itself, z difference is always 0.
   overlap_result =
@@ -80,7 +80,7 @@
 
   overlap_result =
       LayerSorter::CheckOverlap(&front_face, &left_face, z_threshold, &weight);
-  EXPECT_EQ(LayerSorter::BBeforeA, overlap_result);
+  EXPECT_EQ(LayerSorter::B_BEFORE_A, overlap_result);
 }
 
 TEST(LayerSorterTest, IntersectingLayerOverlap) {
@@ -107,7 +107,7 @@
                                              &rotated_face,
                                              z_threshold,
                                              &weight);
-  EXPECT_NE(LayerSorter::None, overlap_result);
+  EXPECT_NE(LayerSorter::NONE, overlap_result);
   EXPECT_EQ(0.f, weight);
 }
 
@@ -145,13 +145,13 @@
 
   overlap_result =
       LayerSorter::CheckOverlap(&layer_a, &layer_c, z_threshold, &weight);
-  EXPECT_EQ(LayerSorter::ABeforeB, overlap_result);
+  EXPECT_EQ(LayerSorter::A_BEFORE_B, overlap_result);
   overlap_result =
       LayerSorter::CheckOverlap(&layer_c, &layer_b, z_threshold, &weight);
-  EXPECT_EQ(LayerSorter::ABeforeB, overlap_result);
+  EXPECT_EQ(LayerSorter::A_BEFORE_B, overlap_result);
   overlap_result =
       LayerSorter::CheckOverlap(&layer_a, &layer_b, z_threshold, &weight);
-  EXPECT_EQ(LayerSorter::None, overlap_result);
+  EXPECT_EQ(LayerSorter::NONE, overlap_result);
 }
 
 TEST(LayerSorterTest, LayersUnderPathologicalPerspectiveTransform) {
@@ -192,7 +192,7 @@
 
   overlap_result =
       LayerSorter::CheckOverlap(&layer_a, &layer_b, z_threshold, &weight);
-  EXPECT_EQ(LayerSorter::ABeforeB, overlap_result);
+  EXPECT_EQ(LayerSorter::A_BEFORE_B, overlap_result);
 }
 
 TEST(LayerSorterTest, VerifyExistingOrderingPreservedWhenNoZDiff) {
diff --git a/cc/trees/layer_tree_host.cc b/cc/trees/layer_tree_host.cc
index f4780789..49e374d 100644
--- a/cc/trees/layer_tree_host.cc
+++ b/cc/trees/layer_tree_host.cc
@@ -133,7 +133,6 @@
       partial_texture_update_requests_(0),
       did_complete_scale_animation_(false),
       in_paint_layer_contents_(false),
-      total_frames_used_for_lcd_text_metrics_(0),
       id_(s_layer_tree_host_sequence_number.GetNext() + 1),
       next_commit_forces_redraw_(false),
       shared_bitmap_manager_(shared_bitmap_manager),
@@ -233,6 +232,10 @@
   client_->DidBeginMainFrame();
 }
 
+void LayerTreeHost::BeginMainFrameNotExpectedSoon() {
+  client_->BeginMainFrameNotExpectedSoon();
+}
+
 void LayerTreeHost::BeginMainFrame(const BeginFrameArgs& args) {
   inside_begin_main_frame_ = true;
   client_->BeginMainFrame(args);
@@ -559,19 +562,19 @@
         animation_controllers.find(event_layer_id);
     if (iter != animation_controllers.end()) {
       switch ((*events)[event_index].type) {
-        case AnimationEvent::Started:
+        case AnimationEvent::STARTED:
           (*iter).second->NotifyAnimationStarted((*events)[event_index]);
           break;
 
-        case AnimationEvent::Finished:
+        case AnimationEvent::FINISHED:
           (*iter).second->NotifyAnimationFinished((*events)[event_index]);
           break;
 
-        case AnimationEvent::Aborted:
+        case AnimationEvent::ABORTED:
           (*iter).second->NotifyAnimationAborted((*events)[event_index]);
           break;
 
-        case AnimationEvent::PropertyUpdate:
+        case AnimationEvent::PROPERTY_UPDATE:
           (*iter).second->NotifyAnimationPropertyUpdate((*events)[event_index]);
           break;
       }
@@ -809,18 +812,6 @@
   gpu_rasterization_histogram_recorded_ = true;
 }
 
-void LayerTreeHost::CalculateLCDTextMetricsCallback(Layer* layer) {
-  if (!layer->SupportsLCDText())
-    return;
-
-  lcd_text_metrics_.total_num_cc_layers++;
-  if (layer->draw_properties().can_use_lcd_text) {
-    lcd_text_metrics_.total_num_cc_layers_can_use_lcd_text++;
-    if (layer->contents_opaque())
-      lcd_text_metrics_.total_num_cc_layers_will_use_lcd_text++;
-  }
-}
-
 bool LayerTreeHost::UsingSharedMemoryResources() {
   return GetRendererCapabilities().using_shared_memory_resources;
 }
@@ -861,29 +852,6 @@
         settings_.verify_property_trees, &update_list,
         render_surface_layer_list_id);
     LayerTreeHostCommon::CalculateDrawProperties(&inputs);
-
-    if (total_frames_used_for_lcd_text_metrics_ <=
-        kTotalFramesToUseForLCDTextMetrics) {
-      LayerTreeHostCommon::CallFunctionForSubtree(
-          root_layer,
-          base::Bind(&LayerTreeHost::CalculateLCDTextMetricsCallback,
-                     base::Unretained(this)));
-      total_frames_used_for_lcd_text_metrics_++;
-    }
-
-    if (total_frames_used_for_lcd_text_metrics_ ==
-        kTotalFramesToUseForLCDTextMetrics) {
-      total_frames_used_for_lcd_text_metrics_++;
-
-      UMA_HISTOGRAM_PERCENTAGE(
-          "Renderer4.LCDText.PercentageOfCandidateLayers",
-          lcd_text_metrics_.total_num_cc_layers_can_use_lcd_text * 100.0 /
-          lcd_text_metrics_.total_num_cc_layers);
-      UMA_HISTOGRAM_PERCENTAGE(
-          "Renderer4.LCDText.PercentageOfAALayers",
-          lcd_text_metrics_.total_num_cc_layers_will_use_lcd_text * 100.0 /
-          lcd_text_metrics_.total_num_cc_layers_can_use_lcd_text);
-    }
   }
 
   // Reset partial texture update requests.
@@ -1251,8 +1219,7 @@
          ui_resource_client_map_.end());
 
   bool resource_lost = false;
-  UIResourceRequest request(UIResourceRequest::UIResourceCreate,
-                            next_id,
+  UIResourceRequest request(UIResourceRequest::UI_RESOURCE_CREATE, next_id,
                             client->GetBitmap(next_id, resource_lost));
   ui_resource_request_queue_.push_back(request);
 
@@ -1270,7 +1237,7 @@
   if (iter == ui_resource_client_map_.end())
     return;
 
-  UIResourceRequest request(UIResourceRequest::UIResourceDelete, uid);
+  UIResourceRequest request(UIResourceRequest::UI_RESOURCE_DELETE, uid);
   ui_resource_request_queue_.push_back(request);
   ui_resource_client_map_.erase(iter);
 }
@@ -1282,8 +1249,7 @@
     UIResourceId uid = iter->first;
     const UIResourceClientData& data = iter->second;
     bool resource_lost = true;
-    UIResourceRequest request(UIResourceRequest::UIResourceCreate,
-                              uid,
+    UIResourceRequest request(UIResourceRequest::UI_RESOURCE_CREATE, uid,
                               data.client->GetBitmap(uid, resource_lost));
     ui_resource_request_queue_.push_back(request);
   }
diff --git a/cc/trees/layer_tree_host.h b/cc/trees/layer_tree_host.h
index b17b5a4..d41e73df 100644
--- a/cc/trees/layer_tree_host.h
+++ b/cc/trees/layer_tree_host.h
@@ -113,6 +113,7 @@
   void WillBeginMainFrame();
   void DidBeginMainFrame();
   void BeginMainFrame(const BeginFrameArgs& args);
+  void BeginMainFrameNotExpectedSoon();
   void AnimateLayers(base::TimeTicks monotonic_frame_begin_time);
   void DidStopFlinging();
   void Layout();
@@ -447,20 +448,6 @@
 
   bool in_paint_layer_contents_;
 
-  static const int kTotalFramesToUseForLCDTextMetrics = 50;
-  int total_frames_used_for_lcd_text_metrics_;
-
-  struct LCDTextMetrics {
-    LCDTextMetrics()
-        : total_num_cc_layers(0),
-          total_num_cc_layers_can_use_lcd_text(0),
-          total_num_cc_layers_will_use_lcd_text(0) {}
-
-    int64 total_num_cc_layers;
-    int64 total_num_cc_layers_can_use_lcd_text;
-    int64 total_num_cc_layers_will_use_lcd_text;
-  };
-  LCDTextMetrics lcd_text_metrics_;
   int id_;
   bool next_commit_forces_redraw_;
 
diff --git a/cc/trees/layer_tree_host_client.h b/cc/trees/layer_tree_host_client.h
index 574325b..6b305326 100644
--- a/cc/trees/layer_tree_host_client.h
+++ b/cc/trees/layer_tree_host_client.h
@@ -26,6 +26,7 @@
   // Marks finishing compositing-related tasks on the main thread. In threaded
   // mode, this corresponds to DidCommit().
   virtual void BeginMainFrame(const BeginFrameArgs& args) = 0;
+  virtual void BeginMainFrameNotExpectedSoon() = 0;
   virtual void DidBeginMainFrame() = 0;
   virtual void Layout() = 0;
   virtual void ApplyViewportDeltas(
diff --git a/cc/trees/layer_tree_host_common.cc b/cc/trees/layer_tree_host_common.cc
index 70c50372..7a3b5adb 100644
--- a/cc/trees/layer_tree_host_common.cc
+++ b/cc/trees/layer_tree_host_common.cc
@@ -161,8 +161,8 @@
 }
 
 enum TranslateRectDirection {
-  TranslateRectDirectionToAncestor,
-  TranslateRectDirectionToDescendant
+  TRANSLATE_RECT_DIRECTION_TO_ANCESTOR,
+  TRANSLATE_RECT_DIRECTION_TO_DESCENDANT
 };
 
 template <typename LayerType>
@@ -172,7 +172,7 @@
                                             TranslateRectDirection direction) {
   gfx::Vector2dF translation = ComputeChangeOfBasisTranslation<LayerType>(
       ancestor_layer, descendant_layer);
-  if (direction == TranslateRectDirectionToDescendant)
+  if (direction == TRANSLATE_RECT_DIRECTION_TO_DESCENDANT)
     translation.Scale(-1.f);
   return gfx::ToEnclosingRect(
       gfx::RectF(rect.origin() + translation, rect.size()));
@@ -211,20 +211,16 @@
   // clip rects will want to be in its target space, not ours.
   if (clip_parent == layer->clip_parent()) {
     *clip_rect_in_parent_target_space = TranslateRectToTargetSpace<LayerType>(
-        *clip_parent,
-        *layer->parent(),
-        *clip_rect_in_parent_target_space,
-        TranslateRectDirectionToDescendant);
+        *clip_parent, *layer->parent(), *clip_rect_in_parent_target_space,
+        TRANSLATE_RECT_DIRECTION_TO_DESCENDANT);
   } else {
     // If we're being clipped by our scroll parent, we must translate through
     // our common ancestor. This happens to be our parent, so it is sufficent to
     // translate from our clip parent's space to the space of its ancestor (our
     // parent).
-    *clip_rect_in_parent_target_space =
-        TranslateRectToTargetSpace<LayerType>(*layer->parent(),
-                                              *clip_parent,
-                                              *clip_rect_in_parent_target_space,
-                                              TranslateRectDirectionToAncestor);
+    *clip_rect_in_parent_target_space = TranslateRectToTargetSpace<LayerType>(
+        *layer->parent(), *clip_parent, *clip_rect_in_parent_target_space,
+        TRANSLATE_RECT_DIRECTION_TO_ANCESTOR);
   }
 }
 
@@ -286,10 +282,8 @@
     // so we'll need to transform it before it is applied.
     if (layer->clip_parent()) {
       clip_rect = TranslateRectToTargetSpace<LayerType>(
-          *layer->clip_parent(),
-          *layer,
-          clip_rect,
-          TranslateRectDirectionToDescendant);
+          *layer->clip_parent(), *layer, clip_rect,
+          TRANSLATE_RECT_DIRECTION_TO_DESCENDANT);
     }
     target_rect.Intersect(clip_rect);
   }
@@ -1203,12 +1197,30 @@
   }
 };
 
+static bool ValidateRenderSurface(LayerImpl* layer) {
+  // This test verifies that there are no cases where a LayerImpl needs
+  // a render surface, but doesn't have one.
+  if (layer->render_surface())
+    return true;
+
+  return layer->filters().IsEmpty() && layer->background_filters().IsEmpty() &&
+         !layer->mask_layer() && !layer->replica_layer() &&
+         !IsRootLayer(layer) && !layer->is_root_for_isolated_group() &&
+         !layer->HasCopyRequest();
+}
+
+static bool ValidateRenderSurface(Layer* layer) {
+  return true;
+}
+
 // Recursively walks the layer tree to compute any information that is needed
 // before doing the main recursion.
 template <typename LayerType>
 static void PreCalculateMetaInformation(
     LayerType* layer,
     PreCalculateMetaInformationRecursiveData* recursive_data) {
+  DCHECK(ValidateRenderSurface(layer));
+
   layer->draw_properties().sorted_for_recursion = false;
   layer->draw_properties().has_child_with_a_scroll_parent = false;
 
diff --git a/cc/trees/layer_tree_host_common_unittest.cc b/cc/trees/layer_tree_host_common_unittest.cc
index de5f44a..30cc15ab 100644
--- a/cc/trees/layer_tree_host_common_unittest.cc
+++ b/cc/trees/layer_tree_host_common_unittest.cc
@@ -5677,56 +5677,60 @@
 class LCDTextTest
     : public LayerTreeHostCommonTestBase,
       public testing::TestWithParam<LCDTextTestParam> {
+ public:
+  LCDTextTest()
+      : host_impl_(&proxy_, &shared_bitmap_manager_),
+        root_(nullptr),
+        child_(nullptr),
+        grand_child_(nullptr) {}
+
  protected:
   void SetUp() override {
     can_use_lcd_text_ = std::tr1::get<0>(GetParam());
     layers_always_allowed_lcd_text_ = std::tr1::get<1>(GetParam());
 
-    root_ = Layer::Create();
-    child_ = Layer::Create();
-    grand_child_ = Layer::Create();
-    child_->AddChild(grand_child_.get());
-    root_->AddChild(child_.get());
+    scoped_ptr<LayerImpl> root_ptr =
+        LayerImpl::Create(host_impl_.active_tree(), 1);
+    scoped_ptr<LayerImpl> child_ptr =
+        LayerImpl::Create(host_impl_.active_tree(), 2);
+    scoped_ptr<LayerImpl> grand_child_ptr =
+        LayerImpl::Create(host_impl_.active_tree(), 3);
+
+    // Stash raw pointers to look at later.
+    root_ = root_ptr.get();
+    child_ = child_ptr.get();
+    grand_child_ = grand_child_ptr.get();
+
+    child_->AddChild(grand_child_ptr.Pass());
+    root_->AddChild(child_ptr.Pass());
+    host_impl_.active_tree()->SetRootLayer(root_ptr.Pass());
 
     root_->SetContentsOpaque(true);
     child_->SetContentsOpaque(true);
     grand_child_->SetContentsOpaque(true);
 
     gfx::Transform identity_matrix;
-    SetLayerPropertiesForTesting(root_.get(),
-                                 identity_matrix,
-                                 gfx::Point3F(),
-                                 gfx::PointF(),
-                                 gfx::Size(1, 1),
-                                 true,
+    SetLayerPropertiesForTesting(root_, identity_matrix, gfx::Point3F(),
+                                 gfx::PointF(), gfx::Size(1, 1), true, false,
+                                 true);
+    SetLayerPropertiesForTesting(child_, identity_matrix, gfx::Point3F(),
+                                 gfx::PointF(), gfx::Size(1, 1), true, false,
+                                 std::tr1::get<2>(GetParam()));
+    SetLayerPropertiesForTesting(grand_child_, identity_matrix, gfx::Point3F(),
+                                 gfx::PointF(), gfx::Size(1, 1), true, false,
                                  false);
-    SetLayerPropertiesForTesting(child_.get(),
-                                 identity_matrix,
-                                 gfx::Point3F(),
-                                 gfx::PointF(),
-                                 gfx::Size(1, 1),
-                                 true,
-                                 false);
-    SetLayerPropertiesForTesting(grand_child_.get(),
-                                 identity_matrix,
-                                 gfx::Point3F(),
-                                 gfx::PointF(),
-                                 gfx::Size(1, 1),
-                                 true,
-                                 false);
-
-    child_->SetForceRenderSurface(std::tr1::get<2>(GetParam()));
-
-    host_ = CreateFakeLayerTreeHost();
-    host_->SetRootLayer(root_);
   }
 
   bool can_use_lcd_text_;
   bool layers_always_allowed_lcd_text_;
-  scoped_ptr<FakeLayerTreeHost> host_;
-  scoped_refptr<Layer> root_;
-  scoped_refptr<Layer> child_;
-  scoped_refptr<Layer> grand_child_;
+
+  FakeImplProxy proxy_;
+  TestSharedBitmapManager shared_bitmap_manager_;
+  FakeLayerTreeHostImpl host_impl_;
+
+  LayerImpl* root_;
+  LayerImpl* child_;
+  LayerImpl* grand_child_;
 };
 
 TEST_P(LCDTextTest, CanUseLCDText) {
@@ -5735,7 +5739,7 @@
 
   // Case 1: Identity transform.
   gfx::Transform identity_matrix;
-  ExecuteCalculateDrawProperties(root_.get(), 1.f, 1.f, NULL, can_use_lcd_text_,
+  ExecuteCalculateDrawProperties(root_, 1.f, 1.f, NULL, can_use_lcd_text_,
                                  layers_always_allowed_lcd_text_);
   EXPECT_EQ(expect_lcd_text, root_->can_use_lcd_text());
   EXPECT_EQ(expect_lcd_text, child_->can_use_lcd_text());
@@ -5745,7 +5749,7 @@
   gfx::Transform integral_translation;
   integral_translation.Translate(1.0, 2.0);
   child_->SetTransform(integral_translation);
-  ExecuteCalculateDrawProperties(root_.get(), 1.f, 1.f, NULL, can_use_lcd_text_,
+  ExecuteCalculateDrawProperties(root_, 1.f, 1.f, NULL, can_use_lcd_text_,
                                  layers_always_allowed_lcd_text_);
   EXPECT_EQ(expect_lcd_text, root_->can_use_lcd_text());
   EXPECT_EQ(expect_lcd_text, child_->can_use_lcd_text());
@@ -5755,7 +5759,7 @@
   gfx::Transform non_integral_translation;
   non_integral_translation.Translate(1.5, 2.5);
   child_->SetTransform(non_integral_translation);
-  ExecuteCalculateDrawProperties(root_.get(), 1.f, 1.f, NULL, can_use_lcd_text_,
+  ExecuteCalculateDrawProperties(root_, 1.f, 1.f, NULL, can_use_lcd_text_,
                                  layers_always_allowed_lcd_text_);
   EXPECT_EQ(expect_lcd_text, root_->can_use_lcd_text());
   EXPECT_EQ(expect_not_lcd_text, child_->can_use_lcd_text());
@@ -5765,7 +5769,7 @@
   gfx::Transform rotation;
   rotation.Rotate(10.0);
   child_->SetTransform(rotation);
-  ExecuteCalculateDrawProperties(root_.get(), 1.f, 1.f, NULL, can_use_lcd_text_,
+  ExecuteCalculateDrawProperties(root_, 1.f, 1.f, NULL, can_use_lcd_text_,
                                  layers_always_allowed_lcd_text_);
   EXPECT_EQ(expect_lcd_text, root_->can_use_lcd_text());
   EXPECT_EQ(expect_not_lcd_text, child_->can_use_lcd_text());
@@ -5775,7 +5779,7 @@
   gfx::Transform scale;
   scale.Scale(2.0, 2.0);
   child_->SetTransform(scale);
-  ExecuteCalculateDrawProperties(root_.get(), 1.f, 1.f, NULL, can_use_lcd_text_,
+  ExecuteCalculateDrawProperties(root_, 1.f, 1.f, NULL, can_use_lcd_text_,
                                  layers_always_allowed_lcd_text_);
   EXPECT_EQ(expect_lcd_text, root_->can_use_lcd_text());
   EXPECT_EQ(expect_not_lcd_text, child_->can_use_lcd_text());
@@ -5785,7 +5789,7 @@
   gfx::Transform skew;
   skew.SkewX(10.0);
   child_->SetTransform(skew);
-  ExecuteCalculateDrawProperties(root_.get(), 1.f, 1.f, NULL, can_use_lcd_text_,
+  ExecuteCalculateDrawProperties(root_, 1.f, 1.f, NULL, can_use_lcd_text_,
                                  layers_always_allowed_lcd_text_);
   EXPECT_EQ(expect_lcd_text, root_->can_use_lcd_text());
   EXPECT_EQ(expect_not_lcd_text, child_->can_use_lcd_text());
@@ -5794,7 +5798,7 @@
   // Case 7: Translucent.
   child_->SetTransform(identity_matrix);
   child_->SetOpacity(0.5f);
-  ExecuteCalculateDrawProperties(root_.get(), 1.f, 1.f, NULL, can_use_lcd_text_,
+  ExecuteCalculateDrawProperties(root_, 1.f, 1.f, NULL, can_use_lcd_text_,
                                  layers_always_allowed_lcd_text_);
   EXPECT_EQ(expect_lcd_text, root_->can_use_lcd_text());
   EXPECT_EQ(expect_not_lcd_text, child_->can_use_lcd_text());
@@ -5803,7 +5807,7 @@
   // Case 8: Sanity check: restore transform and opacity.
   child_->SetTransform(identity_matrix);
   child_->SetOpacity(1.f);
-  ExecuteCalculateDrawProperties(root_.get(), 1.f, 1.f, NULL, can_use_lcd_text_,
+  ExecuteCalculateDrawProperties(root_, 1.f, 1.f, NULL, can_use_lcd_text_,
                                  layers_always_allowed_lcd_text_);
   EXPECT_EQ(expect_lcd_text, root_->can_use_lcd_text());
   EXPECT_EQ(expect_lcd_text, child_->can_use_lcd_text());
@@ -5811,7 +5815,7 @@
 
   // Case 9: Non-opaque content.
   child_->SetContentsOpaque(false);
-  ExecuteCalculateDrawProperties(root_.get(), 1.f, 1.f, NULL, can_use_lcd_text_,
+  ExecuteCalculateDrawProperties(root_, 1.f, 1.f, NULL, can_use_lcd_text_,
                                  layers_always_allowed_lcd_text_);
   EXPECT_EQ(expect_lcd_text, root_->can_use_lcd_text());
   EXPECT_EQ(expect_not_lcd_text, child_->can_use_lcd_text());
@@ -5819,7 +5823,7 @@
 
   // Case 10: Sanity check: restore content opaqueness.
   child_->SetContentsOpaque(true);
-  ExecuteCalculateDrawProperties(root_.get(), 1.f, 1.f, NULL, can_use_lcd_text_,
+  ExecuteCalculateDrawProperties(root_, 1.f, 1.f, NULL, can_use_lcd_text_,
                                  layers_always_allowed_lcd_text_);
   EXPECT_EQ(expect_lcd_text, root_->can_use_lcd_text());
   EXPECT_EQ(expect_lcd_text, child_->can_use_lcd_text());
@@ -5830,7 +5834,7 @@
   bool expect_lcd_text = can_use_lcd_text_ || layers_always_allowed_lcd_text_;
 
   // Sanity check: Make sure can_use_lcd_text_ is set on each node.
-  ExecuteCalculateDrawProperties(root_.get(), 1.f, 1.f, NULL, can_use_lcd_text_,
+  ExecuteCalculateDrawProperties(root_, 1.f, 1.f, NULL, can_use_lcd_text_,
                                  layers_always_allowed_lcd_text_);
   EXPECT_EQ(expect_lcd_text, root_->can_use_lcd_text());
   EXPECT_EQ(expect_lcd_text, child_->can_use_lcd_text());
@@ -5841,7 +5845,7 @@
   AddOpacityTransitionToController(
       child_->layer_animation_controller(), 10.0, 0.9f, 0.1f, false);
 
-  ExecuteCalculateDrawProperties(root_.get(), 1.f, 1.f, NULL, can_use_lcd_text_,
+  ExecuteCalculateDrawProperties(root_, 1.f, 1.f, NULL, can_use_lcd_text_,
                                  layers_always_allowed_lcd_text_);
   // Text AA should not be adjusted while animation is active.
   // Make sure LCD text AA setting remains unchanged.
@@ -7971,11 +7975,11 @@
       0.f, grand_child_raw->draw_properties().maximum_animation_contents_scale);
 
   grand_parent->layer_animation_controller()->AbortAnimations(
-      Animation::Transform);
+      Animation::TRANSFORM);
   parent_raw->layer_animation_controller()->AbortAnimations(
-      Animation::Transform);
+      Animation::TRANSFORM);
   child_raw->layer_animation_controller()->AbortAnimations(
-      Animation::Transform);
+      Animation::TRANSFORM);
 
   TransformOperations perspective;
   perspective.AppendPerspective(10.f);
@@ -7995,7 +7999,7 @@
       0.f, grand_child_raw->draw_properties().maximum_animation_contents_scale);
 
   child_raw->layer_animation_controller()->AbortAnimations(
-      Animation::Transform);
+      Animation::TRANSFORM);
 
   gfx::Transform scale_matrix;
   scale_matrix.Scale(1.f, 2.f);
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc
index 3cb2858..eef1cc37 100644
--- a/cc/trees/layer_tree_host_impl.cc
+++ b/cc/trees/layer_tree_host_impl.cc
@@ -307,8 +307,11 @@
   if (settings_.impl_side_painting) {
     // Impl-side painting needs an update immediately post-commit to have the
     // opportunity to create tilings.  Other paths can call UpdateDrawProperties
-    // more lazily when needed prior to drawing.
-    sync_tree()->UpdateDrawProperties();
+    // more lazily when needed prior to drawing.  Because invalidations may
+    // be coming from the main thread, it's safe to do an update for lcd text
+    // at this point and see if lcd text needs to be disabled on any layers.
+    bool update_lcd_text = true;
+    sync_tree()->UpdateDrawProperties(update_lcd_text);
     // Start working on newly created tiles immediately if needed.
     if (tile_manager_ && tile_priorities_dirty_)
       PrepareTiles();
@@ -473,7 +476,7 @@
 }
 
 static ScrollBlocksOn EffectiveScrollBlocksOn(LayerImpl* layer) {
-  ScrollBlocksOn blocks = ScrollBlocksOnNone;
+  ScrollBlocksOn blocks = SCROLL_BLOCKS_ON_NONE;
   for (; layer; layer = NextScrollLayer(layer)) {
     blocks |= layer->scroll_blocks_on();
   }
@@ -492,7 +495,7 @@
   LayerImpl* layer_impl =
       active_tree_->FindLayerThatIsHitByPoint(device_viewport_point);
   ScrollBlocksOn blocking = EffectiveScrollBlocksOn(layer_impl);
-  if (!(blocking & ScrollBlocksOnStartTouch))
+  if (!(blocking & SCROLL_BLOCKS_ON_START_TOUCH))
     return false;
 
   // Now determine if there are actually any handlers at that point.
@@ -1046,7 +1049,8 @@
   UMA_HISTOGRAM_CUSTOM_COUNTS(
       "Compositing.NumActiveLayers", active_tree_->NumLayers(), 1, 400, 20);
 
-  bool ok = active_tree_->UpdateDrawProperties();
+  bool update_lcd_text = false;
+  bool ok = active_tree_->UpdateDrawProperties(update_lcd_text);
   DCHECK(ok) << "UpdateDrawProperties failed during draw";
 
   // This will cause NotifyTileStateChanged() to be called for any visible tiles
@@ -1619,12 +1623,19 @@
   }
   CompositorFrameMetadata metadata = MakeCompositorFrameMetadata();
   active_tree()->FinishSwapPromises(&metadata);
-  for (size_t i = 0; i < metadata.latency_info.size(); i++) {
+  for (auto& latency : metadata.latency_info) {
     TRACE_EVENT_FLOW_STEP0(
         "input,benchmark",
         "LatencyInfo.Flow",
-        TRACE_ID_DONT_MANGLE(metadata.latency_info[i].trace_id),
+        TRACE_ID_DONT_MANGLE(latency.trace_id),
         "SwapBuffers");
+    // Only add the latency component once for renderer swap, not the browser
+    // swap.
+    if (!latency.FindLatency(ui::INPUT_EVENT_LATENCY_RENDERER_SWAP_COMPONENT,
+                             0, nullptr)) {
+      latency.AddLatencyNumber(ui::INPUT_EVENT_LATENCY_RENDERER_SWAP_COMPONENT,
+                               0, 0);
+    }
   }
   renderer_->SwapBuffers(metadata);
   return true;
@@ -2295,7 +2306,7 @@
     // thread.
     ScrollStatus status =
         layer_impl->TryScroll(device_viewport_point, type, block_mode);
-    if (status == ScrollOnMainThread) {
+    if (status == SCROLL_ON_MAIN_THREAD) {
       *scroll_on_main_thread = true;
       return NULL;
     }
@@ -2307,7 +2318,7 @@
     status =
         scroll_layer_impl->TryScroll(device_viewport_point, type, block_mode);
     // If any layer wants to divert the scroll event to the main thread, abort.
-    if (status == ScrollOnMainThread) {
+    if (status == SCROLL_ON_MAIN_THREAD) {
       *scroll_on_main_thread = true;
       return NULL;
     }
@@ -2316,7 +2327,7 @@
         scroll_layer_impl->have_scroll_event_handlers())
       *optional_has_ancestor_scroll_handler = true;
 
-    if (status == ScrollStarted && !potentially_scrolling_layer_impl)
+    if (status == SCROLL_STARTED && !potentially_scrolling_layer_impl)
       potentially_scrolling_layer_impl = scroll_layer_impl;
   }
 
@@ -2363,7 +2374,7 @@
         active_tree_->FindFirstScrollingLayerThatIsHitByPoint(
             device_viewport_point);
     if (scroll_layer_impl && !HasScrollAncestor(layer_impl, scroll_layer_impl))
-      return ScrollUnknown;
+      return SCROLL_UNKNOWN;
   }
 
   bool scroll_on_main_thread = false;
@@ -2376,18 +2387,18 @@
 
   if (scroll_on_main_thread) {
     UMA_HISTOGRAM_BOOLEAN("TryScroll.SlowScroll", true);
-    return ScrollOnMainThread;
+    return SCROLL_ON_MAIN_THREAD;
   }
 
   if (scrolling_layer_impl) {
     active_tree_->SetCurrentlyScrollingLayer(scrolling_layer_impl);
-    should_bubble_scrolls_ = (type != NonBubblingGesture);
-    wheel_scrolling_ = (type == Wheel);
+    should_bubble_scrolls_ = (type != NON_BUBBLING_GESTURE);
+    wheel_scrolling_ = (type == WHEEL);
     client_->RenewTreePriority();
     UMA_HISTOGRAM_BOOLEAN("TryScroll.SlowScroll", false);
-    return ScrollStarted;
+    return SCROLL_STARTED;
   }
-  return ScrollIgnored;
+  return SCROLL_IGNORED;
 }
 
 InputHandler::ScrollStatus LayerTreeHostImpl::ScrollAnimated(
@@ -2396,9 +2407,9 @@
   if (LayerImpl* layer_impl = CurrentlyScrollingLayer()) {
     Animation* animation =
         layer_impl->layer_animation_controller()->GetAnimation(
-            Animation::ScrollOffset);
+            Animation::SCROLL_OFFSET);
     if (!animation)
-      return ScrollIgnored;
+      return SCROLL_IGNORED;
 
     ScrollOffsetAnimationCurve* curve =
         animation->curve()->ToScrollOffsetAnimationCurve();
@@ -2413,13 +2424,13 @@
                        CurrentBeginFrameArgs().frame_time).InSecondsF(),
         new_target);
 
-    return ScrollStarted;
+    return SCROLL_STARTED;
   }
   // ScrollAnimated is only used for wheel scrolls. We use the same bubbling
   // behavior as ScrollBy to determine which layer to animate, but we do not
   // do the Android-specific things in ScrollBy like showing top controls.
-  InputHandler::ScrollStatus scroll_status = ScrollBegin(viewport_point, Wheel);
-  if (scroll_status == ScrollStarted) {
+  InputHandler::ScrollStatus scroll_status = ScrollBegin(viewport_point, WHEEL);
+  if (scroll_status == SCROLL_STARTED) {
     gfx::Vector2dF pending_delta = scroll_delta;
     for (LayerImpl* layer_impl = CurrentlyScrollingLayer(); layer_impl;
          layer_impl = layer_impl->parent()) {
@@ -2450,17 +2461,15 @@
                                              EaseInOutTimingFunction::Create());
       curve->SetInitialValue(current_offset);
 
-      scoped_ptr<Animation> animation =
-          Animation::Create(curve.Pass(),
-                            AnimationIdProvider::NextAnimationId(),
-                            AnimationIdProvider::NextGroupId(),
-                            Animation::ScrollOffset);
+      scoped_ptr<Animation> animation = Animation::Create(
+          curve.Pass(), AnimationIdProvider::NextAnimationId(),
+          AnimationIdProvider::NextGroupId(), Animation::SCROLL_OFFSET);
       animation->set_is_impl_only(true);
 
       layer_impl->layer_animation_controller()->AddAnimation(animation.Pass());
 
       SetNeedsAnimate();
-      return ScrollStarted;
+      return SCROLL_STARTED;
     }
   }
   ScrollEnd();
@@ -2811,13 +2820,13 @@
 
 InputHandler::ScrollStatus LayerTreeHostImpl::FlingScrollBegin() {
   if (!active_tree_->CurrentlyScrollingLayer())
-    return ScrollIgnored;
+    return SCROLL_IGNORED;
 
   if (settings_.ignore_root_layer_flings &&
       (active_tree_->CurrentlyScrollingLayer() == InnerViewportScrollLayer() ||
        active_tree_->CurrentlyScrollingLayer() == OuterViewportScrollLayer())) {
     ClearCurrentlyScrollingLayer();
-    return ScrollIgnored;
+    return SCROLL_IGNORED;
   }
 
   if (!wheel_scrolling_) {
@@ -2827,7 +2836,7 @@
     should_bubble_scrolls_ = false;
   }
 
-  return ScrollStarted;
+  return SCROLL_STARTED;
 }
 
 float LayerTreeHostImpl::DeviceSpaceDistanceToLayer(
@@ -2871,12 +2880,9 @@
   }
 
   bool scroll_on_main_thread = false;
-  LayerImpl* scroll_layer_impl =
-      FindScrollLayerForDeviceViewportPoint(device_viewport_point,
-                                            InputHandler::Gesture,
-                                            layer_impl,
-                                            &scroll_on_main_thread,
-                                            NULL);
+  LayerImpl* scroll_layer_impl = FindScrollLayerForDeviceViewportPoint(
+      device_viewport_point, InputHandler::GESTURE, layer_impl,
+      &scroll_on_main_thread, NULL);
   if (scroll_on_main_thread || !scroll_layer_impl)
     return;
 
@@ -3373,11 +3379,9 @@
       format = ETC1;
       break;
   }
-  id =
-      resource_provider_->CreateResource(bitmap.GetSize(),
-                                         wrap_mode,
-                                         ResourceProvider::TextureHintImmutable,
-                                         format);
+  id = resource_provider_->CreateResource(
+      bitmap.GetSize(), wrap_mode, ResourceProvider::TEXTURE_HINT_IMMUTABLE,
+      format);
 
   UIResourceData data;
   data.resource_id = id;
@@ -3387,11 +3391,8 @@
   ui_resource_map_[uid] = data;
 
   AutoLockUIResourceBitmap bitmap_lock(bitmap);
-  resource_provider_->SetPixels(id,
-                                bitmap_lock.GetPixels(),
-                                gfx::Rect(bitmap.GetSize()),
-                                gfx::Rect(bitmap.GetSize()),
-                                gfx::Vector2d(0, 0));
+  resource_provider_->CopyToResource(id, bitmap_lock.GetPixels(),
+                                     bitmap.GetSize());
   MarkUIResourceNotEvicted(uid);
 }
 
diff --git a/cc/trees/layer_tree_host_impl.h b/cc/trees/layer_tree_host_impl.h
index 6dd0d5a74..60f4b447 100644
--- a/cc/trees/layer_tree_host_impl.h
+++ b/cc/trees/layer_tree_host_impl.h
@@ -328,6 +328,8 @@
   const LayerTreeImpl* recycle_tree() const { return recycle_tree_.get(); }
   // Returns the tree LTH synchronizes with.
   LayerTreeImpl* sync_tree() {
+    // TODO(enne): This is bogus.  It should return based on the value of
+    // Proxy::CommitToActiveTree and not whether the pending tree exists.
     return pending_tree_ ? pending_tree_.get() : active_tree_.get();
   }
   virtual void CreatePendingTree();
diff --git a/cc/trees/layer_tree_host_impl_unittest.cc b/cc/trees/layer_tree_host_impl_unittest.cc
index aa46125a..70fa92b 100644
--- a/cc/trees/layer_tree_host_impl_unittest.cc
+++ b/cc/trees/layer_tree_host_impl_unittest.cc
@@ -502,16 +502,16 @@
   host_impl_->SetViewportSize(gfx::Size(50, 50));
   DrawFrame();
 
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(), InputHandler::Wheel));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::WHEEL));
   EXPECT_TRUE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point(),
-                                                      InputHandler::Wheel));
+                                                      InputHandler::WHEEL));
   host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(0, 10));
   EXPECT_TRUE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point(0, 10),
-                                                      InputHandler::Wheel));
+                                                      InputHandler::WHEEL));
   host_impl_->ScrollEnd();
   EXPECT_FALSE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point(),
-                                                       InputHandler::Wheel));
+                                                       InputHandler::WHEEL));
   EXPECT_TRUE(did_request_redraw_);
   EXPECT_TRUE(did_request_commit_);
 }
@@ -521,8 +521,8 @@
   host_impl_->SetViewportSize(gfx::Size(50, 50));
   DrawFrame();
 
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(), InputHandler::Wheel));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::WHEEL));
   EXPECT_FALSE(host_impl_->IsActivelyScrolling());
   host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(0, 10));
   EXPECT_TRUE(host_impl_->IsActivelyScrolling());
@@ -532,8 +532,8 @@
 
 TEST_F(LayerTreeHostImplTest, ScrollWithoutRootLayer) {
   // We should not crash when trying to scroll an empty layer tree.
-  EXPECT_EQ(InputHandler::ScrollIgnored,
-            host_impl_->ScrollBegin(gfx::Point(), InputHandler::Wheel));
+  EXPECT_EQ(InputHandler::SCROLL_IGNORED,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::WHEEL));
 }
 
 TEST_F(LayerTreeHostImplTest, ScrollWithoutRenderer) {
@@ -549,8 +549,8 @@
 
   // We should not crash when trying to scroll after the renderer initialization
   // fails.
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(), InputHandler::Wheel));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::WHEEL));
 }
 
 TEST_F(LayerTreeHostImplTest, ReplaceTreeWhileScrolling) {
@@ -559,8 +559,8 @@
   DrawFrame();
 
   // We should not crash if the tree is replaced while we are scrolling.
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(), InputHandler::Wheel));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::WHEEL));
   host_impl_->active_tree()->DetachLayerTree();
 
   scroll_layer = SetupScrollAndContentsLayers(gfx::Size(100, 100));
@@ -583,25 +583,26 @@
   // With registered event handlers, wheel scrolls don't necessarily
   // have to go to the main thread.
   root->SetHaveWheelEventHandlers(true);
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(), InputHandler::Wheel));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::WHEEL));
   host_impl_->ScrollEnd();
 
   // But typically the scroll-blocks-on mode will require them to.
-  root->SetScrollBlocksOn(ScrollBlocksOnWheelEvent | ScrollBlocksOnStartTouch);
-  EXPECT_EQ(InputHandler::ScrollOnMainThread,
-            host_impl_->ScrollBegin(gfx::Point(), InputHandler::Wheel));
+  root->SetScrollBlocksOn(SCROLL_BLOCKS_ON_WHEEL_EVENT |
+                          SCROLL_BLOCKS_ON_START_TOUCH);
+  EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::WHEEL));
 
   // But gesture scrolls can still be handled.
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::GESTURE));
   host_impl_->ScrollEnd();
 
   // And if the handlers go away, wheel scrolls can again be processed
   // on impl (despite the scroll-blocks-on mode).
   root->SetHaveWheelEventHandlers(false);
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(), InputHandler::Wheel));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::WHEEL));
   host_impl_->ScrollEnd();
 }
 
@@ -626,20 +627,21 @@
   // Touch handler regions determine whether touch events block scroll.
   root->SetTouchEventHandlerRegion(gfx::Rect(0, 0, 100, 100));
   EXPECT_FALSE(host_impl_->DoTouchEventsBlockScrollAt(gfx::Point(10, 10)));
-  root->SetScrollBlocksOn(ScrollBlocksOnStartTouch | ScrollBlocksOnWheelEvent);
+  root->SetScrollBlocksOn(SCROLL_BLOCKS_ON_START_TOUCH |
+                          SCROLL_BLOCKS_ON_WHEEL_EVENT);
   EXPECT_TRUE(host_impl_->DoTouchEventsBlockScrollAt(gfx::Point(10, 10)));
 
   // But they don't influence the actual handling of the scroll gestures.
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::GESTURE));
   host_impl_->ScrollEnd();
 
   // It's the union of scroll-blocks-on mode bits across all layers in the
   // scroll paret chain that matters.
   EXPECT_TRUE(host_impl_->DoTouchEventsBlockScrollAt(gfx::Point(10, 30)));
-  root->SetScrollBlocksOn(ScrollBlocksOnNone);
+  root->SetScrollBlocksOn(SCROLL_BLOCKS_ON_NONE);
   EXPECT_FALSE(host_impl_->DoTouchEventsBlockScrollAt(gfx::Point(10, 30)));
-  child->SetScrollBlocksOn(ScrollBlocksOnStartTouch);
+  child->SetScrollBlocksOn(SCROLL_BLOCKS_ON_START_TOUCH);
   EXPECT_TRUE(host_impl_->DoTouchEventsBlockScrollAt(gfx::Point(10, 30)));
 }
 
@@ -652,30 +654,31 @@
   // With registered scroll handlers, scrolls don't generally have to go
   // to the main thread.
   root->SetHaveScrollEventHandlers(true);
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(), InputHandler::Wheel));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::WHEEL));
   host_impl_->ScrollEnd();
 
   // Even the default scroll blocks on mode doesn't require this.
-  root->SetScrollBlocksOn(ScrollBlocksOnWheelEvent | ScrollBlocksOnStartTouch);
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture));
+  root->SetScrollBlocksOn(SCROLL_BLOCKS_ON_WHEEL_EVENT |
+                          SCROLL_BLOCKS_ON_START_TOUCH);
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::GESTURE));
   host_impl_->ScrollEnd();
 
   // But the page can opt in to blocking on scroll event handlers.
-  root->SetScrollBlocksOn(ScrollBlocksOnScrollEvent);
-  EXPECT_EQ(InputHandler::ScrollOnMainThread,
-            host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture));
+  root->SetScrollBlocksOn(SCROLL_BLOCKS_ON_SCROLL_EVENT);
+  EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::GESTURE));
 
-  // Gesture and Wheel scrolls behave identically in this regard.
-  EXPECT_EQ(InputHandler::ScrollOnMainThread,
-            host_impl_->ScrollBegin(gfx::Point(), InputHandler::Wheel));
+  // GESTURE and WHEEL scrolls behave identically in this regard.
+  EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::WHEEL));
 
   // And if the handlers go away, scrolls can again be processed on impl
   // (despite the scroll-blocks-on mode).
   root->SetHaveScrollEventHandlers(false);
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::GESTURE));
   host_impl_->ScrollEnd();
 }
 
@@ -718,36 +721,36 @@
   }
 
   // Scroll-blocks-on on a layer affects scrolls that hit that layer.
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(10, 10), InputHandler::Gesture));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(10, 10), InputHandler::GESTURE));
   host_impl_->ScrollEnd();
-  child1->SetScrollBlocksOn(ScrollBlocksOnScrollEvent);
-  EXPECT_EQ(InputHandler::ScrollOnMainThread,
-            host_impl_->ScrollBegin(gfx::Point(10, 10), InputHandler::Gesture));
+  child1->SetScrollBlocksOn(SCROLL_BLOCKS_ON_SCROLL_EVENT);
+  EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD,
+            host_impl_->ScrollBegin(gfx::Point(10, 10), InputHandler::GESTURE));
 
   // But not those that hit only other layers.
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(10, 25), InputHandler::Gesture));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(10, 25), InputHandler::GESTURE));
   host_impl_->ScrollEnd();
 
   // It's the union of bits set across the scroll ancestor chain that matters.
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(10, 25), InputHandler::Gesture));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(10, 25), InputHandler::GESTURE));
   host_impl_->ScrollEnd();
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(10, 25), InputHandler::Wheel));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(10, 25), InputHandler::WHEEL));
   host_impl_->ScrollEnd();
-  root->SetScrollBlocksOn(ScrollBlocksOnWheelEvent);
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(10, 25), InputHandler::Gesture));
+  root->SetScrollBlocksOn(SCROLL_BLOCKS_ON_WHEEL_EVENT);
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(10, 25), InputHandler::GESTURE));
   host_impl_->ScrollEnd();
-  EXPECT_EQ(InputHandler::ScrollOnMainThread,
-            host_impl_->ScrollBegin(gfx::Point(10, 25), InputHandler::Wheel));
-  child2->SetScrollBlocksOn(ScrollBlocksOnScrollEvent);
-  EXPECT_EQ(InputHandler::ScrollOnMainThread,
-            host_impl_->ScrollBegin(gfx::Point(10, 25), InputHandler::Wheel));
-  EXPECT_EQ(InputHandler::ScrollOnMainThread,
-            host_impl_->ScrollBegin(gfx::Point(10, 25), InputHandler::Gesture));
+  EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD,
+            host_impl_->ScrollBegin(gfx::Point(10, 25), InputHandler::WHEEL));
+  child2->SetScrollBlocksOn(SCROLL_BLOCKS_ON_SCROLL_EVENT);
+  EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD,
+            host_impl_->ScrollBegin(gfx::Point(10, 25), InputHandler::WHEEL));
+  EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD,
+            host_impl_->ScrollBegin(gfx::Point(10, 25), InputHandler::GESTURE));
 }
 
 TEST_F(LayerTreeHostImplTest, FlingOnlyWhenScrollingTouchscreen) {
@@ -756,16 +759,14 @@
   DrawFrame();
 
   // Ignore the fling since no layer is being scrolled
-  EXPECT_EQ(InputHandler::ScrollIgnored,
-            host_impl_->FlingScrollBegin());
+  EXPECT_EQ(InputHandler::SCROLL_IGNORED, host_impl_->FlingScrollBegin());
 
   // Start scrolling a layer
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::GESTURE));
 
   // Now the fling should go ahead since we've started scrolling a layer
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->FlingScrollBegin());
+  EXPECT_EQ(InputHandler::SCROLL_STARTED, host_impl_->FlingScrollBegin());
 }
 
 TEST_F(LayerTreeHostImplTest, FlingOnlyWhenScrollingTouchpad) {
@@ -774,16 +775,14 @@
   DrawFrame();
 
   // Ignore the fling since no layer is being scrolled
-  EXPECT_EQ(InputHandler::ScrollIgnored,
-            host_impl_->FlingScrollBegin());
+  EXPECT_EQ(InputHandler::SCROLL_IGNORED, host_impl_->FlingScrollBegin());
 
   // Start scrolling a layer
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(), InputHandler::Wheel));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::WHEEL));
 
   // Now the fling should go ahead since we've started scrolling a layer
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->FlingScrollBegin());
+  EXPECT_EQ(InputHandler::SCROLL_STARTED, host_impl_->FlingScrollBegin());
 }
 
 TEST_F(LayerTreeHostImplTest, NoFlingWhenScrollingOnMain) {
@@ -795,12 +794,11 @@
   root->SetShouldScrollOnMainThread(true);
 
   // Start scrolling a layer
-  EXPECT_EQ(InputHandler::ScrollOnMainThread,
-            host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture));
+  EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::GESTURE));
 
   // The fling should be ignored since there's no layer being scrolled impl-side
-  EXPECT_EQ(InputHandler::ScrollIgnored,
-            host_impl_->FlingScrollBegin());
+  EXPECT_EQ(InputHandler::SCROLL_IGNORED, host_impl_->FlingScrollBegin());
 }
 
 TEST_F(LayerTreeHostImplTest, ShouldScrollOnMainThread) {
@@ -811,10 +809,10 @@
 
   root->SetShouldScrollOnMainThread(true);
 
-  EXPECT_EQ(InputHandler::ScrollOnMainThread,
-            host_impl_->ScrollBegin(gfx::Point(), InputHandler::Wheel));
-  EXPECT_EQ(InputHandler::ScrollOnMainThread,
-            host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture));
+  EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::WHEEL));
+  EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::GESTURE));
 }
 
 TEST_F(LayerTreeHostImplTest, NonFastScrollableRegionBasic) {
@@ -828,38 +826,34 @@
   DrawFrame();
 
   // All scroll types inside the non-fast scrollable region should fail.
-  EXPECT_EQ(InputHandler::ScrollOnMainThread,
-            host_impl_->ScrollBegin(gfx::Point(25, 25),
-                                    InputHandler::Wheel));
+  EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD,
+            host_impl_->ScrollBegin(gfx::Point(25, 25), InputHandler::WHEEL));
   EXPECT_FALSE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point(25, 25),
-                                                       InputHandler::Wheel));
-  EXPECT_EQ(InputHandler::ScrollOnMainThread,
-            host_impl_->ScrollBegin(gfx::Point(25, 25),
-                                    InputHandler::Gesture));
+                                                       InputHandler::WHEEL));
+  EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD,
+            host_impl_->ScrollBegin(gfx::Point(25, 25), InputHandler::GESTURE));
   EXPECT_FALSE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point(25, 25),
-                                                       InputHandler::Gesture));
+                                                       InputHandler::GESTURE));
 
   // All scroll types outside this region should succeed.
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(75, 75),
-                                    InputHandler::Wheel));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(75, 75), InputHandler::WHEEL));
   EXPECT_TRUE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point(75, 75),
-                                                      InputHandler::Gesture));
+                                                      InputHandler::GESTURE));
   host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(0, 10));
   EXPECT_FALSE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point(25, 25),
-                                                       InputHandler::Gesture));
+                                                       InputHandler::GESTURE));
   host_impl_->ScrollEnd();
   EXPECT_FALSE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point(75, 75),
-                                                       InputHandler::Gesture));
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(75, 75),
-                                    InputHandler::Gesture));
+                                                       InputHandler::GESTURE));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(75, 75), InputHandler::GESTURE));
   EXPECT_TRUE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point(75, 75),
-                                                      InputHandler::Gesture));
+                                                      InputHandler::GESTURE));
   host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(0, 10));
   host_impl_->ScrollEnd();
   EXPECT_FALSE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point(75, 75),
-                                                       InputHandler::Gesture));
+                                                       InputHandler::GESTURE));
 }
 
 TEST_F(LayerTreeHostImplTest, NonFastScrollableRegionWithOffset) {
@@ -875,18 +869,16 @@
 
   // This point would fall into the non-fast scrollable region except that we've
   // moved the layer down by 25 pixels.
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(40, 10),
-                                    InputHandler::Wheel));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(40, 10), InputHandler::WHEEL));
   EXPECT_TRUE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point(40, 10),
-                                                      InputHandler::Wheel));
+                                                      InputHandler::WHEEL));
   host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(0, 1));
   host_impl_->ScrollEnd();
 
   // This point is still inside the non-fast region.
-  EXPECT_EQ(InputHandler::ScrollOnMainThread,
-            host_impl_->ScrollBegin(gfx::Point(10, 10),
-                                    InputHandler::Wheel));
+  EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD,
+            host_impl_->ScrollBegin(gfx::Point(10, 10), InputHandler::WHEEL));
 }
 
 TEST_F(LayerTreeHostImplTest, ScrollHandlerNotPresent) {
@@ -896,7 +888,7 @@
   DrawFrame();
 
   EXPECT_FALSE(host_impl_->scroll_affects_scroll_handler());
-  host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture);
+  host_impl_->ScrollBegin(gfx::Point(), InputHandler::GESTURE);
   EXPECT_FALSE(host_impl_->scroll_affects_scroll_handler());
   host_impl_->ScrollEnd();
   EXPECT_FALSE(host_impl_->scroll_affects_scroll_handler());
@@ -909,7 +901,7 @@
   DrawFrame();
 
   EXPECT_FALSE(host_impl_->scroll_affects_scroll_handler());
-  host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture);
+  host_impl_->ScrollBegin(gfx::Point(), InputHandler::GESTURE);
   EXPECT_TRUE(host_impl_->scroll_affects_scroll_handler());
   host_impl_->ScrollEnd();
   EXPECT_FALSE(host_impl_->scroll_affects_scroll_handler());
@@ -921,8 +913,8 @@
 
   DrawFrame();
 
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::GESTURE));
 
   // Trying to scroll to the left/top will not succeed.
   EXPECT_FALSE(
@@ -967,9 +959,8 @@
 
   DrawFrame();
 
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(),
-                                    InputHandler::Wheel));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::WHEEL));
 
   // Trying to scroll without a vertical scrollbar will fail.
   EXPECT_FALSE(host_impl_->ScrollVerticallyByPage(
@@ -1011,8 +1002,8 @@
   DrawFrame();
   gfx::Point scroll_position(10, 10);
 
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(scroll_position, InputHandler::Wheel));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(scroll_position, InputHandler::WHEEL));
   EXPECT_VECTOR_EQ(gfx::Vector2dF(), scroll_layer->CurrentScrollOffset());
   EXPECT_VECTOR_EQ(gfx::Vector2dF(), overflow->CurrentScrollOffset());
 
@@ -1024,8 +1015,8 @@
 
   overflow->set_user_scrollable_horizontal(false);
 
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(scroll_position, InputHandler::Wheel));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(scroll_position, InputHandler::WHEEL));
   EXPECT_VECTOR_EQ(gfx::Vector2dF(), scroll_layer->CurrentScrollOffset());
   EXPECT_VECTOR_EQ(gfx::Vector2dF(10, 10), overflow->CurrentScrollOffset());
 
@@ -1036,8 +1027,8 @@
 
   overflow->set_user_scrollable_vertical(false);
 
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(scroll_position, InputHandler::Wheel));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(scroll_position, InputHandler::WHEEL));
   EXPECT_VECTOR_EQ(gfx::Vector2dF(10, 0), scroll_layer->CurrentScrollOffset());
   EXPECT_VECTOR_EQ(gfx::Vector2dF(10, 20), overflow->CurrentScrollOffset());
 
@@ -1068,7 +1059,7 @@
 
     float page_scale_delta = 2.f;
 
-    host_impl_->ScrollBegin(gfx::Point(50, 50), InputHandler::Gesture);
+    host_impl_->ScrollBegin(gfx::Point(50, 50), InputHandler::GESTURE);
     host_impl_->PinchGestureBegin();
     host_impl_->PinchGestureUpdate(page_scale_delta, gfx::Point(50, 50));
     host_impl_->PinchGestureEnd();
@@ -1095,16 +1086,15 @@
     scroll_layer->SetScrollDelta(gfx::Vector2d());
 
     float page_scale_delta = 2.f;
-    host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture);
+    host_impl_->ScrollBegin(gfx::Point(), InputHandler::GESTURE);
     host_impl_->PinchGestureBegin();
     host_impl_->PinchGestureUpdate(page_scale_delta, gfx::Point());
     host_impl_->PinchGestureEnd();
     host_impl_->ScrollEnd();
 
     gfx::Vector2d scroll_delta(0, 10);
-    EXPECT_EQ(InputHandler::ScrollStarted,
-              host_impl_->ScrollBegin(gfx::Point(5, 5),
-                                      InputHandler::Wheel));
+    EXPECT_EQ(InputHandler::SCROLL_STARTED,
+              host_impl_->ScrollBegin(gfx::Point(5, 5), InputHandler::WHEEL));
     host_impl_->ScrollBy(gfx::Point(), scroll_delta);
     host_impl_->ScrollEnd();
 
@@ -1122,8 +1112,8 @@
       new LatencyInfoSwapPromise(latency_info));
 
   SetupScrollAndContentsLayers(gfx::Size(100, 100));
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::GESTURE));
   host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(0, 10));
   host_impl_->QueueSwapPromiseForMainThreadScrollUpdate(swap_promise.Pass());
   host_impl_->ScrollEnd();
@@ -1151,7 +1141,7 @@
     scroll_layer->SetScrollDelta(gfx::Vector2d());
 
     float page_scale_delta = 2.f;
-    host_impl_->ScrollBegin(gfx::Point(50, 50), InputHandler::Gesture);
+    host_impl_->ScrollBegin(gfx::Point(50, 50), InputHandler::GESTURE);
     host_impl_->PinchGestureBegin();
     host_impl_->PinchGestureUpdate(page_scale_delta, gfx::Point(50, 50));
     host_impl_->PinchGestureEnd();
@@ -1172,7 +1162,7 @@
     scroll_layer->SetScrollDelta(gfx::Vector2d());
     float page_scale_delta = 10.f;
 
-    host_impl_->ScrollBegin(gfx::Point(50, 50), InputHandler::Gesture);
+    host_impl_->ScrollBegin(gfx::Point(50, 50), InputHandler::GESTURE);
     host_impl_->PinchGestureBegin();
     host_impl_->PinchGestureUpdate(page_scale_delta, gfx::Point(50, 50));
     host_impl_->PinchGestureEnd();
@@ -1192,7 +1182,7 @@
     scroll_layer->PushScrollOffsetFromMainThread(gfx::ScrollOffset(50, 50));
 
     float page_scale_delta = 0.1f;
-    host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture);
+    host_impl_->ScrollBegin(gfx::Point(), InputHandler::GESTURE);
     host_impl_->PinchGestureBegin();
     host_impl_->PinchGestureUpdate(page_scale_delta, gfx::Point());
     host_impl_->PinchGestureEnd();
@@ -1214,7 +1204,7 @@
     scroll_layer->PushScrollOffsetFromMainThread(gfx::ScrollOffset(20, 20));
 
     float page_scale_delta = 1.f;
-    host_impl_->ScrollBegin(gfx::Point(10, 10), InputHandler::Gesture);
+    host_impl_->ScrollBegin(gfx::Point(10, 10), InputHandler::GESTURE);
     host_impl_->PinchGestureBegin();
     host_impl_->PinchGestureUpdate(page_scale_delta, gfx::Point(10, 10));
     host_impl_->PinchGestureUpdate(page_scale_delta, gfx::Point(20, 20));
@@ -1236,7 +1226,7 @@
     scroll_layer->PushScrollOffsetFromMainThread(gfx::ScrollOffset(20, 20));
 
     float page_scale_delta = 1.f;
-    host_impl_->ScrollBegin(gfx::Point(10, 10), InputHandler::Gesture);
+    host_impl_->ScrollBegin(gfx::Point(10, 10), InputHandler::GESTURE);
     host_impl_->PinchGestureBegin();
     host_impl_->PinchGestureUpdate(page_scale_delta, gfx::Point(10, 10));
     host_impl_->ScrollBy(gfx::Point(10, 10), gfx::Vector2d(-10, -10));
@@ -1257,7 +1247,7 @@
     scroll_layer->PullDeltaForMainThread();
     scroll_layer->PushScrollOffsetFromMainThread(gfx::ScrollOffset(0, 0));
 
-    host_impl_->ScrollBegin(gfx::Point(0, 0), InputHandler::Gesture);
+    host_impl_->ScrollBegin(gfx::Point(0, 0), InputHandler::GESTURE);
     host_impl_->PinchGestureBegin();
     host_impl_->PinchGestureUpdate(2.f, gfx::Point(0, 0));
     host_impl_->PinchGestureUpdate(1.f, gfx::Point(0, 0));
@@ -1623,7 +1613,7 @@
 
 TEST_F(LayerTreeHostImplTest, ScrollbarLinearFadeScheduling) {
   LayerTreeSettings settings;
-  settings.scrollbar_animator = LayerTreeSettings::LinearFade;
+  settings.scrollbar_animator = LayerTreeSettings::LINEAR_FADE;
   settings.scrollbar_fade_delay_ms = 20;
   settings.scrollbar_fade_duration_ms = 20;
 
@@ -1635,14 +1625,14 @@
   EXPECT_FALSE(did_request_redraw_);
 
   // If no scroll happened during a scroll gesture, it should have no effect.
-  host_impl_->ScrollBegin(gfx::Point(), InputHandler::Wheel);
+  host_impl_->ScrollBegin(gfx::Point(), InputHandler::WHEEL);
   host_impl_->ScrollEnd();
   EXPECT_EQ(base::TimeDelta(), requested_scrollbar_animation_delay_);
   EXPECT_FALSE(did_request_redraw_);
   EXPECT_TRUE(scrollbar_fade_start_.Equals(base::Closure()));
 
   // After a scroll, a fade animation should be scheduled about 20ms from now.
-  host_impl_->ScrollBegin(gfx::Point(), InputHandler::Wheel);
+  host_impl_->ScrollBegin(gfx::Point(), InputHandler::WHEEL);
   host_impl_->ScrollBy(gfx::Point(), gfx::Vector2dF(0, 5));
   host_impl_->ScrollEnd();
   did_request_redraw_ = false;
@@ -1673,7 +1663,7 @@
   requested_scrollbar_animation_delay_ = base::TimeDelta();
 
   // Unnecessarily Fade animation of solid color scrollbar is not triggered.
-  host_impl_->ScrollBegin(gfx::Point(), InputHandler::Wheel);
+  host_impl_->ScrollBegin(gfx::Point(), InputHandler::WHEEL);
   host_impl_->ScrollBy(gfx::Point(), gfx::Vector2dF(5, 0));
   host_impl_->ScrollEnd();
   EXPECT_EQ(base::TimeDelta(), requested_scrollbar_animation_delay_);
@@ -1681,7 +1671,7 @@
 
 TEST_F(LayerTreeHostImplTest, ScrollbarFadePinchZoomScrollbars) {
   LayerTreeSettings settings;
-  settings.scrollbar_animator = LayerTreeSettings::LinearFade;
+  settings.scrollbar_animator = LayerTreeSettings::LINEAR_FADE;
   settings.scrollbar_fade_delay_ms = 20;
   settings.scrollbar_fade_duration_ms = 20;
   settings.use_pinch_zoom_scrollbars = true;
@@ -1696,14 +1686,14 @@
   EXPECT_FALSE(did_request_animate_);
 
   // If no scroll happened during a scroll gesture, it should have no effect.
-  host_impl_->ScrollBegin(gfx::Point(), InputHandler::Wheel);
+  host_impl_->ScrollBegin(gfx::Point(), InputHandler::WHEEL);
   host_impl_->ScrollEnd();
   EXPECT_EQ(base::TimeDelta(), requested_scrollbar_animation_delay_);
   EXPECT_FALSE(did_request_animate_);
   EXPECT_TRUE(scrollbar_fade_start_.Equals(base::Closure()));
 
   // After a scroll, no fade animation should be scheduled.
-  host_impl_->ScrollBegin(gfx::Point(), InputHandler::Wheel);
+  host_impl_->ScrollBegin(gfx::Point(), InputHandler::WHEEL);
   host_impl_->ScrollBy(gfx::Point(), gfx::Vector2dF(5, 0));
   host_impl_->ScrollEnd();
   did_request_redraw_ = false;
@@ -1720,7 +1710,7 @@
   host_impl_->SetPageScaleOnActiveTree(1.1f);
 
   // After a scroll, a fade animation should be scheduled about 20ms from now.
-  host_impl_->ScrollBegin(gfx::Point(), InputHandler::Wheel);
+  host_impl_->ScrollBegin(gfx::Point(), InputHandler::WHEEL);
   host_impl_->ScrollBy(gfx::Point(), gfx::Vector2dF(5, 0));
   host_impl_->ScrollEnd();
   did_request_redraw_ = false;
@@ -1742,7 +1732,7 @@
   LayerTreeSettings settings;
   settings.scrollbar_fade_delay_ms = 500;
   settings.scrollbar_fade_duration_ms = 300;
-  settings.scrollbar_animator = LayerTreeSettings::Thinning;
+  settings.scrollbar_animator = LayerTreeSettings::THINNING;
 
   gfx::Size viewport_size(300, 200);
   gfx::Size device_viewport_size = gfx::ToFlooredSize(
@@ -1849,8 +1839,8 @@
   }
 
   // Scrolling should update metadata immediately.
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(), InputHandler::Wheel));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::WHEEL));
   host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(0, 10));
   {
     CompositorFrameMetadata metadata =
@@ -1883,7 +1873,7 @@
   }
 
   // Page scale should update metadata correctly (shrinking only the viewport).
-  host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture);
+  host_impl_->ScrollBegin(gfx::Point(), InputHandler::GESTURE);
   host_impl_->PinchGestureBegin();
   host_impl_->PinchGestureUpdate(2.f, gfx::Point());
   host_impl_->PinchGestureEnd();
@@ -2449,8 +2439,8 @@
   DrawFrame();
 
   // Scroll event is ignored because layer is not scrollable.
-  EXPECT_EQ(InputHandler::ScrollIgnored,
-            host_impl_->ScrollBegin(gfx::Point(), InputHandler::Wheel));
+  EXPECT_EQ(InputHandler::SCROLL_IGNORED,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::WHEEL));
   EXPECT_FALSE(did_request_redraw_);
   EXPECT_FALSE(did_request_commit_);
 }
@@ -2607,8 +2597,8 @@
       gfx::Size(10, 10), gfx::Size(10, 10), gfx::Size(10, 10));
   DrawFrame();
 
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::GESTURE));
 
   // Make the test scroll delta a fractional amount, to verify that the
   // fixed container size delta is (1) non-zero, and (2) fractional, and
@@ -2648,8 +2638,8 @@
   outer_scroll->SetDrawsContent(true);
   host_impl_->active_tree()->PushPageScaleFromMainThread(2.f, 1.f, 2.f);
 
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::GESTURE));
   host_impl_->ScrollBy(gfx::Point(), gfx::Vector2dF(0.f, 50.f));
 
   // The entire scroll delta should have been used to hide the top controls.
@@ -2667,8 +2657,8 @@
 
   host_impl_->ScrollEnd();
 
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::GESTURE));
   EXPECT_EQ(host_impl_->CurrentlyScrollingLayer(), inner_scroll);
 
   host_impl_->ScrollBy(gfx::Point(), gfx::Vector2dF(0.f, -50.f));
@@ -2714,8 +2704,8 @@
   // not be scaled.
   host_impl_->active_tree()->PushPageScaleFromMainThread(page_scale, 1.f, 2.f);
 
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::GESTURE));
 
   // Scroll down, the top controls hiding should expand the viewport size so
   // the delta should be equal to the scroll distance.
@@ -2786,8 +2776,8 @@
 
   // Scroll 25px to hide top controls
   gfx::Vector2dF scroll_delta(0.f, 25.f);
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::GESTURE));
   host_impl_->ScrollBy(gfx::Point(), scroll_delta);
   host_impl_->ScrollEnd();
 
@@ -2935,8 +2925,8 @@
 
   // Hide the top controls by 25px.
   gfx::Vector2dF scroll_delta(0.f, 25.f);
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::GESTURE));
   host_impl_->ScrollBy(gfx::Point(), scroll_delta);
 
   // scrolling down at the max extents no longer hides the top controls
@@ -2961,8 +2951,8 @@
 
   // Bring the top controls down by 25px.
   scroll_delta = gfx::Vector2dF(0.f, -25.f);
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::GESTURE));
   host_impl_->ScrollBy(gfx::Point(), scroll_delta);
   host_impl_->ScrollEnd();
 
@@ -2988,8 +2978,8 @@
                   host_impl_->top_controls_manager()->ContentTopOffset());
 
   gfx::Vector2dF scroll_delta(0.f, 25.f);
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::GESTURE));
   host_impl_->ScrollBy(gfx::Point(), scroll_delta);
   host_impl_->ScrollEnd();
 
@@ -3026,8 +3016,8 @@
   // Send a gesture scroll that will scroll the outer viewport, make sure the
   // top controls get scrolled.
   gfx::Vector2dF scroll_delta(0.f, 15.f);
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::GESTURE));
   host_impl_->ScrollBy(gfx::Point(), scroll_delta);
   EXPECT_EQ(host_impl_->OuterViewportScrollLayer(),
             host_impl_->CurrentlyScrollingLayer());
@@ -3038,8 +3028,8 @@
                       host_impl_->top_controls_manager()->ContentTopOffset());
 
   scroll_delta = gfx::Vector2dF(0.f, 50.f);
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::GESTURE));
   host_impl_->ScrollBy(gfx::Point(), scroll_delta);
 
   EXPECT_EQ(0, host_impl_->top_controls_manager()->ContentTopOffset());
@@ -3054,8 +3044,8 @@
   host_impl_->InnerViewportScrollLayer()->SetScrollDelta(inner_viewport_offset);
 
   scroll_delta = gfx::Vector2dF(0.f, -65.f);
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::GESTURE));
   host_impl_->ScrollBy(gfx::Point(), scroll_delta);
 
   EXPECT_EQ(top_controls_height_,
@@ -3073,8 +3063,8 @@
   SetupTopControlsAndScrollLayer();
   DrawFrame();
 
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::GESTURE));
 
   host_impl_->top_controls_manager()->ScrollBegin();
   host_impl_->top_controls_manager()->ScrollBy(gfx::Vector2dF(0.f, 50.f));
@@ -3086,8 +3076,8 @@
 
   host_impl_->ScrollEnd();
 
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::GESTURE));
 
   float scroll_increment_y = -25.f;
   host_impl_->top_controls_manager()->ScrollBegin();
@@ -3115,8 +3105,8 @@
       gfx::ScrollOffset(),
       host_impl_->active_tree()->InnerViewportScrollLayer()->MaxScrollOffset());
 
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::GESTURE));
 }
 
 TEST_F(LayerTreeHostImplTest, ScrollNonCompositedRoot) {
@@ -3151,9 +3141,8 @@
   host_impl_->SetViewportSize(surface_size);
   DrawFrame();
 
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(5, 5),
-                                    InputHandler::Wheel));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(5, 5), InputHandler::WHEEL));
   host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(0, 10));
   host_impl_->ScrollEnd();
   EXPECT_TRUE(did_request_redraw_);
@@ -3172,9 +3161,8 @@
   host_impl_->SetViewportSize(surface_size);
   DrawFrame();
 
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(5, 5),
-                                    InputHandler::Wheel));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(5, 5), InputHandler::WHEEL));
   host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(0, 10));
   host_impl_->ScrollEnd();
   EXPECT_TRUE(did_request_redraw_);
@@ -3192,9 +3180,8 @@
 
   // Scroll event is ignored because the input coordinate is outside the layer
   // boundaries.
-  EXPECT_EQ(InputHandler::ScrollIgnored,
-            host_impl_->ScrollBegin(gfx::Point(15, 5),
-                                    InputHandler::Wheel));
+  EXPECT_EQ(InputHandler::SCROLL_IGNORED,
+            host_impl_->ScrollBegin(gfx::Point(15, 5), InputHandler::WHEEL));
   EXPECT_FALSE(did_request_redraw_);
   EXPECT_FALSE(did_request_commit_);
 }
@@ -3218,9 +3205,8 @@
 
   // Scroll event is ignored because the scrollable layer is not facing the
   // viewer and there is nothing scrollable behind it.
-  EXPECT_EQ(InputHandler::ScrollIgnored,
-            host_impl_->ScrollBegin(gfx::Point(5, 5),
-                                    InputHandler::Wheel));
+  EXPECT_EQ(InputHandler::SCROLL_IGNORED,
+            host_impl_->ScrollBegin(gfx::Point(5, 5), InputHandler::WHEEL));
   EXPECT_FALSE(did_request_redraw_);
   EXPECT_FALSE(did_request_commit_);
 }
@@ -3248,9 +3234,8 @@
 
   // Scrolling fails because the content layer is asking to be scrolled on the
   // main thread.
-  EXPECT_EQ(InputHandler::ScrollOnMainThread,
-            host_impl_->ScrollBegin(gfx::Point(5, 5),
-                                    InputHandler::Wheel));
+  EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD,
+            host_impl_->ScrollBegin(gfx::Point(5, 5), InputHandler::WHEEL));
 }
 
 TEST_F(LayerTreeHostImplTest, ScrollRootAndChangePageScaleOnMainThread) {
@@ -3283,9 +3268,8 @@
   gfx::Vector2d scroll_delta(0, 10);
   gfx::Vector2d expected_scroll_delta = scroll_delta;
   gfx::ScrollOffset expected_max_scroll = root_scroll->MaxScrollOffset();
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(5, 5),
-                                    InputHandler::Wheel));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(5, 5), InputHandler::WHEEL));
   host_impl_->ScrollBy(gfx::Point(), scroll_delta);
   host_impl_->ScrollEnd();
 
@@ -3335,14 +3319,13 @@
   gfx::Vector2d scroll_delta(0, 10);
   gfx::Vector2d expected_scroll_delta = scroll_delta;
   gfx::ScrollOffset expected_max_scroll = root_scroll->MaxScrollOffset();
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(5, 5),
-                                    InputHandler::Wheel));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(5, 5), InputHandler::WHEEL));
   host_impl_->ScrollBy(gfx::Point(), scroll_delta);
   host_impl_->ScrollEnd();
 
   // Set new page scale on impl thread by pinching.
-  host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture);
+  host_impl_->ScrollBegin(gfx::Point(), InputHandler::GESTURE);
   host_impl_->PinchGestureBegin();
   host_impl_->PinchGestureUpdate(page_scale, gfx::Point());
   host_impl_->PinchGestureEnd();
@@ -3384,7 +3367,7 @@
   LayerImpl* grand_child = child->children()[0];
 
   // Set new page scale on impl thread by pinching.
-  host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture);
+  host_impl_->ScrollBegin(gfx::Point(), InputHandler::GESTURE);
   host_impl_->PinchGestureBegin();
   host_impl_->PinchGestureUpdate(new_page_scale, gfx::Point());
   host_impl_->PinchGestureEnd();
@@ -3447,9 +3430,8 @@
   gfx::Vector2d scroll_delta(0, 10);
   gfx::Vector2d expected_scroll_delta(scroll_delta);
   gfx::ScrollOffset expected_max_scroll(child->MaxScrollOffset());
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(5, 5),
-                                    InputHandler::Wheel));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(5, 5), InputHandler::WHEEL));
   host_impl_->ScrollBy(gfx::Point(), scroll_delta);
   host_impl_->ScrollEnd();
 
@@ -3499,9 +3481,8 @@
   DrawFrame();
   {
     gfx::Vector2d scroll_delta(-8, -7);
-    EXPECT_EQ(InputHandler::ScrollStarted,
-              host_impl_->ScrollBegin(gfx::Point(),
-                                      InputHandler::Wheel));
+    EXPECT_EQ(InputHandler::SCROLL_STARTED,
+              host_impl_->ScrollBegin(gfx::Point(), InputHandler::WHEEL));
     host_impl_->ScrollBy(gfx::Point(), scroll_delta);
     host_impl_->ScrollEnd();
 
@@ -3553,9 +3534,9 @@
   DrawFrame();
   {
     gfx::Vector2d scroll_delta(0, -10);
-    EXPECT_EQ(InputHandler::ScrollStarted,
+    EXPECT_EQ(InputHandler::SCROLL_STARTED,
               host_impl_->ScrollBegin(gfx::Point(),
-                                      InputHandler::NonBubblingGesture));
+                                      InputHandler::NON_BUBBLING_GESTURE));
     host_impl_->ScrollBy(gfx::Point(), scroll_delta);
     host_impl_->ScrollEnd();
 
@@ -3573,9 +3554,9 @@
 
     // The next time we scroll we should only scroll the parent.
     scroll_delta = gfx::Vector2d(0, -3);
-    EXPECT_EQ(InputHandler::ScrollStarted,
+    EXPECT_EQ(InputHandler::SCROLL_STARTED,
               host_impl_->ScrollBegin(gfx::Point(5, 5),
-                                      InputHandler::NonBubblingGesture));
+                                      InputHandler::NON_BUBBLING_GESTURE));
     EXPECT_EQ(host_impl_->CurrentlyScrollingLayer(), grand_child);
     host_impl_->ScrollBy(gfx::Point(), scroll_delta);
     EXPECT_EQ(host_impl_->CurrentlyScrollingLayer(), child);
@@ -3592,9 +3573,9 @@
     // After scrolling the parent, another scroll on the opposite direction
     // should still scroll the child.
     scroll_delta = gfx::Vector2d(0, 7);
-    EXPECT_EQ(InputHandler::ScrollStarted,
+    EXPECT_EQ(InputHandler::SCROLL_STARTED,
               host_impl_->ScrollBegin(gfx::Point(5, 5),
-                                      InputHandler::NonBubblingGesture));
+                                      InputHandler::NON_BUBBLING_GESTURE));
     EXPECT_EQ(host_impl_->CurrentlyScrollingLayer(), grand_child);
     host_impl_->ScrollBy(gfx::Point(), scroll_delta);
     EXPECT_EQ(host_impl_->CurrentlyScrollingLayer(), grand_child);
@@ -3614,9 +3595,9 @@
     host_impl_->SetPageScaleOnActiveTree(2.f);
 
     scroll_delta = gfx::Vector2d(0, -2);
-    EXPECT_EQ(InputHandler::ScrollStarted,
+    EXPECT_EQ(InputHandler::SCROLL_STARTED,
               host_impl_->ScrollBegin(gfx::Point(1, 1),
-                                      InputHandler::NonBubblingGesture));
+                                      InputHandler::NON_BUBBLING_GESTURE));
     EXPECT_EQ(grand_child, host_impl_->CurrentlyScrollingLayer());
     host_impl_->ScrollBy(gfx::Point(), scroll_delta);
     host_impl_->ScrollEnd();
@@ -3656,9 +3637,8 @@
   DrawFrame();
   {
     gfx::Vector2d scroll_delta(0, 4);
-    EXPECT_EQ(InputHandler::ScrollStarted,
-              host_impl_->ScrollBegin(gfx::Point(5, 5),
-                                      InputHandler::Wheel));
+    EXPECT_EQ(InputHandler::SCROLL_STARTED,
+              host_impl_->ScrollBegin(gfx::Point(5, 5), InputHandler::WHEEL));
     host_impl_->ScrollBy(gfx::Point(), scroll_delta);
     host_impl_->ScrollEnd();
 
@@ -3703,9 +3683,8 @@
   host_impl_->active_tree()->DidBecomeActive();
 
   // Scrolling should still work even though we did not draw yet.
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(5, 5),
-                                    InputHandler::Wheel));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(5, 5), InputHandler::WHEEL));
 }
 
 TEST_F(LayerTreeHostImplTest, ScrollAxisAlignedRotatedLayer) {
@@ -3722,9 +3701,8 @@
 
   // Scroll to the right in screen coordinates with a gesture.
   gfx::Vector2d gesture_scroll_delta(10, 0);
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(),
-                                    InputHandler::Gesture));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::GESTURE));
   host_impl_->ScrollBy(gfx::Point(), gesture_scroll_delta);
   host_impl_->ScrollEnd();
 
@@ -3736,9 +3714,8 @@
   // Reset and scroll down with the wheel.
   scroll_layer->SetScrollDelta(gfx::Vector2dF());
   gfx::Vector2d wheel_scroll_delta(0, 10);
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(),
-                                    InputHandler::Wheel));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::WHEEL));
   host_impl_->ScrollBy(gfx::Point(), wheel_scroll_delta);
   host_impl_->ScrollEnd();
 
@@ -3784,9 +3761,8 @@
   {
     // Scroll down in screen coordinates with a gesture.
     gfx::Vector2d gesture_scroll_delta(0, 10);
-    EXPECT_EQ(InputHandler::ScrollStarted,
-              host_impl_->ScrollBegin(gfx::Point(1, 1),
-                                      InputHandler::Gesture));
+    EXPECT_EQ(InputHandler::SCROLL_STARTED,
+              host_impl_->ScrollBegin(gfx::Point(1, 1), InputHandler::GESTURE));
     host_impl_->ScrollBy(gfx::Point(), gesture_scroll_delta);
     host_impl_->ScrollEnd();
 
@@ -3807,9 +3783,8 @@
     // Now reset and scroll the same amount horizontally.
     child_ptr->SetScrollDelta(gfx::Vector2dF());
     gfx::Vector2d gesture_scroll_delta(10, 0);
-    EXPECT_EQ(InputHandler::ScrollStarted,
-              host_impl_->ScrollBegin(gfx::Point(1, 1),
-                                      InputHandler::Gesture));
+    EXPECT_EQ(InputHandler::SCROLL_STARTED,
+              host_impl_->ScrollBegin(gfx::Point(1, 1), InputHandler::GESTURE));
     host_impl_->ScrollBy(gfx::Point(), gesture_scroll_delta);
     host_impl_->ScrollEnd();
 
@@ -3850,8 +3825,8 @@
 
   // Scroll down in screen coordinates with a gesture.
   gfx::Vector2d scroll_delta(0, 10);
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::GESTURE));
   host_impl_->ScrollBy(gfx::Point(), scroll_delta);
   host_impl_->ScrollEnd();
 
@@ -3865,8 +3840,8 @@
   // Reset and scroll down with the wheel.
   scroll_layer->SetScrollDelta(gfx::Vector2dF());
   gfx::Vector2d wheel_scroll_delta(0, 10);
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(), InputHandler::Wheel));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::WHEEL));
   host_impl_->ScrollBy(gfx::Point(), wheel_scroll_delta);
   host_impl_->ScrollEnd();
 
@@ -4005,7 +3980,7 @@
   // The pinch gesture doesn't put the delegate into a state where the scroll
   // offset is outside of the scroll range.  (this is verified by DCHECKs in the
   // delegate).
-  host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture);
+  host_impl_->ScrollBegin(gfx::Point(), InputHandler::GESTURE);
   host_impl_->PinchGestureBegin();
   host_impl_->PinchGestureUpdate(2.f, gfx::Point());
   host_impl_->PinchGestureUpdate(.5f, gfx::Point());
@@ -4017,8 +3992,8 @@
   gfx::ScrollOffset current_offset(7.f, 8.f);
 
   scroll_delegate.set_getter_return_value(current_offset);
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::GESTURE));
 
   host_impl_->ScrollBy(gfx::Point(), scroll_delta);
   EXPECT_EQ(ScrollOffsetWithDelta(current_offset, scroll_delta),
@@ -4099,8 +4074,8 @@
   EXPECT_EQ(gfx::Vector2dF(), host_impl_->accumulated_root_overscroll());
 
   // In-bounds scrolling does not affect overscroll.
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(), InputHandler::Wheel));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::WHEEL));
   scroll_result = host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(0, 10));
   EXPECT_TRUE(scroll_result.did_scroll);
   EXPECT_FALSE(scroll_result.did_overscroll_root);
@@ -4234,9 +4209,9 @@
   DrawFrame();
   {
     gfx::Vector2d scroll_delta(0, -10);
-    EXPECT_EQ(InputHandler::ScrollStarted,
+    EXPECT_EQ(InputHandler::SCROLL_STARTED,
               host_impl_->ScrollBegin(gfx::Point(),
-                                      InputHandler::NonBubblingGesture));
+                                      InputHandler::NON_BUBBLING_GESTURE));
     host_impl_->ScrollBy(gfx::Point(), scroll_delta);
     EXPECT_EQ(gfx::Vector2dF(), host_impl_->accumulated_root_overscroll());
     host_impl_->ScrollEnd();
@@ -4244,9 +4219,9 @@
     // The next time we scroll we should only scroll the parent, but overscroll
     // should still not reach the root layer.
     scroll_delta = gfx::Vector2d(0, -30);
-    EXPECT_EQ(InputHandler::ScrollStarted,
+    EXPECT_EQ(InputHandler::SCROLL_STARTED,
               host_impl_->ScrollBegin(gfx::Point(5, 5),
-                                      InputHandler::NonBubblingGesture));
+                                      InputHandler::NON_BUBBLING_GESTURE));
     EXPECT_EQ(host_impl_->CurrentlyScrollingLayer(), grand_child_layer);
     EXPECT_EQ(gfx::Vector2dF(), host_impl_->accumulated_root_overscroll());
     host_impl_->ScrollBy(gfx::Point(), scroll_delta);
@@ -4257,9 +4232,9 @@
     // After scrolling the parent, another scroll on the opposite direction
     // should scroll the child.
     scroll_delta = gfx::Vector2d(0, 70);
-    EXPECT_EQ(InputHandler::ScrollStarted,
+    EXPECT_EQ(InputHandler::SCROLL_STARTED,
               host_impl_->ScrollBegin(gfx::Point(5, 5),
-                                      InputHandler::NonBubblingGesture));
+                                      InputHandler::NON_BUBBLING_GESTURE));
     EXPECT_EQ(host_impl_->CurrentlyScrollingLayer(), grand_child_layer);
     host_impl_->ScrollBy(gfx::Point(), scroll_delta);
     EXPECT_EQ(host_impl_->CurrentlyScrollingLayer(), grand_child_layer);
@@ -4296,9 +4271,8 @@
   DrawFrame();
   {
     gfx::Vector2d scroll_delta(0, 8);
-    EXPECT_EQ(InputHandler::ScrollStarted,
-              host_impl_->ScrollBegin(gfx::Point(5, 5),
-                                      InputHandler::Wheel));
+    EXPECT_EQ(InputHandler::SCROLL_STARTED,
+              host_impl_->ScrollBegin(gfx::Point(5, 5), InputHandler::WHEEL));
     host_impl_->ScrollBy(gfx::Point(), scroll_delta);
     EXPECT_EQ(gfx::Vector2dF(), host_impl_->accumulated_root_overscroll());
     host_impl_->ScrollBy(gfx::Point(), scroll_delta);
@@ -4322,8 +4296,8 @@
   EXPECT_EQ(gfx::Vector2dF(), host_impl_->accumulated_root_overscroll());
 
   // Even though the layer can't scroll the overscroll still happens.
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(), InputHandler::Wheel));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::WHEEL));
   host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(0, 10));
   EXPECT_EQ(gfx::Vector2dF(0, 10), host_impl_->accumulated_root_overscroll());
 }
@@ -4360,8 +4334,8 @@
     // Horizontal & Vertical GlowEffect should not be applied when
     // content size is less then view port size. For Example Horizontal &
     // vertical GlowEffect should not be applied in about:blank page.
-    EXPECT_EQ(InputHandler::ScrollStarted,
-              host_impl_->ScrollBegin(gfx::Point(0, 0), InputHandler::Wheel));
+    EXPECT_EQ(InputHandler::SCROLL_STARTED,
+              host_impl_->ScrollBegin(gfx::Point(0, 0), InputHandler::WHEEL));
     host_impl_->ScrollBy(gfx::Point(), gfx::Vector2dF(0, -1));
     EXPECT_EQ(gfx::Vector2dF().ToString(),
               host_impl_->accumulated_root_overscroll().ToString());
@@ -4397,8 +4371,8 @@
     // Edge glow effect should be applicable only upon reaching Edges
     // of the content. unnecessary glow effect calls shouldn't be
     // called while scrolling up without reaching the edge of the content.
-    EXPECT_EQ(InputHandler::ScrollStarted,
-              host_impl_->ScrollBegin(gfx::Point(0, 0), InputHandler::Wheel));
+    EXPECT_EQ(InputHandler::SCROLL_STARTED,
+              host_impl_->ScrollBegin(gfx::Point(0, 0), InputHandler::WHEEL));
     host_impl_->ScrollBy(gfx::Point(), gfx::Vector2dF(0, 100));
     EXPECT_EQ(gfx::Vector2dF().ToString(),
               host_impl_->accumulated_root_overscroll().ToString());
@@ -4408,10 +4382,10 @@
     host_impl_->ScrollEnd();
     // unusedrootDelta should be subtracted from applied delta so that
     // unwanted glow effect calls are not called.
-    EXPECT_EQ(InputHandler::ScrollStarted,
+    EXPECT_EQ(InputHandler::SCROLL_STARTED,
               host_impl_->ScrollBegin(gfx::Point(0, 0),
-                                      InputHandler::NonBubblingGesture));
-    EXPECT_EQ(InputHandler::ScrollStarted, host_impl_->FlingScrollBegin());
+                                      InputHandler::NON_BUBBLING_GESTURE));
+    EXPECT_EQ(InputHandler::SCROLL_STARTED, host_impl_->FlingScrollBegin());
     host_impl_->ScrollBy(gfx::Point(), gfx::Vector2dF(0, 20));
     EXPECT_EQ(gfx::Vector2dF(0.000000f, 17.699997f).ToString(),
               host_impl_->accumulated_root_overscroll().ToString());
@@ -4422,8 +4396,8 @@
     host_impl_->ScrollEnd();
     // TestCase to check  kEpsilon, which prevents minute values to trigger
     // gloweffect without reaching edge.
-    EXPECT_EQ(InputHandler::ScrollStarted,
-              host_impl_->ScrollBegin(gfx::Point(0, 0), InputHandler::Wheel));
+    EXPECT_EQ(InputHandler::SCROLL_STARTED,
+              host_impl_->ScrollBegin(gfx::Point(0, 0), InputHandler::WHEEL));
     host_impl_->ScrollBy(gfx::Point(), gfx::Vector2dF(-0.12f, 0.1f));
     EXPECT_EQ(gfx::Vector2dF().ToString(),
               host_impl_->accumulated_root_overscroll().ToString());
@@ -4498,7 +4472,7 @@
         resource_id_(resource_provider->CreateResource(
             gfx::Size(1, 1),
             GL_CLAMP_TO_EDGE,
-            ResourceProvider::TextureHintImmutable,
+            ResourceProvider::TEXTURE_HINT_IMMUTABLE,
             RGBA_8888)) {
     resource_provider->AllocateForTesting(resource_id_);
     SetBounds(gfx::Size(10, 10));
@@ -6453,7 +6427,8 @@
 
   host_impl_->ActivateSyncTree();
 
-  host_impl_->active_tree()->UpdateDrawProperties();
+  bool update_lcd_text = false;
+  host_impl_->active_tree()->UpdateDrawProperties(update_lcd_text);
   ASSERT_EQ(1u, host_impl_->active_tree()->RenderSurfaceLayerList().size());
 
   LayerTreeHostImpl::FrameData frame;
@@ -6944,12 +6919,10 @@
   host_impl_->active_tree()->DidBecomeActive();
   DrawFrame();
   {
-    EXPECT_EQ(InputHandler::ScrollStarted,
-              host_impl_->ScrollBegin(gfx::Point(),
-                                      InputHandler::Gesture));
+    EXPECT_EQ(InputHandler::SCROLL_STARTED,
+              host_impl_->ScrollBegin(gfx::Point(), InputHandler::GESTURE));
 
-    EXPECT_EQ(InputHandler::ScrollStarted,
-              host_impl_->FlingScrollBegin());
+    EXPECT_EQ(InputHandler::SCROLL_STARTED, host_impl_->FlingScrollBegin());
 
     gfx::Vector2d scroll_delta(0, 100);
     host_impl_->ScrollBy(gfx::Point(), scroll_delta);
@@ -6997,8 +6970,8 @@
     LayerImpl* grand_child = child->children()[0];
 
     gfx::Vector2d scroll_delta(0, -2);
-    EXPECT_EQ(InputHandler::ScrollStarted,
-              host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture));
+    EXPECT_EQ(InputHandler::SCROLL_STARTED,
+              host_impl_->ScrollBegin(gfx::Point(), InputHandler::GESTURE));
     EXPECT_TRUE(host_impl_->ScrollBy(gfx::Point(), scroll_delta).did_scroll);
 
     // The grand child should have scrolled up to its limit.
@@ -7018,7 +6991,7 @@
 
     // The first |ScrollBy| after the fling should re-lock the scrolling
     // layer to the first layer that scrolled, which is the child.
-    EXPECT_EQ(InputHandler::ScrollStarted, host_impl_->FlingScrollBegin());
+    EXPECT_EQ(InputHandler::SCROLL_STARTED, host_impl_->FlingScrollBegin());
     EXPECT_TRUE(host_impl_->ScrollBy(gfx::Point(), scroll_delta).did_scroll);
     EXPECT_EQ(host_impl_->CurrentlyScrollingLayer(), child);
 
@@ -7057,11 +7030,10 @@
   host_impl_->active_tree()->DidBecomeActive();
   DrawFrame();
   {
-    EXPECT_EQ(InputHandler::ScrollStarted,
-              host_impl_->ScrollBegin(gfx::Point(), InputHandler::Wheel));
+    EXPECT_EQ(InputHandler::SCROLL_STARTED,
+              host_impl_->ScrollBegin(gfx::Point(), InputHandler::WHEEL));
 
-    EXPECT_EQ(InputHandler::ScrollStarted,
-              host_impl_->FlingScrollBegin());
+    EXPECT_EQ(InputHandler::SCROLL_STARTED, host_impl_->FlingScrollBegin());
 
     gfx::Vector2d scroll_delta(0, 100);
     host_impl_->ScrollBy(gfx::Point(), scroll_delta);
@@ -7080,7 +7052,7 @@
 
 TEST_F(LayerTreeHostImplTest, ScrollUnknownNotOnAncestorChain) {
   // If we ray cast a scroller that is not on the first layer's ancestor chain,
-  // we should return ScrollUnknown.
+  // we should return SCROLL_UNKNOWN.
   gfx::Size content_size(100, 100);
   SetupScrollAndContentsLayers(content_size);
 
@@ -7106,14 +7078,14 @@
 
   DrawFrame();
 
-  EXPECT_EQ(InputHandler::ScrollUnknown,
-            host_impl_->ScrollBegin(gfx::Point(), InputHandler::Wheel));
+  EXPECT_EQ(InputHandler::SCROLL_UNKNOWN,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::WHEEL));
 }
 
 TEST_F(LayerTreeHostImplTest, ScrollUnknownScrollAncestorMismatch) {
   // If we ray cast a scroller this is on the first layer's ancestor chain, but
   // is not the first scroller we encounter when walking up from the layer, we
-  // should also return ScrollUnknown.
+  // should also return SCROLL_UNKNOWN.
   gfx::Size content_size(100, 100);
   SetupScrollAndContentsLayers(content_size);
 
@@ -7145,8 +7117,8 @@
 
   DrawFrame();
 
-  EXPECT_EQ(InputHandler::ScrollUnknown,
-            host_impl_->ScrollBegin(gfx::Point(), InputHandler::Wheel));
+  EXPECT_EQ(InputHandler::SCROLL_UNKNOWN,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::WHEEL));
 }
 
 TEST_F(LayerTreeHostImplTest, ScrollInvisibleScroller) {
@@ -7172,10 +7144,10 @@
   // it. The reason for this is that if the scrolling the scroll would not move
   // any layer that is a drawn RSLL member, then we can ignore the hit.
   //
-  // Why ScrollStarted? In this case, it's because we've bubbled out and started
-  // overscrolling the inner viewport.
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(), InputHandler::Wheel));
+  // Why SCROLL_STARTED? In this case, it's because we've bubbled out and
+  // started overscrolling the inner viewport.
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::WHEEL));
 
   EXPECT_EQ(2, host_impl_->CurrentlyScrollingLayer()->id());
 }
@@ -7229,10 +7201,10 @@
   // it. The reason for this is that if the scrolling the scroll would not move
   // any layer that is a drawn RSLL member, then we can ignore the hit.
   //
-  // Why ScrollStarted? In this case, it's because we've bubbled out and started
-  // overscrolling the inner viewport.
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(), InputHandler::Wheel));
+  // Why SCROLL_STARTED? In this case, it's because we've bubbled out and
+  // started overscrolling the inner viewport.
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::WHEEL));
 
   EXPECT_EQ(7, host_impl_->CurrentlyScrollingLayer()->id());
 }
@@ -7428,8 +7400,8 @@
     LayerImpl* scroll_layer = SetupScrollAndContentsLayers(gfx::Size(100, 100));
 
     // Scrolling normally should not trigger any forwarding.
-    EXPECT_EQ(InputHandler::ScrollStarted,
-              host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture));
+    EXPECT_EQ(InputHandler::SCROLL_STARTED,
+              host_impl_->ScrollBegin(gfx::Point(), InputHandler::GESTURE));
     EXPECT_TRUE(
         host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(0, 10)).did_scroll);
     host_impl_->ScrollEnd();
@@ -7441,8 +7413,8 @@
     // Scrolling with a scroll handler should defer the swap to the main
     // thread.
     scroll_layer->SetHaveScrollEventHandlers(true);
-    EXPECT_EQ(InputHandler::ScrollStarted,
-              host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture));
+    EXPECT_EQ(InputHandler::SCROLL_STARTED,
+              host_impl_->ScrollBegin(gfx::Point(), InputHandler::GESTURE));
     EXPECT_TRUE(
         host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(0, 10)).did_scroll);
     host_impl_->ScrollEnd();
@@ -7517,8 +7489,8 @@
       BOTH, SHOWN, false);
   DrawFrame();
 
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::GESTURE));
   EXPECT_EQ(0, host_impl_->top_controls_manager()->ControlsTopOffset());
   EXPECT_EQ(gfx::Vector2dF().ToString(),
             scroll_layer->CurrentScrollOffset().ToString());
@@ -7576,8 +7548,8 @@
       BOTH, SHOWN, false);
   DrawFrame();
 
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::GESTURE));
   EXPECT_EQ(0, host_impl_->top_controls_manager()->ControlsTopOffset());
   EXPECT_EQ(gfx::Vector2dF().ToString(),
             scroll_layer->CurrentScrollOffset().ToString());
@@ -7646,8 +7618,8 @@
       gfx::ScrollOffset(0, initial_scroll_offset));
   DrawFrame();
 
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::GESTURE));
   EXPECT_EQ(0, host_impl_->top_controls_manager()->ControlsTopOffset());
   EXPECT_EQ(gfx::Vector2dF(0, initial_scroll_offset).ToString(),
             scroll_layer->CurrentScrollOffset().ToString());
@@ -7707,8 +7679,8 @@
                                                              false);
   DrawFrame();
 
-  EXPECT_EQ(InputHandler::ScrollStarted,
-            host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture));
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
+            host_impl_->ScrollBegin(gfx::Point(), InputHandler::GESTURE));
   EXPECT_EQ(0, host_impl_->top_controls_manager()->ControlsTopOffset());
 
   float offset = 50;
@@ -7840,9 +7812,9 @@
     EXPECT_VECTOR_EQ(outer_expected, outer_scroll->CurrentScrollOffset());
 
     // Make sure the fling goes to the outer viewport first
-    EXPECT_EQ(InputHandler::ScrollStarted,
-        host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture));
-    EXPECT_EQ(InputHandler::ScrollStarted, host_impl_->FlingScrollBegin());
+    EXPECT_EQ(InputHandler::SCROLL_STARTED,
+              host_impl_->ScrollBegin(gfx::Point(), InputHandler::GESTURE));
+    EXPECT_EQ(InputHandler::SCROLL_STARTED, host_impl_->FlingScrollBegin());
 
     gfx::Vector2d scroll_delta(inner_viewport.width(), inner_viewport.height());
     host_impl_->ScrollBy(gfx::Point(), scroll_delta);
@@ -7854,9 +7826,9 @@
     EXPECT_VECTOR_EQ(outer_expected, outer_scroll->CurrentScrollOffset());
 
     // Fling past the outer viewport boundry, make sure inner viewport scrolls.
-    EXPECT_EQ(InputHandler::ScrollStarted,
-        host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture));
-    EXPECT_EQ(InputHandler::ScrollStarted, host_impl_->FlingScrollBegin());
+    EXPECT_EQ(InputHandler::SCROLL_STARTED,
+              host_impl_->ScrollBegin(gfx::Point(), InputHandler::GESTURE));
+    EXPECT_EQ(InputHandler::SCROLL_STARTED, host_impl_->FlingScrollBegin());
 
     host_impl_->ScrollBy(gfx::Point(), scroll_delta);
     outer_expected += gfx::Vector2dF(scroll_delta.x(), scroll_delta.y());
@@ -7889,9 +7861,9 @@
     EXPECT_VECTOR_EQ(outer_expected, outer_scroll->CurrentScrollOffset());
 
     // Make sure the scroll goes to the outer viewport first.
-    EXPECT_EQ(InputHandler::ScrollStarted,
-              host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture));
-    EXPECT_EQ(InputHandler::ScrollStarted, host_impl_->FlingScrollBegin());
+    EXPECT_EQ(InputHandler::SCROLL_STARTED,
+              host_impl_->ScrollBegin(gfx::Point(), InputHandler::GESTURE));
+    EXPECT_EQ(InputHandler::SCROLL_STARTED, host_impl_->FlingScrollBegin());
 
     // Scroll near the edge of the outer viewport.
     gfx::Vector2d scroll_delta(inner_viewport.width(), inner_viewport.height());
@@ -7935,8 +7907,8 @@
     scoped_ptr<ScrollAndScaleSet> scroll_info;
 
     gfx::Vector2d scroll_delta(0, inner_viewport.height());
-    EXPECT_EQ(InputHandler::ScrollStarted,
-              host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture));
+    EXPECT_EQ(InputHandler::SCROLL_STARTED,
+              host_impl_->ScrollBegin(gfx::Point(), InputHandler::GESTURE));
     EXPECT_TRUE(host_impl_->ScrollBy(gfx::Point(), scroll_delta).did_scroll);
 
     // The child should have scrolled up to its limit.
@@ -7947,7 +7919,7 @@
 
     // The first |ScrollBy| after the fling should re-lock the scrolling
     // layer to the first layer that scrolled, the inner viewport scroll layer.
-    EXPECT_EQ(InputHandler::ScrollStarted, host_impl_->FlingScrollBegin());
+    EXPECT_EQ(InputHandler::SCROLL_STARTED, host_impl_->FlingScrollBegin());
     EXPECT_TRUE(host_impl_->ScrollBy(gfx::Point(), scroll_delta).did_scroll);
     EXPECT_EQ(host_impl_->CurrentlyScrollingLayer(), inner_scroll);
 
@@ -8023,7 +7995,7 @@
   base::TimeTicks start_time =
       base::TimeTicks() + base::TimeDelta::FromMilliseconds(100);
 
-  EXPECT_EQ(InputHandler::ScrollStarted,
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
             host_impl_->ScrollAnimated(gfx::Point(), gfx::Vector2d(0, 50)));
 
   LayerImpl* scrolling_layer = host_impl_->CurrentlyScrollingLayer();
@@ -8040,7 +8012,7 @@
   EXPECT_TRUE(y > 1 && y < 49);
 
   // Update target.
-  EXPECT_EQ(InputHandler::ScrollStarted,
+  EXPECT_EQ(InputHandler::SCROLL_STARTED,
             host_impl_->ScrollAnimated(gfx::Point(), gfx::Vector2d(0, 50)));
 
   host_impl_->Animate(start_time + base::TimeDelta::FromMilliseconds(200));
@@ -8383,15 +8355,15 @@
     scroll_layer->SetScrollDelta(gfx::Vector2d());
 
     float page_scale_delta = 2.f;
-    host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture);
+    host_impl_->ScrollBegin(gfx::Point(), InputHandler::GESTURE);
     host_impl_->PinchGestureBegin();
     host_impl_->PinchGestureUpdate(page_scale_delta, gfx::Point());
     host_impl_->PinchGestureEnd();
     host_impl_->ScrollEnd();
 
     gfx::Vector2dF scroll_delta(0, 5);
-    EXPECT_EQ(InputHandler::ScrollStarted,
-              host_impl_->ScrollBegin(gfx::Point(), InputHandler::Wheel));
+    EXPECT_EQ(InputHandler::SCROLL_STARTED,
+              host_impl_->ScrollBegin(gfx::Point(), InputHandler::WHEEL));
     EXPECT_VECTOR_EQ(gfx::Vector2dF(), scroll_layer->CurrentScrollOffset());
 
     host_impl_->ScrollBy(gfx::Point(), scroll_delta);
diff --git a/cc/trees/layer_tree_host_pixeltest_on_demand_raster.cc b/cc/trees/layer_tree_host_pixeltest_on_demand_raster.cc
deleted file mode 100644
index d32817c..0000000
--- a/cc/trees/layer_tree_host_pixeltest_on_demand_raster.cc
+++ /dev/null
@@ -1,116 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "cc/layers/append_quads_data.h"
-#include "cc/layers/content_layer_client.h"
-#include "cc/layers/picture_layer.h"
-#include "cc/layers/picture_layer_impl.h"
-#include "cc/quads/draw_quad.h"
-#include "cc/test/layer_tree_pixel_test.h"
-#include "cc/trees/layer_tree_impl.h"
-#include "third_party/skia/include/core/SkCanvas.h"
-#include "third_party/skia/include/core/SkColor.h"
-#include "ui/gfx/geometry/rect.h"
-#include "ui/gfx/geometry/rect_f.h"
-
-#if !defined(OS_ANDROID)
-
-namespace cc {
-namespace {
-
-class LayerTreeHostOnDemandRasterPixelTest : public LayerTreePixelTest {
- public:
-  void InitializeSettings(LayerTreeSettings* settings) override {
-    settings->impl_side_painting = true;
-  }
-
-  void BeginCommitOnThread(LayerTreeHostImpl* impl) override {
-    // Not enough memory available. Enforce on-demand rasterization.
-    impl->SetMemoryPolicy(
-        ManagedMemoryPolicy(1, gpu::MemoryAllocation::CUTOFF_ALLOW_EVERYTHING,
-                            1000));
-  }
-
-  void SwapBuffersOnThread(LayerTreeHostImpl* host_impl, bool result) override {
-    // Find the PictureLayerImpl ask it to append quads to check their material.
-    // The PictureLayerImpl is assumed to be the first child of the root layer
-    // in the active tree.
-    PictureLayerImpl* picture_layer = static_cast<PictureLayerImpl*>(
-        host_impl->active_tree()->root_layer()->child_at(0));
-
-    scoped_ptr<RenderPass> render_pass = RenderPass::Create();
-
-    AppendQuadsData data;
-    picture_layer->AppendQuads(render_pass.get(), &data);
-
-    for (const auto& quad : render_pass->quad_list)
-      EXPECT_EQ(quad->material, DrawQuad::PICTURE_CONTENT);
-
-    // Triggers pixel readback and ends the test.
-    LayerTreePixelTest::SwapBuffersOnThread(host_impl, result);
-  }
-
-  void RunOnDemandRasterPixelTest();
-};
-
-class BlueYellowLayerClient : public ContentLayerClient {
- public:
-  explicit BlueYellowLayerClient(gfx::Rect layer_rect)
-      : layer_rect_(layer_rect) {}
-
-  bool FillsBoundsCompletely() const override { return false; }
-
-  void PaintContents(SkCanvas* canvas,
-                     const gfx::Rect& clip,
-                     PaintingControlSetting picture_control) override {
-    SkPaint paint;
-    paint.setColor(SK_ColorBLUE);
-    canvas->drawRect(SkRect::MakeWH(layer_rect_.width(),
-                                    layer_rect_.height() / 2),
-                     paint);
-
-    paint.setColor(SK_ColorYELLOW);
-    canvas->drawRect(
-        SkRect::MakeXYWH(0,
-                         layer_rect_.height() / 2,
-                         layer_rect_.width(),
-                         layer_rect_.height() / 2),
-        paint);
-  }
-
-  scoped_refptr<DisplayItemList> PaintContentsToDisplayList(
-      const gfx::Rect& clip,
-      PaintingControlSetting picture_control) override {
-    NOTIMPLEMENTED();
-    return DisplayItemList::Create();
-  }
-
- private:
-  gfx::Rect layer_rect_;
-};
-
-void LayerTreeHostOnDemandRasterPixelTest::RunOnDemandRasterPixelTest() {
-  // Use multiple colors in a single layer to prevent bypassing on-demand
-  // rasterization if a single solid color is detected in picture analysis.
-  gfx::Rect layer_rect(200, 200);
-  BlueYellowLayerClient client(layer_rect);
-  scoped_refptr<PictureLayer> layer = PictureLayer::Create(&client);
-
-  layer->SetIsDrawable(true);
-  layer->SetBounds(layer_rect.size());
-  layer->SetPosition(layer_rect.origin());
-
-  RunPixelTest(PIXEL_TEST_GL,
-               layer,
-               base::FilePath(FILE_PATH_LITERAL("blue_yellow.png")));
-}
-
-TEST_F(LayerTreeHostOnDemandRasterPixelTest, RasterPictureLayer) {
-  RunOnDemandRasterPixelTest();
-}
-
-}  // namespace
-}  // namespace cc
-
-#endif  // OS_ANDROID
diff --git a/cc/trees/layer_tree_host_unittest.cc b/cc/trees/layer_tree_host_unittest.cc
index bb315e7..48be5b8 100644
--- a/cc/trees/layer_tree_host_unittest.cc
+++ b/cc/trees/layer_tree_host_unittest.cc
@@ -1322,7 +1322,7 @@
     // Make sure partial texture updates are turned off.
     settings->max_partial_texture_updates = 0;
     // Linear fade animator prevents scrollbars from drawing immediately.
-    settings->scrollbar_animator = LayerTreeSettings::NoAnimator;
+    settings->scrollbar_animator = LayerTreeSettings::NO_ANIMATOR;
   }
 
   void SetupTree() override {
@@ -2325,40 +2325,10 @@
 
 class LayerTreeHostTestLCDChange : public LayerTreeHostTest {
  public:
-  class PaintClient : public FakeContentLayerClient {
-   public:
-    PaintClient() : paint_count_(0) {}
-
-    int paint_count() const { return paint_count_; }
-
-    void PaintContents(SkCanvas* canvas,
-                       const gfx::Rect& clip,
-                       PaintingControlSetting picture_control) override {
-      FakeContentLayerClient::PaintContents(canvas, clip, picture_control);
-      ++paint_count_;
-    }
-
-    scoped_refptr<DisplayItemList> PaintContentsToDisplayList(
-        const gfx::Rect& clip,
-        PaintingControlSetting picture_control) override {
-      NOTIMPLEMENTED();
-      return DisplayItemList::Create();
-    }
-
-    bool FillsBoundsCompletely() const override { return false; }
-
-   private:
-    int paint_count_;
-  };
-
   void SetupTree() override {
     num_tiles_rastered_ = 0;
 
-    scoped_refptr<Layer> root_layer;
-    if (layer_tree_host()->settings().impl_side_painting)
-      root_layer = PictureLayer::Create(&client_);
-    else
-      root_layer = ContentLayer::Create(&client_);
+    scoped_refptr<Layer> root_layer = PictureLayer::Create(&client_);
     client_.set_fill_with_nonsolid_color(true);
     root_layer->SetIsDrawable(true);
     root_layer->SetBounds(gfx::Size(10, 10));
@@ -2366,10 +2336,9 @@
 
     layer_tree_host()->SetRootLayer(root_layer);
 
-    // The expecations are based on the assumption that the default
+    // The expectations are based on the assumption that the default
     // LCD settings are:
     EXPECT_TRUE(layer_tree_host()->settings().can_use_lcd_text);
-    EXPECT_FALSE(root_layer->can_use_lcd_text());
 
     LayerTreeHostTest::SetupTree();
   }
@@ -2379,33 +2348,17 @@
   void DidCommitAndDrawFrame() override {
     switch (layer_tree_host()->source_frame_number()) {
       case 1:
-        // The first update consists of a paint of the whole layer.
-        EXPECT_EQ(1, client_.paint_count());
-        // LCD text must have been enabled on the layer.
-        EXPECT_TRUE(layer_tree_host()->root_layer()->can_use_lcd_text());
         PostSetNeedsCommitToMainThread();
         break;
       case 2:
-        // Since nothing changed on layer, there should be no paint.
-        EXPECT_EQ(1, client_.paint_count());
-        // LCD text must not have changed.
-        EXPECT_TRUE(layer_tree_host()->root_layer()->can_use_lcd_text());
         // Change layer opacity that should trigger lcd change.
         layer_tree_host()->root_layer()->SetOpacity(.5f);
         break;
       case 3:
-        // LCD text doesn't require re-recording, so no painting should occur.
-        EXPECT_EQ(1, client_.paint_count());
-        // LCD text must have been disabled on the layer due to opacity.
-        EXPECT_FALSE(layer_tree_host()->root_layer()->can_use_lcd_text());
         // Change layer opacity that should not trigger lcd change.
         layer_tree_host()->root_layer()->SetOpacity(1.f);
         break;
       case 4:
-        // LCD text doesn't require re-recording, so no painting should occur.
-        EXPECT_EQ(1, client_.paint_count());
-        // Even though LCD text could be allowed.
-        EXPECT_TRUE(layer_tree_host()->root_layer()->can_use_lcd_text());
         EndTest();
         break;
     }
@@ -2417,22 +2370,34 @@
   }
 
   void DrawLayersOnThread(LayerTreeHostImpl* host_impl) override {
+    PictureLayerImpl* root_layer =
+        static_cast<PictureLayerImpl*>(host_impl->active_tree()->root_layer());
+    bool can_use_lcd_text =
+        host_impl->active_tree()->root_layer()->can_use_lcd_text();
     switch (host_impl->active_tree()->source_frame_number()) {
       case 0:
         // The first draw.
         EXPECT_EQ(1, num_tiles_rastered_);
+        EXPECT_TRUE(can_use_lcd_text);
+        EXPECT_TRUE(root_layer->RasterSourceUsesLCDText());
         break;
       case 1:
         // Nothing changed on the layer.
         EXPECT_EQ(1, num_tiles_rastered_);
+        EXPECT_TRUE(can_use_lcd_text);
+        EXPECT_TRUE(root_layer->RasterSourceUsesLCDText());
         break;
       case 2:
-        // LCD text was disabled, it should be re-rastered with LCD text off.
+        // LCD text was disabled; it should be re-rastered with LCD text off.
         EXPECT_EQ(2, num_tiles_rastered_);
+        EXPECT_FALSE(can_use_lcd_text);
+        EXPECT_FALSE(root_layer->RasterSourceUsesLCDText());
         break;
       case 3:
-        // LCD text was enabled but it's sticky and stays off.
+        // LCD text was enabled, but it's sticky and stays off.
         EXPECT_EQ(2, num_tiles_rastered_);
+        EXPECT_TRUE(can_use_lcd_text);
+        EXPECT_FALSE(root_layer->RasterSourceUsesLCDText());
         break;
     }
   }
@@ -2440,7 +2405,7 @@
   void AfterTest() override {}
 
  private:
-  PaintClient client_;
+  FakeContentLayerClient client_;
   int num_tiles_rastered_;
 };
 
@@ -6320,4 +6285,83 @@
 
 SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestNoTasksBetweenWillAndDidCommit);
 
+// Verify that if a LayerImpl holds onto a copy request for multiple
+// frames that it will continue to have a render surface through
+// multiple commits, even though the Layer itself has no reason
+// to have a render surface.
+class LayerPreserveRenderSurfaceFromOutputRequests : public LayerTreeHostTest {
+ protected:
+  void SetupTree() override {
+    scoped_refptr<Layer> root = Layer::Create();
+    root->CreateRenderSurface();
+    root->SetBounds(gfx::Size(10, 10));
+    child_ = Layer::Create();
+    child_->SetBounds(gfx::Size(20, 20));
+    root->AddChild(child_);
+
+    layer_tree_host()->SetRootLayer(root);
+    LayerTreeHostTest::SetupTree();
+  }
+
+  static void CopyOutputCallback(scoped_ptr<CopyOutputResult> result) {}
+
+  void BeginTest() override {
+    child_->RequestCopyOfOutput(
+        CopyOutputRequest::CreateBitmapRequest(base::Bind(CopyOutputCallback)));
+    EXPECT_TRUE(child_->HasCopyRequest());
+    PostSetNeedsCommitToMainThread();
+  }
+
+  void DidCommit() override { EXPECT_FALSE(child_->HasCopyRequest()); }
+
+  void CommitCompleteOnThread(LayerTreeHostImpl* host_impl) override {
+    LayerImpl* child_impl = host_impl->sync_tree()->LayerById(child_->id());
+
+    switch (host_impl->sync_tree()->source_frame_number()) {
+      case 0:
+        EXPECT_TRUE(child_impl->HasCopyRequest());
+        EXPECT_TRUE(child_impl->render_surface());
+        break;
+      case 1:
+        if (host_impl->proxy()->CommitToActiveTree()) {
+          EXPECT_TRUE(child_impl->HasCopyRequest());
+          EXPECT_TRUE(child_impl->render_surface());
+        } else {
+          EXPECT_FALSE(child_impl->HasCopyRequest());
+          EXPECT_FALSE(child_impl->render_surface());
+        }
+        break;
+      default:
+        NOTREACHED();
+        break;
+    }
+  }
+
+  void DidActivateTreeOnThread(LayerTreeHostImpl* host_impl) override {
+    LayerImpl* child_impl = host_impl->active_tree()->LayerById(child_->id());
+    EXPECT_TRUE(child_impl->HasCopyRequest());
+    EXPECT_TRUE(child_impl->render_surface());
+
+    switch (host_impl->active_tree()->source_frame_number()) {
+      case 0:
+        // Lose output surface to prevent drawing and cause another commit.
+        host_impl->DidLoseOutputSurface();
+        break;
+      case 1:
+        EndTest();
+        break;
+      default:
+        NOTREACHED();
+        break;
+    }
+  }
+
+  void AfterTest() override {}
+
+ private:
+  scoped_refptr<Layer> child_;
+};
+
+SINGLE_AND_MULTI_THREAD_TEST_F(LayerPreserveRenderSurfaceFromOutputRequests);
+
 }  // namespace cc
diff --git a/cc/trees/layer_tree_host_unittest_animation.cc b/cc/trees/layer_tree_host_unittest_animation.cc
index e36f6a0..b8d0320 100644
--- a/cc/trees/layer_tree_host_unittest_animation.cc
+++ b/cc/trees/layer_tree_host_unittest_animation.cc
@@ -130,7 +130,7 @@
 
     LayerAnimationController* controller =
         layer_tree_host()->root_layer()->layer_animation_controller();
-    Animation* animation = controller->GetAnimation(Animation::Opacity);
+    Animation* animation = controller->GetAnimation(Animation::OPACITY);
     if (animation)
       controller->RemoveAnimation(animation->id());
 
@@ -471,8 +471,7 @@
     LayerAnimationController* controller_impl =
         host_impl->active_tree()->root_layer()->children()[0]->
         layer_animation_controller();
-    Animation* animation =
-        controller_impl->GetAnimation(Animation::Opacity);
+    Animation* animation = controller_impl->GetAnimation(Animation::OPACITY);
     if (!animation)
       return;
 
@@ -523,8 +522,7 @@
     LayerAnimationController* controller =
         layer_tree_host()->root_layer()->children()[0]->
         layer_animation_controller();
-    Animation* animation =
-        controller->GetAnimation(Animation::Opacity);
+    Animation* animation = controller->GetAnimation(Animation::OPACITY);
     main_start_time_ = animation->start_time();
     controller->RemoveAnimation(animation->id());
     EndTest();
@@ -535,8 +533,7 @@
     LayerAnimationController* controller =
         impl_host->active_tree()->root_layer()->children()[0]->
         layer_animation_controller();
-    Animation* animation =
-        controller->GetAnimation(Animation::Opacity);
+    Animation* animation = controller->GetAnimation(Animation::OPACITY);
     if (!animation)
       return;
 
@@ -573,8 +570,7 @@
                                int group) override {
     LayerAnimationController* controller =
         layer_tree_host()->root_layer()->layer_animation_controller();
-    Animation* animation =
-        controller->GetAnimation(Animation::Opacity);
+    Animation* animation = controller->GetAnimation(Animation::OPACITY);
     if (animation)
       controller->RemoveAnimation(animation->id());
     EndTest();
@@ -609,7 +605,7 @@
     LayerAnimationController* controller_impl =
         host_impl->active_tree()->root_layer()->layer_animation_controller();
     Animation* animation_impl =
-        controller_impl->GetAnimation(Animation::Opacity);
+        controller_impl->GetAnimation(Animation::OPACITY);
     controller_impl->RemoveAnimation(animation_impl->id());
     EndTest();
   }
@@ -648,8 +644,7 @@
       // Any valid AnimationCurve will do here.
       scoped_ptr<AnimationCurve> curve(new FakeFloatAnimationCurve());
       scoped_ptr<Animation> animation(
-          Animation::Create(curve.Pass(), 1, 1,
-                                  Animation::Opacity));
+          Animation::Create(curve.Pass(), 1, 1, Animation::OPACITY));
       layer->layer_animation_controller()->AddAnimation(animation.Pass());
 
       // We add the animation *before* attaching the layer to the tree.
@@ -986,7 +981,7 @@
                 gfx::ScrollOffset(500.f, 550.f),
                 EaseInOutTimingFunction::Create()));
         scoped_ptr<Animation> animation(
-            Animation::Create(curve.Pass(), 1, 0, Animation::ScrollOffset));
+            Animation::Create(curve.Pass(), 1, 0, Animation::SCROLL_OFFSET));
         animation->set_needs_synchronized_start_time(true);
         bool animation_added = scroll_layer_->AddAnimation(animation.Pass());
         bool impl_scrolling_supported =
@@ -1036,7 +1031,7 @@
         ScrollOffsetAnimationCurve::Create(gfx::ScrollOffset(6500.f, 7500.f),
                                            EaseInOutTimingFunction::Create()));
     scoped_ptr<Animation> animation(
-        Animation::Create(curve.Pass(), 1, 0, Animation::ScrollOffset));
+        Animation::Create(curve.Pass(), 1, 0, Animation::SCROLL_OFFSET));
     animation->set_needs_synchronized_start_time(true);
     scroll_layer_->AddAnimation(animation.Pass());
   }
@@ -1050,7 +1045,7 @@
       case 1: {
         Animation* animation =
             scroll_layer_->layer_animation_controller()->GetAnimation(
-                Animation::ScrollOffset);
+                Animation::SCROLL_OFFSET);
         scroll_layer_->layer_animation_controller()->RemoveAnimation(
             animation->id());
         scroll_layer_->SetScrollOffset(final_postion_);
@@ -1080,9 +1075,9 @@
         host_impl->active_tree()->root_layer()->children()[0];
     Animation* animation =
         scroll_layer_impl->layer_animation_controller()->GetAnimation(
-            Animation::ScrollOffset);
+            Animation::SCROLL_OFFSET);
 
-    if (!animation || animation->run_state() != Animation::Running) {
+    if (!animation || animation->run_state() != Animation::RUNNING) {
       host_impl->BlockNotifyReadyToActivateForTesting(false);
       return;
     }
@@ -1187,20 +1182,20 @@
     LayerAnimationController* root_controller_impl =
         host_impl->active_tree()->root_layer()->layer_animation_controller();
     Animation* root_animation =
-        root_controller_impl->GetAnimation(Animation::Opacity);
-    if (!root_animation || root_animation->run_state() != Animation::Running)
+        root_controller_impl->GetAnimation(Animation::OPACITY);
+    if (!root_animation || root_animation->run_state() != Animation::RUNNING)
       return;
 
     LayerAnimationController* child_controller_impl =
         host_impl->active_tree()->root_layer()->children()
             [0]->layer_animation_controller();
     Animation* child_animation =
-        child_controller_impl->GetAnimation(Animation::Opacity);
-    EXPECT_EQ(Animation::Running, child_animation->run_state());
+        child_controller_impl->GetAnimation(Animation::OPACITY);
+    EXPECT_EQ(Animation::RUNNING, child_animation->run_state());
     EXPECT_EQ(root_animation->start_time(), child_animation->start_time());
-    root_controller_impl->AbortAnimations(Animation::Opacity);
-    root_controller_impl->AbortAnimations(Animation::Transform);
-    child_controller_impl->AbortAnimations(Animation::Opacity);
+    root_controller_impl->AbortAnimations(Animation::OPACITY);
+    root_controller_impl->AbortAnimations(Animation::TRANSFORM);
+    child_controller_impl->AbortAnimations(Animation::OPACITY);
     EndTest();
   }
 
@@ -1257,10 +1252,10 @@
            ++iter) {
         int id = ((*iter).second->id());
         if (id == host_impl->RootLayer()->id()) {
-          Animation* anim = (*iter).second->GetAnimation(Animation::Transform);
+          Animation* anim = (*iter).second->GetAnimation(Animation::TRANSFORM);
           EXPECT_GT((anim->start_time() - base::TimeTicks()).InSecondsF(), 0);
         } else if (id == host_impl->RootLayer()->children()[0]->id()) {
-          Animation* anim = (*iter).second->GetAnimation(Animation::Opacity);
+          Animation* anim = (*iter).second->GetAnimation(Animation::OPACITY);
           EXPECT_GT((anim->start_time() - base::TimeTicks()).InSecondsF(), 0);
         }
       }
diff --git a/cc/trees/layer_tree_host_unittest_context.cc b/cc/trees/layer_tree_host_unittest_context.cc
index 596adc0..c99180d 100644
--- a/cc/trees/layer_tree_host_unittest_context.cc
+++ b/cc/trees/layer_tree_host_unittest_context.cc
@@ -1000,10 +1000,8 @@
 
     ResourceProvider::ResourceId resource =
         child_resource_provider_->CreateResource(
-            gfx::Size(4, 4),
-            GL_CLAMP_TO_EDGE,
-            ResourceProvider::TextureHintImmutable,
-            RGBA_8888);
+            gfx::Size(4, 4), GL_CLAMP_TO_EDGE,
+            ResourceProvider::TEXTURE_HINT_IMMUTABLE, RGBA_8888);
     ResourceProvider::ScopedWriteLockGL lock(child_resource_provider_.get(),
                                              resource);
 
diff --git a/cc/trees/layer_tree_host_unittest_no_message_loop.cc b/cc/trees/layer_tree_host_unittest_no_message_loop.cc
index d2014eab..06b3674 100644
--- a/cc/trees/layer_tree_host_unittest_no_message_loop.cc
+++ b/cc/trees/layer_tree_host_unittest_no_message_loop.cc
@@ -56,6 +56,7 @@
   // LayerTreeHostClient overrides.
   void WillBeginMainFrame() override {}
   void BeginMainFrame(const BeginFrameArgs& args) override {}
+  void BeginMainFrameNotExpectedSoon() override {}
   void DidBeginMainFrame() override {}
   void Layout() override {}
   void ApplyViewportDeltas(const gfx::Vector2dF& inner_delta,
diff --git a/cc/trees/layer_tree_host_unittest_scroll.cc b/cc/trees/layer_tree_host_unittest_scroll.cc
index 03a0860..547ece7 100644
--- a/cc/trees/layer_tree_host_unittest_scroll.cc
+++ b/cc/trees/layer_tree_host_unittest_scroll.cc
@@ -582,12 +582,12 @@
     EXPECT_EQ(device_scale_factor_, impl->active_tree()->device_scale_factor());
     switch (impl->active_tree()->source_frame_number()) {
       case 0: {
-        // Gesture scroll on impl thread.
+        // GESTURE scroll on impl thread.
         InputHandler::ScrollStatus status = impl->ScrollBegin(
             gfx::ToCeiledPoint(expected_scroll_layer_impl->position() -
                                gfx::Vector2dF(0.5f, 0.5f)),
-            InputHandler::Gesture);
-        EXPECT_EQ(InputHandler::ScrollStarted, status);
+            InputHandler::GESTURE);
+        EXPECT_EQ(InputHandler::SCROLL_STARTED, status);
         impl->ScrollBy(gfx::Point(), scroll_amount_);
         impl->ScrollEnd();
 
@@ -599,12 +599,12 @@
         break;
       }
       case 1: {
-        // Wheel scroll on impl thread.
+        // WHEEL scroll on impl thread.
         InputHandler::ScrollStatus status = impl->ScrollBegin(
             gfx::ToCeiledPoint(expected_scroll_layer_impl->position() +
                                gfx::Vector2dF(0.5f, 0.5f)),
-            InputHandler::Wheel);
-        EXPECT_EQ(InputHandler::ScrollStarted, status);
+            InputHandler::WHEEL);
+        EXPECT_EQ(InputHandler::SCROLL_STARTED, status);
         impl->ScrollBy(gfx::Point(), scroll_amount_);
         impl->ScrollEnd();
 
@@ -1040,23 +1040,23 @@
     scroll_layer->SetBounds(
         gfx::Size(root->bounds().width() + 100, root->bounds().height() + 100));
     EXPECT_EQ(
-        InputHandler::ScrollStarted,
-        scroll_layer->TryScroll(gfx::PointF(0.0f, 1.0f), InputHandler::Gesture,
-                                ScrollBlocksOnNone));
+        InputHandler::SCROLL_STARTED,
+        scroll_layer->TryScroll(gfx::PointF(0.0f, 1.0f), InputHandler::GESTURE,
+                                SCROLL_BLOCKS_ON_NONE));
 
     // Set max_scroll_offset = (0, 0).
     scroll_layer->SetBounds(root->bounds());
     EXPECT_EQ(
-        InputHandler::ScrollIgnored,
-        scroll_layer->TryScroll(gfx::PointF(0.0f, 1.0f), InputHandler::Gesture,
-                                ScrollBlocksOnNone));
+        InputHandler::SCROLL_IGNORED,
+        scroll_layer->TryScroll(gfx::PointF(0.0f, 1.0f), InputHandler::GESTURE,
+                                SCROLL_BLOCKS_ON_NONE));
 
     // Set max_scroll_offset = (-100, -100).
     scroll_layer->SetBounds(gfx::Size());
     EXPECT_EQ(
-        InputHandler::ScrollIgnored,
-        scroll_layer->TryScroll(gfx::PointF(0.0f, 1.0f), InputHandler::Gesture,
-                                ScrollBlocksOnNone));
+        InputHandler::SCROLL_IGNORED,
+        scroll_layer->TryScroll(gfx::PointF(0.0f, 1.0f), InputHandler::GESTURE,
+                                SCROLL_BLOCKS_ON_NONE));
 
     EndTest();
   }
diff --git a/cc/trees/layer_tree_impl.cc b/cc/trees/layer_tree_impl.cc
index 4093c42..e86e785 100644
--- a/cc/trees/layer_tree_impl.cc
+++ b/cc/trees/layer_tree_impl.cc
@@ -534,20 +534,27 @@
   outer_viewport_scroll_layer_ = NULL;
 }
 
-bool LayerTreeImpl::UpdateDrawProperties() {
+bool LayerTreeImpl::UpdateDrawProperties(bool update_lcd_text) {
   if (!needs_update_draw_properties_)
     return true;
 
-  // For max_texture_size.
+  // Calling UpdateDrawProperties must clear this flag, so there can be no
+  // early outs before this.
+  needs_update_draw_properties_ = false;
+
+  // For max_texture_size.  When the renderer is re-created in
+  // CreateAndSetRenderer, the needs update draw properties flag is set
+  // again.
   if (!layer_tree_host_impl_->renderer())
     return false;
 
+  // Clear this after the renderer early out, as it should still be
+  // possible to hit test even without a renderer.
+  render_surface_layer_list_.clear();
+
   if (!root_layer())
     return false;
 
-  needs_update_draw_properties_ = false;
-  render_surface_layer_list_.clear();
-
   {
     TRACE_EVENT2(
         "cc", "LayerTreeImpl::UpdateDrawProperties::CalculateDrawProperties",
@@ -647,6 +654,25 @@
         occlusion_tracker.ComputeVisibleRegionInScreen();
   }
 
+  // It'd be ideal if this could be done earlier, but when the raster source
+  // is updated from the main thread during push properties, update draw
+  // properties has not occurred yet and so it's not clear whether or not the
+  // layer can or cannot use lcd text.  So, this is the cleanup pass to
+  // determine if the raster source needs to be replaced with a non-lcd
+  // raster source due to draw properties.
+  if (update_lcd_text) {
+    // TODO(enne): Make LTHI::sync_tree return this value.
+    LayerTreeImpl* sync_tree =
+        layer_tree_host_impl_->proxy()->CommitToActiveTree()
+            ? layer_tree_host_impl_->active_tree()
+            : layer_tree_host_impl_->pending_tree();
+    // If this is not the sync tree, then it is not safe to update lcd text
+    // as it causes invalidations and the tiles may be in use.
+    DCHECK_EQ(this, sync_tree);
+    for (const auto& layer : picture_layers_)
+      layer->UpdateCanUseLCDTextAfterCommit();
+  }
+
   {
     TRACE_EVENT_BEGIN2("cc", "LayerTreeImpl::UpdateDrawProperties::UpdateTiles",
                        "IsActive", IsActiveTree(), "SourceFrameNumber",
@@ -859,6 +885,10 @@
   return layer_tree_host_impl_->recycle_tree() == this;
 }
 
+bool LayerTreeImpl::IsSyncTree() const {
+  return layer_tree_host_impl_->sync_tree() == this;
+}
+
 LayerImpl* LayerTreeImpl::FindActiveTreeLayerById(int id) {
   LayerTreeImpl* tree = layer_tree_host_impl_->active_tree();
   if (!tree)
@@ -912,7 +942,7 @@
   base::TimeDelta duration =
       base::TimeDelta::FromMilliseconds(settings().scrollbar_fade_duration_ms);
   switch (settings().scrollbar_animator) {
-    case LayerTreeSettings::LinearFade: {
+    case LayerTreeSettings::LINEAR_FADE: {
       return ScrollbarAnimationControllerLinearFade::Create(
           scrolling_layer,
           layer_tree_host_impl_,
@@ -920,14 +950,14 @@
           resize_delay,
           duration);
     }
-    case LayerTreeSettings::Thinning: {
+    case LayerTreeSettings::THINNING: {
       return ScrollbarAnimationControllerThinning::Create(scrolling_layer,
                                                           layer_tree_host_impl_,
                                                           delay,
                                                           resize_delay,
                                                           duration);
     }
-    case LayerTreeSettings::NoAnimator:
+    case LayerTreeSettings::NO_ANIMATOR:
       NOTREACHED();
       break;
   }
@@ -1161,13 +1191,13 @@
 void LayerTreeImpl::ProcessUIResourceRequestQueue() {
   for (const auto& req : ui_resource_request_queue_) {
     switch (req.GetType()) {
-      case UIResourceRequest::UIResourceCreate:
+      case UIResourceRequest::UI_RESOURCE_CREATE:
         layer_tree_host_impl_->CreateUIResource(req.GetId(), req.GetBitmap());
         break;
-      case UIResourceRequest::UIResourceDelete:
+      case UIResourceRequest::UI_RESOURCE_DELETE:
         layer_tree_host_impl_->DeleteUIResource(req.GetId());
         break;
-      case UIResourceRequest::UIResourceInvalidRequest:
+      case UIResourceRequest::UI_RESOURCE_INVALID_REQUEST:
         NOTREACHED();
         break;
     }
@@ -1461,7 +1491,8 @@
     const gfx::PointF& screen_space_point) {
   if (!root_layer())
     return NULL;
-  if (!UpdateDrawProperties())
+  bool update_lcd_text = false;
+  if (!UpdateDrawProperties(update_lcd_text))
     return NULL;
   FindClosestMatchingLayerDataForRecursion data_for_recursion;
   FindClosestMatchingLayer(screen_space_point,
@@ -1503,7 +1534,8 @@
     const gfx::PointF& screen_space_point) {
   if (!root_layer())
     return NULL;
-  if (!UpdateDrawProperties())
+  bool update_lcd_text = false;
+  if (!UpdateDrawProperties(update_lcd_text))
     return NULL;
   FindWheelEventLayerFunctor func;
   FindClosestMatchingLayerDataForRecursion data_for_recursion;
@@ -1523,7 +1555,8 @@
     const gfx::PointF& screen_space_point) {
   if (!root_layer())
     return NULL;
-  if (!UpdateDrawProperties())
+  bool update_lcd_text = false;
+  if (!UpdateDrawProperties(update_lcd_text))
     return NULL;
   FindTouchEventLayerFunctor func = {screen_space_point};
   FindClosestMatchingLayerDataForRecursion data_for_recursion;
diff --git a/cc/trees/layer_tree_impl.h b/cc/trees/layer_tree_impl.h
index 54c8660..5935d29 100644
--- a/cc/trees/layer_tree_impl.h
+++ b/cc/trees/layer_tree_impl.h
@@ -87,6 +87,7 @@
   bool IsActiveTree() const;
   bool IsPendingTree() const;
   bool IsRecycleTree() const;
+  bool IsSyncTree() const;
   LayerImpl* FindActiveTreeLayerById(int id);
   LayerImpl* FindPendingTreeLayerById(int id);
   bool PinchGestureActive() const;
@@ -197,8 +198,9 @@
   }
 
   // Updates draw properties and render surface layer list, as well as tile
-  // priorities. Returns false if it was unable to update.
-  bool UpdateDrawProperties();
+  // priorities. Returns false if it was unable to update.  Updating lcd
+  // text may cause invalidations, so should only be done after a commit.
+  bool UpdateDrawProperties(bool update_lcd_text);
 
   void set_needs_update_draw_properties() {
     needs_update_draw_properties_ = true;
diff --git a/cc/trees/layer_tree_settings.cc b/cc/trees/layer_tree_settings.cc
index d3afbe20..9d605aea 100644
--- a/cc/trees/layer_tree_settings.cc
+++ b/cc/trees/layer_tree_settings.cc
@@ -33,7 +33,7 @@
       gpu_rasterization_skewport_target_time_in_seconds(0.0f),
       threaded_gpu_rasterization_enabled(false),
       create_low_res_tiling(false),
-      scrollbar_animator(NoAnimator),
+      scrollbar_animator(NO_ANIMATOR),
       scrollbar_fade_delay_ms(0),
       scrollbar_fade_resize_delay_ms(0),
       scrollbar_fade_duration_ms(0),
diff --git a/cc/trees/layer_tree_settings.h b/cc/trees/layer_tree_settings.h
index fd108f4..a0fbeaa7 100644
--- a/cc/trees/layer_tree_settings.h
+++ b/cc/trees/layer_tree_settings.h
@@ -43,9 +43,9 @@
   bool create_low_res_tiling;
 
   enum ScrollbarAnimator {
-    NoAnimator,
-    LinearFade,
-    Thinning,
+    NO_ANIMATOR,
+    LINEAR_FADE,
+    THINNING,
   };
   ScrollbarAnimator scrollbar_animator;
   int scrollbar_fade_delay_ms;
diff --git a/cc/trees/occlusion_tracker_perftest.cc b/cc/trees/occlusion_tracker_perftest.cc
index 4639b9cd..e53a05f 100644
--- a/cc/trees/occlusion_tracker_perftest.cc
+++ b/cc/trees/occlusion_tracker_perftest.cc
@@ -93,7 +93,8 @@
   opaque_layer->SetContentBounds(viewport_rect.size());
   active_tree()->root_layer()->AddChild(opaque_layer.Pass());
 
-  active_tree()->UpdateDrawProperties();
+  bool update_lcd_text = false;
+  active_tree()->UpdateDrawProperties(update_lcd_text);
   const LayerImplList& rsll = active_tree()->RenderSurfaceLayerList();
   ASSERT_EQ(1u, rsll.size());
   EXPECT_EQ(1u, rsll[0]->render_surface()->layer_list().size());
@@ -164,7 +165,8 @@
     active_tree()->root_layer()->AddChild(opaque_layer.Pass());
   }
 
-  active_tree()->UpdateDrawProperties();
+  bool update_lcd_text = false;
+  active_tree()->UpdateDrawProperties(update_lcd_text);
   const LayerImplList& rsll = active_tree()->RenderSurfaceLayerList();
   ASSERT_EQ(1u, rsll.size());
   EXPECT_EQ(static_cast<size_t>(kNumOpaqueLayers),
diff --git a/cc/trees/property_tree_builder.cc b/cc/trees/property_tree_builder.cc
index ae39f03..7fe0494 100644
--- a/cc/trees/property_tree_builder.cc
+++ b/cc/trees/property_tree_builder.cc
@@ -118,7 +118,7 @@
 
   const bool has_animated_transform =
       layer->layer_animation_controller()->IsAnimatingProperty(
-          Animation::Transform);
+          Animation::TRANSFORM);
 
   const bool has_transform_origin = layer->transform_origin() != gfx::Point3F();
 
diff --git a/cc/trees/single_thread_proxy.cc b/cc/trees/single_thread_proxy.cc
index 4a079f3d4..e45b182 100644
--- a/cc/trees/single_thread_proxy.cc
+++ b/cc/trees/single_thread_proxy.cc
@@ -532,7 +532,8 @@
     DebugScopedSetImplThread impl(const_cast<SingleThreadProxy*>(this));
     if (layer_tree_host_impl_->settings().impl_side_painting) {
       layer_tree_host_impl_->ActivateSyncTree();
-      layer_tree_host_impl_->active_tree()->UpdateDrawProperties();
+      DCHECK(!layer_tree_host_impl_->active_tree()
+                  ->needs_update_draw_properties());
       layer_tree_host_impl_->PrepareTiles();
       layer_tree_host_impl_->SynchronouslyInitializeAllTiles();
     }
@@ -685,6 +686,10 @@
                  weak_factory_.GetWeakPtr()));
 }
 
+void SingleThreadProxy::SendBeginMainFrameNotExpectedSoon() {
+  layer_tree_host_->BeginMainFrameNotExpectedSoon();
+}
+
 void SingleThreadProxy::BeginMainFrame() {
   if (defer_commits_) {
     TRACE_EVENT_INSTANT0("cc", "EarlyOut_DeferCommit",
diff --git a/cc/trees/single_thread_proxy.h b/cc/trees/single_thread_proxy.h
index 52dd876..f463db1 100644
--- a/cc/trees/single_thread_proxy.h
+++ b/cc/trees/single_thread_proxy.h
@@ -79,6 +79,7 @@
   base::TimeDelta CommitToActivateDurationEstimate() override;
   void DidBeginImplFrameDeadline() override;
   void SendBeginFramesToChildren(const BeginFrameArgs& args) override;
+  void SendBeginMainFrameNotExpectedSoon() override;
 
   // LayerTreeHostImplClient implementation
   void UpdateRendererCapabilitiesOnImplThread() override;
diff --git a/cc/trees/thread_proxy.cc b/cc/trees/thread_proxy.cc
index 167cca9..1097805 100644
--- a/cc/trees/thread_proxy.cc
+++ b/cc/trees/thread_proxy.cc
@@ -154,9 +154,9 @@
 }
 
 bool ThreadProxy::CommitToActiveTree() const {
-  // With ThreadProxy we use a pending tree and activate it once it's ready to
-  // draw.
-  return false;
+  // With ThreadProxy and impl-side painting, we use a pending tree and activate
+  // it once it's ready to draw.
+  return !impl().layer_tree_host_impl->settings().impl_side_painting;
 }
 
 void ThreadProxy::SetLayerTreeHostClientReady() {
@@ -701,6 +701,12 @@
   impl().timing_history.DidBeginMainFrame();
 }
 
+void ThreadProxy::SendBeginMainFrameNotExpectedSoon() {
+  Proxy::MainThreadTaskRunner()->PostTask(
+      FROM_HERE, base::Bind(&ThreadProxy::BeginMainFrameNotExpectedSoon,
+                            main_thread_weak_ptr_));
+}
+
 void ThreadProxy::BeginMainFrame(
     scoped_ptr<BeginMainFrameAndCommitState> begin_main_frame_state) {
   benchmark_instrumentation::ScopedBeginFrameTask begin_frame_task(
@@ -861,6 +867,12 @@
   layer_tree_host()->DidBeginMainFrame();
 }
 
+void ThreadProxy::BeginMainFrameNotExpectedSoon() {
+  TRACE_EVENT0("cc", "ThreadProxy::BeginMainFrameNotExpectedSoon");
+  DCHECK(IsMainThread());
+  layer_tree_host()->BeginMainFrameNotExpectedSoon();
+}
+
 void ThreadProxy::StartCommitOnImplThread(CompletionEvent* completion,
                                           ResourceUpdateQueue* raw_queue) {
   TRACE_EVENT0("cc", "ThreadProxy::StartCommitOnImplThread");
@@ -1021,8 +1033,11 @@
   impl().timing_history.DidStartDrawing();
   base::AutoReset<bool> mark_inside(&impl().inside_draw, true);
 
-  if (impl().layer_tree_host_impl->pending_tree())
-    impl().layer_tree_host_impl->pending_tree()->UpdateDrawProperties();
+  if (impl().layer_tree_host_impl->pending_tree()) {
+    bool update_lcd_text = false;
+    impl().layer_tree_host_impl->pending_tree()->UpdateDrawProperties(
+        update_lcd_text);
+  }
 
   // This method is called on a forced draw, regardless of whether we are able
   // to produce a frame, as the calling site on main thread is blocked until its
diff --git a/cc/trees/thread_proxy.h b/cc/trees/thread_proxy.h
index 570d240..8b4c3025 100644
--- a/cc/trees/thread_proxy.h
+++ b/cc/trees/thread_proxy.h
@@ -227,6 +227,7 @@
   base::TimeDelta CommitToActivateDurationEstimate() override;
   void DidBeginImplFrameDeadline() override;
   void SendBeginFramesToChildren(const BeginFrameArgs& args) override;
+  void SendBeginMainFrameNotExpectedSoon() override;
 
   // ResourceUpdateControllerClient implementation
   void ReadyToFinalizeTextureUpdates() override;
@@ -244,6 +245,7 @@
       const RendererCapabilities& capabilities);
   void BeginMainFrame(
       scoped_ptr<BeginMainFrameAndCommitState> begin_main_frame_state);
+  void BeginMainFrameNotExpectedSoon();
   void DidCommitAndDrawFrame();
   void DidCompleteSwapBuffers();
   void SetAnimationEvents(scoped_ptr<AnimationEventsVector> queue);
diff --git a/chrome/BUILD.gn b/chrome/BUILD.gn
index 2ef9bdde..30b14e4 100644
--- a/chrome/BUILD.gn
+++ b/chrome/BUILD.gn
@@ -198,7 +198,7 @@
       if (enable_configuration_policy) {
         deps += [ "//components/policy" ]
       }
-      if (cpu_arch == "x86") {
+      if (current_cpu == "x86") {
         # Add a dependency to custom import library for user32 delay imports only
         # in x86 builds.
         #deps += [ 'chrome_user32_delay_imports' ]  TODO(GYP)
diff --git a/chrome/VERSION b/chrome/VERSION
index f01e306..51fe25c 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
-MAJOR=42
+MAJOR=43
 MINOR=0
-BUILD=2308
+BUILD=2312
 PATCH=0
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index da3a85e..d0908fe 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -104,7 +104,6 @@
   ]
 
   srcjar_deps = [
-    ":app_banner_metrics_ids_javagen",
     ":chrome_android_java_enums_srcjar",
     ":chrome_version_srcjar",
     ":resource_id_javagen",
@@ -139,17 +138,6 @@
   ]
 }
 
-# GYP: //chrome/chrome_browser.gypi:app_banner_metrics_ids_java
-java_cpp_template("app_banner_metrics_ids_javagen") {
-  sources = [
-    "java/AppBannerMetricsIds.template",
-  ]
-  package_name = "org/chromium/chrome/browser/banners"
-  inputs = [
-    "../browser/android/banners/app_banner_metrics_id_list.h",
-  ]
-}
-
 # GYP: //chrome/chrome_browser.gypi:document_tab_model_info_proto_java
 proto_java_library("document_tab_model_info_proto_java") {
   proto_path = "java/src/org/chromium/chrome/browser/tabmodel/document"
diff --git a/chrome/android/java/res/drawable-mdpi/btn_switch_to_on_mtrl_00001.9.png b/chrome/android/java/res/drawable-mdpi/btn_switch_to_on_mtrl_00001.9.png
new file mode 100644
index 0000000..03d3dfb
--- /dev/null
+++ b/chrome/android/java/res/drawable-mdpi/btn_switch_to_on_mtrl_00001.9.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-mdpi/btn_switch_to_on_mtrl_00012.9.png b/chrome/android/java/res/drawable-mdpi/btn_switch_to_on_mtrl_00012.9.png
new file mode 100644
index 0000000..6635830
--- /dev/null
+++ b/chrome/android/java/res/drawable-mdpi/btn_switch_to_on_mtrl_00012.9.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-xhdpi/btn_switch_to_on_mtrl_00001.9.png b/chrome/android/java/res/drawable-xhdpi/btn_switch_to_on_mtrl_00001.9.png
new file mode 100644
index 0000000..8a648b8
--- /dev/null
+++ b/chrome/android/java/res/drawable-xhdpi/btn_switch_to_on_mtrl_00001.9.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-xhdpi/btn_switch_to_on_mtrl_00012.9.png b/chrome/android/java/res/drawable-xhdpi/btn_switch_to_on_mtrl_00012.9.png
new file mode 100644
index 0000000..435ce215
--- /dev/null
+++ b/chrome/android/java/res/drawable-xhdpi/btn_switch_to_on_mtrl_00012.9.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-xxhdpi/btn_switch_to_on_mtrl_00001.9.png b/chrome/android/java/res/drawable-xxhdpi/btn_switch_to_on_mtrl_00001.9.png
new file mode 100644
index 0000000..b149e475
--- /dev/null
+++ b/chrome/android/java/res/drawable-xxhdpi/btn_switch_to_on_mtrl_00001.9.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-xxhdpi/btn_switch_to_on_mtrl_00012.9.png b/chrome/android/java/res/drawable-xxhdpi/btn_switch_to_on_mtrl_00012.9.png
new file mode 100644
index 0000000..00fb83ec
--- /dev/null
+++ b/chrome/android/java/res/drawable-xxhdpi/btn_switch_to_on_mtrl_00012.9.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-xxxhdpi/btn_switch_to_on_mtrl_00001.9.png b/chrome/android/java/res/drawable-xxxhdpi/btn_switch_to_on_mtrl_00001.9.png
new file mode 100644
index 0000000..d3f2a9a
--- /dev/null
+++ b/chrome/android/java/res/drawable-xxxhdpi/btn_switch_to_on_mtrl_00001.9.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-xxxhdpi/btn_switch_to_on_mtrl_00012.9.png b/chrome/android/java/res/drawable-xxxhdpi/btn_switch_to_on_mtrl_00012.9.png
new file mode 100644
index 0000000..a3caefb7
--- /dev/null
+++ b/chrome/android/java/res/drawable-xxxhdpi/btn_switch_to_on_mtrl_00012.9.png
Binary files differ
diff --git a/chrome/android/java/res/layout/account_chooser_infobar_item.xml b/chrome/android/java/res/layout/account_chooser_infobar_item.xml
new file mode 100644
index 0000000..4c144e8c
--- /dev/null
+++ b/chrome/android/java/res/layout/account_chooser_infobar_item.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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. -->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:padding="8dp"
+    android:orientation="horizontal" >
+    <ImageView
+        android:id="@+id/profile_image"
+        android:layout_width="32dp"
+        android:layout_height="32dp"
+        android:layout_gravity="center_vertical"
+        android:contentDescription="@null"/>
+    <LinearLayout
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center_vertical"
+        android:layout_margin="8dp"
+        android:orientation="vertical">
+        <TextView
+            android:id="@+id/username"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:ellipsize="end"
+            android:singleLine="true"
+            android:textAppearance="?android:attr/textAppearanceMedium"/>
+        <TextView
+            android:id="@+id/display_name"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:ellipsize="end"
+            android:singleLine="true"
+            android:textAppearance="?android:attr/textAppearanceSmall"/>
+    </LinearLayout>
+</LinearLayout>
diff --git a/chrome/android/java/res/layout/account_chooser_infobar_list.xml b/chrome/android/java/res/layout/account_chooser_infobar_list.xml
new file mode 100644
index 0000000..da6f5b9
--- /dev/null
+++ b/chrome/android/java/res/layout/account_chooser_infobar_list.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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. -->
+
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content">
+    <ListView
+        android:id="@+id/account_list"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:padding="8dp"/>
+</FrameLayout>
diff --git a/chrome/android/java/res/layout/autofill_card_unmask_prompt.xml b/chrome/android/java/res/layout/autofill_card_unmask_prompt.xml
index 832ca37..feabdf1 100644
--- a/chrome/android/java/res/layout/autofill_card_unmask_prompt.xml
+++ b/chrome/android/java/res/layout/autofill_card_unmask_prompt.xml
@@ -32,19 +32,40 @@
             android:orientation="horizontal">
 
             <!-- TODO(estade): add accessibility content descriptions. -->
-            <Spinner
-                android:id="@+id/expiration_month"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:layout_marginStart="16dp"
-                android:visibility="gone" />
 
-            <Spinner
-                android:id="@+id/expiration_year"
+            <LinearLayout
+                android:id="@+id/expiration_container"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
-                android:layout_marginStart="16dp"
-                android:visibility="gone" />
+                android:orientation="horizontal"
+                android:visibility="gone">
+
+              <EditText
+                  android:id="@+id/expiration_month"
+                  android:layout_width="wrap_content"
+                  android:layout_height="wrap_content"
+                  android:layout_marginStart="16dp"
+                  android:ems="2"
+                  android:gravity="center_horizontal"
+                  android:inputType="number"
+                  android:hint="@string/card_unmask_month_hint" />
+
+              <TextView
+                  android:layout_width="wrap_content"
+                  android:layout_height="wrap_content"
+                  android:layout_marginStart="8dp"
+                  android:layout_marginEnd="8dp"
+                  android:text="@string/card_unmask_expiration_date_separator" />
+
+              <EditText
+                  android:id="@+id/expiration_year"
+                  android:layout_width="wrap_content"
+                  android:layout_height="wrap_content"
+                  android:ems="2"
+                  android:gravity="center_horizontal"
+                  android:inputType="number"
+                  android:hint="@string/card_unmask_year_hint" />
+            </LinearLayout>
 
             <EditText
                 android:id="@+id/card_unmask_input"
@@ -97,12 +118,10 @@
         android:gravity="center"
         android:visibility="gone">
 
-        <!-- TODO(estade): should be material styled. -->
         <ProgressBar
-            style="@android:style/Widget.ProgressBar.Small"
             android:id="@+id/verification_progress_bar"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
+            android:layout_width="36dp"
+            android:layout_height="36dp"
             android:layout_marginBottom="8dp"
             android:layout_marginTop="30dp"
             android:visibility="gone" />
@@ -119,6 +138,6 @@
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:textColor="?android:attr/colorAccent"
-            android:textStyle="bold" />
+            android:textSize="20dp" />
     </LinearLayout>
 </RelativeLayout>
diff --git a/chrome/android/java/res/layout/four_button_menu_item.xml b/chrome/android/java/res/layout/four_button_menu_item.xml
deleted file mode 100644
index 37843d5..0000000
--- a/chrome/android/java/res/layout/four_button_menu_item.xml
+++ /dev/null
@@ -1,46 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="?android:attr/listPreferredItemHeightSmall"
-    android:layout_gravity="top|start"
-    android:orientation="horizontal">
-
-    <org.chromium.chrome.browser.widget.TintedImageButton
-        android:id="@+id/button_one"
-        android:layout_width="59dp"
-        android:layout_height="match_parent"
-        android:paddingEnd="11dp"
-        android:background="?attr/listChoiceBackgroundIndicator"
-        android:scaleType="center" />
-
-    <org.chromium.chrome.browser.widget.TintedImageButton
-        android:id="@+id/button_two"
-        android:layout_width="70dp"
-        android:layout_height="match_parent"
-        android:paddingStart="11dp"
-        android:paddingEnd="11dp"
-        android:background="?attr/listChoiceBackgroundIndicator"
-        android:scaleType="center" />
-
-    <org.chromium.chrome.browser.widget.TintedImageButton
-        android:id="@+id/button_three"
-        android:layout_width="70dp"
-        android:layout_height="match_parent"
-        android:paddingStart="11dp"
-        android:paddingEnd="11dp"
-        android:background="?attr/listChoiceBackgroundIndicator"
-        android:scaleType="center" />
-
-    <org.chromium.chrome.browser.widget.TintedImageButton
-        android:id="@+id/button_four"
-        android:layout_width="59dp"
-        android:layout_height="match_parent"
-        android:paddingStart="11dp"
-        android:background="?attr/listChoiceBackgroundIndicator"
-        android:scaleType="center" />
-</LinearLayout>
\ No newline at end of file
diff --git a/chrome/android/java/res/layout/single_line_bottom_text_dialog.xml b/chrome/android/java/res/layout/single_line_bottom_text_dialog.xml
new file mode 100644
index 0000000..5bfb5600
--- /dev/null
+++ b/chrome/android/java/res/layout/single_line_bottom_text_dialog.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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. -->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:gravity="start|bottom"
+    style="@style/AlertDialogContent">
+
+    <org.chromium.ui.widget.TextViewWithClickableSpans
+        android:id="@+id/summary"
+        android:bufferType="spannable"
+        style="@style/AlertDialogSummaryViewItem" />
+
+</LinearLayout>
+
diff --git a/chrome/android/java/res/layout/two_button_menu_item.xml b/chrome/android/java/res/layout/two_button_menu_item.xml
deleted file mode 100644
index 91800c8..0000000
--- a/chrome/android/java/res/layout/two_button_menu_item.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="?android:attr/listPreferredItemHeightSmall"
-    android:layout_gravity="top|start"
-    android:orientation="horizontal">
-
-    <org.chromium.chrome.browser.widget.TintedImageButton
-        android:id="@+id/button_one"
-        android:layout_width="0dp"
-        android:layout_height="match_parent"
-        android:layout_weight="1"
-        android:background="?attr/listChoiceBackgroundIndicator"
-        android:scaleType="center" />
-
-    <org.chromium.chrome.browser.widget.TintedImageButton
-        android:id="@+id/button_two"
-        android:layout_width="0dp"
-        android:layout_height="match_parent"
-        android:layout_weight="1"
-        android:background="?attr/listChoiceBackgroundIndicator"
-        android:scaleType="center" />
-
-</LinearLayout>
\ No newline at end of file
diff --git a/chrome/android/java/res/layout/website_settings.xml b/chrome/android/java/res/layout/website_settings.xml
index 3c9c9a01..a883d4d 100644
--- a/chrome/android/java/res/layout/website_settings.xml
+++ b/chrome/android/java/res/layout/website_settings.xml
@@ -50,7 +50,7 @@
             android:paddingStart="@dimen/website_settings_popup_button_padding_sides"
             android:text="@string/page_info_copy_url_button"
             android:textColor="@color/light_active_color"
-            style="@style/ButtonBorderlessCompat" />
+            style="@style/ButtonCompatBorderless" />
     </LinearLayout>
 
     <!-- Horizontal separator -->
@@ -92,7 +92,7 @@
                 android:paddingStart="@dimen/website_settings_popup_button_padding_sides"
                 android:text="@string/page_info_site_settings_button"
                 android:textColor="@color/website_settings_popup_button_text"
-                style="@style/ButtonBorderlessCompat" />
+                style="@style/ButtonCompatBorderless" />
         </FrameLayout>
     </LinearLayout>
 
diff --git a/chrome/android/java/res/menu/account_chooser_infobar_more_menu_popup.xml b/chrome/android/java/res/menu/account_chooser_infobar_more_menu_popup.xml
new file mode 100644
index 0000000..addb6282
--- /dev/null
+++ b/chrome/android/java/res/menu/account_chooser_infobar_more_menu_popup.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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. -->
+
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:id="@+id/learn_more"
+          android:title="@string/learn_more"/>
+    <item android:id="@+id/settings"
+          android:title="@string/preferences"/>
+</menu>
diff --git a/chrome/android/java/res/values-v17/styles.xml b/chrome/android/java/res/values-v17/styles.xml
index 95a02e4..5f8807c 100644
--- a/chrome/android/java/res/values-v17/styles.xml
+++ b/chrome/android/java/res/values-v17/styles.xml
@@ -85,6 +85,7 @@
         <item name="colorAccent">@color/pref_accent_color</item>
     </style>
 
+    <!-- Alert dialogs -->
     <style name="AlertDialogContent">
         <item name="android:layout_width">match_parent</item>
         <item name="android:layout_height">wrap_content</item>
@@ -100,6 +101,13 @@
         <item name="android:singleLine">true</item>
         <item name="android:paddingTop">10dp</item>
     </style>
+    <style name="AlertDialogSummaryViewItem" parent="@android:style/TextAppearance.Small">
+        <item name="android:layout_width">match_parent</item>
+        <item name="android:layout_height">wrap_content</item>
+        <item name="android:textAlignment">viewStart</item>
+        <item name="android:paddingTop">10dp</item>
+    </style>
+
     <style name="BoldTextFieldLabel" parent="@android:style/TextAppearance.Medium">
         <item name="android:layout_width">match_parent</item>
         <item name="android:layout_height">wrap_content</item>
@@ -176,6 +184,12 @@
     </style>
 
     <!-- Buttons -->
+    <style name="ButtonCompatOverlay">
+        <item name="android:buttonStyle">@style/ButtonCompat</item>
+    </style>
+    <style name="ButtonCompatBorderlessOverlay">
+        <item name="android:buttonStyle">@style/ButtonCompatBorderless</item>
+    </style>
     <style name="ButtonCompatBase">
         <item name="android:minWidth">88dp</item>
         <item name="android:minHeight">36dp</item>
@@ -191,8 +205,9 @@
     </style>
     <style name="ButtonCompat" parent="ButtonCompatBase">
         <item name="android:background">@drawable/button_compat_shape</item>
+        <item name="android:textStyle">bold</item>
     </style>
-    <style name="ButtonBorderlessCompat" parent="ButtonCompat">
+    <style name="ButtonCompatBorderless" parent="ButtonCompat">
         <item name="android:background">?attr/selectableItemBackground</item>
     </style>
 
diff --git a/chrome/android/java/res/values-v21/styles.xml b/chrome/android/java/res/values-v21/styles.xml
index 4035183..b156f18 100644
--- a/chrome/android/java/res/values-v21/styles.xml
+++ b/chrome/android/java/res/values-v21/styles.xml
@@ -55,7 +55,7 @@
         <item name="android:background">@drawable/button_compat</item>
         <item name="android:fontFamily">sans-serif-medium</item>
     </style>
-    <style name="ButtonBorderlessCompat" parent="ButtonCompat">
+    <style name="ButtonCompatBorderless" parent="ButtonCompat">
         <item name="android:background">@drawable/button_borderless_compat</item>
     </style>
 
diff --git a/chrome/android/java/res/values/arrays.xml b/chrome/android/java/res/values/arrays.xml
index 5e86ef2..8a35a66 100644
--- a/chrome/android/java/res/values/arrays.xml
+++ b/chrome/android/java/res/values/arrays.xml
@@ -10,8 +10,33 @@
     <item>@string/serif</item>
     <item>@string/monospace</item>
   </string-array>
+
+  <!-- Site settings -->
   <string-array name="website_settings_permission_options">
     <item>@string/website_settings_permissions_allow</item>
     <item>@string/website_settings_permissions_block</item>
   </string-array>
+
+  <!-- Privacy preferences -->
+  <string-array name="bandwidth_entries">
+    <item>@string/always_prefetch_bandwidth_entry</item>
+    <item>@string/wifi_prefetch_bandwidth_entry</item>
+    <item>@string/never_prefetch_bandwidth_entry</item>
+  </string-array>
+  <string-array name="bandwidth_entry_values">
+    <item>@string/network_prediction_always_value</item>
+    <item>@string/network_prediction_wifi_only_value</item>
+    <item>@string/network_prediction_never_value</item>
+  </string-array>
+  <string-array name="crash_upload_entries">
+    <item>@string/crash_dump_always_upload</item>
+    <item>@string/crash_dump_only_with_wifi</item>
+    <item>@string/crash_dump_never_upload</item>
+  </string-array>
+  <string-array name="crash_upload_values">
+    <item>@string/crash_dump_always_upload_value</item>
+    <item>@string/crash_dump_only_with_wifi_value</item>
+    <item>@string/crash_dump_never_upload_value</item>
+  </string-array>
+
 </resources>
diff --git a/chrome/android/java/res/values/colors.xml b/chrome/android/java/res/values/colors.xml
index 39ba1f4..0173fc3 100644
--- a/chrome/android/java/res/values/colors.xml
+++ b/chrome/android/java/res/values/colors.xml
@@ -53,7 +53,7 @@
     <color name="password_generation_link_text_color">#5595e3</color>
 
     <!-- Preferences Colors -->
-    <color name="pref_accent_color">#03a9f4</color>
+    <color name="pref_accent_color">@color/light_active_color</color>
     <color name="expandable_group_dark_gray">#484848</color>
 
     <!-- Data Saver Colors -->
diff --git a/chrome/android/java/res/values/dimens.xml b/chrome/android/java/res/values/dimens.xml
index 8f22a92..f17d114 100644
--- a/chrome/android/java/res/values/dimens.xml
+++ b/chrome/android/java/res/values/dimens.xml
@@ -17,7 +17,7 @@
 
     <!-- TabSwitcher - The minimum distance from the edge to commit a side swap. -->
     <dimen name="swipe_commit_distance">120dp</dimen>
-    
+
     <!-- Tab title -->
     <dimen name="border_texture_title_fade">24dp</dimen>                    <!-- 40px hdpi -->
     <dimen name="tab_title_favicon_start_padding">10dp</dimen>
@@ -101,4 +101,8 @@
     <dimen name="badged_user_picture_width">46dp</dimen>
     <dimen name="badge_radius">10dp</dimen>
     <dimen name="badge_border_size">1.3dp</dimen>
+
+    <!-- Account Chooser infobar Dimensions -->
+    <dimen name="account_chooser_infobar_item_height">80dp</dimen>
+
 </resources>
diff --git a/chrome/android/java/res/values/values.xml b/chrome/android/java/res/values/values.xml
index d372216..aa3b441 100644
--- a/chrome/android/java/res/values/values.xml
+++ b/chrome/android/java/res/values/values.xml
@@ -35,5 +35,7 @@
 
     <!-- Menu items IDs in Settings -->
     <item type="id" name="menu_id_translate_help" />
+    <item type="id" name="menu_id_help_privacy" />
+    <item type="id" name="menu_id_contextual_search_learn" />
 
 </resources>
diff --git a/chrome/android/java/res/xml/autofill_wallet_preferences.xml b/chrome/android/java/res/xml/autofill_wallet_preferences.xml
index c210391..9c2f179 100644
--- a/chrome/android/java/res/xml/autofill_wallet_preferences.xml
+++ b/chrome/android/java/res/xml/autofill_wallet_preferences.xml
@@ -17,14 +17,14 @@
     <org.chromium.chrome.browser.preferences.TextMessagePreference
         android:title="@string/autofill_wallet_description" />
 
+    <org.chromium.chrome.browser.preferences.TextMessagePreference
+        android:title="@string/autofill_wallet_cache_explanation" />
+
     <org.chromium.chrome.browser.preferences.HyperlinkPreference
         android:title="@string/autofill_wallet_management_link_text"
         app:url="@string/autofill_manage_wallet_cards_url"
         app:imitateWebLink="true" />
 
-    <org.chromium.chrome.browser.preferences.TextMessagePreference
-        android:title="@string/autofill_wallet_cache_explanation" />
-
     <org.chromium.chrome.browser.preferences.ButtonPreference
         android:key="autofill_clear_unmasked_cards"
         android:title="@string/autofill_wallet_clear_unmasked_cards" />
diff --git a/chrome/android/java/res/xml/contextual_search_preferences.xml b/chrome/android/java/res/xml/contextual_search_preferences.xml
new file mode 100644
index 0000000..d12541b
--- /dev/null
+++ b/chrome/android/java/res/xml/contextual_search_preferences.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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. -->
+
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" >
+
+    <org.chromium.chrome.browser.preferences.ChromeSwitchPreference
+        android:key="contextual_search_switch"
+        android:summaryOn="@string/text_on"
+        android:summaryOff="@string/text_off" />
+
+    <org.chromium.chrome.browser.preferences.TextMessagePreference
+        android:title="@string/contextual_search_description" />
+
+</PreferenceScreen>
diff --git a/chrome/android/java/res/xml/do_not_track_preferences.xml b/chrome/android/java/res/xml/do_not_track_preferences.xml
new file mode 100644
index 0000000..5bbad1e
--- /dev/null
+++ b/chrome/android/java/res/xml/do_not_track_preferences.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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. -->
+
+<PreferenceScreen
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:chrome="http://schemas.android.com/apk/res-auto">
+
+    <org.chromium.chrome.browser.preferences.ChromeSwitchPreference
+        android:key="do_not_track_switch"
+        android:summaryOn="@string/text_on"
+        android:summaryOff="@string/text_off" />
+
+    <org.chromium.chrome.browser.preferences.TextMessagePreference
+        android:title="@string/do_not_track_description" />
+
+    <org.chromium.chrome.browser.preferences.HyperlinkPreference
+        android:key="do_not_track_learn_more"
+        android:title="@string/learn_more"
+        chrome:url="@string/do_not_track_learn_more_url"
+        chrome:imitateWebLink="true" />
+
+</PreferenceScreen>
\ No newline at end of file
diff --git a/chrome/android/java/res/xml/privacy_preferences.xml b/chrome/android/java/res/xml/privacy_preferences.xml
new file mode 100644
index 0000000..c08e9bb
--- /dev/null
+++ b/chrome/android/java/res/xml/privacy_preferences.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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. -->
+
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
+    <org.chromium.chrome.browser.preferences.ChromeBaseCheckBoxPreference
+        android:key="navigation_error"
+        android:title="@string/navigation_error_title"
+        android:summary="@string/navigation_error_summary"
+        android:defaultValue="true" />
+    <org.chromium.chrome.browser.preferences.ChromeBaseCheckBoxPreference
+        android:key="search_suggestions"
+        android:title="@string/search_suggestions_title"
+        android:summary="@string/search_suggestions_summary"
+        android:defaultValue="true" />
+    <Preference
+        android:key="contextual_search"
+        android:title="@string/contextual_search_title"
+        android:fragment="org.chromium.chrome.browser.preferences.privacy.ContextualSearchPreferenceFragment" />
+
+    <!-- Only one of these network prediction preferences will be shown, depending on whether
+         the device has cellular support. -->
+    <org.chromium.chrome.browser.preferences.privacy.NetworkPredictionPreference
+        android:key="network_predictions"
+        android:title="@string/network_predictions_title"
+        android:entries="@array/bandwidth_entries"
+        android:entryValues="@array/bandwidth_entry_values"
+        android:defaultValue="@string/network_prediction_wifi_only_value" />
+    <org.chromium.chrome.browser.preferences.ChromeBaseCheckBoxPreference
+        android:key="network_predictions_no_cellular"
+        android:title="@string/network_predictions_title"
+        android:summary="@string/network_predictions_summary"
+        android:defaultValue="true" />
+
+    <!-- Only one of these "Usage and crash reports" preferences will be shown, depending on whether
+         the device has cellular support. -->
+    <org.chromium.chrome.browser.preferences.privacy.CrashDumpUploadPreference
+        android:key="crash_dump_upload"
+        android:title="@string/crash_dump_upload_title"
+        android:entries="@array/crash_upload_entries"
+        android:entryValues="@array/crash_upload_values"
+        android:defaultValue="@string/crash_dump_never_upload_value" />
+    <org.chromium.chrome.browser.preferences.ChromeBaseCheckBoxPreference
+        android:key="crash_dump_upload_no_cellular"
+        android:title="@string/crash_dump_upload_title"
+        android:defaultValue="false" />
+
+    <Preference
+        android:fragment="org.chromium.chrome.browser.preferences.privacy.DoNotTrackPreference"
+        android:key="do_not_track"
+        android:title="@string/do_not_track_title" />
+    <org.chromium.chrome.browser.preferences.ButtonPreference
+        android:key="clear_browsing_data"
+        android:title="@string/clear_browsing_data_title" />
+</PreferenceScreen>
diff --git a/chrome/android/java/src/org/chromium/chrome/ChromeSwitches.java b/chrome/android/java/src/org/chromium/chrome/ChromeSwitches.java
index 028b17d..e1639c15 100644
--- a/chrome/android/java/src/org/chromium/chrome/ChromeSwitches.java
+++ b/chrome/android/java/src/org/chromium/chrome/ChromeSwitches.java
@@ -119,6 +119,9 @@
     /** Enable the Reader Mode icon in toolbar. */
     public static final String ENABLE_READER_MODE_TOOLBAR_ICON = "enable-reader-mode-toolbar-icon";
 
+    /** Enable the native app banners. */
+    public static final String ENABLE_APP_INSTALL_ALERTS = "enable-app-install-alerts";
+
     /**
      * Use sandbox Wallet environment for requestAutocomplete.
      * Native switch - autofill::switches::kWalletServiceUseSandbox.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromiumApplication.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromiumApplication.java
index a2e9385..4ece3e32 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromiumApplication.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromiumApplication.java
@@ -6,17 +6,24 @@
 
 import android.app.Activity;
 import android.content.Context;
+import android.content.Intent;
 import android.os.Build;
+import android.os.Bundle;
+import android.util.Log;
 
 import org.chromium.base.CalledByNative;
 import org.chromium.base.ThreadUtils;
 import org.chromium.base.library_loader.LibraryProcessType;;
 import org.chromium.base.library_loader.ProcessInitException;
+import org.chromium.chrome.browser.partnercustomizations.PartnerBrowserCustomizations;
 import org.chromium.chrome.browser.preferences.LocationSettings;
+import org.chromium.chrome.browser.preferences.Preferences;
 import org.chromium.chrome.browser.preferences.PreferencesLauncher;
 import org.chromium.chrome.browser.preferences.ProtectedContentPreferences;
 import org.chromium.chrome.browser.preferences.autofill.AutofillPreferences;
 import org.chromium.chrome.browser.preferences.password.ManageSavedPasswordsPreferences;
+import org.chromium.chrome.browser.preferences.privacy.PrivacyPreferences;
+import org.chromium.chrome.browser.preferences.website.SingleWebsitePreferences;
 import org.chromium.content.app.ContentApplication;
 import org.chromium.content.browser.BrowserStartupController;
 
@@ -26,6 +33,8 @@
  */
 public abstract class ChromiumApplication extends ContentApplication {
 
+    private static final String TAG = "ChromiumApplication";
+
     /**
      * Returns whether the Activity is being shown in multi-window mode.
      */
@@ -61,6 +70,20 @@
     }
 
     /**
+     * Opens the single origin settings page for the given URL.
+     *
+     * @param url The URL to show the single origin settings for. This is a complete url
+     *            including scheme, domain, port, path, etc.
+     */
+    protected void showSingleOriginSettings(String url) {
+        Bundle fragmentArgs = SingleWebsitePreferences.createFragmentArgsForSite(url);
+        Intent intent = PreferencesLauncher.createIntentForSettingsPage(
+                this, SingleWebsitePreferences.class.getName());
+        intent.putExtra(Preferences.EXTRA_SHOW_FRAGMENT_ARGUMENTS, fragmentArgs);
+        startActivity(intent);
+    }
+
+    /**
      * For extending classes to carry out tasks that initialize the browser process.
      * Should be called almost immediately after the native library has loaded to initialize things
      * that really, really have to be set up early.  Avoid putting any long tasks here.
@@ -96,14 +119,29 @@
      * @param tab The tab that triggered the request.
      */
     @CalledByNative
-    protected void openClearBrowsingData(Tab tab) {}
+    protected void openClearBrowsingData(Tab tab) {
+        Activity activity = tab.getWindowAndroid().getActivity().get();
+        if (activity == null) {
+            Log.e(TAG,
+                    "Attempting to open clear browsing data for a tab without a valid activity");
+            return;
+        }
+        Intent intent = PreferencesLauncher.createIntentForSettingsPage(activity,
+                PrivacyPreferences.class.getName());
+        Bundle arguments = new Bundle();
+        arguments.putBoolean(PrivacyPreferences.SHOW_CLEAR_BROWSING_DATA_EXTRA, true);
+        intent.putExtra(Preferences.EXTRA_SHOW_FRAGMENT_ARGUMENTS, arguments);
+        activity.startActivity(intent);
+    }
 
     /**
      * @return Whether parental controls are enabled.  Returning true will disable
      *         incognito mode.
      */
     @CalledByNative
-    protected abstract boolean areParentalControlsEnabled();
+    protected boolean areParentalControlsEnabled() {
+        return PartnerBrowserCustomizations.isIncognitoDisabled();
+    }
 
     // TODO(yfriedman): This is too widely available. Plumb this through ChromeNetworkDelegate
     // instead.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ServiceTabLauncher.java b/chrome/android/java/src/org/chromium/chrome/browser/ServiceTabLauncher.java
index ae2f56e..4eaf752 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ServiceTabLauncher.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ServiceTabLauncher.java
@@ -11,6 +11,7 @@
 import android.util.Log;
 
 import org.chromium.base.CalledByNative;
+import org.chromium.content_public.browser.WebContents;
 
 /**
  * Tab Launcher to be used to launch new tabs from background Android Services, when it is not
@@ -23,6 +24,10 @@
     private static final String SERVICE_TAB_LAUNCHER_KEY =
             "org.chromium.chrome.browser.SERVICE_TAB_LAUNCHER";
 
+    // Name of the extra containing the Id of a tab launch request id.
+    public static final String LAUNCH_REQUEST_ID_EXTRA =
+            "org.chromium.chrome.browser.ServiceTabLauncher.LAUNCH_REQUEST_ID";
+
     private static ServiceTabLauncher sInstance;
 
     /**
@@ -88,4 +93,19 @@
 
         return null;
     }
+
+    /**
+     * To be called by the activity when the WebContents for |requestId| has been created, or has
+     * been recycled from previous use. The |webContents| must not yet have started provisional
+     * load for the main frame.
+     *
+     * @param requestId Id of the tab launching request which has been fulfilled.
+     * @param webContents The WebContents instance associated with this request.
+     */
+    public static void onWebContentsForRequestAvailable(int requestId, WebContents webContents) {
+        nativeOnWebContentsForRequestAvailable(requestId, webContents);
+    }
+
+    private static native void nativeOnWebContentsForRequestAvailable(
+            int requestId, WebContents webContents);
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ShortcutHelper.java b/chrome/android/java/src/org/chromium/chrome/browser/ShortcutHelper.java
index f953e8f..0314aba6 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ShortcutHelper.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ShortcutHelper.java
@@ -8,11 +8,17 @@
 import android.content.Context;
 import android.content.Intent;
 import android.graphics.Bitmap;
+import android.os.Handler;
+import android.os.Looper;
 import android.text.TextUtils;
 import android.util.Base64;
 import android.util.Log;
+import android.widget.Toast;
 
+import org.chromium.base.ApplicationStatus;
 import org.chromium.base.CalledByNative;
+import org.chromium.chrome.R;
+import org.chromium.content_public.browser.WebContents;
 import org.chromium.content_public.common.ScreenOrientationConstants;
 
 import java.io.ByteArrayOutputStream;
@@ -65,7 +71,7 @@
      */
     public void initialize(OnInitialized callback) {
         mCallback = callback;
-        mNativeShortcutHelper = nativeInitialize(mTab.getNativePtr());
+        mNativeShortcutHelper = nativeInitialize(mTab.getWebContents());
     }
 
     /**
@@ -123,7 +129,8 @@
     @SuppressWarnings("unused")
     @CalledByNative
     private static void addShortcut(Context context, String url, String title, Bitmap icon,
-            int red, int green, int blue, boolean isWebappCapable, int orientation) {
+            int red, int green, int blue, boolean isWebappCapable, int orientation,
+            boolean returnToHomescreen) {
         assert sFullScreenAction != null;
 
         Intent shortcutIntent;
@@ -161,14 +168,29 @@
         context.sendBroadcast(BookmarkUtils.createAddToHomeIntent(context, shortcutIntent, title,
                 icon, red, green, blue));
 
-        // User is sent to the homescreen as soon as the shortcut is created.
-        Intent homeIntent = new Intent(Intent.ACTION_MAIN);
-        homeIntent.addCategory(Intent.CATEGORY_HOME);
-        homeIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        context.startActivity(homeIntent);
+        // Alert the user about adding the shortcut.
+        if (returnToHomescreen) {
+            Intent homeIntent = new Intent(Intent.ACTION_MAIN);
+            homeIntent.addCategory(Intent.CATEGORY_HOME);
+            homeIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+            context.startActivity(homeIntent);
+        } else {
+            final String shortUrl = UrlUtilities.getDomainAndRegistry(url, true);
+            Handler handler = new Handler(Looper.getMainLooper());
+            handler.post(new Runnable() {
+                @Override
+                public void run() {
+                    Context applicationContext = ApplicationStatus.getApplicationContext();
+                    String toastText =
+                            applicationContext.getString(R.string.added_to_homescreen, shortUrl);
+                    Toast toast = Toast.makeText(applicationContext, toastText, Toast.LENGTH_SHORT);
+                    toast.show();
+                }
+            });
+        }
     }
 
-    private native long nativeInitialize(long tabAndroidPtr);
+    private native long nativeInitialize(WebContents webContents);
     private native void nativeAddShortcut(long nativeShortcutHelper, String userRequestedTitle,
             int launcherLargeIconSize);
     private native void nativeTearDown(long nativeShortcutHelper);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/Tab.java b/chrome/android/java/src/org/chromium/chrome/browser/Tab.java
index 5e399f1..214a4745 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/Tab.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/Tab.java
@@ -22,6 +22,7 @@
 import org.chromium.base.ObserverList.RewindableIterator;
 import org.chromium.base.TraceEvent;
 import org.chromium.base.VisibleForTesting;
+import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.TabState.WebContentsState;
 import org.chromium.chrome.browser.banners.AppBannerManager;
@@ -1260,7 +1261,7 @@
         initializeNative();
 
         if (AppBannerManager.isEnabled()) {
-            mAppBannerManager = new AppBannerManager(this);
+            mAppBannerManager = new AppBannerManager(this, mContext);
             addObserver(mAppBannerManager);
         }
     }
@@ -1357,6 +1358,10 @@
         mIsTabStateDirty = true;
         updateTitle();
         updateFullscreenEnabledState();
+        if (!isNativePage()) {
+            RecordHistogram.recordBooleanHistogram(
+                    "Navigation.MobileOptimized", mContentViewCore.getIsMobileOptimizedHint());
+        }
 
         for (TabObserver observer : mObservers) observer.onPageLoadFinished(this);
     }
@@ -2361,6 +2366,22 @@
     }
 
     /**
+     * @return The ID of the bookmark associated with the current URL (or -1 if no such bookmark
+     *         exists).
+     */
+    public long getBookmarkId() {
+        return isFrozen() ? -1 : nativeGetBookmarkId(mNativeTabAndroid, false);
+    }
+
+    /**
+     * Same as getBookmarkId() but never returns ids for managed bookmarks, or any other bookmarks
+     * that can't be edited by the user.
+     */
+    public long getUserBookmarkId() {
+        return isFrozen() ? -1 : nativeGetBookmarkId(mNativeTabAndroid, true);
+    }
+
+    /**
      * Request that this tab receive focus. Currently, this function requests focus for the main
      * View (usually a ContentView).
      */
@@ -2482,6 +2503,7 @@
     private native void nativeUpdateTopControlsState(
             long nativeTabAndroid, int constraints, int current, boolean animate);
     private native void nativeSearchByImageInNewTabAsync(long nativeTabAndroid);
+    private native long nativeGetBookmarkId(long nativeTabAndroid, boolean onlyEditable);
     private native void nativeSetInterceptNavigationDelegate(long nativeTabAndroid,
             InterceptNavigationDelegate delegate);
     private native void nativeAttachToTabContentManager(long nativeTabAndroid,
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuAdapter.java b/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuAdapter.java
index 0c6be56..fa87aaa 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuAdapter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuAdapter.java
@@ -40,22 +40,14 @@
      */
     private static final int TITLE_BUTTON_MENU_ITEM = 1;
     /**
-     * Menu item that has two buttons. Every one of these buttons is displayed as an icon.
-     */
-    private static final int TWO_BUTTON_MENU_ITEM = 2;
-    /**
      * Menu item that has three buttons. Every one of these buttons is displayed as an icon.
      */
-    private static final int THREE_BUTTON_MENU_ITEM = 3;
-    /**
-     * Menu item that has four buttons. Every one of these buttons is displayed as an icon.
-     */
-    private static final int FOUR_BUTTON_MENU_ITEM = 4;
+    private static final int THREE_BUTTON_MENU_ITEM = 2;
 
     /**
      * The number of view types specified above.  If you add a view type you MUST increment this.
      */
-    private static final int VIEW_TYPE_COUNT = 5;
+    private static final int VIEW_TYPE_COUNT = 3;
 
     /** MenuItem Animation Constants */
     private static final int ENTER_ITEM_DURATION_MS = 350;
@@ -93,14 +85,9 @@
         MenuItem item = getItem(position);
         int viewCount = item.hasSubMenu() ? item.getSubMenu().size() : 1;
 
-        if (viewCount == 4) {
-            return FOUR_BUTTON_MENU_ITEM;
-        } else if (viewCount == 3) {
+        if (viewCount == 3) {
             return THREE_BUTTON_MENU_ITEM;
         } else if (viewCount == 2) {
-            if (position == 0 && item.getSubMenu().getItem(0).getIcon() != null) {
-                return TWO_BUTTON_MENU_ITEM;
-            }
             return TITLE_BUTTON_MENU_ITEM;
         }
         return STANDARD_MENU_ITEM;
@@ -158,28 +145,6 @@
                 convertView.setEnabled(isEnabled);
                 break;
             }
-            case TWO_BUTTON_MENU_ITEM: {
-                TwoButtonMenuItemViewHolder holder = null;
-                if (convertView == null) {
-                    holder = new TwoButtonMenuItemViewHolder();
-                    convertView = mInflater.inflate(R.layout.two_button_menu_item, parent, false);
-                    holder.buttons[0] =
-                            (TintedImageButton) convertView.findViewById(R.id.button_one);
-                    holder.buttons[1] =
-                            (TintedImageButton) convertView.findViewById(R.id.button_two);
-                    convertView.setTag(holder);
-                    convertView.setTag(R.id.menu_item_enter_anim_id,
-                            buildIconItemEnterAnimator(holder.buttons));
-                } else {
-                    holder = (TwoButtonMenuItemViewHolder) convertView.getTag();
-                }
-                setupImageButton(holder.buttons[0], item.getSubMenu().getItem(0));
-                setupImageButton(holder.buttons[1], item.getSubMenu().getItem(1));
-
-                convertView.setFocusable(false);
-                convertView.setEnabled(false);
-                break;
-            }
             case THREE_BUTTON_MENU_ITEM: {
                 ThreeButtonMenuItemViewHolder holder = null;
                 if (convertView == null) {
@@ -205,34 +170,6 @@
                 convertView.setEnabled(false);
                 break;
             }
-            case FOUR_BUTTON_MENU_ITEM: {
-                FourButtonMenuItemViewHolder holder = null;
-                if (convertView == null) {
-                    holder = new FourButtonMenuItemViewHolder();
-                    convertView = mInflater.inflate(R.layout.four_button_menu_item, parent, false);
-                    holder.buttons[0] =
-                            (TintedImageButton) convertView.findViewById(R.id.button_one);
-                    holder.buttons[1] =
-                            (TintedImageButton) convertView.findViewById(R.id.button_two);
-                    holder.buttons[2] =
-                            (TintedImageButton) convertView.findViewById(R.id.button_three);
-                    holder.buttons[3] =
-                            (TintedImageButton) convertView.findViewById(R.id.button_four);
-                    convertView.setTag(holder);
-                    convertView.setTag(R.id.menu_item_enter_anim_id,
-                            buildIconItemEnterAnimator(holder.buttons));
-                } else {
-                    holder = (FourButtonMenuItemViewHolder) convertView.getTag();
-                }
-                setupImageButton(holder.buttons[0], item.getSubMenu().getItem(0));
-                setupImageButton(holder.buttons[1], item.getSubMenu().getItem(1));
-                setupImageButton(holder.buttons[2], item.getSubMenu().getItem(2));
-                setupImageButton(holder.buttons[3], item.getSubMenu().getItem(3));
-
-                convertView.setFocusable(false);
-                convertView.setEnabled(false);
-                break;
-            }
             case TITLE_BUTTON_MENU_ITEM: {
                 TitleButtonMenuItemViewHolder holder = null;
                 if (convertView == null) {
@@ -376,18 +313,10 @@
         public AppMenuItemIcon image;
     }
 
-    static class TwoButtonMenuItemViewHolder {
-        public TintedImageButton[] buttons = new TintedImageButton[2];
-    }
-
     static class ThreeButtonMenuItemViewHolder {
         public TintedImageButton[] buttons = new TintedImageButton[3];
     }
 
-    static class FourButtonMenuItemViewHolder {
-        public TintedImageButton[] buttons = new TintedImageButton[4];
-    }
-
     static class TitleButtonMenuItemViewHolder {
         public TextView title;
         public TintedImageButton button;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/CardUnmaskPrompt.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/CardUnmaskPrompt.java
index d11baa47..efabf8d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/CardUnmaskPrompt.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/CardUnmaskPrompt.java
@@ -7,6 +7,7 @@
 import android.app.AlertDialog;
 import android.content.Context;
 import android.content.DialogInterface;
+import android.graphics.ColorFilter;
 import android.graphics.PorterDuff;
 import android.graphics.PorterDuffColorFilter;
 import android.os.Build;
@@ -17,18 +18,15 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.inputmethod.InputMethodManager;
-import android.widget.ArrayAdapter;
 import android.widget.Button;
 import android.widget.CheckBox;
 import android.widget.EditText;
 import android.widget.ImageView;
 import android.widget.ProgressBar;
-import android.widget.Spinner;
 import android.widget.TextView;
 
 import org.chromium.chrome.R;
 
-import java.text.NumberFormat;
 import java.util.Calendar;
 
 /**
@@ -38,10 +36,12 @@
     private final CardUnmaskPromptDelegate mDelegate;
     private final AlertDialog mDialog;
     private final boolean mShouldRequestExpirationDate;
+    private final int mThisYear;
 
     private final EditText mCardUnmaskInput;
-    private final Spinner mMonthSpinner;
-    private final Spinner mYearSpinner;
+    private final EditText mMonthInput;
+    private final EditText mYearInput;
+    private final View mExpirationContainer;
     private final TextView mErrorMessage;
     private final CheckBox mStoreLocallyCheckbox;
     private final View mMainContents;
@@ -84,8 +84,9 @@
         ((TextView) v.findViewById(R.id.instructions)).setText(instructions);
 
         mCardUnmaskInput = (EditText) v.findViewById(R.id.card_unmask_input);
-        mMonthSpinner = (Spinner) v.findViewById(R.id.expiration_month);
-        mYearSpinner = (Spinner) v.findViewById(R.id.expiration_year);
+        mMonthInput = (EditText) v.findViewById(R.id.expiration_month);
+        mYearInput = (EditText) v.findViewById(R.id.expiration_year);
+        mExpirationContainer = v.findViewById(R.id.expiration_container);
         mErrorMessage = (TextView) v.findViewById(R.id.error_message);
         mStoreLocallyCheckbox = (CheckBox) v.findViewById(R.id.store_locally_checkbox);
         mStoreLocallyCheckbox.setChecked(defaultToStoringLocally);
@@ -104,12 +105,13 @@
                           .create();
 
         mShouldRequestExpirationDate = shouldRequestExpirationDate;
+        mThisYear = Calendar.getInstance().get(Calendar.YEAR);
     }
 
     public void show() {
         mDialog.show();
 
-        if (mShouldRequestExpirationDate) initializeExpirationDateSpinners();
+        if (mShouldRequestExpirationDate) mExpirationContainer.setVisibility(View.VISIBLE);
 
         // Override the View.OnClickListener so that pressing the positive button doesn't dismiss
         // the dialog.
@@ -119,24 +121,23 @@
             @Override
             public void onClick(View view) {
                 mDelegate.onUserInput(mCardUnmaskInput.getText().toString(),
-                        (String) mMonthSpinner.getSelectedItem(),
-                        (String) mYearSpinner.getSelectedItem(),
+                        mMonthInput.getText().toString(),
+                        mYearInput.getText().toString(),
                         mStoreLocallyCheckbox.isChecked());
             }
         });
 
-        final EditText input = mCardUnmaskInput;
-        input.addTextChangedListener(this);
-        input.post(new Runnable() {
+        mCardUnmaskInput.addTextChangedListener(this);
+        mCardUnmaskInput.post(new Runnable() {
             @Override
             public void run() {
                 setInitialFocus();
             }
         });
-
-        // Calling this from here clobbers the input's background shadow, which is otherwise
-        // highly resistant to styling.
-        setInputError(null);
+        if (mShouldRequestExpirationDate) {
+            mMonthInput.addTextChangedListener(this);
+            mYearInput.addTextChangedListener(this);
+        }
     }
 
     public void dismiss() {
@@ -155,6 +156,10 @@
         if (!success) {
             setInputsEnabled(true);
             setInputError("Credit card could not be verified. Try again.");
+            // TODO(estade): depending on the type of error, we may not want to disable the
+            // verify button. But for the common case, where unmasking failed due to a bad
+            // value, verify should be disabled until the user makes some change.
+            mDialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(false);
             setInitialFocus();
             // TODO(estade): UI decision - should we clear the input?
         } else {
@@ -186,48 +191,25 @@
     @Override
     public void onTextChanged(CharSequence s, int start, int before, int count) {}
 
-    private void initializeExpirationDateSpinners() {
-        ArrayAdapter<CharSequence> monthAdapter = new ArrayAdapter<CharSequence>(
-                mDialog.getContext(), android.R.layout.simple_spinner_item);
-
-        // TODO(estade): i18n, or remove this entry, or something.
-        monthAdapter.add("MM");
-        NumberFormat nf = NumberFormat.getInstance();
-        nf.setMinimumIntegerDigits(2);
-        for (int month = 1; month <= 12; month++) {
-            monthAdapter.add(nf.format(month));
-        }
-        monthAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
-        mMonthSpinner.setAdapter(monthAdapter);
-
-        ArrayAdapter<CharSequence> yearAdapter = new ArrayAdapter<CharSequence>(
-                mDialog.getContext(), android.R.layout.simple_spinner_item);
-        yearAdapter.add("YYYY");
-        Calendar calendar = Calendar.getInstance();
-        int initialYear = calendar.get(Calendar.YEAR);
-        for (int year = initialYear; year < initialYear + 10; year++) {
-            yearAdapter.add(Integer.toString(year));
-        }
-        yearAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
-        mYearSpinner.setAdapter(yearAdapter);
-
-        mMonthSpinner.setVisibility(View.VISIBLE);
-        mYearSpinner.setVisibility(View.VISIBLE);
-    }
-
     private void setInitialFocus() {
-        if (mShouldRequestExpirationDate) return;
-
         InputMethodManager imm = (InputMethodManager) mDialog.getContext().getSystemService(
                 Context.INPUT_METHOD_SERVICE);
-        imm.showSoftInput(mCardUnmaskInput, InputMethodManager.SHOW_IMPLICIT);
+        imm.showSoftInput(mShouldRequestExpirationDate ? mMonthInput : mCardUnmaskInput,
+                InputMethodManager.SHOW_IMPLICIT);
     }
 
     private boolean areInputsValid() {
-        if (mShouldRequestExpirationDate
-                && (mMonthSpinner.getSelectedItemPosition() == 0
-                        || mYearSpinner.getSelectedItemPosition() == 0)) {
-            return false;
+        if (mShouldRequestExpirationDate) {
+            try {
+                int month = Integer.parseInt(mMonthInput.getText().toString());
+                if (month < 1 || month > 12) return false;
+
+                // TODO(estade): allow 4 digit year input?
+                int year = Integer.parseInt(mYearInput.getText().toString());
+                if (year < mThisYear % 100 || year > (mThisYear + 10) % 100) return false;
+            } catch (NumberFormatException e) {
+                return false;
+            }
         }
         return mDelegate.checkUserInputValidity(mCardUnmaskInput.getText().toString());
     }
@@ -239,9 +221,8 @@
      */
     private void setInputsEnabled(boolean enabled) {
         mCardUnmaskInput.setEnabled(enabled);
-        mMonthSpinner.setEnabled(enabled);
-        mYearSpinner.setEnabled(enabled);
-        mStoreLocallyCheckbox.setEnabled(enabled);
+        mMonthInput.setEnabled(enabled);
+        mYearInput.setEnabled(enabled);
         mMainContents.setAlpha(enabled ? 1.0f : 0.15f);
         mMainContents.setImportantForAccessibility(
                 enabled ? View.IMPORTANT_FOR_ACCESSIBILITY_AUTO
@@ -266,13 +247,25 @@
         // draw the TextInput.
         if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) return;
 
-        // The input is always active or in an error state. Simply clearing the color
-        // filter resets the color to input_underline_color, even when it's active.
-        int strokeColor = mDialog.getContext().getResources().getColor(
-                message == null ? R.color.light_active_color
-                                : R.color.input_underline_error_color);
+        ColorFilter filter = null;
+        if (message != null) {
+            filter = new PorterDuffColorFilter(mDialog.getContext().getResources().getColor(
+                    R.color.input_underline_error_color), PorterDuff.Mode.SRC_IN);
+        }
 
-        mCardUnmaskInput.getBackground().mutate().setColorFilter(
-                new PorterDuffColorFilter(strokeColor, PorterDuff.Mode.SRC_IN));
+        // TODO(estade): it would be nicer if the error were specific enough to tell us which input
+        // was invalid.
+        updateColorForInput(mCardUnmaskInput, filter);
+        updateColorForInput(mMonthInput, filter);
+        updateColorForInput(mYearInput, filter);
+    }
+
+    /**
+     * Sets the stroke color for the given input.
+     * @param input The input to modify.
+     * @param filter The color filter to apply to the background.
+     */
+    private void updateColorForInput(EditText input, ColorFilter filter) {
+        input.getBackground().mutate().setColorFilter(filter);
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/banners/AppBannerManager.java b/chrome/android/java/src/org/chromium/chrome/browser/banners/AppBannerManager.java
index 44b4d40..1e17f7cb 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/banners/AppBannerManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/banners/AppBannerManager.java
@@ -4,9 +4,9 @@
 
 package org.chromium.chrome.browser.banners;
 
+import android.content.Context;
 import android.text.TextUtils;
 
-import org.chromium.base.ApplicationStatus;
 import org.chromium.base.CalledByNative;
 import org.chromium.base.JNINamespace;
 import org.chromium.base.VisibleForTesting;
@@ -64,8 +64,9 @@
      * Constructs an AppBannerManager for the given tab.
      * @param tab Tab that the AppBannerManager will be attached to.
      */
-    public AppBannerManager(Tab tab) {
-        mNativePointer = nativeInit();
+    public AppBannerManager(Tab tab, Context context) {
+        int iconSize = context.getResources().getDimensionPixelSize(R.dimen.app_banner_icon_size);
+        mNativePointer = nativeInit(iconSize);
         mTab = tab;
         updatePointers();
     }
@@ -95,21 +96,14 @@
         nativeReplaceWebContents(mNativePointer, mTab.getWebContents());
     }
 
-    @CalledByNative
-    private int getPreferredIconSize() {
-        return ApplicationStatus.getApplicationContext().getResources().getDimensionPixelSize(
-                R.dimen.app_banner_icon_size);
-    }
-
     /**
      * Grabs package information for the banner asynchronously.
      * @param url         URL for the page that is triggering the banner.
      * @param packageName Name of the package that is being advertised.
      */
     @CalledByNative
-    private void fetchAppDetails(String url, String packageName) {
+    private void fetchAppDetails(String url, String packageName, int iconSize) {
         if (sAppDetailsDelegate == null) return;
-        int iconSize = getPreferredIconSize();
         sAppDetailsDelegate.getAppDetailsAsynchronously(
                 createAppDetailsObserver(), url, packageName, iconSize);
     }
@@ -146,14 +140,20 @@
         nativeSetTimeDeltaForTesting(days);
     }
 
-    /** Records how many native BitmapFetchers are actively retrieving app icons. */
+    /** Disables the HTTPS scheme requirement for testing. */
     @VisibleForTesting
-    public int getNumActiveFetchersForTesting() {
-        return nativeGetNumActiveFetchers(mNativePointer);
+    static void disableSecureSchemeCheckForTesting() {
+        nativeDisableSecureSchemeCheckForTesting();
+    }
+
+    /** Returns whether a BitmapFetcher is actively retrieving an app icon. */
+    @VisibleForTesting
+    public boolean isFetcherActiveForTesting() {
+        return nativeIsFetcherActive(mNativePointer);
     }
 
     private static native boolean nativeIsEnabled();
-    private native long nativeInit();
+    private native long nativeInit(int iconSize);
     private native void nativeDestroy(long nativeAppBannerManager);
     private native void nativeReplaceWebContents(long nativeAppBannerManager,
             WebContents webContents);
@@ -162,9 +162,6 @@
 
     // Testing methods.
     private static native void nativeSetTimeDeltaForTesting(int days);
-    private native int nativeGetNumActiveFetchers(long nativeAppBannerManager);
-
-    // UMA tracking.
-    private static native void nativeRecordDismissEvent(int metric);
-    private static native void nativeRecordInstallEvent(int metric);
+    private static native void nativeDisableSecureSchemeCheckForTesting();
+    private native boolean nativeIsFetcherActive(long nativeAppBannerManager);
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/document/PendingDocumentData.java b/chrome/android/java/src/org/chromium/chrome/browser/document/PendingDocumentData.java
index 62711bf..713e6e42 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/document/PendingDocumentData.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/document/PendingDocumentData.java
@@ -31,4 +31,7 @@
 
     /** The original intent */
     public Intent originalIntent;
+
+    /** The tab launch request Id from the service tab launcher. **/
+    public int requestId;
 }
\ No newline at end of file
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadManagerService.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadManagerService.java
index 7104558..d8c73406 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadManagerService.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadManagerService.java
@@ -200,8 +200,7 @@
                     Log.w(TAG, "Download failed: Cleared download id:" + id);
                 }
             }
-            mSharedPrefs.edit().remove(DOWNLOAD_NOTIFICATION_IDS);
-            mSharedPrefs.edit().apply();
+            mSharedPrefs.edit().remove(DOWNLOAD_NOTIFICATION_IDS).apply();
         }
         if (mSharedPrefs.contains(PENDING_OMA_DOWNLOADS)) {
             Set<String> omaDownloads = getStoredDownloadInfo(PENDING_OMA_DOWNLOADS);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/enhanced_bookmarks/EnhancedBookmarksModel.java b/chrome/android/java/src/org/chromium/chrome/browser/enhanced_bookmarks/EnhancedBookmarksModel.java
index 19580cd3a..012222c1 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/enhanced_bookmarks/EnhancedBookmarksModel.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/enhanced_bookmarks/EnhancedBookmarksModel.java
@@ -14,6 +14,7 @@
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.components.bookmarks.BookmarkId;
 import org.chromium.components.bookmarks.BookmarkMatch;
+import org.chromium.components.bookmarks.BookmarkType;
 import org.chromium.components.dom_distiller.core.DomDistillerUrlUtils;
 import org.chromium.content_public.browser.WebContents;
 
@@ -38,8 +39,9 @@
         /**
          * Callback being triggered immediately before bookmarks are deleted.
          * @param titles All titles of the bookmarks to be deleted.
+         * @param isUndoable Whether the deletion is undoable.
          */
-        void onDeleteBookmarks(String[] titles);
+        void onDeleteBookmarks(String[] titles, boolean isUndoable);
     }
 
     private final BookmarksBridge mBookmarksBridge;
@@ -130,8 +132,10 @@
         assert bookmarks != null && bookmarks.length > 0;
         // Store all titles of bookmarks.
         String[] titles = new String[bookmarks.length];
+        boolean isUndoable = true;
         for (int i = 0; i < bookmarks.length; i++) {
             titles[i] = getBookmarkTitle(bookmarks[i]);
+            isUndoable &= (bookmarks[i].getType() == BookmarkType.NORMAL);
         }
 
         if (bookmarks.length == 1) {
@@ -145,7 +149,7 @@
         }
 
         for (EnhancedBookmarkDeleteObserver observer : mDeleteObservers) {
-            observer.onDeleteBookmarks(titles);
+            observer.onDeleteBookmarks(titles, isUndoable);
         }
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/ChromeFullscreenManager.java b/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/ChromeFullscreenManager.java
index 7b9b6f0..e9141cf 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/ChromeFullscreenManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/ChromeFullscreenManager.java
@@ -190,23 +190,6 @@
      * @param persistentFullscreenSupported Whether persistent fullscreen mode is supported.
      * @param modelSelector The model selector providing access to the current tab.
      * @param resControlContainerHeight The dimension resource ID for the control container height.
-     */
-    // TODO(changwan): remove
-    public ChromeFullscreenManager(Activity activity, View controlContainer, boolean enabled,
-            boolean persistentFullscreenSupported, TabModelSelector modelSelector,
-            int resControlContainerHeight) {
-        this(activity, controlContainer, enabled, persistentFullscreenSupported,
-                modelSelector, resControlContainerHeight, true);
-    }
-
-    /**
-     * Creates an instance of the fullscreen mode manager.
-     * @param activity The activity that supports fullscreen.
-     * @param controlContainer Container holding the controls (Toolbar).
-     * @param enabled Whether fullscreen is globally enabled.
-     * @param persistentFullscreenSupported Whether persistent fullscreen mode is supported.
-     * @param modelSelector The model selector providing access to the current tab.
-     * @param resControlContainerHeight The dimension resource ID for the control container height.
      * @param supportsBrowserOverride Whether we want to disable the token system used by the
                                       browser.
      */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/FullscreenHtmlApiHandler.java b/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/FullscreenHtmlApiHandler.java
index 50b640b..6f16a74 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/FullscreenHtmlApiHandler.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/FullscreenHtmlApiHandler.java
@@ -70,21 +70,16 @@
     /**
      * Delegate that allows embedders to react to fullscreen API requests.
      */
-    // TODO(changwan): change this to interface
-    public static class FullscreenHtmlApiDelegate {
+    public interface FullscreenHtmlApiDelegate {
         /**
          * @return The Y offset to be applied to the fullscreen notification.
          */
-        public int getNotificationOffsetY() {
-            return 0;
-        }
+        int getNotificationOffsetY();
 
         /**
          * @return The view that the fullscreen notification will be pinned to.
          */
-        public View getNotificationAnchorView() {
-            return null;
-        }
+        View getNotificationAnchorView();
 
         /**
          * Notifies the delegate that entering fullscreen has been requested and allows them
@@ -93,24 +88,13 @@
          * Once the delegate has hidden the their controls, it must call
          * {@link FullscreenHtmlApiHandler#enterFullscreen(Tab)}.
          */
-        public void onEnterFullscreen() {}
+        void onEnterFullscreen();
 
         /**
          * Cancels a pending enter fullscreen request if present.
          * @return Whether the request was cancelled.
          */
-        public boolean cancelPendingEnterFullscreen() {
-            return true;
-        }
-
-        /**
-         * Notifies the delegate that the window UI has fully exited fullscreen and gives
-         * the embedder a chance to update their controls.
-         *
-         * @param contentViewCore The CVC for the tab whose fullscreen is being exited.
-         */
-        // TODO(changwan): remove
-        public void onFullscreenExited(ContentViewCore contentViewCore) {}
+        boolean cancelPendingEnterFullscreen();
 
         /**
          * Notifies the delegate that the window UI has fully exited fullscreen and gives
@@ -118,15 +102,13 @@
          *
          * @param tab The tab whose fullscreen is being exited.
          */
-        public void onFullscreenExited(Tab tab) {}
+        void onFullscreenExited(Tab tab);
 
         /**
          * @return Whether the notification bubble should be shown. For fullscreen video in
          *         overlay mode, the notification bubble should be disabled.
          */
-        public boolean shouldShowNotificationBubble() {
-            return true;
-        }
+        boolean shouldShowNotificationBubble();
     }
 
     // This static inner class holds a WeakReference to the outer object, to avoid triggering the
@@ -299,16 +281,6 @@
      */
     public void enterFullscreen(final Tab tab) {
         ContentViewCore contentViewCore = tab.getContentViewCore();
-        enterFullscreen(contentViewCore);
-        mTabInFullscreen = tab;
-    }
-
-    /**
-     * Handles hiding the system UI components to allow the content to take up the full screen.
-     * @param tab The CVC for the tab that is entering fullscreen.
-     */
-    // TODO(changwan): remove
-    public void enterFullscreen(final ContentViewCore contentViewCore) {
         if (contentViewCore == null) return;
         final View contentView = contentViewCore.getContainerView();
         int systemUiVisibility = contentView.getSystemUiVisibility();
@@ -352,6 +324,7 @@
         contentView.addOnLayoutChangeListener(mFullscreenOnLayoutChangeListener);
         contentView.setSystemUiVisibility(systemUiVisibility);
         mContentViewCoreInFullscreen = contentViewCore;
+        mTabInFullscreen = tab;
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/infobar/AccountChooserInfoBar.java b/chrome/android/java/src/org/chromium/chrome/browser/infobar/AccountChooserInfoBar.java
new file mode 100644
index 0000000..7f3a7fc
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/infobar/AccountChooserInfoBar.java
@@ -0,0 +1,187 @@
+// 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.chrome.browser.infobar;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.Button;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.ListView;
+import android.widget.PopupMenu;
+import android.widget.PopupMenu.OnMenuItemClickListener;
+import android.widget.TextView;
+
+import org.chromium.base.CalledByNative;
+import org.chromium.chrome.R;
+import org.chromium.chrome.browser.ResourceId;
+import org.chromium.chrome.browser.preferences.PreferencesLauncher;
+import org.chromium.chrome.browser.widget.ButtonCompat;
+
+/**
+ * An infobar offers the user the ability to choose credentials for
+ * authentication. User is presented with username along with avatar and
+ * full name in case they are available.
+ */
+public class AccountChooserInfoBar extends InfoBar implements OnMenuItemClickListener {
+    private enum CredentialType {
+        EMPTY(0),
+        LOCAL(1),
+        FEDERATED(2);
+
+        private final int mType;
+        private CredentialType(int type) {
+            mType = type;
+        };
+
+        public int getValue() {
+            return mType;
+        }
+    }
+
+    private final String[] mUsernames;
+
+    /**
+     * Creates and shows the infobar wich allows user to choose credentials for login.
+     * @param nativeInfoBar Pointer to the native infobar.
+     * @param enumeratedIconId Enum ID corresponding to the icon that the infobar will show.
+     * @param usernames Usernames to display in the infobar.
+     */
+    @CalledByNative
+    private static InfoBar show(long nativeInfoBar, int enumeratedIconId, String[] usernames) {
+        return new AccountChooserInfoBar(
+                nativeInfoBar, ResourceId.mapToDrawableId(enumeratedIconId), usernames);
+    }
+
+    /**
+     * Creates and shows the infobar  which allows user to choose credentials.
+     * @param nativeInfoBar Pointer to the native infobar.
+     * @param iconDrawableId Drawable ID corresponding to the icon that the infobar will show.
+     * @param usernames list of usernames to display in infobar.
+     */
+    public AccountChooserInfoBar(long nativeInfoBar, int iconDrawableId, String[] usernames) {
+        super(null /* Infobar Listener */, iconDrawableId, null /* bitmap*/,
+                null /* message to show */);
+        setNativeInfoBar(nativeInfoBar);
+        mUsernames = usernames.clone();
+    }
+
+    @Override
+    public boolean onMenuItemClick(MenuItem item) {
+        if (item.getItemId() == R.id.settings) {
+            PreferencesLauncher.launchSettingsPage(getContext(), null);
+            return true;
+        }
+        // TODO(melandory): Learn more should open link to help center
+        // article which is not ready yet.
+        return false;
+    }
+
+    @Override
+    public void onCloseButtonClicked() {
+        // Notifies the native infobar, which closes the infobar.
+        nativeOnCloseButtonClicked(mNativeInfoBarPtr);
+    }
+
+    @Override
+    public void onButtonClicked(boolean isPrimaryButton) {
+        onCloseButtonClicked();
+    }
+
+    @Override
+    public void createContent(InfoBarLayout layout) {
+        layout.setMessage(getContext().getString(R.string.account_chooser_infobar_title));
+        createAccountsView(layout);
+        createCustomButtonsView(layout);
+    }
+
+    private void createAccountsView(InfoBarLayout layout) {
+        ViewGroup accountsView = (ViewGroup) LayoutInflater.from(getContext()).inflate(
+                R.layout.account_chooser_infobar_list, null, false);
+        ArrayAdapter<String> adapter = generateAccountsArrayAdapter(getContext(), mUsernames);
+        ListView listView = (ListView) accountsView.findViewById(R.id.account_list);
+        listView.setAdapter(adapter);
+        float numVisibleItems = adapter.getCount() > 2 ? 2.5f : adapter.getCount();
+        int listViewHeight = (int) (numVisibleItems * getContext().getResources().getDimension(
+                R.dimen.account_chooser_infobar_item_height));
+        listView.setLayoutParams(new FrameLayout.LayoutParams(
+                FrameLayout.LayoutParams.MATCH_PARENT, listViewHeight));
+        layout.setCustomContent(accountsView);
+    }
+
+    private ArrayAdapter<String> generateAccountsArrayAdapter(Context context, String[] usernames) {
+        return new ArrayAdapter<String>(context, 0, usernames) {
+            @Override
+            public View getView(int position, View convertView, ViewGroup parent) {
+                if (convertView == null) {
+                    convertView = (LinearLayout) LayoutInflater.from(getContext()).inflate(
+                        R.layout.account_chooser_infobar_item, parent, false);
+                }
+                ImageView avatarView = (ImageView) convertView.findViewById(R.id.profile_image);
+                TextView usernameView = (TextView) convertView.findViewById(R.id.username);
+                TextView displayNameView = (TextView) convertView.findViewById(R.id.display_name);
+                String username = getItem(position);
+                usernameView.setText(username);
+                // TODO(melandory): View should show the full name. Temporarily the view shows
+                // username.
+                displayNameView.setText(username);
+                // TODO(melandory): View should show proper avatar. Temporarily the view shows
+                // blue man icon.
+                avatarView.setImageResource(R.drawable.account_management_no_picture);
+                final int currentCredentialIndex = position;
+                convertView.setOnClickListener(new View.OnClickListener() {
+                    @Override
+                    public void onClick(View view) {
+                        passCredentialsToNative(currentCredentialIndex);
+                    }
+                });
+                return convertView;
+            }
+        };
+    }
+
+    /**
+     * Creates button row which consists of "No thanks" button and "More" button.
+     * "No thanks" buttons dismisses infobar. "More" button opens a popup menu,
+     * which allows to go to help center article or Settings.
+     */
+    private void createCustomButtonsView(InfoBarLayout layout) {
+        layout.setButtons(getContext().getString(R.string.no_thanks), null);
+        Button moreButton = ButtonCompat.createBorderlessButton(getContext());
+        moreButton.setText(getContext().getString(R.string.more));
+        // TODO(melandory): Looks like spinner in mocks.
+        moreButton.setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View view) {
+                showMorePopup(view);
+            }
+        });
+        layout.setCustomViewInButtonRow(moreButton);
+    }
+
+    private void passCredentialsToNative(int credentialIndex) {
+        // TODO(melandory): Adding federated login support should change this
+        // code.
+        nativeOnCredentialClicked(
+                mNativeInfoBarPtr, credentialIndex, CredentialType.LOCAL.getValue());
+    }
+
+    /** Pops up menu with two items: Setting and Learn More when user clicks More button. */
+    private void showMorePopup(View v) {
+        PopupMenu popup = new PopupMenu(getContext(), v);
+        popup.setOnMenuItemClickListener(this);
+        popup.inflate(R.menu.account_chooser_infobar_more_menu_popup);
+        popup.show();
+    }
+
+    private native void nativeOnCredentialClicked(
+            long nativeAccountChooserInfoBar, int credentialId, int credentialType);
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/infobar/AppBannerInfoBar.java b/chrome/android/java/src/org/chromium/chrome/browser/infobar/AppBannerInfoBar.java
index 5c57008..2f62227 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/infobar/AppBannerInfoBar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/infobar/AppBannerInfoBar.java
@@ -91,17 +91,25 @@
             ratingView.setRating(mAppData.rating());
             layout.getPrimaryButton().setButtonColor(getContext().getResources().getColor(
                     R.color.app_banner_install_button_bg));
-            layout.setContentDescription(context.getString(
+            mTitleView.setContentDescription(context.getString(
                     R.string.app_banner_view_native_app_accessibility, mAppTitle,
                     mAppData.rating()));
             mTitleView.removeView(webAppUrl);
+            updateButton();
         } else {
             // Web app.
             webAppUrl.setText(mAppUrl);
-            layout.setContentDescription(context.getString(
+            mTitleView.setContentDescription(context.getString(
                     R.string.app_banner_view_web_app_accessibility, mAppTitle,
                     mAppUrl));
             mTitleView.removeView(ratingView);
+
+        }
+
+        // Hide uninteresting views from accessibility.
+        ratingView.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
+        if (mIconView != null) {
+            mIconView.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
         }
 
         // Set up clicking on the controls to bring up the app details.
@@ -127,13 +135,15 @@
     }
 
     private void updateButton() {
+        assert mAppData != null;
+
         String text;
         String accessibilityText = null;
         boolean enabled = true;
         if (mInstallState == INSTALL_STATE_NOT_INSTALLED) {
             text = mAppData.installButtonText();
-            accessibilityText =
-                    getContext().getString(R.string.app_banner_install_accessibility, text);
+            accessibilityText = getContext().getString(
+                    R.string.app_banner_view_native_app_install_accessibility, text);
         } else if (mInstallState == INSTALL_STATE_INSTALLING) {
             text = getContext().getString(R.string.app_banner_installing);
             enabled = false;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/infobar/InfoBarLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/infobar/InfoBarLayout.java
index f4bd007a..bd4eed31 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/infobar/InfoBarLayout.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/infobar/InfoBarLayout.java
@@ -624,6 +624,9 @@
                 measureChildWithFixedWidth(view1, view1.getMeasuredWidth() + extraWidth1);
             }
         }
+        if (row == ROW_OTHER && mCustomGroup.views.length == 1) {
+            mCustomGroup.gravity = Gravity.FILL_HORIZONTAL;
+        }
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/infobar/TranslateInfoBar.java b/chrome/android/java/src/org/chromium/chrome/browser/infobar/TranslateInfoBar.java
index cd2fcb5a..bbfa182 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/infobar/TranslateInfoBar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/infobar/TranslateInfoBar.java
@@ -120,8 +120,7 @@
                 String translated = context.getString(
                         R.string.translate_infobar_translation_done, mOptions.targetLanguage());
                 if (needsAlwaysPanel()) {
-                    String moreOptions = context.getString(
-                            R.string.translate_infobar_translation_more_options);
+                    String moreOptions = context.getString(R.string.more);
                     return formatAfterTranslateInfoBarMessage(translated, moreOptions,
                             ALWAYS_PANEL);
                 } else {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationUIManager.java b/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationUIManager.java
index 7e0ed32..261023e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationUIManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationUIManager.java
@@ -12,7 +12,6 @@
 import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.Color;
-import android.os.Bundle;
 import android.support.v4.app.NotificationCompat;
 import android.util.Log;
 
@@ -20,7 +19,7 @@
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.preferences.Preferences;
 import org.chromium.chrome.browser.preferences.PreferencesLauncher;
-import org.chromium.chrome.browser.preferences.website.WebsitePreferences;
+import org.chromium.chrome.browser.preferences.website.SingleWebsitePreferences;
 import org.chromium.chrome.browser.widget.RoundedIconGenerator;
 
 /**
@@ -32,9 +31,6 @@
 public class NotificationUIManager {
     private static final String TAG = NotificationUIManager.class.getSimpleName();
 
-    // Category in the preferences for displaying Push Notification permissions.
-    private static final String CATEGORY_PUSH_NOTIFICATIONS = "push_notifications";
-
     private static final int NOTIFICATION_ICON_BG_COLOR = Color.rgb(150, 150, 150);
     private static final int NOTIFICATION_TEXT_SIZE_DP = 28;
 
@@ -159,19 +155,11 @@
 
         Resources res = mAppContext.getResources();
 
-        // TODO(peter): The current implementation introduces a [Site settings] button for opening
-        // the "Notifications" panel in the site settings section, rather than the settings of an
-        // individual website, which it should do instead.
-
-        Intent settingsIntent = PreferencesLauncher.createIntentForSettingsPage(mAppContext,
-                WebsitePreferences.class.getName());
-
-        Bundle arguments = new Bundle();
-        arguments.putString(WebsitePreferences.EXTRA_CATEGORY, CATEGORY_PUSH_NOTIFICATIONS);
-        arguments.putString(WebsitePreferences.EXTRA_TITLE,
-                res.getString(R.string.push_notifications_permission_title));
-
-        settingsIntent.putExtra(Preferences.EXTRA_SHOW_FRAGMENT_ARGUMENTS, arguments);
+        // Set up a pending intent for going to the settings screen for |origin|.
+        Intent settingsIntent = PreferencesLauncher.createIntentForSettingsPage(
+                mAppContext, SingleWebsitePreferences.class.getName());
+        settingsIntent.putExtra(Preferences.EXTRA_SHOW_FRAGMENT_ARGUMENTS,
+                SingleWebsitePreferences.createFragmentArgsForSite(origin));
         PendingIntent pendingSettingsIntent = PendingIntent.getActivity(
                 mAppContext, 0, settingsIntent, PendingIntent.FLAG_UPDATE_CURRENT);
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/Preferences.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/Preferences.java
index 677f5fa1..27ef67d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/Preferences.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/Preferences.java
@@ -70,6 +70,8 @@
      * Opens a URL in a new activity.
      * @param titleResId The resource ID of the title to show above the web page.
      * @param urlResId The resource ID of the URL to load.
+     *
+     * TODO(newt): remove this method when EmbedContentViewActivity is upstreamed.
      */
     public abstract void showUrl(int titleResId, int urlResId);
 
@@ -78,6 +80,35 @@
      */
     public void showGoogleTranslateHelp() {}
 
+    /**
+     * Launches the help page for privacy settings.
+     */
+    public void showPrivacyPreferencesHelp() {}
+
+    /**
+     * Called when user changes the contextual search preference.
+     * @param newValue Whether contextual search is now enabled.
+     *
+     * TODO(newt): remove this method when contextual search is upstreamed.
+     */
+    public void logContextualSearchToggled(boolean newValue) {}
+
+    /**
+     * Returns whether contextual search is enabled.
+     *
+     * TODO(newt): remove this method when contextual search is upstreamed.
+     */
+    public boolean isContextualSearchEnabled() {
+        return false;
+    }
+
+    /**
+     * Notifies the precache launcher that the user has changed the network prediction preference.
+     *
+     * TODO(newt): remove this method when precache logic is upstreamed.
+     */
+    public void updatePrecachingEnabled() {}
+
     @SuppressFBWarnings("DM_EXIT")
     @SuppressLint("InlinedApi")
     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/privacy/ClearBrowsingDataDialogFragment.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/privacy/ClearBrowsingDataDialogFragment.java
new file mode 100644
index 0000000..fdaac2f6
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/privacy/ClearBrowsingDataDialogFragment.java
@@ -0,0 +1,225 @@
+// 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.chrome.browser.preferences.privacy;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.app.ProgressDialog;
+import android.content.DialogInterface;
+import android.content.res.Resources;
+import android.os.Bundle;
+import android.text.SpannableString;
+import android.text.TextPaint;
+import android.text.method.LinkMovementMethod;
+import android.text.style.ClickableSpan;
+import android.view.View;
+import android.widget.Button;
+import android.widget.TextView;
+
+import org.chromium.chrome.R;
+import org.chromium.chrome.browser.preferences.PrefServiceBridge;
+import org.chromium.chrome.browser.preferences.Preferences;
+import org.chromium.chrome.browser.signin.AccountManagementFragment;
+import org.chromium.sync.signin.ChromeSigninController;
+import org.chromium.ui.text.SpanApplier;
+
+import java.util.Arrays;
+import java.util.EnumSet;
+
+/**
+ * Modal dialog with options for selection the type of browsing data
+ * to clear (history, cookies), triggered from a preference.
+ */
+public class ClearBrowsingDataDialogFragment extends DialogFragment implements
+        PrefServiceBridge.OnClearBrowsingDataListener,
+        DialogInterface.OnMultiChoiceClickListener, DialogInterface.OnClickListener {
+
+    /** The tag used when showing the clear browsing fragment. */
+    public static final String FRAGMENT_TAG = "ClearBrowsingDataDialogFragment";
+
+    /**
+     * Enum for Dialog options to be displayed in the dialog.
+     */
+    public enum DialogOption {
+        CLEAR_HISTORY(R.string.clear_history_title),
+        CLEAR_CACHE(R.string.clear_cache_title),
+        CLEAR_COOKIES_AND_SITE_DATA(R.string.clear_cookies_and_site_data_title),
+        CLEAR_PASSWORDS(R.string.clear_passwords_title),
+        CLEAR_FORM_DATA(R.string.clear_formdata_title),
+        // Clear bookmarks is only used by ClearSyncData dialog.
+        CLEAR_BOOKMARKS_DATA(R.string.clear_bookmarks_title);
+
+        private final int mResourceId;
+
+        private DialogOption(int resourceId) {
+            mResourceId = resourceId;
+        }
+
+        /**
+         * @return resource id of the Dialog option.
+         */
+        public int getResourceId() {
+            return mResourceId;
+        }
+    }
+
+    private EnumSet<DialogOption> mSelectedOptions;
+    private DialogOption[] mOptions;
+    private AlertDialog mDialog;
+    private ProgressDialog mProgressDialog;
+
+    protected final void clearBrowsingData(EnumSet<DialogOption> selectedOptions) {
+        PrefServiceBridge.getInstance().clearBrowsingData(this,
+                selectedOptions.contains(DialogOption.CLEAR_HISTORY),
+                selectedOptions.contains(DialogOption.CLEAR_CACHE),
+                selectedOptions.contains(DialogOption.CLEAR_COOKIES_AND_SITE_DATA),
+                selectedOptions.contains(DialogOption.CLEAR_PASSWORDS),
+                selectedOptions.contains(DialogOption.CLEAR_FORM_DATA));
+    }
+
+    protected void dismissProgressDialog() {
+        android.util.Log.i(FRAGMENT_TAG, "in dismissProgressDialog");
+        if (mProgressDialog != null && mProgressDialog.isShowing()) {
+            android.util.Log.i(FRAGMENT_TAG, "progress dialog dismissed");
+            mProgressDialog.dismiss();
+        }
+        mProgressDialog = null;
+    }
+
+    /**
+     * Returns the Array of dialog options. Options are displayed in the same
+     * order as they appear in the array.
+     */
+    protected DialogOption[] getDialogOptions() {
+        return new DialogOption[] {
+            DialogOption.CLEAR_HISTORY,
+            DialogOption.CLEAR_CACHE,
+            DialogOption.CLEAR_COOKIES_AND_SITE_DATA,
+            DialogOption.CLEAR_PASSWORDS,
+            DialogOption.CLEAR_FORM_DATA};
+    }
+
+    /**
+     * Get the default selections for the dialog.
+     * @return EnumSet containing dialog options to be selected.
+     */
+    protected EnumSet<DialogOption> getDefaultDialogOptionsSelections() {
+        return EnumSet.of(DialogOption.CLEAR_HISTORY, DialogOption.CLEAR_CACHE,
+                DialogOption.CLEAR_COOKIES_AND_SITE_DATA);
+    }
+
+    // Called when "clear browsing data" completes.
+    // Implements the ChromePreferences.OnClearBrowsingDataListener interface.
+    @Override
+    public void onBrowsingDataCleared() {
+        dismissProgressDialog();
+    }
+
+    @Override
+    public void onClick(DialogInterface dialog, int which) {
+        if (which == AlertDialog.BUTTON_POSITIVE) {
+            dismissProgressDialog();
+            onOptionSelected(mSelectedOptions);
+        }
+    }
+
+    /**
+     * Disable the "Clear" button if none of the options are selected. Otherwise, enable it.
+     */
+    private void updateButtonState() {
+        Button clearButton = mDialog.getButton(AlertDialog.BUTTON_POSITIVE);
+        if (clearButton != null) clearButton.setEnabled(!mSelectedOptions.isEmpty());
+    }
+
+    @Override
+    public void onClick(DialogInterface dialog, int whichButton, boolean isChecked) {
+        if (isChecked) {
+            mSelectedOptions.add(mOptions[whichButton]);
+        } else {
+            mSelectedOptions.remove(mOptions[whichButton]);
+        }
+        updateButtonState();
+    }
+
+    @Override
+    public Dialog onCreateDialog(Bundle savedInstanceState) {
+        DialogOption[] options = getDialogOptions();
+        mOptions = Arrays.copyOf(options, options.length);
+        mSelectedOptions = getDefaultDialogOptionsSelections();
+
+        String[] items = new String[mOptions.length];
+        boolean[] itemsChecked = new boolean[mOptions.length];
+        Resources resources = getResources();
+        for (int i = 0; i < mOptions.length; i++) {
+            items[i] = resources.getString(mOptions[i].getResourceId());
+            itemsChecked[i] = mSelectedOptions.contains(mOptions[i]);
+        }
+
+        final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity())
+                .setTitle(R.string.clear_browsing_data_title)
+                .setPositiveButton(R.string.clear_data_delete, this)
+                .setNegativeButton(R.string.cancel, this)
+                .setMultiChoiceItems(items, itemsChecked, this);
+
+        if (ChromeSigninController.get(getActivity()).isSignedIn()) {
+            final String message = getString(R.string.clear_cookies_no_sign_out_summary);
+            final SpannableString messageWithLink = SpanApplier.applySpans(message,
+                    new SpanApplier.SpanInfo("<link>", "</link>", new ClickableSpan() {
+                        @Override
+                        public void onClick(View widget) {
+                            dismiss();
+                            Preferences prefActivity = (Preferences) getActivity();
+                            prefActivity.startFragment(AccountManagementFragment.class.getName(),
+                                    null);
+                        }
+
+                        // Change link formatting to use no underline
+                        @Override
+                        public void updateDrawState(TextPaint textPaint) {
+                            textPaint.setColor(textPaint.linkColor);
+                            textPaint.setUnderlineText(false);
+                        }
+                    }));
+
+            View view = getActivity().getLayoutInflater().inflate(
+                    R.layout.single_line_bottom_text_dialog, null);
+            TextView summaryView = (TextView) view.findViewById(R.id.summary);
+            summaryView.setText(messageWithLink);
+            summaryView.setMovementMethod(LinkMovementMethod.getInstance());
+            builder.setView(view);
+        }
+
+        mDialog = builder.create();
+        return mDialog;
+    }
+
+    @Override
+    public void onActivityCreated(Bundle savedInstanceState) {
+        super.onActivityCreated(savedInstanceState);
+        // Now that the dialog's view has been created, update the button state.
+        updateButtonState();
+    }
+
+    /**
+     * Called when PositiveButton is clicked for the dialog.
+     *
+     * @param selectedOptions options which were selected.
+     */
+    protected void onOptionSelected(final EnumSet<DialogOption> selectedOptions) {
+        showProgressDialog();
+        clearBrowsingData(selectedOptions);
+    }
+
+    protected final void showProgressDialog() {
+        if (getActivity() == null) return;
+
+        android.util.Log.i(FRAGMENT_TAG, "progress dialog shown");
+        mProgressDialog = ProgressDialog.show(getActivity(),
+                getActivity().getString(R.string.clear_browsing_data_progress_title),
+                getActivity().getString(R.string.clear_browsing_data_progress_message), true,
+                false);
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/privacy/ContextualSearchPreferenceFragment.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/privacy/ContextualSearchPreferenceFragment.java
new file mode 100644
index 0000000..30ce396
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/privacy/ContextualSearchPreferenceFragment.java
@@ -0,0 +1,78 @@
+// 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.chrome.browser.preferences.privacy;
+
+import android.os.Bundle;
+import android.preference.Preference;
+import android.preference.Preference.OnPreferenceChangeListener;
+import android.preference.PreferenceFragment;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+
+import org.chromium.chrome.R;
+import org.chromium.chrome.browser.preferences.ChromeSwitchPreference;
+import org.chromium.chrome.browser.preferences.ManagedPreferenceDelegate;
+import org.chromium.chrome.browser.preferences.PrefServiceBridge;
+import org.chromium.chrome.browser.preferences.Preferences;
+
+/**
+ * Fragment to manage the Contextual Search preference and to explain to the user what it does.
+ */
+public class ContextualSearchPreferenceFragment extends PreferenceFragment {
+
+    private static final String PREF_CONTEXTUAL_SEARCH_SWITCH = "contextual_search_switch";
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        addPreferencesFromResource(R.xml.contextual_search_preferences);
+        getActivity().setTitle(R.string.contextual_search_title);
+        setHasOptionsMenu(true);
+        initContextualSearchSwitch();
+    }
+
+    @Override
+    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+        menu.clear();
+        menu.add(Menu.NONE, R.id.menu_id_contextual_search_learn, Menu.NONE, R.string.learn_more);
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        if (item.getItemId() != R.id.menu_id_contextual_search_learn) {
+            return false;
+        }
+
+        ((Preferences) getActivity()).showUrl(R.string.learn_more,
+                R.string.contextual_search_learn_more_url);
+        return true;
+    }
+
+    private void initContextualSearchSwitch() {
+        ChromeSwitchPreference contextualSearchSwitch =
+                (ChromeSwitchPreference) findPreference(PREF_CONTEXTUAL_SEARCH_SWITCH);
+
+        boolean isContextualSearchEnabled =
+                !PrefServiceBridge.getInstance().isContextualSearchDisabled();
+        contextualSearchSwitch.setChecked(isContextualSearchEnabled);
+
+        contextualSearchSwitch.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
+            @Override
+            public boolean onPreferenceChange(Preference preference, Object newValue) {
+                PrefServiceBridge.getInstance().setContextualSearchState((boolean) newValue);
+                ((Preferences) getActivity()).logContextualSearchToggled((boolean) newValue);
+                return true;
+            }
+        });
+        contextualSearchSwitch.setManagedPreferenceDelegate(new ManagedPreferenceDelegate() {
+            @Override
+            public boolean isPreferenceControlledByPolicy(Preference preference) {
+                return PrefServiceBridge.getInstance().isContextualSearchDisabledByPolicy();
+            }
+        });
+    }
+
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/privacy/CrashDumpUploadPreference.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/privacy/CrashDumpUploadPreference.java
new file mode 100644
index 0000000..1939526
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/privacy/CrashDumpUploadPreference.java
@@ -0,0 +1,54 @@
+// 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.chrome.browser.preferences.privacy;
+
+import android.content.Context;
+import android.preference.Preference;
+import android.preference.Preference.OnPreferenceChangeListener;
+import android.util.AttributeSet;
+
+import org.chromium.chrome.R;
+import org.chromium.chrome.browser.preferences.ChromeBaseListPreference;
+
+/**
+ * Crash upload bandwidth preference.
+ */
+public class CrashDumpUploadPreference extends ChromeBaseListPreference
+        implements OnPreferenceChangeListener {
+
+    private Context mContext;
+
+    public CrashDumpUploadPreference(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        mContext = context;
+        setOnPreferenceChangeListener(this);
+        String currentCrashPreference =
+                PrivacyPreferencesManager.getInstance(context).getPrefCrashDumpUploadPreference();
+        setSummary(getSummaryText(currentCrashPreference));
+    }
+
+    @Override
+    public boolean onPreferenceChange(Preference preference, Object newValue) {
+        setSummary(getSummaryText((String) newValue));
+        return true;
+    }
+
+    /**
+     * Text to display in the summary of the preference.
+     * @param currentCrashPreference current value OR selected value if exists.
+     * @return resource of the text.
+     */
+    public int getSummaryText(String currentCrashPreference) {
+        if (currentCrashPreference.equals(mContext.getString(
+                R.string.crash_dump_always_upload_value))) {
+            return R.string.crash_dump_always_upload;
+        } else if (currentCrashPreference.equals(mContext.getString(
+                R.string.crash_dump_only_with_wifi_value))) {
+            return R.string.crash_dump_only_with_wifi;
+        } else {
+            return R.string.crash_dump_never_upload;
+        }
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/privacy/DoNotTrackPreference.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/privacy/DoNotTrackPreference.java
new file mode 100644
index 0000000..f75620af
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/privacy/DoNotTrackPreference.java
@@ -0,0 +1,43 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.preferences.privacy;
+
+import android.os.Bundle;
+import android.preference.Preference;
+import android.preference.Preference.OnPreferenceChangeListener;
+import android.preference.PreferenceFragment;
+
+import org.chromium.chrome.R;
+import org.chromium.chrome.browser.preferences.ChromeSwitchPreference;
+import org.chromium.chrome.browser.preferences.PrefServiceBridge;
+
+/**
+ * Fragment to manage 'Do Not Track' preference and to explain to the user what it does.
+ */
+public class DoNotTrackPreference extends PreferenceFragment {
+
+    private static final String PREF_DO_NOT_TRACK_SWITCH = "do_not_track_switch";
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        addPreferencesFromResource(R.xml.do_not_track_preferences);
+        getActivity().setTitle(R.string.do_not_track_title);
+
+        ChromeSwitchPreference doNotTrackSwitch =
+                (ChromeSwitchPreference) findPreference(PREF_DO_NOT_TRACK_SWITCH);
+
+        boolean isDoNotTrackEnabled = PrefServiceBridge.getInstance().isDoNotTrackEnabled();
+        doNotTrackSwitch.setChecked(isDoNotTrackEnabled);
+
+        doNotTrackSwitch.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
+            @Override
+            public boolean onPreferenceChange(Preference preference, Object newValue) {
+                PrefServiceBridge.getInstance().setDoNotTrackEnabled((boolean) newValue);
+                return true;
+            }
+        });
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/privacy/NetworkPredictionPreference.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/privacy/NetworkPredictionPreference.java
new file mode 100644
index 0000000..a038a57
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/privacy/NetworkPredictionPreference.java
@@ -0,0 +1,42 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.preferences.privacy;
+
+import android.content.Context;
+import android.preference.Preference;
+import android.preference.Preference.OnPreferenceChangeListener;
+import android.util.AttributeSet;
+
+import org.chromium.chrome.R;
+import org.chromium.chrome.browser.preferences.ChromeBaseListPreference;
+import org.chromium.chrome.browser.preferences.NetworkPredictionOptions;
+import org.chromium.chrome.browser.preferences.PrefServiceBridge;
+
+/**
+ * Fragment to set and retrieve the prerender preference.
+ */
+public class NetworkPredictionPreference extends ChromeBaseListPreference
+        implements OnPreferenceChangeListener {
+
+    private final Context mContext;
+
+    public NetworkPredictionPreference(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        mContext = context;
+        String[] networkPredictionPrefs = context.getResources().getStringArray(
+                R.array.bandwidth_entries);
+        assert networkPredictionPrefs.length == NetworkPredictionOptions.choiceCount();
+        setOnPreferenceChangeListener(this);
+        setSummary(context.getString(
+                PrefServiceBridge.getInstance().getNetworkPredictionOptions().getDisplayTitle()));
+    }
+
+    @Override
+    public boolean onPreferenceChange(Preference preference, Object newValue) {
+        setSummary(mContext.getString(
+                NetworkPredictionOptions.stringToEnum((String) newValue).getDisplayTitle()));
+        return true;
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/privacy/PrivacyPreferences.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/privacy/PrivacyPreferences.java
new file mode 100644
index 0000000..efd8ebe
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/privacy/PrivacyPreferences.java
@@ -0,0 +1,265 @@
+// 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.chrome.browser.preferences.privacy;
+
+import android.os.Bundle;
+import android.preference.CheckBoxPreference;
+import android.preference.Preference;
+import android.preference.Preference.OnPreferenceChangeListener;
+import android.preference.Preference.OnPreferenceClickListener;
+import android.preference.PreferenceFragment;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+
+import org.chromium.chrome.R;
+import org.chromium.chrome.browser.preferences.ButtonPreference;
+import org.chromium.chrome.browser.preferences.ChromeBaseCheckBoxPreference;
+import org.chromium.chrome.browser.preferences.ManagedPreferenceDelegate;
+import org.chromium.chrome.browser.preferences.NetworkPredictionOptions;
+import org.chromium.chrome.browser.preferences.PrefServiceBridge;
+import org.chromium.chrome.browser.preferences.Preferences;
+
+/**
+ * Fragment to keep track of the all the privacy related preferences.
+ */
+public class PrivacyPreferences extends PreferenceFragment
+        implements OnPreferenceChangeListener {
+
+    /**
+     * Set to true in the {@link Preferences#EXTRA_SHOW_FRAGMENT_ARGUMENTS} bundle to
+     * trigger the clear browsing data dialog when showing the privacy preferences.
+     */
+    public static final String SHOW_CLEAR_BROWSING_DATA_EXTRA =
+            "ShowClearBrowsingData";
+
+    private static final String PREF_NAVIGATION_ERROR = "navigation_error";
+    private static final String PREF_SEARCH_SUGGESTIONS = "search_suggestions";
+    private static final String PREF_CONTEXTUAL_SEARCH = "contextual_search";
+    private static final String PREF_NETWORK_PREDICTIONS = "network_predictions";
+    private static final String PREF_NETWORK_PREDICTIONS_NO_CELLULAR =
+            "network_predictions_no_cellular";
+    private static final String PREF_CRASH_DUMP_UPLOAD = "crash_dump_upload";
+    private static final String PREF_CRASH_DUMP_UPLOAD_NO_CELLULAR =
+            "crash_dump_upload_no_cellular";
+    private static final String PREF_DO_NOT_TRACK = "do_not_track";
+    private static final String PREF_CLEAR_BROWSING_DATA = "clear_browsing_data";
+
+    private ClearBrowsingDataDialogFragment mClearBrowsingDataDialogFragment;
+    private ManagedPreferenceDelegate mManagedPreferenceDelegate;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        PrivacyPreferencesManager.getInstance(getActivity()).migrateNetworkPredictionPreferences();
+        addPreferencesFromResource(R.xml.privacy_preferences);
+        getActivity().setTitle(R.string.prefs_privacy);
+        setHasOptionsMenu(true);
+
+        mManagedPreferenceDelegate = createManagedPreferenceDelegate();
+
+        NetworkPredictionPreference networkPredictionPref =
+                (NetworkPredictionPreference) findPreference(PREF_NETWORK_PREDICTIONS);
+        ChromeBaseCheckBoxPreference networkPredictionNoCellularPref =
+                (ChromeBaseCheckBoxPreference) findPreference(PREF_NETWORK_PREDICTIONS_NO_CELLULAR);
+        NetworkPredictionOptions networkPredictionOptions = PrefServiceBridge.getInstance()
+                .getNetworkPredictionOptions();
+
+        boolean isMobileNetworkCapable =
+                PrivacyPreferencesManager.getInstance(getActivity()).isMobileNetworkCapable();
+        if (isMobileNetworkCapable) {
+            getPreferenceScreen().removePreference(networkPredictionNoCellularPref);
+            networkPredictionPref.setValue(networkPredictionOptions.enumToString());
+            networkPredictionPref.setOnPreferenceChangeListener(this);
+            networkPredictionPref.setManagedPreferenceDelegate(mManagedPreferenceDelegate);
+        } else {
+            getPreferenceScreen().removePreference(networkPredictionPref);
+            networkPredictionNoCellularPref.setChecked(
+                    networkPredictionOptions != NetworkPredictionOptions.NETWORK_PREDICTION_NEVER);
+            networkPredictionNoCellularPref.setOnPreferenceChangeListener(this);
+            networkPredictionNoCellularPref.setManagedPreferenceDelegate(
+                    mManagedPreferenceDelegate);
+        }
+
+        CrashDumpUploadPreference uploadCrashDumpPref =
+                (CrashDumpUploadPreference) findPreference(PREF_CRASH_DUMP_UPLOAD);
+        ChromeBaseCheckBoxPreference uploadCrashDumpNoCellularPref =
+                (ChromeBaseCheckBoxPreference) findPreference(PREF_CRASH_DUMP_UPLOAD_NO_CELLULAR);
+
+        if (isMobileNetworkCapable) {
+            getPreferenceScreen().removePreference(uploadCrashDumpNoCellularPref);
+            uploadCrashDumpPref.setOnPreferenceChangeListener(this);
+            uploadCrashDumpPref.setManagedPreferenceDelegate(mManagedPreferenceDelegate);
+        } else {
+            getPreferenceScreen().removePreference(uploadCrashDumpPref);
+            uploadCrashDumpNoCellularPref.setOnPreferenceChangeListener(this);
+            uploadCrashDumpNoCellularPref.setManagedPreferenceDelegate(mManagedPreferenceDelegate);
+        }
+
+        ChromeBaseCheckBoxPreference navigationErrorPref =
+                (ChromeBaseCheckBoxPreference) findPreference(PREF_NAVIGATION_ERROR);
+        navigationErrorPref.setOnPreferenceChangeListener(this);
+        navigationErrorPref.setManagedPreferenceDelegate(mManagedPreferenceDelegate);
+
+        ChromeBaseCheckBoxPreference searchSuggestionsPref =
+                (ChromeBaseCheckBoxPreference) findPreference(PREF_SEARCH_SUGGESTIONS);
+        searchSuggestionsPref.setOnPreferenceChangeListener(this);
+        searchSuggestionsPref.setManagedPreferenceDelegate(mManagedPreferenceDelegate);
+
+        if (!((Preferences) getActivity()).isContextualSearchEnabled()) {
+            getPreferenceScreen().removePreference(findPreference(PREF_CONTEXTUAL_SEARCH));
+        }
+
+        ButtonPreference clearBrowsingData =
+                (ButtonPreference) findPreference(PREF_CLEAR_BROWSING_DATA);
+        clearBrowsingData.setOnPreferenceClickListener(new OnPreferenceClickListener() {
+            @Override
+            public boolean onPreferenceClick(Preference preference) {
+                showClearBrowsingDialog();
+                return true;
+            }
+        });
+
+        if (getArguments() != null) {
+            boolean showClearBrowsingData =
+                    getArguments().getBoolean(SHOW_CLEAR_BROWSING_DATA_EXTRA, false);
+            if (showClearBrowsingData) showClearBrowsingDialog();
+        }
+        updateSummaries();
+    }
+
+    @Override
+    public boolean onPreferenceChange(Preference preference, Object newValue) {
+        // CrashDumpUploadPreference listens to its own PreferenceChanged to update its text.
+        // We have replaced the listener. If we do run into a CrashDumpUploadPreference change,
+        // we will call onPreferenceChange to change the displayed text.
+        if (preference instanceof CrashDumpUploadPreference) {
+            ((CrashDumpUploadPreference) preference).onPreferenceChange(preference, newValue);
+        }
+
+        // NetworkPredictionPreference listens to its own PreferenceChanged to update its text.
+        // We have replaced the listener. If we do run into a NetworkPredictionPreference change,
+        // we will call onPreferenceChange to change the displayed text.
+        if (preference instanceof NetworkPredictionPreference) {
+            ((NetworkPredictionPreference) preference).onPreferenceChange(preference, newValue);
+        }
+
+        String key = preference.getKey();
+        if (PREF_SEARCH_SUGGESTIONS.equals(key)) {
+            PrefServiceBridge.getInstance().setSearchSuggestEnabled((boolean) newValue);
+        } else if (PREF_NETWORK_PREDICTIONS.equals(key)) {
+            PrefServiceBridge.getInstance().setNetworkPredictionOptions(
+                    NetworkPredictionOptions.stringToEnum((String) newValue));
+            ((Preferences) getActivity()).updatePrecachingEnabled();
+        } else if (PREF_NETWORK_PREDICTIONS_NO_CELLULAR.equals(key)) {
+            PrefServiceBridge.getInstance().setNetworkPredictionOptions((boolean) newValue
+                    ? NetworkPredictionOptions.NETWORK_PREDICTION_ALWAYS
+                    : NetworkPredictionOptions.NETWORK_PREDICTION_NEVER);
+            ((Preferences) getActivity()).updatePrecachingEnabled();
+        } else if (PREF_NAVIGATION_ERROR.equals(key)) {
+            PrefServiceBridge.getInstance().setResolveNavigationErrorEnabled((boolean) newValue);
+        } else if (PREF_CRASH_DUMP_UPLOAD_NO_CELLULAR.equals(key)) {
+            PrefServiceBridge.getInstance().setCrashReporting((boolean) newValue);
+        } else if (PREF_CRASH_DUMP_UPLOAD.equals(key)) {
+            PrivacyPreferencesManager.getInstance(getActivity()).setUploadCrashDump(
+                    (String) newValue);
+        }
+
+        return true;
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        updateSummaries();
+    }
+
+    /**
+     * Updates the summaries for several preferences.
+     */
+    public void updateSummaries() {
+        PrefServiceBridge prefServiceBridge = PrefServiceBridge.getInstance();
+
+        CheckBoxPreference navigationErrorPref = (CheckBoxPreference) findPreference(
+                PREF_NAVIGATION_ERROR);
+        navigationErrorPref.setChecked(prefServiceBridge.isResolveNavigationErrorEnabled());
+
+        CheckBoxPreference searchSuggestionsPref = (CheckBoxPreference) findPreference(
+                PREF_SEARCH_SUGGESTIONS);
+        searchSuggestionsPref.setChecked(prefServiceBridge.isSearchSuggestEnabled());
+
+        Preference doNotTrackPref = findPreference(PREF_DO_NOT_TRACK);
+        if (prefServiceBridge.isDoNotTrackEnabled()) {
+            doNotTrackPref.setSummary(getActivity().getResources().getText(R.string.text_on));
+        } else {
+            doNotTrackPref.setSummary(getActivity().getResources().getText(R.string.text_off));
+        }
+        Preference contextualPref = findPreference(PREF_CONTEXTUAL_SEARCH);
+        if (contextualPref != null) {
+            if (prefServiceBridge.isContextualSearchDisabled()) {
+                contextualPref.setSummary(getActivity().getResources().getText(R.string.text_off));
+            } else {
+                contextualPref.setSummary(getActivity().getResources().getText(R.string.text_on));
+            }
+        }
+    }
+
+    private ManagedPreferenceDelegate createManagedPreferenceDelegate() {
+        return new ManagedPreferenceDelegate() {
+            @Override
+            public boolean isPreferenceControlledByPolicy(Preference preference) {
+                String key = preference.getKey();
+                PrefServiceBridge prefs = PrefServiceBridge.getInstance();
+                if (PREF_NAVIGATION_ERROR.equals(key)) {
+                    return prefs.isResolveNavigationErrorManaged();
+                }
+                if (PREF_SEARCH_SUGGESTIONS.equals(key)) {
+                    return prefs.isSearchSuggestManaged();
+                }
+                if (PREF_NETWORK_PREDICTIONS_NO_CELLULAR.equals(key)
+                        || PREF_NETWORK_PREDICTIONS.equals(key)) {
+                    return prefs.isNetworkPredictionManaged();
+                }
+                if (PREF_CRASH_DUMP_UPLOAD.equals(key)
+                        || PREF_CRASH_DUMP_UPLOAD_NO_CELLULAR.equals(key)) {
+                    return prefs.isCrashReportManaged();
+                }
+                return false;
+            }
+        };
+    }
+
+    private void showClearBrowsingDialog() {
+        mClearBrowsingDataDialogFragment = new ClearBrowsingDataDialogFragment();
+        mClearBrowsingDataDialogFragment.show(
+                getFragmentManager(), ClearBrowsingDataDialogFragment.FRAGMENT_TAG);
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        if (mClearBrowsingDataDialogFragment != null) {
+            // In case the progress dialog is still showing and waiting for a callback, dismiss it.
+            // See bug http://b/13396757.
+            mClearBrowsingDataDialogFragment.dismissProgressDialog();
+        }
+        mClearBrowsingDataDialogFragment = null;
+    }
+
+    @Override
+    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+        menu.clear();
+        menu.add(Menu.NONE, R.id.menu_id_help_privacy, Menu.NONE, R.string.menu_help);
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        if (item.getItemId() == R.id.menu_id_help_privacy) {
+            ((Preferences) getActivity()).showPrivacyPreferencesHelp();
+            return true;
+        }
+        return false;
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/privacy/PrivacyPreferencesManager.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/privacy/PrivacyPreferencesManager.java
new file mode 100644
index 0000000..e42e855
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/privacy/PrivacyPreferencesManager.java
@@ -0,0 +1,291 @@
+// 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.chrome.browser.preferences.privacy;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.preference.PreferenceManager;
+
+import org.chromium.base.CommandLine;
+import org.chromium.base.VisibleForTesting;
+import org.chromium.chrome.ChromeSwitches;
+import org.chromium.chrome.R;
+import org.chromium.chrome.browser.device.DeviceClassManager;
+import org.chromium.chrome.browser.preferences.NetworkPredictionOptions;
+import org.chromium.chrome.browser.preferences.PrefServiceBridge;
+import org.chromium.chrome.browser.preferences.bandwidth.BandwidthType;
+
+/**
+ * Reads, writes, and migrates preferences related to network usage and privacy.
+ */
+public class PrivacyPreferencesManager {
+
+    static final String PREF_CRASH_DUMP_UPLOAD = "crash_dump_upload";
+    static final String PREF_CRASH_DUMP_UPLOAD_NO_CELLULAR = "crash_dump_upload_no_cellular";
+    private static final String PREF_NETWORK_PREDICTIONS = "network_predictions";
+    private static final String PREF_BANDWIDTH_OLD = "prefetch_bandwidth";
+    private static final String PREF_BANDWIDTH_NO_CELLULAR_OLD = "prefetch_bandwidth_no_cellular";
+    private static final String ALLOW_PRERENDER_OLD = "allow_prefetch";
+
+    private static PrivacyPreferencesManager sInstance;
+
+    private final Context mContext;
+    private final SharedPreferences mSharedPreferences;
+
+    private boolean mCrashUploadingEnabled;
+    private final String mCrashDumpNeverUpload;
+    private final String mCrashDumpWifiOnlyUpload;
+    private final String mCrashDumpAlwaysUpload;
+
+    @VisibleForTesting
+    PrivacyPreferencesManager(Context context) {
+        mContext = context;
+        mSharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
+        mCrashUploadingEnabled = true;
+        mCrashDumpNeverUpload = context.getString(R.string.crash_dump_never_upload_value);
+        mCrashDumpWifiOnlyUpload = context.getString(R.string.crash_dump_only_with_wifi_value);
+        mCrashDumpAlwaysUpload = context.getString(R.string.crash_dump_always_upload_value);
+    }
+
+    public static PrivacyPreferencesManager getInstance(Context context) {
+        if (sInstance == null) {
+            sInstance = new PrivacyPreferencesManager(context);
+        }
+        return sInstance;
+    }
+
+    /**
+     * Returns the Crash Dump Upload preference value.
+     * @return String value of the preference.
+     */
+    public String getPrefCrashDumpUploadPreference() {
+        return mSharedPreferences.getString(PREF_CRASH_DUMP_UPLOAD,
+                mCrashDumpNeverUpload);
+    }
+
+    /**
+     * Migrate and delete old preferences.  Note that migration has to happen in Android-specific
+     * code because we need to access ALLOW_PRERENDER sharedPreference.
+     * TODO(bnc) https://crbug.com/394845. This change is planned for M38. After a year or so, it
+     * would be worth considering removing this migration code (also removing accessors in
+     * PrefServiceBridge and pref_service_bridge), and reverting to default for users
+     * who had set preferences but have not used Chrome for a year. This change would be subject to
+     * privacy review.
+     */
+    public void migrateNetworkPredictionPreferences() {
+        PrefServiceBridge prefService = PrefServiceBridge.getInstance();
+
+        // See if PREF_NETWORK_PREDICTIONS is an old boolean value.
+        boolean predictionOptionIsBoolean = false;
+        try {
+            mSharedPreferences.getString(PREF_NETWORK_PREDICTIONS, "");
+        } catch (ClassCastException ex) {
+            predictionOptionIsBoolean = true;
+        }
+
+        // Nothing to do if the user or this migration code has already set the new
+        // preference.
+        if (!predictionOptionIsBoolean
+                && prefService.networkPredictionOptionsHasUserSetting()) {
+            return;
+        }
+
+        // Nothing to do if the old preferences are unset.
+        if (!predictionOptionIsBoolean
+                && !mSharedPreferences.contains(PREF_BANDWIDTH_OLD)
+                && !mSharedPreferences.contains(PREF_BANDWIDTH_NO_CELLULAR_OLD)) {
+            return;
+        }
+
+        // Migrate if the old preferences are at their default values.
+        // (Note that for PREF_BANDWIDTH*, if the setting is default, then there is no way to tell
+        // whether the user has set it.)
+        final String prefBandwidthDefault = BandwidthType.PRERENDER_ON_WIFI.title();
+        final String prefBandwidth =
+                mSharedPreferences.getString(PREF_BANDWIDTH_OLD, prefBandwidthDefault);
+        boolean prefBandwidthNoCellularDefault = true;
+        boolean prefBandwidthNoCellular = mSharedPreferences.getBoolean(
+                PREF_BANDWIDTH_NO_CELLULAR_OLD, prefBandwidthNoCellularDefault);
+
+        if (!(prefBandwidthDefault.equals(prefBandwidth))
+                || (prefBandwidthNoCellular != prefBandwidthNoCellularDefault)) {
+            NetworkPredictionOptions newValue = NetworkPredictionOptions.DEFAULT;
+            // Observe PREF_BANDWIDTH on mobile network capable devices.
+            if (isMobileNetworkCapable()) {
+                if (mSharedPreferences.contains(PREF_BANDWIDTH_OLD)) {
+                    BandwidthType prefetchBandwidthTypePref = BandwidthType.getBandwidthFromTitle(
+                            prefBandwidth);
+                    if (BandwidthType.NEVER_PRERENDER.equals(prefetchBandwidthTypePref)) {
+                        newValue = NetworkPredictionOptions.NETWORK_PREDICTION_NEVER;
+                    } else if (BandwidthType.PRERENDER_ON_WIFI.equals(prefetchBandwidthTypePref)) {
+                        newValue = NetworkPredictionOptions.NETWORK_PREDICTION_WIFI_ONLY;
+                    } else if (BandwidthType.ALWAYS_PRERENDER.equals(prefetchBandwidthTypePref)) {
+                        newValue = NetworkPredictionOptions.NETWORK_PREDICTION_ALWAYS;
+                    }
+                }
+            // Observe PREF_BANDWIDTH_NO_CELLULAR on devices without mobile network.
+            } else {
+                if (mSharedPreferences.contains(PREF_BANDWIDTH_NO_CELLULAR_OLD)) {
+                    if (prefBandwidthNoCellular) {
+                        newValue = NetworkPredictionOptions.NETWORK_PREDICTION_WIFI_ONLY;
+                    } else {
+                        newValue = NetworkPredictionOptions.NETWORK_PREDICTION_NEVER;
+                    }
+                }
+            }
+            // But disable after all if kNetworkPredictionEnabled was disabled by the user.
+            if (prefService.networkPredictionEnabledHasUserSetting()
+                    && !prefService.getNetworkPredictionEnabledUserPrefValue()) {
+                newValue = NetworkPredictionOptions.NETWORK_PREDICTION_NEVER;
+            }
+            // Save new value in Chrome PrefService.
+            prefService.setNetworkPredictionOptions(newValue);
+        }
+
+        // Delete old sharedPreferences.
+        SharedPreferences.Editor sharedPreferencesEditor = mSharedPreferences.edit();
+        // Delete PREF_BANDWIDTH and PREF_BANDWIDTH_NO_CELLULAR: just migrated these options.
+        if (mSharedPreferences.contains(PREF_BANDWIDTH_OLD)) {
+            sharedPreferencesEditor.remove(PREF_BANDWIDTH_OLD);
+        }
+        if (mSharedPreferences.contains(PREF_BANDWIDTH_NO_CELLULAR_OLD)) {
+            sharedPreferencesEditor.remove(PREF_BANDWIDTH_NO_CELLULAR_OLD);
+        }
+        // Also delete ALLOW_PRERENDER, which was updated based on PREF_BANDWIDTH[_NO_CELLULAR] and
+        // network connectivity type, therefore does not carry additional information.
+        if (mSharedPreferences.contains(ALLOW_PRERENDER_OLD)) {
+            sharedPreferencesEditor.remove(ALLOW_PRERENDER_OLD);
+        }
+        // Delete bool PREF_NETWORK_PREDICTIONS so that string values can be stored. Note that this
+        // SharedPreference carries no information, because it used to be overwritten by
+        // kNetworkPredictionEnabled on startup, and now it is overwritten by
+        // kNetworkPredictionOptions on startup.
+        if (mSharedPreferences.contains(PREF_NETWORK_PREDICTIONS)) {
+            sharedPreferencesEditor.remove(PREF_NETWORK_PREDICTIONS);
+        }
+        sharedPreferencesEditor.apply();
+    }
+
+    private NetworkInfo getActiveNetworkInfo() {
+        ConnectivityManager connectivityManager =
+                (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
+        return connectivityManager.getActiveNetworkInfo();
+    }
+
+    protected boolean isNetworkAvailable() {
+        NetworkInfo networkInfo = getActiveNetworkInfo();
+        return (networkInfo != null && networkInfo.isConnected());
+    }
+
+    protected boolean isWiFiOrEthernetNetwork() {
+        NetworkInfo networkInfo = getActiveNetworkInfo();
+        return networkInfo != null
+                && (networkInfo.getType() == ConnectivityManager.TYPE_WIFI
+                        || networkInfo.getType() == ConnectivityManager.TYPE_ETHERNET);
+    }
+
+    protected boolean isMobileNetworkCapable() {
+        return ((ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE))
+            .getNetworkInfo(ConnectivityManager.TYPE_MOBILE) != null;
+    }
+
+    /**
+     * Checks whether prerender should be allowed and updates the preference if it is not set yet.
+     * @return Whether prerendering should be allowed.
+     */
+    public boolean shouldPrerender() {
+        if (!DeviceClassManager.enablePrerendering()) return false;
+        migrateNetworkPredictionPreferences();
+        return PrefServiceBridge.getInstance().canPredictNetworkActions();
+    }
+
+    /**
+     * Check whether to allow uploading crash dump. The option should be either
+     * "always upload", or "wifi only" with current connection being wifi/ethernet.
+     *
+     * @return boolean to whether to allow uploading crash dump.
+     */
+    private boolean allowUploadCrashDump() {
+        if (!isMobileNetworkCapable()) {
+            return mSharedPreferences.getBoolean(PREF_CRASH_DUMP_UPLOAD_NO_CELLULAR, false);
+        } else {
+            String option =
+                    mSharedPreferences.getString(PREF_CRASH_DUMP_UPLOAD, mCrashDumpNeverUpload);
+            return option.equals(mCrashDumpAlwaysUpload)
+                    || (option.equals(mCrashDumpWifiOnlyUpload) && isWiFiOrEthernetNetwork());
+        }
+    }
+
+    /**
+     * Sets the crash upload preference, which determines whether crash dumps will be uploaded
+     * always, never, or only on wifi.
+     *
+     * @param when A String denoting when crash dump uploading is allowed. One of
+     *             R.array.crash_upload_values.
+     */
+    public void setUploadCrashDump(String when) {
+        // Set the crash upload preference regardless of the current connection status.
+        boolean canUpload = !when.equals(mCrashDumpNeverUpload);
+        PrefServiceBridge.getInstance().setCrashReporting(canUpload);
+    }
+
+    /**
+     * Provides a way to disable crash uploading entirely, regardless of the preferences.
+     * Used by tests that trigger crashers intentionally, so these crashers are not uploaded.
+     */
+    public void disableCrashUploading() {
+        mCrashUploadingEnabled = false;
+    }
+
+    /**
+     * Check whether to allow uploading crash dump now.
+     * {@link #allowUploadCrashDump()} should return {@code true},
+     * and the network should be connected as well.
+     *
+     * @return boolean to whether to allow uploading crash dump now.
+     */
+    public boolean allowUploadCrashDumpNow() {
+        return mCrashUploadingEnabled && isNetworkAvailable() && (allowUploadCrashDump()
+                || CommandLine.getInstance().hasSwitch(ChromeSwitches.FORCE_CRASH_DUMP_UPLOAD));
+    }
+
+    /**
+     * Check whether crash dump upload preference is set to NEVER only.
+     *
+     * @return boolean {@code true} if the option is set to NEVER
+     */
+    public boolean isNeverUploadCrashDump() {
+        boolean option;
+        if (isMobileNetworkCapable()) {
+            option = mSharedPreferences.getString(PREF_CRASH_DUMP_UPLOAD, mCrashDumpNeverUpload)
+                    .equals(mCrashDumpNeverUpload);
+        } else {
+            option = !mSharedPreferences.getBoolean(PREF_CRASH_DUMP_UPLOAD_NO_CELLULAR, false);
+        }
+        return option;
+    }
+
+    /**
+     * Sets the initial value for whether crash stacks may be uploaded.
+     * This should be called only once, the first time Chrome is launched.
+     */
+    public void initCrashUploadPreference(boolean allowCrashUpload) {
+        SharedPreferences.Editor ed = mSharedPreferences.edit();
+        if (isMobileNetworkCapable()) {
+            if (allowCrashUpload) {
+                ed.putString(PREF_CRASH_DUMP_UPLOAD, mCrashDumpWifiOnlyUpload);
+            } else {
+                ed.putString(PREF_CRASH_DUMP_UPLOAD, mCrashDumpNeverUpload);
+            }
+        } else {
+            ed.putString(PREF_CRASH_DUMP_UPLOAD, mCrashDumpNeverUpload);
+            ed.putBoolean(PREF_CRASH_DUMP_UPLOAD_NO_CELLULAR, allowCrashUpload);
+        }
+        ed.apply();
+        PrefServiceBridge.getInstance().setCrashReporting(allowCrashUpload);
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/ContentPreferences.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/ContentPreferences.java
index 5b2e6bdd..466b1b1 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/ContentPreferences.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/ContentPreferences.java
@@ -11,8 +11,6 @@
 import android.preference.Preference.OnPreferenceClickListener;
 import android.preference.PreferenceFragment;
 
-import org.chromium.base.CommandLine;
-import org.chromium.chrome.ChromeSwitches;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ContentSettingsType;
 import org.chromium.chrome.browser.preferences.ChromeBaseCheckBoxPreference;
@@ -53,10 +51,6 @@
             getPreferenceScreen().removePreference(findPreference(PROTECTED_CONTENT_KEY));
         }
 
-        if (!pushNotificationsSupported()) {
-            getPreferenceScreen().removePreference(findPreference(PUSH_NOTIFICATIONS_KEY));
-        }
-
         // Set up the checkbox preferences.
         List<String> checkBoxPreferences = new ArrayList<String>();
         checkBoxPreferences.add(JAVASCRIPT_KEY);
@@ -88,14 +82,6 @@
         return -1;
     }
 
-    /**
-     * Returns whether Push Notifications (Push Messaging) is supported.
-     */
-    public static boolean pushNotificationsSupported() {
-        return CommandLine.getInstance().hasSwitch(
-                ChromeSwitches.EXPERIMENTAL_WEB_PLAFTORM_FEATURES);
-    }
-
     private void updatePreferenceStates() {
         PrefServiceBridge prefServiceBridge = PrefServiceBridge.getInstance();
 
@@ -119,9 +105,7 @@
         }
         websitePrefs.add(COOKIES_KEY);
         websitePrefs.add(CAMERA_AND_MIC_KEY);
-        if (pushNotificationsSupported()) {
-            websitePrefs.add(PUSH_NOTIFICATIONS_KEY);
-        }
+        websitePrefs.add(PUSH_NOTIFICATIONS_KEY);
         websitePrefs.add(POPUPS_KEY);
         // Initialize the summary and icon for all preferences that have an
         // associated content settings entry.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/PermissionInfo.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/PermissionInfo.java
index feb6950..40a07b7e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/PermissionInfo.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/PermissionInfo.java
@@ -26,7 +26,7 @@
         return mEmbedder;
     }
 
-    private String getEmbedderSafe() {
+    public String getEmbedderSafe() {
         return mEmbedder != null ? mEmbedder : mOrigin;
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/SingleWebsitePreferences.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/SingleWebsitePreferences.java
index d337bfa..0361e8e01 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/SingleWebsitePreferences.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/SingleWebsitePreferences.java
@@ -19,6 +19,13 @@
 
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ContentSettingsType;
+import org.chromium.chrome.browser.UrlUtilities;
+
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
 
 /**
  * Shows a list of HTML5 settings for a single website.
@@ -26,7 +33,13 @@
 public class SingleWebsitePreferences extends PreferenceFragment
         implements DialogInterface.OnClickListener, OnPreferenceChangeListener,
                 OnPreferenceClickListener {
+    // SingleWebsitePreferences expects either EXTRA_SITE (a Website) or
+    // EXTRA_ADDRESS (a WebsiteAddress) to be present (but not both). If
+    // EXTRA_SITE is present, the fragment will display the permissions in that
+    // Website object. If EXTRA_ADDRESS is present, the fragment will find all
+    // permissions for that website address and display those.
     public static final String EXTRA_SITE = "org.chromium.chrome.preferences.site";
+    public static final String EXTRA_ADDRESS = "org.chromium.chrome.preferences.address";
 
     // Preference keys, see single_website_preferences.xml
     // Headings:
@@ -51,13 +64,145 @@
 
     // The website this page is displaying details about.
     private Website mSite;
+
+    // The address of the site we want to display. Used only if EXTRA_ADDRESS is provided.
+    private WebsiteAddress mSiteAddress;
+
     // A list of possible options for each list preference summary.
     private String[] mListPreferenceSummaries;
 
+    private class SingleWebsitePermissionsPopulator
+            implements WebsitePermissionsFetcher.WebsitePermissionsCallback {
+        @Override
+        public void onWebsitePermissionsAvailable(
+                Map<String, Set<Website>> sitesByOrigin, Map<String, Set<Website>> sitesByHost) {
+            // This method may be called after the activity has been destroyed.
+            // In that case, bail out.
+            if (getActivity() == null) return;
+
+            // TODO(mvanouwerkerk): Do this merge at data retrieval time in C++, instead of now.
+            List<Set<Website>> allSites = new ArrayList<>();
+            allSites.addAll(sitesByOrigin.values());
+            allSites.addAll(sitesByHost.values());
+            // TODO(mvanouwerkerk): Avoid modifying the outer class from this inner class.
+            mSite = mergePermissionInfoForTopLevelOrigin(mSiteAddress, allSites);
+            displaySitePermissions();
+        }
+    }
+
+    /**
+     * Creates a Bundle with the correct arguments for opening this fragment for
+     * the website with the given url.
+     *
+     * @param url The URL to open the fragment with. This is a complete url including scheme,
+     *            domain, port,  path, etc.
+     * @return The bundle to attach to the preferences intent.
+     */
+    public static Bundle createFragmentArgsForSite(String url) {
+        Bundle fragmentArgs = new Bundle();
+        // TODO(mvanouwerkerk): Define a pure getOrigin method in UrlUtilities that is the
+        // equivalent of the call below, because this is perfectly fine for non-display purposes.
+        String origin = UrlUtilities.getOriginForDisplay(URI.create(url), true /*  schowScheme */);
+        fragmentArgs.putSerializable(
+                SingleWebsitePreferences.EXTRA_ADDRESS, WebsiteAddress.create(origin));
+        return fragmentArgs;
+    }
+
     @Override
     public void onActivityCreated(Bundle savedInstanceState) {
         getActivity().setTitle(R.string.prefs_content_settings);
-        mSite = (Website) getArguments().getSerializable(EXTRA_SITE);
+        Object extraSite = getArguments().getSerializable(EXTRA_SITE);
+        Object extraAddress = getArguments().getSerializable(EXTRA_ADDRESS);
+
+        if (extraSite != null && extraAddress == null) {
+            mSite = (Website) extraSite;
+            displaySitePermissions();
+        } else if (extraAddress != null && extraSite == null) {
+            mSiteAddress = (WebsiteAddress) extraAddress;
+            WebsitePermissionsFetcher fetcher =
+                    new WebsitePermissionsFetcher(new SingleWebsitePermissionsPopulator());
+            fetcher.fetchAllPreferences();
+        } else {
+            assert false : "Exactly one of EXTRA_SITE or EXTRA_SITE_ADDRESS must be provided.";
+        }
+
+        super.onActivityCreated(savedInstanceState);
+    }
+
+    /**
+     * Given an address and a list of sets of websites, returns a new site with the same origin
+     * as |address| which has merged into it the permissions of the matching input sites. If a
+     * permission is found more than once, the one found first is used and the latter are ignored.
+     * This should not drop any relevant data as this method already aggressively filters on an
+     * exact match of origin and embedder.
+     *
+     * @param address The address to search for.
+     * @param websiteSets The websites to search in.
+     * @return The merged website.
+     */
+    private static Website mergePermissionInfoForTopLevelOrigin(
+            WebsiteAddress address, List<Set<Website>> websiteSets) {
+        String origin = address.getOrigin();
+        Website merged = new Website(address);
+        // This nested loop looks expensive, but the amount of data is likely to be relatively
+        // small because most sites have very few permissions.
+        for (Set<Website> websiteSet : websiteSets) {
+            for (Website other : websiteSet) {
+                if (merged.getCookieInfo() == null && other.getCookieInfo() != null
+                        && permissionInfoIsForTopLevelOrigin(other.getCookieInfo(), origin)) {
+                    merged.setCookieInfo(other.getCookieInfo());
+                }
+                if (merged.getGeolocationInfo() == null && other.getGeolocationInfo() != null
+                        && permissionInfoIsForTopLevelOrigin(other.getGeolocationInfo(), origin)) {
+                    merged.setGeolocationInfo(other.getGeolocationInfo());
+                }
+                if (merged.getMidiInfo() == null && other.getMidiInfo() != null
+                        && permissionInfoIsForTopLevelOrigin(other.getMidiInfo(), origin)) {
+                    merged.setMidiInfo(other.getMidiInfo());
+                }
+                if (merged.getProtectedMediaIdentifierInfo() == null
+                        && other.getProtectedMediaIdentifierInfo() != null
+                        && permissionInfoIsForTopLevelOrigin(
+                                   other.getProtectedMediaIdentifierInfo(), origin)) {
+                    merged.setProtectedMediaIdentifierInfo(other.getProtectedMediaIdentifierInfo());
+                }
+                if (merged.getPushNotificationInfo() == null
+                        && other.getPushNotificationInfo() != null
+                        && permissionInfoIsForTopLevelOrigin(
+                                   other.getPushNotificationInfo(), origin)) {
+                    merged.setPushNotificationInfo(other.getPushNotificationInfo());
+                }
+                if (merged.getVoiceAndVideoCaptureInfo() == null
+                        && other.getVoiceAndVideoCaptureInfo() != null) {
+                    if (origin.equals(other.getVoiceAndVideoCaptureInfo().getOrigin())
+                            && (origin.equals(other.getVoiceAndVideoCaptureInfo().getEmbedderSafe())
+                                       || "*".equals(other.getVoiceAndVideoCaptureInfo()
+                                                             .getEmbedderSafe()))) {
+                        merged.setVoiceAndVideoCaptureInfo(other.getVoiceAndVideoCaptureInfo());
+                    }
+                }
+                // TODO(mvanouwerkerk): Can VoiceAndVideoCaptureInfo share a base type with the
+                // others?
+                // TODO(mvanouwerkerk): Merge in PopupExceptionInfo? It is not a PermissionInfo.
+                // TODO(mvanouwerkerk): Merge in LocalStorageInfo? It is not a PermissionInfo.
+                // TODO(mvanouwerkerk): Merge in all instances of StorageInfo? It is not a
+                // PermissionInfo.
+            }
+        }
+        return merged;
+    }
+
+    private static boolean permissionInfoIsForTopLevelOrigin(PermissionInfo info, String origin) {
+        // TODO(mvanouwerkerk): Find a more generic place for this method.
+        return origin.equals(info.getOrigin())
+                && (origin.equals(info.getEmbedderSafe()) || "*".equals(info.getEmbedderSafe()));
+    }
+
+    /**
+     * Updates the permissions displayed in the UI by fetching them from mSite.
+     * Must only be called once mSite is set.
+     */
+    private void displaySitePermissions() {
         addPreferencesFromResource(R.xml.single_website_preferences);
         mListPreferenceSummaries = getActivity().getResources().getStringArray(
                 R.array.website_settings_permission_options);
@@ -91,11 +236,7 @@
             } else if (PREF_PROTECTED_MEDIA_IDENTIFIER_PERMISSION.equals(preference.getKey())) {
                 setUpListPreference(preference, mSite.getProtectedMediaIdentifierPermission());
             } else if (PREF_PUSH_NOTIFICATIONS_PERMISSION.equals(preference.getKey())) {
-                if (ContentPreferences.pushNotificationsSupported()) {
-                    setUpListPreference(preference, mSite.getPushNotificationPermission());
-                } else {
-                    getPreferenceScreen().removePreference(preference);
-                }
+                setUpListPreference(preference, mSite.getPushNotificationPermission());
             } else if (PREF_VOICE_AND_VIDEO_CAPTURE_PERMISSION.equals(preference.getKey())) {
                 configureVoiceAndVideoPreference(preference);
             }
@@ -111,8 +252,6 @@
             Preference heading = preferenceScreen.findPreference(PREF_PERMISSIONS);
             preferenceScreen.removePreference(heading);
         }
-
-        super.onActivityCreated(savedInstanceState);
     }
 
     private boolean hasUsagePreferences() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/VoiceAndVideoCaptureInfo.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/VoiceAndVideoCaptureInfo.java
index 7bd1cad..1725d1c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/VoiceAndVideoCaptureInfo.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/VoiceAndVideoCaptureInfo.java
@@ -42,7 +42,7 @@
     /**
      * @return Embedding frame origin for this permission. It it's null, returns origin.
      */
-    private String getEmbedderSafe() {
+    public String getEmbedderSafe() {
         return mEmbedder != null ? mEmbedder : mOrigin;
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/Website.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/Website.java
index fcd4422..158f979 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/Website.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/Website.java
@@ -265,6 +265,10 @@
         }
     }
 
+    public CookieInfo getCookieInfo() {
+        return mCookieInfo;
+    }
+
     /**
      * Gets the permission that governs cookie preferences.
      */
@@ -292,6 +296,10 @@
         }
     }
 
+    public GeolocationInfo getGeolocationInfo() {
+        return mGeolocationInfo;
+    }
+
     /**
      * Returns what permission governs geolocation access.
      */
@@ -319,6 +327,10 @@
         }
     }
 
+    public MidiInfo getMidiInfo() {
+        return mMidiInfo;
+    }
+
     /**
      * Returns what permission governs MIDI usage access.
      */
@@ -342,6 +354,10 @@
         mPopupExceptionInfo = info;
     }
 
+    public PopupExceptionInfo getPopupExceptionInfo() {
+        return mPopupExceptionInfo;
+    }
+
     /**
      * Returns what permission governs Popup permission.
      */
@@ -370,6 +386,10 @@
         }
     }
 
+    public ProtectedMediaIdentifierInfo getProtectedMediaIdentifierInfo() {
+        return mProtectedMediaIdentifierInfo;
+    }
+
     /**
      * Returns what permission governs Protected Media Identifier access.
      */
@@ -394,6 +414,10 @@
         mPushNotificationInfo = info;
     }
 
+    public PushNotificationInfo getPushNotificationInfo() {
+        return mPushNotificationInfo;
+    }
+
     /**
      * Returns what setting governs push notification access.
      */
@@ -421,6 +445,10 @@
         }
     }
 
+    public VoiceAndVideoCaptureInfo getVoiceAndVideoCaptureInfo() {
+        return mVoiceAndVideoCaptureInfo;
+    }
+
     /**
      * Returns what setting governs voice capture access.
      */
@@ -492,10 +520,18 @@
         mLocalStorageInfo = info;
     }
 
+    public LocalStorageInfo getLocalStorageInfo() {
+        return mLocalStorageInfo;
+    }
+
     public void addStorageInfo(StorageInfo info) {
         mStorageInfo.add(info);
     }
 
+    public List<StorageInfo> getStorageInfo() {
+        return new ArrayList<StorageInfo>(mStorageInfo);
+    }
+
     public void clearAllStoredData(final StoredDataClearedCallback callback) {
         if (mLocalStorageInfo != null) {
             mLocalStorageInfo.clear();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/WebsiteAddress.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/WebsiteAddress.java
index 2269cf40..340ea3a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/WebsiteAddress.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/WebsiteAddress.java
@@ -10,6 +10,8 @@
 
 import java.io.Serializable;
 
+import javax.annotation.Nullable;
+
 /**
  * WebsiteAddress is a robust class for storing website address, which can be a
  * fully specified origin, or just a host, or a website name pattern.
@@ -24,25 +26,43 @@
     private static final String SCHEME_SUFFIX = "://";
     private static final String ANY_SUBDOMAIN_PATTERN = "[*.]";
 
+    /**
+     * Creates a new WebsiteAddress from |originOrHostOrPattern|.
+     *
+     * @return A new WebsiteAddress, or null if |originOrHostOrPattern| was null or empty.
+     */
+    @Nullable
     public static WebsiteAddress create(String originOrHostOrPattern) {
+        // TODO(mvanouwerkerk): Define the behavior of this method if a url with path, query, or
+        // fragment is passed in.
+
         if (originOrHostOrPattern == null || originOrHostOrPattern.isEmpty()) {
             return null;
-        } else if (originOrHostOrPattern.startsWith(ANY_SUBDOMAIN_PATTERN)) {
-            // Pattern
-            return new WebsiteAddress(null, null,
-                    originOrHostOrPattern.substring(ANY_SUBDOMAIN_PATTERN.length()), true);
-        } else if (originOrHostOrPattern.indexOf(SCHEME_SUFFIX) != -1) {
-            // Origin
-            Uri uri = Uri.parse(originOrHostOrPattern);
-            return new WebsiteAddress(trimTrailingBackslash(originOrHostOrPattern),
-                    uri.getScheme(),
-                    uri.getHost(),
-                    HTTP_SCHEME.equals(uri.getScheme())
-                            && (uri.getPort() == -1 || uri.getPort() == 80));
-        } else {
-            // Host
-            return new WebsiteAddress(null, null, originOrHostOrPattern, true);
         }
+
+        // Pattern
+        if (originOrHostOrPattern.startsWith(ANY_SUBDOMAIN_PATTERN)) {
+            String origin = null;
+            String scheme = null;
+            String host = originOrHostOrPattern.substring(ANY_SUBDOMAIN_PATTERN.length());
+            boolean omitProtocolAndPort = true;
+            return new WebsiteAddress(origin, scheme, host, omitProtocolAndPort);
+        }
+
+        // Origin
+        if (originOrHostOrPattern.indexOf(SCHEME_SUFFIX) != -1) {
+            Uri uri = Uri.parse(originOrHostOrPattern);
+            String origin = trimTrailingBackslash(originOrHostOrPattern);
+            boolean omitProtocolAndPort = HTTP_SCHEME.equals(uri.getScheme())
+                    && (uri.getPort() == -1 || uri.getPort() == 80);
+            return new WebsiteAddress(origin, uri.getScheme(), uri.getHost(), omitProtocolAndPort);
+        }
+
+        // Host
+        String origin = null;
+        String scheme = null;
+        boolean omitProtocolAndPort = true;
+        return new WebsiteAddress(origin, scheme, originOrHostOrPattern, omitProtocolAndPort);
     }
 
     private WebsiteAddress(String origin, String scheme, String host, boolean omitProtocolAndPort) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/WebsitePermissionsFetcher.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/WebsitePermissionsFetcher.java
new file mode 100644
index 0000000..5abd361
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/WebsitePermissionsFetcher.java
@@ -0,0 +1,312 @@
+// 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.chrome.browser.preferences.website;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Utility class that asynchronously fetches any Websites and the permissions
+ * that the user has set for them.
+ */
+public class WebsitePermissionsFetcher {
+    /**
+     * A callback to pass to WebsitePermissionsFetcher. This is run when the
+     * website permissions have been fetched.
+     */
+    public interface WebsitePermissionsCallback {
+        void onWebsitePermissionsAvailable(
+                Map<String, Set<Website>> sitesByOrigin, Map<String, Set<Website>> sitesByHost);
+    }
+
+    // This is a 1 <--> 1..N mapping between origin and Website.
+    // TODO(mvanouwerkerk): The Website class has no equals or hashCode methods so storing them in
+    // a HashSet is really confusing to readers of this code. There is no deduplication at all.
+    private final Map<String, Set<Website>> mSitesByOrigin = new HashMap<>();
+
+    // This is a 1 <--> 1..N mapping between host and Website.
+    // TODO(mvanouwerkerk): The Website class has no equals or hashCode methods so storing them in
+    // a HashSet is really confusing to readers of this code. There is no deduplication at all.
+    private final Map<String, Set<Website>> mSitesByHost = new HashMap<>();
+
+    // The callback to run when the permissions have been fetched.
+    private final WebsitePermissionsCallback mCallback;
+
+    /**
+     * @param callback The callback to run when the fetch is complete.
+     */
+    public WebsitePermissionsFetcher(WebsitePermissionsCallback callback) {
+        mCallback = callback;
+    }
+
+    /**
+     * Fetches preferences for all sites that have them.
+     * TODO(mvanouwerkerk): Add an argument |url| to only fetch permissions for
+     * sites from the same origin as that of |url| - https://crbug.com/459222.
+     */
+    public void fetchAllPreferences() {
+        TaskQueue queue = new TaskQueue();
+        // Populate features from more specific to less specific.
+        // Geolocation lookup permission is per-origin and per-embedder.
+        queue.add(new GeolocationInfoFetcher());
+        // Midi sysex access permission is per-origin and per-embedder.
+        queue.add(new MidiInfoFetcher());
+        // Cookies are stored per-origin.
+        queue.add(new CookieInfoFetcher());
+        // Local storage info is per-origin.
+        queue.add(new LocalStorageInfoFetcher());
+        // Website storage is per-host.
+        queue.add(new WebStorageInfoFetcher());
+        // Popup exceptions are host-based patterns (unless we start
+        // synchronizing popup exceptions with desktop Chrome.)
+        queue.add(new PopupExceptionInfoFetcher());
+        // Protected media identifier permission is per-origin and per-embedder.
+        queue.add(new ProtectedMediaIdentifierInfoFetcher());
+        // Push notification permission is per-origin and per-embedder.
+        queue.add(new PushNotificationInfoFetcher());
+        // Voice and Video capture permission is per-origin and per-embedder.
+        queue.add(new VoiceAndVideoCaptureInfoFetcher());
+        queue.add(new PermissionsAvailableCallbackRunner());
+        queue.next();
+    }
+
+    /**
+     * Fetches preferences for all sites that have them that apply to the given
+     * filter.
+     *
+     * @param filter A filter to apply to sites to fetch. See
+     *               WebsiteSettingsCategoryFilter for a list of valid filters.
+     */
+    public void fetchPreferencesWithFilter(String filter) {
+        WebsiteSettingsCategoryFilter filterHelper = new WebsiteSettingsCategoryFilter();
+        if (filterHelper.showAllSites(filter)) {
+            fetchAllPreferences();
+            return;
+        }
+
+        TaskQueue queue = new TaskQueue();
+        // Populate features from more specific to less specific.
+        if (filterHelper.showGeolocationSites(filter)) {
+            // Geolocation lookup permission is per-origin and per-embedder.
+            queue.add(new GeolocationInfoFetcher());
+        } else if (filterHelper.showCookiesSites(filter)) {
+            // Cookies are stored per-origin.
+            queue.add(new CookieInfoFetcher());
+        } else if (filterHelper.showStorageSites(filter)) {
+            // Local storage info is per-origin.
+            queue.add(new LocalStorageInfoFetcher());
+        } else if (filterHelper.showCameraMicSites(filter)) {
+            // Voice and Video capture permission is per-origin and per-embedder.
+            queue.add(new VoiceAndVideoCaptureInfoFetcher());
+        } else if (filterHelper.showPopupSites(filter)) {
+            // Popup exceptions are host-based patterns (unless we start
+            // synchronizing popup exceptions with desktop Chrome.)
+            queue.add(new PopupExceptionInfoFetcher());
+        } else if (filterHelper.showPushNotificationsSites(filter)) {
+            // Push notification permission is per-origin and per-embedder.
+            queue.add(new PushNotificationInfoFetcher());
+        }
+        queue.add(new PermissionsAvailableCallbackRunner());
+        queue.next();
+    }
+
+    private Website createSiteByOriginAndHost(WebsiteAddress address) {
+        String origin = address.getOrigin();
+        String host = address.getHost();
+        Website site = new Website(address);
+        if (!mSitesByOrigin.containsKey(origin)) mSitesByOrigin.put(origin, new HashSet<Website>());
+        mSitesByOrigin.get(origin).add(site);
+        if (!mSitesByHost.containsKey(host)) mSitesByHost.put(host, new HashSet<Website>());
+        mSitesByHost.get(host).add(site);
+        return site;
+    }
+
+    private Set<Website> findOrCreateSitesByOrigin(WebsiteAddress address) {
+        String origin = address.getOrigin();
+        if (!mSitesByOrigin.containsKey(origin)) createSiteByOriginAndHost(address);
+        return mSitesByOrigin.get(origin);
+    }
+
+    private Set<Website> findOrCreateSitesByHost(WebsiteAddress address) {
+        String host = address.getHost();
+        if (!mSitesByHost.containsKey(host)) {
+            mSitesByHost.put(host, new HashSet<Website>());
+            mSitesByHost.get(host).add(new Website(address));
+        }
+        return mSitesByHost.get(host);
+    }
+
+    /**
+     * A single task in the WebsitePermissionsFetcher task queue. We need
+     * fetching of features to be serialized, as we need to have all the origins
+     * in place prior to populating the hosts.
+     */
+    private interface Task {
+        void run(TaskQueue queue);
+    }
+
+    /**
+     * A queue used to store the sequence of tasks to run to fetch the website
+     * preferences. Each task is run sequentially (although the queue as a whole
+     * is run asynchronously). Each task should call queue.next() at the end to
+     * run the next task in the queue.
+     */
+    private static class TaskQueue extends LinkedList<Task> {
+        void next() {
+            if (!isEmpty()) removeFirst().run(this);
+        }
+    }
+
+    private class GeolocationInfoFetcher implements Task {
+        @Override
+        public void run(TaskQueue queue) {
+            for (GeolocationInfo info : WebsitePreferenceBridge.getGeolocationInfo()) {
+                WebsiteAddress address = WebsiteAddress.create(info.getOrigin());
+                if (address == null) continue;
+                createSiteByOriginAndHost(address).setGeolocationInfo(info);
+            }
+            queue.next();
+        }
+    }
+
+    private class MidiInfoFetcher implements Task {
+        @Override
+        public void run(TaskQueue queue) {
+            for (MidiInfo info : WebsitePreferenceBridge.getMidiInfo()) {
+                WebsiteAddress address = WebsiteAddress.create(info.getOrigin());
+                if (address == null) continue;
+                createSiteByOriginAndHost(address).setMidiInfo(info);
+            }
+            queue.next();
+        }
+    }
+
+    private class PopupExceptionInfoFetcher implements Task {
+        @Override
+        public void run(TaskQueue queue) {
+            for (PopupExceptionInfo info : WebsitePreferenceBridge.getPopupExceptionInfo()) {
+                // The pattern "*" represents the default setting, not a
+                // specific website.
+                if (info.getPattern().equals("*")) continue;
+                WebsiteAddress address = WebsiteAddress.create(info.getPattern());
+                if (address == null) continue;
+                Set<Website> sites = findOrCreateSitesByHost(address);
+                for (Website site : sites) {
+                    site.setPopupExceptionInfo(info);
+                }
+            }
+            queue.next();
+        }
+    }
+
+    private class CookieInfoFetcher implements Task {
+        @Override
+        public void run(TaskQueue queue) {
+            for (CookieInfo info : WebsitePreferenceBridge.getCookieInfo()) {
+                WebsiteAddress address = WebsiteAddress.create(info.getOrigin());
+                if (address == null) continue;
+                createSiteByOriginAndHost(address).setCookieInfo(info);
+            }
+            queue.next();
+        }
+    }
+
+    private class LocalStorageInfoFetcher implements Task {
+        @Override
+        public void run(final TaskQueue queue) {
+            WebsitePreferenceBridge.fetchLocalStorageInfo(
+                    new WebsitePreferenceBridge.LocalStorageInfoReadyCallback() {
+                        @SuppressWarnings("unchecked")
+                        @Override
+                        public void onLocalStorageInfoReady(HashMap map) {
+                            for (Object o : map.entrySet()) {
+                                Map.Entry<String, LocalStorageInfo> entry =
+                                        (Map.Entry<String, LocalStorageInfo>) o;
+                                WebsiteAddress address = WebsiteAddress.create(entry.getKey());
+                                if (address == null) continue;
+                                Set<Website> sites = findOrCreateSitesByOrigin(address);
+                                for (Website site : sites) {
+                                    site.setLocalStorageInfo(entry.getValue());
+                                }
+                            }
+                            queue.next();
+                        }
+                    });
+        }
+    }
+
+    private class WebStorageInfoFetcher implements Task {
+        @Override
+        public void run(final TaskQueue queue) {
+            WebsitePreferenceBridge.fetchStorageInfo(
+                    new WebsitePreferenceBridge.StorageInfoReadyCallback() {
+                        @SuppressWarnings("unchecked")
+                        @Override
+                        public void onStorageInfoReady(ArrayList array) {
+                            ArrayList<StorageInfo> infoArray = array;
+                            for (StorageInfo info : infoArray) {
+                                WebsiteAddress address = WebsiteAddress.create(info.getHost());
+                                if (address == null) continue;
+                                Set<Website> sites = findOrCreateSitesByHost(address);
+                                for (Website site : sites) {
+                                    site.addStorageInfo(info);
+                                }
+                            }
+                            queue.next();
+                        }
+                    });
+        }
+    }
+
+    private class ProtectedMediaIdentifierInfoFetcher implements Task {
+        @Override
+        public void run(TaskQueue queue) {
+            for (ProtectedMediaIdentifierInfo info :
+                    WebsitePreferenceBridge.getProtectedMediaIdentifierInfo()) {
+                WebsiteAddress address = WebsiteAddress.create(info.getOrigin());
+                if (address == null) continue;
+                createSiteByOriginAndHost(address).setProtectedMediaIdentifierInfo(info);
+            }
+            queue.next();
+        }
+    }
+
+    private class PushNotificationInfoFetcher implements Task {
+        @Override
+        public void run(TaskQueue queue) {
+            for (PushNotificationInfo info : WebsitePreferenceBridge.getPushNotificationInfo()) {
+                WebsiteAddress address = WebsiteAddress.create(info.getOrigin());
+                if (address == null) continue;
+                createSiteByOriginAndHost(address).setPushNotificationInfo(info);
+            }
+            queue.next();
+        }
+    }
+
+    private class VoiceAndVideoCaptureInfoFetcher implements Task {
+        @Override
+        public void run(TaskQueue queue) {
+            for (VoiceAndVideoCaptureInfo info :
+                    WebsitePreferenceBridge.getVoiceAndVideoCaptureInfo()) {
+                WebsiteAddress address = WebsiteAddress.create(info.getOrigin());
+                if (address == null) continue;
+                createSiteByOriginAndHost(address).setVoiceAndVideoCaptureInfo(info);
+            }
+            queue.next();
+        }
+    }
+
+    private class PermissionsAvailableCallbackRunner implements Task {
+        @Override
+        public void run(TaskQueue queue) {
+            mCallback.onWebsitePermissionsAvailable(mSitesByOrigin, mSitesByHost);
+            queue.next();
+        }
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/WebsitePreferences.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/WebsitePreferences.java
index 070cd69..41a9eda 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/WebsitePreferences.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/WebsitePreferences.java
@@ -37,9 +37,7 @@
 
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.HashMap;
 import java.util.HashSet;
-import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -56,13 +54,6 @@
     public static final String EXTRA_CATEGORY = "category";
     public static final String EXTRA_TITLE = "title";
 
-    // This is a 0..1 <--> 1..N mapping between origin and Website.
-    private final Map<String, Set<Website>> mSitesByOrigin =
-            new HashMap<String, Set<Website>>();
-    // This is a 1 <--> 1..N mapping between host and Website.
-    private final Map<String, Set<Website>> mSitesByHost =
-            new HashMap<String, Set<Website>>();
-
     // The view to show when the list is empty.
     private TextView mEmptyView;
     // The view for searching the list of items.
@@ -92,198 +83,9 @@
     private static final String ALLOWED_GROUP = "allowed_group";
     private static final String BLOCKED_GROUP = "blocked_group";
 
-    // It is required that features fetching is serialized, as we need to have
-    // all origins in place prior to populating hosts.
-    private interface Task {
-        void run(TaskQueue queue);
-    }
-
-    private static class TaskQueue extends LinkedList<Task> {
-        void next() {
-            if (!isEmpty()) removeFirst().run(this);
-        }
-    }
-
     private void getInfoForOrigins() {
-        mSitesByOrigin.clear();
-        mSitesByHost.clear();
-        // Populate features from more specific to less specific.
-        // Geolocation lookup permission is per-origin and per-embedder.
-        TaskQueue queue = new TaskQueue();
-        if (mFilter.showAllSites(mCategoryFilter)) {
-            queue.add(new GeolocationInfoFetcher());
-            // Midi sysex access permission is per-origin and per-embedder.
-            queue.add(new MidiInfoFetcher());
-            // Cookies are stored per-origin.
-            queue.add(new CookieInfoFetcher());
-            // Local storage info is per-origin.
-            queue.add(new LocalStorageInfoFetcher());
-            // Website storage is per-host.
-            queue.add(new WebStorageInfoFetcher());
-            // Popup exceptions are host-based patterns (unless we start
-            // synchronizing popup exceptions with desktop Chrome.)
-            queue.add(new PopupExceptionInfoFetcher());
-            // Protected media identifier permission is per-origin and per-embedder.
-            queue.add(new ProtectedMediaIdentifierInfoFetcher());
-            if (ContentPreferences.pushNotificationsSupported()) {
-                // Push notification permission is per-origin and per-embedder.
-                queue.add(new PushNotificationInfoFetcher());
-            }
-            // Voice and Video capture permission is per-origin and per-embedder.
-            queue.add(new VoiceAndVideoCaptureInfoFetcher());
-        } else if (mFilter.showGeolocationSites(mCategoryFilter)) {
-            queue.add(new GeolocationInfoFetcher());
-        } else if (mFilter.showCookiesSites(mCategoryFilter)) {
-            queue.add(new CookieInfoFetcher());
-        } else if (mFilter.showStorageSites(mCategoryFilter)) {
-            queue.add(new LocalStorageInfoFetcher());
-        } else if (mFilter.showCameraMicSites(mCategoryFilter)) {
-            queue.add(new VoiceAndVideoCaptureInfoFetcher());
-        } else if (mFilter.showPopupSites(mCategoryFilter)) {
-            queue.add(new PopupExceptionInfoFetcher());
-        } else if (mFilter.showPushNotificationsSites(mCategoryFilter)) {
-            queue.add(new PushNotificationInfoFetcher());
-        }
-        queue.add(new ResultsPopulator());
-        queue.next();
-    }
-
-    private class GeolocationInfoFetcher implements Task {
-        @Override
-        public void run(TaskQueue queue) {
-            for (GeolocationInfo info : WebsitePreferenceBridge.getGeolocationInfo()) {
-                WebsiteAddress address = WebsiteAddress.create(info.getOrigin());
-                if (address == null) continue;
-                createSiteByOrigin(address).setGeolocationInfo(info);
-            }
-            queue.next();
-        }
-    }
-
-    private class MidiInfoFetcher implements Task {
-        @Override
-        public void run(TaskQueue queue) {
-            for (MidiInfo info : WebsitePreferenceBridge.getMidiInfo()) {
-                WebsiteAddress address = WebsiteAddress.create(info.getOrigin());
-                if (address == null) continue;
-                createSiteByOrigin(address).setMidiInfo(info);
-            }
-            queue.next();
-        }
-    }
-
-    private class PopupExceptionInfoFetcher implements Task {
-        @Override
-        public void run(TaskQueue queue) {
-            for (PopupExceptionInfo info : WebsitePreferenceBridge.getPopupExceptionInfo()) {
-                // The pattern "*" represents the default setting, not a specific website.
-                if (info.getPattern().equals("*")) continue;
-                WebsiteAddress address = WebsiteAddress.create(info.getPattern());
-                if (address == null) continue;
-                Set<Website> sites = findOrCreateSitesByHost(address);
-                for (Website site : sites) {
-                    site.setPopupExceptionInfo(info);
-                }
-            }
-            queue.next();
-        }
-    }
-
-    private class CookieInfoFetcher implements Task {
-        @Override
-        public void run(TaskQueue queue) {
-            for (CookieInfo info : WebsitePreferenceBridge.getCookieInfo()) {
-                WebsiteAddress address = WebsiteAddress.create(info.getOrigin());
-                if (address == null) continue;
-                createSiteByOrigin(address).setCookieInfo(info);
-            }
-            queue.next();
-        }
-    }
-
-    private class LocalStorageInfoFetcher implements Task {
-        @Override
-        public void run(final TaskQueue queue) {
-            WebsitePreferenceBridge.fetchLocalStorageInfo(
-                    new WebsitePreferenceBridge.LocalStorageInfoReadyCallback() {
-                        @SuppressWarnings("unchecked")
-                        @Override
-                        public void onLocalStorageInfoReady(HashMap map) {
-                            for (Object o : map.entrySet()) {
-                                Map.Entry<String, LocalStorageInfo> entry =
-                                        (Map.Entry<String, LocalStorageInfo>) o;
-                                WebsiteAddress address = WebsiteAddress.create(entry.getKey());
-                                if (address == null) continue;
-                                Set<Website> sites = findOrCreateSitesByOrigin(address);
-                                for (Website site : sites) {
-                                    site.setLocalStorageInfo(entry.getValue());
-                                }
-                            }
-                            queue.next();
-                        }
-                    });
-        }
-    }
-
-    private class WebStorageInfoFetcher implements Task {
-        @Override
-        public void run(final TaskQueue queue) {
-            WebsitePreferenceBridge.fetchStorageInfo(
-                    new WebsitePreferenceBridge.StorageInfoReadyCallback() {
-                        @SuppressWarnings("unchecked")
-                        @Override
-                        public void onStorageInfoReady(ArrayList array) {
-                            ArrayList<StorageInfo> infoArray = array;
-                            for (StorageInfo info : infoArray) {
-                                WebsiteAddress address = WebsiteAddress.create(info.getHost());
-                                if (address == null) continue;
-                                Set<Website> sites = findOrCreateSitesByHost(address);
-                                for (Website site : sites) {
-                                    site.addStorageInfo(info);
-                                }
-                            }
-                            queue.next();
-                        }
-                    });
-        }
-    }
-
-    private class ProtectedMediaIdentifierInfoFetcher implements Task {
-        @Override
-        public void run(TaskQueue queue) {
-            for (ProtectedMediaIdentifierInfo info :
-                         WebsitePreferenceBridge.getProtectedMediaIdentifierInfo()) {
-                WebsiteAddress address = WebsiteAddress.create(info.getOrigin());
-                if (address == null) continue;
-                createSiteByOrigin(address).setProtectedMediaIdentifierInfo(info);
-            }
-            queue.next();
-        }
-    }
-
-    private class PushNotificationInfoFetcher implements Task {
-        @Override
-        public void run(TaskQueue queue) {
-            for (PushNotificationInfo info : WebsitePreferenceBridge.getPushNotificationInfo()) {
-                WebsiteAddress address = WebsiteAddress.create(info.getOrigin());
-                if (address == null) continue;
-                createSiteByOrigin(address).setPushNotificationInfo(info);
-            }
-            queue.next();
-        }
-    }
-
-    private class VoiceAndVideoCaptureInfoFetcher implements Task {
-        @Override
-        public void run(TaskQueue queue) {
-            for (VoiceAndVideoCaptureInfo info :
-                    WebsitePreferenceBridge.getVoiceAndVideoCaptureInfo()) {
-                WebsiteAddress address = WebsiteAddress.create(info.getOrigin());
-                if (address == null) continue;
-                createSiteByOrigin(address).setVoiceAndVideoCaptureInfo(info);
-            }
-            queue.next();
-        }
+        WebsitePermissionsFetcher fetcher = new WebsitePermissionsFetcher(new ResultsPopulator());
+        fetcher.fetchPreferencesWithFilter(mCategoryFilter);
     }
 
     private void displayEmptyScreenMessage() {
@@ -292,21 +94,18 @@
         }
     }
 
-    private class ResultsPopulator implements Task {
+    private class ResultsPopulator implements WebsitePermissionsFetcher.WebsitePermissionsCallback {
         @Override
-        public void run(TaskQueue queue) {
-            // Although a preferences fragment should normally be bound to an
-            // activity, crash reports suggest that this isn't always true.
-            // Preferences can't be instantiated without a context, so we are
-            // bailing out.
-            if (getActivity() == null) {
-                queue.next();
-                return;
-            }
+        public void onWebsitePermissionsAvailable(
+                Map<String, Set<Website>> sitesByOrigin, Map<String, Set<Website>> sitesByHost) {
+            // This method may be called after the activity has been destroyed.
+            // In that case, bail out.
+            if (getActivity() == null) return;
+
             // First we scan origins to get settings from there.
-            List<WebsitePreference> websites = new ArrayList<WebsitePreference>();
-            Set<Website> displayedSites = new HashSet<Website>();
-            for (Map.Entry<String, Set<Website>> element : mSitesByOrigin.entrySet()) {
+            List<WebsitePreference> websites = new ArrayList<>();
+            Set<Website> displayedSites = new HashSet<>();
+            for (Map.Entry<String, Set<Website>> element : sitesByOrigin.entrySet()) {
                 for (Website site : element.getValue()) {
                     if (mSearch.isEmpty() || site.getTitle().contains(mSearch)) {
                         websites.add(new WebsitePreference(getActivity(), site, mCategoryFilter));
@@ -315,7 +114,7 @@
                 }
             }
             // Next we add sites that are only accessible by host name.
-            for (Map.Entry<String, Set<Website>> element : mSitesByHost.entrySet()) {
+            for (Map.Entry<String, Set<Website>> element : sitesByHost.entrySet()) {
                 for (Website site : element.getValue()) {
                     if (!displayedSites.contains(site)) {
                         if (mSearch.isEmpty() || site.getTitle().contains(mSearch)) {
@@ -390,8 +189,6 @@
                 updateBlockedHeader(0);
                 updateAllowedHeader(0, true);
             }
-
-            queue.next();
         }
     }
 
@@ -459,37 +256,6 @@
         blockedGroup.setIcon(icon);
     }
 
-    private Website createSiteByOrigin(WebsiteAddress address) {
-        String origin = address.getOrigin();
-        String host = address.getHost();
-        Website site = new Website(address);
-        if (!mSitesByOrigin.containsKey(origin)) {
-            mSitesByOrigin.put(origin, new HashSet<Website>());
-        }
-        mSitesByOrigin.get(origin).add(site);
-        if (!mSitesByHost.containsKey(host)) {
-            mSitesByHost.put(host, new HashSet<Website>());
-        }
-        mSitesByHost.get(host).add(site);
-        return site;
-    }
-
-    private Set<Website> findOrCreateSitesByOrigin(WebsiteAddress address) {
-        String origin = address.getOrigin();
-        if (!mSitesByOrigin.containsKey(origin))
-            createSiteByOrigin(address);
-        return mSitesByOrigin.get(origin);
-    }
-
-    private Set<Website> findOrCreateSitesByHost(WebsiteAddress address) {
-        String host = address.getHost();
-        if (!mSitesByHost.containsKey(host)) {
-            mSitesByHost.put(host, new HashSet<Website>());
-            mSitesByHost.get(host).add(new Website(address));
-        }
-        return mSitesByHost.get(host);
-    }
-
     @Override
     public void onActivityCreated(Bundle savedInstanceState) {
         addPreferencesFromResource(R.xml.website_preferences);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/ButtonCompat.java b/chrome/android/java/src/org/chromium/chrome/browser/widget/ButtonCompat.java
index 357774f..93b3c81 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/widget/ButtonCompat.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/ButtonCompat.java
@@ -49,8 +49,7 @@
      * Returns a new borderless material-style button.
      */
     public static Button createBorderlessButton(Context context) {
-        Context wrapper = new ContextThemeWrapper(context, R.style.ButtonBorderlessCompat);
-        return new Button(wrapper, null, 0);
+        return new Button(new ContextThemeWrapper(context, R.style.ButtonCompatBorderlessOverlay));
     }
 
     /**
@@ -68,10 +67,9 @@
     }
 
     private ButtonCompat(Context context, int buttonColor, AttributeSet attrs) {
-        // To apply the ButtonCompat style to this view, use a ContextThemeWrapper to inject the
-        // ButtonCompat style into the current theme, and pass 0 as the defStyleAttr to the super
-        // constructor to prevent the default Button style from being applied.
-        super(new ContextThemeWrapper(context, R.style.ButtonCompat), attrs, 0);
+        // To apply the ButtonCompat style to this view, use a ContextThemeWrapper to overlay the
+        // ButtonCompatThemeOverlay, which simply sets the buttonStyle to @style/ButtonCompat.
+        super(new ContextThemeWrapper(context, R.style.ButtonCompatOverlay), attrs);
 
         getBackground().mutate();
         setButtonColor(buttonColor);
diff --git a/chrome/android/java/strings/android_chrome_strings.grd b/chrome/android/java/strings/android_chrome_strings.grd
index 52f82f0e..f912ff8 100644
--- a/chrome/android/java/strings/android_chrome_strings.grd
+++ b/chrome/android/java/strings/android_chrome_strings.grd
@@ -115,6 +115,9 @@
       <message name="IDS_LEARN_MORE" desc="Generic label for menu item to learn more about a feature. [CHAR-LIMIT=32]">
         Learn more
       </message>
+      <message name="IDS_MORE" desc="Generic label for a button to show more items or options. [CHAR-LIMIT=20]">
+        More
+      </message>
       <message name="IDS_CLOSE" desc="Content description for a button to close a dialog or popup" >
         Close
       </message>
@@ -247,6 +250,114 @@
         Default
       </message>
 
+      <!-- Privacy preferences -->
+      <message name="IDS_PREFS_PRIVACY" desc="Title for the Privacy preferences. [CHAR-LIMIT=32]">
+        Privacy
+      </message>
+      <message name="IDS_NAVIGATION_ERROR_TITLE" desc="Title for checkbox to enable or disable navigation error suggestions.">
+        Navigation error suggestions
+      </message>
+      <message name="IDS_NAVIGATION_ERROR_SUMMARY" desc="Summary for navigation error suggestions.">
+        Show suggestions when a web address does not resolve or a connection cannot be made
+      </message>
+      <message name="IDS_SEARCH_SUGGESTIONS_TITLE" desc="Title for search and url suggestions.">
+        Search and URL suggestions
+      </message>
+      <message name="IDS_SEARCH_SUGGESTIONS_SUMMARY" desc="Summary for search and url suggestions.">
+        Use a prediction service to show related queries and popular websites as you type in the address bar
+      </message>
+      <message name="IDS_NETWORK_PREDICTIONS_TITLE" desc="Title for Prefetch page resources.">
+        Prefetch page resources
+      </message>
+      <message name="IDS_NETWORK_PREDICTIONS_SUMMARY" desc="Summary for Prefetch page resources.">
+        Improve page load performance
+      </message>
+      <message name="IDS_NETWORK_PREDICTION_ALWAYS_VALUE" desc="Internal value for prefetch page resources preference" translateable="false">
+        network_prediction_always
+      </message>
+      <message name="IDS_NETWORK_PREDICTION_WIFI_ONLY_VALUE" desc="Internal value for prefetch page resources preference" translateable="false">
+        network_prediction_wifi_only
+      </message>
+      <message name="IDS_NETWORK_PREDICTION_NEVER_VALUE" desc="Internal value for prefetch page resources preference" translateable="false">
+        network_prediction_never
+      </message>
+      <message name="IDS_CONTEXTUAL_SEARCH_TITLE" desc="Name for the Contextual Search feature, which allows users to search for a term in a web page by tapping on it.">
+        Touch to Search
+      </message>
+      <message name="IDS_CONTEXTUAL_SEARCH_DESCRIPTION" desc="Description for Contextual Search preference">
+        Learn about topics on websites without leaving the page. Touch to Search sends a word and its surrounding context to Google Search, returning definitions, pictures, search results, and other details.
+
+To adjust your search term, long press to select. To refine your search, slide the panel all the way up and touch the search box.
+      </message>
+      <message name="IDS_CONTEXTUAL_SEARCH_LEARN_MORE_URL" desc="URL for learning more about Contextual Search preference" translateable="false">
+        https://support.google.com/chrome/answer/95440#android
+      </message>
+      <message name="IDS_CRASH_DUMP_UPLOAD_TITLE" desc="Title for crash upload upload preference">
+        Usage and crash reports
+      </message>
+      <message name="IDS_CRASH_DUMP_NEVER_UPLOAD" desc="Settings option never to upload crash reports [CHAR-LIMIT=32]">
+        Never send
+      </message>
+      <message name="IDS_CRASH_DUMP_ALWAYS_UPLOAD" desc="Settings option to upload crash reports only anytime (when on Wi-Fi or mobile data) [CHAR-LIMIT=32]">
+        Always send
+      </message>
+      <message name="IDS_CRASH_DUMP_ONLY_WITH_WIFI" desc="Settings option to upload crash reports only over Wi-Fi [CHAR-LIMIT=32]">
+        Only send on Wi-Fi
+      </message>
+      <message name="IDS_CRASH_DUMP_NEVER_UPLOAD_VALUE" desc="Internal value for crash uploading preference." translateable="false">
+        crash_dump_never_upload
+      </message>
+      <message name="IDS_CRASH_DUMP_ALWAYS_UPLOAD_VALUE" desc="Internal value for crash uploading preference." translateable="false">
+        crash_dump_always_upload
+      </message>
+      <message name="IDS_CRASH_DUMP_ONLY_WITH_WIFI_VALUE" desc="Internal value for crash uploading preference." translateable="false">
+        crash_dump_only_with_wifi
+      </message>
+      <message name="IDS_DO_NOT_TRACK_TITLE" desc="Title for 'Do Not Track' preference">
+        ‘Do Not Track’
+      </message>
+      <message name="IDS_DO_NOT_TRACK_DESCRIPTION" desc="Description for 'Do Not Track' preference">
+        Enabling ‘Do Not Track’ means that a request will be included with your browsing traffic. Any effect depends on whether a website responds to the request, and how the request is interpreted.
+
+For example, some websites may respond to this request by showing you ads that aren’t based on other websites you’ve visited. Many websites will still collect and use your browsing data - for example to improve security, to provide content, ads and recommendations on their websites, and to generate reporting statistics.
+      </message>
+      <message name="IDS_DO_NOT_TRACK_LEARN_MORE_URL" desc="URL for learning more about 'Do Not Track' preference" translateable="false">
+      https://support.google.com/chrome/?p=settings_do_not_track
+      </message>
+      <message name="IDS_CLEAR_BROWSING_DATA_TITLE" desc="Button to Clear Browsing Data [CHAR-LIMIT=24]">
+        Clear browsing data
+      </message>
+      <message name="IDS_CLEAR_CACHE_TITLE" desc="Title for Clear Cache in Clear Browsing Data dialog">
+        Clear the cache
+      </message>
+      <message name="IDS_CLEAR_HISTORY_TITLE" desc="Title for Clear History in Clear Browsing Data dialog">
+        Clear browsing history
+      </message>
+      <message name="IDS_CLEAR_COOKIES_AND_SITE_DATA_TITLE" desc="Title for Clear Cookies and site data in Clear Browsing Data dialog">
+        Clear cookies, site data
+      </message>
+      <message name="IDS_CLEAR_COOKIES_NO_SIGN_OUT_SUMMARY" desc="Text at the bottom of the Clear Browsing Data dialog informing that clearing cookies wouldn't sign the user out of Google Accounts. Dialog also contains a link which opens the Account Management screen.">
+        You won't be signed out of your <ph name="BEGIN_LINK">&lt;link&gt;</ph>Google Accounts<ph name="END_LINK">&lt;/link&gt;</ph>
+      </message>
+      <message name="IDS_CLEAR_PASSWORDS_TITLE" desc="Title for Clear Passwords in Clear Browsing Data preference">
+        Clear saved passwords
+      </message>
+      <message name="IDS_CLEAR_FORMDATA_TITLE" desc="Title for Clear Form Data in Clear Browsing Data preference">
+        Clear autofill data
+      </message>
+      <message name="IDS_CLEAR_BOOKMARKS_TITLE" desc="Title for Clear Bookmarks data in Clear Synced Data dialog">
+        Clear bookmarks
+      </message>
+      <message name="IDS_CLEAR_BROWSING_DATA_PROGRESS_TITLE" desc='Title for the progress dialog used when waiting for "clear browsing data" to complete.'>
+        Clearing browsing data
+      </message>
+      <message name="IDS_CLEAR_DATA_DELETE" desc="Button that allows the user to clear their browsing data. [CHAR-LIMIT=20]">
+        Clear
+      </message>
+      <message name="IDS_CLEAR_BROWSING_DATA_PROGRESS_MESSAGE" desc='Message on the progress dialog used when waiting for "clear browsing data" to complete.'>
+        Please wait…
+      </message>
+
       <!-- Accessibility preferences -->
       <message name="IDS_PREFS_ACCESSIBILITY" desc="Title of Accessibility settings, which allows the user to change webpage font sizes. [CHAR-LIMIT=32]">
         Accessibility
@@ -757,6 +868,11 @@
         Close
       </message>
 
+      <!-- Account chooser infobar strings. -->
+      <message name="IDS_ACCOUNT_CHOOSER_INFOBAR_TITLE" desc="The title text for Account chooser infobar.">
+        Choose an account from your Google Smart Lock.
+      </message>
+
       <!-- TranslateInfoBar -->
       <message name="IDS_TRANSLATE_INFOBAR_TEXT" desc="Text to display on the translate infobar to offer a translate. [CHAR-LIMIT=64]" meaning="Android">
         This page is in <ph name="SOURCE_LANGUAGE">^1<ex>English</ex></ph>. Translate it to <ph name="TARGET_LANGUAGE">^2<ex>FRENCH</ex></ph>?
@@ -767,9 +883,6 @@
       <message name="IDS_TRANSLATE_INFOBAR_TRANSLATION_DONE">
         Translated to <ph name="TARGET_LANGUAGE">%1$s<ex>FRENCH</ex></ph>.
       </message>
-      <message name="IDS_TRANSLATE_INFOBAR_TRANSLATION_MORE_OPTIONS" desc="Text link that will provide more options after a translate [CHAR-LIMIT=24]">
-        More
-      </message>
       <message name="IDS_TRANSLATE_INFOBAR_TRANSLATING">
         Translating page to <ph name="SOURCE_LANGUAGE">%1$s<ex>English</ex></ph>...
       </message>
@@ -909,14 +1022,15 @@
       </message>
 
       <!-- App banner accessibility strings, used for touch exploration -->
-      <message name="IDS_APP_BANNER_VIEW_WEB_APP_ACCESSIBILITY" desc="Accessibililty text: Describes the banner content, including the app name and its URL.">
-        Prompt to add page to the homescreen.  App name: <ph name="APP_NAME">%1$s<ex>Gmail</ex></ph>.  App URL: <ph name="APP_URL">%2$s<ex>http://gmail.com</ex></ph>.
+      <message name="IDS_APP_BANNER_VIEW_NATIVE_APP_ACCESSIBILITY" desc="Accessibililty text: Describes a prompt promoting the installation of an app related to the current page, including the app's name and its rating.">
+        View app <ph name="APP_NAME">%1$s<ex>Gmail</ex></ph> on the Play Store.  Rating: <ph name="APP_RATING">%2$.1f<ex>4.2</ex></ph>.
       </message>
-      <message name="IDS_APP_BANNER_VIEW_NATIVE_APP_ACCESSIBILITY" desc="Accessibililty text: Describes the banner content, including the app name and rating.">
-        Prompt to get app from the Google Play Store.  App name: <ph name="APP_NAME">%1$s<ex>Gmail</ex></ph>.  App average rating: <ph name="APP_RATING">%2$.1f<ex>4.2</ex></ph>.
+      <message name="IDS_APP_BANNER_VIEW_NATIVE_APP_INSTALL_ACCESSIBILITY" desc="Accessibility text: Indicates that clicking on the button will either purchase the app or install it.">
+        Get the app from the Google Play Store: <ph name="APP_ACTION">%s<ex>Install</ex></ph>
       </message>
-      <message name="IDS_APP_BANNER_INSTALL_ACCESSIBILITY" desc="Accessibility text: Indicates that clicking on the button will purchase the app or install it.">
-        Get app from the Google Play Store: <ph name="APP_ACTION">%s<ex>Install</ex></ph>
+
+      <message name="IDS_APP_BANNER_VIEW_WEB_APP_ACCESSIBILITY" desc="Accessibililty text: Details the web app being promoted by the current page, including the app name and its URL.">
+        <ph name="APP_NAME">%1$s<ex>Gmail</ex></ph>, web app. <ph name="APP_URL">%2$s<ex>google.com</ex></ph>
       </message>
 
       <!-- DOM Distiller strings -->
@@ -980,6 +1094,9 @@
       <message name="IDS_MENU_ADD_TO_HOMESCREEN" desc="Menu item for adding a shortcut to the homescreen. [CHAR-LIMIT=27]">
         Add to homescreen
       </message>
+      <message name="IDS_ADDED_TO_HOMESCREEN" desc="Indicates that the current domain was added to the user's homescreen.">
+        <ph name="DOMAIN_NAME">%1$s<ex>google.com</ex></ph> was added to your homescreen
+      </message>
 
       <!-- WebsiteSettingsPopup (PageInfo dialog) -->
       <message name="IDS_PAGE_INFO_COPY_URL_BUTTON" desc="Text in the button that copies the URL to the clipboard.">
@@ -1112,6 +1229,15 @@
       <message name="IDS_CARD_UNMASK_INPUT_HINT" desc="Hint text for an input field containing the user's credit card CVC (card verification code).">
         CVC
       </message>
+      <message name="IDS_CARD_UNMASK_MONTH_HINT" desc="Hint text for an input field containing a credit card expiration month in two digit format.">
+        MM
+      </message>
+      <message name="IDS_CARD_UNMASK_YEAR_HINT" desc="Hint text for an input field containing a credit card expiration year in two digit format.">
+        YY
+      </message>
+      <message name="IDS_CARD_UNMASK_EXPIRATION_DATE_SEPARATOR" desc="Separator for a credit card expiration date, e.g. the slash in 05/16.">
+        /
+      </message>
       <message name="IDS_AUTOFILL_CARD_UNMASK_PROMPT_STORE_LOCALLY" desc="Checkbox text that permits a user to skip past this dialog next time this credit card is used on this device.">
         Don't ask again for this card
       </message>
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/ShortcutHelperTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/ShortcutHelperTest.java
index addf158..3890d98 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/ShortcutHelperTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/ShortcutHelperTest.java
@@ -1,17 +1,20 @@
-// Copyright 2013 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.
 
 package org.chromium.chrome.browser;
 
 import android.content.Intent;
-import android.test.FlakyTest;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.text.TextUtils;
 
+import org.chromium.base.test.util.Feature;
 import org.chromium.base.test.util.UrlUtils;
 import org.chromium.chrome.shell.ChromeShellActivity;
 import org.chromium.chrome.shell.ChromeShellApplication;
 import org.chromium.chrome.shell.ChromeShellApplicationObserver;
 import org.chromium.chrome.shell.ChromeShellTestBase;
+import org.chromium.chrome.test.util.browser.TabLoadObserver;
 import org.chromium.content.browser.test.util.Criteria;
 import org.chromium.content.browser.test.util.CriteriaHelper;
 
@@ -42,12 +45,13 @@
             + "<head><title>" + NORMAL_TITLE + "</title></head>"
             + "<body>Not Webapp capable</body></html>");
 
+    private static final String META_APP_NAME_PAGE_TITLE = "Not the right title";
     private static final String META_APP_NAME_TITLE = "Web application-name";
     private static final String META_APP_NAME_HTML = UrlUtils.encodeHtmlDataUri(
             "<html><head>"
             + "<meta name=\"mobile-web-app-capable\" content=\"yes\" />"
             + "<meta name=\"application-name\" content=\"" + META_APP_NAME_TITLE + "\">"
-            + "<title>Not the right title</title>"
+            + "<title>" + META_APP_NAME_PAGE_TITLE + "</title>"
             + "</head><body>Webapp capable</body></html>");
 
     private static class TestObserver implements ChromeShellApplicationObserver {
@@ -74,6 +78,8 @@
 
     @Override
     public void setUp() throws Exception {
+        super.setUp();
+
         ShortcutHelper.setFullScreenAction(WEBAPP_ACTION_NAME);
         mActivity = launchChromeShellWithBlankPage();
 
@@ -82,19 +88,13 @@
         ChromeShellApplication application =
                 (ChromeShellApplication) mActivity.getApplication();
         application.addObserver(mTestObserver);
-
-        super.setUp();
     }
 
-    /**
-     * @MediumTest
-     * @Feature("{Webapp}")
-     * crbug.com/303486
-     */
-    @FlakyTest
+    @SmallTest
+    @Feature("{Webapp}")
     public void testAddWebappShortcuts() throws InterruptedException {
         // Add a webapp shortcut and make sure the intent's parameters make sense.
-        addShortcutToURL(WEBAPP_HTML, "");
+        addShortcutToURL(WEBAPP_HTML, WEBAPP_TITLE, "");
         Intent firedIntent = mTestObserver.mFiredIntent;
         assertEquals(WEBAPP_TITLE, firedIntent.getStringExtra(Intent.EXTRA_SHORTCUT_NAME));
 
@@ -105,7 +105,7 @@
 
         // Add a second shortcut and make sure it matches the second webapp's parameters.
         mTestObserver.reset();
-        addShortcutToURL(SECOND_WEBAPP_HTML, "");
+        addShortcutToURL(SECOND_WEBAPP_HTML, SECOND_WEBAPP_TITLE, "");
         Intent newFiredIntent = mTestObserver.mFiredIntent;
         assertEquals(SECOND_WEBAPP_TITLE,
                 newFiredIntent.getStringExtra(Intent.EXTRA_SHORTCUT_NAME));
@@ -116,14 +116,10 @@
         assertEquals(mActivity.getPackageName(), newLaunchIntent.getPackage());
     }
 
-    /**
-     * @MediumTest
-     * @Feature("{Webapp}")
-     * crbug.com/303486
-     */
-    @FlakyTest
+    @SmallTest
+    @Feature("{Webapp}")
     public void testAddBookmarkShortcut() throws InterruptedException {
-        addShortcutToURL(NORMAL_HTML, "");
+        addShortcutToURL(NORMAL_HTML, NORMAL_TITLE, "");
 
         // Make sure the intent's parameters make sense.
         Intent firedIntent = mTestObserver.mFiredIntent;
@@ -135,48 +131,45 @@
         assertEquals(NORMAL_HTML, launchIntent.getDataString());
     }
 
-    /**
-     * @MediumTest
-     * @Feature("{Webapp}")
-     * crbug.com/303486
-     */
-    @FlakyTest
+    @SmallTest
+    @Feature("{Webapp}")
     public void testAddWebappShortcutsWithoutTitleEdit() throws InterruptedException {
-        // Add a webapp shortcut to check unedited title.
-        addShortcutToURL(WEBAPP_HTML, "");
+        // Add a webapp shortcut using the page's title.
+        addShortcutToURL(WEBAPP_HTML, WEBAPP_TITLE, "");
         Intent firedIntent = mTestObserver.mFiredIntent;
         assertEquals(WEBAPP_TITLE, firedIntent.getStringExtra(Intent.EXTRA_SHORTCUT_NAME));
     }
 
-    /**
-     * @MediumTest
-     * @Feature("{Webapp}")
-     * crbug.com/303486
-     */
-    @FlakyTest
+    @SmallTest
+    @Feature("{Webapp}")
     public void testAddWebappShortcutsWithTitleEdit() throws InterruptedException {
-        // Add a webapp shortcut to check edited title.
-        addShortcutToURL(WEBAPP_HTML, EDITED_WEBAPP_TITLE);
+        // Add a webapp shortcut with a custom title.
+        addShortcutToURL(WEBAPP_HTML, WEBAPP_TITLE, EDITED_WEBAPP_TITLE);
         Intent firedIntent = mTestObserver.mFiredIntent;
-        assertEquals(EDITED_WEBAPP_TITLE , firedIntent.getStringExtra(Intent.EXTRA_SHORTCUT_NAME));
+        assertEquals(EDITED_WEBAPP_TITLE, firedIntent.getStringExtra(Intent.EXTRA_SHORTCUT_NAME));
     }
 
-    /**
-     * @MediumTest
-     * @Feature("{Webapp}")
-     * crbug.com/303486
-     */
-    @FlakyTest
+    @SmallTest
+    @Feature("{Webapp}")
     public void testAddWebappShortcutsWithApplicationName() throws InterruptedException {
-        // Add a webapp shortcut to check edited title.
-        addShortcutToURL(META_APP_NAME_HTML, "");
+        addShortcutToURL(META_APP_NAME_HTML, META_APP_NAME_PAGE_TITLE, "");
         Intent firedIntent = mTestObserver.mFiredIntent;
-        assertEquals(META_APP_NAME_TITLE , firedIntent.getStringExtra(Intent.EXTRA_SHORTCUT_NAME));
+        assertEquals(META_APP_NAME_TITLE, firedIntent.getStringExtra(Intent.EXTRA_SHORTCUT_NAME));
     }
 
-    private void addShortcutToURL(String url, final String title) throws InterruptedException {
-        loadUrlWithSanitization(url);
-        assertTrue(waitForActiveShellToBeDoneLoading());
+    private void addShortcutToURL(String url, final String expectedPageTitle, final String title)
+            throws InterruptedException {
+        final Tab activeTab = getActivity().getActiveTab();
+        TabLoadObserver observer = new TabLoadObserver(activeTab, url) {
+            @Override
+            public boolean isSatisfied() {
+                // The page title is often updated over several iterations.  Wait until the right
+                // one appears.
+                return super.isSatisfied()
+                        && TextUtils.equals(activeTab.getTitle(), expectedPageTitle);
+            }
+        };
+        assertTrue(CriteriaHelper.pollForUIThreadCriteria(observer));
 
         // Add the shortcut.
         getInstrumentation().runOnMainSync(new Runnable() {
@@ -184,8 +177,7 @@
             public void run() {
                 final ShortcutHelper shortcutHelper = new ShortcutHelper(
                         mActivity.getApplicationContext(), mActivity.getActiveTab());
-                // Calling initialize() isn't strictly required but it is
-                // testing this code path.
+                // Calling initialize() isn't strictly required but it is testing this code path.
                 shortcutHelper.initialize(new ShortcutHelper.OnInitialized() {
                     @Override
                     public void onInitialized(String t) {
@@ -196,7 +188,7 @@
         });
 
         // Make sure that the shortcut was added.
-        assertTrue(CriteriaHelper.pollForCriteria(new Criteria() {
+        assertTrue(CriteriaHelper.pollForUIThreadCriteria(new Criteria() {
             @Override
             public boolean isSatisfied() {
                 return mTestObserver.mFiredIntent != null;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/banners/AppBannerManagerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/banners/AppBannerManagerTest.java
index 8002714..66a66f2 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/banners/AppBannerManagerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/banners/AppBannerManagerTest.java
@@ -11,7 +11,9 @@
 import android.widget.TextView;
 
 import org.chromium.base.ThreadUtils;
+import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.base.test.util.Feature;
+import org.chromium.chrome.ChromeSwitches;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.Tab;
 import org.chromium.chrome.browser.infobar.AnimationHelper;
@@ -93,6 +95,7 @@
         mActivityTab = getActivity().getActiveTab();
         mInfoBarContainer = mActivityTab.getInfoBarContainer();
         mAppBannerManager = mActivityTab.getAppBannerManagerForTesting();
+        AppBannerManager.disableSecureSchemeCheckForTesting();
     }
 
     private boolean waitUntilNoInfoBarsExist() throws Exception {
@@ -109,7 +112,7 @@
             @Override
             public boolean isSatisfied() {
                 return mDetailsDelegate.mNumRetrieved == numExpected
-                        && mAppBannerManager.getNumActiveFetchersForTesting() == 0;
+                        && !mAppBannerManager.isFetcherActiveForTesting();
             }
         });
     }
@@ -130,6 +133,7 @@
         });
     }
 
+    @CommandLineFlags.Add(ChromeSwitches.ENABLE_APP_INSTALL_ALERTS)
     @SmallTest
     @Feature({"AppBanners"})
     public void testBannerAppears() throws Exception {
@@ -147,6 +151,7 @@
         assertTrue(waitUntilAppBannerInfoBarAppears());
     }
 
+    @CommandLineFlags.Add(ChromeSwitches.ENABLE_APP_INSTALL_ALERTS)
     @MediumTest
     @Feature({"AppBanners"})
     public void testBannerAppearsThenDoesNotAppearAgainForMonths() throws Exception {
@@ -197,6 +202,7 @@
         assertTrue(waitUntilAppBannerInfoBarAppears());
     }
 
+    @CommandLineFlags.Add(ChromeSwitches.ENABLE_APP_INSTALL_ALERTS)
     @MediumTest
     @Feature({"AppBanners"})
     public void testBlockedBannerDoesNotAppearAgainForMonths() throws Exception {
@@ -253,4 +259,23 @@
         assertTrue(waitUntilAppDetailsRetrieved(6));
         assertTrue(waitUntilAppBannerInfoBarAppears());
     }
+
+    @CommandLineFlags.Add(ChromeSwitches.ENABLE_APP_INSTALL_ALERTS)
+    @MediumTest
+    @Feature({"AppBanners"})
+    public void testBitmapFetchersCanOverlapWithoutCrashing() throws Exception {
+        // Visit a site that requests a banner rapidly and repeatedly.
+        for (int i = 1; i <= 10; i++) {
+            assertTrue(CriteriaHelper.pollForUIThreadCriteria(
+                    new TabLoadObserver(getActivity().getActiveTab(), NATIVE_APP_URL)));
+
+            final Integer iteration = Integer.valueOf(i);
+            assertTrue(CriteriaHelper.pollForUIThreadCriteria(new Criteria() {
+                @Override
+                public boolean isSatisfied() {
+                    return mDetailsDelegate.mNumRetrieved == iteration;
+                }
+            }));
+        }
+    }
 }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadManagerServiceTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadManagerServiceTest.java
index 2a446b0..bb7391d 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadManagerServiceTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadManagerServiceTest.java
@@ -392,7 +392,8 @@
                 (DownloadManager) getTestContext().getSystemService(Context.DOWNLOAD_SERVICE);
         long downloadId = manager.addCompletedDownload(
                 "test", "test", false, "text/html",
-                UrlUtils.getTestFilePath("clank/download/download.txt"), 4, true);
+                UrlUtils.getIsolatedTestFilePath("chrome/test/data/android/download/download.txt"),
+                4, true);
         MockDownloadNotifier notifier = new MockDownloadNotifier();
         DownloadManagerServiceForTest dService = new DownloadManagerServiceForTest(
                 getTestContext(), notifier, UPDATE_DELAY_FOR_TEST);
@@ -423,7 +424,7 @@
                 .setDownloadId(0)
                 .setMimeType(OMADownloadHandler.OMA_DRM_MESSAGE_MIME)
                 .setFileName("test.gzip")
-                .setUrl(TestHttpServerClient.getUrl("clank/test/data/android/download/test.gzip"))
+                .setUrl(TestHttpServerClient.getUrl("chrome/test/data/android/download/test.gzip"))
                 .build();
         MockDownloadNotifier notifier = new MockDownloadNotifier();
         DownloadManagerServiceForTest dService = new DownloadManagerServiceForTest(
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/preferences/privacy/PrivacyPreferencesManagerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/preferences/privacy/PrivacyPreferencesManagerTest.java
new file mode 100644
index 0000000..a18570e
--- /dev/null
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/preferences/privacy/PrivacyPreferencesManagerTest.java
@@ -0,0 +1,170 @@
+// 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.chrome.browser.preferences.privacy;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.preference.PreferenceManager;
+import android.test.InstrumentationTestCase;
+import android.test.UiThreadTest;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.chromium.base.CommandLine;
+import org.chromium.base.test.util.AdvancedMockContext;
+import org.chromium.base.test.util.Feature;
+import org.chromium.chrome.R;
+
+public class PrivacyPreferencesManagerTest extends InstrumentationTestCase {
+
+    private static final boolean CELLULAR_DEVICE = true;
+    private static final boolean WIFI_DEVICE = false;
+
+    private static final boolean CONNECTED = true;
+    private static final boolean DISCONNECTED = false;
+
+    private static final boolean WIFI_ON = true;
+    private static final boolean WIFI_OFF = false;
+
+    private static final boolean UPLOAD_OK = true;
+    private static final boolean UPLOAD_NOT_PERMITTED = false;
+
+    private static final UserUploadPreference UPLOAD_ALWAYS = UserUploadPreference.ALWAYS;
+    private static final UserUploadPreference UPLOAD_WIFI_ONLY = UserUploadPreference.WIFI_ONLY;
+    private static final UserUploadPreference UPLOAD_NEVER = UserUploadPreference.NEVER;
+
+    // Perform the same test a few times to make sure any sort of
+    // caching still works.
+    private static final int REPS = 3;
+
+    /**
+     * Enum used to specify user upload preference that is easy to read.
+     */
+    private static enum UserUploadPreference {
+        ALWAYS(R.string.crash_dump_always_upload_value),
+        WIFI_ONLY(R.string.crash_dump_only_with_wifi_value),
+        NEVER(R.string.crash_dump_never_upload_value);
+
+        private final int mValueId;
+
+        UserUploadPreference(int valueId) {
+            mValueId = valueId;
+        }
+
+        public String toStringValue(Context context) {
+            return context.getString(mValueId);
+        }
+
+        public boolean toBooleanValue() {
+            return !equals(NEVER);  // return true for WIFI_ONLY for now
+        }
+    }
+
+    @SmallTest
+    @Feature({"Android-AppBase"})
+    @UiThreadTest
+    public void testAllowCrashDumpUploadNowCellDev() {
+        CommandLine.init(null);
+        runTest(CELLULAR_DEVICE, UPLOAD_ALWAYS, CONNECTED, WIFI_ON, UPLOAD_OK);
+        runTest(CELLULAR_DEVICE, UPLOAD_ALWAYS, DISCONNECTED, WIFI_ON, UPLOAD_NOT_PERMITTED);
+        runTest(CELLULAR_DEVICE, UPLOAD_ALWAYS, CONNECTED, WIFI_OFF, UPLOAD_OK);
+        runTest(CELLULAR_DEVICE, UPLOAD_ALWAYS, DISCONNECTED, WIFI_OFF, UPLOAD_NOT_PERMITTED);
+
+        runTest(CELLULAR_DEVICE, UPLOAD_WIFI_ONLY, CONNECTED, WIFI_ON, UPLOAD_OK);
+        runTest(CELLULAR_DEVICE, UPLOAD_WIFI_ONLY, DISCONNECTED, WIFI_ON, UPLOAD_NOT_PERMITTED);
+        runTest(CELLULAR_DEVICE, UPLOAD_WIFI_ONLY, CONNECTED, WIFI_OFF, UPLOAD_NOT_PERMITTED);
+        runTest(CELLULAR_DEVICE, UPLOAD_WIFI_ONLY, DISCONNECTED, WIFI_OFF, UPLOAD_NOT_PERMITTED);
+
+        runTest(CELLULAR_DEVICE, UPLOAD_NEVER, CONNECTED, WIFI_ON, UPLOAD_NOT_PERMITTED);
+        runTest(CELLULAR_DEVICE, UPLOAD_NEVER, DISCONNECTED, WIFI_ON, UPLOAD_NOT_PERMITTED);
+        runTest(CELLULAR_DEVICE, UPLOAD_NEVER, CONNECTED, WIFI_OFF, UPLOAD_NOT_PERMITTED);
+        runTest(CELLULAR_DEVICE, UPLOAD_NEVER, DISCONNECTED, WIFI_OFF, UPLOAD_NOT_PERMITTED);
+    }
+
+    @SmallTest
+    @Feature({"Android-AppBase"})
+    @UiThreadTest
+    public void testAllowCrashDumpUploadNowWifiDev() {
+        CommandLine.init(null);
+        runTest(WIFI_DEVICE, UPLOAD_ALWAYS, CONNECTED, WIFI_ON, UPLOAD_OK);
+        runTest(WIFI_DEVICE, UPLOAD_ALWAYS, DISCONNECTED, WIFI_OFF, UPLOAD_NOT_PERMITTED);
+
+        runTest(WIFI_DEVICE, UPLOAD_NEVER, CONNECTED, WIFI_ON, UPLOAD_NOT_PERMITTED);
+        runTest(WIFI_DEVICE, UPLOAD_NEVER, DISCONNECTED, WIFI_OFF, UPLOAD_NOT_PERMITTED);
+    }
+
+    private void runTest(boolean mobileCapable, UserUploadPreference userPreference,
+            boolean isConnected, boolean wifiOn, boolean uploadPermitted) {
+        PermissionContext context = new PermissionContext(getInstrumentation().getTargetContext());
+        PrivacyPreferencesManager preferenceManager =
+                new MockPrivacyPreferencesManager(context, mobileCapable, isConnected, wifiOn);
+
+        for (int i = 0; i < REPS; i++) {
+            setUpUserPreferences(context, userPreference);
+            String state = String.format(
+                    "[cellular = %b, preference = %b, connected = %b, wifi = %b]",
+                    mobileCapable, userPreference.toBooleanValue(), isConnected, wifiOn);
+            boolean res = preferenceManager.allowUploadCrashDumpNow();
+            if (uploadPermitted) {
+                assertTrue("Upload should be permitted for " + state, res);
+            } else {
+                assertFalse("Upload should NOT be permitted for " + state, res);
+            }
+        }
+    }
+
+    private void setUpUserPreferences(Context context, UserUploadPreference userPreference) {
+        SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(context);
+        SharedPreferences.Editor ed = pref.edit()
+                .putString(PrivacyPreferencesManager.PREF_CRASH_DUMP_UPLOAD,
+                        userPreference.toStringValue(context))
+                .putBoolean(PrivacyPreferencesManager.PREF_CRASH_DUMP_UPLOAD_NO_CELLULAR,
+                        userPreference.toBooleanValue());
+        ed.apply();
+    }
+
+    private static class MockPrivacyPreferencesManager extends PrivacyPreferencesManager {
+        private final boolean mIsMobileCapable;
+        private final boolean mIsConnected;
+        private final boolean mIsWifi;
+
+        MockPrivacyPreferencesManager(Context context, boolean isMobileCapable, boolean isConnected,
+                boolean isWifi) {
+            super(context);
+            mIsMobileCapable = isMobileCapable;
+            mIsConnected = isConnected;
+            mIsWifi = isWifi;
+        }
+
+        @Override
+        public boolean isMobileNetworkCapable() {
+            return mIsMobileCapable;
+        }
+
+        @Override
+        public boolean isNetworkAvailable() {
+            return mIsConnected;
+        }
+
+        @Override
+        public boolean isWiFiOrEthernetNetwork() {
+            return mIsWifi;
+        }
+    }
+
+    private static class PermissionContext extends AdvancedMockContext {
+        public PermissionContext(Context targetContext) {
+            super(targetContext);
+        }
+
+        @Override
+        public Object getSystemService(String name) {
+            if (Context.CONNECTIVITY_SERVICE.equals(name)) {
+                return null;
+            }
+            fail("Should not ask for any other service than the ConnectionManager.");
+            return super.getSystemService(name);
+        }
+    }
+}
diff --git a/chrome/android/shell/java/src/org/chromium/chrome/shell/ChromeShellActivity.java b/chrome/android/shell/java/src/org/chromium/chrome/shell/ChromeShellActivity.java
index f74528e..9f14d5d 100644
--- a/chrome/android/shell/java/src/org/chromium/chrome/shell/ChromeShellActivity.java
+++ b/chrome/android/shell/java/src/org/chromium/chrome/shell/ChromeShellActivity.java
@@ -8,6 +8,7 @@
 import android.app.FragmentManager;
 import android.content.Intent;
 import android.os.Bundle;
+import android.provider.Browser;
 import android.support.v7.app.ActionBarActivity;
 import android.text.TextUtils;
 import android.util.Log;
@@ -29,6 +30,7 @@
 import org.chromium.base.library_loader.ProcessInitException;
 import org.chromium.chrome.browser.DevToolsServer;
 import org.chromium.chrome.browser.FileProviderHelper;
+import org.chromium.chrome.browser.ServiceTabLauncher;
 import org.chromium.chrome.browser.Tab;
 import org.chromium.chrome.browser.appmenu.AppMenuHandler;
 import org.chromium.chrome.browser.appmenu.AppMenuPropertiesDelegate;
@@ -244,10 +246,23 @@
         if (MemoryPressureListener.handleDebugIntent(this, intent.getAction())) return;
 
         String url = getUrlFromIntent(intent);
-        if (!TextUtils.isEmpty(url)) {
-            ChromeShellTab tab = getActiveTab();
-            if (tab != null) tab.loadUrlWithSanitization(url);
+        if (TextUtils.isEmpty(url)) return;
+
+        if (intent.getBooleanExtra(Browser.EXTRA_CREATE_NEW_TAB, false)) {
+            if (mTabManager == null) return;
+
+            Tab newTab = mTabManager.createTab(url, TabLaunchType.FROM_LINK);
+            if (newTab != null && intent.hasExtra(ServiceTabLauncher.LAUNCH_REQUEST_ID_EXTRA)) {
+                ServiceTabLauncher.onWebContentsForRequestAvailable(
+                        intent.getIntExtra(ServiceTabLauncher.LAUNCH_REQUEST_ID_EXTRA, 0),
+                        newTab.getWebContents());
+            }
+
+            return;
         }
+
+        ChromeShellTab tab = getActiveTab();
+        if (tab != null) tab.loadUrlWithSanitization(url);
     }
 
     @Override
diff --git a/chrome/android/shell/java/src/org/chromium/chrome/shell/ChromeShellServiceTabLauncher.java b/chrome/android/shell/java/src/org/chromium/chrome/shell/ChromeShellServiceTabLauncher.java
index 4bf04be2..cb622f8 100644
--- a/chrome/android/shell/java/src/org/chromium/chrome/shell/ChromeShellServiceTabLauncher.java
+++ b/chrome/android/shell/java/src/org/chromium/chrome/shell/ChromeShellServiceTabLauncher.java
@@ -43,6 +43,8 @@
                 break;
         }
 
+        intent.putExtra(ServiceTabLauncher.LAUNCH_REQUEST_ID_EXTRA, requestId);
+
         // TODO(peter): Support |incognito| when ChromeShell supports that.
         // TODO(peter): Support the referrer information, extra headers and post data if
         // ChromeShell gets support for those properties from intent extras.
diff --git a/chrome/app/address_input_strings.grd b/chrome/app/address_input_strings.grd
index a349d682b..d0f003dc 100644
--- a/chrome/app/address_input_strings.grd
+++ b/chrome/app/address_input_strings.grd
@@ -130,7 +130,59 @@
 
   <!-- Chromium translations. -->
   <translations>
-    <part file="address_input_strings_translations.grdp" />
+    <file path="resources/address_input_strings_am.xtb" lang="am" />
+    <file path="resources/address_input_strings_ar.xtb" lang="ar" />
+    <file path="resources/address_input_strings_bg.xtb" lang="bg" />
+    <file path="resources/address_input_strings_bn.xtb" lang="bn" />
+    <file path="resources/address_input_strings_ca.xtb" lang="ca" />
+    <file path="resources/address_input_strings_cs.xtb" lang="cs" />
+    <file path="resources/address_input_strings_da.xtb" lang="da" />
+    <file path="resources/address_input_strings_de.xtb" lang="de" />
+    <file path="resources/address_input_strings_el.xtb" lang="el" />
+    <file path="resources/address_input_strings_en-GB.xtb" lang="en-GB" />
+    <file path="resources/address_input_strings_es.xtb" lang="es" />
+    <file path="resources/address_input_strings_es-419.xtb" lang="es-419" />
+    <file path="resources/address_input_strings_et.xtb" lang="et" />
+    <file path="resources/address_input_strings_fa.xtb" lang="fa" />
+    <file path="resources/address_input_strings_fi.xtb" lang="fi" />
+    <file path="resources/address_input_strings_fil.xtb" lang="fil" />
+    <file path="resources/address_input_strings_fr.xtb" lang="fr" />
+    <file path="resources/address_input_strings_gu.xtb" lang="gu" />
+    <file path="resources/address_input_strings_hi.xtb" lang="hi" />
+    <file path="resources/address_input_strings_hr.xtb" lang="hr" />
+    <file path="resources/address_input_strings_hu.xtb" lang="hu" />
+    <file path="resources/address_input_strings_id.xtb" lang="id" />
+    <file path="resources/address_input_strings_it.xtb" lang="it" />
+    <!-- The translation console uses 'iw' for Hebrew, but we use 'he'. -->
+    <file path="resources/address_input_strings_iw.xtb" lang="he" />
+    <file path="resources/address_input_strings_ja.xtb" lang="ja" />
+    <file path="resources/address_input_strings_kn.xtb" lang="kn" />
+    <file path="resources/address_input_strings_ko.xtb" lang="ko" />
+    <file path="resources/address_input_strings_lt.xtb" lang="lt" />
+    <file path="resources/address_input_strings_lv.xtb" lang="lv" />
+    <file path="resources/address_input_strings_ml.xtb" lang="ml" />
+    <file path="resources/address_input_strings_mr.xtb" lang="mr" />
+    <file path="resources/address_input_strings_ms.xtb" lang="ms" />
+    <file path="resources/address_input_strings_nl.xtb" lang="nl" />
+    <file path="resources/address_input_strings_no.xtb" lang="no" />
+    <file path="resources/address_input_strings_pl.xtb" lang="pl" />
+    <file path="resources/address_input_strings_pt-BR.xtb" lang="pt-BR" />
+    <file path="resources/address_input_strings_pt-PT.xtb" lang="pt-PT" />
+    <file path="resources/address_input_strings_ro.xtb" lang="ro" />
+    <file path="resources/address_input_strings_ru.xtb" lang="ru" />
+    <file path="resources/address_input_strings_sk.xtb" lang="sk" />
+    <file path="resources/address_input_strings_sl.xtb" lang="sl" />
+    <file path="resources/address_input_strings_sr.xtb" lang="sr" />
+    <file path="resources/address_input_strings_sv.xtb" lang="sv" />
+    <file path="resources/address_input_strings_sw.xtb" lang="sw" />
+    <file path="resources/address_input_strings_ta.xtb" lang="ta" />
+    <file path="resources/address_input_strings_te.xtb" lang="te" />
+    <file path="resources/address_input_strings_th.xtb" lang="th" />
+    <file path="resources/address_input_strings_tr.xtb" lang="tr" />
+    <file path="resources/address_input_strings_uk.xtb" lang="uk" />
+    <file path="resources/address_input_strings_vi.xtb" lang="vi" />
+    <file path="resources/address_input_strings_zh-CN.xtb" lang="zh-CN" />
+    <file path="resources/address_input_strings_zh-TW.xtb" lang="zh-TW" />
   </translations>
   <release seq="1" allow_pseudo="false">
     <messages fallback_to_english="true">
diff --git a/chrome/app/address_input_strings_translations.grdp b/chrome/app/address_input_strings_translations.grdp
deleted file mode 100644
index 50605e6..0000000
--- a/chrome/app/address_input_strings_translations.grdp
+++ /dev/null
@@ -1,61 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-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.
--->
-<grit-part>
-  <file path="resources/address_input_strings_am.xtb" lang="am" />
-  <file path="resources/address_input_strings_ar.xtb" lang="ar" />
-  <file path="resources/address_input_strings_bg.xtb" lang="bg" />
-  <file path="resources/address_input_strings_bn.xtb" lang="bn" />
-  <file path="resources/address_input_strings_ca.xtb" lang="ca" />
-  <file path="resources/address_input_strings_cs.xtb" lang="cs" />
-  <file path="resources/address_input_strings_da.xtb" lang="da" />
-  <file path="resources/address_input_strings_de.xtb" lang="de" />
-  <file path="resources/address_input_strings_el.xtb" lang="el" />
-  <file path="resources/address_input_strings_en-GB.xtb" lang="en-GB" />
-  <file path="resources/address_input_strings_es.xtb" lang="es" />
-  <file path="resources/address_input_strings_es-419.xtb" lang="es-419" />
-  <file path="resources/address_input_strings_et.xtb" lang="et" />
-  <file path="resources/address_input_strings_fa.xtb" lang="fa" />
-  <file path="resources/address_input_strings_fi.xtb" lang="fi" />
-  <file path="resources/address_input_strings_fil.xtb" lang="fil" />
-  <file path="resources/address_input_strings_fr.xtb" lang="fr" />
-  <file path="resources/address_input_strings_gu.xtb" lang="gu" />
-  <file path="resources/address_input_strings_hi.xtb" lang="hi" />
-  <file path="resources/address_input_strings_hr.xtb" lang="hr" />
-  <file path="resources/address_input_strings_hu.xtb" lang="hu" />
-  <file path="resources/address_input_strings_id.xtb" lang="id" />
-  <file path="resources/address_input_strings_it.xtb" lang="it" />
-  <!-- The translation console uses 'iw' for Hebrew, but we use 'he'. -->
-  <file path="resources/address_input_strings_iw.xtb" lang="he" />
-  <file path="resources/address_input_strings_ja.xtb" lang="ja" />
-  <file path="resources/address_input_strings_kn.xtb" lang="kn" />
-  <file path="resources/address_input_strings_ko.xtb" lang="ko" />
-  <file path="resources/address_input_strings_lt.xtb" lang="lt" />
-  <file path="resources/address_input_strings_lv.xtb" lang="lv" />
-  <file path="resources/address_input_strings_ml.xtb" lang="ml" />
-  <file path="resources/address_input_strings_mr.xtb" lang="mr" />
-  <file path="resources/address_input_strings_ms.xtb" lang="ms" />
-  <file path="resources/address_input_strings_nl.xtb" lang="nl" />
-  <file path="resources/address_input_strings_no.xtb" lang="no" />
-  <file path="resources/address_input_strings_pl.xtb" lang="pl" />
-  <file path="resources/address_input_strings_pt-BR.xtb" lang="pt-BR" />
-  <file path="resources/address_input_strings_pt-PT.xtb" lang="pt-PT" />
-  <file path="resources/address_input_strings_ro.xtb" lang="ro" />
-  <file path="resources/address_input_strings_ru.xtb" lang="ru" />
-  <file path="resources/address_input_strings_sk.xtb" lang="sk" />
-  <file path="resources/address_input_strings_sl.xtb" lang="sl" />
-  <file path="resources/address_input_strings_sr.xtb" lang="sr" />
-  <file path="resources/address_input_strings_sv.xtb" lang="sv" />
-  <file path="resources/address_input_strings_sw.xtb" lang="sw" />
-  <file path="resources/address_input_strings_ta.xtb" lang="ta" />
-  <file path="resources/address_input_strings_te.xtb" lang="te" />
-  <file path="resources/address_input_strings_th.xtb" lang="th" />
-  <file path="resources/address_input_strings_tr.xtb" lang="tr" />
-  <file path="resources/address_input_strings_uk.xtb" lang="uk" />
-  <file path="resources/address_input_strings_vi.xtb" lang="vi" />
-  <file path="resources/address_input_strings_zh-CN.xtb" lang="zh-CN" />
-  <file path="resources/address_input_strings_zh-TW.xtb" lang="zh-TW" />
-</grit-part>
diff --git a/chrome/app/android/chrome_jni_onload.cc b/chrome/app/android/chrome_jni_onload.cc
index 75afa88..f0986ae 100644
--- a/chrome/app/android/chrome_jni_onload.cc
+++ b/chrome/app/android/chrome_jni_onload.cc
@@ -3,23 +3,17 @@
 // found in the LICENSE file.
 
 #include "base/android/jni_android.h"
-#include "base/android/jni_onload_delegate.h"
+#include "base/bind.h"
 #include "chrome/app/android/chrome_android_initializer.h"
 #include "content/public/app/content_jni_onload.h"
 
 namespace {
 
-class ChromeJNIOnLoadDelegate : public base::android::JNIOnLoadDelegate {
- public:
-  bool RegisterJNI(JNIEnv* env) override;
-  bool Init() override;
-};
-
-bool ChromeJNIOnLoadDelegate::RegisterJNI(JNIEnv* env) {
+bool RegisterJNI(JNIEnv* env) {
   return true;
 }
 
-bool ChromeJNIOnLoadDelegate::Init() {
+bool Init() {
   // TODO(michaelbai): Move the JNI registration from RunChrome() to
   // RegisterJNI().
   return RunChrome();
@@ -30,8 +24,9 @@
 
 // This is called by the VM when the shared library is first loaded.
 JNI_EXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
-  ChromeJNIOnLoadDelegate delegate;
-  if (!content::android::OnJNIOnLoad(vm, &delegate))
+  if (!content::android::OnJNIOnLoadRegisterJNI(
+          vm, base::Bind(&RegisterJNI)) ||
+      !content::android::OnJNIOnLoadInit(base::Bind(&Init)))
     return -1;
 
   return JNI_VERSION_1_4;
diff --git a/chrome/app/chrome_watcher_command_line_unittest_win.cc b/chrome/app/chrome_watcher_command_line_unittest_win.cc
index 6aac76e..b316203 100644
--- a/chrome/app/chrome_watcher_command_line_unittest_win.cc
+++ b/chrome/app/chrome_watcher_command_line_unittest_win.cc
@@ -8,22 +8,29 @@
 
 #include "base/command_line.h"
 #include "base/files/file_path.h"
-#include "base/process/process.h"
+#include "base/process/process_handle.h"
 #include "base/win/scoped_handle.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 TEST(ChromeWatcherCommandLineTest, BasicTest) {
-  base::Process current = base::Process::Open(base::GetCurrentProcId());
-  ASSERT_TRUE(current.IsValid());
+  // Ownership of these handles is passed to the ScopedHandles below via
+  // InterpretChromeWatcherCommandLine().
+  base::ProcessHandle current =
+      ::OpenProcess(PROCESS_QUERY_INFORMATION | SYNCHRONIZE,
+                    TRUE,  // Inheritable
+                    ::GetCurrentProcessId());
+  ASSERT_NE(nullptr, current);
+
   HANDLE event = ::CreateEvent(nullptr, FALSE, FALSE, nullptr);
+  ASSERT_NE(nullptr, event);
 
   base::CommandLine cmd_line = GenerateChromeWatcherCommandLine(
-      base::FilePath(L"example.exe"), current.Handle(), event);
+      base::FilePath(L"example.exe"), current, event);
 
   base::win::ScopedHandle current_result;
   base::win::ScopedHandle event_result;
   ASSERT_TRUE(InterpretChromeWatcherCommandLine(cmd_line, &current_result,
                                                 &event_result));
-  ASSERT_EQ(current.Handle(), current_result.Get());
+  ASSERT_EQ(current, current_result.Get());
   ASSERT_EQ(event, event_result.Get());
 }
diff --git a/chrome/app/chromeos_strings.grdp b/chrome/app/chromeos_strings.grdp
index 3579ce95..fca8c6d 100644
--- a/chrome/app/chromeos_strings.grdp
+++ b/chrome/app/chromeos_strings.grdp
@@ -445,9 +445,18 @@
   <message name="IDS_FILE_BROWSER_SHARE_BUTTON_LABEL" desc="Menu item label, showing dialog to share the selected file.">
     Share
   </message>
+  <message name="IDS_FILE_BROWSER_TASKS_BUTTON_LABEL" desc="Label for button that selects a task to open the current file.">
+    Tasks
+  </message>
   <message name="IDS_FILE_BROWSER_CANCEL_SELECTION_BUTTON_LABEL" desc="Label for button that unselects all selected items.">
     Cancel selection
   </message>
+  <message name="IDS_FILE_BROWSER_CHANGE_TO_LISTVIEW_BUTTON_LABEL" desc="Label for button that changes the view mode to 'list view' mode.">
+    List view
+  </message>
+  <message name="IDS_FILE_BROWSER_CHANGE_TO_THUMBNAILVIEW_BUTTON_LABEL" desc="Label for button that changes the view mode to 'thumbnail view' mode.">
+    Thumbnail view
+  </message>
   <message name="IDS_FILE_BROWSER_CLOUD_IMPORT_TITLE" desc="Title of the cloud import feature.">
     Cloud backup
   </message>
@@ -474,7 +483,7 @@
     Scanning your media device...
   </message>
   <message name="IDS_FILE_BROWSER_CLOUD_IMPORT_STATUS_NO_MEDIA" desc="Cloud import no-new-media-found status message. Shown after backup is completed and when a search for new files to backup produces no result.">
-    All backed up!
+    All backed up to <ph name="BEGIN_LINK">&lt;span class='destination-link'&gt;</ph>Google Drive!<ph name="END_LINK">&lt;/span&gt;</ph>
   </message>
   <message name="IDS_FILE_BROWSER_CLOUD_IMPORT_TOOLTIP_NO_MEDIA" desc="Cloud import no-new-media-found tooltip. Shown after backup is completed and when a search for new files to backup produces no result.">
     All backed up!
@@ -509,6 +518,9 @@
   <message name="IDS_FILE_BROWSER_CLOUD_IMPORT_TOOLTIP_DONE" desc="Cloud import backup is complete tooltip.">
     <ph name="FILE_COUNT">$1<ex>5</ex></ph> photos backed up
   </message>
+  <message name="IDS_FILE_BROWSER_CLOUD_IMPORT_ITEMS_REMAINING" desc="File Manager status message.">
+    Importing <ph name="FILE_COUNT">$1<ex>5</ex></ph> files...
+  </message>
   <message name="IDS_FILE_BROWSER_COPY_FILE_NAME" desc="File Manager status message.">
     Copying <ph name="FILE_NAME">$1<ex>movie.avi</ex></ph>...
   </message>
@@ -5494,22 +5506,28 @@
     Connect to network
   </message>
   <message name="IDS_PORTAL_DETECTION_NOTIFICATION_TITLE_WIFI" desc="Title for the system notification that current Wi-Fi network is behind captive portal">
-    Connect to Wi-Fi
+    Connect to Wi-Fi network
   </message>
   <message name="IDS_PORTAL_DETECTION_NOTIFICATION_MESSAGE_WIRED" desc="Body of the system notification that current wired network is behind captive portal">
     The network you are using (<ph name="NETWORK_ID">$1<ex>Public Network</ex></ph>) may require you to visit its login page.
   </message>
   <message name="IDS_PORTAL_DETECTION_NOTIFICATION_MESSAGE_WIFI" desc="Body of the system notification that current Wi-Fi network is behind captive portal">
-    The Wi-Fi you are using (<ph name="NETWORK_ID">$1<ex>Public Network</ex></ph>) may require you to visit its login page.
+    The Wi-Fi network you are using (<ph name="NETWORK_ID">$1<ex>Public Network</ex></ph>) may require you to visit its login page.
   </message>
-  <message name="IDS_PORTAL_DETECTION_NOTIFICATION_CHOICE_MESSAGE" desc="Body of the system notification that current network is behind captive portal">
-    <ph name="NETWORK_ID">$1<ex>Public Wifi</ex></ph> may require visiting<ph name="LOGIN_URL">$2<ex>portal.ipass.net</ex></ph> to complete your connection..
+  <message name="IDS_PORTAL_DETECTION_NOTIFICATION_MESSAGE_ASK_WIFI" desc="Body of the system notification that asks a user whether to use an extension to authenticate to the network">
+    The Wi-Fi network you are using (<ph name="NETWORK_ID">$1<ex>Public Wifi</ex></ph>) may require authentication.
   </message>
-  <message name="IDS_PORTAL_DETECTION_NOTIFICATION_BUTTON_EXTENSION_TITLE" desc="Text for the button in the captive portal system notification to connect using extension">
-    Connect using <ph name="PORTAL_PROVIDER">$1<ex>iPass</ex></ph>
+  <message name="IDS_PORTAL_DETECTION_NOTIFICATION_MESSAGE_FAILED_WIFI" desc="Body of the system notification that authentication failed and current network is still behind captive portal">
+    Authentication failed. Click to visit the login page for the Wi-Fi network you are using (<ph name="NETWORK_ID">$1<ex>Public Wifi</ex></ph>).
   </message>
-  <message name="IDS_PORTAL_DETECTION_NOTIFICATION_BUTTON_PORTAL_TITLE" desc="Text for the button in the captive portal system notification to open up the captive portal login page">
-    Visit captive portal site
+  <message name="IDS_PORTAL_DETECTION_NOTIFICATION_BUTTON_EXTENSION" desc="Button caption to connect using the authenticating extension">
+    Connect using <ph name="ExtensionName">$1<ex>WiFi Company Authenticator</ex></ph>
+  </message>
+  <message name="IDS_PORTAL_DETECTION_NOTIFICATION_BUTTON_EXTENSION_RETRY" desc="Button caption to connect using the authenticating extension">
+    Retry using <ph name="ExtensionName">$1<ex>WiFi Company Authenticator</ex></ph>
+  </message>
+  <message name="IDS_PORTAL_DETECTION_NOTIFICATION_BUTTON_PORTAL" desc="Button caption to open the captive portal login page.">
+    Visit captive portal login page
   </message>
 
   <!-- Locale Change Notification-->
@@ -5876,51 +5894,6 @@
     For network logs, see: <ph name="DEVICE_LOG_LINK">&lt;a href="chrome://device-log"&gt;chrome://device-log&lt;/a&gt;</ph>
   </message>
 
-  <message name="IDS_DEVICE_LOG_TITLE" desc="Title of the chrome://device-log page">
-    Device Log
-  </message>
-  <message name="IDS_DEVICE_AUTO_REFRESH" desc="Instructions for how to auto-refresh the page">
-    Add a query param in URL to auto-refresh the page: chrome://device-log/?refresh=&lt;sec&gt;
-  </message>
-  <message name="IDS_DEVICE_LOG_REFRESH" desc="Button to refresh device log">
-    Refresh
-  </message>
-  <message name="IDS_DEVICE_LOG_LEVEL_SHOW" desc="'Show' label before logging level checkboxes">
-    Show:
-  </message>
-  <message name="IDS_DEVICE_LOG_LEVEL_ERROR" desc="Error logging level checkbox">
-    Error
-  </message>
-  <message name="IDS_DEVICE_LOG_LEVEL_USER" desc="User logging level checkbox">
-    User
-  </message>
-  <message name="IDS_DEVICE_LOG_LEVEL_EVENT" desc="Event logging level checkbox">
-    Event
-  </message>
-  <message name="IDS_DEVICE_LOG_LEVEL_DEBUG" desc="Debug logging level checkbox">
-    Debug
-  </message>
-  <message name="IDS_DEVICE_LOG_TYPE_LOGIN" desc="Checkbox to enable showing events of type LOGIN">
-    Login
-  </message>
-  <message name="IDS_DEVICE_LOG_TYPE_NETWORK" desc="Checkbox to enable showing events of type NETWORK">
-    Network
-  </message>
-  <message name="IDS_DEVICE_LOG_TYPE_POWER" desc="Checkbox to enable showing events of type POWER">
-    Power
-  </message>
-  <message name="IDS_DEVICE_LOG_FILEINFO" desc="File info checkbox in device event log">
-    File Info
-  </message>
-  <message name="IDS_DEVICE_LOG_TIME_DETAIL" desc="Detailed timestamps checkbox in device event log">
-    Detailed Timestamps
-  </message>
-  <message name="IDS_DEVICE_LOG_ENTRY" desc="Format string for lines in chrome://device-log.">
-    [<ph name="TIMESTAMP">$1<ex>Timestamp</ex></ph>]
-    <ph name="FILE_INFO">$2<ex>file:123</ex></ph>
-    <ph name="EVENT_NAME">$3<ex>Event Description</ex></ph>
-  </message>
-
   <!-- About NFC Debug UI display strings. They aren't being internationalized bacause this page is meant for debugging only. -->
   <message name="IDS_NFC_DEBUG_TITLE" desc="Title of the debug page meant for developers to test NFC functionality" translateable="false">
     NFC API Test Page
@@ -6105,6 +6078,6 @@
     If set, automatic timezone update by the current geolocation is turned off.
   </message>
   <message name="IDS_OPTIONS_RESOLVE_TIMEZONE_BY_GEOLOCATION_DESCRIPTION" desc="Label for checkbox to allow automatic timezone update by user geolocation.">
-    Automatically resolve timezone by geolocation
+    Set time zone automatically using your location
   </message>
 </grit-part>
diff --git a/chrome/app/chromium_strings.grd b/chrome/app/chromium_strings.grd
index 3fde6d0..28c6724 100644
--- a/chrome/app/chromium_strings.grd
+++ b/chrome/app/chromium_strings.grd
@@ -1223,7 +1223,7 @@
         Touch to Search
       </message>
       <message name="IDS_CONTEXTUAL_SEARCH_PROMO_DESCRIPTION_2" desc="Part 2 of the description of the Contextual Search feature. See explanation in DESCRIPTION_1.">
-        sends a word and its surrounding context to Google Search, returning definitions, pictures, search results, and other details.
+        sends a word and its surrounding context to Google Search, returning definitions, pictures, and other search results.
       </message>
       <message name="IDS_CONTEXTUAL_SEARCH_PROMO_OPTIN" desc="Text for the button the user clicks to opt in.">
         Accept &amp; Search
@@ -1316,6 +1316,52 @@
         </message>
       </if>
 
+      <!-- About device logs UI display strings -->
+      <message name="IDS_DEVICE_LOG_TITLE" desc="Title of the chrome://device-log page">
+        Device Log
+      </message>
+      <message name="IDS_DEVICE_AUTO_REFRESH" desc="Instructions for how to auto-refresh the page">
+        Add a query param in URL to auto-refresh the page: chrome://device-log/?refresh=&lt;sec&gt;
+      </message>
+      <message name="IDS_DEVICE_LOG_REFRESH" desc="Button to refresh device log">
+        Refresh
+      </message>
+      <message name="IDS_DEVICE_LOG_LEVEL_SHOW" desc="'Show' label before logging level checkboxes">
+        Show:
+      </message>
+      <message name="IDS_DEVICE_LOG_LEVEL_ERROR" desc="Error logging level checkbox">
+        Error
+      </message>
+      <message name="IDS_DEVICE_LOG_LEVEL_USER" desc="User logging level checkbox">
+        User
+      </message>
+      <message name="IDS_DEVICE_LOG_LEVEL_EVENT" desc="Event logging level checkbox">
+        Event
+      </message>
+      <message name="IDS_DEVICE_LOG_LEVEL_DEBUG" desc="Debug logging level checkbox">
+        Debug
+      </message>
+      <message name="IDS_DEVICE_LOG_TYPE_LOGIN" desc="Checkbox to enable showing events of type LOGIN">
+        Login
+      </message>
+      <message name="IDS_DEVICE_LOG_TYPE_NETWORK" desc="Checkbox to enable showing events of type NETWORK">
+        Network
+      </message>
+      <message name="IDS_DEVICE_LOG_TYPE_POWER" desc="Checkbox to enable showing events of type POWER">
+        Power
+      </message>
+      <message name="IDS_DEVICE_LOG_FILEINFO" desc="File info checkbox in device event log">
+        File Info
+      </message>
+      <message name="IDS_DEVICE_LOG_TIME_DETAIL" desc="Detailed timestamps checkbox in device event log">
+        Detailed Timestamps
+      </message>
+      <message name="IDS_DEVICE_LOG_ENTRY" desc="Format string for lines in chrome://device-log.">
+        [<ph name="TIMESTAMP">$1<ex>Timestamp</ex></ph>]
+        <ph name="FILE_INFO">$2<ex>file:123</ex></ph>
+        <ph name="EVENT_NAME">$3<ex>Event Description</ex></ph>
+      </message>
+
     </messages>
   </release>
 </grit>
diff --git a/chrome/app/client_util.cc b/chrome/app/client_util.cc
index 738ae9e..97ff58e 100644
--- a/chrome/app/client_util.cc
+++ b/chrome/app/client_util.cc
@@ -5,6 +5,7 @@
 #include <windows.h>
 #include <shlwapi.h>
 
+#include "base/base_paths.h"
 #include "base/command_line.h"
 #include "base/compiler_specific.h"
 #include "base/environment.h"
@@ -12,6 +13,7 @@
 #include "base/lazy_instance.h"
 #include "base/logging.h"
 #include "base/memory/scoped_ptr.h"
+#include "base/path_service.h"
 #include "base/strings/string16.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
@@ -27,6 +29,7 @@
 #include "chrome/app/image_pre_reader_win.h"
 #include "chrome/chrome_watcher/chrome_watcher_main_api.h"
 #include "chrome/common/chrome_constants.h"
+#include "chrome/common/chrome_paths.h"
 #include "chrome/common/chrome_result_codes.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/env_vars.h"
@@ -179,6 +182,8 @@
   }
 
   if (process_type_ == "watcher") {
+    chrome::RegisterPathProvider();
+
     base::win::ScopedHandle parent_process;
     base::win::ScopedHandle on_initialized_event;
     if (!InterpretChromeWatcherCommandLine(cmd_line, &parent_process,
@@ -186,6 +191,10 @@
       return chrome::RESULT_CODE_UNSUPPORTED_PARAM;
     }
 
+    base::FilePath watcher_data_directory;
+    if (!PathService::Get(chrome::DIR_WATCHER_DATA, &watcher_data_directory))
+      return chrome::RESULT_CODE_MISSING_DATA;
+
     // Intentionally leaked.
     HMODULE watcher_dll = Load(&version, &file);
     if (!watcher_dll)
@@ -195,7 +204,8 @@
         reinterpret_cast<ChromeWatcherMainFunction>(
             ::GetProcAddress(watcher_dll, kChromeWatcherDLLEntrypoint));
     return watcher_main(chrome::kBrowserExitCodesRegistryPath,
-                        parent_process.Take(), on_initialized_event.Take());
+                        parent_process.Take(), on_initialized_event.Take(),
+                        watcher_data_directory.value().c_str());
   }
 
   // Initialize the sandbox services.
@@ -260,11 +270,14 @@
 
     // Launch the watcher process if stats collection consent has been granted.
     if (g_chrome_crash_client.Get().GetCollectStatsConsent()) {
-      base::char16 exe_path[MAX_PATH];
-      ::GetModuleFileNameW(nullptr, exe_path, arraysize(exe_path));
-      ChromeWatcherClient watcher_client(base::Bind(
-          &GenerateChromeWatcherCommandLine, base::FilePath(exe_path)));
-      watcher_client.LaunchWatcher();
+      base::FilePath exe_path;
+      if (PathService::Get(base::FILE_EXE, &exe_path)) {
+        ChromeWatcherClient watcher_client(
+            base::Bind(&GenerateChromeWatcherCommandLine, exe_path));
+        watcher_client.LaunchWatcher();
+      } else {
+        NOTREACHED();
+      }
     }
   }
 }
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 923ff2b..7108c959 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -767,7 +767,10 @@
           <message name="IDS_CONTENT_CONTEXT_OPENIMAGENEWTAB" desc="The name of the Open Image in New Tab command in the content area context menu">
             Open &amp;image in new tab
           </message>
-
+          <message name="IDS_CONTENT_CONTEXT_OPEN_ORIGINAL_IMAGE_NEW_TAB" desc="The name of the Open Original Image in New Tab command in the content area context menu">
+            Open original &amp;image in new tab
+          </message>
+          
           <message name="IDS_CONTENT_CONTEXT_LOOP" desc="The name of the Loop command for audio and video playback in the content area context menu">
             &amp;Loop
           </message>
@@ -967,6 +970,9 @@
           <message name="IDS_CONTENT_CONTEXT_OPENIMAGENEWTAB" desc="In Title Case: The name of the Open Image in New Tab command in the content area context menu">
             Open &amp;Image in New Tab
           </message>
+          <message name="IDS_CONTENT_CONTEXT_OPEN_ORIGINAL_IMAGE_NEW_TAB" desc="In Title Case: The name of the Open Original Image in New Tab command in the content area context menu">
+            Open Original &amp;Image in New Tab
+          </message>
 
           <message name="IDS_CONTENT_CONTEXT_LOOP" desc="In Title Case: The name of the Loop command for audio and video playback in the content area context menu">
             &amp;Loop
@@ -2190,16 +2196,32 @@
       </message>
 
       <!-- Bookmark app bubble -->
-      <if expr="use_titlecase">
-        <message name="IDS_BOOKMARK_APP_BUBBLE_TITLE" desc="Title of the bubble to add a bookmark app.">
-          Add Shortcut
+      <if expr="is_win">
+        <message name="IDS_ADD_TO_TASKBAR_BUBBLE_TITLE" desc="Title of the bubble for adding a bookmark app to the taskbar.">
+          Add to taskbar
+        </message>
+      </if>
+      <if expr="is_macosx">
+        <message name="IDS_ADD_TO_APPLICATIONS_BUBBLE_TITLE" desc="Title of the bubble adding a bookmark app to the Applications Folder.">
+          Add to Applications
+        </message>
+      </if>
+      <if expr="use_ash">
+        <message name="IDS_ADD_TO_SHELF_BUBBLE_TITLE" desc="Title of the bubble for adding a bookmark app to the shelf.">
+          Add to shelf
         </message>
       </if>
       <if expr="not use_titlecase">
-        <message name="IDS_BOOKMARK_APP_BUBBLE_TITLE" desc="Title of the bubble to add a bookmark app.">
-          Add shortcut
+        <message name="IDS_ADD_TO_DESKTOP_BUBBLE_TITLE" desc="Title of the bubble for adding a bookmark app to the desktop.">
+          Add to desktop
         </message>
       </if>
+      <if expr="use_titlecase">
+        <message name="IDS_ADD_TO_DESKTOP_BUBBLE_TITLE" desc="Title of the bubble for adding a bookmark app to the desktop.">
+          Add to Desktop
+        </message>
+      </if>
+
       <message name="IDS_BOOKMARK_APP_AX_BUBBLE_NAME_LABEL" desc="Text preceding the name of a bookmark app, read by spoken feedback.">
         Shortcut name
       </message>
@@ -2271,7 +2293,7 @@
           Permissions
         </message>
         <message name="IDS_APPLICATION_INFO_UNINSTALL_BUTTON_TEXT" desc="Text for the button that removes (uninstalls) the app or extension.">
-          Remove
+          Remove...
         </message>
         <message name="IDS_APPLICATION_INFO_LICENSES_BUTTON_TEXT" desc="Text for the button that displays the licenses for the app or extension.">
           Licenses
@@ -2283,7 +2305,7 @@
           Version:
         </message>
         <message name="IDS_APPLICATION_INFO_CREATE_SHORTCUTS_BUTTON_TEXT" desc="Text for the button that opens the dialog to create shortcuts for the app.">
-          Create shortcuts
+          Create shortcuts...
         </message>
       </if>
       <if expr="use_titlecase">
@@ -2300,7 +2322,7 @@
           Permissions
         </message>
         <message name="IDS_APPLICATION_INFO_UNINSTALL_BUTTON_TEXT" desc="In Title Case: Text for the button that removes (uninstalls) the app or extension.">
-          Remove
+          Remove...
         </message>
         <message name="IDS_APPLICATION_INFO_LICENSES_BUTTON_TEXT" desc="In Title Case: Text for the button that displays the licenses for the app or extension.">
           Licenses
@@ -2312,7 +2334,7 @@
           Version:
         </message>
         <message name="IDS_APPLICATION_INFO_CREATE_SHORTCUTS_BUTTON_TEXT" desc="In Title Case: Text for the button that opens the dialog to create shortcuts for the app.">
-          Create Shortcuts
+          Create Shortcuts...
         </message>
       </if>
       <message name="IDS_APPLICATION_INFO_SIZE_LOADING_LABEL" desc="Text displayed instead of the app or extension's size on disk in the details section of the dialog while the size is being calculated.">
@@ -6121,11 +6143,11 @@
         <message name="IDS_FLAGS_DISABLE_TOUCH_FEEDBACK_DESCRIPTION" desc="Description for the flag that disables additional visual feedback for touch.">
           Certain UI components will stop displaying visual feedback upon touch interactions.
         </message>
-        <message name="IDS_FLAGS_ASH_DISABLE_SWIPE_TO_CLOSE_IN_OVERVIEW_MODE_NAME" desc="Title for the flag to disable swipe gestures to close windows while in overview mode.">
+        <message name="IDS_FLAGS_ASH_ENABLE_SWIPE_TO_CLOSE_IN_OVERVIEW_MODE_NAME" desc="Title for the flag to enable swipe gestures to close windows while in overview mode.">
           Swipe to dismiss windows in overview mode.
         </message>
-        <message name="IDS_FLAGS_ASH_DISABLE_SWIPE_TO_CLOSE_IN_OVERVIEW_MODE_DESCRIPTION" desc="Description for the flag to disable swipe gestures to close windows while in overview mode.">
-          Disable swipe gesture to dismiss windows in overview mode.
+        <message name="IDS_FLAGS_ASH_ENABLE_SWIPE_TO_CLOSE_IN_OVERVIEW_MODE_DESCRIPTION" desc="Description for the flag to enable swipe gestures to close windows while in overview mode.">
+          Enable swipe gesture to dismiss windows in overview mode.
         </message>
         <message name="IDS_FLAGS_ASH_DISABLE_TEXT_FILTERING_IN_OVERVIEW_MODE_NAME" desc="Title for the flag to disable window filtering in overview mode by inputing text">
           Disable text filtering in Overview Mode.
@@ -6650,11 +6672,11 @@
           Do not use Smart Lock, which allows you to unlock your Chromebook when in proximity to your phone. If you are the owner of the device, this also turns off Smart Lock on sign-in screen.
         </message>
       </if>
-      <message name="IDS_FLAGS_DISABLE_NEW_BOOKMARK_APPS_NAME" desc="Name of the flag to disable the new bookmark app system.">
-        Disable the new bookmark app system.
+      <message name="IDS_FLAGS_NEW_BOOKMARK_APPS_NAME" desc="Name of the flag to enable the new bookmark app system.">
+        Enable the new bookmark app system.
       </message>
-      <message name="IDS_FLAGS_DISABLE_NEW_BOOKMARK_APPS_DESCRIPTION" desc="Description for the flag to disable the new bookmark app system.">
-        Disables the new system for creating bookmark apps.
+      <message name="IDS_FLAGS_NEW_BOOKMARK_APPS_DESCRIPTION" desc="Description for the flag to enable the new bookmark app system.">
+        Enables the new system for creating bookmark apps.
       </message>
       <message name="IDS_FLAGS_DISABLE_HOSTED_APP_SHIM_CREATION_NAME" desc="Name of the flag to enable creation of app shims for hosted apps on Mac.">
         Disable creation of app shims for hosted apps on Mac.
@@ -6833,6 +6855,12 @@
       <message name="IDS_FLAGS_ENABLE_WIFI_CREDENTIAL_SYNC_DESCRIPTION" desc="Decription for the flag to enable WiFi credential sync, a feature which enables synchronizing WiFi network settings across devices.">
         Enables synchronizing WiFi network settings across devices. When enabled, the WiFi credential datatype is registered with Chrome Sync, and WiFi credentials are synchronized subject to user preferences. (See also, chrome://settings/syncSetup.)
       </message>
+      <message name="IDS_FLAGS_SYNC_SANDBOX_NAME" desc="Name for the flag that causes Chrome to use the sandbox (testing) server for Sync.">
+        Use Chrome Sync sandbox
+      </message>
+      <message name="IDS_FLAGS_SYNC_SANDBOX_DESCRIPTION" desc="Description for the flag that causes Chrome to use the sandbox (testing) server for Sync.">
+        Connects to the testing server for Chrome Sync.
+      </message>
 
       <!-- Crashes -->
       <message name="IDS_CRASHES_TITLE" desc="Title for the chrome://crashes page.">
@@ -9961,6 +9989,11 @@
         Connect
       </message>
 
+      <!-- Strings for the certificate selection dialog triggered by platformKeys -->
+      <message name="IDS_PLATFORM_KEYS_SELECT_CERT_DIALOG_TEXT" desc="The text in the dialog that prompts the user to select a certificate when an extension requests certificates through the platformKeys API.">
+        Extension '<ph name="EXTENSION_NAME">$1<ex>Better OpenVPN App</ex></ph>' has requested a certificate. Selecting a certificate will grant the extension the ability to use this identity with servers now and in the future. You should only select a certificate if you trust the extension.
+      </message>
+
       <!-- Misc strings for SSL UI -->
       <message name="IDS_UNSAFE_FRAME_MESSAGE" desc="The text displayed in the content that is subsituted to an unsafe frame.">
         This frame was blocked because it contains some insecure content.
@@ -10291,6 +10324,9 @@
         <message name="IDS_OPTIONS_DEFAULTSEARCH_MANAGE_ENGINES" desc="The label of the 'Manage search engines' button">
           Manage search engines...
         </message>
+        <message name="IDS_OPTIONS_ENABLE_GOOGLE_NOW" desc="The label of the 'Enable Google Now' checkbox in search options.">
+          Show Google Now cards in the launcher
+        </message>
         <message name="IDS_OPTIONS_HOMEPAGE_NTP" desc="The value displayed on the settings page to indicate that we're using the new tab page as the homepage">
           New Tab page
         </message>
@@ -11098,6 +11134,12 @@
         </message>
       </if>
 
+      <message name="IDS_AUTOFILL_CARD_UNMASK_PROMPT_TITLE" desc="Title for the credit card unmasking dialog.">
+        Verify your card <ph name="CREDIT_CARD">$1<ex>Visa - 5679</ex></ph>
+      </message>
+      <message name="IDS_AUTOFILL_CARD_UNMASK_PROMPT_UPDATE_TITLE" desc="Title for the credit card unmasking dialog when the credit card is expired.">
+        Update and verify your card <ph name="CREDIT_CARD">$1<ex>Visa - 5679</ex></ph>
+      </message>
       <message name="IDS_AUTOFILL_CARD_UNMASK_PROMPT_INSTRUCTIONS" desc="Text explaining what the user should do in the card unmasking dialog.">
         Enter the three digit verification code from the back of your credit card
       </message>
@@ -13493,6 +13535,12 @@
       <message name="IDS_MANAGE_PASSWORDS_IDENTITY_PROVIDER" desc="The description of an identity provider in the Account Chooser dialog.">
         via <ph name="PROVIDER">$1<ex>facebook.com</ex></ph>
       </message>
+      <message name="IDS_MANAGE_PASSWORDS_AUTO_SIGNIN_TITLE" desc="The title of the auto-signin toast.">
+        Signing in with Google Smart Lock as '<ph name="FULL_NAME">$1<ex>Brad Pitt</ex></ph>'
+      </message>
+      <message name="IDS_MANAGE_PASSWORDS_DELETED_ACCOUNT" desc="The text that is used in the manage accounts bubble when an account is deleted.">
+        Account removed
+      </message>
 
       <message name="IDS_ALLOW_AUTOFILL_SYNC_CREDENTIAL" desc="The text for the choice to allow autofilling in about:flags">
         Allow
@@ -14555,7 +14603,13 @@
           Click to start voice search
         </message>
         <message name="IDS_APP_LIST_HOTWORD_LISTENING" desc="Tooltip text for the mic button when it waits for the hotword.">
-          Click or say "Ok, Google" to start voice search
+          Click, or say "Ok Google" to start voice search
+        </message>
+        <message name="IDS_SEARCH_BOX_HINT" desc="Hint text for the search box in app list window.">
+          Search or type URL
+        </message>
+        <message name="IDS_SEARCH_BOX_HOTWORD_HINT" desc="Hint text for the search box in app list window when the hotword is enabled.">
+          Search, or say "Ok Google"
         </message>
         <if expr="not use_titlecase">
           <message name="IDS_APP_LIST_CONTEXT_MENU_NEW_TAB" desc="Title text for the 'open new' context menu item of an app list item configured to open in a tab">
@@ -14574,12 +14628,6 @@
                    desc="Title text for the 'uninstall' context menu item of an app list item.">
             Uninstall...
           </message>
-          <message name="IDS_SEARCH_BOX_HINT" desc="Hint text for the search box in app list window.">
-            Search
-          </message>
-          <message name="IDS_SEARCH_BOX_HOTWORD_HINT" desc="Hint text for the search box in app list window when the hotword is enabled.">
-            Search, or say "Ok Google"
-          </message>
           <message name="IDS_APP_LIST_SIGNIN_BUTTON" desc="App launcher sign-in button text.">
             Sign in
           </message>
@@ -14619,12 +14667,6 @@
                    desc="Title text for the 'uninstall' context menu item of an app list item.">
             Uninstall...
           </message>
-          <message name="IDS_SEARCH_BOX_HINT" desc="Hint text for the search box in app list window.">
-            Search
-          </message>
-          <message name="IDS_SEARCH_BOX_HOTWORD_HINT" desc="Hint text for the search box in app list window when the hotword is enabled.">
-            Search or say "Ok, Google"
-          </message>
           <message name="IDS_APP_LIST_SIGNIN_BUTTON" desc="App launcher sign-in button text.">
             Sign in
           </message>
diff --git a/chrome/app/google_chrome_strings.grd b/chrome/app/google_chrome_strings.grd
index b070a11f..a7cbc31a 100644
--- a/chrome/app/google_chrome_strings.grd
+++ b/chrome/app/google_chrome_strings.grd
@@ -1146,7 +1146,7 @@
         Touch to Search
       </message>
       <message name="IDS_CONTEXTUAL_SEARCH_PROMO_DESCRIPTION_2" desc="Part 2 of the description of the Contextual Search feature. See explanation in DESCRIPTION_1.">
-        sends a word and its surrounding context to Google Search, returning definitions, pictures, search results, and other details.
+        sends a word and its surrounding context to Google Search, returning definitions, pictures, and other search results.
       </message>
       <message name="IDS_CONTEXTUAL_SEARCH_PROMO_OPTIN" desc="Text for the button the user clicks to opt in.">
         Accept &amp; Search
diff --git a/chrome/app/theme/default_100_percent/common/app_list_mic_hotword_off.png b/chrome/app/theme/default_100_percent/common/app_list_mic_hotword_off.png
index db78128..9f7db87 100644
--- a/chrome/app/theme/default_100_percent/common/app_list_mic_hotword_off.png
+++ b/chrome/app/theme/default_100_percent/common/app_list_mic_hotword_off.png
Binary files differ
diff --git a/chrome/app/theme/default_100_percent/common/app_list_mic_hotword_on.png b/chrome/app/theme/default_100_percent/common/app_list_mic_hotword_on.png
new file mode 100644
index 0000000..ad216dae
--- /dev/null
+++ b/chrome/app/theme/default_100_percent/common/app_list_mic_hotword_on.png
Binary files differ
diff --git a/chrome/app/theme/default_100_percent/cros/icon_more_vert_white.png b/chrome/app/theme/default_100_percent/cros/icon_more_vert_white.png
new file mode 100644
index 0000000..fdc4a5a
--- /dev/null
+++ b/chrome/app/theme/default_100_percent/cros/icon_more_vert_white.png
Binary files differ
diff --git a/chrome/app/theme/default_200_percent/common/app_list_mic_hotword_off.png b/chrome/app/theme/default_200_percent/common/app_list_mic_hotword_off.png
index 3282e40..9d82452 100644
--- a/chrome/app/theme/default_200_percent/common/app_list_mic_hotword_off.png
+++ b/chrome/app/theme/default_200_percent/common/app_list_mic_hotword_off.png
Binary files differ
diff --git a/chrome/app/theme/default_200_percent/common/app_list_mic_hotword_on.png b/chrome/app/theme/default_200_percent/common/app_list_mic_hotword_on.png
new file mode 100644
index 0000000..26510654
--- /dev/null
+++ b/chrome/app/theme/default_200_percent/common/app_list_mic_hotword_on.png
Binary files differ
diff --git a/chrome/app/theme/default_200_percent/cros/icon_more_vert_white.png b/chrome/app/theme/default_200_percent/cros/icon_more_vert_white.png
new file mode 100644
index 0000000..2955c02
--- /dev/null
+++ b/chrome/app/theme/default_200_percent/cros/icon_more_vert_white.png
Binary files differ
diff --git a/chrome/app/theme/theme_resources.grd b/chrome/app/theme/theme_resources.grd
index 742df53..445a8f7 100644
--- a/chrome/app/theme/theme_resources.grd
+++ b/chrome/app/theme/theme_resources.grd
@@ -52,6 +52,7 @@
       <if expr="enable_app_list">
         <structure type="chrome_scaled_image" name="IDR_APP_LIST_TAB_OVERLAY" file="common/app_list_v1_overlay.png" />
         <structure type="chrome_scaled_image" name="IDR_APP_LIST_MIC_HOTWORD_OFF" file="common/app_list_mic_hotword_off.png" />
+        <structure type="chrome_scaled_image" name="IDR_APP_LIST_MIC_HOTWORD_ON" file="common/app_list_mic_hotword_on.png" />
       </if>
       <if expr="toolkit_views and not is_macosx">
         <structure type="chrome_scaled_image" name="IDR_APP_WINDOW_CLOSE" file="common/app_window_close.png" />
@@ -391,6 +392,7 @@
       </if>
       <if expr="chromeos">
         <structure type="chrome_scaled_image" name="IDR_ICON_ADD_USER_WHITE" file="cros/icon_add_user_white.png" />
+        <structure type="chrome_scaled_image" name="IDR_ICON_MORE_VERT_WHITE" file="cros/icon_more_vert_white.png" />
         <structure type="chrome_scaled_image" name="IDR_ICON_GUEST_WHITE" file="cros/icon_guest_white.png" />
         <structure type="chrome_scaled_image" name="IDR_ICON_POWER_WHITE" file="cros/icon_power_white.png" />
       </if>
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 6619495..1f084a9 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -88,6 +88,7 @@
     "//components/content_settings/core/common",
     "//components/crx_file",
     "//components/data_reduction_proxy/core/browser",
+    "//components/device_event_log",
     "//components/domain_reliability",
     "//components/enhanced_bookmarks",
     "//components/favicon_base",
diff --git a/chrome/browser/DEPS b/chrome/browser/DEPS
index 2f13d99..511fed1 100644
--- a/chrome/browser/DEPS
+++ b/chrome/browser/DEPS
@@ -30,6 +30,7 @@
   "+components/crx_file",
   "+components/data_reduction_proxy",
   "+components/network_hints",
+  "+components/device_event_log",
   "+components/dom_distiller",
   "+components/domain_reliability",
   "+components/enhanced_bookmarks",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index e63b0fe..04aed4e 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -954,12 +954,12 @@
     SINGLE_VALUE_TYPE(ash::switches::kAshDebugShortcuts),
   },
   {
-    "ash-disable-swipe-to-close-in-overview-mode",
-    IDS_FLAGS_ASH_DISABLE_SWIPE_TO_CLOSE_IN_OVERVIEW_MODE_NAME,
-    IDS_FLAGS_ASH_DISABLE_SWIPE_TO_CLOSE_IN_OVERVIEW_MODE_DESCRIPTION,
+    "ash-enable-swipe-to-close-in-overview-mode",
+    IDS_FLAGS_ASH_ENABLE_SWIPE_TO_CLOSE_IN_OVERVIEW_MODE_NAME,
+    IDS_FLAGS_ASH_ENABLE_SWIPE_TO_CLOSE_IN_OVERVIEW_MODE_DESCRIPTION,
     // TODO(bruthig): Add kOsWin when http://crbug.com/333758 is resolved.
     kOsCrOS,
-    SINGLE_VALUE_TYPE(ash::switches::kAshDisableSwipeToCloseInOverviewMode),
+    SINGLE_VALUE_TYPE(ash::switches::kAshEnableSwipeToCloseInOverviewMode),
   },
   { "ash-enable-touch-view-testing",
     IDS_FLAGS_ASH_ENABLE_TOUCH_VIEW_TESTING_NAME,
@@ -1537,10 +1537,11 @@
 #endif
   {
     "disable-new-bookmark-apps",
-    IDS_FLAGS_DISABLE_NEW_BOOKMARK_APPS_NAME,
-    IDS_FLAGS_DISABLE_NEW_BOOKMARK_APPS_DESCRIPTION,
+    IDS_FLAGS_NEW_BOOKMARK_APPS_NAME,
+    IDS_FLAGS_NEW_BOOKMARK_APPS_DESCRIPTION,
     kOsWin | kOsCrOS | kOsLinux | kOsMac,
-    SINGLE_VALUE_TYPE(switches::kDisableNewBookmarkApps)
+    ENABLE_DISABLE_VALUE_TYPE(switches::kEnableNewBookmarkApps,
+                              switches::kDisableNewBookmarkApps)
   },
 #if defined(OS_MACOSX)
   {
@@ -2190,6 +2191,15 @@
     kOsAndroid,
     SINGLE_VALUE_TYPE(switches::kBypassAppBannerEngagementChecks)
   },
+  {
+    "use-sync-sandbox",
+    IDS_FLAGS_SYNC_SANDBOX_NAME,
+    IDS_FLAGS_SYNC_SANDBOX_DESCRIPTION,
+    kOsAll,
+    SINGLE_VALUE_TYPE_AND_VALUE(
+        switches::kSyncServiceURL,
+        "https://chrome-sync.sandbox.google.com/chrome-sync/alpha")
+  },
 
   // NOTE: Adding new command-line switches requires adding corresponding
   // entries to enum "LoginCustomFlags" in histograms.xml. See note in
diff --git a/chrome/browser/android/banners/app_banner_infobar_delegate.cc b/chrome/browser/android/banners/app_banner_infobar_delegate.cc
index 18f146f..c5568049 100644
--- a/chrome/browser/android/banners/app_banner_infobar_delegate.cc
+++ b/chrome/browser/android/banners/app_banner_infobar_delegate.cc
@@ -14,8 +14,10 @@
 #include "chrome/browser/android/shortcut_helper.h"
 #include "chrome/browser/android/shortcut_info.h"
 #include "chrome/browser/android/tab_android.h"
+#include "chrome/browser/banners/app_banner_metrics.h"
 #include "chrome/browser/banners/app_banner_settings_helper.h"
 #include "chrome/browser/infobars/infobar_service.h"
+#include "chrome/browser/metrics/rappor/sampling.h"
 #include "chrome/browser/ui/android/infobars/app_banner_infobar.h"
 #include "chrome/grit/generated_resources.h"
 #include "components/infobars/core/infobar.h"
@@ -87,6 +89,7 @@
 }
 
 AppBannerInfoBarDelegate::~AppBannerInfoBarDelegate() {
+  TrackDismissEvent(DISMISS_EVENT_DISMISSED);
   JNIEnv* env = base::android::AttachCurrentThread();
   Java_AppBannerInfoBarDelegate_destroy(env,
                                         java_delegate_.obj());
@@ -111,39 +114,58 @@
   if (!web_contents)
     return;
 
+  TrackDismissEvent(DISMISS_EVENT_CLOSE_BUTTON);
+
   if (!native_app_data_.is_null()) {
     AppBannerSettingsHelper::RecordBannerEvent(
         web_contents, web_contents->GetURL(),
         native_app_package_,
         AppBannerSettingsHelper::APP_BANNER_EVENT_DID_BLOCK,
         AppBannerManager::GetCurrentTime());
+
+    rappor::SampleDomainAndRegistryFromGURL("AppBanner.NativeApp.Dismissed",
+                                            web_contents->GetURL());
   } else if (!web_app_data_.IsEmpty()) {
     AppBannerSettingsHelper::RecordBannerEvent(
         web_contents, web_contents->GetURL(),
         web_app_data_.start_url.spec(),
         AppBannerSettingsHelper::APP_BANNER_EVENT_DID_BLOCK,
         AppBannerManager::GetCurrentTime());
+
+    rappor::SampleDomainAndRegistryFromGURL("AppBanner.WebApp.Dismissed",
+                                            web_contents->GetURL());
   }
 }
 
 bool AppBannerInfoBarDelegate::Accept() {
   content::WebContents* web_contents =
       InfoBarService::WebContentsFromInfoBar(infobar());
-  if (!web_contents)
+  if (!web_contents) {
+    TrackDismissEvent(DISMISS_EVENT_ERROR);
     return true;
+  }
 
   if (!native_app_data_.is_null()) {
     JNIEnv* env = base::android::AttachCurrentThread();
 
     TabAndroid* tab = TabAndroid::FromWebContents(web_contents);
-    if (tab == nullptr)
+    if (tab == nullptr) {
+      TrackDismissEvent(DISMISS_EVENT_ERROR);
       return true;
+    }
 
-    return Java_AppBannerInfoBarDelegate_installOrOpenNativeApp(
+    bool was_opened = Java_AppBannerInfoBarDelegate_installOrOpenNativeApp(
         env,
         java_delegate_.obj(),
         tab->GetJavaObject().obj(),
         native_app_data_.obj());
+
+    if (was_opened) {
+      TrackDismissEvent(DISMISS_EVENT_APP_OPEN);
+    } else {
+      TrackInstallEvent(INSTALL_EVENT_NATIVE_APP_INSTALL_TRIGGERED);
+    }
+    return was_opened;
   } else if (!web_app_data_.IsEmpty()) {
     AppBannerSettingsHelper::RecordBannerEvent(
         web_contents, web_contents->GetURL(),
@@ -157,8 +179,13 @@
         FROM_HERE,
         base::Bind(&ShortcutHelper::AddShortcutInBackgroundWithSkBitmap,
                    info,
-                   *app_icon_.get()),
+                   *app_icon_.get(),
+                   false),
         true);
+
+    TrackInstallEvent(INSTALL_EVENT_WEB_APP_INSTALLED);
+    rappor::SampleDomainAndRegistryFromGURL("AppBanner.WebApp.Installed",
+                                            web_contents->GetURL());
     return true;
   }
 
@@ -176,13 +203,17 @@
       InfoBarService::WebContentsFromInfoBar(infobar());
   TabAndroid* tab = web_contents ? TabAndroid::FromWebContents(web_contents)
                                  : nullptr;
-  if (tab == nullptr)
+  if (tab == nullptr) {
+    TrackDismissEvent(DISMISS_EVENT_ERROR);
     return true;
+  }
 
   Java_AppBannerInfoBarDelegate_showAppDetails(env,
                                                java_delegate_.obj(),
                                                tab->GetJavaObject().obj(),
                                                native_app_data_.obj());
+
+  TrackDismissEvent(DISMISS_EVENT_BANNER_CLICK);
   return true;
 }
 
@@ -205,6 +236,10 @@
         native_app_package_,
         AppBannerSettingsHelper::APP_BANNER_EVENT_DID_ADD_TO_HOMESCREEN,
         AppBannerManager::GetCurrentTime());
+
+    TrackInstallEvent(INSTALL_EVENT_NATIVE_APP_INSTALL_STARTED);
+    rappor::SampleDomainAndRegistryFromGURL("AppBanner.NativeApp.Installed",
+                                            web_contents->GetURL());
   }
 
   UpdateInstallState(env, obj);
@@ -217,8 +252,10 @@
     return;
 
   if (success) {
+    TrackInstallEvent(INSTALL_EVENT_NATIVE_APP_INSTALL_COMPLETED);
     UpdateInstallState(env, obj);
   } else if (infobar()->owner()) {
+    TrackDismissEvent(DISMISS_EVENT_INSTALL_TIMEOUT);
     infobar()->owner()->RemoveInfoBar(infobar());
   }
 }
diff --git a/chrome/browser/android/banners/app_banner_manager.cc b/chrome/browser/android/banners/app_banner_manager.cc
index 0f0dcb5..f428abb 100644
--- a/chrome/browser/android/banners/app_banner_manager.cc
+++ b/chrome/browser/android/banners/app_banner_manager.cc
@@ -8,16 +8,18 @@
 #include "base/android/jni_string.h"
 #include "base/bind.h"
 #include "base/command_line.h"
+#include "base/metrics/field_trial.h"
 #include "base/metrics/histogram.h"
+#include "base/strings/string_util.h"
 #include "chrome/browser/android/banners/app_banner_infobar_delegate.h"
-#include "chrome/browser/android/banners/app_banner_metrics_ids.h"
-#include "chrome/browser/android/banners/app_banner_utilities.h"
 #include "chrome/browser/android/manifest_icon_selector.h"
 #include "chrome/browser/android/shortcut_helper.h"
 #include "chrome/browser/android/shortcut_info.h"
+#include "chrome/browser/banners/app_banner_metrics.h"
 #include "chrome/browser/banners/app_banner_settings_helper.h"
 #include "chrome/browser/bitmap_fetcher/bitmap_fetcher.h"
 #include "chrome/browser/infobars/infobar_service.h"
+#include "chrome/browser/metrics/rappor/sampling.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/common/chrome_constants.h"
 #include "chrome/common/chrome_switches.h"
@@ -43,17 +45,85 @@
 using base::android::ConvertUTF16ToJavaString;
 
 namespace {
+
 const char kBannerTag[] = "google-play-id";
 base::TimeDelta gTimeDeltaForTesting;
+bool gDisableSecureCheckForTesting = false;
+const int kIconMinimumSize = 144;
+
+// The requirement for now is an image/png that is at least 144x144.
+bool DoesManifestContainRequiredIcon(const content::Manifest& manifest) {
+  for (const auto& icon : manifest.icons) {
+    if (EqualsASCII(icon.type.string(), "image/png"))
+      continue;
+
+    for (const auto& size : icon.sizes) {
+      if (size.IsEmpty()) // "any"
+        return true;
+      if (size.width() >= kIconMinimumSize && size.height() >= kIconMinimumSize)
+        return true;
+    }
+  }
+
+  return false;
 }
 
+}  // anonymous namespace
+
 namespace banners {
 
-AppBannerManager::AppBannerManager(JNIEnv* env, jobject obj)
-    : weak_java_banner_view_manager_(env, obj), weak_factory_(this) {
+// Fetches a bitmap and deletes itself when completed.
+class AppBannerManager::BannerBitmapFetcher
+    : public chrome::BitmapFetcher,
+      public chrome::BitmapFetcherDelegate {
+ public:
+  BannerBitmapFetcher(const GURL& image_url,
+                      banners::AppBannerManager* manager);
+
+  // Prevents informing the AppBannerManager that the fetch has completed.
+  void Cancel();
+
+  // chrome::BitmapFetcherDelegate overrides
+  void OnFetchComplete(const GURL url, const SkBitmap* icon) override;
+
+ private:
+  banners::AppBannerManager* manager_;
+  bool is_cancelled_;
+};
+
+AppBannerManager::BannerBitmapFetcher::BannerBitmapFetcher(
+    const GURL& image_url,
+    banners::AppBannerManager* manager)
+    : chrome::BitmapFetcher(image_url, this),
+      manager_(manager),
+      is_cancelled_(false) {
+}
+
+void AppBannerManager::BannerBitmapFetcher::Cancel() {
+  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+  is_cancelled_ = true;
+}
+
+void AppBannerManager::BannerBitmapFetcher::OnFetchComplete(
+    const GURL url,
+    const SkBitmap* icon) {
+  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+
+  if (!is_cancelled_)
+    manager_->OnFetchComplete(this, url, icon);
+
+  delete this;
+}
+
+AppBannerManager::AppBannerManager(JNIEnv* env, jobject obj, int icon_size)
+    : preferred_icon_size_(icon_size),
+      fetcher_(nullptr),
+      weak_java_banner_view_manager_(env, obj),
+      weak_factory_(this) {
 }
 
 AppBannerManager::~AppBannerManager() {
+  CancelActiveFetcher();
 }
 
 void AppBannerManager::Destroy(JNIEnv* env, jobject obj) {
@@ -72,7 +142,7 @@
     const content::LoadCommittedDetails& details,
     const content::FrameNavigateParams& params) {
   // Clear current state.
-  fetcher_.reset();
+  CancelActiveFetcher();
   app_title_ = base::string16();
   web_app_data_ = content::Manifest();
   native_app_data_.Reset();
@@ -86,11 +156,14 @@
     return;
   validated_url_ = validated_url;
 
-  // See if the page has a manifest. Using Unretained(this) here is safe as the
-  // lifetime of this object extends beyond the lifetime of the web_contents(),
-  // and when web_contents() is destroyed it will call OnDidGetManifest.
+  // A secure scheme is required to show banners, so exit early if we see the
+  // URL is invalid.
+  if (!validated_url_.SchemeIsSecure() && !gDisableSecureCheckForTesting)
+    return;
+
+  // See if the page has a manifest.
   web_contents()->GetManifest(base::Bind(&AppBannerManager::OnDidGetManifest,
-                                         base::Unretained(this)));
+                                         weak_factory_.GetWeakPtr()));
 }
 
 void AppBannerManager::OnDidGetManifest(const content::Manifest& manifest) {
@@ -99,14 +172,20 @@
 
   if (manifest.IsEmpty()
       || !manifest.start_url.is_valid()
-      || (manifest.name.is_null() && manifest.short_name.is_null())) {
+      || (manifest.name.is_null() && manifest.short_name.is_null())
+      || !DoesManifestContainRequiredIcon(manifest)) {
     // No usable manifest, see if there is a play store meta tag.
+    if (!IsEnabledForNativeApps())
+      return;
+
     Send(new ChromeViewMsg_RetrieveMetaTagContent(routing_id(),
                                                   validated_url_,
                                                   kBannerTag));
     return;
   }
 
+  banners::TrackDisplayEvent(DISPLAY_EVENT_BANNER_REQUESTED);
+
   web_app_data_ = manifest;
   app_title_ = web_app_data_.name.string();
 
@@ -132,12 +211,14 @@
     GURL icon_url =
         ManifestIconSelector::FindBestMatchingIcon(
             web_app_data_.icons,
-            GetPreferredIconSize(),
+            preferred_icon_size_,
             gfx::Screen::GetScreenFor(web_contents()->GetNativeView()));
     if (icon_url.is_empty())
       return;
 
     FetchIcon(icon_url);
+  } else {
+    TrackDisplayEvent(DISPLAY_EVENT_LACKS_SERVICE_WORKER);
   }
 }
 
@@ -162,6 +243,14 @@
   return true;
 }
 
+void AppBannerManager::CancelActiveFetcher() {
+  // Fetchers delete themselves.
+  if (fetcher_ != nullptr) {
+    fetcher_->Cancel();
+    fetcher_ = nullptr;
+  }
+}
+
 bool AppBannerManager::OnMessageReceived(const IPC::Message& message) {
   bool handled = true;
   IPC_BEGIN_MESSAGE_MAP(AppBannerManager, message)
@@ -172,10 +261,18 @@
   return handled;
 }
 
-void AppBannerManager::OnFetchComplete(const GURL url, const SkBitmap* bitmap) {
-  fetcher_.reset();
-  if (!bitmap || url != app_icon_url_) {
-    DVLOG(1) << "Failed to retrieve image: " << url;
+void AppBannerManager::OnFetchComplete(BannerBitmapFetcher* fetcher,
+                                       const GURL url,
+                                       const SkBitmap* bitmap) {
+  if (fetcher_ != fetcher)
+    return;
+  fetcher_ = nullptr;
+
+  if (!web_contents()
+      || web_contents()->IsBeingDestroyed()
+      || validated_url_ != web_contents()->GetURL()
+      || !bitmap
+      || url != app_icon_url_) {
     return;
   }
 
@@ -198,6 +295,9 @@
         new SkBitmap(*bitmap),
         native_app_data_,
         native_app_package_);
+
+    rappor::SampleDomainAndRegistryFromGURL("AppBanner.NativeApp.Shown",
+                                            web_contents()->GetURL());
   } else if (!web_app_data_.IsEmpty()){
     RecordCouldShowBanner(web_app_data_.start_url.spec());
     if (!CheckIfShouldShow(web_app_data_.start_url.spec()))
@@ -208,10 +308,13 @@
         app_title_,
         new SkBitmap(*bitmap),
         web_app_data_);
+
+    rappor::SampleDomainAndRegistryFromGURL("AppBanner.WebApp.Shown",
+                                            web_contents()->GetURL());
   }
 
   if (weak_infobar_ptr != nullptr)
-    banners::TrackDisplayEvent(DISPLAY_CREATED);
+    banners::TrackDisplayEvent(DISPLAY_EVENT_CREATED);
 }
 
 void AppBannerManager::OnDidRetrieveMetaTagContent(
@@ -225,7 +328,7 @@
     return;
   }
 
-  banners::TrackDisplayEvent(DISPLAY_BANNER_REQUESTED);
+  banners::TrackDisplayEvent(DISPLAY_EVENT_BANNER_REQUESTED);
 
   // Send the info to the Java side to get info about the app.
   JNIEnv* env = base::android::AttachCurrentThread();
@@ -240,7 +343,8 @@
   Java_AppBannerManager_fetchAppDetails(env,
                                         jobj.obj(),
                                         jurl.obj(),
-                                        jpackage.obj());
+                                        jpackage.obj(),
+                                        preferred_icon_size_);
 }
 
 bool AppBannerManager::OnAppDetailsRetrieved(JNIEnv* env,
@@ -259,8 +363,8 @@
   return FetchIcon(GURL(image_url));
 }
 
-int AppBannerManager::GetNumActiveFetchers(JNIEnv* env, jobject obj) {
-  return fetcher_.get() ? 1 : 0;
+bool AppBannerManager::IsFetcherActive(JNIEnv* env, jobject obj) {
+  return fetcher_ != nullptr;
 }
 
 bool AppBannerManager::FetchIcon(const GURL& image_url) {
@@ -270,8 +374,10 @@
   // Begin asynchronously fetching the app icon.
   Profile* profile =
       Profile::FromBrowserContext(web_contents()->GetBrowserContext());
-  fetcher_.reset(new chrome::BitmapFetcher(image_url, this));
-  fetcher_.get()->Start(
+
+  CancelActiveFetcher();
+  fetcher_ = new BannerBitmapFetcher(image_url, this);
+  fetcher_->Start(
       profile->GetRequestContext(),
       std::string(),
       net::URLRequest::CLEAR_REFERRER_ON_TRANSITION_FROM_SECURE_TO_INSECURE,
@@ -280,13 +386,10 @@
   return true;
 }
 
-int AppBannerManager::GetPreferredIconSize() {
-  JNIEnv* env = base::android::AttachCurrentThread();
-  ScopedJavaLocalRef<jobject> jobj = weak_java_banner_view_manager_.get(env);
-  if (jobj.is_null())
-    return 0;
-
-  return Java_AppBannerManager_getPreferredIconSize(env, jobj.obj());
+// static
+bool AppBannerManager::IsEnabledForNativeApps() {
+  return base::CommandLine::ForCurrentProcess()->HasSwitch(
+      switches::kEnableAppInstallAlerts);
 }
 
 // static
@@ -294,28 +397,25 @@
   return base::Time::Now() + gTimeDeltaForTesting;
 }
 
-void RecordDismissEvent(JNIEnv* env, jclass clazz, jint metric) {
-  banners::TrackDismissEvent(metric);
-}
-
-void RecordInstallEvent(JNIEnv* env, jclass clazz, jint metric) {
-  banners::TrackInstallEvent(metric);
-}
-
-jlong Init(JNIEnv* env, jobject obj) {
-  AppBannerManager* manager = new AppBannerManager(env, obj);
+jlong Init(JNIEnv* env, jobject obj, jint icon_size) {
+  AppBannerManager* manager = new AppBannerManager(env, obj, icon_size);
   return reinterpret_cast<intptr_t>(manager);
 }
 
 jboolean IsEnabled(JNIEnv* env, jclass clazz) {
-  return base::CommandLine::ForCurrentProcess()->HasSwitch(
-      switches::kEnableAppInstallAlerts);
+  const std::string group_name =
+      base::FieldTrialList::FindFullName("AppBanners");
+  return group_name == "Enabled";
 }
 
 void SetTimeDeltaForTesting(JNIEnv* env, jclass clazz, jint days) {
   gTimeDeltaForTesting = base::TimeDelta::FromDays(days);
 }
 
+void DisableSecureSchemeCheckForTesting(JNIEnv* env, jclass clazz) {
+  gDisableSecureCheckForTesting = true;
+}
+
 // Register native methods
 bool RegisterAppBannerManager(JNIEnv* env) {
   return RegisterNativesImpl(env);
diff --git a/chrome/browser/android/banners/app_banner_manager.h b/chrome/browser/android/banners/app_banner_manager.h
index 9a8520c..d4f7c26 100644
--- a/chrome/browser/android/banners/app_banner_manager.h
+++ b/chrome/browser/android/banners/app_banner_manager.h
@@ -31,46 +31,19 @@
  * Manages when an app banner is created or dismissed.
  *
  * Hooks the wiring together for getting the data for a particular app.
- * Monitors at most one package at a time, and tracks the info for the
- * most recent app that was requested.  Any work in progress for other apps is
- * discarded.
+ * Monitors at most one app at a time, tracking the info for the most recently
+ * requested app. Any work in progress for other apps is discarded.
  *
- * The procedure for creating a banner spans multiple asynchronous calls across
- * the JNI boundary, as well as querying a Service to get info about the app.
- *
- * 0) A navigation of the main frame is triggered.  Upon completion of the load,
- *    the page is parsed for the correct meta tag.  If it doesn't exist, abort.
- *
- * 1) The AppBannerManager is alerted about the tag's contents, which should
- *    be the Play Store package name.  This is sent to the Java side
- *    AppBannerManager.
- *
- * 2) The AppBannerManager's ServiceDelegate is asynchronously queried about the
- *    package name.
- *
- * 3) At some point, the Java-side AppBannerManager is alerted of the completed
- *    query and is given back data about the requested package, which includes a
- *    URL for the app's icon.  This URL is sent to native code for retrieval.
- *
- * 4) The process of fetching the icon begins by invoking the BitmapFetcher,
- *    which works asynchonously.
- *
- * 5) Once the icon has been downloaded, the icon is sent to the Java-side
- *    AppBannerManager to (finally) create a AppBannerView, assuming that the
- *    app we retrieved the details for is still for the page that requested it.
- *
- * Because of the asynchronous nature of this pipeline, it's entirely possible
- * that a request to show a banner is interrupted by another request.  The
- * Java side manages what happens in these situations, which will usually result
- * in dropping the old banner request on the floor.
+ * TODO(dfalcantara): Update this when the pipeline requirements resolidify.
  */
 
 namespace banners {
 
-class AppBannerManager : public chrome::BitmapFetcherDelegate,
-                         public content::WebContentsObserver {
+class AppBannerManager : public content::WebContentsObserver {
  public:
-  AppBannerManager(JNIEnv* env, jobject obj);
+  class BannerBitmapFetcher;
+
+  AppBannerManager(JNIEnv* env, jobject obj, int icon_size);
   ~AppBannerManager() override;
 
   // Destroys the AppBannerManager.
@@ -94,8 +67,13 @@
   // Returns |false| if this couldn't be kicked off.
   bool FetchIcon(const GURL& image_url);
 
-  // Return how many fetchers are active.
-  int GetNumActiveFetchers(JNIEnv* env, jobject jobj);
+  // Called when everything required to show a banner is ready.
+  void OnFetchComplete(BannerBitmapFetcher* fetcher,
+                       const GURL url,
+                       const SkBitmap* icon);
+
+  // Return whether a BitmapFetcher is active.
+  bool IsFetcherActive(JNIEnv* env, jobject jobj);
 
   // Returns the current time.
   static base::Time GetCurrentTime();
@@ -108,13 +86,7 @@
                      const GURL& validated_url) override;
   bool OnMessageReceived(const IPC::Message& message) override;
 
-  // BitmapFetcherDelegate overrides.
-  void OnFetchComplete(const GURL url, const SkBitmap* bitmap) override;
-
  private:
-  // Gets the preferred icon size for the banner icons.
-  int GetPreferredIconSize();
-
   // Called when the manifest has been retrieved, or if there is no manifest to
   // retrieve.
   void OnDidGetManifest(const content::Manifest& manifest);
@@ -138,8 +110,18 @@
   // Check if the banner should be shown.
   bool CheckIfShouldShow(const std::string& package_or_start_url);
 
-  // Fetches the icon for an app.
-  scoped_ptr<chrome::BitmapFetcher> fetcher_;
+  // Cancels an active BitmapFetcher, stopping its banner from appearing.
+  void CancelActiveFetcher();
+
+  // Whether or not the banners should appear for native apps.
+  static bool IsEnabledForNativeApps();
+
+  // Icon size that we want to be use for the launcher.
+  const int preferred_icon_size_;
+
+  // Fetches the icon for an app.  Weakly held because they delete themselves.
+  BannerBitmapFetcher* fetcher_;
+
   GURL validated_url_;
   GURL app_icon_url_;
 
diff --git a/chrome/browser/android/banners/app_banner_metrics_id_list.h b/chrome/browser/android/banners/app_banner_metrics_id_list.h
deleted file mode 100644
index 67edc18..0000000
--- a/chrome/browser/android/banners/app_banner_metrics_id_list.h
+++ /dev/null
@@ -1,39 +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 CHROME_BROWSER_ANDROID_BANNERS_APP_BANNER_METRICS_ID_LIST_H_
-#define CHROME_BROWSER_ANDROID_BANNERS_APP_BANNER_METRICS_ID_LIST_H_
-
-// Keep in sync with the values defined in histograms.xml.
-
-DEFINE_APP_BANNER_METRICS_ID_ENUM_START(DisplayEvent)
-DEFINE_APP_BANNER_METRICS_ID(DISPLAY_MIN, 0)
-DEFINE_APP_BANNER_METRICS_ID(DISPLAY_BANNER_REQUESTED, 1)
-DEFINE_APP_BANNER_METRICS_ID(DISPLAY_BLOCKED_PREVIOUSLY, 2)
-DEFINE_APP_BANNER_METRICS_ID(DISPLAY_BLOCKED_TOO_MANY_OTHERS, 3)
-DEFINE_APP_BANNER_METRICS_ID(DISPLAY_CREATED, 4)
-DEFINE_APP_BANNER_METRICS_ID(DISPLAY_MAX, 5)
-DEFINE_APP_BANNER_METRICS_ID_ENUM_END()
-
-DEFINE_APP_BANNER_METRICS_ID_ENUM_START(InstallEvent)
-DEFINE_APP_BANNER_METRICS_ID(INSTALL_MIN, 20)
-DEFINE_APP_BANNER_METRICS_ID(INSTALL_TRIGGERED, 21)
-DEFINE_APP_BANNER_METRICS_ID(INSTALL_STARTED, 22)
-DEFINE_APP_BANNER_METRICS_ID(INSTALL_COMPLETED, 23)
-DEFINE_APP_BANNER_METRICS_ID(INSTALL_MAX, 24)
-DEFINE_APP_BANNER_METRICS_ID_ENUM_END()
-
-DEFINE_APP_BANNER_METRICS_ID_ENUM_START(DismissEvent)
-DEFINE_APP_BANNER_METRICS_ID(DISMISS_MIN, 40)
-DEFINE_APP_BANNER_METRICS_ID(DISMISS_ERROR, 41)
-DEFINE_APP_BANNER_METRICS_ID(DISMISS_APP_OPEN, 42)
-DEFINE_APP_BANNER_METRICS_ID(DISMISS_BANNER_CLICK, 43)
-DEFINE_APP_BANNER_METRICS_ID(DISMISS_BANNER_SWIPE, 44)
-DEFINE_APP_BANNER_METRICS_ID(DISMISS_CLOSE_BUTTON, 45)
-DEFINE_APP_BANNER_METRICS_ID(DISMISS_INSTALL_TIMEOUT, 46)
-DEFINE_APP_BANNER_METRICS_ID(DISMISS_NAVIGATE, 47)
-DEFINE_APP_BANNER_METRICS_ID(DISMISS_MAX, 48)
-DEFINE_APP_BANNER_METRICS_ID_ENUM_END()
-
-#endif  // CHROME_BROWSER_ANDROID_BANNERS_APP_BANNER_METRICS_ID_LIST_H_
diff --git a/chrome/browser/android/banners/app_banner_metrics_ids.h b/chrome/browser/android/banners/app_banner_metrics_ids.h
deleted file mode 100644
index f2d9325..0000000
--- a/chrome/browser/android/banners/app_banner_metrics_ids.h
+++ /dev/null
@@ -1,17 +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 CHROME_BROWSER_ANDROID_BANNERS_APP_BANNER_METRICS_IDS_H_
-#define CHROME_BROWSER_ANDROID_BANNERS_APP_BANNER_METRICS_IDS_H_
-
-namespace banners {
-
-#define DEFINE_APP_BANNER_METRICS_ID_ENUM_START(identifier) enum identifier {
-#define DEFINE_APP_BANNER_METRICS_ID(identifier,int) identifier = int,
-#define DEFINE_APP_BANNER_METRICS_ID_ENUM_END() };
-#include "chrome/browser/android/banners/app_banner_metrics_id_list.h"
-
-};  // namespace banners
-
-#endif  // CHROME_BROWSER_ANDROID_BANNERS_APP_BANNER_METRICS_IDS_H_
diff --git a/chrome/browser/android/banners/app_banner_utilities.cc b/chrome/browser/android/banners/app_banner_utilities.cc
deleted file mode 100644
index 94012bc..0000000
--- a/chrome/browser/android/banners/app_banner_utilities.cc
+++ /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.
-
-#include "chrome/browser/android/banners/app_banner_utilities.h"
-
-#include <algorithm>
-#include <string>
-
-#include "base/metrics/histogram.h"
-#include "chrome/browser/android/banners/app_banner_metrics_ids.h"
-
-namespace banners {
-
-void TrackDismissEvent(int event) {
-  std::vector<int> codes;
-  for (int i = DISMISS_MIN + 1; i < DISMISS_MAX; ++i) {
-    codes.push_back(i);
-  }
-  DCHECK(std::find(codes.begin(), codes.end(), event) != codes.end());
-  UMA_HISTOGRAM_CUSTOM_ENUMERATION("AppBanners.DismissEvent", event, codes);
-}
-
-void TrackDisplayEvent(int event) {
-  std::vector<int> codes;
-  for (int i = DISPLAY_MIN + 1; i < DISPLAY_MAX; ++i) {
-    codes.push_back(i);
-  }
-  DCHECK(std::find(codes.begin(), codes.end(), event) != codes.end());
-  UMA_HISTOGRAM_CUSTOM_ENUMERATION("AppBanners.DisplayEvent", event, codes);
-}
-
-void TrackInstallEvent(int event) {
-  std::vector<int> codes;
-  for (int i = INSTALL_MIN + 1; i < INSTALL_MAX; ++i) {
-    codes.push_back(i);
-  }
-  DCHECK(std::find(codes.begin(), codes.end(), event) != codes.end());
-  UMA_HISTOGRAM_CUSTOM_ENUMERATION("AppBanners.InstallEvent", event, codes);
-}
-
-}  // namespace banners
diff --git a/chrome/browser/android/banners/app_banner_utilities.h b/chrome/browser/android/banners/app_banner_utilities.h
deleted file mode 100644
index 77e3a84..0000000
--- a/chrome/browser/android/banners/app_banner_utilities.h
+++ /dev/null
@@ -1,17 +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 CHROME_BROWSER_ANDROID_BANNERS_APP_BANNER_UTILITIES_H_
-#define CHROME_BROWSER_ANDROID_BANNERS_APP_BANNER_UTILITIES_H_
-
-namespace banners {
-
-// Records metrics for app banners.
-void TrackDisplayEvent(int event);
-void TrackDismissEvent(int event);
-void TrackInstallEvent(int event);
-
-}  // namespace banners
-
-#endif  // CHROME_BROWSER_ANDROID_BANNERS_APP_BANNER_UTILITIES_H_
diff --git a/chrome/browser/android/bookmarks/bookmarks_bridge.cc b/chrome/browser/android/bookmarks/bookmarks_bridge.cc
index dabdc7a..1ea35d0 100644
--- a/chrome/browser/android/bookmarks/bookmarks_bridge.cc
+++ b/chrome/browser/android/bookmarks/bookmarks_bridge.cc
@@ -231,7 +231,8 @@
         client_->supervised_node()->child_count() > 0) {
       top_level_folders.push_back(client_->supervised_node());
     }
-    if (partner_bookmarks_shim_->HasPartnerBookmarks()) {
+    if (partner_bookmarks_shim_->HasPartnerBookmarks()
+        && IsReachable(partner_bookmarks_shim_->GetPartnerBookmarksRoot())) {
       top_level_folders.push_back(
           partner_bookmarks_shim_->GetPartnerBookmarksRoot());
     }
diff --git a/chrome/browser/android/chrome_jni_registrar.cc b/chrome/browser/android/chrome_jni_registrar.cc
index 88ef2bc6..62091e0b6 100644
--- a/chrome/browser/android/chrome_jni_registrar.cc
+++ b/chrome/browser/android/chrome_jni_registrar.cc
@@ -80,6 +80,7 @@
 #include "chrome/browser/ui/android/autofill/password_generation_popup_view_android.h"
 #include "chrome/browser/ui/android/chrome_http_auth_handler.h"
 #include "chrome/browser/ui/android/context_menu_helper.h"
+#include "chrome/browser/ui/android/infobars/account_chooser_infobar.h"
 #include "chrome/browser/ui/android/infobars/app_banner_infobar.h"
 #include "chrome/browser/ui/android/infobars/confirm_infobar.h"
 #include "chrome/browser/ui/android/infobars/data_reduction_proxy_infobar.h"
@@ -124,6 +125,7 @@
      web_contents_delegate_android::RegisterWebContentsDelegateAndroidJni},
     // Register JNI for chrome classes.
     {"AccessibilityUtils", AccessibilityUtil::Register},
+    {"AccountChooserInfoBar", RegisterAccountChooserInfoBar},
     {"AccountManagementScreenHelper", AccountManagementScreenHelper::Register},
     {"AndroidProfileOAuth2TokenService",
      AndroidProfileOAuth2TokenService::Register},
diff --git a/chrome/browser/android/compositor/layer/contextual_search_layer.cc b/chrome/browser/android/compositor/layer/contextual_search_layer.cc
index b98bbb3..317eef0 100644
--- a/chrome/browser/android/compositor/layer/contextual_search_layer.cc
+++ b/chrome/browser/android/compositor/layer/contextual_search_layer.cc
@@ -72,6 +72,10 @@
   DCHECK(search_provider_icon_resource);
   DCHECK(search_icon_resource);
 
+  // Round values to avoid pixel gap between layers.
+  search_bar_height = floor(search_bar_height);
+  search_bar_margin_top = floor(search_bar_margin_top);
+
   // ---------------------------------------------------------------------------
   // Search Bar Background
   // ---------------------------------------------------------------------------
diff --git a/chrome/browser/android/preferences/website_preference_bridge.cc b/chrome/browser/android/preferences/website_preference_bridge.cc
index 793d7d2..64bf3e8 100644
--- a/chrome/browser/android/preferences/website_preference_bridge.cc
+++ b/chrome/browser/android/preferences/website_preference_bridge.cc
@@ -10,6 +10,7 @@
 #include "base/bind.h"
 #include "base/bind_helpers.h"
 #include "base/files/file_path.h"
+#include "base/strings/string_util.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/browsing_data/browsing_data_local_storage_helper.h"
 #include "chrome/browser/browsing_data/cookies_tree_model.h"
@@ -24,6 +25,7 @@
 #include "jni/WebsitePreferenceBridge_jni.h"
 #include "storage/browser/quota/quota_client.h"
 #include "storage/browser/quota/quota_manager.h"
+#include "url/url_constants.h"
 
 using base::android::ConvertJavaStringToUTF8;
 using base::android::ConvertUTF8ToJavaString;
@@ -58,7 +60,32 @@
     }
     const std::string origin = settings_it.primary_pattern.ToString();
     const std::string embedder = settings_it.secondary_pattern.ToString();
-    ScopedJavaLocalRef<jstring> jorigin = ConvertUTF8ToJavaString(env, origin);
+
+    // The string |jorigin| is used to group permissions together in the Site
+    // Settings list. In order to group sites with the same origin, remove any
+    // standard port from the end of the URL if it's present (i.e. remove :443
+    // for HTTPS sites and :80 for HTTP sites).
+    // TODO(sashab,lgarron): Find out which settings are being saved with the
+    // port and omit it if it's the standard port.
+    // TODO(mvanouwerkerk): Remove all this logic and take two passes through
+    // HostContentSettingsMap: once to get all the 'interesting' hosts, and once
+    // (on SingleWebsitePreferences) to find permission patterns which match
+    // each of these hosts.
+    const char* kHttpPortSuffix = ":80";
+    const char* kHttpsPortSuffix = ":443";
+    ScopedJavaLocalRef<jstring> jorigin;
+    if (StartsWithASCII(origin, url::kHttpsScheme, false) &&
+        EndsWith(origin, kHttpsPortSuffix, false)) {
+      jorigin = ConvertUTF8ToJavaString(
+          env, origin.substr(0, origin.size() - strlen(kHttpsPortSuffix)));
+    } else if (StartsWithASCII(origin, url::kHttpScheme, false) &&
+               EndsWith(origin, kHttpPortSuffix, false)) {
+      jorigin = ConvertUTF8ToJavaString(
+          env, origin.substr(0, origin.size() - strlen(kHttpPortSuffix)));
+    } else {
+      jorigin = ConvertUTF8ToJavaString(env, origin);
+    }
+
     ScopedJavaLocalRef<jstring> jembedder;
     if (embedder != origin)
       jembedder = ConvertUTF8ToJavaString(env, embedder);
diff --git a/chrome/browser/android/service_tab_launcher.cc b/chrome/browser/android/service_tab_launcher.cc
index f10f223..da4db67 100644
--- a/chrome/browser/android/service_tab_launcher.cc
+++ b/chrome/browser/android/service_tab_launcher.cc
@@ -8,12 +8,21 @@
 #include "base/callback.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/page_navigator.h"
+#include "content/public/browser/web_contents.h"
 #include "jni/ServiceTabLauncher_jni.h"
 
 using base::android::AttachCurrentThread;
 using base::android::ConvertUTF8ToJavaString;
 using base::android::GetApplicationContext;
 
+// Called by Java when the WebContents instance for a request Id is available.
+void OnWebContentsForRequestAvailable(
+    JNIEnv* env, jclass clazz, jint request_id, jobject android_web_contents) {
+  chrome::android::ServiceTabLauncher::GetInstance()->OnTabLaunched(
+      request_id,
+      content::WebContents::FromJavaWebContents(android_web_contents));
+}
+
 namespace chrome {
 namespace android {
 
@@ -33,7 +42,7 @@
 void ServiceTabLauncher::LaunchTab(
     content::BrowserContext* browser_context,
     const content::OpenURLParams& params,
-    const base::Callback<void(content::WebContents*)>& callback) {
+    const TabLaunchedCallback& callback) {
   if (!java_object_.obj()) {
     LOG(ERROR) << "No ServiceTabLauncher is available to launch a new tab.";
     callback.Run(nullptr);
@@ -58,10 +67,14 @@
 
   ScopedJavaLocalRef<jbyteArray> post_data;
 
+  int request_id = tab_launched_callbacks_.Add(
+      new TabLaunchedCallback(callback));
+  DCHECK_GE(request_id, 1);
+
   Java_ServiceTabLauncher_launchTab(env,
                                     java_object_.obj(),
                                     GetApplicationContext(),
-                                    0 /* request_id */,
+                                    request_id,
                                     browser_context->IsOffTheRecord(),
                                     url.obj(),
                                     disposition,
@@ -69,11 +82,17 @@
                                     params.referrer.policy,
                                     headers.obj(),
                                     post_data.obj());
+}
 
-  // TODO(peter): We need to wait for the Android Activity to reply to the
-  // launch intent with the ID of the launched Web Contents, so that the Java
-  // side can invoke a method on the native side with the request id and the
-  // WebContents enabling us to invoke |callback|. See https://crbug.com/454809.
+void ServiceTabLauncher::OnTabLaunched(int request_id,
+                                       content::WebContents* web_contents) {
+  TabLaunchedCallback* callback = tab_launched_callbacks_.Lookup(request_id);
+  DCHECK(callback);
+
+  if (callback)
+    callback->Run(web_contents);
+
+  tab_launched_callbacks_.Remove(request_id);
 }
 
 bool ServiceTabLauncher::RegisterServiceTabLauncher(JNIEnv* env) {
diff --git a/chrome/browser/android/service_tab_launcher.h b/chrome/browser/android/service_tab_launcher.h
index 3e9fb233..2906ceb 100644
--- a/chrome/browser/android/service_tab_launcher.h
+++ b/chrome/browser/android/service_tab_launcher.h
@@ -7,6 +7,7 @@
 
 #include "base/android/jni_android.h"
 #include "base/callback_forward.h"
+#include "base/id_map.h"
 #include "base/memory/singleton.h"
 
 namespace content {
@@ -19,8 +20,12 @@
 namespace android {
 
 // Launcher for creating new tabs on Android from a background service, where
-// there may not necessarily be an Activity or a tab model at all.
+// there may not necessarily be an Activity or a tab model at all. When the
+// tab has been launched, the user of this class will be informed with the
+// content::WebContents instance associated with the tab.
 class ServiceTabLauncher {
+  using TabLaunchedCallback = base::Callback<void(content::WebContents*)>;
+
  public:
   // Returns the singleton instance of the service tab launcher.
   static ServiceTabLauncher* GetInstance();
@@ -30,7 +35,12 @@
   // the tab is avialable. This method must only be called from the UI thread.
   void LaunchTab(content::BrowserContext* browser_context,
                  const content::OpenURLParams& params,
-                 const base::Callback<void(content::WebContents*)>& callback);
+                 const TabLaunchedCallback& callback);
+
+  // To be called when the tab for |request_id| has launched, with the
+  // associated |web_contents|. The WebContents must not yet have started
+  // the provisional load for the main frame of the navigation.
+  void OnTabLaunched(int request_id, content::WebContents* web_contents);
 
   static bool RegisterServiceTabLauncher(JNIEnv* env);
 
@@ -40,6 +50,8 @@
   ServiceTabLauncher();
   ~ServiceTabLauncher();
 
+  IDMap<TabLaunchedCallback> tab_launched_callbacks_;
+
   base::android::ScopedJavaGlobalRef<jobject> java_object_;
 
   DISALLOW_COPY_AND_ASSIGN(ServiceTabLauncher);
diff --git a/chrome/browser/android/shortcut_helper.cc b/chrome/browser/android/shortcut_helper.cc
index 9ee1d42..4263ac91 100644
--- a/chrome/browser/android/shortcut_helper.cc
+++ b/chrome/browser/android/shortcut_helper.cc
@@ -16,7 +16,6 @@
 #include "base/task/cancelable_task_tracker.h"
 #include "base/threading/worker_pool.h"
 #include "chrome/browser/android/manifest_icon_selector.h"
-#include "chrome/browser/android/tab_android.h"
 #include "chrome/browser/banners/app_banner_settings_helper.h"
 #include "chrome/browser/favicon/favicon_service.h"
 #include "chrome/browser/favicon/favicon_service_factory.h"
@@ -46,11 +45,10 @@
 // http://developer.android.com/design/style/iconography.html
 const int ShortcutHelper::kPreferredIconSizeInDp = 48;
 
-jlong Initialize(JNIEnv* env, jobject obj, jlong tab_android_ptr) {
-  TabAndroid* tab = reinterpret_cast<TabAndroid*>(tab_android_ptr);
-
-  ShortcutHelper* shortcut_helper =
-      new ShortcutHelper(env, obj, tab->web_contents());
+jlong Initialize(JNIEnv* env, jobject obj, jobject java_web_contents) {
+  content::WebContents* web_contents =
+      content::WebContents::FromJavaWebContents(java_web_contents);
+  ShortcutHelper* shortcut_helper = new ShortcutHelper(env, obj, web_contents);
   shortcut_helper->Initialize();
 
   return reinterpret_cast<intptr_t>(shortcut_helper);
@@ -216,16 +214,17 @@
 }
 
 void ShortcutHelper::AddShortcutUsingManifestIcon() {
+  RecordAddToHomescreen();
+
   // Stop observing so we don't get destroyed while doing the last steps.
   Observe(NULL);
 
-  RecordAddToHomescreen();
-
   base::WorkerPool::PostTask(
       FROM_HERE,
       base::Bind(&ShortcutHelper::AddShortcutInBackgroundWithSkBitmap,
                  shortcut_info_,
-                 manifest_icon_),
+                 manifest_icon_,
+                 true),
       true);
 
   Destroy();
@@ -306,12 +305,13 @@
                           &icon_bitmap);
   }
 
-  AddShortcutInBackgroundWithSkBitmap(info, icon_bitmap);
+  AddShortcutInBackgroundWithSkBitmap(info, icon_bitmap, true);
 }
 
 void ShortcutHelper::AddShortcutInBackgroundWithSkBitmap(
     const ShortcutInfo& info,
-    const SkBitmap& icon_bitmap) {
+    const SkBitmap& icon_bitmap,
+    const bool return_to_homescreen) {
   DCHECK(base::WorkerPool::RunsTasksOnCurrentThread());
 
   SkColor color = color_utils::CalculateKMeanColorOfBitmap(icon_bitmap);
@@ -339,7 +339,8 @@
       g_value,
       b_value,
       info.display == content::Manifest::DISPLAY_MODE_STANDALONE,
-      info.orientation);
+      info.orientation,
+      return_to_homescreen);
 }
 
 void ShortcutHelper::RecordAddToHomescreen() {
diff --git a/chrome/browser/android/shortcut_helper.h b/chrome/browser/android/shortcut_helper.h
index 9b3074b..6200c3c89 100644
--- a/chrome/browser/android/shortcut_helper.h
+++ b/chrome/browser/android/shortcut_helper.h
@@ -91,7 +91,8 @@
   // Must be called from a WorkerPool task.
   static void AddShortcutInBackgroundWithSkBitmap(
       const ShortcutInfo& info,
-      const SkBitmap& icon_bitmap);
+      const SkBitmap& icon_bitmap,
+      const bool return_to_homescreen);
 
   // Registers JNI hooks.
   static bool RegisterShortcutHelper(JNIEnv* env);
diff --git a/chrome/browser/android/tab_android.cc b/chrome/browser/android/tab_android.cc
index ed5094f94..0ddf50d 100644
--- a/chrome/browser/android/tab_android.cc
+++ b/chrome/browser/android/tab_android.cc
@@ -13,6 +13,9 @@
 #include "chrome/browser/android/chrome_web_contents_delegate_android.h"
 #include "chrome/browser/android/compositor/tab_content_manager.h"
 #include "chrome/browser/android/uma_utils.h"
+#include "chrome/browser/bookmarks/bookmark_model_factory.h"
+#include "chrome/browser/bookmarks/chrome_bookmark_client.h"
+#include "chrome/browser/bookmarks/chrome_bookmark_client_factory.h"
 #include "chrome/browser/browser_about_handler.h"
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/content_settings/tab_specific_content_settings.h"
@@ -46,6 +49,10 @@
 #include "chrome/common/instant_types.h"
 #include "chrome/common/render_messages.h"
 #include "chrome/common/url_constants.h"
+#include "components/bookmarks/browser/bookmark_model.h"
+#include "components/bookmarks/browser/bookmark_node.h"
+#include "components/bookmarks/browser/bookmark_utils.h"
+#include "components/dom_distiller/core/url_utils.h"
 #include "components/infobars/core/infobar_container.h"
 #include "components/navigation_interception/intercept_navigation_delegate.h"
 #include "components/navigation_interception/navigation_params.h"
@@ -304,7 +311,8 @@
       did_finish_load);
 }
 
-void TabAndroid::DefaultSearchProviderChanged() {
+void TabAndroid::DefaultSearchProviderChanged(
+    bool google_base_url_domain_changed) {
   // TODO(kmadhusu): Move this function definition to a common place and update
   // BrowserInstantController::DefaultSearchProviderChanged to use the same.
   if (!web_contents())
@@ -730,6 +738,32 @@
                     kImageSearchThumbnailMaxHeight)));
 }
 
+jlong TabAndroid::GetBookmarkId(JNIEnv* env,
+                               jobject obj,
+                               jboolean only_editable) {
+  const GURL& url = dom_distiller::url_utils::GetOriginalUrlFromDistillerUrl(
+      web_contents()->GetURL());
+  Profile* profile = GetProfile();
+
+  // Get all the nodes for |url| and sort them by date added.
+  std::vector<const bookmarks::BookmarkNode*> nodes;
+  ChromeBookmarkClient* client =
+      ChromeBookmarkClientFactory::GetForProfile(profile);
+  bookmarks::BookmarkModel* model =
+      BookmarkModelFactory::GetForProfile(profile);
+  model->GetNodesByURL(url, &nodes);
+  std::sort(nodes.begin(), nodes.end(), &bookmarks::MoreRecentlyAdded);
+
+  // Return the first node matching the search criteria.
+  for (size_t i = 0; i < nodes.size(); ++i) {
+    if (only_editable && !client->CanBeEditedByUser(nodes[i]))
+      continue;
+    return nodes[i]->id();
+  }
+
+  return -1;
+}
+
 namespace {
 
 class ChromeInterceptNavigationDelegate : public InterceptNavigationDelegate {
diff --git a/chrome/browser/android/tab_android.h b/chrome/browser/android/tab_android.h
index ac8e3976..8bb80a47 100644
--- a/chrome/browser/android/tab_android.h
+++ b/chrome/browser/android/tab_android.h
@@ -125,7 +125,8 @@
                        bool did_finish_load) override;
 
   // Overridden from InstantServiceObserver:
-  void DefaultSearchProviderChanged() override;
+  void DefaultSearchProviderChanged(
+      bool google_base_url_domain_changed) override;
 
   // Overridden from SearchTabHelperDelegate:
   void OnWebContentsInstantSupportDisabled(
@@ -190,6 +191,8 @@
 
   void SearchByImageInNewTabAsync(JNIEnv* env, jobject obj);
 
+  jlong GetBookmarkId(JNIEnv* env, jobject obj, jboolean only_editable);
+
   void SetInterceptNavigationDelegate(JNIEnv* env,
                                       jobject obj,
                                       jobject delegate);
diff --git a/chrome/browser/app_controller_mac.h b/chrome/browser/app_controller_mac.h
index 8d53e33..4b2dc898 100644
--- a/chrome/browser/app_controller_mac.h
+++ b/chrome/browser/app_controller_mac.h
@@ -10,6 +10,7 @@
 #import <Cocoa/Cocoa.h>
 #include <vector>
 
+#include "base/files/file_path.h"
 #include "base/mac/scoped_nsobject.h"
 #include "base/memory/scoped_ptr.h"
 #include "base/observer_list.h"
@@ -57,7 +58,7 @@
   // a weak pointer that is updated to match the corresponding cache entry
   // during a profile switch.
   BookmarkMenuBridge* bookmarkMenuBridge_;
-  std::map<Profile*, BookmarkMenuBridge*> profileBookmarkMenuBridgeMap_;
+  std::map<base::FilePath, BookmarkMenuBridge*> profileBookmarkMenuBridgeMap_;
 
   scoped_ptr<HistoryMenuBridge> historyMenuBridge_;
 
diff --git a/chrome/browser/app_controller_mac.mm b/chrome/browser/app_controller_mac.mm
index 41043f7f..01feef4d 100644
--- a/chrome/browser/app_controller_mac.mm
+++ b/chrome/browser/app_controller_mac.mm
@@ -893,18 +893,14 @@
 
 // Called from the AppControllerProfileObserver every time a profile is deleted.
 - (void)profileWasRemoved:(const base::FilePath&)profilePath {
-  Profile* lastProfile = [self lastProfile];
-
   // If the lastProfile has been deleted, the profile manager has
   // already loaded a new one, so the pointer needs to be updated;
   // otherwise we will try to start up a browser window with a pointer
   // to the old profile.
-  if (profilePath == lastProfile->GetPath())
+  if (lastProfile_ && profilePath == lastProfile_->GetPath())
     lastProfile_ = g_browser_process->profile_manager()->GetLastUsedProfile();
 
-  Profile* profile =
-      g_browser_process->profile_manager()->GetProfile(profilePath);
-  auto it = profileBookmarkMenuBridgeMap_.find(profile);
+  auto it = profileBookmarkMenuBridgeMap_.find(profilePath);
   if (it != profileBookmarkMenuBridgeMap_.end()) {
     delete it->second;
     profileBookmarkMenuBridgeMap_.erase(it);
@@ -1582,12 +1578,12 @@
   // Rebuild the menus with the new profile.
   lastProfile_ = profile;
 
-  auto it = profileBookmarkMenuBridgeMap_.find(profile);
+  auto it = profileBookmarkMenuBridgeMap_.find(profile->GetPath());
   if (it == profileBookmarkMenuBridgeMap_.end()) {
     base::scoped_nsobject<NSMenu> submenu(
         [[[[NSApp mainMenu] itemWithTag:IDC_BOOKMARKS_MENU] submenu] copy]);
-    bookmarkMenuBridge_ = new BookmarkMenuBridge(lastProfile_, submenu);
-    profileBookmarkMenuBridgeMap_[profile] = bookmarkMenuBridge_;
+    bookmarkMenuBridge_ = new BookmarkMenuBridge(profile, submenu);
+    profileBookmarkMenuBridgeMap_[profile->GetPath()] = bookmarkMenuBridge_;
   } else {
     bookmarkMenuBridge_ = it->second;
   }
diff --git a/chrome/browser/apps/app_shim/app_shim_interactive_uitest_mac.mm b/chrome/browser/apps/app_shim/app_shim_interactive_uitest_mac.mm
index 59c61d6b..3a3e202 100644
--- a/chrome/browser/apps/app_shim/app_shim_interactive_uitest_mac.mm
+++ b/chrome/browser/apps/app_shim/app_shim_interactive_uitest_mac.mm
@@ -45,6 +45,11 @@
   AppShimInteractiveTest()
       : auto_reset_(&g_app_shims_allow_update_and_launch_in_tests, true) {}
 
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    PlatformAppBrowserTest::SetUpCommandLine(command_line);
+    command_line->AppendSwitch(switches::kEnableNewBookmarkApps);
+  }
+
  private:
   // Temporarily enable app shims.
   base::AutoReset<bool> auto_reset_;
diff --git a/chrome/browser/apps/custom_launcher_page_browsertest_views.cc b/chrome/browser/apps/custom_launcher_page_browsertest_views.cc
index 4cde0fc8..8f4fb35 100644
--- a/chrome/browser/apps/custom_launcher_page_browsertest_views.cc
+++ b/chrome/browser/apps/custom_launcher_page_browsertest_views.cc
@@ -196,11 +196,11 @@
   // Scrolling left, right or up should do nothing.
   event_generator.MoveMouseWheel(-5, 0);
   event_generator.MoveMouseWheel(5, 0);
-  event_generator.MoveMouseWheel(0, -5);
+  event_generator.MoveMouseWheel(0, 5);
   EXPECT_TRUE(
       contents_view->IsStateActive(app_list::AppListModel::STATE_START));
   // Scroll down to open launcher page.
-  event_generator.MoveMouseWheel(0, 5);
+  event_generator.MoveMouseWheel(0, -5);
   EXPECT_TRUE(contents_view->IsStateActive(
       app_list::AppListModel::STATE_CUSTOM_LAUNCHER_PAGE));
 
@@ -240,12 +240,12 @@
                                  num_steps, num_fingers);
   event_generator.ScrollSequence(point_in_clickzone, step_delay, 5, 0,
                                  num_steps, num_fingers);
-  event_generator.ScrollSequence(point_in_clickzone, step_delay, 0, -5,
+  event_generator.ScrollSequence(point_in_clickzone, step_delay, 0, 5,
                                  num_steps, num_fingers);
   EXPECT_TRUE(
       contents_view->IsStateActive(app_list::AppListModel::STATE_START));
   // Scroll up to open launcher page.
-  event_generator.ScrollSequence(point_in_clickzone, step_delay, 0, 5,
+  event_generator.ScrollSequence(point_in_clickzone, step_delay, 0, -5,
                                  num_steps, num_fingers);
   EXPECT_TRUE(contents_view->IsStateActive(
       app_list::AppListModel::STATE_CUSTOM_LAUNCHER_PAGE));
diff --git a/chrome/browser/apps/guest_view/extension_view_browsertest.cc b/chrome/browser/apps/guest_view/extension_view_browsertest.cc
index b0eb37c..f539220 100644
--- a/chrome/browser/apps/guest_view/extension_view_browsertest.cc
+++ b/chrome/browser/apps/guest_view/extension_view_browsertest.cc
@@ -125,5 +125,18 @@
 
 // Tests that verify that <extensionview> can navigate to different sources.
 IN_PROC_BROWSER_TEST_F(ExtensionViewTest, ShimSrcAttribute) {
-  ASSERT_TRUE(RunPlatformAppTest("platform_apps/extension_view/src_attribute"));
+  ASSERT_TRUE(RunPlatformAppTest(
+      "platform_apps/extension_view/src_attribute"));
+}
+
+// Tests that verify that <extensionview> can call the connect function.
+IN_PROC_BROWSER_TEST_F(ExtensionViewTest, ConnectAPICall) {
+  ASSERT_TRUE(RunPlatformAppTest("platform_apps/extension_view/connect_api"));
+}
+
+// Tests that verify that <extensionview> does not change extension ID if
+// someone tries to change it in JavaScript.
+IN_PROC_BROWSER_TEST_F(ExtensionViewTest, ShimExtensionAttribute) {
+  ASSERT_TRUE(RunPlatformAppTest(
+      "platform_apps/extension_view/extension_attribute"));
 }
diff --git a/chrome/browser/apps/guest_view/web_view_browsertest.cc b/chrome/browser/apps/guest_view/web_view_browsertest.cc
index a7ef8be..bec47cb 100644
--- a/chrome/browser/apps/guest_view/web_view_browsertest.cc
+++ b/chrome/browser/apps/guest_view/web_view_browsertest.cc
@@ -5,6 +5,7 @@
 #include "base/path_service.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
+#include "chrome/app/chrome_command_ids.h"
 #include "chrome/browser/apps/app_browsertest_util.h"
 #include "chrome/browser/chrome_content_browser_client.h"
 #include "chrome/browser/prerender/prerender_link_manager.h"
@@ -1831,6 +1832,86 @@
   mock->WaitForRequestMediaPermission();
 }
 
+IN_PROC_BROWSER_TEST_F(WebViewTest, OpenURLFromTab_CurrentTab_Abort) {
+  LoadAppWithGuest("web_view/simple");
+
+  // Verify that OpenURLFromTab with a window disposition of CURRENT_TAB will
+  // navigate the current <webview>.
+  ExtensionTestMessageListener load_listener("WebViewTest.LOADSTOP", false);
+
+  // Navigating to a file URL is forbidden inside a <webview>.
+  content::OpenURLParams params(GURL("file://foo"),
+                                content::Referrer(),
+                                CURRENT_TAB,
+                                ui::PAGE_TRANSITION_AUTO_TOPLEVEL,
+                                true /* is_renderer_initiated */);
+  GetGuestWebContents()->GetDelegate()->OpenURLFromTab(
+      GetGuestWebContents(), params);
+
+  ASSERT_TRUE(load_listener.WaitUntilSatisfied());
+
+  // Verify that the <webview> ends up at about:blank.
+  EXPECT_EQ(GURL(url::kAboutBlankURL),
+            GetGuestWebContents()->GetLastCommittedURL());
+}
+
+IN_PROC_BROWSER_TEST_F(WebViewTest, OpenURLFromTab_NewWindow_Abort) {
+  LoadAppWithGuest("web_view/simple");
+
+  // Verify that OpenURLFromTab with a window disposition of NEW_BACKGROUND_TAB
+  // will trigger the <webview>'s New Window API.
+  ExtensionTestMessageListener new_window_listener(
+      "WebViewTest.NEWWINDOW", false);
+
+  // Navigating to a file URL is forbidden inside a <webview>.
+  content::OpenURLParams params(GURL("file://foo"),
+                                content::Referrer(),
+                                NEW_BACKGROUND_TAB,
+                                ui::PAGE_TRANSITION_AUTO_TOPLEVEL,
+                                true /* is_renderer_initiated */);
+  GetGuestWebContents()->GetDelegate()->OpenURLFromTab(
+      GetGuestWebContents(), params);
+
+  ASSERT_TRUE(new_window_listener.WaitUntilSatisfied());
+
+  // Verify that a new guest was created.
+  content::WebContents* new_guest_web_contents =
+      GetGuestViewManager()->GetLastGuestCreated();
+  EXPECT_NE(GetGuestWebContents(), new_guest_web_contents);
+
+  // Verify that the new <webview> guest ends up at about:blank.
+  EXPECT_EQ(GURL(url::kAboutBlankURL),
+            new_guest_web_contents->GetLastCommittedURL());
+}
+
+// This test executes the context menu command 'LanguageSettings' which will
+// load chrome://settings/languages in a browser window. This is a browser-
+// initiated operation and so we expect this to succeed if the embedder is
+// allowed to perform the operation.
+IN_PROC_BROWSER_TEST_F(WebViewTest, ContextMenuLanguageSettings) {
+  LoadAppWithGuest("web_view/context_menus/basic");
+
+  content::WebContents* guest_web_contents = GetGuestWebContents();
+  content::WebContents* embedder = GetEmbedderWebContents();
+  ASSERT_TRUE(embedder);
+
+  // Create and build our test context menu.
+  content::WebContentsAddedObserver web_contents_added_observer;
+
+  GURL page_url("http://www.google.com");
+  scoped_ptr<TestRenderViewContextMenu> menu(TestRenderViewContextMenu::Create(
+      guest_web_contents, page_url, GURL(), GURL()));
+  menu->ExecuteCommand(IDC_CONTENT_CONTEXT_LANGUAGE_SETTINGS, 0);
+
+  content::WebContents* new_contents =
+      web_contents_added_observer.GetWebContents();
+
+  // Verify that a new WebContents has been created that is at the Language
+  // Settings page.
+  EXPECT_EQ(GURL("chrome://settings/languages"),
+            new_contents->GetVisibleURL());
+}
+
 IN_PROC_BROWSER_TEST_F(WebViewTest, ContextMenusAPI_Basic) {
   LoadAppWithGuest("web_view/context_menus/basic");
 
@@ -1956,7 +2037,7 @@
 #endif
 IN_PROC_BROWSER_TEST_F(WebViewTest, MAYBE_TearDownTest) {
   const extensions::Extension* extension =
-      LoadAndLaunchPlatformApp("web_view/teardown", "guest-loaded");
+      LoadAndLaunchPlatformApp("web_view/simple", "WebViewTest.LAUNCHED");
   extensions::AppWindow* window = NULL;
   if (!GetAppWindowCount())
     window = CreateAppWindow(extension);
@@ -1965,7 +2046,7 @@
   CloseAppWindow(window);
 
   // Load the app again.
-  LoadAndLaunchPlatformApp("web_view/teardown", "guest-loaded");
+  LoadAndLaunchPlatformApp("web_view/simple", "WebViewTest.LAUNCHED");
 }
 
 // In following GeolocationAPIEmbedderHasNoAccess* tests, embedder (i.e. the
diff --git a/chrome/browser/apps/shortcut_manager.cc b/chrome/browser/apps/shortcut_manager.cc
index 88ec3f427..be1533a 100644
--- a/chrome/browser/apps/shortcut_manager.cc
+++ b/chrome/browser/apps/shortcut_manager.cc
@@ -36,7 +36,9 @@
 // need to be recreated. This might happen when we change various aspects of app
 // shortcuts like command-line flags or associated icons, binaries, etc.
 #if defined(OS_MACOSX)
-const int kCurrentAppShortcutsVersion = 3;
+// This was changed to 3 at r316520, but reverted again. Next time we need to
+// trigger a recreate, increment this to 4.
+const int kCurrentAppShortcutsVersion = 2;
 #else
 const int kCurrentAppShortcutsVersion = 0;
 #endif
diff --git a/chrome/browser/autocomplete/scored_history_match_builder_impl.cc b/chrome/browser/autocomplete/scored_history_match_builder_impl.cc
index ceb1fb9..3db26785 100644
--- a/chrome/browser/autocomplete/scored_history_match_builder_impl.cc
+++ b/chrome/browser/autocomplete/scored_history_match_builder_impl.cc
@@ -12,6 +12,8 @@
 #include "base/logging.h"
 #include "base/metrics/histogram.h"
 #include "base/numerics/safe_conversions.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_split.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/autocomplete/history_url_provider.h"
@@ -124,6 +126,10 @@
 int ScoredHistoryMatchBuilderImpl::bookmark_value_ = 1;
 bool ScoredHistoryMatchBuilderImpl::allow_tld_matches_ = false;
 bool ScoredHistoryMatchBuilderImpl::allow_scheme_matches_ = false;
+bool ScoredHistoryMatchBuilderImpl::hqp_experimental_scoring_enabled_ = false;
+float ScoredHistoryMatchBuilderImpl::topicality_threshold_ = -1;
+std::vector<ScoredHistoryMatchBuilderImpl::ScoreMaxRelevance>*
+    ScoredHistoryMatchBuilderImpl::hqp_relevance_buckets_ = NULL;
 
 ScoredHistoryMatchBuilderImpl::ScoredHistoryMatchBuilderImpl(
     const IsBookmarkedCallback& is_bookmarked)
@@ -247,7 +253,8 @@
   const float frequency_score = GetFrequency(
       now, (!is_bookmarked_.is_null() && is_bookmarked_.Run(gurl)), visits);
   scored_history_match.raw_score = base::saturated_cast<int>(
-      GetFinalRelevancyScore(topicality_score, frequency_score));
+      GetFinalRelevancyScore(topicality_score, frequency_score,
+                             *hqp_relevance_buckets_));
 
   if (kAlsoDoHupLikeScoring && scored_history_match.can_inline) {
     // HistoryURL-provider-like scoring gives any match that is
@@ -384,6 +391,7 @@
 
   InitRawTermScoreToTopicalityScoreArray();
   InitDaysAgoToRecencyScoreArray();
+  InitHQPExperimentalParams();
 }
 
 // static
@@ -522,7 +530,15 @@
   // TODO(mpearson): If there are multiple terms, consider taking the
   // geometric mean of per-term scores rather than the arithmetic mean.
 
-  return topicality_score / num_terms;
+  const float final_topicality_score = topicality_score / num_terms;
+
+  // Demote the URL if the topicality score is less than threshold.
+  if (hqp_experimental_scoring_enabled_ &&
+      (final_topicality_score < topicality_threshold_)) {
+    return 0.0;
+  }
+
+  return final_topicality_score;
 }
 
 // static
@@ -566,7 +582,11 @@
 // static
 float ScoredHistoryMatchBuilderImpl::GetFinalRelevancyScore(
     float topicality_score,
-    float frequency_score) {
+    float frequency_score,
+    const std::vector<ScoreMaxRelevance>& hqp_relevance_buckets) {
+  DCHECK(hqp_relevance_buckets.size() > 0);
+  DCHECK_EQ(hqp_relevance_buckets[0].first, 0.0);
+
   if (topicality_score == 0)
     return 0;
   // Here's how to interpret intermediate_score: Suppose the omnibox
@@ -582,25 +602,93 @@
   // - a typed visit once a week -> 11.77
   // - a typed visit every three days -> 14.12
   // - at least ten typed visits today -> 20.0 (maximum score)
-  const float intermediate_score = topicality_score * frequency_score;
+  //
   // The below code maps intermediate_score to the range [0, 1399].
-  // The score maxes out at 1400 (i.e., cannot beat a good inline result).
-  if (intermediate_score <= 1) {
-    // Linearly extrapolate between 0 and 1.5 so 0 has a score of 400
-    // and 1.5 has a score of 600.
-    const float slope = (600 - 400) / (1.5f - 0.0f);
-    return 400 + slope * intermediate_score;
+  // For example:
+  // HQP default scoring buckets: "0.0:400,1.5:600,12.0:1300,20.0:1399"
+  // We will linearly interpolate the scores between:
+  //      0 to 1.5    --> 400 to 600
+  //    1.5 to 12.0   --> 600 to 1300
+  //    12.0 to 20.0  --> 1300 to 1399
+  //       >= 20.0    --> 1399
+  //
+  // The score maxes out at 1399 (i.e., cannot beat a good inlineable result
+  // from HistoryURL provider).
+  const float intermediate_score = topicality_score * frequency_score;
+
+  // Find the threshold where intermediate score is greater than bucket.
+  size_t i = 1;
+  for (; i < hqp_relevance_buckets.size(); ++i) {
+    const ScoreMaxRelevance& hqp_bucket = hqp_relevance_buckets[i];
+    if (intermediate_score >= hqp_bucket.first) {
+      continue;
+    }
+    const ScoreMaxRelevance& previous_bucket = hqp_relevance_buckets[i-1];
+    const float slope = ((hqp_bucket.second - previous_bucket.second) /
+                         (hqp_bucket.first - previous_bucket.first));
+    return (previous_bucket.second +
+            (slope * (intermediate_score - previous_bucket.first)));
   }
-  if (intermediate_score <= 12.0) {
-    // Linearly extrapolate up to 12 so 12 has a score of 1300.
-    const float slope = (1300 - 600) / (12.0f - 1.5f);
-    return 600 + slope * (intermediate_score - 1.5);
+  // It will reach this stage when the score is > highest bucket score.
+  // Return the highest bucket score.
+  return hqp_relevance_buckets[i-1].second;
+}
+
+// static
+void ScoredHistoryMatchBuilderImpl::InitHQPExperimentalParams() {
+  // These are default HQP relevance scoring buckets.
+  // See GetFinalRelevancyScore() for details.
+  std::string hqp_relevance_buckets_str = "0.0:400,1.5:600,12.0:1300,20.0:1399";
+
+  // Fetch the experiment params if they are any.
+  hqp_experimental_scoring_enabled_ =
+    OmniboxFieldTrial::HQPExperimentalScoringEnabled();
+
+  if (hqp_experimental_scoring_enabled_) {
+    // Add the topicality threshold from experiment params.
+    float hqp_experimental_topicality_threhold =
+      OmniboxFieldTrial::HQPExperimentalTopicalityThreshold();
+      topicality_threshold_ = hqp_experimental_topicality_threhold;
+
+    // Add the HQP experimental scoring buckets.
+    std::string hqp_experimental_scoring_buckets =
+      OmniboxFieldTrial::HQPExperimentalScoringBuckets();
+    if (!hqp_experimental_scoring_buckets.empty())
+      hqp_relevance_buckets_str = hqp_experimental_scoring_buckets;
   }
-  // Linearly extrapolate so a score of 20 (or more) has a score of 1399.
-  // (Scores above 20 are possible for URLs that have multiple term hits
-  // in the URL and/or title and that are visited practically all
-  // the time using typed visits.  We don't attempt to distinguish
-  // between these very good results.)
-  const float slope = (1399 - 1300) / (20.0f - 12.0f);
-  return std::min(1399.0, 1300 + slope * (intermediate_score - 12.0));
+
+  // Parse the hqp_relevance_buckets_str string once and store them in vector
+  // which is easy to access.
+  hqp_relevance_buckets_ =
+      new std::vector<ScoredHistoryMatchBuilderImpl::ScoreMaxRelevance>();
+
+  bool is_valid_bucket_str = GetHQPBucketsFromString(hqp_relevance_buckets_str,
+                                                     hqp_relevance_buckets_);
+  DCHECK(is_valid_bucket_str);
+}
+
+// static
+bool ScoredHistoryMatchBuilderImpl::GetHQPBucketsFromString(
+    const std::string& buckets_str,
+    std::vector<ScoreMaxRelevance>* hqp_buckets) {
+  DCHECK(hqp_buckets != NULL);
+  DCHECK(!buckets_str.empty());
+
+  base::StringPairs kv_pairs;
+  if (base::SplitStringIntoKeyValuePairs(buckets_str,
+                                         ':', ',', &kv_pairs)) {
+    for (base::StringPairs::const_iterator it = kv_pairs.begin();
+         it != kv_pairs.end(); ++it) {
+      ScoreMaxRelevance bucket;
+      bool is_valid_intermediate_score = base::StringToDouble(it->first,
+                                                              &bucket.first);
+      DCHECK(is_valid_intermediate_score);
+      bool is_valid_hqp_score = base::StringToInt(it->second,
+                                                  &bucket.second);
+      DCHECK(is_valid_hqp_score);
+      hqp_buckets->push_back(bucket);
+    }
+    return true;
+  }
+  return false;
 }
diff --git a/chrome/browser/autocomplete/scored_history_match_builder_impl.h b/chrome/browser/autocomplete/scored_history_match_builder_impl.h
index b1728f2..578d243 100644
--- a/chrome/browser/autocomplete/scored_history_match_builder_impl.h
+++ b/chrome/browser/autocomplete/scored_history_match_builder_impl.h
@@ -36,6 +36,12 @@
   // support being called multiple times.
   typedef base::Callback<bool(const GURL& url)> IsBookmarkedCallback;
 
+  // ScoreMaxRelevance maps from an intermediate-score to the maximum
+  // final-relevance score given to a URL for this intermediate score.
+  // This is used to store the score ranges of HQP relevance buckets.
+  // Please see GetFinalRelevancyScore() for details.
+  typedef std::pair<double, int> ScoreMaxRelevance;
+
   explicit ScoredHistoryMatchBuilderImpl(
       const IsBookmarkedCallback& is_bookmarked);
   ~ScoredHistoryMatchBuilderImpl() override;
@@ -65,6 +71,10 @@
 
  private:
   friend class ScoredHistoryMatchBuilderImplTest;
+  FRIEND_TEST_ALL_PREFIXES(ScoredHistoryMatchBuilderImplTest,
+                           GetFinalRelevancyScore);
+  FRIEND_TEST_ALL_PREFIXES(ScoredHistoryMatchBuilderImplTest,
+                           GetHQPBucketsFromString);
   FRIEND_TEST_ALL_PREFIXES(ScoredHistoryMatchBuilderImplTest, ScoringBookmarks);
   FRIEND_TEST_ALL_PREFIXES(ScoredHistoryMatchBuilderImplTest, ScoringScheme);
   FRIEND_TEST_ALL_PREFIXES(ScoredHistoryMatchBuilderImplTest, ScoringTLD);
@@ -98,9 +108,28 @@
                             const history::VisitInfoVector& visits);
 
   // Combines the two component scores into a final score that's
-  // an appropriate value to use as a relevancy score.
-  static float GetFinalRelevancyScore(float topicality_score,
-                                      float frequency_score);
+  // an appropriate value to use as a relevancy score. Scoring buckets are
+  // specified through |hqp_relevance_buckets|. Please see the function
+  // implementation for more details.
+  static float GetFinalRelevancyScore(
+      float topicality_score,
+      float frequency_score,
+      const std::vector<ScoreMaxRelevance>& hqp_relevance_buckets);
+
+  // Initializes the HQP experimental params: |hqp_relevance_buckets_|
+  // to default buckets. If hqp experimental scoring is enabled, it
+  // fetches the |hqp_experimental_scoring_enabled_|, |topicality_threshold_|
+  // and |hqp_relevance_buckets_| from omnibox field trials.
+  static void InitHQPExperimentalParams();
+
+  // Helper function to parse the string containing the scoring buckets.
+  // For example,
+  // String: "0.0:400,1.5:600,12.0:1300,20.0:1399"
+  // Buckets: vector[(0.0, 400),(1.5,600),(12.0,1300),(20.0,1399)]
+  // Returns false, in case if it fail to parse the string.
+  static bool GetHQPBucketsFromString(
+      const std::string& buckets_str,
+      std::vector<ScoreMaxRelevance>* hqp_buckets);
 
   // Untyped visits to bookmarked pages score this, compared to 1 for
   // untyped visits to non-bookmarked pages and 20 for typed visits.
@@ -116,6 +145,19 @@
   // be unset during testing.
   IsBookmarkedCallback is_bookmarked_;
 
+  // True, if hqp experimental scoring is enabled.
+  static bool hqp_experimental_scoring_enabled_;
+
+  // |topicality_threshold_| is used to control the topicality scoring.
+  // If |topicality_threshold_| > 0, then URLs with topicality-score < threshold
+  // are given topicality score of 0. By default it is initalized to -1.
+  static float topicality_threshold_;
+
+  // |hqp_relevance_buckets_| gives mapping from (topicality*frequency)
+  // to the final relevance scoring. Please see GetFinalRelevancyScore()
+  // for more details and scoring method.
+  static std::vector<ScoreMaxRelevance>* hqp_relevance_buckets_;
+
   DISALLOW_COPY_AND_ASSIGN(ScoredHistoryMatchBuilderImpl);
 };
 
diff --git a/chrome/browser/autocomplete/scored_history_match_builder_impl_unittest.cc b/chrome/browser/autocomplete/scored_history_match_builder_impl_unittest.cc
index 774735d..406beeee8 100644
--- a/chrome/browser/autocomplete/scored_history_match_builder_impl_unittest.cc
+++ b/chrome/browser/autocomplete/scored_history_match_builder_impl_unittest.cc
@@ -10,9 +10,12 @@
 #include "base/strings/string16.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/autocomplete/scored_history_match_builder_impl.h"
+#include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 using base::ASCIIToUTF16;
+using testing::ElementsAre;
+using testing::Pair;
 
 namespace {
 
@@ -469,3 +472,62 @@
   EXPECT_GT(hostname_mid_word_score, tld_score);
   EXPECT_GT(hostname_mid_word_score, tld_mid_word_score);
 }
+
+// Test the function GetFinalRelevancyScore().
+TEST_F(ScoredHistoryMatchBuilderImplTest, GetFinalRelevancyScore) {
+  // hqp_relevance_buckets = "0.0:100,1.0:200,4.0:500,8.0:900,10.0:1000";
+  std::vector<ScoredHistoryMatchBuilderImpl::ScoreMaxRelevance> hqp_buckets;
+  hqp_buckets.push_back(std::make_pair(0.0, 100));
+  hqp_buckets.push_back(std::make_pair(1.0, 200));
+  hqp_buckets.push_back(std::make_pair(4.0, 500));
+  hqp_buckets.push_back(std::make_pair(8.0, 900));
+  hqp_buckets.push_back(std::make_pair(10.0, 1000));
+  // Check when topicality score is zero.
+  float topicality_score = 0.0;
+  float frequency_score = 10.0;
+  // intermediate_score = 0.0 * 10.0 = 0.0.
+  EXPECT_EQ(0,
+            ScoredHistoryMatchBuilderImpl::GetFinalRelevancyScore(
+              topicality_score, frequency_score, hqp_buckets));
+
+  // Check when intermediate score falls at the border range.
+  topicality_score = 0.4f;
+  frequency_score = 10.0f;
+  // intermediate_score = 0.5 * 10.0 = 4.0.
+  EXPECT_EQ(500,
+            ScoredHistoryMatchBuilderImpl::GetFinalRelevancyScore(
+              topicality_score, frequency_score, hqp_buckets));
+
+  // Checking the score that falls into one of the buckets.
+  topicality_score = 0.5f;
+  frequency_score = 10.0f;
+  // intermediate_score = 0.5 * 10.0 = 5.0.
+  EXPECT_EQ(600,  // 500 + (((900 - 500)/(8 -4)) * 1) = 600.
+            ScoredHistoryMatchBuilderImpl::GetFinalRelevancyScore(
+              topicality_score, frequency_score, hqp_buckets));
+
+  // Never give the score greater than maximum specified.
+  topicality_score = 0.5f;
+  frequency_score = 22.0f;
+  // intermediate_score = 0.5 * 22.0 = 11.0
+  EXPECT_EQ(1000,
+            ScoredHistoryMatchBuilderImpl::GetFinalRelevancyScore(
+              topicality_score, frequency_score, hqp_buckets));
+}
+
+// Test the function GetHQPBucketsFromString().
+TEST_F(ScoredHistoryMatchBuilderImplTest, GetHQPBucketsFromString) {
+  std::string buckets_str = "0.0:400,1.5:600,12.0:1300,20.0:1399";
+  std::vector<ScoredHistoryMatchBuilderImpl::ScoreMaxRelevance> hqp_buckets;
+
+  EXPECT_TRUE(ScoredHistoryMatchBuilderImpl::GetHQPBucketsFromString(
+                buckets_str, &hqp_buckets));
+  EXPECT_THAT(hqp_buckets, ElementsAre(Pair(0.0, 400),
+                                       Pair(1.5, 600),
+                                       Pair(12.0, 1300),
+                                       Pair(20.0, 1399)));
+  // invalid string.
+  buckets_str = "0.0,400,1.5,600";
+  EXPECT_FALSE(ScoredHistoryMatchBuilderImpl::GetHQPBucketsFromString(
+                 buckets_str, &hqp_buckets));
+}
diff --git a/chrome/browser/autocomplete/shortcuts_provider_unittest.cc b/chrome/browser/autocomplete/shortcuts_provider_unittest.cc
index e5546bd7..b2e4da31 100644
--- a/chrome/browser/autocomplete/shortcuts_provider_unittest.cc
+++ b/chrome/browser/autocomplete/shortcuts_provider_unittest.cc
@@ -314,6 +314,7 @@
   // Run all pending tasks or else some threads hold on to the message loop
   // and prevent it from being deleted.
   message_loop_.RunUntilIdle();
+  profile_.DestroyHistoryService();
   provider_ = NULL;
 }
 
diff --git a/chrome/browser/background/background_contents.cc b/chrome/browser/background/background_contents.cc
index 7a1150b3..bf94280 100644
--- a/chrome/browser/background/background_contents.cc
+++ b/chrome/browser/background/background_contents.cc
@@ -16,6 +16,9 @@
 #include "content/public/browser/session_storage_namespace.h"
 #include "content/public/browser/site_instance.h"
 #include "content/public/browser/web_contents.h"
+#include "extensions/browser/extension_host_delegate.h"
+#include "extensions/browser/extension_host_queue.h"
+#include "extensions/browser/extensions_browser_client.h"
 #include "extensions/browser/view_type_utils.h"
 #include "ui/gfx/geometry/rect.h"
 
@@ -29,7 +32,9 @@
     Delegate* delegate,
     const std::string& partition_id,
     content::SessionStorageNamespace* session_storage_namespace)
-    : delegate_(delegate) {
+    : delegate_(delegate),
+      extension_host_delegate_(extensions::ExtensionsBrowserClient::Get()
+                                   ->CreateExtensionHostDelegate()) {
   profile_ = Profile::FromBrowserContext(
       site_instance->GetBrowserContext());
 
@@ -82,12 +87,19 @@
       chrome::NOTIFICATION_BACKGROUND_CONTENTS_DELETED,
       content::Source<Profile>(profile_),
       content::Details<BackgroundContents>(this));
+
+  extension_host_delegate_->GetExtensionHostQueue()->Remove(this);
 }
 
 const GURL& BackgroundContents::GetURL() const {
   return web_contents_.get() ? web_contents_->GetURL() : GURL::EmptyGURL();
 }
 
+void BackgroundContents::CreateRenderViewSoon(const GURL& url) {
+  initial_url_ = url;
+  extension_host_delegate_->GetExtensionHostQueue()->Add(this);
+}
+
 void BackgroundContents::CloseContents(WebContents* source) {
   content::NotificationService::current()->Notify(
       chrome::NOTIFICATION_BACKGROUND_CONTENTS_CLOSED,
@@ -160,3 +172,9 @@
       break;
   }
 }
+
+void BackgroundContents::CreateRenderViewNow() {
+  web_contents()->GetController().LoadURL(initial_url_, content::Referrer(),
+                                          ui::PAGE_TRANSITION_LINK,
+                                          std::string());
+}
diff --git a/chrome/browser/background/background_contents.h b/chrome/browser/background/background_contents.h
index e7345e3..b4a94c4 100644
--- a/chrome/browser/background/background_contents.h
+++ b/chrome/browser/background/background_contents.h
@@ -12,7 +12,9 @@
 #include "content/public/browser/notification_registrar.h"
 #include "content/public/browser/web_contents_delegate.h"
 #include "content/public/browser/web_contents_observer.h"
+#include "extensions/browser/deferred_start_render_host.h"
 #include "ui/base/window_open_disposition.h"
+#include "url/gurl.h"
 
 class Profile;
 
@@ -21,10 +23,15 @@
 class SiteInstance;
 };
 
+namespace extensions {
+class ExtensionHostDelegate;
+}
+
 // This class maintains a WebContents used in the background. It can host a
 // renderer, but does not have any visible display.
 // TODO(atwilson): Unify this with background pages; http://crbug.com/77790
-class BackgroundContents : public content::WebContentsDelegate,
+class BackgroundContents : public extensions::DeferredStartRenderHost,
+                           public content::WebContentsDelegate,
                            public content::WebContentsObserver,
                            public content::NotificationObserver {
  public:
@@ -56,6 +63,9 @@
   content::WebContents* web_contents() const { return web_contents_.get(); }
   virtual const GURL& GetURL() const;
 
+  // Adds this BackgroundContents to the queue of RenderViews to create.
+  void CreateRenderViewSoon(const GURL& url);
+
   // content::WebContentsDelegate implementation:
   void CloseContents(content::WebContents* source) override;
   bool ShouldSuppressDialogs(content::WebContents* source) override;
@@ -81,13 +91,22 @@
   BackgroundContents();
 
  private:
+  // DeferredStartRenderHost implementation:
+  void CreateRenderViewNow() override;
+
   // The delegate for this BackgroundContents.
   Delegate* delegate_;
 
+  // Delegate for choosing an ExtensionHostQueue.
+  scoped_ptr<extensions::ExtensionHostDelegate> extension_host_delegate_;
+
   Profile* profile_;
   scoped_ptr<content::WebContents> web_contents_;
   content::NotificationRegistrar registrar_;
 
+  // The initial URL to load.
+  GURL initial_url_;
+
   DISALLOW_COPY_AND_ASSIGN(BackgroundContents);
 };
 
diff --git a/chrome/browser/background/background_contents_service.cc b/chrome/browser/background/background_contents_service.cc
index 503b422c..9cb6447 100644
--- a/chrome/browser/background/background_contents_service.cc
+++ b/chrome/browser/background/background_contents_service.cc
@@ -189,7 +189,7 @@
       extension->is_app() ? IDS_BACKGROUND_CRASHED_APP_BALLOON_MESSAGE :
                             IDS_BACKGROUND_CRASHED_EXTENSION_BALLOON_MESSAGE,
       base::UTF8ToUTF16(extension->name()));
-  extension_misc::ExtensionIcons size(extension_misc::EXTENSION_ICON_MEDIUM);
+  extension_misc::ExtensionIcons size(extension_misc::EXTENSION_ICON_LARGE);
   extensions::ExtensionResource resource =
       extensions::IconsInfo::GetIconResource(
           extension, size, ExtensionIconSet::MATCH_SMALLER);
@@ -634,10 +634,7 @@
       std::string(),
       NULL);
 
-  // TODO(atwilson): Create RenderViews asynchronously to avoid increasing
-  // startup latency (http://crbug.com/47236).
-  contents->web_contents()->GetController().LoadURL(
-      url, content::Referrer(), ui::PAGE_TRANSITION_LINK, std::string());
+  contents->CreateRenderViewSoon(url);
 }
 
 BackgroundContents* BackgroundContentsService::CreateBackgroundContents(
diff --git a/chrome/browser/banners/app_banner_metrics.cc b/chrome/browser/banners/app_banner_metrics.cc
new file mode 100644
index 0000000..57a7d0f
--- /dev/null
+++ b/chrome/browser/banners/app_banner_metrics.cc
@@ -0,0 +1,38 @@
+// 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 "chrome/browser/banners/app_banner_metrics.h"
+
+#include "base/metrics/histogram.h"
+
+namespace banners {
+
+void TrackDismissEvent(int event) {
+  std::vector<int> codes;
+  for (int i = DISMISS_EVENT_MIN + 1; i < DISMISS_EVENT_MAX; ++i) {
+    codes.push_back(i);
+  }
+  DCHECK(std::find(codes.begin(), codes.end(), event) != codes.end());
+  UMA_HISTOGRAM_CUSTOM_ENUMERATION("AppBanners.DismissEvent", event, codes);
+}
+
+void TrackDisplayEvent(int event) {
+  std::vector<int> codes;
+  for (int i = DISPLAY_EVENT_MIN + 1; i < DISPLAY_EVENT_MAX; ++i) {
+    codes.push_back(i);
+  }
+  DCHECK(std::find(codes.begin(), codes.end(), event) != codes.end());
+  UMA_HISTOGRAM_CUSTOM_ENUMERATION("AppBanners.DisplayEvent", event, codes);
+}
+
+void TrackInstallEvent(int event) {
+  std::vector<int> codes;
+  for (int i = INSTALL_EVENT_MIN + 1; i < INSTALL_EVENT_MAX; ++i) {
+    codes.push_back(i);
+  }
+  DCHECK(std::find(codes.begin(), codes.end(), event) != codes.end());
+  UMA_HISTOGRAM_CUSTOM_ENUMERATION("AppBanners.InstallEvent", event, codes);
+}
+
+}  // namespace banners
diff --git a/chrome/browser/banners/app_banner_metrics.h b/chrome/browser/banners/app_banner_metrics.h
new file mode 100644
index 0000000..3ae1912
--- /dev/null
+++ b/chrome/browser/banners/app_banner_metrics.h
@@ -0,0 +1,52 @@
+// 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 CHROME_BROWSER_BANNERS_APP_BANNER_METRICS_H_
+#define CHROME_BROWSER_BANNERS_APP_BANNER_METRICS_H_
+
+namespace banners {
+
+// Keep in sync with the values defined in histograms.xml.
+enum DisplayEvent {
+  DISPLAY_EVENT_MIN = 0,
+  DISPLAY_EVENT_BANNER_REQUESTED = 1,
+  DISPLAY_EVENT_BLOCKED_PREVIOUSLY = 2,
+  DISPLAY_EVENT_PROMOTED_TOO_MANY_OTHERS = 3,
+  DISPLAY_EVENT_CREATED = 4,
+  DISPLAY_EVENT_INSTALLED_PREVIOUSLY = 5,
+  DISPLAY_EVENT_IGNORED_PREVIOUSLY = 6,
+  DISPLAY_EVENT_LACKS_SERVICE_WORKER = 7,
+  DISPLAY_EVENT_NOT_VISITED_ENOUGH = 8,
+  DISPLAY_EVENT_MAX = 9,
+};
+
+enum InstallEvent {
+  INSTALL_EVENT_MIN = 20,
+  INSTALL_EVENT_NATIVE_APP_INSTALL_TRIGGERED = 21,
+  INSTALL_EVENT_NATIVE_APP_INSTALL_STARTED = 22,
+  INSTALL_EVENT_NATIVE_APP_INSTALL_COMPLETED = 23,
+  INSTALL_EVENT_WEB_APP_INSTALLED = 24,
+  INSTALL_EVENT_MAX = 25,
+};
+
+enum DismissEvent {
+  DISMISS_EVENT_MIN = 40,
+  DISMISS_EVENT_ERROR = 41,
+  DISMISS_EVENT_APP_OPEN = 42,
+  DISMISS_EVENT_BANNER_CLICK = 43,
+  DISMISS_EVENT_BANNER_SWIPE = 44,
+  DISMISS_EVENT_CLOSE_BUTTON = 45,
+  DISMISS_EVENT_INSTALL_TIMEOUT = 46,
+  DISMISS_EVENT_DISMISSED = 47,
+  DISMISS_EVENT_MAX = 48,
+};
+
+void TrackDismissEvent(int event);
+void TrackDisplayEvent(int event);
+void TrackInstallEvent(int event);
+
+};  // namespace banners
+
+#endif  // CHROME_BROWSER_BANNERS_APP_BANNER_METRICS_H_
+
diff --git a/chrome/browser/banners/app_banner_settings_helper.cc b/chrome/browser/banners/app_banner_settings_helper.cc
index 56040ba..33c42ef 100644
--- a/chrome/browser/banners/app_banner_settings_helper.cc
+++ b/chrome/browser/banners/app_banner_settings_helper.cc
@@ -8,6 +8,7 @@
 #include <string>
 
 #include "base/command_line.h"
+#include "chrome/browser/banners/app_banner_metrics.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/common/chrome_switches.h"
 #include "components/content_settings/core/browser/host_content_settings_map.h"
@@ -188,8 +189,10 @@
   base::Time added_time =
       GetSingleBannerEvent(web_contents, origin_url, package_name_or_start_url,
                            APP_BANNER_EVENT_DID_ADD_TO_HOMESCREEN);
-  if (!added_time.is_null())
+  if (!added_time.is_null()) {
+    banners::TrackDisplayEvent(banners::DISPLAY_EVENT_INSTALLED_PREVIOUSLY);
     return false;
+  }
 
   base::Time blocked_time =
       GetSingleBannerEvent(web_contents, origin_url, package_name_or_start_url,
@@ -199,6 +202,7 @@
   // null events will always be greater than the limits.
   if (time - blocked_time <
       base::TimeDelta::FromDays(kMinimumBannerBlockedToBannerShown)) {
+    banners::TrackDisplayEvent(banners::DISPLAY_EVENT_BLOCKED_PREVIOUSLY);
     return false;
   }
 
@@ -207,12 +211,18 @@
                            APP_BANNER_EVENT_DID_SHOW);
   if (time - shown_time <
       base::TimeDelta::FromDays(kMinimumDaysBetweenBannerShows)) {
+    banners::TrackDisplayEvent(banners::DISPLAY_EVENT_IGNORED_PREVIOUSLY);
     return false;
   }
 
   std::vector<base::Time> could_show_events = GetCouldShowBannerEvents(
       web_contents, origin_url, package_name_or_start_url);
-  return could_show_events.size() >= kCouldShowEventsToTrigger;
+  if (could_show_events.size() < kCouldShowEventsToTrigger) {
+    banners::TrackDisplayEvent(banners::DISPLAY_EVENT_NOT_VISITED_ENOUGH);
+    return false;
+  }
+
+  return true;
 }
 
 std::vector<base::Time> AppBannerSettingsHelper::GetCouldShowBannerEvents(
diff --git a/chrome/browser/banners/app_banner_settings_helper_unittest.cc b/chrome/browser/banners/app_banner_settings_helper_unittest.cc
index 2cf2b62..e744c0e 100644
--- a/chrome/browser/banners/app_banner_settings_helper_unittest.cc
+++ b/chrome/browser/banners/app_banner_settings_helper_unittest.cc
@@ -9,7 +9,7 @@
 
 namespace {
 
-const char kTestURL[] = "http://www.google.com";
+const char kTestURL[] = "https://www.google.com";
 const char kTestPackageName[] = "test.package";
 
 base::Time GetReferenceTime() {
diff --git a/chrome/browser/bookmarks/chrome_bookmark_client.cc b/chrome/browser/bookmarks/chrome_bookmark_client.cc
index 6c4e541..15d835df 100644
--- a/chrome/browser/bookmarks/chrome_bookmark_client.cc
+++ b/chrome/browser/bookmarks/chrome_bookmark_client.cc
@@ -276,7 +276,7 @@
 
 std::string ChromeBookmarkClient::GetManagedBookmarksDomain() {
   policy::ProfilePolicyConnector* connector =
-      policy::ProfilePolicyConnectorFactory::GetForProfile(profile_);
+      policy::ProfilePolicyConnectorFactory::GetForBrowserContext(profile_);
   if (connector->IsPolicyFromCloudPolicy(policy::key::kManagedBookmarks))
     return connector->GetManagementDomain();
   return std::string();
diff --git a/chrome/browser/browser_about_handler.cc b/chrome/browser/browser_about_handler.cc
index 7d19585..42a453a 100644
--- a/chrome/browser/browser_about_handler.cc
+++ b/chrome/browser/browser_about_handler.cc
@@ -16,11 +16,21 @@
 #include "chrome/common/url_constants.h"
 #include "components/url_fixer/url_fixer.h"
 
+bool FixupBrowserAboutURL(GURL* url,
+                          content::BrowserContext* browser_context) {
+  // Ensure that any cleanup done by FixupURL happens before the rewriting
+  // phase that determines the virtual URL, by including it in an initial
+  // URLHandler.  This prevents minor changes from producing a virtual URL,
+  // which could lead to a URL spoof.
+  *url = url_fixer::FixupURL(url->possibly_invalid_spec(), std::string());
+  return true;
+}
+
 bool WillHandleBrowserAboutURL(GURL* url,
                                content::BrowserContext* browser_context) {
   // TODO(msw): Eliminate "about:*" constants and literals from code and tests,
   //            then hopefully we can remove this forced fixup.
-  *url = url_fixer::FixupURL(url->possibly_invalid_spec(), std::string());
+  FixupBrowserAboutURL(url, browser_context);
 
   // Check that about: URLs are fixed up to chrome: by url_fixer::FixupURL.
   DCHECK((*url == GURL(url::kAboutBlankURL)) ||
diff --git a/chrome/browser/browser_about_handler.h b/chrome/browser/browser_about_handler.h
index 93b1a20..7cc3785 100644
--- a/chrome/browser/browser_about_handler.h
+++ b/chrome/browser/browser_about_handler.h
@@ -11,6 +11,10 @@
 class BrowserContext;
 }
 
+// A preliminary URLHandler that performs cleanup on the URL before it is
+// rewritten.  Changes that happen here will not lead to a virtual URL.
+bool FixupBrowserAboutURL(GURL* url, content::BrowserContext* browser_context);
+
 // Returns true if the given URL will be handled by the browser about handler.
 // Nowadays, these go through the webui, so the return is always false.
 // Either way, |url| will be processed by url_fixer::FixupURL, which
diff --git a/chrome/browser/browser_about_handler_unittest.cc b/chrome/browser/browser_about_handler_unittest.cc
index 2754d4948..2448653 100644
--- a/chrome/browser/browser_about_handler_unittest.cc
+++ b/chrome/browser/browser_about_handler_unittest.cc
@@ -8,11 +8,17 @@
 #include "chrome/browser/browser_about_handler.h"
 #include "chrome/common/url_constants.h"
 #include "chrome/test/base/testing_profile.h"
+#include "content/public/browser/navigation_controller.h"
+#include "content/public/browser/navigation_entry.h"
+#include "content/public/common/referrer.h"
 #include "content/public/test/test_browser_thread.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
 
 using content::BrowserThread;
+using content::NavigationController;
+using content::NavigationEntry;
+using content::Referrer;
 
 typedef testing::Test BrowserAboutHandlerTest;
 
@@ -74,3 +80,24 @@
     EXPECT_EQ(test_data[i].result_url, url);
   }
 }
+
+// Ensure that minor BrowserAboutHandler fixup to a URL does not cause us to
+// keep a separate virtual URL, which would not be updated on redirects.
+// See https://crbug.com/449829.
+TEST_F(BrowserAboutHandlerTest, NoVirtualURLForFixup) {
+  GURL url("view-source:http://.foo");
+
+  // Fixup will remove the dot and add a slash.
+  GURL fixed_url("view-source:http://foo/");
+
+  // Rewriters will remove the view-source prefix and expect it to stay in the
+  // virtual URL.
+  GURL rewritten_url("http://foo/");
+
+  TestingProfile profile;
+  scoped_ptr<NavigationEntry> entry(NavigationController::CreateNavigationEntry(
+      url, Referrer(), ui::PAGE_TRANSITION_RELOAD, false, std::string(),
+      &profile));
+  EXPECT_EQ(fixed_url, entry->GetVirtualURL());
+  EXPECT_EQ(rewritten_url, entry->GetURL());
+}
diff --git a/chrome/browser/browser_resources.grd b/chrome/browser/browser_resources.grd
index 37f25b8f..c17a533 100644
--- a/chrome/browser/browser_resources.grd
+++ b/chrome/browser/browser_resources.grd
@@ -134,12 +134,6 @@
       </if>
       <if expr="enable_extensions">
         <include name="IDR_EXTENSION_COMMAND_LIST_JS" file="resources\extensions\extension_command_list.js" flattenhtml="true" type="BINDATA" />
-        <if expr="is_macosx">
-          <include name="IDR_EXTENSIONS_INFOBAR_CSS" file="resources\extensions_infobar_mac.css" flattenhtml="true" type="BINDATA" />
-        </if>
-        <if expr="not is_macosx">
-          <include name="IDR_EXTENSIONS_INFOBAR_CSS" file="resources\extensions_infobar.css" flattenhtml="true" type="BINDATA" />
-        </if>
         <include name="IDR_EXTENSION_LIST_JS" file="resources\extensions\extension_list.js" flattenhtml="true" type="BINDATA" />
         <include name="IDR_EXTENSIONS_JS" file="resources\extensions\extensions.js" flattenhtml="true" type="BINDATA" />
       </if>
@@ -380,10 +374,10 @@
         <include name="IDR_IDENTITY_INTERNALS_CSS" file="resources\identity_internals.css" type="BINDATA" />
         <include name="IDR_IDENTITY_INTERNALS_JS" file="resources\identity_internals.js" type="BINDATA" />
       </if>
+      <include name="IDR_DEVICE_LOG_UI_HTML" file="resources\device_log_ui\device_log_ui.html" type="BINDATA" />
+      <include name="IDR_DEVICE_LOG_UI_JS" file="resources\device_log_ui\device_log_ui.js" type="BINDATA" />
+      <include name="IDR_DEVICE_LOG_UI_CSS" file="resources\device_log_ui\device_log_ui.css" type="BINDATA" />
       <if expr="chromeos">
-        <include name="IDR_DEVICE_LOG_UI_HTML" file="resources\chromeos\device_log_ui\device_log_ui.html" type="BINDATA" />
-        <include name="IDR_DEVICE_LOG_UI_JS" file="resources\chromeos\device_log_ui\device_log_ui.js" type="BINDATA" />
-        <include name="IDR_DEVICE_LOG_UI_CSS" file="resources\chromeos\device_log_ui\device_log_ui.css" type="BINDATA" />
         <include name="IDR_NETWORK_UI_HTML" file="resources\chromeos\network_ui\network_ui.html" type="BINDATA" />
         <include name="IDR_NETWORK_UI_JS" file="resources\chromeos\network_ui\network_ui.js" type="BINDATA" />
         <include name="IDR_NETWORK_UI_CSS" file="resources\chromeos\network_ui\network_ui.css" type="BINDATA" />
diff --git a/chrome/browser/browsing_data/browsing_data_remover.cc b/chrome/browser/browsing_data/browsing_data_remover.cc
index 3315912..f58af07 100644
--- a/chrome/browser/browsing_data/browsing_data_remover.cc
+++ b/chrome/browser/browsing_data/browsing_data_remover.cc
@@ -702,7 +702,7 @@
         pepper_flash_settings_manager_->DeauthorizeContentLicenses(prefs);
 #if defined(OS_CHROMEOS)
     // On Chrome OS, also delete any content protection platform keys.
-    user_manager::User* user =
+    const user_manager::User* user =
         chromeos::ProfileHelper::Get()->GetUserByProfile(profile_);
     if (!user) {
       LOG(WARNING) << "Failed to find user for current profile.";
diff --git a/chrome/browser/chrome_browser_field_trials.cc b/chrome/browser/chrome_browser_field_trials.cc
index 8e752cb..acaf1e8 100644
--- a/chrome/browser/chrome_browser_field_trials.cc
+++ b/chrome/browser/chrome_browser_field_trials.cc
@@ -13,7 +13,6 @@
 #include "base/time/time.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/chrome_version_info.h"
-#include "chrome/common/variations/uniformity_field_trials.h"
 #include "components/metrics/metrics_pref_names.h"
 #include "components/omnibox/omnibox_field_trial.h"
 
@@ -36,7 +35,6 @@
   DCHECK(!install_time.is_null());
 
   // Field trials that are shared by all platforms.
-  chrome_variations::SetupUniformityFieldTrials(install_time);
   InstantiateDynamicTrials();
 
 #if defined(OS_ANDROID) || defined(OS_IOS)
diff --git a/chrome/browser/chrome_browser_main.cc b/chrome/browser/chrome_browser_main.cc
index 5dced63c..cfd3b10 100644
--- a/chrome/browser/chrome_browser_main.cc
+++ b/chrome/browser/chrome_browser_main.cc
@@ -105,6 +105,7 @@
 #include "chrome/grit/generated_resources.h"
 #include "chrome/installer/util/google_update_settings.h"
 #include "components/component_updater/component_updater_service.h"
+#include "components/device_event_log/device_event_log.h"
 #include "components/google/core/browser/google_util.h"
 #include "components/language_usage_metrics/language_usage_metrics.h"
 #include "components/metrics/metrics_service.h"
@@ -676,11 +677,7 @@
   g_browser_process->metrics_service()->CheckForClonedInstall(
       BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE));
 
-  bool may_record = g_browser_process->GetMetricsServicesManager()->
-      IsMetricsReportingEnabled();
-
-  g_browser_process->GetMetricsServicesManager()->UpdatePermissions(
-      may_record, true);
+  g_browser_process->GetMetricsServicesManager()->UpdateUploadPermissions(true);
 }
 
 void ChromeBrowserMainParts::RecordBrowserStartupTime() {
@@ -760,6 +757,9 @@
 
 void ChromeBrowserMainParts::PreMainMessageLoopStart() {
   TRACE_EVENT0("startup", "ChromeBrowserMainParts::PreMainMessageLoopStart");
+
+  device_event_log::Initialize(0 /* default max entries */);
+
   for (size_t i = 0; i < chrome_extra_parts_.size(); ++i)
     chrome_extra_parts_[i]->PreMainMessageLoopStart();
 }
@@ -1709,6 +1709,7 @@
   browser_shutdown::ShutdownPostThreadsStop(restart_last_session_);
   master_prefs_.reset();
   process_singleton_.reset();
+  device_event_log::Shutdown();
 
   // We need to do this check as late as possible, but due to modularity, this
   // may be the last point in Chrome.  This would be more effective if done at
diff --git a/chrome/browser/chrome_browser_main_mac.mm b/chrome/browser/chrome_browser_main_mac.mm
index 4fcf17f..7c15afc 100644
--- a/chrome/browser/chrome_browser_main_mac.mm
+++ b/chrome/browser/chrome_browser_main_mac.mm
@@ -259,7 +259,14 @@
       // in the window server, which is the default when not not using the
       // 10.9 SDK.
       // TODO: Remove this when we build with the 10.9 SDK.
-      @"NSWindowHostsLayersInWindowServer": @(base::mac::IsOSMavericksOrLater())
+      @"NSWindowHostsLayersInWindowServer":
+          @(base::mac::IsOSMavericksOrLater()),
+      // This setting prevents views from ditching their layers when the view
+      // gets removed from the view hierarchy. It defaults to YES for
+      // applications linked against an OSX 10.8+ SDK. In Yosemite, failing to
+      // set this to YES causes an AppKit crash. http://crbug.com/428977
+      // TODO(erikchen): Remove this when we build with an OSX 10.8+ SDK.
+      @"NSViewKeepLayersAround": @(YES)
   }];
 }
 
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index 51f5ee2..4f7e591 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -105,6 +105,7 @@
 #include "components/pref_registry/pref_registry_syncable.h"
 #include "components/signin/core/common/profile_management_switches.h"
 #include "components/translate/core/common/translate_switches.h"
+#include "components/url_fixer/url_fixer.h"
 #include "content/public/browser/browser_child_process_host.h"
 #include "content/public/browser/browser_main_parts.h"
 #include "content/public/browser/browser_ppapi_host.h"
@@ -1465,6 +1466,7 @@
       switches::kEnableNaClNonSfiMode,
 #endif
       switches::kEnableNetBenchmarking,
+      switches::kEnableNewBookmarkApps,
       switches::kEnableOutOfProcessPdf,
       switches::kEnablePluginPlaceholderShadowDom,
       switches::kEnableShowModalDialog,
@@ -2262,7 +2264,8 @@
 
   // about: handler. Must come before chrome: handler, since it will
   // rewrite about: urls to chrome: URLs and then expect chrome: to
-  // actually handle them.
+  // actually handle them.  Also relies on a preliminary fixup phase.
+  handler->SetFixupHandler(&FixupBrowserAboutURL);
   handler->AddHandlerPair(&WillHandleBrowserAboutURL,
                           BrowserURLHandler::null_handler());
 
diff --git a/chrome/browser/chromeos/accessibility/spoken_feedback_browsertest.cc b/chrome/browser/chromeos/accessibility/spoken_feedback_browsertest.cc
index 8407bda..f87a773 100644
--- a/chrome/browser/chromeos/accessibility/spoken_feedback_browsertest.cc
+++ b/chrome/browser/chromeos/accessibility/spoken_feedback_browsertest.cc
@@ -312,17 +312,11 @@
 
   SendKeyPress(ui::VKEY_RETURN);
 
-  // TODO(mgiuca): This is incorrect behaviour; it should read out "Search, text
-  // box" or similar (see http://crbug.com/386826).
-  EXPECT_TRUE(MatchPattern(speech_monitor_.GetNextUtterance(), "Edit text"));
+  EXPECT_TRUE(MatchPattern(speech_monitor_.GetNextUtterance(),
+                           "Search or type U R L Edit text"));
 
-  // TODO(mgiuca): The next part of the test fails in the experimental app list,
-  // because there is no keyboard navigation (see http://crbug.com/438568). Only
-  // check this in the classic app launcher.
-  if (!app_list::switches::IsExperimentalAppListEnabled()) {
-    SendKeyPress(ui::VKEY_DOWN);
-    EXPECT_TRUE(MatchPattern(speech_monitor_.GetNextUtterance(), "* Button"));
-  }
+  SendKeyPress(ui::VKEY_DOWN);
+  EXPECT_TRUE(MatchPattern(speech_monitor_.GetNextUtterance(), "* Button"));
 }
 
 IN_PROC_BROWSER_TEST_P(SpokenFeedbackTest, OpenStatusTray) {
diff --git a/chrome/browser/chromeos/attestation/platform_verification_flow.cc b/chrome/browser/chromeos/attestation/platform_verification_flow.cc
index 1b95c98..6330430 100644
--- a/chrome/browser/chromeos/attestation/platform_verification_flow.cc
+++ b/chrome/browser/chromeos/attestation/platform_verification_flow.cc
@@ -97,7 +97,8 @@
     return url;
   }
 
-  user_manager::User* GetUser(content::WebContents* web_contents) override {
+  const user_manager::User* GetUser(
+      content::WebContents* web_contents) override {
     return ProfileHelper::Get()->GetUserByProfile(
         Profile::FromBrowserContext(web_contents->GetBrowserContext()));
   }
@@ -286,7 +287,7 @@
 
   // At this point all user interaction is complete and we can proceed with the
   // certificate request.
-  user_manager::User* user = delegate_->GetUser(context.web_contents);
+  const user_manager::User* user = delegate_->GetUser(context.web_contents);
   if (!user) {
     ReportError(context.callback, INTERNAL_ERROR);
     LOG(ERROR) << "Profile does not map to a valid user.";
diff --git a/chrome/browser/chromeos/attestation/platform_verification_flow.h b/chrome/browser/chromeos/attestation/platform_verification_flow.h
index 48b5de5..09d9887d 100644
--- a/chrome/browser/chromeos/attestation/platform_verification_flow.h
+++ b/chrome/browser/chromeos/attestation/platform_verification_flow.h
@@ -107,7 +107,8 @@
 
     // Gets the user associated with the given |web_contents|.  NULL may be
     // returned.
-    virtual user_manager::User* GetUser(content::WebContents* web_contents) = 0;
+    virtual const user_manager::User* GetUser(
+        content::WebContents* web_contents) = 0;
 
     // Gets the content settings map associated with the given |web_contents|.
     virtual HostContentSettingsMap* GetContentSettings(
diff --git a/chrome/browser/chromeos/chrome_browser_main_chromeos.cc b/chrome/browser/chromeos/chrome_browser_main_chromeos.cc
index 08d90f6..1e43b04 100644
--- a/chrome/browser/chromeos/chrome_browser_main_chromeos.cc
+++ b/chrome/browser/chromeos/chrome_browser_main_chromeos.cc
@@ -522,7 +522,7 @@
       new GuestLanguageSetCallbackData(profile));
   locale_util::SwitchLanguageCallback callback(base::Bind(
       &GuestLanguageSetCallbackData::Callback, base::Passed(data.Pass())));
-  user_manager::User* const user =
+  const user_manager::User* const user =
       ProfileHelper::Get()->GetUserByProfile(profile);
   UserSessionManager::GetInstance()->RespectLocalePreference(
       profile, user, callback);
diff --git a/chrome/browser/chromeos/dbus/printer_service_provider.cc b/chrome/browser/chromeos/dbus/printer_service_provider.cc
index f3d89056..c70305eb 100644
--- a/chrome/browser/chromeos/dbus/printer_service_provider.cc
+++ b/chrome/browser/chromeos/dbus/printer_service_provider.cc
@@ -11,6 +11,7 @@
 #include "base/bind_helpers.h"
 #include "base/metrics/histogram.h"
 #include "base/strings/stringprintf.h"
+#include "base/sys_info.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_tabstrip.h"
@@ -18,6 +19,7 @@
 #include "chrome/browser/ui/scoped_tabbed_browser_displayer.h"
 #include "chrome/browser/ui/tab_contents/tab_contents_iterator.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/common/chrome_version_info.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/web_contents.h"
 #include "dbus/bus.h"
@@ -95,8 +97,8 @@
 
 void PrinterServiceProvider::Start(
     scoped_refptr<dbus::ExportedObject> exported_object) {
-
   exported_object_ = exported_object;
+
   DVLOG(1) << "PrinterServiceProvider started";
   exported_object_->ExportMethod(
       kLibCrosServiceInterface,
@@ -130,6 +132,15 @@
     dbus::ExportedObject::ResponseSender response_sender) {
   DVLOG(1) << "PrinterAdded " << method_call->ToString();
 
+  // Disable showing Cloudprint help on canary and dev channel, as these have
+  // support for printerProvider API.
+  // TODO(tbarzic): Remove this and offer the user to search for an extension
+  // that can act as a print driver (using printerProvider API) for USB printers
+  // detected by this service. http://crbug.com/439448
+  if (base::SysInfo::IsRunningOnChromeOS() &&
+      chrome::VersionInfo::GetChannel() <= chrome::VersionInfo::CHANNEL_DEV)
+    return;
+
   dbus::MessageReader reader(method_call);
   std::string vendor;
   std::string product;
diff --git a/chrome/browser/chromeos/drive/file_system_util.cc b/chrome/browser/chromeos/drive/file_system_util.cc
index b2069225c..88ed4c2 100644
--- a/chrome/browser/chromeos/drive/file_system_util.cc
+++ b/chrome/browser/chromeos/drive/file_system_util.cc
@@ -121,7 +121,7 @@
     // enabled. In that case, we fall back to use UserManager (it basically just
     // returns currently active users's hash in such a case.) I still try
     // ProfileHelper first because it works better in tests.
-    user_manager::User* const user =
+    const user_manager::User* const user =
         user_manager::UserManager::IsInitialized()
             ? chromeos::ProfileHelper::Get()->GetUserByProfile(
                   profile->GetOriginalProfile())
diff --git a/chrome/browser/chromeos/drive/fileapi/webkit_file_stream_reader_impl_unittest.cc b/chrome/browser/chromeos/drive/fileapi/webkit_file_stream_reader_impl_unittest.cc
index 86ef586..bb1d5c0 100644
--- a/chrome/browser/chromeos/drive/fileapi/webkit_file_stream_reader_impl_unittest.cc
+++ b/chrome/browser/chromeos/drive/fileapi/webkit_file_stream_reader_impl_unittest.cc
@@ -187,10 +187,9 @@
       entry->resource_id(),
       std::string(),  // parent_resource_id
       std::string(),  // title
-      expected_modification_time,
-      base::Time(),
-      google_apis::test_util::CreateCopyResultCallback(&status,
-                                                       &server_entry));
+      expected_modification_time, base::Time(),
+      google_apis::drive::Properties(),
+      google_apis::test_util::CreateCopyResultCallback(&status, &server_entry));
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(google_apis::HTTP_SUCCESS, status);
 
diff --git a/chrome/browser/chromeos/drive/job_scheduler.cc b/chrome/browser/chromeos/drive/job_scheduler.cc
index bef62954..d5735e0a 100644
--- a/chrome/browser/chromeos/drive/job_scheduler.cc
+++ b/chrome/browser/chromeos/drive/job_scheduler.cc
@@ -521,16 +521,11 @@
   JobEntry* new_job = CreateNewJob(TYPE_UPDATE_RESOURCE);
   new_job->context = context;
   new_job->task = base::Bind(
-      &DriveServiceInterface::UpdateResource,
-      base::Unretained(drive_service_),
-      resource_id,
-      parent_resource_id,
-      new_title,
-      last_modified,
-      last_viewed_by_me,
+      &DriveServiceInterface::UpdateResource, base::Unretained(drive_service_),
+      resource_id, parent_resource_id, new_title, last_modified,
+      last_viewed_by_me, google_apis::drive::Properties(),
       base::Bind(&JobScheduler::OnGetFileResourceJobDone,
-                 weak_ptr_factory_.GetWeakPtr(),
-                 new_job->job_info.job_id,
+                 weak_ptr_factory_.GetWeakPtr(), new_job->job_info.job_id,
                  callback));
   new_job->abort_callback = CreateErrorRunCallback(callback);
   StartJob(new_job);
diff --git a/chrome/browser/chromeos/events/event_rewriter_unittest.cc b/chrome/browser/chromeos/events/event_rewriter_unittest.cc
index aed81d1..2467cc4 100644
--- a/chrome/browser/chromeos/events/event_rewriter_unittest.cc
+++ b/chrome/browser/chromeos/events/event_rewriter_unittest.cc
@@ -29,6 +29,7 @@
 #include "ui/base/ime/chromeos/fake_ime_keyboard.h"
 #include "ui/events/event.h"
 #include "ui/events/event_rewriter.h"
+#include "ui/events/event_utils.h"
 #include "ui/events/test/events_test_utils.h"
 #include "ui/events/test/test_event_processor.h"
 
@@ -1984,10 +1985,8 @@
 
   // Test Alt + Left click.
   {
-    ui::MouseEvent press(ui::ET_MOUSE_PRESSED,
-                         gfx::Point(),
-                         gfx::Point(),
-                         kLeftAndAltFlag,
+    ui::MouseEvent press(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(),
+                         ui::EventTimeForNow(), kLeftAndAltFlag,
                          ui::EF_LEFT_MOUSE_BUTTON);
     ui::EventTestApi test_press(&press);
     test_press.set_source_device_id(10);
@@ -2002,10 +2001,8 @@
     EXPECT_EQ(ui::EF_RIGHT_MOUSE_BUTTON, result->changed_button_flags());
   }
   {
-    ui::MouseEvent release(ui::ET_MOUSE_RELEASED,
-                           gfx::Point(),
-                           gfx::Point(),
-                           kLeftAndAltFlag,
+    ui::MouseEvent release(ui::ET_MOUSE_RELEASED, gfx::Point(), gfx::Point(),
+                           ui::EventTimeForNow(), kLeftAndAltFlag,
                            ui::EF_LEFT_MOUSE_BUTTON);
     ui::EventTestApi test_release(&release);
     test_release.set_source_device_id(10);
@@ -2049,10 +2046,8 @@
 
   // No ALT in frst click.
   {
-    ui::MouseEvent press(ui::ET_MOUSE_PRESSED,
-                         gfx::Point(),
-                         gfx::Point(),
-                         ui::EF_LEFT_MOUSE_BUTTON,
+    ui::MouseEvent press(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(),
+                         ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
                          ui::EF_LEFT_MOUSE_BUTTON);
     ui::EventTestApi test_press(&press);
     test_press.set_source_device_id(10);
@@ -2063,10 +2058,8 @@
     EXPECT_EQ(ui::EF_LEFT_MOUSE_BUTTON, result->changed_button_flags());
   }
   {
-    ui::MouseEvent release(ui::ET_MOUSE_RELEASED,
-                           gfx::Point(),
-                           gfx::Point(),
-                           kLeftAndAltFlag,
+    ui::MouseEvent release(ui::ET_MOUSE_RELEASED, gfx::Point(), gfx::Point(),
+                           ui::EventTimeForNow(), kLeftAndAltFlag,
                            ui::EF_LEFT_MOUSE_BUTTON);
     ui::EventTestApi test_release(&release);
     test_release.set_source_device_id(10);
@@ -2104,10 +2097,8 @@
 
   // ALT on different device.
   {
-    ui::MouseEvent press(ui::ET_MOUSE_PRESSED,
-                         gfx::Point(),
-                         gfx::Point(),
-                         kLeftAndAltFlag,
+    ui::MouseEvent press(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(),
+                         ui::EventTimeForNow(), kLeftAndAltFlag,
                          ui::EF_LEFT_MOUSE_BUTTON);
     ui::EventTestApi test_press(&press);
     test_press.set_source_device_id(11);
@@ -2119,10 +2110,8 @@
     EXPECT_EQ(ui::EF_RIGHT_MOUSE_BUTTON, result->changed_button_flags());
   }
   {
-    ui::MouseEvent release(ui::ET_MOUSE_RELEASED,
-                           gfx::Point(),
-                           gfx::Point(),
-                           kLeftAndAltFlag,
+    ui::MouseEvent release(ui::ET_MOUSE_RELEASED, gfx::Point(), gfx::Point(),
+                           ui::EventTimeForNow(), kLeftAndAltFlag,
                            ui::EF_LEFT_MOUSE_BUTTON);
     ui::EventTestApi test_release(&release);
     test_release.set_source_device_id(10);
@@ -2133,10 +2122,8 @@
     EXPECT_EQ(ui::EF_LEFT_MOUSE_BUTTON, result->changed_button_flags());
   }
   {
-    ui::MouseEvent release(ui::ET_MOUSE_RELEASED,
-                           gfx::Point(),
-                           gfx::Point(),
-                           kLeftAndAltFlag,
+    ui::MouseEvent release(ui::ET_MOUSE_RELEASED, gfx::Point(), gfx::Point(),
+                           ui::EventTimeForNow(), kLeftAndAltFlag,
                            ui::EF_LEFT_MOUSE_BUTTON);
     ui::EventTestApi test_release(&release);
     test_release.set_source_device_id(11);
@@ -2230,10 +2217,8 @@
 
   // Test mouse press event is correctly modified.
   gfx::Point location(0, 0);
-  ui::MouseEvent press(ui::ET_MOUSE_PRESSED,
-                       location,
-                       location,
-                       ui::EF_LEFT_MOUSE_BUTTON,
+  ui::MouseEvent press(ui::ET_MOUSE_PRESSED, location, location,
+                       ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
                        ui::EF_LEFT_MOUSE_BUTTON);
   ui::EventDispatchDetails details = Send(&press);
   ASSERT_FALSE(details.dispatcher_destroyed);
@@ -2244,10 +2229,8 @@
 
   // Test mouse release event is correctly modified and modifier release
   // event is sent. The mouse event should have the correct DIP location.
-  ui::MouseEvent release(ui::ET_MOUSE_RELEASED,
-                         location,
-                         location,
-                         ui::EF_LEFT_MOUSE_BUTTON,
+  ui::MouseEvent release(ui::ET_MOUSE_RELEASED, location, location,
+                         ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
                          ui::EF_LEFT_MOUSE_BUTTON);
   details = Send(&release);
   ASSERT_FALSE(details.dispatcher_destroyed);
@@ -2268,10 +2251,8 @@
   SendActivateStickyKeyPattern(ui::VKEY_CONTROL);
   PopEvents(&events);
   gfx::Point location(0, 0);
-  ui::MouseEvent mev(ui::ET_MOUSEWHEEL,
-                     location,
-                     location,
-                     ui::EF_LEFT_MOUSE_BUTTON,
+  ui::MouseEvent mev(ui::ET_MOUSEWHEEL, location, location,
+                     ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
                      ui::EF_LEFT_MOUSE_BUTTON);
   ui::MouseWheelEvent positive(mev, 0, ui::MouseWheelEvent::kWheelDelta);
   ui::EventDispatchDetails details = Send(&positive);
diff --git a/chrome/browser/chromeos/extensions/device_local_account_management_policy_provider.cc b/chrome/browser/chromeos/extensions/device_local_account_management_policy_provider.cc
index ea00c3b..d385b0ff 100644
--- a/chrome/browser/chromeos/extensions/device_local_account_management_policy_provider.cc
+++ b/chrome/browser/chromeos/extensions/device_local_account_management_policy_provider.cc
@@ -24,6 +24,7 @@
     "djflhoibgkdhkhhcedjiklpkjnoahfmg",  // User Agent Switcher
     "iabmpiboiopbgfabjmgeedhcmjenhbla",  // VNC Viewer
     "haiffjcadagjlijoggckpgfnoeiflnem",  // Citrix Receiver
+    "mfaihdlpglflfgpfjcifdjdjcckigekc",  // ARC Runtime
 
     // Libraries:
     "aclofikceldphonlfmghmimkodjdmhck",  // Ancoris login component
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_drive.cc b/chrome/browser/chromeos/extensions/file_manager/private_api_drive.cc
index 26b2f86..b676241a 100644
--- a/chrome/browser/chromeos/extensions/file_manager/private_api_drive.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/private_api_drive.cc
@@ -4,6 +4,9 @@
 
 #include "chrome/browser/chromeos/extensions/file_manager/private_api_drive.h"
 
+#include <map>
+#include <set>
+
 #include "base/command_line.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/chromeos/drive/drive_integration_service.h"
@@ -38,6 +41,7 @@
 using chromeos::file_system_provider::ProvidedFileSystemInterface;
 using chromeos::file_system_provider::util::FileSystemURLParser;
 using extensions::api::file_manager_private::EntryProperties;
+using extensions::api::file_manager_private::EntryPropertyName;
 using file_manager::util::EntryDefinition;
 using file_manager::util::EntryDefinitionCallback;
 using file_manager::util::EntryDefinitionList;
@@ -69,8 +73,8 @@
   properties->shared.reset(new bool(entry_proto.shared()));
 
   const drive::PlatformFileInfoProto& file_info = entry_proto.file_info();
-  properties->file_size.reset(new double(file_info.size()));
-  properties->last_modified_time.reset(new double(
+  properties->size.reset(new double(file_info.size()));
+  properties->modification_time.reset(new double(
       base::Time::FromInternalValue(file_info.last_modified()).ToJsTime()));
 
   if (!entry_proto.has_file_specific_info())
@@ -96,32 +100,31 @@
     properties->image_rotation.reset(
         new int(file_specific_info.image_rotation()));
   }
-  properties->is_hosted.reset(
-      new bool(file_specific_info.is_hosted_document()));
+  properties->hosted.reset(new bool(file_specific_info.is_hosted_document()));
   properties->content_mime_type.reset(
       new std::string(file_specific_info.content_mime_type()));
-  properties->is_pinned.reset(
+  properties->pinned.reset(
       new bool(file_specific_info.cache_state().is_pinned()));
-  properties->is_dirty.reset(
+  properties->dirty.reset(
       new bool(file_specific_info.cache_state().is_dirty()));
-  properties->is_present.reset(
+  properties->present.reset(
       new bool(file_specific_info.cache_state().is_present()));
 
   if (file_specific_info.cache_state().is_present()) {
-    properties->is_available_offline.reset(new bool(true));
+    properties->available_offline.reset(new bool(true));
   } else if (file_specific_info.is_hosted_document() &&
              file_specific_info.has_document_extension()) {
     const std::string file_extension = file_specific_info.document_extension();
     // What's available offline? See the 'Web' column at:
     // http://support.google.com/drive/answer/1628467
-    properties->is_available_offline.reset(
+    properties->available_offline.reset(
         new bool(file_extension == ".gdoc" || file_extension == ".gdraw" ||
                  file_extension == ".gsheet" || file_extension == ".gslides"));
   } else {
-    properties->is_available_offline.reset(new bool(false));
+    properties->available_offline.reset(new bool(false));
   }
 
-  properties->is_available_when_metered.reset(
+  properties->available_when_metered.reset(
       new bool(file_specific_info.cache_state().is_present() ||
                file_specific_info.is_hosted_document()));
 }
@@ -158,12 +161,14 @@
 
   // Creates an instance and starts the process.
   static void Start(const base::FilePath local_path,
+                    const std::set<EntryPropertyName>& names,
                     Profile* const profile,
                     const ResultCallback& callback) {
     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
 
     SingleEntryPropertiesGetterForDrive* instance =
-        new SingleEntryPropertiesGetterForDrive(local_path, profile, callback);
+        new SingleEntryPropertiesGetterForDrive(local_path, names, profile,
+                                                callback);
     instance->StartProcess();
 
     // The instance will be destroyed by itself.
@@ -172,9 +177,11 @@
   virtual ~SingleEntryPropertiesGetterForDrive() {}
 
  private:
-  SingleEntryPropertiesGetterForDrive(const base::FilePath local_path,
-                                      Profile* const profile,
-                                      const ResultCallback& callback)
+  SingleEntryPropertiesGetterForDrive(
+      const base::FilePath local_path,
+      const std::set<EntryPropertyName>& /* names */,
+      Profile* const profile,
+      const ResultCallback& callback)
       : callback_(callback),
         local_path_(local_path),
         running_profile_(profile),
@@ -365,12 +372,13 @@
 
   // Creates an instance and starts the process.
   static void Start(const storage::FileSystemURL file_system_url,
+                    const std::set<EntryPropertyName>& names,
                     const ResultCallback& callback) {
     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
 
     SingleEntryPropertiesGetterForFileSystemProvider* instance =
         new SingleEntryPropertiesGetterForFileSystemProvider(file_system_url,
-                                                             callback);
+                                                             names, callback);
     instance->StartProcess();
 
     // The instance will be destroyed by itself.
@@ -381,9 +389,11 @@
  private:
   SingleEntryPropertiesGetterForFileSystemProvider(
       const storage::FileSystemURL& file_system_url,
+      const std::set<EntryPropertyName>& names,
       const ResultCallback& callback)
       : callback_(callback),
         file_system_url_(file_system_url),
+        names_(names),
         properties_(new EntryProperties),
         weak_ptr_factory_(this) {
     DCHECK(!callback_.is_null());
@@ -398,9 +408,17 @@
       return;
     }
 
+    ProvidedFileSystemInterface::MetadataFieldMask field_mask =
+        ProvidedFileSystemInterface::METADATA_FIELD_DEFAULT;
+    // TODO(mtomasz): Add other fields. All of them should be requested on
+    // demand. crbug.com/413161.
+    if (names_.find(
+            api::file_manager_private::ENTRY_PROPERTY_NAME_THUMBNAILURL) !=
+        names_.end())
+      field_mask |= ProvidedFileSystemInterface::METADATA_FIELD_THUMBNAIL;
+
     parser.file_system()->GetMetadata(
-        parser.file_path(),
-        ProvidedFileSystemInterface::METADATA_FIELD_THUMBNAIL,
+        parser.file_path(), field_mask,
         base::Bind(&SingleEntryPropertiesGetterForFileSystemProvider::
                        OnGetMetadataCompleted,
                    weak_ptr_factory_.GetWeakPtr()));
@@ -415,8 +433,8 @@
       return;
     }
 
-    properties_->file_size.reset(new double(metadata->size));
-    properties_->last_modified_time.reset(
+    properties_->size.reset(new double(metadata->size));
+    properties_->modification_time.reset(
         new double(metadata->modification_time.ToJsTime()));
 
     if (!metadata->thumbnail.empty())
@@ -436,6 +454,7 @@
   // Given parameters.
   const ResultCallback callback_;
   const storage::FileSystemURL file_system_url_;
+  const std::set<EntryPropertyName> names_;
 
   // Values used in the process.
   scoped_ptr<EntryProperties> properties_;
@@ -467,6 +486,8 @@
           GetProfile(), render_view_host());
 
   properties_list_.resize(params->file_urls.size());
+  const std::set<EntryPropertyName> names_as_set(params->names.begin(),
+                                                 params->names.end());
   for (size_t i = 0; i < params->file_urls.size(); i++) {
     const GURL url = GURL(params->file_urls[i]);
     const storage::FileSystemURL file_system_url =
@@ -474,19 +495,21 @@
     switch (file_system_url.type()) {
       case storage::kFileSystemTypeDrive:
         SingleEntryPropertiesGetterForDrive::Start(
-            file_system_url.path(), GetProfile(),
+            file_system_url.path(), names_as_set, GetProfile(),
             base::Bind(&FileManagerPrivateGetEntryPropertiesFunction::
                            CompleteGetEntryProperties,
                        this, i, file_system_url));
         break;
       case storage::kFileSystemTypeProvided:
         SingleEntryPropertiesGetterForFileSystemProvider::Start(
-            file_system_url,
+            file_system_url, names_as_set,
             base::Bind(&FileManagerPrivateGetEntryPropertiesFunction::
                            CompleteGetEntryProperties,
                        this, i, file_system_url));
         break;
       default:
+        // TODO(yawano) Change this to support other voluems (e.g. local) ,and
+        // integrate fileManagerPrivate.getMimeType to this method.
         LOG(ERROR) << "Not supported file system type.";
         CompleteGetEntryProperties(i, file_system_url,
                                    make_scoped_ptr(new EntryProperties),
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_drive.h b/chrome/browser/chromeos/extensions/file_manager/private_api_drive.h
index b3dbc94..af9b20d 100644
--- a/chrome/browser/chromeos/extensions/file_manager/private_api_drive.h
+++ b/chrome/browser/chromeos/extensions/file_manager/private_api_drive.h
@@ -7,6 +7,9 @@
 #ifndef CHROME_BROWSER_CHROMEOS_EXTENSIONS_FILE_MANAGER_PRIVATE_API_DRIVE_H_
 #define CHROME_BROWSER_CHROMEOS_EXTENSIONS_FILE_MANAGER_PRIVATE_API_DRIVE_H_
 
+#include <string>
+#include <vector>
+
 #include "base/files/file.h"
 #include "base/memory/scoped_ptr.h"
 #include "chrome/browser/chromeos/drive/file_errors.h"
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_misc.cc b/chrome/browser/chromeos/extensions/file_manager/private_api_misc.cc
index ac8f319..6515aef 100644
--- a/chrome/browser/chromeos/extensions/file_manager/private_api_misc.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/private_api_misc.cc
@@ -97,7 +97,7 @@
 } // namespace
 
 bool FileManagerPrivateLogoutUserForReauthenticationFunction::RunSync() {
-  user_manager::User* user =
+  const user_manager::User* user =
       chromeos::ProfileHelper::Get()->GetUserByProfile(GetProfile());
   if (user) {
     user_manager::UserManager::Get()->SaveUserOAuthStatus(
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_strings.cc b/chrome/browser/chromeos/extensions/file_manager/private_api_strings.cc
index 1d397d2..16e7888 100644
--- a/chrome/browser/chromeos/extensions/file_manager/private_api_strings.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/private_api_strings.cc
@@ -288,6 +288,8 @@
              IDS_FILE_BROWSER_CLOUD_IMPORT_STATUS_READY);
   SET_STRING("CLOUD_IMPORT_STATUS_SCANNING",
              IDS_FILE_BROWSER_CLOUD_IMPORT_STATUS_SCANNING);
+  SET_STRING("CLOUD_IMPORT_ITEMS_REMAINING",
+             IDS_FILE_BROWSER_CLOUD_IMPORT_ITEMS_REMAINING);
 
   SET_STRING("CLOUD_IMPORT_TOOLTIP_READY",
              IDS_FILE_BROWSER_CLOUD_IMPORT_TOOLTIP_DONE);
@@ -484,7 +486,12 @@
   SET_STRING("SEARCH_NO_MATCHING_FILES_HTML",
              IDS_FILE_BROWSER_SEARCH_NO_MATCHING_FILES_HTML);
   SET_STRING("SEARCH_TEXT_LABEL", IDS_FILE_BROWSER_SEARCH_TEXT_LABEL);
+  SET_STRING("TASKS_BUTTON_LABEL", IDS_FILE_BROWSER_TASKS_BUTTON_LABEL);
   SET_STRING("SHARE_BUTTON_LABEL", IDS_FILE_BROWSER_SHARE_BUTTON_LABEL);
+  SET_STRING("CHANGE_TO_LISTVIEW_BUTTON_LABEL",
+             IDS_FILE_BROWSER_CHANGE_TO_LISTVIEW_BUTTON_LABEL);
+  SET_STRING("CHANGE_TO_THUMBNAILVIEW_BUTTON_LABEL",
+             IDS_FILE_BROWSER_CHANGE_TO_THUMBNAILVIEW_BUTTON_LABEL);
   SET_STRING("CANCEL_SELECTION_BUTTON_LABEL",
              IDS_FILE_BROWSER_CANCEL_SELECTION_BUTTON_LABEL);
   SET_STRING("SHARE_ERROR", IDS_FILE_BROWSER_SHARE_ERROR);
diff --git a/chrome/browser/chromeos/extensions/first_run_private_api.cc b/chrome/browser/chromeos/extensions/first_run_private_api.cc
index 305a68c1..088f3fc7 100644
--- a/chrome/browser/chromeos/extensions/first_run_private_api.cc
+++ b/chrome/browser/chromeos/extensions/first_run_private_api.cc
@@ -18,7 +18,7 @@
 bool FirstRunPrivateGetLocalizedStringsFunction::RunSync() {
   UMA_HISTOGRAM_COUNTS("CrosFirstRun.DialogShown", 1);
   base::DictionaryValue* localized_strings = new base::DictionaryValue();
-  user_manager::User* user =
+  const user_manager::User* user =
       chromeos::ProfileHelper::Get()->GetUserByProfile(GetProfile());
   if (!user->GetGivenName().empty()) {
     localized_strings->SetString(
diff --git a/chrome/browser/chromeos/extensions/input_view_browsertest.cc b/chrome/browser/chromeos/extensions/input_view_browsertest.cc
index 441a26f..8fc4b38 100644
--- a/chrome/browser/chromeos/extensions/input_view_browsertest.cc
+++ b/chrome/browser/chromeos/extensions/input_view_browsertest.cc
@@ -77,21 +77,24 @@
   }
 };
 
-IN_PROC_BROWSER_TEST_F(InputViewBrowserTest, TypingTest) {
+// Disabled for flaking. See crbug.com/459420.
+IN_PROC_BROWSER_TEST_F(InputViewBrowserTest, DISABLED_TypingTest) {
   std::string id = InstallIMEExtension();
   ASSERT_FALSE(id.empty());
   RunTest(base::FilePath("typing_test.js"),
           InputViewConfig(id, kDefaultLayout));
 }
 
-IN_PROC_BROWSER_TEST_F(InputViewBrowserTest, CompactTypingTest) {
+// Disabled for flaking. See crbug.com/459420.
+IN_PROC_BROWSER_TEST_F(InputViewBrowserTest, DISABLED_CompactTypingTest) {
   std::string id = InstallIMEExtension();
   ASSERT_FALSE(id.empty());
   RunTest(base::FilePath("typing_test.js"),
           InputViewConfig(id, kCompactLayout));
 }
 
-IN_PROC_BROWSER_TEST_F(InputViewBrowserTest, CompactLongpressTest) {
+// Disabled for flaking. See crbug.com/459420.
+IN_PROC_BROWSER_TEST_F(InputViewBrowserTest, DISABLED_CompactLongpressTest) {
   std::string id = InstallIMEExtension();
   ASSERT_FALSE(id.empty());
   RunTest(base::FilePath("longpress_test.js"),
diff --git a/chrome/browser/chromeos/extensions/wallpaper_private_api.cc b/chrome/browser/chromeos/extensions/wallpaper_private_api.cc
index 289c38a..d4983fe6 100644
--- a/chrome/browser/chromeos/extensions/wallpaper_private_api.cc
+++ b/chrome/browser/chromeos/extensions/wallpaper_private_api.cc
@@ -26,6 +26,7 @@
 #include "base/threading/worker_pool.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager.h"
+#include "chrome/browser/chromeos/profiles/profile_helper.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/sync/profile_sync_service.h"
 #include "chrome/browser/sync/profile_sync_service_factory.h"
@@ -101,6 +102,18 @@
          base::ReadFileToString(path, data);
 }
 
+// Gets the |User| for a given |BrowserContext|. The function will only return
+// valid objects.
+const user_manager::User* GetUserFromBrowserContext(
+    content::BrowserContext* context) {
+  Profile* profile = Profile::FromBrowserContext(context);
+  DCHECK(profile);
+  const user_manager::User* user =
+      chromeos::ProfileHelper::Get()->GetUserByProfile(profile);
+  DCHECK(user);
+  return user;
+}
+
 // WindowStateManager remembers which windows have been minimized in order to
 // restore them when the wallpaper viewer is hidden.
 class WindowStateManager : public aura::WindowObserver {
@@ -323,7 +336,9 @@
   params = set_wallpaper_if_exists::Params::Create(*args_);
   EXTENSION_FUNCTION_VALIDATE(params);
 
-  user_id_ = user_manager::UserManager::Get()->GetActiveUser()->email();
+  // Gets email address from caller, ensuring multiprofile compatibility.
+  const user_manager::User* user = GetUserFromBrowserContext(browser_context());
+  user_id_ = user->email();
 
   base::FilePath wallpaper_path;
   base::FilePath fallback_path;
@@ -432,8 +447,9 @@
   params = set_wallpaper::Params::Create(*args_);
   EXTENSION_FUNCTION_VALIDATE(params);
 
-  // Gets email address while at UI thread.
-  user_id_ = user_manager::UserManager::Get()->GetActiveUser()->email();
+  // Gets email address from caller, ensuring multiprofile compatibility.
+  const user_manager::User* user = GetUserFromBrowserContext(browser_context());
+  user_id_ = user->email();
 
   StartDecode(params->wallpaper);
 
@@ -567,10 +583,10 @@
   params = set_custom_wallpaper::Params::Create(*args_);
   EXTENSION_FUNCTION_VALIDATE(params);
 
-  // Gets email address and username hash while at UI thread.
-  user_id_ = user_manager::UserManager::Get()->GetActiveUser()->email();
-  user_id_hash_ =
-      user_manager::UserManager::Get()->GetActiveUser()->username_hash();
+  // Gets email address from caller, ensuring multiprofile compatibility.
+  const user_manager::User* user = GetUserFromBrowserContext(browser_context());
+  user_id_ = user->email();
+  user_id_hash_ = user->username_hash();
 
   StartDecode(params->wallpaper);
 
diff --git a/chrome/browser/chromeos/file_manager/external_filesystem_apitest.cc b/chrome/browser/chromeos/file_manager/external_filesystem_apitest.cc
index 600cd58..c93c6d7d 100644
--- a/chrome/browser/chromeos/file_manager/external_filesystem_apitest.cc
+++ b/chrome/browser/chromeos/file_manager/external_filesystem_apitest.cc
@@ -180,8 +180,8 @@
       resource_id,
       std::string(),  // parent_resource_id
       std::string(),  // title
-      last_modified_time,
-      last_viewed_by_me_time,
+      last_modified_time, last_viewed_by_me_time,
+      google_apis::drive::Properties(),
       google_apis::test_util::CreateCopyResultCallback(&error, &entry));
   base::RunLoop().RunUntilIdle();
   if (error != google_apis::HTTP_SUCCESS)
diff --git a/chrome/browser/chromeos/file_manager/file_manager_jstest.cc b/chrome/browser/chromeos/file_manager/file_manager_jstest.cc
index 0c627e1..415edfa 100644
--- a/chrome/browser/chromeos/file_manager/file_manager_jstest.cc
+++ b/chrome/browser/chromeos/file_manager/file_manager_jstest.cc
@@ -95,6 +95,10 @@
       FILE_PATH_LITERAL("common/js/async_util_unittest.html")));
 }
 
+IN_PROC_BROWSER_TEST_F(FileManagerJsTest, MetricsTest) {
+  RunTest(base::FilePath(FILE_PATH_LITERAL("common/js/metrics_unittest.html")));
+}
+
 IN_PROC_BROWSER_TEST_F(FileManagerJsTest, TaskController) {
   RunTest(base::FilePath(
       FILE_PATH_LITERAL("foreground/js/task_controller_unittest.html")));
@@ -149,3 +153,8 @@
   RunTest(base::FilePath(FILE_PATH_LITERAL(
       "foreground/js/metadata/file_system_metadata_unittest.html")));
 }
+
+IN_PROC_BROWSER_TEST_F(FileManagerJsTest, ThumbnailModel) {
+  RunTest(base::FilePath(FILE_PATH_LITERAL(
+      "foreground/js/metadata/thumbnail_model_unittest.html")));
+}
diff --git a/chrome/browser/chromeos/file_manager/path_util.cc b/chrome/browser/chromeos/file_manager/path_util.cc
index 4416f14..053899e7 100644
--- a/chrome/browser/chromeos/file_manager/path_util.cc
+++ b/chrome/browser/chromeos/file_manager/path_util.cc
@@ -61,7 +61,7 @@
   // to "Downloads". Note that some profiles (like login or test profiles)
   // are not associated with an user account. In that case, no suffix is added
   // because such a profile never belongs to a multi-profile session.
-  user_manager::User* const user =
+  const user_manager::User* const user =
       user_manager::UserManager::IsInitialized()
           ? chromeos::ProfileHelper::Get()->GetUserByProfile(
                 profile->GetOriginalProfile())
diff --git a/chrome/browser/chromeos/file_system_provider/mount_path_util.cc b/chrome/browser/chromeos/file_system_provider/mount_path_util.cc
index 27439c2..22a1aa1 100644
--- a/chrome/browser/chromeos/file_system_provider/mount_path_util.cc
+++ b/chrome/browser/chromeos/file_system_provider/mount_path_util.cc
@@ -50,7 +50,7 @@
 base::FilePath GetMountPath(Profile* profile,
                             const std::string& extension_id,
                             const std::string& file_system_id) {
-  user_manager::User* const user =
+  const user_manager::User* const user =
       user_manager::UserManager::IsInitialized()
           ? chromeos::ProfileHelper::Get()->GetUserByProfile(
                 profile->GetOriginalProfile())
diff --git a/chrome/browser/chromeos/login/easy_unlock/easy_unlock_tpm_key_manager_factory.cc b/chrome/browser/chromeos/login/easy_unlock/easy_unlock_tpm_key_manager_factory.cc
index 16580c48..1e05745 100644
--- a/chrome/browser/chromeos/login/easy_unlock/easy_unlock_tpm_key_manager_factory.cc
+++ b/chrome/browser/chromeos/login/easy_unlock/easy_unlock_tpm_key_manager_factory.cc
@@ -58,7 +58,7 @@
 KeyedService* EasyUnlockTpmKeyManagerFactory::BuildServiceInstanceFor(
     content::BrowserContext* context) const {
   Profile* profile = Profile::FromBrowserContext(context);
-  user_manager::User* user = NULL;
+  const user_manager::User* user = NULL;
   if (!chromeos::ProfileHelper::IsSigninProfile(profile))
     user = chromeos::ProfileHelper::Get()->GetUserByProfile(profile);
   return new EasyUnlockTpmKeyManager(user ? user->email() : std::string(),
diff --git a/chrome/browser/chromeos/login/lock/screen_locker_delegate.h b/chrome/browser/chromeos/login/lock/screen_locker_delegate.h
index 3227a36..8ddd8bc 100644
--- a/chrome/browser/chromeos/login/lock/screen_locker_delegate.h
+++ b/chrome/browser/chromeos/login/lock/screen_locker_delegate.h
@@ -72,6 +72,9 @@
   // Called when webui lock screen wallpaper is loaded and displayed.
   virtual void OnLockBackgroundDisplayed() = 0;
 
+  // Called when the webui header bar becomes visible.
+  virtual void OnHeaderBarVisible() = 0;
+
   // Returns screen locker associated with delegate.
   ScreenLocker* screen_locker() { return screen_locker_; }
 
diff --git a/chrome/browser/chromeos/login/lock/webui_screen_locker.cc b/chrome/browser/chromeos/login/lock/webui_screen_locker.cc
index 0b558f7b0..08cdae9e7 100644
--- a/chrome/browser/chromeos/login/lock/webui_screen_locker.cc
+++ b/chrome/browser/chromeos/login/lock/webui_screen_locker.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/browser/chromeos/login/lock/webui_screen_locker.h"
 
+#include "ash/shell.h"
+#include "ash/system/chromeos/power/power_event_observer.h"
 #include "ash/wm/lock_state_controller.h"
 #include "ash/wm/lock_state_observer.h"
 #include "base/command_line.h"
@@ -203,6 +205,12 @@
                       base::TimeTicks::Now() - lock_time_);
 }
 
+void WebUIScreenLocker::OnHeaderBarVisible() {
+  DCHECK(ash::Shell::HasInstance());
+
+  ash::Shell::GetInstance()->power_event_observer()->OnLockAnimationsComplete();
+}
+
 OobeUI* WebUIScreenLocker::GetOobeUI() {
   return static_cast<OobeUI*>(GetWebUI()->GetController());
 }
diff --git a/chrome/browser/chromeos/login/lock/webui_screen_locker.h b/chrome/browser/chromeos/login/lock/webui_screen_locker.h
index 0be77edc..c3c1878b 100644
--- a/chrome/browser/chromeos/login/lock/webui_screen_locker.h
+++ b/chrome/browser/chromeos/login/lock/webui_screen_locker.h
@@ -71,6 +71,7 @@
   content::WebUI* GetAssociatedWebUI() override;
   void OnLockWebUIReady() override;
   void OnLockBackgroundDisplayed() override;
+  void OnHeaderBarVisible() override;
 
   // LoginDisplay::Delegate:
   void CancelPasswordChangedFlow() override;
diff --git a/chrome/browser/chromeos/login/saml/saml_offline_signin_limiter.cc b/chrome/browser/chromeos/login/saml/saml_offline_signin_limiter.cc
index 42321e3..b5639d39 100644
--- a/chrome/browser/chromeos/login/saml/saml_offline_signin_limiter.cc
+++ b/chrome/browser/chromeos/login/saml/saml_offline_signin_limiter.cc
@@ -141,7 +141,8 @@
 }
 
 void SAMLOfflineSigninLimiter::ForceOnlineLogin() {
-  user_manager::User* user = ProfileHelper::Get()->GetUserByProfile(profile_);
+  const user_manager::User* user =
+      ProfileHelper::Get()->GetUserByProfile(profile_);
   if (!user) {
     NOTREACHED();
     return;
diff --git a/chrome/browser/chromeos/login/screens/user_image_screen.cc b/chrome/browser/chromeos/login/screens/user_image_screen.cc
index 5ca1b54..3ea07ea 100644
--- a/chrome/browser/chromeos/login/screens/user_image_screen.cc
+++ b/chrome/browser/chromeos/login/screens/user_image_screen.cc
@@ -243,8 +243,8 @@
   DCHECK(!policy_registrar_);
   if (Profile* profile = ProfileHelper::Get()->GetProfileByUser(GetUser())) {
     policy::PolicyService* policy_service =
-        policy::ProfilePolicyConnectorFactory::GetForProfile(profile)->
-            policy_service();
+        policy::ProfilePolicyConnectorFactory::GetForBrowserContext(profile)
+            ->policy_service();
     if (policy_service->GetPolicies(
             policy::PolicyNamespace(policy::POLICY_DOMAIN_CHROME,
                                     std::string()))
diff --git a/chrome/browser/chromeos/login/screens/user_selection_screen.cc b/chrome/browser/chromeos/login/screens/user_selection_screen.cc
index 1ce98ab..c271019 100644
--- a/chrome/browser/chromeos/login/screens/user_selection_screen.cc
+++ b/chrome/browser/chromeos/login/screens/user_selection_screen.cc
@@ -471,6 +471,13 @@
   service->AttemptAuth(user_id);
 }
 
+void UserSelectionScreen::RecordClickOnLockIcon(const std::string& user_id) {
+  EasyUnlockService* service = GetEasyUnlockServiceForUser(user_id);
+  if (!service)
+    return;
+  service->RecordClickOnLockIcon();
+}
+
 EasyUnlockService* UserSelectionScreen::GetEasyUnlockServiceForUser(
     const std::string& user_id) const {
   if (GetScreenType() == OTHER_SCREEN)
diff --git a/chrome/browser/chromeos/login/screens/user_selection_screen.h b/chrome/browser/chromeos/login/screens/user_selection_screen.h
index bcbb88f..bae06d3 100644
--- a/chrome/browser/chromeos/login/screens/user_selection_screen.h
+++ b/chrome/browser/chromeos/login/screens/user_selection_screen.h
@@ -81,6 +81,7 @@
   void SendUserList() override;
   void HardLockPod(const std::string& user_id) override;
   void AttemptEasyUnlock(const std::string& user_id) override;
+  void RecordClickOnLockIcon(const std::string& user_id) override;
 
   // Fills |user_dict| with information about |user|.
   static void FillUserDictionary(
diff --git a/chrome/browser/chromeos/login/session/user_session_manager.cc b/chrome/browser/chromeos/login/session/user_session_manager.cc
index 7b7dfd1..d50a9a72 100644
--- a/chrome/browser/chromeos/login/session/user_session_manager.cc
+++ b/chrome/browser/chromeos/login/session/user_session_manager.cc
@@ -445,7 +445,7 @@
     return;
   }
 
-  user_manager::User* user =
+  const user_manager::User* user =
       ProfileHelper::Get()->GetUserByProfile(user_profile);
   DCHECK(user);
   if (!net::NetworkChangeNotifier::IsOffline()) {
@@ -858,7 +858,8 @@
 void UserSessionManager::InitProfilePreferences(
     Profile* profile,
     const UserContext& user_context) {
-  user_manager::User* user = ProfileHelper::Get()->GetUserByProfile(profile);
+  const user_manager::User* user =
+      ProfileHelper::Get()->GetUserByProfile(profile);
   if (user->is_active()) {
     input_method::InputMethodManager* manager =
         input_method::InputMethodManager::Get();
@@ -1513,7 +1514,7 @@
   if (browser_shutdown::IsTryingToQuit())
     return;
 
-  user_manager::User* const user =
+  const user_manager::User* const user =
       ProfileHelper::Get()->GetUserByProfile(profile);
   locale_util::SwitchLanguageCallback locale_switched_callback(base::Bind(
       &UserSessionManager::RunCallbackOnLocaleLoaded, callback,
diff --git a/chrome/browser/chromeos/login/signin/auth_sync_observer.cc b/chrome/browser/chromeos/login/signin/auth_sync_observer.cc
index 83d8b64..6cd4e76f 100644
--- a/chrome/browser/chromeos/login/signin/auth_sync_observer.cc
+++ b/chrome/browser/chromeos/login/signin/auth_sync_observer.cc
@@ -48,7 +48,8 @@
          user_manager::UserManager::Get()->IsLoggedInAsSupervisedUser());
   ProfileSyncService* sync_service =
       ProfileSyncServiceFactory::GetForProfile(profile_);
-  user_manager::User* user = ProfileHelper::Get()->GetUserByProfile(profile_);
+  const user_manager::User* user =
+      ProfileHelper::Get()->GetUserByProfile(profile_);
   GoogleServiceAuthError::State state =
       sync_service->GetAuthError().state();
   if (state != GoogleServiceAuthError::NONE &&
diff --git a/chrome/browser/chromeos/login/ui/models/user_board_model.h b/chrome/browser/chromeos/login/ui/models/user_board_model.h
index 6ce3bf7..757e558 100644
--- a/chrome/browser/chromeos/login/ui/models/user_board_model.h
+++ b/chrome/browser/chromeos/login/ui/models/user_board_model.h
@@ -20,6 +20,7 @@
   // Methods for easy unlock support.
   virtual void HardLockPod(const std::string& user_id) = 0;
   virtual void AttemptEasyUnlock(const std::string& user_id) = 0;
+  virtual void RecordClickOnLockIcon(const std::string& user_id) = 0;
 
   // BaseScreen implementation:
   std::string GetName() const override;
diff --git a/chrome/browser/chromeos/login/users/chrome_user_manager_impl.cc b/chrome/browser/chromeos/login/users/chrome_user_manager_impl.cc
index 9c0b638..f543561 100644
--- a/chrome/browser/chromeos/login/users/chrome_user_manager_impl.cc
+++ b/chrome/browser/chromeos/login/users/chrome_user_manager_impl.cc
@@ -1059,7 +1059,8 @@
     return;
   }
 
-  user_manager::User* user = ProfileHelper::Get()->GetUserByProfile(profile);
+  const user_manager::User* user =
+      ProfileHelper::Get()->GetUserByProfile(profile);
   if (user == NULL)
     return;
 
diff --git a/chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager.cc b/chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager.cc
index bdb8e84..a65d979 100644
--- a/chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager.cc
+++ b/chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager.cc
@@ -96,8 +96,7 @@
   PrefService* pref_service = g_browser_process->local_state();
 
   return !(pref_service->FindPreference(
-                             prefs::kCustomizationDefaultWallpaperURL)
-               ->IsDefaultValue());
+      prefs::kCustomizationDefaultWallpaperURL)->IsDefaultValue());
 }
 
 // Returns index of the first public session user found in |users|
@@ -578,9 +577,15 @@
 
   const base::FilePath* file = NULL;
 
+  const user_manager::User* user =
+      user_manager::UserManager::Get()->FindUser(user_id);
+
   if (user_manager::UserManager::Get()->IsLoggedInAsGuest()) {
     file =
         use_small ? &guest_small_wallpaper_file_ : &guest_large_wallpaper_file_;
+  } else if (user && user->GetType() == user_manager::USER_TYPE_CHILD) {
+    file =
+        use_small ? &child_small_wallpaper_file_ : &child_large_wallpaper_file_;
   } else {
     file = use_small ? &default_small_wallpaper_file_
                      : &default_large_wallpaper_file_;
@@ -634,7 +639,7 @@
 void WallpaperManager::ScheduleSetUserWallpaper(const std::string& user_id,
                                                 bool delayed) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-  // Some unit tests come here without a UserManager or without a pref system.
+  // Some unit tests come here without a UserManager or without a pref system.q
   if (!user_manager::UserManager::IsInitialized() ||
       !g_browser_process->local_state()) {
     return;
diff --git a/chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager_browsertest.cc b/chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager_browsertest.cc
index 77631ad995..092b8c2d 100644
--- a/chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager_browsertest.cc
+++ b/chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager_browsertest.cc
@@ -119,6 +119,17 @@
     WaitAsyncWallpaperLoadStarted();
   }
 
+  // Logs in |username| and sets it as child account.
+  void LogInAsChild(
+      const std::string& username, const std::string& username_hash) {
+    user_manager::UserManager::Get()->UserLoggedIn(
+        username, username_hash, false);
+    user_manager::User* user =
+        user_manager::UserManager::Get()->FindUserAndModify(username);
+    user_manager::UserManager::Get()->ChangeUserChildStatus(
+        user, true /* is_child */);
+  }
+
   int LoadedWallpapers() {
     return WallpaperManager::Get()->loaded_wallpapers();
   }
@@ -753,6 +764,33 @@
       wallpaper_manager_test_utils::kLargeGuestWallpaperColor));
 }
 
+IN_PROC_BROWSER_TEST_F(WallpaperManagerBrowserTest, SmallChildWallpaper) {
+  if (!ash::test::AshTestHelper::SupportsMultipleDisplays())
+    return;
+  CreateCmdlineWallpapers();
+  LogInAsChild(kTestUser1, kTestUser1Hash);
+  UpdateDisplay("800x600");
+  WallpaperManager::Get()->SetDefaultWallpaperNow(std::string());
+  wallpaper_manager_test_utils::WaitAsyncWallpaperLoadFinished();
+  EXPECT_TRUE(wallpaper_manager_test_utils::ImageIsNearColor(
+      controller_->GetWallpaper(),
+      wallpaper_manager_test_utils::kSmallChildWallpaperColor));
+}
+
+IN_PROC_BROWSER_TEST_F(WallpaperManagerBrowserTest, LargeChildWallpaper) {
+  if (!ash::test::AshTestHelper::SupportsMultipleDisplays())
+    return;
+
+  CreateCmdlineWallpapers();
+  LogInAsChild(kTestUser1, kTestUser1Hash);
+  UpdateDisplay("1600x1200");
+  WallpaperManager::Get()->SetDefaultWallpaperNow(std::string());
+  wallpaper_manager_test_utils::WaitAsyncWallpaperLoadFinished();
+  EXPECT_TRUE(wallpaper_manager_test_utils::ImageIsNearColor(
+      controller_->GetWallpaper(),
+      wallpaper_manager_test_utils::kLargeChildWallpaperColor));
+}
+
 IN_PROC_BROWSER_TEST_F(WallpaperManagerBrowserTest,
                        SwitchBetweenDefaultAndCustom) {
   // Start loading the default wallpaper.
diff --git a/chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager_test_utils.cc b/chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager_test_utils.cc
index 9587215..b655409 100644
--- a/chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager_test_utils.cc
+++ b/chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager_test_utils.cc
@@ -68,6 +68,8 @@
 const SkColor kSmallDefaultWallpaperColor = SK_ColorGREEN;
 const SkColor kLargeGuestWallpaperColor = SK_ColorBLUE;
 const SkColor kSmallGuestWallpaperColor = SK_ColorYELLOW;
+const SkColor kLargeChildWallpaperColor = SK_ColorCYAN;
+const SkColor kSmallChildWallpaperColor = SK_ColorMAGENTA;
 
 const SkColor kCustomWallpaperColor = SK_ColorMAGENTA;
 
@@ -186,6 +188,17 @@
                     chromeos::switches::kGuestWallpaperLarge + "=" +
                     guest_large_file.value());
 
+  const base::FilePath child_small_file =
+      dir.path().Append(FILE_PATH_LITERAL("child_small.jpg"));
+  options.push_back(std::string("--") +
+                    chromeos::switches::kChildWallpaperSmall + "=" +
+                    child_small_file.value());
+  const base::FilePath child_large_file =
+      dir.path().Append(FILE_PATH_LITERAL("child_large.jpg"));
+  options.push_back(std::string("--") +
+                    chromeos::switches::kChildWallpaperLarge + "=" +
+                    child_large_file.value());
+
   ASSERT_TRUE(WriteJPEGFile(
       small_file, kWallpaperSize, kWallpaperSize, kSmallDefaultWallpaperColor));
   ASSERT_TRUE(WriteJPEGFile(
@@ -200,6 +213,15 @@
                             kWallpaperSize,
                             kLargeGuestWallpaperColor));
 
+  ASSERT_TRUE(WriteJPEGFile(child_small_file,
+                            kWallpaperSize,
+                            kWallpaperSize,
+                            kSmallChildWallpaperColor));
+  ASSERT_TRUE(WriteJPEGFile(child_large_file,
+                            kWallpaperSize,
+                            kWallpaperSize,
+                            kLargeChildWallpaperColor));
+
   command_line->reset(new base::CommandLine(options));
   WallpaperManager::Get()->SetCommandLineForTesting(command_line->get());
 }
diff --git a/chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager_test_utils.h b/chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager_test_utils.h
index ba51834..1b53d34 100644
--- a/chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager_test_utils.h
+++ b/chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager_test_utils.h
@@ -23,6 +23,8 @@
 extern const SkColor kSmallDefaultWallpaperColor;
 extern const SkColor kLargeGuestWallpaperColor;
 extern const SkColor kSmallGuestWallpaperColor;
+extern const SkColor kLargeChildWallpaperColor;
+extern const SkColor kSmallChildWallpaperColor;
 
 // A custom color, specifically chosen to not
 // conflict with any of the default wallpaper colors.
diff --git a/chrome/browser/chromeos/mobile/mobile_activator.cc b/chrome/browser/chromeos/mobile/mobile_activator.cc
index f206a61..a538cf1 100644
--- a/chrome/browser/chromeos/mobile/mobile_activator.cc
+++ b/chrome/browser/chromeos/mobile/mobile_activator.cc
@@ -277,7 +277,7 @@
 }
 
 void MobileActivator::ContinueActivation() {
-  NetworkHandler::Get()->network_configuration_handler()->GetProperties(
+  NetworkHandler::Get()->network_configuration_handler()->GetShillProperties(
       service_path_,
       base::Bind(&MobileActivator::GetPropertiesAndContinueActivation,
                  weak_ptr_factory_.GetWeakPtr()),
@@ -313,13 +313,11 @@
   // We want shill to connect us after activations, so enable autoconnect.
   base::DictionaryValue auto_connect_property;
   auto_connect_property.SetBoolean(shill::kAutoConnectProperty, true);
-  NetworkHandler::Get()->network_configuration_handler()->SetProperties(
-      service_path_,
-      auto_connect_property,
+  NetworkHandler::Get()->network_configuration_handler()->SetShillProperties(
+      service_path_, auto_connect_property,
       // Activation is triggered by the UI.
       NetworkConfigurationObserver::SOURCE_USER_ACTION,
-      base::Bind(&base::DoNothing),
-      network_handler::ErrorCallback());
+      base::Bind(&base::DoNothing), network_handler::ErrorCallback());
   StartActivation();
 }
 
diff --git a/chrome/browser/chromeos/net/network_portal_detector_impl.cc b/chrome/browser/chromeos/net/network_portal_detector_impl.cc
index c83cc0d1..302318e 100644
--- a/chrome/browser/chromeos/net/network_portal_detector_impl.cc
+++ b/chrome/browser/chromeos/net/network_portal_detector_impl.cc
@@ -15,10 +15,10 @@
 #include "chrome/browser/chrome_notification_types.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/shill_profile_client.h"
-#include "chromeos/device_event_log.h"
 #include "chromeos/login/login_state.h"
 #include "chromeos/network/network_state.h"
 #include "chromeos/network/network_state_handler.h"
+#include "components/device_event_log/device_event_log.h"
 #include "content/public/browser/notification_service.h"
 #include "content/public/common/content_switches.h"
 #include "net/http/http_status_code.h"
@@ -336,6 +336,7 @@
     const NetworkState* default_network) {
   DCHECK(CalledOnValidThread());
 
+  notification_controller_.DefaultNetworkChanged(default_network);
   if (!default_network) {
     NET_LOG(EVENT) << "Default network changed: None";
 
diff --git a/chrome/browser/chromeos/net/network_portal_detector_impl_unittest.cc b/chrome/browser/chromeos/net/network_portal_detector_impl_unittest.cc
index 233c904..ecd59bb 100644
--- a/chrome/browser/chromeos/net/network_portal_detector_impl_unittest.cc
+++ b/chrome/browser/chromeos/net/network_portal_detector_impl_unittest.cc
@@ -13,18 +13,24 @@
 #include "base/metrics/histogram_samples.h"
 #include "base/metrics/statistics_recorder.h"
 #include "base/run_loop.h"
+#include "chrome/browser/chromeos/login/users/fake_chrome_user_manager.h"
+#include "chrome/browser/chromeos/login/users/scoped_user_manager_enabler.h"
 #include "chrome/browser/chromeos/net/network_portal_detector_impl.h"
 #include "chrome/browser/chromeos/net/network_portal_detector_test_utils.h"
+#include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile.h"
+#include "chrome/test/base/testing_profile_manager.h"
 #include "chromeos/chromeos_switches.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/shill_device_client.h"
 #include "chromeos/dbus/shill_service_client.h"
+#include "chromeos/network/network_handler.h"
 #include "chromeos/network/network_state.h"
 #include "chromeos/network/network_state_handler.h"
 #include "chromeos/network/portal_detector/network_portal_detector_strategy.h"
 #include "components/captive_portal/captive_portal_detector.h"
 #include "components/captive_portal/captive_portal_testing_utils.h"
+#include "components/user_manager/user_manager.h"
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "dbus/object_path.h"
 #include "net/base/net_errors.h"
@@ -66,15 +72,32 @@
     : public testing::Test,
       public captive_portal::CaptivePortalDetectorTestBase {
  protected:
+  NetworkPortalDetectorImplTest()
+      : test_profile_manager_(TestingBrowserProcess::GetGlobal()) {}
+
   void SetUp() override {
     base::CommandLine* cl = base::CommandLine::ForCurrentProcess();
     cl->AppendSwitch(switches::kDisableNetworkPortalNotification);
 
+    FakeChromeUserManager* user_manager = new FakeChromeUserManager();
+    user_manager_enabler_.reset(new ScopedUserManagerEnabler(user_manager));
+
     DBusThreadManager::Initialize();
     base::StatisticsRecorder::Initialize();
     SetupNetworkHandler();
 
-    profile_.reset(new TestingProfile());
+    ASSERT_TRUE(test_profile_manager_.SetUp());
+
+    // Add a user.
+    const char kTestUserName[] = "test-user@example.com";
+    user_manager->AddUser(kTestUserName);
+    user_manager->LoginUser(kTestUserName);
+
+    // Create a profile for the user.
+    profile_ = test_profile_manager_.CreateTestingProfile(kTestUserName);
+    test_profile_manager_.SetLoggedIn(true);
+    EXPECT_TRUE(user_manager::UserManager::Get()->GetPrimaryUser());
+
     network_portal_detector_.reset(
         new NetworkPortalDetectorImpl(profile_->GetRequestContext()));
     network_portal_detector_->Enable(false);
@@ -93,7 +116,7 @@
 
   void TearDown() override {
     network_portal_detector_.reset();
-    profile_.reset();
+    profile_ = nullptr;
     NetworkHandler::Shutdown();
     DBusThreadManager::Shutdown();
     PortalDetectorStrategy::reset_fields_for_testing();
@@ -123,7 +146,7 @@
     CompleteURLFetch(net_error, status_code, NULL);
   }
 
-  Profile* profile() { return profile_.get(); }
+  Profile* profile() { return profile_; }
 
   NetworkPortalDetectorImpl* network_portal_detector() {
     return network_portal_detector_.get();
@@ -267,9 +290,11 @@
   }
 
   content::TestBrowserThreadBundle thread_bundle_;
-  scoped_ptr<TestingProfile> profile_;
+  Profile* profile_ = nullptr;
   scoped_ptr<NetworkPortalDetectorImpl> network_portal_detector_;
   scoped_ptr<base::HistogramSamples> original_samples_;
+  scoped_ptr<ScopedUserManagerEnabler> user_manager_enabler_;
+  TestingProfileManager test_profile_manager_;
 };
 
 TEST_F(NetworkPortalDetectorImplTest, NoPortal) {
diff --git a/chrome/browser/chromeos/net/network_portal_notification_controller.cc b/chrome/browser/chromeos/net/network_portal_notification_controller.cc
index 24aac64..d9461a0e 100644
--- a/chrome/browser/chromeos/net/network_portal_notification_controller.cc
+++ b/chrome/browser/chromeos/net/network_portal_notification_controller.cc
@@ -4,16 +4,20 @@
 
 #include "chrome/browser/chromeos/net/network_portal_notification_controller.h"
 
+#include <vector>
+
 #include "ash/shell.h"
 #include "ash/system/system_notifier.h"
 #include "ash/system/tray/system_tray_notifier.h"
 #include "base/basictypes.h"
 #include "base/command_line.h"
 #include "base/compiler_specific.h"
+#include "base/memory/ref_counted.h"
 #include "base/memory/scoped_ptr.h"
 #include "base/message_loop/message_loop.h"
 #include "base/metrics/histogram.h"
 #include "base/strings/string16.h"
+#include "base/strings/string_number_conversions.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/chromeos/mobile/mobile_activator.h"
@@ -31,6 +35,10 @@
 #include "chromeos/network/network_state.h"
 #include "chromeos/network/network_type_pattern.h"
 #include "components/captive_portal/captive_portal_detector.h"
+#include "components/user_manager/user_manager.h"
+#include "extensions/browser/api/networking_config/networking_config_service.h"
+#include "extensions/browser/api/networking_config/networking_config_service_factory.h"
+#include "third_party/cros_system_api/dbus/service_constants.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/message_center/message_center.h"
@@ -45,32 +53,83 @@
 
 namespace {
 
+const int kUseExtensionButtonIndex = 0;
+const int kOpenPortalButtonIndex = 1;
+
 bool IsPortalNotificationEnabled() {
   return !base::CommandLine::ForCurrentProcess()->HasSwitch(
       switches::kDisableNetworkPortalNotification);
 }
 
-
 void CloseNotification() {
   message_center::MessageCenter::Get()->RemoveNotification(
       NetworkPortalNotificationController::kNotificationId, false);
 }
 
+Profile* GetProfileForPrimaryUser() {
+  const user_manager::User* primary_user =
+      user_manager::UserManager::Get()->GetPrimaryUser();
+  if (!primary_user)
+    return nullptr;
+  return ProfileHelper::Get()->GetProfileByUser(primary_user);
+}
+
+// Note that NetworkingConfigService may change after login as the profile
+// switches from the login to the user's profile.
+extensions::NetworkingConfigService* GetNetworkingConfigService(
+    Profile* profile) {
+  if (!profile)
+    return nullptr;
+  return extensions::NetworkingConfigServiceFactory::GetForBrowserContext(
+      profile);
+}
+
+const extensions::Extension* LookupExtensionForRawSsid(
+    extensions::NetworkingConfigService* networking_config_service,
+    const std::vector<uint8_t>& raw_ssid) {
+  DCHECK(networking_config_service);
+  Profile* profile = GetProfileForPrimaryUser();
+  if (!profile || !networking_config_service)
+    return nullptr;
+  std::string extension_id;
+  std::string hex_ssid =
+      base::HexEncode(vector_as_array(&raw_ssid), raw_ssid.size());
+  extension_id =
+      networking_config_service->LookupExtensionIdForHexSsid(hex_ssid);
+  if (extension_id.empty())
+    return nullptr;
+  return extensions::ExtensionRegistry::Get(profile)
+      ->GetExtensionById(extension_id, extensions::ExtensionRegistry::ENABLED);
+}
+
 class NetworkPortalNotificationControllerDelegate
     : public message_center::NotificationDelegate {
  public:
   explicit NetworkPortalNotificationControllerDelegate(
+      const std::string& extension_id,
+      const std::string& guid,
       base::WeakPtr<NetworkPortalNotificationController> controller)
-      : clicked_(false), controller_(controller) {}
+      : extension_id_(extension_id),
+        guid_(guid),
+        clicked_(false),
+        controller_(controller) {}
 
   // Overridden from message_center::NotificationDelegate:
   void Display() override;
   void Close(bool by_user) override;
   void Click() override;
+  void ButtonClick(int button_click) override;
 
  private:
   ~NetworkPortalNotificationControllerDelegate() override {}
 
+  // ID of the extension responsible for network configuration of the network
+  // that this notification is generated for. Empty if none.
+  std::string extension_id_;
+
+  // GUID of the network this notification is generated for.
+  std::string guid_;
+
   bool clicked_;
 
   base::WeakPtr<NetworkPortalNotificationController> controller_;
@@ -89,12 +148,10 @@
   if (clicked_)
     return;
   NetworkPortalNotificationController::UserActionMetric metric =
-      by_user
-      ? NetworkPortalNotificationController::USER_ACTION_METRIC_CLOSED
-      : NetworkPortalNotificationController::USER_ACTION_METRIC_IGNORED;
+      by_user ? NetworkPortalNotificationController::USER_ACTION_METRIC_CLOSED
+              : NetworkPortalNotificationController::USER_ACTION_METRIC_IGNORED;
   UMA_HISTOGRAM_ENUMERATION(
-      NetworkPortalNotificationController::kUserActionMetric,
-      metric,
+      NetworkPortalNotificationController::kUserActionMetric, metric,
       NetworkPortalNotificationController::USER_ACTION_METRIC_COUNT);
 }
 
@@ -131,6 +188,23 @@
   CloseNotification();
 }
 
+void NetworkPortalNotificationControllerDelegate::ButtonClick(
+    int button_index) {
+  if (button_index == kUseExtensionButtonIndex) {
+    Profile* profile = GetProfileForPrimaryUser();
+    extensions::NetworkingConfigServiceFactory::GetForBrowserContext(profile)
+        ->DispatchPortalDetectedEvent(extension_id_, guid_);
+  } else if (button_index == kOpenPortalButtonIndex) {
+    Click();
+  }
+}
+
+gfx::Image& GetImageForNotification() {
+  ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
+  gfx::Image& icon = bundle.GetImageNamed(IDR_PORTAL_DETECTION_ALERT);
+  return icon;
+}
+
 }  // namespace
 
 // static
@@ -149,7 +223,23 @@
     : dialog_(nullptr) {
 }
 
-NetworkPortalNotificationController::~NetworkPortalNotificationController() {}
+NetworkPortalNotificationController::~NetworkPortalNotificationController() {
+}
+
+void NetworkPortalNotificationController::DefaultNetworkChanged(
+    const NetworkState* network) {
+  if (!network)
+    return;
+  Profile* profile = GetProfileForPrimaryUser();
+  extensions::NetworkingConfigService* networking_config_service =
+      GetNetworkingConfigService(profile);
+  if (!networking_config_service)
+    return;
+  extensions::NetworkingConfigService::AuthenticationResult
+      authentication_result(std::string(), network->guid(),
+                            extensions::NetworkingConfigService::NOTRY);
+  networking_config_service->SetAuthenticationResult(authentication_result);
+}
 
 void NetworkPortalNotificationController::OnPortalDetectionCompleted(
     const NetworkState* network,
@@ -178,36 +268,14 @@
     return;
   last_network_path_ = network->path();
 
-  ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
-  gfx::Image& icon = bundle.GetImageNamed(IDR_PORTAL_DETECTION_ALERT);
-  message_center::NotifierId notifier_id(
-      message_center::NotifierId::SYSTEM_COMPONENT,
-      ash::system_notifier::kNotifierNetworkPortalDetector);
-
-  bool is_wifi = NetworkTypePattern::WiFi().MatchesType(network->type());
-  scoped_ptr<Notification> notification(new Notification(
-      message_center::NOTIFICATION_TYPE_SIMPLE, kNotificationId,
-      l10n_util::GetStringUTF16(
-          is_wifi ?
-          IDS_PORTAL_DETECTION_NOTIFICATION_TITLE_WIFI :
-          IDS_PORTAL_DETECTION_NOTIFICATION_TITLE_WIRED),
-      l10n_util::GetStringFUTF16(
-          is_wifi ?
-          IDS_PORTAL_DETECTION_NOTIFICATION_MESSAGE_WIFI :
-          IDS_PORTAL_DETECTION_NOTIFICATION_MESSAGE_WIRED,
-          base::UTF8ToUTF16(network->name())),
-      icon, base::string16() /* display_source */, notifier_id,
-      message_center::RichNotificationData(),
-      new NetworkPortalNotificationControllerDelegate(AsWeakPtr())));
-  notification->SetSystemPriority();
-
   if (ash::Shell::HasInstance()) {
     ash::Shell::GetInstance()
         ->system_tray_notifier()
         ->NotifyOnCaptivePortalDetected(network->path());
   }
 
-  message_center::MessageCenter::Get()->AddNotification(notification.Pass());
+  message_center::MessageCenter::Get()->AddNotification(
+      GetNotification(network, state));
 }
 
 void NetworkPortalNotificationController::ShowDialog() {
@@ -228,4 +296,102 @@
   }
 }
 
+scoped_ptr<message_center::Notification>
+NetworkPortalNotificationController::CreateDefaultCaptivePortalNotification(
+    const NetworkState* network) {
+  message_center::RichNotificationData data;
+  scoped_refptr<NetworkPortalNotificationControllerDelegate> delegate(
+      new NetworkPortalNotificationControllerDelegate(
+          std::string(), network->guid(), AsWeakPtr()));
+  gfx::Image& icon = GetImageForNotification();
+  message_center::NotifierId notifier_id(
+      message_center::NotifierId::SYSTEM_COMPONENT,
+      ash::system_notifier::kNotifierNetworkPortalDetector);
+  base::string16 notificationText;
+  bool is_wifi = NetworkTypePattern::WiFi().MatchesType(network->type());
+  scoped_ptr<Notification> notification(new Notification(
+      message_center::NOTIFICATION_TYPE_SIMPLE, kNotificationId,
+      l10n_util::GetStringUTF16(
+          is_wifi ? IDS_PORTAL_DETECTION_NOTIFICATION_TITLE_WIFI
+                  : IDS_PORTAL_DETECTION_NOTIFICATION_TITLE_WIRED),
+      l10n_util::GetStringFUTF16(
+          is_wifi ? IDS_PORTAL_DETECTION_NOTIFICATION_MESSAGE_WIFI
+                  : IDS_PORTAL_DETECTION_NOTIFICATION_MESSAGE_WIRED,
+          base::UTF8ToUTF16(network->name())),
+      icon, base::string16(), notifier_id, data, delegate.get()));
+  notification->SetSystemPriority();
+  return notification.Pass();
+}
+
+scoped_ptr<message_center::Notification> NetworkPortalNotificationController::
+    CreateCaptivePortalNotificationForExtension(
+        const NetworkState* network,
+        extensions::NetworkingConfigService* networking_config_service,
+        const extensions::Extension* extension) {
+  message_center::RichNotificationData data;
+  scoped_refptr<NetworkPortalNotificationControllerDelegate> delegate(
+      new NetworkPortalNotificationControllerDelegate(
+          extension->id(), network->guid(), AsWeakPtr()));
+  gfx::Image& icon = GetImageForNotification();
+  message_center::NotifierId notifier_id(
+      message_center::NotifierId::SYSTEM_COMPONENT,
+      ash::system_notifier::kNotifierNetworkPortalDetector);
+
+  const extensions::NetworkingConfigService::AuthenticationResult&
+      authentication_result =
+          networking_config_service->GetAuthenticationResult();
+  base::string16 notificationText;
+  if (authentication_result.authentication_state ==
+          extensions::NetworkingConfigService::NOTRY ||
+      network->guid() != authentication_result.guid) {
+    notificationText = l10n_util::GetStringFUTF16(
+        IDS_PORTAL_DETECTION_NOTIFICATION_MESSAGE_ASK_WIFI,
+        base::UTF8ToUTF16(network->name()));
+    data.buttons.push_back(
+        message_center::ButtonInfo(l10n_util::GetStringFUTF16(
+            IDS_PORTAL_DETECTION_NOTIFICATION_BUTTON_EXTENSION,
+            base::UTF8ToUTF16(extension->name()))));
+    data.buttons.push_back(message_center::ButtonInfo(l10n_util::GetStringUTF16(
+        IDS_PORTAL_DETECTION_NOTIFICATION_BUTTON_PORTAL)));
+  } else {
+    notificationText = l10n_util::GetStringFUTF16(
+        IDS_PORTAL_DETECTION_NOTIFICATION_MESSAGE_FAILED_WIFI,
+        base::UTF8ToUTF16(network->name()));
+    data.buttons.push_back(
+        message_center::ButtonInfo(l10n_util::GetStringFUTF16(
+            IDS_PORTAL_DETECTION_NOTIFICATION_BUTTON_EXTENSION_RETRY,
+            base::UTF8ToUTF16(extension->name()))));
+    data.buttons.push_back(message_center::ButtonInfo(l10n_util::GetStringUTF16(
+        IDS_PORTAL_DETECTION_NOTIFICATION_BUTTON_PORTAL)));
+  }
+  scoped_ptr<Notification> notification(new Notification(
+      message_center::NOTIFICATION_TYPE_SIMPLE, kNotificationId,
+      l10n_util::GetStringUTF16(IDS_PORTAL_DETECTION_NOTIFICATION_TITLE_WIFI),
+      notificationText, icon, base::string16() /* display_source */,
+      notifier_id, data, delegate.get()));
+  notification->SetSystemPriority();
+  return notification.Pass();
+}
+
+scoped_ptr<Notification> NetworkPortalNotificationController::GetNotification(
+    const NetworkState* network,
+    const NetworkPortalDetector::CaptivePortalState& state) {
+  base::string16 notificationText;
+  Profile* profile = GetProfileForPrimaryUser();
+  extensions::NetworkingConfigService* networking_config_service =
+      GetNetworkingConfigService(profile);
+  std::string extension_id;
+  const extensions::Extension* extension = nullptr;
+  if (networking_config_service) {
+    extension = LookupExtensionForRawSsid(networking_config_service,
+                                          network->raw_ssid());
+  }
+  if (extension) {
+    return CreateCaptivePortalNotificationForExtension(
+        network, networking_config_service, extension);
+  } else {
+    return CreateDefaultCaptivePortalNotification(network);
+  }
+}
+
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/net/network_portal_notification_controller.h b/chrome/browser/chromeos/net/network_portal_notification_controller.h
index 1d279af..10c1984e 100644
--- a/chrome/browser/chromeos/net/network_portal_notification_controller.h
+++ b/chrome/browser/chromeos/net/network_portal_notification_controller.h
@@ -10,6 +10,14 @@
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "chromeos/network/portal_detector/network_portal_detector.h"
+#include "ui/message_center/notification.h"
+
+namespace extensions {
+
+class Extension;
+class NetworkingConfigService;
+
+}  // namespace extensions
 
 namespace chromeos {
 
@@ -44,7 +52,9 @@
   static const char kUserActionMetric[];
 
   NetworkPortalNotificationController();
-  virtual ~NetworkPortalNotificationController();
+  ~NetworkPortalNotificationController();
+
+  void DefaultNetworkChanged(const NetworkState* network);
 
   void OnPortalDetectionCompleted(
       const NetworkState* network,
@@ -57,6 +67,29 @@
   void OnDialogDestroyed(const NetworkPortalWebDialog* dialog);
 
  private:
+  // Creates the default notification informing the user that a captive portal
+  // has been detected. On click the captive portal login page is opened in the
+  // browser.
+  scoped_ptr<message_center::Notification>
+  CreateDefaultCaptivePortalNotification(const NetworkState* network);
+
+  // Creates an advanced captive portal notification informing the user that a
+  // captive portal has been detected and an extension has registered to perform
+  // captive portal authentication for that network. Gives the user the choice
+  // to either authenticate using that extension or open the captive portal
+  // login page in the browser.
+  scoped_ptr<message_center::Notification>
+  CreateCaptivePortalNotificationForExtension(
+      const NetworkState* network,
+      extensions::NetworkingConfigService* networking_config_service,
+      const extensions::Extension* extension);
+
+  // Constructs a notification to inform the user that a captive portal has been
+  // detected.
+  scoped_ptr<message_center::Notification> GetNotification(
+      const NetworkState* network,
+      const NetworkPortalDetector::CaptivePortalState& state);
+
   // Last network path for which notification was displayed.
   std::string last_network_path_;
 
diff --git a/chrome/browser/chromeos/net/network_portal_notification_controller_unittest.cc b/chrome/browser/chromeos/net/network_portal_notification_controller_unittest.cc
index 946cc85a..823076a 100644
--- a/chrome/browser/chromeos/net/network_portal_notification_controller_unittest.cc
+++ b/chrome/browser/chromeos/net/network_portal_notification_controller_unittest.cc
@@ -3,6 +3,8 @@
 // found in the LICENSE file.
 
 #include "base/command_line.h"
+#include "chrome/browser/chromeos/login/users/fake_chrome_user_manager.h"
+#include "chrome/browser/chromeos/login/users/scoped_user_manager_enabler.h"
 #include "chrome/browser/chromeos/net/network_portal_notification_controller.h"
 #include "chromeos/chromeos_switches.h"
 #include "chromeos/network/network_state.h"
@@ -61,7 +63,8 @@
 
 class NetworkPortalNotificationControllerTest : public testing::Test {
  public:
-  NetworkPortalNotificationControllerTest() {}
+  NetworkPortalNotificationControllerTest()
+      : user_manager_enabler_(new chromeos::FakeChromeUserManager()) {}
   ~NetworkPortalNotificationControllerTest() override {}
 
   void SetUp() override {
@@ -86,6 +89,7 @@
   NotificationObserver& observer() { return observer_; }
 
  private:
+  ScopedUserManagerEnabler user_manager_enabler_;
   NetworkPortalNotificationController controller_;
   NotificationObserver observer_;
 
diff --git a/chrome/browser/chromeos/net/onc_utils.cc b/chrome/browser/chromeos/net/onc_utils.cc
index 3c067f45..abcd08a3 100644
--- a/chrome/browser/chromeos/net/onc_utils.cc
+++ b/chrome/browser/chromeos/net/onc_utils.cc
@@ -217,6 +217,8 @@
         normalizer.NormalizeObject(&onc::kNetworkConfigurationSignature,
                                    *network);
 
+    // TODO(pneubeck): Use ONC and ManagedNetworkConfigurationHandler instead.
+    // crbug.com/457936
     scoped_ptr<base::DictionaryValue> shill_dict =
         onc::TranslateONCObjectToShill(&onc::kNetworkConfigurationSignature,
                                        *normalized_network);
@@ -243,7 +245,7 @@
           NetworkHandler::Get()->network_state_handler()->FirstNetworkByType(
               NetworkTypePattern::Ethernet());
       if (ethernet) {
-        config_handler->SetProperties(
+        config_handler->SetShillProperties(
             ethernet->path(), *shill_dict,
             NetworkConfigurationObserver::SOURCE_USER_ACTION, base::Closure(),
             network_handler::ErrorCallback());
@@ -252,9 +254,8 @@
       }
 
     } else {
-      config_handler->CreateConfiguration(
-          *shill_dict,
-          NetworkConfigurationObserver::SOURCE_USER_ACTION,
+      config_handler->CreateShillConfiguration(
+          *shill_dict, NetworkConfigurationObserver::SOURCE_USER_ACTION,
           network_handler::StringResultCallback(),
           network_handler::ErrorCallback());
     }
diff --git a/chrome/browser/chromeos/options/vpn_config_view.cc b/chrome/browser/chromeos/options/vpn_config_view.cc
index 024bbaf..7c3334b 100644
--- a/chrome/browser/chromeos/options/vpn_config_view.cc
+++ b/chrome/browser/chromeos/options/vpn_config_view.cc
@@ -375,7 +375,7 @@
     }
 
     ui::NetworkConnect::Get()->CreateConfigurationAndConnect(&properties,
-                                                              shared);
+                                                             shared);
   } else {
     const NetworkState* vpn = NetworkHandler::Get()->network_state_handler()->
         GetNetworkState(service_path_);
@@ -693,10 +693,9 @@
   Refresh();
 
   if (vpn) {
-    NetworkHandler::Get()->network_configuration_handler()->GetProperties(
-        service_path_,
-        base::Bind(&VPNConfigView::InitFromProperties,
-                   weak_ptr_factory_.GetWeakPtr()),
+    NetworkHandler::Get()->network_configuration_handler()->GetShillProperties(
+        service_path_, base::Bind(&VPNConfigView::InitFromProperties,
+                                  weak_ptr_factory_.GetWeakPtr()),
         base::Bind(&VPNConfigView::GetPropertiesError,
                    weak_ptr_factory_.GetWeakPtr()));
   }
diff --git a/chrome/browser/chromeos/options/wifi_config_view.cc b/chrome/browser/chromeos/options/wifi_config_view.cc
index dc51bc9..f246182 100644
--- a/chrome/browser/chromeos/options/wifi_config_view.cc
+++ b/chrome/browser/chromeos/options/wifi_config_view.cc
@@ -1195,11 +1195,9 @@
   UpdateErrorLabel();
 
   if (network) {
-    NetworkHandler::Get()->network_configuration_handler()->GetProperties(
-        service_path_,
-        base::Bind(&WifiConfigView::InitFromProperties,
-                   weak_ptr_factory_.GetWeakPtr(),
-                   show_8021x),
+    NetworkHandler::Get()->network_configuration_handler()->GetShillProperties(
+        service_path_, base::Bind(&WifiConfigView::InitFromProperties,
+                                  weak_ptr_factory_.GetWeakPtr(), show_8021x),
         base::Bind(&ShillError, "GetProperties"));
   }
 }
diff --git a/chrome/browser/chromeos/options/wimax_config_view.cc b/chrome/browser/chromeos/options/wimax_config_view.cc
index ed39f49f..f5ea282 100644
--- a/chrome/browser/chromeos/options/wimax_config_view.cc
+++ b/chrome/browser/chromeos/options/wimax_config_view.cc
@@ -347,10 +347,9 @@
   UpdateErrorLabel();
 
   if (wimax) {
-    NetworkHandler::Get()->network_configuration_handler()->GetProperties(
-        service_path_,
-        base::Bind(&WimaxConfigView::InitFromProperties,
-                   weak_ptr_factory_.GetWeakPtr()),
+    NetworkHandler::Get()->network_configuration_handler()->GetShillProperties(
+        service_path_, base::Bind(&WimaxConfigView::InitFromProperties,
+                                  weak_ptr_factory_.GetWeakPtr()),
         base::Bind(&ShillError, "GetProperties"));
   }
 }
diff --git a/chrome/browser/chromeos/platform_keys/platform_keys.h b/chrome/browser/chromeos/platform_keys/platform_keys.h
index 6c5d8c8..79fff7d 100644
--- a/chrome/browser/chromeos/platform_keys/platform_keys.h
+++ b/chrome/browser/chromeos/platform_keys/platform_keys.h
@@ -113,16 +113,19 @@
 
 }  // namespace subtle
 
+// Returns the DER encoding of the X.509 Subject Public Key Info of the public
+// key in |certificate|.
+std::string GetSubjectPublicKeyInfo(
+    const scoped_refptr<net::X509Certificate>& certificate);
+
 // Obtains information about the public key in |certificate|.
 // If |certificate| contains an RSA key, sets |key_size_bits| to the modulus
-// length, |public_key_spki_der| to the DER encoding of the X.509 Subject Public
-// Key Info, and |key_type| to type RSA and returns true.
+// length, and |key_type| to type RSA and returns true.
 // If |certificate| contains any other key type, or if the public exponent of
 // the RSA key in |certificate| is not F4, returns false and does not update any
 // of the output parameters.
 // All pointer arguments must not be null.
 bool GetPublicKey(const scoped_refptr<net::X509Certificate>& certificate,
-                  std::string* public_key_spki_der,
                   net::X509Certificate::PublicKeyType* key_type,
                   size_t* key_size_bits);
 
diff --git a/chrome/browser/chromeos/platform_keys/platform_keys_nss.cc b/chrome/browser/chromeos/platform_keys/platform_keys_nss.cc
index 978f115..a6d1a07f 100644
--- a/chrome/browser/chromeos/platform_keys/platform_keys_nss.cc
+++ b/chrome/browser/chromeos/platform_keys/platform_keys_nss.cc
@@ -756,8 +756,9 @@
   cert_request_info->cert_key_types = request.certificate_key_types;
   cert_request_info->cert_authorities = request.certificate_authorities;
 
-  user_manager::User* user = chromeos::ProfileHelper::Get()->GetUserByProfile(
-      Profile::FromBrowserContext(browser_context));
+  const user_manager::User* user =
+      chromeos::ProfileHelper::Get()->GetUserByProfile(
+          Profile::FromBrowserContext(browser_context));
 
   // Use the device-wide system key slot only if the user is of the same
   // domain as the device is registered to.
@@ -776,12 +777,15 @@
 
 }  // namespace subtle
 
+std::string GetSubjectPublicKeyInfo(
+    const scoped_refptr<net::X509Certificate>& certificate) {
+  const SECItem& spki_der = certificate->os_cert_handle()->derPublicKey;
+  return std::string(spki_der.data, spki_der.data + spki_der.len);
+}
+
 bool GetPublicKey(const scoped_refptr<net::X509Certificate>& certificate,
-                  std::string* public_key_spki_der,
                   net::X509Certificate::PublicKeyType* key_type,
                   size_t* key_size_bits) {
-  const SECItem& spki_der = certificate->os_cert_handle()->derPublicKey;
-
   net::X509Certificate::PublicKeyType key_type_tmp =
       net::X509Certificate::kPublicKeyTypeUnknown;
   size_t key_size_bits_tmp = 0;
@@ -809,10 +813,8 @@
     return false;
   }
 
-  public_key_spki_der->assign(spki_der.data, spki_der.data + spki_der.len);
   *key_type = key_type_tmp;
   *key_size_bits = key_size_bits_tmp;
-
   return true;
 }
 
diff --git a/chrome/browser/chromeos/platform_keys/platform_keys_service.cc b/chrome/browser/chromeos/platform_keys/platform_keys_service.cc
index 4f9eb17..34228d3 100644
--- a/chrome/browser/chromeos/platform_keys/platform_keys_service.cc
+++ b/chrome/browser/chromeos/platform_keys/platform_keys_service.cc
@@ -17,18 +17,125 @@
 
 namespace chromeos {
 
+struct PlatformKeysService::KeyEntry {
+  // The base64-encoded DER of a X.509 Subject Public Key Info.
+  std::string spki_b64;
+
+  // True if the key can be used once for singing.
+  // This permission is granted if an extension generated a key using the
+  // enterprise.platformKeys API, so that it can build a certification request..
+  // After the first signing operation this permission will be revoked.
+  bool sign_once = false;
+
+  // True if the key can be used for signing an unlimited number of times.
+  // This permission is granted by the user or by policy to allow the extension
+  // to use the key for signing through the enterprise.platformKeys or
+  // platformKeys API.
+  // This permission is granted until revoked by the user or the policy.
+  bool sign_unlimited = false;
+};
+
 namespace {
 
 const char kErrorKeyNotAllowedForSigning[] =
     "This key is not allowed for signing. Either it was used for signing "
     "before or it was not correctly generated.";
-const char kStateStorePlatformKeys[] = "PlatformKeys";
 
-scoped_ptr<base::StringValue> GetPublicKeyValue(
-    const std::string& public_key_spki_der) {
-  std::string public_key_spki_der_b64;
-  base::Base64Encode(public_key_spki_der, &public_key_spki_der_b64);
-  return make_scoped_ptr(new base::StringValue(public_key_spki_der_b64));
+// The key at which platform key specific data is stored in each extension's
+// state store.
+// From older versions of ChromeOS, this key can hold a list of base64 and
+// DER-encoded SPKIs. A key can be used for signing at most once if it is part
+// of that list
+// and removed from that list afterwards.
+//
+// The current format of data that is written to the PlatformKeys field is a
+// list of serialized KeyEntry objects:
+//   { 'SPKI': string,
+//     'signOnce': bool,  // if not present, defaults to false
+//     'signUnlimited': bool  // if not present, defaults to false
+//   }
+//
+// Do not change this constant as clients will lose their existing state.
+const char kStateStorePlatformKeys[] = "PlatformKeys";
+const char kStateStoreSPKI[] = "SPKI";
+const char kStateStoreSignOnce[] = "signOnce";
+const char kStateStoreSignUnlimited[] = "signUnlimited";
+
+scoped_ptr<PlatformKeysService::KeyEntries> KeyEntriesFromState(
+    const base::Value& state) {
+  scoped_ptr<PlatformKeysService::KeyEntries> new_entries(
+      new PlatformKeysService::KeyEntries);
+
+  const base::ListValue* entries = nullptr;
+  if (!state.GetAsList(&entries)) {
+    LOG(ERROR) << "Found a state store of wrong type.";
+    return new_entries.Pass();
+  }
+  for (const base::Value* entry : *entries) {
+    if (!entry) {
+      LOG(ERROR) << "Found invalid NULL entry in PlatformKeys state store.";
+      continue;
+    }
+
+    PlatformKeysService::KeyEntry new_entry;
+    const base::DictionaryValue* dict_entry = nullptr;
+    if (entry->GetAsString(&new_entry.spki_b64)) {
+      // This handles the case that the store contained a plain list of base64
+      // and DER-encoded SPKIs from an older version of ChromeOS.
+      new_entry.sign_once = true;
+    } else if (entry->GetAsDictionary(&dict_entry)) {
+      dict_entry->GetStringWithoutPathExpansion(kStateStoreSPKI,
+                                                &new_entry.spki_b64);
+      dict_entry->GetBooleanWithoutPathExpansion(kStateStoreSignOnce,
+                                                 &new_entry.sign_once);
+      dict_entry->GetBooleanWithoutPathExpansion(kStateStoreSignUnlimited,
+                                                 &new_entry.sign_unlimited);
+    } else {
+      LOG(ERROR) << "Found invalid entry of type " << entry->GetType()
+                 << " in PlatformKeys state store.";
+      continue;
+    }
+    new_entries->push_back(new_entry);
+  }
+  return new_entries.Pass();
+}
+
+scoped_ptr<base::ListValue> KeyEntriesToState(
+    const PlatformKeysService::KeyEntries& entries) {
+  scoped_ptr<base::ListValue> new_state(new base::ListValue);
+  for (const PlatformKeysService::KeyEntry& entry : entries) {
+    // Drop entries that the extension doesn't have any permissions for anymore.
+    if (!entry.sign_once && !entry.sign_unlimited)
+      continue;
+
+    scoped_ptr<base::DictionaryValue> new_entry(new base::DictionaryValue);
+    new_entry->SetStringWithoutPathExpansion(kStateStoreSPKI, entry.spki_b64);
+    // Omit writing default values, namely |false|.
+    if (entry.sign_once) {
+      new_entry->SetBooleanWithoutPathExpansion(kStateStoreSignOnce,
+                                                entry.sign_once);
+    }
+    if (entry.sign_unlimited) {
+      new_entry->SetBooleanWithoutPathExpansion(kStateStoreSignUnlimited,
+                                                entry.sign_unlimited);
+    }
+    new_state->Append(new_entry.release());
+  }
+  return new_state.Pass();
+}
+
+// Searches |platform_keys| for an entry for |public_key_spki_der_b64|. If found
+// returns a pointer to it, otherwise returns null.
+PlatformKeysService::KeyEntry* GetMatchingEntry(
+    const std::string& public_key_spki_der_b64,
+    PlatformKeysService::KeyEntries* platform_keys) {
+  for (PlatformKeysService::KeyEntry& entry : *platform_keys) {
+    // For every ASN.1 value there is exactly one DER encoding, so it is fine to
+    // compare the DER (or its base64 encoding).
+    if (entry.spki_b64 == public_key_spki_der_b64)
+      return &entry;
+  }
+  return nullptr;
 }
 
 }  // namespace
@@ -55,14 +162,16 @@
   // Creates a task that reads the current permission for an extension to access
   // a certain key. Afterwards it updates and persists the permission to the new
   // value |new_permission_value|. |callback| will be run after the permission
-  // was persisted. The old permission value is then accessible through
-  // old_permission_value().
-  PermissionUpdateTask(const bool new_permission_value,
+  // was persisted. The old permission values are then available through
+  // old_key_entry().
+  PermissionUpdateTask(const SignPermission permission,
+                       const bool new_permission_value,
                        const std::string& public_key_spki_der,
                        const std::string& extension_id,
                        base::Callback<void(Task*)> callback,
                        PlatformKeysService* service)
-      : new_permission_value_(new_permission_value),
+      : permission_(permission),
+        new_permission_value_(new_permission_value),
         public_key_spki_der_(public_key_spki_der),
         extension_id_(extension_id),
         callback_(callback),
@@ -78,9 +187,9 @@
 
   bool IsDone() override { return next_step_ == Step::DONE; }
 
-  // The original permission value before setting the new value
-  // |new_permission_value|.
-  bool old_permission_value() { return old_permission_value_; }
+  // The old key entry with the old permissions before setting |permission| to
+  // the new value |new_permission_value|.
+  const KeyEntry& old_key_entry() { return old_key_entry_; }
 
  private:
   void DoStep() {
@@ -113,38 +222,59 @@
                                   weak_factory_.GetWeakPtr()));
   }
 
-  void GotPlatformKeys(scoped_ptr<base::ListValue> platform_keys) {
+  void GotPlatformKeys(scoped_ptr<KeyEntries> platform_keys) {
     platform_keys_ = platform_keys.Pass();
     DoStep();
   }
 
-  // Returns whether the extension has permission to use the key for signing
-  // according to the PlatformKeys value read from the extensions state store.
-  // Invalidates the key if it was found to be valid.
+  // Persists the existing KeyEntry in |old_key_entry_|, updates the entry with
+  // the new permission and persists it to the extension's state store if it was
+  // changed.
   void WriteUpdate() {
-    scoped_ptr<base::StringValue> key_value(
-        GetPublicKeyValue(public_key_spki_der_));
+    DCHECK(platform_keys_);
 
-    base::ListValue::const_iterator it = platform_keys_->Find(*key_value);
-    old_permission_value_ = it != platform_keys_->end();
-    if (old_permission_value_ == new_permission_value_)
-      return;
+    std::string public_key_spki_der_b64;
+    base::Base64Encode(public_key_spki_der_, &public_key_spki_der_b64);
 
-    if (new_permission_value_)
-      platform_keys_->Append(key_value.release());
-    else
-      platform_keys_->Remove(*key_value, nullptr);
+    KeyEntry* matching_entry =
+        GetMatchingEntry(public_key_spki_der_b64, platform_keys_.get());
 
-    service_->SetPlatformKeysOfExtension(extension_id_, platform_keys_.Pass());
+    if (!matching_entry) {
+      platform_keys_->push_back(KeyEntry());
+      matching_entry = &platform_keys_->back();
+      matching_entry->spki_b64 = public_key_spki_der_b64;
+    } else if (permission_ == SignPermission::ONCE && new_permission_value_) {
+      // The one-time sign permission is supposed to be granted once per key
+      // during generation. Generated keys should be unique and thus this case
+      // should never occur.
+      NOTREACHED() << "Requested one-time sign permission on existing key.";
+    }
+    old_key_entry_ = *matching_entry;
+
+    bool* permission_value = nullptr;
+    switch (permission_) {
+      case SignPermission::ONCE:
+        permission_value = &matching_entry->sign_once;
+        break;
+      case SignPermission::UNLIMITED:
+        permission_value = &matching_entry->sign_unlimited;
+        break;
+    }
+
+    if (*permission_value != new_permission_value_) {
+      *permission_value = new_permission_value_;
+      service_->SetPlatformKeysOfExtension(extension_id_, *platform_keys_);
+    }
   }
 
   Step next_step_ = Step::READ_PLATFORM_KEYS;
-  scoped_ptr<base::ListValue> platform_keys_;
-  bool old_permission_value_ = false;
+  KeyEntry old_key_entry_;
 
+  const SignPermission permission_;
   const bool new_permission_value_;
   const std::string public_key_spki_der_;
   const std::string extension_id_;
+  scoped_ptr<KeyEntries> platform_keys_;
   base::Callback<void(Task*)> callback_;
   PlatformKeysService* const service_;
   base::WeakPtrFactory<PermissionUpdateTask> weak_factory_;
@@ -198,17 +328,21 @@
         next_step_ = Step::SIGN_OR_ABORT;
         UpdatePermission();
         return;
-      case Step::SIGN_OR_ABORT:
+      case Step::SIGN_OR_ABORT: {
         next_step_ = Step::DONE;
-        if (!service_->permission_check_enabled_ ||
-            permission_update_->old_permission_value()) {
+        bool sign_granted = permission_update_->old_key_entry().sign_once ||
+                            permission_update_->old_key_entry().sign_unlimited;
+        if (sign_granted) {
           Sign();
         } else {
-          callback_.Run(std::string() /* no signature */,
-                        kErrorKeyNotAllowedForSigning);
+          if (!callback_.is_null()) {
+            callback_.Run(std::string() /* no signature */,
+                          kErrorKeyNotAllowedForSigning);
+          }
           DoStep();
         }
         return;
+      }
       case Step::DONE:
         service_->TaskFinished(this);
         // |this| might be invalid now.
@@ -221,7 +355,8 @@
   // signing operations with that key.
   void UpdatePermission() {
     permission_update_.reset(new PermissionUpdateTask(
-        false /* new permission value */, public_key_, extension_id_,
+        SignPermission::ONCE, false /* new permission value */, public_key_,
+        extension_id_,
         base::Bind(&SignTask::DidUpdatePermission, weak_factory_.GetWeakPtr()),
         service_));
     permission_update_->Start();
@@ -251,7 +386,7 @@
   }
 
   Step next_step_ = Step::UPDATE_PERMISSION;
-  scoped_ptr<base::ListValue> platform_keys_;
+  scoped_ptr<KeyEntries> platform_keys_;
   scoped_ptr<PermissionUpdateTask> permission_update_;
 
   const std::string token_id_;
@@ -271,6 +406,217 @@
   DISALLOW_COPY_AND_ASSIGN(SignTask);
 };
 
+class PlatformKeysService::SelectTask : public Task {
+ public:
+  enum class Step {
+    GET_MATCHING_CERTS,
+    SELECT_CERTS,
+    READ_PLATFORM_KEYS,
+    UPDATE_PERMISSION,
+    FILTER_BY_PERMISSIONS,
+    DONE,
+  };
+
+  // This task determines all known client certs matching |request|. If
+  // |interactive| is true, calls |service->select_delegate_->Select()| to
+  // select a cert from all matches. The extension with |extension_id| will be
+  // granted unlimited sign permission for the selected cert.
+  // Finally, either the selection or, if |interactive| is false, matching certs
+  // that the extension has permission for are passed to |callback|.
+  SelectTask(const platform_keys::ClientCertificateRequest& request,
+             bool interactive,
+             const std::string& extension_id,
+             const SelectCertificatesCallback& callback,
+             content::WebContents* web_contents,
+             PlatformKeysService* service)
+      : request_(request),
+        interactive_(interactive),
+        extension_id_(extension_id),
+        callback_(callback),
+        web_contents_(web_contents),
+        service_(service),
+        weak_factory_(this) {}
+  ~SelectTask() override {}
+
+  void Start() override {
+    CHECK(next_step_ == Step::GET_MATCHING_CERTS);
+    DoStep();
+  }
+  bool IsDone() override { return next_step_ == Step::DONE; }
+
+ private:
+  void DoStep() {
+    switch (next_step_) {
+      case Step::GET_MATCHING_CERTS:
+        if (interactive_)
+          next_step_ = Step::SELECT_CERTS;
+        else  // Skip SelectCerts and UpdatePermission if not interactive.
+          next_step_ = Step::READ_PLATFORM_KEYS;
+        GetMatchingCerts();
+        return;
+      case Step::SELECT_CERTS:
+        next_step_ = Step::UPDATE_PERMISSION;
+        SelectCerts();
+        return;
+      case Step::UPDATE_PERMISSION:
+        next_step_ = Step::READ_PLATFORM_KEYS;
+        UpdatePermission();
+        return;
+      case Step::READ_PLATFORM_KEYS:
+        next_step_ = Step::FILTER_BY_PERMISSIONS;
+        ReadPlatformKeys();
+        return;
+      case Step::FILTER_BY_PERMISSIONS:
+        next_step_ = Step::DONE;
+        FilterSelectionByPermission();
+        return;
+      case Step::DONE:
+        service_->TaskFinished(this);
+        // |this| might be invalid now.
+        return;
+    }
+  }
+
+  // Retrieves all certificates matching |request_|. Will call back to
+  // |GotMatchingCerts()|.
+  void GetMatchingCerts() {
+    platform_keys::subtle::SelectClientCertificates(
+        request_,
+        base::Bind(&SelectTask::GotMatchingCerts, weak_factory_.GetWeakPtr()),
+        service_->browser_context_);
+  }
+
+  // If the certificate request could be processed successfully, |matches| will
+  // contain the list of matching certificates (maybe empty) and |error_message|
+  // will be empty. If an error occurred, |matches| will be null and
+  // |error_message| contain an error message.
+  void GotMatchingCerts(scoped_ptr<net::CertificateList> matches,
+                        const std::string& error_message) {
+    if (!error_message.empty()) {
+      next_step_ = Step::DONE;
+      callback_.Run(nullptr /* no certificates */, error_message);
+      DoStep();
+      return;
+    }
+    matches_.swap(*matches);
+    DoStep();
+  }
+
+  // Calls |service_->select_delegate_->Select()| to select a cert from
+  // |matches_|, which will be stored in |selected_cert_|.
+  // Will call back to |GotSelection()|.
+  void SelectCerts() {
+    CHECK(interactive_);
+    if (matches_.empty()) {
+      // Don't show a select dialog if no certificate is matching.
+      DoStep();
+      return;
+    }
+    service_->select_delegate_->Select(
+        extension_id_, matches_,
+        base::Bind(&SelectTask::GotSelection, base::Unretained(this)),
+        web_contents_, service_->browser_context_);
+  }
+
+  // Will be called by |SelectCerts()| with the selected cert or null if no cert
+  // was selected.
+  void GotSelection(const scoped_refptr<net::X509Certificate>& selected_cert) {
+    selected_cert_ = selected_cert;
+    DoStep();
+  }
+
+  // Updates the extension's state store about unlimited sign permission for the
+  // selected cert. Does nothing if no cert was selected.
+  // Will call back to |DidUpdatePermission()|.
+  void UpdatePermission() {
+    CHECK(interactive_);
+    if (!selected_cert_) {
+      DoStep();
+      return;
+    }
+    const std::string public_key_spki_der(
+        platform_keys::GetSubjectPublicKeyInfo(selected_cert_));
+    permission_update_.reset(new PermissionUpdateTask(
+        SignPermission::UNLIMITED, true /* new permission value */,
+        public_key_spki_der, extension_id_,
+        base::Bind(&SelectTask::DidUpdatePermission, base::Unretained(this)),
+        service_));
+    permission_update_->Start();
+  }
+
+  void DidUpdatePermission(Task* /* task */) { DoStep(); }
+
+  // Reads the PlatformKeys value from the extension's state store and calls
+  // back to GotPlatformKeys().
+  void ReadPlatformKeys() {
+    service_->GetPlatformKeysOfExtension(
+        extension_id_,
+        base::Bind(&SelectTask::GotPlatformKeys, weak_factory_.GetWeakPtr()));
+  }
+
+  void GotPlatformKeys(scoped_ptr<KeyEntries> platform_keys) {
+    platform_keys_ = platform_keys.Pass();
+    DoStep();
+  }
+
+  // Filters from all matches (if not interactive) or from the selection (if
+  // interactive), the certificates that the extension has unlimited sign
+  // permission for. Passes the filtered certs to |callback_|.
+  void FilterSelectionByPermission() {
+    scoped_ptr<net::CertificateList> selection(new net::CertificateList);
+    if (interactive_) {
+      if (selected_cert_)
+        selection->push_back(selected_cert_);
+    } else {
+      selection->assign(matches_.begin(), matches_.end());
+    }
+
+    scoped_ptr<net::CertificateList> filtered_certs(new net::CertificateList);
+    for (scoped_refptr<net::X509Certificate> selected_cert : *selection) {
+      const std::string public_key_spki_der(
+          platform_keys::GetSubjectPublicKeyInfo(selected_cert));
+      std::string public_key_spki_der_b64;
+      base::Base64Encode(public_key_spki_der, &public_key_spki_der_b64);
+
+      KeyEntry* matching_entry =
+          GetMatchingEntry(public_key_spki_der_b64, platform_keys_.get());
+      if (!matching_entry || !matching_entry->sign_unlimited)
+        continue;
+
+      filtered_certs->push_back(selected_cert);
+    }
+    // Note: In the interactive case this should have filtered exactly the
+    // one selected cert. Checking the permissions again is not striclty
+    // necessary but this ensures that the permissions were updated correctly.
+    CHECK(!selected_cert_ || (filtered_certs->size() == 1 &&
+                              filtered_certs->front() == selected_cert_));
+    callback_.Run(filtered_certs.Pass(), std::string() /* no error */);
+    DoStep();
+  }
+
+  Step next_step_ = Step::GET_MATCHING_CERTS;
+  scoped_ptr<KeyEntries> platform_keys_;
+  scoped_ptr<PermissionUpdateTask> permission_update_;
+
+  net::CertificateList matches_;
+  scoped_refptr<net::X509Certificate> selected_cert_;
+  platform_keys::ClientCertificateRequest request_;
+  const bool interactive_;
+  const std::string extension_id_;
+  const SelectCertificatesCallback callback_;
+  content::WebContents* const web_contents_;
+  PlatformKeysService* const service_;
+  base::WeakPtrFactory<SelectTask> weak_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(SelectTask);
+};
+
+PlatformKeysService::SelectDelegate::SelectDelegate() {
+}
+
+PlatformKeysService::SelectDelegate::~SelectDelegate() {
+}
+
 PlatformKeysService::PlatformKeysService(
     content::BrowserContext* browser_context,
     extensions::StateStore* state_store)
@@ -283,8 +629,22 @@
 PlatformKeysService::~PlatformKeysService() {
 }
 
-void PlatformKeysService::DisablePermissionCheckForTesting() {
-  permission_check_enabled_ = false;
+void PlatformKeysService::SetSelectDelegate(
+    scoped_ptr<SelectDelegate> delegate) {
+  select_delegate_ = delegate.Pass();
+}
+
+void PlatformKeysService::GrantUnlimitedSignPermission(
+    const std::string& extension_id,
+    scoped_refptr<net::X509Certificate> cert) {
+  const std::string public_key_spki_der(
+      platform_keys::GetSubjectPublicKeyInfo(cert));
+
+  StartOrQueueTask(make_scoped_ptr(new PermissionUpdateTask(
+      SignPermission::UNLIMITED, true /* new permission value */,
+      public_key_spki_der, extension_id,
+      base::Bind(&PlatformKeysService::TaskFinished, base::Unretained(this)),
+      this)));
 }
 
 void PlatformKeysService::GenerateRSAKey(const std::string& token_id,
@@ -326,15 +686,13 @@
 
 void PlatformKeysService::SelectClientCertificates(
     const platform_keys::ClientCertificateRequest& request,
+    bool interactive,
     const std::string& extension_id,
-    const SelectCertificatesCallback& callback) {
+    const SelectCertificatesCallback& callback,
+    content::WebContents* web_contents) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-
-  platform_keys::subtle::SelectClientCertificates(
-      request,
-      base::Bind(&PlatformKeysService::SelectClientCertificatesCallback,
-                 weak_factory_.GetWeakPtr(), extension_id, callback),
-      browser_context_);
+  StartOrQueueTask(make_scoped_ptr(new SelectTask(
+      request, interactive, extension_id, callback, web_contents, this)));
 }
 
 void PlatformKeysService::StartOrQueueTask(scoped_ptr<Task> task) {
@@ -367,9 +725,9 @@
 
 void PlatformKeysService::SetPlatformKeysOfExtension(
     const std::string& extension_id,
-    scoped_ptr<base::ListValue> platform_keys) {
+    const KeyEntries& platform_keys) {
   state_store_->SetExtensionValue(extension_id, kStateStorePlatformKeys,
-                                  platform_keys.Pass());
+                                  KeyEntriesToState(platform_keys));
 }
 
 void PlatformKeysService::GeneratedKey(const std::string& extension_id,
@@ -382,7 +740,8 @@
   }
 
   StartOrQueueTask(make_scoped_ptr(new PermissionUpdateTask(
-      true /* new permission value */, public_key_spki_der, extension_id,
+      SignPermission::ONCE, true /* new permission value */,
+      public_key_spki_der, extension_id,
       base::Bind(&PlatformKeysService::RegisteredGeneratedKey,
                  base::Unretained(this), callback, public_key_spki_der),
       this)));
@@ -396,35 +755,16 @@
   TaskFinished(task);
 }
 
-void PlatformKeysService::SelectClientCertificatesCallback(
-    const std::string& extension_id,
-    const SelectCertificatesCallback& callback,
-    scoped_ptr<net::CertificateList> matches,
-    const std::string& error_message) {
-  if (permission_check_enabled_)
-    matches->clear();
-
-  // TODO(pneubeck): Remove all certs that the extension doesn't have access to.
-  callback.Run(matches.Pass(), error_message);
-}
 
 void PlatformKeysService::GotPlatformKeysOfExtension(
     const std::string& extension_id,
     const GetPlatformKeysCallback& callback,
     scoped_ptr<base::Value> value) {
-  if (!value)
-    value.reset(new base::ListValue);
+  scoped_ptr<KeyEntries> key_entries(new KeyEntries);
+  if (value)
+    key_entries = KeyEntriesFromState(*value);
 
-  base::ListValue* keys = NULL;
-  if (!value->GetAsList(&keys)) {
-    LOG(ERROR) << "Found a value of wrong type.";
-
-    keys = new base::ListValue;
-    value.reset(keys);
-  }
-
-  ignore_result(value.release());
-  callback.Run(make_scoped_ptr(keys));
+  callback.Run(key_entries.Pass());
 }
 
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/platform_keys/platform_keys_service.h b/chrome/browser/chromeos/platform_keys/platform_keys_service.h
index 71adf3b..23d44d3 100644
--- a/chrome/browser/chromeos/platform_keys/platform_keys_service.h
+++ b/chrome/browser/chromeos/platform_keys/platform_keys_service.h
@@ -19,6 +19,7 @@
 
 namespace content {
 class BrowserContext;
+class WebContents;
 }
 
 namespace base {
@@ -39,6 +40,39 @@
 
 class PlatformKeysService : public KeyedService {
  public:
+  struct KeyEntry;
+  using KeyEntries = std::vector<KeyEntry>;
+
+  // The SelectDelegate is used to select a single certificate from all
+  // certificates matching a request (see SelectClientCertificates). E.g. this
+  // can happen by exposing UI to let the user select.
+  class SelectDelegate {
+   public:
+    using CertificateSelectedCallback = base::Callback<void(
+        const scoped_refptr<net::X509Certificate>& selection)>;
+
+    SelectDelegate();
+    virtual ~SelectDelegate();
+
+    // Called on an interactive SelectClientCertificates call with the list of
+    // matching certificates, |certs|.
+    // The certificate passed to |callback| will be forwarded to the
+    // calling extension and the extension will get unlimited sign permission
+    // for this cert. By passing null to |callback|, no cert will be selected.
+    // Must eventually call |callback| or be destructed. |callback| must not be
+    // called after this delegate is destructed.
+    // |web_contents| and |context| provide the context in which the
+    // certificates were requested and are not null.
+    virtual void Select(const std::string& extension_id,
+                        const net::CertificateList& certs,
+                        const CertificateSelectedCallback& callback,
+                        content::WebContents* web_contents,
+                        content::BrowserContext* context) = 0;
+
+   private:
+    DISALLOW_ASSIGN(SelectDelegate);
+  };
+
   // Stores registration information in |state_store|, i.e. for each extension
   // the list of public keys that are valid to be used for signing. Each key can
   // be used for signing at most once.
@@ -50,18 +84,22 @@
                                extensions::StateStore* state_store);
   ~PlatformKeysService() override;
 
-  // Disables the checks whether an extension is allowed to read client
-  // certificates or allowed to use the signing function of a key.
-  // TODO(pneubeck): Remove this once a permissions are implemented.
-  void DisablePermissionCheckForTesting();
+  // Sets the delegate which will be used for interactive
+  // SelectClientCertificates calls.
+  void SetSelectDelegate(scoped_ptr<SelectDelegate> delegate);
+
+  // Grants unlimited sign permission for |cert| to the extension with the ID
+  // |extension_id|.
+  void GrantUnlimitedSignPermission(const std::string& extension_id,
+                                    scoped_refptr<net::X509Certificate> cert);
 
   // If the generation was successful, |public_key_spki_der| will contain the
   // DER encoding of the SubjectPublicKeyInfo of the generated key and
   // |error_message| will be empty. If it failed, |public_key_spki_der| will be
   // empty and |error_message| contain an error message.
-  typedef base::Callback<void(const std::string& public_key_spki_der,
-                              const std::string& error_message)>
-      GenerateKeyCallback;
+  using GenerateKeyCallback =
+      base::Callback<void(const std::string& public_key_spki_der,
+                          const std::string& error_message)>;
 
   // Generates an RSA key pair with |modulus_length_bits| and registers the key
   // to allow a single sign operation by the given extension. |token_id| is
@@ -77,8 +115,8 @@
   // If signing was successful, |signature| will be contain the signature and
   // |error_message| will be empty. If it failed, |signature| will be empty and
   // |error_message| contain an error message.
-  typedef base::Callback<void(const std::string& signature,
-                              const std::string& error_message)> SignCallback;
+  using SignCallback = base::Callback<void(const std::string& signature,
+                                           const std::string& error_message)>;
 
   // Digests |data|, applies PKCS1 padding and afterwards signs the data with
   // the private key matching |params.public_key|. If a non empty token id is
@@ -118,27 +156,37 @@
   // contain the list of matching certificates (maybe empty) and |error_message|
   // will be empty. If an error occurred, |matches| will be null and
   // |error_message| contain an error message.
-  typedef base::Callback<void(scoped_ptr<net::CertificateList> matches,
-                              const std::string& error_message)>
-      SelectCertificatesCallback;
+  using SelectCertificatesCallback =
+      base::Callback<void(scoped_ptr<net::CertificateList> matches,
+                          const std::string& error_message)>;
 
-  // Returns the list of all certificates that match |request|. |callback| will
-  // be invoked with these matches or an error message.
+  // Returns a list of certificates matching |request|.
+  // 1) all certificates that match the request (like being rooted in one of the
+  // give CAs) are determined. 2) if |interactive| is true, the currently set
+  // SelectDelegate is used to select a single certificate from these matches
+  // which will the extension will also be granted access to. 3) only
+  // certificates, that the extension has unlimited sign permission for, will be
+  // returned.
+  // |callback| will be invoked with these certificates or an error message.
   // Will only call back during the lifetime of this object.
-  // TODO(pneubeck): Add the interactive option and integrate the select
-  // certificate dialog.
+  // |web_contents| must not be null.
   void SelectClientCertificates(
       const platform_keys::ClientCertificateRequest& request,
+      bool interactive,
       const std::string& extension_id,
-      const SelectCertificatesCallback& callback);
+      const SelectCertificatesCallback& callback,
+      content::WebContents* web_contents);
 
  private:
   using GetPlatformKeysCallback =
-      base::Callback<void(scoped_ptr<base::ListValue> platform_keys)>;
+      base::Callback<void(scoped_ptr<KeyEntries> platform_keys)>;
 
-  class Task;
-  class SignTask;
+  enum SignPermission { ONCE, UNLIMITED };
+
   class PermissionUpdateTask;
+  class SelectTask;
+  class SignTask;
+  class Task;
 
   // Starts |task| eventually. To ensure that at most one |Task| is running at a
   // time, it queues |task| for later execution if necessary.
@@ -159,7 +207,7 @@
   // Writes |platform_keys| to the state store of the extension with id
   // |extension_id|.
   void SetPlatformKeysOfExtension(const std::string& extension_id,
-                                  scoped_ptr<base::ListValue> platform_keys);
+                                  const KeyEntries& platform_keys);
 
   // Callback used by |GenerateRSAKey|.
   // If the key generation was successful, registers the generated public key
@@ -179,17 +227,6 @@
                               const std::string& public_key_spki_der,
                               Task* task);
 
-  // Calback used by |SelectClientCertificates|.
-  // If the certificate request could be processed successfully, |matches| will
-  // contain the list of matching certificates (maybe empty) and |error_message|
-  // will be empty. If an error occurred, |matches| will be null and
-  // |error_message| contain an error message.
-  void SelectClientCertificatesCallback(
-      const std::string& extension_id,
-      const SelectCertificatesCallback& callback,
-      scoped_ptr<net::CertificateList> matches,
-      const std::string& error_message);
-
   // Callback used by |GetPlatformKeysOfExtension|.
   // Is called with |value| set to the PlatformKeys value read from the
   // StateStore, which it forwards to |callback|. On error, calls |callback|
@@ -200,7 +237,7 @@
 
   content::BrowserContext* browser_context_;
   extensions::StateStore* state_store_;
-  bool permission_check_enabled_ = true;
+  scoped_ptr<SelectDelegate> select_delegate_;
   std::queue<linked_ptr<Task>> tasks_;
   base::WeakPtrFactory<PlatformKeysService> weak_factory_;
 
diff --git a/chrome/browser/chromeos/platform_keys/platform_keys_service_factory.cc b/chrome/browser/chromeos/platform_keys/platform_keys_service_factory.cc
index 8394c2f..5652156 100644
--- a/chrome/browser/chromeos/platform_keys/platform_keys_service_factory.cc
+++ b/chrome/browser/chromeos/platform_keys/platform_keys_service_factory.cc
@@ -4,14 +4,87 @@
 
 #include "chrome/browser/chromeos/platform_keys/platform_keys_service_factory.h"
 
+#include "base/bind.h"
+#include "base/callback.h"
 #include "base/logging.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
 #include "base/memory/singleton.h"
+#include "base/memory/weak_ptr.h"
 #include "chrome/browser/chromeos/platform_keys/platform_keys_service.h"
 #include "chrome/browser/extensions/extension_system_factory.h"
+#include "chrome/browser/policy/profile_policy_connector.h"
+#include "chrome/browser/policy/profile_policy_connector_factory.h"
 #include "chrome/browser/profiles/incognito_helpers.h"
+#include "chrome/browser/ui/platform_keys_certificate_selector_chromeos.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
+#include "extensions/browser/extension_registry.h"
+#include "extensions/common/extension.h"
+#include "net/cert/x509_certificate.h"
 
 namespace chromeos {
+namespace {
+
+// This SelectDelegate always selects no certificate.
+class NoOpSelectDelegate
+    : public chromeos::PlatformKeysService::SelectDelegate {
+ public:
+  NoOpSelectDelegate() {}
+
+  void Select(const std::string& extension_id,
+              const net::CertificateList& certs,
+              const CertificateSelectedCallback& callback,
+              content::WebContents* web_contents,
+              content::BrowserContext* context) override {
+    callback.Run(nullptr);
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(NoOpSelectDelegate);
+};
+
+// This delegate selects a certificate by showing the certificate selection
+// dialog to the user.
+class DefaultSelectDelegate
+    : public chromeos::PlatformKeysService::SelectDelegate {
+ public:
+  DefaultSelectDelegate() : weak_factory_(this) {}
+  ~DefaultSelectDelegate() override {}
+
+  void Select(const std::string& extension_id,
+              const net::CertificateList& certs,
+              const CertificateSelectedCallback& callback,
+              content::WebContents* web_contents,
+              content::BrowserContext* context) override {
+    CHECK(web_contents);
+    const extensions::Extension* const extension =
+        extensions::ExtensionRegistry::Get(context)->GetExtensionById(
+            extension_id, extensions::ExtensionRegistry::ENABLED);
+    if (!extension) {
+      callback.Run(nullptr /* no certificate selected */);
+      return;
+    }
+    ShowPlatformKeysCertificateSelector(
+        web_contents, extension->short_name(), certs,
+        // Don't call |callback| once this delegate is destructed, thus use a
+        // WeakPtr.
+        base::Bind(&DefaultSelectDelegate::SelectedCertificate,
+                   weak_factory_.GetWeakPtr(), callback));
+  }
+
+  void SelectedCertificate(
+      const CertificateSelectedCallback& callback,
+      const scoped_refptr<net::X509Certificate>& selected_cert) {
+    callback.Run(selected_cert);
+  }
+
+ private:
+  base::WeakPtrFactory<DefaultSelectDelegate> weak_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(DefaultSelectDelegate);
+};
+
+}  // namespace
 
 // static
 PlatformKeysService* PlatformKeysServiceFactory::GetForBrowserContext(
@@ -42,10 +115,23 @@
 
 KeyedService* PlatformKeysServiceFactory::BuildServiceInstanceFor(
     content::BrowserContext* context) const {
-  extensions::StateStore* store =
+  extensions::StateStore* const store =
       extensions::ExtensionSystem::Get(context)->state_store();
   DCHECK(store);
-  return new PlatformKeysService(context, store);
+  PlatformKeysService* const service = new PlatformKeysService(context, store);
+
+  policy::ProfilePolicyConnector* const connector =
+      policy::ProfilePolicyConnectorFactory::GetForBrowserContext(context);
+  // Only allow the user to grant certificate permissions to extensions if the
+  // user is not managed by policy. Otherwise the user might leak access to
+  // (private keys of) certificates against the intentions of the administrator.
+  // TODO(pneubeck): Remove this once the respective policy is implemented.
+  //   https://crbug.com/460232
+  if (connector->IsManaged())
+    service->SetSelectDelegate(make_scoped_ptr(new NoOpSelectDelegate()));
+  else
+    service->SetSelectDelegate(make_scoped_ptr(new DefaultSelectDelegate()));
+  return service;
 }
 
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/platform_keys/platform_keys_service_factory.h b/chrome/browser/chromeos/platform_keys/platform_keys_service_factory.h
index b132d68..deb9479 100644
--- a/chrome/browser/chromeos/platform_keys/platform_keys_service_factory.h
+++ b/chrome/browser/chromeos/platform_keys/platform_keys_service_factory.h
@@ -5,16 +5,12 @@
 #ifndef CHROME_BROWSER_CHROMEOS_PLATFORM_KEYS_PLATFORM_KEYS_SERVICE_FACTORY_H_
 #define CHROME_BROWSER_CHROMEOS_PLATFORM_KEYS_PLATFORM_KEYS_SERVICE_FACTORY_H_
 
-#include "base/basictypes.h"
-#include "base/compiler_specific.h"
 #include "base/macros.h"
 #include "components/keyed_service/content/browser_context_keyed_service_factory.h"
 
 template <typename T>
 struct DefaultSingletonTraits;
 
-class Profile;
-
 namespace chromeos {
 
 class PlatformKeysService;
diff --git a/chrome/browser/chromeos/policy/affiliated_invalidation_service_provider_impl.cc b/chrome/browser/chromeos/policy/affiliated_invalidation_service_provider_impl.cc
index ad052be4..1a23a87 100644
--- a/chrome/browser/chromeos/policy/affiliated_invalidation_service_provider_impl.cc
+++ b/chrome/browser/chromeos/policy/affiliated_invalidation_service_provider_impl.cc
@@ -154,7 +154,7 @@
     // ignore it.
     return;
   }
-  user_manager::User* user =
+  const user_manager::User* user =
       chromeos::ProfileHelper::Get()->GetUserByProfile(profile);
   if (!user ||
       g_browser_process->platform_part()->browser_policy_connector_chromeos()->
@@ -209,7 +209,7 @@
 
   registrar_.RemoveAll();
   profile_invalidation_service_observers_.clear();
-  DestroyDeviceInvalidationService();
+  device_invalidation_service_observer_.reset();
 
   if (invalidation_service_) {
     invalidation_service_ = nullptr;
@@ -217,6 +217,8 @@
     // is no longer available.
     SetInvalidationService(nullptr);
   }
+
+  DestroyDeviceInvalidationService();
 }
 
 invalidation::TiclInvalidationService*
diff --git a/chrome/browser/chromeos/policy/affiliated_invalidation_service_provider_impl_unittest.cc b/chrome/browser/chromeos/policy/affiliated_invalidation_service_provider_impl_unittest.cc
index 071658f..dedc6b4 100644
--- a/chrome/browser/chromeos/policy/affiliated_invalidation_service_provider_impl_unittest.cc
+++ b/chrome/browser/chromeos/policy/affiliated_invalidation_service_provider_impl_unittest.cc
@@ -625,4 +625,36 @@
   EXPECT_FALSE(provider_->GetDeviceInvalidationServiceForTest());
 }
 
+// A consumer is registered with the AffiliatedInvalidationServiceProviderImpl.
+// A device-global invalidation service exists, is connected and is made
+// available to the consumer. Verifies that when the provider is shut down, the
+// consumer is informed that no invalidation service is available for use
+// anymore before the device-global invalidation service is destroyed.
+// This is a regression test for http://crbug.com/455504.
+TEST_F(AffiliatedInvalidationServiceProviderImplTest,
+       ConnectedDeviceGlobalInvalidationServiceOnShutdown) {
+  consumer_.reset(new FakeConsumer(provider_.get()));
+
+  // Verify that a device-global invalidation service has been created.
+  EXPECT_TRUE(provider_->GetDeviceInvalidationServiceForTest());
+
+  // Indicate that the device-global invalidation service connected. Verify that
+  // that the consumer is informed about this.
+  ConnectDeviceGlobalInvalidationService();
+
+  // Shut down the |provider_|. Verify that the |consumer_| is informed that no
+  // invalidation service is available for use anymore. This also serves as a
+  // regression test which verifies that the invalidation service is not
+  // destroyed until the |consumer_| has been informed: If the invalidation
+  // service was destroyed too early, the |consumer_| would still be registered
+  // as an observer and the invalidation service's destructor would DCHECK().
+  EXPECT_EQ(0, consumer_->GetAndClearInvalidationServiceSetCount());
+  provider_->Shutdown();
+  EXPECT_EQ(1, consumer_->GetAndClearInvalidationServiceSetCount());
+  EXPECT_EQ(nullptr, consumer_->GetInvalidationService());
+
+  // Verify that the device-global invalidation service has been destroyed.
+  EXPECT_FALSE(provider_->GetDeviceInvalidationServiceForTest());
+}
+
 }  // namespace policy
diff --git a/chrome/browser/chromeos/policy/cloud_external_data_policy_observer.cc b/chrome/browser/chromeos/policy/cloud_external_data_policy_observer.cc
index 68091f4..2c7f590 100644
--- a/chrome/browser/chromeos/policy/cloud_external_data_policy_observer.cc
+++ b/chrome/browser/chromeos/policy/cloud_external_data_policy_observer.cc
@@ -182,7 +182,7 @@
   }
 
   ProfilePolicyConnector* policy_connector =
-      ProfilePolicyConnectorFactory::GetForProfile(profile);
+      ProfilePolicyConnectorFactory::GetForBrowserContext(profile);
   logged_in_user_observers_[user_id] = make_linked_ptr(
       new PolicyServiceObserver(this,
                                 user_id,
diff --git a/chrome/browser/chromeos/policy/device_local_account_browsertest.cc b/chrome/browser/chromeos/policy/device_local_account_browsertest.cc
index 5cfd1fa5..d69d4b6 100644
--- a/chrome/browser/chromeos/policy/device_local_account_browsertest.cc
+++ b/chrome/browser/chromeos/policy/device_local_account_browsertest.cc
@@ -1357,7 +1357,7 @@
   // Verify that the external data reference has propagated to the device-local
   // account's ProfilePolicyConnector.
   ProfilePolicyConnector* policy_connector =
-      ProfilePolicyConnectorFactory::GetForProfile(GetProfileForTest());
+      ProfilePolicyConnectorFactory::GetForBrowserContext(GetProfileForTest());
   ASSERT_TRUE(policy_connector);
   const PolicyMap& policies = policy_connector->policy_service()->GetPolicies(
       PolicyNamespace(POLICY_DOMAIN_CHROME, std::string()));
@@ -2214,7 +2214,7 @@
 
   // Wait for the app policy if it hasn't been fetched yet.
   ProfilePolicyConnector* connector =
-      ProfilePolicyConnectorFactory::GetForProfile(profile);
+      ProfilePolicyConnectorFactory::GetForBrowserContext(profile);
   ASSERT_TRUE(connector);
   PolicyService* policy_service = connector->policy_service();
   ASSERT_TRUE(policy_service);
diff --git a/chrome/browser/chromeos/policy/policy_cert_service_factory.cc b/chrome/browser/chromeos/policy/policy_cert_service_factory.cc
index b0bee1c..4955bc0 100644
--- a/chrome/browser/chromeos/policy/policy_cert_service_factory.cc
+++ b/chrome/browser/chromeos/policy/policy_cert_service_factory.cc
@@ -95,8 +95,9 @@
   Profile* profile = static_cast<Profile*>(context);
 
   user_manager::UserManager* user_manager = user_manager::UserManager::Get();
-  user_manager::User* user = chromeos::ProfileHelper::Get()->GetUserByProfile(
-      profile->GetOriginalProfile());
+  const user_manager::User* user =
+      chromeos::ProfileHelper::Get()->GetUserByProfile(
+          profile->GetOriginalProfile());
   if (!user)
     return NULL;
 
diff --git a/chrome/browser/chromeos/policy/power_policy_browsertest.cc b/chrome/browser/chromeos/policy/power_policy_browsertest.cc
index c8ae290e..37b89884 100644
--- a/chrome/browser/chromeos/policy/power_policy_browsertest.cc
+++ b/chrome/browser/chromeos/policy/power_policy_browsertest.cc
@@ -258,7 +258,8 @@
       .WillOnce(InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit));
   EXPECT_CALL(observer, OnPolicyServiceInitialized(_)).Times(AnyNumber());
   PolicyService* policy_service =
-      ProfilePolicyConnectorFactory::GetForProfile(profile)->policy_service();
+      ProfilePolicyConnectorFactory::GetForBrowserContext(profile)
+          ->policy_service();
   ASSERT_TRUE(policy_service);
   policy_service->AddObserver(POLICY_DOMAIN_CHROME, &observer);
   closure.Run();
diff --git a/chrome/browser/chromeos/policy/user_cloud_external_data_manager_browsertest.cc b/chrome/browser/chromeos/policy/user_cloud_external_data_manager_browsertest.cc
index 0a92665..3112502e 100644
--- a/chrome/browser/chromeos/policy/user_cloud_external_data_manager_browsertest.cc
+++ b/chrome/browser/chromeos/policy/user_cloud_external_data_manager_browsertest.cc
@@ -77,7 +77,7 @@
   content::RunAllPendingInMessageLoop();
 
   ProfilePolicyConnector* policy_connector =
-      ProfilePolicyConnectorFactory::GetForProfile(browser()->profile());
+      ProfilePolicyConnectorFactory::GetForBrowserContext(browser()->profile());
   ASSERT_TRUE(policy_connector);
   const PolicyMap& policies = policy_connector->policy_service()->GetPolicies(
       PolicyNamespace(POLICY_DOMAIN_CHROME, std::string()));
diff --git a/chrome/browser/chromeos/policy/user_cloud_policy_manager_factory_chromeos.cc b/chrome/browser/chromeos/policy/user_cloud_policy_manager_factory_chromeos.cc
index 264692d..97fb5f7b 100644
--- a/chrome/browser/chromeos/policy/user_cloud_policy_manager_factory_chromeos.cc
+++ b/chrome/browser/chromeos/policy/user_cloud_policy_manager_factory_chromeos.cc
@@ -125,7 +125,7 @@
   // |user| should never be NULL except for the signin profile. This object is
   // created as part of the Profile creation, which happens right after
   // sign-in. The just-signed-in User is the active user during that time.
-  user_manager::User* user =
+  const user_manager::User* user =
       chromeos::ProfileHelper::Get()->GetUserByProfile(profile);
   CHECK(user);
 
diff --git a/chrome/browser/chromeos/policy/user_network_configuration_updater_factory.cc b/chrome/browser/chromeos/policy/user_network_configuration_updater_factory.cc
index 876153fbe..9d66ff0d 100644
--- a/chrome/browser/chromeos/policy/user_network_configuration_updater_factory.cc
+++ b/chrome/browser/chromeos/policy/user_network_configuration_updater_factory.cc
@@ -60,11 +60,11 @@
 
 KeyedService* UserNetworkConfigurationUpdaterFactory::BuildServiceInstanceFor(
     content::BrowserContext* context) const {
-  Profile* profile = static_cast<Profile*>(context);
+  Profile* profile = Profile::FromBrowserContext(context);
   if (chromeos::ProfileHelper::IsSigninProfile(profile))
     return NULL;  // On the login screen only device network policies apply.
 
-  user_manager::User* user =
+  const user_manager::User* user =
       chromeos::ProfileHelper::Get()->GetUserByProfile(profile);
   DCHECK(user);
   // Currently, only the network policy of the primary user is supported. See
@@ -75,7 +75,7 @@
   const bool allow_trusted_certs_from_policy = user->HasGaiaAccount();
 
   ProfilePolicyConnector* profile_connector =
-      ProfilePolicyConnectorFactory::GetForProfile(profile);
+      ProfilePolicyConnectorFactory::GetForBrowserContext(context);
 
   return UserNetworkConfigurationUpdater::CreateForUserPolicy(
       profile,
diff --git a/chrome/browser/chromeos/preferences.cc b/chrome/browser/chromeos/preferences.cc
index 6334a02..32babb7c 100644
--- a/chrome/browser/chromeos/preferences.cc
+++ b/chrome/browser/chromeos/preferences.cc
@@ -89,7 +89,7 @@
                                 false);
   registry->RegisterStringPref(prefs::kLogoutStartedLast, std::string());
   registry->RegisterBooleanPref(prefs::kResolveDeviceTimezoneByGeolocation,
-                                false);
+                                true);
 }
 
 // static
@@ -319,7 +319,7 @@
       user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
 
   registry->RegisterBooleanPref(
-      prefs::kResolveTimezoneByGeolocation, false,
+      prefs::kResolveTimezoneByGeolocation, true,
       user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
 
   input_method::InputMethodSyncer::RegisterProfilePrefs(registry);
diff --git a/chrome/browser/chromeos/profiles/profile_helper.cc b/chrome/browser/chromeos/profiles/profile_helper.cc
index 79337681..9f78be2 100644
--- a/chrome/browser/chromeos/profiles/profile_helper.cc
+++ b/chrome/browser/chromeos/profiles/profile_helper.cc
@@ -109,7 +109,7 @@
 }
 
 // static
-std::string ProfileHelper::GetUserIdHashFromProfile(Profile* profile) {
+std::string ProfileHelper::GetUserIdHashFromProfile(const Profile* profile) {
   if (!profile)
     return std::string();
 
@@ -140,7 +140,7 @@
 }
 
 // static
-bool ProfileHelper::IsSigninProfile(Profile* profile) {
+bool ProfileHelper::IsSigninProfile(const Profile* profile) {
   return profile->GetPath().BaseName().value() == chrome::kInitialProfile;
 }
 
@@ -148,7 +148,8 @@
 bool ProfileHelper::IsOwnerProfile(Profile* profile) {
   if (!profile)
     return false;
-  user_manager::User* user = ProfileHelper::Get()->GetUserByProfile(profile);
+  const user_manager::User* user =
+      ProfileHelper::Get()->GetUserByProfile(profile);
   if (!user)
     return false;
 
@@ -156,10 +157,11 @@
 }
 
 // static
-bool ProfileHelper::IsPrimaryProfile(Profile* profile) {
+bool ProfileHelper::IsPrimaryProfile(const Profile* profile) {
   if (!profile)
     return false;
-  user_manager::User* user = ProfileHelper::Get()->GetUserByProfile(profile);
+  const user_manager::User* user =
+      ProfileHelper::Get()->GetUserByProfile(profile);
   if (!user)
     return false;
   return user == user_manager::UserManager::Get()->GetPrimaryUser();
@@ -260,12 +262,12 @@
   return profile;
 }
 
-user_manager::User* ProfileHelper::GetUserByProfile(Profile* profile) {
+const user_manager::User* ProfileHelper::GetUserByProfile(
+    const Profile* profile) const {
   // This map is non-empty only in tests.
   if (enable_profile_to_user_testing || !user_list_for_testing_.empty()) {
     if (always_return_primary_user_for_testing)
-      return const_cast<user_manager::User*>(
-          user_manager::UserManager::Get()->GetPrimaryUser());
+      return user_manager::UserManager::Get()->GetPrimaryUser();
 
     const std::string& user_name = profile->GetProfileUserName();
     for (user_manager::UserList::const_iterator it =
@@ -277,8 +279,7 @@
     }
 
     // In case of test setup we should always default to primary user.
-    return const_cast<user_manager::User*>(
-        user_manager::UserManager::Get()->GetPrimaryUser());
+    return user_manager::UserManager::Get()->GetPrimaryUser();
   }
 
   DCHECK(!content::BrowserThread::IsThreadInitialized(
@@ -314,10 +315,15 @@
   return active_user &&
                  ProfileHelper::GetProfilePathByUserIdHash(
                      active_user->username_hash()) == profile->GetPath()
-             ? const_cast<user_manager::User*>(active_user)
+             ? active_user
              : NULL;
 }
 
+user_manager::User* ProfileHelper::GetUserByProfile(Profile* profile) const {
+  return const_cast<user_manager::User*>(
+      GetUserByProfile(static_cast<const Profile*>(profile)));
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // ProfileHelper, BrowsingDataRemover::Observer implementation:
 
diff --git a/chrome/browser/chromeos/profiles/profile_helper.h b/chrome/browser/chromeos/profiles/profile_helper.h
index e15475fc..a7a078c1 100644
--- a/chrome/browser/chromeos/profiles/profile_helper.h
+++ b/chrome/browser/chromeos/profiles/profile_helper.h
@@ -67,7 +67,7 @@
 
   // Returns user_id hash for |profile| instance or empty string if hash
   // could not be extracted from |profile|.
-  static std::string GetUserIdHashFromProfile(Profile* profile);
+  static std::string GetUserIdHashFromProfile(const Profile* profile);
 
   // Returns user profile dir in a format [u-user_id_hash].
   static base::FilePath GetUserProfileDir(const std::string& user_id_hash);
@@ -75,14 +75,14 @@
   // Returns true if |profile| is the signin Profile. This can be used during
   // construction of the signin Profile to determine if that Profile is the
   // signin Profile.
-  static bool IsSigninProfile(Profile* profile);
+  static bool IsSigninProfile(const Profile* profile);
 
   // Returns true when |profile| corresponds to owner's profile.
   static bool IsOwnerProfile(Profile* profile);
 
   // Returns true when |profile| corresponds to the primary user profile
   // of the current session.
-  static bool IsPrimaryProfile(Profile* profile);
+  static bool IsPrimaryProfile(const Profile* profile);
 
   // Initialize a bunch of services that are tied to a browser profile.
   // TODO(dzhioev): Investigate whether or not this method is needed.
@@ -117,7 +117,8 @@
   Profile* GetProfileByUserUnsafe(const user_manager::User* user);
 
   // Returns NULL if User is not created.
-  user_manager::User* GetUserByProfile(Profile* profile);
+  const user_manager::User* GetUserByProfile(const Profile* profile) const;
+  user_manager::User* GetUserByProfile(Profile* profile) const;
 
   static std::string GetUserIdHashByUserIdForTesting(
       const std::string& user_id);
diff --git a/chrome/browser/chromeos/proxy_config_service_impl.cc b/chrome/browser/chromeos/proxy_config_service_impl.cc
index 9134736..8b59c5d 100644
--- a/chrome/browser/chromeos/proxy_config_service_impl.cc
+++ b/chrome/browser/chromeos/proxy_config_service_impl.cc
@@ -140,6 +140,13 @@
     VLOG(1) << "Respect proxy of not-shared networks.";
     return false;
   }
+  if (onc_source == ::onc::ONC_SOURCE_USER_POLICY) {
+    // Note that this case can occur if the network is shared (e.g. ethernet)
+    // but the proxy is determined by user policy.
+    // See https://crbug.com/454966 .
+    VLOG(1) << "Respect proxy from user policy although network is shared.";
+    return false;
+  }
   if (onc_source == ::onc::ONC_SOURCE_DEVICE_POLICY) {
     policy::BrowserPolicyConnectorChromeOS* connector =
         g_browser_process->platform_part()->browser_policy_connector_chromeos();
diff --git a/chrome/browser/chromeos/proxy_config_service_impl_unittest.cc b/chrome/browser/chromeos/proxy_config_service_impl_unittest.cc
index 6783891..785c48a 100644
--- a/chrome/browser/chromeos/proxy_config_service_impl_unittest.cc
+++ b/chrome/browser/chromeos/proxy_config_service_impl_unittest.cc
@@ -12,6 +12,7 @@
 #include "base/message_loop/message_loop.h"
 #include "base/prefs/testing_pref_service.h"
 #include "base/strings/stringprintf.h"
+#include "chrome/browser/chromeos/net/proxy_config_handler.h"
 #include "chrome/browser/chromeos/settings/cros_settings.h"
 #include "chrome/browser/chromeos/settings/device_settings_service.h"
 #include "chrome/browser/chromeos/ui_proxy_config.h"
@@ -20,9 +21,13 @@
 #include "chromeos/dbus/shill_profile_client.h"
 #include "chromeos/dbus/shill_service_client.h"
 #include "chromeos/network/network_handler.h"
+#include "chromeos/network/network_profile_handler.h"
 #include "chromeos/network/network_state.h"
 #include "chromeos/network/network_state_handler.h"
+#include "chromeos/network/onc/onc_utils.h"
+#include "components/pref_registry/testing_pref_service_syncable.h"
 #include "content/public/test/test_browser_thread.h"
+#include "net/proxy/proxy_config.h"
 #include "net/proxy/proxy_config_service_common_unittest.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/cros_system_api/dbus/service_constants.h"
@@ -204,6 +209,17 @@
   },
 };  // tests
 
+const char kEthernetPolicy[] =
+    "    { \"GUID\": \"{485d6076-dd44-6b6d-69787465725f5040}\","
+    "      \"Type\": \"Ethernet\","
+    "      \"Name\": \"MyEthernet\","
+    "      \"Ethernet\": {"
+    "        \"Authentication\": \"None\" },"
+    "      \"ProxySettings\": {"
+    "        \"PAC\": \"http://domain.com/x\","
+    "        \"Type\": \"PAC\" }"
+    "    }";
+
 const char kUserProfilePath[] = "user_profile";
 
 }  // namespace
@@ -218,14 +234,15 @@
     DBusThreadManager::Initialize();
     NetworkHandler::Initialize();
 
-    SetUpNetwork();
-
     PrefProxyConfigTrackerImpl::RegisterPrefs(pref_service_.registry());
+    chromeos::proxy_config::RegisterPrefs(pref_service_.registry());
+    PrefProxyConfigTrackerImpl::RegisterProfilePrefs(profile_prefs_.registry());
+    chromeos::proxy_config::RegisterProfilePrefs(profile_prefs_.registry());
+  }
 
-    // Create a ProxyConfigServiceImpl like for the system request context.
+  void SetUpProxyConfigService(PrefService* profile_prefs) {
     config_service_impl_.reset(
-        new ProxyConfigServiceImpl(NULL,  // no profile prefs
-                                   &pref_service_));
+        new ProxyConfigServiceImpl(profile_prefs, &pref_service_));
     proxy_config_service_ =
         config_service_impl_->CreateTrackingProxyConfigService(
             scoped_ptr<net::ProxyConfigService>());
@@ -236,7 +253,7 @@
     loop_.RunUntilIdle();
   }
 
-  void SetUpNetwork() {
+  void SetUpPrivateWiFi() {
     ShillProfileClient::TestInterface* profile_test =
         DBusThreadManager::Get()->GetShillProfileClient()->GetTestInterface();
     ShillServiceClient::TestInterface* service_test =
@@ -259,6 +276,28 @@
     loop_.RunUntilIdle();
   }
 
+  void SetUpSharedEthernet() {
+    ShillProfileClient::TestInterface* profile_test =
+        DBusThreadManager::Get()->GetShillProfileClient()->GetTestInterface();
+    ShillServiceClient::TestInterface* service_test =
+        DBusThreadManager::Get()->GetShillServiceClient()->GetTestInterface();
+
+    // Process any pending notifications before clearing services.
+    loop_.RunUntilIdle();
+    service_test->ClearServices();
+
+    // Sends a notification about the added profile.
+    profile_test->AddProfile(kUserProfilePath, "user_hash");
+
+    service_test->AddService("/service/ethernet", "stub_ethernet" /* guid */,
+                             "eth0", shill::kTypeEthernet, shill::kStateOnline,
+                             true /* visible */);
+    profile_test->AddService(NetworkProfileHandler::GetSharedProfilePath(),
+                             "/service/ethernet");
+
+    loop_.RunUntilIdle();
+  }
+
   void TearDown() override {
     config_service_impl_->DetachFromPrefService();
     loop_.RunUntilIdle();
@@ -270,30 +309,28 @@
 
   void InitConfigWithTestInput(const Input& input,
                                base::DictionaryValue* result) {
-    base::DictionaryValue* new_config = NULL;
+    scoped_ptr<base::DictionaryValue> new_config;
     switch (input.mode) {
       case MK_MODE(DIRECT):
-        new_config = ProxyConfigDictionary::CreateDirect();
+        new_config.reset(ProxyConfigDictionary::CreateDirect());
         break;
       case MK_MODE(AUTO_DETECT):
-        new_config = ProxyConfigDictionary::CreateAutoDetect();
+        new_config.reset(ProxyConfigDictionary::CreateAutoDetect());
         break;
       case MK_MODE(PAC_SCRIPT):
-        new_config =
-            ProxyConfigDictionary::CreatePacScript(input.pac_url, false);
+        new_config.reset(
+            ProxyConfigDictionary::CreatePacScript(input.pac_url, false));
         break;
       case MK_MODE(SINGLE_PROXY):
       case MK_MODE(PROXY_PER_SCHEME):
-        new_config =
-          ProxyConfigDictionary::CreateFixedServers(input.server,
-                                                    input.bypass_rules);
+        new_config.reset(ProxyConfigDictionary::CreateFixedServers(
+            input.server, input.bypass_rules));
         break;
     }
-    result->Swap(new_config);
-    delete new_config;
+    result->Swap(new_config.get());
   }
 
-  void SetConfig(base::DictionaryValue* pref_proxy_config_dict) {
+  void SetUserConfigInShill(base::DictionaryValue* pref_proxy_config_dict) {
     std::string proxy_config;
     if (pref_proxy_config_dict)
       base::JSONWriter::Write(pref_proxy_config_dict, &proxy_config);
@@ -325,6 +362,7 @@
   scoped_ptr<net::ProxyConfigService> proxy_config_service_;
   scoped_ptr<ProxyConfigServiceImpl> config_service_impl_;
   TestingPrefServiceSimple pref_service_;
+  user_prefs::TestingPrefServiceSyncable profile_prefs_;
 
  private:
   ScopedTestDeviceSettingsService test_device_settings_service_;
@@ -334,13 +372,16 @@
 };
 
 TEST_F(ProxyConfigServiceImplTest, NetworkProxy) {
+  SetUpPrivateWiFi();
+  // Create a ProxyConfigServiceImpl like for the system request context.
+  SetUpProxyConfigService(nullptr /* no profile prefs */);
   for (size_t i = 0; i < arraysize(tests); ++i) {
     SCOPED_TRACE(base::StringPrintf("Test[%" PRIuS "] %s", i,
                                     tests[i].description.c_str()));
 
     base::DictionaryValue test_config;
     InitConfigWithTestInput(tests[i].input, &test_config);
-    SetConfig(&test_config);
+    SetUserConfigInShill(&test_config);
 
     net::ProxyConfig config;
     SyncGetLatestProxyConfig(&config);
@@ -352,6 +393,9 @@
 }
 
 TEST_F(ProxyConfigServiceImplTest, DynamicPrefsOverride) {
+  SetUpPrivateWiFi();
+  // Create a ProxyConfigServiceImpl like for the system request context.
+  SetUpProxyConfigService(nullptr /* no profile prefs */);
   // Groupings of 3 test inputs to use for managed, recommended and network
   // proxies respectively.  Only valid and non-direct test inputs are used.
   const size_t proxies[][3] = {
@@ -396,7 +440,7 @@
 
     // Managed proxy pref should take effect over recommended proxy and
     // non-existent network proxy.
-    SetConfig(NULL);
+    SetUserConfigInShill(nullptr);
     pref_service_.SetManagedPref(prefs::kProxy, managed_config.DeepCopy());
     pref_service_.SetRecommendedPref(prefs::kProxy,
                                      recommended_config.DeepCopy());
@@ -417,7 +461,7 @@
         actual_config.proxy_rules()));
 
     // Network proxy should take take effect over recommended proxy pref.
-    SetConfig(&network_config);
+    SetUserConfigInShill(&network_config);
     SyncGetLatestProxyConfig(&actual_config);
     EXPECT_EQ(network_params.auto_detect, actual_config.auto_detect());
     EXPECT_EQ(network_params.pac_url, actual_config.pac_url());
@@ -451,4 +495,29 @@
   }
 }
 
+// Tests whether the proxy settings from user policy are used for ethernet even
+// if 'UseSharedProxies' is set to false.
+// See https://crbug.com/454966 .
+TEST_F(ProxyConfigServiceImplTest, SharedEthernetAndUserPolicy) {
+  SetUpSharedEthernet();
+  SetUpProxyConfigService(&profile_prefs_);
+
+  scoped_ptr<base::DictionaryValue> ethernet_policy(
+      chromeos::onc::ReadDictionaryFromJson(kEthernetPolicy));
+
+  scoped_ptr<base::ListValue> network_configs(new base::ListValue);
+  network_configs->Append(ethernet_policy.release());
+
+  profile_prefs_.SetUserPref(prefs::kUseSharedProxies,
+                             new base::FundamentalValue(false));
+  profile_prefs_.SetManagedPref(prefs::kOpenNetworkConfiguration,
+                                network_configs.release());
+
+  net::ProxyConfig actual_config;
+  SyncGetLatestProxyConfig(&actual_config);
+  net::ProxyConfig expected_config =
+      net::ProxyConfig::CreateFromCustomPacURL(GURL("http://domain.com/x"));
+  EXPECT_TRUE(expected_config.Equals(actual_config));
+}
+
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/system/device_disabling_browsertest.cc b/chrome/browser/chromeos/system/device_disabling_browsertest.cc
index a7ec074..1470800 100644
--- a/chrome/browser/chromeos/system/device_disabling_browsertest.cc
+++ b/chrome/browser/chromeos/system/device_disabling_browsertest.cc
@@ -2,38 +2,74 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "base/command_line.h"
+#include <string>
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
 #include "base/memory/scoped_ptr.h"
 #include "base/run_loop.h"
+#include "chrome/browser/chromeos/login/test/oobe_base_test.h"
+#include "chrome/browser/chromeos/login/test/oobe_screen_waiter.h"
+#include "chrome/browser/chromeos/login/ui/login_display_host_impl.h"
+#include "chrome/browser/chromeos/login/ui/oobe_display.h"
+#include "chrome/browser/chromeos/login/ui/webui_login_view.h"
 #include "chrome/browser/chromeos/login/wizard_controller.h"
 #include "chrome/browser/chromeos/policy/device_policy_builder.h"
 #include "chrome/browser/chromeos/policy/device_policy_cros_browser_test.h"
+#include "chrome/browser/chromeos/profiles/profile_helper.h"
 #include "chrome/browser/chromeos/settings/cros_settings.h"
-#include "chrome/test/base/in_process_browser_test.h"
-#include "chromeos/chromeos_switches.h"
+#include "chrome/browser/ui/webui/chromeos/login/network_state_informer.h"
+#include "chrome/browser/ui/webui/chromeos/login/oobe_ui.h"
+#include "chrome/browser/ui/webui/chromeos/login/signin_screen_handler.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/fake_session_manager_client.h"
+#include "chromeos/dbus/fake_shill_manager_client.h"
+#include "chromeos/dbus/session_manager_client.h"
+#include "chromeos/dbus/shill_manager_client.h"
+#include "chromeos/dbus/shill_service_client.h"
 #include "chromeos/settings/cros_settings_names.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/test/browser_test_utils.h"
+#include "dbus/object_path.h"
 #include "policy/proto/device_management_backend.pb.h"
+#include "testing/gtest/include/gtest/gtest.h"
 
 namespace chromeos {
 namespace system {
 
-class DeviceDisablingTest : public InProcessBrowserTest {
+namespace {
+
+void ErrorCallbackFunction(const std::string& error_name,
+                           const std::string& error_message) {
+  LOG(ERROR) << "Shill Error: " << error_name << " : " << error_message;
+}
+
+}  // namespace
+
+class DeviceDisablingTest
+    : public OobeBaseTest,
+      public NetworkStateInformer::NetworkStateInformerObserver {
  public:
   DeviceDisablingTest();
 
-  // Device policy is updated in two steps:
-  // - First, set up the policy builder that GetDevicePolicyBuilder() returns.
-  // - Second, call SimulatePolicyFetch() to build and flush the resulting
-  //   policy blob to the browser.
-  policy::DevicePolicyBuilder* GetDevicePolicyBuilder();
-  void SimulatePolicyFetch();
+  // Sets up a device state blob that indicates the device is disabled, triggers
+  // a policy plus device state fetch and waits for it to succeed.
+  void MarkDisabledAndWaitForPolicyFetch();
+
+  std::string GetCurrentScreenName(content::WebContents* web_contents);
+
+ protected:
+  base::RunLoop network_state_change_wait_run_loop_;
 
  private:
-  // InProcessBrowserTest:
+  // OobeBaseTest:
   void SetUpInProcessBrowserTestFixture() override;
-  void SetUpCommandLine(base::CommandLine* command_line) override;
+  void SetUpOnMainThread() override;
+
+  // NetworkStateInformer::NetworkStateInformerObserver:
+  void UpdateState(NetworkError::ErrorReason reason) override;
 
   FakeSessionManagerClient* fake_session_manager_client_;
   policy::DevicePolicyCrosTestHelper test_helper_;
@@ -46,18 +82,40 @@
     : fake_session_manager_client_(new FakeSessionManagerClient) {
 }
 
-policy::DevicePolicyBuilder* DeviceDisablingTest::GetDevicePolicyBuilder() {
-  return test_helper_.device_policy();
+void DeviceDisablingTest::MarkDisabledAndWaitForPolicyFetch() {
+  base::RunLoop run_loop;
+  // Set up an |observer| that will wait for the disabled setting to change.
+  scoped_ptr<CrosSettings::ObserverSubscription> observer =
+      CrosSettings::Get()->AddSettingsObserver(
+           kDeviceDisabled,
+           run_loop.QuitClosure());
+  // Prepare a policy fetch response that indicates the device is disabled.
+  test_helper_.device_policy()->policy_data().mutable_device_state()->
+      set_device_mode(enterprise_management::DeviceState::DEVICE_MODE_DISABLED);
+  test_helper_.device_policy()->Build();
+  fake_session_manager_client_->set_device_policy(
+      test_helper_.device_policy()->GetBlob());
+  // Trigger a policy fetch.
+  fake_session_manager_client_->OnPropertyChangeComplete(true);
+  // Wait for the policy fetch to complete and the disabled setting to change.
+  run_loop.Run();
 }
 
-void DeviceDisablingTest::SimulatePolicyFetch() {
-  GetDevicePolicyBuilder()->Build();
-  fake_session_manager_client_->set_device_policy(
-      GetDevicePolicyBuilder()->GetBlob());
-  fake_session_manager_client_->OnPropertyChangeComplete(true);
+std::string DeviceDisablingTest::GetCurrentScreenName(
+    content::WebContents* web_contents ) {
+  std::string screen_name;
+  if (!content::ExecuteScriptAndExtractString(
+          web_contents,
+          "domAutomationController.send(Oobe.getInstance().currentScreen.id);",
+          &screen_name)) {
+    ADD_FAILURE();
+  }
+  return screen_name;
 }
 
 void DeviceDisablingTest::SetUpInProcessBrowserTestFixture() {
+  OobeBaseTest::SetUpInProcessBrowserTestFixture();
+
   DBusThreadManager::GetSetterForTesting()->SetSessionManagerClient(
       scoped_ptr<SessionManagerClient>(fake_session_manager_client_));
 
@@ -65,30 +123,101 @@
   test_helper_.MarkAsEnterpriseOwned();
 }
 
-void DeviceDisablingTest::SetUpCommandLine(base::CommandLine* command_line) {
-  command_line->AppendSwitch(switches::kLoginManager);
-  command_line->AppendSwitch(switches::kForceLoginManagerInTests);
+void DeviceDisablingTest::SetUpOnMainThread() {
+  OobeBaseTest::SetUpOnMainThread();
+
+  // Set up fake networks.
+  DBusThreadManager::Get()->GetShillManagerClient()->GetTestInterface()->
+      SetupDefaultEnvironment();
 }
 
+void DeviceDisablingTest::UpdateState(NetworkError::ErrorReason reason) {
+  network_state_change_wait_run_loop_.Quit();
+}
+
+
 IN_PROC_BROWSER_TEST_F(DeviceDisablingTest, DisableDuringNormalOperation) {
   // Mark the device as disabled and wait until cros settings update.
-  base::RunLoop run_loop;
-  scoped_ptr<CrosSettings::ObserverSubscription> observer =
-      CrosSettings::Get()->AddSettingsObserver(
-           kDeviceDisabled,
-           run_loop.QuitClosure());
-  GetDevicePolicyBuilder()->policy_data().mutable_device_state()->
-      set_device_mode(enterprise_management::DeviceState::DEVICE_MODE_DISABLED);
-  SimulatePolicyFetch();
-  run_loop.Run();
+  MarkDisabledAndWaitForPolicyFetch();
 
   // Verify that the device disabled screen is being shown.
-  WizardController* wizard_controller = WizardController::default_controller();
+  WizardController* const wizard_controller =
+      WizardController::default_controller();
   ASSERT_TRUE(wizard_controller);
   EXPECT_EQ(wizard_controller->GetScreen(
                 WizardController::kDeviceDisabledScreenName),
             wizard_controller->current_screen());
 }
 
+// Verifies that device disabling works when the ephemeral users policy is
+// enabled. This case warrants its own test because the UI behaves somewhat
+// differently when the policy is set: A background job runs on startup that
+// causes the UI to try and show the login screen after some delay. It must
+// be ensured that the login screen does not show and does not clobber the
+// disabled screen.
+IN_PROC_BROWSER_TEST_F(DeviceDisablingTest, DisableWithEphemeralUsers) {
+  // Connect to the fake Ethernet network. This ensures that Chrome OS will not
+  // try to show the offline error screen.
+  base::RunLoop connect_run_loop;
+  DBusThreadManager::Get()->GetShillServiceClient()->Connect(
+      dbus::ObjectPath("/service/eth1"),
+      connect_run_loop.QuitClosure(),
+      base::Bind(&ErrorCallbackFunction));
+  connect_run_loop.Run();
+
+  // Skip to the login screen.
+  WizardController* wizard_controller = WizardController::default_controller();
+  ASSERT_TRUE(wizard_controller);
+  wizard_controller->SkipToLoginForTesting(LoginScreenContext());
+  OobeScreenWaiter(OobeDisplay::SCREEN_GAIA_SIGNIN).Wait();
+
+  // Mark the device as disabled and wait until cros settings update.
+  MarkDisabledAndWaitForPolicyFetch();
+
+  // When the ephemeral users policy is enabled, Chrome OS removes any non-owner
+  // cryptohomes on startup. At the end of that process, JavaScript attempts to
+  // show the login screen. Simulate this.
+  const LoginDisplayHostImpl* const host =
+      static_cast<LoginDisplayHostImpl*>(LoginDisplayHostImpl::default_host());
+  ASSERT_TRUE(host);
+  WebUILoginView* const webui_login_view = host->GetWebUILoginView();
+  ASSERT_TRUE(webui_login_view);
+  content::WebContents* const web_contents = webui_login_view->GetWebContents();
+  ASSERT_TRUE(web_contents);
+  ASSERT_TRUE(content::ExecuteScript(web_contents,
+                                     "Oobe.showAddUserForTesting();"));
+
+  // The login profile is scrubbed before attempting to show the login screen.
+  // Wait for the scrubbing to finish.
+  base::RunLoop run_loop;
+  ProfileHelper::Get()->ClearSigninProfile(run_loop.QuitClosure());
+  run_loop.Run();
+  base::RunLoop().RunUntilIdle();
+
+  // Verify that the login screen was not shown and the device disabled screen
+  // is still being shown instead.
+  EXPECT_EQ(OobeUI::kScreenDeviceDisabled, GetCurrentScreenName(web_contents));
+
+  // Disconnect from the fake Ethernet network.
+  OobeUI* const oobe_ui = host->GetOobeUI();
+  ASSERT_TRUE(oobe_ui);
+  const scoped_refptr<NetworkStateInformer> network_state_informer =
+      oobe_ui->network_state_informer_for_test();
+  ASSERT_TRUE(network_state_informer);
+  network_state_informer->AddObserver(this);
+  SigninScreenHandler* const signin_screen_handler =
+      oobe_ui->signin_screen_handler_for_test();
+  ASSERT_TRUE(signin_screen_handler);
+  signin_screen_handler->ZeroOfflineTimeoutForTesting();
+  SimulateNetworkOffline();
+  network_state_change_wait_run_loop_.Run();
+  network_state_informer->RemoveObserver(this);
+  base::RunLoop().RunUntilIdle();
+
+  // Verify that the offline error screen was not shown and the device disabled
+  // screen is still being shown instead.
+  EXPECT_EQ(OobeUI::kScreenDeviceDisabled, GetCurrentScreenName(web_contents));
+}
+
 }  // namespace system
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/system_logs/device_event_log_source.cc b/chrome/browser/chromeos/system_logs/device_event_log_source.cc
index 552a583..0fb8a01 100644
--- a/chrome/browser/chromeos/system_logs/device_event_log_source.cc
+++ b/chrome/browser/chromeos/system_logs/device_event_log_source.cc
@@ -5,7 +5,7 @@
 #include "chrome/browser/chromeos/system_logs/device_event_log_source.h"
 
 #include "base/message_loop/message_loop.h"
-#include "chromeos/device_event_log.h"
+#include "components/device_event_log/device_event_log.h"
 #include "content/public/browser/browser_thread.h"
 
 namespace system_logs {
@@ -25,16 +25,12 @@
 
   scoped_ptr<SystemLogsResponse> response(new SystemLogsResponse);
   const int kMaxDeviceEventsForAboutSystem = 400;
-  (*response)[kNetworkEventLogEntry] = chromeos::device_event_log::GetAsString(
-      chromeos::device_event_log::OLDEST_FIRST, "time,file,level",
-      "network",
-      chromeos::device_event_log::kDefaultLogLevel,
-      kMaxDeviceEventsForAboutSystem);
-  (*response)[kDeviceEventLogEntry] = chromeos::device_event_log::GetAsString(
-      chromeos::device_event_log::OLDEST_FIRST, "time,file,type,level",
-      "non-network",
-      chromeos::device_event_log::LOG_LEVEL_DEBUG,
-      kMaxDeviceEventsForAboutSystem);
+  (*response)[kNetworkEventLogEntry] = device_event_log::GetAsString(
+      device_event_log::OLDEST_FIRST, "time,file,level", "network",
+      device_event_log::kDefaultLogLevel, kMaxDeviceEventsForAboutSystem);
+  (*response)[kDeviceEventLogEntry] = device_event_log::GetAsString(
+      device_event_log::OLDEST_FIRST, "time,file,type,level", "non-network",
+      device_event_log::LOG_LEVEL_DEBUG, kMaxDeviceEventsForAboutSystem);
   callback.Run(response.get());
 }
 
diff --git a/chrome/browser/component_updater/ppapi_utils.cc b/chrome/browser/component_updater/ppapi_utils.cc
index 544e1651..2300f509 100644
--- a/chrome/browser/component_updater/ppapi_utils.cc
+++ b/chrome/browser/component_updater/ppapi_utils.cc
@@ -76,6 +76,7 @@
 #include "ppapi/c/ppb_view.h"
 #include "ppapi/c/ppb_websocket.h"
 #include "ppapi/c/private/ppb_camera_capabilities_private.h"
+#include "ppapi/c/private/ppb_camera_device_private.h"
 #include "ppapi/c/private/ppb_content_decryptor_private.h"
 #include "ppapi/c/private/ppb_ext_crx_file_system_private.h"
 #include "ppapi/c/private/ppb_file_io_private.h"
@@ -92,7 +93,6 @@
 #include "ppapi/c/private/ppb_flash_message_loop.h"
 #include "ppapi/c/private/ppb_flash_print.h"
 #include "ppapi/c/private/ppb_host_resolver_private.h"
-#include "ppapi/c/private/ppb_image_capture_private.h"
 #include "ppapi/c/private/ppb_input_event_private.h"
 #include "ppapi/c/private/ppb_isolated_file_system_private.h"
 #include "ppapi/c/private/ppb_output_protection_private.h"
diff --git a/chrome/browser/copresence/DEPS b/chrome/browser/copresence/DEPS
new file mode 100644
index 0000000..c3a5b96
--- /dev/null
+++ b/chrome/browser/copresence/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+  "+media",
+]
diff --git a/chrome/browser/copresence/chrome_whispernet_client.cc b/chrome/browser/copresence/chrome_whispernet_client.cc
index d8b4869..0c2e9d63 100644
--- a/chrome/browser/copresence/chrome_whispernet_client.cc
+++ b/chrome/browser/copresence/chrome_whispernet_client.cc
@@ -14,6 +14,9 @@
 #include "extensions/browser/event_router.h"
 #include "extensions/browser/extension_system.h"
 #include "grit/browser_resources.h"
+#include "media/audio/audio_manager.h"
+#include "media/audio/audio_manager_base.h"
+#include "media/audio/audio_parameters.h"
 
 using audio_modem::AUDIBLE;
 using audio_modem::AudioType;
@@ -22,16 +25,39 @@
 using audio_modem::SamplesCallback;
 using audio_modem::SuccessCallback;
 using audio_modem::TokensCallback;
+using audio_modem::TokenParameters;
 
-namespace copresence_private = extensions::api::copresence_private;
+using extensions::api::copresence_private::AudioParameters;
+using extensions::api::copresence_private::DecodeSamplesParameters;
+using extensions::api::copresence_private::EncodeTokenParameters;
+using ApiTokenParams = extensions::api::copresence_private::TokenParameters;
+
+namespace OnConfigAudio =
+    extensions::api::copresence_private::OnConfigAudio;
+namespace OnDecodeSamplesRequest =
+    extensions::api::copresence_private::OnDecodeSamplesRequest;
+namespace OnEncodeTokenRequest =
+    extensions::api::copresence_private::OnEncodeTokenRequest;
+
+using extensions::Event;
+using extensions::copresence_private::RegisterWhispernetClient;
 
 namespace {
 
 AudioParamData GetDefaultAudioConfig() {
+  media::AudioParameters params =
+      media::AudioManager::Get()->GetInputStreamParameters(
+          media::AudioManagerBase::kDefaultDeviceId);
+
   AudioParamData config_data = {};
+
   config_data.audio_dtmf.coder_sample_rate =
       config_data.audio_dsss.coder_sample_rate =
           audio_modem::kDefaultSampleRate;
+
+  config_data.audio_dtmf.recording_sample_rate =
+      config_data.audio_dsss.recording_sample_rate = params.sample_rate();
+
   config_data.audio_dtmf.num_repetitions_to_play =
       config_data.audio_dsss.num_repetitions_to_play =
           audio_modem::kDefaultRepetitions;
@@ -40,18 +66,27 @@
   config_data.audio_dsss.desired_carrier_frequency =
       audio_modem::kDefaultCarrierFrequency;
 
-  config_data.recording_channels = audio_modem::kDefaultChannels;
+  config_data.recording_channels = params.channels();
 
   return config_data;
 }
 
+// ApiTokenParams is not copyable, so we must take it as an output argument.
+// TODO(ckehoe): Pass protos to Whispernet to avoid all these conversions.
+void ConvertTokenParams(const TokenParameters& in, ApiTokenParams* out) {
+  out->length = in.length;
+  out->crc = in.crc;
+  out->parity = in.parity;
+}
+
 }  // namespace
 
 // static
 const char ChromeWhispernetClient::kWhispernetProxyExtensionId[] =
     "bpfmnplchembfbdgieamdodgaencleal";
 
-// Public:
+
+// Public functions.
 
 ChromeWhispernetClient::ChromeWhispernetClient(
     content::BrowserContext* browser_context)
@@ -59,81 +94,67 @@
       event_router_(extensions::EventRouter::Get(browser_context)),
       extension_loaded_(false) {
   DCHECK(browser_context_);
-  DCHECK(event_router_);
 }
 
-ChromeWhispernetClient::~ChromeWhispernetClient() {
-}
+ChromeWhispernetClient::~ChromeWhispernetClient() {}
 
 void ChromeWhispernetClient::Initialize(
     const SuccessCallback& init_callback) {
   DVLOG(3) << "Initializing whispernet proxy client.";
+
   init_callback_ = init_callback;
+  DCHECK(!init_callback_.is_null());
 
-  extensions::ExtensionSystem* es =
-      extensions::ExtensionSystem::Get(browser_context_);
-  DCHECK(es);
-  ExtensionService* service = es->extension_service();
-  DCHECK(service);
-  extensions::ComponentLoader* loader = service->component_loader();
+  extensions::ComponentLoader* loader =
+      extensions::ExtensionSystem::Get(browser_context_)
+          ->extension_service()->component_loader();
   DCHECK(loader);
 
-  // This callback is cancelled in Shutdown().
-  extension_loaded_callback_ = base::Bind(
-      &ChromeWhispernetClient::OnExtensionLoaded, base::Unretained(this));
-
   if (!loader->Exists(kWhispernetProxyExtensionId)) {
     DVLOG(3) << "Loading Whispernet proxy.";
     loader->Add(IDR_WHISPERNET_PROXY_MANIFEST,
                 base::FilePath(FILE_PATH_LITERAL("whispernet_proxy")));
   } else {
     init_callback_.Run(true);
+    extension_loaded_ = true;
   }
 
+  client_id_ = RegisterWhispernetClient(this);
   AudioConfiguration(GetDefaultAudioConfig());
 }
 
-void ChromeWhispernetClient::EncodeToken(const std::string& token_str,
-                                         AudioType type) {
-  copresence_private::EncodeTokenParameters params;
+void ChromeWhispernetClient::EncodeToken(
+    const std::string& token_str,
+    AudioType type,
+    const TokenParameters token_params[2]) {
+  DCHECK(type == AUDIBLE || type == INAUDIBLE);
+
+  EncodeTokenParameters params;
   params.token.token = token_str;
-  params.token.audible = type == AUDIBLE;
-  scoped_ptr<extensions::Event> event(new extensions::Event(
-      copresence_private::OnEncodeTokenRequest::kEventName,
-      copresence_private::OnEncodeTokenRequest::Create(params),
-      browser_context_));
+  params.token.audible = (type == AUDIBLE);
+  ConvertTokenParams(token_params[type], &params.token_params);
 
-  SendEventIfLoaded(event.Pass());
+  SendEventIfLoaded(make_scoped_ptr(new Event(
+      OnEncodeTokenRequest::kEventName,
+      OnEncodeTokenRequest::Create(client_id_, params),
+      browser_context_)));
 }
 
-void ChromeWhispernetClient::DecodeSamples(AudioType type,
-                                           const std::string& samples,
-                                           const size_t token_length[2]) {
-  copresence_private::DecodeSamplesParameters params;
+void ChromeWhispernetClient::DecodeSamples(
+    AudioType type,
+    const std::string& samples,
+    const TokenParameters token_params[2]) {
+  DecodeSamplesParameters params;
   params.samples.assign(samples.begin(), samples.end());
-  params.decode_audible =
-      type == AUDIBLE || type == BOTH;
-  params.decode_inaudible =
-      type == INAUDIBLE || type == BOTH;
-  params.audible_token_length = token_length[AUDIBLE];
-  params.inaudible_token_length = token_length[INAUDIBLE];
+  params.decode_audible = (type == AUDIBLE || type == BOTH);
+  params.decode_inaudible = (type == INAUDIBLE || type == BOTH);
+  ConvertTokenParams(token_params[AUDIBLE], &params.audible_token_params);
+  ConvertTokenParams(token_params[INAUDIBLE], &params.inaudible_token_params);
 
-  scoped_ptr<extensions::Event> event(new extensions::Event(
-      copresence_private::OnDecodeSamplesRequest::kEventName,
-      copresence_private::OnDecodeSamplesRequest::Create(
-          params),
-      browser_context_));
-
-  SendEventIfLoaded(event.Pass());
-}
-
-void ChromeWhispernetClient::DetectBroadcast() {
-  scoped_ptr<extensions::Event> event(new extensions::Event(
-      copresence_private::OnDetectBroadcastRequest::kEventName,
-      make_scoped_ptr(new base::ListValue()),
-      browser_context_));
-
-  SendEventIfLoaded(event.Pass());
+  SendEventIfLoaded(make_scoped_ptr(new Event(
+      OnDecodeSamplesRequest::kEventName,
+      OnDecodeSamplesRequest::Create(client_id_, params),
+      browser_context_)));
 }
 
 void ChromeWhispernetClient::RegisterTokensCallback(
@@ -146,11 +167,6 @@
   samples_callback_ = samples_callback;
 }
 
-void ChromeWhispernetClient::RegisterDetectBroadcastCallback(
-    const SuccessCallback& db_callback) {
-  db_callback_ = db_callback;
-}
-
 TokensCallback ChromeWhispernetClient::GetTokensCallback() {
   return tokens_callback_;
 }
@@ -159,18 +175,16 @@
   return samples_callback_;
 }
 
-SuccessCallback ChromeWhispernetClient::GetDetectBroadcastCallback() {
-  return db_callback_;
-}
-
 SuccessCallback ChromeWhispernetClient::GetInitializedCallback() {
-  return extension_loaded_callback_;
+  return base::Bind(&ChromeWhispernetClient::OnExtensionLoaded,
+                    base::Unretained(this));
 }
 
-// Private:
+
+// Private functions.
 
 void ChromeWhispernetClient::AudioConfiguration(const AudioParamData& params) {
-  copresence_private::AudioParameters audio_params;
+  AudioParameters audio_params;
 
   // We serialize AudioConfigData to a string and send it to the whispernet
   // nacl wrapper.
@@ -178,16 +192,16 @@
   audio_params.param_data.resize(params_size);
   memcpy(vector_as_array(&audio_params.param_data), &params, params_size);
 
-  scoped_ptr<extensions::Event> event(new extensions::Event(
-      copresence_private::OnConfigAudio::kEventName,
-      copresence_private::OnConfigAudio::Create(audio_params),
-      browser_context_));
-
-  SendEventIfLoaded(event.Pass());
+  SendEventIfLoaded(make_scoped_ptr(new Event(
+      OnConfigAudio::kEventName,
+      OnConfigAudio::Create(client_id_, audio_params),
+      browser_context_)));
 }
 
 void ChromeWhispernetClient::SendEventIfLoaded(
     scoped_ptr<extensions::Event> event) {
+  DCHECK(event_router_);
+
   if (extension_loaded_) {
     event_router_->DispatchEventToExtension(kWhispernetProxyExtensionId,
                                             event.Pass());
@@ -198,15 +212,16 @@
 }
 
 void ChromeWhispernetClient::OnExtensionLoaded(bool success) {
-  if (!init_callback_.is_null())
-    init_callback_.Run(success);
+  DCHECK(!init_callback_.is_null());
+  init_callback_.Run(success);
 
   DVLOG(3) << "Sending " << queued_events_.size()
            << " queued requests to whispernet";
 
   // In this loop, ownership of each Event is passed to a scoped_ptr instead.
   // Thus we can just discard the pointers at the end.
-  for (extensions::Event* event : queued_events_) {
+  DCHECK(event_router_);
+  for (Event* event : queued_events_) {
     event_router_->DispatchEventToExtension(kWhispernetProxyExtensionId,
                                             make_scoped_ptr(event));
   }
diff --git a/chrome/browser/copresence/chrome_whispernet_client.h b/chrome/browser/copresence/chrome_whispernet_client.h
index 74c1e7da..5548104 100644
--- a/chrome/browser/copresence/chrome_whispernet_client.h
+++ b/chrome/browser/copresence/chrome_whispernet_client.h
@@ -45,21 +45,19 @@
   // WhispernetClient overrides:
   void Initialize(const audio_modem::SuccessCallback& init_callback) override;
   void EncodeToken(const std::string& token_str,
-                   audio_modem::AudioType type) override;
-  void DecodeSamples(audio_modem::AudioType type,
-                     const std::string& samples,
-                     const size_t token_length[2]) override;
-  void DetectBroadcast() override;
+                   audio_modem::AudioType type,
+                   const audio_modem::TokenParameters token_params[2]) override;
+  void DecodeSamples(
+      audio_modem::AudioType type,
+      const std::string& samples,
+      const audio_modem::TokenParameters token_params[2]) override;
   void RegisterTokensCallback(
       const audio_modem::TokensCallback& tokens_callback) override;
   void RegisterSamplesCallback(
       const audio_modem::SamplesCallback& samples_callback) override;
-  void RegisterDetectBroadcastCallback(
-      const audio_modem::SuccessCallback& db_callback) override;
 
   audio_modem::TokensCallback GetTokensCallback() override;
   audio_modem::SamplesCallback GetSamplesCallback() override;
-  audio_modem::SuccessCallback GetDetectBroadcastCallback() override;
   audio_modem::SuccessCallback GetInitializedCallback() override;
 
   static const char kWhispernetProxyExtensionId[];
@@ -70,19 +68,18 @@
 
   void SendEventIfLoaded(scoped_ptr<extensions::Event> event);
 
-  // This gets called twice; once when the proxy extension loads, the second
-  // time when we have initialized the proxy extension's encoder and decoder.
+  // This gets called when the proxy extension loads.
   void OnExtensionLoaded(bool success);
 
   content::BrowserContext* const browser_context_;
   extensions::EventRouter* const event_router_;
+  std::string client_id_;
 
   audio_modem::SuccessCallback extension_loaded_callback_;
   audio_modem::SuccessCallback init_callback_;
 
   audio_modem::TokensCallback tokens_callback_;
   audio_modem::SamplesCallback samples_callback_;
-  audio_modem::SuccessCallback db_callback_;
 
   ScopedVector<extensions::Event> queued_events_;
   bool extension_loaded_;
diff --git a/chrome/browser/copresence/chrome_whispernet_client_browsertest.cc b/chrome/browser/copresence/chrome_whispernet_client_browsertest.cc
index 5c1f5b8..772d2e1 100644
--- a/chrome/browser/copresence/chrome_whispernet_client_browsertest.cc
+++ b/chrome/browser/copresence/chrome_whispernet_client_browsertest.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/copresence/chrome_whispernet_client.h"
 
+#include <cmath>
 #include <cstdlib>
 #include <string>
 
@@ -17,12 +18,18 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/test/base/in_process_browser_test.h"
+#include "components/audio_modem/public/audio_modem_types.h"
 #include "components/audio_modem/public/whispernet_client.h"
+#include "media/audio/audio_manager.h"
+#include "media/audio/audio_manager_base.h"
+#include "media/audio/audio_parameters.h"
 #include "media/base/audio_bus.h"
+#include "media/base/audio_converter.h"
 
 using audio_modem::WhispernetClient;
 using audio_modem::AUDIBLE;
 using audio_modem::INAUDIBLE;
+using audio_modem::TokenParameters;
 
 namespace {
 
@@ -31,13 +38,7 @@
 const char kEightZeros[] = "MDAwMDAwMDA";
 const char kNineZeros[] = "MDAwMDAwMDAw";
 
-const size_t kTokenLengths[] = {6, 6};
-
-WhispernetClient* GetWhispernetClient(content::BrowserContext* context) {
-  extensions::CopresenceService* service =
-      extensions::CopresenceService::GetFactoryInstance()->Get(context);
-  return service ? service->whispernet_client() : NULL;
-}
+const size_t kTokenLengths[2] = {6, 6};
 
 // Copied from src/components/copresence/mediums/audio/audio_recorder.cc
 std::string AudioBusToString(scoped_refptr<media::AudioBusRefCounted> source) {
@@ -54,29 +55,66 @@
   return buffer;
 }
 
+void GetTokenParamsForLengths(const size_t token_lengths[2],
+                              TokenParameters* params) {
+  params[0].length = token_lengths[0];
+  params[1].length = token_lengths[1];
+}
+
+void IgnoreResult(bool success) {}
+
 }  // namespace
 
-class ChromeWhispernetClientTest : public ExtensionBrowserTest {
+class ChromeWhispernetClientTest : public ExtensionBrowserTest,
+                                   public media::AudioConverter::InputCallback {
  protected:
   ChromeWhispernetClientTest()
-      : context_(NULL), expected_audible_(false), initialized_(false) {}
+      : initialized_(false),
+        expected_audible_(false),
+        saved_samples_index_(0) {}
 
   ~ChromeWhispernetClientTest() override {}
 
   void InitializeWhispernet() {
-    context_ = browser()->profile();
-    run_loop_.reset(new base::RunLoop());
-    GetWhispernetClient(context_)->Initialize(base::Bind(
+    scoped_ptr<WhispernetClient> client(
+        new ChromeWhispernetClient(browser()->profile()));
+    client->Initialize(base::Bind(
         &ChromeWhispernetClientTest::InitCallback, base::Unretained(this)));
-    run_loop_->Run();
 
+    run_loop_.reset(new base::RunLoop());
+    run_loop_->Run();
     EXPECT_TRUE(initialized_);
   }
 
-  void EncodeTokenAndSaveSamples(bool audible, const std::string& token) {
-    WhispernetClient* client = GetWhispernetClient(context_);
-    ASSERT_TRUE(client);
+  // This needs to be called before any of the decoder tests are run. We can't
+  // run this code in the constructor or the SetUp methods because the audio
+  // manager seems to get initialized only *after* ExtensionBrowserTest::SetUp
+  // has finished executing. Setting up a testing AudioMager causes the actual
+  // create happening later in the browser initialization to fail. The only way
+  // around this at the moment seems to be to have this method called from
+  // every test before they try to decode.
+  void SetupDecode() {
+    // We get default parameters here instead of the constructor since
+    // initializing Whispernet also creates our AudioManager. Initializing from
+    // the test instead causes issues.
+    default_params_ = media::AudioManager::Get()->GetInputStreamParameters(
+        media::AudioManagerBase::kDefaultDeviceId);
 
+    coder_params_ = media::AudioParameters(
+        default_params_.format(), audio_modem::kDefaultChannelLayout,
+        audio_modem::kDefaultSampleRate, audio_modem::kDefaultBitsPerSample,
+        default_params_.frames_per_buffer(),
+        media::AudioParameters::NO_EFFECTS);
+
+    converter_.reset(new media::AudioConverter(
+        coder_params_, default_params_,
+        default_params_.sample_rate() == coder_params_.sample_rate()));
+    converter_->AddInput(this);
+  }
+
+  void EncodeTokenAndSaveSamples(WhispernetClient* client,
+                                 bool audible,
+                                 const std::string& token) {
     run_loop_.reset(new base::RunLoop());
     client->RegisterSamplesCallback(
         base::Bind(&ChromeWhispernetClientTest::SamplesCallback,
@@ -84,18 +122,17 @@
     expected_token_ = token;
     expected_audible_ = audible;
 
-    client->EncodeToken(token, audible ? AUDIBLE : INAUDIBLE);
+    TokenParameters token_params[2];
+    client->EncodeToken(token, audible ? AUDIBLE : INAUDIBLE, token_params);
     run_loop_->Run();
 
     EXPECT_GT(saved_samples_->frames(), 0);
   }
 
-  void DecodeSamplesAndVerifyToken(bool expect_audible,
+  void DecodeSamplesAndVerifyToken(WhispernetClient* client,
+                                   bool expect_audible,
                                    const std::string& expected_token,
-                                   const size_t token_length[2]) {
-    WhispernetClient* client = GetWhispernetClient(context_);
-    ASSERT_TRUE(client);
-
+                                   const TokenParameters token_params[2]) {
     run_loop_.reset(new base::RunLoop());
     client->RegisterTokensCallback(base::Bind(
         &ChromeWhispernetClientTest::TokensCallback, base::Unretained(this)));
@@ -104,32 +141,10 @@
 
     ASSERT_GT(saved_samples_->frames(), 0);
 
-    // Convert our single channel samples to two channel. Decode samples
-    // expects 2 channel data.
     scoped_refptr<media::AudioBusRefCounted> samples_bus =
-        media::AudioBusRefCounted::Create(2, saved_samples_->frames());
-    memcpy(samples_bus->channel(0),
-           saved_samples_->channel(0),
-           sizeof(float) * saved_samples_->frames());
-    memcpy(samples_bus->channel(1),
-           saved_samples_->channel(0),
-           sizeof(float) * saved_samples_->frames());
-
-    client->DecodeSamples(
-        expect_audible ? AUDIBLE : INAUDIBLE,
-        AudioBusToString(samples_bus), token_length);
-    run_loop_->Run();
-  }
-
-  void DetectBroadcast() {
-    WhispernetClient* client = GetWhispernetClient(context_);
-    ASSERT_TRUE(client);
-
-    run_loop_.reset(new base::RunLoop());
-    client->RegisterDetectBroadcastCallback(
-        base::Bind(&ChromeWhispernetClientTest::DetectBroadcastCallback,
-                   base::Unretained(this)));
-    client->DetectBroadcast();
+        ConvertSavedSamplesToSystemParams();
+    client->DecodeSamples(expect_audible ? AUDIBLE : INAUDIBLE,
+                          AudioBusToString(samples_bus), token_params);
     run_loop_->Run();
   }
 
@@ -159,21 +174,59 @@
     EXPECT_EQ(expected_audible_, tokens[0].audible);
   }
 
-  void DetectBroadcastCallback(bool success) {
-    EXPECT_TRUE(success);
-    ASSERT_TRUE(run_loop_);
-    run_loop_->Quit();
+ private:
+  scoped_refptr<media::AudioBusRefCounted> ConvertSavedSamplesToSystemParams() {
+    int new_size =
+        saved_samples_->frames() *
+        std::ceil(static_cast<double>(default_params_.sample_rate()) /
+                  coder_params_.sample_rate());
+    new_size =
+        std::ceil(static_cast<double>(new_size) / converter_->ChunkSize()) *
+        converter_->ChunkSize();
+
+    scoped_refptr<media::AudioBusRefCounted> converted_samples =
+        media::AudioBusRefCounted::Create(default_params_.channels(), new_size);
+
+    // Convert our single channel samples to two channel. Decode samples
+    // expects 2 channel data.
+    saved_samples_stereo_ =
+        media::AudioBusRefCounted::Create(2, saved_samples_->frames());
+    memcpy(saved_samples_stereo_->channel(0), saved_samples_->channel(0),
+           sizeof(float) * saved_samples_->frames());
+    memcpy(saved_samples_stereo_->channel(1), saved_samples_->channel(0),
+           sizeof(float) * saved_samples_->frames());
+
+    saved_samples_index_ = 0;
+    converter_->Convert(converted_samples.get());
+
+    return converted_samples;
   }
 
- private:
+  // AudioConverter::InputCallback overrides:
+  double ProvideInput(media::AudioBus* dest,
+                      base::TimeDelta /* buffer_delay */) override {
+    int remaining_frames = saved_samples_->frames() - saved_samples_index_;
+    int frames_to_copy = std::min(remaining_frames, dest->frames());
+    saved_samples_stereo_->CopyPartialFramesTo(saved_samples_index_,
+                                               frames_to_copy, 0, dest);
+    saved_samples_index_ += frames_to_copy;
+    return 1.0;
+  }
+
   scoped_ptr<base::RunLoop> run_loop_;
-  content::BrowserContext* context_;
+  bool initialized_;
 
   std::string expected_token_;
   bool expected_audible_;
-  scoped_refptr<media::AudioBusRefCounted> saved_samples_;
 
-  bool initialized_;
+  scoped_refptr<media::AudioBusRefCounted> saved_samples_;
+  scoped_refptr<media::AudioBusRefCounted> saved_samples_stereo_;
+  int saved_samples_index_;
+
+  scoped_ptr<media::AudioConverter> converter_;
+
+  media::AudioParameters default_params_;
+  media::AudioParameters coder_params_;
 
   DISALLOW_COPY_AND_ASSIGN(ChromeWhispernetClientTest);
 };
@@ -181,55 +234,76 @@
 // These tests are irrelevant if NACL is disabled. See crbug.com/449198
 #if defined(DISABLE_NACL)
 #define MAYBE_Initialize DISABLED_Initialize
-#define MAYBE_EncodeToken DISABLED_EncodeToken
-#define MAYBE_DecodeSamples DISABLED_DecodeSamples
-#define MAYBE_DetectBroadcast DISABLED_DetectBroadcast
-#define MAYBE_Audible DISABLED_Audible
+#define MAYBE_EncodeAndDecode DISABLED_EncodeAndDecode
 #define MAYBE_TokenLengths DISABLED_TokenLengths
+#define MAYBE_MultipleClients DISABLED_MultipleClients
 #else
 #define MAYBE_Initialize Initialize
-#define MAYBE_EncodeToken EncodeToken
-#define MAYBE_DecodeSamples DecodeSamples
-#define MAYBE_DetectBroadcast DetectBroadcast
-#define MAYBE_Audible Audible
+#define MAYBE_EncodeAndDecode EncodeAndDecode
 #define MAYBE_TokenLengths TokenLengths
+#define MAYBE_MultipleClients MultipleClients
 #endif
 
 IN_PROC_BROWSER_TEST_F(ChromeWhispernetClientTest, MAYBE_Initialize) {
   InitializeWhispernet();
 }
 
-IN_PROC_BROWSER_TEST_F(ChromeWhispernetClientTest, MAYBE_EncodeToken) {
-  InitializeWhispernet();
-  EncodeTokenAndSaveSamples(false, kSixZeros);
-}
+IN_PROC_BROWSER_TEST_F(ChromeWhispernetClientTest, MAYBE_EncodeAndDecode) {
+  scoped_ptr<WhispernetClient> client(
+      new ChromeWhispernetClient(browser()->profile()));
+  client->Initialize(base::Bind(&IgnoreResult));
 
-IN_PROC_BROWSER_TEST_F(ChromeWhispernetClientTest, MAYBE_DecodeSamples) {
-  InitializeWhispernet();
-  EncodeTokenAndSaveSamples(false, kSixZeros);
-  DecodeSamplesAndVerifyToken(false, kSixZeros, kTokenLengths);
-}
+  TokenParameters token_params[2];
+  GetTokenParamsForLengths(kTokenLengths, token_params);
 
-IN_PROC_BROWSER_TEST_F(ChromeWhispernetClientTest, MAYBE_DetectBroadcast) {
-  InitializeWhispernet();
-  EncodeTokenAndSaveSamples(false, kSixZeros);
-  DecodeSamplesAndVerifyToken(false, kSixZeros, kTokenLengths);
-  DetectBroadcast();
-}
+  SetupDecode();
 
-IN_PROC_BROWSER_TEST_F(ChromeWhispernetClientTest, MAYBE_Audible) {
-  InitializeWhispernet();
-  EncodeTokenAndSaveSamples(true, kSixZeros);
-  DecodeSamplesAndVerifyToken(true, kSixZeros, kTokenLengths);
+  EncodeTokenAndSaveSamples(client.get(), true, kSixZeros);
+  DecodeSamplesAndVerifyToken(client.get(), true, kSixZeros, token_params);
+
+  EncodeTokenAndSaveSamples(client.get(), false, kSixZeros);
+  DecodeSamplesAndVerifyToken(client.get(), false, kSixZeros, token_params);
 }
 
 IN_PROC_BROWSER_TEST_F(ChromeWhispernetClientTest, MAYBE_TokenLengths) {
-  InitializeWhispernet();
-  size_t kLongTokenLengths[2] = {8, 9};
+  scoped_ptr<WhispernetClient> client(
+      new ChromeWhispernetClient(browser()->profile()));
+  client->Initialize(base::Bind(&IgnoreResult));
 
-  EncodeTokenAndSaveSamples(true, kEightZeros);
-  DecodeSamplesAndVerifyToken(true, kEightZeros, kLongTokenLengths);
+  const size_t kLongTokenLengths[2] = {8, 9};
+  TokenParameters token_params[2];
+  GetTokenParamsForLengths(kLongTokenLengths, token_params);
 
-  EncodeTokenAndSaveSamples(false, kNineZeros);
-  DecodeSamplesAndVerifyToken(false, kNineZeros, kLongTokenLengths);
+  SetupDecode();
+
+  EncodeTokenAndSaveSamples(client.get(), true, kEightZeros);
+  DecodeSamplesAndVerifyToken(client.get(), true, kEightZeros, token_params);
+
+  EncodeTokenAndSaveSamples(client.get(), false, kNineZeros);
+  DecodeSamplesAndVerifyToken(client.get(), false, kNineZeros, token_params);
 }
+
+IN_PROC_BROWSER_TEST_F(ChromeWhispernetClientTest, MAYBE_MultipleClients) {
+  scoped_ptr<WhispernetClient> client_1(
+      new ChromeWhispernetClient(browser()->profile()));
+  scoped_ptr<WhispernetClient> client_2(
+      new ChromeWhispernetClient(browser()->profile()));
+
+  TokenParameters token_params[2];
+  GetTokenParamsForLengths(kTokenLengths, token_params);
+
+  SetupDecode();
+
+  client_1->Initialize(base::Bind(&IgnoreResult));
+
+  EncodeTokenAndSaveSamples(client_1.get(), true, kSixZeros);
+  DecodeSamplesAndVerifyToken(client_1.get(), true, kSixZeros, token_params);
+
+  client_2->Initialize(base::Bind(&IgnoreResult));
+
+  EncodeTokenAndSaveSamples(client_2.get(), true, kSixZeros);
+  DecodeSamplesAndVerifyToken(client_2.get(), true, kSixZeros, token_params);
+}
+
+// TODO(ckehoe): Test crc and parity
+// TODO(ckehoe): More multi-client testing
diff --git a/chrome/browser/copresence/chrome_whispernet_config.h b/chrome/browser/copresence/chrome_whispernet_config.h
index 6aab63dd..256a4ed9f8 100644
--- a/chrome/browser/copresence/chrome_whispernet_config.h
+++ b/chrome/browser/copresence/chrome_whispernet_config.h
@@ -19,6 +19,7 @@
   double desired_carrier_frequency;
   int64_t use_crc_16;
   double coder_sample_rate;
+  double recording_sample_rate;
   int64_t bits_per_symbol;
   int64_t min_cycles_per_frame;
   int64_t baseband_decimation_factor;
@@ -38,6 +39,7 @@
   int64_t include_parity_symbol;
   int64_t use_crc_16;
   double coder_sample_rate;
+  double recording_sample_rate;
   int64_t baseband_decimation_factor;
   int64_t frequencies_per_symbol;
   int64_t window_duration_millis;
diff --git a/chrome/browser/devtools/BUILD.gn b/chrome/browser/devtools/BUILD.gn
index e1edca7..a7580cb 100644
--- a/chrome/browser/devtools/BUILD.gn
+++ b/chrome/browser/devtools/BUILD.gn
@@ -47,11 +47,8 @@
   ]
   sources += get_target_outputs(":devtools_protocol_constants")
 
-  if (is_win) {
-    # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
-    cflags = [ "/wd4267" ]
-  }
   configs += [
+    "//build/config/compiler:no_size_t_to_int_warning",
     "//build/config/compiler:wexit_time_destructors",
     "//third_party/WebKit/public:debug_devtools",
   ]
diff --git a/chrome/browser/devtools/devtools_targets_ui.cc b/chrome/browser/devtools/devtools_targets_ui.cc
index fa0568bf..e84a602 100644
--- a/chrome/browser/devtools/devtools_targets_ui.cc
+++ b/chrome/browser/devtools/devtools_targets_ui.cc
@@ -53,7 +53,6 @@
 const char kAdbBrowserUserField[] = "adbBrowserUser";
 const char kAdbBrowserVersionField[] = "adbBrowserVersion";
 const char kAdbBrowserChromeVersionField[] = "adbBrowserChromeVersion";
-const char kCompatibleVersion[] = "compatibleVersion";
 const char kAdbPagesList[] = "pages";
 
 const char kAdbScreenWidthField[] = "adbScreenWidth";
@@ -384,16 +383,6 @@
       browser_data->SetString(kTargetIdField, browser_id);
       browser_data->SetString(kTargetSourceField, source_id());
 
-      base::Version remote_version;
-      remote_version = base::Version(browser->version());
-
-      chrome::VersionInfo version_info;
-      base::Version local_version(version_info.Version());
-
-      browser_data->SetBoolean(kCompatibleVersion,
-          (!remote_version.IsValid()) || (!local_version.IsValid()) ||
-          remote_version.components()[0] <= local_version.components()[0]);
-
       base::ListValue* page_list = new base::ListValue();
       remote_browsers_[browser_id] = browser;
             browser_data->Set(kAdbPagesList, page_list);
diff --git a/chrome/browser/drive/drive_api_service.cc b/chrome/browser/drive/drive_api_service.cc
index 48f2cb0..d22811f00 100644
--- a/chrome/browser/drive/drive_api_service.cc
+++ b/chrome/browser/drive/drive_api_service.cc
@@ -489,6 +489,7 @@
     const std::string& new_title,
     const base::Time& last_modified,
     const base::Time& last_viewed_by_me,
+    const google_apis::drive::Properties& properties,
     const FileResourceCallback& callback) {
   DCHECK(thread_checker_.CalledOnValidThread());
   DCHECK(!callback.is_null());
@@ -511,6 +512,7 @@
     request->set_last_viewed_by_me_date(last_viewed_by_me);
   }
   request->set_fields(kFileResourceFields);
+  request->set_properties(properties);
   return sender_->StartRequestWithRetry(request);
 }
 
diff --git a/chrome/browser/drive/drive_api_service.h b/chrome/browser/drive/drive_api_service.h
index ffe26bc..c0d1502 100644
--- a/chrome/browser/drive/drive_api_service.h
+++ b/chrome/browser/drive/drive_api_service.h
@@ -126,6 +126,7 @@
       const std::string& new_title,
       const base::Time& last_modified,
       const base::Time& last_viewed_by_me,
+      const google_apis::drive::Properties& properties,
       const google_apis::FileResourceCallback& callback) override;
   google_apis::CancelCallback AddResourceToDirectory(
       const std::string& parent_resource_id,
diff --git a/chrome/browser/drive/drive_service_interface.h b/chrome/browser/drive/drive_service_interface.h
index 016a99ac..1f789a8 100644
--- a/chrome/browser/drive/drive_service_interface.h
+++ b/chrome/browser/drive/drive_service_interface.h
@@ -265,8 +265,6 @@
   // and it'll be named |new_title|.
   // If |last_modified| is not null, the modified date of the resource on the
   // server will be set to the date.
-  // This request is supported only on DriveAPIService, because GData WAPI
-  // doesn't support the function unfortunately.
   // Upon completion, invokes |callback| with results on the calling thread.
   // |callback| must not be null.
   virtual google_apis::CancelCallback CopyResource(
@@ -280,8 +278,7 @@
   // |parent_resource_id| with renaming to |new_title|.
   // If |last_modified| or |last_accessed| is not null, the modified/accessed
   // date of the resource on the server will be set to the date.
-  // This request is supported only on DriveAPIService, because GData WAPI
-  // doesn't support the function unfortunately.
+  // If |properties| are specified, then they will be set on |resource_id|.
   // Upon completion, invokes |callback| with results on the calling thread.
   // |callback| must not be null.
   virtual google_apis::CancelCallback UpdateResource(
@@ -290,6 +287,7 @@
       const std::string& new_title,
       const base::Time& last_modified,
       const base::Time& last_viewed_by_me,
+      const google_apis::drive::Properties& properties,
       const google_apis::FileResourceCallback& callback) = 0;
 
   // Adds a resource (document, file, or collection) identified by its
@@ -427,7 +425,6 @@
       const google_apis::EntryActionCallback& callback) = 0;
 
   // Authorizes the account |email| to access |resource_id| as a |role|.
-  //
   // |callback| must not be null.
   virtual google_apis::CancelCallback AddPermission(
       const std::string& resource_id,
diff --git a/chrome/browser/drive/dummy_drive_service.cc b/chrome/browser/drive/dummy_drive_service.cc
index 7207ac6..2e8fafc6 100644
--- a/chrome/browser/drive/dummy_drive_service.cc
+++ b/chrome/browser/drive/dummy_drive_service.cc
@@ -124,6 +124,7 @@
     const std::string& new_title,
     const base::Time& last_modified,
     const base::Time& last_viewed_by_me,
+    const google_apis::drive::Properties& properties,
     const google_apis::FileResourceCallback& callback) {
   return CancelCallback();
 }
diff --git a/chrome/browser/drive/dummy_drive_service.h b/chrome/browser/drive/dummy_drive_service.h
index c2b4aae..44772ee2 100644
--- a/chrome/browser/drive/dummy_drive_service.h
+++ b/chrome/browser/drive/dummy_drive_service.h
@@ -5,6 +5,8 @@
 #ifndef CHROME_BROWSER_DRIVE_DUMMY_DRIVE_SERVICE_H_
 #define CHROME_BROWSER_DRIVE_DUMMY_DRIVE_SERVICE_H_
 
+#include <string>
+
 #include "chrome/browser/drive/drive_service_interface.h"
 #include "google_apis/drive/auth_service_interface.h"
 
@@ -86,6 +88,7 @@
       const std::string& new_title,
       const base::Time& last_modified,
       const base::Time& last_viewed_by_me,
+      const google_apis::drive::Properties& properties,
       const google_apis::FileResourceCallback& callback) override;
   google_apis::CancelCallback AddResourceToDirectory(
       const std::string& parent_resource_id,
diff --git a/chrome/browser/drive/fake_drive_service.cc b/chrome/browser/drive/fake_drive_service.cc
index 52d790a5..f34c0d62 100644
--- a/chrome/browser/drive/fake_drive_service.cc
+++ b/chrome/browser/drive/fake_drive_service.cc
@@ -880,6 +880,7 @@
     const std::string& new_title,
     const base::Time& last_modified,
     const base::Time& last_viewed_by_me,
+    const google_apis::drive::Properties& properties,
     const google_apis::FileResourceCallback& callback) {
   DCHECK(thread_checker_.CalledOnValidThread());
   DCHECK(!callback.is_null());
diff --git a/chrome/browser/drive/fake_drive_service.h b/chrome/browser/drive/fake_drive_service.h
index 6d52b677e..ebb16581 100644
--- a/chrome/browser/drive/fake_drive_service.h
+++ b/chrome/browser/drive/fake_drive_service.h
@@ -5,6 +5,8 @@
 #ifndef CHROME_BROWSER_DRIVE_FAKE_DRIVE_SERVICE_H_
 #define CHROME_BROWSER_DRIVE_FAKE_DRIVE_SERVICE_H_
 
+#include <string>
+
 #include "base/files/file_path.h"
 #include "base/threading/thread_checker.h"
 #include "chrome/browser/drive/drive_service_interface.h"
@@ -199,6 +201,7 @@
       const std::string& new_title,
       const base::Time& last_modified,
       const base::Time& last_viewed_by_me,
+      const google_apis::drive::Properties& properties,
       const google_apis::FileResourceCallback& callback) override;
   google_apis::CancelCallback AddResourceToDirectory(
       const std::string& parent_resource_id,
diff --git a/chrome/browser/drive/fake_drive_service_unittest.cc b/chrome/browser/drive/fake_drive_service_unittest.cc
index 03410483..1d1f5c9 100644
--- a/chrome/browser/drive/fake_drive_service_unittest.cc
+++ b/chrome/browser/drive/fake_drive_service_unittest.cc
@@ -1163,11 +1163,10 @@
   DriveApiErrorCode error = DRIVE_OTHER_ERROR;
   scoped_ptr<FileResource> entry;
   fake_service_.UpdateResource(
-      kResourceId,
-      kParentResourceId,
-      "new title",
+      kResourceId, kParentResourceId, "new title",
       base::Time::FromUTCExploded(kModifiedDate),
       base::Time::FromUTCExploded(kViewedDate),
+      google_apis::drive::Properties(),
       test_util::CreateCopyResultCallback(&error, &entry));
   base::RunLoop().RunUntilIdle();
 
@@ -1194,11 +1193,8 @@
   DriveApiErrorCode error = DRIVE_OTHER_ERROR;
   scoped_ptr<FileResource> entry;
   fake_service_.UpdateResource(
-      kResourceId,
-      "1_folder_resource_id",
-      "new title",
-      base::Time(),
-      base::Time(),
+      kResourceId, "1_folder_resource_id", "new title", base::Time(),
+      base::Time(), google_apis::drive::Properties(),
       test_util::CreateCopyResultCallback(&error, &entry));
   base::RunLoop().RunUntilIdle();
 
@@ -1218,11 +1214,8 @@
   DriveApiErrorCode error = DRIVE_OTHER_ERROR;
   scoped_ptr<FileResource> entry;
   fake_service_.UpdateResource(
-      kResourceId,
-      std::string(),
-      "new title",
-      base::Time(),
-      base::Time(),
+      kResourceId, std::string(), "new title", base::Time(), base::Time(),
+      google_apis::drive::Properties(),
       test_util::CreateCopyResultCallback(&error, &entry));
   base::RunLoop().RunUntilIdle();
 
@@ -1246,11 +1239,8 @@
   DriveApiErrorCode error = DRIVE_OTHER_ERROR;
   scoped_ptr<FileResource> entry;
   fake_service_.UpdateResource(
-      kResourceId,
-      std::string(),
-      "new title",
-      base::Time(),
-      base::Time(),
+      kResourceId, std::string(), "new title", base::Time(), base::Time(),
+      google_apis::drive::Properties(),
       test_util::CreateCopyResultCallback(&error, &entry));
   base::RunLoop().RunUntilIdle();
 
@@ -1268,11 +1258,8 @@
   DriveApiErrorCode error = DRIVE_OTHER_ERROR;
   scoped_ptr<FileResource> entry;
   fake_service_.UpdateResource(
-      kResourceId,
-      std::string(),
-      "new title",
-      base::Time(),
-      base::Time(),
+      kResourceId, std::string(), "new title", base::Time(), base::Time(),
+      google_apis::drive::Properties(),
       test_util::CreateCopyResultCallback(&error, &entry));
   base::RunLoop().RunUntilIdle();
 
diff --git a/chrome/browser/extensions/active_script_controller.cc b/chrome/browser/extensions/active_script_controller.cc
index 3efa950..c3e9956 100644
--- a/chrome/browser/extensions/active_script_controller.cc
+++ b/chrome/browser/extensions/active_script_controller.cc
@@ -39,6 +39,7 @@
 ActiveScriptController::ActiveScriptController(
     content::WebContents* web_contents)
     : content::WebContentsObserver(web_contents),
+      num_page_requests_(0),
       browser_context_(web_contents->GetBrowserContext()),
       was_used_on_page_(false),
       extension_registry_observer_(this) {
@@ -237,6 +238,8 @@
     return;
   }
 
+  ++num_page_requests_;
+
   switch (RequiresUserConsentForScriptInjection(extension, script_type)) {
     case PermissionsData::ACCESS_ALLOWED:
       PermitScriptInjection(request_id);
@@ -315,6 +318,7 @@
     return;
 
   LogUMA();
+  num_page_requests_ = 0;
   permitted_extensions_.clear();
   pending_requests_.clear();
   was_used_on_page_ = false;
diff --git a/chrome/browser/extensions/active_script_controller.h b/chrome/browser/extensions/active_script_controller.h
index b4310950..981f60e 100644
--- a/chrome/browser/extensions/active_script_controller.h
+++ b/chrome/browser/extensions/active_script_controller.h
@@ -68,6 +68,8 @@
   // run.
   bool WantsToRun(const Extension* extension);
 
+  int num_page_requests() const { return num_page_requests_; }
+
 #if defined(UNIT_TEST)
   // Only used in tests.
   PermissionsData::AccessType RequiresUserConsentForScriptInjectionForTesting(
@@ -127,6 +129,11 @@
                            const Extension* extension,
                            UnloadedExtensionInfo::Reason reason) override;
 
+  // The total number of requests from the renderer on the current page,
+  // including any that are pending or were immediately granted.
+  // Right now, used only in tests.
+  int num_page_requests_;
+
   // The associated browser context.
   content::BrowserContext* browser_context_;
 
diff --git a/chrome/browser/extensions/active_script_controller_browsertest.cc b/chrome/browser/extensions/active_script_controller_browsertest.cc
index eea16bd5..e28bf662 100644
--- a/chrome/browser/extensions/active_script_controller_browsertest.cc
+++ b/chrome/browser/extensions/active_script_controller_browsertest.cc
@@ -8,6 +8,7 @@
 #include "chrome/browser/extensions/active_script_controller.h"
 #include "chrome/browser/extensions/extension_action.h"
 #include "chrome/browser/extensions/extension_browsertest.h"
+#include "chrome/browser/extensions/extension_util.h"
 #include "chrome/browser/extensions/tab_helper.h"
 #include "chrome/browser/extensions/test_extension_dir.h"
 #include "chrome/browser/ui/browser.h"
@@ -402,6 +403,59 @@
   inject_success_listener.WaitUntilSatisfied();
 }
 
+// Test that granting the extension all urls permission allows it to run on
+// pages, and that the permission update is sent to existing renderers.
+IN_PROC_BROWSER_TEST_F(ActiveScriptControllerBrowserTest,
+                       GrantExtensionAllUrlsPermission) {
+
+  // Loadup an extension and navigate.
+  const Extension* extension = CreateExtension(ALL_HOSTS, CONTENT_SCRIPT);
+  ASSERT_TRUE(extension);
+
+  content::WebContents* web_contents =
+      browser()->tab_strip_model()->GetActiveWebContents();
+  ASSERT_TRUE(web_contents);
+  ActiveScriptController* active_script_controller =
+      ActiveScriptController::GetForWebContents(web_contents);
+  ASSERT_TRUE(active_script_controller);
+
+  ExtensionTestMessageListener inject_success_listener(
+      new ExtensionTestMessageListener(kInjectSucceeded,
+                                       false /* won't reply */));
+  inject_success_listener.set_extension_id(extension->id());
+
+  ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
+  GURL url = embedded_test_server()->GetURL("/extensions/test_file.html");
+  ui_test_utils::NavigateToURL(browser(), url);
+
+  // The extension shouldn't be allowed to run.
+  EXPECT_TRUE(active_script_controller->WantsToRun(extension));
+  EXPECT_EQ(1, active_script_controller->num_page_requests());
+  EXPECT_FALSE(inject_success_listener.was_satisfied());
+
+  // Enable the extension to run on all urls.
+  util::SetAllowedScriptingOnAllUrls(extension->id(), profile(), true);
+  EXPECT_TRUE(RunAllPendingInRenderer(web_contents));
+
+  // Navigate again - this time, the extension should execute immediately (and
+  // should not need to ask the script controller for permission).
+  ui_test_utils::NavigateToURL(browser(), url);
+  EXPECT_FALSE(active_script_controller->WantsToRun(extension));
+  EXPECT_EQ(0, active_script_controller->num_page_requests());
+  EXPECT_TRUE(inject_success_listener.WaitUntilSatisfied());
+
+  // Revoke all urls permissions.
+  inject_success_listener.Reset();
+  util::SetAllowedScriptingOnAllUrls(extension->id(), profile(), false);
+  EXPECT_TRUE(RunAllPendingInRenderer(web_contents));
+
+  // Re-navigate; the extension should again need permission to run.
+  ui_test_utils::NavigateToURL(browser(), url);
+  EXPECT_TRUE(active_script_controller->WantsToRun(extension));
+  EXPECT_EQ(1, active_script_controller->num_page_requests());
+  EXPECT_FALSE(inject_success_listener.was_satisfied());
+}
+
 // A version of the test with the flag off, in order to test that everything
 // still works as expected.
 class FlagOffActiveScriptControllerBrowserTest
diff --git a/chrome/browser/extensions/api/audio_modem/OWNERS b/chrome/browser/extensions/api/audio_modem/OWNERS
new file mode 100644
index 0000000..9364bf7
--- /dev/null
+++ b/chrome/browser/extensions/api/audio_modem/OWNERS
@@ -0,0 +1,2 @@
+ckehoe@chromium.org
+rkc@chromium.org
diff --git a/chrome/browser/extensions/api/audio_modem/audio_modem_api.cc b/chrome/browser/extensions/api/audio_modem/audio_modem_api.cc
new file mode 100644
index 0000000..5c6f081
--- /dev/null
+++ b/chrome/browser/extensions/api/audio_modem/audio_modem_api.cc
@@ -0,0 +1,348 @@
+// 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 <map>
+#include <string>
+#include <vector>
+
+#include "base/base64.h"
+#include "base/bind.h"
+#include "base/guid.h"
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/stl_util.h"
+#include "base/strings/string_util.h"
+#include "base/timer/timer.h"
+#include "chrome/browser/copresence/chrome_whispernet_client.h"
+#include "chrome/browser/extensions/api/audio_modem/audio_modem_api.h"
+#include "chrome/common/extensions/api/audio_modem.h"
+#include "extensions/browser/event_router.h"
+
+// TODO(ckehoe): Implement transmit fail checking.
+
+namespace extensions {
+
+using api::audio_modem::AUDIOBAND_AUDIBLE;
+using api::audio_modem::AUDIOBAND_INAUDIBLE;
+using api::audio_modem::Audioband;
+using api::audio_modem::STATUS_CODERERROR;
+using api::audio_modem::STATUS_INUSE;
+using api::audio_modem::STATUS_INVALIDREQUEST;
+using api::audio_modem::STATUS_SUCCESS;
+using api::audio_modem::ReceivedToken;
+using api::audio_modem::RequestParams;
+using api::audio_modem::Status;
+
+namespace Transmit = api::audio_modem::Transmit;
+namespace StopTransmit = api::audio_modem::StopTransmit;
+namespace Receive = api::audio_modem::Receive;
+namespace StopReceive = api::audio_modem::StopReceive;
+namespace OnReceived = api::audio_modem::OnReceived;
+
+using audio_modem::AUDIBLE;
+using audio_modem::AudioToken;
+using audio_modem::AudioType;
+using audio_modem::INAUDIBLE;
+using audio_modem::TokenParameters;
+
+namespace {
+
+const char kInitFailedError[] = "The audio modem is not available. "
+    "Failed to initialize the token encoder/decoder.";
+const char kInvalidTokenLengthError[] =
+    "The token length must be greater than zero.";
+const char kIncorrectTokenLengthError[] =
+    "The token provided did not match the declared token length.";
+const char kInvalidTimeoutError[] =
+    "Transmit and receive timeouts must be greater than zero.";
+
+const int kMaxTransmitTimeout = 10 * 60 * 1000;  // 10 minutes
+const int kMaxReceiveTimeout = 60 * 60 * 1000;  // 1 hour
+
+base::LazyInstance<BrowserContextKeyedAPIFactory<AudioModemAPI>>
+    g_factory = LAZY_INSTANCE_INITIALIZER;
+
+AudioType AudioTypeForBand(Audioband band) {
+  switch (band) {
+    case AUDIOBAND_AUDIBLE:
+      return AUDIBLE;
+    case AUDIOBAND_INAUDIBLE:
+      return INAUDIBLE;
+    default:
+      NOTREACHED();
+      return audio_modem::AUDIO_TYPE_UNKNOWN;
+  }
+}
+
+TokenParameters TokenParamsForEncoding(
+    const api::audio_modem::TokenEncoding& encoding) {
+  return TokenParameters(encoding.token_length,
+                         encoding.crc ? *encoding.crc : false,
+                         encoding.parity ? *encoding.parity : true);
+}
+
+const std::string DecodeBase64Token(std::string encoded_token) {
+  // Make sure the token is padded correctly.
+  while (encoded_token.size() % 4 > 0)
+    encoded_token += "=";
+
+  // Decode and return the token.
+  std::string raw_token;
+  bool decode_success = base::Base64Decode(encoded_token, &raw_token);
+  DCHECK(decode_success);
+  return raw_token;
+}
+
+}  // namespace
+
+
+// Public functions.
+
+AudioModemAPI::AudioModemAPI(content::BrowserContext* context)
+    : AudioModemAPI(context,
+                    make_scoped_ptr(new ChromeWhispernetClient(context)),
+                    audio_modem::Modem::Create()) {}
+
+AudioModemAPI::AudioModemAPI(
+    content::BrowserContext* context,
+    scoped_ptr<audio_modem::WhispernetClient> whispernet_client,
+    scoped_ptr<audio_modem::Modem> modem)
+    : browser_context_(context),
+      whispernet_client_(whispernet_client.Pass()),
+      modem_(modem.Pass()),
+      init_failed_(false) {
+  // We own these objects, so these callbacks will not outlive us.
+  whispernet_client_->Initialize(
+      base::Bind(&AudioModemAPI::WhispernetInitComplete,
+                 base::Unretained(this)));
+  modem_->Initialize(whispernet_client_.get(),
+                     base::Bind(&AudioModemAPI::TokensReceived,
+                                base::Unretained(this)));
+}
+
+AudioModemAPI::~AudioModemAPI() {
+  for (const auto& timer_entry : receive_timers_[0])
+    delete timer_entry.second;
+  for (const auto& timer_entry : receive_timers_[1])
+    delete timer_entry.second;
+}
+
+Status AudioModemAPI::StartTransmit(const std::string& app_id,
+                                    const RequestParams& params,
+                                    const std::string& token) {
+  AudioType audio_type = AudioTypeForBand(params.band);
+  if (transmitters_[audio_type].empty())
+    transmitters_[audio_type] = app_id;
+  else if (transmitters_[audio_type] != app_id)
+    return STATUS_INUSE;
+
+  DVLOG(3) << "Starting transmit for app " << app_id;
+
+  std::string encoded_token;
+  base::Base64Encode(token, &encoded_token);
+  base::RemoveChars(encoded_token, "=", &encoded_token);
+
+  modem_->SetTokenParams(audio_type, TokenParamsForEncoding(params.encoding));
+  modem_->SetToken(audio_type, encoded_token);
+  modem_->StartPlaying(audio_type);
+
+  transmit_timers_[audio_type].Start(
+      FROM_HERE,
+      base::TimeDelta::FromMilliseconds(params.timeout_millis),
+      base::Bind(base::IgnoreResult(&AudioModemAPI::StopTransmit),
+                 base::Unretained(this),
+                 app_id,
+                 audio_type));
+  return STATUS_SUCCESS;
+}
+
+Status AudioModemAPI::StopTransmit(const std::string& app_id,
+                                   AudioType audio_type) {
+  if (transmitters_[audio_type] != app_id)
+    return STATUS_INVALIDREQUEST;
+
+  DVLOG(3) << "Stopping transmit for app " << app_id;
+  transmitters_[audio_type].clear();
+  modem_->StopPlaying(audio_type);
+  return STATUS_SUCCESS;
+}
+
+void AudioModemAPI::StartReceive(const std::string& app_id,
+                                 const RequestParams& params) {
+  DVLOG(3) << "Starting receive for app " << app_id;
+
+  AudioType audio_type = AudioTypeForBand(params.band);
+  modem_->SetTokenParams(audio_type, TokenParamsForEncoding(params.encoding));
+  modem_->StartRecording(audio_type);
+
+  if (receive_timers_[audio_type].count(app_id) == 0)
+    receive_timers_[audio_type][app_id] = new base::OneShotTimer<AudioModemAPI>;
+  DCHECK(receive_timers_[audio_type][app_id]);
+  receive_timers_[audio_type][app_id]->Start(
+      FROM_HERE,
+      base::TimeDelta::FromMilliseconds(params.timeout_millis),
+      base::Bind(base::IgnoreResult(&AudioModemAPI::StopReceive),
+                 base::Unretained(this),
+                 app_id,
+                 audio_type));
+}
+
+Status AudioModemAPI::StopReceive(const std::string& app_id,
+                                  AudioType audio_type) {
+  if (receive_timers_[audio_type].count(app_id) == 0)
+    return STATUS_INVALIDREQUEST;
+
+  DCHECK(receive_timers_[audio_type][app_id]);
+  delete receive_timers_[audio_type][app_id];
+  receive_timers_[audio_type].erase(app_id);
+
+  DVLOG(3) << "Stopping receive for app " << app_id;
+  if (receive_timers_[audio_type].empty())
+    modem_->StopRecording(audio_type);
+  return STATUS_SUCCESS;
+}
+
+// static
+BrowserContextKeyedAPIFactory<AudioModemAPI>*
+AudioModemAPI::GetFactoryInstance() {
+  return g_factory.Pointer();
+}
+
+
+// Private functions.
+
+void AudioModemAPI::WhispernetInitComplete(bool success) {
+  if (success) {
+    VLOG(2) << "Whispernet initialized successfully.";
+  } else {
+    LOG(ERROR) << "Failed to initialize Whispernet!";
+    init_failed_ = true;
+  }
+}
+
+void AudioModemAPI::TokensReceived(const std::vector<AudioToken>& tokens) {
+  // Distribute the tokens to the appropriate app(s).
+  std::map<std::string, std::vector<linked_ptr<ReceivedToken>>> tokens_by_app;
+  for (const AudioToken& token : tokens) {
+    linked_ptr<ReceivedToken> api_token(new ReceivedToken);
+    const std::string& raw_token = DecodeBase64Token(token.token);
+    api_token->token.assign(raw_token.c_str(),
+                            raw_token.c_str() + raw_token.size());
+    api_token->band = token.audible ? AUDIOBAND_AUDIBLE : AUDIOBAND_INAUDIBLE;
+    for (const auto& receiver :
+         receive_timers_[token.audible ? AUDIBLE : INAUDIBLE]) {
+      tokens_by_app[receiver.first].push_back(api_token);
+    }
+  }
+
+  // Send events to the appropriate app(s).
+  for (const auto& app_entry : tokens_by_app) {
+    const std::string& app_id = app_entry.first;
+    const auto& tokens = app_entry.second;
+    if (app_id.empty())
+      continue;
+
+    EventRouter::Get(browser_context_)->DispatchEventToExtension(
+      app_id,
+      make_scoped_ptr(new Event(OnReceived::kEventName,
+                                OnReceived::Create(tokens))));
+  }
+}
+
+
+// Functions outside the API scope.
+
+template <>
+void
+BrowserContextKeyedAPIFactory<AudioModemAPI>::DeclareFactoryDependencies() {
+  DependsOn(ExtensionsBrowserClient::Get()->GetExtensionSystemFactory());
+}
+
+ExtensionFunction::ResponseAction AudioModemTransmitFunction::Run() {
+  scoped_ptr<Transmit::Params> params(Transmit::Params::Create(*args_));
+  EXTENSION_FUNCTION_VALIDATE(params.get());
+  AudioModemAPI* api =
+      AudioModemAPI::GetFactoryInstance()->Get(browser_context());
+  if (api->init_failed()) {
+    return RespondNow(ErrorWithArguments(
+        Transmit::Results::Create(STATUS_CODERERROR),
+        kInitFailedError));
+  }
+
+  // Check the token length.
+  int token_length = params->params.encoding.token_length;
+  if (token_length <= 0) {
+    return RespondNow(ErrorWithArguments(
+        Transmit::Results::Create(STATUS_INVALIDREQUEST),
+        kInvalidTokenLengthError));
+  }
+  const char* token = vector_as_array(&params->token);
+  std::string token_str(token, params->token.size());
+  if (static_cast<int>(token_str.size()) != token_length) {
+    return RespondNow(ErrorWithArguments(
+        Transmit::Results::Create(STATUS_INVALIDREQUEST),
+        kIncorrectTokenLengthError));
+  }
+
+  // Check the timeout.
+  int64_t timeout_millis = params->params.timeout_millis;
+  if (timeout_millis <= 0) {
+    return RespondNow(ErrorWithArguments(
+        Transmit::Results::Create(STATUS_INVALIDREQUEST),
+        kInvalidTimeoutError));
+  }
+  if (timeout_millis > kMaxTransmitTimeout)
+    timeout_millis = kMaxTransmitTimeout;
+
+  // Start transmission.
+  Status status = api->StartTransmit(extension_id(), params->params, token_str);
+  return RespondNow(ArgumentList(Transmit::Results::Create(status)));
+}
+
+ExtensionFunction::ResponseAction AudioModemStopTransmitFunction::Run() {
+  scoped_ptr<StopTransmit::Params> params(StopTransmit::Params::Create(*args_));
+  EXTENSION_FUNCTION_VALIDATE(params.get());
+
+  Status status = AudioModemAPI::GetFactoryInstance()->Get(browser_context())
+      ->StopTransmit(extension_id(), AudioTypeForBand(params->band));
+  return RespondNow(ArgumentList(StopTransmit::Results::Create(status)));
+}
+
+ExtensionFunction::ResponseAction AudioModemReceiveFunction::Run() {
+  scoped_ptr<Receive::Params> params(Receive::Params::Create(*args_));
+  EXTENSION_FUNCTION_VALIDATE(params.get());
+  AudioModemAPI* api =
+      AudioModemAPI::GetFactoryInstance()->Get(browser_context());
+  if (api->init_failed()) {
+    return RespondNow(ErrorWithArguments(
+        Transmit::Results::Create(STATUS_CODERERROR),
+        kInitFailedError));
+  }
+
+  // Check the timeout.
+  int64_t timeout_millis = params->params.timeout_millis;
+  if (timeout_millis <= 0) {
+    return RespondNow(ErrorWithArguments(
+        Receive::Results::Create(STATUS_INVALIDREQUEST),
+        kInvalidTimeoutError));
+  }
+  if (timeout_millis > kMaxReceiveTimeout)
+    timeout_millis = kMaxReceiveTimeout;
+
+  // Start receiving.
+  api->StartReceive(extension_id(), params->params);
+  return RespondNow(ArgumentList(Receive::Results::Create(STATUS_SUCCESS)));
+}
+
+ExtensionFunction::ResponseAction AudioModemStopReceiveFunction::Run() {
+  scoped_ptr<StopReceive::Params> params(StopReceive::Params::Create(*args_));
+  EXTENSION_FUNCTION_VALIDATE(params.get());
+
+  Status status = AudioModemAPI::GetFactoryInstance()->Get(browser_context())
+      ->StopReceive(extension_id(), AudioTypeForBand(params->band));
+  return RespondNow(ArgumentList(StopReceive::Results::Create(status)));
+}
+
+}  // namespace extensions
diff --git a/chrome/browser/extensions/api/audio_modem/audio_modem_api.h b/chrome/browser/extensions/api/audio_modem/audio_modem_api.h
new file mode 100644
index 0000000..557eceff
--- /dev/null
+++ b/chrome/browser/extensions/api/audio_modem/audio_modem_api.h
@@ -0,0 +1,130 @@
+// 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 CHROME_BROWSER_EXTENSIONS_API_AUDIO_MODEM_AUDIO_MODEM_API_H_
+#define CHROME_BROWSER_EXTENSIONS_API_AUDIO_MODEM_AUDIO_MODEM_API_H_
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/memory/scoped_ptr.h"
+#include "chrome/common/extensions/api/audio_modem.h"
+#include "components/audio_modem/public/modem.h"
+#include "extensions/browser/browser_context_keyed_api_factory.h"
+#include "extensions/browser/extension_function.h"
+#include "extensions/browser/extension_function_histogram_value.h"
+
+namespace extensions {
+
+// Implementation of the chrome.audioModem API.
+class AudioModemAPI final : public BrowserContextKeyedAPI {
+ public:
+  // Default constructor.
+  explicit AudioModemAPI(content::BrowserContext* context);
+
+  // Testing constructor: pass in dependencies.
+  AudioModemAPI(content::BrowserContext* context,
+                scoped_ptr<audio_modem::WhispernetClient> whispernet_client,
+                scoped_ptr<audio_modem::Modem> modem);
+
+  ~AudioModemAPI() override;
+
+  // Starts transmitting a token, and returns the associated API status.
+  // Fails if another app is already transmitting the same AudioType.
+  api::audio_modem::Status StartTransmit(
+      const std::string& app_id,
+      const api::audio_modem::RequestParams& params,
+      const std::string& token);
+
+  // Stops an in-progress transmit, and returns the associated API status.
+  // Fails if the specified app is not currently transmitting.
+  api::audio_modem::Status StopTransmit(const std::string& app_id,
+                                        audio_modem::AudioType audio_type);
+
+  // Starts receiving for the specified app.
+  // Multiple apps may receive the same AudioType simultaneously.
+  void StartReceive(const std::string& app_id,
+                    const api::audio_modem::RequestParams& params);
+
+  // Stops receiving for the specified app, and returns the associated
+  // API status. Fails if that app is not currently receiving.
+  api::audio_modem::Status StopReceive(const std::string& app_id,
+                                       audio_modem::AudioType audio_type);
+
+  bool init_failed() const { return init_failed_; }
+
+  // BrowserContextKeyedAPI implementation.
+  static BrowserContextKeyedAPIFactory<AudioModemAPI>* GetFactoryInstance();
+
+ private:
+  friend class BrowserContextKeyedAPIFactory<AudioModemAPI>;
+
+  void WhispernetInitComplete(bool success);
+  void TokensReceived(const std::vector<audio_modem::AudioToken>& tokens);
+
+  content::BrowserContext* const browser_context_;
+  scoped_ptr<audio_modem::WhispernetClient> whispernet_client_;
+  scoped_ptr<audio_modem::Modem> modem_;
+  bool init_failed_;
+
+  // IDs for the currently transmitting app (if any), indexed by AudioType.
+  std::string transmitters_[2];
+
+  // Timeouts for the currently active transmits, indexed by AudioType.
+  base::OneShotTimer<AudioModemAPI> transmit_timers_[2];
+
+  // Maps of currently receiving app ID => timeouts. Indexed by AudioType.
+  // We own all of these pointers. Do not remove them without calling delete.
+  std::map<std::string, base::OneShotTimer<AudioModemAPI>*> receive_timers_[2];
+
+  // BrowserContextKeyedAPI implementation.
+  static const char* service_name() { return "AudioModemAPI"; }
+
+  DISALLOW_COPY_AND_ASSIGN(AudioModemAPI);
+};
+
+template<>
+void BrowserContextKeyedAPIFactory<AudioModemAPI>::DeclareFactoryDependencies();
+
+class AudioModemTransmitFunction : public UIThreadExtensionFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION("audioModem.transmit", AUDIOMODEM_TRANSMIT);
+
+ protected:
+  ~AudioModemTransmitFunction() override {}
+  ExtensionFunction::ResponseAction Run() override;
+};
+
+class AudioModemStopTransmitFunction : public UIThreadExtensionFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION("audioModem.stopTransmit",
+                             AUDIOMODEM_STOPTRANSMIT);
+
+ protected:
+  ~AudioModemStopTransmitFunction() override {}
+  ExtensionFunction::ResponseAction Run() override;
+};
+
+class AudioModemReceiveFunction : public UIThreadExtensionFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION("audioModem.receive", AUDIOMODEM_RECEIVE);
+
+ protected:
+  ~AudioModemReceiveFunction() override {}
+  ExtensionFunction::ResponseAction Run() override;
+};
+
+class AudioModemStopReceiveFunction : public UIThreadExtensionFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION("audioModem.stopReceive", AUDIOMODEM_STOPRECEIVE);
+
+ protected:
+  ~AudioModemStopReceiveFunction() override {}
+  ExtensionFunction::ResponseAction Run() override;
+};
+
+}  // namespace extensions
+
+#endif  // CHROME_BROWSER_EXTENSIONS_API_AUDIO_MODEM_AUDIO_MODEM_API_H_
diff --git a/chrome/browser/extensions/api/audio_modem/audio_modem_api_unittest.cc b/chrome/browser/extensions/api/audio_modem/audio_modem_api_unittest.cc
new file mode 100644
index 0000000..f1fa5ac
--- /dev/null
+++ b/chrome/browser/extensions/api/audio_modem/audio_modem_api_unittest.cc
@@ -0,0 +1,380 @@
+// 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 <map>
+#include <string>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/values.h"
+#include "chrome/browser/extensions/api/audio_modem/audio_modem_api.h"
+#include "chrome/browser/extensions/extension_api_unittest.h"
+#include "chrome/browser/extensions/extension_function_test_utils.h"
+#include "chrome/browser/extensions/test_extension_system.h"
+#include "components/audio_modem/public/modem.h"
+#include "components/audio_modem/test/stub_modem.h"
+#include "components/audio_modem/test/stub_whispernet_client.h"
+#include "extensions/browser/api_test_utils.h"
+#include "extensions/browser/event_router.h"
+
+using audio_modem::AUDIBLE;
+using audio_modem::AudioToken;
+using audio_modem::INAUDIBLE;
+using audio_modem::StubModem;
+using audio_modem::StubWhispernetClient;
+
+using base::BinaryValue;
+using base::DictionaryValue;
+using base::ListValue;
+using base::StringValue;
+using base::Value;
+
+using content::BrowserContext;
+
+namespace ext_test_utils = extension_function_test_utils;
+
+namespace extensions {
+
+namespace {
+
+// The TestingFactoryFunction uses a BrowserContext as its context pointer.
+// But each BrowserContext is still associated with a unit test.
+// So we store the StubModem created in each test.
+std::map<BrowserContext*, StubModem*> g_modems;
+
+// Create a test AudioModemAPI and store the modem it uses.
+KeyedService* ApiFactoryFunction(BrowserContext* context) {
+  StubModem* modem = new StubModem;
+  g_modems[context] = modem;
+  return new AudioModemAPI(
+      context,
+      make_scoped_ptr<audio_modem::WhispernetClient>(new StubWhispernetClient),
+      make_scoped_ptr<audio_modem::Modem>(modem));
+}
+
+DictionaryValue* CreateParams(const std::string& audio_band) {
+  DictionaryValue* params = new DictionaryValue;
+  params->SetInteger("timeoutMillis", 60000);
+  params->SetString("band", audio_band);
+  params->SetInteger("encoding.tokenLength", 4);
+  return params;
+}
+
+BinaryValue* CreateToken(const std::string& token) {
+  return BinaryValue::CreateWithCopiedBuffer(token.c_str(), token.size());
+}
+
+scoped_ptr<ListValue> CreateList(Value* single_elt) {
+  scoped_ptr<ListValue> list(new ListValue);
+  list->Append(single_elt);
+  return list.Pass();
+}
+
+scoped_ptr<ListValue> CreateList(Value* elt1, Value* elt2) {
+  scoped_ptr<ListValue> list(new ListValue);
+  list->Append(elt1);
+  list->Append(elt2);
+  return list.Pass();
+}
+
+DictionaryValue* CreateReceivedToken(const std::string& token,
+                                     const std::string& audio_band) {
+  DictionaryValue* out = new DictionaryValue;
+  out->Set("token", CreateToken(token));
+  out->SetString("band", audio_band);
+  return out;
+}
+
+bool ReceivedSingleToken(const Event* event,
+                         const DictionaryValue* expected_token) {
+  ListValue* received_tokens;
+  event->event_args->GetList(0, &received_tokens);
+  if (received_tokens->GetSize() != 1)
+    return false;
+
+  DictionaryValue* received_token;
+  received_tokens->GetDictionary(0, &received_token);
+  return received_token->Equals(expected_token);
+}
+
+// TODO(ckehoe): Put this in //extensions/test.
+// Then replace the other EventRouter mocks.
+class StubEventRouter : public EventRouter {
+ public:
+  // Callback to receive events. First argument is
+  // the extension id to receive the event.
+  using EventCallback = base::Callback<void(const std::string&,
+                                            scoped_ptr<Event>)>;
+
+  StubEventRouter(BrowserContext* context, EventCallback event_callback)
+      : EventRouter(context, nullptr),
+        event_callback_(event_callback) {}
+
+  void DispatchEventToExtension(const std::string& extension_id,
+                                scoped_ptr<Event> event) override {
+    event_callback_.Run(extension_id, event.Pass());
+  }
+
+  void ClearEventCallback() {
+    event_callback_.Reset();
+  }
+
+ private:
+  EventCallback event_callback_;
+};
+
+}  // namespace
+
+class AudioModemApiUnittest : public ExtensionApiUnittest {
+ public:
+  AudioModemApiUnittest() {}
+  ~AudioModemApiUnittest() override {
+    for (const auto& events : events_by_extension_id_) {
+      for (const Event* event : events.second)
+        delete event;
+    }
+  }
+
+ protected:
+  template<typename Function>
+  const std::string RunFunction(scoped_ptr<ListValue> args,
+                                const Extension* extension) {
+    scoped_refptr<UIThreadExtensionFunction> function(new Function);
+    function->set_extension(extension);
+    function->set_browser_context(profile());
+    function->set_has_callback(true);
+    ext_test_utils::RunFunction(
+        function.get(), args.Pass(), browser(), ext_test_utils::NONE);
+
+    std::string result_status;
+    CHECK(function->GetResultList()->GetString(0, &result_status));
+    return result_status;
+  }
+
+  template<typename Function>
+  const std::string RunFunction(scoped_ptr<ListValue> args) {
+    return RunFunction<Function>(args.Pass(), GetExtension(std::string()));
+  }
+
+  StubModem* GetModem() const {
+    return g_modems[profile()];
+  }
+
+  const Extension* GetExtension(const std::string& name) {
+    if (!extensions_by_name_[name].get()) {
+      scoped_ptr<DictionaryValue> extension_definition(new DictionaryValue);
+      extension_definition->SetString("name", name);
+      extension_definition->SetString("version", "1.0");
+      extensions_by_name_[name] = api_test_utils::CreateExtension(
+          Manifest::INTERNAL, extension_definition.get(), name);
+      DVLOG(2) << "Created extension " << extensions_by_name_[name]->id();
+    }
+    return extensions_by_name_[name].get();
+  }
+
+  const std::vector<const Event*>&
+  GetEventsForExtension(const std::string& name) {
+    const Extension* extension = extensions_by_name_[name].get();
+    DCHECK(extension);
+    return events_by_extension_id_[extension->id()];
+  }
+
+  const std::vector<const Event*>& GetEvents() {
+    return GetEventsForExtension(std::string());
+  }
+
+ private:
+  void SetUp() override {
+    ExtensionApiUnittest::SetUp();
+    AudioModemAPI::GetFactoryInstance()->SetTestingFactory(
+        profile(), &ApiFactoryFunction);
+
+    scoped_ptr<EventRouter> router(new StubEventRouter(
+        profile(),
+        // The EventRouter is deleted in TearDown().
+        // It will lose this callback before we are destructed.
+        base::Bind(&AudioModemApiUnittest::CaptureEvent,
+                   base::Unretained(this))));
+    static_cast<TestExtensionSystem*>(ExtensionSystem::Get(profile()))
+        ->SetEventRouter(router.Pass());
+  }
+
+  void CaptureEvent(const std::string& extension_id,
+                    scoped_ptr<Event> event) {
+    // Since scoped_ptr (and ScopedVector) do not work inside STL containers,
+    // we must manage this memory manually. It is cleaned up by the destructor.
+    events_by_extension_id_[extension_id].push_back(event.release());
+  }
+
+  std::map<std::string, scoped_refptr<Extension>> extensions_by_name_;
+
+  // We own all of these pointers.
+  // Do not remove them from the map without calling delete.
+  std::map<std::string, std::vector<const Event*>> events_by_extension_id_;
+};
+
+TEST_F(AudioModemApiUnittest, TransmitBasic) {
+  // Start transmitting inaudibly.
+  EXPECT_EQ("success", RunFunction<AudioModemTransmitFunction>(
+      CreateList(CreateParams("inaudible"), CreateToken("1234"))));
+  EXPECT_TRUE(GetModem()->IsPlaying(INAUDIBLE));
+
+  // Can't cancel audible transmit - we haven't started it yet.
+  EXPECT_EQ("invalidRequest", RunFunction<AudioModemStopTransmitFunction>(
+      CreateList(new StringValue("audible"))));
+
+  // Start transmitting audibly.
+  EXPECT_EQ("success", RunFunction<AudioModemTransmitFunction>(
+      CreateList(CreateParams("audible"), CreateToken("ABCD"))));
+  EXPECT_TRUE(GetModem()->IsPlaying(AUDIBLE));
+
+  // Stop audible transmit. We're still transmitting inaudibly.
+  EXPECT_EQ("success", RunFunction<AudioModemStopTransmitFunction>(
+      CreateList(new StringValue("audible"))));
+  EXPECT_FALSE(GetModem()->IsPlaying(AUDIBLE));
+  EXPECT_TRUE(GetModem()->IsPlaying(INAUDIBLE));
+
+  // Stop inaudible transmit.
+  EXPECT_EQ("success", RunFunction<AudioModemStopTransmitFunction>(
+      CreateList(new StringValue("inaudible"))));
+  EXPECT_FALSE(GetModem()->IsPlaying(INAUDIBLE));
+}
+
+TEST_F(AudioModemApiUnittest, ReceiveBasic) {
+  // Start listening for audible tokens.
+  EXPECT_EQ("success", RunFunction<AudioModemReceiveFunction>(
+      CreateList(CreateParams("audible"))));
+  EXPECT_TRUE(GetModem()->IsRecording(AUDIBLE));
+
+  // Can't cancel inaudible receive - we haven't started it yet.
+  EXPECT_EQ("invalidRequest", RunFunction<AudioModemStopReceiveFunction>(
+      CreateList(new StringValue("inaudible"))));
+
+  // Send some audible tokens.
+  std::vector<AudioToken> tokens;
+  tokens.push_back(AudioToken("1234", true));
+  tokens.push_back(AudioToken("ABCD", true));
+  tokens.push_back(AudioToken("abcd", false));
+  GetModem()->DeliverTokens(tokens);
+
+  // Check the tokens received.
+  EXPECT_EQ(1u, GetEvents().size());
+  scoped_ptr<ListValue> expected_tokens(new ListValue);
+  expected_tokens->Append(CreateReceivedToken("1234", "audible"));
+  expected_tokens->Append(CreateReceivedToken("ABCD", "audible"));
+  ListValue* received_tokens;
+  GetEvents()[0]->event_args->GetList(0, &received_tokens);
+  EXPECT_TRUE(received_tokens->Equals(expected_tokens.get()));
+
+  // Start listening for inaudible tokens.
+  EXPECT_EQ("success", RunFunction<AudioModemReceiveFunction>(
+      CreateList(CreateParams("inaudible"))));
+  EXPECT_TRUE(GetModem()->IsRecording(INAUDIBLE));
+
+  // Send some more tokens.
+  tokens.push_back(AudioToken("5678", false));
+  GetModem()->DeliverTokens(tokens);
+
+  // Check the tokens received.
+  EXPECT_EQ(2u, GetEvents().size());
+  expected_tokens->Append(CreateReceivedToken("abcd", "inaudible"));
+  expected_tokens->Append(CreateReceivedToken("5678", "inaudible"));
+  GetEvents()[1]->event_args->GetList(0, &received_tokens);
+  EXPECT_TRUE(received_tokens->Equals(expected_tokens.get()));
+
+  // Stop audible receive. We're still receiving inaudible.
+  EXPECT_EQ("success", RunFunction<AudioModemStopReceiveFunction>(
+      CreateList(new StringValue("audible"))));
+  EXPECT_FALSE(GetModem()->IsRecording(AUDIBLE));
+  EXPECT_TRUE(GetModem()->IsRecording(INAUDIBLE));
+
+  // Stop inaudible receive.
+  EXPECT_EQ("success", RunFunction<AudioModemStopReceiveFunction>(
+      CreateList(new StringValue("inaudible"))));
+  EXPECT_FALSE(GetModem()->IsRecording(INAUDIBLE));
+}
+
+TEST_F(AudioModemApiUnittest, TransmitMultiple) {
+  // Start transmit.
+  EXPECT_EQ("success", RunFunction<AudioModemTransmitFunction>(
+      CreateList(CreateParams("audible"), CreateToken("1234")),
+      GetExtension("ext1")));
+  EXPECT_TRUE(GetModem()->IsPlaying(AUDIBLE));
+
+  // Another extension can't interfere with the first one.
+  EXPECT_EQ("inUse", RunFunction<AudioModemTransmitFunction>(
+      CreateList(CreateParams("audible"), CreateToken("ABCD")),
+      GetExtension("ext2")));
+  EXPECT_EQ("invalidRequest", RunFunction<AudioModemStopTransmitFunction>(
+      CreateList(new StringValue("audible")), GetExtension("ext2")));
+  EXPECT_TRUE(GetModem()->IsPlaying(AUDIBLE));
+
+  // The other extension can use the other audio band, however.
+  EXPECT_EQ("success", RunFunction<AudioModemTransmitFunction>(
+      CreateList(CreateParams("inaudible"), CreateToken("ABCD")),
+      GetExtension("ext2")));
+  EXPECT_TRUE(GetModem()->IsPlaying(INAUDIBLE));
+
+  // The first extension can change its token.
+  // But the other band is still in use.
+  EXPECT_EQ("success", RunFunction<AudioModemTransmitFunction>(
+      CreateList(CreateParams("audible"), CreateToken("abcd")),
+      GetExtension("ext1")));
+  EXPECT_EQ("inUse", RunFunction<AudioModemTransmitFunction>(
+      CreateList(CreateParams("inaudible"), CreateToken("1234")),
+      GetExtension("ext1")));
+
+  // Stop transmission.
+  EXPECT_EQ("success", RunFunction<AudioModemStopTransmitFunction>(
+      CreateList(new StringValue("audible")), GetExtension("ext1")));
+  EXPECT_FALSE(GetModem()->IsPlaying(AUDIBLE));
+  EXPECT_EQ("success", RunFunction<AudioModemStopTransmitFunction>(
+      CreateList(new StringValue("inaudible")), GetExtension("ext2")));
+  EXPECT_FALSE(GetModem()->IsPlaying(INAUDIBLE));
+}
+
+TEST_F(AudioModemApiUnittest, ReceiveMultiple) {
+  // Start receive. Multiple extensions can receive on the same band.
+  EXPECT_EQ("success", RunFunction<AudioModemReceiveFunction>(
+      CreateList(CreateParams("inaudible")), GetExtension("ext1")));
+  EXPECT_TRUE(GetModem()->IsRecording(INAUDIBLE));
+  EXPECT_EQ("success", RunFunction<AudioModemReceiveFunction>(
+      CreateList(CreateParams("inaudible")), GetExtension("ext2")));
+
+  // Receive a token.
+  GetModem()->DeliverTokens(std::vector<AudioToken>(
+      1, AudioToken("abcd", false)));
+  EXPECT_EQ(1u, GetEventsForExtension("ext1").size());
+  EXPECT_EQ(1u, GetEventsForExtension("ext2").size());
+
+  // Check the token received.
+  scoped_ptr<DictionaryValue> expected_token(
+      CreateReceivedToken("abcd", "inaudible"));
+  EXPECT_TRUE(ReceivedSingleToken(
+      GetEventsForExtension("ext1")[0], expected_token.get()));
+  EXPECT_TRUE(ReceivedSingleToken(
+      GetEventsForExtension("ext2")[0], expected_token.get()));
+
+  // If one extension stops, the modem is still receiving for the other.
+  EXPECT_EQ("success", RunFunction<AudioModemStopReceiveFunction>(
+      CreateList(new StringValue("inaudible")), GetExtension("ext1")));
+  EXPECT_TRUE(GetModem()->IsRecording(INAUDIBLE));
+
+  // Receive another token. Should only go to ext2.
+  GetModem()->DeliverTokens(std::vector<AudioToken>(
+      1, AudioToken("1234", false)));
+  EXPECT_EQ(1u, GetEventsForExtension("ext1").size());
+  EXPECT_EQ(2u, GetEventsForExtension("ext2").size());
+  expected_token.reset(CreateReceivedToken("1234", "inaudible"));
+  EXPECT_TRUE(ReceivedSingleToken(
+      GetEventsForExtension("ext2")[1], expected_token.get()));
+
+  EXPECT_EQ("success", RunFunction<AudioModemStopReceiveFunction>(
+      CreateList(new StringValue("inaudible")), GetExtension("ext2")));
+  EXPECT_FALSE(GetModem()->IsRecording(INAUDIBLE));
+}
+
+}  // namespace extensions
diff --git a/chrome/browser/extensions/api/automation/automation_apitest.cc b/chrome/browser/extensions/api/automation/automation_apitest.cc
index c103439c..18d62a7 100644
--- a/chrome/browser/extensions/api/automation/automation_apitest.cc
+++ b/chrome/browser/extensions/api/automation/automation_apitest.cc
@@ -237,10 +237,10 @@
                        int routing_id,
                        BrowserContext* browser_context) {
     ui::AXTreeUpdate update;
-    serializer->SerializeChanges(tree->GetRoot(), &update);
+    serializer->SerializeChanges(tree->root(), &update);
     SendUpdate(update,
                ui::AX_EVENT_LAYOUT_COMPLETE,
-               tree->GetRoot()->id(),
+               tree->root()->id(),
                routing_id,
                browser_context);
   }
@@ -398,14 +398,14 @@
     ui::AXEvent event =
         is_last_update ? AX_EVENT_ASSERT_EQUAL : AX_EVENT_IGNORE;
     state.SendUpdate(
-        update, event, target_tree->GetRoot()->id(), kTab0Rid, browser_context);
+        update, event, target_tree->root()->id(), kTab0Rid, browser_context);
   }
 }
 
 // Helper method to send a no-op tree update to tab 0 with the given event.
 void SendEvent(ui::AXEvent event, content::BrowserContext* browser_context) {
   ui::AXTreeUpdate update;
-  ui::AXNode* root = state.tree0->GetRoot();
+  ui::AXNode* root = state.tree0->root();
   state.serializer0->SerializeChanges(root, &update);
   state.SendUpdate(update, event, root->id(), kTab0Rid, browser_context);
 }
diff --git a/chrome/browser/extensions/api/copresence_private/copresence_private_api.cc b/chrome/browser/extensions/api/copresence_private/copresence_private_api.cc
index fd6e508d..54fba44 100644
--- a/chrome/browser/extensions/api/copresence_private/copresence_private_api.cc
+++ b/chrome/browser/extensions/api/copresence_private/copresence_private_api.cc
@@ -4,59 +4,77 @@
 
 #include "chrome/browser/extensions/api/copresence_private/copresence_private_api.h"
 
+#include <map>
+#include <string>
 #include <vector>
 
+#include "base/guid.h"
 #include "base/lazy_instance.h"
 #include "base/stl_util.h"
 #include "chrome/browser/copresence/chrome_whispernet_client.h"
-#include "chrome/browser/extensions/api/copresence/copresence_api.h"
 #include "chrome/common/extensions/api/copresence_private.h"
 #include "media/base/audio_bus.h"
 
+using audio_modem::WhispernetClient;
+
+namespace {
+
+base::LazyInstance<std::map<std::string, WhispernetClient*>>
+g_whispernet_clients = LAZY_INSTANCE_INITIALIZER;
+
+WhispernetClient* GetWhispernetClient(const std::string& id) {
+  WhispernetClient* client = g_whispernet_clients.Get()[id];
+  DCHECK(client);
+  return client;
+}
+
+}  // namespace
+
 namespace extensions {
 
 namespace SendFound = api::copresence_private::SendFound;
 namespace SendSamples = api::copresence_private::SendSamples;
-namespace SendDetect = api::copresence_private::SendDetect;
 namespace SendInitialized = api::copresence_private::SendInitialized;
 
+namespace copresence_private {
+
+const std::string RegisterWhispernetClient(WhispernetClient* client) {
+  std::string id = base::GenerateGUID();
+  g_whispernet_clients.Get()[id] = client;
+  return id;
+}
+
+}  // namespace copresence_private
+
 // Copresence Private functions.
 
-audio_modem::WhispernetClient*
-CopresencePrivateFunction::GetWhispernetClient() {
-  CopresenceService* service =
-      CopresenceService::GetFactoryInstance()->Get(browser_context());
-  return service ? service->whispernet_client() : NULL;
-}
-
 // CopresenceSendFoundFunction implementation:
 ExtensionFunction::ResponseAction CopresencePrivateSendFoundFunction::Run() {
-  if (!GetWhispernetClient() ||
-      GetWhispernetClient()->GetTokensCallback().is_null()) {
-    return RespondNow(NoArguments());
-  }
-
   scoped_ptr<SendFound::Params> params(SendFound::Params::Create(*args_));
   EXTENSION_FUNCTION_VALIDATE(params.get());
+
+  WhispernetClient* whispernet_client = GetWhispernetClient(params->client_id);
+  if (whispernet_client->GetTokensCallback().is_null())
+    return RespondNow(NoArguments());
+
   std::vector<audio_modem::AudioToken> tokens;
   for (size_t i = 0; i < params->tokens.size(); ++i) {
     tokens.push_back(audio_modem::AudioToken(params->tokens[i]->token,
                                             params->tokens[i]->audible));
   }
-  GetWhispernetClient()->GetTokensCallback().Run(tokens);
+  whispernet_client->GetTokensCallback().Run(tokens);
   return RespondNow(NoArguments());
 }
 
 // CopresenceSendEncodedFunction implementation:
 ExtensionFunction::ResponseAction CopresencePrivateSendSamplesFunction::Run() {
-  if (!GetWhispernetClient() ||
-      GetWhispernetClient()->GetSamplesCallback().is_null()) {
-    return RespondNow(NoArguments());
-  }
-
   scoped_ptr<SendSamples::Params> params(SendSamples::Params::Create(*args_));
   EXTENSION_FUNCTION_VALIDATE(params.get());
 
+  WhispernetClient* whispernet_client = GetWhispernetClient(params->client_id);
+  if (whispernet_client->GetSamplesCallback().is_null())
+    return RespondNow(NoArguments());
+
   scoped_refptr<media::AudioBusRefCounted> samples =
       media::AudioBusRefCounted::Create(1,  // Mono
                                         params->samples.size() / sizeof(float));
@@ -64,39 +82,28 @@
   memcpy(samples->channel(0), vector_as_array(&params->samples),
          params->samples.size());
 
-  GetWhispernetClient()->GetSamplesCallback().Run(
+  whispernet_client->GetSamplesCallback().Run(
       params->token.audible ? audio_modem::AUDIBLE : audio_modem::INAUDIBLE,
       params->token.token, samples);
   return RespondNow(NoArguments());
 }
 
-// CopresenceSendDetectFunction implementation:
-ExtensionFunction::ResponseAction CopresencePrivateSendDetectFunction::Run() {
-  if (!GetWhispernetClient() ||
-      GetWhispernetClient()->GetDetectBroadcastCallback().is_null()) {
-    return RespondNow(NoArguments());
-  }
-
-  scoped_ptr<SendDetect::Params> params(SendDetect::Params::Create(*args_));
-  EXTENSION_FUNCTION_VALIDATE(params.get());
-
-  GetWhispernetClient()->GetDetectBroadcastCallback().Run(params->detected);
-  return RespondNow(NoArguments());
-}
-
 // CopresenceSendInitializedFunction implementation:
 ExtensionFunction::ResponseAction
 CopresencePrivateSendInitializedFunction::Run() {
-  if (!GetWhispernetClient() ||
-      GetWhispernetClient()->GetInitializedCallback().is_null()) {
-    return RespondNow(NoArguments());
-  }
-
   scoped_ptr<SendInitialized::Params> params(
       SendInitialized::Params::Create(*args_));
   EXTENSION_FUNCTION_VALIDATE(params.get());
 
-  GetWhispernetClient()->GetInitializedCallback().Run(params->success);
+  DVLOG(2) << "Forwarding init callback to "
+           << g_whispernet_clients.Get().size() << " clients";
+  for (auto client_entry : g_whispernet_clients.Get()) {
+    audio_modem::SuccessCallback init_callback =
+        client_entry.second->GetInitializedCallback();
+    if (!init_callback.is_null())
+      init_callback.Run(params->success);
+  }
+
   return RespondNow(NoArguments());
 }
 
diff --git a/chrome/browser/extensions/api/copresence_private/copresence_private_api.h b/chrome/browser/extensions/api/copresence_private/copresence_private_api.h
index 3377336..d4c2098 100644
--- a/chrome/browser/extensions/api/copresence_private/copresence_private_api.h
+++ b/chrome/browser/extensions/api/copresence_private/copresence_private_api.h
@@ -5,7 +5,9 @@
 #ifndef CHROME_BROWSER_EXTENSIONS_API_COPRESENCE_PRIVATE_COPRESENCE_PRIVATE_API_H_
 #define CHROME_BROWSER_EXTENSIONS_API_COPRESENCE_PRIVATE_COPRESENCE_PRIVATE_API_H_
 
-#include "chrome/browser/extensions/chrome_extension_function.h"
+#include <string>
+
+#include "extensions/browser/extension_function.h"
 
 namespace audio_modem {
 class WhispernetClient;
@@ -13,13 +15,15 @@
 
 namespace extensions {
 
-class CopresencePrivateFunction : public ChromeUIThreadExtensionFunction {
- protected:
-  audio_modem::WhispernetClient* GetWhispernetClient();
-  ~CopresencePrivateFunction() override {}
-};
+namespace copresence_private {
 
-class CopresencePrivateSendFoundFunction : public CopresencePrivateFunction {
+// Register a client to receive events from Whispernet.
+const std::string
+RegisterWhispernetClient(audio_modem::WhispernetClient* client);
+
+}  // namespace copresence_private
+
+class CopresencePrivateSendFoundFunction : public UIThreadExtensionFunction {
  public:
   DECLARE_EXTENSION_FUNCTION("copresencePrivate.sendFound",
                              COPRESENCEPRIVATE_SENDFOUND);
@@ -29,7 +33,7 @@
   ExtensionFunction::ResponseAction Run() override;
 };
 
-class CopresencePrivateSendSamplesFunction : public CopresencePrivateFunction {
+class CopresencePrivateSendSamplesFunction : public UIThreadExtensionFunction {
  public:
   DECLARE_EXTENSION_FUNCTION("copresencePrivate.sendSamples",
                              COPRESENCEPRIVATE_SENDSAMPLES);
@@ -39,7 +43,7 @@
   ExtensionFunction::ResponseAction Run() override;
 };
 
-class CopresencePrivateSendDetectFunction : public CopresencePrivateFunction {
+class CopresencePrivateSendDetectFunction : public UIThreadExtensionFunction {
  public:
   DECLARE_EXTENSION_FUNCTION("copresencePrivate.sendDetect",
                              COPRESENCEPRIVATE_SENDDETECT);
@@ -50,7 +54,7 @@
 };
 
 class CopresencePrivateSendInitializedFunction
-    : public CopresencePrivateFunction {
+    : public UIThreadExtensionFunction {
  public:
   DECLARE_EXTENSION_FUNCTION("copresencePrivate.sendInitialized",
                              COPRESENCEPRIVATE_SENDINITIALIZED);
diff --git a/chrome/browser/extensions/api/dial/dial_api.h b/chrome/browser/extensions/api/dial/dial_api.h
index 6548a72..51f0f55 100644
--- a/chrome/browser/extensions/api/dial/dial_api.h
+++ b/chrome/browser/extensions/api/dial/dial_api.h
@@ -21,6 +21,10 @@
 // the DIAL registry. It takes care of creating the registry on the IO thread
 // and is an observer of the registry. It makes sure devices events are sent out
 // to extension listeners on the right thread.
+//
+// TODO(mfoltz): This should probably inherit from BrowserContextKeyedAPI
+// instead; ShutdownOnUIThread below is a no-op, which is the whole point of
+// RefcountedKeyedService.
 class DialAPI : public RefcountedKeyedService,
                 public EventRouter::Observer,
                 public DialRegistry::Observer {
diff --git a/chrome/browser/extensions/api/dial/dial_registry.cc b/chrome/browser/extensions/api/dial/dial_registry.cc
index 22d43c06..50f60f2 100644
--- a/chrome/browser/extensions/api/dial/dial_registry.cc
+++ b/chrome/browser/extensions/api/dial/dial_registry.cc
@@ -42,7 +42,6 @@
 
 DialRegistry::~DialRegistry() {
   DCHECK(thread_checker_.CalledOnValidThread());
-  DCHECK_EQ(0, num_listeners_);
   NetworkChangeNotifier::RemoveNetworkChangeObserver(this);
 }
 
diff --git a/chrome/browser/extensions/api/easy_unlock_private/easy_unlock_private_api_chromeos_unittest.cc b/chrome/browser/extensions/api/easy_unlock_private/easy_unlock_private_api_chromeos_unittest.cc
index 909aace..d5b0e9c1 100644
--- a/chrome/browser/extensions/api/easy_unlock_private/easy_unlock_private_api_chromeos_unittest.cc
+++ b/chrome/browser/extensions/api/easy_unlock_private/easy_unlock_private_api_chromeos_unittest.cc
@@ -506,7 +506,7 @@
       "[{\"success\":false, \"errorMessage\":\"fake_error\"}]",
       browser(),
       extension_function_test_utils::NONE));
-  EXPECT_EQ(false, result.success);
+  EXPECT_FALSE(result.success);
   EXPECT_EQ("fake_error", result.error);
 
   // Test SetAutoPairingResult call with success.
@@ -518,9 +518,8 @@
       "[{\"success\":true}]",
       browser(),
       extension_function_test_utils::NONE));
-  EXPECT_EQ(true, result.success);
+  EXPECT_TRUE(result.success);
   EXPECT_TRUE(result.error.empty());
 }
 
 }  // namespace
-
diff --git a/chrome/browser/extensions/api/management/management_apitest.cc b/chrome/browser/extensions/api/management/management_apitest.cc
index f0ba11c..d64a32e 100644
--- a/chrome/browser/extensions/api/management/management_apitest.cc
+++ b/chrome/browser/extensions/api/management/management_apitest.cc
@@ -7,6 +7,7 @@
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/extensions/extension_apitest.h"
 #include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/extensions/extension_util.h"
 #include "chrome/browser/extensions/launch_util.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
@@ -310,12 +311,23 @@
   // If the ID changed, then the pref will not apply to the app.
   ASSERT_EQ(app_id, app_id_new);
 
+  unsigned expected_browser_count = 2;
+#if defined(OS_MACOSX)
+  // Without the new Bookmark Apps, Mac has no way of making standalone browser
+  // windows for apps, so it will add to the tabstrip instead.
+  EXPECT_FALSE(extensions::util::IsNewBookmarkAppsEnabled());
+  expected_browser_count = 1;
+  ASSERT_EQ(2, browser()->tab_strip_model()->count());
+#endif
   // Find the app's browser.  Opening in a new window will create
   // a new browser.
-  ASSERT_EQ(2u, chrome::GetBrowserCount(browser()->profile(),
-                                        browser()->host_desktop_type()));
-  Browser* app_browser = FindOtherBrowser(browser());
-  ASSERT_TRUE(app_browser->is_app());
+  ASSERT_EQ(expected_browser_count,
+            chrome::GetBrowserCount(browser()->profile(),
+                                    browser()->host_desktop_type()));
+  if (expected_browser_count == 2) {
+    Browser* app_browser = FindOtherBrowser(browser());
+    ASSERT_TRUE(app_browser->is_app());
+  }
 }
 
 IN_PROC_BROWSER_TEST_F(ExtensionManagementApiTest, LaunchType) {
diff --git a/chrome/browser/extensions/api/messaging/native_message_host_chromeos.cc b/chrome/browser/extensions/api/messaging/native_message_host_chromeos.cc
index 222c9e3..416db29 100644
--- a/chrome/browser/extensions/api/messaging/native_message_host_chromeos.cc
+++ b/chrome/browser/extensions/api/messaging/native_message_host_chromeos.cc
@@ -18,6 +18,7 @@
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/extensions/api/messaging/native_messaging_test_util.h"
 #include "components/policy/core/common/policy_service.h"
+#include "content/public/browser/browser_thread.h"
 #include "extensions/common/constants.h"
 #include "extensions/common/url_pattern.h"
 #include "net/url_request/url_request_context_getter.h"
@@ -96,7 +97,13 @@
   host_factory->set_policy_service(g_browser_process->policy_service());
   scoped_ptr<remoting::ChromotingHostContext> context =
       remoting::ChromotingHostContext::CreateForChromeOS(
-          make_scoped_refptr(g_browser_process->system_request_context()));
+          make_scoped_refptr(g_browser_process->system_request_context()),
+          content::BrowserThread::GetMessageLoopProxyForThread(
+              content::BrowserThread::IO),
+          content::BrowserThread::GetMessageLoopProxyForThread(
+              content::BrowserThread::UI),
+          content::BrowserThread::GetMessageLoopProxyForThread(
+              content::BrowserThread::FILE));
   scoped_ptr<NativeMessageHost> host(new remoting::It2MeNativeMessagingHost(
       context.Pass(), host_factory.Pass()));
   return host.Pass();
diff --git a/chrome/browser/extensions/api/networking_private/networking_private_apitest.cc b/chrome/browser/extensions/api/networking_private/networking_private_apitest.cc
index 0baf75a..89c2f77 100644
--- a/chrome/browser/extensions/api/networking_private/networking_private_apitest.cc
+++ b/chrome/browser/extensions/api/networking_private/networking_private_apitest.cc
@@ -20,6 +20,11 @@
 // This tests just the interface for the networkingPrivate API, i.e. it ensures
 // that the delegate methods are called as expected.
 
+// The implementations (which differ significantly between chromeos and
+// windows/mac) are tested independently in
+// networking_private_[chromeos|service_client]_apitest.cc.
+// See also crbug.com/460119.
+
 namespace {
 
 const char kFailure[] = "Failure";
diff --git a/chrome/browser/extensions/api/networking_private/networking_private_chromeos_apitest.cc b/chrome/browser/extensions/api/networking_private/networking_private_chromeos_apitest.cc
index 7d630ec..799a473 100644
--- a/chrome/browser/extensions/api/networking_private/networking_private_chromeos_apitest.cc
+++ b/chrome/browser/extensions/api/networking_private/networking_private_chromeos_apitest.cc
@@ -43,11 +43,10 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "third_party/cros_system_api/dbus/service_constants.h"
 
-// This tests the Chrome OS implementaiton of the networkingPrivate API.
-// Note: the expectations in test/data/extensions/api_test/networking/test.js
-// are shared between this and the Win/Mac tests. TODO(stevenjb): Develop
-// a mechanism to specify the test expecations from here to eliminate that
-// dependency.
+// This tests the Chrome OS implementation of the networkingPrivate API
+// (NetworkingPrivateChromeOS). Note: The test expectations for chromeos, and
+// win/mac (NetworkingPrivateServiceClient) are different to reflect the
+// different implementations, but should be kept similar where possible.
 
 using testing::Return;
 using testing::_;
@@ -137,7 +136,7 @@
         device_test_(NULL) {}
 
   bool RunNetworkingSubtest(const std::string& subtest) {
-    return RunExtensionSubtest("networking",
+    return RunExtensionSubtest("networking_private/chromeos",
                                "main.html?" + subtest,
                                kFlagEnableFileAccess | kFlagLoadAsComponent);
   }
diff --git a/chrome/browser/extensions/api/networking_private/networking_private_service_client_apitest.cc b/chrome/browser/extensions/api/networking_private/networking_private_service_client_apitest.cc
index c6c1e53..6de03c9 100644
--- a/chrome/browser/extensions/api/networking_private/networking_private_service_client_apitest.cc
+++ b/chrome/browser/extensions/api/networking_private/networking_private_service_client_apitest.cc
@@ -20,6 +20,13 @@
 #include "extensions/common/switches.h"
 #include "testing/gmock/include/gmock/gmock.h"
 
+// This tests the Windows / Mac implementation of the networkingPrivate API
+// (NetworkingPrivateServiceClient). Note, only a subset of the
+// networkingPrivate API is implemented in NetworkingPrivateServiceClient, so
+// this uses its own set of test expectations to reflect that. The expectations
+// should be kept similar to the ChromeOS (primary) implementation as much as
+// possible. See also crbug.com/460119.
+
 using testing::Return;
 using testing::_;
 
@@ -29,12 +36,6 @@
 using extensions::NetworkingPrivateEventRouterFactory;
 using extensions::NetworkingPrivateServiceClient;
 
-// This tests the Windows / Mac implementation of the networkingPrivate API.
-// Note: the expectations in test/data/extensions/api_test/networking/test.js
-// are shared between this and the Chrome OS tests. TODO(stevenjb): Develop
-// a mechanism to specify the test expectations from here to eliminate that
-// dependency.
-
 namespace {
 
 // Stub Verify* methods implementation to satisfy expectations of
@@ -69,7 +70,7 @@
   NetworkingPrivateServiceClientApiTest() {}
 
   bool RunNetworkingSubtest(const std::string& subtest) {
-    return RunExtensionSubtest("networking",
+    return RunExtensionSubtest("networking_private/service_client",
                                "main.html?" + subtest,
                                kFlagEnableFileAccess | kFlagLoadAsComponent);
   }
@@ -134,14 +135,14 @@
       << message_;
 }
 
-// TODO(stevenjb/mef): Fix these, crbug.com/371442.
+// TODO(stevenjb/mef): Implement |limit| to fix this, crbug.com/371442.
 IN_PROC_BROWSER_TEST_F(NetworkingPrivateServiceClientApiTest,
                        DISABLED_GetNetworks) {
   EXPECT_TRUE(RunNetworkingSubtest("getNetworks")) << message_;
 }
 
 IN_PROC_BROWSER_TEST_F(NetworkingPrivateServiceClientApiTest,
-                       DISABLED_GetVisibleNetworks) {
+                       GetVisibleNetworks) {
   EXPECT_TRUE(RunNetworkingSubtest("getVisibleNetworks")) << message_;
 }
 
@@ -150,9 +151,8 @@
   EXPECT_TRUE(RunNetworkingSubtest("getVisibleNetworksWifi")) << message_;
 }
 
-// TODO(stevenjb/mef): Fix this, crbug.com/371442.
 IN_PROC_BROWSER_TEST_F(NetworkingPrivateServiceClientApiTest,
-                       DISABLED_RequestNetworkScan) {
+                       RequestNetworkScan) {
   EXPECT_TRUE(RunNetworkingSubtest("requestNetworkScan")) << message_;
 }
 
@@ -195,9 +195,8 @@
       << message_;
 }
 
-// TODO(stevenjb/mef): Fix this, crbug.com/371442.
 IN_PROC_BROWSER_TEST_F(NetworkingPrivateServiceClientApiTest,
-                       DISABLED_OnNetworkListChangedEvent) {
+                       OnNetworkListChangedEvent) {
   EXPECT_TRUE(RunNetworkingSubtest("onNetworkListChangedEvent")) << message_;
 }
 
diff --git a/chrome/browser/extensions/api/platform_keys/platform_keys_api.cc b/chrome/browser/extensions/api/platform_keys/platform_keys_api.cc
index f3ad6512a..15f7a9f1 100644
--- a/chrome/browser/extensions/api/platform_keys/platform_keys_api.cc
+++ b/chrome/browser/extensions/api/platform_keys/platform_keys_api.cc
@@ -114,9 +114,10 @@
     return RespondNow(Error(kErrorInvalidX509Cert));
 
   PublicKeyInfo key_info;
-  if (!chromeos::platform_keys::GetPublicKey(
-          cert_x509, &key_info.public_key_spki_der, &key_info.key_type,
-          &key_info.key_size_bits) ||
+  key_info.public_key_spki_der =
+      chromeos::platform_keys::GetSubjectPublicKeyInfo(cert_x509);
+  if (!chromeos::platform_keys::GetPublicKey(cert_x509, &key_info.key_type,
+                                             &key_info.key_size_bits) ||
       key_info.key_type != net::X509Certificate::kPublicKeyTypeRSA) {
     return RespondNow(Error(kErrorAlgorithmNotSupported));
   }
@@ -154,10 +155,11 @@
   }
 
   service->SelectClientCertificates(
-      request, extension_id(),
+      request, params->details.interactive, extension_id(),
       base::Bind(&PlatformKeysInternalSelectClientCertificatesFunction::
                      OnSelectedCertificates,
-                 this));
+                 this),
+      GetAssociatedWebContents());
   return RespondLater();
 }
 
@@ -173,9 +175,10 @@
   std::vector<linked_ptr<api_pk::Match>> result_matches;
   for (const scoped_refptr<net::X509Certificate>& match : *matches) {
     PublicKeyInfo key_info;
-    if (!chromeos::platform_keys::GetPublicKey(
-            match, &key_info.public_key_spki_der, &key_info.key_type,
-            &key_info.key_size_bits)) {
+    key_info.public_key_spki_der =
+        chromeos::platform_keys::GetSubjectPublicKeyInfo(match);
+    if (!chromeos::platform_keys::GetPublicKey(match, &key_info.key_type,
+                                               &key_info.key_size_bits)) {
       LOG(ERROR) << "Could not retrieve public key info.";
       continue;
     }
diff --git a/chrome/browser/extensions/api/platform_keys/platform_keys_apitest_nss.cc b/chrome/browser/extensions/api/platform_keys/platform_keys_apitest_nss.cc
index c15fb5e..2c51e27b 100644
--- a/chrome/browser/extensions/api/platform_keys/platform_keys_apitest_nss.cc
+++ b/chrome/browser/extensions/api/platform_keys/platform_keys_apitest_nss.cc
@@ -100,8 +100,8 @@
       loop.Run();
     }
 
-    chromeos::PlatformKeysServiceFactory::GetForBrowserContext(
-        browser()->profile())->DisablePermissionCheckForTesting();
+    base::FilePath extension_path = test_data_dir_.AppendASCII("platform_keys");
+    extension_ = LoadExtension(extension_path);
   }
 
   void TearDownOnMainThread() override {
@@ -115,22 +115,47 @@
     loop.Run();
   }
 
+  chromeos::PlatformKeysService* GetPlatformKeysService() {
+    return chromeos::PlatformKeysServiceFactory::GetForBrowserContext(
+        browser()->profile());
+  }
+
+  bool RunExtensionTest(const std::string& test_suite_name) {
+    // By default, the system token is not available.
+    std::string system_token_availability;
+
+    // Only if the current user is of the same domain as the device is enrolled
+    // to, the system token is available to the extension.
+    if (GetParam().device_status_ == DEVICE_STATUS_ENROLLED &&
+        GetParam().user_affiliation_ == USER_AFFILIATION_ENROLLED_DOMAIN) {
+      system_token_availability = "systemTokenEnabled";
+    }
+
+    GURL url = extension_->GetResourceURL(base::StringPrintf(
+        "basic.html?%s#%s", system_token_availability.c_str(),
+        test_suite_name.c_str()));
+    return RunExtensionSubtest("platform_keys", url.spec());
+  }
+
+ protected:
+  scoped_refptr<net::X509Certificate> client_cert1_;
+  scoped_refptr<net::X509Certificate> client_cert2_;
+  const extensions::Extension* extension_;
+
  private:
   void SetupTestCerts(const base::Closure& done_callback,
                       net::NSSCertDatabase* cert_db) {
-    scoped_refptr<net::X509Certificate> client_cert1 =
-        net::ImportClientCertAndKeyFromFile(net::GetTestCertsDirectory(),
-                                            "client_1.pem", "client_1.pk8",
-                                            cert_db->GetPrivateSlot().get());
-    ASSERT_TRUE(client_cert1.get());
+    client_cert1_ = net::ImportClientCertAndKeyFromFile(
+        net::GetTestCertsDirectory(), "client_1.pem", "client_1.pk8",
+        cert_db->GetPrivateSlot().get());
+    ASSERT_TRUE(client_cert1_.get());
 
     // Import a second client cert signed by another CA than client_1 into the
     // system wide key slot.
-    scoped_refptr<net::X509Certificate> client_cert2 =
-        net::ImportClientCertAndKeyFromFile(net::GetTestCertsDirectory(),
-                                            "client_2.pem", "client_2.pk8",
-                                            test_system_slot_->slot());
-    ASSERT_TRUE(client_cert2.get());
+    client_cert2_ = net::ImportClientCertAndKeyFromFile(
+        net::GetTestCertsDirectory(), "client_2.pem", "client_2.pk8",
+        test_system_slot_->slot());
+    ASSERT_TRUE(client_cert2_.get());
 
     done_callback.Run();
   }
@@ -157,22 +182,62 @@
   scoped_ptr<crypto::ScopedTestSystemNSSKeySlot> test_system_slot_;
 };
 
-}  // namespace
+class TestSelectDelegate
+    : public chromeos::PlatformKeysService::SelectDelegate {
+ public:
+  explicit TestSelectDelegate(
+      scoped_refptr<net::X509Certificate> cert_to_select)
+      : cert_to_select_(cert_to_select) {}
+  ~TestSelectDelegate() override {}
 
-IN_PROC_BROWSER_TEST_P(PlatformKeysTest, Basic) {
-  // By default, the system token is not available.
-  std::string system_token_availability;
-
-  // Only if the current user is of the same domain as the device is enrolled
-  // to, the system token is available to the extension.
-  if (GetParam().device_status_ == DEVICE_STATUS_ENROLLED &&
-      GetParam().user_affiliation_ == USER_AFFILIATION_ENROLLED_DOMAIN) {
-    system_token_availability = "systemTokenEnabled";
+  void Select(const std::string& extension_id,
+              const net::CertificateList& certs,
+              const CertificateSelectedCallback& callback,
+              content::WebContents* web_contents,
+              content::BrowserContext* context) override {
+    ASSERT_TRUE(web_contents);
+    ASSERT_TRUE(context);
+    if (!cert_to_select_) {
+      callback.Run(nullptr /* no cert */);
+      return;
+    }
+    scoped_refptr<net::X509Certificate> selection;
+    for (scoped_refptr<net::X509Certificate> cert : certs) {
+      if (cert->Equals(cert_to_select_.get())) {
+        selection = cert;
+        break;
+      }
+    }
+    callback.Run(selection);
   }
 
-  ASSERT_TRUE(RunExtensionSubtest("platform_keys",
-                                  "basic.html?" + system_token_availability))
-      << message_;
+ private:
+  scoped_refptr<net::X509Certificate> cert_to_select_;
+};
+
+}  // namespace
+
+// Basic tests that start with already granted permissions for both client_cert1
+// and client_cert2.
+// On interactive calls, the simulated user does not select any cert.
+IN_PROC_BROWSER_TEST_P(PlatformKeysTest, Basic) {
+  GetPlatformKeysService()->SetSelectDelegate(
+      make_scoped_ptr(new TestSelectDelegate(nullptr /* select no cert */)));
+  GetPlatformKeysService()->GrantUnlimitedSignPermission(extension_->id(),
+                                                         client_cert1_);
+  GetPlatformKeysService()->GrantUnlimitedSignPermission(extension_->id(),
+                                                         client_cert2_);
+
+  ASSERT_TRUE(RunExtensionTest("basicTests")) << message_;
+}
+
+// This permission test starts without any granted permissions.
+// On interactive calls, the simulated user selects client_1, if matching.
+IN_PROC_BROWSER_TEST_P(PlatformKeysTest, Permissions) {
+  GetPlatformKeysService()->SetSelectDelegate(
+      make_scoped_ptr(new TestSelectDelegate(client_cert1_)));
+
+  ASSERT_TRUE(RunExtensionTest("permissionTests")) << message_;
 }
 
 INSTANTIATE_TEST_CASE_P(
diff --git a/chrome/browser/extensions/api/preference/preference_api.cc b/chrome/browser/extensions/api/preference/preference_api.cc
index c9f5c254..11d79528 100644
--- a/chrome/browser/extensions/api/preference/preference_api.cc
+++ b/chrome/browser/extensions/api/preference/preference_api.cc
@@ -112,6 +112,10 @@
      APIPermission::kPrivacy, APIPermission::kPrivacy},
     {"translationServiceEnabled", prefs::kEnableTranslate,
      APIPermission::kPrivacy, APIPermission::kPrivacy},
+#if defined(ENABLE_WEBRTC)
+    {"webRTCMultipleRoutesEnabled", prefs::kWebRTCMultipleRoutesEnabled,
+     APIPermission::kPrivacy, APIPermission::kPrivacy},
+#endif
     // accessibilityFeatures.animationPolicy is available for
     // all platforms but the others from accessibilityFeatures
     // is only available for OS_CHROMEOS.
diff --git a/chrome/browser/extensions/api/preference/preference_apitest.cc b/chrome/browser/extensions/api/preference/preference_apitest.cc
index 4af18bf..1ed8201 100644
--- a/chrome/browser/extensions/api/preference/preference_apitest.cc
+++ b/chrome/browser/extensions/api/preference/preference_apitest.cc
@@ -118,6 +118,9 @@
                     false);
   prefs->SetBoolean(prefs::kSafeBrowsingEnabled, false);
   prefs->SetBoolean(prefs::kSearchSuggestEnabled, false);
+#if defined(ENABLE_WEBRTC)
+  prefs->SetBoolean(prefs::kWebRTCMultipleRoutesEnabled, false);
+#endif
 
   const char kExtensionPath[] = "preference/standard";
 
diff --git a/chrome/browser/extensions/api/runtime/chrome_runtime_api_delegate.cc b/chrome/browser/extensions/api/runtime/chrome_runtime_api_delegate.cc
index 72cc78b..1e3eb46 100644
--- a/chrome/browser/extensions/api/runtime/chrome_runtime_api_delegate.cc
+++ b/chrome/browser/extensions/api/runtime/chrome_runtime_api_delegate.cc
@@ -11,6 +11,7 @@
 #include "base/metrics/histogram.h"
 #include "base/time/time.h"
 #include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/extensions/extension_tab_util.h"
 #include "chrome/browser/extensions/updater/extension_updater.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser_finder.h"
@@ -240,6 +241,15 @@
   return false;
 }
 
+bool ChromeRuntimeAPIDelegate::OpenOptionsPage(const Extension* extension) {
+  Profile* profile = Profile::FromBrowserContext(browser_context_);
+  Browser* browser =
+      chrome::FindLastActiveWithProfile(profile, chrome::GetActiveDesktop());
+  if (!browser)
+    return false;
+  return extensions::ExtensionTabUtil::OpenOptionsPage(extension, browser);
+}
+
 void ChromeRuntimeAPIDelegate::Observe(
     int type,
     const content::NotificationSource& source,
diff --git a/chrome/browser/extensions/api/runtime/chrome_runtime_api_delegate.h b/chrome/browser/extensions/api/runtime/chrome_runtime_api_delegate.h
index 4684904f..952a0fd 100644
--- a/chrome/browser/extensions/api/runtime/chrome_runtime_api_delegate.h
+++ b/chrome/browser/extensions/api/runtime/chrome_runtime_api_delegate.h
@@ -50,6 +50,7 @@
   bool GetPlatformInfo(
       extensions::core_api::runtime::PlatformInfo* info) override;
   bool RestartDevice(std::string* error_message) override;
+  bool OpenOptionsPage(const extensions::Extension* extension) override;
 
   // content::NotificationObserver implementation.
   void Observe(int type,
diff --git a/chrome/browser/extensions/api/storage/managed_value_store_cache.cc b/chrome/browser/extensions/api/storage/managed_value_store_cache.cc
index 2d3b0cc..3def5bb2 100644
--- a/chrome/browser/extensions/api/storage/managed_value_store_cache.cc
+++ b/chrome/browser/extensions/api/storage/managed_value_store_cache.cc
@@ -236,8 +236,9 @@
     const scoped_refptr<SettingsStorageFactory>& factory,
     const scoped_refptr<SettingsObserverList>& observers)
     : profile_(Profile::FromBrowserContext(context)),
-      policy_service_(policy::ProfilePolicyConnectorFactory::GetForProfile(
-                          profile_)->policy_service()),
+      policy_service_(
+          policy::ProfilePolicyConnectorFactory::GetForBrowserContext(context)
+              ->policy_service()),
       storage_factory_(factory),
       observers_(observers),
       base_path_(profile_->GetPath().AppendASCII(
diff --git a/chrome/browser/extensions/api/webrtc_logging_private/webrtc_logging_private_api.cc b/chrome/browser/extensions/api/webrtc_logging_private/webrtc_logging_private_api.cc
index f2d762a..04b908f 100644
--- a/chrome/browser/extensions/api/webrtc_logging_private/webrtc_logging_private_api.cc
+++ b/chrome/browser/extensions/api/webrtc_logging_private/webrtc_logging_private_api.cc
@@ -2,16 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include <string>
-
 #include "chrome/browser/extensions/api/webrtc_logging_private/webrtc_logging_private_api.h"
 
+#include "base/hash.h"
 #include "base/logging.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/supports_user_data.h"
 #include "chrome/browser/extensions/api/tabs/tabs_constants.h"
 #include "chrome/browser/extensions/extension_tab_util.h"
-#include "chrome/browser/media/webrtc_logging_handler_host.h"
 #include "chrome/browser/profiles/profile.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/render_process_host.h"
@@ -21,22 +19,30 @@
 #include "extensions/browser/process_manager.h"
 #include "extensions/common/error_utils.h"
 
-using content::BrowserThread;
-
 namespace extensions {
 
-namespace SetMetaData = api::webrtc_logging_private::SetMetaData;
-namespace Start = api::webrtc_logging_private::Start;
-namespace SetUploadOnRenderClose =
-    api::webrtc_logging_private::SetUploadOnRenderClose;
-namespace Stop = api::webrtc_logging_private::Stop;
-namespace Upload = api::webrtc_logging_private::Upload;
-namespace Discard = api::webrtc_logging_private::Discard;
-namespace StartRtpDump = api::webrtc_logging_private::StartRtpDump;
-namespace StopRtpDump = api::webrtc_logging_private::StopRtpDump;
-
 using api::webrtc_logging_private::MetaDataEntry;
 using api::webrtc_logging_private::RequestInfo;
+using content::BrowserThread;
+
+namespace Discard = api::webrtc_logging_private::Discard;
+namespace SetMetaData = api::webrtc_logging_private::SetMetaData;
+namespace SetUploadOnRenderClose =
+    api::webrtc_logging_private::SetUploadOnRenderClose;
+namespace Start = api::webrtc_logging_private::Start;
+namespace StartRtpDump = api::webrtc_logging_private::StartRtpDump;
+namespace Stop = api::webrtc_logging_private::Stop;
+namespace StopRtpDump = api::webrtc_logging_private::StopRtpDump;
+namespace Store = api::webrtc_logging_private::Store;
+namespace Upload = api::webrtc_logging_private::Upload;
+namespace UploadStored = api::webrtc_logging_private::UploadStored;
+
+namespace {
+std::string HashIdWithOrigin(const std::string& security_origin,
+                             const std::string& log_id) {
+  return base::UintToString(base::Hash(security_origin + log_id));
+}
+}  // namespace
 
 content::RenderProcessHost* WebrtcLoggingPrivateFunction::RphFromRequest(
     const RequestInfo& request, const std::string& security_origin) {
@@ -74,41 +80,27 @@
   return contents->GetRenderProcessHost();
 }
 
-WebrtcLoggingPrivateSetMetaDataFunction::
-WebrtcLoggingPrivateSetMetaDataFunction() {}
-
-WebrtcLoggingPrivateSetMetaDataFunction::
-~WebrtcLoggingPrivateSetMetaDataFunction() {}
-
-bool WebrtcLoggingPrivateSetMetaDataFunction::RunAsync() {
-  scoped_ptr<SetMetaData::Params> params(SetMetaData::Params::Create(*args_));
-  EXTENSION_FUNCTION_VALIDATE(params.get());
-
-  content::RenderProcessHost* host =
-      RphFromRequest(params->request, params->security_origin);
+scoped_refptr<WebRtcLoggingHandlerHost>
+WebrtcLoggingPrivateFunction::LoggingHandlerFromRequest(
+    const api::webrtc_logging_private::RequestInfo& request,
+    const std::string& security_origin) {
+  content::RenderProcessHost* host = RphFromRequest(request, security_origin);
   if (!host)
-    return false;
+    return nullptr;
 
-  scoped_refptr<WebRtcLoggingHandlerHost> webrtc_logging_handler_host(
-      base::UserDataAdapter<WebRtcLoggingHandlerHost>::Get(host, host));
-
-  std::map<std::string, std::string> meta_data;
-  for (std::vector<linked_ptr<MetaDataEntry> >::const_iterator it =
-       params->meta_data.begin(); it != params->meta_data.end(); ++it) {
-    meta_data[(*it)->key] = (*it)->value;
-  }
-
-  WebRtcLoggingHandlerHost::GenericDoneCallback callback = base::Bind(
-      &WebrtcLoggingPrivateSetMetaDataFunction::SetMetaDataCallback, this);
-
-  BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, base::Bind(
-      &WebRtcLoggingHandlerHost::SetMetaData, webrtc_logging_handler_host,
-      meta_data, callback));
-
-  return true;
+  return base::UserDataAdapter<WebRtcLoggingHandlerHost>::Get(host, host);
 }
 
-void WebrtcLoggingPrivateSetMetaDataFunction::SetMetaDataCallback(
+scoped_refptr<WebRtcLoggingHandlerHost>
+WebrtcLoggingPrivateFunctionWithGenericCallback::PrepareTask(
+    const RequestInfo& request, const std::string& security_origin,
+    WebRtcLoggingHandlerHost::GenericDoneCallback* callback) {
+  *callback = base::Bind(
+      &WebrtcLoggingPrivateFunctionWithGenericCallback::FireCallback, this);
+  return LoggingHandlerFromRequest(request, security_origin);
+}
+
+void WebrtcLoggingPrivateFunctionWithGenericCallback::FireCallback(
     bool success, const std::string& error_message) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   if (!success)
@@ -116,126 +108,7 @@
   SendResponse(success);
 }
 
-WebrtcLoggingPrivateStartFunction::WebrtcLoggingPrivateStartFunction() {}
-
-WebrtcLoggingPrivateStartFunction::~WebrtcLoggingPrivateStartFunction() {}
-
-bool WebrtcLoggingPrivateStartFunction::RunAsync() {
-  scoped_ptr<Start::Params> params(Start::Params::Create(*args_));
-  EXTENSION_FUNCTION_VALIDATE(params.get());
-
-  content::RenderProcessHost* host =
-      RphFromRequest(params->request, params->security_origin);
-  if (!host)
-    return false;
-
-  scoped_refptr<WebRtcLoggingHandlerHost> webrtc_logging_handler_host(
-      base::UserDataAdapter<WebRtcLoggingHandlerHost>::Get(host, host));
-
-  WebRtcLoggingHandlerHost::GenericDoneCallback callback = base::Bind(
-      &WebrtcLoggingPrivateStartFunction::StartCallback, this);
-
-  BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, base::Bind(
-      &WebRtcLoggingHandlerHost::StartLogging, webrtc_logging_handler_host,
-      callback));
-
-  return true;
-}
-
-void WebrtcLoggingPrivateStartFunction::StartCallback(
-    bool success, const std::string& error_message) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  if (!success)
-    SetError(error_message);
-  SendResponse(success);
-}
-
-WebrtcLoggingPrivateSetUploadOnRenderCloseFunction::
-WebrtcLoggingPrivateSetUploadOnRenderCloseFunction() {}
-
-WebrtcLoggingPrivateSetUploadOnRenderCloseFunction::
-~WebrtcLoggingPrivateSetUploadOnRenderCloseFunction() {}
-
-bool WebrtcLoggingPrivateSetUploadOnRenderCloseFunction::RunAsync() {
-  scoped_ptr<SetUploadOnRenderClose::Params> params(
-      SetUploadOnRenderClose::Params::Create(*args_));
-  EXTENSION_FUNCTION_VALIDATE(params.get());
-
-  content::RenderProcessHost* host =
-      RphFromRequest(params->request, params->security_origin);
-  if (!host)
-    return false;
-
-  scoped_refptr<WebRtcLoggingHandlerHost> webrtc_logging_handler_host(
-      base::UserDataAdapter<WebRtcLoggingHandlerHost>::Get(host, host));
-
-  webrtc_logging_handler_host->set_upload_log_on_render_close(
-      params->should_upload);
-
-  return true;
-}
-
-WebrtcLoggingPrivateStopFunction::WebrtcLoggingPrivateStopFunction() {}
-
-WebrtcLoggingPrivateStopFunction::~WebrtcLoggingPrivateStopFunction() {}
-
-bool WebrtcLoggingPrivateStopFunction::RunAsync() {
-  scoped_ptr<Stop::Params> params(Stop::Params::Create(*args_));
-  EXTENSION_FUNCTION_VALIDATE(params.get());
-
-  content::RenderProcessHost* host =
-      RphFromRequest(params->request, params->security_origin);
-  if (!host)
-    return false;
-
-  scoped_refptr<WebRtcLoggingHandlerHost> webrtc_logging_handler_host(
-      base::UserDataAdapter<WebRtcLoggingHandlerHost>::Get(host, host));
-
-  WebRtcLoggingHandlerHost::GenericDoneCallback callback = base::Bind(
-      &WebrtcLoggingPrivateStopFunction::StopCallback, this);
-
-  BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, base::Bind(
-      &WebRtcLoggingHandlerHost::StopLogging, webrtc_logging_handler_host,
-      callback));
-
-  return true;
-}
-
-void WebrtcLoggingPrivateStopFunction::StopCallback(
-    bool success, const std::string& error_message) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  if (!success)
-    SetError(error_message);
-  SendResponse(success);
-}
-
-WebrtcLoggingPrivateUploadFunction::WebrtcLoggingPrivateUploadFunction() {}
-
-WebrtcLoggingPrivateUploadFunction::~WebrtcLoggingPrivateUploadFunction() {}
-
-bool WebrtcLoggingPrivateUploadFunction::RunAsync() {
-  scoped_ptr<Upload::Params> params(Upload::Params::Create(*args_));
-  EXTENSION_FUNCTION_VALIDATE(params.get());
-
-  content::RenderProcessHost* host =
-      RphFromRequest(params->request, params->security_origin);
-  if (!host)
-    return false;
-
-  scoped_refptr<WebRtcLoggingHandlerHost> webrtc_logging_handler_host(
-      base::UserDataAdapter<WebRtcLoggingHandlerHost>::Get(host, host));
-
-  WebRtcLoggingHandlerHost::UploadDoneCallback callback = base::Bind(
-      &WebrtcLoggingPrivateUploadFunction::UploadCallback, this);
-
-  BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, base::Bind(
-      &WebRtcLoggingHandlerHost::UploadLog, webrtc_logging_handler_host,
-      callback));
-
-  return true;
-}
-
-void WebrtcLoggingPrivateUploadFunction::UploadCallback(
+void WebrtcLoggingPrivateFunctionWithUploadCallback::FireCallback(
     bool success, const std::string& report_id,
     const std::string& error_message) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
@@ -249,25 +122,147 @@
   SendResponse(success);
 }
 
-WebrtcLoggingPrivateDiscardFunction::WebrtcLoggingPrivateDiscardFunction() {}
+bool WebrtcLoggingPrivateSetMetaDataFunction::RunAsync() {
+  scoped_ptr<SetMetaData::Params> params(SetMetaData::Params::Create(*args_));
+  EXTENSION_FUNCTION_VALIDATE(params.get());
 
-WebrtcLoggingPrivateDiscardFunction::~WebrtcLoggingPrivateDiscardFunction() {}
+  WebRtcLoggingHandlerHost::GenericDoneCallback callback;
+  scoped_refptr<WebRtcLoggingHandlerHost> webrtc_logging_handler_host =
+      PrepareTask(params->request, params->security_origin, &callback);
+  if (!webrtc_logging_handler_host.get())
+    return false;
+
+  scoped_ptr<MetaDataMap> meta_data(new MetaDataMap());
+  for (const linked_ptr<MetaDataEntry>& entry : params->meta_data)
+    (*meta_data.get())[entry->key] = entry->value;
+
+  BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, base::Bind(
+      &WebRtcLoggingHandlerHost::SetMetaData, webrtc_logging_handler_host,
+      base::Passed(&meta_data), callback));
+
+  return true;
+}
+
+bool WebrtcLoggingPrivateStartFunction::RunAsync() {
+  scoped_ptr<Start::Params> params(Start::Params::Create(*args_));
+  EXTENSION_FUNCTION_VALIDATE(params.get());
+
+  WebRtcLoggingHandlerHost::GenericDoneCallback callback;
+  scoped_refptr<WebRtcLoggingHandlerHost> webrtc_logging_handler_host =
+      PrepareTask(params->request, params->security_origin, &callback);
+  if (!webrtc_logging_handler_host.get())
+    return false;
+
+  BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, base::Bind(
+      &WebRtcLoggingHandlerHost::StartLogging, webrtc_logging_handler_host,
+      callback));
+
+  return true;
+}
+
+bool WebrtcLoggingPrivateSetUploadOnRenderCloseFunction::RunAsync() {
+  scoped_ptr<SetUploadOnRenderClose::Params> params(
+      SetUploadOnRenderClose::Params::Create(*args_));
+  EXTENSION_FUNCTION_VALIDATE(params.get());
+
+  scoped_refptr<WebRtcLoggingHandlerHost> webrtc_logging_handler_host(
+      LoggingHandlerFromRequest(params->request, params->security_origin));
+  if (!webrtc_logging_handler_host.get())
+    return false;
+
+  webrtc_logging_handler_host->set_upload_log_on_render_close(
+      params->should_upload);
+
+  return true;
+}
+
+bool WebrtcLoggingPrivateStopFunction::RunAsync() {
+  scoped_ptr<Stop::Params> params(Stop::Params::Create(*args_));
+  EXTENSION_FUNCTION_VALIDATE(params.get());
+
+  WebRtcLoggingHandlerHost::GenericDoneCallback callback;
+  scoped_refptr<WebRtcLoggingHandlerHost> webrtc_logging_handler_host =
+      PrepareTask(params->request, params->security_origin, &callback);
+  if (!webrtc_logging_handler_host.get())
+    return false;
+
+  BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, base::Bind(
+      &WebRtcLoggingHandlerHost::StopLogging, webrtc_logging_handler_host,
+      callback));
+
+  return true;
+}
+
+bool WebrtcLoggingPrivateStoreFunction::RunAsync() {
+  scoped_ptr<Store::Params> params(Store::Params::Create(*args_));
+  EXTENSION_FUNCTION_VALIDATE(params.get());
+
+  WebRtcLoggingHandlerHost::GenericDoneCallback callback;
+  scoped_refptr<WebRtcLoggingHandlerHost> webrtc_logging_handler_host =
+      PrepareTask(params->request, params->security_origin, &callback);
+  if (!webrtc_logging_handler_host.get())
+    return false;
+
+  const std::string local_log_id(HashIdWithOrigin(params->security_origin,
+                                                  params->log_id));
+
+  BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, base::Bind(
+      &WebRtcLoggingHandlerHost::StoreLog,
+      webrtc_logging_handler_host, local_log_id, callback));
+
+  return true;
+}
+
+bool WebrtcLoggingPrivateUploadStoredFunction::RunAsync() {
+  scoped_ptr<UploadStored::Params> params(UploadStored::Params::Create(*args_));
+  EXTENSION_FUNCTION_VALIDATE(params.get());
+
+  scoped_refptr<WebRtcLoggingHandlerHost> logging_handler(
+      LoggingHandlerFromRequest(params->request, params->security_origin));
+  if (!logging_handler.get())
+    return false;
+
+  WebRtcLoggingHandlerHost::UploadDoneCallback callback = base::Bind(
+      &WebrtcLoggingPrivateUploadStoredFunction::FireCallback, this);
+
+  const std::string local_log_id(HashIdWithOrigin(params->security_origin,
+                                                  params->log_id));
+
+  BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, base::Bind(
+      &WebRtcLoggingHandlerHost::UploadStoredLog, logging_handler, local_log_id,
+      callback));
+
+  return true;
+}
+
+bool WebrtcLoggingPrivateUploadFunction::RunAsync() {
+  scoped_ptr<Upload::Params> params(Upload::Params::Create(*args_));
+  EXTENSION_FUNCTION_VALIDATE(params.get());
+
+  scoped_refptr<WebRtcLoggingHandlerHost> logging_handler(
+      LoggingHandlerFromRequest(params->request, params->security_origin));
+  if (!logging_handler.get())
+    return false;
+
+  WebRtcLoggingHandlerHost::UploadDoneCallback callback = base::Bind(
+      &WebrtcLoggingPrivateUploadFunction::FireCallback, this);
+
+  BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, base::Bind(
+      &WebRtcLoggingHandlerHost::UploadLog, logging_handler, callback));
+
+  return true;
+}
 
 bool WebrtcLoggingPrivateDiscardFunction::RunAsync() {
   scoped_ptr<Discard::Params> params(Discard::Params::Create(*args_));
   EXTENSION_FUNCTION_VALIDATE(params.get());
 
-  content::RenderProcessHost* host =
-      RphFromRequest(params->request, params->security_origin);
-  if (!host)
+  WebRtcLoggingHandlerHost::GenericDoneCallback callback;
+  scoped_refptr<WebRtcLoggingHandlerHost> webrtc_logging_handler_host =
+      PrepareTask(params->request, params->security_origin, &callback);
+  if (!webrtc_logging_handler_host.get())
     return false;
 
-  scoped_refptr<WebRtcLoggingHandlerHost> webrtc_logging_handler_host(
-      base::UserDataAdapter<WebRtcLoggingHandlerHost>::Get(host, host));
-
-  WebRtcLoggingHandlerHost::GenericDoneCallback callback = base::Bind(
-      &WebrtcLoggingPrivateDiscardFunction::DiscardCallback, this);
-
   BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, base::Bind(
       &WebRtcLoggingHandlerHost::DiscardLog, webrtc_logging_handler_host,
       callback));
@@ -275,26 +270,12 @@
   return true;
 }
 
-void WebrtcLoggingPrivateDiscardFunction::DiscardCallback(
-    bool success, const std::string& error_message) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  if (!success)
-    SetError(error_message);
-  SendResponse(success);
-}
-
-WebrtcLoggingPrivateStartRtpDumpFunction::
-    WebrtcLoggingPrivateStartRtpDumpFunction() {}
-
-WebrtcLoggingPrivateStartRtpDumpFunction::
-    ~WebrtcLoggingPrivateStartRtpDumpFunction() {}
-
 bool WebrtcLoggingPrivateStartRtpDumpFunction::RunAsync() {
   scoped_ptr<StartRtpDump::Params> params(StartRtpDump::Params::Create(*args_));
   EXTENSION_FUNCTION_VALIDATE(params.get());
 
   if (!params->incoming && !params->outgoing) {
-    StartRtpDumpCallback(false, "Either incoming or outgoing must be true.");
+    FireCallback(false, "Either incoming or outgoing must be true.");
     return true;
   }
 
@@ -312,7 +293,7 @@
       base::UserDataAdapter<WebRtcLoggingHandlerHost>::Get(host, host));
 
   WebRtcLoggingHandlerHost::GenericDoneCallback callback = base::Bind(
-      &WebrtcLoggingPrivateStartRtpDumpFunction::StartRtpDumpCallback, this);
+      &WebrtcLoggingPrivateStartRtpDumpFunction::FireCallback, this);
 
   // This call cannot fail.
   content::RenderProcessHost::WebRtcStopRtpDumpCallback stop_callback =
@@ -331,27 +312,12 @@
   return true;
 }
 
-void WebrtcLoggingPrivateStartRtpDumpFunction::StartRtpDumpCallback(
-    bool success,
-    const std::string& error_message) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  if (!success)
-    SetError(error_message);
-  SendResponse(success);
-}
-
-WebrtcLoggingPrivateStopRtpDumpFunction::
-    WebrtcLoggingPrivateStopRtpDumpFunction() {}
-
-WebrtcLoggingPrivateStopRtpDumpFunction::
-    ~WebrtcLoggingPrivateStopRtpDumpFunction() {}
-
 bool WebrtcLoggingPrivateStopRtpDumpFunction::RunAsync() {
   scoped_ptr<StopRtpDump::Params> params(StopRtpDump::Params::Create(*args_));
   EXTENSION_FUNCTION_VALIDATE(params.get());
 
   if (!params->incoming && !params->outgoing) {
-    StopRtpDumpCallback(false, "Either incoming or outgoing must be true.");
+    FireCallback(false, "Either incoming or outgoing must be true.");
     return true;
   }
 
@@ -369,7 +335,7 @@
       base::UserDataAdapter<WebRtcLoggingHandlerHost>::Get(host, host));
 
   WebRtcLoggingHandlerHost::GenericDoneCallback callback = base::Bind(
-      &WebrtcLoggingPrivateStopRtpDumpFunction::StopRtpDumpCallback, this);
+      &WebrtcLoggingPrivateStopRtpDumpFunction::FireCallback, this);
 
   BrowserThread::PostTask(BrowserThread::IO,
                           FROM_HERE,
@@ -380,13 +346,4 @@
   return true;
 }
 
-void WebrtcLoggingPrivateStopRtpDumpFunction::StopRtpDumpCallback(
-    bool success,
-    const std::string& error_message) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  if (!success)
-    SetError(error_message);
-  SendResponse(success);
-}
-
 }  // namespace extensions
diff --git a/chrome/browser/extensions/api/webrtc_logging_private/webrtc_logging_private_api.h b/chrome/browser/extensions/api/webrtc_logging_private/webrtc_logging_private_api.h
index b123447f2..202c727 100644
--- a/chrome/browser/extensions/api/webrtc_logging_private/webrtc_logging_private_api.h
+++ b/chrome/browser/extensions/api/webrtc_logging_private/webrtc_logging_private_api.h
@@ -6,6 +6,9 @@
 #define CHROME_BROWSER_EXTENSIONS_API_WEBRTC_LOGGING_PRIVATE_WEBRTC_LOGGING_PRIVATE_API_H_
 
 #include "chrome/browser/extensions/chrome_extension_function.h"
+#if defined(ENABLE_WEBRTC)
+#include "chrome/browser/media/webrtc_logging_handler_host.h"
+#endif
 #include "chrome/common/extensions/api/webrtc_logging_private.h"
 
 namespace content {
@@ -20,46 +23,78 @@
  protected:
   ~WebrtcLoggingPrivateFunction() override {}
 
+#if defined(ENABLE_WEBRTC)
   // Returns the RenderProcessHost associated with the given |request|
   // authorized by the |security_origin|. Returns null if unauthorized or
   // the RPH does not exist.
   content::RenderProcessHost* RphFromRequest(
       const api::webrtc_logging_private::RequestInfo& request,
       const std::string& security_origin);
+
+  scoped_refptr<WebRtcLoggingHandlerHost> LoggingHandlerFromRequest(
+      const api::webrtc_logging_private::RequestInfo& request,
+      const std::string& security_origin);
+#endif
+};
+
+class WebrtcLoggingPrivateFunctionWithGenericCallback
+    : public WebrtcLoggingPrivateFunction {
+ protected:
+  ~WebrtcLoggingPrivateFunctionWithGenericCallback() override {}
+
+#if defined(ENABLE_WEBRTC)
+  // Finds the appropriate logging handler for performing the task and prepares
+  // a generic callback object for when the task is completed.
+  // If the logging handler can't be found for the given request+origin, the
+  // returned ptr will be null.
+  scoped_refptr<WebRtcLoggingHandlerHost> PrepareTask(
+    const api::webrtc_logging_private::RequestInfo& request,
+    const std::string& security_origin,
+    WebRtcLoggingHandlerHost::GenericDoneCallback* callback);
+
+  // Must be called on UI thread.
+  void FireCallback(bool success, const std::string& error_message);
+#endif
+};
+
+class WebrtcLoggingPrivateFunctionWithUploadCallback
+    : public WebrtcLoggingPrivateFunction {
+ protected:
+  ~WebrtcLoggingPrivateFunctionWithUploadCallback() override {}
+
+#if defined(ENABLE_WEBRTC)
+  // Must be called on UI thread.
+  void FireCallback(bool success, const std::string& report_id,
+                    const std::string& error_message);
+#endif
 };
 
 class WebrtcLoggingPrivateSetMetaDataFunction
-    : public WebrtcLoggingPrivateFunction {
+    : public WebrtcLoggingPrivateFunctionWithGenericCallback {
  public:
   DECLARE_EXTENSION_FUNCTION("webrtcLoggingPrivate.setMetaData",
                              WEBRTCLOGGINGPRIVATE_SETMETADATA)
-  WebrtcLoggingPrivateSetMetaDataFunction();
+  WebrtcLoggingPrivateSetMetaDataFunction() {}
 
  private:
-  ~WebrtcLoggingPrivateSetMetaDataFunction() override;
+  ~WebrtcLoggingPrivateSetMetaDataFunction() override {}
 
   // ExtensionFunction overrides.
   bool RunAsync() override;
-
-  // Must be called on UI thread.
-  void SetMetaDataCallback(bool success, const std::string& error_message);
 };
 
 class WebrtcLoggingPrivateStartFunction
-    : public WebrtcLoggingPrivateFunction {
+    : public WebrtcLoggingPrivateFunctionWithGenericCallback {
  public:
   DECLARE_EXTENSION_FUNCTION("webrtcLoggingPrivate.start",
                              WEBRTCLOGGINGPRIVATE_START)
-  WebrtcLoggingPrivateStartFunction();
+  WebrtcLoggingPrivateStartFunction() {}
 
  private:
-  ~WebrtcLoggingPrivateStartFunction() override;
+  ~WebrtcLoggingPrivateStartFunction() override {}
 
   // ExtensionFunction overrides.
   bool RunAsync() override;
-
-  // Must be called on UI thread.
-  void StartCallback(bool success, const std::string& error_message);
 };
 
 class WebrtcLoggingPrivateSetUploadOnRenderCloseFunction
@@ -67,99 +102,111 @@
  public:
   DECLARE_EXTENSION_FUNCTION("webrtcLoggingPrivate.setUploadOnRenderClose",
                              WEBRTCLOGGINGPRIVATE_SETUPLOADONRENDERCLOSE)
-  WebrtcLoggingPrivateSetUploadOnRenderCloseFunction();
+  WebrtcLoggingPrivateSetUploadOnRenderCloseFunction() {}
 
  private:
-  ~WebrtcLoggingPrivateSetUploadOnRenderCloseFunction() override;
+  ~WebrtcLoggingPrivateSetUploadOnRenderCloseFunction() override {}
 
   // ExtensionFunction overrides.
   bool RunAsync() override;
 };
 
 class WebrtcLoggingPrivateStopFunction
-    : public WebrtcLoggingPrivateFunction {
+    : public WebrtcLoggingPrivateFunctionWithGenericCallback {
  public:
   DECLARE_EXTENSION_FUNCTION("webrtcLoggingPrivate.stop",
                              WEBRTCLOGGINGPRIVATE_STOP)
-  WebrtcLoggingPrivateStopFunction();
+  WebrtcLoggingPrivateStopFunction() {}
 
  private:
-  ~WebrtcLoggingPrivateStopFunction() override;
+  ~WebrtcLoggingPrivateStopFunction() override {}
 
   // ExtensionFunction overrides.
   bool RunAsync() override;
+};
 
-  // Must be called on UI thread.
-  void StopCallback(bool success, const std::string& error_message);
+class WebrtcLoggingPrivateStoreFunction
+    : public WebrtcLoggingPrivateFunctionWithGenericCallback {
+ public:
+  DECLARE_EXTENSION_FUNCTION("webrtcLoggingPrivate.store",
+                             WEBRTCLOGGINGPRIVATE_STORE)
+  WebrtcLoggingPrivateStoreFunction() {}
+
+ private:
+  ~WebrtcLoggingPrivateStoreFunction() override {}
+
+  // ExtensionFunction overrides.
+  bool RunAsync() override;
+};
+
+class WebrtcLoggingPrivateUploadStoredFunction
+    : public WebrtcLoggingPrivateFunctionWithUploadCallback {
+ public:
+  DECLARE_EXTENSION_FUNCTION("webrtcLoggingPrivate.uploadStored",
+                             WEBRTCLOGGINGPRIVATE_UPLOADSTORED)
+  WebrtcLoggingPrivateUploadStoredFunction() {}
+
+ private:
+  ~WebrtcLoggingPrivateUploadStoredFunction() override {}
+
+  // ExtensionFunction overrides.
+  bool RunAsync() override;
 };
 
 class WebrtcLoggingPrivateUploadFunction
-    : public WebrtcLoggingPrivateFunction {
+    : public WebrtcLoggingPrivateFunctionWithUploadCallback {
  public:
   DECLARE_EXTENSION_FUNCTION("webrtcLoggingPrivate.upload",
                              WEBRTCLOGGINGPRIVATE_UPLOAD)
-  WebrtcLoggingPrivateUploadFunction();
+  WebrtcLoggingPrivateUploadFunction() {}
 
  private:
-  ~WebrtcLoggingPrivateUploadFunction() override;
+  ~WebrtcLoggingPrivateUploadFunction() override {}
 
   // ExtensionFunction overrides.
   bool RunAsync() override;
-
-  // Must be called on UI thread.
-  void UploadCallback(bool success, const std::string& report_id,
-                      const std::string& error_message);
 };
 
 class WebrtcLoggingPrivateDiscardFunction
-    : public WebrtcLoggingPrivateFunction {
+    : public WebrtcLoggingPrivateFunctionWithGenericCallback {
  public:
   DECLARE_EXTENSION_FUNCTION("webrtcLoggingPrivate.discard",
                              WEBRTCLOGGINGPRIVATE_DISCARD)
-  WebrtcLoggingPrivateDiscardFunction();
+  WebrtcLoggingPrivateDiscardFunction() {}
 
  private:
-  ~WebrtcLoggingPrivateDiscardFunction() override;
+  ~WebrtcLoggingPrivateDiscardFunction() override {}
 
   // ExtensionFunction overrides.
   bool RunAsync() override;
-
-  // Must be called on UI thread.
-  void DiscardCallback(bool success, const std::string& error_message);
 };
 
 class WebrtcLoggingPrivateStartRtpDumpFunction
-    : public WebrtcLoggingPrivateFunction {
+    : public WebrtcLoggingPrivateFunctionWithGenericCallback {
  public:
   DECLARE_EXTENSION_FUNCTION("webrtcLoggingPrivate.startRtpDump",
                              WEBRTCLOGGINGPRIVATE_STARTRTPDUMP)
-  WebrtcLoggingPrivateStartRtpDumpFunction();
+  WebrtcLoggingPrivateStartRtpDumpFunction() {}
 
  private:
-  ~WebrtcLoggingPrivateStartRtpDumpFunction() override;
+  ~WebrtcLoggingPrivateStartRtpDumpFunction() override {}
 
   // ExtensionFunction overrides.
   bool RunAsync() override;
-
-  // Must be called on UI thread.
-  void StartRtpDumpCallback(bool success, const std::string& error_message);
 };
 
 class WebrtcLoggingPrivateStopRtpDumpFunction
-    : public WebrtcLoggingPrivateFunction {
+    : public WebrtcLoggingPrivateFunctionWithGenericCallback {
  public:
   DECLARE_EXTENSION_FUNCTION("webrtcLoggingPrivate.stopRtpDump",
                              WEBRTCLOGGINGPRIVATE_STOPRTPDUMP)
-  WebrtcLoggingPrivateStopRtpDumpFunction();
+  WebrtcLoggingPrivateStopRtpDumpFunction() {}
 
  private:
-  ~WebrtcLoggingPrivateStopRtpDumpFunction() override;
+  ~WebrtcLoggingPrivateStopRtpDumpFunction() override {}
 
   // ExtensionFunction overrides.
   bool RunAsync() override;
-
-  // Must be called on UI thread.
-  void StopRtpDumpCallback(bool success, const std::string& error_message);
 };
 
 }  // namespace extensions
diff --git a/chrome/browser/extensions/api/webrtc_logging_private/webrtc_logging_private_api_stub.cc b/chrome/browser/extensions/api/webrtc_logging_private/webrtc_logging_private_api_stub.cc
index 6a55645..836e06b 100644
--- a/chrome/browser/extensions/api/webrtc_logging_private/webrtc_logging_private_api_stub.cc
+++ b/chrome/browser/extensions/api/webrtc_logging_private/webrtc_logging_private_api_stub.cc
@@ -14,62 +14,41 @@
 
 }  // namespace
 
-WebrtcLoggingPrivateSetMetaDataFunction::
-WebrtcLoggingPrivateSetMetaDataFunction() {}
-
-WebrtcLoggingPrivateSetMetaDataFunction::
-~WebrtcLoggingPrivateSetMetaDataFunction() {}
-
 bool WebrtcLoggingPrivateSetMetaDataFunction::RunAsync() {
   SetError(kErrorNotSupported);
   SendResponse(false);
   return false;
 }
 
-void WebrtcLoggingPrivateSetMetaDataFunction::SetMetaDataCallback(
-    bool success, const std::string& error_message) {}
-
-WebrtcLoggingPrivateStartFunction::WebrtcLoggingPrivateStartFunction() {}
-
-WebrtcLoggingPrivateStartFunction::~WebrtcLoggingPrivateStartFunction() {}
-
 bool WebrtcLoggingPrivateStartFunction::RunAsync() {
   SetError(kErrorNotSupported);
   SendResponse(false);
   return false;
 }
 
-void WebrtcLoggingPrivateStartFunction::StartCallback(
-    bool success, const std::string& error_message) {}
-
-WebrtcLoggingPrivateSetUploadOnRenderCloseFunction::
-WebrtcLoggingPrivateSetUploadOnRenderCloseFunction() {}
-
-WebrtcLoggingPrivateSetUploadOnRenderCloseFunction::
-~WebrtcLoggingPrivateSetUploadOnRenderCloseFunction() {}
-
 bool WebrtcLoggingPrivateSetUploadOnRenderCloseFunction::RunAsync() {
   SetError(kErrorNotSupported);
   SendResponse(false);
   return false;
 }
 
-WebrtcLoggingPrivateStopFunction::WebrtcLoggingPrivateStopFunction() {}
-
-WebrtcLoggingPrivateStopFunction::~WebrtcLoggingPrivateStopFunction() {}
-
 bool WebrtcLoggingPrivateStopFunction::RunAsync() {
   SetError(kErrorNotSupported);
   SendResponse(false);
   return false;
 }
 
-void WebrtcLoggingPrivateStopFunction::StopCallback(
-    bool success, const std::string& error_message) {}
+bool WebrtcLoggingPrivateStoreFunction::RunAsync() {
+  SetError(kErrorNotSupported);
+  SendResponse(false);
+  return false;
+}
 
-WebrtcLoggingPrivateUploadFunction::WebrtcLoggingPrivateUploadFunction() {}
-
-WebrtcLoggingPrivateUploadFunction::~WebrtcLoggingPrivateUploadFunction() {}
+bool WebrtcLoggingPrivateUploadStoredFunction::RunAsync() {
+  SetError(kErrorNotSupported);
+  SendResponse(false);
+  return false;
+}
 
 bool WebrtcLoggingPrivateUploadFunction::RunAsync() {
   SetError(kErrorNotSupported);
@@ -77,60 +56,22 @@
   return false;
 }
 
-void WebrtcLoggingPrivateUploadFunction::UploadCallback(
-    bool success, const std::string& report_id,
-    const std::string& error_message) {
-}
-
-WebrtcLoggingPrivateDiscardFunction::WebrtcLoggingPrivateDiscardFunction() {}
-
-WebrtcLoggingPrivateDiscardFunction::~WebrtcLoggingPrivateDiscardFunction() {}
-
 bool WebrtcLoggingPrivateDiscardFunction::RunAsync() {
   SetError(kErrorNotSupported);
   SendResponse(false);
   return false;
 }
 
-void WebrtcLoggingPrivateDiscardFunction::DiscardCallback(
-    bool success, const std::string& error_message) {}
-
-WebrtcLoggingPrivateStartRtpDumpFunction::
-    WebrtcLoggingPrivateStartRtpDumpFunction() {
-}
-
-WebrtcLoggingPrivateStartRtpDumpFunction::
-    ~WebrtcLoggingPrivateStartRtpDumpFunction() {
-}
-
 bool WebrtcLoggingPrivateStartRtpDumpFunction::RunAsync() {
   SetError(kErrorNotSupported);
   SendResponse(false);
   return false;
 }
 
-void WebrtcLoggingPrivateStartRtpDumpFunction::StartRtpDumpCallback(
-    bool success,
-    const std::string& error_message) {
-}
-
-WebrtcLoggingPrivateStopRtpDumpFunction::
-    WebrtcLoggingPrivateStopRtpDumpFunction() {
-}
-
-WebrtcLoggingPrivateStopRtpDumpFunction::
-    ~WebrtcLoggingPrivateStopRtpDumpFunction() {
-}
-
 bool WebrtcLoggingPrivateStopRtpDumpFunction::RunAsync() {
   SetError(kErrorNotSupported);
   SendResponse(false);
   return false;
 }
 
-void WebrtcLoggingPrivateStopRtpDumpFunction::StopRtpDumpCallback(
-    bool success,
-    const std::string& error_message) {
-}
-
 }  // namespace extensions
diff --git a/chrome/browser/extensions/api/webrtc_logging_private/webrtc_logging_private_apitest.cc b/chrome/browser/extensions/api/webrtc_logging_private/webrtc_logging_private_apitest.cc
index baf405e1..fc80b08 100644
--- a/chrome/browser/extensions/api/webrtc_logging_private/webrtc_logging_private_apitest.cc
+++ b/chrome/browser/extensions/api/webrtc_logging_private/webrtc_logging_private_apitest.cc
@@ -16,6 +16,15 @@
 #include "extensions/common/test_util.h"
 
 using extensions::Extension;
+using extensions::WebrtcLoggingPrivateDiscardFunction;
+using extensions::WebrtcLoggingPrivateSetMetaDataFunction;
+using extensions::WebrtcLoggingPrivateStartFunction;
+using extensions::WebrtcLoggingPrivateStartRtpDumpFunction;
+using extensions::WebrtcLoggingPrivateStopFunction;
+using extensions::WebrtcLoggingPrivateStopRtpDumpFunction;
+using extensions::WebrtcLoggingPrivateStoreFunction;
+using extensions::WebrtcLoggingPrivateUploadFunction;
+using extensions::WebrtcLoggingPrivateUploadStoredFunction;
 
 namespace utils = extension_function_test_utils;
 
@@ -24,70 +33,165 @@
 static const char kTestLoggingSessionId[] = "0123456789abcdef";
 static const char kTestLoggingUrl[] = "dummy url string";
 
+std::string ParamsToString(const base::ListValue& parameters) {
+  std::string parameter_string;
+  EXPECT_TRUE(base::JSONWriter::Write(&parameters, &parameter_string));
+  return parameter_string;
+}
+
+void InitializeTestMetaData(base::ListValue* parameters) {
+  base::DictionaryValue* meta_data_entry = new base::DictionaryValue();
+  meta_data_entry->SetString("key", "app_session_id");
+  meta_data_entry->SetString("value", kTestLoggingSessionId);
+  base::ListValue* meta_data = new base::ListValue();
+  meta_data->Append(meta_data_entry);
+  meta_data_entry = new base::DictionaryValue();
+  meta_data_entry->SetString("key", "url");
+  meta_data_entry->SetString("value", kTestLoggingUrl);
+  meta_data->Append(meta_data_entry);
+  parameters->Append(meta_data);
+}
+
 class WebrtcLoggingPrivateApiTest : public ExtensionApiTest {
+ protected:
+
+  void SetUp() override {
+    ExtensionApiTest::SetUp();
+    extension_ = extensions::test_util::CreateEmptyExtension();
+  }
+
+  template<typename T>
+  scoped_refptr<T> CreateFunction() {
+    scoped_refptr<T> function(new T());
+    function->set_extension(extension_.get());
+    function->set_has_callback(true);
+    return function;
+  }
+
+  content::WebContents* web_contents() {
+    return browser()->tab_strip_model()->GetActiveWebContents();
+  }
+
+  void AppendTabIdAndUrl(base::ListValue* parameters) {
+    base::DictionaryValue* request_info = new base::DictionaryValue();
+    request_info->SetInteger(
+        "tabId", extensions::ExtensionTabUtil::GetTabId(web_contents()));
+    parameters->Append(request_info);
+    parameters->AppendString(web_contents()->GetURL().GetOrigin().spec());
+  }
+
+  bool RunFunction(UIThreadExtensionFunction* function,
+                   const base::ListValue& parameters,
+                   bool expect_results) {
+    scoped_ptr<base::Value> result(utils::RunFunctionAndReturnSingleResult(
+        function, ParamsToString(parameters), browser()));
+    if (expect_results) {
+      EXPECT_TRUE(result.get());
+      return result.get() != nullptr;
+    }
+
+    EXPECT_FALSE(result.get());
+    return result.get() == nullptr;
+  }
+
+  template<typename Function>
+  bool RunFunction(const base::ListValue& parameters, bool expect_results) {
+    scoped_refptr<Function> function(CreateFunction<Function>());
+    return RunFunction(function.get(), parameters, expect_results);
+  }
+
+  template<typename Function>
+  bool RunNoArgsFunction(bool expect_results) {
+    base::ListValue params;
+    AppendTabIdAndUrl(&params);
+    scoped_refptr<Function> function(CreateFunction<Function>());
+    return RunFunction(function.get(), params, expect_results);
+  }
+
+  bool StartLogging() {
+    return RunNoArgsFunction<WebrtcLoggingPrivateStartFunction>(false);
+  }
+
+  bool StopLogging() {
+    return RunNoArgsFunction<WebrtcLoggingPrivateStopFunction>(false);
+  }
+
+  bool DiscardLog() {
+    return RunNoArgsFunction<WebrtcLoggingPrivateDiscardFunction>(false);
+  }
+
+  bool UploadLog() {
+    return RunNoArgsFunction<WebrtcLoggingPrivateUploadFunction>(true);
+  }
+
+  bool SetMetaData(const base::ListValue& data) {
+    return RunFunction<WebrtcLoggingPrivateSetMetaDataFunction>(data, false);
+  }
+
+  bool StartRtpDump(bool incoming, bool outgoing) {
+    base::ListValue params;
+    AppendTabIdAndUrl(&params);
+    params.AppendBoolean(incoming);
+    params.AppendBoolean(outgoing);
+    return RunFunction<WebrtcLoggingPrivateStartRtpDumpFunction>(params, false);
+  }
+
+  bool StopRtpDump(bool incoming, bool outgoing) {
+    base::ListValue params;
+    AppendTabIdAndUrl(&params);
+    params.AppendBoolean(incoming);
+    params.AppendBoolean(outgoing);
+    return RunFunction<WebrtcLoggingPrivateStopRtpDumpFunction>(params, false);
+  }
+
+  bool StoreLog(const std::string& log_id) {
+    base::ListValue params;
+    AppendTabIdAndUrl(&params);
+    params.AppendString(log_id);
+    return RunFunction<WebrtcLoggingPrivateStoreFunction>(params, false);
+  }
+
+  bool UploadStoredLog(const std::string& log_id) {
+    base::ListValue params;
+    AppendTabIdAndUrl(&params);
+    params.AppendString(log_id);
+    return RunFunction<WebrtcLoggingPrivateUploadStoredFunction>(params, true);
+  }
+
+ private:
+  scoped_refptr<Extension> extension_;
+};
+
+// Helper class to temporarily tell the uploader to save the multipart buffer to
+// a test string instead of uploading.
+class ScopedOverrideUploadBuffer {
+ public:
+  ScopedOverrideUploadBuffer() {
+    g_browser_process->webrtc_log_uploader()->
+        OverrideUploadWithBufferForTesting(&multipart_);
+  }
+
+  ~ScopedOverrideUploadBuffer() {
+    g_browser_process->webrtc_log_uploader()->
+        OverrideUploadWithBufferForTesting(nullptr);
+  }
+
+  const std::string& multipart() const { return multipart_; }
+
+ private:
+  std::string multipart_;
 };
 
 }  // namespace
 
 IN_PROC_BROWSER_TEST_F(WebrtcLoggingPrivateApiTest, TestStartStopDiscard) {
-  scoped_refptr<Extension> empty_extension(
-      extensions::test_util::CreateEmptyExtension());
+  ScopedOverrideUploadBuffer buffer_override;
 
-  // Tell the uploader to save the multipart to a buffer instead of uploading.
-  std::string multipart;
-  g_browser_process->webrtc_log_uploader()->
-      OverrideUploadWithBufferForTesting(&multipart);
+  EXPECT_TRUE(StartLogging());
+  EXPECT_TRUE(StopLogging());
+  EXPECT_TRUE(DiscardLog());
 
-  // Start
-
-  scoped_refptr<extensions::WebrtcLoggingPrivateStartFunction>
-      start_function(new extensions::WebrtcLoggingPrivateStartFunction());
-  start_function->set_extension(empty_extension.get());
-  start_function->set_has_callback(true);
-
-  content::WebContents* contents =
-      browser()->tab_strip_model()->GetActiveWebContents();
-  base::ListValue parameters;
-  base::DictionaryValue* request_info = new base::DictionaryValue();
-  request_info->SetInteger("tabId",
-                           extensions::ExtensionTabUtil::GetTabId(contents));
-  parameters.Append(request_info);
-  parameters.AppendString(contents->GetURL().GetOrigin().spec());
-  std::string parameter_string;
-  base::JSONWriter::Write(&parameters, &parameter_string);
-
-  // TODO(grunell): MaybeRunFunction is suitable for those calls not returning
-  // anything.
-  scoped_ptr<base::Value> result(utils::RunFunctionAndReturnSingleResult(
-      start_function.get(), parameter_string, browser()));
-  ASSERT_FALSE(result.get());
-
-  // Stop
-
-  scoped_refptr<extensions::WebrtcLoggingPrivateStopFunction>
-      stop_function(new extensions::WebrtcLoggingPrivateStopFunction());
-  stop_function->set_extension(empty_extension.get());
-  stop_function->set_has_callback(true);
-
-  result.reset(utils::RunFunctionAndReturnSingleResult(
-      stop_function.get(), parameter_string, browser()));
-  ASSERT_FALSE(result.get());
-
-  // Discard
-
-  scoped_refptr<extensions::WebrtcLoggingPrivateDiscardFunction>
-      discard_function(new extensions::WebrtcLoggingPrivateDiscardFunction());
-  discard_function->set_extension(empty_extension.get());
-  discard_function->set_has_callback(true);
-
-  result.reset(utils::RunFunctionAndReturnSingleResult(
-      discard_function.get(), parameter_string, browser()));
-  ASSERT_FALSE(result.get());
-
-  ASSERT_TRUE(multipart.empty());
-
-  g_browser_process->webrtc_log_uploader()->OverrideUploadWithBufferForTesting(
-      NULL);
+  EXPECT_TRUE(buffer_override.multipart().empty());
 }
 
 // Tests WebRTC diagnostic logging. Sets up the browser to save the multipart
@@ -125,91 +229,19 @@
 // ------**--yradnuoBgoLtrapitluMklaTelgooG--**------
 //
 IN_PROC_BROWSER_TEST_F(WebrtcLoggingPrivateApiTest, TestStartStopUpload) {
-  scoped_refptr<Extension> empty_extension(
-      extensions::test_util::CreateEmptyExtension());
+  ScopedOverrideUploadBuffer buffer_override;
 
-  // Tell the uploader to save the multipart to a buffer instead of uploading.
-  std::string multipart;
-  g_browser_process->webrtc_log_uploader()->
-      OverrideUploadWithBufferForTesting(&multipart);
-
-  // SetMetaData.
-
-  scoped_refptr<extensions::WebrtcLoggingPrivateSetMetaDataFunction>
-      set_meta_data_function(
-          new extensions::WebrtcLoggingPrivateSetMetaDataFunction());
-  set_meta_data_function->set_extension(empty_extension.get());
-  set_meta_data_function->set_has_callback(true);
-
-  content::WebContents* contents =
-      browser()->tab_strip_model()->GetActiveWebContents();
   base::ListValue parameters;
-  base::DictionaryValue* request_info = new base::DictionaryValue();
-  request_info->SetInteger("tabId",
-                           extensions::ExtensionTabUtil::GetTabId(contents));
-  parameters.Append(request_info);
-  parameters.AppendString(contents->GetURL().GetOrigin().spec());
-  base::DictionaryValue* meta_data_entry = new base::DictionaryValue();
-  meta_data_entry->SetString("key", "app_session_id");
-  meta_data_entry->SetString("value", kTestLoggingSessionId);
-  base::ListValue* meta_data = new base::ListValue();
-  meta_data->Append(meta_data_entry);
-  meta_data_entry = new base::DictionaryValue();
-  meta_data_entry->SetString("key", "url");
-  meta_data_entry->SetString("value", kTestLoggingUrl);
-  meta_data->Append(meta_data_entry);
-  parameters.Append(meta_data);
+  AppendTabIdAndUrl(&parameters);
+  InitializeTestMetaData(&parameters);
 
-  std::string parameter_string;
-  base::JSONWriter::Write(&parameters, &parameter_string);
+  SetMetaData(parameters);
 
-  // TODO(grunell): MaybeRunFunction is suitable for those calls not returning
-  // anything.
-  scoped_ptr<base::Value> result(utils::RunFunctionAndReturnSingleResult(
-      set_meta_data_function.get(), parameter_string, browser()));
-  ASSERT_FALSE(result.get());
+  StartLogging();
+  StopLogging();
+  UploadLog();
 
-  // Start.
-
-  scoped_refptr<extensions::WebrtcLoggingPrivateStartFunction>
-      start_function(new extensions::WebrtcLoggingPrivateStartFunction());
-  start_function->set_extension(empty_extension.get());
-  start_function->set_has_callback(true);
-
-  parameters.Clear();
-  request_info = new base::DictionaryValue();
-  request_info->SetInteger("tabId",
-                           extensions::ExtensionTabUtil::GetTabId(contents));
-  parameters.Append(request_info);
-  parameters.AppendString(contents->GetURL().GetOrigin().spec());
-  base::JSONWriter::Write(&parameters, &parameter_string);
-
-  result.reset(utils::RunFunctionAndReturnSingleResult(
-      start_function.get(), parameter_string, browser()));
-  ASSERT_FALSE(result.get());
-
-  // Stop.
-
-  scoped_refptr<extensions::WebrtcLoggingPrivateStopFunction>
-      stop_function(new extensions::WebrtcLoggingPrivateStopFunction());
-  stop_function->set_extension(empty_extension.get());
-  stop_function->set_has_callback(true);
-
-  result.reset(utils::RunFunctionAndReturnSingleResult(
-      stop_function.get(), parameter_string, browser()));
-  ASSERT_FALSE(result.get());
-
-  // Upload.
-
-  scoped_refptr<extensions::WebrtcLoggingPrivateUploadFunction>
-      upload_function(new extensions::WebrtcLoggingPrivateUploadFunction());
-  upload_function->set_extension(empty_extension.get());
-  upload_function->set_has_callback(true);
-
-  result.reset(utils::RunFunctionAndReturnSingleResult(
-      upload_function.get(), parameter_string, browser()));
-  ASSERT_TRUE(result.get());
-
+  std::string multipart = buffer_override.multipart();
   ASSERT_FALSE(multipart.empty());
 
   // Check multipart data.
@@ -284,46 +316,80 @@
   final_delimiter += "--";
   EXPECT_STREQ(final_delimiter.c_str(), multipart_lines[29].c_str());
   EXPECT_TRUE(multipart_lines[30].empty());
-
-  g_browser_process->webrtc_log_uploader()->OverrideUploadWithBufferForTesting(
-      NULL);
 }
 
 IN_PROC_BROWSER_TEST_F(WebrtcLoggingPrivateApiTest, TestStartStopRtpDump) {
-  scoped_refptr<Extension> empty_extension(
-      extensions::test_util::CreateEmptyExtension());
-
-  // Start RTP dump.
-  scoped_refptr<extensions::WebrtcLoggingPrivateStartRtpDumpFunction>
-      start_function(
-          new extensions::WebrtcLoggingPrivateStartRtpDumpFunction());
-  start_function->set_extension(empty_extension.get());
-  start_function->set_has_callback(true);
-
-  content::WebContents* contents =
-      browser()->tab_strip_model()->GetActiveWebContents();
-  base::ListValue parameters;
-  base::DictionaryValue* request_info = new base::DictionaryValue();
-  request_info->SetInteger("tabId",
-                           extensions::ExtensionTabUtil::GetTabId(contents));
-  parameters.Append(request_info);
-  parameters.AppendString(contents->GetURL().GetOrigin().spec());
-  parameters.AppendBoolean(true);
-  parameters.AppendBoolean(true);
-  std::string parameter_string;
-  base::JSONWriter::Write(&parameters, &parameter_string);
-
-  scoped_ptr<base::Value> result(utils::RunFunctionAndReturnSingleResult(
-      start_function.get(), parameter_string, browser()));
-  ASSERT_FALSE(result.get());
-
-  // Stop RTP dump.
-  scoped_refptr<extensions::WebrtcLoggingPrivateStopRtpDumpFunction>
-      stop_function(new extensions::WebrtcLoggingPrivateStopRtpDumpFunction());
-  stop_function->set_extension(empty_extension.get());
-  stop_function->set_has_callback(true);
-
-  result.reset(utils::RunFunctionAndReturnSingleResult(
-      stop_function.get(), parameter_string, browser()));
-  ASSERT_FALSE(result.get());
+  // TODO(tommi): As is, these tests are missing verification of the actual
+  // RTP dump data.  We should fix that, e.g. use SetDumpWriterForTesting.
+  StartRtpDump(true, true);
+  StopRtpDump(true, true);
 }
+
+// Tests trying to store a log when a log is not being captured.
+// We should get a failure callback in this case.
+IN_PROC_BROWSER_TEST_F(WebrtcLoggingPrivateApiTest, TestStoreWithoutLog) {
+  base::ListValue parameters;
+  AppendTabIdAndUrl(&parameters);
+  parameters.AppendString("MyLogId");
+  scoped_refptr<WebrtcLoggingPrivateStoreFunction> store(
+      CreateFunction<WebrtcLoggingPrivateStoreFunction>());
+  const std::string error = utils::RunFunctionAndReturnError(
+      store.get(), ParamsToString(parameters), browser());
+  ASSERT_FALSE(error.empty());
+}
+
+IN_PROC_BROWSER_TEST_F(WebrtcLoggingPrivateApiTest, TestStartStopStore) {
+  ASSERT_TRUE(StartLogging());
+  ASSERT_TRUE(StopLogging());
+  EXPECT_TRUE(StoreLog("MyLogID"));
+}
+
+IN_PROC_BROWSER_TEST_F(WebrtcLoggingPrivateApiTest,
+                       TestStartStopStoreAndUpload) {
+  static const char kLogId[] = "TestStartStopStoreAndUpload";
+  ASSERT_TRUE(StartLogging());
+  ASSERT_TRUE(StopLogging());
+  ASSERT_TRUE(StoreLog(kLogId));
+
+  ScopedOverrideUploadBuffer buffer_override;
+  EXPECT_TRUE(UploadStoredLog(kLogId));
+  EXPECT_NE(std::string::npos,
+            buffer_override.multipart().find("filename=\"webrtc_log.gz\""));
+}
+
+IN_PROC_BROWSER_TEST_F(WebrtcLoggingPrivateApiTest,
+                       TestStartStopStoreAndUploadWithRtp) {
+  static const char kLogId[] = "TestStartStopStoreAndUploadWithRtp";
+  ASSERT_TRUE(StartLogging());
+  ASSERT_TRUE(StartRtpDump(true, true));
+  ASSERT_TRUE(StopLogging());
+  ASSERT_TRUE(StopRtpDump(true, true));
+  ASSERT_TRUE(StoreLog(kLogId));
+
+  ScopedOverrideUploadBuffer buffer_override;
+  EXPECT_TRUE(UploadStoredLog(kLogId));
+  EXPECT_NE(std::string::npos,
+            buffer_override.multipart().find("filename=\"webrtc_log.gz\""));
+}
+
+IN_PROC_BROWSER_TEST_F(WebrtcLoggingPrivateApiTest,
+                       TestStartStopStoreAndUploadWithMetaData) {
+  static const char kLogId[] = "TestStartStopStoreAndUploadWithRtp";
+  ASSERT_TRUE(StartLogging());
+
+  base::ListValue parameters;
+  AppendTabIdAndUrl(&parameters);
+  InitializeTestMetaData(&parameters);
+  SetMetaData(parameters);
+
+  ASSERT_TRUE(StopLogging());
+  ASSERT_TRUE(StoreLog(kLogId));
+
+  ScopedOverrideUploadBuffer buffer_override;
+  EXPECT_TRUE(UploadStoredLog(kLogId));
+  EXPECT_NE(std::string::npos,
+            buffer_override.multipart().find("filename=\"webrtc_log.gz\""));
+  EXPECT_NE(std::string::npos,
+            buffer_override.multipart().find(kTestLoggingUrl));
+}
+
diff --git a/chrome/browser/extensions/bookmark_app_helper.cc b/chrome/browser/extensions/bookmark_app_helper.cc
index 0954109..b7e8db5 100644
--- a/chrome/browser/extensions/bookmark_app_helper.cc
+++ b/chrome/browser/extensions/bookmark_app_helper.cc
@@ -422,11 +422,11 @@
 
   // Pin the app to the relevant launcher depending on the OS.
   Profile* current_profile = profile_->GetOriginalProfile();
-  chrome::HostDesktopType desktop = browser->host_desktop_type();
 
 // On Mac, shortcuts are automatically created for hosted apps when they are
-// installed, so there is no need to call this again."
+// installed, so there is no need to create them again.
 #if !defined(OS_MACOSX)
+  chrome::HostDesktopType desktop = browser->host_desktop_type();
   if (desktop != chrome::HOST_DESKTOP_TYPE_ASH) {
     web_app::ShortcutLocations creation_locations;
 #if defined(OS_LINUX)
@@ -446,28 +446,12 @@
   }
 #endif
 
-  // Show the newly installed app in the app launcher, in finder (on Mac) or
-  // chrome://apps.
-  if (IsAppLauncherEnabled()) {
-    AppListService::Get(desktop)
-        ->ShowForAppInstall(current_profile, extension->id(), false);
 #if defined(OS_MACOSX)
-  } else if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
+  if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
                  switches::kDisableHostedAppShimCreation)) {
-    web_app::RevealAppShimInFinderForApp(profile_, extension);
-#endif
-  } else {
-    chrome::NavigateParams params(current_profile,
-                                  GURL(chrome::kChromeUIAppsURL),
-                                  ui::PAGE_TRANSITION_LINK);
-    params.disposition = SINGLETON_TAB;
-    chrome::Navigate(&params);
-
-    content::NotificationService::current()->Notify(
-        chrome::NOTIFICATION_APP_INSTALLED_TO_NTP,
-        content::Source<content::WebContents>(params.target_contents),
-        content::Details<const std::string>(&extension->id()));
+    web_app::RevealAppShimInFinderForApp(current_profile, extension);
   }
+#endif
 
   callback_.Run(extension, web_app_info_);
 }
diff --git a/chrome/browser/extensions/chrome_extension_function_details.h b/chrome/browser/extensions/chrome_extension_function_details.h
index 79c5c09..2484158 100644
--- a/chrome/browser/extensions/chrome_extension_function_details.h
+++ b/chrome/browser/extensions/chrome_extension_function_details.h
@@ -38,9 +38,9 @@
   // Gets the "current" browser, if any.
   //
   // Many extension APIs operate relative to the current browser, which is the
-  // browser the calling code is running inside of. For example, popups, tabs,
-  // and infobars all have a containing browser, but background pages and
-  // notification bubbles do not.
+  // browser the calling code is running inside of. For example, popups and tabs
+  // all have a containing browser, but background pages and notification
+  // bubbles do not.
   //
   // If there is no containing window, the current browser defaults to the
   // foremost one.
diff --git a/chrome/browser/extensions/chrome_extension_host_delegate.cc b/chrome/browser/extensions/chrome_extension_host_delegate.cc
index 8cb4ec6..6a1af5e 100644
--- a/chrome/browser/extensions/chrome_extension_host_delegate.cc
+++ b/chrome/browser/extensions/chrome_extension_host_delegate.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/extensions/chrome_extension_host_delegate.h"
 
+#include "base/lazy_instance.h"
 #include "chrome/browser/extensions/chrome_extension_web_contents_observer.h"
 #include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/extensions/extension_tab_util.h"
@@ -12,6 +13,7 @@
 #include "components/app_modal/javascript_dialog_manager.h"
 #include "extensions/browser/extension_host.h"
 #include "extensions/browser/extension_system.h"
+#include "extensions/browser/serial_extension_host_queue.h"
 
 namespace extensions {
 
@@ -66,4 +68,11 @@
           web_contents, security_origin, type, extension);
 }
 
+static base::LazyInstance<SerialExtensionHostQueue> g_queue =
+    LAZY_INSTANCE_INITIALIZER;
+
+ExtensionHostQueue* ChromeExtensionHostDelegate::GetExtensionHostQueue() const {
+  return g_queue.Pointer();
+}
+
 }  // namespace extensions
diff --git a/chrome/browser/extensions/chrome_extension_host_delegate.h b/chrome/browser/extensions/chrome_extension_host_delegate.h
index 0ba69555..c34b0d82 100644
--- a/chrome/browser/extensions/chrome_extension_host_delegate.h
+++ b/chrome/browser/extensions/chrome_extension_host_delegate.h
@@ -32,6 +32,7 @@
                                   const GURL& security_origin,
                                   content::MediaStreamType type,
                                   const Extension* extension) override;
+  ExtensionHostQueue* GetExtensionHostQueue() const override;
 };
 
 }  // namespace extensions
diff --git a/chrome/browser/extensions/component_loader.cc b/chrome/browser/extensions/component_loader.cc
index 83ec093..0d431c2a 100644
--- a/chrome/browser/extensions/component_loader.cc
+++ b/chrome/browser/extensions/component_loader.cc
@@ -59,6 +59,10 @@
 #include "chrome/grit/chromium_strings.h"
 #endif
 
+#if defined(ENABLE_APP_LIST) && defined(OS_CHROMEOS)
+#include "chrome/browser/ui/app_list/google_now_extension.h"
+#endif
+
 using content::BrowserThread;
 
 namespace extensions {
@@ -615,6 +619,14 @@
 
   bool enabled = enabled_via_field_trial || enabled_via_trunk_build;
 
+#if defined(ENABLE_APP_LIST) && defined(OS_CHROMEOS)
+  // Don't load if newer trial is running (== new extension id is available).
+  std::string ignored_extension_id;
+  if (GetGoogleNowExtensionId(&ignored_extension_id)) {
+    enabled = false;
+  }
+#endif
+
   if (!skip_session_components && enabled) {
     Add(IDR_GOOGLE_NOW_MANIFEST,
         base::FilePath(FILE_PATH_LITERAL("google_now")));
diff --git a/chrome/browser/extensions/extension_tab_util.cc b/chrome/browser/extensions/extension_tab_util.cc
index 4009221..d5feb70 100644
--- a/chrome/browser/extensions/extension_tab_util.cc
+++ b/chrome/browser/extensions/extension_tab_util.cc
@@ -575,9 +575,10 @@
   return NULL;
 }
 
-void ExtensionTabUtil::OpenOptionsPage(const Extension* extension,
+bool ExtensionTabUtil::OpenOptionsPage(const Extension* extension,
                                        Browser* browser) {
-  DCHECK(OptionsPageInfo::HasOptionsPage(extension));
+  if (!OptionsPageInfo::HasOptionsPage(extension))
+    return false;
 
   // Force the options page to open in non-OTR window, because it won't be
   // able to save settings from OTR.
@@ -617,6 +618,8 @@
         browser->tab_strip_model()->GetActiveWebContents();
     web_contents->GetDelegate()->ActivateContents(web_contents);
   }
+
+  return true;
 }
 
 }  // namespace extensions
diff --git a/chrome/browser/extensions/extension_tab_util.h b/chrome/browser/extensions/extension_tab_util.h
index 5a45abf..6736db4 100644
--- a/chrome/browser/extensions/extension_tab_util.h
+++ b/chrome/browser/extensions/extension_tab_util.h
@@ -171,8 +171,10 @@
   static WindowController* GetWindowControllerOfTab(
       const content::WebContents* web_contents);
 
-  // Open the extension's options page.
-  static void OpenOptionsPage(const Extension* extension, Browser* browser);
+  // Open the extension's options page. Returns true if an options page was
+  // successfully opened (though it may not necessarily *load*, e.g. if the
+  // URL does not exist).
+  static bool OpenOptionsPage(const Extension* extension, Browser* browser);
 };
 
 }  // namespace extensions
diff --git a/chrome/browser/extensions/extension_toolbar_model.cc b/chrome/browser/extensions/extension_toolbar_model.cc
index c578535..21451194 100644
--- a/chrome/browser/extensions/extension_toolbar_model.cc
+++ b/chrome/browser/extensions/extension_toolbar_model.cc
@@ -312,45 +312,74 @@
       std::find(last_known_positions_.begin(),
                 last_known_positions_.end(),
                 extension->id()) == last_known_positions_.end();
-  size_t new_index = is_new_extension ? toolbar_items_.size() :
-      FindNewPositionFromLastKnownGood(extension);
-  toolbar_items_.insert(toolbar_items_.begin() + new_index,
-                        make_scoped_refptr(extension));
+
+  // New extensions go at the right (end) of the visible extensions. Other
+  // extensions go at their previous position.
+  size_t new_index = 0;
   if (is_new_extension) {
-    last_known_positions_.push_back(extension->id());
+    new_index = visible_icon_count();
+    // For the last-known position, we use the index of the extension that is
+    // just before this extension, plus one. (Note that this isn't the same
+    // as new_index + 1, because last_known_positions_ can include disabled
+    // extensions.)
+    int new_last_known_index =
+        new_index == 0 ? 0 :
+        std::find(last_known_positions_.begin(),
+                  last_known_positions_.end(),
+                  toolbar_items_[new_index - 1]->id()) -
+            last_known_positions_.begin() + 1;
+    // In theory, the extension before this one should always
+    // be in last known positions, but if something funny happened with prefs,
+    // make sure we handle it.
+    // TODO(devlin): Track down these cases so we can CHECK this.
+    new_last_known_index =
+        std::min<int>(new_last_known_index, last_known_positions_.size());
+    last_known_positions_.insert(
+        last_known_positions_.begin() + new_last_known_index, extension->id());
     UpdatePrefs();
+  } else {
+    new_index = FindNewPositionFromLastKnownGood(extension);
   }
 
-  MaybeUpdateVisibilityPref(extension, new_index);
+  toolbar_items_.insert(toolbar_items_.begin() + new_index, extension);
 
   // If we're currently highlighting, then even though we add a browser action
   // to the full list (|toolbar_items_|, there won't be another *visible*
   // browser action, which was what the observers care about.
   if (!is_highlighting_) {
-    // If this is an incognito profile, we also have to check to make sure the
-    // overflow matches the main bar's status.
-    if (profile_->IsOffTheRecord()) {
+    FOR_EACH_OBSERVER(
+        Observer, observers_, ToolbarExtensionAdded(extension, new_index));
+
+    int visible_count_delta = 0;
+    if (is_new_extension && !all_icons_visible()) {
+      // If this is a new extension (and not all extensions are visible), we
+      // expand the toolbar out so that the new one can be seen.
+      visible_count_delta = 1;
+    } else if (profile_->IsOffTheRecord()) {
+      // If this is an incognito profile, we also have to check to make sure the
+      // overflow matches the main bar's status.
       ExtensionToolbarModel* main_model =
           ExtensionToolbarModel::Get(profile_->GetOriginalProfile());
       // Find what the index will be in the main bar. Because Observer calls are
       // nondeterministic, we can't just assume the main bar will have the
       // extension and look it up.
-      size_t main_index = is_new_extension ?
-          main_model->toolbar_items_.size() :
+      size_t main_index =
           main_model->FindNewPositionFromLastKnownGood(extension);
       bool visible = main_index < main_model->visible_icon_count();
       // We may need to adjust the visible count if the incognito bar isn't
       // showing all icons and this one is visible, or if it is showing all
       // icons and this is hidden.
       if (visible && !all_icons_visible())
-        SetVisibleIconCount(visible_icon_count() + 1);
+        visible_count_delta = 1;
       else if (!visible && all_icons_visible())
-        SetVisibleIconCount(visible_icon_count() - 1);
+        visible_count_delta = -1;
     }
 
-    FOR_EACH_OBSERVER(
-        Observer, observers_, ToolbarExtensionAdded(extension, new_index));
+    if (visible_count_delta)
+      SetVisibleIconCount(visible_icon_count() + visible_count_delta);
   }
+
+  MaybeUpdateVisibilityPref(extension, new_index);
 }
 
 void ExtensionToolbarModel::RemoveExtension(const Extension* extension) {
@@ -400,19 +429,19 @@
   if (profile_->IsOffTheRecord())
     IncognitoPopulate();
   else
-    Populate(last_known_positions_);
+    Populate(&last_known_positions_);
 
   extensions_initialized_ = true;
   MaybeUpdateVisibilityPrefs();
   FOR_EACH_OBSERVER(Observer, observers_, OnToolbarModelInitialized());
 }
 
-void ExtensionToolbarModel::Populate(const ExtensionIdList& positions) {
+void ExtensionToolbarModel::Populate(ExtensionIdList* positions) {
   DCHECK(!profile_->IsOffTheRecord());
   const ExtensionSet& extensions =
       ExtensionRegistry::Get(profile_)->enabled_extensions();
   // Items that have explicit positions.
-  ExtensionList sorted(positions.size(), NULL);
+  ExtensionList sorted(positions->size(), NULL);
   // The items that don't have explicit positions.
   ExtensionList unsorted;
 
@@ -427,11 +456,15 @@
     }
 
     ExtensionIdList::const_iterator pos =
-        std::find(positions.begin(), positions.end(), extension->id());
-    if (pos != positions.end())
-      sorted[pos - positions.begin()] = extension;
-    else
+        std::find(positions->begin(), positions->end(), extension->id());
+    if (pos != positions->end()) {
+      sorted[pos - positions->begin()] = extension;
+    } else {
+      // Unknown extension - push it to the back of unsorted, and add it to the
+      // list of ids at the end.
       unsorted.push_back(extension);
+      positions->push_back(extension->id());
+    }
   }
 
   // Merge the lists.
@@ -567,7 +600,7 @@
   DCHECK(toolbar_items_.empty());
 
   // ...Add the new...
-  Populate(last_known_positions_);
+  Populate(&last_known_positions_);
 
   // ...And notify.
   for (size_t i = 0; i < toolbar_items().size(); ++i) {
diff --git a/chrome/browser/extensions/extension_toolbar_model.h b/chrome/browser/extensions/extension_toolbar_model.h
index 9b9e30f..7482648 100644
--- a/chrome/browser/extensions/extension_toolbar_model.h
+++ b/chrome/browser/extensions/extension_toolbar_model.h
@@ -187,7 +187,7 @@
   // takes the shortcut - looking at the regular model's content and modifying
   // it.
   void InitializeExtensionList();
-  void Populate(const ExtensionIdList& positions);
+  void Populate(ExtensionIdList* positions);
   void IncognitoPopulate();
 
   // Save the model to prefs.
diff --git a/chrome/browser/extensions/extension_toolbar_model_unittest.cc b/chrome/browser/extensions/extension_toolbar_model_unittest.cc
index 2791d65..c779b55 100644
--- a/chrome/browser/extensions/extension_toolbar_model_unittest.cc
+++ b/chrome/browser/extensions/extension_toolbar_model_unittest.cc
@@ -484,6 +484,78 @@
   EXPECT_EQ(browser_action_a(), GetExtensionAtIndex(2u));
 }
 
+// Test that new extensions are always visible on installation and inserted at
+// the "end" of the visible section.
+TEST_F(ExtensionToolbarModelUnitTest, NewToolbarExtensionsAreVisible) {
+  Init();
+
+  // Three extensions with actions.
+  scoped_refptr<const Extension> extension_a =
+      extension_action_test_util::CreateActionExtension(
+          "a", extension_action_test_util::BROWSER_ACTION);
+  scoped_refptr<const Extension> extension_b =
+      extension_action_test_util::CreateActionExtension(
+          "b", extension_action_test_util::BROWSER_ACTION);
+  scoped_refptr<const Extension> extension_c =
+      extension_action_test_util::CreateActionExtension(
+          "c", extension_action_test_util::BROWSER_ACTION);
+  scoped_refptr<const Extension> extension_d =
+      extension_action_test_util::CreateActionExtension(
+          "d", extension_action_test_util::BROWSER_ACTION);
+
+  // We should start off without any extensions.
+  EXPECT_EQ(0u, num_toolbar_items());
+  EXPECT_EQ(0u, toolbar_model()->visible_icon_count());
+
+  // Add one extension. It should be visible.
+  service()->AddExtension(extension_a.get());
+  EXPECT_EQ(1u, num_toolbar_items());
+  EXPECT_EQ(1u, toolbar_model()->visible_icon_count());
+  EXPECT_EQ(extension_a.get(), GetExtensionAtIndex(0u));
+
+  // Hide all extensions.
+  toolbar_model()->SetVisibleIconCount(0);
+  EXPECT_EQ(0u, toolbar_model()->visible_icon_count());
+
+  // Add a new extension - it should be visible, so it should be in the first
+  // index. The other extension should remain hidden.
+  service()->AddExtension(extension_b.get());
+  EXPECT_EQ(2u, num_toolbar_items());
+  EXPECT_EQ(1u, toolbar_model()->visible_icon_count());
+  EXPECT_EQ(extension_b.get(), GetExtensionAtIndex(0u));
+  EXPECT_EQ(extension_a.get(), GetExtensionAtIndex(1u));
+
+  // Show all extensions.
+  toolbar_model()->SetVisibleIconCount(2);
+  EXPECT_EQ(2u, toolbar_model()->visible_icon_count());
+  EXPECT_TRUE(toolbar_model()->all_icons_visible());
+
+  // Add the third extension. Since all extensions are visible, it should go in
+  // the last index.
+  service()->AddExtension(extension_c.get());
+  EXPECT_EQ(3u, num_toolbar_items());
+  EXPECT_EQ(3u, toolbar_model()->visible_icon_count());
+  EXPECT_TRUE(toolbar_model()->all_icons_visible());
+  EXPECT_EQ(extension_b.get(), GetExtensionAtIndex(0u));
+  EXPECT_EQ(extension_a.get(), GetExtensionAtIndex(1u));
+  EXPECT_EQ(extension_c.get(), GetExtensionAtIndex(2u));
+
+  // Hide one extension (two remaining visible).
+  toolbar_model()->SetVisibleIconCount(2);
+  EXPECT_EQ(2u, toolbar_model()->visible_icon_count());
+
+  // Add a fourth extension. It should go at the end of the visible section and
+  // be visible, so it increases visible count by 1, and goes into the third
+  // index. The hidden extension should remain hidden.
+  service()->AddExtension(extension_d.get());
+  EXPECT_EQ(4u, num_toolbar_items());
+  EXPECT_EQ(3u, toolbar_model()->visible_icon_count());
+  EXPECT_EQ(extension_b.get(), GetExtensionAtIndex(0u));
+  EXPECT_EQ(extension_a.get(), GetExtensionAtIndex(1u));
+  EXPECT_EQ(extension_d.get(), GetExtensionAtIndex(2u));
+  EXPECT_EQ(extension_c.get(), GetExtensionAtIndex(3u));
+}
+
 TEST_F(ExtensionToolbarModelUnitTest, ExtensionToolbarHighlightMode) {
   Init();
 
diff --git a/chrome/browser/extensions/extension_util.cc b/chrome/browser/extensions/extension_util.cc
index 7ac3762..23f5e6a 100644
--- a/chrome/browser/extensions/extension_util.cc
+++ b/chrome/browser/extensions/extension_util.cc
@@ -384,8 +384,13 @@
 }
 
 bool IsNewBookmarkAppsEnabled() {
+#if defined(OS_CHROMEOS)
   return !base::CommandLine::ForCurrentProcess()->HasSwitch(
       switches::kDisableNewBookmarkApps);
+#else
+  return base::CommandLine::ForCurrentProcess()->HasSwitch(
+      switches::kEnableNewBookmarkApps);
+#endif
 }
 
 }  // namespace util
diff --git a/chrome/browser/extensions/extension_view.h b/chrome/browser/extensions/extension_view.h
index e974d09..f8a5390f 100644
--- a/chrome/browser/extensions/extension_view.h
+++ b/chrome/browser/extensions/extension_view.h
@@ -25,9 +25,6 @@
  public:
   virtual ~ExtensionView() {}
 
-  // Initialize the view, once a newly created view has been set in the host.
-  virtual void Init() = 0;
-
   // If attached to a Browser (e.g. popups), the Browser it is attached to.
   virtual Browser* GetBrowser() = 0;
 
diff --git a/chrome/browser/extensions/extension_view_host.cc b/chrome/browser/extensions/extension_view_host.cc
index 40e88d6c..ee79eba 100644
--- a/chrome/browser/extensions/extension_view_host.cc
+++ b/chrome/browser/extensions/extension_view_host.cc
@@ -77,7 +77,6 @@
 
 void ExtensionViewHost::CreateView(Browser* browser) {
   view_ = CreateExtensionView(this, browser);
-  view_->Init();
 }
 
 void ExtensionViewHost::SetAssociatedWebContents(WebContents* web_contents) {
diff --git a/chrome/browser/extensions/external_provider_impl.cc b/chrome/browser/extensions/external_provider_impl.cc
index 100dacdc..9966755 100644
--- a/chrome/browser/extensions/external_provider_impl.cc
+++ b/chrome/browser/extensions/external_provider_impl.cc
@@ -473,14 +473,11 @@
     int external_apps_path_id = profile->IsSupervised() ?
         chrome::DIR_SUPERVISED_USERS_DEFAULT_APPS :
         chrome::DIR_STANDALONE_EXTERNAL_EXTENSIONS;
-    ExternalPrefLoader::Options pref_load_flags = profile->IsNewProfile() ?
-        ExternalPrefLoader::DELAY_LOAD_UNTIL_PRIORITY_SYNC :
-        ExternalPrefLoader::NONE;
     provider_list->push_back(
         linked_ptr<ExternalProviderInterface>(new ExternalProviderImpl(
             service,
             new ExternalPrefLoader(external_apps_path_id,
-                                   pref_load_flags,
+                                   ExternalPrefLoader::NONE,
                                    profile),
             profile,
             Manifest::EXTERNAL_PREF,
diff --git a/chrome/browser/extensions/permissions_updater.cc b/chrome/browser/extensions/permissions_updater.cc
index 953b25c..f902df4 100644
--- a/chrome/browser/extensions/permissions_updater.cc
+++ b/chrome/browser/extensions/permissions_updater.cc
@@ -253,16 +253,29 @@
                           &active_explicit,
                           &withheld_explicit);
 
+  URLPatternSet delta_explicit;
+  URLPatternSet::CreateDifference(
+      active->explicit_hosts(), active_explicit, &delta_explicit);
+  URLPatternSet delta_scriptable;
+  URLPatternSet::CreateDifference(
+      active->scriptable_hosts(), active_scriptable, &delta_scriptable);
+
   SetPermissions(extension,
                  new PermissionSet(active->apis(),
                                    active->manifest_permissions(),
                                    active_explicit,
                                    active_scriptable),
-                  new PermissionSet(withheld->apis(),
-                                    withheld->manifest_permissions(),
-                                    withheld_explicit,
-                                    withheld_scriptable));
-  // TODO(rdevlin.cronin) We should notify the observers/renderer.
+                 new PermissionSet(withheld->apis(),
+                                   withheld->manifest_permissions(),
+                                   withheld_explicit,
+                                   withheld_scriptable));
+
+  scoped_refptr<const PermissionSet> delta(new PermissionSet(
+      APIPermissionSet(),
+      ManifestPermissionSet(),
+      delta_explicit,
+      delta_scriptable));
+  NotifyPermissionsUpdated(REMOVED, extension, delta.get());
 }
 
 void PermissionsUpdater::GrantWithheldImpliedAllHosts(
@@ -284,6 +297,13 @@
                              withheld->scriptable_hosts(),
                              &scriptable_hosts);
 
+  URLPatternSet delta_explicit;
+  URLPatternSet::CreateDifference(
+      explicit_hosts, active->explicit_hosts(), &delta_explicit);
+  URLPatternSet delta_scriptable;
+  URLPatternSet::CreateDifference(
+      scriptable_hosts, active->scriptable_hosts(), &delta_scriptable);
+
   // Since we only withhold host permissions (so far), we know that withheld
   // permissions will be empty.
   SetPermissions(extension,
@@ -292,7 +312,13 @@
                                    explicit_hosts,
                                    scriptable_hosts),
                  new PermissionSet());
-  // TODO(rdevlin.cronin) We should notify the observers/renderer.
+
+  scoped_refptr<const PermissionSet> delta(new PermissionSet(
+      APIPermissionSet(),
+      ManifestPermissionSet(),
+      delta_explicit,
+      delta_scriptable));
+  NotifyPermissionsUpdated(ADDED, extension, delta.get());
 }
 
 void PermissionsUpdater::SetPermissions(
diff --git a/chrome/browser/extensions/window_open_apitest.cc b/chrome/browser/extensions/window_open_apitest.cc
index 0c9aacd..011ff07 100644
--- a/chrome/browser/extensions/window_open_apitest.cc
+++ b/chrome/browser/extensions/window_open_apitest.cc
@@ -46,11 +46,15 @@
 using content::Referrer;
 using content::WebContents;
 
-// Disabled, http://crbug.com/64899.
-IN_PROC_BROWSER_TEST_F(ExtensionApiTest, DISABLED_WindowOpen) {
-  base::CommandLine::ForCurrentProcess()->AppendSwitch(
-      extensions::switches::kEnableExperimentalExtensionApis);
-
+// The test uses the chrome.browserAction.openPopup API, which requires that the
+// window can automatically be activated.
+// See comments at BrowserActionInteractiveTest::ShouldRunPopupTest
+#if defined(OS_MACOSX)
+#define MAYBE_WindowOpen DISABLED_WindowOpen
+#else
+#define MAYBE_WindowOpen WindowOpen
+#endif
+IN_PROC_BROWSER_TEST_F(ExtensionApiTest, MAYBE_WindowOpen) {
   extensions::ResultCatcher catcher;
   ASSERT_TRUE(LoadExtensionIncognito(test_data_dir_
       .AppendASCII("window_open").AppendASCII("spanning")));
diff --git a/chrome/browser/history/DEPS b/chrome/browser/history/DEPS
index 030f20e..7dfc95c71 100644
--- a/chrome/browser/history/DEPS
+++ b/chrome/browser/history/DEPS
@@ -29,8 +29,6 @@
   "!chrome/browser/ui/browser_finder.h",
   "!components/bookmarks/browser/bookmark_utils.h",
   "!components/dom_distiller/core/url_constants.h",
-  "!components/visitedlink/browser/visitedlink_delegate.h",
-  "!components/visitedlink/browser/visitedlink_master.h",
 ]
 
 specific_include_rules = {
@@ -54,6 +52,11 @@
     "+components/bookmarks/browser",
     "+components/keyed_service/content",
   ],
+  # Those files will move to //components/history/content/browser and thus
+  # can depend on //content even indirectly.
+  'content_.*\.(cc|h)': [
+    "+components/visitedlink/browser",
+  ],
   # TODO(sdefresne): Bring this list to zero.
   #
   # Do not add to the list of temporarily-allowed dependencies below,
diff --git a/chrome/browser/history/android/android_history_provider_service.cc b/chrome/browser/history/android/android_history_provider_service.cc
index 149fa5b3..4f3e828 100644
--- a/chrome/browser/history/android/android_history_provider_service.cc
+++ b/chrome/browser/history/android/android_history_provider_service.cc
@@ -6,12 +6,236 @@
 
 #include "chrome/browser/favicon/favicon_service.h"
 #include "chrome/browser/favicon/favicon_service_factory.h"
+#include "chrome/browser/history/android/android_provider_backend.h"
 #include "chrome/browser/history/history_backend.h"
 #include "chrome/browser/history/history_service.h"
 #include "chrome/browser/history/history_service_factory.h"
 #include "chrome/browser/profiles/profile.h"
+#include "components/history/core/browser/android/android_history_types.h"
+#include "components/history/core/browser/history_db_task.h"
 
-using history::HistoryBackend;
+namespace {
+
+// AndroidProviderTask wraps two callbacks into an HistoryDBTask so that they
+// can be passed to HistoryService::ScheduleDBTask. ResultType must be zero
+// constructible (i.e. ResultType(0) should build an initialized default value)
+// and copyable.
+template <typename ResultType>
+class AndroidProviderTask : public history::HistoryDBTask {
+ public:
+  typedef base::Callback<ResultType(history::AndroidProviderBackend*)>
+      RequestCallback;
+  typedef base::Callback<void(ResultType)> ResultCallback;
+
+  AndroidProviderTask(const RequestCallback& request_cb,
+                      const ResultCallback& result_cb)
+      : request_cb_(request_cb), result_cb_(result_cb), result_(0) {
+    DCHECK(!request_cb_.is_null());
+    DCHECK(!result_cb_.is_null());
+  }
+
+  ~AndroidProviderTask() override {}
+
+ private:
+  // history::HistoryDBTask implementation.
+  bool RunOnDBThread(history::HistoryBackend* backend,
+                     history::HistoryDatabase* db) override {
+    if (backend->android_provider_backend())
+      result_ = request_cb_.Run(backend->android_provider_backend());
+    return true;
+  }
+
+  void DoneRunOnMainThread() override { result_cb_.Run(result_); }
+
+  RequestCallback request_cb_;
+  ResultCallback result_cb_;
+  ResultType result_;
+};
+
+// Creates an instance of AndroidProviderTask using the two callback and using
+// type deduction.
+template <typename ResultType>
+scoped_ptr<history::HistoryDBTask> CreateAndroidProviderTask(
+    const base::Callback<ResultType(history::AndroidProviderBackend*)>&
+        request_cb,
+    const base::Callback<void(ResultType)>& result_cb) {
+  return scoped_ptr<history::HistoryDBTask>(
+      new AndroidProviderTask<ResultType>(request_cb, result_cb));
+}
+
+// History and bookmarks ----------------------------------------------------
+
+// Inserts the given values into android provider backend.
+history::AndroidURLID InsertHistoryAndBookmarkAdapter(
+    const history::HistoryAndBookmarkRow& row,
+    history::AndroidProviderBackend* backend) {
+  return backend->InsertHistoryAndBookmark(row);
+}
+
+// Runs the given query on android provider backend and returns the result.
+//
+// |projections| is the vector of the result columns.
+// |selection| is the SQL WHERE clause without 'WHERE'.
+// |selection_args| is the arguments for WHERE clause.
+// |sort_order| is the SQL ORDER clause.
+history::AndroidStatement* QueryHistoryAndBookmarksAdapter(
+    const std::vector<history::HistoryAndBookmarkRow::ColumnID>& projections,
+    const std::string& selection,
+    const std::vector<base::string16>& selection_args,
+    const std::string& sort_order,
+    history::AndroidProviderBackend* backend) {
+  return backend->QueryHistoryAndBookmarks(projections, selection,
+                                           selection_args, sort_order);
+}
+
+// Returns the number of row updated by the update query.
+//
+// |row| is the value to update.
+// |selection| is the SQL WHERE clause without 'WHERE'.
+// |selection_args| is the arguments for the WHERE clause.
+int UpdateHistoryAndBookmarksAdapter(
+    const history::HistoryAndBookmarkRow& row,
+    const std::string& selection,
+    const std::vector<base::string16>& selection_args,
+    history::AndroidProviderBackend* backend) {
+  int count = 0;
+  backend->UpdateHistoryAndBookmarks(row, selection, selection_args, &count);
+  return count;
+}
+
+// Deletes the specified rows and returns the number of rows deleted.
+//
+// |selection| is the SQL WHERE clause without 'WHERE'.
+// |selection_args| is the arguments for the WHERE clause.
+//
+// If |selection| is empty all history and bookmarks are deleted.
+int DeleteHistoryAndBookmarksAdapter(
+    const std::string& selection,
+    const std::vector<base::string16>& selection_args,
+    history::AndroidProviderBackend* backend) {
+  int count = 0;
+  backend->DeleteHistoryAndBookmarks(selection, selection_args, &count);
+  return count;
+}
+
+// Deletes the matched history and returns the number of rows deleted.
+int DeleteHistoryAdapter(const std::string& selection,
+                         const std::vector<base::string16>& selection_args,
+                         history::AndroidProviderBackend* backend) {
+  int count = 0;
+  backend->DeleteHistory(selection, selection_args, &count);
+  return count;
+}
+
+// Statement ----------------------------------------------------------------
+
+// Move the statement's current position.
+int MoveStatementAdapter(history::AndroidStatement* statement,
+                         int current_pos,
+                         int destination,
+                         history::AndroidProviderBackend* backend) {
+  DCHECK_LE(-1, current_pos);
+  DCHECK_LE(-1, destination);
+
+  int cur = current_pos;
+  if (current_pos > destination) {
+    statement->statement()->Reset(false);
+    cur = -1;
+  }
+  for (; cur < destination; ++cur) {
+    if (!statement->statement()->Step())
+      break;
+  }
+
+  return cur;
+}
+
+// CloseStatementTask delete the passed |statement| in the DB thread (or in the
+// UI thread if the HistoryBackend is destroyed before the task is executed).
+class CloseStatementTask : public history::HistoryDBTask {
+ public:
+  // Close the given statement. The ownership is transfered.
+  explicit CloseStatementTask(history::AndroidStatement* statement)
+      : statement_(statement) {
+    DCHECK(statement_);
+  }
+
+  ~CloseStatementTask() override { delete statement_; }
+
+  // Returns the cancelable task tracker to use for this task. The task owns it,
+  // so it can never be cancelled. This is required due to be compatible with
+  // HistoryService::ScheduleDBTask() interface.
+  base::CancelableTaskTracker* tracker() { return &tracker_; }
+
+ private:
+  // history::HistoryDBTask implementation.
+  bool RunOnDBThread(history::HistoryBackend* backend,
+                     history::HistoryDatabase* db) override {
+    delete statement_;
+    statement_ = nullptr;
+    return true;
+  }
+
+  void DoneRunOnMainThread() override {}
+
+  history::AndroidStatement* statement_;
+  base::CancelableTaskTracker tracker_;
+};
+
+// Search terms -------------------------------------------------------------
+
+// Inserts the given values and returns the SearchTermID of the inserted row.
+history::SearchTermID InsertSearchTermAdapter(
+    const history::SearchRow& row,
+    history::AndroidProviderBackend* backend) {
+  return backend->InsertSearchTerm(row);
+}
+
+// Returns the number of row updated by the update query.
+//
+// |row| is the value to update.
+// |selection| is the SQL WHERE clause without 'WHERE'.
+// |selection_args| is the arguments for the WHERE clause.
+int UpdateSearchTermsAdapter(const history::SearchRow& row,
+                             const std::string& selection,
+                             const std::vector<base::string16> selection_args,
+                             history::AndroidProviderBackend* backend) {
+  int count = 0;
+  backend->UpdateSearchTerms(row, selection, selection_args, &count);
+  return count;
+}
+
+// Deletes the matched rows and returns the number of deleted rows.
+//
+// |selection| is the SQL WHERE clause without 'WHERE'.
+// |selection_args| is the arguments for WHERE clause.
+//
+// If |selection| is empty all search terms will be deleted.
+int DeleteSearchTermsAdapter(const std::string& selection,
+                             const std::vector<base::string16> selection_args,
+                             history::AndroidProviderBackend* backend) {
+  int count = 0;
+  backend->DeleteSearchTerms(selection, selection_args, &count);
+  return count;
+}
+
+// Returns the result of the given query.
+//
+// |projections| specifies the result columns.
+// |selection| is the SQL WHERE clause without 'WHERE'.
+// |selection_args| is the arguments for WHERE clause.
+// |sort_order| is the SQL ORDER clause.
+history::AndroidStatement* QuerySearchTermsAdapter(
+    const std::vector<history::SearchRow::ColumnID>& projections,
+    const std::string& selection,
+    const std::vector<base::string16>& selection_args,
+    const std::string& sort_order,
+    history::AndroidProviderBackend* backend) {
+  return backend->QuerySearchTerms(projections, selection, selection_args,
+                                   sort_order);
+}
+
+}  // namespace
 
 AndroidHistoryProviderService::AndroidHistoryProviderService(Profile* profile)
     : profile_(profile) {
@@ -30,23 +254,16 @@
     base::CancelableTaskTracker* tracker) {
   HistoryService* hs = HistoryServiceFactory::GetForProfile(
       profile_, ServiceAccessType::EXPLICIT_ACCESS);
-  if (hs) {
-    DCHECK(hs->thread_) << "History service being called after cleanup";
-    DCHECK(hs->thread_checker_.CalledOnValidThread());
-    return tracker->PostTaskAndReplyWithResult(
-        hs->thread_->message_loop_proxy().get(),
-        FROM_HERE,
-        base::Bind(&HistoryBackend::QueryHistoryAndBookmarks,
-                   hs->history_backend_.get(),
-                   projections,
-                   selection,
-                   selection_args,
-                   sort_order),
-        callback);
-  } else {
-    callback.Run(NULL);
+  if (!hs) {
+    callback.Run(nullptr);
     return base::CancelableTaskTracker::kBadTaskId;
   }
+  return hs->ScheduleDBTask(
+      CreateAndroidProviderTask(
+          base::Bind(&QueryHistoryAndBookmarksAdapter, projections, selection,
+                     selection_args, sort_order),
+          callback),
+      tracker);
 }
 
 base::CancelableTaskTracker::TaskId
@@ -58,22 +275,15 @@
     base::CancelableTaskTracker* tracker) {
   HistoryService* hs = HistoryServiceFactory::GetForProfile(
       profile_, ServiceAccessType::EXPLICIT_ACCESS);
-  if (hs) {
-    DCHECK(hs->thread_) << "History service being called after cleanup";
-    DCHECK(hs->thread_checker_.CalledOnValidThread());
-    return tracker->PostTaskAndReplyWithResult(
-        hs->thread_->message_loop_proxy().get(),
-        FROM_HERE,
-        base::Bind(&HistoryBackend::UpdateHistoryAndBookmarks,
-                   hs->history_backend_.get(),
-                   row,
-                   selection,
-                   selection_args),
-        callback);
-  } else {
+  if (!hs) {
     callback.Run(0);
     return base::CancelableTaskTracker::kBadTaskId;
   }
+  return hs->ScheduleDBTask(
+      CreateAndroidProviderTask(base::Bind(&UpdateHistoryAndBookmarksAdapter,
+                                           row, selection, selection_args),
+                                callback),
+      tracker);
 }
 
 base::CancelableTaskTracker::TaskId
@@ -84,21 +294,15 @@
     base::CancelableTaskTracker* tracker) {
   HistoryService* hs = HistoryServiceFactory::GetForProfile(
       profile_, ServiceAccessType::EXPLICIT_ACCESS);
-  if (hs) {
-    DCHECK(hs->thread_) << "History service being called after cleanup";
-    DCHECK(hs->thread_checker_.CalledOnValidThread());
-    return tracker->PostTaskAndReplyWithResult(
-        hs->thread_->message_loop_proxy().get(),
-        FROM_HERE,
-        base::Bind(&HistoryBackend::DeleteHistoryAndBookmarks,
-                   hs->history_backend_.get(),
-                   selection,
-                   selection_args),
-        callback);
-  } else {
+  if (!hs) {
     callback.Run(0);
     return base::CancelableTaskTracker::kBadTaskId;
   }
+  return hs->ScheduleDBTask(
+      CreateAndroidProviderTask(base::Bind(&DeleteHistoryAndBookmarksAdapter,
+                                           selection, selection_args),
+                                callback),
+      tracker);
 }
 
 base::CancelableTaskTracker::TaskId
@@ -108,20 +312,14 @@
     base::CancelableTaskTracker* tracker) {
   HistoryService* hs = HistoryServiceFactory::GetForProfile(
       profile_, ServiceAccessType::EXPLICIT_ACCESS);
-  if (hs) {
-    DCHECK(hs->thread_) << "History service being called after cleanup";
-    DCHECK(hs->thread_checker_.CalledOnValidThread());
-    return tracker->PostTaskAndReplyWithResult(
-        hs->thread_->message_loop_proxy().get(),
-        FROM_HERE,
-        base::Bind(&HistoryBackend::InsertHistoryAndBookmark,
-                   hs->history_backend_.get(),
-                   values),
-        callback);
-  } else {
+  if (!hs) {
     callback.Run(0);
     return base::CancelableTaskTracker::kBadTaskId;
   }
+  return hs->ScheduleDBTask(
+      CreateAndroidProviderTask(
+          base::Bind(&InsertHistoryAndBookmarkAdapter, values), callback),
+      tracker);
 }
 
 base::CancelableTaskTracker::TaskId
@@ -132,21 +330,15 @@
     base::CancelableTaskTracker* tracker) {
   HistoryService* hs = HistoryServiceFactory::GetForProfile(
       profile_, ServiceAccessType::EXPLICIT_ACCESS);
-  if (hs) {
-    DCHECK(hs->thread_) << "History service being called after cleanup";
-    DCHECK(hs->thread_checker_.CalledOnValidThread());
-    return tracker->PostTaskAndReplyWithResult(
-        hs->thread_->message_loop_proxy().get(),
-        FROM_HERE,
-        base::Bind(&HistoryBackend::DeleteHistory,
-                   hs->history_backend_.get(),
-                   selection,
-                   selection_args),
-        callback);
-  } else {
+  if (!hs) {
     callback.Run(0);
     return base::CancelableTaskTracker::kBadTaskId;
   }
+  return hs->ScheduleDBTask(
+      CreateAndroidProviderTask(
+          base::Bind(&DeleteHistoryAdapter, selection, selection_args),
+          callback),
+      tracker);
 }
 
 base::CancelableTaskTracker::TaskId
@@ -158,35 +350,28 @@
     base::CancelableTaskTracker* tracker) {
   HistoryService* hs = HistoryServiceFactory::GetForProfile(
       profile_, ServiceAccessType::EXPLICIT_ACCESS);
-  if (hs) {
-    DCHECK(hs->thread_) << "History service being called after cleanup";
-    DCHECK(hs->thread_checker_.CalledOnValidThread());
-    return tracker->PostTaskAndReplyWithResult(
-        hs->thread_->message_loop_proxy().get(),
-        FROM_HERE,
-        base::Bind(&HistoryBackend::MoveStatement,
-                   hs->history_backend_.get(),
-                   statement,
-                   current_pos,
-                   destination),
-        callback);
-  } else {
+  if (!hs) {
     callback.Run(current_pos);
     return base::CancelableTaskTracker::kBadTaskId;
   }
+  return hs->ScheduleDBTask(
+      CreateAndroidProviderTask(base::Bind(&MoveStatementAdapter, statement,
+                                           current_pos, destination),
+                                callback),
+      tracker);
 }
 
 void AndroidHistoryProviderService::CloseStatement(
     history::AndroidStatement* statement) {
   HistoryService* hs = HistoryServiceFactory::GetForProfile(
       profile_, ServiceAccessType::EXPLICIT_ACCESS);
-  if (hs) {
-    hs->ScheduleTask(HistoryService::PRIORITY_NORMAL,
-                     base::Bind(&HistoryBackend::CloseStatement,
-                                hs->history_backend_.get(), statement));
-  } else {
+  if (!hs) {
     delete statement;
+    return;
   }
+  scoped_ptr<CloseStatementTask> task(new CloseStatementTask(statement));
+  base::CancelableTaskTracker* tracker = task->tracker();
+  hs->ScheduleDBTask(task.Pass(), tracker);
 }
 
 base::CancelableTaskTracker::TaskId
@@ -196,19 +381,14 @@
     base::CancelableTaskTracker* tracker) {
   HistoryService* hs = HistoryServiceFactory::GetForProfile(
       profile_, ServiceAccessType::EXPLICIT_ACCESS);
-  if (hs) {
-    DCHECK(hs->thread_) << "History service being called after cleanup";
-    DCHECK(hs->thread_checker_.CalledOnValidThread());
-    return tracker->PostTaskAndReplyWithResult(
-        hs->thread_->message_loop_proxy().get(),
-        FROM_HERE,
-        base::Bind(
-            &HistoryBackend::InsertSearchTerm, hs->history_backend_.get(), row),
-        callback);
-  } else {
+  if (!hs) {
     callback.Run(0);
     return base::CancelableTaskTracker::kBadTaskId;
   }
+  return hs->ScheduleDBTask(
+      CreateAndroidProviderTask(base::Bind(&InsertSearchTermAdapter, row),
+                                callback),
+      tracker);
 }
 
 base::CancelableTaskTracker::TaskId
@@ -220,22 +400,15 @@
     base::CancelableTaskTracker* tracker) {
   HistoryService* hs = HistoryServiceFactory::GetForProfile(
       profile_, ServiceAccessType::EXPLICIT_ACCESS);
-  if (hs) {
-    DCHECK(hs->thread_) << "History service being called after cleanup";
-    DCHECK(hs->thread_checker_.CalledOnValidThread());
-    return tracker->PostTaskAndReplyWithResult(
-        hs->thread_->message_loop_proxy().get(),
-        FROM_HERE,
-        base::Bind(&HistoryBackend::UpdateSearchTerms,
-                   hs->history_backend_.get(),
-                   row,
-                   selection,
-                   selection_args),
-        callback);
-  } else {
+  if (!hs) {
     callback.Run(0);
     return base::CancelableTaskTracker::kBadTaskId;
   }
+  return hs->ScheduleDBTask(
+      CreateAndroidProviderTask(
+          base::Bind(&UpdateSearchTermsAdapter, row, selection, selection_args),
+          callback),
+      tracker);
 }
 
 base::CancelableTaskTracker::TaskId
@@ -246,21 +419,15 @@
     base::CancelableTaskTracker* tracker) {
   HistoryService* hs = HistoryServiceFactory::GetForProfile(
       profile_, ServiceAccessType::EXPLICIT_ACCESS);
-  if (hs) {
-    DCHECK(hs->thread_) << "History service being called after cleanup";
-    DCHECK(hs->thread_checker_.CalledOnValidThread());
-    return tracker->PostTaskAndReplyWithResult(
-        hs->thread_->message_loop_proxy().get(),
-        FROM_HERE,
-        base::Bind(&HistoryBackend::DeleteSearchTerms,
-                   hs->history_backend_.get(),
-                   selection,
-                   selection_args),
-        callback);
-  } else {
+  if (!hs) {
     callback.Run(0);
     return base::CancelableTaskTracker::kBadTaskId;
   }
+  return hs->ScheduleDBTask(
+      CreateAndroidProviderTask(
+          base::Bind(&DeleteSearchTermsAdapter, selection, selection_args),
+          callback),
+      tracker);
 }
 
 base::CancelableTaskTracker::TaskId
@@ -273,23 +440,16 @@
     base::CancelableTaskTracker* tracker) {
   HistoryService* hs = HistoryServiceFactory::GetForProfile(
       profile_, ServiceAccessType::EXPLICIT_ACCESS);
-  if (hs) {
-    DCHECK(hs->thread_) << "History service being called after cleanup";
-    DCHECK(hs->thread_checker_.CalledOnValidThread());
-    return tracker->PostTaskAndReplyWithResult(
-        hs->thread_->message_loop_proxy().get(),
-        FROM_HERE,
-        base::Bind(&HistoryBackend::QuerySearchTerms,
-                   hs->history_backend_.get(),
-                   projections,
-                   selection,
-                   selection_args,
-                   sort_order),
-        callback);
-  } else {
-    callback.Run(NULL);
+  if (!hs) {
+    callback.Run(nullptr);
     return base::CancelableTaskTracker::kBadTaskId;
   }
+  return hs->ScheduleDBTask(
+      CreateAndroidProviderTask(
+          base::Bind(&QuerySearchTermsAdapter, projections, selection,
+                     selection_args, sort_order),
+          callback),
+      tracker);
 }
 
 base::CancelableTaskTracker::TaskId
diff --git a/chrome/browser/history/content_visit_delegate.cc b/chrome/browser/history/content_visit_delegate.cc
new file mode 100644
index 0000000..6bd3935
--- /dev/null
+++ b/chrome/browser/history/content_visit_delegate.cc
@@ -0,0 +1,121 @@
+// 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 "chrome/browser/history/content_visit_delegate.h"
+
+#include "base/logging.h"
+#include "base/memory/ref_counted.h"
+#include "chrome/browser/history/history_backend.h"
+#include "chrome/browser/history/history_service.h"
+#include "components/history/core/browser/history_database.h"
+#include "components/history/core/browser/history_db_task.h"
+#include "components/visitedlink/browser/visitedlink_master.h"
+#include "url/gurl.h"
+
+namespace {
+
+// URLIterator from std::vector<GURL>
+class URLIteratorFromURLs : public visitedlink::VisitedLinkMaster::URLIterator {
+ public:
+  explicit URLIteratorFromURLs(const std::vector<GURL>& urls)
+      : itr_(urls.begin()), end_(urls.end()) {}
+
+  // visitedlink::VisitedLinkMaster::URLIterator implementation.
+  const GURL& NextURL() override { return *(itr_++); }
+  bool HasNextURL() const override { return itr_ != end_; }
+
+ private:
+  std::vector<GURL>::const_iterator itr_;
+  std::vector<GURL>::const_iterator end_;
+
+  DISALLOW_COPY_AND_ASSIGN(URLIteratorFromURLs);
+};
+
+// IterateUrlsDBTask bridge HistoryBackend::URLEnumerator to
+// visitedlink::VisitedLinkDelegate::URLEnumerator.
+class IterateUrlsDBTask : public history::HistoryDBTask {
+ public:
+  explicit IterateUrlsDBTask(const scoped_refptr<
+      visitedlink::VisitedLinkDelegate::URLEnumerator>& enumerator);
+  ~IterateUrlsDBTask() override;
+
+ private:
+  // Implementation of history::HistoryDBTask.
+  bool RunOnDBThread(history::HistoryBackend* backend,
+                     history::HistoryDatabase* db) override;
+  void DoneRunOnMainThread() override;
+
+  scoped_refptr<visitedlink::VisitedLinkDelegate::URLEnumerator> enumerator_;
+
+  DISALLOW_COPY_AND_ASSIGN(IterateUrlsDBTask);
+};
+
+IterateUrlsDBTask::IterateUrlsDBTask(const scoped_refptr<
+    visitedlink::VisitedLinkDelegate::URLEnumerator>& enumerator)
+    : enumerator_(enumerator) {
+}
+
+IterateUrlsDBTask::~IterateUrlsDBTask() {
+}
+
+bool IterateUrlsDBTask::RunOnDBThread(history::HistoryBackend* backend,
+                                      history::HistoryDatabase* db) {
+  bool success = false;
+  if (db) {
+    history::HistoryDatabase::URLEnumerator iter;
+    if (db->InitURLEnumeratorForEverything(&iter)) {
+      history::URLRow row;
+      while (iter.GetNextURL(&row))
+        enumerator_->OnURL(row.url());
+      success = true;
+    }
+  }
+  enumerator_->OnComplete(success);
+  return true;
+}
+
+void IterateUrlsDBTask::DoneRunOnMainThread() {
+}
+
+}  // namespace
+
+ContentVisitDelegate::ContentVisitDelegate(
+    content::BrowserContext* browser_context)
+    : history_service_(nullptr),
+      visitedlink_master_(
+          new visitedlink::VisitedLinkMaster(browser_context, this, true)) {
+}
+
+ContentVisitDelegate::~ContentVisitDelegate() {
+}
+
+bool ContentVisitDelegate::Init(HistoryService* history_service) {
+  DCHECK(history_service);
+  history_service_ = history_service;
+  return visitedlink_master_->Init();
+}
+
+void ContentVisitDelegate::AddURL(const GURL& url) {
+  visitedlink_master_->AddURL(url);
+}
+
+void ContentVisitDelegate::AddURLs(const std::vector<GURL>& urls) {
+  visitedlink_master_->AddURLs(urls);
+}
+
+void ContentVisitDelegate::DeleteURLs(const std::vector<GURL>& urls) {
+  URLIteratorFromURLs iterator(urls);
+  visitedlink_master_->DeleteURLs(&iterator);
+}
+
+void ContentVisitDelegate::DeleteAllURLs() {
+  visitedlink_master_->DeleteAllURLs();
+}
+
+void ContentVisitDelegate::RebuildTable(
+    const scoped_refptr<URLEnumerator>& enumerator) {
+  DCHECK(history_service_);
+  scoped_ptr<history::HistoryDBTask> task(new IterateUrlsDBTask(enumerator));
+  history_service_->ScheduleDBTask(task.Pass(), &task_tracker_);
+}
diff --git a/chrome/browser/history/content_visit_delegate.h b/chrome/browser/history/content_visit_delegate.h
new file mode 100644
index 0000000..df98c72
--- /dev/null
+++ b/chrome/browser/history/content_visit_delegate.h
@@ -0,0 +1,51 @@
+// 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 CHROME_BROWSER_HISTORY_CONTENT_VISIT_DELEGATE_H_
+#define CHROME_BROWSER_HISTORY_CONTENT_VISIT_DELEGATE_H_
+
+#include <vector>
+
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/task/cancelable_task_tracker.h"
+#include "components/history/core/browser/visit_delegate.h"
+#include "components/visitedlink/browser/visitedlink_delegate.h"
+
+namespace content {
+class BrowserContext;
+}
+
+namespace visitedlink {
+class VisitedLinkMaster;
+}
+
+// ContentVisitDelegate bridge history::VisitDelegate events to
+// visitedlink::VisitedLinkMaster.
+class ContentVisitDelegate : public history::VisitDelegate,
+                             public visitedlink::VisitedLinkDelegate {
+ public:
+  explicit ContentVisitDelegate(content::BrowserContext* browser_context);
+  ~ContentVisitDelegate() override;
+
+ private:
+  // Implementation of history::VisitDelegate.
+  bool Init(HistoryService* history_service) override;
+  void AddURL(const GURL& url) override;
+  void AddURLs(const std::vector<GURL>& urls) override;
+  void DeleteURLs(const std::vector<GURL>& urls) override;
+  void DeleteAllURLs() override;
+
+  // Implementation of visitedlink::VisitedLinkDelegate.
+  void RebuildTable(const scoped_refptr<
+      visitedlink::VisitedLinkDelegate::URLEnumerator>& enumerator) override;
+
+  HistoryService* history_service_;  // Weak.
+  scoped_ptr<visitedlink::VisitedLinkMaster> visitedlink_master_;
+  base::CancelableTaskTracker task_tracker_;
+
+  DISALLOW_COPY_AND_ASSIGN(ContentVisitDelegate);
+};
+
+#endif  // CHROME_BROWSER_HISTORY_CONTENT_VISIT_DELEGATE_H_
diff --git a/chrome/browser/history/history_backend.cc b/chrome/browser/history/history_backend.cc
index 26c7133..0bbbf400 100644
--- a/chrome/browser/history/history_backend.cc
+++ b/chrome/browser/history/history_backend.cc
@@ -172,8 +172,7 @@
   return is_canceled_.Run();
 }
 
-bool QueuedHistoryDBTask::Run(HistoryBackend* backend,
-                                        HistoryDatabase* db) {
+bool QueuedHistoryDBTask::Run(HistoryBackend* backend, HistoryDatabase* db) {
   return task_->RunOnDBThread(backend, db);
 }
 
@@ -924,23 +923,6 @@
   db_->AddURL(url_info);
 }
 
-void HistoryBackend::IterateURLs(
-    const scoped_refptr<visitedlink::VisitedLinkDelegate::URLEnumerator>&
-    iterator) {
-  if (db_) {
-    HistoryDatabase::URLEnumerator e;
-    if (db_->InitURLEnumeratorForEverything(&e)) {
-      URLRow info;
-      while (e.GetNextURL(&info)) {
-        iterator->OnURL(info.url());
-      }
-      iterator->OnComplete(true);  // Success.
-      return;
-    }
-  }
-  iterator->OnComplete(false);  // Failure.
-}
-
 bool HistoryBackend::GetAllTypedURLs(URLRows* urls) {
   if (db_)
     return db_->GetAllTypedUrls(urls);
diff --git a/chrome/browser/history/history_backend.h b/chrome/browser/history/history_backend.h
index 0750ac7e..f30731f0 100644
--- a/chrome/browser/history/history_backend.h
+++ b/chrome/browser/history/history_backend.h
@@ -25,13 +25,8 @@
 #include "components/history/core/browser/keyword_id.h"
 #include "components/history/core/browser/thumbnail_database.h"
 #include "components/history/core/browser/visit_tracker.h"
-#include "components/visitedlink/browser/visitedlink_delegate.h"
 #include "sql/init_status.h"
 
-#if defined(OS_ANDROID)
-#include "components/history/core/browser/android/android_history_types.h"
-#endif
-
 class HistoryURLProvider;
 struct HistoryURLProviderParams;
 class SkBitmap;
@@ -220,9 +215,6 @@
   void ScheduleAutocomplete(const base::Callback<
       void(history::HistoryBackend*, history::URLDatabase*)>& callback);
 
-  void IterateURLs(
-      const scoped_refptr<visitedlink::VisitedLinkDelegate::URLEnumerator>&
-          enumerator);
   void QueryURL(const GURL& url,
                 bool want_visits,
                 QueryURLResult* query_url_result);
@@ -340,90 +332,11 @@
 #if defined(OS_ANDROID)
   // Android Provider ---------------------------------------------------------
 
-  // History and bookmarks ----------------------------------------------------
-  // Inserts the given values into history backend.
-  AndroidURLID InsertHistoryAndBookmark(const HistoryAndBookmarkRow& row);
-
-  // Runs the given query on history backend and returns the result.
-  //
-  // |projections| is the vector of the result columns.
-  // |selection| is the SQL WHERE clause without 'WHERE'.
-  // |selection_args| is the arguments for WHERE clause.
-  // |sort_order| is the SQL ORDER clause.
-  history::AndroidStatement* QueryHistoryAndBookmarks(
-      const std::vector<HistoryAndBookmarkRow::ColumnID>& projections,
-      const std::string& selection,
-      const std::vector<base::string16>& selection_args,
-      const std::string& sort_order);
-
-  // Returns the number of row updated by the update query.
-  //
-  // |row| is the value to update.
-  // |selection| is the SQL WHERE clause without 'WHERE'.
-  // |selection_args| is the arguments for the WHERE clause.
-  int UpdateHistoryAndBookmarks(
-      const HistoryAndBookmarkRow& row,
-      const std::string& selection,
-      const std::vector<base::string16>& selection_args);
-
-  // Deletes the specified rows and returns the number of rows deleted.
-  //
-  // |selection| is the SQL WHERE clause without 'WHERE'.
-  // |selection_args| is the arguments for the WHERE clause.
-  //
-  // If |selection| is empty all history and bookmarks are deleted.
-  int DeleteHistoryAndBookmarks(
-      const std::string& selection,
-      const std::vector<base::string16>& selection_args);
-
-  // Deletes the matched history and returns the number of rows deleted.
-  int DeleteHistory(const std::string& selection,
-                    const std::vector<base::string16>& selection_args);
-
-  // Statement ----------------------------------------------------------------
-  // Move the statement's current position.
-  int MoveStatement(history::AndroidStatement* statement,
-                    int current_pos,
-                    int destination);
-
-  // Close the given statement. The ownership is transfered.
-  void CloseStatement(AndroidStatement* statement);
-
-  // Search terms -------------------------------------------------------------
-  // Inserts the given values and returns the SearchTermID of the inserted row.
-  SearchTermID InsertSearchTerm(const SearchRow& row);
-
-  // Returns the number of row updated by the update query.
-  //
-  // |row| is the value to update.
-  // |selection| is the SQL WHERE clause without 'WHERE'.
-  // |selection_args| is the arguments for the WHERE clause.
-  int UpdateSearchTerms(const SearchRow& row,
-                        const std::string& selection,
-                        const std::vector<base::string16> selection_args);
-
-  // Deletes the matched rows and returns the number of deleted rows.
-  //
-  // |selection| is the SQL WHERE clause without 'WHERE'.
-  // |selection_args| is the arguments for WHERE clause.
-  //
-  // If |selection| is empty all search terms will be deleted.
-  int DeleteSearchTerms(const std::string& selection,
-                        const std::vector<base::string16> selection_args);
-
-  // Returns the result of the given query.
-  //
-  // |projections| specifies the result columns.
-  // |selection| is the SQL WHERE clause without 'WHERE'.
-  // |selection_args| is the arguments for WHERE clause.
-  // |sort_order| is the SQL ORDER clause.
-  history::AndroidStatement* QuerySearchTerms(
-      const std::vector<SearchRow::ColumnID>& projections,
-      const std::string& selection,
-      const std::vector<base::string16>& selection_args,
-      const std::string& sort_order);
-
-#endif  // defined(OS_ANDROID)
+  // Returns the android provider backend associated with the HistoryBackend.
+  AndroidProviderBackend* android_provider_backend() {
+    return android_provider_backend_.get();
+  }
+#endif
 
   // Generic operations --------------------------------------------------------
 
diff --git a/chrome/browser/history/history_backend_android.cc b/chrome/browser/history/history_backend_android.cc
deleted file mode 100644
index 86336036..0000000
--- a/chrome/browser/history/history_backend_android.cc
+++ /dev/null
@@ -1,135 +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.
-
-#include "chrome/browser/history/history_backend.h"
-
-#include "chrome/browser/history/android/android_provider_backend.h"
-
-namespace history {
-
-AndroidURLID HistoryBackend::InsertHistoryAndBookmark(
-    const HistoryAndBookmarkRow& row) {
-  AndroidURLID id = 0;
-  if (android_provider_backend_)
-    id = android_provider_backend_->InsertHistoryAndBookmark(row);
-  return id;
-}
-
-AndroidStatement* HistoryBackend::QueryHistoryAndBookmarks(
-    const std::vector<HistoryAndBookmarkRow::ColumnID>& projections,
-    const std::string& selection,
-    const std::vector<base::string16>& selection_args,
-    const std::string& sort_order) {
-  AndroidStatement* statement = NULL;
-  if (android_provider_backend_) {
-    statement = android_provider_backend_->QueryHistoryAndBookmarks(
-        projections, selection, selection_args, sort_order);
-  }
-  return statement;
-}
-
-int HistoryBackend::UpdateHistoryAndBookmarks(
-    const HistoryAndBookmarkRow& row,
-    const std::string& selection,
-    const std::vector<base::string16>& selection_args) {
-  int count = 0;
-  if (android_provider_backend_) {
-    android_provider_backend_->UpdateHistoryAndBookmarks(
-        row, selection, selection_args, &count);
-  }
-  return count;
-}
-
-int HistoryBackend::DeleteHistoryAndBookmarks(
-    const std::string& selection,
-    const std::vector<base::string16>& selection_args) {
-  int count = 0;
-  if (android_provider_backend_) {
-    android_provider_backend_->DeleteHistoryAndBookmarks(
-        selection, selection_args, &count);
-  }
-  return count;
-}
-
-int HistoryBackend::DeleteHistory(
-    const std::string& selection,
-    const std::vector<base::string16>& selection_args) {
-  int count = 0;
-  if (android_provider_backend_) {
-    android_provider_backend_->DeleteHistory(selection, selection_args, &count);
-  }
-  return count;
-}
-
-// Statement -------------------------------------------------------------------
-
-int HistoryBackend::MoveStatement(history::AndroidStatement* statement,
-                                  int current_pos,
-                                  int destination) {
-  DCHECK_LE(-1, current_pos);
-  DCHECK_LE(-1, destination);
-
-  int cur = current_pos;
-  if (current_pos > destination) {
-    statement->statement()->Reset(false);
-    cur = -1;
-  }
-  for (; cur < destination; ++cur) {
-    if (!statement->statement()->Step())
-      break;
-  }
-
-  return cur;
-}
-
-void HistoryBackend::CloseStatement(AndroidStatement* statement) {
-  delete statement;
-}
-
-// Search Term -----------------------------------------------------------------
-
-SearchTermID HistoryBackend::InsertSearchTerm(const SearchRow& row) {
-  SearchTermID id = 0;
-  if (android_provider_backend_)
-    id = android_provider_backend_->InsertSearchTerm(row);
-  return id;
-}
-
-int HistoryBackend::UpdateSearchTerms(
-    const SearchRow& row,
-    const std::string& selection,
-    const std::vector<base::string16> selection_args) {
-  int count = 0;
-  if (android_provider_backend_) {
-    android_provider_backend_->UpdateSearchTerms(
-        row, selection, selection_args, &count);
-  }
-  return count;
-}
-
-int HistoryBackend::DeleteSearchTerms(
-    const std::string& selection,
-    const std::vector<base::string16> selection_args) {
-  int count = 0;
-  if (android_provider_backend_) {
-    android_provider_backend_->DeleteSearchTerms(
-        selection, selection_args, &count);
-  }
-  return count;
-}
-
-AndroidStatement* HistoryBackend::QuerySearchTerms(
-    const std::vector<SearchRow::ColumnID>& projections,
-    const std::string& selection,
-    const std::vector<base::string16>& selection_args,
-    const std::string& sort_order) {
-  AndroidStatement* statement = NULL;
-  if (android_provider_backend_) {
-    statement = android_provider_backend_->QuerySearchTerms(projections,
-        selection, selection_args, sort_order);
-  }
-  return statement;
-}
-
-}  // namespace history
diff --git a/chrome/browser/history/history_backend_unittest.cc b/chrome/browser/history/history_backend_unittest.cc
index 6f3a46d..9eeca2a 100644
--- a/chrome/browser/history/history_backend_unittest.cc
+++ b/chrome/browser/history/history_backend_unittest.cc
@@ -22,6 +22,7 @@
 #include "base/strings/string16.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/history/content_visit_delegate.h"
 #include "chrome/browser/history/history_service.h"
 #include "chrome/browser/history/history_service_factory.h"
 #include "chrome/browser/history/in_memory_history_backend.h"
@@ -3041,8 +3042,8 @@
   GURL url("http://www.google.com");
   HistoryClientMock history_client;
   history_client.AddBookmark(url);
-  scoped_ptr<HistoryService> service(
-      new HistoryService(&history_client, profile.get()));
+  scoped_ptr<HistoryService> service(new HistoryService(
+      &history_client, scoped_ptr<history::VisitDelegate>()));
   EXPECT_TRUE(
       service->Init(profile->GetPrefs()->GetString(prefs::kAcceptLanguages),
                     TestHistoryDatabaseParamsForPath(profile->GetPath())));
diff --git a/chrome/browser/history/history_service.cc b/chrome/browser/history/history_service.cc
index 61d4cdfc..a14daae 100644
--- a/chrome/browser/history/history_service.cc
+++ b/chrome/browser/history/history_service.cc
@@ -42,12 +42,10 @@
 #include "components/history/core/browser/in_memory_database.h"
 #include "components/history/core/browser/keyword_search_term.h"
 #include "components/history/core/browser/visit_database.h"
+#include "components/history/core/browser/visit_delegate.h"
 #include "components/history/core/browser/visit_filter.h"
 #include "components/history/core/browser/web_history_service.h"
 #include "components/history/core/common/thumbnail_score.h"
-#include "components/visitedlink/browser/visitedlink_master.h"
-#include "content/public/browser/browser_thread.h"
-#include "content/public/browser/download_item.h"
 #include "sync/api/sync_error_factory.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 
@@ -82,26 +80,6 @@
   callback.Run(result->success, result->count, result->first_visit);
 }
 
-// Extract history::URLRows into GURLs for VisitedLinkMaster.
-class URLIteratorFromURLRows
-    : public visitedlink::VisitedLinkMaster::URLIterator {
- public:
-  explicit URLIteratorFromURLRows(const history::URLRows& url_rows)
-      : itr_(url_rows.begin()),
-        end_(url_rows.end()) {
-  }
-
-  const GURL& NextURL() override { return (itr_++)->url(); }
-
-  bool HasNextURL() const override { return itr_ != end_; }
-
- private:
-  history::URLRows::const_iterator itr_;
-  history::URLRows::const_iterator end_;
-
-  DISALLOW_COPY_AND_ASSIGN(URLIteratorFromURLRows);
-};
-
 // Callback from WebHistoryService::ExpireWebHistory().
 void ExpireWebHistoryComplete(bool success) {
   // Ignore the result.
@@ -214,18 +192,18 @@
 // history thread.
 HistoryService::HistoryService()
     : thread_(new base::Thread(kHistoryThreadName)),
-      history_client_(NULL),
+      history_client_(nullptr),
       backend_loaded_(false),
       no_db_(false),
       weak_ptr_factory_(this) {
 }
 
 HistoryService::HistoryService(
-    history::HistoryClient* history_client, Profile* profile)
+    history::HistoryClient* history_client,
+    scoped_ptr<history::VisitDelegate> visit_delegate)
     : thread_(new base::Thread(kHistoryThreadName)),
+      visit_delegate_(visit_delegate.Pass()),
       history_client_(history_client),
-      visitedlink_master_(new visitedlink::VisitedLinkMaster(
-          profile, this, true)),
       backend_loaded_(false),
       no_db_(false),
       weak_ptr_factory_(this) {
@@ -253,7 +231,7 @@
 
 history::URLDatabase* HistoryService::InMemoryDatabase() {
   DCHECK(thread_checker_.CalledOnValidThread());
-  return in_memory_backend_ ? in_memory_backend_->db() : NULL;
+  return in_memory_backend_ ? in_memory_backend_->db() : nullptr;
 }
 
 bool HistoryService::GetTypedCountForURL(const GURL& url, int* typed_count) {
@@ -351,15 +329,17 @@
   observers_.RemoveObserver(observer);
 }
 
-void HistoryService::ScheduleDBTask(scoped_ptr<history::HistoryDBTask> task,
-                                    base::CancelableTaskTracker* tracker) {
+base::CancelableTaskTracker::TaskId HistoryService::ScheduleDBTask(
+    scoped_ptr<history::HistoryDBTask> task,
+    base::CancelableTaskTracker* tracker) {
   DCHECK(thread_) << "History service being called after cleanup";
   DCHECK(thread_checker_.CalledOnValidThread());
   base::CancelableTaskTracker::IsCanceledCallback is_canceled;
-  tracker->NewTrackedTaskId(&is_canceled);
+  base::CancelableTaskTracker::TaskId task_id =
+      tracker->NewTrackedTaskId(&is_canceled);
   // Use base::ThreadTaskRunnerHandler::Get() to get a message loop proxy to
   // the current message loop so that we can forward the call to the method
-  // HistoryDBTask::DoneRunOnMainThread in the correct thread.
+  // HistoryDBTask::DoneRunOnMainThread() in the correct thread.
   thread_->message_loop_proxy()->PostTask(
       FROM_HERE,
       base::Bind(&HistoryBackend::ProcessDBTask,
@@ -367,6 +347,7 @@
                  base::Passed(&task),
                  base::ThreadTaskRunnerHandle::Get(),
                  is_canceled));
+  return task_id;
 }
 
 void HistoryService::FlushForTest(const base::Closure& flushed) {
@@ -404,7 +385,7 @@
                              history::VisitSource visit_source) {
   DCHECK(thread_checker_.CalledOnValidThread());
   AddPage(
-      history::HistoryAddPageArgs(url, time, NULL, 0, GURL(),
+      history::HistoryAddPageArgs(url, time, nullptr, 0, GURL(),
                                   history::RedirectList(),
                                   ui::PAGE_TRANSITION_LINK,
                                   visit_source, false));
@@ -421,19 +402,17 @@
   if (!CanAddURL(add_page_args.url))
     return;
 
-  // Add link & all redirects to visited link list.
-  if (visitedlink_master_) {
-    visitedlink_master_->AddURL(add_page_args.url);
-
+  // Inform VisitedDelegate of all links and redirects.
+  if (visit_delegate_) {
     if (!add_page_args.redirects.empty()) {
-      // We should not be asked to add a page in the middle of a redirect chain.
-      DCHECK_EQ(add_page_args.url,
-                add_page_args.redirects[add_page_args.redirects.size() - 1]);
-
-      // We need the !redirects.empty() condition above since size_t is unsigned
-      // and will wrap around when we subtract one from a 0 size.
-      for (size_t i = 0; i < add_page_args.redirects.size() - 1; i++)
-        visitedlink_master_->AddURL(add_page_args.redirects[i]);
+      // We should not be asked to add a page in the middle of a redirect chain,
+      // and thus add_page_args.url should be the last element in the array
+      // add_page_args.redirects which mean we can use VisitDelegate::AddURLs()
+      // with the whole array.
+      DCHECK_EQ(add_page_args.url, add_page_args.redirects.back());
+      visit_delegate_->AddURLs(add_page_args.redirects);
+    } else {
+      visit_delegate_->AddURL(add_page_args.url);
     }
   }
 
@@ -487,9 +466,9 @@
   if (!CanAddURL(url))
     return;
 
-  // Add to the visited links system.
-  if (visitedlink_master_)
-    visitedlink_master_->AddURL(url);
+  // Inform VisitDelegate of the URL.
+  if (visit_delegate_)
+    visit_delegate_->AddURL(url);
 
   history::URLRow row(url);
   row.set_title(title);
@@ -510,15 +489,14 @@
                                          history::VisitSource visit_source) {
   DCHECK(thread_) << "History service being called after cleanup";
   DCHECK(thread_checker_.CalledOnValidThread());
-  // Add to the visited links system.
-  if (visitedlink_master_) {
+
+  // Inform the VisitDelegate of the URLs
+  if (!info.empty() && visit_delegate_) {
     std::vector<GURL> urls;
     urls.reserve(info.size());
-    for (history::URLRows::const_iterator i = info.begin(); i != info.end();
-         ++i)
-      urls.push_back(i->url());
-
-    visitedlink_master_->AddURLs(urls);
+    for (const auto& row : info)
+      urls.push_back(row.url());
+    visit_delegate_->AddURLs(urls);
   }
 
   ScheduleTask(PRIORITY_NORMAL,
@@ -750,7 +728,7 @@
   DCHECK(thread_checker_.CalledOnValidThread());
   std::vector<history::DownloadRow>* rows =
     new std::vector<history::DownloadRow>();
-  scoped_ptr<std::vector<history::DownloadRow> > scoped_rows(rows);
+  scoped_ptr<std::vector<history::DownloadRow>> scoped_rows(rows);
   // Beware! The first Bind() does not simply |scoped_rows.get()| because
   // base::Passed(&scoped_rows) nullifies |scoped_rows|, and compilers do not
   // guarantee that the first Bind's arguments are evaluated before the second
@@ -936,26 +914,18 @@
     ScheduleTask(PRIORITY_NORMAL, closing_task);
     closing_task.Reset();
     HistoryBackend* raw_ptr = history_backend_.get();
-    history_backend_ = NULL;
+    history_backend_ = nullptr;
     thread_->message_loop()->ReleaseSoon(FROM_HERE, raw_ptr);
   }
 
   // Delete the thread, which joins with the background thread. We defensively
-  // NULL the pointer before deleting it in case somebody tries to use it
+  // nullptr the pointer before deleting it in case somebody tries to use it
   // during shutdown, but this shouldn't happen.
   base::Thread* thread = thread_;
-  thread_ = NULL;
+  thread_ = nullptr;
   delete thread;
 }
 
-void HistoryService::RebuildTable(
-    const scoped_refptr<URLEnumerator>& enumerator) {
-  DCHECK(thread_) << "History service being called after cleanup";
-  DCHECK(thread_checker_.CalledOnValidThread());
-  ScheduleTask(PRIORITY_NORMAL, base::Bind(&HistoryBackend::IterateURLs,
-                                           history_backend_.get(), enumerator));
-}
-
 bool HistoryService::Init(
     bool no_db,
     const std::string& languages,
@@ -991,10 +961,8 @@
                base::Bind(&HistoryBackend::Init, history_backend_.get(),
                           languages, no_db_, history_database_params));
 
-  if (visitedlink_master_) {
-    bool result = visitedlink_master_->Init();
-    DCHECK(result);
-  }
+  if (visit_delegate_ && !visit_delegate_->Init(this))
+    return false;
 
   return true;
 }
@@ -1220,21 +1188,23 @@
   if (!thread_)
     return;
 
-  // Update the visited link system for deleted URLs. We will update the
-  // visited link system for added URLs as soon as we get the add
-  // notification (we don't have to wait for the backend, which allows us to
-  // be faster to update the state).
+  // Inform the VisitDelegate of the deleted URLs. We will inform the delegate
+  // of added URLs as soon as we get the add notification (we don't have to wait
+  // for the backend, which allows us to be faster to update the state).
   //
   // For deleted URLs, we don't typically know what will be deleted since
   // delete notifications are by time. We would also like to be more
   // respectful of privacy and never tell the user something is gone when it
   // isn't. Therefore, we update the delete URLs after the fact.
-  if (visitedlink_master_) {
+  if (visit_delegate_) {
     if (all_history) {
-      visitedlink_master_->DeleteAllURLs();
+      visit_delegate_->DeleteAllURLs();
     } else {
-      URLIteratorFromURLRows iterator(deleted_rows);
-      visitedlink_master_->DeleteURLs(&iterator);
+      std::vector<GURL> urls;
+      urls.reserve(deleted_rows.size());
+      for (const auto& row : deleted_rows)
+        urls.push_back(row.url());
+      visit_delegate_->DeleteURLs(urls);
     }
   }
 
diff --git a/chrome/browser/history/history_service.h b/chrome/browser/history/history_service.h
index 8b2b4b0..6a204c3 100644
--- a/chrome/browser/history/history_service.h
+++ b/chrome/browser/history/history_service.h
@@ -29,7 +29,6 @@
 #include "components/favicon_base/favicon_usage_data.h"
 #include "components/history/core/browser/keyword_id.h"
 #include "components/keyed_service/core/keyed_service.h"
-#include "components/visitedlink/browser/visitedlink_delegate.h"
 #include "sql/init_status.h"
 #include "sync/api/syncable_service.h"
 #include "ui/base/page_transition_types.h"
@@ -39,9 +38,7 @@
 #endif
 
 class GURL;
-class HistoryService;
 class PageUsageRequest;
-class Profile;
 class SkBitmap;
 
 namespace base {
@@ -49,10 +46,6 @@
 class Thread;
 }
 
-namespace visitedlink {
-class VisitedLinkMaster;
-}
-
 namespace history {
 
 struct DownloadRow;
@@ -71,6 +64,7 @@
 struct KeywordSearchTermVisit;
 class PageUsageData;
 class URLDatabase;
+class VisitDelegate;
 class VisitFilter;
 class WebHistoryService;
 
@@ -81,19 +75,18 @@
 //
 // This service is thread safe. Each request callback is invoked in the
 // thread that made the request.
-class HistoryService : public syncer::SyncableService,
-                       public KeyedService,
-                       public visitedlink::VisitedLinkDelegate {
+class HistoryService : public syncer::SyncableService, public KeyedService {
  public:
   // Miscellaneous commonly-used types.
   typedef std::vector<history::PageUsageData*> PageUsageDataList;
 
-  // Must call Init after construction. The |history::HistoryClient| object
-  // must be valid for the whole lifetime of |HistoryService|.
-  HistoryService(history::HistoryClient* client, Profile* profile);
-  // The empty constructor is provided only for testing.
+  // Must call Init after construction. The empty constructor provided only for
+  // unit tests. When using the full constructor, |history_client| and |profile|
+  // should only be null during testing, while |visit_delegate| may be null if
+  // the embedder use another way to track visited links.
   HistoryService();
-
+  HistoryService(history::HistoryClient* history_client,
+                 scoped_ptr<history::VisitDelegate> visit_delegate);
   ~HistoryService() override;
 
   // Initializes the history service, returning true on success. On false, do
@@ -119,7 +112,7 @@
   void ClearCachedDataForContextID(history::ContextID context_id);
 
   // Triggers the backend to load if it hasn't already, and then returns the
-  // in-memory URL database. The returned pointer MAY BE NULL if the in-memory
+  // in-memory URL database. The returned pointer may be null if the in-memory
   // database has not been loaded yet. This pointer is owned by the history
   // system. Callers should not store or cache this value.
   //
@@ -161,7 +154,7 @@
   // are only unique inside a given context, so we need that to differentiate
   // them.
   //
-  // The context/page ids can be NULL if there is no meaningful tracking
+  // The context/page ids can be null if there is no meaningful tracking
   // information that can be performed on the given URL. The 'nav_entry_id'
   // should be the unique ID of the current navigation entry in the given
   // process.
@@ -393,9 +386,8 @@
 
   // Implemented by the caller of 'QueryDownloads' below, and is called when the
   // history service has retrieved a list of all download state. The call
-  typedef base::Callback<void(
-      scoped_ptr<std::vector<history::DownloadRow> >)>
-          DownloadQueryCallback;
+  typedef base::Callback<void(scoped_ptr<std::vector<history::DownloadRow>>)>
+      DownloadQueryCallback;
 
   // Begins a history request to retrieve the state of all downloads in the
   // history db. 'callback' runs when the history service request is complete,
@@ -446,8 +438,9 @@
 
   // Schedules a HistoryDBTask for running on the history backend thread. See
   // HistoryDBTask for details on what this does. Takes ownership of |task|.
-  virtual void ScheduleDBTask(scoped_ptr<history::HistoryDBTask> task,
-                              base::CancelableTaskTracker* tracker);
+  virtual base::CancelableTaskTracker::TaskId ScheduleDBTask(
+      scoped_ptr<history::HistoryDBTask> task,
+      base::CancelableTaskTracker* tracker);
 
   // This callback is invoked when favicon change for urls.
   typedef base::Callback<void(const std::set<GURL>&)> OnFaviconChangedCallback;
@@ -527,9 +520,6 @@
 
  private:
   class BackendDelegate;
-#if defined(OS_ANDROID)
-  friend class AndroidHistoryProviderService;
-#endif
   friend class base::RefCountedThreadSafe<HistoryService>;
   friend class BackendDelegate;
   friend class FaviconService;
@@ -550,7 +540,7 @@
   // Called on shutdown, this will tell the history backend to complete and
   // will release pointers to it. No other functions should be called once
   // cleanup has happened that may dispatch to the history thread (because it
-  // will be NULL).
+  // will be null).
   //
   // In practice, this will be called by the service manager (BrowserProcess)
   // when it is being destroyed. Because that reference is being destroyed, it
@@ -558,9 +548,6 @@
   // still in memory (pending requests may be holding a reference to us).
   void Cleanup();
 
-  // Implementation of visitedlink::VisitedLinkDelegate.
-  void RebuildTable(const scoped_refptr<URLEnumerator>& enumerator) override;
-
   // Low-level Init().  Same as the public version, but adds a |no_db| parameter
   // that is only set by unittests which causes the backend to not init its DB.
   bool Init(bool no_db,
@@ -812,14 +799,14 @@
   // TODO(mrossetti): Consider changing ownership. See http://crbug.com/138321
   scoped_ptr<history::InMemoryHistoryBackend> in_memory_backend_;
 
+  // The history service will inform its VisitDelegate of URLs recorded and
+  // removed from the history database. This may be null during testing.
+  scoped_ptr<history::VisitDelegate> visit_delegate_;
+
   // The history client, may be null when testing. The object should otherwise
   // outlive |HistoryService|.
   history::HistoryClient* history_client_;
 
-  // Used for propagating link highlighting data across renderers. May be null
-  // in tests.
-  scoped_ptr<visitedlink::VisitedLinkMaster> visitedlink_master_;
-
   // Has the backend finished loading? The backend is loaded once Init has
   // completed.
   bool backend_loaded_;
diff --git a/chrome/browser/history/history_service_factory.cc b/chrome/browser/history/history_service_factory.cc
index 1c39d82..6295ea9 100644
--- a/chrome/browser/history/history_service_factory.cc
+++ b/chrome/browser/history/history_service_factory.cc
@@ -10,6 +10,7 @@
 #include "chrome/browser/bookmarks/chrome_bookmark_client_factory.h"
 #include "chrome/browser/history/chrome_history_client.h"
 #include "chrome/browser/history/chrome_history_client_factory.h"
+#include "chrome/browser/history/content_visit_delegate.h"
 #include "chrome/browser/history/history_service.h"
 #include "chrome/browser/profiles/incognito_helpers.h"
 #include "chrome/browser/profiles/profile.h"
@@ -77,7 +78,8 @@
     content::BrowserContext* context) const {
   Profile* profile = Profile::FromBrowserContext(context);
   scoped_ptr<HistoryService> history_service(new HistoryService(
-      ChromeHistoryClientFactory::GetForProfile(profile), profile));
+      ChromeHistoryClientFactory::GetForProfile(profile),
+      scoped_ptr<history::VisitDelegate>(new ContentVisitDelegate(profile))));
   if (!history_service->Init(
           profile->GetPrefs()->GetString(prefs::kAcceptLanguages),
           history::HistoryDatabaseParamsForPath(profile->GetPath()))) {
diff --git a/chrome/browser/interstitials/security_interstitial_metrics_helper.cc b/chrome/browser/interstitials/security_interstitial_metrics_helper.cc
index 322220e..2c7961be 100644
--- a/chrome/browser/interstitials/security_interstitial_metrics_helper.cc
+++ b/chrome/browser/interstitials/security_interstitial_metrics_helper.cc
@@ -10,6 +10,7 @@
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/history/history_service.h"
 #include "chrome/browser/history/history_service_factory.h"
+#include "chrome/browser/metrics/rappor/sampling.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/webdata/web_data_service_factory.h"
 #include "components/rappor/rappor_service.h"
@@ -69,11 +70,9 @@
   rappor::RapporService* rappor_service = g_browser_process->rappor_service();
   if (rappor_service && rappor_reporting_ == REPORT_RAPPOR &&
       (decision == PROCEED || decision == DONT_PROCEED)) {
-    // |domain| will be empty for hosts w/o TLDs (localhost, ip addrs)
-    const std::string domain =
-        net::registry_controlled_domains::GetDomainAndRegistry(
-            request_url_,
-            net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES);
+    // |domain| will be empty for hosts w/o TLDs
+    const std::string domain = rappor::GetDomainAndRegistrySampleFromGURL(
+        request_url_);
 
     // e.g. "interstitial.malware.domain" or "interstitial.ssl.domain"
     const std::string metric_name =
diff --git a/chrome/browser/io_thread.cc b/chrome/browser/io_thread.cc
index 41d8709..72a745b 100644
--- a/chrome/browser/io_thread.cc
+++ b/chrome/browser/io_thread.cc
@@ -42,6 +42,7 @@
 #include "chrome/common/chrome_version_info.h"
 #include "chrome/common/pref_names.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_prefs.h"
+#include "components/data_reduction_proxy/core/common/data_reduction_proxy_params.h"
 #include "components/policy/core/common/policy_service.h"
 #include "components/variations/variations_associated_data.h"
 #include "content/public/browser/browser_thread.h"
@@ -1039,6 +1040,7 @@
       &params->alternate_protocol_probability_threshold);
 
   globals.enable_quic.CopyToIfSet(&params->enable_quic);
+  globals.enable_quic_for_proxies.CopyToIfSet(&params->enable_quic_for_proxies);
   globals.quic_always_require_handshake_confirmation.CopyToIfSet(
       &params->quic_always_require_handshake_confirmation);
   globals.quic_disable_connection_pooling.CopyToIfSet(
@@ -1168,6 +1170,9 @@
     IOThread::Globals* globals) {
   bool enable_quic = ShouldEnableQuic(command_line, quic_trial_group);
   globals->enable_quic.set(enable_quic);
+  bool enable_quic_for_proxies = ShouldEnableQuicForProxies(command_line,
+                                                            quic_trial_group);
+  globals->enable_quic_for_proxies.set(enable_quic_for_proxies);
   if (enable_quic) {
     globals->quic_always_require_handshake_confirmation.set(
         ShouldQuicAlwaysRequireHandshakeConfirmation(quic_trial_params));
@@ -1249,6 +1254,25 @@
       quic_trial_group.starts_with(kQuicFieldTrialHttpsEnabledGroupName);
 }
 
+// static
+bool IOThread::ShouldEnableQuicForProxies(const base::CommandLine& command_line,
+                                          base::StringPiece quic_trial_group) {
+  return ShouldEnableQuic(command_line, quic_trial_group) ||
+      ShouldEnableQuicForDataReductionProxy();
+}
+
+// static
+bool IOThread::ShouldEnableQuicForDataReductionProxy() {
+  const base::CommandLine& command_line =
+        *base::CommandLine::ForCurrentProcess();
+
+  if (command_line.HasSwitch(switches::kDisableQuic))
+    return false;
+
+  return data_reduction_proxy::DataReductionProxyParams::
+      IsIncludedInQuicFieldTrial();
+}
+
 bool IOThread::ShouldEnableQuicPortSelection(
     const base::CommandLine& command_line) {
   if (command_line.HasSwitch(switches::kDisableQuicPortSelection))
diff --git a/chrome/browser/io_thread.h b/chrome/browser/io_thread.h
index 3fbbe9e..55c07a5 100644
--- a/chrome/browser/io_thread.h
+++ b/chrome/browser/io_thread.h
@@ -182,6 +182,7 @@
     Optional<double> alternate_protocol_probability_threshold;
 
     Optional<bool> enable_quic;
+    Optional<bool> enable_quic_for_proxies;
     Optional<bool> enable_quic_port_selection;
     Optional<bool> quic_always_require_handshake_confirmation;
     Optional<bool> quic_disable_connection_pooling;
@@ -236,6 +237,10 @@
 
   base::TimeTicks creation_time() const;
 
+  // Returns true if QUIC should be enabled for data reduction proxy, either as
+  // a result of a field trial or a command line flag.
+  static bool ShouldEnableQuicForDataReductionProxy();
+
  private:
   // Map from name to value for all parameters associate with a field trial.
   typedef std::map<std::string, std::string> VariationParameters;
@@ -330,6 +335,12 @@
       const base::CommandLine& command_line,
       base::StringPiece quic_trial_group);
 
+  // Returns true if QUIC should be enabled for proxies, either as a result
+  // of a field trial or a command line flag.
+  static bool ShouldEnableQuicForProxies(
+      const base::CommandLine& command_line,
+      base::StringPiece quic_trial_group);
+
   // Returns true if the selection of the ephemeral port in bind() should be
   // performed by Chromium, and false if the OS should select the port.  The OS
   // option is used to prevent Windows from posting a security security warning
diff --git a/chrome/browser/io_thread_unittest.cc b/chrome/browser/io_thread_unittest.cc
index 5b60d602..dfb0a9a 100644
--- a/chrome/browser/io_thread_unittest.cc
+++ b/chrome/browser/io_thread_unittest.cc
@@ -3,7 +3,9 @@
 // found in the LICENSE file.
 
 #include "base/command_line.h"
+#include "base/metrics/field_trial.h"
 #include "chrome/browser/io_thread.h"
+#include "components/data_reduction_proxy/core/common/data_reduction_proxy_params.h"
 #include "net/http/http_network_session.h"
 #include "net/http/http_server_properties_impl.h"
 #include "net/quic/quic_protocol.h"
@@ -14,6 +16,16 @@
 
 using ::testing::ElementsAre;
 
+class BadEntropyProvider : public base::FieldTrial::EntropyProvider {
+ public:
+  ~BadEntropyProvider() override {}
+
+  double GetEntropyForTrial(const std::string& trial_name,
+                            uint32 randomization_seed) const override {
+    return 0.5;
+  }
+};
+
 class IOThreadPeer {
  public:
   static void ConfigureQuicGlobals(
@@ -114,6 +126,8 @@
   net::HttpNetworkSession::Params params;
   InitializeNetworkSessionParams(&params);
   EXPECT_FALSE(params.enable_quic);
+  EXPECT_FALSE(params.enable_quic_for_proxies);
+  EXPECT_FALSE(IOThread::ShouldEnableQuicForDataReductionProxy());
 }
 
 TEST_F(IOThreadTest, EnableQuicFromFieldTrialGroup) {
@@ -124,6 +138,7 @@
   net::HttpNetworkSession::Params params;
   InitializeNetworkSessionParams(&params);
   EXPECT_TRUE(params.enable_quic);
+  EXPECT_TRUE(params.enable_quic_for_proxies);
   EXPECT_EQ(1350u, params.quic_max_packet_length);
   EXPECT_EQ(1.0, params.alternate_protocol_probability_threshold);
   EXPECT_EQ(default_params.quic_supported_versions,
@@ -135,6 +150,21 @@
   EXPECT_EQ(0.0f, params.quic_load_server_info_timeout_srtt_multiplier);
   EXPECT_FALSE(params.quic_enable_truncated_connection_ids);
   EXPECT_FALSE(params.quic_enable_connection_racing);
+  EXPECT_FALSE(IOThread::ShouldEnableQuicForDataReductionProxy());
+}
+
+TEST_F(IOThreadTest, EnableQuicFromQuicProxyFieldTrialGroup) {
+  base::FieldTrialList field_trial_list(new BadEntropyProvider());
+  base::FieldTrialList::CreateFieldTrial(
+      data_reduction_proxy::DataReductionProxyParams::GetQuicFieldTrialName(),
+      "Enabled");
+
+  ConfigureQuicGlobals();
+  net::HttpNetworkSession::Params params;
+  InitializeNetworkSessionParams(&params);
+  EXPECT_FALSE(params.enable_quic);
+  EXPECT_TRUE(params.enable_quic_for_proxies);
+  EXPECT_TRUE(IOThread::ShouldEnableQuicForDataReductionProxy());
 }
 
 TEST_F(IOThreadTest, EnableQuicFromCommandLine) {
@@ -144,6 +174,8 @@
   net::HttpNetworkSession::Params params;
   InitializeNetworkSessionParams(&params);
   EXPECT_TRUE(params.enable_quic);
+  EXPECT_TRUE(params.enable_quic_for_proxies);
+  EXPECT_FALSE(IOThread::ShouldEnableQuicForDataReductionProxy());
 }
 
 TEST_F(IOThreadTest, EnablePacingFromCommandLine) {
diff --git a/chrome/browser/local_discovery/privet_http_impl.cc b/chrome/browser/local_discovery/privet_http_impl.cc
index dd340df..38d546f 100644
--- a/chrome/browser/local_discovery/privet_http_impl.cc
+++ b/chrome/browser/local_discovery/privet_http_impl.cc
@@ -21,8 +21,6 @@
 #include "components/cloud_devices/common/printer_description.h"
 #include "printing/pdf_render_settings.h"
 #include "printing/pwg_raster_settings.h"
-#include "printing/units.h"
-#include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/text_elider.h"
 #endif  // ENABLE_PRINT_PREVIEW
 
@@ -399,7 +397,6 @@
       has_extended_workflow_(false),
       started_(false),
       offline_(false),
-      dpi_(printing::kDefaultPdfDpi),
       invalid_job_retries_(0),
       weak_factory_(this) {
 }
@@ -452,8 +449,7 @@
 
 void PrivetLocalPrintOperationImpl::StartInitialRequest() {
   use_pdf_ = false;
-  using namespace cloud_devices::printer;
-  ContentTypesCapability content_types;
+  cloud_devices::printer::ContentTypesCapability content_types;
   if (content_types.LoadFrom(capabilities_)) {
     use_pdf_ = content_types.Contains(kPrivetContentTypePDF) ||
                content_types.Contains(kPrivetContentTypeAny);
@@ -462,10 +458,6 @@
   if (use_pdf_) {
     StartPrinting();
   } else {
-    DpiCapability dpis;
-    if (dpis.LoadFrom(capabilities_)) {
-      dpi_ = std::max(dpis.GetDefault().horizontal, dpis.GetDefault().vertical);
-    }
     StartConvertToPWG();
   }
 }
@@ -546,71 +538,14 @@
   }
 }
 
-void PrivetLocalPrintOperationImpl::FillPwgRasterSettings(
-    printing::PwgRasterSettings* transform_settings) {
-  using namespace cloud_devices::printer;
-  PwgRasterConfigCapability raster_capability;
-  // If the raster capability fails to load, raster_capability will contain
-  // the default value.
-  raster_capability.LoadFrom(capabilities_);
-
-  DuplexTicketItem duplex_item;
-  DuplexType duplex_value = NO_DUPLEX;
-
-  DocumentSheetBack document_sheet_back =
-      raster_capability.value().document_sheet_back;
-
-  if (duplex_item.LoadFrom(ticket_)) {
-    duplex_value = duplex_item.value();
-  }
-
-  transform_settings->odd_page_transform = printing::TRANSFORM_NORMAL;
-  switch (duplex_value) {
-    case NO_DUPLEX:
-      transform_settings->odd_page_transform = printing::TRANSFORM_NORMAL;
-      break;
-    case LONG_EDGE:
-      if (document_sheet_back == ROTATED) {
-        transform_settings->odd_page_transform = printing::TRANSFORM_ROTATE_180;
-      } else if (document_sheet_back == FLIPPED) {
-        transform_settings->odd_page_transform =
-            printing::TRANSFORM_FLIP_VERTICAL;
-      }
-      break;
-    case SHORT_EDGE:
-      if (document_sheet_back == MANUAL_TUMBLE) {
-        transform_settings->odd_page_transform = printing::TRANSFORM_ROTATE_180;
-      } else if (document_sheet_back == FLIPPED) {
-        transform_settings->odd_page_transform =
-            printing::TRANSFORM_FLIP_HORIZONTAL;
-      }
-  }
-
-  transform_settings->rotate_all_pages =
-      raster_capability.value().rotate_all_pages;
-
-  transform_settings->reverse_page_order =
-      raster_capability.value().reverse_order_streaming;
-}
-
 void PrivetLocalPrintOperationImpl::StartConvertToPWG() {
-  printing::PwgRasterSettings transform_settings;
-
-  FillPwgRasterSettings(&transform_settings);
-
   if (!pwg_raster_converter_)
     pwg_raster_converter_ = PWGRasterConverter::CreateDefault();
 
-  double scale = dpi_;
-  scale /= printing::kPointsPerInch;
-  // Make vertical rectangle to optimize streaming to printer. Fix orientation
-  // by autorotate.
-  gfx::Rect area(std::min(page_size_.width(), page_size_.height()) * scale,
-                 std::max(page_size_.width(), page_size_.height()) * scale);
   pwg_raster_converter_->Start(
       data_.get(),
-      printing::PdfRenderSettings(area, dpi_, true),
-      transform_settings,
+      PWGRasterConverter::GetConversionSettings(capabilities_, page_size_),
+      PWGRasterConverter::GetBitmapSettings(capabilities_, ticket_),
       base::Bind(&PrivetLocalPrintOperationImpl::OnPWGRasterConverted,
                  base::Unretained(this)));
 }
diff --git a/chrome/browser/local_discovery/privet_http_impl.h b/chrome/browser/local_discovery/privet_http_impl.h
index 49d11be..1814aff 100644
--- a/chrome/browser/local_discovery/privet_http_impl.h
+++ b/chrome/browser/local_discovery/privet_http_impl.h
@@ -16,10 +16,6 @@
 #include "components/cloud_devices/common/cloud_device_description.h"
 #include "ui/gfx/geometry/size.h"
 
-namespace printing {
-struct PwgRasterSettings;
-};
-
 namespace local_discovery {
 
 class PrivetHTTPClient;
@@ -206,7 +202,6 @@
   void OnCreatejobResponse(bool has_error,
                            const base::DictionaryValue* value);
   void OnPWGRasterConverted(bool success, const base::FilePath& pwg_file_path);
-  void FillPwgRasterSettings(printing::PwgRasterSettings* transfrom_settings);
 
   PrivetHTTPClient* privet_client_;
   PrivetLocalPrintOperation::Delegate* delegate_;
@@ -224,7 +219,6 @@
   bool started_;
   bool offline_;
   gfx::Size page_size_;
-  int dpi_;
 
   std::string user_;
   std::string jobname_;
diff --git a/chrome/browser/local_discovery/pwg_raster_converter.cc b/chrome/browser/local_discovery/pwg_raster_converter.cc
index 40f3b0e..11b6c317 100644
--- a/chrome/browser/local_discovery/pwg_raster_converter.cc
+++ b/chrome/browser/local_discovery/pwg_raster_converter.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/browser/local_discovery/pwg_raster_converter.h"
 
+#include <algorithm>
+
 #include "base/bind_helpers.h"
 #include "base/cancelable_callback.h"
 #include "base/files/file.h"
@@ -12,12 +14,17 @@
 #include "base/logging.h"
 #include "chrome/common/chrome_utility_messages.h"
 #include "chrome/common/chrome_utility_printing_messages.h"
+#include "components/cloud_devices/common/cloud_device_description.h"
+#include "components/cloud_devices/common/printer_description.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/child_process_data.h"
 #include "content/public/browser/utility_process_host.h"
 #include "content/public/browser/utility_process_host_client.h"
 #include "printing/pdf_render_settings.h"
 #include "printing/pwg_raster_settings.h"
+#include "printing/units.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/size.h"
 
 namespace local_discovery {
 
@@ -283,4 +290,70 @@
   return scoped_ptr<PWGRasterConverter>(new PWGRasterConverterImpl());
 }
 
+// static
+printing::PdfRenderSettings PWGRasterConverter::GetConversionSettings(
+    const cloud_devices::CloudDeviceDescription& printer_capabilities,
+    const gfx::Size& page_size) {
+  int dpi = printing::kDefaultPdfDpi;
+  cloud_devices::printer::DpiCapability dpis;
+  if (dpis.LoadFrom(printer_capabilities))
+    dpi = std::max(dpis.GetDefault().horizontal, dpis.GetDefault().vertical);
+
+  double scale = dpi;
+  scale /= printing::kPointsPerInch;
+
+  // Make vertical rectangle to optimize streaming to printer. Fix orientation
+  // by autorotate.
+  gfx::Rect area(std::min(page_size.width(), page_size.height()) * scale,
+                 std::max(page_size.width(), page_size.height()) * scale);
+  return printing::PdfRenderSettings(area, dpi, true /* autorotate */);
+}
+
+// static
+printing::PwgRasterSettings PWGRasterConverter::GetBitmapSettings(
+    const cloud_devices::CloudDeviceDescription& printer_capabilities,
+    const cloud_devices::CloudDeviceDescription& ticket) {
+  printing::PwgRasterSettings result;
+  cloud_devices::printer::PwgRasterConfigCapability raster_capability;
+  // If the raster capability fails to load, raster_capability will contain
+  // the default value.
+  raster_capability.LoadFrom(printer_capabilities);
+
+  cloud_devices::printer::DuplexTicketItem duplex_item;
+  cloud_devices::printer::DuplexType duplex_value =
+      cloud_devices::printer::NO_DUPLEX;
+
+  cloud_devices::printer::DocumentSheetBack document_sheet_back =
+      raster_capability.value().document_sheet_back;
+
+  if (duplex_item.LoadFrom(ticket)) {
+    duplex_value = duplex_item.value();
+  }
+
+  result.odd_page_transform = printing::TRANSFORM_NORMAL;
+  switch (duplex_value) {
+    case cloud_devices::printer::NO_DUPLEX:
+      result.odd_page_transform = printing::TRANSFORM_NORMAL;
+      break;
+    case cloud_devices::printer::LONG_EDGE:
+      if (document_sheet_back == cloud_devices::printer::ROTATED) {
+        result.odd_page_transform = printing::TRANSFORM_ROTATE_180;
+      } else if (document_sheet_back == cloud_devices::printer::FLIPPED) {
+        result.odd_page_transform = printing::TRANSFORM_FLIP_VERTICAL;
+      }
+      break;
+    case cloud_devices::printer::SHORT_EDGE:
+      if (document_sheet_back == cloud_devices::printer::MANUAL_TUMBLE) {
+        result.odd_page_transform = printing::TRANSFORM_ROTATE_180;
+      } else if (document_sheet_back == cloud_devices::printer::FLIPPED) {
+        result.odd_page_transform = printing::TRANSFORM_FLIP_HORIZONTAL;
+      }
+  }
+
+  result.rotate_all_pages = raster_capability.value().rotate_all_pages;
+
+  result.reverse_page_order = raster_capability.value().reverse_order_streaming;
+  return result;
+}
+
 }  // namespace local_discovery
diff --git a/chrome/browser/local_discovery/pwg_raster_converter.h b/chrome/browser/local_discovery/pwg_raster_converter.h
index f31c59b..90412bea 100644
--- a/chrome/browser/local_discovery/pwg_raster_converter.h
+++ b/chrome/browser/local_discovery/pwg_raster_converter.h
@@ -12,6 +12,10 @@
 class FilePath;
 }
 
+namespace cloud_devices {
+class CloudDeviceDescription;
+}
+
 namespace gfx {
 class Size;
 }
@@ -36,6 +40,19 @@
 
   static scoped_ptr<PWGRasterConverter> CreateDefault();
 
+  // Generates conversion settings to be used with converter from printer
+  // capabilities and page size.
+  // TODO(vitalybuka): Extract page size from pdf document data.
+  static printing::PdfRenderSettings GetConversionSettings(
+      const cloud_devices::CloudDeviceDescription& printer_capabilities,
+      const gfx::Size& page_size);
+
+  // Generates pwg bitmap settings to be used with the converter from
+  // device capabilites and printing ticket.
+  static printing::PwgRasterSettings GetBitmapSettings(
+      const cloud_devices::CloudDeviceDescription& printer_capabilities,
+      const cloud_devices::CloudDeviceDescription& ticket);
+
   virtual void Start(base::RefCountedMemory* data,
                      const printing::PdfRenderSettings& conversion_settings,
                      const printing::PwgRasterSettings& bitmap_settings,
diff --git a/chrome/browser/media/router/OWNERS b/chrome/browser/media/router/OWNERS
new file mode 100644
index 0000000..d26ae0d
--- /dev/null
+++ b/chrome/browser/media/router/OWNERS
@@ -0,0 +1,5 @@
+# Until upstreaming is complete, substantial changes should also be reviewed by
+# someone in chrome/browser/media/OWNERS.
+
+mfoltz@chromium.org
+wez@chromium.org
diff --git a/chrome/browser/media/router/README b/chrome/browser/media/router/README
new file mode 100644
index 0000000..3bc92fd
--- /dev/null
+++ b/chrome/browser/media/router/README
@@ -0,0 +1,3 @@
+The Chrome Media Router allows applications to share media with connected
+screens.
+Design doc: http://www.chromium.org/developers/design-documents/media-router
\ No newline at end of file
diff --git a/chrome/browser/media/webrtc_browsertest_audio.cc b/chrome/browser/media/webrtc_browsertest_audio.cc
index 1772c92..6dc07aa 100644
--- a/chrome/browser/media/webrtc_browsertest_audio.cc
+++ b/chrome/browser/media/webrtc_browsertest_audio.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "webrtc_browsertest_audio.h"
+#include "chrome/browser/media/webrtc_browsertest_audio.h"
 
 #include "base/files/file.h"
 #include "base/files/file_path.h"
diff --git a/chrome/browser/media/webrtc_log_uploader.cc b/chrome/browser/media/webrtc_log_uploader.cc
index cb2e138..ce20a04 100644
--- a/chrome/browser/media/webrtc_log_uploader.cc
+++ b/chrome/browser/media/webrtc_log_uploader.cc
@@ -8,6 +8,7 @@
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
 #include "base/logging.h"
+#include "base/pickle.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_split.h"
 #include "base/strings/stringprintf.h"
@@ -49,10 +50,9 @@
 
 // Adds |compressed_log| to |post_data|.
 void AddLogData(std::string* post_data,
-                const std::vector<uint8>& compressed_log) {
+                const std::string& compressed_log) {
   AddMultipartFileContentHeader(post_data, "webrtc_log");
-  post_data->append(reinterpret_cast<const char*>(&compressed_log[0]),
-                    compressed_log.size());
+  post_data->append(compressed_log);
   post_data->append("\r\n");
 }
 
@@ -136,17 +136,16 @@
 }
 
 void WebRtcLogUploader::LoggingStoppedDoUpload(
-    scoped_ptr<unsigned char[]> log_buffer,
-    uint32 length,
-    const std::map<std::string, std::string>& meta_data,
+    scoped_ptr<WebRtcLogBuffer> log_buffer,
+    scoped_ptr<MetaDataMap> meta_data,
     const WebRtcLogUploadDoneData& upload_done_data) {
   DCHECK(file_thread_checker_.CalledOnValidThread());
   DCHECK(log_buffer.get());
+  DCHECK(meta_data.get());
   DCHECK(!upload_done_data.log_path.empty());
 
-  std::vector<uint8> compressed_log;
-  CompressLog(
-      &compressed_log, reinterpret_cast<uint8*>(&log_buffer[0]), length);
+  std::string compressed_log;
+  CompressLog(&compressed_log, log_buffer.get());
 
   std::string local_log_id;
 
@@ -167,13 +166,24 @@
 
   WebRtcLogUploadDoneData upload_done_data_with_log_id = upload_done_data;
   upload_done_data_with_log_id.local_log_id = local_log_id;
+  UploadCompressedLog(compressed_log, meta_data.Pass(),
+      upload_done_data_with_log_id);
+}
+
+void WebRtcLogUploader::UploadCompressedLog(
+    const std::string& compressed_log,
+    scoped_ptr<MetaDataMap> meta_data,
+    const WebRtcLogUploadDoneData& upload_done_data) {
+  DCHECK(file_thread_checker_.CalledOnValidThread());
+  DCHECK(!compressed_log.empty());
+  DCHECK(meta_data.get());
 
   scoped_ptr<std::string> post_data(new std::string());
   SetupMultipart(post_data.get(),
                  compressed_log,
                  upload_done_data.incoming_rtp_dump,
                  upload_done_data.outgoing_rtp_dump,
-                 meta_data);
+                 *meta_data.get());
 
   // If a test has set the test string pointer, write to it and skip uploading.
   // Still fire the upload callback so that we can run an extension API test
@@ -182,7 +192,7 @@
   // implemented according to the test plan. http://crbug.com/257329.
   if (post_data_) {
     *post_data_ = *post_data;
-    NotifyUploadDone(kHttpResponseOk, "", upload_done_data_with_log_id);
+    NotifyUploadDone(kHttpResponseOk, "", upload_done_data);
     return;
   }
 
@@ -191,13 +201,117 @@
       FROM_HERE,
       base::Bind(&WebRtcLogUploader::CreateAndStartURLFetcher,
                  base::Unretained(this),
-                 upload_done_data_with_log_id,
+                 upload_done_data,
                  Passed(&post_data)));
 
   content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
       base::Bind(&WebRtcLogUploader::DecreaseLogCount, base::Unretained(this)));
 }
 
+void WebRtcLogUploader::UploadStoredLog(
+    const WebRtcLogUploadDoneData& upload_data) {
+  DCHECK(file_thread_checker_.CalledOnValidThread());
+  DCHECK(!upload_data.local_log_id.empty());
+  DCHECK(!upload_data.log_path.empty());
+
+  base::FilePath native_log_path =
+      upload_data.log_path.AppendASCII(upload_data.local_log_id)
+      .AddExtension(FILE_PATH_LITERAL(".gz"));
+
+  std::string compressed_log;
+  if (!base::ReadFileToString(native_log_path, &compressed_log)) {
+    DPLOG(WARNING) << "Could not read WebRTC log file.";
+    content::BrowserThread::PostTask(
+        content::BrowserThread::UI, FROM_HERE,
+        base::Bind(upload_data.callback, false, "", "Log doesn't exist."));
+    return;
+  }
+
+  WebRtcLogUploadDoneData upload_data_with_rtp = upload_data;
+
+  // Optimistically set the rtp paths to what they should be if they exist.
+  upload_data_with_rtp.incoming_rtp_dump =
+      upload_data.log_path.AppendASCII(upload_data.local_log_id)
+      .AddExtension(FILE_PATH_LITERAL(".rtp_in"));
+
+  upload_data_with_rtp.outgoing_rtp_dump =
+      upload_data.log_path.AppendASCII(upload_data.local_log_id)
+      .AddExtension(FILE_PATH_LITERAL(".rtp_out"));
+
+  scoped_ptr<MetaDataMap> meta_data(new MetaDataMap());
+  {
+    std::string meta_data_contents;
+    base::FilePath meta_path =
+        upload_data.log_path.AppendASCII(upload_data.local_log_id)
+        .AddExtension(FILE_PATH_LITERAL(".meta"));
+    if (base::ReadFileToString(meta_path, &meta_data_contents) &&
+        !meta_data_contents.empty()) {
+      Pickle pickle(&meta_data_contents[0], meta_data_contents.size());
+      PickleIterator it(pickle);
+      std::string key, value;
+      while (it.ReadString(&key) && it.ReadString(&value))
+        (*meta_data.get())[key] = value;
+    }
+  }
+
+  UploadCompressedLog(compressed_log, meta_data.Pass(), upload_data_with_rtp);
+}
+
+void WebRtcLogUploader::LoggingStoppedDoStore(
+    const WebRtcLogPaths& log_paths,
+    const std::string& log_id,
+    scoped_ptr<WebRtcLogBuffer> log_buffer,
+    scoped_ptr<MetaDataMap> meta_data,
+    const WebRtcLoggingHandlerHost::GenericDoneCallback& done_callback) {
+  DCHECK(file_thread_checker_.CalledOnValidThread());
+  DCHECK(!log_id.empty());
+  DCHECK(log_buffer.get());
+  DCHECK(!log_paths.log_path.empty());
+
+  WebRtcLogUtil::DeleteOldWebRtcLogFiles(log_paths.log_path);
+
+  base::FilePath log_list_path =
+      WebRtcLogList::GetWebRtcLogListFileForDirectory(log_paths.log_path);
+
+  // Store the native log with a ".gz" extension.
+  std::string compressed_log;
+  CompressLog(&compressed_log, log_buffer.get());
+  base::FilePath native_log_path = log_paths.log_path.AppendASCII(log_id)
+      .AddExtension(FILE_PATH_LITERAL(".gz"));
+  WriteCompressedLogToFile(compressed_log, native_log_path);
+  AddLocallyStoredLogInfoToUploadListFile(log_list_path, log_id);
+
+  // Move the rtp dump files to the log directory with a name of
+  // <log id>.rtp_[in|out].
+  if (!log_paths.incoming_rtp_dump.empty()) {
+    base::FilePath rtp_path = log_paths.log_path.AppendASCII(log_id)
+        .AddExtension(FILE_PATH_LITERAL(".rtp_in"));
+    base::Move(log_paths.incoming_rtp_dump, rtp_path);
+  }
+
+  if (!log_paths.outgoing_rtp_dump.empty()) {
+    base::FilePath rtp_path = log_paths.log_path.AppendASCII(log_id)
+        .AddExtension(FILE_PATH_LITERAL(".rtp_out"));
+    base::Move(log_paths.outgoing_rtp_dump, rtp_path);
+  }
+
+  if (meta_data.get() && !meta_data->empty()) {
+    Pickle pickle;
+    for (const auto& it : *meta_data.get()) {
+      pickle.WriteString(it.first);
+      pickle.WriteString(it.second);
+    }
+    base::FilePath meta_path = log_paths.log_path.AppendASCII(log_id)
+        .AddExtension(FILE_PATH_LITERAL(".meta"));
+    base::WriteFile(meta_path, static_cast<const char*>(pickle.data()),
+        pickle.size());
+  }
+
+  content::BrowserThread::PostTask(
+      content::BrowserThread::UI, FROM_HERE,
+      base::Bind(done_callback, true, ""));
+}
+
 void WebRtcLogUploader::StartShutdown() {
   DCHECK(create_thread_checker_.CalledOnValidThread());
   DCHECK(!shutting_down_);
@@ -214,7 +328,7 @@
 
 void WebRtcLogUploader::SetupMultipart(
     std::string* post_data,
-    const std::vector<uint8>& compressed_log,
+    const std::string& compressed_log,
     const base::FilePath& incoming_rtp_dump,
     const base::FilePath& outgoing_rtp_dump,
     const std::map<std::string, std::string>& meta_data) {
@@ -269,11 +383,8 @@
   net::AddMultipartFinalDelimiterForUpload(kMultipartBoundary, post_data);
 }
 
-void WebRtcLogUploader::CompressLog(std::vector<uint8>* compressed_log,
-                                    uint8* input,
-                                    uint32 input_size) {
-  PartialCircularBuffer read_pcb(input, input_size);
-
+void WebRtcLogUploader::CompressLog(std::string* compressed_log,
+                                    WebRtcLogBuffer* buffer) {
   z_stream stream = {0};
   int result = deflateInit2(&stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED,
                             // windowBits = 15 is default, 16 is added to
@@ -287,10 +398,11 @@
   ResizeForNextOutput(compressed_log, &stream);
   uint32 read = 0;
 
+  PartialCircularBuffer read_buffer(buffer->Read());
   do {
     if (stream.avail_in == 0) {
-      read = read_pcb.Read(&intermediate_buffer[0],
-                           kIntermediateCompressionBufferBytes);
+      read = read_buffer.Read(&intermediate_buffer[0],
+                              sizeof(intermediate_buffer));
       stream.next_in = &intermediate_buffer[0];
       stream.avail_in = read;
       if (read != kIntermediateCompressionBufferBytes)
@@ -315,11 +427,12 @@
   compressed_log->resize(compressed_log->size() - stream.avail_out);
 }
 
-void WebRtcLogUploader::ResizeForNextOutput(std::vector<uint8>* compressed_log,
+void WebRtcLogUploader::ResizeForNextOutput(std::string* compressed_log,
                                             z_stream* stream) {
   size_t old_size = compressed_log->size() - stream->avail_out;
   compressed_log->resize(old_size + kIntermediateCompressionBufferBytes);
-  stream->next_out = &(*compressed_log)[old_size];
+  stream->next_out = reinterpret_cast<unsigned char*>(
+      &(*compressed_log)[old_size]);
   stream->avail_out = kIntermediateCompressionBufferBytes;
 }
 
@@ -349,13 +462,11 @@
 }
 
 void WebRtcLogUploader::WriteCompressedLogToFile(
-    const std::vector<uint8>& compressed_log,
+    const std::string& compressed_log,
     const base::FilePath& log_file_path) {
   DCHECK(file_thread_checker_.CalledOnValidThread());
   DCHECK(!compressed_log.empty());
-  base::WriteFile(log_file_path,
-                  reinterpret_cast<const char*>(&compressed_log[0]),
-                  compressed_log.size());
+  base::WriteFile(log_file_path, &compressed_log[0], compressed_log.size());
 }
 
 void WebRtcLogUploader::AddLocallyStoredLogInfoToUploadListFile(
diff --git a/chrome/browser/media/webrtc_log_uploader.h b/chrome/browser/media/webrtc_log_uploader.h
index dd9c916e..eee38a5 100644
--- a/chrome/browser/media/webrtc_log_uploader.h
+++ b/chrome/browser/media/webrtc_log_uploader.h
@@ -29,13 +29,10 @@
 
 // Used when uploading is done to perform post-upload actions. |log_path| is
 // also used pre-upload.
-struct WebRtcLogUploadDoneData {
+struct WebRtcLogUploadDoneData : public WebRtcLogPaths {
   WebRtcLogUploadDoneData();
   ~WebRtcLogUploadDoneData();
 
-  base::FilePath log_path;
-  base::FilePath incoming_rtp_dump;
-  base::FilePath outgoing_rtp_dump;
   WebRtcLoggingHandlerHost::UploadDoneCallback callback;
   scoped_refptr<WebRtcLoggingHandlerHost> host;
   std::string local_log_id;
@@ -75,11 +72,23 @@
   // |upload_done_data.local_log_id| is set and used internally and should be
   // left empty.
   void LoggingStoppedDoUpload(
-      scoped_ptr<unsigned char[]> log_buffer,
-      uint32 length,
-      const std::map<std::string, std::string>& meta_data,
+      scoped_ptr<WebRtcLogBuffer> log_buffer,
+      scoped_ptr<MetaDataMap> meta_data,
       const WebRtcLogUploadDoneData& upload_done_data);
 
+  // Uploads a previously stored log (see LoggingStoppedDoStore()).
+  void UploadStoredLog(const WebRtcLogUploadDoneData& upload_data);
+
+  // Similarly to LoggingStoppedDoUpload(), we store the log in compressed
+  // format on disk but add the option to specify a unique |log_id| for later
+  // identification and potential upload.
+  void LoggingStoppedDoStore(
+      const WebRtcLogPaths& log_paths,
+      const std::string& log_id,
+      scoped_ptr<WebRtcLogBuffer> log_buffer,
+      scoped_ptr<MetaDataMap> meta_data,
+      const WebRtcLoggingHandlerHost::GenericDoneCallback& done_callback);
+
   // Cancels URL fetcher operation by deleting all URL fetchers. This cancels
   // any pending uploads and releases SystemURLRequestContextGetter references.
   // Sets |shutting_down_| which prevent new fetchers to be created.
@@ -101,16 +110,15 @@
   // Sets up a multipart body to be uploaded. The body is produced according
   // to RFC 2046.
   void SetupMultipart(std::string* post_data,
-                      const std::vector<uint8>& compressed_log,
+                      const std::string& compressed_log,
                       const base::FilePath& incoming_rtp_dump,
                       const base::FilePath& outgoing_rtp_dump,
                       const std::map<std::string, std::string>& meta_data);
 
-  void CompressLog(std::vector<uint8>* compressed_log,
-                   uint8* input,
-                   uint32 input_size);
+  void CompressLog(std::string* compressed_log,
+                   WebRtcLogBuffer* buffer);
 
-  void ResizeForNextOutput(std::vector<uint8>* compressed_log,
+  void ResizeForNextOutput(std::string* compressed_log,
                            z_stream* stream);
 
   void CreateAndStartURLFetcher(
@@ -120,9 +128,14 @@
   void DecreaseLogCount();
 
   // Must be called on the FILE thread.
-  void WriteCompressedLogToFile(const std::vector<uint8>& compressed_log,
+  void WriteCompressedLogToFile(const std::string& compressed_log,
                                 const base::FilePath& log_file_path);
 
+  void UploadCompressedLog(
+      const std::string& compressed_log,
+      scoped_ptr<MetaDataMap> meta_data,
+      const WebRtcLogUploadDoneData& upload_done_data);
+
   // Append information (upload time, report ID and local ID) about a log to a
   // log list file, limited to |kLogListLimitLines| entries. This list is used
   // for viewing the logs under chrome://webrtc-logs, see WebRtcLogUploadList.
diff --git a/chrome/browser/media/webrtc_log_uploader_unittest.cc b/chrome/browser/media/webrtc_log_uploader_unittest.cc
index 4b60b39..2994c8b 100644
--- a/chrome/browser/media/webrtc_log_uploader_unittest.cc
+++ b/chrome/browser/media/webrtc_log_uploader_unittest.cc
@@ -250,15 +250,10 @@
   upload_done_data.incoming_rtp_dump = incoming_dump;
   upload_done_data.outgoing_rtp_dump = outgoing_dump;
 
-  const size_t log_length = 100;
-  scoped_ptr<unsigned char[]> log(new unsigned char[log_length]);
-  memset(log.get(), 0, log_length);
-
+  scoped_ptr<WebRtcLogBuffer> log(new WebRtcLogBuffer());
+  log->SetComplete();
   webrtc_log_uploader->LoggingStoppedDoUpload(
-      log.Pass(),
-      log_length,
-      std::map<std::string, std::string>(),
-      upload_done_data);
+      log.Pass(), make_scoped_ptr(new MetaDataMap()), upload_done_data);
 
   VerifyRtpDumpInMultipart(post_data, "rtpdump_recv", incoming_dump_content);
   VerifyRtpDumpInMultipart(post_data, "rtpdump_send", outgoing_dump_content);
diff --git a/chrome/browser/media/webrtc_logging_handler_host.cc b/chrome/browser/media/webrtc_logging_handler_host.cc
index 3696ff7..c118017 100644
--- a/chrome/browser/media/webrtc_logging_handler_host.cc
+++ b/chrome/browser/media/webrtc_logging_handler_host.cc
@@ -24,7 +24,6 @@
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/chrome_version_info.h"
 #include "chrome/common/media/webrtc_logging_messages.h"
-#include "chrome/common/partial_circular_buffer.h"
 #include "chromeos/settings/cros_settings_names.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/content_browser_client.h"
@@ -51,12 +50,6 @@
 
 namespace {
 
-#if defined(OS_ANDROID)
-const size_t kWebRtcLogSize = 1 * 1024 * 1024;  // 1 MB
-#else
-const size_t kWebRtcLogSize = 6 * 1024 * 1024;  // 6 MB
-#endif
-
 const char kLogNotStoppedOrNoLogOpen[] =
     "Logging not stopped or no log open.";
 
@@ -108,8 +101,61 @@
   message->resize(message->size() - 1);
 }
 
+void FireGenericDoneCallback(
+    const WebRtcLoggingHandlerHost::GenericDoneCallback& callback,
+    bool success,
+    const std::string& error_message) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK(!callback.is_null());
+  content::BrowserThread::PostTask(
+      content::BrowserThread::UI,
+      FROM_HERE,
+      base::Bind(callback, success, error_message));
+}
+
+void FireAndResetGenericDoneCallback(
+    WebRtcLoggingHandlerHost::GenericDoneCallback* callback,
+    bool success,
+    const std::string& error_message) {
+  FireGenericDoneCallback(*callback, success, error_message);
+  callback->Reset();
+}
+
 }  // namespace
 
+WebRtcLogBuffer::WebRtcLogBuffer()
+    : buffer_(),
+      circular_(&buffer_[0], sizeof(buffer_), sizeof(buffer_) / 2, false),
+      read_only_(false) {
+}
+
+WebRtcLogBuffer::~WebRtcLogBuffer() {
+  DCHECK(read_only_ || thread_checker_.CalledOnValidThread());
+}
+
+void WebRtcLogBuffer::Log(const std::string& message) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK(!read_only_);
+  circular_.Write(message.c_str(), message.length());
+  const char eol = '\n';
+  circular_.Write(&eol, 1);
+}
+
+PartialCircularBuffer WebRtcLogBuffer::Read() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK(read_only_);
+  return PartialCircularBuffer(&buffer_[0], sizeof(buffer_));
+}
+
+void WebRtcLogBuffer::SetComplete() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK(!read_only_) << "Already set? (programmer error)";
+  read_only_ = true;
+  // Detach from the current thread so that we can check reads on a different
+  // thread.  This is to make sure that Read()s still happen on one thread only.
+  thread_checker_.DetachFromThread();
+}
+
 WebRtcLoggingHandlerHost::WebRtcLoggingHandlerHost(Profile* profile)
     : BrowserMessageFilter(WebRtcLoggingMsgStart),
       profile_(profile),
@@ -121,26 +167,30 @@
 WebRtcLoggingHandlerHost::~WebRtcLoggingHandlerHost() {}
 
 void WebRtcLoggingHandlerHost::SetMetaData(
-    const MetaDataMap& meta_data,
+    scoped_ptr<MetaDataMap> meta_data,
     const GenericDoneCallback& callback) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
   DCHECK(!callback.is_null());
 
   std::string error_message;
   if (logging_state_ == CLOSED) {
-    meta_data_ = meta_data;
+    if (!meta_data_.get())
+      meta_data_ = meta_data.Pass();
   } else if (logging_state_ == STARTED) {
-    meta_data_ = meta_data;
     std::string meta_data_message;
-    FormatMetaDataAsLogMessage(meta_data_, &meta_data_message);
+    FormatMetaDataAsLogMessage(*meta_data.get(), &meta_data_message);
     LogToCircularBuffer(meta_data_message);
   } else {
     error_message = "Meta data must be set before stop or upload.";
   }
-  bool success = error_message.empty();
-  content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
-                                   base::Bind(callback, success,
-                                              error_message));
+
+  if (error_message.empty() && meta_data.get()) {
+    // Keep the meta data around for uploading separately from the log.
+    for (const auto& it : *meta_data.get())
+      (*meta_data_.get())[it.first] = it.second;
+  }
+
+  FireGenericDoneCallback(callback, error_message.empty(), error_message);
 }
 
 void WebRtcLoggingHandlerHost::StartLogging(
@@ -148,14 +198,14 @@
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
   DCHECK(!callback.is_null());
 
-  start_callback_ = callback;
   if (logging_state_ != CLOSED) {
-    FireGenericDoneCallback(&start_callback_, false, "A log is already open");
+    FireGenericDoneCallback(callback, false, "A log is already open");
     return;
   }
+
   logging_state_ = STARTING;
   BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind(
-      &WebRtcLoggingHandlerHost::StartLoggingIfAllowed, this));
+      &WebRtcLoggingHandlerHost::StartLoggingIfAllowed, this, callback));
 }
 
 void WebRtcLoggingHandlerHost::StopLogging(
@@ -163,11 +213,12 @@
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
   DCHECK(!callback.is_null());
 
-  stop_callback_ = callback;
   if (logging_state_ != STARTED) {
-    FireGenericDoneCallback(&stop_callback_, false, "Logging not started");
+    FireGenericDoneCallback(callback, false, "Logging not started");
     return;
   }
+
+  stop_callback_ = callback;
   logging_state_ = STOPPING;
   Send(new WebRtcLoggingMsg_StopLogging());
 }
@@ -184,14 +235,38 @@
     return;
   }
 
-  upload_callback_ = callback;
-  logging_state_ = UPLOADING;
   content::BrowserThread::PostTaskAndReplyWithResult(
       content::BrowserThread::FILE,
       FROM_HERE,
       base::Bind(&WebRtcLoggingHandlerHost::GetLogDirectoryAndEnsureExists,
                  this),
-      base::Bind(&WebRtcLoggingHandlerHost::TriggerUpload, this));
+      base::Bind(&WebRtcLoggingHandlerHost::TriggerUpload, this, callback));
+}
+
+void WebRtcLoggingHandlerHost::UploadStoredLog(
+    const std::string& log_id,
+    const UploadDoneCallback& callback) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK(!callback.is_null());
+
+  content::BrowserThread::PostTask(content::BrowserThread::FILE,
+      FROM_HERE,
+      base::Bind(&WebRtcLoggingHandlerHost::UploadStoredLogOnFileThread,
+                 this, log_id, callback));
+}
+
+void WebRtcLoggingHandlerHost::UploadStoredLogOnFileThread(
+    const std::string& log_id,
+    const UploadDoneCallback& callback) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+
+  WebRtcLogUploadDoneData upload_data;
+  upload_data.log_path = GetLogDirectoryAndEnsureExists();
+  upload_data.callback = callback;
+  upload_data.host = this;
+  upload_data.local_log_id = log_id;
+
+  g_browser_process->webrtc_log_uploader()->UploadStoredLog(upload_data);
 }
 
 void WebRtcLoggingHandlerHost::UploadLogDone() {
@@ -203,19 +278,62 @@
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
   DCHECK(!callback.is_null());
 
-  GenericDoneCallback discard_callback = callback;
   if (logging_state_ != STOPPED) {
-    FireGenericDoneCallback(&discard_callback, false,
-                            kLogNotStoppedOrNoLogOpen);
+    FireGenericDoneCallback(callback, false, kLogNotStoppedOrNoLogOpen);
     return;
   }
   g_browser_process->webrtc_log_uploader()->LoggingStoppedDontUpload();
-  circular_buffer_.reset();
   log_buffer_.reset();
+  meta_data_.reset();
   logging_state_ = CLOSED;
   rtp_dump_handler_.reset();
   stop_rtp_dump_callback_.Reset();
-  FireGenericDoneCallback(&discard_callback, true, "");
+  FireGenericDoneCallback(callback, true, "");
+}
+
+// Stores the log locally using a hash of log_id + security origin.
+void WebRtcLoggingHandlerHost::StoreLog(
+    const std::string& log_id,
+    const GenericDoneCallback& callback) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK(!callback.is_null());
+
+  if (logging_state_ != STOPPED) {
+    FireGenericDoneCallback(callback, false, kLogNotStoppedOrNoLogOpen);
+    return;
+  }
+
+  if (rtp_dump_handler_) {
+    BrowserThread::PostTask(
+        BrowserThread::UI,
+        FROM_HERE,
+        base::Bind(stop_rtp_dump_callback_, true, true));
+
+    rtp_dump_handler_->StopOngoingDumps(
+        base::Bind(&WebRtcLoggingHandlerHost::StoreLogContinue,
+                   this, log_id, callback));
+    return;
+  }
+
+  StoreLogContinue(log_id, callback);
+}
+
+void WebRtcLoggingHandlerHost::StoreLogContinue(
+    const std::string& log_id,
+    const GenericDoneCallback& callback) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK(!callback.is_null());
+
+  scoped_ptr<WebRtcLogPaths> log_paths(new WebRtcLogPaths());
+  ReleaseRtpDumps(log_paths.get());
+
+  content::BrowserThread::PostTaskAndReplyWithResult(
+      content::BrowserThread::FILE,
+      FROM_HERE,
+      base::Bind(&WebRtcLoggingHandlerHost::GetLogDirectoryAndEnsureExists,
+                 this),
+      base::Bind(&WebRtcLoggingHandlerHost::StoreLogInDirectory, this, log_id,
+                 Passed(&log_paths), callback));
 }
 
 void WebRtcLoggingHandlerHost::LogMessage(const std::string& message) {
@@ -252,8 +370,7 @@
     return;
   }
 
-  GenericDoneCallback start_callback = callback;
-  DoStartRtpDump(type, &start_callback);
+  DoStartRtpDump(type, callback);
 }
 
 void WebRtcLoggingHandlerHost::StopRtpDump(
@@ -263,9 +380,7 @@
   DCHECK(!callback.is_null());
 
   if (!rtp_dump_handler_) {
-    GenericDoneCallback stop_callback = callback;
-    FireGenericDoneCallback(
-        &stop_callback, false, "RTP dump has not been started.");
+    FireGenericDoneCallback(callback, false, "RTP dump has not been started.");
     return;
   }
 
@@ -317,7 +432,6 @@
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
   if (logging_state_ == STARTED || logging_state_ == STOPPED) {
     if (upload_log_on_render_close_) {
-      logging_state_ = UPLOADING;
       logging_started_time_ = base::Time();
 
       content::BrowserThread::PostTaskAndReplyWithResult(
@@ -325,7 +439,8 @@
           FROM_HERE,
           base::Bind(&WebRtcLoggingHandlerHost::GetLogDirectoryAndEnsureExists,
                      this),
-          base::Bind(&WebRtcLoggingHandlerHost::TriggerUpload, this));
+          base::Bind(&WebRtcLoggingHandlerHost::TriggerUpload, this,
+                     UploadDoneCallback()));
     } else {
       g_browser_process->webrtc_log_uploader()->LoggingStoppedDontUpload();
     }
@@ -379,37 +494,46 @@
   }
   logging_started_time_ = base::Time();
   logging_state_ = STOPPED;
-  FireGenericDoneCallback(&stop_callback_, true, "");
+  FireAndResetGenericDoneCallback(&stop_callback_, true, "");
 }
 
-void WebRtcLoggingHandlerHost::StartLoggingIfAllowed() {
+void WebRtcLoggingHandlerHost::StartLoggingIfAllowed(
+    const GenericDoneCallback& callback) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-  if (!g_browser_process->webrtc_log_uploader()->ApplyForStartLogging()) {
-    logging_state_ = CLOSED;
-      FireGenericDoneCallback(
-          &start_callback_, false, "Cannot start, maybe the maximum number of "
-          "simultaneuos logs has been reached.");
+  BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, base::Bind(
+      &WebRtcLoggingHandlerHost::DoStartLogging, this,
+      g_browser_process->webrtc_log_uploader()->ApplyForStartLogging(),
+      callback));
+}
+
+void WebRtcLoggingHandlerHost::DoStartLogging(
+    bool permissions_granted,
+    const GenericDoneCallback& callback) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  if (logging_state_ != STARTING) {
+    FireGenericDoneCallback(callback, false, "Logging cancelled.");
     return;
   }
-  BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, base::Bind(
-      &WebRtcLoggingHandlerHost::DoStartLogging, this));
-}
 
-void WebRtcLoggingHandlerHost::DoStartLogging() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  if (!permissions_granted) {
+    logging_state_ = CLOSED;
+    FireGenericDoneCallback(callback, false,
+        "Cannot start, maybe the maximum number of "
+        "simultaneuos logs has been reached.");
+    return;
+  }
 
-  log_buffer_.reset(new unsigned char[kWebRtcLogSize]);
-  circular_buffer_.reset(
-    new PartialCircularBuffer(log_buffer_.get(),
-                              kWebRtcLogSize,
-                              kWebRtcLogSize / 2,
-                              false));
+  DCHECK(!log_buffer_.get());
+  log_buffer_.reset(new WebRtcLogBuffer());
+  if (!meta_data_.get())
+    meta_data_.reset(new MetaDataMap());
 
   BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, base::Bind(
-      &WebRtcLoggingHandlerHost::LogInitialInfoOnFileThread, this));
+      &WebRtcLoggingHandlerHost::LogInitialInfoOnFileThread, this, callback));
 }
 
-void WebRtcLoggingHandlerHost::LogInitialInfoOnFileThread() {
+void WebRtcLoggingHandlerHost::LogInitialInfoOnFileThread(
+    const GenericDoneCallback& callback) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
 
   net::NetworkInterfaceList network_list;
@@ -417,12 +541,18 @@
                       net::EXCLUDE_HOST_SCOPE_VIRTUAL_INTERFACES);
 
   BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, base::Bind(
-      &WebRtcLoggingHandlerHost::LogInitialInfoOnIOThread, this, network_list));
+      &WebRtcLoggingHandlerHost::LogInitialInfoOnIOThread, this, network_list,
+      callback));
 }
 
 void WebRtcLoggingHandlerHost::LogInitialInfoOnIOThread(
-    const net::NetworkInterfaceList& network_list) {
+    const net::NetworkInterfaceList& network_list,
+    const GenericDoneCallback& callback) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  if (logging_state_ != STARTING) {
+    FireGenericDoneCallback(callback, false, "Logging cancelled.");
+    return;
+  }
 
   // Log start time (current time). We don't use base/i18n/time_formatting.h
   // here because we don't want the format of the current locale.
@@ -433,9 +563,9 @@
       now.day_of_month, now.hour, now.minute, now.second));
 
   // Write metadata if received before logging started.
-  if (!meta_data_.empty()) {
+  if (meta_data_.get() && !meta_data_->empty()) {
     std::string info;
-    FormatMetaDataAsLogMessage(meta_data_, &info);
+    FormatMetaDataAsLogMessage(*meta_data_.get(), &info);
     LogToCircularBuffer(info);
   }
 
@@ -502,23 +632,23 @@
         net::NetworkChangeNotifier::ConnectionTypeToString(it->type));
   }
 
-  NotifyLoggingStarted();
+  NotifyLoggingStarted(callback);
 }
 
-void WebRtcLoggingHandlerHost::NotifyLoggingStarted() {
+void WebRtcLoggingHandlerHost::NotifyLoggingStarted(
+    const GenericDoneCallback& callback) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK_EQ(logging_state_, STARTING);
   Send(new WebRtcLoggingMsg_StartLogging());
   logging_started_time_ = base::Time::Now();
   logging_state_ = STARTED;
-  FireGenericDoneCallback(&start_callback_, true, "");
+  FireGenericDoneCallback(callback, true, "");
 }
 
 void WebRtcLoggingHandlerHost::LogToCircularBuffer(const std::string& message) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
-  DCHECK(circular_buffer_.get());
-  circular_buffer_->Write(message.c_str(), message.length());
-  const char eol = '\n';
-  circular_buffer_->Write(&eol, 1);
+  DCHECK_NE(logging_state_, CLOSED);
+  log_buffer_->Log(message);
 }
 
 base::FilePath WebRtcLoggingHandlerHost::GetLogDirectoryAndEnsureExists() {
@@ -534,9 +664,9 @@
 }
 
 void WebRtcLoggingHandlerHost::TriggerUpload(
+    const UploadDoneCallback& callback,
     const base::FilePath& log_directory) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
-  DCHECK_EQ(logging_state_, UPLOADING);
 
   if (rtp_dump_handler_) {
     BrowserThread::PostTask(
@@ -547,60 +677,57 @@
     rtp_dump_handler_->StopOngoingDumps(
         base::Bind(&WebRtcLoggingHandlerHost::DoUploadLogAndRtpDumps,
                    this,
-                   log_directory));
+                   log_directory,
+                   callback));
     return;
   }
 
-  DoUploadLogAndRtpDumps(log_directory);
+  DoUploadLogAndRtpDumps(log_directory, callback);
+}
+
+void WebRtcLoggingHandlerHost::StoreLogInDirectory(
+    const std::string& log_id,
+    scoped_ptr<WebRtcLogPaths> log_paths,
+    const GenericDoneCallback& done_callback,
+    const base::FilePath& directory) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  log_paths->log_path = directory;
+
+  log_buffer_->SetComplete();
+  BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
+      base::Bind(&WebRtcLogUploader::LoggingStoppedDoStore,
+          base::Unretained(g_browser_process->webrtc_log_uploader()),
+          *log_paths.get(), log_id, Passed(&log_buffer_), Passed(&meta_data_),
+          done_callback));
+
+  logging_state_ = CLOSED;
 }
 
 void WebRtcLoggingHandlerHost::DoUploadLogAndRtpDumps(
-    const base::FilePath& log_directory) {
+    const base::FilePath& log_directory,
+    const UploadDoneCallback& callback) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
   WebRtcLogUploadDoneData upload_done_data;
   upload_done_data.log_path = log_directory;
-
-  if (rtp_dump_handler_) {
-    WebRtcRtpDumpHandler::ReleasedDumps rtp_dumps(
-        rtp_dump_handler_->ReleaseDumps());
-    upload_done_data.incoming_rtp_dump = rtp_dumps.incoming_dump_path;
-    upload_done_data.outgoing_rtp_dump = rtp_dumps.outgoing_dump_path;
-
-    rtp_dump_handler_.reset();
-    stop_rtp_dump_callback_.Reset();
-  }
-
-  upload_done_data.callback = upload_callback_;
+  upload_done_data.callback = callback;
   upload_done_data.host = this;
-  upload_callback_.Reset();
+  ReleaseRtpDumps(&upload_done_data);
 
+  log_buffer_->SetComplete();
   BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, base::Bind(
       &WebRtcLogUploader::LoggingStoppedDoUpload,
       base::Unretained(g_browser_process->webrtc_log_uploader()),
       Passed(&log_buffer_),
-      kWebRtcLogSize,
-      meta_data_,
+      Passed(&meta_data_),
       upload_done_data));
 
-  meta_data_.clear();
-  circular_buffer_.reset();
-}
-
-void WebRtcLoggingHandlerHost::FireGenericDoneCallback(
-    GenericDoneCallback* callback,
-    bool success,
-    const std::string& error_message) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
-  DCHECK(!(*callback).is_null());
-  content::BrowserThread::PostTask(
-      content::BrowserThread::UI,
-      FROM_HERE,
-      base::Bind(*callback, success, error_message));
-  (*callback).Reset();
+  logging_state_ = CLOSED;
 }
 
 void WebRtcLoggingHandlerHost::CreateRtpDumpHandlerAndStart(
     RtpDumpType type,
-    GenericDoneCallback callback,
+    const GenericDoneCallback& callback,
     const base::FilePath& dump_dir) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
 
@@ -610,16 +737,33 @@
   if (!rtp_dump_handler_)
     rtp_dump_handler_.reset(new WebRtcRtpDumpHandler(dump_dir));
 
-  DoStartRtpDump(type, &callback);
+  DoStartRtpDump(type, callback);
 }
 
-void WebRtcLoggingHandlerHost::DoStartRtpDump(RtpDumpType type,
-                                              GenericDoneCallback* callback) {
+void WebRtcLoggingHandlerHost::DoStartRtpDump(
+    RtpDumpType type, const GenericDoneCallback& callback) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
   DCHECK(rtp_dump_handler_);
 
   std::string error;
-
   bool result = rtp_dump_handler_->StartDump(type, &error);
   FireGenericDoneCallback(callback, result, error);
 }
+
+bool WebRtcLoggingHandlerHost::ReleaseRtpDumps(WebRtcLogPaths* log_paths) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK(log_paths);
+
+  if (!rtp_dump_handler_)
+    return false;
+
+  WebRtcRtpDumpHandler::ReleasedDumps rtp_dumps(
+      rtp_dump_handler_->ReleaseDumps());
+  log_paths->incoming_rtp_dump = rtp_dumps.incoming_dump_path;
+  log_paths->outgoing_rtp_dump = rtp_dumps.outgoing_dump_path;
+
+  rtp_dump_handler_.reset();
+  stop_rtp_dump_callback_.Reset();
+
+  return true;
+}
diff --git a/chrome/browser/media/webrtc_logging_handler_host.h b/chrome/browser/media/webrtc_logging_handler_host.h
index 9bf5db2..e600777 100644
--- a/chrome/browser/media/webrtc_logging_handler_host.h
+++ b/chrome/browser/media/webrtc_logging_handler_host.h
@@ -10,6 +10,7 @@
 #include "chrome/browser/media/rtp_dump_type.h"
 #include "chrome/browser/media/webrtc_rtp_dump_handler.h"
 #include "chrome/common/media/webrtc_logging_message_data.h"
+#include "chrome/common/partial_circular_buffer.h"
 #include "content/public/browser/browser_message_filter.h"
 #include "content/public/browser/render_process_host.h"
 #include "net/base/net_util.h"
@@ -18,11 +19,49 @@
 class URLRequestContextGetter;
 }  // namespace net
 
-class PartialCircularBuffer;
 class Profile;
 
+#if defined(OS_ANDROID)
+const size_t kWebRtcLogSize = 1 * 1024 * 1024;  // 1 MB
+#else
+const size_t kWebRtcLogSize = 6 * 1024 * 1024;  // 6 MB
+#endif
+
 typedef std::map<std::string, std::string> MetaDataMap;
 
+struct WebRtcLogPaths {
+  base::FilePath log_path;  // todo: rename to directory.
+  base::FilePath incoming_rtp_dump;
+  base::FilePath outgoing_rtp_dump;
+};
+
+class WebRtcLogBuffer {
+ public:
+  WebRtcLogBuffer();
+  ~WebRtcLogBuffer();
+
+  void Log(const std::string& message);
+
+  // Returns a circular buffer instance for reading the internal log buffer.
+  // Must only be called after the log has been marked as complete
+  // (see SetComplete) and the caller must ensure that the WebRtcLogBuffer
+  // instance remains in scope for the lifetime of the returned circular buffer.
+  PartialCircularBuffer Read();
+
+  // Switches the buffer to read-only mode, where access to the internal
+  // buffer is allowed from different threads than were used to contribute
+  // to the log.  Calls to Log() won't be allowed after calling
+  // SetComplete() and the call to SetComplete() must be done on the same
+  // thread as constructed the buffer and calls Log().
+  void SetComplete();
+
+ private:
+  base::ThreadChecker thread_checker_;
+  uint8 buffer_[kWebRtcLogSize];
+  PartialCircularBuffer circular_;
+  bool read_only_;
+};
+
 // WebRtcLoggingHandlerHost handles operations regarding the WebRTC logging:
 // - Opens a shared memory buffer that the handler in the render process
 //   writes to.
@@ -43,7 +82,7 @@
   // Sets meta data that will be uploaded along with the log and also written
   // in the beginning of the log. Must be called on the IO thread before calling
   // StartLogging.
-  void SetMetaData(const MetaDataMap& meta_data,
+  void SetMetaData(scoped_ptr<MetaDataMap> meta_data,
                    const GenericDoneCallback& callback);
 
   // Opens a log and starts logging. Must be called on the IO thread.
@@ -57,6 +96,11 @@
   // called after logging has stopped. Must be called on the IO thread.
   void UploadLog(const UploadDoneCallback& callback);
 
+  // Uploads a log that was previously saved via a call to StoreLog().
+  // Otherwise operates in the same way as UploadLog.
+  void UploadStoredLog(const std::string& log_id,
+                       const UploadDoneCallback& callback);
+
   // Called by WebRtcLogUploader when uploading has finished. Must be called on
   // the IO thread.
   void UploadLogDone();
@@ -65,6 +109,9 @@
   // stopped. Must be called on the IO thread.
   void DiscardLog(const GenericDoneCallback& callback);
 
+  // Stores the log locally using a hash of log_id + security origin.
+  void StoreLog(const std::string& log_id, const GenericDoneCallback& callback);
+
   // Adds a message to the log.
   void LogMessage(const std::string& message);
 
@@ -114,7 +161,6 @@
     STARTED,   // Logging started.
     STOPPING,  // Stop logging is in progress.
     STOPPED,   // Logging has been stopped, log still open in memory.
-    UPLOADING  // Uploading log is in progress.
   };
 
   friend class content::BrowserThread;
@@ -134,11 +180,17 @@
   // Handles log message requests from browser process.
   void AddLogMessageFromBrowser(const WebRtcLoggingMessageData& message);
 
-  void StartLoggingIfAllowed();
-  void DoStartLogging();
-  void LogInitialInfoOnFileThread();
-  void LogInitialInfoOnIOThread(const net::NetworkInterfaceList& network_list);
-  void NotifyLoggingStarted();
+  void StartLoggingIfAllowed(const GenericDoneCallback& callback);
+  void DoStartLogging(bool permissions_granted,
+                      const GenericDoneCallback& callback);
+  void LogInitialInfoOnFileThread(const GenericDoneCallback& callback);
+  void LogInitialInfoOnIOThread(const net::NetworkInterfaceList& network_list,
+                                const GenericDoneCallback& callback);
+  void NotifyLoggingStarted(const GenericDoneCallback& callback);
+
+  // Called after stopping RTP dumps.
+  void StoreLogContinue(const std::string& log_id,
+      const GenericDoneCallback& callback);
 
   // Writes a formatted log |message| to the |circular_buffer_|.
   void LogToCircularBuffer(const std::string& message);
@@ -147,24 +199,30 @@
   // called on the FILE thread.
   base::FilePath GetLogDirectoryAndEnsureExists();
 
-  void TriggerUpload(const base::FilePath& log_directory);
+  void TriggerUpload(const UploadDoneCallback& callback,
+                     const base::FilePath& log_directory);
+
+  void StoreLogInDirectory(const std::string& log_id,
+                           scoped_ptr<WebRtcLogPaths> log_paths,
+                           const GenericDoneCallback& done_callback,
+                           const base::FilePath& directory);
+
+  void UploadStoredLogOnFileThread(const std::string& log_id,
+                                   const UploadDoneCallback& callback);
 
   // A helper for TriggerUpload to do the real work.
-  void DoUploadLogAndRtpDumps(const base::FilePath& log_directory);
-
-  void FireGenericDoneCallback(GenericDoneCallback* callback,
-                               bool success,
-                               const std::string& error_message);
+  void DoUploadLogAndRtpDumps(const base::FilePath& log_directory,
+                              const UploadDoneCallback& callback);
 
   // Create the RTP dump handler and start dumping. Must be called after making
   // sure the log directory exists.
   void CreateRtpDumpHandlerAndStart(RtpDumpType type,
-                                    GenericDoneCallback callback,
+                                    const GenericDoneCallback& callback,
                                     const base::FilePath& dump_dir);
 
   // A helper for starting RTP dump assuming the RTP dump handler has been
   // created.
-  void DoStartRtpDump(RtpDumpType type, GenericDoneCallback* callback);
+  void DoStartRtpDump(RtpDumpType type, const GenericDoneCallback& callback);
 
   // Adds the packet to the dump on IO thread.
   void DumpRtpPacketOnIOThread(scoped_ptr<uint8[]> packet_header,
@@ -172,21 +230,20 @@
                                size_t packet_length,
                                bool incoming);
 
-  scoped_ptr<unsigned char[]> log_buffer_;
-  scoped_ptr<PartialCircularBuffer> circular_buffer_;
+  bool ReleaseRtpDumps(WebRtcLogPaths* log_paths);
+
+  scoped_ptr<WebRtcLogBuffer> log_buffer_;
 
   // The profile associated with our renderer process.
-  Profile* profile_;
+  Profile* const profile_;
 
   // These are only accessed on the IO thread, except when in STARTING state. In
   // this state we are protected since entering any function that alters the
   // state is not allowed.
-  MetaDataMap meta_data_;
+  scoped_ptr<MetaDataMap> meta_data_;
 
   // These are only accessed on the IO thread.
-  GenericDoneCallback start_callback_;
   GenericDoneCallback stop_callback_;
-  UploadDoneCallback upload_callback_;
 
   // Only accessed on the IO thread, except when in STARTING, STOPPING or
   // UPLOADING state if the action fails and the state must be reset. In these
diff --git a/chrome/browser/metrics/chrome_metrics_service_accessor.cc b/chrome/browser/metrics/chrome_metrics_service_accessor.cc
index 271af31b..546ac0cd 100644
--- a/chrome/browser/metrics/chrome_metrics_service_accessor.cc
+++ b/chrome/browser/metrics/chrome_metrics_service_accessor.cc
@@ -4,8 +4,11 @@
 
 #include "chrome/browser/metrics/chrome_metrics_service_accessor.h"
 
+#include "base/command_line.h"
 #include "base/prefs/pref_service.h"
 #include "chrome/browser/browser_process.h"
+#include "chrome/browser/metrics/metrics_services_manager.h"
+#include "chrome/common/chrome_switches.h"
 #include "chrome/common/pref_names.h"
 #include "components/metrics/metrics_service.h"
 #include "components/variations/metrics_util.h"
@@ -15,28 +18,33 @@
 #endif
 
 // static
+// TODO(asvitkine): This function does not report the correct value on Android,
+// see http://crbug.com/362192.
 bool ChromeMetricsServiceAccessor::IsMetricsReportingEnabled() {
-  bool result = false;
-  const PrefService* local_state = g_browser_process->local_state();
-  if (local_state) {
-    const PrefService::Preference* uma_pref =
-        local_state->FindPreference(prefs::kMetricsReportingEnabled);
-    if (uma_pref) {
-      bool success = uma_pref->GetValue()->GetAsBoolean(&result);
-      DCHECK(success);
-    }
+  // If the user permits metrics reporting with the checkbox in the
+  // prefs, we turn on recording.  We disable metrics completely for
+  // non-official builds, or when field trials are forced.
+  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kForceFieldTrials)) {
+    return false;
   }
-  return result;
+
+  bool enabled = false;
+#if defined(GOOGLE_CHROME_BUILD)
+#if defined(OS_CHROMEOS)
+  chromeos::CrosSettings::Get()->GetBoolean(chromeos::kStatsReportingPref,
+                                            &enabled);
+#else
+  enabled = g_browser_process->local_state()->
+      GetBoolean(prefs::kMetricsReportingEnabled);
+#endif  // #if defined(OS_CHROMEOS)
+#endif  // defined(GOOGLE_CHROME_BUILD)
+  return enabled;
 }
 
 bool ChromeMetricsServiceAccessor::IsCrashReportingEnabled() {
 #if defined(GOOGLE_CHROME_BUILD)
-#if defined(OS_CHROMEOS)
-  bool reporting_enabled = false;
-  chromeos::CrosSettings::Get()->GetBoolean(chromeos::kStatsReportingPref,
-                                            &reporting_enabled);
-  return reporting_enabled;
-#elif defined(OS_ANDROID)
+#if defined(OS_ANDROID)
   // Android has its own settings for metrics / crash uploading.
   const PrefService* prefs = g_browser_process->local_state();
   return prefs->GetBoolean(prefs::kCrashReportingEnabled);
diff --git a/chrome/browser/metrics/chrome_metrics_service_accessor.h b/chrome/browser/metrics/chrome_metrics_service_accessor.h
index 2b45a56d..8e2dddf 100644
--- a/chrome/browser/metrics/chrome_metrics_service_accessor.h
+++ b/chrome/browser/metrics/chrome_metrics_service_accessor.h
@@ -66,6 +66,7 @@
   friend class options::BrowserOptionsHandler;
   friend void InitiateMetricsReportingChange(
       bool, const OnMetricsReportingCallbackType&);
+  friend class MetricsServicesManager;
 
   FRIEND_TEST_ALL_PREFIXES(ChromeMetricsServiceAccessorTest,
                            MetricsReportingEnabled);
diff --git a/chrome/browser/metrics/chrome_metrics_service_accessor_unittest.cc b/chrome/browser/metrics/chrome_metrics_service_accessor_unittest.cc
index dc0271b..7c93160 100644
--- a/chrome/browser/metrics/chrome_metrics_service_accessor_unittest.cc
+++ b/chrome/browser/metrics/chrome_metrics_service_accessor_unittest.cc
@@ -26,6 +26,7 @@
 };
 
 TEST_F(ChromeMetricsServiceAccessorTest, MetricsReportingEnabled) {
+#if defined(GOOGLE_CHROME_BUILD)
 #if !defined(OS_CHROMEOS)
   GetLocalState()->SetBoolean(prefs::kMetricsReportingEnabled, false);
   EXPECT_FALSE(ChromeMetricsServiceAccessor::IsMetricsReportingEnabled());
@@ -38,6 +39,10 @@
   // device settings for metrics reporting.
   EXPECT_FALSE(ChromeMetricsServiceAccessor::IsMetricsReportingEnabled());
 #endif
+#else
+  // Metrics Reporting is never enabled when GOOGLE_CHROME_BUILD is undefined.
+  EXPECT_FALSE(ChromeMetricsServiceAccessor::IsMetricsReportingEnabled());
+#endif
 }
 
 TEST_F(ChromeMetricsServiceAccessorTest, CrashReportingEnabled) {
diff --git a/chrome/browser/metrics/chrome_metrics_service_client.cc b/chrome/browser/metrics/chrome_metrics_service_client.cc
index 5d30941..a6ee8b7 100644
--- a/chrome/browser/metrics/chrome_metrics_service_client.cc
+++ b/chrome/browser/metrics/chrome_metrics_service_client.cc
@@ -36,6 +36,7 @@
 #include "components/metrics/net/network_metrics_provider.h"
 #include "components/metrics/profiler/profiler_metrics_provider.h"
 #include "components/metrics/profiler/tracking_synchronizer.h"
+#include "components/metrics/url_constants.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/histogram_fetcher.h"
 #include "content/public/browser/notification_service.h"
@@ -236,12 +237,12 @@
 
 scoped_ptr<metrics::MetricsLogUploader>
 ChromeMetricsServiceClient::CreateUploader(
-    const std::string& server_url,
-    const std::string& mime_type,
     const base::Callback<void(int)>& on_upload_complete) {
   return scoped_ptr<metrics::MetricsLogUploader>(
       new metrics::NetMetricsLogUploader(
-          g_browser_process->system_request_context(), server_url, mime_type,
+          g_browser_process->system_request_context(),
+          metrics::kDefaultMetricsServerUrl,
+          metrics::kDefaultMetricsMimeType,
           on_upload_complete));
 }
 
diff --git a/chrome/browser/metrics/chrome_metrics_service_client.h b/chrome/browser/metrics/chrome_metrics_service_client.h
index 3f2e96c..2b95742 100644
--- a/chrome/browser/metrics/chrome_metrics_service_client.h
+++ b/chrome/browser/metrics/chrome_metrics_service_client.h
@@ -67,8 +67,6 @@
   void StartGatheringMetrics(const base::Closure& done_callback) override;
   void CollectFinalMetrics(const base::Closure& done_callback) override;
   scoped_ptr<metrics::MetricsLogUploader> CreateUploader(
-      const std::string& server_url,
-      const std::string& mime_type,
       const base::Callback<void(int)>& on_upload_complete) override;
   base::string16 GetRegistryBackupKey() override;
 
diff --git a/chrome/browser/metrics/metrics_reporting_state.cc b/chrome/browser/metrics/metrics_reporting_state.cc
index a4a61fea..81e49748 100644
--- a/chrome/browser/metrics/metrics_reporting_state.cc
+++ b/chrome/browser/metrics/metrics_reporting_state.cc
@@ -80,6 +80,10 @@
 
 } // namespace
 
+bool HasRapporOption() {
+  return base::FieldTrialList::FindFullName("RapporOption") == "Enabled";
+}
+
 void InitiateMetricsReportingChange(
     bool enabled,
     const OnMetricsReportingCallbackType& callback_fn) {
diff --git a/chrome/browser/metrics/metrics_reporting_state.h b/chrome/browser/metrics/metrics_reporting_state.h
index 4716929..5d6ff4c 100644
--- a/chrome/browser/metrics/metrics_reporting_state.h
+++ b/chrome/browser/metrics/metrics_reporting_state.h
@@ -9,6 +9,9 @@
 
 typedef base::Callback<void(bool)> OnMetricsReportingCallbackType;
 
+// Returns true if RAPPOR is controlled by a seperate option.
+bool HasRapporOption();
+
 // Initiates a change to metrics reporting state to the new value of |enabled|.
 // Starts or stops the metrics service based on the new state and then runs
 // |callback_fn| (which can be null) with the updated state (as the operation
diff --git a/chrome/browser/metrics/metrics_services_manager.cc b/chrome/browser/metrics/metrics_services_manager.cc
index 8f9384f..b471d9cc 100644
--- a/chrome/browser/metrics/metrics_services_manager.cc
+++ b/chrome/browser/metrics/metrics_services_manager.cc
@@ -10,7 +10,9 @@
 #include "base/logging.h"
 #include "base/prefs/pref_service.h"
 #include "chrome/browser/browser_process.h"
+#include "chrome/browser/metrics/chrome_metrics_service_accessor.h"
 #include "chrome/browser/metrics/chrome_metrics_service_client.h"
+#include "chrome/browser/metrics/metrics_reporting_state.h"
 #include "chrome/browser/metrics/variations/variations_service.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
@@ -56,8 +58,14 @@
 }  // namespace
 
 MetricsServicesManager::MetricsServicesManager(PrefService* local_state)
-    : local_state_(local_state) {
+    : local_state_(local_state),
+      may_upload_(false),
+      may_record_(false) {
   DCHECK(local_state);
+  pref_change_registrar_.Init(local_state);
+  pref_change_registrar_.Add(rappor::prefs::kRapporEnabled,
+      base::Bind(&MetricsServicesManager::UpdateRapporService,
+                 base::Unretained(this)));
 }
 
 MetricsServicesManager::~MetricsServicesManager() {
@@ -109,8 +117,7 @@
   if (!metrics_state_manager_) {
     metrics_state_manager_ = metrics::MetricsStateManager::Create(
         local_state_,
-        base::Bind(&MetricsServicesManager::IsMetricsReportingEnabled,
-                   base::Unretained(this)),
+        base::Bind(&ChromeMetricsServiceAccessor::IsMetricsReportingEnabled),
         base::Bind(&PostStoreMetricsClientInfo),
         base::Bind(&GoogleUpdateSettings::LoadMetricsClientInfo));
   }
@@ -128,27 +135,34 @@
 
 rappor::RecordingLevel MetricsServicesManager::GetRapporRecordingLevel(
     bool metrics_enabled) const {
-   rappor::RecordingLevel recording_level = rappor::RECORDING_DISABLED;
+  rappor::RecordingLevel recording_level = rappor::RECORDING_DISABLED;
 #if defined(GOOGLE_CHROME_BUILD)
-// TODO(holte): Remove special casing once the UIs for iOS/Android are updated
-// to allow control of the rappor.enabled pref.
-#if defined(OS_IOS) || defined(OS_ANDROID)
-  if (metrics_enabled) {
+  if (HasRapporOption()) {
+    if (IsRapporEnabled(metrics_enabled)) {
+      recording_level = metrics_enabled ?
+                        rappor::FINE_LEVEL :
+                        rappor::COARSE_LEVEL;
+    }
+  } else if (metrics_enabled) {
     recording_level = rappor::FINE_LEVEL;
   }
-#else  // defined(OS_IOS) || defined(OS_ANDROID)
-  if (IsRapporEnabled(metrics_enabled)) {
-    recording_level = metrics_enabled ?
-                      rappor::FINE_LEVEL :
-                      rappor::COARSE_LEVEL;
-  }
-#endif  // defined(OS_IOS) || defined(OS_ANDROID)
 #endif  // defined(GOOGLE_CHROME_BUILD)
   return recording_level;
 }
 
+void MetricsServicesManager::UpdateRapporService() {
+  GetRapporService()->Update(GetRapporRecordingLevel(may_record_), may_upload_);
+}
+
 void MetricsServicesManager::UpdatePermissions(bool may_record,
                                                bool may_upload) {
+  // Stash the current permissions so that we can update the RapporService
+  // correctly when the Rappor preference changes.  The metrics recording
+  // preference partially determines the initial rappor setting, and also
+  // controls whether FINE metrics are sent.
+  may_record_ = may_record;
+  may_upload_ = may_upload;
+
   metrics::MetricsService* metrics = GetMetricsService();
 
   const base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
@@ -175,27 +189,10 @@
     metrics->Stop();
   }
 
-  GetRapporService()->Update(GetRapporRecordingLevel(may_record), may_upload);
+  UpdateRapporService();
 }
 
-// TODO(asvitkine): This function does not report the correct value on Android,
-// see http://crbug.com/362192.
-bool MetricsServicesManager::IsMetricsReportingEnabled() const {
-  // If the user permits metrics reporting with the checkbox in the
-  // prefs, we turn on recording.  We disable metrics completely for
-  // non-official builds, or when field trials are forced.
-  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
-          switches::kForceFieldTrials))
-    return false;
-
-  bool enabled = false;
-#if defined(GOOGLE_CHROME_BUILD)
-#if defined(OS_CHROMEOS)
-  chromeos::CrosSettings::Get()->GetBoolean(chromeos::kStatsReportingPref,
-                                            &enabled);
-#else
-  enabled = local_state_->GetBoolean(prefs::kMetricsReportingEnabled);
-#endif  // #if defined(OS_CHROMEOS)
-#endif  // defined(GOOGLE_CHROME_BUILD)
-  return enabled;
+void MetricsServicesManager::UpdateUploadPermissions(bool may_upload) {
+  return UpdatePermissions(
+      ChromeMetricsServiceAccessor::IsMetricsReportingEnabled(), may_upload);
 }
diff --git a/chrome/browser/metrics/metrics_services_manager.h b/chrome/browser/metrics/metrics_services_manager.h
index 5e789327..72ec3f79 100644
--- a/chrome/browser/metrics/metrics_services_manager.h
+++ b/chrome/browser/metrics/metrics_services_manager.h
@@ -7,6 +7,7 @@
 
 #include "base/basictypes.h"
 #include "base/memory/scoped_ptr.h"
+#include "base/prefs/pref_change_registrar.h"
 #include "base/threading/thread_checker.h"
 #include "components/rappor/rappor_service.h"
 
@@ -56,8 +57,8 @@
   // metrics change.
   void UpdatePermissions(bool may_record, bool may_upload);
 
-  // Returns true iff metrics reporting is enabled.
-  bool IsMetricsReportingEnabled() const;
+  // Update the managed services when permissions for uploading metrics change.
+  void UpdateUploadPermissions(bool may_upload);
 
   // Returns true iff Rappor reporting is enabled.
   bool IsRapporEnabled(bool metrics_enabled) const;
@@ -66,6 +67,10 @@
   rappor::RecordingLevel GetRapporRecordingLevel(bool metrics_enabled) const;
 
  private:
+  // Update the managed services when permissions for recording/uploading
+  // metrics change.
+  void UpdateRapporService();
+
   // Returns the ChromeMetricsServiceClient, creating it if it hasn't been
   // created yet (and additionally creating the MetricsService in that case).
   ChromeMetricsServiceClient* GetChromeMetricsServiceClient();
@@ -78,6 +83,15 @@
   // Weak pointer to the local state prefs store.
   PrefService* local_state_;
 
+  // A change registrar for local_state_;
+  PrefChangeRegistrar pref_change_registrar_;
+
+  // The current metrics reporting setting.
+  bool may_upload_;
+
+  // The current metrics recording setting.
+  bool may_record_;
+
   // MetricsStateManager which is passed as a parameter to service constructors.
   scoped_ptr<metrics::MetricsStateManager> metrics_state_manager_;
 
diff --git a/chrome/browser/metrics/metrics_services_manager_unittest.cc b/chrome/browser/metrics/metrics_services_manager_unittest.cc
index ef21534..a930c05 100644
--- a/chrome/browser/metrics/metrics_services_manager_unittest.cc
+++ b/chrome/browser/metrics/metrics_services_manager_unittest.cc
@@ -6,6 +6,7 @@
 
 #include <string>
 
+#include "base/metrics/field_trial.h"
 #include "base/prefs/testing_pref_service.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/test/base/testing_browser_process.h"
@@ -14,11 +15,25 @@
 #include "components/rappor/rappor_prefs.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+namespace {
+
+#if defined(GOOGLE_CHROME_BUILD)
+
+void UseRapporOption() {
+  ASSERT_TRUE(base::FieldTrialList::CreateFieldTrial(
+      "RapporOption", "Enabled"));
+}
+
+#endif  // defined(GOOGLE_CHROME_BUILD)
+
+}  // namespace
+
 class MetricsServicesManagerTest : public testing::Test {
  public:
   MetricsServicesManagerTest()
       : test_profile_manager_(TestingBrowserProcess::GetGlobal()),
-        manager_(&test_prefs_) {
+        manager_(&test_prefs_),
+        field_trial_list_(NULL) {
     rappor::internal::RegisterPrefs(test_prefs_.registry());
   }
 
@@ -39,6 +54,7 @@
   TestingProfileManager test_profile_manager_;
   TestingPrefServiceSimple test_prefs_;
   MetricsServicesManager manager_;
+  base::FieldTrialList field_trial_list_;
 
   DISALLOW_COPY_AND_ASSIGN(MetricsServicesManagerTest);
 };
@@ -98,12 +114,10 @@
   EXPECT_FALSE(test_prefs()->GetBoolean(rappor::prefs::kRapporEnabled));
 }
 
-// TODO(holte): Remove special casing here when it is removed from
-// GetRapporRecordingLevel
 #if defined(GOOGLE_CHROME_BUILD)
-#if !defined(OS_IOS) && !defined(OS_ANDROID)
 
 TEST_F(MetricsServicesManagerTest, GetRecordingLevelDisabled) {
+  UseRapporOption();
   test_prefs()->SetBoolean(rappor::prefs::kRapporEnabled, false);
   bool uma_enabled = true;
 
@@ -112,6 +126,7 @@
 }
 
 TEST_F(MetricsServicesManagerTest, GetRecordingLevelFine) {
+  UseRapporOption();
   test_prefs()->SetBoolean(rappor::prefs::kRapporEnabled, true);
   bool uma_enabled = true;
 
@@ -120,6 +135,7 @@
 }
 
 TEST_F(MetricsServicesManagerTest, GetRecordingLevelCoarse) {
+  UseRapporOption();
   test_prefs()->SetBoolean(rappor::prefs::kRapporEnabled, true);
   bool uma_enabled = false;
 
@@ -127,5 +143,4 @@
             manager()->GetRapporRecordingLevel(uma_enabled));
 }
 
-#endif  // !defined(OS_IOS) && !defined(OS_ANDROID)
 #endif  // defined(GOOGLE_CHROME_BUILD)
diff --git a/chrome/browser/metrics/rappor/sampling.cc b/chrome/browser/metrics/rappor/sampling.cc
index d5c01b3..1000098 100644
--- a/chrome/browser/metrics/rappor/sampling.cc
+++ b/chrome/browser/metrics/rappor/sampling.cc
@@ -6,6 +6,7 @@
 
 #include "chrome/browser/browser_process.h"
 #include "components/rappor/rappor_service.h"
+#include "net/base/net_util.h"
 #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
 #include "url/gurl.h"
 
@@ -13,6 +14,10 @@
 
 std::string GetDomainAndRegistrySampleFromGURL(const GURL& gurl) {
   if (gurl.SchemeIsHTTPOrHTTPS()) {
+    if (net::IsLocalhost(gurl.host()))
+      return "localhost";
+    if (gurl.HostIsIPAddress())
+      return "ip_address";
     return net::registry_controlled_domains::GetDomainAndRegistry(
         gurl, net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES);
   }
diff --git a/chrome/browser/metrics/rappor/sampling_unittest.cc b/chrome/browser/metrics/rappor/sampling_unittest.cc
index fdaa1ab7..6cb1071 100644
--- a/chrome/browser/metrics/rappor/sampling_unittest.cc
+++ b/chrome/browser/metrics/rappor/sampling_unittest.cc
@@ -19,6 +19,18 @@
       GURL("chrome-extension://abc1234/foo.html")));
   EXPECT_EQ("chrome-search://local-ntp", GetDomainAndRegistrySampleFromGURL(
       GURL("chrome-search://local-ntp/local-ntp.html")));
+  EXPECT_EQ("localhost", GetDomainAndRegistrySampleFromGURL(
+      GURL("http://localhost:8000/foo.html")));
+  EXPECT_EQ("localhost", GetDomainAndRegistrySampleFromGURL(
+      GURL("http://127.0.0.1/foo.html")));
+  EXPECT_EQ("ip_address", GetDomainAndRegistrySampleFromGURL(
+      GURL("http://192.168.0.1/foo.html")));
+  EXPECT_EQ("ip_address", GetDomainAndRegistrySampleFromGURL(
+      GURL("http://[2001:db8::1]/")));
+  EXPECT_EQ("", GetDomainAndRegistrySampleFromGURL(
+      GURL("http://www/")));
+  EXPECT_EQ("www.corp", GetDomainAndRegistrySampleFromGURL(
+      GURL("http://www.corp/")));
 }
 
 // Make sure recording a sample during tests, when the Rappor service is NULL,
diff --git a/chrome/browser/net/pref_proxy_config_tracker_impl.cc b/chrome/browser/net/pref_proxy_config_tracker_impl.cc
index 6e9b771..3e969c8 100644
--- a/chrome/browser/net/pref_proxy_config_tracker_impl.cc
+++ b/chrome/browser/net/pref_proxy_config_tracker_impl.cc
@@ -7,7 +7,6 @@
 #include "base/bind.h"
 #include "base/prefs/pref_registry_simple.h"
 #include "base/prefs/pref_service.h"
-#include "base/profiler/scoped_tracker.h"
 #include "base/values.h"
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/prefs/proxy_config_dictionary.h"
@@ -102,10 +101,6 @@
 void ChromeProxyConfigService::OnProxyConfigChanged(
     const net::ProxyConfig& config,
     ConfigAvailability availability) {
-  // TODO(pkasting): Remove ScopedTracker below once crbug.com/455942 is fixed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "455942 ChromeProxyConfigService::OnProxyConfigChanged"));
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
 
   // Check whether there is a proxy configuration defined by preferences. In
diff --git a/chrome/browser/net/spdyproxy/data_reduction_proxy_chrome_io_data.cc b/chrome/browser/net/spdyproxy/data_reduction_proxy_chrome_io_data.cc
index 6512a1fd7..e6e8ae21 100644
--- a/chrome/browser/net/spdyproxy/data_reduction_proxy_chrome_io_data.cc
+++ b/chrome/browser/net/spdyproxy/data_reduction_proxy_chrome_io_data.cc
@@ -4,64 +4,55 @@
 
 #include "chrome/browser/net/spdyproxy/data_reduction_proxy_chrome_io_data.h"
 
-#include "base/bind.h"
-#include "base/prefs/pref_service.h"
-#include "base/time/time.h"
 #include "chrome/browser/net/spdyproxy/data_reduction_proxy_chrome_settings.h"
-#include "chrome/browser/net/spdyproxy/data_reduction_proxy_chrome_settings_factory.h"
-#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_configurator.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.h"
-#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_statistics_prefs.h"
-#include "components/data_reduction_proxy/core/common/data_reduction_proxy_event_store.h"
+#include "components/data_reduction_proxy/core/common/data_reduction_proxy_params.h"
+
+#if defined(OS_ANDROID)
+#include "base/android/build_info.h"
+#endif
 
 #if defined(ENABLE_DATA_REDUCTION_PROXY_DEBUGGING)
 #include "chrome/browser/browser_process.h"
 #include "components/data_reduction_proxy/content/browser/content_data_reduction_proxy_debug_ui_service.h"
+#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_configurator.h"
 #endif
 
 namespace content {
 class BrowserContext;
 }
 
+using data_reduction_proxy::DataReductionProxyParams;
+
 scoped_ptr<data_reduction_proxy::DataReductionProxyIOData>
 CreateDataReductionProxyChromeIOData(
     net::NetLog* net_log,
-    content::BrowserContext* browser_context,
     PrefService* prefs,
     const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner,
-    const scoped_refptr<base::SingleThreadTaskRunner>& ui_task_runner) {
+    const scoped_refptr<base::SingleThreadTaskRunner>& ui_task_runner,
+    bool enable_quic) {
   DCHECK(net_log);
   DCHECK(prefs);
-  DCHECK(browser_context);
-  DataReductionProxyChromeSettings* settings =
-      DataReductionProxyChromeSettingsFactory::GetForBrowserContext(
-          browser_context);
 
-#if defined(OS_ANDROID) || defined(OS_IOS)
-  // On mobile we write data reduction proxy prefs directly to the pref service.
-  // On desktop we store data reduction proxy prefs in memory, writing to disk
-  // every 60 minutes and on termination. Shutdown hooks must be added for
-  // Android and iOS in order for non-zero delays to be supported.
-  // (http://crbug.com/408264)
-  base::TimeDelta commit_delay = base::TimeDelta();
-#else
-  base::TimeDelta commit_delay = base::TimeDelta::FromMinutes(60);
+  int flags = DataReductionProxyParams::kAllowed |
+      DataReductionProxyParams::kFallbackAllowed |
+      DataReductionProxyParams::kAlternativeAllowed;
+  if (DataReductionProxyParams::IsIncludedInPromoFieldTrial())
+    flags |= DataReductionProxyParams::kPromoAllowed;
+  if (DataReductionProxyParams::IsIncludedInHoldbackFieldTrial())
+    flags |= DataReductionProxyParams::kHoldback;
+#if defined(OS_ANDROID)
+  if (DataReductionProxyParams::IsIncludedInAndroidOnePromoFieldTrial(
+          base::android::BuildInfo::GetInstance()->android_build_fp())) {
+    flags |= DataReductionProxyParams::kPromoAllowed;
+  }
 #endif
 
-  data_reduction_proxy::DataReductionProxyStatisticsPrefs*
-      data_reduction_proxy_statistics_prefs =
-          new data_reduction_proxy::DataReductionProxyStatisticsPrefs(
-              prefs, ui_task_runner, commit_delay);
-
   scoped_ptr<data_reduction_proxy::DataReductionProxyIOData>
       data_reduction_proxy_io_data(
           new data_reduction_proxy::DataReductionProxyIOData(
-              DataReductionProxyChromeSettings::GetClient(),
-              make_scoped_ptr(data_reduction_proxy_statistics_prefs),
-              settings,
-              net_log,
-              io_task_runner,
-              ui_task_runner));
+              DataReductionProxyChromeSettings::GetClient(), flags, net_log,
+              io_task_runner, ui_task_runner, enable_quic));
   data_reduction_proxy_io_data->InitOnUIThread(prefs);
 
 #if defined(ENABLE_DATA_REDUCTION_PROXY_DEBUGGING)
diff --git a/chrome/browser/net/spdyproxy/data_reduction_proxy_chrome_io_data.h b/chrome/browser/net/spdyproxy/data_reduction_proxy_chrome_io_data.h
index f60240c..d39c0a1 100644
--- a/chrome/browser/net/spdyproxy/data_reduction_proxy_chrome_io_data.h
+++ b/chrome/browser/net/spdyproxy/data_reduction_proxy_chrome_io_data.h
@@ -15,14 +15,8 @@
 class SingleThreadTaskRunner;
 }
 
-namespace content {
-class BrowserContext;
-}
-
 namespace data_reduction_proxy {
-class DataReductionProxyConfigurator;
 class DataReductionProxyIOData;
-class DataReductionProxySettings;
 }
 
 namespace net {
@@ -34,9 +28,9 @@
 scoped_ptr<data_reduction_proxy::DataReductionProxyIOData>
 CreateDataReductionProxyChromeIOData(
     net::NetLog* net_log,
-    content::BrowserContext* browser_context,
     PrefService* prefs,
     const scoped_refptr<base::SingleThreadTaskRunner>& io_thread_runner,
-    const scoped_refptr<base::SingleThreadTaskRunner>& ui_thread_runner);
+    const scoped_refptr<base::SingleThreadTaskRunner>& ui_thread_runner,
+    bool enable_quic);
 
 #endif  // CHROME_BROWSER_NET_SPDYPROXY_DATA_REDUCTION_PROXY_CHROME_IO_DATA_H_
diff --git a/chrome/browser/net/spdyproxy/data_reduction_proxy_chrome_settings.cc b/chrome/browser/net/spdyproxy/data_reduction_proxy_chrome_settings.cc
index 761c2d3..5d63307 100644
--- a/chrome/browser/net/spdyproxy/data_reduction_proxy_chrome_settings.cc
+++ b/chrome/browser/net/spdyproxy/data_reduction_proxy_chrome_settings.cc
@@ -4,9 +4,12 @@
 
 #include "chrome/browser/net/spdyproxy/data_reduction_proxy_chrome_settings.h"
 
+#include <string>
+
 #include "base/memory/scoped_ptr.h"
 #include "base/prefs/pref_service.h"
 #include "base/prefs/scoped_user_pref_update.h"
+#include "base/time/time.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/metrics/chrome_metrics_service_accessor.h"
 #include "chrome/browser/prefs/proxy_prefs.h"
@@ -15,6 +18,7 @@
 #include "chrome/common/pref_names.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_configurator.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.h"
+#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_service.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_statistics_prefs.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_params.h"
@@ -29,13 +33,14 @@
 // DIRECT for a URL. It no longer can be a ProxyConfig in the proxy preference
 // hierarchy. This method removes the Data Reduction Proxy configuration from
 // prefs, if present. |proxy_pref_name| is the name of the proxy pref.
-void MigrateDataReductionProxyOffProxyPrefs(PrefService* prefs) {
+void DataReductionProxyChromeSettings::MigrateDataReductionProxyOffProxyPrefs(
+    PrefService* prefs) {
   base::DictionaryValue* dict =
       (base::DictionaryValue*) prefs->GetUserPrefValue(prefs::kProxy);
   if (!dict)
     return;
 
-  // Clear empty "proxy" dictionary created by a bug. See http://crbug/448172
+  // Clear empty "proxy" dictionary created by a bug. See http://crbug/448172.
   if (dict->empty()) {
     prefs->ClearPref(prefs::kProxy);
     return;
@@ -44,6 +49,12 @@
   std::string mode;
   if (!dict->GetString("mode", &mode))
     return;
+  // Clear "system" proxy entry since this is the default. This entry was
+  // created by bug (http://crbug/448172).
+  if (ProxyModeToString(ProxyPrefs::MODE_SYSTEM) == mode) {
+    prefs->ClearPref(prefs::kProxy);
+    return;
+  }
   if (ProxyModeToString(ProxyPrefs::MODE_FIXED_SERVERS) != mode)
     return;
   std::string proxy_server;
@@ -58,26 +69,46 @@
   prefs->ClearPref(prefs::kProxy);
 }
 
-DataReductionProxyChromeSettings::DataReductionProxyChromeSettings(
-    scoped_ptr<DataReductionProxyParams> params)
-    : DataReductionProxySettings(params.Pass()) {
+DataReductionProxyChromeSettings::DataReductionProxyChromeSettings()
+    : DataReductionProxySettings() {
 }
 
 DataReductionProxyChromeSettings::~DataReductionProxyChromeSettings() {
 }
 
+void DataReductionProxyChromeSettings::Shutdown() {
+  data_reduction_proxy_service()->Shutdown();
+}
+
 void DataReductionProxyChromeSettings::InitDataReductionProxySettings(
     data_reduction_proxy::DataReductionProxyIOData* io_data,
     PrefService* profile_prefs,
-    PrefService* local_state_prefs,
-    net::URLRequestContextGetter* request_context) {
-  SetProxyConfigurator(io_data->configurator());
+    net::URLRequestContextGetter* request_context_getter,
+    const scoped_refptr<base::SingleThreadTaskRunner>& ui_task_runner) {
+#if defined(OS_ANDROID) || defined(OS_IOS)
+  // On mobile we write Data Reduction Proxy prefs directly to the pref service.
+  // On desktop we store Data Reduction Proxy prefs in memory, writing to disk
+  // every 60 minutes and on termination. Shutdown hooks must be added for
+  // Android and iOS in order for non-zero delays to be supported.
+  // (http://crbug.com/408264)
+  base::TimeDelta commit_delay = base::TimeDelta();
+#else
+  base::TimeDelta commit_delay = base::TimeDelta::FromMinutes(60);
+#endif
+
+  scoped_ptr<data_reduction_proxy::DataReductionProxyStatisticsPrefs>
+      statistics_prefs = make_scoped_ptr(
+          new data_reduction_proxy::DataReductionProxyStatisticsPrefs(
+              profile_prefs, ui_task_runner, commit_delay));
+  scoped_ptr<data_reduction_proxy::DataReductionProxyService>
+      service = make_scoped_ptr(
+          new data_reduction_proxy::DataReductionProxyService(
+              statistics_prefs.Pass(), this, request_context_getter));
   DataReductionProxySettings::InitDataReductionProxySettings(
-      profile_prefs,
-      io_data->PassStatisticsPrefs(),
-      request_context,
-      io_data->net_log(),
-      io_data->event_store());
+      profile_prefs, io_data, service.Pass());
+  io_data->SetDataReductionProxyService(
+      data_reduction_proxy_service()->GetWeakPtr());
+
   DataReductionProxySettings::SetOnDataReductionEnabledCallback(
       base::Bind(&DataReductionProxyChromeSettings::RegisterSyntheticFieldTrial,
                  base::Unretained(this)));
diff --git a/chrome/browser/net/spdyproxy/data_reduction_proxy_chrome_settings.h b/chrome/browser/net/spdyproxy/data_reduction_proxy_chrome_settings.h
index bce19bac..4f3b283e 100644
--- a/chrome/browser/net/spdyproxy/data_reduction_proxy_chrome_settings.h
+++ b/chrome/browser/net/spdyproxy/data_reduction_proxy_chrome_settings.h
@@ -5,24 +5,21 @@
 #ifndef CHROME_BROWSER_NET_SPDYPROXY_DATA_REDUCTION_PROXY_CHROME_SETTINGS_H_
 #define CHROME_BROWSER_NET_SPDYPROXY_DATA_REDUCTION_PROXY_CHROME_SETTINGS_H_
 
-#include "base/memory/scoped_ptr.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.h"
 #include "components/keyed_service/core/keyed_service.h"
 
-namespace base {
 class PrefService;
+
+namespace base {
+class SingleThreadTaskRunner;
 }
 
 namespace data_reduction_proxy {
-class DataReductionProxyConfigurator;
 class DataReductionProxyIOData;
-class DataReductionProxyEventStore;
-class DataReductionProxyParams;
 }
 
 namespace net {
-class NetLog;
 class URLRequestContextGetter;
 }
 
@@ -34,26 +31,30 @@
     : public data_reduction_proxy::DataReductionProxySettings,
       public KeyedService {
  public:
-  // Constructs a settings object with the given configuration parameters.
-  // Construction and destruction must happen on the UI thread.
-  explicit DataReductionProxyChromeSettings(
-      scoped_ptr<data_reduction_proxy::DataReductionProxyParams> params);
+  // Constructs a settings object. Construction and destruction must happen on
+  // the UI thread.
+  DataReductionProxyChromeSettings();
 
   // Destructs the settings object.
   ~DataReductionProxyChromeSettings() override;
 
-  // Initialize the settings object with the given configurator, prefs services,
-  // and request context. Settings takes ownership of statistics prefs from
-  // |io_data|.
+  // Overrides KeyedService::Shutdown:
+  void Shutdown() override;
+
+  // Initialize the settings object with the given io_data, prefs services,
+  // request context getter, and task runner.
   void InitDataReductionProxySettings(
       data_reduction_proxy::DataReductionProxyIOData* io_data,
       PrefService* profile_prefs,
-      PrefService* local_state_prefs,
-      net::URLRequestContextGetter* request_context);
+      net::URLRequestContextGetter* request_context_getter,
+      const scoped_refptr<base::SingleThreadTaskRunner>& ui_task_runner);
 
   // Gets the client type for the data reduction proxy.
   static data_reduction_proxy::Client GetClient();
 
+  // Public for testing.
+  void MigrateDataReductionProxyOffProxyPrefs(PrefService* prefs);
+
  private:
   // Registers the DataReductionProxyEnabled synthetic field trial with
   // the group |data_reduction_proxy_enabled|.
diff --git a/chrome/browser/net/spdyproxy/data_reduction_proxy_chrome_settings_factory.cc b/chrome/browser/net/spdyproxy/data_reduction_proxy_chrome_settings_factory.cc
index 90f23841..33fcb42 100644
--- a/chrome/browser/net/spdyproxy/data_reduction_proxy_chrome_settings_factory.cc
+++ b/chrome/browser/net/spdyproxy/data_reduction_proxy_chrome_settings_factory.cc
@@ -7,19 +7,8 @@
 #include "base/bind.h"
 #include "base/memory/singleton.h"
 #include "chrome/browser/net/spdyproxy/data_reduction_proxy_chrome_settings.h"
-#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_usage_stats.h"
-#include "components/data_reduction_proxy/core/common/data_reduction_proxy_params.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
 #include "components/keyed_service/content/browser_context_keyed_service_factory.h"
-#include "content/public/browser/browser_thread.h"
-
-#if defined(OS_ANDROID)
-#include "base/android/build_info.h"
-#endif
-
-using content::BrowserThread;
-using data_reduction_proxy::DataReductionProxyParams;
-using data_reduction_proxy::DataReductionProxyUsageStats;
 
 // static
 DataReductionProxyChromeSettings*
@@ -56,21 +45,5 @@
 
 KeyedService* DataReductionProxyChromeSettingsFactory::BuildServiceInstanceFor(
     content::BrowserContext* context) const {
-  int flags = DataReductionProxyParams::kAllowed |
-      DataReductionProxyParams::kFallbackAllowed |
-      DataReductionProxyParams::kAlternativeAllowed;
-  if (DataReductionProxyParams::IsIncludedInPromoFieldTrial())
-    flags |= DataReductionProxyParams::kPromoAllowed;
-  if (DataReductionProxyParams::IsIncludedInHoldbackFieldTrial())
-    flags |= DataReductionProxyParams::kHoldback;
-#if defined(OS_ANDROID)
-  if (DataReductionProxyParams::IsIncludedInAndroidOnePromoFieldTrial(
-          base::android::BuildInfo::GetInstance()->android_build_fp())) {
-    flags |= DataReductionProxyParams::kPromoAllowed;
-  }
-#endif
-
-  return new DataReductionProxyChromeSettings(
-      scoped_ptr<DataReductionProxyParams>(new DataReductionProxyParams(flags))
-          .Pass());
+  return new DataReductionProxyChromeSettings();
 }
diff --git a/chrome/browser/net/spdyproxy/data_reduction_proxy_chrome_settings_factory.h b/chrome/browser/net/spdyproxy/data_reduction_proxy_chrome_settings_factory.h
index 05cb123f..a4a22bf 100644
--- a/chrome/browser/net/spdyproxy/data_reduction_proxy_chrome_settings_factory.h
+++ b/chrome/browser/net/spdyproxy/data_reduction_proxy_chrome_settings_factory.h
@@ -7,7 +7,6 @@
 
 #include "base/compiler_specific.h"
 #include "base/memory/singleton.h"
-#include "components/data_reduction_proxy/core/common/data_reduction_proxy_params.h"
 #include "components/keyed_service/content/browser_context_keyed_service_factory.h"
 
 class DataReductionProxyChromeSettings;
diff --git a/chrome/browser/net/spdyproxy/data_reduction_proxy_chrome_settings_unittest.cc b/chrome/browser/net/spdyproxy/data_reduction_proxy_chrome_settings_unittest.cc
new file mode 100644
index 0000000..d36deb4
--- /dev/null
+++ b/chrome/browser/net/spdyproxy/data_reduction_proxy_chrome_settings_unittest.cc
@@ -0,0 +1,79 @@
+// 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 <string>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/prefs/pref_registry_simple.h"
+#include "base/prefs/testing_pref_service.h"
+#include "chrome/browser/net/spdyproxy/data_reduction_proxy_chrome_settings.h"
+#include "chrome/common/pref_names.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using base::DictionaryValue;
+using data_reduction_proxy::DataReductionProxyParams;
+using data_reduction_proxy::DataReductionProxySettings;
+
+class DataReductionProxyChromeSettingsTest : public testing::Test {
+ public:
+  void SetUp() override {
+    drp_chrome_settings_ =
+        make_scoped_ptr(new DataReductionProxyChromeSettings());
+    dict_ = make_scoped_ptr(new DictionaryValue());
+    mock_pref_service_ = make_scoped_ptr(new TestingPrefServiceSimple());
+
+    PrefRegistrySimple* registry = mock_pref_service_->registry();
+    registry->RegisterDictionaryPref(prefs::kProxy);
+  }
+
+  scoped_ptr<DataReductionProxyChromeSettings> drp_chrome_settings_;
+  scoped_ptr<base::DictionaryValue> dict_;
+  scoped_ptr<TestingPrefServiceSimple> mock_pref_service_;
+};
+
+TEST_F(DataReductionProxyChromeSettingsTest, MigrateEmptyProxy) {
+  drp_chrome_settings_->MigrateDataReductionProxyOffProxyPrefs(
+      mock_pref_service_.get());
+
+  EXPECT_EQ(NULL, mock_pref_service_->GetUserPref(prefs::kProxy));
+}
+
+TEST_F(DataReductionProxyChromeSettingsTest, MigrateSystemProxy) {
+  dict_->SetString("mode", "system");
+  mock_pref_service_->Set(prefs::kProxy, *dict_.get());
+
+  drp_chrome_settings_->MigrateDataReductionProxyOffProxyPrefs(
+      mock_pref_service_.get());
+
+  EXPECT_EQ(NULL, mock_pref_service_->GetUserPref(prefs::kProxy));
+}
+
+TEST_F(DataReductionProxyChromeSettingsTest, MigrateDataReductionProxy) {
+  dict_->SetString("mode", "fixed_servers");
+  dict_->SetString("server", "http=https://proxy.googlezip.net");
+  mock_pref_service_->Set(prefs::kProxy, *dict_.get());
+
+  drp_chrome_settings_->MigrateDataReductionProxyOffProxyPrefs(
+      mock_pref_service_.get());
+
+  EXPECT_EQ(NULL, mock_pref_service_->GetUserPref(prefs::kProxy));
+}
+
+TEST_F(DataReductionProxyChromeSettingsTest, MigrateIgnoreOtherProxy) {
+  dict_->SetString("mode", "fixed_servers");
+  dict_->SetString("server", "http=https://youtube.com");
+  mock_pref_service_->Set(prefs::kProxy, *dict_.get());
+
+  drp_chrome_settings_->MigrateDataReductionProxyOffProxyPrefs(
+      mock_pref_service_.get());
+
+  DictionaryValue* value =
+      (DictionaryValue*)mock_pref_service_->GetUserPref(prefs::kProxy);
+  std::string mode;
+  EXPECT_TRUE(value->GetString("mode", &mode));
+  EXPECT_EQ("fixed_servers", mode);
+  std::string server;
+  EXPECT_TRUE(value->GetString("server", &server));
+  EXPECT_EQ("http=https://youtube.com", server);
+}
diff --git a/chrome/browser/net/spdyproxy/data_reduction_proxy_settings_android.cc b/chrome/browser/net/spdyproxy/data_reduction_proxy_settings_android.cc
index ce94aae..8eaf2d2 100644
--- a/chrome/browser/net/spdyproxy/data_reduction_proxy_settings_android.cc
+++ b/chrome/browser/net/spdyproxy/data_reduction_proxy_settings_android.cc
@@ -31,12 +31,12 @@
 
 jboolean DataReductionProxySettingsAndroid::IsDataReductionProxyAllowed(
     JNIEnv* env, jobject obj) {
-  return Settings()->params()->allowed();
+  return Settings()->Allowed();
 }
 
 jboolean DataReductionProxySettingsAndroid::IsDataReductionProxyPromoAllowed(
     JNIEnv* env, jobject obj) {
-  return Settings()->params()->promo_allowed();
+  return Settings()->PromoAllowed();
 }
 
 jboolean DataReductionProxySettingsAndroid::IsIncludedInAltFieldTrial(
@@ -47,7 +47,7 @@
 ScopedJavaLocalRef<jstring>
 DataReductionProxySettingsAndroid::GetDataReductionProxyOrigin(
     JNIEnv* env, jobject obj) {
-  return ConvertUTF8ToJavaString(env, Settings()->params()->origin().ToURI());
+  return ConvertUTF8ToJavaString(env, Settings()->PrimaryOrigin());
 }
 
 jboolean DataReductionProxySettingsAndroid::IsDataReductionProxyEnabled(
diff --git a/chrome/browser/net/spdyproxy/data_reduction_proxy_settings_unittest_android.cc b/chrome/browser/net/spdyproxy/data_reduction_proxy_settings_unittest_android.cc
index 478cd913..9c89795 100644
--- a/chrome/browser/net/spdyproxy/data_reduction_proxy_settings_unittest_android.cc
+++ b/chrome/browser/net/spdyproxy/data_reduction_proxy_settings_unittest_android.cc
@@ -18,8 +18,11 @@
 #include "chrome/browser/prefs/proxy_prefs.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/pref_names.h"
+#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_config_test_utils.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_settings_test_utils.h"
-#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_statistics_prefs.h"
+#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.h"
+#include "components/data_reduction_proxy/core/common/data_reduction_proxy_params.h"
+#include "components/data_reduction_proxy/core/common/data_reduction_proxy_params_test_utils.h"
 #include "net/proxy/proxy_server.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -70,49 +73,19 @@
   if (holdback)
     flags |= DataReductionProxyParams::kHoldback;
   MockDataReductionProxySettings<C>* settings =
-      new MockDataReductionProxySettings<C>(flags);
+      new MockDataReductionProxySettings<C>();
+  settings->config_ = test_context_->config();
+  settings->data_reduction_proxy_service_ =
+      test_context_->CreateDataReductionProxyService();
+  test_context_->config()->ResetParamFlagsForTest(flags);
+  settings->UpdateConfigValues();
   EXPECT_CALL(*settings, GetOriginalProfilePrefs())
       .Times(AnyNumber())
-      .WillRepeatedly(Return(&pref_service_));
+      .WillRepeatedly(Return(test_context_->pref_service()));
   EXPECT_CALL(*settings, GetLocalStatePrefs())
       .Times(AnyNumber())
-      .WillRepeatedly(Return(&pref_service_));
-  EXPECT_CALL(*settings, GetURLFetcherForAvailabilityCheck()).Times(0);
-  EXPECT_CALL(*settings, LogProxyState(_, _, _)).Times(0);
+      .WillRepeatedly(Return(test_context_->pref_service()));
   settings_.reset(settings);
-  settings_->SetDataReductionProxyStatisticsPrefs(
-      scoped_ptr<DataReductionProxyStatisticsPrefs>(
-          new DataReductionProxyStatisticsPrefs(
-              &pref_service_,
-              scoped_refptr<base::TestSimpleTaskRunner>(
-                  new base::TestSimpleTaskRunner()),
-              base::TimeDelta())));
-}
-
-template <class C>
-void data_reduction_proxy::DataReductionProxySettingsTestBase::SetProbeResult(
-    const std::string& test_url,
-    const std::string& response,
-    ProbeURLFetchResult result,
-    bool success,
-    int expected_calls)  {
-  MockDataReductionProxySettings<C>* settings =
-      static_cast<MockDataReductionProxySettings<C>*>(settings_.get());
-  if (0 == expected_calls) {
-    EXPECT_CALL(*settings, GetURLFetcherForAvailabilityCheck()).Times(0);
-    EXPECT_CALL(*settings, RecordProbeURLFetchResult(_)).Times(0);
-  } else {
-    EXPECT_CALL(*settings, RecordProbeURLFetchResult(result)).Times(1);
-    EXPECT_CALL(*settings, GetURLFetcherForAvailabilityCheck())
-        .Times(expected_calls)
-        .WillRepeatedly(Return(new net::FakeURLFetcher(
-            GURL(test_url),
-            settings,
-            response,
-            success ? net::HTTP_OK : net::HTTP_INTERNAL_SERVER_ERROR,
-            success ? net::URLRequestStatus::SUCCESS :
-                      net::URLRequestStatus::FAILED)));
-  }
 }
 
 template void
@@ -123,14 +96,6 @@
                                        bool promo_allowed,
                                        bool holdback);
 
-template void
-data_reduction_proxy::DataReductionProxySettingsTestBase::SetProbeResult<
-    DataReductionProxyChromeSettings>(const std::string& test_url,
-                                       const std::string& response,
-                                       ProbeURLFetchResult result,
-                                       bool success,
-                                       int expected_calls);
-
 class DataReductionProxySettingsAndroidTest
     : public data_reduction_proxy::ConcreteDataReductionProxySettingsTest<
           DataReductionProxyChromeSettings> {
@@ -166,7 +131,7 @@
       SettingsAndroid()->GetDataReductionProxyOrigin(env_, NULL);
   ASSERT_TRUE(result.obj());
   const base::android::JavaRef<jstring>& str_ref = result;
-  EXPECT_EQ(expected_params_->DefaultOrigin(),
+  EXPECT_EQ(test_context_->config()->test_params()->DefaultOrigin(),
             ConvertJavaStringToUTF8(str_ref));
 }
 
diff --git a/chrome/browser/net/websocket_browsertest.cc b/chrome/browser/net/websocket_browsertest.cc
index 3ef21c0b..c96e38b 100644
--- a/chrome/browser/net/websocket_browsertest.cc
+++ b/chrome/browser/net/websocket_browsertest.cc
@@ -296,4 +296,49 @@
   EXPECT_EQ("PASS", WaitAndGetTitle());
 }
 
+// Regression test for crbug.com/903553005
+IN_PROC_BROWSER_TEST_F(WebSocketBrowserTest, WebSocketAppliesHSTS) {
+  net::SpawnedTestServer https_server(
+      net::SpawnedTestServer::TYPE_HTTPS,
+      net::SpawnedTestServer::SSLOptions(),
+      base::FilePath(FILE_PATH_LITERAL("chrome/test/data")));
+  // This test sets HSTS on 127.0.0.1. To avoid being redirected to https, start
+  // the http server on "localhost" instead.
+  net::SpawnedTestServer http_server(
+      net::SpawnedTestServer::TYPE_HTTP,
+      "localhost",
+      base::FilePath(FILE_PATH_LITERAL("chrome/test/data")));
+  ASSERT_TRUE(https_server.StartInBackground());
+  ASSERT_TRUE(http_server.StartInBackground());
+  ASSERT_TRUE(wss_server_.StartInBackground());
+  ASSERT_TRUE(https_server.BlockUntilStarted());
+
+  // Set HSTS on 127.0.0.1.
+  content::TitleWatcher title_watcher(
+      browser()->tab_strip_model()->GetActiveWebContents(),
+      base::ASCIIToUTF16("SET"));
+  ui_test_utils::NavigateToURL(
+      browser(), https_server.GetURL("files/websocket/set-hsts.html"));
+  const base::string16 result = title_watcher.WaitAndGetTitle();
+  EXPECT_TRUE(EqualsASCII(result, "SET"));
+
+  // Verify that it applies to WebSockets.
+  ASSERT_TRUE(wss_server_.BlockUntilStarted());
+  GURL wss_url = wss_server_.GetURL("echo-with-no-extension");
+  std::string scheme("ws");
+  GURL::Replacements scheme_replacement;
+  scheme_replacement.SetSchemeStr(scheme);
+  GURL ws_url = wss_url.ReplaceComponents(scheme_replacement);
+
+  // An https: URL won't work here here because the mixed content policy
+  // disallows connections to unencrypted WebSockets from encrypted pages.
+  ASSERT_TRUE(http_server.BlockUntilStarted());
+  GURL http_url =
+      http_server.GetURL("files/websocket/check-hsts.html#" + ws_url.spec());
+
+  ui_test_utils::NavigateToURL(browser(), http_url);
+
+  EXPECT_EQ("PASS", WaitAndGetTitle());
+}
+
 }  // namespace
diff --git a/chrome/browser/notifications/desktop_notification_service.cc b/chrome/browser/notifications/desktop_notification_service.cc
index fffe236..19b3b16 100644
--- a/chrome/browser/notifications/desktop_notification_service.cc
+++ b/chrome/browser/notifications/desktop_notification_service.cc
@@ -6,6 +6,7 @@
 
 #include "base/bind.h"
 #include "base/metrics/histogram.h"
+#include "base/metrics/histogram_macros.h"
 #include "base/prefs/scoped_user_pref_update.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/browser_process.h"
@@ -127,6 +128,12 @@
   }
 #endif
 
+  // Track whether the requesting and embedding origins are different when
+  // permission to display Web Notifications is being requested.
+  UMA_HISTOGRAM_BOOLEAN("Notifications.DifferentRequestingEmbeddingOrigins",
+                        requesting_origin.GetOrigin() !=
+                            web_contents->GetLastCommittedURL().GetOrigin());
+
   RequestPermission(web_contents,
                     request_id,
                     requesting_origin,
diff --git a/chrome/browser/password_manager/account_chooser_infobar_delegate_android.cc b/chrome/browser/password_manager/account_chooser_infobar_delegate_android.cc
new file mode 100644
index 0000000..21a252e2
--- /dev/null
+++ b/chrome/browser/password_manager/account_chooser_infobar_delegate_android.cc
@@ -0,0 +1,56 @@
+// 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 "chrome/browser/password_manager/account_chooser_infobar_delegate_android.h"
+
+#include "chrome/browser/infobars/infobar_service.h"
+#include "chrome/browser/ui/android/infobars/account_chooser_infobar.h"
+#include "components/autofill/core/common/password_form.h"
+#include "components/password_manager/content/common/credential_manager_types.h"
+
+// static
+void AccountChooserInfoBarDelegateAndroid::Create(
+    InfoBarService* infobar_service,
+    ManagePasswordsUIController* ui_controller) {
+  infobar_service->AddInfoBar(
+      make_scoped_ptr(new AccountChooserInfoBar(make_scoped_ptr(
+          new AccountChooserInfoBarDelegateAndroid(ui_controller)))));
+}
+
+AccountChooserInfoBarDelegateAndroid::AccountChooserInfoBarDelegateAndroid(
+    ManagePasswordsUIController* ui_controller)
+    : ui_controller_(ui_controller) {
+}
+
+AccountChooserInfoBarDelegateAndroid::~AccountChooserInfoBarDelegateAndroid() {
+}
+
+void AccountChooserInfoBarDelegateAndroid::ChooseCredential(
+    size_t credential_index,
+    password_manager::CredentialType credential_type) {
+  using namespace password_manager;
+  if (credential_type == CredentialType::CREDENTIAL_TYPE_EMPTY) {
+    ui_controller_->ChooseCredential(autofill::PasswordForm(), credential_type);
+    return;
+  }
+  DCHECK(credential_type == CredentialType::CREDENTIAL_TYPE_LOCAL ||
+         credential_type == CredentialType::CREDENTIAL_TYPE_FEDERATED);
+  auto& credentials_forms =
+      (credential_type == CredentialType::CREDENTIAL_TYPE_LOCAL)
+          ? ui_controller_->local_credentials_forms()
+          : ui_controller_->federated_credentials_forms();
+  if (credential_index < credentials_forms.size()) {
+    ui_controller_->ChooseCredential(*credentials_forms[credential_index],
+                                     credential_type);
+  }
+}
+
+void AccountChooserInfoBarDelegateAndroid::InfoBarDismissed() {
+  ChooseCredential(-1, password_manager::CredentialType::CREDENTIAL_TYPE_EMPTY);
+}
+
+infobars::InfoBarDelegate::Type
+AccountChooserInfoBarDelegateAndroid::GetInfoBarType() const {
+  return PAGE_ACTION_TYPE;
+}
diff --git a/chrome/browser/password_manager/account_chooser_infobar_delegate_android.h b/chrome/browser/password_manager/account_chooser_infobar_delegate_android.h
new file mode 100644
index 0000000..7ac175ad
--- /dev/null
+++ b/chrome/browser/password_manager/account_chooser_infobar_delegate_android.h
@@ -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.
+
+#ifndef CHROME_BROWSER_PASSWORD_MANAGER_ACCOUNT_CHOOSER_INFOBAR_DELEGATE_ANDROID_H_
+#define CHROME_BROWSER_PASSWORD_MANAGER_ACCOUNT_CHOOSER_INFOBAR_DELEGATE_ANDROID_H_
+
+#include "base/macros.h"
+#include "chrome/browser/ui/passwords/manage_passwords_ui_controller.h"
+#include "components/infobars/core/infobar_delegate.h"
+
+class InfoBarService;
+
+namespace password_manager {
+enum class CredentialType : unsigned int;
+}
+
+namespace autofill {
+struct PasswordForm;
+}
+
+// Android-only infobar delegate to allow user to choose credentials for login.
+class AccountChooserInfoBarDelegateAndroid : public infobars::InfoBarDelegate {
+ public:
+  // Creates an account chooser infobar and delegate and adds the infobar to
+  // |infobar_service|.
+  static void Create(InfoBarService* infobar_service,
+                     ManagePasswordsUIController* ui_controller);
+
+  ~AccountChooserInfoBarDelegateAndroid() override;
+
+  const ScopedVector<autofill::PasswordForm>& local_credentials_forms() const {
+    return ui_controller_->local_credentials_forms();
+  }
+
+  void ChooseCredential(size_t credential_index,
+                        password_manager::CredentialType credential_type);
+
+ private:
+  explicit AccountChooserInfoBarDelegateAndroid(
+      ManagePasswordsUIController* ui_controller);
+
+  // infobars::InfoBarDelegate:
+  void InfoBarDismissed() override;
+  Type GetInfoBarType() const override;
+
+  // Owned by WebContents.
+  ManagePasswordsUIController* ui_controller_;
+
+  DISALLOW_COPY_AND_ASSIGN(AccountChooserInfoBarDelegateAndroid);
+};
+
+#endif  // CHROME_BROWSER_PASSWORD_MANAGER_ACCOUNT_CHOOSER_INFOBAR_DELEGATE_ANDROID_H_
diff --git a/chrome/browser/password_manager/chrome_password_manager_client.cc b/chrome/browser/password_manager/chrome_password_manager_client.cc
index ab8cf7c5..6c586311 100644
--- a/chrome/browser/password_manager/chrome_password_manager_client.cc
+++ b/chrome/browser/password_manager/chrome_password_manager_client.cc
@@ -185,7 +185,7 @@
 
 bool ChromePasswordManagerClient::PromptUserToSavePassword(
     scoped_ptr<password_manager::PasswordFormManager> form_to_save,
-    password_manager::CredentialSourceType) {
+    password_manager::CredentialSourceType type) {
   // Save password infobar and the password bubble prompts in case of
   // "webby" URLs and do not prompt in case of "non-webby" URLS (e.g. file://).
   if (!BrowsingDataHelper::IsWebScheme(
@@ -215,10 +215,16 @@
     ScopedVector<autofill::PasswordForm> federated_forms,
     const GURL& origin,
     base::Callback<void(const password_manager::CredentialInfo&)> callback) {
-  ManagePasswordsUIController* manage_passwords_ui_controller =
-      ManagePasswordsUIController::FromWebContents(web_contents());
-  return manage_passwords_ui_controller->OnChooseCredentials(
-      local_forms.Pass(), federated_forms.Pass(), origin, callback);
+  return ManagePasswordsUIController::FromWebContents(web_contents())->
+      OnChooseCredentials(local_forms.Pass(), federated_forms.Pass(), origin,
+                          callback);
+}
+
+void ChromePasswordManagerClient::NotifyUserAutoSignin(
+    ScopedVector<autofill::PasswordForm> local_forms) {
+  DCHECK(!local_forms.empty());
+  ManagePasswordsUIController::FromWebContents(web_contents())->
+      OnAutoSignin(local_forms.Pass());
 }
 
 void ChromePasswordManagerClient::AutomaticPasswordSave(
diff --git a/chrome/browser/password_manager/chrome_password_manager_client.h b/chrome/browser/password_manager/chrome_password_manager_client.h
index b5912693..93e8067 100644
--- a/chrome/browser/password_manager/chrome_password_manager_client.h
+++ b/chrome/browser/password_manager/chrome_password_manager_client.h
@@ -59,6 +59,8 @@
       const GURL& origin,
       base::Callback<void(const password_manager::CredentialInfo&)> callback)
       override;
+  void NotifyUserAutoSignin(
+      ScopedVector<autofill::PasswordForm> local_forms) override;
   void AutomaticPasswordSave(scoped_ptr<password_manager::PasswordFormManager>
                                  saved_form_manager) override;
   void PasswordWasAutofilled(
diff --git a/chrome/browser/password_manager/password_manager_util_mac.mm b/chrome/browser/password_manager/password_manager_util_mac.mm
index d4b6adc..705b94d 100644
--- a/chrome/browser/password_manager/password_manager_util_mac.mm
+++ b/chrome/browser/password_manager/password_manager_util_mac.mm
@@ -10,6 +10,7 @@
 
 #include "base/basictypes.h"
 #include "base/mac/authorization_util.h"
+#include "base/mac/bundle_locations.h"
 #include "base/mac/foundation_util.h"
 #include "base/mac/scoped_authorizationref.h"
 #include "chrome/grit/chromium_strings.h"
@@ -18,8 +19,11 @@
 namespace password_manager_util {
 
 bool AuthenticateUser(gfx::NativeWindow window) {
+  NSString* identifier = [base::mac::MainBundle() bundleIdentifier];
+  AuthorizationString name =
+      [[identifier stringByAppendingString:@".show-passwords"] UTF8String];
   AuthorizationItem right_items[] = {
-    {"com.google.Chrome.show-passwords", 0, NULL, 0}
+    {name, 0, NULL, 0}
   };
   AuthorizationRights rights = {arraysize(right_items), right_items};
 
diff --git a/chrome/browser/plugins/plugin_prefs_unittest.cc b/chrome/browser/plugins/plugin_prefs_unittest.cc
index 70deb5d..49c76c01e 100644
--- a/chrome/browser/plugins/plugin_prefs_unittest.cc
+++ b/chrome/browser/plugins/plugin_prefs_unittest.cc
@@ -208,7 +208,6 @@
 
   PluginService::GetInstance()->Init();
   PluginService::GetInstance()->DisablePluginsDiscoveryForTesting();
-  PluginService::GetInstance()->EnableNpapiPluginsForTesting();
 
   base::string16 component_updated_plugin_name(
       ASCIIToUTF16("Component-updated Pepper Flash"));
@@ -226,6 +225,12 @@
                                         GetBundledPepperFlashPath(),
                                         ASCIIToUTF16("11.3.31.229"),
                                         ASCIIToUTF16(""));
+  component_updated_plugin_1.type =
+      content::WebPluginInfo::PLUGIN_TYPE_PEPPER_OUT_OF_PROCESS;
+  component_updated_plugin_2.type =
+      content::WebPluginInfo::PLUGIN_TYPE_PEPPER_OUT_OF_PROCESS;
+  bundled_plugin.type =
+      content::WebPluginInfo::PLUGIN_TYPE_PEPPER_OUT_OF_PROCESS;
 
   PluginService::GetInstance()->RegisterInternalPlugin(
       component_updated_plugin_1, false);
diff --git a/chrome/browser/policy/chrome_browser_policy_connector.cc b/chrome/browser/policy/chrome_browser_policy_connector.cc
index a36e06a..e233a87 100644
--- a/chrome/browser/policy/chrome_browser_policy_connector.cc
+++ b/chrome/browser/policy/chrome_browser_policy_connector.cc
@@ -25,6 +25,7 @@
 #include "components/policy/core/common/policy_types.h"
 #include "components/signin/core/common/signin_switches.h"
 #include "content/public/browser/browser_thread.h"
+#include "content/public/common/content_switches.h"
 #include "net/url_request/url_request_context_getter.h"
 #include "policy/policy_constants.h"
 
@@ -89,7 +90,7 @@
   BrowserPolicyConnector::Init(
       local_state, request_context, device_management_service.Pass());
 
-  AppendExtraFlagPerPolicy();
+  AppendExtraFlagsPerPolicy();
 }
 
 ConfigurationPolicyProvider*
@@ -123,12 +124,12 @@
 #endif
 }
 
-void ChromeBrowserPolicyConnector::AppendExtraFlagPerPolicy() {
+void ChromeBrowserPolicyConnector::AppendExtraFlagsPerPolicy() {
   PolicyService* policy_service = GetPolicyService();
   PolicyNamespace chrome_ns = PolicyNamespace(POLICY_DOMAIN_CHROME, "");
   const PolicyMap& chrome_policy = policy_service->GetPolicies(chrome_ns);
   const base::Value* policy_value =
-      chrome_policy.GetValue(key::kEnableWebBasedSignin);
+      chrome_policy.GetValue(key::kEnableDeprecatedWebBasedSignin);
   bool enabled = false;
   base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
   if (policy_value && policy_value->GetAsBoolean(&enabled) && enabled) {
@@ -139,6 +140,23 @@
     if (!command_line->HasSwitch(switches::kEnableIframeBasedSignin))
       command_line->AppendSwitch(switches::kEnableIframeBasedSignin);
   }
+
+  if (command_line->HasSwitch(switches::kEnableNpapi))
+    return;
+
+  // The list of Plugin related policies that re-enable NPAPI. Remove once NPAPI
+  // is dead.
+  const std::string plugin_policies[] = { key::kEnabledPlugins,
+                                          key::kPluginsAllowedForUrls,
+                                          key::kPluginsBlockedForUrls,
+                                          key::kDisabledPluginsExceptions,
+                                          key::kDisabledPlugins };
+  for (auto policy : plugin_policies) {
+    if (chrome_policy.GetValue(policy)) {
+      command_line->AppendSwitch(switches::kEnableNpapi);
+      break;
+    }
+  }
 }
 
 }  // namespace policy
diff --git a/chrome/browser/policy/chrome_browser_policy_connector.h b/chrome/browser/policy/chrome_browser_policy_connector.h
index 2d0fdde..78c82240 100644
--- a/chrome/browser/policy/chrome_browser_policy_connector.h
+++ b/chrome/browser/policy/chrome_browser_policy_connector.h
@@ -40,11 +40,10 @@
  private:
   ConfigurationPolicyProvider* CreatePlatformProvider();
 
-  // Appends the --enable-web-based-signin flag if the
-  // enable-web-based-signin policy is enabled.
+  // Appends any required flags if certain policies are set.
   // TODO(guohui): Needs to move this to a more proper place and also to handle
   // dynamic refresh.
-  void AppendExtraFlagPerPolicy();
+  void AppendExtraFlagsPerPolicy();
 
   DISALLOW_COPY_AND_ASSIGN(ChromeBrowserPolicyConnector);
 };
diff --git a/chrome/browser/policy/cloud/cloud_policy_browsertest.cc b/chrome/browser/policy/cloud/cloud_policy_browsertest.cc
index 6773f69..5224eca8 100644
--- a/chrome/browser/policy/cloud/cloud_policy_browsertest.cc
+++ b/chrome/browser/policy/cloud/cloud_policy_browsertest.cc
@@ -288,7 +288,8 @@
 
   PolicyService* GetPolicyService() {
     ProfilePolicyConnector* profile_connector =
-        ProfilePolicyConnectorFactory::GetForProfile(browser()->profile());
+        ProfilePolicyConnectorFactory::GetForBrowserContext(
+            browser()->profile());
     return profile_connector->policy_service();
   }
 
diff --git a/chrome/browser/policy/cloud/component_cloud_policy_browsertest.cc b/chrome/browser/policy/cloud/component_cloud_policy_browsertest.cc
index 7ca11bfe..c909b22 100644
--- a/chrome/browser/policy/cloud/component_cloud_policy_browsertest.cc
+++ b/chrome/browser/policy/cloud/component_cloud_policy_browsertest.cc
@@ -220,7 +220,8 @@
 
   void RefreshPolicies() {
     ProfilePolicyConnector* profile_connector =
-        ProfilePolicyConnectorFactory::GetForProfile(browser()->profile());
+        ProfilePolicyConnectorFactory::GetForBrowserContext(
+            browser()->profile());
     PolicyService* policy_service = profile_connector->policy_service();
     base::RunLoop run_loop;
     policy_service->RefreshPolicies(run_loop.QuitClosure());
diff --git a/chrome/browser/policy/policy_browsertest.cc b/chrome/browser/policy/policy_browsertest.cc
index fb699b3..3477286 100644
--- a/chrome/browser/policy/policy_browsertest.cc
+++ b/chrome/browser/policy/policy_browsertest.cc
@@ -1059,7 +1059,7 @@
           ->GetPolicies(PolicyNamespace(POLICY_DOMAIN_CHROME, std::string()));
   EXPECT_TRUE(expected.Equals(actual_from_browser));
   const PolicyMap& actual_from_profile =
-      ProfilePolicyConnectorFactory::GetForProfile(browser()->profile())
+      ProfilePolicyConnectorFactory::GetForBrowserContext(browser()->profile())
           ->policy_service()
           ->GetPolicies(PolicyNamespace(POLICY_DOMAIN_CHROME, std::string()));
   EXPECT_TRUE(expected.Equals(actual_from_profile));
diff --git a/chrome/browser/policy/profile_policy_connector_factory.cc b/chrome/browser/policy/profile_policy_connector_factory.cc
index 9c91e00..5c2cb666 100644
--- a/chrome/browser/policy/profile_policy_connector_factory.cc
+++ b/chrome/browser/policy/profile_policy_connector_factory.cc
@@ -7,6 +7,7 @@
 #include "base/logging.h"
 #include "base/memory/singleton.h"
 #include "chrome/browser/policy/profile_policy_connector.h"
+#include "chrome/browser/profiles/incognito_helpers.h"
 #include "chrome/browser/profiles/profile.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
 #include "components/policy/core/common/policy_service.h"
@@ -34,22 +35,24 @@
 }
 
 // static
-ProfilePolicyConnector* ProfilePolicyConnectorFactory::GetForProfile(
-    Profile* profile) {
-  return GetInstance()->GetForProfileInternal(profile);
+ProfilePolicyConnector* ProfilePolicyConnectorFactory::GetForBrowserContext(
+    content::BrowserContext* context) {
+  return GetInstance()->GetForBrowserContextInternal(context);
 }
 
 // static
 scoped_ptr<ProfilePolicyConnector>
-ProfilePolicyConnectorFactory::CreateForProfile(Profile* profile,
-                                                bool force_immediate_load) {
-  return GetInstance()->CreateForProfileInternal(profile, force_immediate_load);
+ProfilePolicyConnectorFactory::CreateForBrowserContext(
+    content::BrowserContext* context,
+    bool force_immediate_load) {
+  return GetInstance()->CreateForBrowserContextInternal(context,
+                                                        force_immediate_load);
 }
 
 void ProfilePolicyConnectorFactory::SetServiceForTesting(
-    Profile* profile,
+    content::BrowserContext* context,
     ProfilePolicyConnector* connector) {
-  ProfilePolicyConnector*& map_entry = connectors_[profile];
+  ProfilePolicyConnector*& map_entry = connectors_[context];
   CHECK(!map_entry);
   map_entry = connector;
 }
@@ -80,30 +83,33 @@
 }
 
 ProfilePolicyConnector*
-    ProfilePolicyConnectorFactory::GetForProfileInternal(Profile* profile) {
+ProfilePolicyConnectorFactory::GetForBrowserContextInternal(
+    content::BrowserContext* context) {
   // Get the connector for the original Profile, so that the incognito Profile
   // gets managed settings from the same PolicyService.
-  ConnectorMap::const_iterator it =
-      connectors_.find(profile->GetOriginalProfile());
+  content::BrowserContext* const original_context =
+      chrome::GetBrowserContextRedirectedInIncognito(context);
+  const ConnectorMap::const_iterator it = connectors_.find(original_context);
   CHECK(it != connectors_.end());
   return it->second;
 }
 
 scoped_ptr<ProfilePolicyConnector>
-ProfilePolicyConnectorFactory::CreateForProfileInternal(
-    Profile* profile,
+ProfilePolicyConnectorFactory::CreateForBrowserContextInternal(
+    content::BrowserContext* context,
     bool force_immediate_load) {
-  DCHECK(connectors_.find(profile) == connectors_.end());
+  DCHECK(connectors_.find(context) == connectors_.end());
 
   SchemaRegistry* schema_registry = NULL;
   CloudPolicyManager* user_cloud_policy_manager = NULL;
 
 #if defined(ENABLE_CONFIGURATION_POLICY)
   schema_registry =
-      SchemaRegistryServiceFactory::GetForContext(profile)->registry();
+      SchemaRegistryServiceFactory::GetForContext(context)->registry();
 
 #if defined(OS_CHROMEOS)
-  user_manager::User* user = NULL;
+  Profile* const profile = Profile::FromBrowserContext(context);
+  const user_manager::User* user = NULL;
   if (!chromeos::ProfileHelper::IsSigninProfile(profile)) {
     user = chromeos::ProfileHelper::Get()->GetUserByProfile(profile);
     CHECK(user);
@@ -112,11 +118,11 @@
       UserCloudPolicyManagerFactoryChromeOS::GetForProfile(profile);
 #else
   user_cloud_policy_manager =
-      UserCloudPolicyManagerFactory::GetForBrowserContext(profile);
+      UserCloudPolicyManagerFactory::GetForBrowserContext(context);
 #endif  // defined(OS_CHROMEOS)
 #endif  // defined(ENABLE_CONFIGURATION_POLICY)
 
-  ProfilePolicyConnector* connector = new ProfilePolicyConnector();
+  scoped_ptr<ProfilePolicyConnector> connector(new ProfilePolicyConnector());
 
 #if defined(ENABLE_CONFIGURATION_POLICY)
   if (test_providers_.empty()) {
@@ -137,23 +143,22 @@
   connector->Init(false, NULL, NULL);
 #endif
 
-  connectors_[profile] = connector;
-  return make_scoped_ptr(connector);
+  connectors_[context] = connector.get();
+  return connector.Pass();
 }
 
 void ProfilePolicyConnectorFactory::BrowserContextShutdown(
     content::BrowserContext* context) {
-  Profile* profile = static_cast<Profile*>(context);
-  if (profile->IsOffTheRecord())
+  if (Profile::FromBrowserContext(context)->IsOffTheRecord())
     return;
-  ConnectorMap::iterator it = connectors_.find(profile);
+  const ConnectorMap::const_iterator it = connectors_.find(context);
   if (it != connectors_.end())
     it->second->Shutdown();
 }
 
 void ProfilePolicyConnectorFactory::BrowserContextDestroyed(
     content::BrowserContext* context) {
-  ConnectorMap::iterator it = connectors_.find(static_cast<Profile*>(context));
+  const ConnectorMap::iterator it = connectors_.find(context);
   if (it != connectors_.end())
     connectors_.erase(it);
   BrowserContextKeyedBaseFactory::BrowserContextDestroyed(context);
diff --git a/chrome/browser/policy/profile_policy_connector_factory.h b/chrome/browser/policy/profile_policy_connector_factory.h
index f11c3de..1080a4c0 100644
--- a/chrome/browser/policy/profile_policy_connector_factory.h
+++ b/chrome/browser/policy/profile_policy_connector_factory.h
@@ -15,8 +15,6 @@
 template <typename T>
 struct DefaultSingletonTraits;
 
-class Profile;
-
 namespace base {
 class SequencedTaskRunner;
 }
@@ -32,33 +30,34 @@
 
 // Creates ProfilePolicyConnectors for Profiles, which manage the common
 // policy providers and other policy components.
-// TODO(joaodasilva): convert this class to a proper PKS once the PrefService,
-// which depends on this class, becomes a PKS too.
+// TODO(joaodasilva): convert this class to a proper BCKS once the PrefService,
+// which depends on this class, becomes a BCKS too.
 class ProfilePolicyConnectorFactory : public BrowserContextKeyedBaseFactory {
  public:
   // Returns the ProfilePolicyConnectorFactory singleton.
   static ProfilePolicyConnectorFactory* GetInstance();
 
-  // Returns the ProfilePolicyConnector associated with |profile|. This is only
-  // valid before |profile| is shut down.
-  static ProfilePolicyConnector* GetForProfile(Profile* profile);
+  // Returns the ProfilePolicyConnector associated with |context|. This is only
+  // valid before |context| is shut down.
+  static ProfilePolicyConnector* GetForBrowserContext(
+      content::BrowserContext* context);
 
-  // Creates a new ProfilePolicyConnector for |profile|, which must be managed
-  // by the caller. Subsequent calls to GetForProfile() will return the instance
-  // created, as long as it lives.
+  // Creates a new ProfilePolicyConnector for |context|, which must be managed
+  // by the caller. Subsequent calls to GetForBrowserContext() will return the
+  // instance created, as long as it lives.
   // If |force_immediate_load| is true then policy is loaded synchronously on
   // startup.
-  static scoped_ptr<ProfilePolicyConnector> CreateForProfile(
-      Profile* profile,
+  static scoped_ptr<ProfilePolicyConnector> CreateForBrowserContext(
+      content::BrowserContext* context,
       bool force_immediate_load);
 
-  // Overrides the |connector| for the given |profile|; use only in tests.
-  // Once this class becomes a proper PKS then it can reuse the testing
+  // Overrides the |connector| for the given |context|; use only in tests.
+  // Once this class becomes a proper BCKS then it can reuse the testing
   // methods of BrowserContextKeyedServiceFactory.
-  void SetServiceForTesting(Profile* profile,
+  void SetServiceForTesting(content::BrowserContext* context,
                             ProfilePolicyConnector* connector);
 
-  // The next Profile to call CreateForProfile() will get a PolicyService
+  // The next Profile to call CreateForBrowserContext() will get a PolicyService
   // with |provider| as its sole policy provider. This can be called multiple
   // times to override the policy providers for more than 1 Profile.
   void PushProviderForTesting(ConfigurationPolicyProvider* provider);
@@ -69,10 +68,11 @@
   ProfilePolicyConnectorFactory();
   ~ProfilePolicyConnectorFactory() override;
 
-  ProfilePolicyConnector* GetForProfileInternal(Profile* profile);
+  ProfilePolicyConnector* GetForBrowserContextInternal(
+      content::BrowserContext* context);
 
-  scoped_ptr<ProfilePolicyConnector> CreateForProfileInternal(
-      Profile* profile,
+  scoped_ptr<ProfilePolicyConnector> CreateForBrowserContextInternal(
+      content::BrowserContext* context,
       bool force_immediate_load);
 
   // BrowserContextKeyedBaseFactory:
@@ -82,7 +82,8 @@
   bool HasTestingFactory(content::BrowserContext* context) override;
   void CreateServiceNow(content::BrowserContext* context) override;
 
-  typedef std::map<Profile*, ProfilePolicyConnector*> ConnectorMap;
+  typedef std::map<content::BrowserContext*, ProfilePolicyConnector*>
+      ConnectorMap;
   ConnectorMap connectors_;
   std::list<ConfigurationPolicyProvider*> test_providers_;
 
diff --git a/chrome/browser/policy/schema_registry_service_factory.cc b/chrome/browser/policy/schema_registry_service_factory.cc
index a2997abb..58930228 100644
--- a/chrome/browser/policy/schema_registry_service_factory.cc
+++ b/chrome/browser/policy/schema_registry_service_factory.cc
@@ -38,7 +38,7 @@
     return NULL;
   }
 
-  user_manager::User* user =
+  const user_manager::User* user =
       chromeos::ProfileHelper::Get()->GetUserByProfile(profile);
   if (!user)
     return NULL;
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc
index 635f01e..e733f82 100644
--- a/chrome/browser/prefs/browser_prefs.cc
+++ b/chrome/browser/prefs/browser_prefs.cc
@@ -6,7 +6,7 @@
 
 #include <string>
 
-#include "base/metrics/histogram.h"
+#include "base/metrics/histogram_macros.h"
 #include "base/prefs/pref_registry_simple.h"
 #include "base/prefs/pref_service.h"
 #include "base/prefs/scoped_user_pref_update.h"
@@ -200,6 +200,10 @@
 #include "chrome/browser/extensions/default_apps.h"
 #endif
 
+#if defined(OS_CHROMEOS) && defined(ENABLE_APP_LIST)
+#include "chrome/browser/ui/app_list/google_now_extension.h"
+#endif
+
 #if defined(OS_MACOSX)
 #include "chrome/browser/ui/cocoa/apps/quit_with_apps_controller_mac.h"
 #include "chrome/browser/ui/cocoa/confirm_quit.h"
@@ -379,7 +383,8 @@
 
 // Register prefs applicable to all profiles.
 void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry) {
-  TRACE_EVENT0("browser", "chrome::RegisterUserPrefs");
+  TRACE_EVENT0("browser", "chrome::RegisterProfilePrefs");
+  SCOPED_UMA_HISTOGRAM_TIMER("Settings.RegisterProfilePrefsTime");
   // User prefs. Please keep this list alphabetized.
   autofill::AutofillManager::RegisterProfilePrefs(registry);
   bookmarks::RegisterProfilePrefs(registry);
@@ -574,6 +579,10 @@
 #if defined(OS_MACOSX) && !defined(OS_IOS)
   autofill::AutofillManager::MigrateUserPrefs(prefs);
 #endif  // defined(OS_MACOSX) && !defined(OS_IOS)
+
+#if defined(OS_CHROMEOS) && defined(ENABLE_APP_LIST)
+  MigrateGoogleNowPrefs(profile);
+#endif
 }
 
 void MigrateBrowserPrefs(Profile* profile, PrefService* local_state) {
diff --git a/chrome/browser/prefs/chrome_pref_service_factory.cc b/chrome/browser/prefs/chrome_pref_service_factory.cc
index 0deb832..6b7fd8a 100644
--- a/chrome/browser/prefs/chrome_pref_service_factory.cc
+++ b/chrome/browser/prefs/chrome_pref_service_factory.cc
@@ -11,7 +11,7 @@
 #include "base/compiler_specific.h"
 #include "base/files/file_path.h"
 #include "base/metrics/field_trial.h"
-#include "base/metrics/histogram.h"
+#include "base/metrics/histogram_macros.h"
 #include "base/prefs/default_pref_store.h"
 #include "base/prefs/json_pref_store.h"
 #include "base/prefs/pref_filter.h"
@@ -501,6 +501,7 @@
     const scoped_refptr<user_prefs::PrefRegistrySyncable>& pref_registry,
     bool async) {
   TRACE_EVENT0("browser", "chrome_prefs::CreateProfilePrefs");
+  SCOPED_UMA_HISTOGRAM_TIMER("PrefService.CreateProfilePrefsTime");
 
   // A StartSyncFlare used to kick sync early in case of a reset event. This is
   // done since sync may bring back the user's server value post-reset which
diff --git a/chrome/browser/prerender/prerender_message_filter.cc b/chrome/browser/prerender/prerender_message_filter.cc
index 14b6beb..4a306d8 100644
--- a/chrome/browser/prerender/prerender_message_filter.cc
+++ b/chrome/browser/prerender/prerender_message_filter.cc
@@ -5,42 +5,62 @@
 #include "chrome/browser/prerender/prerender_message_filter.h"
 
 #include "base/bind.h"
-#include "chrome/browser/browser_process.h"
+#include "base/memory/singleton.h"
 #include "chrome/browser/prerender/prerender_link_manager.h"
 #include "chrome/browser/prerender/prerender_link_manager_factory.h"
-#include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/profiles/profile.h"
 #include "chrome/common/prerender_messages.h"
+#include "components/keyed_service/content/browser_context_keyed_service_shutdown_notifier_factory.h"
 #include "content/public/browser/browser_thread.h"
 
 using content::BrowserThread;
 
+namespace prerender {
+
 namespace {
 
-void OnChannelClosingInUIThread(Profile* profile, int render_process_id) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-  if (!g_browser_process->profile_manager()->IsValidProfile(profile))
-    return;
-  prerender::PrerenderLinkManager* prerender_link_manager =
-      prerender::PrerenderLinkManagerFactory::GetForProfile(profile);
-  if (!prerender_link_manager)
-    return;
-  prerender_link_manager->OnChannelClosing(render_process_id);
-}
+class ShutdownNotifierFactory
+    : public BrowserContextKeyedServiceShutdownNotifierFactory {
+ public:
+  static ShutdownNotifierFactory* GetInstance() {
+    return Singleton<ShutdownNotifierFactory>::get();
+  }
+
+ private:
+  friend struct DefaultSingletonTraits<ShutdownNotifierFactory>;
+
+  ShutdownNotifierFactory()
+      : BrowserContextKeyedServiceShutdownNotifierFactory(
+            "PrerenderMessageFilter") {
+    DependsOn(PrerenderLinkManagerFactory::GetInstance());
+  }
+  ~ShutdownNotifierFactory() override {}
+
+  DISALLOW_COPY_AND_ASSIGN(ShutdownNotifierFactory);
+};
 
 }  // namespace
 
-namespace prerender {
-
 PrerenderMessageFilter::PrerenderMessageFilter(int render_process_id,
                                                Profile* profile)
     : BrowserMessageFilter(PrerenderMsgStart),
       render_process_id_(render_process_id),
-      profile_(profile) {
+      prerender_link_manager_(
+          PrerenderLinkManagerFactory::GetForProfile(profile)) {
+  shutdown_notifier_ =
+      ShutdownNotifierFactory::GetInstance()->Get(profile)->Subscribe(
+          base::Bind(&PrerenderMessageFilter::ShutdownOnUIThread,
+                     base::Unretained(this)));
 }
 
 PrerenderMessageFilter::~PrerenderMessageFilter() {
 }
 
+// static
+void PrerenderMessageFilter::EnsureShutdownNotifierFactoryBuilt() {
+  ShutdownNotifierFactory::GetInstance();
+}
+
 bool PrerenderMessageFilter::OnMessageReceived(const IPC::Message& message) {
   bool handled = true;
   IPC_BEGIN_MESSAGE_MAP(PrerenderMessageFilter, message)
@@ -67,7 +87,7 @@
 void PrerenderMessageFilter::OnChannelClosing() {
   BrowserThread::PostTask(
       BrowserThread::UI, FROM_HERE,
-      base::Bind(&OnChannelClosingInUIThread, profile_, render_process_id_));
+      base::Bind(&PrerenderMessageFilter::OnChannelClosingInUIThread, this));
 }
 
 void PrerenderMessageFilter::OnAddPrerender(
@@ -77,11 +97,9 @@
     const gfx::Size& size,
     int render_view_route_id) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-  PrerenderLinkManager* prerender_link_manager =
-      PrerenderLinkManagerFactory::GetForProfile(profile_);
-  if (!prerender_link_manager)
+  if (!prerender_link_manager_)
     return;
-  prerender_link_manager->OnAddPrerender(
+  prerender_link_manager_->OnAddPrerender(
       render_process_id_, prerender_id,
       attributes.url, attributes.rel_types, referrer,
       size, render_view_route_id);
@@ -90,21 +108,29 @@
 void PrerenderMessageFilter::OnCancelPrerender(
     int prerender_id) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-  PrerenderLinkManager* prerender_link_manager =
-      PrerenderLinkManagerFactory::GetForProfile(profile_);
-  if (!prerender_link_manager)
+  if (!prerender_link_manager_)
     return;
-  prerender_link_manager->OnCancelPrerender(render_process_id_, prerender_id);
+  prerender_link_manager_->OnCancelPrerender(render_process_id_, prerender_id);
 }
 
 void PrerenderMessageFilter::OnAbandonPrerender(
     int prerender_id) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-  PrerenderLinkManager* prerender_link_manager =
-      PrerenderLinkManagerFactory::GetForProfile(profile_);
-  if (!prerender_link_manager)
+  if (!prerender_link_manager_)
     return;
-  prerender_link_manager->OnAbandonPrerender(render_process_id_, prerender_id);
+  prerender_link_manager_->OnAbandonPrerender(render_process_id_, prerender_id);
+}
+
+void PrerenderMessageFilter::ShutdownOnUIThread() {
+  prerender_link_manager_ = nullptr;
+  shutdown_notifier_.reset();
+}
+
+void PrerenderMessageFilter::OnChannelClosingInUIThread() {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  if (!prerender_link_manager_)
+    return;
+  prerender_link_manager_->OnChannelClosing(render_process_id_);
 }
 
 }  // namespace prerender
diff --git a/chrome/browser/prerender/prerender_message_filter.h b/chrome/browser/prerender/prerender_message_filter.h
index 2f96b46..596a30a5 100644
--- a/chrome/browser/prerender/prerender_message_filter.h
+++ b/chrome/browser/prerender/prerender_message_filter.h
@@ -6,6 +6,7 @@
 #define CHROME_BROWSER_PRERENDER_PRERENDER_MESSAGE_FILTER_H_
 
 #include "base/compiler_specific.h"
+#include "components/keyed_service/core/keyed_service_shutdown_notifier.h"
 #include "content/public/browser/browser_message_filter.h"
 #include "url/gurl.h"
 
@@ -26,10 +27,14 @@
 
 namespace prerender {
 
+class PrerenderLinkManager;
+
 class PrerenderMessageFilter : public content::BrowserMessageFilter {
  public:
   PrerenderMessageFilter(int render_process_id, Profile* profile);
 
+  static void EnsureShutdownNotifierFactoryBuilt();
+
  private:
   ~PrerenderMessageFilter() override;
 
@@ -47,8 +52,15 @@
   void OnCancelPrerender(int prerender_id);
   void OnAbandonPrerender(int prerender_id);
 
+  void ShutdownOnUIThread();
+
+  void OnChannelClosingInUIThread();
+
   const int render_process_id_;
-  Profile* const profile_;
+
+  PrerenderLinkManager* prerender_link_manager_;
+
+  scoped_ptr<KeyedServiceShutdownNotifier::Subscription> shutdown_notifier_;
 
   DISALLOW_COPY_AND_ASSIGN(PrerenderMessageFilter);
 };
diff --git a/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc b/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
index 9d8faee7..9271d9e3 100644
--- a/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
+++ b/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
@@ -30,6 +30,7 @@
 #include "chrome/browser/predictors/resource_prefetch_predictor_factory.h"
 #include "chrome/browser/prerender/prerender_link_manager_factory.h"
 #include "chrome/browser/prerender/prerender_manager_factory.h"
+#include "chrome/browser/prerender/prerender_message_filter.h"
 #include "chrome/browser/printing/cloud_print/cloud_print_proxy_service_factory.h"
 #include "chrome/browser/profiles/gaia_info_update_service_factory.h"
 #include "chrome/browser/search/instant_service_factory.h"
@@ -261,8 +262,9 @@
   predictors::AutocompleteActionPredictorFactory::GetInstance();
   predictors::PredictorDatabaseFactory::GetInstance();
   predictors::ResourcePrefetchPredictorFactory::GetInstance();
-  prerender::PrerenderManagerFactory::GetInstance();
   prerender::PrerenderLinkManagerFactory::GetInstance();
+  prerender::PrerenderManagerFactory::GetInstance();
+  prerender::PrerenderMessageFilter::EnsureShutdownNotifierFactoryBuilt();
   ProfileSyncServiceFactory::GetInstance();
   ProtocolHandlerRegistryFactory::GetInstance();
 #if defined(ENABLE_SESSION_SERVICE)
diff --git a/chrome/browser/profiles/off_the_record_profile_impl.cc b/chrome/browser/profiles/off_the_record_profile_impl.cc
index 1cd6531d..e998faf 100644
--- a/chrome/browser/profiles/off_the_record_profile_impl.cc
+++ b/chrome/browser/profiles/off_the_record_profile_impl.cc
@@ -225,7 +225,7 @@
                      base::Unretained(this)));
 }
 
-std::string OffTheRecordProfileImpl::GetProfileUserName() {
+std::string OffTheRecordProfileImpl::GetProfileUserName() const {
   // Incognito profile should not return the username.
   return std::string();
 }
@@ -298,6 +298,10 @@
   return prefs_;
 }
 
+const PrefService* OffTheRecordProfileImpl::GetPrefs() const {
+  return prefs_;
+}
+
 PrefService* OffTheRecordProfileImpl::GetOffTheRecordPrefs() {
   return prefs_;
 }
diff --git a/chrome/browser/profiles/off_the_record_profile_impl.h b/chrome/browser/profiles/off_the_record_profile_impl.h
index a60358c..db86507 100644
--- a/chrome/browser/profiles/off_the_record_profile_impl.h
+++ b/chrome/browser/profiles/off_the_record_profile_impl.h
@@ -37,7 +37,7 @@
   void Init();
 
   // Profile implementation.
-  std::string GetProfileUserName() override;
+  std::string GetProfileUserName() const override;
   ProfileType GetProfileType() const override;
   Profile* GetOffTheRecordProfile() override;
   void DestroyOffTheRecordProfile() override;
@@ -48,6 +48,7 @@
   bool IsLegacySupervised() override;
   ExtensionSpecialStoragePolicy* GetExtensionSpecialStoragePolicy() override;
   PrefService* GetPrefs() override;
+  const PrefService* GetPrefs() const override;
   PrefService* GetOffTheRecordPrefs() override;
   net::URLRequestContextGetter* GetRequestContextForExtensions() override;
   net::URLRequestContextGetter* CreateRequestContext(
diff --git a/chrome/browser/profiles/profile.cc b/chrome/browser/profiles/profile.cc
index 7292b30c..efa3109 100644
--- a/chrome/browser/profiles/profile.cc
+++ b/chrome/browser/profiles/profile.cc
@@ -113,6 +113,13 @@
       false,
       user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
 #endif
+  // This pref is intentionally outside the above #if. That flag corresponds
+  // to the Notifier extension and does not gate the launcher page.
+  // TODO(skare): Remove or rename ENABLE_GOOGLE_NOW: http://crbug.com/459827.
+  registry->RegisterBooleanPref(
+      prefs::kGoogleNowLauncherEnabled,
+      true,
+      user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
   registry->RegisterBooleanPref(
       prefs::kDisableExtensions,
       false,
diff --git a/chrome/browser/profiles/profile.h b/chrome/browser/profiles/profile.h
index 3faa74c..c3b68a1 100644
--- a/chrome/browser/profiles/profile.h
+++ b/chrome/browser/profiles/profile.h
@@ -153,7 +153,7 @@
 
   // Returns the username associated with this profile, if any. In non-test
   // implementations, this is usually the Google-services email address.
-  virtual std::string GetProfileUserName() = 0;
+  virtual std::string GetProfileUserName() const = 0;
 
   // Returns the profile type.
   virtual ProfileType GetProfileType() const = 0;
@@ -192,6 +192,7 @@
   // Retrieves a pointer to the PrefService that manages the
   // preferences for this user profile.
   virtual PrefService* GetPrefs() = 0;
+  virtual const PrefService* GetPrefs() const = 0;
 
   // Retrieves a pointer to the PrefService that manages the default zoom
   // level and the per-host zoom levels for this user profile.
diff --git a/chrome/browser/profiles/profile_impl.cc b/chrome/browser/profiles/profile_impl.cc
index 4fcb7e47..9b7389f 100644
--- a/chrome/browser/profiles/profile_impl.cc
+++ b/chrome/browser/profiles/profile_impl.cc
@@ -15,6 +15,7 @@
 #include "base/files/file_util.h"
 #include "base/memory/scoped_ptr.h"
 #include "base/memory/weak_ptr.h"
+#include "base/metrics/histogram_macros.h"
 #include "base/path_service.h"
 #include "base/prefs/json_pref_store.h"
 #include "base/prefs/scoped_user_pref_update.h"
@@ -266,10 +267,10 @@
 Profile* Profile::CreateProfile(const base::FilePath& path,
                                 Delegate* delegate,
                                 CreateMode create_mode) {
-  TRACE_EVENT_BEGIN1("browser",
-                     "Profile::CreateProfile",
-                     "profile_path",
-                     path.value().c_str());
+  TRACE_EVENT1("browser,startup",
+               "Profile::CreateProfile",
+               "profile_path",
+               path.AsUTF8Unsafe());
 
   // Get sequenced task runner for making sure that file operations of
   // this profile (defined by |path|) are executed in expected order
@@ -282,9 +283,9 @@
     CreateProfileDirectory(sequenced_task_runner.get(), path);
   } else if (create_mode == CREATE_MODE_SYNCHRONOUS) {
     if (!base::PathExists(path)) {
-      // TODO(tc): http://b/1094718 Bad things happen if we can't write to the
-      // profile directory.  We should eventually be able to run in this
-      // situation.
+      // TODO(rogerta): http://crbug/160553 - Bad things happen if we can't
+      // write to the profile directory.  We should eventually be able to run in
+      // this situation.
       if (!base::CreateDirectory(path))
         return NULL;
     }
@@ -411,7 +412,7 @@
       start_time_(Time::Now()),
       delegate_(delegate),
       predictor_(NULL) {
-  TRACE_EVENT0("browser", "ProfileImpl::ctor")
+  TRACE_EVENT0("browser,startup", "ProfileImpl::ctor")
   DCHECK(!path.empty()) << "Using an empty path will attempt to write " <<
                             "profile files to the root directory!";
 
@@ -457,7 +458,7 @@
 #endif
 #endif
   profile_policy_connector_ =
-      policy::ProfilePolicyConnectorFactory::CreateForProfile(
+      policy::ProfilePolicyConnectorFactory::CreateForBrowserContext(
           this, force_immediate_policy_load);
 
   DCHECK(create_mode == CREATE_MODE_ASYNCHRONOUS ||
@@ -666,12 +667,6 @@
   // as a URLDataSource early.
   RegisterDomDistillerViewerSource(this);
 
-  // Creation has been finished.
-  TRACE_EVENT_END1("browser",
-                   "Profile::CreateProfile",
-                   "profile_path",
-                   path_.value().c_str());
-
 #if defined(OS_CHROMEOS)
   if (chromeos::UserSessionManager::GetInstance()
           ->RestartToApplyPerSessionFlagsIfNeed(this, true)) {
@@ -754,9 +749,9 @@
     SetExitType(EXIT_NORMAL);
 }
 
-std::string ProfileImpl::GetProfileUserName() {
-  SigninManagerBase* signin_manager =
-      SigninManagerFactory::GetForProfile(this);
+std::string ProfileImpl::GetProfileUserName() const {
+  const SigninManagerBase* signin_manager =
+      SigninManagerFactory::GetForProfileIfExists(this);
   if (signin_manager)
     return signin_manager->GetAuthenticatedUsername();
 
@@ -848,7 +843,8 @@
 }
 
 void ProfileImpl::OnPrefsLoaded(bool success) {
-  TRACE_EVENT0("browser", "ProfileImpl::OnPrefsLoaded")
+  TRACE_EVENT0("browser", "ProfileImpl::OnPrefsLoaded");
+  SCOPED_UMA_HISTOGRAM_TIMER("Profile.OnPrefsLoadedTime");
   if (!success) {
     if (delegate_)
       delegate_->OnProfileCreated(this, false, false);
@@ -939,6 +935,11 @@
 }
 
 PrefService* ProfileImpl::GetPrefs() {
+  return const_cast<PrefService*>(
+      static_cast<const ProfileImpl*>(this)->GetPrefs());
+}
+
+const PrefService* ProfileImpl::GetPrefs() const {
   DCHECK(prefs_);  // Should explicitly be initialized.
   return prefs_.get();
 }
diff --git a/chrome/browser/profiles/profile_impl.h b/chrome/browser/profiles/profile_impl.h
index 8a27655c..c66f140 100644
--- a/chrome/browser/profiles/profile_impl.h
+++ b/chrome/browser/profiles/profile_impl.h
@@ -93,7 +93,7 @@
   scoped_refptr<base::SequencedTaskRunner> GetIOTaskRunner() override;
   // Note that this implementation returns the Google-services username, if any,
   // not the Chrome user's display name.
-  std::string GetProfileUserName() override;
+  std::string GetProfileUserName() const override;
   ProfileType GetProfileType() const override;
   bool IsOffTheRecord() const override;
   Profile* GetOffTheRecordProfile() override;
@@ -105,6 +105,7 @@
   bool IsLegacySupervised() override;
   ExtensionSpecialStoragePolicy* GetExtensionSpecialStoragePolicy() override;
   PrefService* GetPrefs() override;
+  const PrefService* GetPrefs() const override;
   chrome::ChromeZoomLevelPrefs* GetZoomLevelPrefs() override;
   PrefService* GetOffTheRecordPrefs() override;
   net::URLRequestContextGetter* GetRequestContextForExtensions() override;
diff --git a/chrome/browser/profiles/profile_impl_io_data.cc b/chrome/browser/profiles/profile_impl_io_data.cc
index c6522a8..4935556 100644
--- a/chrome/browser/profiles/profile_impl_io_data.cc
+++ b/chrome/browser/profiles/profile_impl_io_data.cc
@@ -38,13 +38,8 @@
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/common/url_constants.h"
-#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_configurator.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.h"
-#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_prefs.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.h"
-#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_statistics_prefs.h"
-#include "components/data_reduction_proxy/core/common/data_reduction_proxy_params.h"
-#include "components/data_reduction_proxy/core/common/data_reduction_proxy_pref_names.h"
 #include "components/domain_reliability/monitor.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/cookie_store_factory.h"
@@ -170,20 +165,24 @@
   if (io_data_->domain_reliability_monitor_)
     io_data_->domain_reliability_monitor_->MoveToNetworkThread();
 
-  ChromeNetLog* const net_log = g_browser_process->io_thread()->net_log();
+  // TODO(tbansal): Move this to IO thread once the data reduction proxy
+  // params are unified into a single object.
+  bool enable_quic_for_data_reduction_proxy =
+      IOThread::ShouldEnableQuicForDataReductionProxy();
 
   io_data_->set_data_reduction_proxy_io_data(
       CreateDataReductionProxyChromeIOData(
-          net_log, profile_, profile_->GetPrefs(),
+          g_browser_process->io_thread()->net_log(), profile_->GetPrefs(),
           BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO),
-          BrowserThread::GetMessageLoopProxyForThread(
-              BrowserThread::UI)).Pass());
+          BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI),
+          enable_quic_for_data_reduction_proxy)
+          .Pass());
 
   DataReductionProxyChromeSettingsFactory::GetForBrowserContext(profile_)->
-      InitDataReductionProxySettings(io_data_->data_reduction_proxy_io_data(),
-                                     profile_->GetPrefs(),
-                                     g_browser_process->local_state(),
-                                     profile_->GetRequestContext());
+      InitDataReductionProxySettings(
+          io_data_->data_reduction_proxy_io_data(), profile_->GetPrefs(),
+          profile_->GetRequestContext(),
+          BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI));
 }
 
 content::ResourceContext*
@@ -459,8 +458,6 @@
 
   main_context->set_net_log(io_thread->net_log());
 
-  data_reduction_proxy_io_data()->Init();
-
   network_delegate_ = data_reduction_proxy_io_data()->CreateNetworkDelegate(
       chrome_network_delegate.Pass(), true).Pass();
 
diff --git a/chrome/browser/profiles/profile_io_data.cc b/chrome/browser/profiles/profile_io_data.cc
index 5443b01..eb7280c 100644
--- a/chrome/browser/profiles/profile_io_data.cc
+++ b/chrome/browser/profiles/profile_io_data.cc
@@ -419,7 +419,7 @@
 #if defined(OS_CHROMEOS)
   user_manager::UserManager* user_manager = user_manager::UserManager::Get();
   if (user_manager) {
-    user_manager::User* user =
+    const user_manager::User* user =
         chromeos::ProfileHelper::Get()->GetUserByProfile(profile);
     // No need to initialize NSS for users with empty username hash:
     // Getters for a user's NSS slots always return NULL slot if the user's
diff --git a/chrome/browser/profiles/profile_manager.cc b/chrome/browser/profiles/profile_manager.cc
index 7811585..b524dc3 100644
--- a/chrome/browser/profiles/profile_manager.cc
+++ b/chrome/browser/profiles/profile_manager.cc
@@ -374,21 +374,13 @@
 
 Profile* ProfileManager::GetProfile(const base::FilePath& profile_dir) {
   TRACE_EVENT0("browser", "ProfileManager::GetProfile");
-  SCOPED_UMA_HISTOGRAM_TIMER("Profile.GetProfile");
 
   // If the profile is already loaded (e.g., chrome.exe launched twice), just
   // return it.
   Profile* profile = GetProfileByPath(profile_dir);
-  if (NULL != profile)
+  if (profile)
     return profile;
-
-  profile = CreateProfileHelper(profile_dir);
-  DCHECK(profile);
-  if (profile) {
-    bool result = AddProfile(profile);
-    DCHECK(result);
-  }
-  return profile;
+  return CreateAndInitializeProfile(profile_dir);
 }
 
 size_t ProfileManager::GetNumberOfProfiles() {
@@ -402,10 +394,10 @@
     const base::string16& icon_url,
     const std::string& supervised_user_id) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-  TRACE_EVENT1("startup",
+  TRACE_EVENT1("browser,startup",
                "ProfileManager::CreateProfileAsync",
                "profile_path",
-               profile_path.value().c_str());
+               profile_path.AsUTF8Unsafe());
 
   // Make sure that this profile is not pending deletion.
   if (IsProfileMarkedForDeletion(profile_path)) {
@@ -560,10 +552,18 @@
   return profiles;
 }
 
+Profile* ProfileManager::GetProfileByPathInternal(
+    const base::FilePath& path) const {
+  TRACE_EVENT0("browser", "ProfileManager::GetProfileByPathInternal");
+  ProfileInfo* profile_info = GetProfileInfoByPath(path);
+  return profile_info ? profile_info->profile.get() : nullptr;
+}
+
 Profile* ProfileManager::GetProfileByPath(const base::FilePath& path) const {
   TRACE_EVENT0("browser", "ProfileManager::GetProfileByPath");
   ProfileInfo* profile_info = GetProfileInfoByPath(path);
-  return profile_info ? profile_info->profile.get() : NULL;
+  return profile_info && profile_info->created ? profile_info->profile.get()
+                                               : nullptr;
 }
 
 // static
@@ -865,7 +865,7 @@
       // Confirm that we hadn't loaded the new profile previously.
       base::FilePath default_profile_dir = user_data_dir_.Append(
           GetInitialProfileDir());
-      CHECK(!GetProfileByPath(default_profile_dir))
+      CHECK(!GetProfileByPathInternal(default_profile_dir))
           << "The default profile was loaded before we mounted the cryptohome.";
     }
     return;
@@ -1076,6 +1076,8 @@
 
 Profile* ProfileManager::CreateProfileHelper(const base::FilePath& path) {
   TRACE_EVENT0("browser", "ProfileManager::CreateProfileHelper");
+  SCOPED_UMA_HISTOGRAM_TIMER("Profile.CreateProfileHelperTime");
+
   return Profile::CreateProfile(path, NULL, Profile::CREATE_MODE_SYNCHRONOUS);
 }
 
@@ -1129,7 +1131,7 @@
 
   // Make sure that we're not loading a profile with the same ID as a profile
   // that's already loaded.
-  if (GetProfileByPath(profile->GetPath())) {
+  if (GetProfileByPathInternal(profile->GetPath())) {
     NOTREACHED() << "Attempted to add profile with the same path (" <<
                     profile->GetPath().value() <<
                     ") as an already-loaded profile.";
@@ -1142,6 +1144,25 @@
   return true;
 }
 
+Profile* ProfileManager::CreateAndInitializeProfile(
+    const base::FilePath& profile_dir) {
+  TRACE_EVENT0("browser", "ProfileManager::CreateAndInitializeProfile");
+  SCOPED_UMA_HISTOGRAM_LONG_TIMER("Profile.CreateAndInitializeProfile");
+
+  // CHECK that we are not trying to load the same profile twice, to prevent
+  // profile corruption. Note that this check also covers the case when we have
+  // already started loading the profile but it is not fully initialized yet,
+  // which would make Bad Things happen if we returned it.
+  CHECK(!GetProfileByPathInternal(profile_dir));
+  Profile* profile = CreateProfileHelper(profile_dir);
+  DCHECK(profile);
+  if (profile) {
+    bool result = AddProfile(profile);
+    DCHECK(result);
+  }
+  return profile;
+}
+
 void ProfileManager::FinishDeletingProfile(const base::FilePath& profile_dir) {
   ProfileInfoCache& cache = GetProfileInfoCache();
   // TODO(sail): Due to bug 88586 we don't delete the profile instance. Once we
diff --git a/chrome/browser/profiles/profile_manager.h b/chrome/browser/profiles/profile_manager.h
index c16b6df8..ff28e3d1 100644
--- a/chrome/browser/profiles/profile_manager.h
+++ b/chrome/browser/profiles/profile_manager.h
@@ -74,6 +74,11 @@
   // Returns a profile for a specific profile directory within the user data
   // dir. This will return an existing profile it had already been created,
   // otherwise it will create and manage it.
+  // Because this method might synchronously create a new profile, it should
+  // only be called for the initial profile or in tests, where blocking is
+  // acceptable.
+  // TODO(bauerb): Migrate calls from other code to GetProfileByPath(), then
+  // make this method private.
   Profile* GetProfile(const base::FilePath& profile_dir);
 
   // Returns total number of profiles available on this machine.
@@ -113,13 +118,13 @@
   std::vector<Profile*> GetLastOpenedProfiles(
       const base::FilePath& user_data_dir);
 
-  // Returns created profiles. Note, profiles order is NOT guaranteed to be
-  // related with the creation order.
+  // Returns created and fully initialized profiles. Note, profiles order is NOT
+  // guaranteed to be related with the creation order.
   std::vector<Profile*> GetLoadedProfiles() const;
 
-  // If a profile with the given path is currently managed by this object,
-  // return a pointer to the corresponding Profile object;
-  // otherwise return NULL.
+  // If a profile with the given path is currently managed by this object and
+  // fully initialized, return a pointer to the corresponding Profile object;
+  // otherwise return null.
   Profile* GetProfileByPath(const base::FilePath& path) const;
 
   // Creates a new profile in the next available multiprofile directory.
@@ -248,7 +253,7 @@
   // and we can't create it.
   // The profile used can be overridden by using --login-profile on cros.
   Profile* GetActiveUserOrOffTheRecordProfileFromPath(
-               const base::FilePath& user_data_dir);
+      const base::FilePath& user_data_dir);
 
   // Adds a pre-existing Profile object to the set managed by this
   // ProfileManager. This ProfileManager takes ownership of the Profile.
@@ -256,6 +261,10 @@
   // Returns true if the profile was added, false otherwise.
   bool AddProfile(Profile* profile);
 
+  // Synchronously creates and returns a profile. This handles both the full
+  // creation and adds it to the set managed by this ProfileManager.
+  Profile* CreateAndInitializeProfile(const base::FilePath& profile_dir);
+
   // Schedules the profile at the given path to be deleted on shutdown.
   void FinishDeletingProfile(const base::FilePath& profile_dir);
 
@@ -263,10 +272,15 @@
   // entry.
   ProfileInfo* RegisterProfile(Profile* profile, bool created);
 
-  // Returns ProfileInfo associated with given |path|, registred earlier with
+  // Returns ProfileInfo associated with given |path|, registered earlier with
   // RegisterProfile.
   ProfileInfo* GetProfileInfoByPath(const base::FilePath& path) const;
 
+  // Returns a registered profile. In contrast to GetProfileByPath(), this will
+  // also return a profile that is not fully initialized yet, so this method
+  // should be used carefully.
+  Profile* GetProfileByPathInternal(const base::FilePath& path) const;
+
   // Adds |profile| to the profile info cache if it hasn't been added yet.
   void AddProfileToCache(Profile* profile);
 
diff --git a/chrome/browser/profiles/profile_window.cc b/chrome/browser/profiles/profile_window.cc
index 98437d5..2473f1b 100644
--- a/chrome/browser/profiles/profile_window.cc
+++ b/chrome/browser/profiles/profile_window.cc
@@ -509,10 +509,7 @@
       *tutorial_mode = TUTORIAL_MODE_SHOW_ERROR;
       return;
     case BrowserWindow::AVATAR_BUBBLE_MODE_FAST_USER_SWITCH:
-      if (switches::IsFastUserSwitching())
-        *bubble_view_mode = profiles::BUBBLE_VIEW_MODE_FAST_PROFILE_CHOOSER;
-      else
-        *bubble_view_mode = profiles::BUBBLE_VIEW_MODE_PROFILE_CHOOSER;
+      *bubble_view_mode = profiles::BUBBLE_VIEW_MODE_FAST_PROFILE_CHOOSER;
       return;
     default:
       *bubble_view_mode = profiles::BUBBLE_VIEW_MODE_PROFILE_CHOOSER;
diff --git a/chrome/browser/renderer_context_menu/render_view_context_menu.cc b/chrome/browser/renderer_context_menu/render_view_context_menu.cc
index d8e0ccfb..af666b7 100644
--- a/chrome/browser/renderer_context_menu/render_view_context_menu.cc
+++ b/chrome/browser/renderer_context_menu/render_view_context_menu.cc
@@ -762,7 +762,8 @@
 }
 
 void RenderViewContextMenu::AppendPluginItems() {
-  if (params_.page_url == params_.src_url) {
+  if (params_.page_url == params_.src_url ||
+      extensions::GuestViewBase::IsGuest(source_web_contents_)) {
     // Full page plugin, so show page menu items.
     if (params_.link_url.is_empty() && params_.selection_text.empty())
       AppendPageItems();
@@ -1011,25 +1012,25 @@
       IncognitoModePrefs::GetAvailability(prefs);
   switch (id) {
     case IDC_BACK:
-      return source_web_contents_->GetController().CanGoBack();
+      return embedder_web_contents_->GetController().CanGoBack();
 
     case IDC_FORWARD:
-      return source_web_contents_->GetController().CanGoForward();
+      return embedder_web_contents_->GetController().CanGoForward();
 
     case IDC_RELOAD: {
       CoreTabHelper* core_tab_helper =
-          CoreTabHelper::FromWebContents(source_web_contents_);
+          CoreTabHelper::FromWebContents(embedder_web_contents_);
       if (!core_tab_helper)
         return false;
 
       CoreTabHelperDelegate* core_delegate = core_tab_helper->delegate();
       return !core_delegate ||
-             core_delegate->CanReloadContents(source_web_contents_);
+             core_delegate->CanReloadContents(embedder_web_contents_);
     }
 
     case IDC_VIEW_SOURCE:
     case IDC_CONTENT_CONTEXT_VIEWFRAMESOURCE:
-      return source_web_contents_->GetController().CanViewSource();
+      return embedder_web_contents_->GetController().CanViewSource();
 
     case IDC_CONTENT_CONTEXT_INSPECTELEMENT:
     case IDC_CONTENT_CONTEXT_INSPECTBACKGROUNDPAGE:
@@ -1038,16 +1039,16 @@
       return IsDevCommandEnabled(id);
 
     case IDC_CONTENT_CONTEXT_VIEWPAGEINFO:
-      if (source_web_contents_->GetController().GetVisibleEntry() == NULL)
+      if (embedder_web_contents_->GetController().GetVisibleEntry() == NULL)
         return false;
       // Disabled if no browser is associated (e.g. desktop notifications).
-      if (chrome::FindBrowserWithWebContents(source_web_contents_) == NULL)
+      if (chrome::FindBrowserWithWebContents(embedder_web_contents_) == NULL)
         return false;
       return true;
 
     case IDC_CONTENT_CONTEXT_TRANSLATE: {
       ChromeTranslateClient* chrome_translate_client =
-          ChromeTranslateClient::FromWebContents(source_web_contents_);
+          ChromeTranslateClient::FromWebContents(embedder_web_contents_);
       if (!chrome_translate_client)
         return false;
       std::string original_lang =
@@ -1062,13 +1063,13 @@
       return ((params_.edit_flags & WebContextMenuData::CanTranslate) != 0) &&
              !original_lang.empty() &&  // Did we receive the page language yet?
              !chrome_translate_client->GetLanguageState().IsPageTranslated() &&
-             !source_web_contents_->GetInterstitialPage() &&
+             !embedder_web_contents_->GetInterstitialPage() &&
              // There are some application locales which can't be used as a
              // target language for translation.
              translate::TranslateDownloadManager::IsSupportedLanguage(
                  target_lang) &&
              // Disable on the Instant Extended NTP.
-             !chrome::IsInstantNTP(source_web_contents_);
+             !chrome::IsInstantNTP(embedder_web_contents_);
     }
 
     case IDC_CONTENT_CONTEXT_OPENLINKNEWTAB:
@@ -1163,13 +1164,13 @@
 
     case IDC_SAVE_PAGE: {
       CoreTabHelper* core_tab_helper =
-          CoreTabHelper::FromWebContents(source_web_contents_);
+          CoreTabHelper::FromWebContents(embedder_web_contents_);
       if (!core_tab_helper)
         return false;
 
       CoreTabHelperDelegate* core_delegate = core_tab_helper->delegate();
       if (core_delegate &&
-          !core_delegate->CanSaveContents(source_web_contents_))
+          !core_delegate->CanSaveContents(embedder_web_contents_))
         return false;
 
       PrefService* local_state = g_browser_process->local_state();
@@ -1181,7 +1182,7 @@
       // We save the last committed entry (which the user is looking at), as
       // opposed to any pending URL that hasn't committed yet.
       NavigationEntry* entry =
-          source_web_contents_->GetController().GetLastCommittedEntry();
+          embedder_web_contents_->GetController().GetLastCommittedEntry();
       return content::IsSavableURL(entry ? entry->GetURL() : GURL());
     }
 
@@ -1467,19 +1468,19 @@
       break;
 
     case IDC_BACK:
-      source_web_contents_->GetController().GoBack();
+      embedder_web_contents_->GetController().GoBack();
       break;
 
     case IDC_FORWARD:
-      source_web_contents_->GetController().GoForward();
+      embedder_web_contents_->GetController().GoForward();
       break;
 
     case IDC_SAVE_PAGE:
-      source_web_contents_->OnSavePage();
+      embedder_web_contents_->OnSavePage();
       break;
 
     case IDC_RELOAD:
-      source_web_contents_->GetController().Reload(true);
+      embedder_web_contents_->GetController().Reload(true);
       break;
 
     case IDC_CONTENT_CONTEXT_RELOAD_PACKAGED_APP: {
@@ -1522,7 +1523,7 @@
     }
 
     case IDC_VIEW_SOURCE:
-      source_web_contents_->ViewSource();
+      embedder_web_contents_->ViewSource();
       break;
 
     case IDC_CONTENT_CONTEXT_INSPECTELEMENT:
@@ -1540,15 +1541,16 @@
     }
 
     case IDC_CONTENT_CONTEXT_VIEWPAGEINFO: {
-      NavigationController* controller = &source_web_contents_->GetController();
+      NavigationController* controller =
+          &embedder_web_contents_->GetController();
       // Important to use GetVisibleEntry to match what's showing in the
       // omnibox.  This may return null.
       NavigationEntry* nav_entry = controller->GetVisibleEntry();
       if (!nav_entry)
         return;
       Browser* browser =
-          chrome::FindBrowserWithWebContents(source_web_contents_);
-      chrome::ShowWebsiteSettings(browser, source_web_contents_,
+          chrome::FindBrowserWithWebContents(embedder_web_contents_);
+      chrome::ShowWebsiteSettings(browser, embedder_web_contents_,
                                   nav_entry->GetURL(), nav_entry->GetSSL());
       break;
     }
@@ -1557,7 +1559,7 @@
       // A translation might have been triggered by the time the menu got
       // selected, do nothing in that case.
       ChromeTranslateClient* chrome_translate_client =
-          ChromeTranslateClient::FromWebContents(source_web_contents_);
+          ChromeTranslateClient::FromWebContents(embedder_web_contents_);
       if (!chrome_translate_client ||
           chrome_translate_client->GetLanguageState().IsPageTranslated() ||
           chrome_translate_client->GetLanguageState().translation_pending()) {
diff --git a/chrome/browser/renderer_preferences_util.cc b/chrome/browser/renderer_preferences_util.cc
index 49848a18..68b5f45 100644
--- a/chrome/browser/renderer_preferences_util.cc
+++ b/chrome/browser/renderer_preferences_util.cc
@@ -46,6 +46,10 @@
   prefs->enable_referrers = pref_service->GetBoolean(prefs::kEnableReferrers);
   prefs->enable_do_not_track =
       pref_service->GetBoolean(prefs::kEnableDoNotTrack);
+#if defined(ENABLE_WEBRTC)
+  prefs->enable_webrtc_multiple_routes =
+      pref_service->GetBoolean(prefs::kWebRTCMultipleRoutesEnabled);
+#endif
 
   double default_zoom_level = 0;
   bool default_zoom_level_set = false;
diff --git a/chrome/browser/resources/app_list/start_page.js b/chrome/browser/resources/app_list/start_page.js
index e205592..fc5ebc4 100644
--- a/chrome/browser/resources/app_list/start_page.js
+++ b/chrome/browser/resources/app_list/start_page.js
@@ -46,8 +46,11 @@
    *
    * @param {boolean} hotwordEnabled Whether the hotword is enabled or not.
    */
-  function onAppListShown(hotwordEnabled) {
-    speechManager.onShown(hotwordEnabled);
+  function onAppListShown(hotwordEnabled, legacySpeechEnabled) {
+    if (legacySpeechEnabled)
+      speechManager.onShown(hotwordEnabled);
+
+    chrome.send('appListShown', [this.doodle != null]);
   }
 
   /**
@@ -112,6 +115,10 @@
       doodleLink.href = doodleData.target_url;
       doodleLink.target = '_blank';
       doodleLink.appendChild(doodleImage);
+      doodleLink.onclick = function() {
+        chrome.send('doodleClicked');
+        return true;
+      };
       this.doodle.appendChild(doodleLink);
     } else {
       this.doodle.appendChild(doodleImage);
diff --git a/chrome/browser/resources/chromeos/chromevox/chromevox/injected/event_watcher.js b/chrome/browser/resources/chromeos/chromevox/chromevox/injected/event_watcher.js
index 885c9253..b4d3c7e 100644
--- a/chrome/browser/resources/chromeos/chromevox/chromevox/injected/event_watcher.js
+++ b/chrome/browser/resources/chromeos/chromevox/chromevox/injected/event_watcher.js
@@ -252,15 +252,11 @@
  * event was received.
  *
  * @param {Event} evt The event to be added to the events queue.
- * @param {boolean=} opt_ignoreVisibility Whether to ignore visibility
- * checking on the document. By default, this is set to false (so an
- * invisible document would result in this event not being added).
  */
-cvox.ChromeVoxEventWatcher.addEvent = function(evt, opt_ignoreVisibility) {
+cvox.ChromeVoxEventWatcher.addEvent = function(evt) {
   // Don't add any events to the events queue if ChromeVox is inactive or the
-  // page is hidden unless specified to not do so.
-  if (!cvox.ChromeVox.isActive ||
-      (document.webkitHidden && !opt_ignoreVisibility)) {
+  // page is hidden.
+  if (!cvox.ChromeVox.isActive || document.webkitHidden) {
     return;
   }
   cvox.ChromeVoxEventWatcher.events_.push(evt);
@@ -486,7 +482,7 @@
         var evt = new window.Event('LiveRegion');
         evt.navDescriptions = navDescriptions;
         evt.assertive = assertive;
-        cvox.ChromeVoxEventWatcher.addEvent(evt, true);
+        cvox.ChromeVoxEventWatcher.addEvent(evt);
         return true;
       });
 };
diff --git a/chrome/browser/resources/chromeos/chromevox/chromevox/injected/live_regions.js b/chrome/browser/resources/chromeos/chromevox/chromevox/injected/live_regions.js
index 9147692..2cb2c2e 100644
--- a/chrome/browser/resources/chromeos/chromevox/chromevox/injected/live_regions.js
+++ b/chrome/browser/resources/chromeos/chromevox/chromevox/injected/live_regions.js
@@ -61,14 +61,6 @@
 cvox.LiveRegions.MAX_DISCARD_DUPS_MS = 2000;
 
 /**
- * Maximum time interval in which to discard duplicate live region announcement
- * when document.webkitHidden.
- * @type {number}
- * @const
- */
-cvox.LiveRegions.HIDDEN_DOC_MAX_DISCARD_DUPS_MS = 60000;
-
-/**
  * @type {Date}
 */
 cvox.LiveRegions.lastAnnouncedTime = null;
@@ -367,14 +359,11 @@
     }
   }
 
-  var discardDupsMs = document.webkitHidden ?
-      cvox.LiveRegions.HIDDEN_DOC_MAX_DISCARD_DUPS_MS :
-      cvox.LiveRegions.MAX_DISCARD_DUPS_MS;
-
   // First, evict expired entries.
   var now = new Date();
   for (var announced in cvox.LiveRegions.lastAnnouncedMap) {
-    if (now - cvox.LiveRegions.lastAnnouncedMap[announced] > discardDupsMs) {
+    if (now - cvox.LiveRegions.lastAnnouncedMap[announced] >
+        cvox.LiveRegions.MAX_DISCARD_DUPS_MS) {
       delete cvox.LiveRegions.lastAnnouncedMap[announced];
     }
   }
diff --git a/chrome/browser/resources/chromeos/chromevox/common/chrome_extension_externs.js b/chrome/browser/resources/chromeos/chromevox/common/chrome_extension_externs.js
index 41e1c20e..39eb707 100644
--- a/chrome/browser/resources/chromeos/chromevox/common/chrome_extension_externs.js
+++ b/chrome/browser/resources/chromeos/chromevox/common/chrome_extension_externs.js
@@ -1511,6 +1511,7 @@
     unknown: 'unknown',
     tooltip: 'tooltip',
     webArea: 'webArea',
+    webView: 'webView',
     window: 'window'
 };
 /**
diff --git a/chrome/browser/resources/chromeos/chromevox/common/editable_text_base.js b/chrome/browser/resources/chromeos/chromevox/common/editable_text_base.js
index d7d7bd4..5731239 100644
--- a/chrome/browser/resources/chromeos/chromevox/common/editable_text_base.js
+++ b/chrome/browser/resources/chromeos/chromevox/common/editable_text_base.js
@@ -6,6 +6,7 @@
 goog.provide('cvox.TextChangeEvent');
 goog.provide('cvox.TypingEcho');
 
+goog.require('cvox.AbstractTts');
 goog.require('cvox.ChromeVox');
 goog.require('cvox.TtsInterface');
 goog.require('goog.i18n.MessageFormat');
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/automation_util.js b/chrome/browser/resources/chromeos/chromevox/cvox2/background/automation_util.js
index 54fa994..b0f4e59 100644
--- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/automation_util.js
+++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/automation_util.js
@@ -88,8 +88,12 @@
   while (cur) {
     var next = dir == Dir.BACKWARD ?
         cur.previousSibling : cur.nextSibling;
+    if (!AutomationUtil.isInSameTree(cur, next))
+      return null;
     if (next)
       return next;
+    if (!AutomationUtil.isInSameTree(cur, cur.parent))
+      return null;
     cur = cur.parent;
   }
 };
@@ -164,6 +168,10 @@
   var candidate = node;
   while (candidate) {
     ret.push(candidate);
+
+    if (!AutomationUtil.isInSameTree(candidate, candidate.parent))
+      break;
+
     candidate = candidate.parent;
   }
   return ret.reverse();
@@ -226,4 +234,17 @@
   return divA.indexInParent <= divB.indexInParent ? Dir.FORWARD : Dir.BACKWARD;
 };
 
+/**
+ * Determines whether the two given nodes come from the same tree source.
+ * @param {AutomationNode} a
+ * @param {AutomationNode} b
+ * @return {boolean}
+ */
+AutomationUtil.isInSameTree = function(a, b) {
+  if (!a || !b)
+    return true;
+
+  return a.root === b.root;
+};
+
 });  // goog.scope
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/automation_util_test.extjs b/chrome/browser/resources/chromeos/chromevox/cvox2/background/automation_util_test.extjs
index 0a17f0a..90d76a8 100644
--- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/automation_util_test.extjs
+++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/automation_util_test.extjs
@@ -27,7 +27,7 @@
 };
 
 TEST_F('AutomationUtilE2ETest', 'GetAncestors', function() {
-  this.runWithAutomation(this.basicDoc,
+  this.runWithLoadedTree(this.basicDoc,
     function(root) {
       var expectedLength = 1;
       while (root) {
@@ -40,7 +40,7 @@
 });
   
 TEST_F('AutomationUtilE2ETest', 'GetUniqueAncestors', function() {
-  this.runWithAutomation(this.basicDoc, function(root) {
+  this.runWithLoadedTree(this.basicDoc, function(root) {
     var leftmost = root, rightmost = root;
     while (leftmost.firstChild)
       leftmost = leftmost.firstChild;
@@ -83,7 +83,7 @@
 });
   
 TEST_F('AutomationUtilE2ETest', 'GetDirection', function() {
-  this.runWithAutomation(this.basicDoc, function(root) {
+  this.runWithLoadedTree(this.basicDoc, function(root) {
     var left = root, right = root;
 
     // Same node.
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/background.js b/chrome/browser/resources/chromeos/chromevox/cvox2/background/background.js
index 6d16a02..2dc5c92a 100644
--- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/background.js
+++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/background.js
@@ -16,7 +16,6 @@
 goog.require('Output.EventType');
 goog.require('cursors.Cursor');
 goog.require('cvox.ChromeVoxEditableTextBase');
-goog.require('cvox.TabsApiHandler');
 
 goog.scope(function() {
 var AutomationNode = chrome.automation.AutomationNode;
@@ -37,14 +36,6 @@
   this.whitelist_ = ['chromevox_next_test'];
 
   /**
-   * @type {cvox.TabsApiHandler}
-   * @private
-   */
-  this.tabsHandler_ = new cvox.TabsApiHandler(cvox.ChromeVox.tts,
-                                              cvox.ChromeVox.braille,
-                                              cvox.ChromeVox.earcons);
-
-  /**
    * @type {cursors.Range}
    * @private
    */
@@ -82,43 +73,29 @@
 
   // Register listeners for ...
   // Desktop.
-  chrome.automation.getDesktop(this.onGotTree);
-
-  // Tabs.
-  chrome.tabs.onUpdated.addListener(this.onTabUpdated);
+  chrome.automation.getDesktop(this.onGotDesktop);
 };
 
 Background.prototype = {
   /**
-   * Handles chrome.tabs.onUpdated.
-   * @param {number} tabId
-   * @param {Object} changeInfo
-   */
-  onTabUpdated: function(tabId, changeInfo) {
-    if (changeInfo.status != 'complete')
-      return;
-    chrome.tabs.get(tabId, function(tab) {
-      if (!tab.url)
-        return;
-
-      var next = this.isWhitelisted_(tab.url);
-
-      this.toggleChromeVoxVersion({next: next, classic: !next});
-    }.bind(this));
-  },
-
-  /**
    * Handles all setup once a new automation tree appears.
-   * @param {chrome.automation.AutomationNode} root
+   * @param {chrome.automation.AutomationNode} desktop
    */
-  onGotTree: function(root) {
+  onGotDesktop: function(desktop) {
     // Register all automation event listeners.
     for (var eventType in this.listeners_)
-      root.addEventListener(eventType, this.listeners_[eventType], true);
+      desktop.addEventListener(eventType, this.listeners_[eventType], true);
 
-    if (root.attributes.docLoaded) {
-      this.onLoadComplete(
-          {target: root, type: chrome.automation.EventType.loadComplete});
+    // The focused state gets set on the containing webView node.
+    var webView = desktop.find({role: chrome.automation.RoleType.webView,
+                                state: {focused: true}});
+    if (webView) {
+      var root = webView.find({role: chrome.automation.RoleType.rootWebArea});
+      if (root) {
+        this.onLoadComplete(
+            {target: root,
+             type: chrome.automation.EventType.loadComplete});
+      }
     }
   },
 
@@ -270,14 +247,28 @@
    * @param {Object} evt
    */
   onLoadComplete: function(evt) {
+    var next = this.isWhitelisted_(evt.target.attributes.url);
+    this.toggleChromeVoxVersion({next: next, classic: !next});
     // Don't process nodes inside of web content if ChromeVox Next is inactive.
     if (evt.target.root.role != chrome.automation.RoleType.desktop &&
         !this.active_)
       return;
 
-    var node = AutomationUtil.findNodePost(evt.target,
+    if (this.currentRange_)
+      return;
+
+    var root = evt.target;
+    var webView = root;
+    while (webView && webView.role != chrome.automation.RoleType.webView)
+      webView = webView.parent;
+
+    if (!webView || !webView.state.focused)
+      return;
+
+    var node = AutomationUtil.findNodePost(root,
         Dir.FORWARD,
         AutomationPredicate.leaf);
+
     if (node)
       this.currentRange_ = cursors.Range.fromNode(node);
 
@@ -362,21 +353,11 @@
 
     if (opt_options.next) {
       if (!chrome.commands.onCommand.hasListener(this.onGotCommand))
-        chrome.commands.onCommand.addListener(this.onGotCommand);
-
-      if (!this.active_)
-        chrome.automation.getTree(this.onGotTree);
-      this.active_ = true;
+          chrome.commands.onCommand.addListener(this.onGotCommand);
+        this.active_ = true;
     } else {
       if (chrome.commands.onCommand.hasListener(this.onGotCommand))
         chrome.commands.onCommand.removeListener(this.onGotCommand);
-
-      if (this.active_) {
-        for (var eventType in this.listeners_) {
-          this.currentRange_.getStart().getNode().root.removeEventListener(
-              eventType, this.listeners_[eventType], true);
-        }
-      }
       this.active_ = false;
     }
 
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/background_test.extjs b/chrome/browser/resources/chromeos/chromevox/cvox2/background/background_test.extjs
index 7ed70f7c..27fba15f 100644
--- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/background_test.extjs
+++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/background_test.extjs
@@ -91,23 +91,18 @@
 });
 
 /** Tests feedback once a page loads. */
-TEST_F('BackgroundTest', 'InitialFeedback', function() {
-  cvox.ChromeVox.tts.expectSpeech('start', function() {
-    global.backgroundObj.onGotCommand('nextLine');
-  }, true);
-  cvox.ChromeVox.tts.expectSpeech('end', testDone, true);
+TEST_F('BackgroundTest', 'MANUAL_InitialFeedback', function() {
+  cvox.ChromeVox.tts.expectSpeech('start', testDone);
 
-  this.runWithDocument(function() {/*!
+  this.runWithTab(function() {/*!
     <p>start
     <p>end
-  */},
-  function() {});
+  */});
 });
 
 /** Tests consistency of navigating forward and backward. */
-TEST_F('BackgroundTest', 'ForwardBackwardNavigation', function() {
-  cvox.ChromeVox.tts.expectSpeech('start', null, true);
-  this.runWithDocument(this.linksAndHeadingsDoc, function() {
+TEST_F('BackgroundTest', 'MANUAL_ForwardBackwardNavigation', function() {
+  this.runWithLoadedTree(this.linksAndHeadingsDoc, function() {
       var doCmd = this.doCmd.bind(this);
       var expectAfter =
           cvox.ChromeVox.tts.expectSpeechAfter.bind(cvox.ChromeVox.tts);
@@ -136,9 +131,8 @@
   );
 });
 
-TEST_F('BackgroundTest', 'CaretNavigation', function() {
-  cvox.ChromeVox.tts.expectSpeech('start', null, true);
-  this.runWithDocument(this.linksAndHeadingsDoc, function() {
+TEST_F('BackgroundTest', 'MANUAL_CaretNavigation', function() {
+  this.runWithLoadedTree(this.linksAndHeadingsDoc, function() {
       var doCmd = this.doCmd.bind(this);
       var expectAfter =
           cvox.ChromeVox.tts.expectSpeechAfter.bind(cvox.ChromeVox.tts);
@@ -168,7 +162,7 @@
 
 // Flaky: http://crbug.com/451362
 TEST_F('BackgroundTest', 'DISABLED_SelectSingleBasic', function() {
-  this.runWithDocument(this.formsDoc, function(tabId) {
+  this.runWithLoadedTree(this.formsDoc, function(tabId) {
     var sendDownToSelect =
         this.sendKeyToElement.bind(this, tabId, 'Down', '#fruitSelect');
     var expect = cvox.ChromeVox.tts.expectSpeech.bind(cvox.ChromeVox.tts);
@@ -179,9 +173,8 @@
   }.bind(this));
 });
 
-TEST_F('BackgroundTest', 'ContinuousRead', function() {
-  cvox.ChromeVox.tts.expectSpeech('start', null, true);
-  this.runWithDocument(this.linksAndHeadingsDoc, function() {
+TEST_F('BackgroundTest', 'MANUAL_ContinuousRead', function() {
+  this.runWithLoadedTree(this.linksAndHeadingsDoc, function() {
     var doCmd = this.doCmd.bind(this);
     var expect =
         cvox.ChromeVox.tts.expectSpeechAfter.bind(cvox.ChromeVox.tts);
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/cursors_test.extjs b/chrome/browser/resources/chromeos/chromevox/cvox2/background/cursors_test.extjs
index 7cc281a4b..669c605 100644
--- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/cursors_test.extjs
+++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/cursors_test.extjs
@@ -70,6 +70,7 @@
       range = range.move(move[0], move[1]);
       var expectedStart = move[2];
       var expectedEnd = move[3];
+
       this.makeCursorAssertion(expectedStart, range.getStart());
       this.makeCursorAssertion(expectedEnd, range.getEnd());
     }
@@ -93,27 +94,25 @@
    * @param {string=} opt_testType Either CURSOR or RANGE.
    */
   runCursorMovesOnDocument: function(doc, moves, opt_testType) {
-    this.runWithDocument(doc,
-    function() {
-      chrome.automation.getTree(function(root) {
-        var start = null;
+    this.runWithLoadedTree(doc,
+    function(root) {
+      var start = null;
 
-        // This occurs as a result of a load complete.
-        var start = AutomationUtil.findNodePost(root,
-            FORWARD,
-            AutomationPredicate.leaf);
+      // This occurs as a result of a load complete.
+      var start = AutomationUtil.findNodePost(root,
+          FORWARD,
+          AutomationPredicate.leaf);
 
+      var cursor = new cursors.Cursor(start, 0);
+      if (!opt_testType || opt_testType == this.CURSOR) {
         var cursor = new cursors.Cursor(start, 0);
-        if (!opt_testType || opt_testType == this.CURSOR) {
-          var cursor = new cursors.Cursor(start, 0);
-          this.cursorMoveAndAssert(cursor, moves);
-          testDone();
-        } else if (opt_testType == this.RANGE) {
-          var range = new cursors.Range(cursor, cursor);
-          this.rangeMoveAndAssert(range, moves);
-          testDone();
-        }
-      }.bind(this));
+        this.cursorMoveAndAssert(cursor, moves);
+        testDone();
+      } else if (opt_testType == this.RANGE) {
+        var range = new cursors.Range(cursor, cursor);
+        this.rangeMoveAndAssert(range, moves);
+        testDone();
+      }
     }.bind(this));
   },
 
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/output.js b/chrome/browser/resources/chromeos/chromevox/cvox2/background/output.js
index 2c02cd35..12181bf 100644
--- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/output.js
+++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/output.js
@@ -13,6 +13,8 @@
 goog.require('cursors.Cursor');
 goog.require('cursors.Range');
 goog.require('cursors.Unit');
+goog.require('cvox.AbstractEarcons');
+goog.require('cvox.NavBraille');
 goog.require('cvox.Spannable');
 goog.require('cvox.ValueSelectionSpan');
 goog.require('cvox.ValueSpan');
@@ -94,9 +96,9 @@
       speak: '$name $earcon(BUTTON, @tag_button)'
     },
     checkBox: {
-      speak: '$or($checked, @describe_checkbox_checked($name), ' +
+      speak: '$if($checked, @describe_checkbox_checked($name), ' +
           '@describe_checkbox_unchecked($name)) ' +
-          '$or($checked, ' +
+          '$if($checked, ' +
               '$earcon(CHECK_ON, @input_type_checkbox), ' +
               '$earcon(CHECK_OFF, @input_type_checkbox))'
     },
@@ -122,7 +124,7 @@
       enter: '$role'
     },
     menuItem: {
-      speak: '$or($haspopup, @describe_menu_item_with_submenu($name), ' +
+      speak: '$if($haspopup, @describe_menu_item_with_submenu($name), ' +
           '@describe_menu_item($name)) ' +
           '@describe_index($indexInParent, $parentChildCount)'
     },
@@ -135,12 +137,12 @@
     },
     popUpButton: {
       speak: '$value $name @tag_button @aria_has_popup $earcon(LISTBOX) ' +
-          '$or($collapsed, @aria_expanded_false, @aria_expanded_true)'
+          '$if($collapsed, @aria_expanded_false, @aria_expanded_true)'
     },
     radioButton: {
-      speak: '$or($checked, @describe_radio_selected($name), ' +
+      speak: '$if($checked, @describe_radio_selected($name), ' +
           '@describe_radio_unselected($name)) ' +
-          '$or($checked, ' +
+          '$if($checked, ' +
               '$earcon(CHECK_ON, @input_type_radio), ' +
               '$earcon(CHECK_OFF, @input_type_radio))'
     },
@@ -509,7 +511,7 @@
           this.addToSpannable_(buff, token, options);
         } else if (tree.firstChild) {
           // Custom functions.
-          if (token == 'or') {
+          if (token == 'if') {
             var cond = tree.firstChild;
             var attrib = cond.value.slice(1);
             if (node.attributes[attrib] || node.state[attrib])
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/output_test.extjs b/chrome/browser/resources/chromeos/chromevox/cvox2/background/output_test.extjs
index 8e2709f..c6238ea 100644
--- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/output_test.extjs
+++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/output_test.extjs
@@ -22,7 +22,7 @@
 };
 
 TEST_F('OutputE2ETest', 'RenderBasic', function() {
-  this.runWithAutomation('<a href="#">Click here</a>',
+  this.runWithLoadedTree('<a href="#">Click here</a>',
     function(root) {
       var link = root.firstChild.firstChild;
       var range = cursors.Range.fromNode(link);
diff --git a/chrome/browser/resources/chromeos/chromevox/testing/chromevox_e2e_test_base.js b/chrome/browser/resources/chromeos/chromevox/testing/chromevox_e2e_test_base.js
index 1edf316..9b797cb 100644
--- a/chrome/browser/resources/chromeos/chromevox/testing/chromevox_e2e_test_base.js
+++ b/chrome/browser/resources/chromeos/chromevox/testing/chromevox_e2e_test_base.js
@@ -43,11 +43,6 @@
   /** @override */
   testGenPreamble: function() {
     GEN_BLOCK(function() {/*!
-  if (chromeos::AccessibilityManager::Get()->IsSpokenFeedbackEnabled()) {
-    chromeos::AccessibilityManager::Get()->EnableSpokenFeedback(false,
-        ui::A11Y_NOTIFICATION_NONE);
-  }
-
   base::Closure load_cb =
       base::Bind(&chromeos::AccessibilityManager::EnableSpokenFeedback,
           base::Unretained(chromeos::AccessibilityManager::Get()),
@@ -58,11 +53,26 @@
   },
 
   /**
-   * Run a test with the specified HTML snippet loaded.
+   * Launch a new tab, wait until tab status complete, then run callback.
    * @param {function() : void} doc Snippet wrapped inside of a function.
    * @param {function()} callback Called once the document is ready.
    */
-  runWithDocument: function(doc, callback) {
+  runWithLoadedTab: function(doc, callback) {
+    this.launchNewTabWithDoc(doc, function(tab) {
+      chrome.tabs.onUpdated.addListener(function(tabId, changeInfo) {
+        if (tabId == tab.id && changeInfo.status == 'complete') {
+          callback(tabId);
+        }
+      });
+    });
+  },
+
+  /**
+   * Launches the given document in a new tab.
+   * @param {function() : void} doc Snippet wrapped inside of a function.
+   * @param {function()} opt_callback Called once the document is created.
+   */
+  runWithTab: function(doc, opt_callback) {
     var docString = TestUtils.extractHtmlFromCommentEncodedString(doc);
     var url = 'data:text/html,<!doctype html>' +
         docString +
@@ -71,13 +81,7 @@
       active: true,
       url: url
     };
-    chrome.tabs.create(createParams, function(tab) {
-      chrome.tabs.onUpdated.addListener(function(tabId, changeInfo) {
-        if (tabId == tab.id && changeInfo.status == 'complete') {
-          callback(tabId);
-        }
-      });
-    });
+    chrome.tabs.create(createParams, opt_callback);
   },
 
   /**
diff --git a/chrome/browser/resources/chromeos/chromevox/testing/chromevox_next_e2e_test_base.js b/chrome/browser/resources/chromeos/chromevox/testing/chromevox_next_e2e_test_base.js
index e432a998..5eebf0b 100644
--- a/chrome/browser/resources/chromeos/chromevox/testing/chromevox_next_e2e_test_base.js
+++ b/chrome/browser/resources/chromeos/chromevox/testing/chromevox_next_e2e_test_base.js
@@ -29,11 +29,24 @@
     GEN('#include "base/command_line.h"');
   },
 
-  runWithAutomation: function(doc, callback) {
-    this.runWithDocument(doc, function() {
-      chrome.automation.getTree(function(root) {
-        callback(root);
-      }.bind(this));
+  /**
+   * Launches a new tab with the given document, and runs callback when a load
+   * complete fires.
+   * @param {function() : void} doc Snippet wrapped inside of a function.
+   * @param {function()} opt_callback Called once the document is ready.
+   */
+  runWithLoadedTree: function(doc, callback) {
+    chrome.automation.getDesktop(function(r) {
+      function callbackInternal(evt) {
+        if (!evt.target.attributes.url ||
+            evt.target.attributes.url.indexOf('test') == -1)
+          return;
+
+        r.removeEventListener(callbackInternal);
+        callback(evt.target);
+      }
+      r.addEventListener('loadComplete', callbackInternal, true);
+      this.runWithTab(doc);
     }.bind(this));
   }
 };
diff --git a/chrome/browser/resources/chromeos/chromevox/testing/mock_tts.js b/chrome/browser/resources/chromeos/chromevox/testing/mock_tts.js
index a6942dea..04fdb7d3 100644
--- a/chrome/browser/resources/chromeos/chromevox/testing/mock_tts.js
+++ b/chrome/browser/resources/chromeos/chromevox/testing/mock_tts.js
@@ -113,7 +113,7 @@
     // Process any idleUtterances.
     this.idleUtterances_.forEach(function(utterance) {
       this.process_(utterance, true);
-    });
+    }.bind(this));
   },
 
   /**
diff --git a/chrome/browser/resources/chromeos/chromevox/tools/chromevox_webstore_util.py b/chrome/browser/resources/chromeos/chromevox/tools/chromevox_webstore_util.py
index 5aa96f7..f2ded5d 100755
--- a/chrome/browser/resources/chromeos/chromevox/tools/chromevox_webstore_util.py
+++ b/chrome/browser/resources/chromeos/chromevox/tools/chromevox_webstore_util.py
@@ -24,7 +24,10 @@
   'redirect_uri': 'http://localhost:8000'
 }
 
+# Globals.
 PORT = 8000
+g_auth_code = None
+g_oauth_token = None
 
 APP_ID = 'kgejglhpjiefppelpmljglcjbhoiplfn'
 OAUTH_DOMAIN = 'accounts.google.com'
@@ -45,6 +48,10 @@
     self.rfile.close()
 
 def GetAuthCode():
+  global g_auth_code
+  if g_auth_code:
+    return g_auth_code
+
   Handler = CodeRequestHandler
   httpd = SocketServer.TCPServer(("", PORT), Handler)
   query = '&'.join(['response_type=code',
@@ -56,9 +63,14 @@
   webbrowser.open(auth_url)
   httpd.handle_request()
   httpd.server_close()
-  return httpd.code
+  g_auth_code = httpd.code
+  return g_auth_code
 
 def GetOauthToken(code, client_secret):
+  global g_oauth_token
+  if g_oauth_token:
+    return g_oauth_token
+
   PROJECT_ARGS['code'] = code
   PROJECT_ARGS['client_secret'] = client_secret
   body = urllib.urlencode(PROJECT_ARGS)
@@ -69,11 +81,14 @@
   conn.endheaders()
   conn.send(body)
   content = conn.getresponse().read()
-  return json.loads(content)
+  conn.close()
+  g_oauth_token = json.loads(content)
+  return g_oauth_token
 
 def GetPopulatedHeader(client_secret):
   code = GetAuthCode()
   access_token = GetOauthToken(code, client_secret)
+
   url = 'www.googleapis.com'
 
   return {'Authorization': 'Bearer %(access_token)s' % access_token,
@@ -85,14 +100,18 @@
   headers = GetPopulatedHeader(client_secret)
   conn = httplib.HTTPSConnection(API_ENDPOINT_DOMAIN)
   conn.request('GET', command, '', headers)
-  return conn.getresponse()
+  r = conn.getresponse()
+  conn.close()
+  return r
 
 def SendPostCommand(command, client_secret, header_additions = {}, body=None):
   headers = GetPopulatedHeader(client_secret)
   headers = dict(headers.items() + header_additions.items())
   conn = httplib.HTTPSConnection(API_ENDPOINT_DOMAIN)
   conn.request('POST', command, body, headers)
-  return conn.getresponse()
+  r = conn.getresponse()
+  conn.close()
+  return r
 
 def GetUploadStatus(client_secret):
   '''Gets the status of a previous upload.
diff --git a/chrome/browser/resources/chromeos/chromevox/tools/upload_chromevox_to_webstore.py b/chrome/browser/resources/chromeos/chromevox/tools/upload_chromevox_to_webstore.py
index 2425010..9ca991e 100755
--- a/chrome/browser/resources/chromeos/chromevox/tools/upload_chromevox_to_webstore.py
+++ b/chrome/browser/resources/chromeos/chromevox/tools/upload_chromevox_to_webstore.py
@@ -43,7 +43,9 @@
 
 def CreateOptionParser():
   parser = optparse.OptionParser(description=__doc__)
-  parser.usage = '%prog <extension_path> <output_path> <client_secret'
+  parser.usage = '%prog <extension_path> <client_secret>'
+  parser.add_option('-p', '--publish', action='store_true',
+                    help='publish the extension')
   return parser
 
 
@@ -88,11 +90,11 @@
              chromevox_webstore_util.GetUploadStatus(client_secret).read())
     elif input == 'u':
       print ('Uploaded with status: %s' %
-             chromevox_webstore_util.PostUpload(output_path, client_secret))
+          chromevox_webstore_util.PostUpload(output_path.name, client_secret))
     elif input == 't':
       print ('Published to trusted testers with status: %s' %
-             chromevox_webstore_util.PostPublishTrustedTesters(
-                 client_secret).read())
+          chromevox_webstore_util.PostPublishTrustedTesters(
+              client_secret).read())
     elif input == 'p':
       print ('Published to public with status: %s' %
              chromevox_webstore_util.PostPublish(client_secret).read())
@@ -102,14 +104,15 @@
       print 'Unrecognized option: %s' % input
 
 def main():
-  _, args = CreateOptionParser().parse_args()
-  if len(args) != 3:
-    print 'Expected exactly three arguments'
+  options, args = CreateOptionParser().parse_args()
+  if len(args) != 2:
+    print 'Expected exactly two arguments'
+    print str(args)
     sys.exit(1)
 
   extension_path = args[0]
-  output_path = args[1]
-  client_secret = args[2]
+  client_secret = args[1]
+  output_path = tempfile.NamedTemporaryFile()
 
   with ZipFile(output_path, 'w') as zip:
     for root, dirs, files in os.walk(extension_path):
@@ -125,9 +128,15 @@
                   os.path.join(rel_path, extension_file))
     manifest_file = MakeManifest()
     zip.write(manifest_file.name, 'manifest.json')
-  print 'Created ChromeVox zip file in %s' % output_path
+  print 'Created ChromeVox zip file in %s' % output_path.name
   print 'Please run manual smoke tests before proceeding.'
-  RunInteractivePrompt(client_secret, output_path)
+  if options.publish:
+    print('Uploading...%s' %
+        chromevox_webstore_util.PostUpload(output_path.name, client_secret))
+    print('publishing...%s' %
+        chromevox_webstore_util.PostPublish(client_secret).read())
+  else:
+    RunInteractivePrompt(client_secret, output_path)
 
 
 if __name__ == '__main__':
diff --git a/chrome/browser/resources/chromeos/login/header_bar.css b/chrome/browser/resources/chromeos/login/header_bar.css
index 7676f02..c199d22 100644
--- a/chrome/browser/resources/chromeos/login/header_bar.css
+++ b/chrome/browser/resources/chromeos/login/header_bar.css
@@ -65,6 +65,22 @@
   height: 34px;
 }
 
+.add-supervised-user-menu {
+  display: none;
+}
+
+#more-settings-header-bar-item.active .add-supervised-user-menu {
+  background-color: white;
+  border: 1px solid lightgray;
+  border-radius: 2px;
+  bottom: 15px;
+  display: block;
+  font-size: 13px;
+  left: 15px;
+  position: absolute;
+  width: 220px;
+}
+
 html[dir=rtl] .header-bar-item {
   background-position: right center;
 }
@@ -73,6 +89,7 @@
 #login-header-bar #restart-button,
 #login-header-bar #add-user-button,
 #login-header-bar #guest-user-button,
+#login-header-bar #more-settings-button,
 #login-header-bar #cancel-multiple-sign-in-button {
   -webkit-padding-start: 24px;
   background-position: left center;
@@ -83,11 +100,16 @@
 html[dir=rtl] #login-header-bar #shutdown-button,
 html[dir=rtl] #login-header-bar #restart-button,
 html[dir=rtl] #login-header-bar #add-user-button,
+html[dir=rtl] #login-header-bar #more-settings-button,
 html[dir=rtl] #login-header-bar #guest-user-button,
 html[dir=rtl] #login-header-bar #cancel-multiple-sign-in-button {
   background-position: right center;
 }
 
+#login-header-bar #more-settings-header-bar-item {
+  position: relative;
+}
+
 #login-header-bar #shutdown-button {
   background-image: url(chrome://theme/IDR_ICON_POWER_WHITE);
 }
@@ -100,6 +122,10 @@
   background-image: url(chrome://theme/IDR_ICON_ADD_USER_WHITE);
 }
 
+#login-header-bar #more-settings-button {
+  background-image: url(chrome://theme/IDR_ICON_MORE_VERT_WHITE);
+}
+
 #login-header-bar #guest-user-button {
   background-image: url(chrome://theme/IDR_ICON_GUEST_WHITE);
 }
diff --git a/chrome/browser/resources/chromeos/login/header_bar.html b/chrome/browser/resources/chromeos/login/header_bar.html
index b523745..a106c250 100644
--- a/chrome/browser/resources/chromeos/login/header_bar.html
+++ b/chrome/browser/resources/chromeos/login/header_bar.html
@@ -11,15 +11,21 @@
     <button id="show-apps-button" class="custom-appearance"
         i18n-content="showApps"></button>
   </div>
+  <div id="guest-user-header-bar-item" class="header-bar-item" hidden>
+    <button id="guest-user-button" class="custom-appearance"
+        i18n-content="browseAsGuest"></button>
+  </div>
   <div id="add-user-header-bar-item" class="header-bar-item" hidden>
     <button id="add-user-button" class="custom-appearance"
         i18n-content="addUser"></button>
     <button id="cancel-add-user-button" class="custom-appearance"
         i18n-content="cancel" hidden></button>
   </div>
-  <div id="guest-user-header-bar-item" class="header-bar-item" hidden>
-    <button id="guest-user-button" class="custom-appearance"
-        i18n-content="browseAsGuest"></button>
+  <div id="more-settings-header-bar-item" class="header-bar-item">
+    <button id="more-settings-button" class="custom-appearance"></button>
+    <div class="add-supervised-user-menu" aria-label="Add supervised user">
+      <span class="add-supervised-user-command">Add supervised user</span>
+    </div>
   </div>
   <div id="sign-out-user-item" class="header-bar-item" hidden>
     <button id="sign-out-user-button" class="custom-appearance"
diff --git a/chrome/browser/resources/chromeos/login/header_bar.js b/chrome/browser/resources/chromeos/login/header_bar.js
index 3a3ba9ba..2a3b622f9 100644
--- a/chrome/browser/resources/chromeos/login/header_bar.js
+++ b/chrome/browser/resources/chromeos/login/header_bar.js
@@ -21,6 +21,9 @@
     // Whether guest button should be shown when header bar is in normal mode.
     showGuest_: false,
 
+    // Whehter MinuteMaid flow is active.
+    isMinuteMaid_: false,
+
     // Whether the reboot button should be shown the when header bar is in
     // normal mode.
     showReboot_: false,
@@ -29,6 +32,10 @@
     // normal mode.
     showShutdown_: true,
 
+    // Whether the create supervised user button should be shown when the header
+    // bar is in normal mode. It will be shown in "More settings" menu.
+    showCreateSupervised_: false,
+
     // Current UI state of the sign-in screen.
     signinUIState_: SIGNIN_UI_STATE.HIDDEN,
 
@@ -37,6 +44,7 @@
 
     /** @override */
     decorate: function() {
+      document.addEventListener('click', this.handleClick_.bind(this));
       $('shutdown-header-bar-item').addEventListener('click',
           this.handleShutdownClick_);
       $('shutdown-button').addEventListener('click',
@@ -47,6 +55,8 @@
           this.handleShutdownClick_);
       $('add-user-button').addEventListener('click',
           this.handleAddUserClick_);
+      $('more-settings-button').addEventListener('click',
+          this.handleMoreSettingsClick_.bind(this));
       $('cancel-add-user-button').addEventListener('click',
           this.handleCancelAddUserClick_);
       $('guest-user-header-bar-item').addEventListener('click',
@@ -60,6 +70,8 @@
       $('cancel-consumer-management-enrollment-button').addEventListener(
           'click',
           this.handleCancelConsumerManagementEnrollmentClick_);
+      this.addSupervisedUserMenu.addEventListener('click',
+          this.handleAddSupervisedUserClick_.bind(this));
       if (Oobe.getInstance().displayType == DISPLAY_TYPE.LOGIN ||
           Oobe.getInstance().displayType == DISPLAY_TYPE.OOBE) {
         if (Oobe.getInstance().newKioskUI)
@@ -94,6 +106,32 @@
           button.disabled = value;
     },
 
+    get getMoreSettingsMenu() {
+      return $('more-settings-header-bar-item');
+    },
+
+    get addSupervisedUserMenu() {
+      return this.querySelector('.add-supervised-user-menu');
+    },
+
+    /**
+     * Whether action box button is in active state.
+     * @type {boolean}
+     */
+    get isMoreSettingsActive() {
+      return this.getMoreSettingsMenu.classList.contains('active');
+    },
+    set isMoreSettingsActive(active) {
+      if (active == this.isMoreSettingsActive)
+        return;
+      if (active) {
+        this.getMoreSettingsMenu.classList.add('active');
+      } else {
+        this.getMoreSettingsMenu.classList.remove('active');
+      }
+    },
+
+
     /**
      * Add user button click handler.
      *
@@ -107,6 +145,19 @@
       e.stopPropagation();
     },
 
+    handleMoreSettingsClick_: function(e) {
+      this.isMoreSettingsActive = !this.isMoreSettingsActive;
+      e.stopPropagation();
+    },
+
+    handleClick_: function(e) {
+      this.isMoreSettingsActive = false;
+    },
+
+    handleAddSupervisedUserClick_: function(e) {
+      chrome.send('showSupervisedUserCreationScreen');
+      e.preventDefault();
+    },
     /**
      * Cancel add user button click handler.
      *
@@ -188,6 +239,16 @@
       this.updateUI_();
     },
 
+    set minuteMaid(value) {
+      this.isMinuteMaid_ = value;
+      this.updateUI_();
+    },
+
+    set showCreateSupervisedButton(value) {
+      this.showCreateSupervised_ = value;
+      this.updateUI_();
+    },
+
     /**
      * If true the "Restart" button is shown.
      *
@@ -267,11 +328,17 @@
 
       $('add-user-button').hidden =
           !accountPickerIsActive || isMultiProfilesUI || isLockScreen;
-      $('cancel-add-user-button').hidden = accountPickerIsActive ||
+      $('more-settings-header-bar-item').hidden = !this.isMinuteMaid_ ||
+          !gaiaIsActive ||
+          !this.showCreateSupervised_;
+      $('cancel-add-user-button').hidden =
+          (gaiaIsActive && this.isMinuteMaid_) ||
+          accountPickerIsActive ||
           !this.allowCancel_ ||
           wrongHWIDWarningIsActive ||
           isMultiProfilesUI;
-      $('guest-user-header-bar-item').hidden = gaiaIsActive ||
+      $('guest-user-header-bar-item').hidden =
+          (gaiaIsActive && !this.isMinuteMaid_) ||
           supervisedUserCreationDialogIsActive ||
           !this.showGuest_ ||
           wrongHWIDWarningIsActive ||
@@ -316,11 +383,13 @@
     },
 
     /**
-     * Animates Header bar to slowly appear on the screen.
+     * Animates Header bar to appear on the screen.
      *
+     * @param {boolean} fast Whether the animation should complete quickly or
+     *     slowly.
      * @param {function()} callback will be called once animation is finished.
      */
-    animateIn: function(callback) {
+    animateIn: function(fast, callback) {
       if (callback) {
         var launcher = this;
         launcher.addEventListener(
@@ -332,7 +401,7 @@
         ensureTransitionEndEvent(launcher, 2250);
       }
 
-      if (Oobe.getInstance().displayType == DISPLAY_TYPE.OOBE) {
+      if (fast) {
         this.classList.remove('login-header-bar-animate-slow');
         this.classList.add('login-header-bar-animate-fast');
       } else {
@@ -354,8 +423,8 @@
   /**
    * Convenience wrapper of animateIn.
    */
-  HeaderBar.animateIn = function(callback) {
-    $('login-header-bar').animateIn(callback);
+  HeaderBar.animateIn = function(fast, callback) {
+    $('login-header-bar').animateIn(fast, callback);
   };
 
   return {
diff --git a/chrome/browser/resources/chromeos/login/login_common.js b/chrome/browser/resources/chromeos/login/login_common.js
index 2c07869..6f2bd4b 100644
--- a/chrome/browser/resources/chromeos/login/login_common.js
+++ b/chrome/browser/resources/chromeos/login/login_common.js
@@ -95,7 +95,7 @@
 
       // Callback to animate the header bar in.
       var showHeaderBar = function() {
-        login.HeaderBar.animateIn(function() {
+        login.HeaderBar.animateIn(false, function() {
           chrome.send('headerBarVisible');
         });
       };
@@ -106,7 +106,7 @@
       Oobe.getInstance().prepareForLoginDisplay_();
       // Ensure header bar is visible when switching to Login UI from oobe.
       if (Oobe.getInstance().displayType == DISPLAY_TYPE.OOBE)
-        login.HeaderBar.animateIn();
+        login.HeaderBar.animateIn(true);
     }
 
     Oobe.getInstance().headerHidden = false;
@@ -211,7 +211,9 @@
    * Displays animations that have to happen once login UI is fully displayed.
    */
   Oobe.animateOnceFullyDisplayed = function() {
-    login.HeaderBar.animateIn();
+    login.HeaderBar.animateIn(true, function() {
+      chrome.send('headerBarVisible');
+    });
   };
 
   /**
@@ -307,6 +309,13 @@
   };
 
   /**
+   * Shows the add user dialog. Used in browser tests.
+   */
+  Oobe.showAddUserForTesting = function() {
+    chrome.send('showAddUser');
+  };
+
+  /**
    * Hotrod requisition for telemetry.
    */
   Oobe.remoraRequisitionForTesting = function() {
diff --git a/chrome/browser/resources/chromeos/login/screen_gaia_signin.css b/chrome/browser/resources/chromeos/login/screen_gaia_signin.css
index 37a2808..aeacbd9 100644
--- a/chrome/browser/resources/chromeos/login/screen_gaia_signin.css
+++ b/chrome/browser/resources/chromeos/login/screen_gaia_signin.css
@@ -13,6 +13,11 @@
   width: 522px;
 }
 
+.minute-maid #gaia-signin {
+  background: rgb(66, 133, 244);
+  padding: 50px 0 0 0;
+  width: 522px;
+}
 #gaia-signin.full-width {
   padding: 75px 0 0;
 }
@@ -37,10 +42,19 @@
   }
 }
 
+.minute-maid #signin-right,
 .no-right-panel #signin-right {
   display: none;
 }
 
+.minute-maid #close-button-item {
+  background: transparent none;
+  background-image: url(chrome://theme/IDR_PANEL_CLOSE);
+  background-position: left center;
+  background-repeat: no-repeat;
+  background-size: 30px;
+}
+
 .signin-text {
   color: #666;
   margin-top: 20px;
@@ -63,6 +77,8 @@
   height: 100%;
 }
 
+.minute-maid #signin-frame,
+.minute-maid #gaia-signin-form-container,
 .full-width #signin-frame,
 .full-width #gaia-signin-form-container {
   width: 100%;
@@ -78,6 +94,8 @@
   width: 1px;
 }
 
+.gaia-signin .minute-maid #step-logo,
+.gaia-signin .minute-maid #gaia-signin-divider,
 .no-right-panel #gaia-signin-divider {
   display: none;
 }
@@ -91,6 +109,7 @@
 
 webview#signin-frame {
   display: block;
+  margin-left: 0;
   overflow: hidden;
   padding: 0;
 }
@@ -117,6 +136,10 @@
   text-align: center;
 }
 
+.minute-maid #enterprise-info-container {
+  display: none;
+}
+
 #enterprise-info-container #enterprise-info {
   display: inline-block;
 }
diff --git a/chrome/browser/resources/chromeos/login/screen_gaia_signin.html b/chrome/browser/resources/chromeos/login/screen_gaia_signin.html
index acae182..f16eef8 100644
--- a/chrome/browser/resources/chromeos/login/screen_gaia_signin.html
+++ b/chrome/browser/resources/chromeos/login/screen_gaia_signin.html
@@ -1,5 +1,7 @@
 <div class="step right hidden" id="gaia-signin" role="group"
     aria-live="polite" hidden>
+  <div id="close-button-item" hidden>
+  </div>
   <div class="step-contents">
     <div id="gaia-signin-form-container">
         <div id="login-box"><!-- Aligned with the login box in iframe --></div>
diff --git a/chrome/browser/resources/chromeos/login/screen_gaia_signin.js b/chrome/browser/resources/chromeos/login/screen_gaia_signin.js
index bd0de57..cde1b91 100644
--- a/chrome/browser/resources/chromeos/login/screen_gaia_signin.js
+++ b/chrome/browser/resources/chromeos/login/screen_gaia_signin.js
@@ -48,6 +48,12 @@
     isLocal_: false,
 
     /**
+     * Whether MinuteMaid flow is active.
+     * @type {boolean}
+     */
+    isMinuteMaid: false,
+
+    /**
      * Email of the user, which is logging in using offline mode.
      * @type {string}
      */
@@ -138,6 +144,11 @@
         e.preventDefault();
       });
 
+      $('close-button-item').addEventListener('click', function(e) {
+        this.cancel();
+        e.preventDefault();
+      }.bind(this));
+
       this.updateLocalizedContent();
     },
 
@@ -300,11 +311,20 @@
       if (data.localizedStrings)
         params.localizedStrings = data.localizedStrings;
 
-      if (data.gaiaEndpoint) {
+      if (data.useMinuteMaid) {
+        this.isMinuteMaid = true;
+        $('inner-container').classList.add('minute-maid');
+        $('progress-dots').hidden = true;
         data.useEmbedded = false;
-        params.gaiaPath = data.gaiaEndpoint;
+        $('login-header-bar').showGuestButton = true;
       }
 
+      if (data.gaiaEndpoint)
+        params.gaiaPath = data.gaiaEndpoint;
+
+      $('login-header-bar').minuteMaid = this.isMinuteMaid;
+
+
       if (data.useEmbedded)
         params.gaiaPath = 'EmbeddedSignIn';
 
@@ -346,16 +366,21 @@
         reasonLabel.hidden = true;
       }
 
-      $('createAccount').hidden = !data.createAccount;
-      $('guestSignin').hidden = !data.guestSignin;
-      $('createSupervisedUserPane').hidden = !data.supervisedUsersEnabled;
+      if (this.isMinuteMaid) {
+        $('login-header-bar').showCreateSupervisedButton =
+            data.supervisedUsersCanCreate;
+      } else {
+        $('createAccount').hidden = !data.createAccount;
+        $('guestSignin').hidden = !data.guestSignin;
+        $('createSupervisedUserPane').hidden = !data.supervisedUsersEnabled;
 
-      $('createSupervisedUserLinkPlaceholder').hidden =
-          !data.supervisedUsersCanCreate;
-      $('createSupervisedUserNoManagerText').hidden =
-          data.supervisedUsersCanCreate;
-      $('createSupervisedUserNoManagerText').textContent =
-          data.supervisedUsersRestrictionReason;
+        $('createSupervisedUserLinkPlaceholder').hidden =
+            !data.supervisedUsersCanCreate;
+        $('createSupervisedUserNoManagerText').hidden =
+            data.supervisedUsersCanCreate;
+        $('createSupervisedUserNoManagerText').textContent =
+            data.supervisedUsersRestrictionReason;
+      }
 
       var isEnrollingConsumerManagement = data.isEnrollingConsumerManagement;
       $('consumerManagementEnrollment').hidden = !isEnrollingConsumerManagement;
@@ -384,6 +409,7 @@
     updateCancelButtonState: function() {
       this.cancelAllowed_ = this.isShowUsers_ && $('pod-row').pods.length;
       $('login-header-bar').allowCancel = this.cancelAllowed_;
+      $('close-button-item').hidden = !this.cancelAllowed_;
     },
 
     switchToFullTab: function() {
@@ -419,6 +445,7 @@
       if (Oobe.getInstance().currentScreen === this) {
         Oobe.getInstance().updateScreenSize(this);
         $('login-header-bar').allowCancel = isSAML || this.cancelAllowed_;
+        $('close-button-item').hidden = !(isSAML || this.cancelAllowed_);
       }
     },
 
diff --git a/chrome/browser/resources/chromeos/network_ui/network_ui.js b/chrome/browser/resources/chromeos/network_ui/network_ui.js
index d03b71f..970d6b7 100644
--- a/chrome/browser/resources/chromeos/network_ui/network_ui.js
+++ b/chrome/browser/resources/chromeos/network_ui/network_ui.js
@@ -32,7 +32,7 @@
     'Type',
     'profile_path',
     'visible',
-    'onc_source'
+    'Source'
   ];
 
   /**
@@ -143,6 +143,16 @@
   };
 
   /**
+   * Returns a valid HTMLElement id from |guid|.
+   *
+   * @param {string} guid A GUID which may start with a digit
+   * @return {string} A valid HTMLElement id.
+   */
+  var idFromGuid = function(guid) {
+    return '_' + guid.replace(/[{}]/g, '');
+  };
+
+  /**
    * This callback function is triggered when visible networks are received.
    *
    * @param {Array} states A list of network state information for each
@@ -198,7 +208,7 @@
     emptyCell.style.border = 'none';
     expandedRow.appendChild(emptyCell);
     var detailCell = document.createElement('td');
-    detailCell.id = guid;
+    detailCell.id = idFromGuid(guid);
     detailCell.className = 'state-table-expanded-cell';
     detailCell.colSpan = baseRow.childNodes.length - 1;
     expandedRow.appendChild(detailCell);
@@ -236,7 +246,7 @@
       return;
     }
 
-    var detailCell = document.querySelector('td#' + guid);
+    var detailCell = document.querySelector('td#' + idFromGuid(guid));
     if (!detailCell) {
       console.error('No cell for GUID: ' + guid);
       return;
diff --git a/chrome/browser/resources/chromeos/wallpaper_manager/images/icon128.png b/chrome/browser/resources/chromeos/wallpaper_manager/images/icon128.png
index dfba48c..7e9e04f 100644
--- a/chrome/browser/resources/chromeos/wallpaper_manager/images/icon128.png
+++ b/chrome/browser/resources/chromeos/wallpaper_manager/images/icon128.png
Binary files differ
diff --git a/chrome/browser/resources/chromeos/wallpaper_manager/images/icon16.png b/chrome/browser/resources/chromeos/wallpaper_manager/images/icon16.png
new file mode 100644
index 0000000..cfb31c1
--- /dev/null
+++ b/chrome/browser/resources/chromeos/wallpaper_manager/images/icon16.png
Binary files differ
diff --git a/chrome/browser/resources/chromeos/wallpaper_manager/images/icon256.png b/chrome/browser/resources/chromeos/wallpaper_manager/images/icon256.png
new file mode 100644
index 0000000..2829e20
--- /dev/null
+++ b/chrome/browser/resources/chromeos/wallpaper_manager/images/icon256.png
Binary files differ
diff --git a/chrome/browser/resources/chromeos/wallpaper_manager/images/icon32.png b/chrome/browser/resources/chromeos/wallpaper_manager/images/icon32.png
new file mode 100644
index 0000000..51dd168d
--- /dev/null
+++ b/chrome/browser/resources/chromeos/wallpaper_manager/images/icon32.png
Binary files differ
diff --git a/chrome/browser/resources/chromeos/wallpaper_manager/images/icon48.png b/chrome/browser/resources/chromeos/wallpaper_manager/images/icon48.png
new file mode 100644
index 0000000..e310aa1
--- /dev/null
+++ b/chrome/browser/resources/chromeos/wallpaper_manager/images/icon48.png
Binary files differ
diff --git a/chrome/browser/resources/chromeos/wallpaper_manager/images/icon64.png b/chrome/browser/resources/chromeos/wallpaper_manager/images/icon64.png
new file mode 100644
index 0000000..d87afbf
--- /dev/null
+++ b/chrome/browser/resources/chromeos/wallpaper_manager/images/icon64.png
Binary files differ
diff --git a/chrome/browser/resources/chromeos/wallpaper_manager/images/icon96.png b/chrome/browser/resources/chromeos/wallpaper_manager/images/icon96.png
new file mode 100644
index 0000000..6c7863a
--- /dev/null
+++ b/chrome/browser/resources/chromeos/wallpaper_manager/images/icon96.png
Binary files differ
diff --git a/chrome/browser/resources/chromeos/wallpaper_manager/manifest.json b/chrome/browser/resources/chromeos/wallpaper_manager/manifest.json
index 77c2e104..c52ada0 100644
--- a/chrome/browser/resources/chromeos/wallpaper_manager/manifest.json
+++ b/chrome/browser/resources/chromeos/wallpaper_manager/manifest.json
@@ -8,7 +8,13 @@
   "manifest_version": 2,
   "description": "An experimental wallpaper picker UI",
   "icons": {
-    "128": "images/icon128.png"
+    "16": "images/icon16.png",
+    "32": "images/icon32.png",
+    "48": "images/icon48.png",
+    "64": "images/icon64.png",
+    "96": "images/icon96.png",
+    "128": "images/icon128.png",
+    "256": "images/icon256.png"
   },
   "permissions": [
     "alarms",
diff --git a/chrome/browser/resources/cryptotoken/devicestatuscodes.js b/chrome/browser/resources/cryptotoken/devicestatuscodes.js
index 0f6f143..dc037c0 100644
--- a/chrome/browser/resources/cryptotoken/devicestatuscodes.js
+++ b/chrome/browser/resources/cryptotoken/devicestatuscodes.js
@@ -1,4 +1,4 @@
-// Copyright 2014 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.
 
@@ -55,4 +55,3 @@
  * @const
  */
 DeviceStatusCodes.GONE_STATUS = -8;
-
diff --git a/chrome/browser/resources/cryptotoken/gnubby.js b/chrome/browser/resources/cryptotoken/gnubby.js
index 8a051028..f3d449b 100644
--- a/chrome/browser/resources/cryptotoken/gnubby.js
+++ b/chrome/browser/resources/cryptotoken/gnubby.js
@@ -25,7 +25,7 @@
   this.closed = false;
   this.commandPending = false;
   this.notifyOnClose = [];
-  this.busyMillis = (opt_busySeconds ? opt_busySeconds * 1000 : 2500);
+  this.busyMillis = (opt_busySeconds ? opt_busySeconds * 1000 : 9500);
 }
 
 /**
@@ -44,6 +44,19 @@
 };
 
 /**
+ * Return cid as hex string.
+ * @param {number} cid to convert.
+ * @return {string} hexadecimal string.
+ */
+Gnubby.hexCid = function(cid) {
+  var tmp = [(cid >>> 24) & 255,
+             (cid >>> 16) & 255,
+             (cid >>> 8) & 255,
+             (cid >>> 0) & 255];
+  return UTIL_BytesToHex(tmp);
+};
+
+/**
  * Opens the gnubby with the given index, or the first found gnubby if no
  * index is specified.
  * @param {GnubbyDeviceId} which The device to open. If null, the first
@@ -262,7 +275,7 @@
     if (!callback || !tid) return;  // Already done.
 
     console.error(UTIL_fmt(
-        '[' + self.cid.toString(16) + '] timeout!'));
+        '[' + Gnubby.hexCid(self.cid) + '] timeout!'));
 
     if (self.dev) {
       self.dev.destroy();  // Stop pretending this thing works.
@@ -283,7 +296,7 @@
     if (rcmd == GnubbyDevice.CMD_ERROR && totalLen == 1) {
       // Error from device; forward.
       console.log(UTIL_fmt(
-          '[' + self.cid.toString(16) + '] error frame ' +
+          '[' + Gnubby.hexCid(self.cid) + '] error frame ' +
           UTIL_BytesToHex(f)));
       if (f[7] == GnubbyDevice.GONE) {
         self.closed = true;
@@ -295,7 +308,7 @@
     if ((rcmd & 0x80)) {
       // Not an CONT frame, ignore.
       console.log(UTIL_fmt(
-          '[' + self.cid.toString(16) + '] ignoring non-cont frame ' +
+          '[' + Gnubby.hexCid(self.cid) + '] ignoring non-cont frame ' +
           UTIL_BytesToHex(f)));
       self.notifyFrame_(cont_frame);
       return;
@@ -304,7 +317,7 @@
     var seq = (rcmd & 0x7f);
     if (seq != seqno++) {
       console.log(UTIL_fmt(
-          '[' + self.cid.toString(16) + '] bad cont frame ' +
+          '[' + Gnubby.hexCid(self.cid) + '] bad cont frame ' +
           UTIL_BytesToHex(f)));
       schedule_cb(-GnubbyDevice.INVALID_SEQ);
       return;
@@ -337,7 +350,7 @@
       // Don't log busy frames, they're "normal".
       if (f[7] != GnubbyDevice.BUSY) {
         console.log(UTIL_fmt(
-            '[' + self.cid.toString(16) + '] error frame ' +
+            '[' + Gnubby.hexCid(self.cid) + '] error frame ' +
             UTIL_BytesToHex(f)));
       }
       if (f[7] == GnubbyDevice.GONE) {
@@ -350,7 +363,7 @@
     if (!(rcmd & 0x80)) {
       // Not an init frame, ignore.
       console.log(UTIL_fmt(
-          '[' + self.cid.toString(16) + '] ignoring non-init frame ' +
+          '[' + Gnubby.hexCid(self.cid) + '] ignoring non-init frame ' +
           UTIL_BytesToHex(f)));
       self.notifyFrame_(init_frame);
       return;
@@ -359,7 +372,7 @@
     if (rcmd != cmd) {
       // Not expected ack, read more.
       console.log(UTIL_fmt(
-          '[' + self.cid.toString(16) + '] ignoring non-ack frame ' +
+          '[' + Gnubby.hexCid(self.cid) + '] ignoring non-ack frame ' +
           UTIL_BytesToHex(f)));
       self.notifyFrame_(init_frame);
       return;
@@ -409,8 +422,7 @@
           (f[2] << 8) |
           (f[3]);
   return c === this.cid ||
-         c === Gnubby.NOTIFICATION_CID ||
-         c === Gnubby.BROADCAST_CID;
+         c === Gnubby.NOTIFICATION_CID;
 };
 
 /**
@@ -491,9 +503,7 @@
 
   function returnValue(rc) {
     done = true;
-    // Wait a bit to cater to devices being slow coming out of suspend.
-    // TODO: remove when usb fw gets fixed.
-    window.setTimeout(cb.bind(null, rc), 200);
+    window.setTimeout(cb.bind(null, rc), 0);
     if (self.closingWhenIdle) self.idleClose_();
   }
 
@@ -526,8 +536,10 @@
 
   function sendInitSentinel() {
     var cid = self.cid;
-    if (cid == Gnubby.defaultChannelId_(self.gnubbyInstance, self.which)) {
-      cid = Gnubby.BROADCAST_CID;
+    // If we do not have a specific CID yet, reset to BROADCAST for init.
+    if (self.cid == Gnubby.defaultChannelId_(self.gnubbyInstance, self.which)) {
+      self.cid = Gnubby.BROADCAST_CID;
+      cid = self.cid;
     }
     var cmd = GnubbyDevice.CMD_INIT;
     self.dev.queueCommand(cid, cmd, nonce);
@@ -560,6 +572,7 @@
       // INIT command not supported or is missing the returned channel id:
       // Pick a random cid to try to prevent collisions on the USB bus.
       var rnd = UTIL_getRandom(2);
+      self.cid = Gnubby.defaultChannelId_(self.gnubbyInstance, self.which);
       self.cid ^= (rnd[0] << 16) | (rnd[1] << 8);
       // Now sync with that cid, to make sure we've got it.
       setSync();
@@ -581,6 +594,12 @@
     // Stop on errors and return them.
     if (f[4] == GnubbyDevice.CMD_ERROR &&
         f[5] == 0 && f[6] == 1) {
+      if (f[7] == GnubbyDevice.BUSY) {
+        // Not spec but some devices do this; retry.
+        sendSentinel();
+        self.notifyFrame_(checkSentinel);
+        return;
+      }
       if (f[7] == GnubbyDevice.GONE) {
         // Device disappeared on us.
         self.closed = true;
diff --git a/chrome/browser/resources/cryptotoken/gnubbycodetypes.js b/chrome/browser/resources/cryptotoken/gnubbycodetypes.js
index d6eda06..94b8ab52 100644
--- a/chrome/browser/resources/cryptotoken/gnubbycodetypes.js
+++ b/chrome/browser/resources/cryptotoken/gnubbycodetypes.js
@@ -29,5 +29,4 @@
 
   /** Bad request. */
   'BAD_REQUEST': 12
-
 };
diff --git a/chrome/browser/resources/cryptotoken/gnubbydevice.js b/chrome/browser/resources/cryptotoken/gnubbydevice.js
index f7ef27f..77c3eb5 100644
--- a/chrome/browser/resources/cryptotoken/gnubbydevice.js
+++ b/chrome/browser/resources/cryptotoken/gnubbydevice.js
@@ -118,31 +118,3 @@
  * }}
  */
 var UsbDeviceSpec;
-
-/**
- * Gets the list of USB devices permitted by this app.
- * @param {function(!Array<!UsbDeviceSpec>)} cb Called back with a list of USB
- *     device specifiers.
- */
-GnubbyDevice.getPermittedUsbDevices = function(cb) {
-  chrome.permissions.getAll(function(perms) {
-    if (!perms.hasOwnProperty('permissions')) {
-      cb([]);
-      return;
-    }
-    var devs = [];
-    var permissions = perms['permissions'];
-    for (var i = 0; i < permissions.length; i++) {
-      var permission = permissions[i];
-      if (typeof permission === 'object' &&
-          permission.hasOwnProperty('usbDevices')) {
-        for (var j = 0; j < permission['usbDevices'].length; j++) {
-          var dev = permission['usbDevices'][j];
-          devs.push(
-              {'vendorId': dev['vendorId'], 'productId': dev['productId']});
-        }
-      }
-    }
-    cb(devs);
-  });
-};
diff --git a/chrome/browser/resources/cryptotoken/hidgnubbydevice.js b/chrome/browser/resources/cryptotoken/hidgnubbydevice.js
index 9c59323..e4b2ad3 100644
--- a/chrome/browser/resources/cryptotoken/hidgnubbydevice.js
+++ b/chrome/browser/resources/cryptotoken/hidgnubbydevice.js
@@ -99,7 +99,7 @@
     } else {
       changes = true;
       console.log(UTIL_fmt(
-          '[' + client.cid.toString(16) + '] left?'));
+          '[' + Gnubby.hexCid(client.cid) + '] left?'));
     }
   }
   if (changes) this.clients = remaining;
@@ -277,7 +277,7 @@
       this.lockTID = window.setTimeout(
           function() {
             console.warn(UTIL_fmt(
-                'lock for CID ' + cid.toString(16) + ' expired!'));
+                'lock for CID ' + Gnubby.hexCid(cid) + ' expired!'));
             self.lockTID = null;
             self.lockCID = 0;
           },
@@ -405,31 +405,55 @@
 };
 
 /**
+ * List of legacy HID devices that do not support the F1D0 usage page as
+ * mandated by the spec, but still need to be supported.
+ * TODO(juanlang): remove when these devices no longer need to be supported.
+ * @const
+ */
+HidGnubbyDevice.HID_VID_PIDS = [
+  {'vendorId': 4176, 'productId': 512}  // Google-specific Yubico HID
+];
+
+/**
  * @param {function(Array)} cb Enumeration callback
  */
 HidGnubbyDevice.enumerate = function(cb) {
-  var permittedDevs;
+  /**
+   * One pass using getDevices, and one for each of the hardcoded vid/pids.
+   * @const
+   */
+  var ENUMERATE_PASSES = 1 + HidGnubbyDevice.HID_VID_PIDS.length;
   var numEnumerated = 0;
   var allDevs = [];
 
   function enumerated(devs) {
-    allDevs = allDevs.concat(devs);
-    if (++numEnumerated == permittedDevs.length) {
+    // Don't double-add a device, it'll just confuse things.
+    for (var i = 0; i < devs.length; i++) {
+      var dev = devs[i];
+      // Unfortunately indexOf is not usable, since the two calls produce
+      // different objects. Compare their deviceIds instead.
+      var found = false;
+      for (var j = 0; j < allDevs.length; j++) {
+        if (allDevs[j].deviceId == dev.deviceId) {
+          found = true;
+          break;
+        }
+      }
+      if (!found) {
+        allDevs.push(dev);
+      }
+    }
+    if (++numEnumerated == ENUMERATE_PASSES) {
       cb(allDevs);
     }
   }
 
-  try {
-    chrome.hid.getDevices({filters: [{usagePage: 0xf1d0}]}, cb);
-  } catch (e) {
-    console.log(e);
-    console.log(UTIL_fmt('falling back to vid/pid enumeration'));
-    GnubbyDevice.getPermittedUsbDevices(function(devs) {
-      permittedDevs = devs;
-      for (var i = 0; i < devs.length; i++) {
-        chrome.hid.getDevices(devs[i], enumerated);
-      }
-    });
+  // Pass 1: usagePage-based enumeration.
+  chrome.hid.getDevices({filters: [{usagePage: 0xf1d0}]}, enumerated);
+  // Pass 2: vid/pid-based enumeration, for legacy devices.
+  for (var i = 0; i < HidGnubbyDevice.HID_VID_PIDS.length; i++) {
+    var dev = HidGnubbyDevice.HID_VID_PIDS[i];
+    chrome.hid.getDevices({filters: [dev]}, enumerated);
   }
 };
 
diff --git a/chrome/browser/resources/cryptotoken/manifest.json b/chrome/browser/resources/cryptotoken/manifest.json
index 212c8096..c48d96e 100644
--- a/chrome/browser/resources/cryptotoken/manifest.json
+++ b/chrome/browser/resources/cryptotoken/manifest.json
@@ -1,7 +1,7 @@
 {
   "name": "CryptoTokenExtension",
   "description": "CryptoToken Component Extension",
-  "version": "0.9.10",
+  "version": "0.9.20",
   "key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAq7zRobvA+AVlvNqkHSSVhh1sEWsHSqz4oR/XptkDe/Cz3+gW9ZGumZ20NCHjaac8j1iiesdigp8B1LJsd/2WWv2Dbnto4f8GrQ5MVphKyQ9WJHwejEHN2K4vzrTcwaXqv5BSTXwxlxS/mXCmXskTfryKTLuYrcHEWK8fCHb+0gvr8b/kvsi75A1aMmb6nUnFJvETmCkOCPNX5CHTdy634Ts/x0fLhRuPlahk63rdf7agxQv5viVjQFk+tbgv6aa9kdSd11Js/RZ9yZjrFgHOBWgP4jTBqud4+HUglrzu8qynFipyNRLCZsaxhm+NItTyNgesxLdxZcwOz56KD1Q4IQIDAQAB",
   "manifest_version": 2,
   "permissions": [
diff --git a/chrome/browser/resources/cryptotoken/usbgnubbydevice.js b/chrome/browser/resources/cryptotoken/usbgnubbydevice.js
index 82ca2f46..723d125 100644
--- a/chrome/browser/resources/cryptotoken/usbgnubbydevice.js
+++ b/chrome/browser/resources/cryptotoken/usbgnubbydevice.js
@@ -114,7 +114,7 @@
     } else {
       changes = true;
       console.log(UTIL_fmt(
-          '[' + client.cid.toString(16) + '] left?'));
+          '[' + Gnubby.hexCid(client.cid) + '] left?'));
     }
   }
   if (changes) this.clients = remaining;
@@ -346,7 +346,7 @@
       this.lockTID = window.setTimeout(
           function() {
             console.warn(UTIL_fmt(
-                'lock for CID ' + cid.toString(16) + ' expired!'));
+                'lock for CID ' + Gnubby.hexCid(cid) + ' expired!'));
             self.lockTID = null;
             self.lockCID = 0;
           },
diff --git a/chrome/browser/resources/cryptotoken/webrequest.js b/chrome/browser/resources/cryptotoken/webrequest.js
index ef2daf1..6919644f 100644
--- a/chrome/browser/resources/cryptotoken/webrequest.js
+++ b/chrome/browser/resources/cryptotoken/webrequest.js
@@ -258,7 +258,6 @@
 
     case ErrorCodes.TIMEOUT:
       return GnubbyCodeTypes.WAIT_TOUCH;
-
   }
   return GnubbyCodeTypes.UNKNOWN_ERROR;
 }
@@ -278,7 +277,6 @@
     case DeviceStatusCodes.WAIT_TOUCH_STATUS:
       return {errorCode: ErrorCodes.TIMEOUT};
 
-
     default:
       var reportedError = {
         errorCode: ErrorCodes.OTHER_ERROR,
diff --git a/chrome/browser/resources/chromeos/device_log_ui/OWNERS b/chrome/browser/resources/device_log_ui/OWNERS
similarity index 100%
rename from chrome/browser/resources/chromeos/device_log_ui/OWNERS
rename to chrome/browser/resources/device_log_ui/OWNERS
diff --git a/chrome/browser/resources/chromeos/device_log_ui/device_log_ui.css b/chrome/browser/resources/device_log_ui/device_log_ui.css
similarity index 100%
rename from chrome/browser/resources/chromeos/device_log_ui/device_log_ui.css
rename to chrome/browser/resources/device_log_ui/device_log_ui.css
diff --git a/chrome/browser/resources/chromeos/device_log_ui/device_log_ui.html b/chrome/browser/resources/device_log_ui/device_log_ui.html
similarity index 100%
rename from chrome/browser/resources/chromeos/device_log_ui/device_log_ui.html
rename to chrome/browser/resources/device_log_ui/device_log_ui.html
diff --git a/chrome/browser/resources/chromeos/device_log_ui/device_log_ui.js b/chrome/browser/resources/device_log_ui/device_log_ui.js
similarity index 100%
rename from chrome/browser/resources/chromeos/device_log_ui/device_log_ui.js
rename to chrome/browser/resources/device_log_ui/device_log_ui.js
diff --git a/chrome/browser/resources/extensions/extension_error_overlay.js b/chrome/browser/resources/extensions/extension_error_overlay.js
index 2b31335..32dcedff 100644
--- a/chrome/browser/resources/extensions/extension_error_overlay.js
+++ b/chrome/browser/resources/extensions/extension_error_overlay.js
@@ -242,7 +242,8 @@
           ExtensionErrorOverlay.requestFileSource(
               {extensionId: this.error_.extensionId,
                message: this.error_.message,
-               pathSuffix: getRelativeUrl(frame.url, this.extensionUrl_),
+               pathSuffix: getRelativeUrl(frame.url,
+                                          assert(this.extensionUrl_)),
                lineNumber: frame.lineNumber});
         }.bind(this, frame, frameNode));
 
diff --git a/chrome/browser/resources/extensions/extension_list.js b/chrome/browser/resources/extensions/extension_list.js
index d4fcbf6..6083af0 100644
--- a/chrome/browser/resources/extensions/extension_list.js
+++ b/chrome/browser/resources/extensions/extension_list.js
@@ -123,14 +123,15 @@
       var seenIds = [];
 
       // Iterate over the extension data and add each item to the list.
-      this.data_.extensions.forEach(function(extension) {
+      this.data_.extensions.forEach(function(extension, i) {
+        var nextExt = this.data_.extensions[i + 1];
         var node = $(extension.id);
         seenIds.push(extension.id);
 
         if (node)
           this.updateNode_(extension, node);
         else
-          this.createNode_(extension);
+          this.createNode_(extension, nextExt ? $(nextExt.id) : null);
       }, this);
 
       // Remove extensions that are no longer installed.
@@ -173,9 +174,11 @@
      * Synthesizes and initializes an HTML element for the extension metadata
      * given in |extension|.
      * @param {ExtensionData} extension A dictionary of extension metadata.
+     * @param {?Element} nextNode |node| should be inserted before |nextNode|.
+     *     |node| will be appended to the end if |nextNode| is null.
      * @private
      */
-    createNode_: function(extension) {
+    createNode_: function(extension, nextNode) {
       var template = $('template-collection').querySelector(
           '.extension-list-item-wrapper');
       var node = template.cloneNode(true);
@@ -296,7 +299,9 @@
         e.preventDefault();
       });
 
-      this.appendChild(node);
+      // Maintain the order that nodes should be in when creating as well as
+      // when adding only one new node.
+      this.insertBefore(node, nextNode);
       this.updateNode_(extension, node);
     },
 
diff --git a/chrome/browser/resources/extensions/extension_options_overlay.js b/chrome/browser/resources/extensions/extension_options_overlay.js
index 07684a9..5934073 100644
--- a/chrome/browser/resources/extensions/extension_options_overlay.js
+++ b/chrome/browser/resources/extensions/extension_options_overlay.js
@@ -127,8 +127,14 @@
       extensionoptions.onpreferredsizechanged = function(evt) {
         var oldWidth = parseInt(overlayStyle.width, 10);
         var oldHeight = parseInt(overlayStyle.height, 10);
-        var newWidth = Math.max(evt.width, minWidth);
-        var newHeight = Math.min(evt.height, maxHeight);
+        // The overlay must be slightly larger than the extension options to
+        // avoid creating scrollbars.
+        // TODO(paulmeyer): This shouldn't be necessary, but the preferred size
+        // (coming from Blink) seems to be too small for some zoom levels. The
+        // 2-pixel addition should be removed once this problem is investigated
+        // and corrected.
+        var newWidth = Math.max(evt.width + 2, minWidth);
+        var newHeight = Math.min(evt.height + 2, maxHeight);
 
         // animationTime is the amount of time in ms that will be used to resize
         // the overlay. It is calculated by multiplying the pythagorean distance
diff --git a/chrome/browser/resources/extensions_infobar.css b/chrome/browser/resources/extensions_infobar.css
deleted file mode 100644
index 5958777..0000000
--- a/chrome/browser/resources/extensions_infobar.css
+++ /dev/null
@@ -1,15 +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. */
-
-/**
- * The following style rules affect Extension Infobars.
- */
-
-body {
-  background: -webkit-linear-gradient(#E9E9E9, #DADADA);
-  font-size: 11px;
-  height: 36px; /* Infobars are limited to 36-72px */
-  margin: 0;
-  overflow: hidden;
-}
diff --git a/chrome/browser/resources/extensions_infobar_mac.css b/chrome/browser/resources/extensions_infobar_mac.css
deleted file mode 100644
index 7dfadd5..0000000
--- a/chrome/browser/resources/extensions_infobar_mac.css
+++ /dev/null
@@ -1,15 +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. */
-
-/**
- * The following style rules affect Extension Infobars on the Mac.
- */
-
-body {
-  background: -webkit-linear-gradient(#ebebeb, #cfcfcf);
-  font-size: 12px;
-  height: 36px;  /* Infobars are limited to 36-72px */
-  margin: 0 38px;
-  overflow: hidden;
-}
diff --git a/chrome/browser/resources/gaia_auth_host/authenticator.js b/chrome/browser/resources/gaia_auth_host/authenticator.js
index a80ec1e..8898e05 100644
--- a/chrome/browser/resources/gaia_auth_host/authenticator.js
+++ b/chrome/browser/resources/gaia_auth_host/authenticator.js
@@ -360,7 +360,10 @@
     // Posts a message to IdP pages to initiate communication.
     var currentUrl = this.webview_.src;
     if (currentUrl.lastIndexOf(this.idpOrigin_) == 0) {
-      this.webview_.contentWindow.postMessage({}, currentUrl);
+      var msg = {
+        'method': 'handshake'
+      };
+      this.webview_.contentWindow.postMessage(msg, currentUrl);
     }
   };
 
@@ -369,9 +372,9 @@
    * @private
    */
   Authenticator.prototype.onLoadStop_ = function(e) {
-    this.webview_.focus();
     if (!this.loaded_) {
       this.loaded_ = true;
+      this.webview_.focus();
       this.dispatchEvent(new Event('ready'));
     }
   };
diff --git a/chrome/browser/resources/google_now/background.js b/chrome/browser/resources/google_now/background.js
index 087b2ac97..e24f70b 100644
--- a/chrome/browser/resources/google_now/background.js
+++ b/chrome/browser/resources/google_now/background.js
@@ -95,6 +95,17 @@
 var SETTINGS_URL = 'https://support.google.com/chrome/?p=ib_google_now_welcome';
 
 /**
+ * GCM registration URL.
+ */
+var GCM_REGISTRATION_URL =
+    'https://android.googleapis.com/gcm/googlenotification';
+
+/**
+ * DevConsole project ID for GCM API use.
+ */
+var GCM_PROJECT_ID = '437902709571';
+
+/**
  * Number of cards that need an explanatory link.
  */
 var EXPLANATORY_CARDS_LINK_THRESHOLD = 4;
@@ -189,6 +200,8 @@
 var tasks = buildTaskManager(areTasksConflicting);
 
 // Add error processing to API calls.
+wrapper.instrumentChromeApiFunction('gcm.onMessage.addListener', 0);
+wrapper.instrumentChromeApiFunction('gcm.register', 1);
 wrapper.instrumentChromeApiFunction('metricsPrivate.getVariationParams', 1);
 wrapper.instrumentChromeApiFunction('notifications.clear', 1);
 wrapper.instrumentChromeApiFunction('notifications.create', 2);
@@ -204,10 +217,9 @@
 wrapper.instrumentChromeApiFunction(
     'notifications.onShowSettings.addListener', 0);
 wrapper.instrumentChromeApiFunction('permissions.contains', 1);
-wrapper.instrumentChromeApiFunction('pushMessaging.onMessage.addListener', 0);
-wrapper.instrumentChromeApiFunction('storage.onChanged.addListener', 0);
 wrapper.instrumentChromeApiFunction('runtime.onInstalled.addListener', 0);
 wrapper.instrumentChromeApiFunction('runtime.onStartup.addListener', 0);
+wrapper.instrumentChromeApiFunction('storage.onChanged.addListener', 0);
 wrapper.instrumentChromeApiFunction('tabs.create', 1);
 
 var updateCardsAttempts = buildAttemptManager(
@@ -1015,6 +1027,7 @@
  */
 function initialize() {
   recordEvent(GoogleNowEvent.EXTENSION_START);
+  registerForGcm();
   onStateChange();
 }
 
@@ -1204,6 +1217,100 @@
 }
 
 /**
+ * Ensures the extension is ready to listen for GCM messages.
+ */
+function registerForGcm() {
+  // We don't need to use the key yet, just ensure the channel is set up.
+  getGcmNotificationKey();
+}
+
+/**
+ * Returns a Promise resolving to either a cached or new GCM notification key.
+ * Rejects if registration fails.
+ * @return {Promise} A Promise that resolves to a potentially-cached GCM key.
+ */
+function getGcmNotificationKey() {
+  return fillFromChromeLocalStorage({gcmNotificationKey: undefined})
+      .then(function(items) {
+        if (items.gcmNotificationKey) {
+          console.log('Reused gcm key from storage.');
+          return Promise.resolve(items.gcmNotificationKey);
+        }
+        return requestNewGcmNotificationKey();
+      });
+}
+
+/**
+ * Returns a promise resolving to a GCM Notificaiton Key. May call
+ * chrome.gcm.register() first if required. Rejects on registration failure.
+ * @return {Promise} A Promise that resolves to a fresh GCM Notification key.
+ */
+function requestNewGcmNotificationKey() {
+  return getGcmRegistrationId().then(function(gcmId) {
+    authenticationManager.getAuthToken().then(function(token) {
+      authenticationManager.getLogin().then(function(username) {
+        return new Promise(function(resolve, reject) {
+          var xhr = new XMLHttpRequest();
+          xhr.responseType = 'application/json';
+          xhr.open('POST', GCM_REGISTRATION_URL, true);
+          xhr.setRequestHeader('Content-Type', 'application/json');
+          xhr.setRequestHeader('Authorization', 'Bearer ' + token);
+          xhr.setRequestHeader('project_id', GCM_PROJECT_ID);
+          var payload = {
+            'operation': 'add',
+            'notification_key_name': username,
+            'registration_ids': [gcmId]
+          };
+          xhr.onloadend = function() {
+            if (xhr.status != 200) {
+              reject();
+            }
+            var obj = JSON.parse(xhr.responseText);
+            var key = obj && obj.notification_key;
+            if (!key) {
+              reject();
+            }
+            console.log('gcm notification key POST: ' + key);
+            chrome.storage.local.set({gcmNotificationKey: key});
+            resolve(key);
+          };
+          xhr.send(JSON.stringify(payload));
+        });
+      });
+    }).catch(function() {
+      // Couldn't obtain a GCM ID. Ignore and fallback to polling.
+    });
+  });
+}
+
+/**
+ * Returns a promise resolving to either a cached or new GCM registration ID.
+ * Rejects if registration fails.
+ * @return {Promise} A Promise that resolves to a GCM registration ID.
+ */
+function getGcmRegistrationId() {
+  return fillFromChromeLocalStorage({gcmRegistrationId: undefined})
+      .then(function(items) {
+        if (items.gcmRegistrationId) {
+          console.log('Reused gcm registration id from storage.');
+          return Promise.resolve(items.gcmRegistrationId);
+        }
+
+        return new Promise(function(resolve, reject) {
+          instrumented.gcm.register([GCM_PROJECT_ID], function(registrationId) {
+            console.log('gcm.register(): ' + registrationId);
+            if (registrationId) {
+              chrome.storage.local.set({gcmRegistrationId: registrationId});
+              resolve(registrationId);
+            } else {
+              reject();
+            }
+          });
+        });
+      });
+}
+
+/**
  * Polls the optin state.
  * Sometimes we get the response to the opted in result too soon during
  * push messaging. We'll recheck the optin state a few times before giving up.
@@ -1334,13 +1441,15 @@
   }
 });
 
-instrumented.pushMessaging.onMessage.addListener(function(message) {
-  // message.payload will be '' when the extension first starts.
-  // Each time after signing in, we'll get latest payload for all channels.
-  // So, we need to poll the server only when the payload is non-empty and has
-  // changed.
-  console.log('pushMessaging.onMessage ' + JSON.stringify(message));
-  if (message.payload.indexOf('REQUEST_CARDS') == 0) {
+instrumented.gcm.onMessage.addListener(function(message) {
+  console.log('gcm.onMessage ' + JSON.stringify(message));
+  if (!message || !message.data) {
+    return;
+  }
+
+  var payload = message.data.payload;
+  var tag = message.data.tag;
+  if (payload.indexOf('REQUEST_CARDS') == 0) {
     tasks.add(ON_PUSH_MESSAGE_START_TASK_NAME, function() {
       // Accept promise rejection on failure since it's safer to do nothing,
       // preventing polling the server when the payload really didn't change.
@@ -1349,11 +1458,10 @@
         /** @type {Object<string, StoredNotificationGroup>} */
         notificationGroups: {}
       }, PromiseRejection.ALLOW).then(function(items) {
-        if (items.lastPollNowPayloads[message.subchannelId] !=
-            message.payload) {
-          items.lastPollNowPayloads[message.subchannelId] = message.payload;
+        if (items.lastPollNowPayloads[tag] != payload) {
+          items.lastPollNowPayloads[tag] = payload;
 
-          items.notificationGroups['PUSH' + message.subchannelId] = {
+          items.notificationGroups['PUSH' + tag] = {
             cards: [],
             nextPollTime: Date.now()
           };
diff --git a/chrome/browser/resources/google_now/background_test_util.js b/chrome/browser/resources/google_now/background_test_util.js
index 52d77daf..7f6e4aa 100644
--- a/chrome/browser/resources/google_now/background_test_util.js
+++ b/chrome/browser/resources/google_now/background_test_util.js
@@ -18,12 +18,12 @@
 var buildCardSet = emptyMock;
 
 var instrumented = {};
+mockChromeEvent(instrumented, 'gcm.onMessage');
 mockChromeEvent(instrumented, 'notifications.onButtonClicked');
 mockChromeEvent(instrumented, 'notifications.onClicked');
 mockChromeEvent(instrumented, 'notifications.onClosed');
 mockChromeEvent(instrumented, 'notifications.onPermissionLevelChanged');
 mockChromeEvent(instrumented, 'notifications.onShowSettings');
-mockChromeEvent(instrumented, 'pushMessaging.onMessage');
 mockChromeEvent(instrumented, 'runtime.onInstalled');
 mockChromeEvent(instrumented, 'runtime.onStartup');
 mockChromeEvent(instrumented, 'storage.onChanged');
diff --git a/chrome/browser/resources/google_now/background_unittest.gtestjs b/chrome/browser/resources/google_now/background_unittest.gtestjs
index 6a6f125..a9578a8 100644
--- a/chrome/browser/resources/google_now/background_unittest.gtestjs
+++ b/chrome/browser/resources/google_now/background_unittest.gtestjs
@@ -713,6 +713,10 @@
   fixture.mockGlobals.stubs().recordEvent(ANYTHING);
   fixture.mockGlobals.
       expects(once()).recordEvent(GoogleNowEvent.EXTENSION_START);
+  fixture.mockApis.expects(once())
+      .fillFromChromeLocalStorage(eqJSON({gcmNotificationKey: undefined}))
+      .will(returnValue(Promise.resolve({gcmNotificationKey: 'gcmkey'})));
+
 }
 
 TEST_F(TEST_NAME,'Initialize_SignedOut', function() {
diff --git a/chrome/browser/resources/google_now/manifest.json b/chrome/browser/resources/google_now/manifest.json
index 1eb6e911..bdca3229 100644
--- a/chrome/browser/resources/google_now/manifest.json
+++ b/chrome/browser/resources/google_now/manifest.json
@@ -11,16 +11,17 @@
   "key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAkhqJr32OFD/bMXW4Md7jMfd7LbwHXVc6x5bBQG5U+dloofoxrICDR20yur/40mQ8O//0sS1b8srvbab1CRlSrxoNCr9T80NAkfzx0gHyVS+p1Zow+1FzLMu9PiGwwFyN80HIB7GI/dIa0wC9K/2OrrzcHEhVH96DacTtWQqjfDVtZPjT7Xwv23dgoWcpbkRC86jMJot3dmX9xnn0KzoVc9gDOHSIkBLbkkr6Sp3LGXCCM4L0DJgxdFwaLr5WBzgC3y5x0/wwPIwN4PtIaK3BhH6njlksfnKwwIJ9iRT41V4BqbWu4mszO/7VJ3HJyw2DBpIc2grU9ZRRxrV3fRQG4wIDAQAB",
   "permissions": [
     "alarms",
+    "gcm",
     "identity",
     "metricsPrivate",
     "notifications",
-    "pushMessaging",
     "storage",
     "tabs",
     "webstorePrivate",
     "*://*.google.com/*",
     "*://*.gstatic.com/*",
     "https://*.googleapis.com/chromenow/v1/*",
+    "https://*.googleapis.com/gcm/*",
     "https://*.googleusercontent.com/*"
   ],
   "optional_permissions": ["background"],
@@ -31,6 +32,9 @@
   },
   "oauth2": {
     "auto_approve": true,
-    "scopes": ["https://www.googleapis.com/auth/googlenow"]
+    "scopes": [
+      "https://www.googleapis.com/auth/gcm",
+      "https://www.googleapis.com/auth/googlenow"
+    ]
   }
 }
diff --git a/chrome/browser/resources/google_now/utility.js b/chrome/browser/resources/google_now/utility.js
index dfcb8d8..c5b3d57 100644
--- a/chrome/browser/resources/google_now/utility.js
+++ b/chrome/browser/resources/google_now/utility.js
@@ -982,15 +982,25 @@
   }
 
   /**
+   * Determines the active account's login (username).
+   * @return {Promise} A promise to determine the current account's login.
+   */
+  function getLogin() {
+    return new Promise(function(resolve) {
+      instrumented.webstorePrivate.getBrowserLogin(function(accountInfo) {
+        resolve(accountInfo.login);
+      });
+    });
+  }
+
+  /**
    * Determines whether there is an account attached to the profile.
    * @return {Promise} A promise to determine if there is an account attached
    *     to the profile.
    */
   function isSignedIn() {
-    return new Promise(function(resolve) {
-      instrumented.webstorePrivate.getBrowserLogin(function(accountInfo) {
-        resolve(!!accountInfo.login);
-      });
+    return getLogin().then(function(login) {
+      return Promise.resolve(!!login);
     });
   }
 
@@ -1055,6 +1065,7 @@
   return {
     addListener: addListener,
     getAuthToken: getAuthToken,
+    getLogin: getLogin,
     isSignedIn: isSignedIn,
     removeToken: removeToken
   };
diff --git a/chrome/browser/resources/hotword/constants.js b/chrome/browser/resources/hotword/constants.js
index ce5dc98e3..55a3d30 100644
--- a/chrome/browser/resources/hotword/constants.js
+++ b/chrome/browser/resources/hotword/constants.js
@@ -42,12 +42,30 @@
 var SPEAKER_MODEL_FILE_NAME = 'speaker_model.data';
 
 /**
- * The size of the file system requested for reading the speaker model file.
- * As of January 27th, 2015, the speaker model is 290 bytes. This number
- * should always be larger than the speaker model file size.
+ * The training utterance file name prefix.
+ * @const {string}
+ */
+var UTTERANCE_FILE_PREFIX = 'utterance-';
+
+/**
+ * The training utterance file extension.
+ * @const {string}
+ */
+var UTTERANCE_FILE_EXTENSION = '.raw';
+
+/**
+ * The number of training utterances required to train the speaker model.
  * @const {number}
  */
-var FILE_SYSTEM_SIZE_BYTES = 526;
+var NUM_TRAINING_UTTERANCES = 3;
+
+/**
+ * The size of the file system requested for reading the speaker model and
+ * utterances. This number should always be larger than the combined file size,
+ * currently 576338 bytes as of February 2015.
+ * @const {number}
+ */
+var FILE_SYSTEM_SIZE_BYTES = 1048576;
 
 /**
  * Time to wait for expected messages, in milliseconds.
@@ -249,10 +267,13 @@
   CLIENT_PORT_NAME: CLIENT_PORT_NAME,
   COMMAND_FIELD_NAME: COMMAND_FIELD_NAME,
   FILE_SYSTEM_SIZE_BYTES: FILE_SYSTEM_SIZE_BYTES,
+  NUM_TRAINING_UTTERANCES: NUM_TRAINING_UTTERANCES,
   SHARED_MODULE_ID: SHARED_MODULE_ID,
   SHARED_MODULE_ROOT: SHARED_MODULE_ROOT,
   SPEAKER_MODEL_FILE_NAME: SPEAKER_MODEL_FILE_NAME,
   UI_LANGUAGE: UI_LANGUAGE,
+  UTTERANCE_FILE_EXTENSION: UTTERANCE_FILE_EXTENSION,
+  UTTERANCE_FILE_PREFIX: UTTERANCE_FILE_PREFIX,
   CommandToPage: CommandToPage,
   CommandFromPage: CommandFromPage,
   Error: Error,
diff --git a/chrome/browser/resources/hotword/manifest.json b/chrome/browser/resources/hotword/manifest.json
index 155c241..727b295 100644
--- a/chrome/browser/resources/hotword/manifest.json
+++ b/chrome/browser/resources/hotword/manifest.json
@@ -28,11 +28,22 @@
   },
 
   "permissions": [
+    "*://*.google.at/*",
+    "*://*.google.ca/*",
     "*://*.google.com/*",
-    "*://*.google.ru/*",
+    "*://*.google.com.au/*",
+    "*://*.google.com.mx/*",
+    "*://*.google.com.br/*",
+    "*://*.google.co.jp/*",
+    "*://*.google.co.kr/*",
+    "*://*.google.co.nz/*",
     "*://*.google.co.uk/*",
-    "*://*.google.fr/*",
+    "*://*.google.co.za/*",
     "*://*.google.de/*",
+    "*://*.google.es/*",
+    "*://*.google.fr/*",
+    "*://*.google.it/*",
+    "*://*.google.ru/*",
     "chrome://newtab/",
     "chrome://resources/",
     "audioCapture",
diff --git a/chrome/browser/resources/hotword/page_audio_manager.js b/chrome/browser/resources/hotword/page_audio_manager.js
index 2118e204..43b763b8 100644
--- a/chrome/browser/resources/hotword/page_audio_manager.js
+++ b/chrome/browser/resources/hotword/page_audio_manager.js
@@ -87,10 +87,21 @@
       ];
       // TODO(amistry): Get this list from a file in the shared module instead.
       var tlds = [
+        'at',
+        'ca',
         'com',
+        'com.au',
+        'com.mx',
+        'com.br',
+        'co.jp',
+        'co.kr',
+        'co.nz',
         'co.uk',
+        'co.za',
         'de',
+        'es',
         'fr',
+        'it',
         'ru'
       ];
 
diff --git a/chrome/browser/resources/hotword/state_manager.js b/chrome/browser/resources/hotword/state_manager.js
index 1b94e70..89148ca 100644
--- a/chrome/browser/resources/hotword/state_manager.js
+++ b/chrome/browser/resources/hotword/state_manager.js
@@ -216,7 +216,8 @@
      */
     isAlwaysOnEnabled: function() {
       assert(this.hotwordStatus_, 'No hotword status (isAlwaysOnEnabled)');
-      return this.hotwordStatus_.alwaysOnEnabled;
+      return this.hotwordStatus_.alwaysOnEnabled &&
+          !this.hotwordStatus_.trainingEnabled;
     },
 
     /**
@@ -485,6 +486,12 @@
             UmaTriggerSources_[session.source_],
             hotword.constants.UmaTriggerSource.MAX);
       }
+
+      // If we're in always-on mode, shut down the hotword detector. The hotword
+      // stream requires that we close and re-open it after a trigger, and the
+      // only way to accomplish this is to shut everything down.
+      if (this.isAlwaysOnEnabled())
+        this.shutdownDetector_();
     },
 
     /**
diff --git a/chrome/browser/resources/hotword/training_manager.js b/chrome/browser/resources/hotword/training_manager.js
index 1bddc5f..61c68c2 100644
--- a/chrome/browser/resources/hotword/training_manager.js
+++ b/chrome/browser/resources/hotword/training_manager.js
@@ -34,20 +34,34 @@
    */
   TrainingManager.onRequestFileSystemSuccess_ = function(fs) {
     fs.root.getFile(hotword.constants.SPEAKER_MODEL_FILE_NAME, {create: false},
-        function(fileEntry) {
-          if (fileEntry.isFile) {
-            hotword.debug('File found: ' + fileEntry.fullPath);
-            if (hotword.DEBUG || window.localStorage['hotword.DEBUG']) {
-              fileEntry.getMetadata(function(md) {
-                hotword.debug('Speaker model file size: ' + md.size);
-              });
-            }
-            fileEntry.remove(function() {
-                hotword.debug('File removed.');
-            }, TrainingManager.fileErrorHandler_);
-          }
-        }, TrainingManager.fileErrorHandler_);
-    };
+        TrainingManager.deleteFile_, TrainingManager.fileErrorHandler_);
+
+    for (var i = 0; i < hotword.constants.NUM_TRAINING_UTTERANCES; ++i) {
+      fs.root.getFile(hotword.constants.UTTERANCE_FILE_PREFIX + i +
+          hotword.constants.UTTERANCE_FILE_EXTENSION,
+          {create: false},
+          TrainingManager.deleteFile_, TrainingManager.fileErrorHandler_);
+    }
+  };
+
+  /**
+   * Deletes a file.
+   * @param {FileEntry} fileEntry The FileEntry object.
+   * @private
+   */
+  TrainingManager.deleteFile_ = function(fileEntry) {
+    if (fileEntry.isFile) {
+      hotword.debug('File found: ' + fileEntry.fullPath);
+      if (hotword.DEBUG || window.localStorage['hotword.DEBUG']) {
+        fileEntry.getMetadata(function(md) {
+          hotword.debug('File size: ' + md.size);
+        });
+      }
+      fileEntry.remove(function() {
+          hotword.debug('File removed: ' + fileEntry.fullPath);
+      }, TrainingManager.fileErrorHandler_);
+    }
+  };
 
   /**
    * Handles a failure event on mounting the file system event.
diff --git a/chrome/browser/resources/inspect/inspect.js b/chrome/browser/resources/inspect/inspect.js
index 2f0ae49..b278aeb 100644
--- a/chrome/browser/resources/inspect/inspect.js
+++ b/chrome/browser/resources/inspect/inspect.js
@@ -264,11 +264,7 @@
 
     for (var b = 0; b < device.browsers.length; b++) {
       var browser = device.browsers[b];
-
       var majorChromeVersion = browser.adbBrowserChromeVersion;
-
-      var incompatibleVersion = browser.hasOwnProperty('compatibleVersion') &&
-                                !browser.compatibleVersion;
       var pageList;
       var browserSection = $(browser.id);
       if (browserSection) {
@@ -296,7 +292,7 @@
         }
         browserSection.appendChild(browserHeader);
 
-        if (!incompatibleVersion && majorChromeVersion >= MIN_VERSION_NEW_TAB) {
+        if (majorChromeVersion >= MIN_VERSION_NEW_TAB) {
           var newPage = document.createElement('div');
           newPage.className = 'open';
 
@@ -330,15 +326,6 @@
             'forwarding. Closing it will drop current connections.';
         browserHeader.appendChild(portForwardingInfo);
 
-        if (incompatibleVersion) {
-          var warningSection = document.createElement('div');
-          warningSection.className = 'warning';
-          warningSection.textContent =
-            'You may need a newer version of desktop Chrome. ' +
-            'Please try Chrome ' + browser.adbBrowserVersion + ' or later.';
-          browserSection.appendChild(warningSection);
-        }
-
         if (browserInspector) {
           var link = document.createElement('span');
           link.classList.add('action');
@@ -356,7 +343,7 @@
         browserSection.appendChild(pageList);
       }
 
-      if (!incompatibleVersion && !alreadyDisplayed(browserSection, browser)) {
+      if (!alreadyDisplayed(browserSection, browser)) {
         pageList.textContent = '';
         for (var p = 0; p < browser.pages.length; p++) {
           var page = browser.pages[p];
diff --git a/chrome/browser/resources/md_settings/OWNERS b/chrome/browser/resources/md_settings/OWNERS
new file mode 100644
index 0000000..057813143
--- /dev/null
+++ b/chrome/browser/resources/md_settings/OWNERS
@@ -0,0 +1,2 @@
+michaelpg@chromium.org
+stevenjb@chromium.org
diff --git a/chrome/browser/resources/md_settings/md_settings.html b/chrome/browser/resources/md_settings/md_settings.html
index 0211eff..ec75331 100644
--- a/chrome/browser/resources/md_settings/md_settings.html
+++ b/chrome/browser/resources/md_settings/md_settings.html
@@ -3,10 +3,15 @@
 <head>
   <meta charset="utf-8">
   <title>Settings</title>
+  <link rel="import" href="chrome://resources/polymer/paper-button/paper-button.html">
+  <link rel="import" href="chrome://resources/polymer/paper-item/paper-item.html">
+  <link rel="import" href="chrome://resources/polymer/core-menu/core-menu.html">
+  <link rel="import"
+      href="chrome://resources/cr_elements/cr_checkbox/cr_checkbox.html">
   <link rel="import"
       href="chrome://resources/cr_elements/cr_toggle_button/cr_toggle_button.html">
   <link rel="import"
-      href="chrome://resources/polymer/paper-button/paper-button.html">
+      href="chrome://resources/cr_elements/cr_dropdown_menu/cr_dropdown_menu.html">
 </head>
 <body>
   <header>
@@ -15,6 +20,17 @@
   <div class="main">
     <paper-button id="manage-button" raised>Manage</paper-button>
     <cr-toggle-button checked></cr-toggle-button>
+    <cr-checkbox checked></cr-checkbox>
+    <section>
+      <h2>cr-dropdown-menu</h2>
+      <cr-dropdown-menu style="width: 200px">
+        <core-menu class="menu">
+          <paper-item>Chrome</paper-item>
+          <paper-item>IE</paper-item>
+          <paper-item>Firefox</paper-item>
+        </core-menu>
+      </cr-dropdown-menu>
+    </section>
   </div>
 </body>
 </html>
diff --git a/chrome/browser/resources/options/browser_options.html b/chrome/browser/resources/options/browser_options.html
index 52d2d4e..7c800e5e 100644
--- a/chrome/browser/resources/options/browser_options.html
+++ b/chrome/browser/resources/options/browser_options.html
@@ -157,6 +157,14 @@
               i18n-content="defaultSearchManageEngines">
           </button>
         </div>
+        <div id="google-now-launcher" hidden>
+          <div class="checkbox">
+            <label>
+              <input pref="google_now_launcher.enabled" type="checkbox">
+              <span i18n-content="googleNowLauncherEnable"></span>
+            </label>
+          </div>
+        </div>
         <div id="hotword-always-on-search" hidden>
           <div class="checkbox controlled-setting-with-label">
             <label>
@@ -423,11 +431,11 @@
             </span>
         </label>
       </div>
-      <div id="metricsReportingSetting"
+      <div id="metrics-reporting-setting"
           class="checkbox controlled-setting-with-label">
 <if expr="chromeos">
         <label>
-          <input id="metricsReportingEnabled"
+          <input id="metrics-reporting-enabled"
               pref="cros.metrics.reportingEnabled" type="checkbox">
           <span>
             <span i18n-content="enableLogging"></span>
@@ -438,7 +446,7 @@
 </if>
 <if expr="not chromeos">
         <label>
-          <input id="metricsReportingEnabled" type="checkbox">
+          <input id="metrics-reporting-enabled" type="checkbox">
           <span>
             <span i18n-content="enableLogging"></span>
             <span id="metrics-reporting-disabled-icon"
@@ -452,7 +460,7 @@
         </span>
 </if>
       </div>
-      <div class="checkbox controlled-setting-with-label">
+      <div id="rappor-setting" class="checkbox controlled-setting-with-label">
         <label>
           <input pref="rappor.enabled" type="checkbox"
               metric="Options_RapporEnabledCheckbox">
diff --git a/chrome/browser/resources/options/browser_options.js b/chrome/browser/resources/options/browser_options.js
index c47916b..3efebf9f 100644
--- a/chrome/browser/resources/options/browser_options.js
+++ b/chrome/browser/resources/options/browser_options.js
@@ -246,6 +246,8 @@
           $('hotword-always-on-search-checkbox');
       chrome.send('requestHotwordAvailable');
 
+      chrome.send('requestGoogleNowAvailable');
+
       if ($('set-wallpaper')) {
         $('set-wallpaper').onclick = function(event) {
           chrome.send('openWallpaperManager');
@@ -435,13 +437,13 @@
       // 'metricsReportingEnabled' element is only present on Chrome branded
       // builds, and the 'metricsReportingCheckboxAction' message is only
       // handled on ChromeOS.
-      if ($('metricsReportingEnabled') && cr.isChromeOS) {
-        $('metricsReportingEnabled').onclick = function(event) {
+      if ($('metrics-reporting-enabled') && cr.isChromeOS) {
+        $('metrics-reporting-enabled').onclick = function(event) {
           chrome.send('metricsReportingCheckboxAction',
               [String(event.currentTarget.checked)]);
         };
       }
-      if ($('metricsReportingEnabled') && !cr.isChromeOS) {
+      if ($('metrics-reporting-enabled') && !cr.isChromeOS) {
         // The localized string has the | symbol on each side of the text that
         // needs to be made into a button to restart Chrome. We parse the text
         // and build the button from that.
@@ -462,17 +464,22 @@
         var updateMetricsRestartButton = function() {
           $('metrics-reporting-reset-restart').hidden =
               loadTimeData.getBoolean('metricsReportingEnabledAtStart') ==
-                  $('metricsReportingEnabled').checked;
+                  $('metrics-reporting-enabled').checked;
         };
-        $('metricsReportingEnabled').onclick = function(event) {
+        $('metrics-reporting-enabled').onclick = function(event) {
           chrome.send('metricsReportingCheckboxChanged',
               [Boolean(event.currentTarget.checked)]);
           updateMetricsRestartButton();
         };
-        $('metricsReportingEnabled').checked =
+        $('metrics-reporting-enabled').checked =
             loadTimeData.getBoolean('metricsReportingEnabledAtStart');
         updateMetricsRestartButton();
       }
+      // 'rappor-setting' element is only present on Chrome branded builds.
+      if ($('rappor-setting')) {
+        $('rappor-setting').hidden =
+            !loadTimeData.getBoolean('hasRapporOption');
+      }
       $('networkPredictionOptions').onchange = function(event) {
         var value = (event.target.checked ?
             NetworkPredictionOptions.WIFI_ONLY :
@@ -1250,6 +1257,15 @@
     },
 
     /**
+     * Controls the visibility of the Now settings.
+     * @param {boolean} visible Whether to show Now settings.
+     * @private
+     */
+    setNowSectionVisible_: function(visible) {
+      $('google-now-launcher').hidden = !visible;
+    },
+
+    /**
      * Activates the Audio History section of the Settings page.
      * @param {boolean} visible Whether the audio history section is visible.
      * @param {string} labelText Text describing current audio history state.
@@ -1698,8 +1714,8 @@
      * @private
      */
     setMetricsReportingCheckboxState_: function(checked, disabled) {
-      $('metricsReportingEnabled').checked = checked;
-      $('metricsReportingEnabled').disabled = disabled;
+      $('metrics-reporting-enabled').checked = checked;
+      $('metrics-reporting-enabled').disabled = disabled;
 
       // If checkbox gets disabled then add an attribute for displaying the
       // special icon. Otherwise remove the indicator attribute.
@@ -1716,9 +1732,9 @@
      */
     setMetricsReportingSettingVisibility_: function(visible) {
       if (visible)
-        $('metricsReportingSetting').style.display = 'block';
+        $('metrics-reporting-setting').style.display = 'block';
       else
-        $('metricsReportingSetting').style.display = 'none';
+        $('metrics-reporting-setting').style.display = 'none';
     },
 
     /**
@@ -2169,6 +2185,7 @@
     'setHotwordRetrainLinkVisible',
     'setNativeThemeButtonEnabled',
     'setNetworkPredictionValue',
+    'setNowSectionVisible',
     'setHighContrastCheckboxState',
     'setAllHotwordSectionsVisible',
     'setMetricsReportingCheckboxState',
diff --git a/chrome/browser/resources/plugin_metadata/plugins_chromeos.json b/chrome/browser/resources/plugin_metadata/plugins_chromeos.json
index 57e36857..86c4bae 100644
--- a/chrome/browser/resources/plugin_metadata/plugins_chromeos.json
+++ b/chrome/browser/resources/plugin_metadata/plugins_chromeos.json
@@ -42,5 +42,18 @@
     ],
     "name": "Chrome PDF Viewer",
     "group_name_matcher": "*Chrome PDF Viewer*"
+  },
+  "chromium-pdf": {
+    "mime_types": [
+    ],
+    "versions": [
+      {
+        "version": "0",
+        "status": "up_to_date",
+        "comment": "Chrome PDF Viewer has no version information."
+      }
+    ],
+    "name": "Chromium PDF Viewer",
+    "group_name_matcher": "*Chromium PDF Viewer*"
   }
 }
diff --git a/chrome/browser/resources/plugin_metadata/plugins_linux.json b/chrome/browser/resources/plugin_metadata/plugins_linux.json
index 7ddd998..b040204e 100644
--- a/chrome/browser/resources/plugin_metadata/plugins_linux.json
+++ b/chrome/browser/resources/plugin_metadata/plugins_linux.json
@@ -123,5 +123,18 @@
     ],
     "name": "Chrome PDF Viewer",
     "group_name_matcher": "*Chrome PDF Viewer*"
+  },
+  "chromium-pdf": {
+    "mime_types": [
+    ],
+    "versions": [
+      {
+        "version": "0",
+        "status": "up_to_date",
+        "comment": "Chrome PDF Viewer has no version information."
+      }
+    ],
+    "name": "Chromium PDF Viewer",
+    "group_name_matcher": "*Chromium PDF Viewer*"
   }
 }
diff --git a/chrome/browser/resources/plugin_metadata/plugins_mac.json b/chrome/browser/resources/plugin_metadata/plugins_mac.json
index 1f1195f..b0831c1 100644
--- a/chrome/browser/resources/plugin_metadata/plugins_mac.json
+++ b/chrome/browser/resources/plugin_metadata/plugins_mac.json
@@ -284,6 +284,19 @@
     "name": "Chrome PDF Viewer",
     "group_name_matcher": "*Chrome PDF Viewer*"
   },
+  "chromium-pdf": {
+    "mime_types": [
+    ],
+    "versions": [
+      {
+        "version": "0",
+        "status": "up_to_date",
+        "comment": "Chrome PDF Viewer has no version information."
+      }
+    ],
+    "name": "Chromium PDF Viewer",
+    "group_name_matcher": "*Chromium PDF Viewer*"
+  },
   "facebook-video-calling": {
     "mime_types": [
       "application/skypesdk-plugin"
diff --git a/chrome/browser/resources/plugin_metadata/plugins_win.json b/chrome/browser/resources/plugin_metadata/plugins_win.json
index 579a402..5317809 100644
--- a/chrome/browser/resources/plugin_metadata/plugins_win.json
+++ b/chrome/browser/resources/plugin_metadata/plugins_win.json
@@ -346,6 +346,19 @@
     "name": "Chrome PDF Viewer",
     "group_name_matcher": "*Chrome PDF Viewer*"
   },
+  "chromium-pdf": {
+    "mime_types": [
+    ],
+    "versions": [
+      {
+        "version": "0",
+        "status": "up_to_date",
+        "comment": "Chrome PDF Viewer has no version information."
+      }
+    ],
+    "name": "Chromium PDF Viewer",
+    "group_name_matcher": "*Chromium PDF Viewer*"
+  },
   "google-update": {
     "mime-types": [
     ],
diff --git a/chrome/browser/resources/print_preview/data/destination_store.js b/chrome/browser/resources/print_preview/data/destination_store.js
index ef1c4e3..c752cd74 100644
--- a/chrome/browser/resources/print_preview/data/destination_store.js
+++ b/chrome/browser/resources/print_preview/data/destination_store.js
@@ -327,11 +327,10 @@
           !this.appState_.selectedDestinationOrigin) {
         this.selectDefaultDestination_();
       } else {
-        assert(typeof this.appState_.selectedDestinationAccount == 'string');
         var key = this.getDestinationKey_(
             this.appState_.selectedDestinationOrigin,
             this.appState_.selectedDestinationId,
-            this.appState_.selectedDestinationAccount);
+            this.appState_.selectedDestinationAccount || '');
         var candidate = this.destinationMap_[key];
         if (candidate != null) {
           this.selectDestination(candidate);
@@ -347,7 +346,7 @@
           this.cloudPrintInterface_.printer(
               this.appState_.selectedDestinationId,
               this.appState_.selectedDestinationOrigin,
-              this.appState_.selectedDestinationAccount);
+              this.appState_.selectedDestinationAccount || '');
         } else if (this.appState_.selectedDestinationOrigin ==
                    print_preview.Destination.Origin.PRIVET) {
           // TODO(noamsml): Resolve a specific printer instead of listing all
diff --git a/chrome/browser/resources/print_preview/search/destination_list.js b/chrome/browser/resources/print_preview/search/destination_list.js
index cffe09c..86112580 100644
--- a/chrome/browser/resources/print_preview/search/destination_list.js
+++ b/chrome/browser/resources/print_preview/search/destination_list.js
@@ -151,6 +151,14 @@
           DestinationList.HEIGHT_OF_ITEM_);
     },
 
+    /**
+     * @return {Element} The element that contains this one. Used for height
+     *     calculations.
+     */
+    getContainerElement: function() {
+      return this.getElement().parentNode;
+    },
+
     /** @param {boolean} isVisible Whether the throbber is visible. */
     setIsThrobberVisible: function(isVisible) {
       setIsVisible(this.getChildElement('.throbber-container'), isVisible);
diff --git a/chrome/browser/resources/print_preview/search/destination_search.css b/chrome/browser/resources/print_preview/search/destination_search.css
index d3a95daa..ea76b84 100644
--- a/chrome/browser/resources/print_preview/search/destination_search.css
+++ b/chrome/browser/resources/print_preview/search/destination_search.css
@@ -64,6 +64,10 @@
   padding: 0 14px 18px;
 }
 
+#destination-search .lists > :last-child {
+  padding-bottom: 0;
+}
+
 #destination-search .invitation-container {
   -webkit-animation: invitation-fadein 500ms;
   -webkit-box-align: center;
diff --git a/chrome/browser/resources/print_preview/search/destination_search.js b/chrome/browser/resources/print_preview/search/destination_search.js
index eb68c2f1..7233e3b 100644
--- a/chrome/browser/resources/print_preview/search/destination_search.js
+++ b/chrome/browser/resources/print_preview/search/destination_search.js
@@ -123,14 +123,6 @@
   };
 
   /**
-   * Padding at the bottom of a destination list in pixels.
-   * @type {number}
-   * @const
-   * @private
-   */
-  DestinationSearch.LIST_BOTTOM_PADDING_ = 18;
-
-  /**
    * Number of unregistered destinations that may be promoted to the top.
    * @type {number}
    * @const
@@ -390,8 +382,9 @@
 
       var getListsTotalHeight = function(lists, counts) {
         return lists.reduce(function(sum, list, index) {
+          var container = list.getContainerElement();
           return sum + list.getEstimatedHeightInPixels(counts[index]) +
-              DestinationSearch.LIST_BOTTOM_PADDING_;
+              parseInt(window.getComputedStyle(container).paddingBottom, 10);
         }, 0);
       };
       var getCounts = function(lists, count) {
diff --git a/chrome/browser/resources/whispernet_proxy/js/init.js b/chrome/browser/resources/whispernet_proxy/js/init.js
index 87abe26..93f60ab 100644
--- a/chrome/browser/resources/whispernet_proxy/js/init.js
+++ b/chrome/browser/resources/whispernet_proxy/js/init.js
@@ -4,72 +4,62 @@
 
 'use strict';
 
-// Globals holding our encoder and decoder. We will never have more than one
-// Global variable that will be used to access this Nacl bridge.
+// Global holding our NaclBridge.
 var whispernetNacl = null;
 
-// copy of an encoder or a decoder at a time.
-var whisperEncoder = null;
-var whisperDecoder = null;
+// Encoders and decoders for each client.
+var whisperEncoders = {};
+var whisperDecoders = {};
 
 /**
  * Initialize the whispernet encoder and decoder.
- * @param {Object} audioParams Object containing the parameters needed for
- *     setting up audio config.
+ * Call this before any other functions.
+ * @param {string} clientId A string identifying the requester.
+ * @param {Object} audioParams Audio parameters for token encoding and decoding.
  */
-function audioConfig(audioParams) {
+function audioConfig(clientId, audioParams) {
   if (!whispernetNacl) {
     chrome.copresencePrivate.sendInitialized(false);
     return;
   }
 
-  console.log('Configuring encoder!');
-  whisperEncoder = new WhisperEncoder(audioParams.paramData, whispernetNacl);
-  whisperEncoder.setAudioDataCallback(chrome.copresencePrivate.sendSamples);
-
-  console.log('Configuring decoder!');
-  whisperDecoder = new WhisperDecoder(audioParams.paramData, whispernetNacl);
-  whisperDecoder.setReceiveCallback(chrome.copresencePrivate.sendFound);
-  whisperDecoder.onDetectBroadcast(chrome.copresencePrivate.sendDetect);
+  console.log('Configuring encoder and decoder for client ' + clientId);
+  whisperEncoders[clientId] =
+      new WhisperEncoder(audioParams.paramData, whispernetNacl, clientId);
+  whisperDecoders[clientId] =
+      new WhisperDecoder(audioParams.paramData, whispernetNacl, clientId);
 }
 
 /**
  * Sends a request to whispernet to encode a token.
+ * @param {string} clientId A string identifying the requester.
  * @param {Object} params Encode token parameters object.
  */
-function encodeTokenRequest(params) {
-  if (whisperEncoder) {
-    whisperEncoder.encode(params);
+function encodeTokenRequest(clientId, params) {
+  if (whisperEncoders[clientId]) {
+    whisperEncoders[clientId].encode(params);
   } else {
-    console.error('encodeTokenRequest: Whisper not initialized!');
+    console.error('encodeTokenRequest: Whisper not initialized for client ' +
+        clientId);
   }
 }
 
 /**
  * Sends a request to whispernet to decode samples.
+ * @param {string} clientId A string identifying the requester.
  * @param {Object} params Process samples parameters object.
  */
-function decodeSamplesRequest(params) {
-  if (whisperDecoder) {
-    whisperDecoder.processSamples(params);
+function decodeSamplesRequest(clientId, params) {
+  if (whisperDecoders[clientId]) {
+    whisperDecoders[clientId].processSamples(params);
   } else {
-    console.error('decodeSamplesRequest: Whisper not initialized!');
+    console.error('decodeSamplesRequest: Whisper not initialized for client ' +
+        clientId);
   }
 }
 
 /**
- * Sends a request to whispernet to detect broadcast.
- */
-function detectBroadcastRequest() {
-  if (whisperDecoder) {
-    whisperDecoder.detectBroadcast();
-  } else {
-    console.error('detectBroadcastRequest: Whisper not initialized!');
-  }
-}
-
-/**
- * Initialize our listerners and signal that the extension is loaded.
+ * Initialize our listeners and signal that the extension is loaded.
  */
 function onWhispernetLoaded() {
   console.log('init: Nacl ready!');
@@ -79,8 +69,6 @@
   chrome.copresencePrivate.onEncodeTokenRequest.addListener(encodeTokenRequest);
   chrome.copresencePrivate.onDecodeSamplesRequest.addListener(
       decodeSamplesRequest);
-  chrome.copresencePrivate.onDetectBroadcastRequest.addListener(
-      detectBroadcastRequest);
 
   // This first initialized is sent to indicate that the library is loaded.
   // Every other time, it will be sent only when Chrome wants to reinitialize
diff --git a/chrome/browser/resources/whispernet_proxy/js/nacl.js b/chrome/browser/resources/whispernet_proxy/js/nacl.js
index 65057285..06f566c 100644
--- a/chrome/browser/resources/whispernet_proxy/js/nacl.js
+++ b/chrome/browser/resources/whispernet_proxy/js/nacl.js
@@ -81,14 +81,13 @@
 };
 
 /**
- * Callback that is called when the Whispernet wrapper is loaded and forward
- * that status to the callback registered with us in the constructor.
+ * Called when the Whispernet wrapper is loaded.
  * @private
  */
 NaclBridge.prototype.onNaclReady_ = function() {
+  this.isEnabled_ = true;
   if (this.readyCallback_)
     this.readyCallback_();
-  this.isEnabled_ = true;
 };
 
 /**
diff --git a/chrome/browser/resources/whispernet_proxy/js/wrapper.js b/chrome/browser/resources/whispernet_proxy/js/wrapper.js
index 6bde1b3b..8203b9a 100644
--- a/chrome/browser/resources/whispernet_proxy/js/wrapper.js
+++ b/chrome/browser/resources/whispernet_proxy/js/wrapper.js
@@ -32,17 +32,19 @@
 /**
  * Creates a whispernet encoder.
  * @constructor
- * @param {Object} params Dictionary of parameters used to initialize the
- * whispernet encoder.
- * @param {Object} whisperNacl The NaclBridge object to use to communicate with
- * the whispernet wrapper.
+ * @param {Object} params Audio parameters for the whispernet encoder.
+ * @param {Object} whisperNacl The NaclBridge object, used to communicate with
+ *     the whispernet wrapper.
+ * @param {string} clientId A string identifying the requester.
  */
-function WhisperEncoder(params, whisperNacl) {
+function WhisperEncoder(params, whisperNacl, clientId) {
   this.whisperNacl_ = whisperNacl;
   this.whisperNacl_.addListener(this.onNaclMessage_.bind(this));
+  this.clientId_ = clientId;
 
   var msg = {
     type: 'initialize_encoder',
+    client_id: clientId,
     params: params
   };
 
@@ -61,37 +63,30 @@
 
   var msg = {
     type: 'encode_token',
+    client_id: this.clientId_,
     // Trying to send the token in binary form to Nacl doesn't work correctly.
     // We end up with the correct string + a bunch of extra characters. This is
     // true of returning a binary string too; hence we communicate back and
     // forth by converting the bytes into an array of integers.
     token: stringToArray(atob(token)),
     repetitions: params.repetitions,
-    use_dtmf: params.token.audible
+    use_dtmf: params.token.audible,
+    use_crc: params.tokenParams.crc,
+    use_parity: params.tokenParams.parity
   };
 
   this.whisperNacl_.send(msg);
 };
 
 /**
- * Method to set the callback for encoded audio data received from the encoder
- * when we finish encoding a token.
- * @param {function(string, ArrayBuffer)} callback Callback which will receive
- * the audio samples.
- */
-WhisperEncoder.prototype.setAudioDataCallback = function(callback) {
-  this.audioDataCallback_ = callback;
-};
-
-/**
  * Method to handle messages from the whispernet NaCl wrapper.
  * @param {Event} e Event from the whispernet wrapper.
  * @private
  */
 WhisperEncoder.prototype.onNaclMessage_ = function(e) {
   var msg = e.data;
-  if (msg.type == 'encode_token_response') {
-    this.audioDataCallback_(
+  if (msg.type == 'encode_token_response' && msg.client_id == this.clientId_) {
+    chrome.copresencePrivate.sendSamples(this.clientId_,
         { token: bytesToBase64(msg.token), audible: msg.audible }, msg.samples);
   }
 };
@@ -99,89 +94,57 @@
 /**
  * Creates a whispernet decoder.
  * @constructor
- * @param {Object} params Dictionary of parameters used to initialize the
- * whispernet decoder.
- * @param {Object} whisperNacl The NaclBridge object to use to communicate with
- * the whispernet wrapper.
+ * @param {Object} params Audio parameters for the whispernet decoder.
+ * @param {Object} whisperNacl The NaclBridge object, used to communicate with
+ *     the whispernet wrapper.
+ * @param {string} clientId A string identifying the requester.
  */
-function WhisperDecoder(params, whisperNacl) {
+function WhisperDecoder(params, whisperNacl, clientId) {
   this.whisperNacl_ = whisperNacl;
   this.whisperNacl_.addListener(this.onNaclMessage_.bind(this));
+  this.clientId_ = clientId;
 
   var msg = {
     type: 'initialize_decoder',
+    client_id: clientId,
     params: params
   };
   this.whisperNacl_.send(msg);
 }
 
 /**
- * Method to request the decoder to wipe its internal buffer.
- */
-WhisperDecoder.prototype.wipeDecoder = function() {
-  var msg = {
-    type: 'wipe_decode_buffer'
-  };
-  this.whisperNacl_.send(msg);
-};
-
-/**
- * Method to request the decoder to detect a broadcast.
- */
-WhisperDecoder.prototype.detectBroadcast = function() {
-  var msg = {
-    type: 'detect_broadcast'
-  };
-  this.whisperNacl_.send(msg);
-};
-
-/**
  * Method to request the decoder to process samples.
  * @param {Object} params Process samples parameters object.
  */
 WhisperDecoder.prototype.processSamples = function(params) {
   var msg = {
     type: 'decode_tokens',
-    decode_audible: params.decodeAudible,
-    decode_inaudible: params.decodeInaudible,
+    client_id: this.clientId_,
     data: params.samples,
-    token_length_dtmf: params.audibleTokenLength,
-    token_length_dsss: params.inaudibleTokenLength
+
+    decode_audible: params.decodeAudible,
+    token_length_dtmf: params.audibleTokenParams.length,
+    crc_dtmf: params.audibleTokenParams.crc,
+    parity_dtmf: params.audibleTokenParams.parity,
+
+    decode_inaudible: params.decodeInaudible,
+    token_length_dsss: params.inaudibleTokenParams.length,
+    crc_dsss: params.inaudibleTokenParams.crc,
+    parity_dsss: params.inaudibleTokenParams.parity,
   };
 
   this.whisperNacl_.send(msg);
 };
 
 /**
- * Method to set the callback for decoded tokens received from the decoder.
- * @param {function(!Array.string)} callback Callback to receive the list of
- * decoded tokens.
- */
-WhisperDecoder.prototype.setReceiveCallback = function(callback) {
-  this.tokenCallback_ = callback;
-};
-
-/**
- * Method to set the callback for receiving the detect callback status received
- * from the decoder.
- * @param {function()} callback Callback to set to receive the detect broadcast
- * status.
- */
-WhisperDecoder.prototype.onDetectBroadcast = function(callback) {
-  this.detectBroadcastCallback_ = callback;
-};
-
-/**
  * Method to handle messages from the whispernet NaCl wrapper.
  * @param {Event} e Event from the whispernet wrapper.
  * @private
  */
 WhisperDecoder.prototype.onNaclMessage_ = function(e) {
   var msg = e.data;
-  if (msg.type == 'decode_tokens_response') {
+  if (msg.type == 'decode_tokens_response' && msg.client_id == this.clientId_) {
     this.handleCandidates_(msg.tokens, msg.audible);
-  } else if (msg.type == 'detect_broadcast_response') {
-    this.detectBroadcastCallback_(msg.detected);
   }
 };
 
@@ -194,7 +157,7 @@
  * @private
  */
 WhisperDecoder.prototype.handleCandidates_ = function(candidates, audible) {
-  if (!this.tokenCallback_ || !candidates || candidates.length == 0)
+  if (!candidates || candidates.length == 0)
     return;
 
   var returnCandidates = [];
@@ -202,6 +165,5 @@
     returnCandidates[i] = { token: bytesToBase64(candidates[i]),
                             audible: audible };
   }
-  this.tokenCallback_(returnCandidates);
+  chrome.copresencePrivate.sendFound(this.clientId_, returnCandidates);
 };
-
diff --git a/chrome/browser/resources/whispernet_proxy/whispernet_proxy.nmf.png b/chrome/browser/resources/whispernet_proxy/whispernet_proxy.nmf.png
index 3d95411..1fed28b 100644
--- a/chrome/browser/resources/whispernet_proxy/whispernet_proxy.nmf.png
+++ b/chrome/browser/resources/whispernet_proxy/whispernet_proxy.nmf.png
@@ -1,7 +1,7 @@
 {
   "program": {
     "portable": {
-      "pnacl-translate": { "url": "whispernet_proxy_pnacl.pexe.png?v00004" }
+      "pnacl-translate": { "url": "whispernet_proxy_pnacl.pexe.png?v00006" }
     }
   }
 }
diff --git a/chrome/browser/resources/whispernet_proxy/whispernet_proxy_pnacl.pexe.png b/chrome/browser/resources/whispernet_proxy/whispernet_proxy_pnacl.pexe.png
index 6cae4da1..e199b554 100644
--- a/chrome/browser/resources/whispernet_proxy/whispernet_proxy_pnacl.pexe.png
+++ b/chrome/browser/resources/whispernet_proxy/whispernet_proxy_pnacl.pexe.png
Binary files differ
diff --git a/chrome/browser/safe_browsing/database_manager.cc b/chrome/browser/safe_browsing/database_manager.cc
index 2f89931..ec780411 100644
--- a/chrome/browser/safe_browsing/database_manager.cc
+++ b/chrome/browser/safe_browsing/database_manager.cc
@@ -11,6 +11,7 @@
 #include "base/callback.h"
 #include "base/command_line.h"
 #include "base/debug/leak_tracker.h"
+#include "base/profiler/scoped_tracker.h"
 #include "base/stl_util.h"
 #include "base/strings/string_util.h"
 #include "base/threading/thread.h"
@@ -651,6 +652,10 @@
     return;
 
   DCHECK(!safe_browsing_thread_.get());
+  // TODO(pkasting): Remove ScopedTracker below once crbug.com/455469 is fixed.
+  tracked_objects::ScopedTracker tracking_profile2(
+      FROM_HERE_WITH_EXPLICIT_FUNCTION(
+          "455469 SafeBrowsingDatabaseManager::StartOnIOThread"));
   safe_browsing_thread_.reset(new base::Thread("Chrome_SafeBrowsingThread"));
   if (!safe_browsing_thread_->Start())
     return;
@@ -764,6 +769,10 @@
 }
 
 bool SafeBrowsingDatabaseManager::DatabaseAvailable() const {
+  // TODO(pkasting): Remove ScopedTracker below once crbug.com/455469 is fixed.
+  tracked_objects::ScopedTracker tracking_profile2(
+      FROM_HERE_WITH_EXPLICIT_FUNCTION(
+          "455469 SafeBrowsingDatabaseManager::DatabaseAvailable"));
   base::AutoLock lock(database_lock_);
   return !closing_database_ && (database_ != NULL);
 }
@@ -771,6 +780,10 @@
 bool SafeBrowsingDatabaseManager::MakeDatabaseAvailable() {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   DCHECK(enabled_);
+  // TODO(pkasting): Remove ScopedTracker below once crbug.com/455469 is fixed.
+  tracked_objects::ScopedTracker tracking_profile2(
+      FROM_HERE_WITH_EXPLICIT_FUNCTION(
+          "455469 SafeBrowsingDatabaseManager::MakeDatabaseAvailable"));
   if (DatabaseAvailable())
     return true;
   safe_browsing_thread_->message_loop()->PostTask(
diff --git a/chrome/browser/safe_browsing/incident_reporting/last_download_finder_unittest.cc b/chrome/browser/safe_browsing/incident_reporting/last_download_finder_unittest.cc
index 7c4726a09..d007508 100644
--- a/chrome/browser/safe_browsing/incident_reporting/last_download_finder_unittest.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/last_download_finder_unittest.cc
@@ -15,6 +15,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/history/chrome_history_client.h"
 #include "chrome/browser/history/chrome_history_client_factory.h"
+#include "chrome/browser/history/content_visit_delegate.h"
 #include "chrome/browser/history/history_service.h"
 #include "chrome/browser/history/history_service_factory.h"
 #include "chrome/browser/history/web_history_service_factory.h"
@@ -54,8 +55,9 @@
     return NULL;
   }
 
-  HistoryService* history_service = new HistoryService(
-      ChromeHistoryClientFactory::GetForProfile(profile), profile);
+  HistoryService* history_service =
+      new HistoryService(ChromeHistoryClientFactory::GetForProfile(profile),
+                         scoped_ptr<history::VisitDelegate>());
   if (history_service->Init(
           profile->GetPrefs()->GetString(prefs::kAcceptLanguages),
           history::HistoryDatabaseParamsForPath(profile->GetPath()))) {
diff --git a/chrome/browser/safe_browsing/incident_reporting/off_domain_inclusion_detector_unittest.cc b/chrome/browser/safe_browsing/incident_reporting/off_domain_inclusion_detector_unittest.cc
index cbc03fa..67b779e1 100644
--- a/chrome/browser/safe_browsing/incident_reporting/off_domain_inclusion_detector_unittest.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/off_domain_inclusion_detector_unittest.cc
@@ -13,6 +13,7 @@
 #include "base/time/time.h"
 #include "chrome/browser/history/chrome_history_client.h"
 #include "chrome/browser/history/chrome_history_client_factory.h"
+#include "chrome/browser/history/content_visit_delegate.h"
 #include "chrome/browser/history/history_service.h"
 #include "chrome/browser/history/history_service_factory.h"
 #include "chrome/browser/history/web_history_service_factory.h"
@@ -67,8 +68,9 @@
     return NULL;
   }
 
-  HistoryService* history_service = new HistoryService(
-      ChromeHistoryClientFactory::GetForProfile(profile), profile);
+  HistoryService* history_service =
+      new HistoryService(ChromeHistoryClientFactory::GetForProfile(profile),
+                         scoped_ptr<history::VisitDelegate>());
   if (history_service->Init(
           profile->GetPrefs()->GetString(prefs::kAcceptLanguages),
           history::HistoryDatabaseParamsForPath(profile->GetPath()))) {
diff --git a/chrome/browser/safe_browsing/malware_details_history.h b/chrome/browser/safe_browsing/malware_details_history.h
index 7482cc5..012a748 100644
--- a/chrome/browser/safe_browsing/malware_details_history.h
+++ b/chrome/browser/safe_browsing/malware_details_history.h
@@ -26,6 +26,8 @@
 typedef std::vector<GURL> RedirectChain;
 }
 
+class Profile;
+
 class MalwareDetailsRedirectsCollector
     : public base::RefCountedThreadSafe<
           MalwareDetailsRedirectsCollector,
diff --git a/chrome/browser/safe_browsing/safe_browsing_service.cc b/chrome/browser/safe_browsing/safe_browsing_service.cc
index a7a6263c..52bd5fc 100644
--- a/chrome/browser/safe_browsing/safe_browsing_service.cc
+++ b/chrome/browser/safe_browsing/safe_browsing_service.cc
@@ -469,34 +469,18 @@
     return;
   enabled_ = true;
 
-  // TODO(pkasting): Remove ScopedTracker below once crbug.com/455469 is fixed.
-  tracked_objects::ScopedTracker tracking_profile1(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "455469 SafeBrowsingService::GetProtocolConfig"));
   SafeBrowsingProtocolConfig config = GetProtocolConfig();
 
 #if defined(FULL_SAFE_BROWSING)
-  // TODO(pkasting): Remove ScopedTracker below once crbug.com/455469 is fixed.
-  tracked_objects::ScopedTracker tracking_profile2(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "455469 SafeBrowsingDatabaseManager::StartOnIOThread"));
   DCHECK(database_manager_.get());
   database_manager_->StartOnIOThread();
 
-  // TODO(pkasting): Remove ScopedTracker below once crbug.com/455469 is fixed.
-  tracked_objects::ScopedTracker tracking_profile3(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "455469 SafeBrowsingProtocolManager::Create"));
   DCHECK(!protocol_manager_);
   protocol_manager_ = SafeBrowsingProtocolManager::Create(
       database_manager_.get(), url_request_context_getter, config);
   protocol_manager_->Initialize();
 #endif
 
-  // TODO(pkasting): Remove ScopedTracker below once crbug.com/455469 is fixed.
-  tracked_objects::ScopedTracker tracking_profile4(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "455469 SafeBrowsingPingManager::Create"));
   DCHECK(!ping_manager_);
   ping_manager_ = SafeBrowsingPingManager::Create(
       url_request_context_getter, config);
diff --git a/chrome/browser/safe_browsing/safe_browsing_util.cc b/chrome/browser/safe_browsing/safe_browsing_util.cc
index 77aed1a2..5d44ac16 100644
--- a/chrome/browser/safe_browsing/safe_browsing_util.cc
+++ b/chrome/browser/safe_browsing/safe_browsing_util.cc
@@ -547,7 +547,7 @@
     return UWS_ON_INVISIBLE;
   if (status == kOn)
     return UWS_ON;
-  return UWS_OFF;
+  return UWS_ON;
 }
 
 }  // namespace safe_browsing_util
diff --git a/chrome/browser/search/hotword_service.cc b/chrome/browser/search/hotword_service.cc
index 5a02b4c..5540f54 100644
--- a/chrome/browser/search/hotword_service.cc
+++ b/chrome/browser/search/hotword_service.cc
@@ -55,12 +55,34 @@
 
 namespace {
 
-// Allowed languages for hotwording.
+// Allowed locales for hotwording. Note that Chrome does not support all of
+// these locales, condensing them to their 2-letter equivalent, but the full
+// list is here for completeness and testing.
 static const char* kSupportedLocales[] = {
   "en",
+  "en_au",
+  "en_ca",
+  "en_gb",
+  "en_nz",
+  "en_us",
+  "en_za",
   "de",
+  "de_at",
+  "de_de",
+  "es",
+  "es_419",
+  "es_es",
   "fr",
-  "ru"
+  "fr_fr",
+  "it",
+  "it_it",
+  "ja",
+  "ja_jp",
+  "ko",
+  "ko_kr",
+  "pt_br",
+  "ru",
+  "ru_ru"
 };
 
 // Maximum number of retries for installing the hotword shared module from the
@@ -266,7 +288,7 @@
   base::StringToLowerASCII(&normalized_locale);
 
   for (size_t i = 0; i < arraysize(kSupportedLocales); i++) {
-    if (normalized_locale.compare(0, 2, kSupportedLocales[i]) == 0)
+    if (normalized_locale == kSupportedLocales[i])
       return true;
   }
   return false;
diff --git a/chrome/browser/search/instant_service.cc b/chrome/browser/search/instant_service.cc
index 79f4237a..f02d656 100644
--- a/chrome/browser/search/instant_service.cc
+++ b/chrome/browser/search/instant_service.cc
@@ -407,18 +407,20 @@
   // changed, the effective URLs might change if they reference the Google base
   // URL. The TemplateURLService will notify us when the effective URL changes
   // in this way but it's up to us to do the work to check both.
+  bool google_base_url_domain_changed = false;
   GURL google_base_url(UIThreadSearchTermsData(profile_).GoogleBaseURLValue());
   if (google_base_url != previous_google_base_url_) {
     previous_google_base_url_ = google_base_url;
     if (template_url && template_url->HasGoogleBaseURLs(
             UIThreadSearchTermsData(profile_)))
-      default_search_provider_changed = true;
+      google_base_url_domain_changed = true;
   }
 
-  if (default_search_provider_changed) {
+  if (default_search_provider_changed || google_base_url_domain_changed) {
     ResetInstantSearchPrerenderer();
-    FOR_EACH_OBSERVER(InstantServiceObserver, observers_,
-                      DefaultSearchProviderChanged());
+    FOR_EACH_OBSERVER(
+        InstantServiceObserver, observers_,
+        DefaultSearchProviderChanged(google_base_url_domain_changed));
   }
 }
 
diff --git a/chrome/browser/search/instant_service_observer.cc b/chrome/browser/search/instant_service_observer.cc
index bda8ff0..2dd5a50 100644
--- a/chrome/browser/search/instant_service_observer.cc
+++ b/chrome/browser/search/instant_service_observer.cc
@@ -11,7 +11,8 @@
     const std::vector<InstantMostVisitedItem>&) {
 }
 
-void InstantServiceObserver::DefaultSearchProviderChanged() {
+void InstantServiceObserver::DefaultSearchProviderChanged(
+    bool google_base_url_domain_changed) {
 }
 
 void InstantServiceObserver::OmniboxStartMarginChanged(
diff --git a/chrome/browser/search/instant_service_observer.h b/chrome/browser/search/instant_service_observer.h
index 68b4c97..d38337d 100644
--- a/chrome/browser/search/instant_service_observer.h
+++ b/chrome/browser/search/instant_service_observer.h
@@ -20,8 +20,11 @@
   virtual void MostVisitedItemsChanged(
       const std::vector<InstantMostVisitedItem>&);
 
-  // Indicates that the default search provider changed.
-  virtual void DefaultSearchProviderChanged();
+  // Indicates that the default search provider changed. Parameter indicates
+  // whether the Google base URL changed (such as when the user migrates from
+  // one google.<TLD> to another TLD).
+  virtual void DefaultSearchProviderChanged(
+      bool google_base_url_domain_changed);
 
   // Indicates that the omnibox start margin has changed.
   virtual void OmniboxStartMarginChanged(int omnibox_start_margin);
diff --git a/chrome/browser/search/instant_service_unittest.cc b/chrome/browser/search/instant_service_unittest.cc
index 0d488df2..7d22a543 100644
--- a/chrome/browser/search/instant_service_unittest.cc
+++ b/chrome/browser/search/instant_service_unittest.cc
@@ -28,7 +28,7 @@
 
 class MockInstantServiceObserver : public InstantServiceObserver {
  public:
-  MOCK_METHOD0(DefaultSearchProviderChanged, void());
+  MOCK_METHOD1(DefaultSearchProviderChanged, void(bool));
   MOCK_METHOD1(OmniboxStartMarginChanged, void(int));
 };
 
@@ -67,29 +67,29 @@
 };
 
 TEST_F(InstantServiceEnabledTest, DispatchDefaultSearchProviderChanged) {
-  EXPECT_CALL(*instant_service_observer_.get(), DefaultSearchProviderChanged())
-      .Times(1);
+  EXPECT_CALL(*instant_service_observer_.get(),
+              DefaultSearchProviderChanged(false)).Times(1);
 
   const std::string new_base_url = "https://bar.com/";
   SetUserSelectedDefaultSearchProvider(new_base_url);
 }
 
 TEST_F(InstantServiceTest, DontDispatchGoogleURLUpdatedForNonGoogleURLs) {
-  EXPECT_CALL(*instant_service_observer_.get(), DefaultSearchProviderChanged())
-      .Times(1);
+  EXPECT_CALL(*instant_service_observer_.get(),
+              DefaultSearchProviderChanged(false)).Times(1);
   const std::string new_dsp_url = "https://bar.com/";
   SetUserSelectedDefaultSearchProvider(new_dsp_url);
   testing::Mock::VerifyAndClearExpectations(instant_service_observer_.get());
 
-  EXPECT_CALL(*instant_service_observer_.get(), DefaultSearchProviderChanged())
-      .Times(0);
+  EXPECT_CALL(*instant_service_observer_.get(),
+              DefaultSearchProviderChanged(false)).Times(0);
   const std::string new_base_url = "https://www.google.es/";
   NotifyGoogleBaseURLUpdate(new_base_url);
 }
 
 TEST_F(InstantServiceTest, DispatchGoogleURLUpdated) {
-  EXPECT_CALL(*instant_service_observer_.get(), DefaultSearchProviderChanged())
-      .Times(1);
+  EXPECT_CALL(*instant_service_observer_.get(),
+              DefaultSearchProviderChanged(true)).Times(1);
 
   const std::string new_base_url = "https://www.google.es/";
   NotifyGoogleBaseURLUpdate(new_base_url);
@@ -123,8 +123,8 @@
 
 TEST_F(InstantServiceEnabledTest,
        ResetInstantSearchPrerenderer_DefaultProviderChanged) {
-  EXPECT_CALL(*instant_service_observer_.get(), DefaultSearchProviderChanged())
-      .Times(2);
+  EXPECT_CALL(*instant_service_observer_.get(),
+              DefaultSearchProviderChanged(false)).Times(2);
 
   // Set a default search provider that doesn't support Instant.
   TemplateURLData data;
@@ -146,8 +146,8 @@
 
 TEST_F(InstantServiceEnabledTest,
        ResetInstantSearchPrerenderer_GoogleBaseURLUpdated) {
-  EXPECT_CALL(*instant_service_observer_.get(), DefaultSearchProviderChanged())
-      .Times(1);
+  EXPECT_CALL(*instant_service_observer_.get(),
+              DefaultSearchProviderChanged(true)).Times(1);
 
   InstantSearchPrerenderer* old_prerenderer = GetInstantSearchPrerenderer();
   EXPECT_TRUE(old_prerenderer != NULL);
diff --git a/chrome/browser/services/gcm/push_messaging_application_id.cc b/chrome/browser/services/gcm/push_messaging_application_id.cc
index 3ddd2041..c35e396 100644
--- a/chrome/browser/services/gcm/push_messaging_application_id.cc
+++ b/chrome/browser/services/gcm/push_messaging_application_id.cc
@@ -91,6 +91,21 @@
   return PushMessagingApplicationId();
 }
 
+// static
+std::vector<PushMessagingApplicationId> PushMessagingApplicationId::GetAll(
+    Profile* profile) {
+  std::vector<PushMessagingApplicationId> result;
+
+  const base::DictionaryValue* map =
+      profile->GetPrefs()->GetDictionary(prefs::kPushMessagingApplicationIdMap);
+  for (auto it = base::DictionaryValue::Iterator(*map); !it.IsAtEnd();
+       it.Advance()) {
+    result.push_back(Get(profile, it.key()));
+  }
+
+  return result;
+}
+
 void PushMessagingApplicationId::PersistToDisk(Profile* profile) const {
   DCHECK(IsValid());
 
diff --git a/chrome/browser/services/gcm/push_messaging_application_id.h b/chrome/browser/services/gcm/push_messaging_application_id.h
index e9da2ca..ddb24d64 100644
--- a/chrome/browser/services/gcm/push_messaging_application_id.h
+++ b/chrome/browser/services/gcm/push_messaging_application_id.h
@@ -6,6 +6,7 @@
 #define CHROME_BROWSER_SERVICES_GCM_PUSH_MESSAGING_APPLICATION_ID_H_
 
 #include <string>
+#include <vector>
 
 #include "base/basictypes.h"
 #include "url/gurl.h"
@@ -44,6 +45,10 @@
                                         const GURL& origin,
                                         int64 service_worker_registration_id);
 
+  // Returns all the PushMessagingApplicationId currently registered for the
+  // given |profile|.
+  static std::vector<PushMessagingApplicationId> GetAll(Profile* profile);
+
   ~PushMessagingApplicationId();
 
   // Persist this application id to disk.
@@ -70,9 +75,9 @@
                              const GURL& origin,
                              int64 service_worker_registration_id);
 
-  const std::string app_id_guid_;
-  const GURL origin_;
-  const int64 service_worker_registration_id_;
+  std::string app_id_guid_;
+  GURL origin_;
+  int64 service_worker_registration_id_;
 };
 
 }  // namespace gcm
diff --git a/chrome/browser/services/gcm/push_messaging_browsertest.cc b/chrome/browser/services/gcm/push_messaging_browsertest.cc
index 9769f91..2baa489 100644
--- a/chrome/browser/services/gcm/push_messaging_browsertest.cc
+++ b/chrome/browser/services/gcm/push_messaging_browsertest.cc
@@ -22,6 +22,7 @@
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "components/content_settings/core/browser/host_content_settings_map.h"
+#include "components/content_settings/core/common/content_settings.h"
 #include "components/content_settings/core/common/content_settings_types.h"
 #include "components/gcm_driver/gcm_client.h"
 #include "components/infobars/core/confirm_infobar_delegate.h"
@@ -88,15 +89,20 @@
 // FakeGCMProfileService::UnregisterCallback.
 class UnregistrationCallback {
  public:
-  UnregistrationCallback() : done_(false) {}
+  UnregistrationCallback() : done_(false), waiting_(false) {}
 
   void Run(const std::string& app_id) {
     app_id_ = app_id;
     done_ = true;
-    base::MessageLoop::current()->Quit();
+    if (waiting_)
+      base::MessageLoop::current()->Quit();
   }
 
   void WaitUntilSatisfied() {
+    if (done_)
+      return;
+
+    waiting_ = true;
     while (!done_)
       content::RunMessageLoop();
   }
@@ -107,6 +113,7 @@
 
  private:
   bool done_;
+  bool waiting_;
   std::string app_id_;
 };
 
@@ -160,6 +167,8 @@
   // InProcessBrowserTest:
   void SetUpCommandLine(base::CommandLine* command_line) override {
     command_line->AppendSwitch(switches::kEnablePushMessagePayload);
+    command_line->AppendSwitch(switches::kEnablePushMessagingHasPermission);
+
     InProcessBrowserTest::SetUpCommandLine(command_line);
   }
 
@@ -299,7 +308,7 @@
 
 PushMessagingApplicationId PushMessagingBrowserTest::GetServiceWorkerAppId(
     int64 service_worker_registration_id) {
-  GURL origin = https_server()->GetURL("").GetOrigin();
+  GURL origin = https_server()->GetURL(std::string()).GetOrigin();
   PushMessagingApplicationId application_id = PushMessagingApplicationId::Get(
       browser()->profile(), origin, service_worker_registration_id);
   EXPECT_TRUE(application_id.IsValid());
@@ -457,6 +466,7 @@
   ASSERT_EQ("true - is controlled", script_result);
 
   GCMClient::IncomingMessage message;
+  message.sender_id = "1234567890";
   message.data["data"] = "testdata";
   push_service()->OnMessage(app_id.app_id_guid(), message);
   ASSERT_TRUE(RunScript("resultQueue.pop()", &script_result));
@@ -494,48 +504,7 @@
                                                   base::Unretained(&callback)));
 
   GCMClient::IncomingMessage message;
-  message.data["data"] = "testdata";
-  push_service()->OnMessage(app_id.app_id_guid(), message);
-
-  callback.WaitUntilSatisfied();
-  EXPECT_EQ(app_id.app_id_guid(), callback.app_id());
-
-  // No push data should have been received.
-  ASSERT_TRUE(RunScript("resultQueue.popImmediately()", &script_result));
-  EXPECT_EQ("null", script_result);
-}
-
-IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, PushEventNoPermission) {
-  if (!IsPushSupported())
-    return;
-
-  std::string script_result;
-
-  TryToRegisterSuccessfully("1-0" /* expected_push_registration_id */);
-
-  PushMessagingApplicationId app_id = GetServiceWorkerAppId(0LL);
-  EXPECT_EQ(app_id.app_id_guid(), gcm_service()->last_registered_app_id());
-  EXPECT_EQ("1234567890", gcm_service()->last_registered_sender_ids()[0]);
-
-  ASSERT_TRUE(RunScript("isControlled()", &script_result));
-  ASSERT_EQ("false - is not controlled", script_result);
-
-  LoadTestPage();  // Reload to become controlled.
-
-  ASSERT_TRUE(RunScript("isControlled()", &script_result));
-  ASSERT_EQ("true - is controlled", script_result);
-
-  // Revoke Push permission.
-  browser()->profile()->GetHostContentSettingsMap()->
-      ClearSettingsForOneType(CONTENT_SETTINGS_TYPE_PUSH_MESSAGING);
-
-  // When the push service will receive its next message, given that there is no
-  // SW available, it should unregister |app_id|.
-  UnregistrationCallback callback;
-  gcm_service()->SetUnregisterCallback(base::Bind(&UnregistrationCallback::Run,
-                                                  base::Unretained(&callback)));
-
-  GCMClient::IncomingMessage message;
+  message.sender_id = "1234567890";
   message.data["data"] = "testdata";
   push_service()->OnMessage(app_id.app_id_guid(), message);
 
@@ -580,6 +549,7 @@
   // If the site is visible in an active tab, we should not force a notification
   // to be shown. Try it twice, since we allow one mistake per 10 push events.
   GCMClient::IncomingMessage message;
+  message.sender_id = "1234567890";
   for (int n = 0; n < 2; n++) {
     message.data["data"] = "testdata";
     push_service()->OnMessage(app_id.app_id_guid(), message);
@@ -670,6 +640,7 @@
       base::Bind(&NotificationAddedCallback::Run, base::Unretained(&callback)));
 
   GCMClient::IncomingMessage message;
+  message.sender_id = "1234567890";
   message.data["data"] = "shownotification-without-waituntil";
   push_service()->OnMessage(app_id.app_id_guid(), message);
   ASSERT_TRUE(RunScript("resultQueue.pop()", &script_result, web_contents));
@@ -766,12 +737,12 @@
   gcm_service()->AddExpectedUnregisterResponse(GCMClient::NETWORK_ERROR);
 
   ASSERT_TRUE(RunScript("unregister()", &script_result));
-  EXPECT_EQ("unregister error: "
-            "NetworkError: Failed to connect to the push server.",
+  EXPECT_EQ("unregister error: NetworkError: "
+            "Unregistration failed - could not connect to push server",
             script_result);
 }
 
-IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, UnregisterUnknownError) {
+IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, UnregisterAbortError) {
   if (!IsPushSupported())
     return;
 
@@ -783,8 +754,8 @@
 
   ASSERT_TRUE(RunScript("unregister()", &script_result));
   EXPECT_EQ("unregister error: "
-            "UnknownError: Unexpected error while trying to unregister from the"
-            " push server.", script_result);
+            "AbortError: Unregistration failed - push service error",
+            script_result);
 }
 
 #if defined(OS_ANDROID)
@@ -800,4 +771,243 @@
 }
 #endif
 
+IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
+                       GlobalResetPushPermissionUnregisters) {
+  std::string script_result;
+
+  TryToRegisterSuccessfully("1-0" /* expected_push_registration_id */);
+
+  ASSERT_TRUE(RunScript("hasRegistration()", &script_result));
+  EXPECT_EQ("true - registered", script_result);
+
+  ASSERT_TRUE(RunScript("hasPermission()", &script_result));
+  EXPECT_EQ("permission status - granted", script_result);
+
+  browser()->profile()->GetHostContentSettingsMap()->
+      ClearSettingsForOneType(CONTENT_SETTINGS_TYPE_PUSH_MESSAGING);
+
+  ASSERT_TRUE(RunScript("hasPermission()", &script_result));
+  EXPECT_EQ("permission status - default", script_result);
+
+  ASSERT_TRUE(RunScript("hasRegistration()", &script_result));
+  EXPECT_EQ("false - not registered", script_result);
+}
+
+IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
+                       LocalResetPushPermissionUnregisters) {
+  std::string script_result;
+
+  TryToRegisterSuccessfully("1-0" /* expected_push_registration_id */);
+
+  ASSERT_TRUE(RunScript("hasRegistration()", &script_result));
+  EXPECT_EQ("true - registered", script_result);
+
+  ASSERT_TRUE(RunScript("hasPermission()", &script_result));
+  EXPECT_EQ("permission status - granted", script_result);
+
+  GURL origin = https_server()->GetURL(std::string()).GetOrigin();
+  browser()->profile()->GetHostContentSettingsMap()->SetContentSetting(
+      ContentSettingsPattern::FromURLNoWildcard(origin),
+      ContentSettingsPattern::FromURLNoWildcard(origin),
+      CONTENT_SETTINGS_TYPE_PUSH_MESSAGING,
+      std::string(),
+      CONTENT_SETTING_DEFAULT);
+
+  ASSERT_TRUE(RunScript("hasPermission()", &script_result));
+  EXPECT_EQ("permission status - default", script_result);
+
+  ASSERT_TRUE(RunScript("hasRegistration()", &script_result));
+  EXPECT_EQ("false - not registered", script_result);
+}
+
+IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
+                       DenyPushPermissionUnregisters) {
+  std::string script_result;
+
+  TryToRegisterSuccessfully("1-0" /* expected_push_registration_id */);
+
+  ASSERT_TRUE(RunScript("hasRegistration()", &script_result));
+  EXPECT_EQ("true - registered", script_result);
+
+  ASSERT_TRUE(RunScript("hasPermission()", &script_result));
+  EXPECT_EQ("permission status - granted", script_result);
+
+  GURL origin = https_server()->GetURL(std::string()).GetOrigin();
+  browser()->profile()->GetHostContentSettingsMap()->SetContentSetting(
+      ContentSettingsPattern::FromURLNoWildcard(origin),
+      ContentSettingsPattern::FromURLNoWildcard(origin),
+      CONTENT_SETTINGS_TYPE_PUSH_MESSAGING,
+      std::string(),
+      CONTENT_SETTING_BLOCK);
+
+  ASSERT_TRUE(RunScript("hasPermission()", &script_result));
+  EXPECT_EQ("permission status - denied", script_result);
+
+  ASSERT_TRUE(RunScript("hasRegistration()", &script_result));
+  EXPECT_EQ("false - not registered", script_result);
+}
+
+IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
+                       GlobalResetNotificationsPermissionUnregisters) {
+  std::string script_result;
+
+  TryToRegisterSuccessfully("1-0" /* expected_push_registration_id */);
+
+  ASSERT_TRUE(RunScript("hasRegistration()", &script_result));
+  EXPECT_EQ("true - registered", script_result);
+
+  ASSERT_TRUE(RunScript("hasPermission()", &script_result));
+  EXPECT_EQ("permission status - granted", script_result);
+
+  browser()->profile()->GetHostContentSettingsMap()->
+      ClearSettingsForOneType(CONTENT_SETTINGS_TYPE_NOTIFICATIONS);
+
+  ASSERT_TRUE(RunScript("hasPermission()", &script_result));
+  EXPECT_EQ("permission status - default", script_result);
+
+  ASSERT_TRUE(RunScript("hasRegistration()", &script_result));
+  EXPECT_EQ("false - not registered", script_result);
+}
+
+IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
+                       LocalResetNotificationsPermissionUnregisters) {
+  std::string script_result;
+
+  TryToRegisterSuccessfully("1-0" /* expected_push_registration_id */);
+
+  ASSERT_TRUE(RunScript("hasRegistration()", &script_result));
+  EXPECT_EQ("true - registered", script_result);
+
+  ASSERT_TRUE(RunScript("hasPermission()", &script_result));
+  EXPECT_EQ("permission status - granted", script_result);
+
+  GURL origin = https_server()->GetURL(std::string()).GetOrigin();
+  browser()->profile()->GetHostContentSettingsMap()->SetContentSetting(
+      ContentSettingsPattern::FromURLNoWildcard(origin),
+      ContentSettingsPattern::Wildcard(),
+      CONTENT_SETTINGS_TYPE_NOTIFICATIONS,
+      std::string(),
+      CONTENT_SETTING_DEFAULT);
+
+  ASSERT_TRUE(RunScript("hasPermission()", &script_result));
+  EXPECT_EQ("permission status - default", script_result);
+
+  ASSERT_TRUE(RunScript("hasRegistration()", &script_result));
+  EXPECT_EQ("false - not registered", script_result);
+}
+
+IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
+                       DenyNotificationsPermissionUnregisters) {
+  std::string script_result;
+
+  TryToRegisterSuccessfully("1-0" /* expected_push_registration_id */);
+
+  ASSERT_TRUE(RunScript("hasRegistration()", &script_result));
+  EXPECT_EQ("true - registered", script_result);
+
+  ASSERT_TRUE(RunScript("hasPermission()", &script_result));
+  EXPECT_EQ("permission status - granted", script_result);
+
+  GURL origin = https_server()->GetURL(std::string()).GetOrigin();
+  browser()->profile()->GetHostContentSettingsMap()->SetContentSetting(
+      ContentSettingsPattern::FromURLNoWildcard(origin),
+      ContentSettingsPattern::Wildcard(),
+      CONTENT_SETTINGS_TYPE_NOTIFICATIONS,
+      std::string(),
+      CONTENT_SETTING_BLOCK);
+
+  ASSERT_TRUE(RunScript("hasPermission()", &script_result));
+  EXPECT_EQ("permission status - denied", script_result);
+
+  ASSERT_TRUE(RunScript("hasRegistration()", &script_result));
+  EXPECT_EQ("false - not registered", script_result);
+}
+
+IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
+                       GrantAlreadyGrantedPermissionDoesNotUnregister) {
+  std::string script_result;
+
+  TryToRegisterSuccessfully("1-0" /* expected_push_registration_id */);
+
+  ASSERT_TRUE(RunScript("hasRegistration()", &script_result));
+  EXPECT_EQ("true - registered", script_result);
+
+  ASSERT_TRUE(RunScript("hasPermission()", &script_result));
+  EXPECT_EQ("permission status - granted", script_result);
+
+  GURL origin = https_server()->GetURL(std::string()).GetOrigin();
+  browser()->profile()->GetHostContentSettingsMap()->SetContentSetting(
+      ContentSettingsPattern::FromURLNoWildcard(origin),
+      ContentSettingsPattern::Wildcard(),
+      CONTENT_SETTINGS_TYPE_NOTIFICATIONS,
+      std::string(),
+      CONTENT_SETTING_ALLOW);
+  browser()->profile()->GetHostContentSettingsMap()->SetContentSetting(
+      ContentSettingsPattern::FromURLNoWildcard(origin),
+      ContentSettingsPattern::FromURLNoWildcard(origin),
+      CONTENT_SETTINGS_TYPE_PUSH_MESSAGING,
+      std::string(),
+      CONTENT_SETTING_ALLOW);
+
+  ASSERT_TRUE(RunScript("hasPermission()", &script_result));
+  EXPECT_EQ("permission status - granted", script_result);
+
+  ASSERT_TRUE(RunScript("hasRegistration()", &script_result));
+  EXPECT_EQ("true - registered", script_result);
+}
+
+// This test is testing some non-trivial content settings rules and make sure
+// that they are respected with regards to automatic unregistration. In other
+// words, it checks that the push service does not end up unregistering origins
+// that have push permission with some non-common rules.
+IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
+                       AutomaticUnregistrationFollowsContentSettingRules) {
+  std::string script_result;
+
+  TryToRegisterSuccessfully("1-0" /* expected_push_registration_id */);
+
+  ASSERT_TRUE(RunScript("hasRegistration()", &script_result));
+  EXPECT_EQ("true - registered", script_result);
+
+  ASSERT_TRUE(RunScript("hasPermission()", &script_result));
+  EXPECT_EQ("permission status - granted", script_result);
+
+  GURL origin = https_server()->GetURL(std::string()).GetOrigin();
+  browser()->profile()->GetHostContentSettingsMap()->SetContentSetting(
+      ContentSettingsPattern::Wildcard(),
+      ContentSettingsPattern::Wildcard(),
+      CONTENT_SETTINGS_TYPE_NOTIFICATIONS,
+      std::string(),
+      CONTENT_SETTING_ALLOW);
+  browser()->profile()->GetHostContentSettingsMap()->SetContentSetting(
+      ContentSettingsPattern::FromString("https://*"),
+      ContentSettingsPattern::FromString("https://*"),
+      CONTENT_SETTINGS_TYPE_PUSH_MESSAGING,
+      std::string(),
+      CONTENT_SETTING_ALLOW);
+  browser()->profile()->GetHostContentSettingsMap()->SetContentSetting(
+      ContentSettingsPattern::FromURLNoWildcard(origin),
+      ContentSettingsPattern::Wildcard(),
+      CONTENT_SETTINGS_TYPE_NOTIFICATIONS,
+      std::string(),
+      CONTENT_SETTING_DEFAULT);
+  browser()->profile()->GetHostContentSettingsMap()->SetContentSetting(
+      ContentSettingsPattern::FromURLNoWildcard(origin),
+      ContentSettingsPattern::FromURLNoWildcard(origin),
+      CONTENT_SETTINGS_TYPE_PUSH_MESSAGING,
+      std::string(),
+      CONTENT_SETTING_DEFAULT);
+
+  // The two first rules should give |origin| the permission to use Push even
+  // if the rules it used to have have been reset.
+  // The Push service should not unsubcribe |origin| because at no point it was
+  // left without permission to use Push.
+
+  ASSERT_TRUE(RunScript("hasPermission()", &script_result));
+  EXPECT_EQ("permission status - granted", script_result);
+
+  ASSERT_TRUE(RunScript("hasRegistration()", &script_result));
+  EXPECT_EQ("true - registered", script_result);
+}
+
 }  // namespace gcm
diff --git a/chrome/browser/services/gcm/push_messaging_service_impl.cc b/chrome/browser/services/gcm/push_messaging_service_impl.cc
index b109e6b..49b36d0 100644
--- a/chrome/browser/services/gcm/push_messaging_service_impl.cc
+++ b/chrome/browser/services/gcm/push_messaging_service_impl.cc
@@ -27,10 +27,12 @@
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/grit/generated_resources.h"
+#include "components/content_settings/core/browser/host_content_settings_map.h"
 #include "components/content_settings/core/common/permission_request_id.h"
 #include "components/gcm_driver/gcm_driver.h"
 #include "components/pref_registry/pref_registry_syncable.h"
 #include "content/public/browser/browser_context.h"
+#include "content/public/browser/browser_thread.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/service_worker_context.h"
 #include "content/public/browser/storage_partition.h"
@@ -39,7 +41,6 @@
 #include "content/public/common/content_switches.h"
 #include "content/public/common/platform_notification_data.h"
 #include "content/public/common/push_messaging_status.h"
-#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 #include "ui/base/l10n/l10n_util.h"
 
@@ -57,6 +58,12 @@
 namespace {
 const int kMaxRegistrations = 1000000;
 
+void RecordDeliveryStatus(content::PushDeliveryStatus status) {
+  UMA_HISTOGRAM_ENUMERATION("PushMessaging.DeliveryStatus",
+                            status,
+                            content::PUSH_DELIVERY_STATUS_LAST + 1);
+}
+
 void RecordUserVisibleStatus(content::PushUserVisibleStatus status) {
   UMA_HISTOGRAM_ENUMERATION("PushMessaging.UserVisibleStatus",
                             status,
@@ -123,11 +130,17 @@
       push_registration_count_(0),
       pending_push_registration_count_(0),
       weak_factory_(this) {
+  // In some tests, we might end up with |profile_| being null at this point.
+  // When that is the case |profile_| will be set in SetProfileForTesting(), at
+  // which point the service will start to observe HostContentSettingsMap.
+  if (profile_)
+    profile_->GetHostContentSettingsMap()->AddObserver(this);
 }
 
 PushMessagingServiceImpl::~PushMessagingServiceImpl() {
   // TODO(johnme): If it's possible for this to be destroyed before GCMDriver,
   // then we should call RemoveAppHandler.
+  profile_->GetHostContentSettingsMap()->RemoveObserver(this);
 }
 
 void PushMessagingServiceImpl::IncreasePushRegistrationCount(int add,
@@ -172,6 +185,8 @@
   // TODO(johnme): Do any necessary cleanup.
 }
 
+// OnMessage methods -----------------------------------------------------------
+
 void PushMessagingServiceImpl::OnMessage(
     const std::string& app_id,
     const GCMClient::IncomingMessage& message) {
@@ -227,17 +242,12 @@
                  application_id.service_worker_registration_id(), message));
 }
 
-void PushMessagingServiceImpl::SetProfileForTesting(Profile* profile) {
-  profile_ = profile;
-}
-
 void PushMessagingServiceImpl::DeliverMessageCallback(
     const std::string& app_id_guid,
     const GURL& requesting_origin,
     int64 service_worker_registration_id,
     const GCMClient::IncomingMessage& message,
     content::PushDeliveryStatus status) {
-  // TODO(mvanouwerkerk): UMA logging.
   // TODO(mvanouwerkerk): Show a warning in the developer console of the
   // Service Worker corresponding to app_id (and/or on an internals page).
   // TODO(mvanouwerkerk): Is there a way to recover from failure?
@@ -255,9 +265,11 @@
     case content::PUSH_DELIVERY_STATUS_UNKNOWN_APP_ID:
     case content::PUSH_DELIVERY_STATUS_PERMISSION_DENIED:
     case content::PUSH_DELIVERY_STATUS_NO_SERVICE_WORKER:
-      Unregister(app_id_guid, true /*retry_on_failure*/, UnregisterCallback());
+      Unregister(app_id_guid, message.sender_id, true /* retry_on_failure */,
+                 UnregisterCallback());
       break;
   }
+  RecordDeliveryStatus(status);
 }
 
 void PushMessagingServiceImpl::RequireUserVisibleUX(
@@ -310,17 +322,7 @@
     // Use the visible URL since that's the one the user is aware of (and it
     // doesn't matter whether the page loaded successfully).
     const GURL& active_url = active_web_contents->GetVisibleURL();
-
-    // Allow https://foo.example.com Service Worker to not show notification
-    // if an https://bar.example.com tab is visible (and hence might
-    // conceivably be showing UI in response to the push message); but http://
-    // doesn't count as the Service Worker can't talk to it, even with
-    // navigator.connect.
-    if (requesting_origin.scheme() != active_url.scheme())
-      continue;
-    if (net::registry_controlled_domains::SameDomainOrHost(
-        requesting_origin, active_url,
-        net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES)) {
+    if (requesting_origin == active_url.GetOrigin()) {
       notification_needed = false;
       break;
     }
@@ -422,6 +424,8 @@
   }
 }
 
+// Other GCMAppHandler methods -------------------------------------------------
+
 void PushMessagingServiceImpl::OnMessagesDeleted(const std::string& app_id) {
   // TODO(mvanouwerkerk): Fire push error event on the Service Worker
   // corresponding to app_id.
@@ -439,10 +443,14 @@
   NOTREACHED() << "The Push API shouldn't have sent messages upstream";
 }
 
+// GetPushEndpoint method ------------------------------------------------------
+
 GURL PushMessagingServiceImpl::GetPushEndpoint() {
   return GURL(std::string(kPushMessagingEndpoint));
 }
 
+// Register and GetPermissionStatus methods ------------------------------------
+
 void PushMessagingServiceImpl::RegisterFromDocument(
     const GURL& requesting_origin,
     int64 service_worker_registration_id,
@@ -572,10 +580,23 @@
     GCMClient::Result result) {
   content::PushRegistrationStatus status =
       content::PUSH_REGISTRATION_STATUS_SERVICE_ERROR;
-  if (result == GCMClient::SUCCESS) {
-    status = content::PUSH_REGISTRATION_STATUS_SUCCESS_FROM_PUSH_SERVICE;
-    application_id.PersistToDisk(profile_);
-    IncreasePushRegistrationCount(1, false /* is_pending */);
+  switch (result) {
+    case GCMClient::SUCCESS:
+      status = content::PUSH_REGISTRATION_STATUS_SUCCESS_FROM_PUSH_SERVICE;
+      application_id.PersistToDisk(profile_);
+      IncreasePushRegistrationCount(1, false /* is_pending */);
+      break;
+    case GCMClient::INVALID_PARAMETER:
+    case GCMClient::GCM_DISABLED:
+    case GCMClient::ASYNC_OPERATION_PENDING:
+    case GCMClient::SERVER_ERROR:
+    case GCMClient::UNKNOWN_ERROR:
+      status = content::PUSH_REGISTRATION_STATUS_SERVICE_ERROR;
+      break;
+    case GCMClient::NETWORK_ERROR:
+    case GCMClient::TTL_EXCEEDED:
+      status = content::PUSH_REGISTRATION_STATUS_NETWORK_ERROR;
+      break;
   }
   RegisterEnd(callback, registration_id, status);
   DecreasePushRegistrationCount(1, true /* was_pending */);
@@ -607,9 +628,12 @@
                  application_id, register_callback));
 }
 
+// Unregister methods ----------------------------------------------------------
+
 void PushMessagingServiceImpl::Unregister(
     const GURL& requesting_origin,
     int64 service_worker_registration_id,
+    const std::string& sender_id,
     bool retry_on_failure,
     const content::PushMessagingService::UnregisterCallback& callback) {
   DCHECK(gcm_profile_service_->driver());
@@ -617,16 +641,20 @@
   PushMessagingApplicationId application_id = PushMessagingApplicationId::Get(
       profile_, requesting_origin, service_worker_registration_id);
   if (!application_id.IsValid()) {
-    callback.Run(
-        content::PUSH_UNREGISTRATION_STATUS_SUCCESS_WAS_NOT_REGISTERED);
+    if (!callback.is_null()) {
+      callback.Run(
+          content::PUSH_UNREGISTRATION_STATUS_SUCCESS_WAS_NOT_REGISTERED);
+    }
     return;
   }
 
-  Unregister(application_id.app_id_guid(), retry_on_failure, callback);
+  Unregister(application_id.app_id_guid(), sender_id, retry_on_failure,
+             callback);
 }
 
 void PushMessagingServiceImpl::Unregister(
     const std::string& app_id_guid,
+    const std::string& sender_id,
     bool retry_on_failure,
     const content::PushMessagingService::UnregisterCallback& callback) {
   DCHECK(gcm_profile_service_->driver());
@@ -642,11 +670,17 @@
       application_id.DeleteFromDisk(profile_);
   }
 
-  gcm_profile_service_->driver()->Unregister(
-      app_id_guid,
+  const auto& unregister_callback =
       base::Bind(&PushMessagingServiceImpl::DidUnregister,
                  weak_factory_.GetWeakPtr(),
-                 app_id_guid, retry_on_failure, callback));
+                 app_id_guid, retry_on_failure, callback);
+#if defined(OS_ANDROID)
+  // On Android the backend is different, and requires the original sender_id.
+  gcm_profile_service_->driver()->UnregisterWithSenderId(app_id_guid, sender_id,
+                                                         unregister_callback);
+#else
+  gcm_profile_service_->driver()->Unregister(app_id_guid, unregister_callback);
+#endif
 }
 
 void PushMessagingServiceImpl::DidUnregister(
@@ -657,40 +691,89 @@
   if (result == GCMClient::SUCCESS) {
     PushMessagingApplicationId application_id =
         PushMessagingApplicationId::Get(profile_, app_id_guid);
-    if (application_id.IsValid())
-      application_id.DeleteFromDisk(profile_);
+    if (!application_id.IsValid()) {
+      if (!callback.is_null()) {
+        callback.Run(
+            content::PUSH_UNREGISTRATION_STATUS_SUCCESS_WAS_NOT_REGISTERED);
+      }
+      return;
+    }
+
+    application_id.DeleteFromDisk(profile_);
     DecreasePushRegistrationCount(1, false /* was_pending */);
   }
 
   // Internal calls pass a null callback.
   if (!callback.is_null()) {
     switch (result) {
-    case GCMClient::SUCCESS:
-      callback.Run(content::PUSH_UNREGISTRATION_STATUS_SUCCESS_UNREGISTER);
-      break;
-    case GCMClient::NETWORK_ERROR:
-    case GCMClient::TTL_EXCEEDED:
-    case GCMClient::ASYNC_OPERATION_PENDING:
-      callback.Run(
-          retry_on_failure
-          ? content::
-            PUSH_UNREGISTRATION_STATUS_SUCCESS_WILL_RETRY_NETWORK_ERROR
-          : content::PUSH_UNREGISTRATION_STATUS_NETWORK_ERROR);
-      break;
-    case GCMClient::SERVER_ERROR:
-    case GCMClient::INVALID_PARAMETER:
-    case GCMClient::GCM_DISABLED:
-    case GCMClient::UNKNOWN_ERROR:
-      callback.Run(content::PUSH_UNREGISTRATION_STATUS_UNKNOWN_ERROR);
-      break;
-    default:
-      NOTREACHED() << "Unexpected GCMClient::Result value.";
-      callback.Run(content::PUSH_UNREGISTRATION_STATUS_UNKNOWN_ERROR);
-      break;
+      case GCMClient::SUCCESS:
+        callback.Run(content::PUSH_UNREGISTRATION_STATUS_SUCCESS_UNREGISTERED);
+        break;
+      case GCMClient::INVALID_PARAMETER:
+      case GCMClient::GCM_DISABLED:
+      case GCMClient::ASYNC_OPERATION_PENDING:
+      case GCMClient::SERVER_ERROR:
+      case GCMClient::UNKNOWN_ERROR:
+        callback.Run(content::PUSH_UNREGISTRATION_STATUS_SERVICE_ERROR);
+        break;
+      case GCMClient::NETWORK_ERROR:
+      case GCMClient::TTL_EXCEEDED:
+        callback.Run(
+            retry_on_failure
+            ? content::
+              PUSH_UNREGISTRATION_STATUS_PENDING_WILL_RETRY_NETWORK_ERROR
+            : content::PUSH_UNREGISTRATION_STATUS_NETWORK_ERROR);
+        break;
     }
   }
 }
 
+// OnContentSettingChanged methods ---------------------------------------------
+
+void PushMessagingServiceImpl::OnContentSettingChanged(
+    const ContentSettingsPattern& primary_pattern,
+    const ContentSettingsPattern& secondary_pattern,
+    ContentSettingsType content_type,
+    std::string resource_identifier) {
+  if (content_type != CONTENT_SETTINGS_TYPE_PUSH_MESSAGING &&
+      content_type != CONTENT_SETTINGS_TYPE_NOTIFICATIONS) {
+    return;
+  }
+
+  for (const auto& id : PushMessagingApplicationId::GetAll(profile_)) {
+    // If |primary_pattern| is not valid, we should always check for a
+    // permission change because it can happen for example when the entire
+    // Push or Notifications permissions are cleared.
+    // Otherwise, the permission should be checked if the pattern matches the
+    // origin.
+    if (primary_pattern.IsValid() && !primary_pattern.Matches(id.origin()))
+      continue;
+
+    if (HasPermission(id.origin()))
+      continue;
+
+    PushMessagingService::GetSenderId(
+        profile_, id.origin(), id.service_worker_registration_id(),
+        base::Bind(
+            &PushMessagingServiceImpl::UnregisterBecausePermissionRevoked,
+            weak_factory_.GetWeakPtr(), id));
+  }
+}
+
+void PushMessagingServiceImpl::UnregisterBecausePermissionRevoked(
+    const PushMessagingApplicationId& id,
+    const std::string& sender_id, bool success, bool not_found) {
+  // Unregister the PushMessagingApplicationId with the push service.
+  Unregister(id.app_id_guid(), sender_id, true /* retry_on_failure */,
+             UnregisterCallback());
+
+  // Clear the associated service worker push registration id.
+  PushMessagingService::ClearPushRegistrationID(
+      profile_, id.origin(), id.service_worker_registration_id());
+}
+
+// Helper methods --------------------------------------------------------------
+
 bool PushMessagingServiceImpl::HasPermission(const GURL& origin) {
   gcm::PushMessagingPermissionContext* permission_context =
       gcm::PushMessagingPermissionContextFactory::GetForProfile(profile_);
@@ -700,4 +783,9 @@
       CONTENT_SETTING_ALLOW;
 }
 
+void PushMessagingServiceImpl::SetProfileForTesting(Profile* profile) {
+  profile_ = profile;
+  profile_->GetHostContentSettingsMap()->AddObserver(this);
+}
+
 }  // namespace gcm
diff --git a/chrome/browser/services/gcm/push_messaging_service_impl.h b/chrome/browser/services/gcm/push_messaging_service_impl.h
index 29c03b89..c5cb500 100644
--- a/chrome/browser/services/gcm/push_messaging_service_impl.h
+++ b/chrome/browser/services/gcm/push_messaging_service_impl.h
@@ -7,6 +7,7 @@
 
 #include "base/compiler_specific.h"
 #include "base/memory/weak_ptr.h"
+#include "components/content_settings/core/browser/content_settings_observer.h"
 #include "components/gcm_driver/gcm_app_handler.h"
 #include "components/gcm_driver/gcm_client.h"
 #include "content/public/browser/push_messaging_service.h"
@@ -25,7 +26,8 @@
 class PushMessagingApplicationId;
 
 class PushMessagingServiceImpl : public content::PushMessagingService,
-                                 public GCMAppHandler {
+                                 public GCMAppHandler,
+                                 public content_settings::Observer {
  public:
   // Register profile-specific prefs for GCM.
   static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
@@ -67,12 +69,19 @@
   void Unregister(
       const GURL& requesting_origin,
       int64 service_worker_registration_id,
+      const std::string& sender_id,
       bool retry_on_failure,
       const content::PushMessagingService::UnregisterCallback&) override;
   blink::WebPushPermissionStatus GetPermissionStatus(
       const GURL& requesting_origin,
       const GURL& embedding_origin) override;
 
+  // content_settings::Observer implementation.
+  void OnContentSettingChanged(const ContentSettingsPattern& primary_pattern,
+                               const ContentSettingsPattern& secondary_pattern,
+                               ContentSettingsType content_type,
+                               std::string resource_identifier) override;
+
   void SetProfileForTesting(Profile* profile);
 
  private:
@@ -80,6 +89,8 @@
   void IncreasePushRegistrationCount(int add, bool is_pending);
   void DecreasePushRegistrationCount(int subtract, bool was_pending);
 
+  // OnMessage methods ---------------------------------------------------------
+
   void DeliverMessageCallback(const std::string& app_id_guid,
                               const GURL& requesting_origin,
                               int64 service_worker_registration_id,
@@ -101,6 +112,8 @@
       bool success,
       bool not_found);
 
+  // Register methods ----------------------------------------------------------
+
   void RegisterEnd(
       const content::PushMessagingService::RegisterCallback& callback,
       const std::string& registration_id,
@@ -118,7 +131,10 @@
       const content::PushMessagingService::RegisterCallback& callback,
       bool allow);
 
+  // Unregister methods --------------------------------------------------------
+
   void Unregister(const std::string& app_id_guid,
+                  const std::string& sender_id,
                   bool retry_on_failure,
                   const content::PushMessagingService::UnregisterCallback&);
 
@@ -127,7 +143,15 @@
                      const content::PushMessagingService::UnregisterCallback&,
                      GCMClient::Result result);
 
-  // Helper method that checks if a given origin is allowed to use Push.
+  // OnContentSettingChanged methods -------------------------------------------
+
+  void UnregisterBecausePermissionRevoked(const PushMessagingApplicationId& id,
+                                          const std::string& sender_id,
+                                          bool success, bool not_found);
+
+  // Helper methods ------------------------------------------------------------
+
+  // Checks if a given origin is allowed to use Push.
   bool HasPermission(const GURL& origin);
 
   GCMProfileService* gcm_profile_service_;  // It owns us.
diff --git a/chrome/browser/sessions/session_restore_browsertest.cc b/chrome/browser/sessions/session_restore_browsertest.cc
index 2f091caa..1800ab1 100644
--- a/chrome/browser/sessions/session_restore_browsertest.cc
+++ b/chrome/browser/sessions/session_restore_browsertest.cc
@@ -396,7 +396,7 @@
       base::FilePath(base::FilePath::kCurrentDirectory),
       base::FilePath(FILE_PATH_LITERAL("title1.html"))));
   // Any page that will yield a 200 status code will work here.
-  GURL url2("about:version");
+  GURL url2("chrome://version");
   GURL url3(ui_test_utils::GetTestUrl(
       base::FilePath(base::FilePath::kCurrentDirectory),
       base::FilePath(FILE_PATH_LITERAL("title3.html"))));
diff --git a/chrome/browser/signin/easy_unlock_metrics.cc b/chrome/browser/signin/easy_unlock_metrics.cc
index 892d752..ba46c7f6 100644
--- a/chrome/browser/signin/easy_unlock_metrics.cc
+++ b/chrome/browser/signin/easy_unlock_metrics.cc
@@ -18,3 +18,10 @@
   UMA_HISTOGRAM_ENUMERATION("EasyUnlock.AuthEvent.Unlock", event,
                             EASY_UNLOCK_AUTH_EVENT_COUNT);
 }
+
+void RecordEasyUnlockTrialRunEvent(EasyUnlockTrialRunEvent event) {
+  DCHECK_LT(event, EASY_UNLOCK_TRIAL_RUN_EVENT_COUNT);
+  UMA_HISTOGRAM_ENUMERATION("EasyUnlock.TrialRun.Events",
+                            event,
+                            EASY_UNLOCK_TRIAL_RUN_EVENT_COUNT);
+}
diff --git a/chrome/browser/signin/easy_unlock_metrics.h b/chrome/browser/signin/easy_unlock_metrics.h
index 019a7ff..44c26473 100644
--- a/chrome/browser/signin/easy_unlock_metrics.h
+++ b/chrome/browser/signin/easy_unlock_metrics.h
@@ -61,7 +61,16 @@
   EASY_UNLOCK_AUTH_EVENT_COUNT  // Must be the last entry.
 };
 
+enum EasyUnlockTrialRunEvent {
+  // A trial run was initiated from the Easy Unlock setup app.
+  EASY_UNLOCK_TRIAL_RUN_EVENT_LAUNCHED = 0,
+  // The user clicked on the lock icon during the trial run.
+  EASY_UNLOCK_TRIAL_RUN_EVENT_CLICKED_LOCK_ICON = 1,
+  EASY_UNLOCK_TRIAL_RUN_EVENT_COUNT  // Must be the last entry.
+};
+
 void RecordEasyUnlockSigninEvent(EasyUnlockAuthEvent event);
 void RecordEasyUnlockScreenUnlockEvent(EasyUnlockAuthEvent event);
+void RecordEasyUnlockTrialRunEvent(EasyUnlockTrialRunEvent event);
 
 #endif  // CHROME_BROWSER_SIGNIN_EASY_UNLOCK_METRICS_H_
diff --git a/chrome/browser/signin/easy_unlock_screenlock_state_handler.cc b/chrome/browser/signin/easy_unlock_screenlock_state_handler.cc
index a42bf1a4..b24539c 100644
--- a/chrome/browser/signin/easy_unlock_screenlock_state_handler.cc
+++ b/chrome/browser/signin/easy_unlock_screenlock_state_handler.cc
@@ -8,6 +8,7 @@
 #include "base/strings/string16.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/chromeos/chromeos_utils.h"
+#include "chrome/browser/signin/easy_unlock_metrics.h"
 #include "chrome/grit/generated_resources.h"
 #include "ui/base/l10n/l10n_util.h"
 
@@ -154,10 +155,12 @@
   icon_options.SetIcon(icon);
 
   // Don't hardlock on trial run.
-  if (!is_trial_run_ && HardlockOnClick(state_))
+  if (is_trial_run_)
+    icon_options.SetTrialRun();
+  else if (HardlockOnClick(state_))
     icon_options.SetHardlockOnClick();
 
-  UpdateTooltipOptions(is_trial_run_, &icon_options);
+  UpdateTooltipOptions(&icon_options);
 
   // For states without tooltips, we still need to set an accessibility label.
   if (state_ == EasyUnlockScreenlockStateHandler::STATE_BLUETOOTH_CONNECTING) {
@@ -198,6 +201,13 @@
     return;
   is_trial_run_ = true;
   RefreshScreenlockState();
+  RecordEasyUnlockTrialRunEvent(EASY_UNLOCK_TRIAL_RUN_EVENT_LAUNCHED);
+}
+
+void EasyUnlockScreenlockStateHandler::RecordClickOnLockIcon() {
+  if (!is_trial_run_)
+    return;
+  RecordEasyUnlockTrialRunEvent(EASY_UNLOCK_TRIAL_RUN_EVENT_CLICKED_LOCK_ICON);
 }
 
 void EasyUnlockScreenlockStateHandler::OnScreenDidLock(
@@ -289,11 +299,10 @@
 }
 
 void EasyUnlockScreenlockStateHandler::UpdateTooltipOptions(
-    bool trial_run,
     ScreenlockBridge::UserPodCustomIconOptions* icon_options) {
   size_t resource_id = 0;
   base::string16 device_name;
-  if (trial_run && state_ == STATE_AUTHENTICATED) {
+  if (is_trial_run_ && state_ == STATE_AUTHENTICATED) {
     resource_id = IDS_EASY_UNLOCK_SCREENLOCK_TOOLTIP_INITIAL_AUTHENTICATED;
   } else {
     resource_id = GetTooltipResourceId(state_);
@@ -316,7 +325,7 @@
 
   icon_options->SetTooltip(
       tooltip,
-      trial_run || (state_ != STATE_AUTHENTICATED) /* autoshow tooltip */);
+      is_trial_run_ || (state_ != STATE_AUTHENTICATED) /* autoshow tooltip */);
 }
 
 base::string16 EasyUnlockScreenlockStateHandler::GetDeviceName() {
diff --git a/chrome/browser/signin/easy_unlock_screenlock_state_handler.h b/chrome/browser/signin/easy_unlock_screenlock_state_handler.h
index 26b40dc..b0c618b 100644
--- a/chrome/browser/signin/easy_unlock_screenlock_state_handler.h
+++ b/chrome/browser/signin/easy_unlock_screenlock_state_handler.h
@@ -97,6 +97,10 @@
   // Marks the current screenlock state as the one for trial Easy Unlock run.
   void SetTrialRun();
 
+  // Records that the user clicked on the lock icon during the trial run
+  // initiated by the Easy Unlock app.
+  void RecordClickOnLockIcon();
+
   State state() const { return state_; }
 
  private:
@@ -113,9 +117,7 @@
   void ShowHardlockUI();
 
   // Updates icon's tooltip options.
-  // |trial_run|: Whether the trial Easy Unlock run is in progress.
   void UpdateTooltipOptions(
-      bool trial_run,
       ScreenlockBridge::UserPodCustomIconOptions* icon_options);
 
   // Gets the name to be used for the device. The name depends on the device
diff --git a/chrome/browser/signin/easy_unlock_screenlock_state_handler_unittest.cc b/chrome/browser/signin/easy_unlock_screenlock_state_handler_unittest.cc
index 1a72f76..75aa1b29 100644
--- a/chrome/browser/signin/easy_unlock_screenlock_state_handler_unittest.cc
+++ b/chrome/browser/signin/easy_unlock_screenlock_state_handler_unittest.cc
@@ -2,13 +2,16 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "chrome/browser/signin/easy_unlock_screenlock_state_handler.h"
+
 #include <string>
 #include <vector>
 
 #include "base/strings/string16.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
-#include "chrome/browser/signin/easy_unlock_screenlock_state_handler.h"
+#include "base/test/histogram_tester.h"
+#include "chrome/browser/signin/easy_unlock_metrics.h"
 #include "chrome/browser/signin/easy_unlock_service.h"
 #include "chrome/browser/signin/screenlock_bridge.h"
 #include "chrome/grit/generated_resources.h"
@@ -751,4 +754,26 @@
   }
 }
 
+TEST_F(EasyUnlockScreenlockStateHandlerTest, TrialRunMetrics) {
+  base::HistogramTester histogram_tester;
+
+  // Simulate the user clicking on the lock icon twice outside of a trial run.
+  // No trial run metrics should be recorded.
+  state_handler_->RecordClickOnLockIcon();
+  state_handler_->RecordClickOnLockIcon();
+  histogram_tester.ExpectTotalCount("EasyUnlock.TrialRun.Events", 0);
+
+  // Simulate the user clicking on the lock icon three times during a trial run.
+  state_handler_->SetTrialRun();
+  state_handler_->RecordClickOnLockIcon();
+  state_handler_->RecordClickOnLockIcon();
+  state_handler_->RecordClickOnLockIcon();
+  histogram_tester.ExpectTotalCount("EasyUnlock.TrialRun.Events", 4);
+  histogram_tester.ExpectBucketCount("EasyUnlock.TrialRun.Events",
+                                     EASY_UNLOCK_TRIAL_RUN_EVENT_LAUNCHED, 1);
+  histogram_tester.ExpectBucketCount(
+      "EasyUnlock.TrialRun.Events",
+      EASY_UNLOCK_TRIAL_RUN_EVENT_CLICKED_LOCK_ICON, 3);
+}
+
 }  // namespace
diff --git a/chrome/browser/signin/easy_unlock_service.cc b/chrome/browser/signin/easy_unlock_service.cc
index 8ce8fd49..430a98f 100644
--- a/chrome/browser/signin/easy_unlock_service.cc
+++ b/chrome/browser/signin/easy_unlock_service.cc
@@ -500,13 +500,18 @@
 }
 
 void EasyUnlockService::SetTrialRun() {
-  DCHECK(GetType() == TYPE_REGULAR);
+  DCHECK_EQ(GetType(), TYPE_REGULAR);
 
   EasyUnlockScreenlockStateHandler* handler = GetScreenlockStateHandler();
   if (handler)
     handler->SetTrialRun();
 }
 
+void EasyUnlockService::RecordClickOnLockIcon() {
+  if (screenlock_state_handler_)
+    screenlock_state_handler_->RecordClickOnLockIcon();
+}
+
 void EasyUnlockService::AddObserver(EasyUnlockServiceObserver* observer) {
   observers_.AddObserver(observer);
 }
diff --git a/chrome/browser/signin/easy_unlock_service.h b/chrome/browser/signin/easy_unlock_service.h
index 406550e2..372acdd1 100644
--- a/chrome/browser/signin/easy_unlock_service.h
+++ b/chrome/browser/signin/easy_unlock_service.h
@@ -193,6 +193,10 @@
   // trial run initiated by Easy Unlock app.
   void SetTrialRun();
 
+  // Records that the user clicked on the lock icon during the trial run
+  // initiated by the Easy Unlock app.
+  void RecordClickOnLockIcon();
+
   void AddObserver(EasyUnlockServiceObserver* observer);
   void RemoveObserver(EasyUnlockServiceObserver* observer);
 
@@ -219,7 +223,8 @@
   void Shutdown() override;
 
   // Exposes the profile to which the service is attached to subclasses.
-  Profile* profile() const { return profile_; }
+  const Profile* profile() const { return profile_; }
+  Profile* profile() { return profile_; }
 
   // Opens an Easy Unlock Setup app window.
   void OpenSetupApp();
diff --git a/chrome/browser/signin/easy_unlock_service_regular.cc b/chrome/browser/signin/easy_unlock_service_regular.cc
index f7fdf26d..96da4036 100644
--- a/chrome/browser/signin/easy_unlock_service_regular.cc
+++ b/chrome/browser/signin/easy_unlock_service_regular.cc
@@ -302,7 +302,7 @@
   // ephemeral accounts can be locked, so we should revisit this use case.
   // TODO(tengs): Remove this special case and test this code path after test
   // refactoring is landed (crbug.com/414829).
-  user_manager::User* user =
+  const user_manager::User* user =
       chromeos::ProfileHelper::Get()->GetUserByProfile(profile());
   user_manager::UserManager* user_manager = user_manager::UserManager::Get();
   if (user->email() != chromeos::login::kStubUser &&
diff --git a/chrome/browser/signin/screenlock_bridge.cc b/chrome/browser/signin/screenlock_bridge.cc
index 586ed48..c8db7a3 100644
--- a/chrome/browser/signin/screenlock_bridge.cc
+++ b/chrome/browser/signin/screenlock_bridge.cc
@@ -60,7 +60,8 @@
 
 ScreenlockBridge::UserPodCustomIconOptions::UserPodCustomIconOptions()
     : autoshow_tooltip_(false),
-      hardlock_on_click_(false) {
+      hardlock_on_click_(false),
+      is_trial_run_(false) {
 }
 
 ScreenlockBridge::UserPodCustomIconOptions::~UserPodCustomIconOptions() {}
@@ -84,6 +85,9 @@
   if (hardlock_on_click_)
     result->SetBoolean("hardlockOnClick", true);
 
+  if (is_trial_run_)
+    result->SetBoolean("isTrialRun", true);
+
   return result.Pass();
 }
 
@@ -108,11 +112,16 @@
   hardlock_on_click_ = true;
 }
 
+void ScreenlockBridge::UserPodCustomIconOptions::SetTrialRun() {
+  is_trial_run_ = true;
+}
+
 // static
-std::string ScreenlockBridge::GetAuthenticatedUserEmail(Profile* profile) {
+std::string ScreenlockBridge::GetAuthenticatedUserEmail(
+    const Profile* profile) {
   // |profile| has to be a signed-in profile with SigninManager already
   // created. Otherwise, just crash to collect stack.
-  SigninManagerBase* signin_manager =
+  const SigninManagerBase* signin_manager =
       SigninManagerFactory::GetForProfileIfExists(profile);
   return signin_manager->GetAuthenticatedUsername();
 }
diff --git a/chrome/browser/signin/screenlock_bridge.h b/chrome/browser/signin/screenlock_bridge.h
index 4fc8f7d..d376f68 100644
--- a/chrome/browser/signin/screenlock_bridge.h
+++ b/chrome/browser/signin/screenlock_bridge.h
@@ -67,6 +67,10 @@
     // go to state where password is required for unlock.
     void SetHardlockOnClick();
 
+    // If the current lock screen is a trial run to introduce users to Easy
+    // Unlock, the icon will record metrics upon click.
+    void SetTrialRun();
+
    private:
     UserPodCustomIcon icon_;
 
@@ -77,6 +81,8 @@
 
     bool hardlock_on_click_;
 
+    bool is_trial_run_;
+
     DISALLOW_COPY_AND_ASSIGN(UserPodCustomIconOptions);
   };
 
@@ -152,7 +158,7 @@
   };
 
   static ScreenlockBridge* Get();
-  static std::string GetAuthenticatedUserEmail(Profile* profile);
+  static std::string GetAuthenticatedUserEmail(const Profile* profile);
 
   void SetLockHandler(LockHandler* lock_handler);
   void SetFocusedUser(const std::string& user_id);
diff --git a/chrome/browser/signin/signin_manager_factory.cc b/chrome/browser/signin/signin_manager_factory.cc
index 7867c69..9e6ab705 100644
--- a/chrome/browser/signin/signin_manager_factory.cc
+++ b/chrome/browser/signin/signin_manager_factory.cc
@@ -36,6 +36,13 @@
       GetInstance()->GetServiceForBrowserContext(profile, false));
 }
 
+const SigninManagerBase* SigninManagerFactory::GetForProfileIfExists(
+    const Profile* profile) {
+  return static_cast<const SigninManagerBase*>(
+      GetInstance()->GetServiceForBrowserContext(
+          const_cast<Profile*>(profile), false));
+}
+
 // static
 SigninManagerBase* SigninManagerFactory::GetForProfile(
     Profile* profile) {
@@ -55,6 +62,14 @@
   return static_cast<SigninManager*>(
       GetInstance()->GetServiceForBrowserContext(profile, false));
 }
+
+// static
+const SigninManager* SigninManagerFactory::GetForProfileIfExists(
+    const Profile* profile) {
+  return static_cast<const SigninManager*>(
+      GetInstance()->GetServiceForBrowserContext(
+          const_cast<Profile*>(profile), false));
+}
 #endif
 
 // static
diff --git a/chrome/browser/signin/signin_manager_factory.h b/chrome/browser/signin/signin_manager_factory.h
index 3d00b06..dec6b6d 100644
--- a/chrome/browser/signin/signin_manager_factory.h
+++ b/chrome/browser/signin/signin_manager_factory.h
@@ -42,11 +42,13 @@
   // null if no SigninManager instance currently exists (will not create a new
   // instance).
   static SigninManagerBase* GetForProfileIfExists(Profile* profile);
+  static const SigninManagerBase* GetForProfileIfExists(const Profile* profile);
 #else
   // On non-ChromeOS platforms, the SigninManager the factory creates will be
   // an instance of the extended SigninManager class.
   static SigninManager* GetForProfile(Profile* profile);
   static SigninManager* GetForProfileIfExists(Profile* profile);
+  static const SigninManager* GetForProfileIfExists(const Profile* profile);
 #endif
 
   // Returns an instance of the SigninManagerFactory singleton.
diff --git a/chrome/browser/spellchecker/spellcheck_message_filter_mac.cc b/chrome/browser/spellchecker/spellcheck_message_filter_mac.cc
index 8d0be53..9228cd3 100644
--- a/chrome/browser/spellchecker/spellcheck_message_filter_mac.cc
+++ b/chrome/browser/spellchecker/spellcheck_message_filter_mac.cc
@@ -265,6 +265,13 @@
     std::vector<SpellCheckMarker> markers) {
   DCHECK(!text.empty());
   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+
+  // Initialize the spellcheck service if needed. The service will send the
+  // language code for text breaking to the renderer. (Text breaking is required
+  // for the context menu to show spelling suggestions.) Initialization must
+  // happen on UI thread.
+  SpellcheckServiceFactory::GetForRenderProcessId(render_process_id_);
+
   // Erase invalid markers (with offsets out of boundaries of text length).
   markers.erase(
       std::remove_if(
diff --git a/chrome/browser/supervised_user/chromeos/manager_password_service_factory.cc b/chrome/browser/supervised_user/chromeos/manager_password_service_factory.cc
index 77d6205..e7fb7e8e 100644
--- a/chrome/browser/supervised_user/chromeos/manager_password_service_factory.cc
+++ b/chrome/browser/supervised_user/chromeos/manager_password_service_factory.cc
@@ -44,7 +44,8 @@
 KeyedService* ManagerPasswordServiceFactory::BuildServiceInstanceFor(
     content::BrowserContext* context) const {
   Profile* profile = static_cast<Profile*>(context);
-  user_manager::User* user = ProfileHelper::Get()->GetUserByProfile(profile);
+  const user_manager::User* user =
+      ProfileHelper::Get()->GetUserByProfile(profile);
   if (ChromeUserManager::Get()->GetSupervisedUserManager()->HasSupervisedUsers(
           user->email())) {
     ManagerPasswordService* result = new ManagerPasswordService();
diff --git a/chrome/browser/supervised_user/chromeos/supervised_user_password_service_factory.cc b/chrome/browser/supervised_user/chromeos/supervised_user_password_service_factory.cc
index c320fe5..08f181f4 100644
--- a/chrome/browser/supervised_user/chromeos/supervised_user_password_service_factory.cc
+++ b/chrome/browser/supervised_user/chromeos/supervised_user_password_service_factory.cc
@@ -42,7 +42,8 @@
 KeyedService* SupervisedUserPasswordServiceFactory::BuildServiceInstanceFor(
     content::BrowserContext* context) const {
   Profile* profile = static_cast<Profile*>(context);
-  user_manager::User* user = ProfileHelper::Get()->GetUserByProfile(profile);
+  const user_manager::User* user =
+      ProfileHelper::Get()->GetUserByProfile(profile);
   if (user->GetType() != user_manager::USER_TYPE_SUPERVISED)
     return NULL;
   SupervisedUserPasswordService* result = new SupervisedUserPasswordService();
diff --git a/chrome/browser/sync/backend_migrator_unittest.cc b/chrome/browser/sync/backend_migrator_unittest.cc
index 7736e87..a5c29f80 100644
--- a/chrome/browser/sync/backend_migrator_unittest.cc
+++ b/chrome/browser/sync/backend_migrator_unittest.cc
@@ -5,10 +5,12 @@
 #include "chrome/browser/sync/backend_migrator.h"
 
 #include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
 #include "base/tracked_objects.h"
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/sync/profile_sync_service_mock.h"
 #include "components/sync_driver/data_type_manager_mock.h"
+#include "content/public/test/test_browser_thread_bundle.h"
 #include "sync/internal_api/public/base/model_type_test_util.h"
 #include "sync/internal_api/public/test/test_user_share.h"
 #include "sync/internal_api/public/write_transaction.h"
@@ -84,7 +86,8 @@
           requested_types);
       migrator_->OnConfigureDone(result);
     }
-    message_loop_.RunUntilIdle();
+    base::RunLoop run_loop;
+    run_loop.RunUntilIdle();
   }
 
   ProfileSyncService* service() { return &service_; }
@@ -99,8 +102,7 @@
   }
 
  private:
-  scoped_ptr<SyncSessionSnapshot> snap_;
-  base::MessageLoop message_loop_;
+  content::TestBrowserThreadBundle thread_bundle_;
   syncer::ModelTypeSet preferred_types_;
   TestingProfile profile_;
   NiceMock<ProfileSyncServiceMock> service_;
diff --git a/chrome/browser/sync/glue/bookmark_data_type_controller_unittest.cc b/chrome/browser/sync/glue/bookmark_data_type_controller_unittest.cc
index 2cd6cfb9..f0d6578 100644
--- a/chrome/browser/sync/glue/bookmark_data_type_controller_unittest.cc
+++ b/chrome/browser/sync/glue/bookmark_data_type_controller_unittest.cc
@@ -51,12 +51,11 @@
 
 class HistoryMock : public HistoryService {
  public:
-  explicit HistoryMock(history::HistoryClient* client, Profile* profile)
-      : HistoryService(client, profile) {}
+  HistoryMock() : HistoryService() {}
   MOCK_METHOD0(BackendLoaded, bool(void));
 
  protected:
-  virtual ~HistoryMock() {}
+  ~HistoryMock() override {}
 };
 
 KeyedService* BuildChromeBookmarkClient(content::BrowserContext* context) {
@@ -87,7 +86,7 @@
 }
 
 KeyedService* BuildHistoryService(content::BrowserContext* profile) {
-  return new HistoryMock(NULL, static_cast<Profile*>(profile));
+  return new HistoryMock;
 }
 
 }  // namespace
diff --git a/chrome/browser/sync/glue/sync_backend_host_impl.cc b/chrome/browser/sync/glue/sync_backend_host_impl.cc
index 4114a32f..1455d17 100644
--- a/chrome/browser/sync/glue/sync_backend_host_impl.cc
+++ b/chrome/browser/sync/glue/sync_backend_host_impl.cc
@@ -609,6 +609,11 @@
     const syncer::ModelTypeSet failed_configuration_types,
     const base::Callback<void(syncer::ModelTypeSet,
                               syncer::ModelTypeSet)>& ready_task) {
+  // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/458406 is
+  // fixed.
+  tracked_objects::ScopedTracker tracking_profile1(
+      FROM_HERE_WITH_EXPLICIT_FUNCTION(
+          "458406 SyncBackendHostImpl::FinishConfigureDataTOFL"));
   if (!frontend_)
     return;
 
@@ -618,6 +623,11 @@
         ModelTypeSetToObjectIdSet(enabled_types));
   }
 
+  // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/458406 is
+  // fixed.
+  tracked_objects::ScopedTracker tracking_profile2(
+      FROM_HERE_WITH_EXPLICIT_FUNCTION(
+          "458406 SyncBackendHostImpl::FinishConfigureDataTOFL::ReadyTask"));
   if (!ready_task.is_null())
     ready_task.Run(succeeded_configuration_types, failed_configuration_types);
 }
diff --git a/chrome/browser/sync/glue/synced_session.cc b/chrome/browser/sync/glue/synced_session.cc
index 6a0f070..9abc736d 100644
--- a/chrome/browser/sync/glue/synced_session.cc
+++ b/chrome/browser/sync/glue/synced_session.cc
@@ -56,7 +56,7 @@
 }
 
 // Note: if you modify this, make sure you modify
-// SessionModelAssociator::ShouldSyncTab to ensure the logic matches.
+// browser_sync::sessions_util::ShouldSyncTab to ensure the logic matches.
 bool ShouldSyncSessionTab(const sessions::SessionTab& tab) {
   if (tab.navigations.empty())
     return false;
diff --git a/chrome/browser/sync/profile_sync_service.cc b/chrome/browser/sync/profile_sync_service.cc
index 7bf1cfef..36fa5d3 100644
--- a/chrome/browser/sync/profile_sync_service.cc
+++ b/chrome/browser/sync/profile_sync_service.cc
@@ -49,6 +49,7 @@
 #include "chrome/browser/sync/sessions/notification_service_sessions_router.h"
 #include "chrome/browser/sync/supervised_user_signin_manager_wrapper.h"
 #include "chrome/browser/sync/sync_error_controller.h"
+#include "chrome/browser/sync/sync_stopped_reporter.h"
 #include "chrome/browser/sync/sync_type_preference_provider.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_list.h"
@@ -93,6 +94,8 @@
 #include "sync/internal_api/public/util/sync_db_util.h"
 #include "sync/internal_api/public/util/sync_string_conversions.h"
 #include "sync/js/js_event_details.h"
+#include "sync/protocol/sync.pb.h"
+#include "sync/syncable/directory.h"
 #include "sync/util/cryptographer.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/l10n/time_format.h"
@@ -240,8 +243,14 @@
       backup_finished_(false),
       clear_browsing_data_(base::Bind(&ClearBrowsingData)),
       browsing_data_remover_observer_(NULL),
+      sync_stopped_reporter_(
+          new browser_sync::SyncStoppedReporter(
+              sync_service_url_,
+              profile_->GetRequestContext(),
+              browser_sync::SyncStoppedReporter::ResultCallback())),
       weak_factory_(this),
       startup_controller_weak_factory_(this) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   DCHECK(profile);
   startup_controller_.reset(new browser_sync::StartupController(
       start_behavior,
@@ -819,6 +828,11 @@
     return;
   }
 
+  if (reason == syncer::ShutdownReason::STOP_SYNC
+      || reason == syncer::ShutdownReason::DISABLE_SYNC) {
+    RemoveClientFromServer();
+  }
+
   non_blocking_data_type_manager_.DisconnectSyncBackend();
 
   // First, we spin down the backend to stop change processing as soon as
@@ -881,7 +895,6 @@
   is_auth_in_progress_ = false;
   backend_initialized_ = false;
   cached_passphrase_.clear();
-  access_token_.clear();
   encryption_pending_ = false;
   encrypt_everything_ = false;
   encrypted_types_ = syncer::SyncEncryptionHandler::SensitiveTypes();
@@ -2752,3 +2765,17 @@
     return NULL;
   }
 }
+
+void ProfileSyncService::RemoveClientFromServer() const {
+  if (!backend_initialized_) return;
+  const std::string cache_guid = local_device_->GetLocalSyncCacheGUID();
+  std::string birthday;
+  syncer::UserShare* user_share = GetUserShare();
+  if (user_share && user_share->directory.get()) {
+    birthday = user_share->directory->store_birthday();
+  }
+  if (!access_token_.empty() && !cache_guid.empty() && !birthday.empty()) {
+    sync_stopped_reporter_->ReportSyncStopped(
+        access_token_, cache_guid, birthday);
+  }
+}
diff --git a/chrome/browser/sync/profile_sync_service.h b/chrome/browser/sync/profile_sync_service.h
index a16ab94d..f40ac51 100644
--- a/chrome/browser/sync/profile_sync_service.h
+++ b/chrome/browser/sync/profile_sync_service.h
@@ -30,6 +30,7 @@
 #include "chrome/browser/sync/protocol_event_observer.h"
 #include "chrome/browser/sync/sessions/sessions_sync_manager.h"
 #include "chrome/browser/sync/startup_controller.h"
+#include "chrome/browser/sync/sync_stopped_reporter.h"
 #include "components/keyed_service/core/keyed_service.h"
 #include "components/signin/core/browser/signin_manager_base.h"
 #include "components/sync_driver/data_type_controller.h"
@@ -980,6 +981,9 @@
   // Clean up prefs and backup DB when rollback is not needed.
   void CleanUpBackup();
 
+  // Tell the sync server that this client has disabled sync.
+  void RemoveClientFromServer() const;
+
   // Factory used to create various dependent objects.
   scoped_ptr<ProfileSyncComponentsFactory> factory_;
 
@@ -1157,6 +1161,8 @@
   // The full path to the sync data directory.
   base::FilePath directory_path_;
 
+  scoped_ptr<browser_sync::SyncStoppedReporter> sync_stopped_reporter_;
+
   base::WeakPtrFactory<ProfileSyncService> weak_factory_;
 
   // We don't use |weak_factory_| for the StartupController because the weak
diff --git a/chrome/browser/sync/profile_sync_service_typed_url_unittest.cc b/chrome/browser/sync/profile_sync_service_typed_url_unittest.cc
index 899b220..07f7ae1a 100644
--- a/chrome/browser/sync/profile_sync_service_typed_url_unittest.cc
+++ b/chrome/browser/sync/profile_sync_service_typed_url_unittest.cc
@@ -113,11 +113,11 @@
 
 class HistoryServiceMock : public HistoryService {
  public:
-  HistoryServiceMock(history::HistoryClient* client, Profile* profile)
-      : HistoryService(client, profile), backend_(NULL) {}
+  HistoryServiceMock() : HistoryService(), backend_(nullptr) {}
 
-  virtual void ScheduleDBTask(scoped_ptr<history::HistoryDBTask> task,
-                              base::CancelableTaskTracker* tracker) override {
+  base::CancelableTaskTracker::TaskId ScheduleDBTask(
+      scoped_ptr<history::HistoryDBTask> task,
+      base::CancelableTaskTracker* tracker) override {
     history::HistoryDBTask* task_raw = task.get();
     task_runner_->PostTaskAndReply(
         FROM_HERE,
@@ -125,6 +125,7 @@
                    base::Unretained(this), task_raw),
         base::Bind(&base::DeletePointer<history::HistoryDBTask>,
                    task.release()));
+    return base::CancelableTaskTracker::kBadTaskId;  // unused
   }
 
   MOCK_METHOD0(Shutdown, void());
@@ -144,7 +145,7 @@
   }
 
  private:
-  virtual ~HistoryServiceMock() {}
+  ~HistoryServiceMock() override {}
 
   void RunTaskOnDBThread(history::HistoryDBTask* task) {
     EXPECT_TRUE(task->RunOnDBThread(backend_.get(), NULL));
@@ -162,7 +163,7 @@
 }
 
 KeyedService* BuildHistoryService(content::BrowserContext* profile) {
-  return new HistoryServiceMock(NULL, static_cast<Profile*>(profile));
+  return new HistoryServiceMock;
 }
 
 class TestTypedUrlModelAssociator : public TypedUrlModelAssociator {
diff --git a/chrome/browser/sync/sync_stopped_reporter.cc b/chrome/browser/sync/sync_stopped_reporter.cc
new file mode 100644
index 0000000..b57f03a
--- /dev/null
+++ b/chrome/browser/sync/sync_stopped_reporter.cc
@@ -0,0 +1,108 @@
+// 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 "chrome/browser/sync/sync_stopped_reporter.h"
+
+#include "base/logging.h"
+#include "base/strings/stringprintf.h"
+#include "base/timer/timer.h"
+#include "chrome/browser/profiles/profile.h"
+#include "net/base/load_flags.h"
+#include "net/http/http_status_code.h"
+#include "net/url_request/url_fetcher.h"
+#include "net/url_request/url_request_context_getter.h"
+#include "sync/protocol/sync.pb.h"
+
+namespace {
+  const char kEventEndpoint[] = "event";
+
+  // The request is tiny, so even on poor connections 10 seconds should be
+  // plenty of time. Since sync is off when this request is started, we don't
+  // want anything sync-related hanging around for very long from a human
+  // perspective either. This seems like a good compromise.
+  const base::TimeDelta kRequestTimeout = base::TimeDelta::FromSeconds(10);
+}
+
+namespace browser_sync {
+
+SyncStoppedReporter::SyncStoppedReporter(
+    const GURL& sync_service_url,
+    const scoped_refptr<net::URLRequestContextGetter>& request_context,
+    const ResultCallback& callback)
+    : sync_event_url_(GetSyncEventURL(sync_service_url)),
+      request_context_(request_context),
+      callback_(callback) {
+  DCHECK(!sync_service_url.is_empty());
+  DCHECK(request_context);
+}
+
+SyncStoppedReporter::~SyncStoppedReporter() {}
+
+void SyncStoppedReporter::ReportSyncStopped(const std::string& access_token,
+                                            const std::string& cache_guid,
+                                            const std::string& birthday) {
+  DCHECK(!access_token.empty());
+  DCHECK(!cache_guid.empty());
+  DCHECK(!birthday.empty());
+
+  // Make the request proto with the GUID identifying this client.
+  sync_pb::EventRequest event_request;
+  sync_pb::SyncDisabledEvent* sync_disabled_event =
+      event_request.mutable_sync_disabled();
+  sync_disabled_event->set_cache_guid(cache_guid);
+  sync_disabled_event->set_store_birthday(birthday);
+
+  std::string msg;
+  event_request.SerializeToString(&msg);
+
+  fetcher_.reset(net::URLFetcher::Create(
+      sync_event_url_, net::URLFetcher::POST, this));
+  fetcher_->AddExtraRequestHeader(base::StringPrintf(
+      "%s: Bearer %s", net::HttpRequestHeaders::kAuthorization,
+      access_token.c_str()));
+  fetcher_->SetRequestContext(request_context_.get());
+  fetcher_->SetUploadData("application/octet-stream", msg);
+  fetcher_->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES);
+  fetcher_->Start();
+  timer_.Start(FROM_HERE, kRequestTimeout, this,
+               &SyncStoppedReporter::OnTimeout);
+}
+
+void SyncStoppedReporter::OnURLFetchComplete(const net::URLFetcher* source) {
+  Result result = source->GetResponseCode() == net::HTTP_OK
+      ? RESULT_SUCCESS : RESULT_ERROR;
+  fetcher_.reset();
+  timer_.Stop();
+  if (!callback_.is_null()) {
+    base::MessageLoop::current()->PostTask(FROM_HERE,
+        base::Bind(callback_, result));
+  }
+}
+
+void SyncStoppedReporter::OnTimeout() {
+  fetcher_.reset();
+  if (!callback_.is_null()) {
+    base::MessageLoop::current()->PostTask(FROM_HERE,
+        base::Bind(callback_, RESULT_TIMEOUT));
+  }
+}
+
+// Static.
+GURL SyncStoppedReporter::GetSyncEventURL(const GURL& sync_service_url) {
+  std::string path = sync_service_url.path();
+  if (path.empty() || *path.rbegin() != '/') {
+    path += '/';
+  }
+  path += kEventEndpoint;
+  GURL::Replacements replacements;
+  replacements.SetPathStr(path);
+  return sync_service_url.ReplaceComponents(replacements);
+}
+
+void SyncStoppedReporter::SetTimerTaskRunnerForTest(
+    const scoped_refptr<base::SingleThreadTaskRunner>& task_runner) {
+  timer_.SetTaskRunner(task_runner);
+}
+
+}  // namespace browser_sync
diff --git a/chrome/browser/sync/sync_stopped_reporter.h b/chrome/browser/sync/sync_stopped_reporter.h
new file mode 100644
index 0000000..fb367161
--- /dev/null
+++ b/chrome/browser/sync/sync_stopped_reporter.h
@@ -0,0 +1,76 @@
+// 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 CHROME_BROWSER_SYNC_SYNC_STOPPED_REPORTER_H_
+#define CHROME_BROWSER_SYNC_SYNC_STOPPED_REPORTER_H_
+
+#include "base/callback.h"
+#include "base/timer/timer.h"
+#include "chrome/browser/profiles/profile.h"
+#include "net/url_request/url_fetcher.h"
+#include "net/url_request/url_fetcher_delegate.h"
+#include "net/url_request/url_request_context_getter.h"
+#include "url/gurl.h"
+
+namespace browser_sync {
+
+// Manages informing the sync server that sync has been disabled.
+// An implementation of URLFetcherDelegate was needed in order to
+// clean up the fetcher_ pointer when the request completes.
+class SyncStoppedReporter : public net::URLFetcherDelegate {
+ public:
+  enum Result {
+    RESULT_SUCCESS,
+    RESULT_ERROR,
+    RESULT_TIMEOUT
+  };
+
+  typedef base::Callback<void(const Result&)> ResultCallback;
+
+  SyncStoppedReporter(const GURL& sync_service_url,
+      const scoped_refptr<net::URLRequestContextGetter>& request_context,
+      const ResultCallback& callback);
+  ~SyncStoppedReporter() override;
+
+  // Inform the sync server that sync was stopped on this device.
+  // |access_token|, |cache_guid|, and |birthday| must not be empty.
+  void ReportSyncStopped(const std::string& access_token,
+                         const std::string& cache_guid,
+                         const std::string& birthday);
+
+  // net::URLFetcherDelegate implementation.
+  void OnURLFetchComplete(const net::URLFetcher* source) override;
+
+  // Override the timer's task runner so it can be triggered in tests.
+  void SetTimerTaskRunnerForTest(
+      const scoped_refptr<base::SingleThreadTaskRunner>& task_runner);
+
+ private:
+  // Convert the base sync URL into the sync event URL.
+  static GURL GetSyncEventURL(const GURL& sync_service_url);
+
+  // Callback for a request timing out.
+  void OnTimeout();
+
+  // Handles timing out requests.
+  base::OneShotTimer<SyncStoppedReporter> timer_;
+
+  // The URL for the sync server's event RPC.
+  const GURL sync_event_url_;
+
+  // Stored to simplify the API; needed for URLFetcher::Create().
+  scoped_refptr<net::URLRequestContextGetter> request_context_;
+
+  // The current URLFetcher. Null unless a request is in progress.
+  scoped_ptr<net::URLFetcher> fetcher_;
+
+  // A callback for request completion or timeout.
+  ResultCallback callback_;
+
+  DISALLOW_COPY_AND_ASSIGN(SyncStoppedReporter);
+};
+
+}  // namespace browser_sync
+
+#endif  // CHROME_BROWSER_SYNC_SYNC_STOPPED_REPORTER_H_
diff --git a/chrome/browser/sync/sync_stopped_reporter_unittest.cc b/chrome/browser/sync/sync_stopped_reporter_unittest.cc
new file mode 100644
index 0000000..b507a58
--- /dev/null
+++ b/chrome/browser/sync/sync_stopped_reporter_unittest.cc
@@ -0,0 +1,181 @@
+// 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 "chrome/browser/sync/sync_stopped_reporter.h"
+
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/test/test_simple_task_runner.h"
+#include "base/threading/non_thread_safe.h"
+#include "net/http/http_status_code.h"
+#include "net/url_request/test_url_fetcher_factory.h"
+#include "net/url_request/url_request_test_util.h"
+#include "sync/protocol/sync.pb.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+using browser_sync::SyncStoppedReporter;
+
+const char kTestURL[] = "http://chromium.org/test";
+const char kTestURLTrailingSlash[] = "http://chromium.org/test/";
+const char kEventURL[] = "http://chromium.org/test/event";
+
+const char kAuthToken[] = "multipass";
+const char kCacheGuid[] = "leeloo";
+const char kBirthday[] = "2263";
+
+class SyncStoppedReporterTest : public testing::Test {
+ public:
+  SyncStoppedReporterTest() {}
+  ~SyncStoppedReporterTest() override {}
+
+  void SetUp() override {
+    request_context_ = new net::TestURLRequestContextGetter(
+        message_loop_.task_runner());
+  }
+
+  void RequestFinishedCallback(const SyncStoppedReporter::Result& result) {
+    request_result_ = result;
+  }
+
+  GURL test_url() {
+    return GURL(kTestURL);
+  }
+
+  SyncStoppedReporter::ResultCallback callback() {
+    return base::Bind(&SyncStoppedReporterTest::RequestFinishedCallback,
+                      base::Unretained(this));
+  }
+
+  const SyncStoppedReporter::Result& request_result() const {
+    return request_result_;
+  }
+
+  net::URLRequestContextGetter* request_context() {
+    return request_context_.get();
+  }
+
+ private:
+  base::MessageLoop message_loop_;
+  scoped_refptr<net::URLRequestContextGetter> request_context_;
+  SyncStoppedReporter::Result request_result_;
+
+  DISALLOW_COPY_AND_ASSIGN(SyncStoppedReporterTest);
+};
+
+// Test that the event URL gets constructed correctly.
+TEST_F(SyncStoppedReporterTest, EventURL) {
+  net::TestURLFetcherFactory factory;
+  SyncStoppedReporter ssr(GURL(kTestURL), request_context(), callback());
+  ssr.ReportSyncStopped(kAuthToken, kCacheGuid, kBirthday);
+  net::TestURLFetcher* fetcher = factory.GetFetcherByID(0);
+  EXPECT_EQ(kEventURL, fetcher->GetOriginalURL().spec());
+}
+
+// Test that the event URL gets constructed correctly with a trailing slash.
+TEST_F(SyncStoppedReporterTest, EventURLWithSlash) {
+  net::TestURLFetcherFactory factory;
+  SyncStoppedReporter ssr(GURL(kTestURLTrailingSlash),
+                          request_context(), callback());
+  ssr.ReportSyncStopped(kAuthToken, kCacheGuid, kBirthday);
+  net::TestURLFetcher* fetcher = factory.GetFetcherByID(0);
+  EXPECT_EQ(kEventURL, fetcher->GetOriginalURL().spec());
+}
+
+// Test that the URLFetcher gets configured correctly.
+TEST_F(SyncStoppedReporterTest, FetcherConfiguration) {
+  net::TestURLFetcherFactory factory;
+  SyncStoppedReporter ssr(test_url(), request_context(), callback());
+  ssr.ReportSyncStopped(kAuthToken, kCacheGuid, kBirthday);
+  net::TestURLFetcher* fetcher = factory.GetFetcherByID(0);
+
+  sync_pb::EventRequest event_request;
+  event_request.ParseFromString(fetcher->upload_data());
+
+  EXPECT_EQ(kCacheGuid, event_request.sync_disabled().cache_guid());
+  EXPECT_EQ(kBirthday, event_request.sync_disabled().store_birthday());
+  EXPECT_EQ(kEventURL, fetcher->GetOriginalURL().spec());
+}
+
+TEST_F(SyncStoppedReporterTest, HappyCase) {
+  net::TestURLFetcherFactory factory;
+  SyncStoppedReporter ssr(test_url(), request_context(), callback());
+  ssr.ReportSyncStopped(kAuthToken, kCacheGuid, kBirthday);
+  net::TestURLFetcher* fetcher = factory.GetFetcherByID(0);
+  fetcher->set_response_code(net::HTTP_OK);
+  ssr.OnURLFetchComplete(fetcher);
+  base::RunLoop run_loop;
+  run_loop.RunUntilIdle();
+  EXPECT_EQ(SyncStoppedReporter::RESULT_SUCCESS, request_result());
+}
+
+TEST_F(SyncStoppedReporterTest, ServerNotFound) {
+  net::TestURLFetcherFactory factory;
+  SyncStoppedReporter ssr(test_url(), request_context(), callback());
+  ssr.ReportSyncStopped(kAuthToken, kCacheGuid, kBirthday);
+  net::TestURLFetcher* fetcher = factory.GetFetcherByID(0);
+  fetcher->set_response_code(net::HTTP_NOT_FOUND);
+  ssr.OnURLFetchComplete(fetcher);
+  base::RunLoop run_loop;
+  run_loop.RunUntilIdle();
+  EXPECT_EQ(SyncStoppedReporter::RESULT_ERROR, request_result());
+}
+
+TEST_F(SyncStoppedReporterTest, DestructionDuringRequestHandler) {
+  net::TestURLFetcherFactory factory;
+  factory.set_remove_fetcher_on_delete(true);
+  {
+    SyncStoppedReporter ssr(test_url(), request_context(), callback());
+    ssr.ReportSyncStopped(kAuthToken, kCacheGuid, kBirthday);
+    EXPECT_FALSE(factory.GetFetcherByID(0) == nullptr);
+  }
+  EXPECT_TRUE(factory.GetFetcherByID(0) == nullptr);
+}
+
+TEST_F(SyncStoppedReporterTest, Timeout) {
+  SyncStoppedReporter ssr(test_url(), request_context(), callback());
+
+  // A task runner that can trigger the timeout immediately.
+  scoped_refptr<base::TestSimpleTaskRunner> task_runner(
+      new base::TestSimpleTaskRunner());
+  ssr.SetTimerTaskRunnerForTest(task_runner);
+
+  // Begin request.
+  ssr.ReportSyncStopped(kAuthToken, kCacheGuid, kBirthday);
+
+  // Trigger the timeout.
+  ASSERT_TRUE(task_runner->HasPendingTask());
+  task_runner->RunPendingTasks();
+
+  base::RunLoop run_loop;
+  run_loop.RunUntilIdle();
+  EXPECT_EQ(SyncStoppedReporter::RESULT_TIMEOUT, request_result());
+}
+
+TEST_F(SyncStoppedReporterTest, NoCallback) {
+  net::TestURLFetcherFactory factory;
+  SyncStoppedReporter ssr(GURL(kTestURL), request_context(),
+                          SyncStoppedReporter::ResultCallback());
+  ssr.ReportSyncStopped(kAuthToken, kCacheGuid, kBirthday);
+  net::TestURLFetcher* fetcher = factory.GetFetcherByID(0);
+  fetcher->set_response_code(net::HTTP_OK);
+  ssr.OnURLFetchComplete(fetcher);
+}
+
+TEST_F(SyncStoppedReporterTest, NoCallbackTimeout) {
+  SyncStoppedReporter ssr(GURL(kTestURL), request_context(),
+                          SyncStoppedReporter::ResultCallback());
+
+  // A task runner that can trigger the timeout immediately.
+  scoped_refptr<base::TestSimpleTaskRunner> task_runner(
+      new base::TestSimpleTaskRunner());
+  ssr.SetTimerTaskRunnerForTest(task_runner);
+
+  // Begin request.
+  ssr.ReportSyncStopped(kAuthToken, kCacheGuid, kBirthday);
+
+  // Trigger the timeout.
+  ASSERT_TRUE(task_runner->HasPendingTask());
+  task_runner->RunPendingTasks();
+}
diff --git a/chrome/browser/sync/test/integration/single_client_sessions_sync_test.cc b/chrome/browser/sync/test/integration/single_client_sessions_sync_test.cc
index 5b553b0..0d6c1da3 100644
--- a/chrome/browser/sync/test/integration/single_client_sessions_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_sessions_sync_test.cc
@@ -69,8 +69,7 @@
   ASSERT_TRUE(CheckInitialState(0));
 
   // We want a URL that doesn't 404 and has a non-empty title.
-  // about:version is simple to render, too.
-  const GURL url("about:version");
+  const GURL url("data:text/html,<html><title>Test</title></html>");
 
   ScopedWindowMap windows;
   ASSERT_TRUE(OpenTabAndGetLocalWindows(0, url, windows.GetMutable()));
@@ -103,8 +102,7 @@
   ASSERT_TRUE(CheckInitialState(0));
 
   // We want a URL that doesn't 404 and has a non-empty title.
-  // about:version is simple to render, too.
-  const GURL url("about:version");
+  const GURL url("data:text/html,<html><title>Test</title></html>");
 
   ScopedWindowMap windows;
   ASSERT_TRUE(OpenTabAndGetLocalWindows(0, url, windows.GetMutable()));
diff --git a/chrome/browser/sync_file_system/drive_backend/drive_service_on_worker.cc b/chrome/browser/sync_file_system/drive_backend/drive_service_on_worker.cc
index 3939898..33694dd 100644
--- a/chrome/browser/sync_file_system/drive_backend/drive_service_on_worker.cc
+++ b/chrome/browser/sync_file_system/drive_backend/drive_service_on_worker.cc
@@ -319,6 +319,7 @@
     const std::string& new_title,
     const base::Time& last_modified,
     const base::Time& last_viewed_by_me,
+    const google_apis::drive::Properties& properties,
     const google_apis::FileResourceCallback& callback) {
   NOTREACHED();
   return google_apis::CancelCallback();
diff --git a/chrome/browser/sync_file_system/drive_backend/drive_service_on_worker.h b/chrome/browser/sync_file_system/drive_backend/drive_service_on_worker.h
index 7567d739..0aa5ff4 100644
--- a/chrome/browser/sync_file_system/drive_backend/drive_service_on_worker.h
+++ b/chrome/browser/sync_file_system/drive_backend/drive_service_on_worker.h
@@ -123,6 +123,7 @@
       const std::string& new_title,
       const base::Time& last_modified,
       const base::Time& last_viewed_by_me,
+      const google_apis::drive::Properties& properties,
       const google_apis::FileResourceCallback& callback) override;
   google_apis::CancelCallback AddResourceToDirectory(
       const std::string& parent_resource_id,
diff --git a/chrome/browser/sync_file_system/drive_backend/fake_drive_service_helper.cc b/chrome/browser/sync_file_system/drive_backend/fake_drive_service_helper.cc
index 20b890b..9e1b1b0 100644
--- a/chrome/browser/sync_file_system/drive_backend/fake_drive_service_helper.cc
+++ b/chrome/browser/sync_file_system/drive_backend/fake_drive_service_helper.cc
@@ -165,8 +165,8 @@
     return error;
 
   fake_drive_service_->UpdateResource(
-      file_id, std::string(), entry->title(),
-      modification_time, entry->last_viewed_by_me_date(),
+      file_id, std::string(), entry->title(), modification_time,
+      entry->last_viewed_by_me_date(), google_apis::drive::Properties(),
       CreateResultReceiver(&error, &entry));
   base::RunLoop().RunUntilIdle();
   return error;
@@ -179,7 +179,7 @@
   scoped_ptr<FileResource> entry;
   fake_drive_service_->UpdateResource(
       file_id, std::string(), new_title, base::Time(), base::Time(),
-      CreateResultReceiver(&error, &entry));
+      google_apis::drive::Properties(), CreateResultReceiver(&error, &entry));
   base::RunLoop().RunUntilIdle();
   return error;
 }
diff --git a/chrome/browser/translate/translate_manager_render_view_host_unittest.cc b/chrome/browser/translate/translate_manager_render_view_host_unittest.cc
index a9d805b5..7772956b 100644
--- a/chrome/browser/translate/translate_manager_render_view_host_unittest.cc
+++ b/chrome/browser/translate/translate_manager_render_view_host_unittest.cc
@@ -894,12 +894,12 @@
 
   // Simulate a sub-frame auto-navigating.
   subframe_tester->SendNavigateWithTransition(
-      1, GURL("http://pub.com"), ui::PAGE_TRANSITION_AUTO_SUBFRAME);
+      0, GURL("http://pub.com"), ui::PAGE_TRANSITION_AUTO_SUBFRAME);
   EXPECT_TRUE(GetTranslateInfoBar() == NULL);
 
   // Simulate the user navigating in a sub-frame.
   subframe_tester->SendNavigateWithTransition(
-      2, GURL("http://pub.com"), ui::PAGE_TRANSITION_MANUAL_SUBFRAME);
+      1, GURL("http://pub.com"), ui::PAGE_TRANSITION_MANUAL_SUBFRAME);
   EXPECT_TRUE(GetTranslateInfoBar() == NULL);
 
   // Navigate out of page, a new infobar should show.
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 47f8af4..57ac9b0 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -61,7 +61,6 @@
     "//skia",
     "//third_party/cacheinvalidation",
     "//third_party/icu",
-    "//third_party/libusb",
     "//third_party/libxml",
     "//third_party/zlib",
     "//ui/accessibility",
@@ -135,8 +134,9 @@
                     ".",
                     "//chrome")
     deps += [
-      "//device/bluetooth",
       "//components/copresence",
+      "//device/bluetooth",
+      "//third_party/libusb",
     ]
   }
 
@@ -317,7 +317,6 @@
     deps -= [
       "//chrome/browser/ui/views",
       "//components/feedback/proto",
-      "//third_party/libusb",
       "//ui/events",
     ]
     sources += rebase_path(gypi_values.chrome_browser_ui_android_sources,
diff --git a/chrome/browser/ui/android/infobars/account_chooser_infobar.cc b/chrome/browser/ui/android/infobars/account_chooser_infobar.cc
new file mode 100644
index 0000000..00fa5fd
--- /dev/null
+++ b/chrome/browser/ui/android/infobars/account_chooser_infobar.cc
@@ -0,0 +1,60 @@
+// 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 "chrome/browser/ui/android/infobars/account_chooser_infobar.h"
+
+#include "base/android/jni_android.h"
+#include "base/android/jni_array.h"
+#include "chrome/browser/infobars/infobar_service.h"
+#include "chrome/browser/password_manager/account_chooser_infobar_delegate_android.h"
+#include "components/password_manager/content/common/credential_manager_types.h"
+#include "jni/AccountChooserInfoBar_jni.h"
+
+AccountChooserInfoBar::AccountChooserInfoBar(
+    scoped_ptr<AccountChooserInfoBarDelegateAndroid> delegate)
+    : InfoBarAndroid(delegate.Pass()) {
+}
+
+AccountChooserInfoBar::~AccountChooserInfoBar() {
+}
+
+base::android::ScopedJavaLocalRef<jobject>
+AccountChooserInfoBar::CreateRenderInfoBar(JNIEnv* env) {
+  std::vector<base::string16> usernames;
+  // TODO(melandory): Federated credentials should be processed also.
+  for (auto password_form : GetDelegate()->local_credentials_forms())
+    usernames.push_back(password_form->username_value);
+  base::android::ScopedJavaLocalRef<jobjectArray> java_usernames =
+      base::android::ToJavaArrayOfStrings(env, usernames);
+  return Java_AccountChooserInfoBar_show(env, reinterpret_cast<intptr_t>(this),
+                                         GetEnumeratedIconId(),
+                                         java_usernames.obj());
+}
+
+void AccountChooserInfoBar::OnCredentialClicked(JNIEnv* env,
+                                                jobject obj,
+                                                jint credential_item,
+                                                jint credential_type) {
+  GetDelegate()->ChooseCredential(
+      credential_item,
+      static_cast<password_manager::CredentialType>(credential_type));
+  RemoveSelf();
+}
+
+void AccountChooserInfoBar::ProcessButton(int action,
+                                          const std::string& action_value) {
+  if (!owner())
+    return;  // We're closing; don't call anything, it might access the owner.
+  GetDelegate()->ChooseCredential(
+      -1, password_manager::CredentialType::CREDENTIAL_TYPE_EMPTY);
+  RemoveSelf();
+}
+
+AccountChooserInfoBarDelegateAndroid* AccountChooserInfoBar::GetDelegate() {
+  return static_cast<AccountChooserInfoBarDelegateAndroid*>(delegate());
+}
+
+bool RegisterAccountChooserInfoBar(JNIEnv* env) {
+  return RegisterNativesImpl(env);
+}
diff --git a/chrome/browser/ui/android/infobars/account_chooser_infobar.h b/chrome/browser/ui/android/infobars/account_chooser_infobar.h
new file mode 100644
index 0000000..07b5abb3c4
--- /dev/null
+++ b/chrome/browser/ui/android/infobars/account_chooser_infobar.h
@@ -0,0 +1,43 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_ANDROID_INFOBARS_ACCOUNT_CHOOSER_INFOBAR_H_
+#define CHROME_BROWSER_UI_ANDROID_INFOBARS_ACCOUNT_CHOOSER_INFOBAR_H_
+
+#include <jni.h>
+
+#include <string>
+
+#include "base/android/scoped_java_ref.h"
+#include "base/macros.h"
+#include "chrome/browser/ui/android/infobars/infobar_android.h"
+
+class AccountChooserInfoBarDelegateAndroid;
+
+// The Android infobar that allows user to choose credentials for login.
+class AccountChooserInfoBar : public InfoBarAndroid {
+ public:
+  explicit AccountChooserInfoBar(
+      scoped_ptr<AccountChooserInfoBarDelegateAndroid> delegate);
+  ~AccountChooserInfoBar() override;
+
+  void OnCredentialClicked(JNIEnv* env,
+                           jobject obj,
+                           jint credential_item,
+                           jint credential_type);
+
+ private:
+  // InfoBarAndroid:
+  base::android::ScopedJavaLocalRef<jobject> CreateRenderInfoBar(
+      JNIEnv* env) override;
+  void ProcessButton(int action, const std::string& action_value) override;
+  AccountChooserInfoBarDelegateAndroid* GetDelegate();
+
+  DISALLOW_COPY_AND_ASSIGN(AccountChooserInfoBar);
+};
+
+// Registers native methods.
+bool RegisterAccountChooserInfoBar(JNIEnv* env);
+
+#endif  // CHROME_BROWSER_UI_ANDROID_INFOBARS_ACCOUNT_CHOOSER_INFOBAR_H_
diff --git a/chrome/browser/ui/app_list/app_list_service_impl_browsertest.cc b/chrome/browser/ui/app_list/app_list_service_impl_browsertest.cc
index 7d282a4..6501ba48 100644
--- a/chrome/browser/ui/app_list/app_list_service_impl_browsertest.cc
+++ b/chrome/browser/ui/app_list/app_list_service_impl_browsertest.cc
@@ -60,7 +60,9 @@
 // Test that showing a loaded profile for the first time is lazy and
 // synchronous. Then tests that showing a second loaded profile without
 // dismissing correctly switches profiles.
-IN_PROC_BROWSER_TEST_F(AppListServiceImplBrowserTest, ShowLoadedProfiles) {
+// crbug.com/459649
+IN_PROC_BROWSER_TEST_F(AppListServiceImplBrowserTest,
+                       DISABLED_ShowLoadedProfiles) {
   PrefService* local_state = g_browser_process->local_state();
   EXPECT_FALSE(local_state->HasPrefPath(prefs::kAppListProfile));
 
@@ -158,6 +160,8 @@
 // Test that all the items in the context menu for a hosted app have valid
 // labels.
 IN_PROC_BROWSER_TEST_F(AppListServiceImplBrowserTest, ShowContextMenu) {
+  base::CommandLine::ForCurrentProcess()->AppendSwitch(
+      switches::kEnableNewBookmarkApps);
   AppListService* service = test::GetAppListService();
   EXPECT_TRUE(service);
 
diff --git a/chrome/browser/ui/app_list/app_list_service_interactive_uitest.cc b/chrome/browser/ui/app_list/app_list_service_interactive_uitest.cc
index 24624f34..9756c3a 100644
--- a/chrome/browser/ui/app_list/app_list_service_interactive_uitest.cc
+++ b/chrome/browser/ui/app_list/app_list_service_interactive_uitest.cc
@@ -84,7 +84,7 @@
 
 // Switch profiles on the app list while it is showing.
 IN_PROC_BROWSER_TEST_F(AppListServiceInteractiveTest,
-                       MAYBE_SwitchAppListProfiles) {
+                       DISABLED_SwitchAppListProfiles) {
   InitSecondProfile();
 
   AppListService* service = test::GetAppListService();
@@ -175,7 +175,7 @@
 
 // Test switching app list profiles while search results are visibile.
 IN_PROC_BROWSER_TEST_F(AppListServiceInteractiveTest,
-                       MAYBE_SwitchAppListProfilesDuringSearch) {
+                       DISABLED_SwitchAppListProfilesDuringSearch) {
   InitSecondProfile();
 
   AppListService* service = test::GetAppListService();
diff --git a/chrome/browser/ui/app_list/app_list_view_delegate.cc b/chrome/browser/ui/app_list/app_list_view_delegate.cc
index b95f5e7..c403376 100644
--- a/chrome/browser/ui/app_list/app_list_view_delegate.cc
+++ b/chrome/browser/ui/app_list/app_list_view_delegate.cc
@@ -20,6 +20,7 @@
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/search/hotword_service.h"
 #include "chrome/browser/search/hotword_service_factory.h"
+#include "chrome/browser/search_engines/template_url_service_factory.h"
 #include "chrome/browser/ui/app_list/app_list_controller_delegate.h"
 #include "chrome/browser/ui/app_list/app_list_service.h"
 #include "chrome/browser/ui/app_list/app_list_syncable_service.h"
@@ -37,6 +38,7 @@
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/extensions/extension_constants.h"
 #include "chrome/common/url_constants.h"
+#include "components/search_engines/template_url_prepopulate_data.h"
 #include "components/signin/core/browser/signin_manager.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/notification_service.h"
@@ -158,6 +160,7 @@
       profile_(NULL),
       model_(NULL),
       is_voice_query_(false),
+      template_url_service_observer_(this),
       scoped_observer_(this) {
   // TODO(vadimt): Remove ScopedTracker below once crbug.com/431326 is fixed.
   tracked_objects::ScopedTracker tracking_profile(
@@ -286,6 +289,12 @@
   tracked_objects::ScopedTracker tracking_profile3(
       FROM_HERE_WITH_EXPLICIT_FUNCTION(
           "431326 AppListViewDelegate::SetProfile3"));
+  template_url_service_observer_.RemoveAll();
+  if (app_list::switches::IsExperimentalAppListEnabled()) {
+    TemplateURLService* template_url_service =
+        TemplateURLServiceFactory::GetForProfile(profile_);
+    template_url_service_observer_.Add(template_url_service);
+  }
 
   model_ =
       app_list::AppListSyncableServiceFactory::GetForProfile(profile_)->model();
@@ -297,6 +306,7 @@
   SetUpSearchUI();
   SetUpProfileSwitcher();
   SetUpCustomLauncherPages();
+  OnTemplateURLServiceChanged();
 
   // TODO(vadimt): Remove ScopedTracker below once crbug.com/431326 is fixed.
   tracked_objects::ScopedTracker tracking_profile4(
@@ -322,8 +332,7 @@
       model_->search_box(),
       speech_ui_.get()));
 
-  search_controller_ = CreateSearchController(
-      profile_, model_->search_box(), model_->results(), controller_);
+  search_controller_ = CreateSearchController(profile_, model_, controller_);
 }
 
 void AppListViewDelegate::SetUpProfileSwitcher() {
@@ -366,10 +375,16 @@
     custom_page_contents_.push_back(page_contents);
   }
 
+  std::string first_launcher_page_app_id = custom_launcher_page_urls[0].host();
+  const extensions::Extension* extension =
+      extensions::ExtensionRegistry::Get(profile_)
+          ->GetExtensionById(first_launcher_page_app_id,
+                             extensions::ExtensionRegistry::EVERYTHING);
+  model_->set_custom_launcher_page_name(extension->name());
   // Only the first custom launcher page gets events dispatched to it.
   launcher_page_event_dispatcher_.reset(
-      new app_list::LauncherPageEventDispatcher(
-          profile_, custom_launcher_page_urls[0].host()));
+      new app_list::LauncherPageEventDispatcher(profile_,
+                                                first_launcher_page_app_id));
 }
 
 void AppListViewDelegate::OnHotwordStateChanged(bool started) {
@@ -496,6 +511,7 @@
   if (auto_launch)
     base::RecordAction(base::UserMetricsAction("AppList_AutoLaunched"));
   search_controller_->OpenResult(result, event_flags);
+  is_voice_query_ = false;
 }
 
 void AppListViewDelegate::InvokeSearchResultAction(
@@ -510,10 +526,12 @@
 }
 
 void AppListViewDelegate::AutoLaunchCanceled() {
-  base::RecordAction(base::UserMetricsAction("AppList_AutoLaunchCanceled"));
+  if (is_voice_query_) {
+    base::RecordAction(base::UserMetricsAction("AppList_AutoLaunchCanceled"));
+    // Cancelling the auto launch means we are no longer in a voice query.
+    is_voice_query_ = false;
+  }
   auto_launch_timeout_ = base::TimeDelta();
-  // Cancelling the auto launch means we are no longer in a voice query.
-  is_voice_query_ = false;
 }
 
 void AppListViewDelegate::ViewInitialized() {
@@ -681,6 +699,8 @@
   if (!service)
     return NULL;
 
+  service->LoadContentsIfNeeded();
+
   content::WebContents* web_contents = service->GetStartPageContents();
   if (!web_contents)
     return NULL;
@@ -773,6 +793,27 @@
   observers_.RemoveObserver(observer);
 }
 
+void AppListViewDelegate::OnTemplateURLServiceChanged() {
+  if (!app_list::switches::IsExperimentalAppListEnabled())
+    return;
+
+  TemplateURLService* template_url_service =
+      TemplateURLServiceFactory::GetForProfile(profile_);
+  const TemplateURL* default_provider =
+      template_url_service->GetDefaultSearchProvider();
+  bool is_google =
+      TemplateURLPrepopulateData::GetEngineType(
+          *default_provider, template_url_service->search_terms_data()) ==
+      SEARCH_ENGINE_GOOGLE;
+
+  model_->SetSearchEngineIsGoogle(is_google);
+
+  app_list::StartPageService* start_page_service =
+      app_list::StartPageService::Get(profile_);
+  if (start_page_service)
+    start_page_service->set_search_engine_is_google(is_google);
+}
+
 void AppListViewDelegate::Observe(int type,
                                   const content::NotificationSource& source,
                                   const content::NotificationDetails& details) {
diff --git a/chrome/browser/ui/app_list/app_list_view_delegate.h b/chrome/browser/ui/app_list/app_list_view_delegate.h
index ecd7ed2a..d351d854 100644
--- a/chrome/browser/ui/app_list/app_list_view_delegate.h
+++ b/chrome/browser/ui/app_list/app_list_view_delegate.h
@@ -19,6 +19,8 @@
 #include "chrome/browser/search/hotword_client.h"
 #include "chrome/browser/signin/signin_manager_factory.h"
 #include "chrome/browser/ui/app_list/start_page_observer.h"
+#include "components/search_engines/template_url_service.h"
+#include "components/search_engines/template_url_service_observer.h"
 #include "components/signin/core/browser/signin_manager_base.h"
 #include "content/public/browser/notification_observer.h"
 #include "content/public/browser/notification_registrar.h"
@@ -60,7 +62,8 @@
                             public ProfileInfoCacheObserver,
                             public SigninManagerBase::Observer,
                             public SigninManagerFactory::Observer,
-                            public content::NotificationObserver {
+                            public content::NotificationObserver,
+                            public TemplateURLServiceObserver {
  public:
   // Constructs Chrome's AppListViewDelegate with a NULL Profile.
   // Does not take ownership of |controller|. TODO(tapted): It should.
@@ -116,6 +119,9 @@
   void AddObserver(app_list::AppListViewDelegateObserver* observer) override;
   void RemoveObserver(app_list::AppListViewDelegateObserver* observer) override;
 
+  // Overridden from TemplateURLServiceObserver:
+  void OnTemplateURLServiceChanged() override;
+
  private:
   // Updates the speech webview and start page for the current |profile_|.
   void SetUpSearchUI();
@@ -192,6 +198,9 @@
 
   ObserverList<app_list::AppListViewDelegateObserver> observers_;
 
+  ScopedObserver<TemplateURLService, AppListViewDelegate>
+      template_url_service_observer_;
+
   // Used to track the SigninManagers that this instance is observing so that
   // this instance can be removed as an observer on its destruction.
   ScopedObserver<SigninManagerBase, AppListViewDelegate> scoped_observer_;
diff --git a/chrome/browser/ui/app_list/google_now_extension.cc b/chrome/browser/ui/app_list/google_now_extension.cc
index 303eb30..d66fcfa 100644
--- a/chrome/browser/ui/app_list/google_now_extension.cc
+++ b/chrome/browser/ui/app_list/google_now_extension.cc
@@ -4,10 +4,18 @@
 
 #include "chrome/browser/ui/app_list/google_now_extension.h"
 
+#include "base/prefs/pref_service.h"
+#include "chrome/browser/notifications/desktop_notification_service.h"
+#include "chrome/browser/notifications/desktop_notification_service_factory.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/common/pref_names.h"
 #include "components/variations/variations_associated_data.h"
 
 namespace {
 const char kGoogleNowExtensionFieldTrialName[] = "GoogleNowExtension";
+
+// Extension ID for previous Now notifications component.
+const char kNowNotifierId[] = "pafkbggdmjlpgkdkcbjmhmfcdpncadgh";
 };  // namespace
 
 bool GetGoogleNowExtensionId(std::string* extension_id) {
@@ -15,3 +23,26 @@
       kGoogleNowExtensionFieldTrialName, "id");
   return !extension_id->empty();
 }
+
+// TODO(skare): Remove this if/when the Now Notifications component is
+// deprecated. http://crbug.com/459846
+void MigrateGoogleNowPrefs(Profile* profile) {
+  std::string now_extension_id;
+  if (!GetGoogleNowExtensionId(&now_extension_id))
+    return;
+
+  PrefService* prefs = profile->GetPrefs();
+  const PrefService::Preference* enabled_pref =
+      prefs->FindPreference(prefs::kGoogleNowLauncherEnabled);
+
+  // If the pref is not its default value, migration was performed.
+  if (!enabled_pref->IsDefaultValue())
+    return;
+
+  DesktopNotificationService* const notification_service =
+      DesktopNotificationServiceFactory::GetForProfile(profile);
+  bool notifier_enabled = notification_service->IsNotifierEnabled(
+      message_center::NotifierId(
+          message_center::NotifierId::APPLICATION, kNowNotifierId));
+  prefs->SetBoolean(prefs::kGoogleNowLauncherEnabled, notifier_enabled);
+}
diff --git a/chrome/browser/ui/app_list/google_now_extension.h b/chrome/browser/ui/app_list/google_now_extension.h
index 9b2732b7..fca73420 100644
--- a/chrome/browser/ui/app_list/google_now_extension.h
+++ b/chrome/browser/ui/app_list/google_now_extension.h
@@ -7,8 +7,14 @@
 
 #include <string>
 
+class Profile;
+
 // Returns true and sets |extension_id| if extension experiment enabled
 //         false if no experiment or |extension_id| is empty.
 bool GetGoogleNowExtensionId(std::string* extension_id);
 
+// Migrates settings from the Now Notifications extension to the Now Launcher
+// Page extension.
+void MigrateGoogleNowPrefs(Profile* profile);
+
 #endif  // CHROME_BROWSER_UI_APP_LIST_GOOGLE_NOW_EXTENSION_H_
diff --git a/chrome/browser/ui/app_list/search/app_search_provider.cc b/chrome/browser/ui/app_list/search/app_search_provider.cc
index a45e4d9c..b81afa6 100644
--- a/chrome/browser/ui/app_list/search/app_search_provider.cc
+++ b/chrome/browser/ui/app_list/search/app_search_provider.cc
@@ -14,17 +14,27 @@
 #include "chrome/browser/extensions/extension_ui_util.h"
 #include "chrome/browser/extensions/extension_util.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/app_list/app_list_syncable_service.h"
+#include "chrome/browser/ui/app_list/app_list_syncable_service_factory.h"
 #include "chrome/browser/ui/app_list/search/app_result.h"
 #include "extensions/browser/extension_prefs.h"
 #include "extensions/browser/extension_registry.h"
 #include "extensions/browser/extension_system.h"
 #include "extensions/common/extension.h"
 #include "extensions/common/extension_set.h"
+#include "ui/app_list/app_list_item.h"
+#include "ui/app_list/app_list_model.h"
 #include "ui/app_list/search/tokenized_string.h"
 #include "ui/app_list/search/tokenized_string_match.h"
 
 using extensions::ExtensionRegistry;
 
+namespace {
+
+// The size of each step unlaunched apps should increase their relevance by.
+const double kUnlaunchedAppRelevanceStepSize = 0.0001;
+}
+
 namespace app_list {
 
 class AppSearchProvider::App {
@@ -32,7 +42,7 @@
   explicit App(const extensions::Extension* extension,
                const base::Time& last_launch_time)
       : app_id_(extension->id()),
-        indexed_name_(base::UTF8ToUTF16(extension->name())),
+        indexed_name_(base::UTF8ToUTF16(extension->short_name())),
         last_launch_time_(last_launch_time) {}
   ~App() {}
 
@@ -50,10 +60,12 @@
 
 AppSearchProvider::AppSearchProvider(Profile* profile,
                                      AppListControllerDelegate* list_controller,
-                                     scoped_ptr<base::Clock> clock)
+                                     scoped_ptr<base::Clock> clock,
+                                     AppListItemList* top_level_item_list)
     : profile_(profile),
       list_controller_(list_controller),
       extension_registry_observer_(this),
+      top_level_item_list_(top_level_item_list),
       clock_(clock.Pass()),
       update_results_factory_(this) {
   extension_registry_observer_.Add(ExtensionRegistry::Get(profile_));
@@ -86,23 +98,39 @@
   bool show_recommendations = query_.empty();
   ClearResults();
 
-  for (Apps::const_iterator app_it = apps_.begin();
-       app_it != apps_.end();
-       ++app_it) {
-    scoped_ptr<AppResult> result(new AppResult(
-        profile_, (*app_it)->app_id(), list_controller_, show_recommendations));
-    if (show_recommendations) {
-      result->set_title((*app_it)->indexed_name().text());
-      result->UpdateFromLastLaunched(clock_->Now(),
-                                     (*app_it)->last_launch_time());
-    } else {
+  if (show_recommendations) {
+    // Build a map of app ids to their position in the app list.
+    std::map<std::string, size_t> id_to_app_list_index;
+    for (size_t i = 0; i < top_level_item_list_->item_count(); ++i) {
+      id_to_app_list_index[top_level_item_list_->item_at(i)->id()] = i;
+    }
+
+    for (const App* app : apps_) {
+      scoped_ptr<AppResult> result(
+          new AppResult(profile_, app->app_id(), list_controller_, true));
+      result->set_title(app->indexed_name().text());
+
+      // Use the app list order to tiebreak apps that have never been launched.
+      if (app->last_launch_time().is_null()) {
+        result->set_relevance(
+            kUnlaunchedAppRelevanceStepSize *
+            (apps_.size() - id_to_app_list_index[app->app_id()]));
+      } else {
+        result->UpdateFromLastLaunched(clock_->Now(), app->last_launch_time());
+      }
+      Add(result.Pass());
+    }
+  } else {
+    for (const App* app : apps_) {
+      scoped_ptr<AppResult> result(
+          new AppResult(profile_, app->app_id(), list_controller_, false));
       TokenizedStringMatch match;
-      if (!match.Calculate(query_terms, (*app_it)->indexed_name()))
+      if (!match.Calculate(query_terms, app->indexed_name()))
         continue;
 
-      result->UpdateFromMatch((*app_it)->indexed_name(), match);
+      result->UpdateFromMatch(app->indexed_name(), match);
+      Add(result.Pass());
     }
-    Add(result.Pass());
   }
 
   update_results_factory_.InvalidateWeakPtrs();
diff --git a/chrome/browser/ui/app_list/search/app_search_provider.h b/chrome/browser/ui/app_list/search/app_search_provider.h
index d81a760..d2ce5fd 100644
--- a/chrome/browser/ui/app_list/search/app_search_provider.h
+++ b/chrome/browser/ui/app_list/search/app_search_provider.h
@@ -26,6 +26,8 @@
 
 namespace app_list {
 
+class AppListItemList;
+
 namespace test {
 class AppSearchProviderTest;
 }
@@ -35,7 +37,8 @@
  public:
   AppSearchProvider(Profile* profile,
                     AppListControllerDelegate* list_controller,
-                    scoped_ptr<base::Clock> clock);
+                    scoped_ptr<base::Clock> clock,
+                    AppListItemList* top_level_item_list);
   ~AppSearchProvider() override;
 
   // SearchProvider overrides:
@@ -70,6 +73,8 @@
 
   Apps apps_;
 
+  AppListItemList* top_level_item_list_;
+
   scoped_ptr<base::Clock> clock_;
   base::WeakPtrFactory<AppSearchProvider> update_results_factory_;
 
diff --git a/chrome/browser/ui/app_list/search/app_search_provider_unittest.cc b/chrome/browser/ui/app_list/search/app_search_provider_unittest.cc
index b59c139..8fb6188a 100644
--- a/chrome/browser/ui/app_list/search/app_search_provider_unittest.cc
+++ b/chrome/browser/ui/app_list/search/app_search_provider_unittest.cc
@@ -11,19 +11,24 @@
 #include "base/test/simple_test_clock.h"
 #include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/ui/app_list/app_list_test_util.h"
+#include "chrome/browser/ui/app_list/extension_app_model_builder.h"
 #include "chrome/browser/ui/app_list/search/app_search_provider.h"
+#include "chrome/browser/ui/app_list/test/test_app_list_controller_delegate.h"
 #include "chrome/common/chrome_constants.h"
 #include "chrome/test/base/testing_profile.h"
 #include "extensions/browser/extension_prefs.h"
 #include "extensions/browser/uninstall_reason.h"
 #include "extensions/common/extension_set.h"
+#include "sync/api/string_ordinal.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "ui/app_list/app_list_item.h"
+#include "ui/app_list/app_list_model.h"
 #include "ui/app_list/search_result.h"
 
 namespace app_list {
 namespace test {
 
-const base::Time kTestCurrentTime = base::Time::FromInternalValue(1000);
+const base::Time kTestCurrentTime = base::Time::FromInternalValue(100000);
 
 bool MoreRelevant(const SearchResult* result1, const SearchResult* result2) {
   return result1->relevance() > result2->relevance();
@@ -40,8 +45,12 @@
 
     scoped_ptr<base::SimpleTestClock> clock(new base::SimpleTestClock());
     clock->SetNow(kTestCurrentTime);
-    app_search_.reset(
-        new AppSearchProvider(profile_.get(), NULL, clock.Pass()));
+    model_.reset(new app_list::AppListModel);
+    controller_.reset(new ::test::TestAppListControllerDelegate);
+    builder_.reset(new ExtensionAppModelBuilder(controller_.get()));
+    builder_->InitializeWithProfile(profile_.get(), model_.get());
+    app_search_.reset(new AppSearchProvider(
+        profile_.get(), nullptr, clock.Pass(), model_->top_level_item_list()));
   }
 
   std::string RunQuery(const std::string& query) {
@@ -64,10 +73,14 @@
     return result_str;
   }
 
+  AppListModel* model() { return model_.get(); }
   const SearchProvider::Results& results() { return app_search_->results(); }
 
  private:
+  scoped_ptr<app_list::AppListModel> model_;
   scoped_ptr<AppSearchProvider> app_search_;
+  scoped_ptr<ExtensionAppModelBuilder> builder_;
+  scoped_ptr<::test::TestAppListControllerDelegate> controller_;
 
   DISALLOW_COPY_AND_ASSIGN(AppSearchProviderTest);
 };
@@ -128,10 +141,10 @@
 
   prefs->SetLastLaunchTime(kHostedAppId, base::Time::FromInternalValue(20));
   prefs->SetLastLaunchTime(kPackagedApp1Id, base::Time::FromInternalValue(10));
-  prefs->SetLastLaunchTime(kPackagedApp2Id, base::Time::FromInternalValue(0));
+  prefs->SetLastLaunchTime(kPackagedApp2Id, base::Time::FromInternalValue(5));
   EXPECT_EQ("Hosted App,Packaged App 1,Packaged App 2", RunQuery(""));
 
-  prefs->SetLastLaunchTime(kHostedAppId, base::Time::FromInternalValue(0));
+  prefs->SetLastLaunchTime(kHostedAppId, base::Time::FromInternalValue(5));
   prefs->SetLastLaunchTime(kPackagedApp1Id, base::Time::FromInternalValue(10));
   prefs->SetLastLaunchTime(kPackagedApp2Id, base::Time::FromInternalValue(20));
   EXPECT_EQ("Packaged App 2,Packaged App 1,Hosted App", RunQuery(""));
@@ -140,8 +153,27 @@
   prefs->SetLastLaunchTime(kHostedAppId,
                            kTestCurrentTime + base::TimeDelta::FromSeconds(5));
   prefs->SetLastLaunchTime(kPackagedApp1Id, base::Time::FromInternalValue(10));
+  prefs->SetLastLaunchTime(kPackagedApp2Id, base::Time::FromInternalValue(5));
+  EXPECT_EQ("Hosted App,Packaged App 1,Packaged App 2", RunQuery(""));
+}
+
+TEST_F(AppSearchProviderTest, FetchUnlaunchedRecommendations) {
+  extensions::ExtensionPrefs* prefs =
+      extensions::ExtensionPrefs::Get(profile_.get());
+
+  // The order of unlaunched recommendations should be based on the app list
+  // order and be sorted below launched items.
+  prefs->SetLastLaunchTime(kHostedAppId,
+                           kTestCurrentTime - base::TimeDelta::FromSeconds(5));
+  prefs->SetLastLaunchTime(kPackagedApp1Id, base::Time::FromInternalValue(0));
   prefs->SetLastLaunchTime(kPackagedApp2Id, base::Time::FromInternalValue(0));
   EXPECT_EQ("Hosted App,Packaged App 1,Packaged App 2", RunQuery(""));
+
+  // Switching the app list order should change the query result.
+  model()->SetItemPosition(
+      model()->FindItem(kPackagedApp2Id),
+      model()->FindItem(kPackagedApp1Id)->position().CreateBefore());
+  EXPECT_EQ("Hosted App,Packaged App 2,Packaged App 1", RunQuery(""));
 }
 
 }  // namespace test
diff --git a/chrome/browser/ui/app_list/search/search_controller_factory.cc b/chrome/browser/ui/app_list/search/search_controller_factory.cc
index d1a5a2c4..d69db6a 100644
--- a/chrome/browser/ui/app_list/search/search_controller_factory.cc
+++ b/chrome/browser/ui/app_list/search/search_controller_factory.cc
@@ -16,6 +16,7 @@
 #include "chrome/browser/ui/app_list/search/suggestions/suggestions_search_provider.h"
 #include "chrome/browser/ui/app_list/search/webstore/webstore_provider.h"
 #include "chrome/common/chrome_switches.h"
+#include "ui/app_list/app_list_model.h"
 #include "ui/app_list/search/mixer.h"
 #include "ui/app_list/search_controller.h"
 
@@ -39,16 +40,17 @@
 
 scoped_ptr<SearchController> CreateSearchController(
     Profile* profile,
-    SearchBoxModel* search_box,
-    AppListModel::SearchResults* results,
+    AppListModel* model,
     AppListControllerDelegate* list_controller) {
-  scoped_ptr<SearchController> controller(new SearchController(
-      search_box, results, HistoryFactory::GetForBrowserContext(profile)));
+  scoped_ptr<SearchController> controller(
+      new SearchController(model->search_box(), model->results(),
+                           HistoryFactory::GetForBrowserContext(profile)));
 
-  controller->AddProvider(Mixer::MAIN_GROUP,
-                          scoped_ptr<SearchProvider>(new AppSearchProvider(
-                              profile, list_controller,
-                              make_scoped_ptr(new base::DefaultClock()))));
+  controller->AddProvider(
+      Mixer::MAIN_GROUP,
+      scoped_ptr<SearchProvider>(new AppSearchProvider(
+          profile, list_controller, make_scoped_ptr(new base::DefaultClock()),
+          model->top_level_item_list())));
   controller->AddProvider(Mixer::OMNIBOX_GROUP,
                           scoped_ptr<SearchProvider>(
                               new OmniboxProvider(profile, list_controller)));
diff --git a/chrome/browser/ui/app_list/search/search_controller_factory.h b/chrome/browser/ui/app_list/search/search_controller_factory.h
index 2438abb4..8acac12 100644
--- a/chrome/browser/ui/app_list/search/search_controller_factory.h
+++ b/chrome/browser/ui/app_list/search/search_controller_factory.h
@@ -19,8 +19,7 @@
 // Build a SearchController instance with the profile.
 scoped_ptr<SearchController> CreateSearchController(
     Profile* profile,
-    SearchBoxModel* search_box,
-    AppListModel::SearchResults* results,
+    AppListModel* model,
     AppListControllerDelegate* list_controller);
 
 }  // namespace app_list
diff --git a/chrome/browser/ui/app_list/search/search_resource_manager.cc b/chrome/browser/ui/app_list/search/search_resource_manager.cc
index f83f15b..439c94a 100644
--- a/chrome/browser/ui/app_list/search/search_resource_manager.cc
+++ b/chrome/browser/ui/app_list/search/search_resource_manager.cc
@@ -24,10 +24,11 @@
 
   ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
   return make_scoped_ptr(new SearchBoxModel::SpeechButtonProperty(
-      *bundle.GetImageSkiaNamed(IDR_OMNIBOX_MIC_SEARCH),
+      *bundle.GetImageSkiaNamed(IDR_APP_LIST_MIC_HOTWORD_ON),
       l10n_util::GetStringUTF16(IDS_APP_LIST_HOTWORD_LISTENING),
       *bundle.GetImageSkiaNamed(IDR_APP_LIST_MIC_HOTWORD_OFF),
-      l10n_util::GetStringUTF16(IDS_APP_LIST_START_SPEECH_RECOGNITION)));
+      l10n_util::GetStringUTF16(IDS_APP_LIST_START_SPEECH_RECOGNITION),
+      l10n_util::GetStringUTF16(IDS_TOOLTIP_MIC_SEARCH)));
 }
 
 }  // namespace
@@ -41,6 +42,8 @@
 
   ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
   search_box_->SetIcon(*bundle.GetImageSkiaNamed(IDR_OMNIBOX_SEARCH));
+  search_box_->SetAccessibleName(
+      l10n_util::GetStringUTF16(IDS_SEARCH_BOX_HINT));
   OnSpeechRecognitionStateChanged(speech_ui_->state());
 }
 
diff --git a/chrome/browser/ui/app_list/start_page_service.cc b/chrome/browser/ui/app_list/start_page_service.cc
index c41690d..068bdaf 100644
--- a/chrome/browser/ui/app_list/start_page_service.cc
+++ b/chrome/browser/ui/app_list/start_page_service.cc
@@ -19,6 +19,7 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/search/hotword_service.h"
 #include "chrome/browser/search/hotword_service_factory.h"
+#include "chrome/browser/search_engines/template_url_service_factory.h"
 #include "chrome/browser/search_engines/ui_thread_search_terms_data.h"
 #include "chrome/browser/ui/app_list/speech_auth_helper.h"
 #include "chrome/browser/ui/app_list/speech_recognizer.h"
@@ -30,6 +31,8 @@
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/common/url_constants.h"
+#include "components/search_engines/template_url_prepopulate_data.h"
+#include "components/search_engines/template_url_service.h"
 #include "components/ui/zoom/zoom_controller.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/notification_details.h"
@@ -42,6 +45,7 @@
 #include "content/public/browser/speech_recognition_session_preamble.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_contents_delegate.h"
+#include "content/public/common/content_switches.h"
 #include "extensions/browser/extension_system_provider.h"
 #include "extensions/browser/extensions_browser_client.h"
 #include "extensions/common/extension.h"
@@ -67,6 +71,9 @@
 // Delay between checking for a new doodle when no doodle is found.
 const int kDefaultDoodleRecheckDelayMinutes = 30;
 
+// Delay before loading the start page WebContents on initialization.
+const int kLoadContentsDelaySeconds = 5;
+
 bool InSpeechRecognition(SpeechRecognitionState state) {
   return state == SPEECH_RECOGNITION_RECOGNIZING ||
       state == SPEECH_RECOGNITION_IN_SPEECH;
@@ -270,6 +277,7 @@
       speech_auth_helper_(new SpeechAuthHelper(profile, &clock_)),
       network_available_(true),
       microphone_available_(true),
+      search_engine_is_google_(false),
       weak_factory_(this) {
   // If experimental hotwording is enabled, then we're always "ready".
   // Transitioning into the "hotword recognizing" state is handled by the
@@ -277,14 +285,22 @@
   if (HotwordService::IsExperimentalHotwordingEnabled()) {
     state_ = app_list::SPEECH_RECOGNITION_READY;
   }
-
-  if (app_list::switches::IsExperimentalAppListEnabled())
-    LoadContents();
+  if (switches::IsExperimentalAppListEnabled()) {
+    TemplateURLService* template_url_service =
+        TemplateURLServiceFactory::GetForProfile(profile_);
+    const TemplateURL* default_provider =
+        template_url_service->GetDefaultSearchProvider();
+    search_engine_is_google_ =
+        TemplateURLPrepopulateData::GetEngineType(
+            *default_provider, template_url_service->search_terms_data()) ==
+        SEARCH_ENGINE_GOOGLE;
+  }
 
   network_change_observer_.reset(new NetworkChangeObserver(this));
 }
 
-StartPageService::~StartPageService() {}
+StartPageService::~StartPageService() {
+}
 
 void StartPageService::AddObserver(StartPageObserver* observer) {
   observers_.AddObserver(observer);
@@ -313,19 +329,39 @@
   }
 }
 
+void StartPageService::Init() {
+  // Do not load the start page web contents in tests because many tests assume
+  // no WebContents exist except the ones they make.
+  if (switches::IsExperimentalAppListEnabled() &&
+      !base::CommandLine::ForCurrentProcess()->HasSwitch(
+          ::switches::kTestType)) {
+    content::BrowserThread::PostDelayedTask(
+        content::BrowserThread::UI, FROM_HERE,
+        base::Bind(&StartPageService::LoadContentsIfNeeded,
+                   weak_factory_.GetWeakPtr()),
+        base::TimeDelta::FromSeconds(kLoadContentsDelaySeconds));
+  }
+}
+
+void StartPageService::LoadContentsIfNeeded() {
+  if (!contents_)
+    LoadContents();
+}
+
 void StartPageService::AppListShown() {
   if (!contents_) {
     LoadContents();
   } else if (contents_->IsCrashed()) {
     LoadStartPageURL();
-  } else if (contents_->GetWebUI() &&
-             !HotwordService::IsExperimentalHotwordingEnabled()) {
-    // If experimental hotwording is enabled, don't call onAppListShown.
-    // onAppListShown() initializes the web speech API, which is not used with
+  } else if (contents_->GetWebUI()) {
+    // If experimental hotwording is enabled, don't initialize the web speech
+    // API, which is not used with
     // experimental hotwording.
     contents_->GetWebUI()->CallJavascriptFunction(
         "appList.startPage.onAppListShown",
-        base::FundamentalValue(HotwordEnabled()));
+        base::FundamentalValue(HotwordEnabled()),
+        base::FundamentalValue(
+            !HotwordService::IsExperimentalHotwordingEnabled()));
   }
 
 #if defined(OS_CHROMEOS)
@@ -576,6 +612,9 @@
 }
 
 void StartPageService::FetchDoodleJson() {
+  if (!search_engine_is_google_)
+    return;
+
   GURL::Replacements replacements;
   replacements.SetPathStr(kDoodleJsonPath);
 
diff --git a/chrome/browser/ui/app_list/start_page_service.h b/chrome/browser/ui/app_list/start_page_service.h
index 06b0238..e4fe843 100644
--- a/chrome/browser/ui/app_list/start_page_service.h
+++ b/chrome/browser/ui/app_list/start_page_service.h
@@ -61,6 +61,11 @@
   void AddObserver(StartPageObserver* observer);
   void RemoveObserver(StartPageObserver* observer);
 
+  void Init();
+
+  // Loads the start page WebContents if it hasn't already been loaded.
+  void LoadContentsIfNeeded();
+
   void AppListShown();
   void AppListHidden();
   void ToggleSpeechRecognition(
@@ -77,6 +82,9 @@
   content::WebContents* GetStartPageContents();
   content::WebContents* GetSpeechRecognitionContents();
 
+  void set_search_engine_is_google(bool search_engine_is_google) {
+    search_engine_is_google_ = search_engine_is_google;
+  }
   Profile* profile() { return profile_; }
   SpeechRecognitionState state() { return state_; }
 
@@ -167,6 +175,7 @@
 #endif
   scoped_ptr<NetworkChangeObserver> network_change_observer_;
 
+  bool search_engine_is_google_;
   scoped_ptr<net::URLFetcher> doodle_fetcher_;
 
   base::WeakPtrFactory<StartPageService> weak_factory_;
diff --git a/chrome/browser/ui/app_list/start_page_service_factory.cc b/chrome/browser/ui/app_list/start_page_service_factory.cc
index 0e7c33f..6e5c30b 100644
--- a/chrome/browser/ui/app_list/start_page_service_factory.cc
+++ b/chrome/browser/ui/app_list/start_page_service_factory.cc
@@ -8,6 +8,7 @@
 #include "chrome/browser/extensions/install_tracker_factory.h"
 #include "chrome/browser/profiles/incognito_helpers.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/search_engines/template_url_service_factory.h"
 #include "chrome/browser/ui/app_list/start_page_service.h"
 #include "chrome/common/chrome_switches.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
@@ -40,6 +41,7 @@
   DependsOn(
       extensions::ExtensionsBrowserClient::Get()->GetExtensionSystemFactory());
   DependsOn(extensions::InstallTrackerFactory::GetInstance());
+  DependsOn(TemplateURLServiceFactory::GetInstance());
 }
 
 StartPageServiceFactory::~StartPageServiceFactory() {}
diff --git a/chrome/browser/ui/app_list/test/fake_profile.cc b/chrome/browser/ui/app_list/test/fake_profile.cc
index 1b5a36b..43e5e61b 100644
--- a/chrome/browser/ui/app_list/test/fake_profile.cc
+++ b/chrome/browser/ui/app_list/test/fake_profile.cc
@@ -13,7 +13,7 @@
       path_(path) {
 }
 
-std::string FakeProfile::GetProfileUserName() {
+std::string FakeProfile::GetProfileUserName() const {
   return name_;
 }
 
@@ -34,51 +34,50 @@
   return false;
 }
 
-content::DownloadManagerDelegate*
-FakeProfile::GetDownloadManagerDelegate() {
-  return NULL;
+content::DownloadManagerDelegate* FakeProfile::GetDownloadManagerDelegate() {
+  return nullptr;
 }
 
 net::URLRequestContextGetter* FakeProfile::GetRequestContextForRenderProcess(
     int renderer_child_id) {
-  return NULL;
+  return nullptr;
 }
 
 net::URLRequestContextGetter* FakeProfile::GetMediaRequestContext() {
-  return NULL;
+  return nullptr;
 }
 
 net::URLRequestContextGetter*
 FakeProfile::GetMediaRequestContextForRenderProcess(
     int renderer_child_id) {
-  return NULL;
+  return nullptr;
 }
 
 net::URLRequestContextGetter*
 FakeProfile::GetMediaRequestContextForStoragePartition(
         const base::FilePath& partition_path,
         bool in_memory) {
-  return NULL;
+  return nullptr;
 }
 
 content::ResourceContext* FakeProfile::GetResourceContext() {
-  return NULL;
+  return nullptr;
 }
 
 content::BrowserPluginGuestManager* FakeProfile::GetGuestManager() {
-  return NULL;
+  return nullptr;
 }
 
 storage::SpecialStoragePolicy* FakeProfile::GetSpecialStoragePolicy() {
-  return NULL;
+  return nullptr;
 }
 
 content::PushMessagingService* FakeProfile::GetPushMessagingService() {
-  return NULL;
+  return nullptr;
 }
 
 content::SSLHostStateDelegate* FakeProfile::GetSSLHostStateDelegate() {
-  return NULL;
+  return nullptr;
 }
 
 scoped_refptr<base::SequencedTaskRunner>
@@ -87,7 +86,7 @@
 }
 
 Profile* FakeProfile::GetOffTheRecordProfile() {
-  return NULL;
+  return nullptr;
 }
 
 void FakeProfile::DestroyOffTheRecordProfile() {}
@@ -113,31 +112,35 @@
 }
 
 ExtensionSpecialStoragePolicy* FakeProfile::GetExtensionSpecialStoragePolicy() {
-  return NULL;
+  return nullptr;
 }
 
 PrefService* FakeProfile::GetPrefs() {
-  return NULL;
+  return nullptr;
+}
+
+const PrefService* FakeProfile::GetPrefs() const {
+  return nullptr;
 }
 
 PrefService* FakeProfile::GetOffTheRecordPrefs() {
-  return NULL;
+  return nullptr;
 }
 
 net::URLRequestContextGetter* FakeProfile::GetRequestContext() {
-  return NULL;
+  return nullptr;
 }
 
 net::URLRequestContextGetter* FakeProfile::GetRequestContextForExtensions() {
-  return NULL;
+  return nullptr;
 }
 
 net::SSLConfigService* FakeProfile::GetSSLConfigService() {
-  return NULL;
+  return nullptr;
 }
 
 HostContentSettingsMap* FakeProfile::GetHostContentSettingsMap() {
-  return NULL;
+  return nullptr;
 }
 
 bool FakeProfile::IsSameProfile(Profile* profile) {
@@ -151,7 +154,7 @@
 net::URLRequestContextGetter* FakeProfile::CreateRequestContext(
     content::ProtocolHandlerMap* protocol_handlers,
     content::URLRequestInterceptorScopedVector request_interceptors) {
-  return NULL;
+  return nullptr;
 }
 
 net::URLRequestContextGetter*
@@ -160,7 +163,7 @@
     bool in_memory,
     content::ProtocolHandlerMap* protocol_handlers,
     content::URLRequestInterceptorScopedVector request_interceptors) {
-  return NULL;
+  return nullptr;
 }
 
 base::FilePath FakeProfile::last_selected_directory() {
@@ -177,15 +180,15 @@
 #endif  // defined(OS_CHROMEOS)
 
 PrefProxyConfigTracker* FakeProfile::GetProxyConfigTracker() {
-  return NULL;
+  return nullptr;
 }
 
 chrome_browser_net::Predictor* FakeProfile::GetNetworkPredictor() {
-  return NULL;
+  return nullptr;
 }
 
 DevToolsNetworkController* FakeProfile::GetDevToolsNetworkController() {
-  return NULL;
+  return nullptr;
 }
 
 void FakeProfile::ClearNetworkingHistorySince(
diff --git a/chrome/browser/ui/app_list/test/fake_profile.h b/chrome/browser/ui/app_list/test/fake_profile.h
index c33559c..f24ec25 100644
--- a/chrome/browser/ui/app_list/test/fake_profile.h
+++ b/chrome/browser/ui/app_list/test/fake_profile.h
@@ -31,7 +31,7 @@
   FakeProfile(const std::string& name, const base::FilePath& path);
 
   // Profile overrides.
-  std::string GetProfileUserName() override;
+  std::string GetProfileUserName() const override;
   ProfileType GetProfileType() const override;
   base::FilePath GetPath() const override;
   scoped_ptr<content::ZoomLevelDelegate> CreateZoomLevelDelegate(
@@ -61,6 +61,7 @@
   bool IsLegacySupervised() override;
   ExtensionSpecialStoragePolicy* GetExtensionSpecialStoragePolicy() override;
   PrefService* GetPrefs() override;
+  const PrefService* GetPrefs() const override;
   PrefService* GetOffTheRecordPrefs() override;
   net::URLRequestContextGetter* GetRequestContext() override;
   net::URLRequestContextGetter* GetRequestContextForExtensions() override;
diff --git a/chrome/browser/ui/ash/app_list/app_list_service_ash.cc b/chrome/browser/ui/ash/app_list/app_list_service_ash.cc
index 8ae9ccd..38da2f7c 100644
--- a/chrome/browser/ui/ash/app_list/app_list_service_ash.cc
+++ b/chrome/browser/ui/ash/app_list/app_list_service_ash.cc
@@ -8,6 +8,7 @@
 #include "base/files/file_path.h"
 #include "base/memory/singleton.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/app_list/start_page_service.h"
 #include "chrome/browser/ui/ash/app_list/app_list_controller_ash.h"
 #include "chrome/browser/ui/ash/launcher/chrome_launcher_controller.h"
 #include "ui/app_list/app_list_switches.h"
@@ -52,6 +53,12 @@
                                app_list_was_open /* animate */);
 }
 
+void AppListServiceAsh::Init(Profile* initial_profile) {
+  // Ensure the StartPageService is created here. This early initialization is
+  // necessary to allow the WebContents to load before the app list is shown.
+  app_list::StartPageService::Get(initial_profile)->Init();
+}
+
 base::FilePath AppListServiceAsh::GetProfilePath(
     const base::FilePath& user_data_dir) {
   return ChromeLauncherController::instance()->profile()->GetPath();
diff --git a/chrome/browser/ui/ash/app_list/app_list_service_ash.h b/chrome/browser/ui/ash/app_list/app_list_service_ash.h
index 3f5d198c..a8c42e9c 100644
--- a/chrome/browser/ui/ash/app_list/app_list_service_ash.h
+++ b/chrome/browser/ui/ash/app_list/app_list_service_ash.h
@@ -18,6 +18,9 @@
  public:
   static AppListServiceAsh* GetInstance();
 
+  // AppListService overrides:
+  void Init(Profile* initial_profile) override;
+
  private:
   friend struct DefaultSingletonTraits<AppListServiceAsh>;
 
diff --git a/chrome/browser/ui/ash/ash_keyboard_controller_proxy.cc b/chrome/browser/ui/ash/ash_keyboard_controller_proxy.cc
index d4e35969..0e486fd 100644
--- a/chrome/browser/ui/ash/ash_keyboard_controller_proxy.cc
+++ b/chrome/browser/ui/ash/ash_keyboard_controller_proxy.cc
@@ -8,6 +8,9 @@
 #include "ash/shell.h"
 #include "chrome/browser/extensions/chrome_extension_web_contents_observer.h"
 #include "chrome/browser/media/media_capture_devices_dispatcher.h"
+#include "content/public/browser/host_zoom_map.h"
+#include "content/public/browser/render_process_host.h"
+#include "content/public/browser/render_view_host.h"
 #include "content/public/browser/site_instance.h"
 #include "content/public/browser/web_contents.h"
 #include "extensions/browser/event_router.h"
@@ -134,6 +137,16 @@
   return handled;
 }
 
+void AshKeyboardControllerProxy::RenderViewCreated(
+    content::RenderViewHost* render_view_host) {
+  content::HostZoomMap* zoom_map =
+      content::HostZoomMap::GetDefaultForBrowserContext(browser_context());
+  DCHECK(zoom_map);
+  int render_process_id = render_view_host->GetProcess()->GetID();
+  int render_view_id = render_view_host->GetRoutingID();
+  zoom_map->SetTemporaryZoomLevel(render_process_id, render_view_id, 0);
+}
+
 void AshKeyboardControllerProxy::ShowKeyboardContainer(
     aura::Window* container) {
   // TODO(bshe): Implement logic to decide which root window should display
diff --git a/chrome/browser/ui/ash/ash_keyboard_controller_proxy.h b/chrome/browser/ui/ash/ash_keyboard_controller_proxy.h
index 7bbf5c61..40b4777 100644
--- a/chrome/browser/ui/ash/ash_keyboard_controller_proxy.h
+++ b/chrome/browser/ui/ash/ash_keyboard_controller_proxy.h
@@ -63,6 +63,7 @@
 
   // content::WebContentsObserver overrides
   bool OnMessageReceived(const IPC::Message& message) override;
+  void RenderViewCreated(content::RenderViewHost* render_view_host) override;
 
   scoped_ptr<extensions::ExtensionFunctionDispatcher>
       extension_function_dispatcher_;
diff --git a/chrome/browser/ui/ash/system_tray_delegate_common.cc b/chrome/browser/ui/ash/system_tray_delegate_common.cc
index 384520f..fa6b9e6 100644
--- a/chrome/browser/ui/ash/system_tray_delegate_common.cc
+++ b/chrome/browser/ui/ash/system_tray_delegate_common.cc
@@ -23,6 +23,14 @@
 #include "content/public/browser/notification_service.h"
 #include "ui/base/l10n/l10n_util.h"
 
+namespace {
+
+ash::SystemTrayNotifier* GetSystemTrayNotifier() {
+  return ash::Shell::GetInstance()->system_tray_notifier();
+}
+
+}  // namespace
+
 SystemTrayDelegateCommon::SystemTrayDelegateCommon()
     : clock_type_(base::GetHourClockType()) {
   // Register notifications on construction so that events such as
@@ -53,31 +61,6 @@
   return ash::user::LOGGED_IN_OWNER;
 }
 
-void SystemTrayDelegateCommon::ChangeProfilePicture() {
-}
-
-const std::string SystemTrayDelegateCommon::GetEnterpriseDomain() const {
-  return std::string();
-}
-
-const base::string16 SystemTrayDelegateCommon::GetEnterpriseMessage() const {
-  return base::string16();
-}
-
-const std::string SystemTrayDelegateCommon::GetSupervisedUserManager() const {
-  return std::string();
-}
-
-const base::string16 SystemTrayDelegateCommon::GetSupervisedUserManagerName()
-    const {
-  return base::string16();
-}
-
-const base::string16 SystemTrayDelegateCommon::GetSupervisedUserMessage()
-    const {
-  return base::string16();
-}
-
 bool SystemTrayDelegateCommon::IsUserSupervised() const {
   return false;
 }
@@ -95,29 +78,6 @@
   return clock_type_;
 }
 
-void SystemTrayDelegateCommon::ShowSettings() {
-}
-
-bool SystemTrayDelegateCommon::ShouldShowSettings() {
-  return true;
-}
-
-void SystemTrayDelegateCommon::ShowDateSettings() {
-}
-
-void SystemTrayDelegateCommon::ShowSetTimeDialog() {
-}
-
-void SystemTrayDelegateCommon::ShowNetworkSettings(
-    const std::string& service_path) {
-}
-
-void SystemTrayDelegateCommon::ShowBluetoothSettings() {
-}
-
-void SystemTrayDelegateCommon::ShowDisplaySettings() {
-}
-
 void SystemTrayDelegateCommon::ShowChromeSlow() {
 #if defined(OS_LINUX)
   chrome::ScopedTabbedBrowserDisplayer displayer(
@@ -126,170 +86,21 @@
 #endif  // defined(OS_LINUX)
 }
 
-bool SystemTrayDelegateCommon::ShouldShowDisplayNotification() {
-  return false;
-}
-
-void SystemTrayDelegateCommon::ShowIMESettings() {
-}
-
 void SystemTrayDelegateCommon::ShowHelp() {
   chrome::ShowHelpForProfile(ProfileManager::GetLastUsedProfile(),
                              chrome::HOST_DESKTOP_TYPE_ASH,
                              chrome::HELP_SOURCE_MENU);
 }
 
-void SystemTrayDelegateCommon::ShowAccessibilityHelp() {
-}
-
-void SystemTrayDelegateCommon::ShowAccessibilitySettings() {
-}
-
-void SystemTrayDelegateCommon::ShowPublicAccountInfo() {
-}
-
-void SystemTrayDelegateCommon::ShowSupervisedUserInfo() {
-}
-
-void SystemTrayDelegateCommon::ShowEnterpriseInfo() {
-}
-
-void SystemTrayDelegateCommon::ShowUserLogin() {
-}
-
-void SystemTrayDelegateCommon::SignOut() {
-}
-
-void SystemTrayDelegateCommon::RequestLockScreen() {
-}
-
 void SystemTrayDelegateCommon::RequestRestartForUpdate() {
   chrome::AttemptRestart();
 }
 
-void SystemTrayDelegateCommon::GetAvailableBluetoothDevices(
-    ash::BluetoothDeviceList* list) {
-}
-
-void SystemTrayDelegateCommon::BluetoothStartDiscovering() {
-}
-
-void SystemTrayDelegateCommon::BluetoothStopDiscovering() {
-}
-
-void SystemTrayDelegateCommon::ConnectToBluetoothDevice(
-    const std::string& address) {
-}
-
-bool SystemTrayDelegateCommon::IsBluetoothDiscovering() {
-  return false;
-}
-
-void SystemTrayDelegateCommon::GetCurrentIME(ash::IMEInfo* info) {
-}
-
-void SystemTrayDelegateCommon::GetAvailableIMEList(ash::IMEInfoList* list) {
-}
-
-void SystemTrayDelegateCommon::GetCurrentIMEProperties(
-    ash::IMEPropertyInfoList* list) {
-}
-
-void SystemTrayDelegateCommon::SwitchIME(const std::string& ime_id) {
-}
-
-void SystemTrayDelegateCommon::ActivateIMEProperty(const std::string& key) {
-}
-
-void SystemTrayDelegateCommon::ManageBluetoothDevices() {
-}
-
-void SystemTrayDelegateCommon::ToggleBluetooth() {
-}
-
-void SystemTrayDelegateCommon::ShowOtherNetworkDialog(const std::string& type) {
-}
-
-bool SystemTrayDelegateCommon::GetBluetoothAvailable() {
-  return false;
-}
-
-bool SystemTrayDelegateCommon::GetBluetoothEnabled() {
-  return false;
-}
-
-bool SystemTrayDelegateCommon::GetBluetoothDiscovering() {
-  return false;
-}
-
-void SystemTrayDelegateCommon::ChangeProxySettings() {
-}
-
-ash::NetworkingConfigDelegate*
-SystemTrayDelegateCommon::GetNetworkingConfigDelegate() const {
-  return nullptr;
-}
-
-ash::VolumeControlDelegate* SystemTrayDelegateCommon::GetVolumeControlDelegate()
-    const {
-  return nullptr;
-}
-
-void SystemTrayDelegateCommon::SetVolumeControlDelegate(
-    scoped_ptr<ash::VolumeControlDelegate> delegate) {
-}
-
-bool SystemTrayDelegateCommon::GetSessionStartTime(
-    base::TimeTicks* session_start_time) {
-  return false;
-}
-
-bool SystemTrayDelegateCommon::GetSessionLengthLimit(
-    base::TimeDelta* session_length_limit) {
-  return false;
-}
-
 int SystemTrayDelegateCommon::GetSystemTrayMenuWidth() {
   return l10n_util::GetLocalizedContentsWidthInPixels(
       IDS_SYSTEM_TRAY_MENU_BUBBLE_WIDTH_PIXELS);
 }
 
-void SystemTrayDelegateCommon::ActiveUserWasChanged() {
-}
-
-bool SystemTrayDelegateCommon::IsSearchKeyMappedToCapsLock() {
-  return false;
-}
-
-ash::tray::UserAccountsDelegate*
-SystemTrayDelegateCommon::GetUserAccountsDelegate(const std::string& user_id) {
-  return NULL;
-}
-
-void SystemTrayDelegateCommon::AddCustodianInfoTrayObserver(
-    ash::CustodianInfoTrayObserver* observer) {
-}
-
-void SystemTrayDelegateCommon::RemoveCustodianInfoTrayObserver(
-    ash::CustodianInfoTrayObserver* observer) {
-}
-
-void SystemTrayDelegateCommon::AddShutdownPolicyObserver(
-    ash::ShutdownPolicyObserver* observer) {
-}
-
-void SystemTrayDelegateCommon::RemoveShutdownPolicyObserver(
-    ash::ShutdownPolicyObserver* observer) {
-}
-
-void SystemTrayDelegateCommon::ShouldRebootOnShutdown(
-    const ash::RebootOnShutdownCallback& callback) {
-}
-
-ash::SystemTrayNotifier* SystemTrayDelegateCommon::GetSystemTrayNotifier() {
-  return ash::Shell::GetInstance()->system_tray_notifier();
-}
-
 void SystemTrayDelegateCommon::UpdateClockType() {
   clock_type_ = (base::GetHourClockType() == base::k24HourClock)
                     ? base::k24HourClock
diff --git a/chrome/browser/ui/ash/system_tray_delegate_common.h b/chrome/browser/ui/ash/system_tray_delegate_common.h
index 882ff6f..5a712f20 100644
--- a/chrome/browser/ui/ash/system_tray_delegate_common.h
+++ b/chrome/browser/ui/ash/system_tray_delegate_common.h
@@ -15,10 +15,6 @@
 #include "content/public/browser/notification_observer.h"
 #include "content/public/browser/notification_registrar.h"
 
-namespace ash {
-class SystemTrayNotifier;
-}
-
 // Common base class for platform-specific implementations.
 class SystemTrayDelegateCommon : public ash::SystemTrayDelegate,
                                  public content::NotificationObserver {
@@ -32,78 +28,16 @@
   void Shutdown() override;
   bool GetTrayVisibilityOnStartup() override;
   ash::user::LoginStatus GetUserLoginStatus() const override;
-  void ChangeProfilePicture() override;
-  const std::string GetEnterpriseDomain() const override;
-  const base::string16 GetEnterpriseMessage() const override;
-  const std::string GetSupervisedUserManager() const override;
-  const base::string16 GetSupervisedUserManagerName() const override;
-  const base::string16 GetSupervisedUserMessage() const override;
   bool IsUserSupervised() const override;
   bool IsUserChild() const override;
   void GetSystemUpdateInfo(ash::UpdateInfo* info) const override;
   base::HourClockType GetHourClockType() const override;
-  void ShowSettings() override;
-  bool ShouldShowSettings() override;
-  void ShowDateSettings() override;
-  void ShowSetTimeDialog() override;
-  void ShowNetworkSettings(const std::string& service_path) override;
-  void ShowBluetoothSettings() override;
-  void ShowDisplaySettings() override;
   void ShowChromeSlow() override;
-  bool ShouldShowDisplayNotification() override;
-  void ShowIMESettings() override;
   void ShowHelp() override;
-  void ShowAccessibilityHelp() override;
-  void ShowAccessibilitySettings() override;
-  void ShowPublicAccountInfo() override;
-  void ShowSupervisedUserInfo() override;
-  void ShowEnterpriseInfo() override;
-  void ShowUserLogin() override;
-  void SignOut() override;
-  void RequestLockScreen() override;
   void RequestRestartForUpdate() override;
-  void GetAvailableBluetoothDevices(ash::BluetoothDeviceList* list) override;
-  void BluetoothStartDiscovering() override;
-  void BluetoothStopDiscovering() override;
-  void ConnectToBluetoothDevice(const std::string& address) override;
-  bool IsBluetoothDiscovering() override;
-  void GetCurrentIME(ash::IMEInfo* info) override;
-  void GetAvailableIMEList(ash::IMEInfoList* list) override;
-  void GetCurrentIMEProperties(ash::IMEPropertyInfoList* list) override;
-  void SwitchIME(const std::string& ime_id) override;
-  void ActivateIMEProperty(const std::string& key) override;
-  void ManageBluetoothDevices() override;
-  void ToggleBluetooth() override;
-  void ShowOtherNetworkDialog(const std::string& type) override;
-  bool GetBluetoothAvailable() override;
-  bool GetBluetoothEnabled() override;
-  bool GetBluetoothDiscovering() override;
-  void ChangeProxySettings() override;
-  ash::NetworkingConfigDelegate* GetNetworkingConfigDelegate() const override;
-  ash::VolumeControlDelegate* GetVolumeControlDelegate() const override;
-  void SetVolumeControlDelegate(
-      scoped_ptr<ash::VolumeControlDelegate> delegate) override;
-  bool GetSessionStartTime(base::TimeTicks* session_start_time) override;
-  bool GetSessionLengthLimit(base::TimeDelta* session_length_limit) override;
   int GetSystemTrayMenuWidth() override;
-  void ActiveUserWasChanged() override;
-  bool IsSearchKeyMappedToCapsLock() override;
-  ash::tray::UserAccountsDelegate* GetUserAccountsDelegate(
-      const std::string& user_id) override;
-  void AddCustodianInfoTrayObserver(
-      ash::CustodianInfoTrayObserver* observer) override;
-  void RemoveCustodianInfoTrayObserver(
-      ash::CustodianInfoTrayObserver* observer) override;
-  void AddShutdownPolicyObserver(
-      ash::ShutdownPolicyObserver* observer) override;
-  void RemoveShutdownPolicyObserver(
-      ash::ShutdownPolicyObserver* observer) override;
-  void ShouldRebootOnShutdown(
-      const ash::RebootOnShutdownCallback& callback) override;
 
  private:
-  ash::SystemTrayNotifier* GetSystemTrayNotifier();
-
   void UpdateClockType();
 
   // content::NotificationObserver implementation.
diff --git a/chrome/browser/ui/ash/user_accounts_delegate_chromeos.cc b/chrome/browser/ui/ash/user_accounts_delegate_chromeos.cc
index f71dc0e2..df024639 100644
--- a/chrome/browser/ui/ash/user_accounts_delegate_chromeos.cc
+++ b/chrome/browser/ui/ash/user_accounts_delegate_chromeos.cc
@@ -54,7 +54,7 @@
 
 std::string UserAccountsDelegateChromeOS::GetAccountDisplayName(
     const std::string& account_id) {
-  user_manager::User* user =
+  const user_manager::User* user =
       ProfileHelper::Get()->GetUserByProfile(user_profile_);
   if (gaia::AreEmailsSame(user->email(), account_id) &&
       !user->display_email().empty())
diff --git a/chrome/browser/ui/autofill/autofill_dialog_controller_unittest.cc b/chrome/browser/ui/autofill/autofill_dialog_controller_unittest.cc
index d399f75..55b9a39 100644
--- a/chrome/browser/ui/autofill/autofill_dialog_controller_unittest.cc
+++ b/chrome/browser/ui/autofill/autofill_dialog_controller_unittest.cc
@@ -3521,7 +3521,7 @@
   field.autocomplete_attribute = "billing cc-type";
   field.option_contents.push_back(ASCIIToUTF16("Visa"));
   field.option_values.push_back(ASCIIToUTF16("V"));
-  field.option_contents.push_back(ASCIIToUTF16("American Express"));
+  field.option_contents.push_back(ASCIIToUTF16("Amex"));
   field.option_values.push_back(ASCIIToUTF16("AX"));
   form_data.fields.push_back(field);
   ResetControllerWithFormData(form_data);
diff --git a/chrome/browser/ui/autofill/card_unmask_prompt_controller_impl.cc b/chrome/browser/ui/autofill/card_unmask_prompt_controller_impl.cc
index 419b413b..2911a8c7 100644
--- a/chrome/browser/ui/autofill/card_unmask_prompt_controller_impl.cc
+++ b/chrome/browser/ui/autofill/card_unmask_prompt_controller_impl.cc
@@ -79,14 +79,10 @@
 }
 
 base::string16 CardUnmaskPromptControllerImpl::GetWindowTitle() const {
-  // TODO(estade): i18n.
-  if (ShouldRequestExpirationDate()) {
-    return base::ASCIIToUTF16("Update your card ") +
-           card_.TypeAndLastFourDigits();
-  }
-
-  return base::ASCIIToUTF16("Verify your card ") +
-      card_.TypeAndLastFourDigits();
+  int ids = ShouldRequestExpirationDate()
+      ? IDS_AUTOFILL_CARD_UNMASK_PROMPT_UPDATE_TITLE
+      : IDS_AUTOFILL_CARD_UNMASK_PROMPT_TITLE;
+  return l10n_util::GetStringFUTF16(ids, card_.TypeAndLastFourDigits());
 }
 
 base::string16 CardUnmaskPromptControllerImpl::GetInstructionsMessage() const {
diff --git a/chrome/browser/ui/browser_instant_controller.cc b/chrome/browser/ui/browser_instant_controller.cc
index c579176..c4871ef1 100644
--- a/chrome/browser/ui/browser_instant_controller.cc
+++ b/chrome/browser/ui/browser_instant_controller.cc
@@ -21,10 +21,12 @@
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/common/instant_types.h"
 #include "chrome/common/url_constants.h"
+#include "content/public/browser/navigation_controller.h"
 #include "content/public/browser/render_process_host.h"
 #include "content/public/browser/user_metrics.h"
 #include "content/public/browser/web_contents.h"
-
+#include "content/public/common/referrer.h"
+#include "ui/base/page_transition_types.h"
 
 // Helpers --------------------------------------------------------------------
 
@@ -140,7 +142,8 @@
     instant_.InstantSupportChanged(new_state.instant_support);
 }
 
-void BrowserInstantController::DefaultSearchProviderChanged() {
+void BrowserInstantController::DefaultSearchProviderChanged(
+    bool google_base_url_domain_changed) {
   InstantService* instant_service =
       InstantServiceFactory::GetForProfile(profile());
   if (!instant_service)
@@ -157,17 +160,28 @@
     content::RenderProcessHost* rph = contents->GetRenderProcessHost();
     instant_service->SendSearchURLsToRenderer(rph);
 
-    // Reload the contents to ensure that it gets assigned to a non-priviledged
-    // renderer.
     if (!instant_service->IsInstantProcess(rph->GetID()))
       continue;
 
-    contents->GetController().Reload(false);
+    if (google_base_url_domain_changed &&
+        SearchTabHelper::FromWebContents(contents)->model()->mode().is_ntp()) {
+      // Replace the server NTP with the local NTP.
+      content::NavigationController::LoadURLParams
+          params(chrome::GetLocalInstantURL(profile()));
+      params.should_replace_current_entry = true;
+      params.referrer = content::Referrer();
+      params.transition_type = ui::PAGE_TRANSITION_RELOAD;
+      contents->GetController().LoadURLWithParams(params);
+    } else {
+      // Reload the contents to ensure that it gets assigned to a
+      // non-priviledged renderer.
+      contents->GetController().Reload(false);
 
-    // As the reload was not triggered by the user we don't want to close any
-    // infobars. We have to tell the InfoBarService after the reload, otherwise
-    // it would ignore this call when
-    // WebContentsObserver::DidStartNavigationToPendingEntry is invoked.
-    InfoBarService::FromWebContents(contents)->set_ignore_next_reload();
+      // As the reload was not triggered by the user we don't want to close any
+      // infobars. We have to tell the InfoBarService after the reload,
+      // otherwise it would ignore this call when
+      // WebContentsObserver::DidStartNavigationToPendingEntry is invoked.
+      InfoBarService::FromWebContents(contents)->set_ignore_next_reload();
+    }
   }
 }
diff --git a/chrome/browser/ui/browser_instant_controller.h b/chrome/browser/ui/browser_instant_controller.h
index 6e35ae9b..b8bf95e 100644
--- a/chrome/browser/ui/browser_instant_controller.h
+++ b/chrome/browser/ui/browser_instant_controller.h
@@ -53,7 +53,8 @@
                     const SearchModel::State& new_state) override;
 
   // InstantServiceObserver:
-  void DefaultSearchProviderChanged() override;
+  void DefaultSearchProviderChanged(
+      bool google_base_url_domain_changed) override;
 
   // Replaces the contents at tab |index| with |new_contents| and deletes the
   // existing contents.
diff --git a/chrome/browser/ui/browser_instant_controller_unittest.cc b/chrome/browser/ui/browser_instant_controller_unittest.cc
index f4eaeee6..077390c 100644
--- a/chrome/browser/ui/browser_instant_controller_unittest.cc
+++ b/chrome/browser/ui/browser_instant_controller_unittest.cc
@@ -36,23 +36,40 @@
   friend class FakeWebContentsObserver;
 };
 
-const struct TabReloadTestCase {
+struct TabReloadTestCase {
   const char* description;
   const char* start_url;
   bool start_in_instant_process;
   bool should_reload;
+  bool end_in_local_ntp;
   bool end_in_instant_process;
-} kTabReloadTestCases[] = {
-    {"Local Embedded NTP", chrome::kChromeSearchLocalNtpUrl,
-     true, true, true},
-    {"Remote Embedded NTP", "https://www.google.com/instant?strk",
-     true, true, false},
-    {"Remote Embedded SERP", "https://www.google.com/url?strk&bar=search+terms",
-     true, true, false},
-    {"Other NTP", "https://bar.com/instant?strk",
-     false, false, false}
 };
 
+// Test cases for when Google is the initial, but not final provider.
+const TabReloadTestCase kTabReloadTestCasesFinalProviderNotGoogle[] = {
+    {"Local Embedded NTP", chrome::kChromeSearchLocalNtpUrl,
+     true, true, true, true},
+    {"Remote Embedded NTP", "https://www.google.com/newtab",
+     true, true, false, false},
+    {"Remote Embedded SERP", "https://www.google.com/url?strk&bar=search+terms",
+     true, true, false, false},
+    {"Other NTP", "https://bar.com/newtab",
+     false, false, false, false}
+};
+
+// Test cases for when Google is both the initial and final provider.
+const TabReloadTestCase kTabReloadTestCasesFinalProviderGoogle[] = {
+    {"Local Embedded NTP", chrome::kChromeSearchLocalNtpUrl,
+     true, true, true, true},
+    {"Remote Embedded NTP", "https://www.google.com/newtab",
+     true, false, true, true},
+    {"Remote Embedded SERP", "https://www.google.com/url?strk&bar=search+terms",
+     true, true, false, false},
+    {"Other NTP", "https://bar.com/newtab",
+     false, false, false, false}
+};
+
+
 class FakeWebContentsObserver : public content::WebContentsObserver {
  public:
   explicit FakeWebContentsObserver(content::WebContents* contents)
@@ -66,16 +83,25 @@
       content::NavigationController::ReloadType reload_type) override {
     if (url_ == url)
       num_reloads_++;
+    current_url_ = url;
   }
 
   const GURL url() const {
     return url_;
   }
 
+  const GURL current_url() const {
+    return contents_->GetURL();
+  }
+
   int num_reloads() const {
     return num_reloads_;
   }
 
+  bool can_go_back() const {
+    return contents_->GetController().CanGoBack();
+  }
+
  protected:
   friend class BrowserInstantControllerTest;
   FRIEND_TEST_ALL_PREFIXES(BrowserInstantControllerTest,
@@ -86,14 +112,16 @@
  private:
   content::WebContents* contents_;
   const GURL& url_;
+  GURL current_url_;
   int num_reloads_;
 };
 
 TEST_F(BrowserInstantControllerTest, DefaultSearchProviderChanged) {
-  size_t num_tests = arraysize(kTabReloadTestCases);
+  size_t num_tests = arraysize(kTabReloadTestCasesFinalProviderNotGoogle);
   ScopedVector<FakeWebContentsObserver> observers;
   for (size_t i = 0; i < num_tests; ++i) {
-    const TabReloadTestCase& test = kTabReloadTestCases[i];
+    const TabReloadTestCase& test =
+        kTabReloadTestCasesFinalProviderNotGoogle[i];
     AddTab(browser(), GURL(test.start_url));
     content::WebContents* contents =
       browser()->tab_strip_model()->GetActiveWebContents();
@@ -112,27 +140,34 @@
 
   for (size_t i = 0; i < num_tests; ++i) {
     FakeWebContentsObserver* observer = observers[i];
-    const TabReloadTestCase& test = kTabReloadTestCases[i];
+    const TabReloadTestCase& test =
+        kTabReloadTestCasesFinalProviderNotGoogle[i];
 
     if (test.should_reload) {
       // Validate final instant state.
       EXPECT_EQ(
           test.end_in_instant_process,
-          chrome::ShouldAssignURLToInstantRenderer(observer->url(), profile()))
+          chrome::ShouldAssignURLToInstantRenderer(
+              observer->current_url(), profile()))
         << test.description;
     }
 
     // Ensure only the expected tabs(contents) reloaded.
     EXPECT_EQ(test.should_reload ? 1 : 0, observer->num_reloads())
       << test.description;
+
+    if (test.end_in_local_ntp) {
+      EXPECT_EQ(GURL(chrome::kChromeSearchLocalNtpUrl), observer->current_url())
+          << test.description;
+    }
   }
 }
 
 TEST_F(BrowserInstantControllerTest, GoogleBaseURLUpdated) {
-  const size_t num_tests = arraysize(kTabReloadTestCases);
+  const size_t num_tests = arraysize(kTabReloadTestCasesFinalProviderGoogle);
   ScopedVector<FakeWebContentsObserver> observers;
   for (size_t i = 0; i < num_tests; ++i) {
-    const TabReloadTestCase& test = kTabReloadTestCases[i];
+    const TabReloadTestCase& test = kTabReloadTestCasesFinalProviderGoogle[i];
     AddTab(browser(), GURL(test.start_url));
     content::WebContents* contents =
       browser()->tab_strip_model()->GetActiveWebContents();
@@ -150,20 +185,26 @@
   NotifyGoogleBaseURLUpdate("https://www.google.es/");
 
   for (size_t i = 0; i < num_tests; ++i) {
-    const TabReloadTestCase& test = kTabReloadTestCases[i];
+    const TabReloadTestCase& test = kTabReloadTestCasesFinalProviderGoogle[i];
     FakeWebContentsObserver* observer = observers[i];
 
-    if (test.should_reload) {
-      // Validate final instant state.
-      EXPECT_EQ(
-          test.end_in_instant_process,
-          chrome::ShouldAssignURLToInstantRenderer(observer->url(), profile()))
-        << test.description;
-    }
+    // Validate final instant state.
+    EXPECT_EQ(
+        test.end_in_instant_process,
+        chrome::ShouldAssignURLToInstantRenderer(
+            observer->current_url(), profile()))
+      << test.description;
 
     // Ensure only the expected tabs(contents) reloaded.
     EXPECT_EQ(test.should_reload ? 1 : 0, observer->num_reloads())
       << test.description;
+
+    if (test.end_in_local_ntp) {
+      EXPECT_EQ(GURL(chrome::kChromeSearchLocalNtpUrl), observer->current_url())
+          << test.description;
+      // The navigation to Local NTP should be definitive i.e. can't go back.
+      EXPECT_FALSE(observer->can_go_back());
+    }
   }
 }
 
diff --git a/chrome/browser/ui/browser_ui_prefs.cc b/chrome/browser/ui/browser_ui_prefs.cc
index a6b3491..9edc021b 100644
--- a/chrome/browser/ui/browser_ui_prefs.cc
+++ b/chrome/browser/ui/browser_ui_prefs.cc
@@ -157,6 +157,12 @@
       prefs::kEnableDoNotTrack,
       false,
       user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
+#if defined(ENABLE_WEBRTC)
+  registry->RegisterBooleanPref(
+      prefs::kWebRTCMultipleRoutesEnabled,
+      true,
+      user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
+#endif
 
   // Dictionaries to keep track of default tasks in the file browser.
   registry->RegisterDictionaryPref(
diff --git a/chrome/browser/ui/cocoa/apps/app_shim_menu_controller_mac_browsertest.mm b/chrome/browser/ui/cocoa/apps/app_shim_menu_controller_mac_browsertest.mm
index 5f02a2d..60890ee 100644
--- a/chrome/browser/ui/cocoa/apps/app_shim_menu_controller_mac_browsertest.mm
+++ b/chrome/browser/ui/cocoa/apps/app_shim_menu_controller_mac_browsertest.mm
@@ -40,6 +40,7 @@
 
   void SetUpCommandLine(base::CommandLine* command_line) override {
     PlatformAppBrowserTest::SetUpCommandLine(command_line);
+    command_line->AppendSwitch(switches::kEnableNewBookmarkApps);
   }
 
   // Start testing apps and wait for them to launch. |flags| is a bitmask of
diff --git a/chrome/browser/ui/cocoa/browser_window_cocoa.mm b/chrome/browser/ui/cocoa/browser_window_cocoa.mm
index c5ae8ae..96ea6f1 100644
--- a/chrome/browser/ui/cocoa/browser_window_cocoa.mm
+++ b/chrome/browser/ui/cocoa/browser_window_cocoa.mm
@@ -536,7 +536,8 @@
     const WebApplicationInfo& web_app_info,
     const ShowBookmarkAppBubbleCallback& callback) {
   base::scoped_nsobject<NSAlert> alert([[NSAlert alloc] init]);
-  [alert setMessageText:l10n_util::GetNSString(IDS_BOOKMARK_APP_BUBBLE_TITLE)];
+  [alert setMessageText:l10n_util::GetNSString(
+      IDS_ADD_TO_APPLICATIONS_BUBBLE_TITLE)];
   [alert setAlertStyle:NSInformationalAlertStyle];
 
   NSButton* continue_button =
diff --git a/chrome/browser/ui/cocoa/extensions/extension_popup_controller.mm b/chrome/browser/ui/cocoa/extensions/extension_popup_controller.mm
index 8cc1a36..640b98c 100644
--- a/chrome/browser/ui/cocoa/extensions/extension_popup_controller.mm
+++ b/chrome/browser/ui/cocoa/extensions/extension_popup_controller.mm
@@ -46,12 +46,13 @@
 
 @interface ExtensionPopupController (Private)
 // Callers should be using the public static method for initialization.
-// NOTE: This takes ownership of |host|.
-- (id)initWithHost:(extensions::ExtensionViewHost*)host
-      parentWindow:(NSWindow*)parentWindow
-        anchoredAt:(NSPoint)anchoredAt
-     arrowLocation:(info_bubble::BubbleArrowLocation)arrowLocation
-           devMode:(BOOL)devMode;
+- (id)initWithParentWindow:(NSWindow*)parentWindow
+                anchoredAt:(NSPoint)anchoredAt
+             arrowLocation:(info_bubble::BubbleArrowLocation)arrowLocation
+                   devMode:(BOOL)devMode;
+
+// Set the ExtensionViewHost, taking ownership.
+- (void)setExtensionViewHost:(scoped_ptr<extensions::ExtensionViewHost>)host;
 
 // Called when the extension's hosted NSView has been resized.
 - (void)extensionViewFrameChanged;
@@ -150,11 +151,10 @@
 
 @synthesize extensionId = extensionId_;
 
-- (id)initWithHost:(extensions::ExtensionViewHost*)host
-      parentWindow:(NSWindow*)parentWindow
-        anchoredAt:(NSPoint)anchoredAt
-     arrowLocation:(info_bubble::BubbleArrowLocation)arrowLocation
-           devMode:(BOOL)devMode {
+- (id)initWithParentWindow:(NSWindow*)parentWindow
+                anchoredAt:(NSPoint)anchoredAt
+             arrowLocation:(info_bubble::BubbleArrowLocation)arrowLocation
+                   devMode:(BOOL)devMode {
   base::scoped_nsobject<InfoBubbleWindow> window([[InfoBubbleWindow alloc]
       initWithContentRect:ui::kWindowSizeDeterminedLater
                 styleMask:NSBorderlessWindowMask
@@ -167,36 +167,9 @@
   if ((self = [super initWithWindow:window
                        parentWindow:parentWindow
                          anchoredAt:anchoredAt])) {
-    host_.reset(host);
-    extensionId_ = host_->extension_id();
     beingInspected_ = devMode;
     ignoreWindowDidResignKey_ = NO;
-
-    InfoBubbleView* view = self.bubble;
-    [view setArrowLocation:arrowLocation];
-
-    extensionView_ = host->view()->GetNativeView();
-    container_.reset(new ExtensionPopupContainer(self));
-    static_cast<ExtensionViewMac*>(host->view())
-        ->set_container(container_.get());
-
-    NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
-    [center addObserver:self
-               selector:@selector(extensionViewFrameChanged)
-                   name:NSViewFrameDidChangeNotification
-                 object:extensionView_];
-
-    [view addSubview:extensionView_];
-
-    notificationBridge_.reset(new DevtoolsNotificationBridge(self));
-    registrar_.reset(new content::NotificationRegistrar);
-    if (beingInspected_) {
-      // Listen for the extension to finish loading so the dev tools can be
-      // opened.
-      registrar_->Add(notificationBridge_.get(),
-                      extensions::NOTIFICATION_EXTENSION_HOST_DID_STOP_LOADING,
-                      content::Source<BrowserContext>(host->browser_context()));
-    }
+    [[self bubble] setArrowLocation:arrowLocation];
   }
   return self;
 }
@@ -285,29 +258,25 @@
   // Make Mac behavior the same with Windows and others.
   if (gPopup) {
     std::string extension_id = url.host();
-    extensions::ExtensionViewHost* host = [gPopup extensionViewHost];
-    if (extension_id == host->extension_id()) {
-      [gPopup close];
+    std::string old_extension_id = [gPopup extensionViewHost]->extension_id();
+    [gPopup close];  // Starts the animation to fade out the popup.
+    if (extension_id == old_extension_id)
       return nil;
-    }
   }
 
-  extensions::ExtensionViewHost* host =
-      extensions::ExtensionViewHostFactory::CreatePopupHost(url, browser);
-  DCHECK(host);
-  if (!host)
-    return nil;
-
-  [gPopup close];
-
-  // Takes ownership of |host|. Also will autorelease itself when the popup is
-  // closed, so no need to do that here.
+  // Create the popup first. This establishes an initially hidden NSWindow so
+  // that the renderer is able to gather correct screen metrics for the initial
+  // paint.
   gPopup = [[ExtensionPopupController alloc]
-      initWithHost:host
-      parentWindow:browser->window()->GetNativeWindow()
-        anchoredAt:anchoredAt
-     arrowLocation:arrowLocation
-           devMode:devMode];
+      initWithParentWindow:browser->window()->GetNativeWindow()
+                anchoredAt:anchoredAt
+             arrowLocation:arrowLocation
+                   devMode:devMode];
+
+  scoped_ptr<extensions::ExtensionViewHost> host(
+      extensions::ExtensionViewHostFactory::CreatePopupHost(url, browser));
+  DCHECK(host);
+  [gPopup setExtensionViewHost:host.Pass()];
   return gPopup;
 }
 
@@ -315,6 +284,36 @@
   return gPopup;
 }
 
+- (void)setExtensionViewHost:(scoped_ptr<extensions::ExtensionViewHost>)host {
+  DCHECK(!host_);
+  DCHECK(host);
+  host_.swap(host);
+
+  extensionId_ = host_->extension_id();
+  container_.reset(new ExtensionPopupContainer(self));
+  ExtensionViewMac* hostView = static_cast<ExtensionViewMac*>(host_->view());
+  hostView->set_container(container_.get());
+  hostView->CreateWidgetHostViewIn([self bubble]);
+
+  extensionView_ = hostView->GetNativeView();
+
+  NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
+  [center addObserver:self
+             selector:@selector(extensionViewFrameChanged)
+                 name:NSViewFrameDidChangeNotification
+               object:extensionView_];
+
+  notificationBridge_.reset(new DevtoolsNotificationBridge(self));
+  registrar_.reset(new content::NotificationRegistrar);
+  if (beingInspected_) {
+    // Listen for the extension to finish loading so the dev tools can be
+    // opened.
+    registrar_->Add(notificationBridge_.get(),
+                    extensions::NOTIFICATION_EXTENSION_HOST_DID_STOP_LOADING,
+                    content::Source<BrowserContext>(host_->browser_context()));
+  }
+}
+
 - (void)extensionViewFrameChanged {
   // If there are no changes in the width or height of the frame, then ignore.
   if (NSEqualSizes([extensionView_ frame].size, extensionFrame_.size))
@@ -382,7 +381,7 @@
   // When we update the size, the window will become visible. Stay hidden until
   // the host is loaded.
   pendingSize_ = newSize;
-  if (!host_->did_stop_loading())
+  if (!host_ || !host_->did_stop_loading())
     return;
 
   // No need to use CA here, our caller calls us repeatedly to animate the
diff --git a/chrome/browser/ui/cocoa/extensions/extension_view_mac.h b/chrome/browser/ui/cocoa/extensions/extension_view_mac.h
index ab2dc9d..a67da291 100644
--- a/chrome/browser/ui/cocoa/extensions/extension_view_mac.h
+++ b/chrome/browser/ui/cocoa/extensions/extension_view_mac.h
@@ -53,8 +53,10 @@
   // Informs the view that its containing window's frame changed.
   void WindowFrameChanged();
 
+  // Create the host view, adding it as a subview of |superview|.
+  void CreateWidgetHostViewIn(gfx::NativeView superview);
+
   // extensions::ExtensionView:
-  void Init() override;
   Browser* GetBrowser() override;
   gfx::NativeView GetNativeView() override;
   void ResizeDueToAutoResize(const gfx::Size& new_size) override;
@@ -67,8 +69,6 @@
  private:
   content::RenderViewHost* render_view_host() const;
 
-  void CreateWidgetHostView();
-
   // We wait to show the ExtensionView until several things have loaded.
   void ShowIfCompletelyLoaded();
 
diff --git a/chrome/browser/ui/cocoa/extensions/extension_view_mac.mm b/chrome/browser/ui/cocoa/extensions/extension_view_mac.mm
index 44d12b9..7b62cfc 100644
--- a/chrome/browser/ui/cocoa/extensions/extension_view_mac.mm
+++ b/chrome/browser/ui/cocoa/extensions/extension_view_mac.mm
@@ -38,8 +38,9 @@
     render_view_host()->GetView()->WindowFrameChanged();
 }
 
-void ExtensionViewMac::Init() {
-  CreateWidgetHostView();
+void ExtensionViewMac::CreateWidgetHostViewIn(gfx::NativeView superview) {
+  [superview addSubview:GetNativeView()];
+  extension_host_->CreateRenderViewSoon();
 }
 
 Browser* ExtensionViewMac::GetBrowser() {
@@ -89,10 +90,6 @@
   return extension_host_->render_view_host();
 }
 
-void ExtensionViewMac::CreateWidgetHostView() {
-  extension_host_->CreateRenderViewSoon();
-}
-
 void ExtensionViewMac::ShowIfCompletelyLoaded() {
   // We wait to show the ExtensionView until it has loaded, and the view has
   // actually been created. These can happen in different orders.
diff --git a/chrome/browser/ui/cocoa/passwords/manage_passwords_bubble_blacklist_view_controller.mm b/chrome/browser/ui/cocoa/passwords/manage_passwords_bubble_blacklist_view_controller.mm
index 0d6665b..e772b9bf 100644
--- a/chrome/browser/ui/cocoa/passwords/manage_passwords_bubble_blacklist_view_controller.mm
+++ b/chrome/browser/ui/cocoa/passwords/manage_passwords_bubble_blacklist_view_controller.mm
@@ -40,7 +40,7 @@
 }
 
 - (void)loadView {
-  self.view = [[[NSView alloc] initWithFrame:NSZeroRect] autorelease];
+  base::scoped_nsobject<NSView> view([[NSView alloc] initWithFrame:NSZeroRect]);
 
   // -----------------------------------
   // |  Title                          |
@@ -54,20 +54,24 @@
 
   // Title.
   NSTextField* titleLabel =
-      [self addTitleLabel:base::SysUTF16ToNSString(model_->title())];
+      [self addTitleLabel:base::SysUTF16ToNSString(model_->title())
+                   toView:view];
 
   // Blacklist explanation.
   NSTextField* explanationLabel =
-      [self addLabel:l10n_util::GetNSString(IDS_MANAGE_PASSWORDS_BLACKLISTED)];
+      [self addLabel:l10n_util::GetNSString(IDS_MANAGE_PASSWORDS_BLACKLISTED)
+              toView:view];
 
   // Done button.
   doneButton_.reset([[self addButton:l10n_util::GetNSString(IDS_DONE)
+                              toView:view
                               target:self
                               action:@selector(onDoneClicked:)] retain]);
 
   // Undo button.
   undoBlacklistButton_.reset([[self
       addButton:l10n_util::GetNSString(IDS_PASSWORD_MANAGER_UNBLACKLIST_BUTTON)
+         toView:view
          target:self
          action:@selector(onUndoBlacklistClicked:)] retain]);
 
@@ -106,7 +110,9 @@
   // Update the bubble size.
   const CGFloat height =
       NSMaxY([titleLabel frame]) + kFramePadding;
-  [self.view setFrame:NSMakeRect(0, 0, width, height)];
+  [view setFrame:NSMakeRect(0, 0, width, height)];
+
+  [self setView:view];
 }
 
 @end
diff --git a/chrome/browser/ui/cocoa/passwords/manage_passwords_bubble_confirmation_view_controller.mm b/chrome/browser/ui/cocoa/passwords/manage_passwords_bubble_confirmation_view_controller.mm
index adaa08a..109f9e9 100644
--- a/chrome/browser/ui/cocoa/passwords/manage_passwords_bubble_confirmation_view_controller.mm
+++ b/chrome/browser/ui/cocoa/passwords/manage_passwords_bubble_confirmation_view_controller.mm
@@ -47,7 +47,7 @@
 }
 
 - (void)loadView {
-  self.view = [[[NSView alloc] initWithFrame:NSZeroRect] autorelease];
+  base::scoped_nsobject<NSView> view([[NSView alloc] initWithFrame:NSZeroRect]);
 
   // -----------------------------------
   // |  Title                          |
@@ -61,7 +61,8 @@
 
   // Title.
   NSTextField* titleLabel =
-      [self addTitleLabel:base::SysUTF16ToNSString(model_->title())];
+      [self addTitleLabel:base::SysUTF16ToNSString(model_->title())
+                   toView:view];
 
   // Text.
   confirmationText_.reset([[HyperlinkTextView alloc] initWithFrame:NSZeroRect]);
@@ -94,10 +95,11 @@
   [text addAttribute:NSUnderlineStyleAttributeName
                value:[NSNumber numberWithInt:NSUnderlineStyleNone]
                range:model_->save_confirmation_link_range().ToNSRange()];
-  [[self view] addSubview:confirmationText_];
+  [view addSubview:confirmationText_];
 
   // OK button.
   okButton_.reset([[self addButton:l10n_util::GetNSString(IDS_OK)
+                            toView:view
                             target:self
                             action:@selector(onOKClicked:)] retain]);
 
@@ -120,7 +122,9 @@
 
   // Update the bubble size.
   const CGFloat height = NSMaxY([titleLabel frame]) + kFramePadding;
-  [self.view setFrame:NSMakeRect(0, 0, width, height)];
+  [view setFrame:NSMakeRect(0, 0, width, height)];
+
+  [self setView:view];
 }
 
 @end
diff --git a/chrome/browser/ui/cocoa/passwords/manage_passwords_bubble_content_view_controller.h b/chrome/browser/ui/cocoa/passwords/manage_passwords_bubble_content_view_controller.h
index b5adb70..3474f036 100644
--- a/chrome/browser/ui/cocoa/passwords/manage_passwords_bubble_content_view_controller.h
+++ b/chrome/browser/ui/cocoa/passwords/manage_passwords_bubble_content_view_controller.h
@@ -29,9 +29,12 @@
 
 // Base class for a state of the password management bubble.
 @interface ManagePasswordsBubbleContentViewController : NSViewController
-- (NSButton*)addButton:(NSString*)title target:(id)target action:(SEL)action;
-- (NSTextField*)addTitleLabel:(NSString*)title;
-- (NSTextField*)addLabel:(NSString*)title;
+- (NSButton*)addButton:(NSString*)title
+                toView:(NSView*)view
+                target:(id)target
+                action:(SEL)action;
+- (NSTextField*)addTitleLabel:(NSString*)title toView:(NSView*)view;
+- (NSTextField*)addLabel:(NSString*)title toView:(NSView*)view;
 - (void)bubbleWillDisappear;
 @end
 
diff --git a/chrome/browser/ui/cocoa/passwords/manage_passwords_bubble_content_view_controller.mm b/chrome/browser/ui/cocoa/passwords/manage_passwords_bubble_content_view_controller.mm
index 6ef68a7..0f71bb0 100644
--- a/chrome/browser/ui/cocoa/passwords/manage_passwords_bubble_content_view_controller.mm
+++ b/chrome/browser/ui/cocoa/passwords/manage_passwords_bubble_content_view_controller.mm
@@ -8,7 +8,10 @@
 
 @implementation ManagePasswordsBubbleContentViewController
 
-- (NSButton*)addButton:(NSString*)title target:(id)target action:(SEL)action {
+- (NSButton*)addButton:(NSString*)title
+                toView:(NSView*)view
+                target:(id)target
+                action:(SEL)action {
   base::scoped_nsobject<NSButton> button(
       [[NSButton alloc] initWithFrame:NSZeroRect]);
   [button setFont:[NSFont systemFontOfSize:[NSFont smallSystemFontSize]]];
@@ -18,11 +21,11 @@
   [button setTarget:target];
   [button setAction:action];
   [button sizeToFit];
-  [self.view addSubview:button.get()];
+  [view addSubview:button.get()];
   return button.autorelease();
 }
 
-- (NSTextField*)addTitleLabel:(NSString*)title {
+- (NSTextField*)addTitleLabel:(NSString*)title toView:(NSView*)view {
   base::scoped_nsobject<NSTextField> label(
       [[NSTextField alloc] initWithFrame:NSZeroRect]);
   [label setEditable:NO];
@@ -31,12 +34,12 @@
   [label setBezeled:NO];
   [label setStringValue:title];
   [label sizeToFit];
-  [self.view addSubview:label.get()];
+  [view addSubview:label.get()];
   return label.autorelease();
 }
 
-- (NSTextField*)addLabel:(NSString*)title {
-  NSTextField* label = [self addTitleLabel:title];
+- (NSTextField*)addLabel:(NSString*)title toView:(NSView*)view {
+  NSTextField* label = [self addTitleLabel:title toView:view];
   NSFont* font = [NSFont systemFontOfSize:[NSFont smallSystemFontSize]];
   [label setFont:font];
   [[label cell] setWraps:YES];
diff --git a/chrome/browser/ui/cocoa/passwords/manage_passwords_bubble_manage_view_controller.mm b/chrome/browser/ui/cocoa/passwords/manage_passwords_bubble_manage_view_controller.mm
index 4d409536..3a3a771 100644
--- a/chrome/browser/ui/cocoa/passwords/manage_passwords_bubble_manage_view_controller.mm
+++ b/chrome/browser/ui/cocoa/passwords/manage_passwords_bubble_manage_view_controller.mm
@@ -97,7 +97,7 @@
 }
 
 - (void)loadView {
-  self.view = [[[NSView alloc] initWithFrame:NSZeroRect] autorelease];
+  base::scoped_nsobject<NSView> view([[NSView alloc] initWithFrame:NSZeroRect]);
 
   // -----------------------------------
   // |  Title                          |
@@ -117,7 +117,8 @@
 
   // Create the elements and add them to the view.
   NSTextField* titleLabel =
-      [self addTitleLabel:base::SysUTF16ToNSString(model_->title())];
+      [self addTitleLabel:base::SysUTF16ToNSString(model_->title())
+                   toView:view];
 
   // Content. If we have a list of passwords to store for the current site, we
   // display them to the user for management. Otherwise, we show a "No passwords
@@ -131,11 +132,12 @@
     contentView_.reset(
         [[PasswordItemListView alloc] initWithModel:model_]);
   }
-  [self.view addSubview:contentView_];
+  [view addSubview:contentView_];
   DCHECK_GE(NSWidth([contentView_ frame]), NSWidth([titleLabel frame]));
 
   // Done button.
   doneButton_.reset([[self addButton:l10n_util::GetNSString(IDS_DONE)
+                              toView:view
                               target:self
                               action:@selector(onDoneClicked:)] retain]);
 
@@ -152,7 +154,7 @@
   [manageButton_ sizeToFit];
   [manageButton_ setTarget:self];
   [manageButton_ setAction:@selector(onManageClicked:)];
-  [self.view addSubview:manageButton_];
+  [view addSubview:manageButton_];
 
   // Layout the elements, starting at the bottom and moving up.
 
@@ -181,7 +183,9 @@
   curX = NSMaxX([contentView_ frame]) + kFramePadding;
   curY = NSMaxY([titleLabel frame]) + kFramePadding;
   DCHECK_EQ(width, curX);
-  [self.view setFrameSize:NSMakeSize(curX, curY)];
+  [view setFrameSize:NSMakeSize(curX, curY)];
+
+  [self setView:view];
 }
 
 - (void)onDoneClicked:(id)sender {
diff --git a/chrome/browser/ui/cocoa/passwords/manage_passwords_bubble_never_save_view_controller.mm b/chrome/browser/ui/cocoa/passwords/manage_passwords_bubble_never_save_view_controller.mm
index d4a6e53..305b7135 100644
--- a/chrome/browser/ui/cocoa/passwords/manage_passwords_bubble_never_save_view_controller.mm
+++ b/chrome/browser/ui/cocoa/passwords/manage_passwords_bubble_never_save_view_controller.mm
@@ -38,7 +38,7 @@
 }
 
 - (void)loadView {
-  self.view = [[[NSView alloc] initWithFrame:NSZeroRect] autorelease];
+  base::scoped_nsobject<NSView> view([[NSView alloc] initWithFrame:NSZeroRect]);
 
   // -----------------------------------
   // |  Title                          |
@@ -51,24 +51,28 @@
   // Create the elements and add them to the view.
 
   // Title.
-  NSTextField* titleLabel = [self
-      addTitleLabel:l10n_util::GetNSString(
-          IDS_MANAGE_PASSWORDS_BLACKLIST_CONFIRMATION_TITLE)];
+  NSTextField* titleLabel =
+      [self addTitleLabel:l10n_util::GetNSString(
+                              IDS_MANAGE_PASSWORDS_BLACKLIST_CONFIRMATION_TITLE)
+                   toView:view];
 
   // Blacklist confirmation.
-  NSTextField* confirmationLabel = [self
-      addLabel:l10n_util::GetNSString(
-          IDS_MANAGE_PASSWORDS_BLACKLIST_CONFIRMATION_TEXT)];
+  NSTextField* confirmationLabel =
+      [self addLabel:l10n_util::GetNSString(
+                         IDS_MANAGE_PASSWORDS_BLACKLIST_CONFIRMATION_TEXT)
+              toView:view];
 
   // Undo button.
   undoButton_.reset([[self addButton:l10n_util::GetNSString(IDS_CANCEL)
+                              toView:view
                               target:self
                               action:@selector(onUndoClicked:)] retain]);
 
   // Confirm button.
   confirmButton_.reset(
       [[self addButton:l10n_util::GetNSString(
-        IDS_MANAGE_PASSWORDS_BLACKLIST_CONFIRMATION_BUTTON)
+                           IDS_MANAGE_PASSWORDS_BLACKLIST_CONFIRMATION_BUTTON)
+                toView:view
                 target:self
                 action:@selector(onConfirmClicked:)] retain]);
 
@@ -109,7 +113,9 @@
 
   // Update the bubble size.
   const CGFloat height = NSMaxY([titleLabel frame]) + kFramePadding;
-  [self.view setFrame:NSMakeRect(0, 0, width, height)];
+  [view setFrame:NSMakeRect(0, 0, width, height)];
+
+  [self setView:view];
 }
 
 @end
diff --git a/chrome/browser/ui/cocoa/passwords/manage_passwords_bubble_pending_view_controller.mm b/chrome/browser/ui/cocoa/passwords/manage_passwords_bubble_pending_view_controller.mm
index 372fbdc..a3b3ff14 100644
--- a/chrome/browser/ui/cocoa/passwords/manage_passwords_bubble_pending_view_controller.mm
+++ b/chrome/browser/ui/cocoa/passwords/manage_passwords_bubble_pending_view_controller.mm
@@ -63,7 +63,7 @@
 }
 
 - (void)loadView {
-  self.view = [[[NSView alloc] initWithFrame:NSZeroRect] autorelease];
+  base::scoped_nsobject<NSView> view([[NSView alloc] initWithFrame:NSZeroRect]);
 
   // -----------------------------------
   // |  Title                          |
@@ -81,7 +81,8 @@
 
   // Title.
   NSTextField* titleLabel =
-      [self addTitleLabel:base::SysUTF16ToNSString(model_->title())];
+      [self addTitleLabel:base::SysUTF16ToNSString(model_->title())
+                   toView:view];
 
   // Password item.
   // It should be at least as wide as the box without the padding.
@@ -90,11 +91,12 @@
        passwordForm:model_->pending_password()
            position:password_manager::ui::FIRST_ITEM]);
   NSView* password = [passwordItem_ view];
-  [self.view addSubview:password];
+  [view addSubview:password];
 
   // Save button.
   saveButton_.reset(
       [[self addButton:l10n_util::GetNSString(IDS_PASSWORD_MANAGER_SAVE_BUTTON)
+                toView:view
                 target:self
                 action:@selector(onSaveClicked:)] retain]);
 
@@ -120,7 +122,7 @@
       setTitle:l10n_util::GetNSString(IDS_PASSWORD_MANAGER_CANCEL_BUTTON)];
 
   [nopeButton_ sizeToFit];
-  [self.view addSubview:nopeButton_.get()];
+  [view addSubview:nopeButton_.get()];
 
   // Compute the bubble width using the password item.
   const CGFloat contentWidth =
@@ -152,7 +154,9 @@
 
   // Update the bubble size.
   const CGFloat height = NSMaxY([titleLabel frame]) + kFramePadding;
-  [self.view setFrame:NSMakeRect(0, 0, width, height)];
+  [view setFrame:NSMakeRect(0, 0, width, height)];
+
+  [self setView:view];
 }
 
 @end
diff --git a/chrome/browser/ui/elide_url.cc b/chrome/browser/ui/elide_url.cc
index 0c1d996..88b393c 100644
--- a/chrome/browser/ui/elide_url.cc
+++ b/chrome/browser/ui/elide_url.cc
@@ -205,17 +205,15 @@
 
   // Get filename - note that for a path ending with /
   // such as www.google.com/intl/ads/, the file name is ads/.
+  base::string16 url_filename(
+      url_path_elements.empty() ? base::string16() : url_path_elements.back());
   size_t url_path_number_of_elements = url_path_elements.size();
-  DCHECK(url_path_number_of_elements != 0);
-  base::string16 url_filename;
-  if ((url_path_elements.at(url_path_number_of_elements - 1)).length() > 0) {
-    url_filename = *(url_path_elements.end() - 1);
-  } else if (url_path_number_of_elements > 1) {  // Path ends with a '/'.
-    url_filename = url_path_elements.at(url_path_number_of_elements - 2) +
+  if (url_filename.empty() && (url_path_number_of_elements > 1)) {
+    // Path ends with a '/'.
+    --url_path_number_of_elements;
+    url_filename = url_path_elements[url_path_number_of_elements - 1] +
         kForwardSlash;
-    url_path_number_of_elements--;
   }
-  DCHECK(url_path_number_of_elements != 0);
 
   const size_t kMaxNumberOfUrlPathElementsAllowed = 1024;
   if (url_path_number_of_elements <= 1 ||
diff --git a/chrome/browser/ui/elide_url_unittest.cc b/chrome/browser/ui/elide_url_unittest.cc
index 56826ead..a2cec9e 100644
--- a/chrome/browser/ui/elide_url_unittest.cc
+++ b/chrome/browser/ui/elide_url_unittest.cc
@@ -156,6 +156,12 @@
     {"file://filer/foo/bar/file", "filer/foo/bar/file"},
     {"file://filer/foo/bar/file", "filer/foo/" + kEllipsisStr + "/file"},
     {"file://filer/foo/bar/file", "filer/" + kEllipsisStr + "/file"},
+    {"file://filer/foo/", "file://filer/foo/"},
+    {"file://filer/foo/", "filer/foo/"},
+    {"file://filer/foo/", "filer" + kEllipsisStr},
+    // Eliding file URLs with nothing after the ':' shouldn't crash.
+    {"file:///aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:", "aaa" + kEllipsisStr},
+    {"file:///aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:/", "aaa" + kEllipsisStr},
   };
 
   RunUrlTest(testcases, arraysize(testcases));
diff --git a/chrome/browser/ui/exclusive_access/fullscreen_controller_browsertest.cc b/chrome/browser/ui/exclusive_access/fullscreen_controller_browsertest.cc
index ffa33d1..88e6e5d5 100644
--- a/chrome/browser/ui/exclusive_access/fullscreen_controller_browsertest.cc
+++ b/chrome/browser/ui/exclusive_access/fullscreen_controller_browsertest.cc
@@ -10,6 +10,7 @@
 #include "chrome/browser/ui/exclusive_access/fullscreen_controller_test.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/test/base/ui_test_utils.h"
+#include "components/content_settings/core/browser/host_content_settings_map.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/url_constants.h"
 
@@ -91,3 +92,57 @@
   ASSERT_TRUE(IsFullscreenBubbleDisplayed());
   ASSERT_TRUE(IsFullscreenBubbleDisplayingButtons());
 }
+
+IN_PROC_BROWSER_TEST_F(FullscreenControllerTest, PermissionContentSettings) {
+  GURL url = test_server()->GetURL(kFullscreenMouseLockHTML);
+  ASSERT_TRUE(test_server()->Start());
+  ui_test_utils::NavigateToURL(browser(), url);
+
+  EXPECT_FALSE(browser()->window()->IsFullscreen());
+
+  // The content's origin is not allowed to go fullscreen.
+  EXPECT_EQ(
+      CONTENT_SETTING_ASK,
+      browser()->profile()->GetHostContentSettingsMap()->GetContentSetting(
+          url.GetOrigin(),
+          url.GetOrigin(),
+          CONTENT_SETTINGS_TYPE_FULLSCREEN,
+          std::string()));
+
+  GetFullscreenController()->EnterFullscreenModeForTab(
+      browser()->tab_strip_model()->GetActiveWebContents(), url.GetOrigin());
+  EXPECT_TRUE(IsFullscreenBubbleDisplayed());
+
+  // The content's origin is still not allowed to go fullscreen.
+  EXPECT_EQ(
+      CONTENT_SETTING_ASK,
+      browser()->profile()->GetHostContentSettingsMap()->GetContentSetting(
+          url.GetOrigin(),
+          url.GetOrigin(),
+          CONTENT_SETTINGS_TYPE_FULLSCREEN,
+          std::string()));
+
+  AcceptCurrentFullscreenOrMouseLockRequest();
+
+  // The content's origin is allowed to go fullscreen.
+  EXPECT_EQ(
+      CONTENT_SETTING_ALLOW,
+      browser()->profile()->GetHostContentSettingsMap()->GetContentSetting(
+          url.GetOrigin(),
+          url.GetOrigin(),
+          CONTENT_SETTINGS_TYPE_FULLSCREEN,
+          std::string()));
+
+  // The primary and secondary patterns have been set when setting the
+  // permission, thus setting another secondary pattern shouldn't work.
+  EXPECT_EQ(
+      CONTENT_SETTING_ASK,
+      browser()->profile()->GetHostContentSettingsMap()->GetContentSetting(
+          url.GetOrigin(),
+          GURL("https://test.com"),
+          CONTENT_SETTINGS_TYPE_FULLSCREEN,
+          std::string()));
+
+  browser()->profile()->GetHostContentSettingsMap()->ClearSettingsForOneType(
+      CONTENT_SETTINGS_TYPE_FULLSCREEN);
+}
diff --git a/chrome/browser/ui/find_bar/find_bar_host_interactive_uitest.cc b/chrome/browser/ui/find_bar/find_bar_host_interactive_uitest.cc
index da3581d6..9b890cc 100644
--- a/chrome/browser/ui/find_bar/find_bar_host_interactive_uitest.cc
+++ b/chrome/browser/ui/find_bar/find_bar_host_interactive_uitest.cc
@@ -60,7 +60,13 @@
 // This tests the FindInPage end-state, in other words: what is focused when you
 // close the Find box (ie. if you find within a link the link should be
 // focused).
-IN_PROC_BROWSER_TEST_F(FindInPageInteractiveTest, FindInPageEndState) {
+// Flaky on CrOS.
+#if defined(OS_CHROMEOS)
+#define MAYBE_FindInPageEndState DISABLED_FindInPageEndState
+#else
+#define MAYBE_FindInPageEndState FindInPageEndState
+#endif
+IN_PROC_BROWSER_TEST_F(FindInPageInteractiveTest, MAYBE_FindInPageEndState) {
   ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
 
   // Make sure Chrome is in the foreground, otherwise sending input
diff --git a/chrome/browser/ui/passwords/manage_passwords_bubble_model.cc b/chrome/browser/ui/passwords/manage_passwords_bubble_model.cc
index d80c04f..fe5d7697 100644
--- a/chrome/browser/ui/passwords/manage_passwords_bubble_model.cc
+++ b/chrome/browser/ui/passwords/manage_passwords_bubble_model.cc
@@ -120,12 +120,15 @@
 
   origin_ = controller->origin();
   state_ = controller->state();
-  if (state_ == password_manager::ui::PENDING_PASSWORD_STATE)
+  if (state_ == password_manager::ui::PENDING_PASSWORD_STATE) {
     pending_password_ = controller->PendingPassword();
-  if (state_ == password_manager::ui::CREDENTIAL_REQUEST_STATE) {
+    best_matches_ = controller->best_matches();
+  } else if (state_ == password_manager::ui::CREDENTIAL_REQUEST_STATE) {
     local_pending_credentials_.swap(controller->local_credentials_forms());
     federated_pending_credentials_.swap(
         controller->federated_credentials_forms());
+  } else if (state_ == password_manager::ui::AUTO_SIGNIN_STATE) {
+    pending_password_ = *controller->local_credentials_forms()[0];
   } else {
     best_matches_ = controller->best_matches();
   }
@@ -139,6 +142,8 @@
         l10n_util::GetStringUTF16(IDS_MANAGE_PASSWORDS_CONFIRM_GENERATED_TITLE);
   } else if (state_ == password_manager::ui::CREDENTIAL_REQUEST_STATE) {
     title_ = l10n_util::GetStringUTF16(IDS_MANAGE_PASSWORDS_CHOOSE_TITLE);
+  } else if (state_ == password_manager::ui::AUTO_SIGNIN_STATE) {
+    // There is no title.
   } else if (password_manager::ui::IsAskSubmitURLState(state_)) {
     title_ =
         l10n_util::GetStringUTF16(IDS_MANAGE_PASSWORDS_ASK_TO_SUBMIT_URL_TITLE);
@@ -177,6 +182,8 @@
           metrics_util::AUTOMATIC_GENERATED_PASSWORD_CONFIRMATION;
     } else if (state_ == password_manager::ui::CREDENTIAL_REQUEST_STATE) {
       display_disposition_ = metrics_util::AUTOMATIC_CREDENTIAL_REQUEST;
+    } else if (state_ == password_manager::ui::AUTO_SIGNIN_STATE) {
+      display_disposition_ = metrics_util::AUTOMATIC_SIGNIN_TOAST;
     } else {
       display_disposition_ = metrics_util::AUTOMATIC_WITH_PASSWORD_PENDING;
     }
@@ -225,13 +232,13 @@
 
   metrics_util::LogUIDismissalReason(dismissal_reason_);
   // Other use cases have been reported in the callbacks like OnSaveClicked().
-  if (dismissal_reason_ == metrics_util::NO_DIRECT_INTERACTION)
+  if (state_ == password_manager::ui::PENDING_PASSWORD_STATE &&
+      dismissal_reason_ == metrics_util::NO_DIRECT_INTERACTION)
     RecordExperimentStatistics(web_contents(), dismissal_reason_);
 }
 
 void ManagePasswordsBubbleModel::OnCollectURLClicked(const std::string& url) {
   dismissal_reason_ = metrics_util::CLICKED_COLLECT_URL;
-  RecordExperimentStatistics(web_contents(), dismissal_reason_);
   // User interaction with bubble has happened, do not need to show bubble
   // in case it was before transition to another page.
   state_ = password_manager::ui::ASK_USER_REPORT_URL_BUBBLE_SHOWN_STATE;
@@ -244,7 +251,6 @@
 
 void ManagePasswordsBubbleModel::OnDoNotCollectURLClicked() {
   dismissal_reason_ = metrics_util::CLICKED_DO_NOT_COLLECT_URL;
-  RecordExperimentStatistics(web_contents(), dismissal_reason_);
   // User interaction with bubble has happened, do not need to show bubble
   // in case it was before transition to another page.
   state_ = password_manager::ui::ASK_USER_REPORT_URL_BUBBLE_SHOWN_STATE;
@@ -309,6 +315,10 @@
       ->NavigateToPasswordManagerSettingsPage();
 }
 
+void ManagePasswordsBubbleModel::OnAutoSignInToastTimeout() {
+  dismissal_reason_ = metrics_util::AUTO_SIGNIN_TOAST_TIMEOUT;
+}
+
 void ManagePasswordsBubbleModel::OnPasswordAction(
     const autofill::PasswordForm& password_form,
     PasswordAction action) {
diff --git a/chrome/browser/ui/passwords/manage_passwords_bubble_model.h b/chrome/browser/ui/passwords/manage_passwords_bubble_model.h
index 1c3693e..4651b00 100644
--- a/chrome/browser/ui/passwords/manage_passwords_bubble_model.h
+++ b/chrome/browser/ui/passwords/manage_passwords_bubble_model.h
@@ -80,6 +80,9 @@
   // Called by the view code when the manage link is clicked by the user.
   void OnManageLinkClicked();
 
+  // Called by the view code when the auto-signin toast is about to close.
+  void OnAutoSignInToastTimeout();
+
   // Called by the view code to delete or add a password form to the
   // PasswordStore.
   void OnPasswordAction(const autofill::PasswordForm& password_form,
diff --git a/chrome/browser/ui/passwords/manage_passwords_bubble_model_unittest.cc b/chrome/browser/ui/passwords/manage_passwords_bubble_model_unittest.cc
index e039594..94d0ce8 100644
--- a/chrome/browser/ui/passwords/manage_passwords_bubble_model_unittest.cc
+++ b/chrome/browser/ui/passwords/manage_passwords_bubble_model_unittest.cc
@@ -79,6 +79,12 @@
     controller()->SetState(password_manager::ui::CREDENTIAL_REQUEST_STATE);
   }
 
+  void PretendAutoSigningIn() {
+    model_->set_state(password_manager::ui::AUTO_SIGNIN_STATE);
+    model_->OnBubbleShown(ManagePasswordsBubble::AUTOMATIC);
+    controller()->SetState(password_manager::ui::AUTO_SIGNIN_STATE);
+  }
+
   void PretendManagingPasswords() {
     model_->set_state(password_manager::ui::MANAGE_STATE);
     model_->OnBubbleShown(ManagePasswordsBubble::USER_ACTION);
@@ -402,3 +408,17 @@
       password_manager::metrics_util::NO_DIRECT_INTERACTION,
       1);
 }
+
+TEST_F(ManagePasswordsBubbleModelTest, PopupAutoSigninToast) {
+  base::HistogramTester histogram_tester;
+  PretendAutoSigningIn();
+  model_->OnAutoSignInToastTimeout();
+  model_->OnBubbleHidden();
+  EXPECT_EQ(model_->dismissal_reason(),
+            password_manager::metrics_util::AUTO_SIGNIN_TOAST_TIMEOUT);
+
+  histogram_tester.ExpectUniqueSample(
+      kUIDismissalReasonMetric,
+      password_manager::metrics_util::AUTO_SIGNIN_TOAST_TIMEOUT,
+      1);
+}
diff --git a/chrome/browser/ui/passwords/manage_passwords_test.cc b/chrome/browser/ui/passwords/manage_passwords_test.cc
index b80e50e..1a1f3646 100644
--- a/chrome/browser/ui/passwords/manage_passwords_test.cc
+++ b/chrome/browser/ui/passwords/manage_passwords_test.cc
@@ -21,6 +21,12 @@
 #include "components/password_manager/core/browser/stub_password_manager_driver.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+ManagePasswordsTest::ManagePasswordsTest() {
+}
+
+ManagePasswordsTest::~ManagePasswordsTest() {
+}
+
 void ManagePasswordsTest::SetUpOnMainThread() {
   AddTabAtIndex(0, GURL("http://example.com/"), ui::PAGE_TRANSITION_TYPED);
 }
diff --git a/chrome/browser/ui/passwords/manage_passwords_test.h b/chrome/browser/ui/passwords/manage_passwords_test.h
index 4baa7c0..910d1a5 100644
--- a/chrome/browser/ui/passwords/manage_passwords_test.h
+++ b/chrome/browser/ui/passwords/manage_passwords_test.h
@@ -22,8 +22,8 @@
 // to poke at the bubble, icon, and controller's state.
 class ManagePasswordsTest : public InProcessBrowserTest {
  public:
-  ManagePasswordsTest() = default;
-  ~ManagePasswordsTest() = default;
+  ManagePasswordsTest();
+  ~ManagePasswordsTest();
 
   // InProcessBrowserTest:
   void SetUpOnMainThread() override;
diff --git a/chrome/browser/ui/passwords/manage_passwords_ui_controller.cc b/chrome/browser/ui/passwords/manage_passwords_ui_controller.cc
index e831643c..1d5ae15 100644
--- a/chrome/browser/ui/passwords/manage_passwords_ui_controller.cc
+++ b/chrome/browser/ui/passwords/manage_passwords_ui_controller.cc
@@ -25,6 +25,8 @@
 
 #if defined(OS_ANDROID)
 #include "chrome/browser/android/chromium_application.h"
+#include "chrome/browser/infobars/infobar_service.h"
+#include "chrome/browser/password_manager/account_chooser_infobar_delegate_android.h"
 #endif
 
 using autofill::PasswordFormMap;
@@ -95,6 +97,15 @@
 #endif
 }
 
+void ManagePasswordsUIController::
+    UpdateAndroidAccountChooserInfoBarVisibility() {
+#if defined(OS_ANDROID)
+  AccountChooserInfoBarDelegateAndroid::Create(
+      InfoBarService::FromWebContents(web_contents()), this);
+  should_pop_up_bubble_ = false;
+#endif
+}
+
 base::TimeDelta ManagePasswordsUIController::Elapsed() const {
   return timer_ ? timer_->Elapsed() : base::TimeDelta::Max();
 }
@@ -124,15 +135,15 @@
     const GURL& origin,
     base::Callback<void(const password_manager::CredentialInfo&)> callback) {
   DCHECK(!local_credentials.empty() || !federated_credentials.empty());
-  form_manager_.reset();
+  SaveForms(local_credentials.Pass(), federated_credentials.Pass());
   origin_ = origin;
-  local_credentials_forms_.swap(local_credentials);
-  federated_credentials_forms_.swap(federated_credentials);
-  // The map is useless because usernames may overlap.
-  password_form_map_.clear();
   SetState(password_manager::ui::CREDENTIAL_REQUEST_STATE);
   base::AutoReset<bool> resetter(&should_pop_up_bubble_, true);
+#if defined(OS_ANDROID)
+  UpdateAndroidAccountChooserInfoBarVisibility();
+#else
   UpdateBubbleAndIconVisibility();
+#endif
   if (!should_pop_up_bubble_) {
     credentials_callback_ = callback;
     return true;
@@ -140,6 +151,17 @@
   return false;
 }
 
+void ManagePasswordsUIController::OnAutoSignin(
+    ScopedVector<autofill::PasswordForm> local_forms) {
+  DCHECK(!local_forms.empty());
+  SaveForms(local_forms.Pass(), ScopedVector<autofill::PasswordForm>());
+  origin_ = local_credentials_forms_[0]->origin;
+  SetState(password_manager::ui::AUTO_SIGNIN_STATE);
+  timer_.reset(new base::ElapsedTimer);
+  base::AutoReset<bool> resetter(&should_pop_up_bubble_, true);
+  UpdateBubbleAndIconVisibility();
+}
+
 void ManagePasswordsUIController::OnAutomaticPasswordSave(
     scoped_ptr<PasswordFormManager> form_manager) {
   form_manager_ = form_manager.Pass();
@@ -318,6 +340,8 @@
   // Otherwise, reset the password manager and the timer.
   SetState(password_manager::ui::INACTIVE_STATE);
   UpdateBubbleAndIconVisibility();
+  // This allows the bubble to survive several redirects in case the whole
+  // process of navigating to the landing page is longer than 1 second.
   timer_.reset(new base::ElapsedTimer());
 }
 
@@ -368,6 +392,8 @@
     next_state = password_manager::ui::INACTIVE_STATE;
   else if (state_ == password_manager::ui::CONFIRMATION_STATE)
     next_state = password_manager::ui::MANAGE_STATE;
+  else if (state_ == password_manager::ui::AUTO_SIGNIN_STATE)
+    next_state = password_manager::ui::INACTIVE_STATE;
 
   if (next_state != state_) {
     SetState(next_state);
@@ -381,6 +407,10 @@
   Browser* browser = chrome::FindBrowserWithWebContents(web_contents());
   if (!browser || browser->toolbar_model()->input_in_progress())
     return;
+  if (state_ == password_manager::ui::PENDING_PASSWORD_STATE &&
+      !password_bubble_experiment::ShouldShowBubble(
+          browser->profile()->GetPrefs()))
+    return;
   CommandUpdater* updater = browser->command_controller()->command_updater();
   updater->ExecuteCommand(IDC_MANAGE_PASSWORDS_FOR_PAGE);
 #endif
@@ -396,3 +426,15 @@
   if (password_store)
     password_store->RemoveObserver(this);
 }
+
+void ManagePasswordsUIController::SaveForms(
+    ScopedVector<autofill::PasswordForm> local_forms,
+    ScopedVector<autofill::PasswordForm> federated_forms) {
+  form_manager_.reset();
+  origin_ = GURL();
+  local_credentials_forms_.swap(local_forms);
+  federated_credentials_forms_.swap(federated_forms);
+  // The map is useless because usernames may overlap.
+  password_form_map_.clear();
+  new_password_forms_.clear();
+}
diff --git a/chrome/browser/ui/passwords/manage_passwords_ui_controller.h b/chrome/browser/ui/passwords/manage_passwords_ui_controller.h
index adc7d60..1b02a01 100644
--- a/chrome/browser/ui/passwords/manage_passwords_ui_controller.h
+++ b/chrome/browser/ui/passwords/manage_passwords_ui_controller.h
@@ -54,6 +54,10 @@
       const GURL& origin,
       base::Callback<void(const password_manager::CredentialInfo&)> callback);
 
+  // Called when user is auto signed in to the site. |local_forms[0]| contains
+  // the credential returned to the site.
+  void OnAutoSignin(ScopedVector<autofill::PasswordForm> local_forms);
+
   // Called when the password will be saved automatically, but we still wish to
   // visually inform the user that the save has occured.
   void OnAutomaticPasswordSave(
@@ -160,20 +164,6 @@
   // Sets |state_|. Protected so we can manipulate the value in tests.
   void SetState(password_manager::ui::State state);
 
-  // We create copies of PasswordForm objects that come in with unclear lifetime
-  // and store them in this vector as well as in |password_form_map_| to ensure
-  // that we destroy them correctly. If |new_password_forms_| gets cleared then
-  // |password_form_map_| is to be cleared too.
-  ScopedVector<autofill::PasswordForm> new_password_forms_;
-
-  // Federated credentials. Stores federated credentials which will be shown
-  // when Credential Management API was used.
-  ScopedVector<autofill::PasswordForm> federated_credentials_forms_;
-
-  // Local credentials. Stores local credentials which will be shown
-  // when Credential Management API was used.
-  ScopedVector<autofill::PasswordForm> local_credentials_forms_;
-
   // All previously stored credentials for a specific site.
   // Protected, not private, so we can mess with the value in
   // ManagePasswordsUIControllerMock.
@@ -188,6 +178,14 @@
   // content::WebContentsObserver:
   void WebContentsDestroyed() override;
 
+  // Saves the parameters and clean the previous forms.
+  void SaveForms(ScopedVector<autofill::PasswordForm> local_forms,
+                 ScopedVector<autofill::PasswordForm> federated_forms);
+
+  // Shows infobar which allows user to choose credentials. Placing this
+  // code to separate method allows mocking.
+  virtual void UpdateAndroidAccountChooserInfoBarVisibility();
+
   // The current state of the password manager UI.
   password_manager::ui::State state_;
 
@@ -202,6 +200,20 @@
   // associated login information in Chrome's password store.
   scoped_ptr<password_manager::PasswordFormManager> form_manager_;
 
+  // We create copies of PasswordForm objects that come in with unclear lifetime
+  // and store them in this vector as well as in |password_form_map_| to ensure
+  // that we destroy them correctly. If |new_password_forms_| gets cleared then
+  // |password_form_map_| is to be cleared too.
+  ScopedVector<autofill::PasswordForm> new_password_forms_;
+
+  // Federated credentials. Stores federated credentials which will be shown
+  // when Credential Management API was used.
+  ScopedVector<autofill::PasswordForm> federated_credentials_forms_;
+
+  // Local credentials. Stores local credentials which will be shown
+  // when Credential Management API was used.
+  ScopedVector<autofill::PasswordForm> local_credentials_forms_;
+
   // A callback to be invoked when user selects a credential.
   base::Callback<void(const password_manager::CredentialInfo&)>
       credentials_callback_;
diff --git a/chrome/browser/ui/passwords/manage_passwords_ui_controller_mock.cc b/chrome/browser/ui/passwords/manage_passwords_ui_controller_mock.cc
index 5c62a52..6b4bbe6 100644
--- a/chrome/browser/ui/passwords/manage_passwords_ui_controller_mock.cc
+++ b/chrome/browser/ui/passwords/manage_passwords_ui_controller_mock.cc
@@ -44,6 +44,11 @@
   OnBubbleShown();
 }
 
+void ManagePasswordsUIControllerMock::
+    UpdateAndroidAccountChooserInfoBarVisibility() {
+  OnBubbleShown();
+}
+
 base::TimeDelta ManagePasswordsUIControllerMock::Elapsed() const {
   return elapsed_;
 }
diff --git a/chrome/browser/ui/passwords/manage_passwords_ui_controller_mock.h b/chrome/browser/ui/passwords/manage_passwords_ui_controller_mock.h
index 8f741f3f..4575c72 100644
--- a/chrome/browser/ui/passwords/manage_passwords_ui_controller_mock.h
+++ b/chrome/browser/ui/passwords/manage_passwords_ui_controller_mock.h
@@ -53,6 +53,8 @@
 
   void UpdateBubbleAndIconVisibility() override;
 
+  void UpdateAndroidAccountChooserInfoBarVisibility() override;
+
   base::TimeDelta Elapsed() const override;
 
   // Sneaky setters for testing.
diff --git a/chrome/browser/ui/passwords/manage_passwords_ui_controller_unittest.cc b/chrome/browser/ui/passwords/manage_passwords_ui_controller_unittest.cc
index 0971467..ea97c618 100644
--- a/chrome/browser/ui/passwords/manage_passwords_ui_controller_unittest.cc
+++ b/chrome/browser/ui/passwords/manage_passwords_ui_controller_unittest.cc
@@ -451,6 +451,24 @@
             credential_info()->type);
 }
 
+TEST_F(ManagePasswordsUIControllerTest, AutoSignin) {
+  ScopedVector<autofill::PasswordForm> local_credentials;
+  local_credentials.push_back(new autofill::PasswordForm(test_local_form()));
+  controller()->OnAutoSignin(local_credentials.Pass());
+  EXPECT_EQ(password_manager::ui::AUTO_SIGNIN_STATE, controller()->state());
+  EXPECT_EQ(test_local_form().origin, controller()->origin());
+  ASSERT_FALSE(controller()->local_credentials_forms().empty());
+  EXPECT_EQ(test_local_form(), *controller()->local_credentials_forms()[0]);
+  ManagePasswordsIconMock mock;
+  controller()->UpdateIconAndBubbleState(&mock);
+  EXPECT_EQ(password_manager::ui::AUTO_SIGNIN_STATE, mock.state());
+
+  controller()->OnBubbleHidden();
+  EXPECT_EQ(password_manager::ui::INACTIVE_STATE, controller()->state());
+  controller()->UpdateIconAndBubbleState(&mock);
+  EXPECT_EQ(password_manager::ui::INACTIVE_STATE, mock.state());
+}
+
 TEST_F(ManagePasswordsUIControllerTest, InactiveOnPSLMatched) {
   base::string16 kTestUsername = base::ASCIIToUTF16("test_username");
   autofill::PasswordFormMap map;
diff --git a/chrome/browser/ui/passwords/password_manager_presenter.cc b/chrome/browser/ui/passwords/password_manager_presenter.cc
index f74241d..de2a4c7f 100644
--- a/chrome/browser/ui/passwords/password_manager_presenter.cc
+++ b/chrome/browser/ui/passwords/password_manager_presenter.cc
@@ -132,6 +132,7 @@
       return;
   }
 
+#if defined(PASSWORD_MANAGER_ENABLE_SYNC)
   if (password_manager_sync_metrics::IsSyncAccountCredential(
           password_view_->GetProfile(),
           base::UTF16ToUTF8(password_list_[index]->username_value),
@@ -139,6 +140,7 @@
     content::RecordAction(
         base::UserMetricsAction("PasswordManager_SyncCredentialShown"));
   }
+#endif
 
   // Call back the front end to reveal the password.
   password_view_->ShowPassword(index, password_list_[index]->password_value);
diff --git a/chrome/browser/ui/platform_keys_certificate_selector_chromeos.h b/chrome/browser/ui/platform_keys_certificate_selector_chromeos.h
new file mode 100644
index 0000000..cfee8bc
--- /dev/null
+++ b/chrome/browser/ui/platform_keys_certificate_selector_chromeos.h
@@ -0,0 +1,44 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_PLATFORM_KEYS_CERTIFICATE_SELECTOR_CHROMEOS_H_
+#define CHROME_BROWSER_UI_PLATFORM_KEYS_CERTIFICATE_SELECTOR_CHROMEOS_H_
+
+#include <string>
+#include <vector>
+
+#include "base/callback_forward.h"
+#include "base/memory/ref_counted.h"
+
+namespace content {
+class WebContents;
+}
+
+namespace net {
+class X509Certificate;
+typedef std::vector<scoped_refptr<X509Certificate>> CertificateList;
+}
+
+namespace chromeos {
+
+typedef base::Callback<void(
+    const scoped_refptr<net::X509Certificate>& selected_certificate)>
+    CertificateSelectedCallback;
+
+// Opens a constrained client certificate selection dialog associated with
+// |web_contents|, offering |certificates| to the user and explaining that the
+// selection will grant access to the extension with |extension_id|.
+// When the user has made a selection, the dialog will report back to
+// |callback|. |callback| is notified when the dialog closes in call cases; if
+// the user cancels the dialog, |callback| will be called with a nullptr
+// argument.
+void ShowPlatformKeysCertificateSelector(
+    content::WebContents* web_contents,
+    const std::string& extension_id,
+    const net::CertificateList& certificates,
+    const CertificateSelectedCallback& callback);
+
+}  // namespace chromeos
+
+#endif  // CHROME_BROWSER_UI_PLATFORM_KEYS_CERTIFICATE_SELECTOR_CHROMEOS_H_
diff --git a/chrome/browser/ui/search/search_tab_helper.cc b/chrome/browser/ui/search/search_tab_helper.cc
index d78a8cf..3c4af974 100644
--- a/chrome/browser/ui/search/search_tab_helper.cc
+++ b/chrome/browser/ui/search/search_tab_helper.cc
@@ -123,14 +123,15 @@
   core_tab_helper->set_new_tab_start_time(base::TimeTicks());
 }
 
-// Returns true if the user is signed in and full history sync is enabled,
-// and false otherwise.
+// Returns true if the user wants to sync history. This function returning true
+// is not a guarantee that history is being synced, but it can be used to
+// disable a feature that should not be shown to users who prefer not to sync
+// their history.
 bool IsHistorySyncEnabled(Profile* profile) {
   ProfileSyncService* sync =
       ProfileSyncServiceFactory::GetInstance()->GetForProfile(profile);
   return sync &&
-      sync->SyncActive() &&
-      sync->GetActiveDataTypes().Has(syncer::HISTORY_DELETE_DIRECTIVES);
+      sync->GetPreferredDataTypes().Has(syncer::HISTORY_DELETE_DIRECTIVES);
 }
 
 bool OmniboxHasFocus(OmniboxView* omnibox) {
diff --git a/chrome/browser/ui/search/search_tab_helper_unittest.cc b/chrome/browser/ui/search/search_tab_helper_unittest.cc
index 2d68d46..7d19296 100644
--- a/chrome/browser/ui/search/search_tab_helper_unittest.cc
+++ b/chrome/browser/ui/search/search_tab_helper_unittest.cc
@@ -106,12 +106,11 @@
     ProfileSyncServiceMock* sync_service = static_cast<ProfileSyncServiceMock*>(
         ProfileSyncServiceFactory::GetInstance()->GetForProfile(profile()));
 
-    EXPECT_CALL(*sync_service, SyncActive()).WillRepeatedly(Return(true));
     syncer::ModelTypeSet result;
     if (sync_history) {
       result.Put(syncer::HISTORY_DELETE_DIRECTIVES);
     }
-    EXPECT_CALL(*sync_service, GetActiveDataTypes())
+    EXPECT_CALL(*sync_service, GetPreferredDataTypes())
         .WillRepeatedly(Return(result));
   }
 
@@ -280,26 +279,6 @@
   ASSERT_FALSE(get<1>(params));
 }
 
-TEST_F(SearchTabHelperTest, OnHistorySyncCheckSyncInactive) {
-  NavigateAndCommit(GURL(chrome::kChromeSearchLocalNtpUrl));
-  ProfileSyncServiceMock* sync_service = static_cast<ProfileSyncServiceMock*>(
-      ProfileSyncServiceFactory::GetInstance()->GetForProfile(profile()));
-  EXPECT_CALL(*sync_service, SyncActive()).WillRepeatedly(Return(false));
-  SearchTabHelper* search_tab_helper =
-      SearchTabHelper::FromWebContents(web_contents());
-  ASSERT_NE(static_cast<SearchTabHelper*>(NULL), search_tab_helper);
-
-  search_tab_helper->OnHistorySyncCheck();
-
-  const IPC::Message* message = process()->sink().GetUniqueMessageMatching(
-      ChromeViewMsg_HistorySyncCheckResult::ID);
-  ASSERT_TRUE(message != NULL);
-
-  ChromeViewMsg_HistorySyncCheckResult::Param params;
-  ChromeViewMsg_HistorySyncCheckResult::Read(message, &params);
-  ASSERT_FALSE(get<0>(params));
-}
-
 TEST_F(SearchTabHelperTest, OnHistorySyncCheckSyncing) {
   NavigateAndCommit(GURL(chrome::kChromeSearchLocalNtpUrl));
   SetHistorySync(true);
diff --git a/chrome/browser/ui/search_engines/search_engine_tab_helper.cc b/chrome/browser/ui/search_engines/search_engine_tab_helper.cc
index fa6274d..71bb7ab 100644
--- a/chrome/browser/ui/search_engines/search_engine_tab_helper.cc
+++ b/chrome/browser/ui/search_engines/search_engine_tab_helper.cc
@@ -132,7 +132,7 @@
        (index > 0) && IsFormSubmit(entry);
        entry = controller.GetEntryAtIndex(index))
     --index;
-  if (IsFormSubmit(entry))
+  if (!entry || IsFormSubmit(entry))
     return;
 
   // Autogenerate a keyword for the autodetected case; in the other cases we'll
diff --git a/chrome/browser/ui/startup/bad_flags_prompt.cc b/chrome/browser/ui/startup/bad_flags_prompt.cc
index 4b594970..211b42e 100644
--- a/chrome/browser/ui/startup/bad_flags_prompt.cc
+++ b/chrome/browser/ui/startup/bad_flags_prompt.cc
@@ -74,6 +74,12 @@
     // http://crbug.com/327295
     switches::kEnableSpeechDispatcher,
 #endif
+
+    // These flags control Blink feature state, which is not supported and is
+    // intended only for use by Chromium developers.
+    switches::kDisableBlinkFeatures,
+    switches::kEnableBlinkFeatures,
+
     NULL
   };
 
diff --git a/chrome/browser/ui/startup/startup_browser_creator_impl.cc b/chrome/browser/ui/startup/startup_browser_creator_impl.cc
index c8a0f2d5..355fbe3 100644
--- a/chrome/browser/ui/startup/startup_browser_creator_impl.cc
+++ b/chrome/browser/ui/startup/startup_browser_creator_impl.cc
@@ -382,6 +382,18 @@
 #endif
   }
 
+  // In kiosk mode, we want to always be fullscreen, so switch to that now.
+  if (base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kKioskMode) ||
+      base::CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kStartFullscreen)) {
+    // It's possible for there to be no browser window, e.g. if someone
+    // specified a non-sensical combination of options
+    // ("--kiosk --no_startup_window"); do nothing in that case.
+    Browser* browser = BrowserList::GetInstance(desktop_type)->GetLastActive();
+    if (browser)
+      chrome::ToggleFullscreenMode(browser);
+  }
+
 #if defined(OS_WIN)
   if (process_startup)
     ShellIntegration::MigrateChromiumShortcuts();
@@ -801,12 +813,6 @@
   if (!browser_creator_ || browser_creator_->show_main_browser_window())
     browser->window()->Show();
 
-  // In kiosk mode, we want to always be fullscreen, so switch to that now.
-  if (base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kKioskMode) ||
-      base::CommandLine::ForCurrentProcess()->HasSwitch(
-          switches::kStartFullscreen))
-    chrome::ToggleFullscreenMode(browser);
-
   return browser;
 }
 
diff --git a/chrome/browser/ui/tab_helpers.cc b/chrome/browser/ui/tab_helpers.cc
index 28393333..30d4da1 100644
--- a/chrome/browser/ui/tab_helpers.cc
+++ b/chrome/browser/ui/tab_helpers.cc
@@ -12,6 +12,7 @@
 #include "chrome/browser/history/history_tab_helper.h"
 #include "chrome/browser/infobars/infobar_service.h"
 #include "chrome/browser/net/net_error_tab_helper.h"
+#include "chrome/browser/net/predictor_tab_helper.h"
 #include "chrome/browser/password_manager/chrome_password_manager_client.h"
 #include "chrome/browser/predictors/resource_prefetch_predictor_factory.h"
 #include "chrome/browser/predictors/resource_prefetch_predictor_tab_helper.h"
@@ -24,6 +25,7 @@
 #include "chrome/browser/ui/bookmarks/bookmark_tab_helper.h"
 #include "chrome/browser/ui/find_bar/find_tab_helper.h"
 #include "chrome/browser/ui/navigation_correction_tab_observer.h"
+#include "chrome/browser/ui/passwords/manage_passwords_ui_controller.h"
 #include "chrome/browser/ui/pdf/chrome_pdf_web_contents_helper_client.h"
 #include "chrome/browser/ui/prefs/prefs_tab_helper.h"
 #include "chrome/browser/ui/search/search_tab_helper.h"
@@ -44,12 +46,10 @@
 #include "chrome/browser/ui/android/window_android_helper.h"
 #else
 #include "chrome/browser/external_protocol/external_protocol_observer.h"
-#include "chrome/browser/net/predictor_tab_helper.h"
 #include "chrome/browser/plugins/plugin_observer.h"
 #include "chrome/browser/safe_browsing/safe_browsing_tab_observer.h"
 #include "chrome/browser/thumbnails/thumbnail_tab_helper.h"
 #include "chrome/browser/ui/hung_plugin_tab_helper.h"
-#include "chrome/browser/ui/passwords/manage_passwords_ui_controller.h"
 #include "chrome/browser/ui/sad_tab_helper.h"
 #include "chrome/browser/ui/search_engines/search_engine_tab_helper.h"
 #include "chrome/browser/ui/sync/tab_contents_synced_tab_delegate.h"
@@ -138,14 +138,18 @@
       autofill::AutofillManager::ENABLE_AUTOFILL_DOWNLOAD_MANAGER);
   BookmarkTabHelper::CreateForWebContents(web_contents);
   chrome_browser_net::NetErrorTabHelper::CreateForWebContents(web_contents);
+  chrome_browser_net::PredictorTabHelper::CreateForWebContents(web_contents);
+  ChromeContentSettingsClient::CreateForWebContents(web_contents);
   ChromePasswordManagerClient::CreateForWebContentsWithAutofillClient(
       web_contents,
       autofill::ChromeAutofillClient::FromWebContents(web_contents));
+  ChromeTranslateClient::CreateForWebContents(web_contents);
   CoreTabHelper::CreateForWebContents(web_contents);
   FaviconTabHelper::CreateForWebContents(web_contents);
   FindTabHelper::CreateForWebContents(web_contents);
   HistoryTabHelper::CreateForWebContents(web_contents);
   InfoBarService::CreateForWebContents(web_contents);
+  ManagePasswordsUIController::CreateForWebContents(web_contents);
   NavigationCorrectionTabObserver::CreateForWebContents(web_contents);
   NavigationMetricsRecorder::CreateForWebContents(web_contents);
   PopupBlockerTabHelper::CreateForWebContents(web_contents);
@@ -155,8 +159,6 @@
   // TODO(vabr): Remove TabSpecificContentSettings from here once their function
   // is taken over by ChromeContentSettingsClient. http://crbug.com/387075
   TabSpecificContentSettings::CreateForWebContents(web_contents);
-  ChromeContentSettingsClient::CreateForWebContents(web_contents);
-  ChromeTranslateClient::CreateForWebContents(web_contents);
 
   // --- Platform-specific tab helpers ---
 
@@ -166,13 +168,11 @@
   SingleTabModeTabHelper::CreateForWebContents(web_contents);
   WindowAndroidHelper::CreateForWebContents(web_contents);
 #else
-  chrome_browser_net::PredictorTabHelper::CreateForWebContents(web_contents);
   extensions::ChromeExtensionWebContentsObserver::CreateForWebContents(
       web_contents);
   extensions::WebNavigationTabObserver::CreateForWebContents(web_contents);
   ExternalProtocolObserver::CreateForWebContents(web_contents);
   HungPluginTabHelper::CreateForWebContents(web_contents);
-  ManagePasswordsUIController::CreateForWebContents(web_contents);
   pdf::PDFWebContentsHelper::CreateForWebContentsWithClient(
       web_contents,
       scoped_ptr<pdf::PDFWebContentsHelperClient>(
diff --git a/chrome/browser/ui/toolbar/toolbar_actions_bar_unittest.cc b/chrome/browser/ui/toolbar/toolbar_actions_bar_unittest.cc
index 1706fc57..49240086 100644
--- a/chrome/browser/ui/toolbar/toolbar_actions_bar_unittest.cc
+++ b/chrome/browser/ui/toolbar/toolbar_actions_bar_unittest.cc
@@ -539,6 +539,10 @@
   scoped_refptr<const extensions::Extension> page_action2 =
       CreateAndAddExtension(
           kPageAction2, extensions::extension_action_test_util::PAGE_ACTION);
+  // The new extension was installed visible. Move it to the last index, and
+  // adjust the visible count.
+  toolbar_model()->MoveExtensionIcon(page_action2->id(), 3u);
+  toolbar_model()->SetVisibleIconCount(0);
 
   {
     // The second page action should be added to the end, and no icons should
diff --git a/chrome/browser/ui/views/apps/chrome_native_app_window_views.cc b/chrome/browser/ui/views/apps/chrome_native_app_window_views.cc
index cb15026..cf3624e 100644
--- a/chrome/browser/ui/views/apps/chrome_native_app_window_views.cc
+++ b/chrome/browser/ui/views/apps/chrome_native_app_window_views.cc
@@ -346,45 +346,6 @@
   return views::WidgetDelegateView::CreateNonClientFrameView(widget());
 }
 
-apps::AppWindowFrameView*
-ChromeNativeAppWindowViews::CreateNonStandardAppFrame() {
-  apps::AppWindowFrameView* frame =
-      new apps::AppWindowFrameView(widget(),
-                                   this,
-                                   has_frame_color_,
-                                   active_frame_color_,
-                                   inactive_frame_color_);
-  frame->Init();
-#if defined(USE_ASH)
-  // For Aura windows on the Ash desktop the sizes are different and the user
-  // can resize the window from slightly outside the bounds as well.
-  if (chrome::IsNativeWindowInAsh(widget()->GetNativeWindow())) {
-    frame->SetResizeSizes(ash::kResizeInsideBoundsSize,
-                          ash::kResizeOutsideBoundsSize,
-                          ash::kResizeAreaCornerSize);
-  }
-#endif
-
-#if !defined(OS_CHROMEOS)
-  // For non-Ash windows, install an easy resize window targeter, which ensures
-  // that the root window (not the app) receives mouse events on the edges.
-  if (chrome::GetHostDesktopTypeForNativeWindow(widget()->GetNativeWindow()) !=
-      chrome::HOST_DESKTOP_TYPE_ASH) {
-    aura::Window* window = widget()->GetNativeWindow();
-    int resize_inside = frame->resize_inside_bounds_size();
-    gfx::Insets inset(
-        resize_inside, resize_inside, resize_inside, resize_inside);
-    // Add the EasyResizeWindowTargeter on the window, not its root window. The
-    // root window does not have a delegate, which is needed to handle the event
-    // in Linux.
-    window->SetEventTargeter(scoped_ptr<ui::EventTargeter>(
-        new wm::EasyResizeWindowTargeter(window, inset, inset)));
-  }
-#endif
-
-  return frame;
-}
-
 // ui::BaseWindow implementation.
 
 gfx::Rect ChromeNativeAppWindowViews::GetRestoredBounds() const {
@@ -713,3 +674,42 @@
       extensions::ExtensionKeybindingRegistry::PLATFORM_APPS_ONLY,
       NULL));
 }
+
+apps::AppWindowFrameView*
+ChromeNativeAppWindowViews::CreateNonStandardAppFrame() {
+  apps::AppWindowFrameView* frame =
+      new apps::AppWindowFrameView(widget(),
+                                   this,
+                                   has_frame_color_,
+                                   active_frame_color_,
+                                   inactive_frame_color_);
+  frame->Init();
+#if defined(USE_ASH)
+  // For Aura windows on the Ash desktop the sizes are different and the user
+  // can resize the window from slightly outside the bounds as well.
+  if (chrome::IsNativeWindowInAsh(widget()->GetNativeWindow())) {
+    frame->SetResizeSizes(ash::kResizeInsideBoundsSize,
+                          ash::kResizeOutsideBoundsSize,
+                          ash::kResizeAreaCornerSize);
+  }
+#endif
+
+#if !defined(OS_CHROMEOS)
+  // For non-Ash windows, install an easy resize window targeter, which ensures
+  // that the root window (not the app) receives mouse events on the edges.
+  if (chrome::GetHostDesktopTypeForNativeWindow(widget()->GetNativeWindow()) !=
+      chrome::HOST_DESKTOP_TYPE_ASH) {
+    aura::Window* window = widget()->GetNativeWindow();
+    int resize_inside = frame->resize_inside_bounds_size();
+    gfx::Insets inset(
+        resize_inside, resize_inside, resize_inside, resize_inside);
+    // Add the EasyResizeWindowTargeter on the window, not its root window. The
+    // root window does not have a delegate, which is needed to handle the event
+    // in Linux.
+    window->SetEventTargeter(scoped_ptr<ui::EventTargeter>(
+        new wm::EasyResizeWindowTargeter(window, inset, inset)));
+  }
+#endif
+
+  return frame;
+}
diff --git a/chrome/browser/ui/views/apps/chrome_native_app_window_views.h b/chrome/browser/ui/views/apps/chrome_native_app_window_views.h
index 6df8646..06dd828 100644
--- a/chrome/browser/ui/views/apps/chrome_native_app_window_views.h
+++ b/chrome/browser/ui/views/apps/chrome_native_app_window_views.h
@@ -47,12 +47,6 @@
       const extensions::AppWindow::CreateParams& create_params);
   virtual views::NonClientFrameView* CreateStandardDesktopAppFrame();
 
- private:
-  FRIEND_TEST_ALL_PREFIXES(ShapedAppWindowTargeterTest,
-                           ResizeInsetsWithinBounds);
-
-  apps::AppWindowFrameView* CreateNonStandardAppFrame();
-
   // ui::BaseWindow implementation.
   gfx::Rect GetRestoredBounds() const override;
   ui::WindowShowState GetRestoredState() const override;
@@ -90,6 +84,12 @@
       extensions::AppWindow* app_window,
       const extensions::AppWindow::CreateParams& create_params) override;
 
+ private:
+  FRIEND_TEST_ALL_PREFIXES(ShapedAppWindowTargeterTest,
+                           ResizeInsetsWithinBounds);
+
+  apps::AppWindowFrameView* CreateNonStandardAppFrame();
+
   // True if the window is fullscreen or fullscreen is pending.
   bool is_fullscreen_;
 
diff --git a/chrome/browser/ui/views/apps/chrome_native_app_window_views_win.cc b/chrome/browser/ui/views/apps/chrome_native_app_window_views_win.cc
index ffcc6dba..48f08ce 100644
--- a/chrome/browser/ui/views/apps/chrome_native_app_window_views_win.cc
+++ b/chrome/browser/ui/views/apps/chrome_native_app_window_views_win.cc
@@ -37,7 +37,7 @@
 #include "ui/views/win/hwnd_util.h"
 
 ChromeNativeAppWindowViewsWin::ChromeNativeAppWindowViewsWin()
-    : glass_frame_view_(NULL), weak_ptr_factory_(this) {
+    : glass_frame_view_(NULL), is_translucent_(false), weak_ptr_factory_(this) {
 }
 
 void ChromeNativeAppWindowViewsWin::ActivateParentDesktopIfNecessary() {
@@ -103,6 +103,9 @@
     init_params->context = ash::Shell::GetPrimaryRootWindow();
   else
     init_params->native_widget = new AppWindowDesktopNativeWidgetAuraWin(this);
+
+  is_translucent_ =
+      init_params->opacity == views::Widget::InitParams::TRANSLUCENT_WINDOW;
 }
 
 void ChromeNativeAppWindowViewsWin::InitializeDefaultWindow(
@@ -155,6 +158,13 @@
   ChromeNativeAppWindowViews::Activate();
 }
 
+bool ChromeNativeAppWindowViewsWin::CanMinimize() const {
+  // Resizing on Windows breaks translucency if the window also has shape.
+  // See http://crbug.com/417947.
+  return ChromeNativeAppWindowViews::CanMinimize() &&
+         !(WidgetHasHitTestMask() && is_translucent_);
+}
+
 void ChromeNativeAppWindowViewsWin::UpdateShelfMenu() {
   if (!JumpListUpdater::IsEnabled() || IsRunningInAsh())
     return;
diff --git a/chrome/browser/ui/views/apps/chrome_native_app_window_views_win.h b/chrome/browser/ui/views/apps/chrome_native_app_window_views_win.h
index bef7451..9f523ab 100644
--- a/chrome/browser/ui/views/apps/chrome_native_app_window_views_win.h
+++ b/chrome/browser/ui/views/apps/chrome_native_app_window_views_win.h
@@ -44,6 +44,9 @@
   virtual void Show() override;
   virtual void Activate() override;
 
+  // Overridden from views::WidgetDelegate:
+  bool CanMinimize() const override;
+
   // Overridden from extensions::NativeAppWindow:
   virtual void UpdateShelfMenu() override;
 
@@ -57,6 +60,9 @@
   // Not set for windows running inside Ash.
   base::string16 app_model_id_;
 
+  // Whether the InitParams indicated that this window should be translucent.
+  bool is_translucent_;
+
   base::WeakPtrFactory<ChromeNativeAppWindowViewsWin> weak_ptr_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(ChromeNativeAppWindowViewsWin);
diff --git a/chrome/browser/ui/views/apps/shaped_app_window_targeter_unittest.cc b/chrome/browser/ui/views/apps/shaped_app_window_targeter_unittest.cc
index ef7d0cf7..4aa75e2a 100644
--- a/chrome/browser/ui/views/apps/shaped_app_window_targeter_unittest.cc
+++ b/chrome/browser/ui/views/apps/shaped_app_window_targeter_unittest.cc
@@ -9,6 +9,7 @@
 #include "ui/aura/test/aura_test_base.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_event_dispatcher.h"
+#include "ui/events/event_utils.h"
 #include "ui/views/controls/webview/webview.h"
 #include "ui/wm/core/default_activation_client.h"
 #include "ui/wm/core/easy_resize_window_targeter.h"
@@ -62,9 +63,9 @@
   {
     // Without any custom shapes, the event should be targeted correctly to the
     // window.
-    ui::MouseEvent move(ui::ET_MOUSE_MOVED,
-                        gfx::Point(40, 40), gfx::Point(40, 40),
-                        ui::EF_NONE, ui::EF_NONE);
+    ui::MouseEvent move(ui::ET_MOUSE_MOVED, gfx::Point(40, 40),
+                        gfx::Point(40, 40), ui::EventTimeForNow(), ui::EF_NONE,
+                        ui::EF_NONE);
     ui::EventDispatchDetails details =
         event_processor()->OnEventFromSource(&move);
     ASSERT_FALSE(details.dispatcher_destroyed);
@@ -77,9 +78,9 @@
   {
     // With an empty custom shape, all events within the window should fall
     // through to the root window.
-    ui::MouseEvent move(ui::ET_MOUSE_MOVED,
-                        gfx::Point(40, 40), gfx::Point(40, 40),
-                        ui::EF_NONE, ui::EF_NONE);
+    ui::MouseEvent move(ui::ET_MOUSE_MOVED, gfx::Point(40, 40),
+                        gfx::Point(40, 40), ui::EventTimeForNow(), ui::EF_NONE,
+                        ui::EF_NONE);
     ui::EventDispatchDetails details =
         event_processor()->OnEventFromSource(&move);
     ASSERT_FALSE(details.dispatcher_destroyed);
@@ -102,18 +103,18 @@
   {
     // With the custom shape, the events that don't fall within the custom shape
     // will go through to the root window.
-    ui::MouseEvent move(ui::ET_MOUSE_MOVED,
-                        gfx::Point(40, 40), gfx::Point(40, 40),
-                        ui::EF_NONE, ui::EF_NONE);
+    ui::MouseEvent move(ui::ET_MOUSE_MOVED, gfx::Point(40, 40),
+                        gfx::Point(40, 40), ui::EventTimeForNow(), ui::EF_NONE,
+                        ui::EF_NONE);
     ui::EventDispatchDetails details =
         event_processor()->OnEventFromSource(&move);
     ASSERT_FALSE(details.dispatcher_destroyed);
     EXPECT_EQ(root_window(), move.target());
 
     // But events within the shape will still reach the window.
-    ui::MouseEvent move2(ui::ET_MOUSE_MOVED,
-                         gfx::Point(80, 80), gfx::Point(80, 80),
-                         ui::EF_NONE, ui::EF_NONE);
+    ui::MouseEvent move2(ui::ET_MOUSE_MOVED, gfx::Point(80, 80),
+                         gfx::Point(80, 80), ui::EventTimeForNow(), ui::EF_NONE,
+                         ui::EF_NONE);
     details = event_processor()->OnEventFromSource(&move2);
     ASSERT_FALSE(details.dispatcher_destroyed);
     EXPECT_EQ(window, move2.target());
@@ -132,9 +133,9 @@
   {
     // Without any custom shapes, an event within the window bounds should be
     // targeted correctly to the window.
-    ui::MouseEvent move(ui::ET_MOUSE_MOVED,
-                        gfx::Point(40, 40), gfx::Point(40, 40),
-                        ui::EF_NONE, ui::EF_NONE);
+    ui::MouseEvent move(ui::ET_MOUSE_MOVED, gfx::Point(40, 40),
+                        gfx::Point(40, 40), ui::EventTimeForNow(), ui::EF_NONE,
+                        ui::EF_NONE);
     ui::EventDispatchDetails details =
         event_processor()->OnEventFromSource(&move);
     ASSERT_FALSE(details.dispatcher_destroyed);
@@ -144,9 +145,9 @@
     // Without any custom shapes, an event that falls just outside the window
     // bounds should also be targeted correctly to the window, because of the
     // targeter installed on the root-window.
-    ui::MouseEvent move(ui::ET_MOUSE_MOVED,
-                        gfx::Point(10, 10), gfx::Point(10, 10),
-                        ui::EF_NONE, ui::EF_NONE);
+    ui::MouseEvent move(ui::ET_MOUSE_MOVED, gfx::Point(10, 10),
+                        gfx::Point(10, 10), ui::EventTimeForNow(), ui::EF_NONE,
+                        ui::EF_NONE);
     ui::EventDispatchDetails details =
         event_processor()->OnEventFromSource(&move);
     ASSERT_FALSE(details.dispatcher_destroyed);
@@ -160,9 +161,9 @@
   {
     // With the custom shape, the events that don't fall within the custom shape
     // will go through to the root window.
-    ui::MouseEvent move(ui::ET_MOUSE_MOVED,
-                        gfx::Point(10, 10), gfx::Point(10, 10),
-                        ui::EF_NONE, ui::EF_NONE);
+    ui::MouseEvent move(ui::ET_MOUSE_MOVED, gfx::Point(10, 10),
+                        gfx::Point(10, 10), ui::EventTimeForNow(), ui::EF_NONE,
+                        ui::EF_NONE);
     ui::EventDispatchDetails details =
         event_processor()->OnEventFromSource(&move);
     ASSERT_FALSE(details.dispatcher_destroyed);
@@ -173,9 +174,9 @@
   // app window for events just outside its bounds.
   app_window()->UpdateShape(scoped_ptr<SkRegion>());
   {
-    ui::MouseEvent move(ui::ET_MOUSE_MOVED,
-                        gfx::Point(10, 10), gfx::Point(10, 10),
-                        ui::EF_NONE, ui::EF_NONE);
+    ui::MouseEvent move(ui::ET_MOUSE_MOVED, gfx::Point(10, 10),
+                        gfx::Point(10, 10), ui::EventTimeForNow(), ui::EF_NONE,
+                        ui::EF_NONE);
     ui::EventDispatchDetails details =
         event_processor()->OnEventFromSource(&move);
     ASSERT_FALSE(details.dispatcher_destroyed);
@@ -190,9 +191,9 @@
   {
     // An event in the center of the window should always have
     // |window| as its target.
-    ui::MouseEvent move(ui::ET_MOUSE_MOVED,
-                        gfx::Point(80, 80), gfx::Point(80, 80),
-                        ui::EF_NONE, ui::EF_NONE);
+    ui::MouseEvent move(ui::ET_MOUSE_MOVED, gfx::Point(80, 80),
+                        gfx::Point(80, 80), ui::EventTimeForNow(), ui::EF_NONE,
+                        ui::EF_NONE);
     ui::EventDispatchDetails details =
         event_processor()->OnEventFromSource(&move);
     ASSERT_FALSE(details.dispatcher_destroyed);
@@ -202,9 +203,9 @@
     // Without an EasyResizeTargeter on the container, an event
     // inside the window and within 5px of an edge should have
     // |window| as its target.
-    ui::MouseEvent move(ui::ET_MOUSE_MOVED,
-                        gfx::Point(32, 37), gfx::Point(32, 37),
-                        ui::EF_NONE, ui::EF_NONE);
+    ui::MouseEvent move(ui::ET_MOUSE_MOVED, gfx::Point(32, 37),
+                        gfx::Point(32, 37), ui::EventTimeForNow(), ui::EF_NONE,
+                        ui::EF_NONE);
     ui::EventDispatchDetails details =
         event_processor()->OnEventFromSource(&move);
     ASSERT_FALSE(details.dispatcher_destroyed);
@@ -231,9 +232,9 @@
     // RenderWidgetHostViewAura, we cannot differentiate the two cases. Fix
     // the test environment so that the test can assert that non-border events
     // bubble down to a child of |window|.
-    ui::MouseEvent move(ui::ET_MOUSE_MOVED,
-                        gfx::Point(80, 80), gfx::Point(80, 80),
-                        ui::EF_NONE, ui::EF_NONE);
+    ui::MouseEvent move(ui::ET_MOUSE_MOVED, gfx::Point(80, 80),
+                        gfx::Point(80, 80), ui::EventTimeForNow(), ui::EF_NONE,
+                        ui::EF_NONE);
     ui::EventDispatchDetails details =
         event_processor()->OnEventFromSource(&move);
     ASSERT_FALSE(details.dispatcher_destroyed);
@@ -243,9 +244,9 @@
     // With an EasyResizeTargeter on the container, an event
     // inside the window and within 5px of an edge should have
     // |window| as its target.
-    ui::MouseEvent move(ui::ET_MOUSE_MOVED,
-                        gfx::Point(32, 37), gfx::Point(32, 37),
-                        ui::EF_NONE, ui::EF_NONE);
+    ui::MouseEvent move(ui::ET_MOUSE_MOVED, gfx::Point(32, 37),
+                        gfx::Point(32, 37), ui::EventTimeForNow(), ui::EF_NONE,
+                        ui::EF_NONE);
     ui::EventDispatchDetails details =
         event_processor()->OnEventFromSource(&move);
     ASSERT_FALSE(details.dispatcher_destroyed);
diff --git a/chrome/browser/ui/views/autofill/autofill_popup_base_view.cc b/chrome/browser/ui/views/autofill/autofill_popup_base_view.cc
index 2c73a69..f131ae5 100644
--- a/chrome/browser/ui/views/autofill/autofill_popup_base_view.cc
+++ b/chrome/browser/ui/views/autofill/autofill_popup_base_view.cc
@@ -143,6 +143,11 @@
 }
 
 void AutofillPopupBaseView::OnMouseMoved(const ui::MouseEvent& event) {
+  // A synthesized mouse move will be sent when the popup is first shown.
+  // Don't preview a suggestion if the mouse happens to be hovering there.
+  if (event.flags() & ui::EF_IS_SYNTHESIZED)
+    return;
+
   if (HitTestPoint(event.location()))
     SetSelection(event.location());
   else
diff --git a/chrome/browser/ui/views/autofill/autofill_popup_base_view_browsertest.cc b/chrome/browser/ui/views/autofill/autofill_popup_base_view_browsertest.cc
index 0dc95c71..01931d3 100644
--- a/chrome/browser/ui/views/autofill/autofill_popup_base_view_browsertest.cc
+++ b/chrome/browser/ui/views/autofill/autofill_popup_base_view_browsertest.cc
@@ -122,10 +122,8 @@
 
   ShowView();
 
-  ui::MouseEvent mouse_down(ui::ET_MOUSE_PRESSED,
-                            gfx::Point(0, 0),
-                            gfx::Point(0, 0),
-                            0, 0);
+  ui::MouseEvent mouse_down(ui::ET_MOUSE_PRESSED, gfx::Point(0, 0),
+                            gfx::Point(0, 0), ui::EventTimeForNow(), 0, 0);
   EXPECT_TRUE(static_cast<views::View*>(view_)->OnMousePressed(mouse_down));
 
   // Ignore double clicks.
diff --git a/chrome/browser/ui/views/autofill/password_generation_popup_view_tester_views.cc b/chrome/browser/ui/views/autofill/password_generation_popup_view_tester_views.cc
index dded3a7..6e4e644 100644
--- a/chrome/browser/ui/views/autofill/password_generation_popup_view_tester_views.cc
+++ b/chrome/browser/ui/views/autofill/password_generation_popup_view_tester_views.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/ui/views/autofill/password_generation_popup_view_tester_views.h"
 
 #include "chrome/browser/ui/views/autofill/password_generation_popup_view_views.h"
+#include "ui/events/event_utils.h"
 
 namespace autofill {
 
@@ -24,10 +25,8 @@
 
 void PasswordGenerationPopupViewTesterViews::SimulateMouseMovementAt(
     const gfx::Point& point) {
-  ui::MouseEvent mouse_down(ui::ET_MOUSE_MOVED,
-                            point,
-                            gfx::Point(0, 0),
-                            0, 0);
+  ui::MouseEvent mouse_down(ui::ET_MOUSE_MOVED, point, gfx::Point(0, 0),
+                            ui::EventTimeForNow(), 0, 0);
   static_cast<views::View*>(view_)->OnMouseMoved(mouse_down);
 }
 
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_bar_view.cc b/chrome/browser/ui/views/bookmarks/bookmark_bar_view.cc
index 506ee3e..afe033a 100644
--- a/chrome/browser/ui/views/bookmarks/bookmark_bar_view.cc
+++ b/chrome/browser/ui/views/bookmarks/bookmark_bar_view.cc
@@ -690,11 +690,10 @@
     const GURL& url,
     const base::string16& title,
     Profile* profile) {
-  int max_width = views::TooltipManager::GetMaxWidth(
-      screen_loc.x(),
-      screen_loc.y(),
-      widget->GetNativeView());
-  const gfx::FontList tt_fonts = widget->GetTooltipManager()->GetFontList();
+  const views::TooltipManager* tooltip_manager = widget->GetTooltipManager();
+  int max_width = tooltip_manager->GetMaxWidth(screen_loc,
+                                               widget->GetNativeView());
+  const gfx::FontList tt_fonts = tooltip_manager->GetFontList();
   base::string16 result;
 
   // First the title.
diff --git a/chrome/browser/ui/views/certificate_selector.cc b/chrome/browser/ui/views/certificate_selector.cc
new file mode 100644
index 0000000..ab901d0c
--- /dev/null
+++ b/chrome/browser/ui/views/certificate_selector.cc
@@ -0,0 +1,189 @@
+// 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 "chrome/browser/ui/views/certificate_selector.h"
+
+#include <stddef.h>  // For size_t.
+#include <vector>
+
+#include "base/logging.h"
+#include "base/memory/ref_counted.h"
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/certificate_viewer.h"
+#include "chrome/grit/generated_resources.h"
+#include "components/constrained_window/constrained_window_views.h"
+#include "content/public/browser/web_contents.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/models/table_model.h"
+#include "ui/base/models/table_model_observer.h"
+#include "ui/views/controls/button/label_button.h"
+#include "ui/views/controls/label.h"
+#include "ui/views/controls/table/table_view.h"
+#include "ui/views/layout/grid_layout.h"
+#include "ui/views/layout/layout_constants.h"
+#include "ui/views/widget/widget.h"
+#include "ui/views/window/dialog_client_view.h"
+
+namespace chrome {
+
+class CertificateSelector::CertificateTableModel : public ui::TableModel {
+ public:
+  explicit CertificateTableModel(const net::CertificateList& certificates);
+
+  // ui::TableModel:
+  int RowCount() override;
+  base::string16 GetText(int index, int column_id) override;
+  void SetObserver(ui::TableModelObserver* observer) override;
+
+ private:
+  std::vector<base::string16> items_;
+
+  DISALLOW_COPY_AND_ASSIGN(CertificateTableModel);
+};
+
+CertificateSelector::CertificateTableModel::CertificateTableModel(
+    const net::CertificateList& certificates) {
+  for (const scoped_refptr<net::X509Certificate>& cert : certificates) {
+    items_.push_back(l10n_util::GetStringFUTF16(
+        IDS_CERT_SELECTOR_TABLE_CERT_FORMAT,
+        base::UTF8ToUTF16(cert->subject().GetDisplayName()),
+        base::UTF8ToUTF16(cert->issuer().GetDisplayName())));
+  }
+}
+
+int CertificateSelector::CertificateTableModel::RowCount() {
+  return items_.size();
+}
+
+base::string16 CertificateSelector::CertificateTableModel::GetText(
+    int index,
+    int column_id) {
+  DCHECK_EQ(column_id, 0);
+  DCHECK_GE(index, 0);
+  DCHECK_LT(static_cast<size_t>(index), items_.size());
+
+  return items_[index];
+}
+
+void CertificateSelector::CertificateTableModel::SetObserver(
+    ui::TableModelObserver* observer) {
+}
+
+CertificateSelector::CertificateSelector(
+    const net::CertificateList& certificates,
+    content::WebContents* web_contents)
+    : certificates_(certificates),
+      model_(new CertificateTableModel(certificates)),
+      web_contents_(web_contents),
+      table_(nullptr),
+      view_cert_button_(nullptr) {
+  CHECK(web_contents_);
+}
+
+CertificateSelector::~CertificateSelector() {
+  table_->SetModel(nullptr);
+}
+
+void CertificateSelector::Show() {
+  constrained_window::ShowWebModalDialogViews(this, web_contents_);
+
+  // Select the first row automatically.  This must be done after the dialog has
+  // been created.
+  table_->Select(0);
+}
+
+void CertificateSelector::InitWithText(const base::string16& text) {
+  views::GridLayout* const layout = views::GridLayout::CreatePanel(this);
+  SetLayoutManager(layout);
+
+  // The dimensions of the certificate selector table view, in pixels.
+  const int kTableViewWidth = 400;
+  const int kTableViewHeight = 100;
+
+  const int kColumnSetId = 0;
+  views::ColumnSet* const column_set = layout->AddColumnSet(kColumnSetId);
+  column_set->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 1,
+                        views::GridLayout::USE_PREF, 0, 0);
+
+  layout->StartRow(0, kColumnSetId);
+  scoped_ptr<views::Label> label(new views::Label(text));
+  label->SetMultiLine(true);
+  label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
+  label->SetAllowCharacterBreak(true);
+  label->SizeToFit(kTableViewWidth);
+  layout->AddView(label.release());
+
+  layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
+
+  std::vector<ui::TableColumn> columns;
+  columns.push_back(ui::TableColumn());
+  table_ = new views::TableView(model_.get(), columns, views::TEXT_ONLY,
+                                true /* single_selection */);
+  table_->SetObserver(this);
+  layout->StartRow(1, kColumnSetId);
+  layout->AddView(table_->CreateParentIfNecessary(), 1, 1,
+                  views::GridLayout::FILL, views::GridLayout::FILL,
+                  kTableViewWidth, kTableViewHeight);
+
+  layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
+}
+
+net::X509Certificate* CertificateSelector::GetSelectedCert() const {
+  const int selected = table_->FirstSelectedRow();
+  if (selected < 0)  // Nothing is selected in |table_|.
+    return nullptr;
+  CHECK_LT(static_cast<size_t>(selected), certificates_.size());
+  return certificates_[selected].get();
+}
+
+bool CertificateSelector::CanResize() const {
+  return true;
+}
+
+base::string16 CertificateSelector::GetWindowTitle() const {
+  return l10n_util::GetStringUTF16(IDS_CLIENT_CERT_DIALOG_TITLE);
+}
+
+bool CertificateSelector::IsDialogButtonEnabled(ui::DialogButton button) const {
+  return button != ui::DIALOG_BUTTON_OK || GetSelectedCert() != nullptr;
+}
+
+views::View* CertificateSelector::GetInitiallyFocusedView() {
+  DCHECK(table_);
+  return table_;
+}
+
+views::View* CertificateSelector::CreateExtraView() {
+  DCHECK(!view_cert_button_);
+  scoped_ptr<views::LabelButton> button(new views::LabelButton(
+      this, l10n_util::GetStringUTF16(IDS_PAGEINFO_CERT_INFO_BUTTON)));
+  button->SetStyle(views::Button::STYLE_BUTTON);
+  view_cert_button_ = button.get();
+  return button.release();
+}
+
+ui::ModalType CertificateSelector::GetModalType() const {
+  return ui::MODAL_TYPE_CHILD;
+}
+
+void CertificateSelector::ButtonPressed(views::Button* sender,
+                                        const ui::Event& event) {
+  if (sender == view_cert_button_) {
+    net::X509Certificate* const cert = GetSelectedCert();
+    if (cert)
+      ShowCertificateViewer(web_contents_,
+                            web_contents_->GetTopLevelNativeWindow(), cert);
+  }
+}
+
+void CertificateSelector::OnSelectionChanged() {
+  GetDialogClientView()->ok_button()->SetEnabled(GetSelectedCert() != nullptr);
+}
+
+void CertificateSelector::OnDoubleClick() {
+  if (Accept())
+    GetWidget()->Close();
+}
+
+}  // namespace chrome
diff --git a/chrome/browser/ui/views/certificate_selector.h b/chrome/browser/ui/views/certificate_selector.h
new file mode 100644
index 0000000..e4f52cf
--- /dev/null
+++ b/chrome/browser/ui/views/certificate_selector.h
@@ -0,0 +1,87 @@
+// 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 CHROME_BROWSER_UI_VIEWS_CERTIFICATE_SELECTOR_H_
+#define CHROME_BROWSER_UI_VIEWS_CERTIFICATE_SELECTOR_H_
+
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/string16.h"
+#include "net/cert/x509_certificate.h"
+#include "ui/views/controls/button/button.h"
+#include "ui/views/controls/table/table_view_observer.h"
+#include "ui/views/window/dialog_delegate.h"
+
+namespace content {
+class WebContents;
+}
+
+namespace views {
+class LabelButton;
+class TableView;
+}
+
+namespace chrome {
+
+// A base class for dialogs that show a given list of certificates to the user.
+// The user can select a single certificate and look at details of each
+// certificate.
+// The currently selected certificate can be obtained using |GetSelectedCert()|.
+// The explanatory text shown to the user must be provided to |InitWithText()|.
+class CertificateSelector : public views::DialogDelegateView,
+                            public views::ButtonListener,
+                            public views::TableViewObserver {
+ public:
+  class CertificateTableModel;
+
+  // |web_contents| must not be null.
+  CertificateSelector(const net::CertificateList& certificates,
+                      content::WebContents* web_contents);
+  ~CertificateSelector() override;
+
+  // Returns the currently selected certificate or null if none is selected.
+  // Must be called after |InitWithText()|.
+  net::X509Certificate* GetSelectedCert() const;
+
+  // Shows this dialog as a constrained web modal dialog and focuses the first
+  // certificate.
+  // Must be called after |InitWithText()|.
+  void Show();
+
+  // DialogDelegateView:
+  bool CanResize() const override;
+  base::string16 GetWindowTitle() const override;
+  bool IsDialogButtonEnabled(ui::DialogButton button) const override;
+  views::View* GetInitiallyFocusedView() override;
+  views::View* CreateExtraView() override;
+  ui::ModalType GetModalType() const override;
+
+  // views::ButtonListener:
+  void ButtonPressed(views::Button* sender, const ui::Event& event) override;
+
+  // views::TableViewObserver:
+  void OnSelectionChanged() override;
+  void OnDoubleClick() override;
+
+ protected:
+  // Initializes the dialog. |text| is shown above the list of certificates
+  // and is supposed to explain to the user what the implication of the
+  // certificate selection is.
+  void InitWithText(const base::string16& text);
+
+ private:
+  const net::CertificateList certificates_;
+  scoped_ptr<CertificateTableModel> model_;
+
+  content::WebContents* const web_contents_;
+
+  views::TableView* table_;
+  views::LabelButton* view_cert_button_;
+
+  DISALLOW_COPY_AND_ASSIGN(CertificateSelector);
+};
+
+}  // namespace chrome
+
+#endif  // CHROME_BROWSER_UI_VIEWS_CERTIFICATE_SELECTOR_H_
diff --git a/chrome/browser/ui/views/certificate_selector_browsertest.cc b/chrome/browser/ui/views/certificate_selector_browsertest.cc
new file mode 100644
index 0000000..5e5ff0f
--- /dev/null
+++ b/chrome/browser/ui/views/certificate_selector_browsertest.cc
@@ -0,0 +1,82 @@
+// 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/bind.h"
+#include "base/files/file_path.h"
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/browser/ui/views/certificate_selector.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "chrome/test/base/interactive_test_utils.h"
+#include "content/public/test/browser_test_utils.h"
+#include "net/base/test_data_directory.h"
+#include "net/cert/x509_certificate.h"
+#include "net/test/cert_test_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+class TestCertificateSelector : public chrome::CertificateSelector {
+ public:
+  TestCertificateSelector(const net::CertificateList& certificates,
+                          content::WebContents* web_contents)
+      : CertificateSelector(certificates, web_contents) {}
+
+  void Init() { InitWithText(base::ASCIIToUTF16("some arbitrary text")); }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(TestCertificateSelector);
+};
+
+class CertificateSelectorTest : public InProcessBrowserTest {
+ public:
+  void SetUpInProcessBrowserTestFixture() override {
+    client_1_ =
+        net::ImportCertFromFile(net::GetTestCertsDirectory(), "client_1.pem");
+    ASSERT_NE(nullptr, client_1_);
+
+    client_2_ =
+        net::ImportCertFromFile(net::GetTestCertsDirectory(), "client_2.pem");
+    ASSERT_NE(nullptr, client_2_);
+  }
+
+  void SetUpOnMainThread() override {
+    ASSERT_TRUE(content::WaitForLoadStop(
+        browser()->tab_strip_model()->GetActiveWebContents()));
+
+    net::CertificateList certificates;
+    certificates.push_back(client_1_);
+    certificates.push_back(client_2_);
+
+    selector_ = new TestCertificateSelector(
+        certificates, browser()->tab_strip_model()->GetActiveWebContents());
+    selector_->Init();
+    selector_->Show();
+  }
+
+  void TearDownOnMainThread() override {
+    selector_->Close();
+    selector_ = nullptr;
+  }
+
+ protected:
+  scoped_refptr<net::X509Certificate> client_1_;
+  scoped_refptr<net::X509Certificate> client_2_;
+
+  // The selector will be deleted when the browser is shutdown.
+  TestCertificateSelector* selector_ = nullptr;
+};
+
+}  // namespace
+
+IN_PROC_BROWSER_TEST_F(CertificateSelectorTest, GetSelectedCert) {
+  EXPECT_EQ(client_1_.get(), selector_->GetSelectedCert());
+  EXPECT_TRUE(ui_test_utils::SendKeyPressSync(browser(), ui::VKEY_DOWN, false,
+                                              false, false, false));
+  EXPECT_EQ(client_2_.get(), selector_->GetSelectedCert());
+  EXPECT_TRUE(ui_test_utils::SendKeyPressSync(browser(), ui::VKEY_UP, false,
+                                              false, false, false));
+  EXPECT_EQ(client_1_.get(), selector_->GetSelectedCert());
+}
diff --git a/chrome/browser/ui/views/desktop_media_picker_views_unittest.cc b/chrome/browser/ui/views/desktop_media_picker_views_unittest.cc
index 24f80ca5..0e2e3f1 100644
--- a/chrome/browser/ui/views/desktop_media_picker_views_unittest.cc
+++ b/chrome/browser/ui/views/desktop_media_picker_views_unittest.cc
@@ -13,6 +13,7 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/aura/window.h"
 #include "ui/compositor/test/context_factories_for_test.h"
+#include "ui/events/event_utils.h"
 #include "ui/views/test/views_test_helper.h"
 #include "ui/views/widget/widget.h"
 #include "ui/views/window/dialog_delegate.h"
@@ -108,9 +109,8 @@
 
   media_list_->AddSource(kFakeId);
 
-  ui::MouseEvent double_click(ui::ET_MOUSE_PRESSED,
-                              gfx::Point(),
-                              gfx::Point(),
+  ui::MouseEvent double_click(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(),
+                              ui::EventTimeForNow(),
                               ui::EF_LEFT_MOUSE_BUTTON | ui::EF_IS_DOUBLE_CLICK,
                               ui::EF_LEFT_MOUSE_BUTTON);
 
diff --git a/chrome/browser/ui/views/extensions/bookmark_app_bubble_view.cc b/chrome/browser/ui/views/extensions/bookmark_app_bubble_view.cc
index 71df060..46f8d15f3 100644
--- a/chrome/browser/ui/views/extensions/bookmark_app_bubble_view.cc
+++ b/chrome/browser/ui/views/extensions/bookmark_app_bubble_view.cc
@@ -7,6 +7,7 @@
 #include "base/strings/string16.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/ui/host_desktop.h"
 #include "chrome/grit/generated_resources.h"
 #include "content/public/browser/web_contents.h"
 #include "extensions/common/constants.h"
@@ -102,7 +103,7 @@
 
 void BookmarkAppBubbleView::Init() {
   views::Label* title_label = new views::Label(
-      l10n_util::GetStringUTF16(IDS_BOOKMARK_APP_BUBBLE_TITLE));
+      l10n_util::GetStringUTF16(TitleStringId()));
   ui::ResourceBundle* rb = &ui::ResourceBundle::GetSharedInstance();
   title_label->SetFontList(rb->GetFontList(ui::ResourceBundle::MediumFont));
   title_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
@@ -218,7 +219,7 @@
 
 void BookmarkAppBubbleView::GetAccessibleState(ui::AXViewState* state) {
   views::BubbleDelegateView::GetAccessibleState(state);
-  state->name = l10n_util::GetStringUTF16(IDS_BOOKMARK_APP_BUBBLE_TITLE);
+  state->name = l10n_util::GetStringUTF16(TitleStringId());
 }
 
 gfx::Size BookmarkAppBubbleView::GetMinimumSize() const {
@@ -253,6 +254,22 @@
   add_button_->SetEnabled(!GetTrimmedTitle().empty());
 }
 
+int BookmarkAppBubbleView::TitleStringId() {
+#if defined(OS_WIN)
+    int string_id = IDS_ADD_TO_TASKBAR_BUBBLE_TITLE;
+#else
+    int string_id = IDS_ADD_TO_DESKTOP_BUBBLE_TITLE;
+#endif
+#if defined(USE_ASH)
+    if (chrome::GetHostDesktopTypeForNativeWindow(
+            anchor_widget()->GetNativeWindow()) ==
+        chrome::HOST_DESKTOP_TYPE_ASH) {
+      string_id = IDS_ADD_TO_SHELF_BUBBLE_TITLE;
+    }
+#endif
+    return string_id;
+}
+
 base::string16 BookmarkAppBubbleView::GetTrimmedTitle() {
   base::string16 title(title_tf_->text());
   base::TrimWhitespace(title, base::TRIM_ALL, &title);
diff --git a/chrome/browser/ui/views/extensions/bookmark_app_bubble_view.h b/chrome/browser/ui/views/extensions/bookmark_app_bubble_view.h
index f617e7a..741a66e 100644
--- a/chrome/browser/ui/views/extensions/bookmark_app_bubble_view.h
+++ b/chrome/browser/ui/views/extensions/bookmark_app_bubble_view.h
@@ -72,6 +72,9 @@
   // Update the state of the Add button.
   void UpdateAddButtonState();
 
+  // Get the string ID to use for the bubble title.
+  int TitleStringId();
+
   // Get the trimmed contents of the title text field.
   base::string16 GetTrimmedTitle();
 
diff --git a/chrome/browser/ui/views/extensions/bookmark_override_browsertest.cc b/chrome/browser/ui/views/extensions/bookmark_override_browsertest.cc
index 12a0505a..c61b1e5 100644
--- a/chrome/browser/ui/views/extensions/bookmark_override_browsertest.cc
+++ b/chrome/browser/ui/views/extensions/bookmark_override_browsertest.cc
@@ -15,6 +15,7 @@
 #include "chrome/test/base/ui_test_utils.h"
 #include "extensions/common/manifest_constants.h"
 #include "extensions/test/result_catcher.h"
+#include "ui/events/event_utils.h"
 
 typedef ExtensionApiTest BookmarkOverrideTest;
 
@@ -49,12 +50,12 @@
   views::View* star_view =
       browser_view->GetToolbarView()->location_bar()->star_view();
 
-  ui::MouseEvent pressed_event(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(),
-                               ui::EF_LEFT_MOUSE_BUTTON,
-                               ui::EF_LEFT_MOUSE_BUTTON);
-  ui::MouseEvent released_event(ui::ET_MOUSE_RELEASED, gfx::Point(),
-                                gfx::Point(), ui::EF_LEFT_MOUSE_BUTTON,
-                                ui::EF_LEFT_MOUSE_BUTTON);
+  ui::MouseEvent pressed_event(
+      ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(), ui::EventTimeForNow(),
+      ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
+  ui::MouseEvent released_event(
+      ui::ET_MOUSE_RELEASED, gfx::Point(), gfx::Point(), ui::EventTimeForNow(),
+      ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
 
   // Verify that clicking once shows the bookmark bubble.
   EXPECT_FALSE(BookmarkBubbleView::IsShowing());
diff --git a/chrome/browser/ui/views/extensions/extension_view_views.cc b/chrome/browser/ui/views/extensions/extension_view_views.cc
index 6d5768b9..aacdec136 100644
--- a/chrome/browser/ui/views/extensions/extension_view_views.cc
+++ b/chrome/browser/ui/views/extensions/extension_view_views.cc
@@ -81,10 +81,6 @@
   }
 }
 
-void ExtensionViewViews::Init() {
-  // Initialization continues in ViewHierarchyChanged().
-}
-
 Browser* ExtensionViewViews::GetBrowser() {
   return browser_;
 }
diff --git a/chrome/browser/ui/views/extensions/extension_view_views.h b/chrome/browser/ui/views/extensions/extension_view_views.h
index 1e564ddc..031bbeef 100644
--- a/chrome/browser/ui/views/extensions/extension_view_views.h
+++ b/chrome/browser/ui/views/extensions/extension_view_views.h
@@ -56,7 +56,6 @@
   void SetIsClipped(bool is_clipped);
 
   // extensions::ExtensionView:
-  void Init() override;
   Browser* GetBrowser() override;
   gfx::NativeView GetNativeView() override;
   void ResizeDueToAutoResize(const gfx::Size& new_size) override;
diff --git a/chrome/browser/ui/views/frame/browser_header_painter_ash.cc b/chrome/browser/ui/views/frame/browser_header_painter_ash.cc
index 1a245f7..33d6aaf 100644
--- a/chrome/browser/ui/views/frame/browser_header_painter_ash.cc
+++ b/chrome/browser/ui/views/frame/browser_header_painter_ash.cc
@@ -406,7 +406,6 @@
   caption_button_container_->SetButtonImages(
       ash::CAPTION_BUTTON_ICON_MINIMIZE,
       IDR_ASH_BROWSER_WINDOW_CONTROL_ICON_MINIMIZE,
-      IDR_ASH_BROWSER_WINDOW_CONTROL_ICON_MINIMIZE,
       hover_background_id,
       pressed_background_id);
 
@@ -418,26 +417,22 @@
   caption_button_container_->SetButtonImages(
       ash::CAPTION_BUTTON_ICON_MAXIMIZE_RESTORE,
       size_icon_id,
-      size_icon_id,
       hover_background_id,
       pressed_background_id);
 
   caption_button_container_->SetButtonImages(
       ash::CAPTION_BUTTON_ICON_CLOSE,
       IDR_ASH_BROWSER_WINDOW_CONTROL_ICON_CLOSE,
-      IDR_ASH_BROWSER_WINDOW_CONTROL_ICON_CLOSE,
       hover_background_id,
       pressed_background_id);
   caption_button_container_->SetButtonImages(
       ash::CAPTION_BUTTON_ICON_LEFT_SNAPPED,
       IDR_ASH_BROWSER_WINDOW_CONTROL_ICON_LEFT_SNAPPED,
-      IDR_ASH_BROWSER_WINDOW_CONTROL_ICON_LEFT_SNAPPED,
       hover_background_id,
       pressed_background_id);
   caption_button_container_->SetButtonImages(
       ash::CAPTION_BUTTON_ICON_RIGHT_SNAPPED,
       IDR_ASH_BROWSER_WINDOW_CONTROL_ICON_RIGHT_SNAPPED,
-      IDR_ASH_BROWSER_WINDOW_CONTROL_ICON_RIGHT_SNAPPED,
       hover_background_id,
       pressed_background_id);
 }
diff --git a/chrome/browser/ui/views/frame/opaque_browser_frame_view.cc b/chrome/browser/ui/views/frame/opaque_browser_frame_view.cc
index d5891aa..72dd08d 100644
--- a/chrome/browser/ui/views/frame/opaque_browser_frame_view.cc
+++ b/chrome/browser/ui/views/frame/opaque_browser_frame_view.cc
@@ -335,11 +335,9 @@
         BrowserWindow::AVATAR_BUBBLE_MODE_FAST_USER_SWITCH :
         BrowserWindow::AVATAR_BUBBLE_MODE_DEFAULT;
 
-    if (switches::IsFastUserSwitching() || !isRightClick) {
-      browser_view()->ShowAvatarBubbleFromAvatarButton(
-          mode,
-          signin::ManageAccountsParams());
-    }
+    browser_view()->ShowAvatarBubbleFromAvatarButton(
+        mode,
+        signin::ManageAccountsParams());
   }
 }
 
diff --git a/chrome/browser/ui/views/frame/web_app_left_header_view_ash.cc b/chrome/browser/ui/views/frame/web_app_left_header_view_ash.cc
index 23e92563..4f7e18c 100644
--- a/chrome/browser/ui/views/frame/web_app_left_header_view_ash.cc
+++ b/chrome/browser/ui/views/frame/web_app_left_header_view_ash.cc
@@ -26,8 +26,7 @@
       new ash::FrameCaptionButton(this, ash::CAPTION_BUTTON_ICON_BACK);
   back_button_->SetImages(
       ash::CAPTION_BUTTON_ICON_BACK, ash::FrameCaptionButton::ANIMATE_NO,
-      IDR_AURA_WINDOW_CONTROL_ICON_BACK, IDR_AURA_WINDOW_CONTROL_ICON_BACK_I,
-      IDR_AURA_WINDOW_CONTROL_BACKGROUND_H,
+      IDR_AURA_WINDOW_CONTROL_ICON_BACK, IDR_AURA_WINDOW_CONTROL_BACKGROUND_H,
       IDR_AURA_WINDOW_CONTROL_BACKGROUND_P);
   AddChildView(back_button_);
 
@@ -43,10 +42,10 @@
 
 void WebAppLeftHeaderView::Update() {
   int icon_resource = browser_view_->browser()->toolbar_model()->GetIcon();
-  location_icon_->SetImages(
-      ash::CAPTION_BUTTON_ICON_LOCATION, ash::FrameCaptionButton::ANIMATE_NO,
-      icon_resource, icon_resource, IDR_AURA_WINDOW_CONTROL_BACKGROUND_H,
-      IDR_AURA_WINDOW_CONTROL_BACKGROUND_P);
+  location_icon_->SetImages(ash::CAPTION_BUTTON_ICON_LOCATION,
+                            ash::FrameCaptionButton::ANIMATE_NO, icon_resource,
+                            IDR_AURA_WINDOW_CONTROL_BACKGROUND_H,
+                            IDR_AURA_WINDOW_CONTROL_BACKGROUND_P);
 
   back_button_->SetState(
       chrome::IsCommandEnabled(browser_view_->browser(), IDC_BACK)
diff --git a/chrome/browser/ui/views/link_disambiguation/link_disambiguation_popup.cc b/chrome/browser/ui/views/link_disambiguation/link_disambiguation_popup.cc
index 296ce5cac..fa038d58 100644
--- a/chrome/browser/ui/views/link_disambiguation/link_disambiguation_popup.cc
+++ b/chrome/browser/ui/views/link_disambiguation/link_disambiguation_popup.cc
@@ -93,7 +93,8 @@
       (event->location().x() / scale_) + target_rect_.x(),
       (event->location().y() / scale_) + target_rect_.y());
   ui::MouseEvent xform_event(event->type(), xform_location, xform_location,
-      event->flags(), event->changed_button_flags());
+                             ui::EventTimeForNow(), event->flags(),
+                             event->changed_button_flags());
   mouse_cb_.Run(&xform_event);
   event->SetHandled();
 
diff --git a/chrome/browser/ui/views/location_bar/star_view_browsertest.cc b/chrome/browser/ui/views/location_bar/star_view_browsertest.cc
index 8dece781..717562ba 100644
--- a/chrome/browser/ui/views/location_bar/star_view_browsertest.cc
+++ b/chrome/browser/ui/views/location_bar/star_view_browsertest.cc
@@ -18,6 +18,7 @@
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/test_utils.h"
 #include "ui/base/ui_base_switches.h"
+#include "ui/events/event_utils.h"
 
 #if defined(OS_WIN)
 #include "ui/aura/window.h"
@@ -41,12 +42,12 @@
   views::View* star_view =
       browser_view->GetToolbarView()->location_bar()->star_view();
 
-  ui::MouseEvent pressed_event(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(),
-                               ui::EF_LEFT_MOUSE_BUTTON,
-                               ui::EF_LEFT_MOUSE_BUTTON);
-  ui::MouseEvent released_event(ui::ET_MOUSE_RELEASED, gfx::Point(),
-                                gfx::Point(), ui::EF_LEFT_MOUSE_BUTTON,
-                                ui::EF_LEFT_MOUSE_BUTTON);
+  ui::MouseEvent pressed_event(
+      ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(), ui::EventTimeForNow(),
+      ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
+  ui::MouseEvent released_event(
+      ui::ET_MOUSE_RELEASED, gfx::Point(), gfx::Point(), ui::EventTimeForNow(),
+      ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
 
   // Verify that clicking once shows the bookmark bubble.
   EXPECT_FALSE(BookmarkBubbleView::IsShowing());
diff --git a/chrome/browser/ui/views/omnibox/omnibox_view_views_browsertest.cc b/chrome/browser/ui/views/omnibox/omnibox_view_views_browsertest.cc
index 52aaaef..8d309bd 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_view_views_browsertest.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_view_views_browsertest.cc
@@ -326,13 +326,14 @@
   // Simulate a mouse click before dragging the mouse.
   gfx::Point point(omnibox_view_views->x(), omnibox_view_views->y());
   ui::MouseEvent pressed(ui::ET_MOUSE_PRESSED, point, point,
-                         ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
+                         ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
+                         ui::EF_LEFT_MOUSE_BUTTON);
   omnibox_view_views->OnMousePressed(pressed);
   EXPECT_TRUE(omnibox_view->model()->popup_model()->IsOpen());
 
   // Simulate a mouse drag of the omnibox text, and the omnibox should close.
   ui::MouseEvent dragged(ui::ET_MOUSE_DRAGGED, point, point,
-                         ui::EF_LEFT_MOUSE_BUTTON, 0);
+                         ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON, 0);
   omnibox_view_views->OnMouseDragged(dragged);
 
   EXPECT_FALSE(omnibox_view->model()->popup_model()->IsOpen());
@@ -344,7 +345,7 @@
   OmniboxViewViews* view = BrowserView::GetBrowserViewForBrowser(browser())->
       toolbar()->location_bar()->omnibox_view();
   ASSERT_TRUE(view);
-  EXPECT_FALSE(view->GetRenderText()->background_is_transparent());
+  EXPECT_FALSE(view->GetRenderText()->subpixel_rendering_suppressed());
 }
 
 // Tests if executing a command hides touch editing handles.
diff --git a/chrome/browser/ui/views/passwords/credentials_item_view.cc b/chrome/browser/ui/views/passwords/credentials_item_view.cc
index 6a82ec1f..92b68bc8 100644
--- a/chrome/browser/ui/views/passwords/credentials_item_view.cc
+++ b/chrome/browser/ui/views/passwords/credentials_item_view.cc
@@ -30,14 +30,25 @@
                    upper_label_size.height() + lower_label_size.height());
 }
 
-// Returns either full name or username if the former is empty.
-const base::string16& GetUpperLabelText(const autofill::PasswordForm& form) {
-  return form.display_name.empty() ? form.username_value : form.display_name;
+// Returns the bold upper text for the button.
+base::string16 GetUpperLabelText(const autofill::PasswordForm& form,
+                                 CredentialsItemView::Style style) {
+  const base::string16& name = form.display_name.empty() ? form.username_value
+                                                         : form.display_name;
+  switch (style) {
+    case CredentialsItemView::ACCOUNT_CHOOSER:
+      return name;
+    case CredentialsItemView::AUTO_SIGNIN:
+      return l10n_util::GetStringFUTF16(IDS_MANAGE_PASSWORDS_AUTO_SIGNIN_TITLE,
+                                        name);
+  }
+  NOTREACHED();
+  return base::string16();
 }
 
-// Returns IDP information for federated credentials and username or empty
-// string for non-federated ones.
-base::string16 GetLowerLabelText(const autofill::PasswordForm& form) {
+// Returns the lower text for the button.
+base::string16 GetLowerLabelText(const autofill::PasswordForm& form,
+                                 CredentialsItemView::Style style) {
   if (!form.federation_url.is_empty()) {
     return l10n_util::GetStringFUTF16(
         IDS_MANAGE_PASSWORDS_IDENTITY_PROVIDER,
@@ -75,6 +86,7 @@
     views::ButtonListener* button_listener,
     const autofill::PasswordForm& form,
     password_manager::CredentialType credential_type,
+    Style style,
     net::URLRequestContextGetter* request_context)
     : LabelButton(button_listener, base::string16()),
       form_(form),
@@ -102,11 +114,12 @@
 
   ui::ResourceBundle* rb = &ui::ResourceBundle::GetSharedInstance();
   upper_label_ = new views::Label(
-      GetUpperLabelText(form_), rb->GetFontList(ui::ResourceBundle::BoldFont));
+      GetUpperLabelText(form_, style),
+      rb->GetFontList(ui::ResourceBundle::BoldFont));
   upper_label_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
   AddChildView(upper_label_);
 
-  base::string16 lower_text = GetLowerLabelText(form_);
+  base::string16 lower_text = GetLowerLabelText(form_, style);
   if (!lower_text.empty()) {
     lower_label_ = new views::Label(
         lower_text, rb->GetFontList(ui::ResourceBundle::SmallFont));
diff --git a/chrome/browser/ui/views/passwords/credentials_item_view.h b/chrome/browser/ui/views/passwords/credentials_item_view.h
index bfa3c0d..b9cdfa7 100644
--- a/chrome/browser/ui/views/passwords/credentials_item_view.h
+++ b/chrome/browser/ui/views/passwords/credentials_item_view.h
@@ -30,9 +30,15 @@
 class CredentialsItemView : public AccountAvatarFetcherDelegate,
                             public views::LabelButton {
  public:
+  enum Style {
+    ACCOUNT_CHOOSER,
+    AUTO_SIGNIN,
+  };
+
   CredentialsItemView(views::ButtonListener* button_listener,
                       const autofill::PasswordForm& form,
                       password_manager::CredentialType credential_type,
+                      Style style,
                       net::URLRequestContextGetter* request_context);
   ~CredentialsItemView() override;
 
diff --git a/chrome/browser/ui/views/passwords/manage_credential_item_view.cc b/chrome/browser/ui/views/passwords/manage_credential_item_view.cc
new file mode 100644
index 0000000..36de01c
--- /dev/null
+++ b/chrome/browser/ui/views/passwords/manage_credential_item_view.cc
@@ -0,0 +1,121 @@
+// 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 "chrome/browser/ui/views/passwords/manage_credential_item_view.h"
+
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/passwords/manage_passwords_bubble_model.h"
+#include "chrome/browser/ui/views/passwords/credentials_item_view.h"
+#include "chrome/grit/generated_resources.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/resources/grit/ui_resources.h"
+#include "ui/views/controls/button/image_button.h"
+#include "ui/views/controls/label.h"
+#include "ui/views/controls/link.h"
+#include "ui/views/layout/grid_layout.h"
+#include "ui/views/layout/layout_constants.h"
+
+namespace {
+
+enum ColumnSets {
+  TWO_COLUMN_SET,
+};
+
+void BuildColumnSet(views::GridLayout* layout) {
+  views::ColumnSet* column_set = layout->AddColumnSet(TWO_COLUMN_SET);
+
+  // The credential or "Deleted!" field.
+  column_set->AddColumn(views::GridLayout::FILL,
+                        views::GridLayout::CENTER,
+                        1,
+                        views::GridLayout::USE_PREF,
+                        0,
+                        0);
+
+  // The delete button or "Undo!" field.
+  column_set->AddPaddingColumn(0, views::kItemLabelSpacing);
+  column_set->AddColumn(views::GridLayout::TRAILING,
+                        views::GridLayout::CENTER,
+                        0,
+                        views::GridLayout::USE_PREF,
+                        0,
+                        0);
+}
+
+}  // namespace
+
+ManageCredentialItemView::ManageCredentialItemView(
+    ManagePasswordsBubbleModel* model,
+    const autofill::PasswordForm* password_form)
+    : delete_button_(nullptr),
+      undo_link_(nullptr),
+      model_(model),
+      form_deleted_(false) {
+  net::URLRequestContextGetter* request_context =
+      model_->GetProfile()->GetRequestContext();
+  credential_button_.reset(new CredentialsItemView(
+      this, *password_form,
+      password_manager::CredentialType::CREDENTIAL_TYPE_LOCAL,
+      CredentialsItemView::ACCOUNT_CHOOSER, request_context));
+  credential_button_->set_owned_by_client();
+  credential_button_->SetEnabled(false);
+  Refresh();
+}
+
+ManageCredentialItemView::~ManageCredentialItemView() {
+}
+
+void ManageCredentialItemView::Refresh() {
+  RemoveAllChildViews(true);
+  views::GridLayout* layout = new views::GridLayout(this);
+  SetLayoutManager(layout);
+  BuildColumnSet(layout);
+  layout->StartRow(1, TWO_COLUMN_SET);
+  ui::ResourceBundle* rb = &ui::ResourceBundle::GetSharedInstance();
+  if (form_deleted_) {
+    delete_button_ = nullptr;
+    views::Label* removed_account = new views::Label(
+        l10n_util::GetStringUTF16(IDS_MANAGE_PASSWORDS_DELETED_ACCOUNT),
+        rb->GetFontList(ui::ResourceBundle::SmallFont));
+    removed_account->SetHorizontalAlignment(gfx::ALIGN_LEFT);
+    layout->AddView(removed_account);
+    undo_link_ =
+        new views::Link(l10n_util::GetStringUTF16(IDS_MANAGE_PASSWORDS_UNDO));
+    undo_link_->SetHorizontalAlignment(gfx::ALIGN_RIGHT);
+    undo_link_->set_listener(this);
+    undo_link_->SetUnderline(false);
+    undo_link_->SetFontList(rb->GetFontList(ui::ResourceBundle::BoldFont));
+    layout->AddView(undo_link_);
+  } else {
+    undo_link_ = nullptr;
+    layout->AddView(credential_button_.get());
+    delete_button_ = new views::ImageButton(this);
+    delete_button_->SetImage(views::ImageButton::STATE_NORMAL,
+                             rb->GetImageNamed(IDR_CLOSE_2).ToImageSkia());
+    delete_button_->SetImage(views::ImageButton::STATE_HOVERED,
+                             rb->GetImageNamed(IDR_CLOSE_2_H).ToImageSkia());
+    delete_button_->SetImage(views::ImageButton::STATE_PRESSED,
+                             rb->GetImageNamed(IDR_CLOSE_2_P).ToImageSkia());
+    layout->AddView(delete_button_);
+  }
+
+  GetLayoutManager()->Layout(this);
+}
+
+void ManageCredentialItemView::ButtonPressed(views::Button* sender,
+                                             const ui::Event& event) {
+  DCHECK_EQ(delete_button_, sender);
+  form_deleted_ = true;
+  // TODO(vasilii): notify |model_| about the deletion.
+  Refresh();
+}
+
+void ManageCredentialItemView::LinkClicked(views::Link* source,
+                                           int event_flags) {
+  DCHECK_EQ(undo_link_, source);
+  form_deleted_ = false;
+  // TODO(vasilii): notify |model_| about adding.
+  Refresh();
+}
diff --git a/chrome/browser/ui/views/passwords/manage_credential_item_view.h b/chrome/browser/ui/views/passwords/manage_credential_item_view.h
new file mode 100644
index 0000000..e712957f
--- /dev/null
+++ b/chrome/browser/ui/views/passwords/manage_credential_item_view.h
@@ -0,0 +1,55 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_VIEWS_PASSWORDS_MANAGE_CREDENTIAL_ITEM_VIEW_H_
+#define CHROME_BROWSER_UI_VIEWS_PASSWORDS_MANAGE_CREDENTIAL_ITEM_VIEW_H_
+
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "ui/views/controls/button/button.h"
+#include "ui/views/controls/link_listener.h"
+
+class CredentialsItemView;
+class ManagePasswordsBubbleModel;
+
+namespace autofill {
+struct PasswordForm;
+}
+
+namespace views {
+class ImageButton;
+class Link;
+}
+
+// A custom view that represents one credential row. There are two states:
+// * Present a credential to the user for management.
+// * Offer the user the ability to undo a deletion action.
+class ManageCredentialItemView : public views::View,
+                                 public views::ButtonListener,
+                                 public views::LinkListener {
+ public:
+  ManageCredentialItemView(ManagePasswordsBubbleModel* model,
+                           const autofill::PasswordForm* password_form);
+  ~ManageCredentialItemView() override;
+
+ private:
+  void Refresh();
+
+  // ButtonListener:
+  void ButtonPressed(views::Button* sender, const ui::Event& event) override;
+
+  // LinkListener:
+  void LinkClicked(views::Link* source, int event_flags) override;
+
+  scoped_ptr<CredentialsItemView> credential_button_;
+  views::ImageButton* delete_button_;
+  views::Link* undo_link_;
+
+  ManagePasswordsBubbleModel* const model_;
+  bool form_deleted_;
+
+  DISALLOW_COPY_AND_ASSIGN(ManageCredentialItemView);
+};
+
+#endif  // CHROME_BROWSER_UI_VIEWS_PASSWORDS_MANAGE_CREDENTIAL_ITEM_VIEW_H_
diff --git a/chrome/browser/ui/views/passwords/manage_passwords_bubble_view.cc b/chrome/browser/ui/views/passwords/manage_passwords_bubble_view.cc
index eea0fb7..e90759b 100644
--- a/chrome/browser/ui/views/passwords/manage_passwords_bubble_view.cc
+++ b/chrome/browser/ui/views/passwords/manage_passwords_bubble_view.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/ui/views/passwords/manage_passwords_bubble_view.h"
 
+#include "base/timer/timer.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_finder.h"
@@ -13,6 +14,7 @@
 #include "chrome/browser/ui/passwords/save_password_refusal_combobox_model.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
 #include "chrome/browser/ui/views/passwords/credentials_item_view.h"
+#include "chrome/browser/ui/views/passwords/manage_credential_item_view.h"
 #include "chrome/browser/ui/views/passwords/manage_password_items_view.h"
 #include "chrome/browser/ui/views/passwords/manage_passwords_icon_view.h"
 #include "chrome/grit/generated_resources.h"
@@ -39,6 +41,7 @@
 
 namespace {
 
+const int kAutoSigninToastTimeout = 5;
 const int kDesiredBubbleWidth = 370;
 
 enum ColumnSetType {
@@ -246,8 +249,9 @@
   for (autofill::PasswordForm* form : password_forms) {
     // Add the title to the layout with appropriate padding.
     layout->StartRow(0, SINGLE_VIEW_COLUMN_SET);
-    layout->AddView(
-        new CredentialsItemView(this, *form, type, request_context));
+    layout->AddView(new CredentialsItemView(
+        this, *form, type, CredentialsItemView::ACCOUNT_CHOOSER,
+        request_context));
   }
 }
 
@@ -265,6 +269,55 @@
   parent_->Close();
 }
 
+// ManagePasswordsBubbleView::AutoSigninView ----------------------------------
+
+// A view containing just one credential that was used for for automatic signing
+// in.
+class ManagePasswordsBubbleView::AutoSigninView
+    : public views::View,
+      public views::ButtonListener {
+ public:
+  explicit AutoSigninView(ManagePasswordsBubbleView* parent);
+
+ private:
+  // views::ButtonListener:
+  void ButtonPressed(views::Button* sender, const ui::Event& event) override;
+
+  void OnTimer();
+
+  base::OneShotTimer<AutoSigninView> timer_;
+  ManagePasswordsBubbleView* parent_;
+};
+
+ManagePasswordsBubbleView::AutoSigninView::AutoSigninView(
+    ManagePasswordsBubbleView* parent)
+    : parent_(parent) {
+  SetLayoutManager(new views::FillLayout);
+  CredentialsItemView* credential = new CredentialsItemView(
+      this,
+      parent_->model()->pending_password(),
+      password_manager::CredentialType::CREDENTIAL_TYPE_LOCAL,
+      CredentialsItemView::AUTO_SIGNIN,
+      parent_->model()->GetProfile()->GetRequestContext());
+  AddChildView(credential);
+  // TODO(vasilii): enable the button to switch to the "Managed" state.
+  credential->SetEnabled(false);
+  parent_->set_initially_focused_view(credential);
+
+  timer_.Start(FROM_HERE, base::TimeDelta::FromSeconds(kAutoSigninToastTimeout),
+               this, &AutoSigninView::OnTimer);
+}
+
+void ManagePasswordsBubbleView::AutoSigninView::ButtonPressed(
+    views::Button* sender, const ui::Event& event) {
+  // TODO(vasilii): close the toast and switch to the "Managed" state.
+}
+
+void ManagePasswordsBubbleView::AutoSigninView::OnTimer() {
+  parent_->model()->OnAutoSignInToastTimeout();
+  parent_->Close();
+}
+
 // ManagePasswordsBubbleView::AskUserToSubmitURLView -------------------------
 
 // Asks users if they want to report the URL when the password manager failed
@@ -645,6 +698,40 @@
   parent_->Close();
 }
 
+// ManagePasswordsBubbleView::ManageAccountsView ------------------------------
+
+// A view offering the user a list of his currently saved through the Credential
+// Manager API accounts for the current page.
+class ManagePasswordsBubbleView::ManageAccountsView : public views::View {
+ public:
+   explicit ManageAccountsView(ManagePasswordsBubbleView* parent);
+
+ private:
+   ManagePasswordsBubbleView* parent_;
+};
+
+ManagePasswordsBubbleView::ManageAccountsView::ManageAccountsView(
+    ManagePasswordsBubbleView* parent)
+    : parent_(parent) {
+  views::GridLayout* layout = new views::GridLayout(this);
+  layout->set_minimum_size(gfx::Size(kDesiredBubbleWidth, 0));
+  SetLayoutManager(layout);
+
+  // Add the title.
+  BuildColumnSet(layout, SINGLE_VIEW_COLUMN_SET);
+  AddTitleRow(layout, parent_->model());
+
+  for (const autofill::PasswordForm* form :
+           parent_->model()->local_pending_credentials()) {
+    // Add the title to the layout with appropriate padding.
+    layout->StartRow(0, SINGLE_VIEW_COLUMN_SET);
+    layout->AddView(new ManageCredentialItemView(parent_->model(), form));
+  }
+
+  // Extra padding for visual awesomeness.
+  layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
+}
+
 // ManagePasswordsBubbleView::BlacklistedView ---------------------------------
 
 // A view offering the user the ability to re-enable the password manager for
@@ -974,6 +1061,8 @@
   } else if (model()->state() ==
                  password_manager::ui::CREDENTIAL_REQUEST_STATE) {
     AddChildView(new AccountChooserView(this));
+  } else if (model()->state() == password_manager::ui::AUTO_SIGNIN_STATE) {
+    AddChildView(new AutoSigninView(this));
   } else {
     AddChildView(new ManageView(this));
   }
diff --git a/chrome/browser/ui/views/passwords/manage_passwords_bubble_view.h b/chrome/browser/ui/views/passwords/manage_passwords_bubble_view.h
index 6bf90f5..00dbccac 100644
--- a/chrome/browser/ui/views/passwords/manage_passwords_bubble_view.h
+++ b/chrome/browser/ui/views/passwords/manage_passwords_bubble_view.h
@@ -52,9 +52,11 @@
  private:
   class AskUserToSubmitURLView;
   class AccountChooserView;
+  class AutoSigninView;
   class BlacklistedView;
   class ConfirmNeverView;
   class ManageView;
+  class ManageAccountsView;
   class PendingView;
   class SaveConfirmationView;
   class WebContentMouseHandler;
diff --git a/chrome/browser/ui/views/passwords/manage_passwords_icon_view_browsertest.cc b/chrome/browser/ui/views/passwords/manage_passwords_icon_view_browsertest.cc
index ce76ca8..1c26bac 100644
--- a/chrome/browser/ui/views/passwords/manage_passwords_icon_view_browsertest.cc
+++ b/chrome/browser/ui/views/passwords/manage_passwords_icon_view_browsertest.cc
@@ -16,6 +16,7 @@
 #include "content/public/test/test_utils.h"
 #include "grit/theme_resources.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "ui/events/event_utils.h"
 
 class ManagePasswordsIconViewTest : public ManagePasswordsTest {
  public:
@@ -84,8 +85,8 @@
   SetupPendingPassword();
   EXPECT_TRUE(view()->visible());
   EXPECT_TRUE(view()->active());
-  ui::MouseEvent mouse_down(ui::ET_MOUSE_PRESSED,
-                            gfx::Point(10, 10), gfx::Point(900, 60),
+  ui::MouseEvent mouse_down(ui::ET_MOUSE_PRESSED, gfx::Point(10, 10),
+                            gfx::Point(900, 60), ui::EventTimeForNow(),
                             ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
   view()->OnMousePressed(mouse_down);
   // Wait for the command execution to close the bubble.
diff --git a/chrome/browser/ui/views/platform_keys_certificate_selector_chromeos.cc b/chrome/browser/ui/views/platform_keys_certificate_selector_chromeos.cc
new file mode 100644
index 0000000..b5203ca
--- /dev/null
+++ b/chrome/browser/ui/views/platform_keys_certificate_selector_chromeos.cc
@@ -0,0 +1,61 @@
+// 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 "chrome/browser/ui/views/platform_keys_certificate_selector_chromeos.h"
+
+#include "base/memory/ref_counted.h"
+#include "base/strings/string16.h"
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/grit/generated_resources.h"
+#include "ui/base/l10n/l10n_util.h"
+
+namespace chromeos {
+
+PlatformKeysCertificateSelector::PlatformKeysCertificateSelector(
+    const net::CertificateList& certificates,
+    const std::string& extension_name,
+    const CertificateSelectedCallback& callback,
+    content::WebContents* web_contents)
+    : CertificateSelector(certificates, web_contents),
+      extension_name_(extension_name),
+      callback_(callback) {
+}
+
+PlatformKeysCertificateSelector::~PlatformKeysCertificateSelector() {
+}
+
+void PlatformKeysCertificateSelector::Init() {
+  const base::string16 text =
+      l10n_util::GetStringFUTF16(IDS_PLATFORM_KEYS_SELECT_CERT_DIALOG_TEXT,
+                                 base::ASCIIToUTF16(extension_name_));
+  CertificateSelector::InitWithText(text);
+}
+
+bool PlatformKeysCertificateSelector::Cancel() {
+  callback_.Run(nullptr);
+  return true;
+}
+
+bool PlatformKeysCertificateSelector::Accept() {
+  scoped_refptr<net::X509Certificate> cert = GetSelectedCert();
+  if (!cert)
+    return false;
+  callback_.Run(cert);
+  return true;
+}
+
+void ShowPlatformKeysCertificateSelector(
+    content::WebContents* web_contents,
+    const std::string& extension_name,
+    const net::CertificateList& certificates,
+    const base::Callback<void(const scoped_refptr<net::X509Certificate>&)>&
+        callback) {
+  PlatformKeysCertificateSelector* selector =
+      new PlatformKeysCertificateSelector(certificates, extension_name,
+                                          callback, web_contents);
+  selector->Init();
+  selector->Show();
+}
+
+}  // namespace chromeos
diff --git a/chrome/browser/ui/views/platform_keys_certificate_selector_chromeos.h b/chrome/browser/ui/views/platform_keys_certificate_selector_chromeos.h
new file mode 100644
index 0000000..74e86bb
--- /dev/null
+++ b/chrome/browser/ui/views/platform_keys_certificate_selector_chromeos.h
@@ -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.
+
+#ifndef CHROME_BROWSER_UI_VIEWS_PLATFORM_KEYS_CERTIFICATE_SELECTOR_CHROMEOS_H_
+#define CHROME_BROWSER_UI_VIEWS_PLATFORM_KEYS_CERTIFICATE_SELECTOR_CHROMEOS_H_
+
+#include <string>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "chrome/browser/ui/platform_keys_certificate_selector_chromeos.h"
+#include "chrome/browser/ui/views/certificate_selector.h"
+#include "net/cert/x509_certificate.h"
+
+// Chrome should access this certificate selector only through the interface
+// chrome/browser/ui/platform_keys_certificate_selector_chromeos.h .
+
+namespace content {
+class WebContents;
+}
+
+namespace chromeos {
+
+// A certificate selector dialog that explains to the user that an extension
+// requests access to certificates.
+class PlatformKeysCertificateSelector : public chrome::CertificateSelector {
+ public:
+  PlatformKeysCertificateSelector(const net::CertificateList& certificates,
+                                  const std::string& extension_name,
+                                  const CertificateSelectedCallback& callback,
+                                  content::WebContents* web_contents);
+  ~PlatformKeysCertificateSelector() override;
+
+  void Init();
+
+  // chrome::CertificateSelector:
+  bool Cancel() override;
+  bool Accept() override;
+
+ private:
+  const std::string extension_name_;
+  const CertificateSelectedCallback callback_;
+
+  DISALLOW_COPY_AND_ASSIGN(PlatformKeysCertificateSelector);
+};
+
+}  // namespace chromeos
+
+#endif  // CHROME_BROWSER_UI_VIEWS_PLATFORM_KEYS_CERTIFICATE_SELECTOR_CHROMEOS_H_
diff --git a/chrome/browser/ui/views/profiles/profile_chooser_view_browsertest.cc b/chrome/browser/ui/views/profiles/profile_chooser_view_browsertest.cc
index fb51c78..a159358 100644
--- a/chrome/browser/ui/views/profiles/profile_chooser_view_browsertest.cc
+++ b/chrome/browser/ui/views/profiles/profile_chooser_view_browsertest.cc
@@ -27,6 +27,7 @@
 #include "components/signin/core/common/profile_management_switches.h"
 #include "content/public/test/test_utils.h"
 #include "extensions/browser/extension_registry.h"
+#include "ui/events/event_utils.h"
 
 // ChromeOS and mobile platforms don't have a ProfileChooserView.
 #if !defined(OS_CHROMEOS) && !defined(OS_ANDROID) && !defined(OS_IOS)
@@ -97,7 +98,8 @@
 
     ProfileChooserView::close_on_deactivate_for_testing_ = false;
 
-    ui::MouseEvent e(ui::ET_MOUSE_RELEASED, gfx::Point(), gfx::Point(), 0, 0);
+    ui::MouseEvent e(ui::ET_MOUSE_RELEASED, gfx::Point(), gfx::Point(),
+                     ui::EventTimeForNow(), 0, 0);
     button->NotifyClick(e);
     base::MessageLoop::current()->RunUntilIdle();
     EXPECT_TRUE(ProfileChooserView::IsShowing());
@@ -113,7 +115,8 @@
   }
 
   void ClickProfileChooserViewLockButton() {
-    ui::MouseEvent e(ui::ET_MOUSE_RELEASED, gfx::Point(), gfx::Point(), 0, 0);
+    ui::MouseEvent e(ui::ET_MOUSE_RELEASED, gfx::Point(), gfx::Point(),
+                     ui::EventTimeForNow(), 0, 0);
     ProfileChooserView::profile_bubble_->ButtonPressed(
         ProfileChooserView::profile_bubble_->lock_button_, e);
   }
diff --git a/chrome/browser/ui/views/ssl_client_certificate_selector.cc b/chrome/browser/ui/views/ssl_client_certificate_selector.cc
index de31ce8f..097195f 100644
--- a/chrome/browser/ui/views/ssl_client_certificate_selector.cc
+++ b/chrome/browser/ui/views/ssl_client_certificate_selector.cc
@@ -4,185 +4,52 @@
 
 #include "chrome/browser/ui/views/ssl_client_certificate_selector.h"
 
-#include "base/compiler_specific.h"
+#include "base/bind.h"
+#include "base/bind_helpers.h"
 #include "base/logging.h"
 #include "base/strings/utf_string_conversions.h"
-#include "chrome/browser/certificate_viewer.h"
 #include "chrome/grit/generated_resources.h"
-#include "components/constrained_window/constrained_window_views.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/web_contents.h"
 #include "net/cert/x509_certificate.h"
 #include "net/ssl/ssl_cert_request_info.h"
 #include "ui/base/l10n/l10n_util.h"
-#include "ui/base/models/table_model.h"
-#include "ui/base/models/table_model_observer.h"
-#include "ui/views/controls/button/label_button.h"
-#include "ui/views/controls/label.h"
-#include "ui/views/controls/table/table_view.h"
-#include "ui/views/layout/grid_layout.h"
-#include "ui/views/layout/layout_constants.h"
 #include "ui/views/widget/widget.h"
-#include "ui/views/window/dialog_client_view.h"
 
 #if defined(USE_NSS)
 #include "chrome/browser/ui/crypto_module_password_dialog_nss.h"
 #endif
 
-///////////////////////////////////////////////////////////////////////////////
-// CertificateSelectorTableModel:
-
-class CertificateSelectorTableModel : public ui::TableModel {
- public:
-  explicit CertificateSelectorTableModel(
-      net::SSLCertRequestInfo* cert_request_info);
-
-  // ui::TableModel implementation:
-  int RowCount() override;
-  base::string16 GetText(int index, int column_id) override;
-  void SetObserver(ui::TableModelObserver* observer) override;
-
- private:
-  std::vector<base::string16> items_;
-
-  DISALLOW_COPY_AND_ASSIGN(CertificateSelectorTableModel);
-};
-
-CertificateSelectorTableModel::CertificateSelectorTableModel(
-    net::SSLCertRequestInfo* cert_request_info) {
-  for (size_t i = 0; i < cert_request_info->client_certs.size(); ++i) {
-    net::X509Certificate* cert = cert_request_info->client_certs[i].get();
-    base::string16 text = l10n_util::GetStringFUTF16(
-        IDS_CERT_SELECTOR_TABLE_CERT_FORMAT,
-        base::UTF8ToUTF16(cert->subject().GetDisplayName()),
-        base::UTF8ToUTF16(cert->issuer().GetDisplayName()));
-    items_.push_back(text);
-  }
-}
-
-int CertificateSelectorTableModel::RowCount() {
-  return items_.size();
-}
-
-base::string16 CertificateSelectorTableModel::GetText(int index,
-                                                      int column_id) {
-  DCHECK_EQ(column_id, 0);
-  DCHECK_GE(index, 0);
-  DCHECK_LT(index, static_cast<int>(items_.size()));
-
-  return items_[index];
-}
-
-void CertificateSelectorTableModel::SetObserver(
-    ui::TableModelObserver* observer) {
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// SSLClientCertificateSelector:
-
 SSLClientCertificateSelector::SSLClientCertificateSelector(
     content::WebContents* web_contents,
     const scoped_refptr<net::SSLCertRequestInfo>& cert_request_info,
     const chrome::SelectCertificateCallback& callback)
-    : SSLClientAuthObserver(web_contents->GetBrowserContext(),
-                            cert_request_info, callback),
-      model_(new CertificateSelectorTableModel(cert_request_info.get())),
-      web_contents_(web_contents),
-      table_(NULL),
-      view_cert_button_(NULL) {
+    : CertificateSelector(cert_request_info->client_certs, web_contents),
+      SSLClientAuthObserver(web_contents->GetBrowserContext(),
+                            cert_request_info,
+                            callback) {
   DVLOG(1) << __FUNCTION__;
 }
 
 SSLClientCertificateSelector::~SSLClientCertificateSelector() {
-  table_->SetModel(NULL);
 }
 
 void SSLClientCertificateSelector::Init() {
-  views::GridLayout* layout = views::GridLayout::CreatePanel(this);
-  SetLayoutManager(layout);
-
-  const int column_set_id = 0;
-  views::ColumnSet* column_set = layout->AddColumnSet(column_set_id);
-  column_set->AddColumn(
-      views::GridLayout::FILL, views::GridLayout::FILL,
-      1, views::GridLayout::USE_PREF, 0, 0);
-
-  layout->StartRow(0, column_set_id);
-  base::string16 text = l10n_util::GetStringFUTF16(
-      IDS_CLIENT_CERT_DIALOG_TEXT,
-      base::ASCIIToUTF16(cert_request_info()->host_and_port.ToString()));
-  views::Label* label = new views::Label(text);
-  label->SetMultiLine(true);
-  label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
-  label->SetAllowCharacterBreak(true);
-  layout->AddView(label);
-
-  layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
-
-  // The dimensions of the certificate selector table view, in pixels.
-  static const int kTableViewWidth = 400;
-  static const int kTableViewHeight = 100;
-
-  CreateCertTable();
-  layout->StartRow(1, column_set_id);
-  layout->AddView(table_->CreateParentIfNecessary(), 1, 1,
-                  views::GridLayout::FILL,
-                  views::GridLayout::FILL, kTableViewWidth, kTableViewHeight);
-
-  layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
-
   StartObserving();
-
-  constrained_window::ShowWebModalDialogViews(this, web_contents_);
-
-  // Select the first row automatically.  This must be done after the dialog has
-  // been created.
-  table_->Select(0);
+  InitWithText(l10n_util::GetStringFUTF16(
+      IDS_CLIENT_CERT_DIALOG_TEXT,
+      base::ASCIIToUTF16(cert_request_info()->host_and_port.ToString())));
 }
 
-net::X509Certificate* SSLClientCertificateSelector::GetSelectedCert() const {
-  int selected = table_->FirstSelectedRow();
-  if (selected >= 0 &&
-      selected < static_cast<int>(cert_request_info()->client_certs.size()))
-    return cert_request_info()->client_certs[selected].get();
-  return NULL;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// SSLClientAuthObserver implementation:
-
 void SSLClientCertificateSelector::OnCertSelectedByNotification() {
   DVLOG(1) << __FUNCTION__;
   GetWidget()->Close();
 }
 
-///////////////////////////////////////////////////////////////////////////////
-// DialogDelegateView implementation:
-
-bool SSLClientCertificateSelector::CanResize() const {
-  return true;
-}
-
-base::string16 SSLClientCertificateSelector::GetWindowTitle() const {
-  return l10n_util::GetStringUTF16(IDS_CLIENT_CERT_DIALOG_TITLE);
-}
-
-void SSLClientCertificateSelector::DeleteDelegate() {
-  DVLOG(1) << __FUNCTION__;
-  delete this;
-}
-
-bool SSLClientCertificateSelector::IsDialogButtonEnabled(
-    ui::DialogButton button) const {
-  if (button == ui::DIALOG_BUTTON_OK)
-    return !!GetSelectedCert();
-  return true;
-}
-
 bool SSLClientCertificateSelector::Cancel() {
   DVLOG(1) << __FUNCTION__;
   StopObserving();
-  CertificateSelected(NULL);
+  CertificateSelected(nullptr);
   return true;
 }
 
@@ -196,13 +63,10 @@
     StopObserving();
 #if defined(USE_NSS)
     chrome::UnlockCertSlotIfNecessary(
-        cert.get(),
-        chrome::kCryptoModulePasswordClientAuth,
-        cert_request_info()->host_and_port,
-        GetWidget()->GetNativeView(),
+        cert.get(), chrome::kCryptoModulePasswordClientAuth,
+        cert_request_info()->host_and_port, GetWidget()->GetNativeView(),
         base::Bind(&SSLClientCertificateSelector::Unlocked,
-                   base::Unretained(this),
-                   cert));
+                   base::Unretained(this), cert));
 #else
     Unlocked(cert.get());
 #endif
@@ -212,58 +76,6 @@
   return false;
 }
 
-views::View* SSLClientCertificateSelector::GetInitiallyFocusedView() {
-  return table_;
-}
-
-views::View* SSLClientCertificateSelector::CreateExtraView() {
-  DCHECK(!view_cert_button_);
-  view_cert_button_ = new views::LabelButton(this,
-      l10n_util::GetStringUTF16(IDS_PAGEINFO_CERT_INFO_BUTTON));
-  view_cert_button_->SetStyle(views::Button::STYLE_BUTTON);
-  return view_cert_button_;
-}
-
-ui::ModalType SSLClientCertificateSelector::GetModalType() const {
-  return ui::MODAL_TYPE_CHILD;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// views::ButtonListener implementation:
-
-void SSLClientCertificateSelector::ButtonPressed(
-    views::Button* sender, const ui::Event& event) {
-  if (sender == view_cert_button_) {
-    net::X509Certificate* cert = GetSelectedCert();
-    if (cert)
-      ShowCertificateViewer(web_contents_,
-                            web_contents_->GetTopLevelNativeWindow(),
-                            cert);
-  }
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// views::TableViewObserver implementation:
-void SSLClientCertificateSelector::OnSelectionChanged() {
-  GetDialogClientView()->ok_button()->SetEnabled(!!GetSelectedCert());
-}
-
-void SSLClientCertificateSelector::OnDoubleClick() {
-  if (Accept())
-    GetWidget()->Close();
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// SSLClientCertificateSelector private methods:
-
-void SSLClientCertificateSelector::CreateCertTable() {
-  std::vector<ui::TableColumn> columns;
-  columns.push_back(ui::TableColumn());
-  table_ = new views::TableView(model_.get(), columns, views::TEXT_ONLY,
-                                true /* single_selection */);
-  table_->SetObserver(this);
-}
-
 void SSLClientCertificateSelector::Unlocked(net::X509Certificate* cert) {
   DVLOG(1) << __FUNCTION__;
   CertificateSelected(cert);
@@ -278,8 +90,10 @@
     const chrome::SelectCertificateCallback& callback) {
   DVLOG(1) << __FUNCTION__ << " " << contents;
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  (new SSLClientCertificateSelector(
-       contents, cert_request_info, callback))->Init();
+  SSLClientCertificateSelector* selector =
+      new SSLClientCertificateSelector(contents, cert_request_info, callback);
+  selector->Init();
+  selector->Show();
 }
 
 }  // namespace chrome
diff --git a/chrome/browser/ui/views/ssl_client_certificate_selector.h b/chrome/browser/ui/views/ssl_client_certificate_selector.h
index de4fbed4..d80fc82 100644
--- a/chrome/browser/ui/views/ssl_client_certificate_selector.h
+++ b/chrome/browser/ui/views/ssl_client_certificate_selector.h
@@ -5,38 +5,26 @@
 #ifndef CHROME_BROWSER_UI_VIEWS_SSL_CLIENT_CERTIFICATE_SELECTOR_H_
 #define CHROME_BROWSER_UI_VIEWS_SSL_CLIENT_CERTIFICATE_SELECTOR_H_
 
-#include <string>
-#include <vector>
-
-#include "base/basictypes.h"
-#include "base/strings/string16.h"
+#include "base/macros.h"
 #include "chrome/browser/ssl/ssl_client_auth_observer.h"
 #include "chrome/browser/ssl/ssl_client_certificate_selector.h"
-#include "ui/views/controls/button/button.h"
-#include "ui/views/controls/table/table_view_observer.h"
-#include "ui/views/window/dialog_delegate.h"
+#include "chrome/browser/ui/views/certificate_selector.h"
 
 // This header file exists only for testing.  Chrome should access the
 // certificate selector only through the cross-platform interface
 // chrome/browser/ssl_client_certificate_selector.h.
 
+namespace content {
+class WebContents;
+}
+
 namespace net {
 class SSLCertRequestInfo;
 class X509Certificate;
 }
 
-namespace views {
-class LabelButton;
-class TableView;
-class Widget;
-}
-
-class CertificateSelectorTableModel;
-
-class SSLClientCertificateSelector : public SSLClientAuthObserver,
-                                     public views::DialogDelegateView,
-                                     public views::ButtonListener,
-                                     public views::TableViewObserver {
+class SSLClientCertificateSelector : public chrome::CertificateSelector,
+                                     public SSLClientAuthObserver {
  public:
   SSLClientCertificateSelector(
       content::WebContents* web_contents,
@@ -46,42 +34,17 @@
 
   void Init();
 
-  net::X509Certificate* GetSelectedCert() const;
-
-  // SSLClientAuthObserver implementation:
+  // SSLClientAuthObserver:
   void OnCertSelectedByNotification() override;
 
-  // DialogDelegateView:
-  bool CanResize() const override;
-  base::string16 GetWindowTitle() const override;
-  void DeleteDelegate() override;
-  bool IsDialogButtonEnabled(ui::DialogButton button) const override;
+  // chrome::CertificateSelector:
   bool Cancel() override;
   bool Accept() override;
-  views::View* GetInitiallyFocusedView() override;
-  views::View* CreateExtraView() override;
-  ui::ModalType GetModalType() const override;
-
-  // views::ButtonListener:
-  void ButtonPressed(views::Button* sender, const ui::Event& event) override;
-
-  // views::TableViewObserver:
-  void OnSelectionChanged() override;
-  void OnDoubleClick() override;
 
  private:
-  void CreateCertTable();
-
   // Callback after unlocking certificate slot.
   void Unlocked(net::X509Certificate* cert);
 
-  scoped_ptr<CertificateSelectorTableModel> model_;
-
-  content::WebContents* web_contents_;
-
-  views::TableView* table_;
-  views::LabelButton* view_cert_button_;
-
   DISALLOW_COPY_AND_ASSIGN(SSLClientCertificateSelector);
 };
 
diff --git a/chrome/browser/ui/views/ssl_client_certificate_selector_browsertest.cc b/chrome/browser/ui/views/ssl_client_certificate_selector_browsertest.cc
index 390abd4..63ee691 100644
--- a/chrome/browser/ui/views/ssl_client_certificate_selector_browsertest.cc
+++ b/chrome/browser/ui/views/ssl_client_certificate_selector_browsertest.cc
@@ -26,6 +26,10 @@
 #include "net/url_request/url_request_context_getter.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+#if defined(USE_NSS)
+#include "crypto/scoped_test_nss_db.h"
+#endif
+
 using ::testing::Mock;
 using ::testing::StrictMock;
 using content::BrowserThread;
@@ -45,19 +49,28 @@
   void SetUpInProcessBrowserTestFixture() override {
     base::FilePath certs_dir = net::GetTestCertsDirectory();
 
-    mit_davidben_cert_ = net::ImportCertFromFile(certs_dir, "mit.davidben.der");
-    ASSERT_NE(static_cast<net::X509Certificate*>(NULL),
-              mit_davidben_cert_.get());
-
-    foaf_me_chromium_test_cert_ = net::ImportCertFromFile(
-        certs_dir, "foaf.me.chromium-test-cert.der");
-    ASSERT_NE(static_cast<net::X509Certificate*>(NULL),
-              foaf_me_chromium_test_cert_.get());
+#if defined(USE_NSS)
+    // If USE_NSS, the selector tries to unlock the slot where the private key
+    // of each certificate is stored. If no private key is found, the slot would
+    // be null and the unlock will crash.
+    ASSERT_TRUE(test_nssdb_.is_open());
+    client_cert_1_ = net::ImportClientCertAndKeyFromFile(
+        certs_dir, "client_1.pem", "client_1.pk8", test_nssdb_.slot());
+    client_cert_2_ = net::ImportClientCertAndKeyFromFile(
+        certs_dir, "client_2.pem", "client_2.pk8", test_nssdb_.slot());
+#else
+    // No unlock is attempted if !USE_NSS. Thus, there is no need to import a
+    // private key.
+    client_cert_1_ = net::ImportCertFromFile(certs_dir, "client_1.pem");
+    client_cert_2_ = net::ImportCertFromFile(certs_dir, "client_2.pem");
+#endif
+    ASSERT_NE(nullptr, client_cert_1_.get());
+    ASSERT_NE(nullptr, client_cert_2_.get());
 
     cert_request_info_ = new net::SSLCertRequestInfo;
     cert_request_info_->host_and_port = net::HostPortPair("foo", 123);
-    cert_request_info_->client_certs.push_back(mit_davidben_cert_);
-    cert_request_info_->client_certs.push_back(foaf_me_chromium_test_cert_);
+    cert_request_info_->client_certs.push_back(client_cert_1_);
+    cert_request_info_->client_certs.push_back(client_cert_2_);
   }
 
   void SetUpOnMainThread() override {
@@ -77,8 +90,9 @@
         base::Bind(&SSLClientAuthRequestorMock::CertificateSelected,
                    auth_requestor_));
     selector_->Init();
+    selector_->Show();
 
-    EXPECT_EQ(mit_davidben_cert_.get(), selector_->GetSelectedCert());
+    EXPECT_EQ(client_cert_1_.get(), selector_->GetSelectedCert());
   }
 
   virtual void SetUpOnIOThread() {
@@ -121,12 +135,15 @@
   scoped_refptr<net::URLRequestContextGetter> url_request_context_getter_;
   net::URLRequest* url_request_;
 
-  scoped_refptr<net::X509Certificate> mit_davidben_cert_;
-  scoped_refptr<net::X509Certificate> foaf_me_chromium_test_cert_;
+  scoped_refptr<net::X509Certificate> client_cert_1_;
+  scoped_refptr<net::X509Certificate> client_cert_2_;
   scoped_refptr<net::SSLCertRequestInfo> cert_request_info_;
   scoped_refptr<StrictMock<SSLClientAuthRequestorMock> > auth_requestor_;
   // The selector will be deleted when a cert is selected or the tab is closed.
   SSLClientCertificateSelector* selector_;
+#if defined(USE_NSS)
+  crypto::ScopedTestNSSDB test_nssdb_;
+#endif
 };
 
 class SSLClientCertificateSelectorMultiTabTest
@@ -137,13 +154,13 @@
 
     cert_request_info_1_ = new net::SSLCertRequestInfo;
     cert_request_info_1_->host_and_port = net::HostPortPair("bar", 123);
-    cert_request_info_1_->client_certs.push_back(mit_davidben_cert_);
-    cert_request_info_1_->client_certs.push_back(foaf_me_chromium_test_cert_);
+    cert_request_info_1_->client_certs.push_back(client_cert_1_);
+    cert_request_info_1_->client_certs.push_back(client_cert_2_);
 
     cert_request_info_2_ = new net::SSLCertRequestInfo;
     cert_request_info_2_->host_and_port = net::HostPortPair("bar", 123);
-    cert_request_info_2_->client_certs.push_back(mit_davidben_cert_);
-    cert_request_info_2_->client_certs.push_back(foaf_me_chromium_test_cert_);
+    cert_request_info_2_->client_certs.push_back(client_cert_1_);
+    cert_request_info_2_->client_certs.push_back(client_cert_2_);
   }
 
   void SetUpOnMainThread() override {
@@ -164,16 +181,18 @@
         base::Bind(&SSLClientAuthRequestorMock::CertificateSelected,
                    auth_requestor_1_));
     selector_1_->Init();
+    selector_1_->Show();
     selector_2_ = new SSLClientCertificateSelector(
         browser()->tab_strip_model()->GetWebContentsAt(2),
         auth_requestor_2_->cert_request_info_,
         base::Bind(&SSLClientAuthRequestorMock::CertificateSelected,
                    auth_requestor_2_));
     selector_2_->Init();
+    selector_2_->Show();
 
     EXPECT_EQ(2, browser()->tab_strip_model()->active_index());
-    EXPECT_EQ(mit_davidben_cert_.get(), selector_1_->GetSelectedCert());
-    EXPECT_EQ(mit_davidben_cert_.get(), selector_2_->GetSelectedCert());
+    EXPECT_EQ(client_cert_1_.get(), selector_1_->GetSelectedCert());
+    EXPECT_EQ(client_cert_1_.get(), selector_2_->GetSelectedCert());
   }
 
   void SetUpOnIOThread() override {
@@ -223,8 +242,8 @@
 
     cert_request_info_1_ = new net::SSLCertRequestInfo;
     cert_request_info_1_->host_and_port = net::HostPortPair("foo", 123);
-    cert_request_info_1_->client_certs.push_back(mit_davidben_cert_);
-    cert_request_info_1_->client_certs.push_back(foaf_me_chromium_test_cert_);
+    cert_request_info_1_->client_certs.push_back(client_cert_1_);
+    cert_request_info_1_->client_certs.push_back(client_cert_2_);
   }
 
   void SetUpOnMainThread() override {
@@ -240,8 +259,9 @@
         base::Bind(&SSLClientAuthRequestorMock::CertificateSelected,
                    auth_requestor_1_));
     selector_1_->Init();
+    selector_1_->Show();
 
-    EXPECT_EQ(mit_davidben_cert_.get(), selector_1_->GetSelectedCert());
+    EXPECT_EQ(client_cert_1_.get(), selector_1_->GetSelectedCert());
   }
 
   void SetUpOnIOThread() override {
@@ -288,8 +308,7 @@
   // Let the mock get checked on destruction.
 }
 
-// http://crbug.com/121007
-IN_PROC_BROWSER_TEST_F(SSLClientCertificateSelectorTest, DISABLED_Escape) {
+IN_PROC_BROWSER_TEST_F(SSLClientCertificateSelectorTest, Escape) {
   EXPECT_CALL(*auth_requestor_.get(), CertificateSelected(NULL));
 
   EXPECT_TRUE(ui_test_utils::SendKeyPressSync(
@@ -298,11 +317,9 @@
   Mock::VerifyAndClear(auth_requestor_.get());
 }
 
-// Flaky, http://crbug.com/103534 .
-IN_PROC_BROWSER_TEST_F(SSLClientCertificateSelectorTest,
-                       DISABLED_SelectDefault) {
+IN_PROC_BROWSER_TEST_F(SSLClientCertificateSelectorTest, SelectDefault) {
   EXPECT_CALL(*auth_requestor_.get(),
-              CertificateSelected(mit_davidben_cert_.get()));
+              CertificateSelected(client_cert_1_.get()));
 
   EXPECT_TRUE(ui_test_utils::SendKeyPressSync(
       browser(), ui::VKEY_RETURN, false, false, false, false));
@@ -310,9 +327,7 @@
   Mock::VerifyAndClear(auth_requestor_.get());
 }
 
-// http://crbug.com/121007
-IN_PROC_BROWSER_TEST_F(SSLClientCertificateSelectorMultiTabTest,
-                       DISABLED_Escape) {
+IN_PROC_BROWSER_TEST_F(SSLClientCertificateSelectorMultiTabTest, Escape) {
   // auth_requestor_1_ should get selected automatically by the
   // SSLClientAuthObserver when selector_2_ is accepted, since both 1 & 2 have
   // the same host:port.
@@ -331,23 +346,21 @@
   EXPECT_CALL(*auth_requestor_.get(), CertificateSelected(NULL));
 }
 
-// http://crbug.com/121007
-IN_PROC_BROWSER_TEST_F(SSLClientCertificateSelectorMultiTabTest,
-                       DISABLED_SelectSecond) {
+IN_PROC_BROWSER_TEST_F(SSLClientCertificateSelectorMultiTabTest, SelectSecond) {
   // auth_requestor_1_ should get selected automatically by the
   // SSLClientAuthObserver when selector_2_ is accepted, since both 1 & 2 have
   // the same host:port.
   EXPECT_CALL(*auth_requestor_1_.get(),
-              CertificateSelected(foaf_me_chromium_test_cert_.get()));
+              CertificateSelected(client_cert_2_.get()));
   EXPECT_CALL(*auth_requestor_2_.get(),
-              CertificateSelected(foaf_me_chromium_test_cert_.get()));
+              CertificateSelected(client_cert_2_.get()));
 
   EXPECT_TRUE(ui_test_utils::SendKeyPressSync(
       browser(), ui::VKEY_DOWN, false, false, false, false));
 
-  EXPECT_EQ(mit_davidben_cert_.get(), selector_->GetSelectedCert());
-  EXPECT_EQ(mit_davidben_cert_.get(), selector_1_->GetSelectedCert());
-  EXPECT_EQ(foaf_me_chromium_test_cert_.get(), selector_2_->GetSelectedCert());
+  EXPECT_EQ(client_cert_1_.get(), selector_->GetSelectedCert());
+  EXPECT_EQ(client_cert_1_.get(), selector_1_->GetSelectedCert());
+  EXPECT_EQ(client_cert_2_.get(), selector_2_->GetSelectedCert());
 
   EXPECT_TRUE(ui_test_utils::SendKeyPressSync(
       browser(), ui::VKEY_RETURN, false, false, false, false));
@@ -361,9 +374,7 @@
   EXPECT_CALL(*auth_requestor_.get(), CertificateSelected(NULL));
 }
 
-// http://crbug.com/103529
-IN_PROC_BROWSER_TEST_F(SSLClientCertificateSelectorMultiProfileTest,
-                       DISABLED_Escape) {
+IN_PROC_BROWSER_TEST_F(SSLClientCertificateSelectorMultiProfileTest, Escape) {
   EXPECT_CALL(*auth_requestor_1_.get(), CertificateSelected(NULL));
 
   EXPECT_TRUE(ui_test_utils::SendKeyPressSync(
@@ -377,11 +388,10 @@
   EXPECT_CALL(*auth_requestor_.get(), CertificateSelected(NULL));
 }
 
-// http://crbug.com/103534
 IN_PROC_BROWSER_TEST_F(SSLClientCertificateSelectorMultiProfileTest,
-                       DISABLED_SelectDefault) {
+                       SelectDefault) {
   EXPECT_CALL(*auth_requestor_1_.get(),
-              CertificateSelected(mit_davidben_cert_.get()));
+              CertificateSelected(client_cert_1_.get()));
 
   EXPECT_TRUE(ui_test_utils::SendKeyPressSync(
       browser_1_, ui::VKEY_RETURN, false, false, false, false));
diff --git a/chrome/browser/ui/views/sync/one_click_signin_bubble_view_unittest.cc b/chrome/browser/ui/views/sync/one_click_signin_bubble_view_unittest.cc
index 4fa479d..3466978 100644
--- a/chrome/browser/ui/views/sync/one_click_signin_bubble_view_unittest.cc
+++ b/chrome/browser/ui/views/sync/one_click_signin_bubble_view_unittest.cc
@@ -9,6 +9,7 @@
 #include "base/memory/scoped_ptr.h"
 #include "chrome/browser/ui/sync/one_click_signin_bubble_delegate.h"
 #include "content/public/test/test_utils.h"
+#include "ui/events/event_utils.h"
 #include "ui/views/controls/button/label_button.h"
 #include "ui/views/test/views_test_base.h"
 #include "ui/views/widget/widget.h"
@@ -146,9 +147,8 @@
   // Simulate pressing the OK button.  Set the message loop in the bubble
   // view so that it can be quit once the bubble is hidden.
   views::ButtonListener* listener = view;
-  const ui::MouseEvent event(ui::ET_MOUSE_PRESSED,
-                             gfx::Point(), gfx::Point(),
-                             0, 0);
+  const ui::MouseEvent event(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(),
+                             ui::EventTimeForNow(), 0, 0);
   listener->ButtonPressed(view->ok_button_, event);
 
   // View should no longer be showing.  The message loop will exit once the
@@ -164,9 +164,8 @@
   // Simulate pressing the OK button.  Set the message loop in the bubble
   // view so that it can be quit once the bubble is hidden.
   views::ButtonListener* listener = view;
-  const ui::MouseEvent event(ui::ET_MOUSE_PRESSED,
-                             gfx::Point(), gfx::Point(),
-                             0, 0);
+  const ui::MouseEvent event(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(),
+                             ui::EventTimeForNow(), 0, 0);
   listener->ButtonPressed(view->ok_button_, event);
 
   // View should no longer be showing and sync should start
@@ -184,9 +183,8 @@
   // Simulate pressing the undo button.  Set the message loop in the bubble
   // view so that it can be quit once the bubble is hidden.
   views::ButtonListener* listener = view;
-  const ui::MouseEvent event(ui::ET_MOUSE_PRESSED,
-                             gfx::Point(), gfx::Point(),
-                             0, 0);
+  const ui::MouseEvent event(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(),
+                             ui::EventTimeForNow(), 0, 0);
   listener->ButtonPressed(view->undo_button_, event);
 
   // View should no longer be showing.  The message loop will exit once the
diff --git a/chrome/browser/ui/views/toolbar/reload_button_unittest.cc b/chrome/browser/ui/views/toolbar/reload_button_unittest.cc
index 3355ea6..87579af 100644
--- a/chrome/browser/ui/views/toolbar/reload_button_unittest.cc
+++ b/chrome/browser/ui/views/toolbar/reload_button_unittest.cc
@@ -5,6 +5,7 @@
 #include "base/message_loop/message_loop.h"
 #include "chrome/browser/ui/views/toolbar/reload_button.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "ui/events/event_utils.h"
 
 class ReloadButtonTest : public testing::Test {
  public:
@@ -57,7 +58,8 @@
              false);
 
   // Press the button.  This should start the double-click timer.
-  ui::MouseEvent e(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(), 0, 0);
+  ui::MouseEvent e(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(),
+                   ui::EventTimeForNow(), 0, 0);
   reload_.ButtonPressed(&reload_, e);
   CheckState(true, ReloadButton::MODE_RELOAD, ReloadButton::MODE_RELOAD, true,
              false);
@@ -76,7 +78,8 @@
 
 TEST_F(ReloadButtonTest, DoubleClickTimer) {
   // Start by pressing the button.
-  ui::MouseEvent e(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(), 0, 0);
+  ui::MouseEvent e(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(),
+                   ui::EventTimeForNow(), 0, 0);
   reload_.ButtonPressed(&reload_, e);
 
   // Try to press the button again.  This should do nothing because the timer is
@@ -102,7 +105,8 @@
 
 TEST_F(ReloadButtonTest, DisableOnHover) {
   // Change to stop and hover.
-  ui::MouseEvent e(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(), 0, 0);
+  ui::MouseEvent e(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(),
+                   ui::EventTimeForNow(), 0, 0);
   reload_.ButtonPressed(&reload_, e);
   reload_.ChangeMode(ReloadButton::MODE_STOP, false);
   set_mouse_hovered(true);
@@ -115,7 +119,8 @@
 
   // Un-hover the button, which should allow it to reset.
   set_mouse_hovered(false);
-  ui::MouseEvent e2(ui::ET_MOUSE_MOVED, gfx::Point(), gfx::Point(), 0, 0);
+  ui::MouseEvent e2(ui::ET_MOUSE_MOVED, gfx::Point(), gfx::Point(),
+                    ui::EventTimeForNow(), 0, 0);
   reload_.OnMouseExited(e2);
   CheckState(true, ReloadButton::MODE_RELOAD, ReloadButton::MODE_RELOAD, false,
              false);
@@ -123,7 +128,8 @@
 
 TEST_F(ReloadButtonTest, ResetOnClick) {
   // Change to stop and hover.
-  ui::MouseEvent e(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(), 0, 0);
+  ui::MouseEvent e(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(),
+                   ui::EventTimeForNow(), 0, 0);
   reload_.ButtonPressed(&reload_, e);
   reload_.ChangeMode(ReloadButton::MODE_STOP, false);
   set_mouse_hovered(true);
@@ -137,7 +143,8 @@
 
 TEST_F(ReloadButtonTest, ResetOnTimer) {
   // Change to stop, hover, and change back to reload.
-  ui::MouseEvent e(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(), 0, 0);
+  ui::MouseEvent e(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(),
+                   ui::EventTimeForNow(), 0, 0);
   reload_.ButtonPressed(&reload_, e);
   reload_.ChangeMode(ReloadButton::MODE_STOP, false);
   set_mouse_hovered(true);
diff --git a/chrome/browser/ui/views/toolbar/toolbar_action_view.cc b/chrome/browser/ui/views/toolbar/toolbar_action_view.cc
index 7ef849d..9765e33 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_action_view.cc
+++ b/chrome/browser/ui/views/toolbar/toolbar_action_view.cc
@@ -229,6 +229,12 @@
   return border.Pass();
 }
 
+bool ToolbarActionView::ShouldEnterPushedState(const ui::Event& event) {
+  return view_controller_->HasPopup(GetCurrentWebContents()) ?
+      MenuButton::ShouldEnterPushedState(event) :
+      LabelButton::ShouldEnterPushedState(event);
+}
+
 gfx::ImageSkia ToolbarActionView::GetIconForTest() {
   return GetImage(views::Button::STATE_NORMAL);
 }
diff --git a/chrome/browser/ui/views/toolbar/toolbar_action_view.h b/chrome/browser/ui/views/toolbar/toolbar_action_view.h
index debef33..60b67f0 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_action_view.h
+++ b/chrome/browser/ui/views/toolbar/toolbar_action_view.h
@@ -87,6 +87,10 @@
   // behavior.  MenuButton has the notion of a child popup being shown where the
   // button will stay in the pushed state until the "menu" (a popup in this
   // case) is dismissed.
+  // TODO(devlin): This is a good idea, but it has some funny UI side-effects,
+  // like the fact that label buttons enter a pressed state immediately, but
+  // menu buttons only enter a pressed state on release (if they're draggable).
+  // We should probably just pick a behavior, and stick to it.
   bool Activate() override;
   bool OnMousePressed(const ui::MouseEvent& event) override;
   void OnMouseReleased(const ui::MouseEvent& event) override;
@@ -94,6 +98,7 @@
   bool OnKeyReleased(const ui::KeyEvent& event) override;
   void OnGestureEvent(ui::GestureEvent* event) override;
   scoped_ptr<views::LabelButtonBorder> CreateDefaultBorder() const override;
+  bool ShouldEnterPushedState(const ui::Event& event) override;
 
   // ToolbarActionViewDelegate: (public because called by others).
   void UpdateState() override;
diff --git a/chrome/browser/ui/views/website_settings/website_settings_popup_view.cc b/chrome/browser/ui/views/website_settings/website_settings_popup_view.cc
index b992496b..d865bd2 100644
--- a/chrome/browser/ui/views/website_settings/website_settings_popup_view.cc
+++ b/chrome/browser/ui/views/website_settings/website_settings_popup_view.cc
@@ -390,45 +390,13 @@
 
 void WebsiteSettingsPopupView::LinkClicked(views::Link* source,
                                            int event_flags) {
-  if (source == cookie_dialog_link_) {
-    // Count how often the Collected Cookies dialog is opened.
-    presenter_->RecordWebsiteSettingsAction(
-        WebsiteSettings::WEBSITE_SETTINGS_COOKIES_DIALOG_OPENED);
-
-    new CollectedCookiesViews(web_contents_);
-  } else if (source == certificate_dialog_link_) {
-    gfx::NativeWindow parent = GetAnchorView() ?
-        GetAnchorView()->GetWidget()->GetNativeWindow() : nullptr;
-    presenter_->RecordWebsiteSettingsAction(
-        WebsiteSettings::WEBSITE_SETTINGS_CERTIFICATE_DIALOG_OPENED);
-    ShowCertificateViewerByID(web_contents_, parent, cert_id_);
-  } else if (source == signed_certificate_timestamps_link_) {
-    chrome::ShowSignedCertificateTimestampsViewer(
-        web_contents_, signed_certificate_timestamp_ids_);
-    presenter_->RecordWebsiteSettingsAction(
-        WebsiteSettings::WEBSITE_SETTINGS_TRANSPARENCY_VIEWER_OPENED);
-  } else if (source == help_center_link_) {
-    browser_->OpenURL(
-        content::OpenURLParams(GURL(chrome::kPageInfoHelpCenterURL),
-                               content::Referrer(),
-                               NEW_FOREGROUND_TAB,
-                               ui::PAGE_TRANSITION_LINK,
-                               false));
-    presenter_->RecordWebsiteSettingsAction(
-        WebsiteSettings::WEBSITE_SETTINGS_CONNECTION_HELP_OPENED);
-  } else if (source == site_settings_link_) {
-    // TODO(palmer): This opens the general Content Settings pane, which is OK
-    // for now. But on Android, it opens a page specific to a given origin that
-    // shows all of the settings for that origin. If/when that's available on
-    // desktop we should link to that here, too.
-    browser_->OpenURL(content::OpenURLParams(
-        GURL(chrome::kChromeUIContentSettingsURL), content::Referrer(),
-        NEW_FOREGROUND_TAB, ui::PAGE_TRANSITION_LINK, false));
-    presenter_->RecordWebsiteSettingsAction(
-        WebsiteSettings::WEBSITE_SETTINGS_SITE_SETTINGS_OPENED);
-  } else {
-    NOTREACHED();
-  }
+  // The popup closes automatically when the collected cookies dialog or the
+  // certificate viewer opens. So delay handling of the link clicked to avoid
+  // a crash in the base class which needs to complete the mouse event handling.
+  content::BrowserThread::PostTask(
+      content::BrowserThread::UI, FROM_HERE,
+      base::Bind(&WebsiteSettingsPopupView::HandleLinkClickedAsync,
+                 weak_factory_.GetWeakPtr(), source));
 }
 
 void WebsiteSettingsPopupView::TabSelectedAt(int index) {
@@ -869,3 +837,45 @@
                   views::GridLayout::LEADING);
   layout->AddPaddingRow(0, kConnectionSectionPaddingBottom);
 }
+
+void WebsiteSettingsPopupView::HandleLinkClickedAsync(views::Link* source) {
+  if (source == cookie_dialog_link_) {
+    // Count how often the Collected Cookies dialog is opened.
+    presenter_->RecordWebsiteSettingsAction(
+        WebsiteSettings::WEBSITE_SETTINGS_COOKIES_DIALOG_OPENED);
+
+    new CollectedCookiesViews(web_contents_);
+  } else if (source == certificate_dialog_link_) {
+    gfx::NativeWindow parent = GetAnchorView() ?
+        GetAnchorView()->GetWidget()->GetNativeWindow() : nullptr;
+    presenter_->RecordWebsiteSettingsAction(
+        WebsiteSettings::WEBSITE_SETTINGS_CERTIFICATE_DIALOG_OPENED);
+    ShowCertificateViewerByID(web_contents_, parent, cert_id_);
+  } else if (source == signed_certificate_timestamps_link_) {
+    chrome::ShowSignedCertificateTimestampsViewer(
+        web_contents_, signed_certificate_timestamp_ids_);
+    presenter_->RecordWebsiteSettingsAction(
+        WebsiteSettings::WEBSITE_SETTINGS_TRANSPARENCY_VIEWER_OPENED);
+  } else if (source == help_center_link_) {
+    browser_->OpenURL(
+        content::OpenURLParams(GURL(chrome::kPageInfoHelpCenterURL),
+                               content::Referrer(),
+                               NEW_FOREGROUND_TAB,
+                               ui::PAGE_TRANSITION_LINK,
+                               false));
+    presenter_->RecordWebsiteSettingsAction(
+        WebsiteSettings::WEBSITE_SETTINGS_CONNECTION_HELP_OPENED);
+  } else if (source == site_settings_link_) {
+    // TODO(palmer): This opens the general Content Settings pane, which is OK
+    // for now. But on Android, it opens a page specific to a given origin that
+    // shows all of the settings for that origin. If/when that's available on
+    // desktop we should link to that here, too.
+    browser_->OpenURL(content::OpenURLParams(
+        GURL(chrome::kChromeUIContentSettingsURL), content::Referrer(),
+        NEW_FOREGROUND_TAB, ui::PAGE_TRANSITION_LINK, false));
+    presenter_->RecordWebsiteSettingsAction(
+        WebsiteSettings::WEBSITE_SETTINGS_SITE_SETTINGS_OPENED);
+  } else {
+    NOTREACHED();
+  }
+}
diff --git a/chrome/browser/ui/views/website_settings/website_settings_popup_view.h b/chrome/browser/ui/views/website_settings/website_settings_popup_view.h
index ce647b4..216eda3 100644
--- a/chrome/browser/ui/views/website_settings/website_settings_popup_view.h
+++ b/chrome/browser/ui/views/website_settings/website_settings_popup_view.h
@@ -122,6 +122,11 @@
                               views::Link* secondary_link,
                               views::LabelButton* reset_decisions_button);
 
+  // Used to asynchronously handle clicks since these calls may cause the
+  // destruction of the settings view and the base class window still needs to
+  // be alive to finish handling the mouse or keyboard click.
+  void HandleLinkClickedAsync(views::Link* source);
+
   // The web contents of the current tab. The popup can't live longer than a
   // tab.
   content::WebContents* web_contents_;
diff --git a/chrome/browser/ui/website_settings/website_settings.cc b/chrome/browser/ui/website_settings/website_settings.cc
index b86be9bb..699cf12 100644
--- a/chrome/browser/ui/website_settings/website_settings.cc
+++ b/chrome/browser/ui/website_settings/website_settings.cc
@@ -264,6 +264,7 @@
   switch (type) {
     case CONTENT_SETTINGS_TYPE_GEOLOCATION:
     case CONTENT_SETTINGS_TYPE_MIDI_SYSEX:
+    case CONTENT_SETTINGS_TYPE_FULLSCREEN:
       // TODO(markusheintz): The rule we create here should also change the
       // location permission for iframed content.
       primary_pattern = ContentSettingsPattern::FromURLNoWildcard(site_url_);
@@ -277,7 +278,6 @@
     case CONTENT_SETTINGS_TYPE_JAVASCRIPT:
     case CONTENT_SETTINGS_TYPE_PLUGINS:
     case CONTENT_SETTINGS_TYPE_POPUPS:
-    case CONTENT_SETTINGS_TYPE_FULLSCREEN:
     case CONTENT_SETTINGS_TYPE_MOUSELOCK:
     case CONTENT_SETTINGS_TYPE_AUTOMATIC_DOWNLOADS:
     case CONTENT_SETTINGS_TYPE_PUSH_MESSAGING:
diff --git a/chrome/browser/ui/website_settings/website_settings_unittest.cc b/chrome/browser/ui/website_settings/website_settings_unittest.cc
index e251de1..5f374b36 100644
--- a/chrome/browser/ui/website_settings/website_settings_unittest.cc
+++ b/chrome/browser/ui/website_settings/website_settings_unittest.cc
@@ -226,6 +226,55 @@
   EXPECT_EQ(setting, CONTENT_SETTING_ALLOW);
 }
 
+TEST_F(WebsiteSettingsTest, OnPermissionsChanged_Fullscreen) {
+  // Setup site permissions.
+  HostContentSettingsMap* content_settings =
+      profile()->GetHostContentSettingsMap();
+  ContentSetting setting = content_settings->GetContentSetting(
+      url(), url(), CONTENT_SETTINGS_TYPE_FULLSCREEN, std::string());
+  EXPECT_EQ(setting, CONTENT_SETTING_ASK);
+
+  EXPECT_CALL(*mock_ui(), SetIdentityInfo(_));
+  EXPECT_CALL(*mock_ui(), SetCookieInfo(_));
+  EXPECT_CALL(*mock_ui(), SetFirstVisit(base::string16()));
+  EXPECT_CALL(*mock_ui(), SetSelectedTab(
+      WebsiteSettingsUI::TAB_ID_PERMISSIONS));
+
+  // SetPermissionInfo() is called once initially, and then again every time
+  // OnSitePermissionChanged() is called.
+  // TODO(markusheintz): This is a temporary hack to fix issue:
+  // http://crbug.com/144203.
+#if defined(OS_MACOSX)
+  EXPECT_CALL(*mock_ui(), SetPermissionInfo(_)).Times(3);
+#else
+  EXPECT_CALL(*mock_ui(), SetPermissionInfo(_)).Times(1);
+#endif
+
+  // Execute code under tests.
+  website_settings()->OnSitePermissionChanged(CONTENT_SETTINGS_TYPE_FULLSCREEN,
+                                              CONTENT_SETTING_ALLOW);
+
+  // Verify that the site permissions were changed correctly.
+  setting = content_settings->GetContentSetting(
+      url(), url(), CONTENT_SETTINGS_TYPE_FULLSCREEN, std::string());
+  EXPECT_EQ(setting, CONTENT_SETTING_ALLOW);
+
+  // ... and that the primary pattern must match the secondary one.
+  setting = content_settings->GetContentSetting(
+      url(), GURL("https://test.com"),
+      CONTENT_SETTINGS_TYPE_FULLSCREEN, std::string());
+  EXPECT_EQ(setting, CONTENT_SETTING_ASK);
+
+
+  // Resetting the setting should move the permission back to ASK.
+  website_settings()->OnSitePermissionChanged(CONTENT_SETTINGS_TYPE_FULLSCREEN,
+                                              CONTENT_SETTING_ASK);
+
+  setting = content_settings->GetContentSetting(
+      url(), url(), CONTENT_SETTINGS_TYPE_FULLSCREEN, std::string());
+  EXPECT_EQ(setting, CONTENT_SETTING_ASK);
+}
+
 TEST_F(WebsiteSettingsTest, OnSiteDataAccessed) {
   EXPECT_CALL(*mock_ui(), SetPermissionInfo(_));
   EXPECT_CALL(*mock_ui(), SetIdentityInfo(_));
diff --git a/chrome/browser/ui/webui/app_list/start_page_browsertest.js b/chrome/browser/ui/webui/app_list/start_page_browsertest.js
index eb2099e..5a0511d 100644
--- a/chrome/browser/ui/webui/app_list/start_page_browsertest.js
+++ b/chrome/browser/ui/webui/app_list/start_page_browsertest.js
@@ -162,7 +162,7 @@
 
 TEST_F('AppListStartPageWebUITest', 'SpeechRecognitionState', function() {
   this.mockHandler.expects(once()).setSpeechRecognitionState('READY');
-  appList.startPage.onAppListShown();
+  appList.startPage.onAppListShown(false, true);
   this.mockHandler.expects(once()).setSpeechRecognitionState('RECOGNIZING');
   appList.startPage.toggleSpeechRecognition();
   Mock4JS.verifyAllMocks();
@@ -191,7 +191,7 @@
 
 TEST_F('AppListStartPageWebUITest', 'SpeechRecognition', function() {
   this.mockHandler.expects(once()).setSpeechRecognitionState('READY');
-  appList.startPage.onAppListShown();
+  appList.startPage.onAppListShown(false, true);
   this.mockHandler.expects(once()).setSpeechRecognitionState('RECOGNIZING');
   appList.startPage.toggleSpeechRecognition();
   Mock4JS.verifyAllMocks();
diff --git a/chrome/browser/ui/webui/app_list/start_page_handler.cc b/chrome/browser/ui/webui/app_list/start_page_handler.cc
index 124914b..7868d7d 100644
--- a/chrome/browser/ui/webui/app_list/start_page_handler.cc
+++ b/chrome/browser/ui/webui/app_list/start_page_handler.cc
@@ -8,6 +8,7 @@
 
 #include "base/bind.h"
 #include "base/memory/scoped_ptr.h"
+#include "base/metrics/histogram_macros.h"
 #include "base/values.h"
 #include "base/version.h"
 #include "chrome/browser/profiles/profile.h"
@@ -33,6 +34,18 @@
 
 namespace {
 
+const char kAppListDoodleActionHistogram[] = "Apps.AppListDoodleAction";
+
+// Interactions a user has with the app list doodle. This enum must not have its
+// order altered as it is used in histograms.
+enum DoodleAction {
+  DOODLE_SHOWN = 0,
+  DOODLE_CLICKED,
+  // Add values here.
+
+  DOODLE_ACTION_LAST,
+};
+
 #if defined(OS_CHROMEOS)
 const char kOldHotwordExtensionVersionString[] = "0.1.1.5023";
 #endif
@@ -47,6 +60,12 @@
 
 void StartPageHandler::RegisterMessages() {
   web_ui()->RegisterMessageCallback(
+      "appListShown", base::Bind(&StartPageHandler::HandleAppListShown,
+                                 base::Unretained(this)));
+  web_ui()->RegisterMessageCallback(
+      "doodleClicked", base::Bind(&StartPageHandler::HandleDoodleClicked,
+                                  base::Unretained(this)));
+  web_ui()->RegisterMessageCallback(
       "initialize",
       base::Bind(&StartPageHandler::HandleInitialize, base::Unretained(this)));
   web_ui()->RegisterMessageCallback(
@@ -113,6 +132,19 @@
 }
 #endif
 
+void StartPageHandler::HandleAppListShown(const base::ListValue* args) {
+  bool doodle_shown = false;
+  if (args->GetBoolean(0, &doodle_shown) && doodle_shown) {
+    UMA_HISTOGRAM_ENUMERATION(kAppListDoodleActionHistogram, DOODLE_SHOWN,
+                              DOODLE_ACTION_LAST);
+  }
+}
+
+void StartPageHandler::HandleDoodleClicked(const base::ListValue* args) {
+  UMA_HISTOGRAM_ENUMERATION(kAppListDoodleActionHistogram, DOODLE_CLICKED,
+                            DOODLE_ACTION_LAST);
+}
+
 void StartPageHandler::HandleInitialize(const base::ListValue* args) {
   Profile* profile = Profile::FromWebUI(web_ui());
   StartPageService* service = StartPageService::Get(profile);
diff --git a/chrome/browser/ui/webui/app_list/start_page_handler.h b/chrome/browser/ui/webui/app_list/start_page_handler.h
index 444d20a..f0a5fa6 100644
--- a/chrome/browser/ui/webui/app_list/start_page_handler.h
+++ b/chrome/browser/ui/webui/app_list/start_page_handler.h
@@ -47,6 +47,8 @@
 #endif
 
   // JS callbacks.
+  void HandleAppListShown(const base::ListValue* args);
+  void HandleDoodleClicked(const base::ListValue* args);
   void HandleInitialize(const base::ListValue* args);
   void HandleLaunchApp(const base::ListValue* args);
   void HandleSpeechResult(const base::ListValue* args);
diff --git a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
index 35a9b45..0bbbcce 100644
--- a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
+++ b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
@@ -24,6 +24,7 @@
 #include "chrome/browser/ui/webui/constrained_web_dialog_ui.h"
 #include "chrome/browser/ui/webui/copresence_ui.h"
 #include "chrome/browser/ui/webui/crashes_ui.h"
+#include "chrome/browser/ui/webui/device_log_ui.h"
 #include "chrome/browser/ui/webui/domain_reliability_internals_ui.h"
 #include "chrome/browser/ui/webui/downloads_ui.h"
 #include "chrome/browser/ui/webui/flags_ui.h"
@@ -111,7 +112,6 @@
 #include "chrome/browser/ui/webui/chromeos/certificate_manager_dialog_ui.h"
 #include "chrome/browser/ui/webui/chromeos/choose_mobile_network_ui.h"
 #include "chrome/browser/ui/webui/chromeos/cryptohome_ui.h"
-#include "chrome/browser/ui/webui/chromeos/device_log_ui.h"
 #include "chrome/browser/ui/webui/chromeos/drive_internals_ui.h"
 #include "chrome/browser/ui/webui/chromeos/first_run/first_run_ui.h"
 #include "chrome/browser/ui/webui/chromeos/imageburner/imageburner_ui.h"
@@ -297,6 +297,8 @@
     return &NewWebUI<ConstrainedWebDialogUI>;
   if (url.host() == chrome::kChromeUICrashesHost)
     return &NewWebUI<CrashesUI>;
+  if (url.host() == chrome::kChromeUIDeviceLogHost)
+    return &NewWebUI<chromeos::DeviceLogUI>;
   if (url.host() == chrome::kChromeUIDomainReliabilityInternalsHost)
     return &NewWebUI<DomainReliabilityInternalsUI>;
   if (url.host() == chrome::kChromeUIFlagsHost)
@@ -412,8 +414,6 @@
     return &NewWebUI<chromeos::ChooseMobileNetworkUI>;
   if (url.host() == chrome::kChromeUICryptohomeHost)
     return &NewWebUI<chromeos::CryptohomeUI>;
-  if (url.host() == chrome::kChromeUIDeviceLogHost)
-    return &NewWebUI<chromeos::DeviceLogUI>;
   if (url.host() == chrome::kChromeUIDriveInternalsHost)
     return &NewWebUI<chromeos::DriveInternalsUI>;
   if (url.host() == chrome::kChromeUIImageBurnerHost)
diff --git a/chrome/browser/ui/webui/chromeos/device_log_ui.cc b/chrome/browser/ui/webui/chromeos/device_log_ui.cc
deleted file mode 100644
index cfd47e37..0000000
--- a/chrome/browser/ui/webui/chromeos/device_log_ui.cc
+++ /dev/null
@@ -1,88 +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 "chrome/browser/ui/webui/chromeos/device_log_ui.h"
-
-#include <string>
-
-#include "base/bind.h"
-#include "base/bind_helpers.h"
-#include "base/values.h"
-#include "chrome/common/url_constants.h"
-#include "chrome/grit/generated_resources.h"
-#include "chromeos/device_event_log.h"
-#include "content/public/browser/web_contents.h"
-#include "content/public/browser/web_ui.h"
-#include "content/public/browser/web_ui_data_source.h"
-#include "content/public/browser/web_ui_message_handler.h"
-#include "grit/browser_resources.h"
-
-namespace chromeos {
-
-namespace {
-
-class DeviceLogMessageHandler : public content::WebUIMessageHandler {
- public:
-  DeviceLogMessageHandler() {}
-  ~DeviceLogMessageHandler() override {}
-
-  // WebUIMessageHandler implementation.
-  void RegisterMessages() override {
-    web_ui()->RegisterMessageCallback(
-        "DeviceLog.getLog",
-        base::Bind(&DeviceLogMessageHandler::GetLog,
-                   base::Unretained(this)));
-  }
-
- private:
-  void GetLog(const base::ListValue* value) const {
-    base::StringValue data(chromeos::device_event_log::GetAsString(
-        chromeos::device_event_log::NEWEST_FIRST, "json", "",
-        chromeos::device_event_log::LOG_LEVEL_DEBUG, 0));
-    web_ui()->CallJavascriptFunction("DeviceLogUI.getLogCallback", data);
-  }
-
-  DISALLOW_COPY_AND_ASSIGN(DeviceLogMessageHandler);
-};
-
-}  // namespace
-
-DeviceLogUI::DeviceLogUI(content::WebUI* web_ui)
-    : content::WebUIController(web_ui) {
-  web_ui->AddMessageHandler(new DeviceLogMessageHandler());
-
-  content::WebUIDataSource* html =
-      content::WebUIDataSource::Create(chrome::kChromeUIDeviceLogHost);
-
-  html->AddLocalizedString("titleText", IDS_DEVICE_LOG_TITLE);
-  html->AddLocalizedString("autoRefreshText", IDS_DEVICE_AUTO_REFRESH);
-  html->AddLocalizedString("logRefreshText", IDS_DEVICE_LOG_REFRESH);
-
-  html->AddLocalizedString("logLevelShowText", IDS_DEVICE_LOG_LEVEL_SHOW);
-  html->AddLocalizedString("logLevelErrorText", IDS_DEVICE_LOG_LEVEL_ERROR);
-  html->AddLocalizedString("logLevelUserText", IDS_DEVICE_LOG_LEVEL_USER);
-  html->AddLocalizedString("logLevelEventText", IDS_DEVICE_LOG_LEVEL_EVENT);
-  html->AddLocalizedString("logLevelDebugText", IDS_DEVICE_LOG_LEVEL_DEBUG);
-  html->AddLocalizedString("logLevelFileinfoText", IDS_DEVICE_LOG_FILEINFO);
-  html->AddLocalizedString("logLevelTimeDetailText",
-                           IDS_DEVICE_LOG_TIME_DETAIL);
-
-  html->AddLocalizedString("logTypeLoginText", IDS_DEVICE_LOG_TYPE_LOGIN);
-  html->AddLocalizedString("logTypeNetworkText", IDS_DEVICE_LOG_TYPE_NETWORK);
-  html->AddLocalizedString("logTypePowerText", IDS_DEVICE_LOG_TYPE_POWER);
-
-  html->AddLocalizedString("logEntryFormat", IDS_DEVICE_LOG_ENTRY);
-  html->SetJsonPath("strings.js");
-  html->AddResourcePath("device_log_ui.css", IDR_DEVICE_LOG_UI_CSS);
-  html->AddResourcePath("device_log_ui.js", IDR_DEVICE_LOG_UI_JS);
-  html->SetDefaultResource(IDR_DEVICE_LOG_UI_HTML);
-
-  content::WebUIDataSource::Add(
-      web_ui->GetWebContents()->GetBrowserContext(), html);
-}
-
-DeviceLogUI::~DeviceLogUI() {
-}
-
-}  // namespace chromeos
diff --git a/chrome/browser/ui/webui/chromeos/device_log_ui.h b/chrome/browser/ui/webui/chromeos/device_log_ui.h
deleted file mode 100644
index 343985b..0000000
--- a/chrome/browser/ui/webui/chromeos/device_log_ui.h
+++ /dev/null
@@ -1,23 +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 CHROME_BROWSER_UI_WEBUI_CHROMEOS_DEVICE_LOG_UI_H_
-#define CHROME_BROWSER_UI_WEBUI_CHROMEOS_DEVICE_LOG_UI_H_
-
-#include "content/public/browser/web_ui_controller.h"
-
-namespace chromeos {
-
-class DeviceLogUI : public content::WebUIController {
- public:
-  explicit DeviceLogUI(content::WebUI* web_ui);
-  ~DeviceLogUI() override;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(DeviceLogUI);
-};
-
-}  // namespace chromeos
-
-#endif  // CHROME_BROWSER_UI_WEBUI_CHROMEOS_DEVICE_LOG_UI_H_
diff --git a/chrome/browser/ui/webui/chromeos/login/core_oobe_handler.cc b/chrome/browser/ui/webui/chromeos/login/core_oobe_handler.cc
index 7944878..e55846df 100644
--- a/chrome/browser/ui/webui/chromeos/login/core_oobe_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/core_oobe_handler.cc
@@ -11,6 +11,7 @@
 #include "chrome/browser/chromeos/accessibility/accessibility_manager.h"
 #include "chrome/browser/chromeos/accessibility/magnification_manager.h"
 #include "chrome/browser/chromeos/login/helper.h"
+#include "chrome/browser/chromeos/login/lock/screen_locker.h"
 #include "chrome/browser/chromeos/login/startup_utils.h"
 #include "chrome/browser/chromeos/login/ui/login_display_host_impl.h"
 #include "chrome/browser/chromeos/login/wizard_controller.h"
@@ -418,6 +419,8 @@
   LoginDisplayHost* login_display_host = LoginDisplayHostImpl::default_host();
   if (login_display_host)
     login_display_host->SetStatusAreaVisible(true);
+  if (ScreenLocker::default_screen_locker())
+    ScreenLocker::default_screen_locker()->delegate()->OnHeaderBarVisible();
 }
 
 void CoreOobeHandler::HandleSwitchToNewOobe() {
diff --git a/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc
index 2551a84..7758c65 100644
--- a/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc
@@ -53,6 +53,9 @@
 const char kAuthIframeParentOrigin[] =
     "chrome-extension://mfffpogegjflfpflabcdkioaeobkgjik/";
 
+// TODO(rsorokin): Move this to the proper file.
+const char kMinuteMaidPath[] = "ChromeOsEmbeddedSetup";
+
 void UpdateAuthParams(base::DictionaryValue* params,
                       bool has_users,
                       bool is_enrolling_consumer_management) {
@@ -220,6 +223,14 @@
 
   base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
 
+  if (StartupUtils::IsWebviewSigninEnabled()) {
+    params.SetBoolean("useMinuteMaid", true);
+    if (!command_line->HasSwitch(switches::kGaiaEndpointChromeOS)) {
+      command_line->AppendSwitchASCII(switches::kGaiaEndpointChromeOS,
+                                      kMinuteMaidPath);
+    }
+  }
+
   const GURL gaia_url =
       command_line->HasSwitch(::switches::kGaiaUrl)
           ? GURL(command_line->GetSwitchValueASCII(::switches::kGaiaUrl))
@@ -711,6 +722,10 @@
 }
 
 void GaiaScreenHandler::MaybePreloadAuthExtension() {
+  // TODO(rsorokin): Revert that when issue with hidden webview load will be
+  // fixed.
+  if (StartupUtils::IsWebviewSigninEnabled())
+    return;
   VLOG(1) << "MaybePreloadAuthExtension() call.";
 
   // If cookies clearing was initiated or |dns_clear_task_running_| then auth
diff --git a/chrome/browser/ui/webui/chromeos/login/oobe_ui.h b/chrome/browser/ui/webui/chromeos/login/oobe_ui.h
index a06c6c6..977de2f 100644
--- a/chrome/browser/ui/webui/chromeos/login/oobe_ui.h
+++ b/chrome/browser/ui/webui/chromeos/login/oobe_ui.h
@@ -164,6 +164,10 @@
     return signin_screen_handler_;
   }
 
+  NetworkStateInformer* network_state_informer_for_test() const {
+    return network_state_informer_.get();
+  }
+
  private:
   // Initializes |screen_ids_| and |screen_names_| structures.
   void InitializeScreenMaps();
diff --git a/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.cc
index f098ce1..6d72a2da 100644
--- a/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.cc
@@ -232,26 +232,13 @@
     NetworkErrorModel* network_error_model,
     CoreOobeActor* core_oobe_actor,
     GaiaScreenHandler* gaia_screen_handler)
-    : ui_state_(UI_STATE_UNKNOWN),
-      delegate_(NULL),
-      native_window_delegate_(NULL),
-      show_on_init_(false),
-      oobe_ui_(false),
-      is_account_picker_showing_first_time_(false),
-      network_state_informer_(network_state_informer),
-      webui_visible_(false),
-      preferences_changed_delayed_(false),
+    : network_state_informer_(network_state_informer),
       network_error_model_(network_error_model),
       core_oobe_actor_(core_oobe_actor),
-      is_first_update_state_call_(true),
-      offline_login_active_(false),
-      last_network_state_(NetworkStateInformer::UNKNOWN),
-      has_pending_auth_ui_(false),
       caps_lock_enabled_(chromeos::input_method::InputMethodManager::Get()
                              ->GetImeKeyboard()
                              ->CapsLockIsEnabled()),
       gaia_screen_handler_(gaia_screen_handler),
-      oobe_ui_observer_added_(false),
       histogram_helper_(new ErrorScreensHistogramHelper("Signin")),
       weak_factory_(this) {
   DCHECK(network_state_informer_.get());
@@ -557,6 +544,10 @@
   test_focus_pod_callback_ = callback;
 }
 
+void SigninScreenHandler::ZeroOfflineTimeoutForTesting() {
+  zero_offline_timeout_for_test_ = true;
+}
+
 // SigninScreenHandler, private: -----------------------------------------------
 
 void SigninScreenHandler::ShowImpl() {
@@ -648,7 +639,8 @@
     base::MessageLoop::current()->PostDelayedTask(
         FROM_HERE,
         update_state_closure_.callback(),
-        base::TimeDelta::FromSeconds(kOfflineTimeoutSec));
+        base::TimeDelta::FromSeconds(
+            zero_offline_timeout_for_test_ ? 0 : kOfflineTimeoutSec));
     return;
   }
 
diff --git a/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.h b/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.h
index 01d757d..e1353c0 100644
--- a/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.h
+++ b/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.h
@@ -240,6 +240,12 @@
 
   void SetFocusPODCallbackForTesting(base::Closure callback);
 
+  // To avoid spurious error messages on flaky networks, the offline message is
+  // only shown if the network is offline for a threshold number of seconds.
+  // This method reduces the threshold to zero, allowing the offline message to
+  // show instantaneously in tests.
+  void ZeroOfflineTimeoutForTesting();
+
  private:
   enum UIState {
     UI_STATE_UNKNOWN = 0,
@@ -410,36 +416,37 @@
       const std::string& username) const;
 
   // Current UI state of the signin screen.
-  UIState ui_state_;
+  UIState ui_state_ = UI_STATE_UNKNOWN;
 
   // A delegate that glues this handler with backend LoginDisplay.
-  SigninScreenHandlerDelegate* delegate_;
+  SigninScreenHandlerDelegate* delegate_ = nullptr;
 
   // A delegate used to get gfx::NativeWindow.
-  NativeWindowDelegate* native_window_delegate_;
+  NativeWindowDelegate* native_window_delegate_ = nullptr;
 
   // Whether screen should be shown right after initialization.
-  bool show_on_init_;
+  bool show_on_init_ = false;
 
   // Keeps whether screen should be shown for OOBE.
-  bool oobe_ui_;
+  bool oobe_ui_ = false;
 
   // Is account picker being shown for the first time.
-  bool is_account_picker_showing_first_time_;
+  bool is_account_picker_showing_first_time_ = false;
 
   // Network state informer used to keep signin screen up.
   scoped_refptr<NetworkStateInformer> network_state_informer_;
 
   // Set to true once |LOGIN_WEBUI_VISIBLE| notification is observed.
-  bool webui_visible_;
-  bool preferences_changed_delayed_;
+  bool webui_visible_ = false;
+  bool preferences_changed_delayed_ = false;
 
   NetworkErrorModel* network_error_model_;
   CoreOobeActor* core_oobe_actor_;
 
-  bool is_first_update_state_call_;
-  bool offline_login_active_;
-  NetworkStateInformer::State last_network_state_;
+  bool is_first_update_state_call_ = false;
+  bool offline_login_active_ = false;
+  NetworkStateInformer::State last_network_state_ =
+      NetworkStateInformer::UNKNOWN;
 
   base::CancelableClosure update_state_closure_;
   base::CancelableClosure connecting_closure_;
@@ -449,7 +456,7 @@
   // Whether there is an auth UI pending. This flag is set on receiving
   // NOTIFICATION_AUTH_NEEDED and reset on either NOTIFICATION_AUTH_SUPPLIED or
   // NOTIFICATION_AUTH_CANCELLED.
-  bool has_pending_auth_ui_;
+  bool has_pending_auth_ui_ = false;
 
   bool caps_lock_enabled_;
 
@@ -461,7 +468,7 @@
   scoped_ptr<TouchViewControllerDelegate> max_mode_delegate_;
 
   // Whether consumer management enrollment is in progress.
-  bool is_enrolling_consumer_management_;
+  bool is_enrolling_consumer_management_ = false;
 
   // Input Method Engine state used at signin screen.
   scoped_refptr<input_method::InputMethodManager::State> ime_state_;
@@ -470,7 +477,9 @@
   base::Closure test_focus_pod_callback_;
 
   // True if SigninScreenHandler has already been added to OobeUI observers.
-  bool oobe_ui_observer_added_;
+  bool oobe_ui_observer_added_ = false;
+
+  bool zero_offline_timeout_for_test_ = false;
 
   scoped_ptr<ErrorScreensHistogramHelper> histogram_helper_;
 
diff --git a/chrome/browser/ui/webui/chromeos/login/user_board_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/user_board_screen_handler.cc
index e810a2c..80f8bb7 100644
--- a/chrome/browser/ui/webui/chromeos/login/user_board_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/user_board_screen_handler.cc
@@ -23,6 +23,8 @@
   AddCallback("getUsers", &UserBoardScreenHandler::HandleGetUsers);
   AddCallback("attemptUnlock", &UserBoardScreenHandler::HandleAttemptUnlock);
   AddCallback("hardlockPod", &UserBoardScreenHandler::HandleHardlockPod);
+  AddCallback("recordClickOnLockIcon",
+              &UserBoardScreenHandler::HandleRecordClickOnLockIcon);
 }
 
 void UserBoardScreenHandler::Initialize() {
@@ -45,6 +47,12 @@
   model_->AttemptEasyUnlock(user_id);
 }
 
+void UserBoardScreenHandler::HandleRecordClickOnLockIcon(
+    const std::string& user_id) {
+  CHECK(model_);
+  model_->RecordClickOnLockIcon(user_id);
+}
+
 //----------------- API
 
 void UserBoardScreenHandler::SetPublicSessionDisplayName(
diff --git a/chrome/browser/ui/webui/chromeos/login/user_board_screen_handler.h b/chrome/browser/ui/webui/chromeos/login/user_board_screen_handler.h
index 8560d638..36b1118 100644
--- a/chrome/browser/ui/webui/chromeos/login/user_board_screen_handler.h
+++ b/chrome/browser/ui/webui/chromeos/login/user_board_screen_handler.h
@@ -34,6 +34,7 @@
   void HandleGetUsers();
   void HandleHardlockPod(const std::string& user_id);
   void HandleAttemptUnlock(const std::string& user_id);
+  void HandleRecordClickOnLockIcon(const std::string& user_id);
 
   // UserBoardView implementation:
   void SetPublicSessionDisplayName(const std::string& user_id,
diff --git a/chrome/browser/ui/webui/chromeos/mobile_setup_ui.cc b/chrome/browser/ui/webui/chromeos/mobile_setup_ui.cc
index dffbc15..cf35c04 100644
--- a/chrome/browser/ui/webui/chromeos/mobile_setup_ui.cc
+++ b/chrome/browser/ui/webui/chromeos/mobile_setup_ui.cc
@@ -268,14 +268,12 @@
     int render_process_id,
     int render_frame_id,
     const content::URLDataSource::GotDataCallback& callback) {
-  NetworkHandler::Get()->network_configuration_handler()->GetProperties(
+  NetworkHandler::Get()->network_configuration_handler()->GetShillProperties(
       path,
       base::Bind(&MobileSetupUIHTMLSource::GetPropertiesAndStartDataRequest,
-                 weak_ptr_factory_.GetWeakPtr(),
-                 callback),
+                 weak_ptr_factory_.GetWeakPtr(), callback),
       base::Bind(&MobileSetupUIHTMLSource::GetPropertiesFailure,
-                 weak_ptr_factory_.GetWeakPtr(),
-                 callback, path));
+                 weak_ptr_factory_.GetWeakPtr(), callback, path));
 }
 
 void MobileSetupUIHTMLSource::GetPropertiesAndStartDataRequest(
@@ -399,15 +397,12 @@
     return;
   }
 
-  NetworkHandler::Get()->network_configuration_handler()->GetProperties(
+  NetworkHandler::Get()->network_configuration_handler()->GetShillProperties(
       network->path(),
       base::Bind(&MobileSetupHandler::GetPropertiesAndCallStatusChanged,
-                 weak_ptr_factory_.GetWeakPtr(),
-                 state,
-                 error_description),
+                 weak_ptr_factory_.GetWeakPtr(), state, error_description),
       base::Bind(&MobileSetupHandler::GetPropertiesFailure,
-                 weak_ptr_factory_.GetWeakPtr(),
-                 network->path(),
+                 weak_ptr_factory_.GetWeakPtr(), network->path(),
                  kJsDeviceStatusChangedCallback));
 }
 
@@ -529,13 +524,12 @@
     }
   }
 
-  NetworkHandler::Get()->network_configuration_handler()->GetProperties(
+  NetworkHandler::Get()->network_configuration_handler()->GetShillProperties(
       network->path(),
       base::Bind(&MobileSetupHandler::GetPropertiesAndCallGetDeviceInfo,
                  weak_ptr_factory_.GetWeakPtr()),
       base::Bind(&MobileSetupHandler::GetPropertiesFailure,
-                 weak_ptr_factory_.GetWeakPtr(),
-                 network->path(),
+                 weak_ptr_factory_.GetWeakPtr(), network->path(),
                  kJsGetDeviceInfoCallback));
 }
 
diff --git a/chrome/browser/ui/webui/chromeos/network_ui.cc b/chrome/browser/ui/webui/chromeos/network_ui.cc
index 6cfb6a7..cadfb75 100644
--- a/chrome/browser/ui/webui/chromeos/network_ui.cc
+++ b/chrome/browser/ui/webui/chromeos/network_ui.cc
@@ -11,11 +11,11 @@
 #include "chrome/browser/extensions/tab_helper.h"
 #include "chrome/common/url_constants.h"
 #include "chrome/grit/generated_resources.h"
-#include "chromeos/device_event_log.h"
 #include "chromeos/network/device_state.h"
 #include "chromeos/network/network_configuration_handler.h"
 #include "chromeos/network/network_state.h"
 #include "chromeos/network/network_state_handler.h"
+#include "components/device_event_log/device_event_log.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_ui.h"
 #include "content/public/browser/web_ui_data_source.h"
@@ -89,7 +89,7 @@
       ErrorCallback(guid, "Error.InvalidNetworkGuid", nullptr);
       return;
     }
-    NetworkHandler::Get()->network_configuration_handler()->GetProperties(
+    NetworkHandler::Get()->network_configuration_handler()->GetShillProperties(
         service_path,
         base::Bind(&NetworkConfigMessageHandler::GetShillPropertiesSuccess,
                    weak_ptr_factory_.GetWeakPtr()),
diff --git a/chrome/browser/ui/webui/device_log_ui.cc b/chrome/browser/ui/webui/device_log_ui.cc
new file mode 100644
index 0000000..6231da6
--- /dev/null
+++ b/chrome/browser/ui/webui/device_log_ui.cc
@@ -0,0 +1,88 @@
+// 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 "chrome/browser/ui/webui/device_log_ui.h"
+
+#include <string>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/values.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/chromium_strings.h"
+#include "chrome/grit/generated_resources.h"
+#include "components/device_event_log/device_event_log.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_data_source.h"
+#include "content/public/browser/web_ui_message_handler.h"
+#include "grit/browser_resources.h"
+
+namespace chromeos {
+
+namespace {
+
+class DeviceLogMessageHandler : public content::WebUIMessageHandler {
+ public:
+  DeviceLogMessageHandler() {}
+  ~DeviceLogMessageHandler() override {}
+
+  // WebUIMessageHandler implementation.
+  void RegisterMessages() override {
+    web_ui()->RegisterMessageCallback(
+        "DeviceLog.getLog",
+        base::Bind(&DeviceLogMessageHandler::GetLog, base::Unretained(this)));
+  }
+
+ private:
+  void GetLog(const base::ListValue* value) const {
+    base::StringValue data(device_event_log::GetAsString(
+        device_event_log::NEWEST_FIRST, "json", "",
+        device_event_log::LOG_LEVEL_DEBUG, 0));
+    web_ui()->CallJavascriptFunction("DeviceLogUI.getLogCallback", data);
+  }
+
+  DISALLOW_COPY_AND_ASSIGN(DeviceLogMessageHandler);
+};
+
+}  // namespace
+
+DeviceLogUI::DeviceLogUI(content::WebUI* web_ui)
+    : content::WebUIController(web_ui) {
+  web_ui->AddMessageHandler(new DeviceLogMessageHandler());
+
+  content::WebUIDataSource* html =
+      content::WebUIDataSource::Create(chrome::kChromeUIDeviceLogHost);
+
+  html->AddLocalizedString("titleText", IDS_DEVICE_LOG_TITLE);
+  html->AddLocalizedString("autoRefreshText", IDS_DEVICE_AUTO_REFRESH);
+  html->AddLocalizedString("logRefreshText", IDS_DEVICE_LOG_REFRESH);
+
+  html->AddLocalizedString("logLevelShowText", IDS_DEVICE_LOG_LEVEL_SHOW);
+  html->AddLocalizedString("logLevelErrorText", IDS_DEVICE_LOG_LEVEL_ERROR);
+  html->AddLocalizedString("logLevelUserText", IDS_DEVICE_LOG_LEVEL_USER);
+  html->AddLocalizedString("logLevelEventText", IDS_DEVICE_LOG_LEVEL_EVENT);
+  html->AddLocalizedString("logLevelDebugText", IDS_DEVICE_LOG_LEVEL_DEBUG);
+  html->AddLocalizedString("logLevelFileinfoText", IDS_DEVICE_LOG_FILEINFO);
+  html->AddLocalizedString("logLevelTimeDetailText",
+                           IDS_DEVICE_LOG_TIME_DETAIL);
+
+  html->AddLocalizedString("logTypeLoginText", IDS_DEVICE_LOG_TYPE_LOGIN);
+  html->AddLocalizedString("logTypeNetworkText", IDS_DEVICE_LOG_TYPE_NETWORK);
+  html->AddLocalizedString("logTypePowerText", IDS_DEVICE_LOG_TYPE_POWER);
+
+  html->AddLocalizedString("logEntryFormat", IDS_DEVICE_LOG_ENTRY);
+  html->SetJsonPath("strings.js");
+  html->AddResourcePath("device_log_ui.css", IDR_DEVICE_LOG_UI_CSS);
+  html->AddResourcePath("device_log_ui.js", IDR_DEVICE_LOG_UI_JS);
+  html->SetDefaultResource(IDR_DEVICE_LOG_UI_HTML);
+
+  content::WebUIDataSource::Add(web_ui->GetWebContents()->GetBrowserContext(),
+                                html);
+}
+
+DeviceLogUI::~DeviceLogUI() {
+}
+
+}  // namespace chromeos
diff --git a/chrome/browser/ui/webui/device_log_ui.h b/chrome/browser/ui/webui/device_log_ui.h
new file mode 100644
index 0000000..e08a33d
--- /dev/null
+++ b/chrome/browser/ui/webui/device_log_ui.h
@@ -0,0 +1,23 @@
+// 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 CHROME_BROWSER_UI_WEBUI_DEVICE_LOG_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_DEVICE_LOG_UI_H_
+
+#include "content/public/browser/web_ui_controller.h"
+
+namespace chromeos {
+
+class DeviceLogUI : public content::WebUIController {
+ public:
+  explicit DeviceLogUI(content::WebUI* web_ui);
+  ~DeviceLogUI() override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(DeviceLogUI);
+};
+
+}  // namespace chromeos
+
+#endif  // CHROME_BROWSER_UI_WEBUI_DEVICE_LOG_UI_H_
diff --git a/chrome/browser/ui/webui/net_internals/net_internals_ui.cc b/chrome/browser/ui/webui/net_internals/net_internals_ui.cc
index f3c02bc0..4877cde 100644
--- a/chrome/browser/ui/webui/net_internals/net_internals_ui.cc
+++ b/chrome/browser/ui/webui/net_internals/net_internals_ui.cc
@@ -995,8 +995,9 @@
     const std::string& onc_blob,
     const std::string& passcode,
     net::NSSCertDatabase* nssdb) {
-  user_manager::User* user = chromeos::ProfileHelper::Get()->GetUserByProfile(
-      Profile::FromWebUI(web_ui()));
+  const user_manager::User* user =
+      chromeos::ProfileHelper::Get()->GetUserByProfile(
+          Profile::FromWebUI(web_ui()));
 
   if (!user) {
     std::string error = "User not found.";
diff --git a/chrome/browser/ui/webui/options/browser_options_handler.cc b/chrome/browser/ui/webui/options/browser_options_handler.cc
index 6d95a509..63eb9444 100644
--- a/chrome/browser/ui/webui/options/browser_options_handler.cc
+++ b/chrome/browser/ui/webui/options/browser_options_handler.cc
@@ -277,6 +277,7 @@
     { "fontSizeLabelSmall", IDS_OPTIONS_FONT_SIZE_LABEL_SMALL },
     { "fontSizeLabelVeryLarge", IDS_OPTIONS_FONT_SIZE_LABEL_VERY_LARGE },
     { "fontSizeLabelVerySmall", IDS_OPTIONS_FONT_SIZE_LABEL_VERY_SMALL },
+    { "googleNowLauncherEnable", IDS_OPTIONS_ENABLE_GOOGLE_NOW },
     { "hideAdvancedSettings", IDS_SETTINGS_HIDE_ADVANCED_SETTINGS },
     { "homePageNtp", IDS_OPTIONS_HOMEPAGE_NTP },
     { "homePageShowHomeButton", IDS_OPTIONS_TOOLBAR_SHOW_HOME_BUTTON },
@@ -537,7 +538,7 @@
   Profile* profile = Profile::FromWebUI(web_ui());
   std::string username = profile->GetProfileUserName();
   if (username.empty()) {
-    user_manager::User* user =
+    const user_manager::User* user =
         chromeos::ProfileHelper::Get()->GetUserByProfile(profile);
     if (user && (user->GetType() != user_manager::USER_TYPE_GUEST))
       username = user->email();
@@ -552,6 +553,8 @@
 
   values->SetString("privacyLearnMoreURL", chrome::kPrivacyLearnMoreURL);
 
+  values->SetBoolean("hasRapporOption", HasRapporOption());
+
   base::string16 rappor_url = base::ASCIIToUTF16(chrome::kRapporLearnMoreURL);
   values->SetString("enableRappor",
       l10n_util::GetStringFUTF16(IDS_OPTIONS_ENABLE_RAPPOR, rappor_url));
@@ -777,6 +780,11 @@
   }
 #endif
   web_ui()->RegisterMessageCallback(
+      "requestGoogleNowAvailable",
+      base::Bind(&BrowserOptionsHandler::HandleRequestGoogleNowAvailable,
+                 base::Unretained(this)));
+
+  web_ui()->RegisterMessageCallback(
       "requestHotwordAvailable",
       base::Bind(&BrowserOptionsHandler::HandleRequestHotwordAvailable,
                  base::Unretained(this)));
@@ -951,8 +959,8 @@
 #if defined(OS_CHROMEOS)
   if (!policy_registrar_) {
     policy_registrar_.reset(new policy::PolicyChangeRegistrar(
-        policy::ProfilePolicyConnectorFactory::GetForProfile(profile)->
-            policy_service(),
+        policy::ProfilePolicyConnectorFactory::GetForBrowserContext(profile)
+            ->policy_service(),
         policy::PolicyNamespace(policy::POLICY_DOMAIN_CHROME, std::string())));
     policy_registrar_->Observe(
         policy::key::kUserAvatarImage,
@@ -1009,11 +1017,11 @@
 
   Profile* profile = Profile::FromWebUI(web_ui());
   OnAccountPictureManagedChanged(
-      policy::ProfilePolicyConnectorFactory::GetForProfile(profile)->
-          policy_service()->GetPolicies(
-              policy::PolicyNamespace(policy::POLICY_DOMAIN_CHROME,
-                                      std::string()))
-             .Get(policy::key::kUserAvatarImage));
+      policy::ProfilePolicyConnectorFactory::GetForBrowserContext(profile)
+          ->policy_service()
+          ->GetPolicies(policy::PolicyNamespace(policy::POLICY_DOMAIN_CHROME,
+                                                std::string()))
+          .Get(policy::key::kUserAvatarImage));
 
   OnWallpaperManagedChanged(
       chromeos::WallpaperManager::Get()->IsPolicyControlled(
@@ -1212,6 +1220,7 @@
   SetupExtensionControlledIndicators();
 
   HandleRequestHotwordAvailable(nullptr);
+  HandleRequestGoogleNowAvailable(nullptr);
 }
 
 void BrowserOptionsHandler::SetDefaultSearchEngine(
@@ -1685,6 +1694,27 @@
       base::StringValue(audio_history_state));
 }
 
+void BrowserOptionsHandler::HandleRequestGoogleNowAvailable(
+    const base::ListValue* args) {
+  bool is_search_provider_google = false;
+  if (template_url_service_ && template_url_service_->loaded()) {
+    const TemplateURL* default_url =
+        template_url_service_->GetDefaultSearchProvider();
+    if (default_url && default_url->HasGoogleBaseURLs(
+            template_url_service_->search_terms_data())) {
+      is_search_provider_google = true;
+    }
+  }
+
+  std::string group = base::FieldTrialList::FindFullName("GoogleNowLauncher");
+  bool has_field_trial = !group.empty() && group != "Disabled";
+
+  bool should_show = is_search_provider_google && has_field_trial;
+  web_ui()->CallJavascriptFunction(
+      "BrowserOptions.setNowSectionVisible",
+      base::FundamentalValue(should_show));
+}
+
 void BrowserOptionsHandler::HandleRequestHotwordAvailable(
     const base::ListValue* args) {
   Profile* profile = Profile::FromWebUI(web_ui());
diff --git a/chrome/browser/ui/webui/options/browser_options_handler.h b/chrome/browser/ui/webui/options/browser_options_handler.h
index fdbd444..d7a1254 100644
--- a/chrome/browser/ui/webui/options/browser_options_handler.h
+++ b/chrome/browser/ui/webui/options/browser_options_handler.h
@@ -303,6 +303,9 @@
   // Callback for "launchHotwordAudioVerificationApp" message.
   void HandleLaunchHotwordAudioVerificationApp(const base::ListValue* args);
 
+  // Callback for "requestGoogleNowAvailable" message.
+  void HandleRequestGoogleNowAvailable(const base::ListValue* args);
+
   // Callback for "launchEasyUnlockSetup" message.
   void HandleLaunchEasyUnlockSetup(const base::ListValue* args);
 
diff --git a/chrome/browser/ui/webui/options/chromeos/change_picture_options_handler.cc b/chrome/browser/ui/webui/options/chromeos/change_picture_options_handler.cc
index 52bed9d..300965fa 100644
--- a/chrome/browser/ui/webui/options/chromeos/change_picture_options_handler.cc
+++ b/chrome/browser/ui/webui/options/chromeos/change_picture_options_handler.cc
@@ -471,9 +471,10 @@
   NOTREACHED() << "Failed to decode PNG image from WebUI";
 }
 
-user_manager::User* ChangePictureOptionsHandler::GetUser() const {
+const user_manager::User* ChangePictureOptionsHandler::GetUser() const {
   Profile* profile = Profile::FromWebUI(web_ui());
-  user_manager::User* user = ProfileHelper::Get()->GetUserByProfile(profile);
+  const user_manager::User* user =
+      ProfileHelper::Get()->GetUserByProfile(profile);
   if (!user)
     return user_manager::UserManager::Get()->GetActiveUser();
   return user;
diff --git a/chrome/browser/ui/webui/options/chromeos/change_picture_options_handler.h b/chrome/browser/ui/webui/options/chromeos/change_picture_options_handler.h
index 16e3794..d0282e2 100644
--- a/chrome/browser/ui/webui/options/chromeos/change_picture_options_handler.h
+++ b/chrome/browser/ui/webui/options/chromeos/change_picture_options_handler.h
@@ -120,7 +120,7 @@
 
   // Returns user related to current WebUI. If this user doesn't exist,
   // returns active user.
-  user_manager::User* GetUser() const;
+  const user_manager::User* GetUser() const;
 
   scoped_refptr<ui::SelectFileDialog> select_file_dialog_;
 
diff --git a/chrome/browser/ui/webui/options/chromeos/core_chromeos_options_handler.cc b/chrome/browser/ui/webui/options/chromeos/core_chromeos_options_handler.cc
index 432a3b2..c18207f3 100644
--- a/chrome/browser/ui/webui/options/chromeos/core_chromeos_options_handler.cc
+++ b/chrome/browser/ui/webui/options/chromeos/core_chromeos_options_handler.cc
@@ -99,7 +99,8 @@
 // Checks whether this is a secondary user in a multi-profile session.
 bool IsSecondaryUser(Profile* profile) {
   user_manager::UserManager* user_manager = user_manager::UserManager::Get();
-  user_manager::User* user = ProfileHelper::Get()->GetUserByProfile(profile);
+  const user_manager::User* user =
+      ProfileHelper::Get()->GetUserByProfile(profile);
   return user && user->email() != user_manager->GetPrimaryUser()->email();
 }
 
diff --git a/chrome/browser/ui/webui/options/chromeos/cros_language_options_handler.cc b/chrome/browser/ui/webui/options/chromeos/cros_language_options_handler.cc
index d53f9bb..8efb2e3 100644
--- a/chrome/browser/ui/webui/options/chromeos/cros_language_options_handler.cc
+++ b/chrome/browser/ui/webui/options/chromeos/cros_language_options_handler.cc
@@ -187,7 +187,8 @@
   user_manager::UserManager* user_manager = user_manager::UserManager::Get();
 
   // Secondary users and public session users cannot change the locale.
-  user_manager::User* user = ProfileHelper::Get()->GetUserByProfile(profile);
+  const user_manager::User* user =
+      ProfileHelper::Get()->GetUserByProfile(profile);
   if (user &&
       user->email() == user_manager->GetPrimaryUser()->email() &&
       user->GetType() != user_manager::USER_TYPE_PUBLIC_ACCOUNT) {
diff --git a/chrome/browser/ui/webui/policy_ui.cc b/chrome/browser/ui/webui/policy_ui.cc
index 4ab6762..904724a8 100644
--- a/chrome/browser/ui/webui/policy_ui.cc
+++ b/chrome/browser/ui/webui/policy_ui.cc
@@ -43,6 +43,7 @@
 #include "content/public/browser/notification_observer.h"
 #include "content/public/browser/notification_registrar.h"
 #include "content/public/browser/notification_service.h"
+#include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_ui.h"
 #include "content/public/browser/web_ui_data_source.h"
 #include "content/public/browser/web_ui_message_handler.h"
@@ -64,7 +65,6 @@
 #else
 #include "chrome/browser/policy/cloud/user_cloud_policy_manager_factory.h"
 #include "components/policy/core/common/cloud/user_cloud_policy_manager.h"
-#include "content/public/browser/web_contents.h"
 #endif
 
 #if defined(ENABLE_EXTENSIONS)
@@ -780,8 +780,8 @@
 }
 
 policy::PolicyService* PolicyUIHandler::GetPolicyService() const {
-  return policy::ProfilePolicyConnectorFactory::GetForProfile(
-      Profile::FromWebUI(web_ui()))->policy_service();
+  return policy::ProfilePolicyConnectorFactory::GetForBrowserContext(
+             web_ui()->GetWebContents()->GetBrowserContext())->policy_service();
 }
 
 PolicyUI::PolicyUI(content::WebUI* web_ui) : WebUIController(web_ui) {
diff --git a/chrome/browser/ui/webui/print_preview/extension_printer_handler.cc b/chrome/browser/ui/webui/print_preview/extension_printer_handler.cc
index 4f032c8..e3eef1a 100644
--- a/chrome/browser/ui/webui/print_preview/extension_printer_handler.cc
+++ b/chrome/browser/ui/webui/print_preview/extension_printer_handler.cc
@@ -4,21 +4,71 @@
 
 #include "chrome/browser/ui/webui/print_preview/extension_printer_handler.h"
 
+#include <algorithm>
+
 #include "base/bind.h"
 #include "base/callback.h"
-#include "base/values.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/location.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/ref_counted_memory.h"
+#include "base/task_runner_util.h"
+#include "base/threading/worker_pool.h"
+#include "chrome/browser/local_discovery/pwg_raster_converter.h"
 #include "components/cloud_devices/common/cloud_device_description.h"
 #include "components/cloud_devices/common/printer_description.h"
 #include "extensions/browser/api/printer_provider/printer_provider_api.h"
-#include "printing/print_job_constants.h"
+#include "printing/pdf_render_settings.h"
+#include "printing/pwg_raster_settings.h"
+
+using local_discovery::PWGRasterConverter;
 
 namespace {
 
 const char kContentTypePdf[] = "application/pdf";
+const char kContentTypePWGRaster[] = "image/pwg-raster";
 const char kContentTypeAll[] = "*/*";
 
 const char kInvalidDataPrintError[] = "INVALID_DATA";
 
+// Reads raster data from file path |raster_path| and returns it as
+// RefCountedMemory. Returns NULL on error.
+scoped_refptr<base::RefCountedMemory> ReadConvertedPWGRasterFileOnWorkerThread(
+    const base::FilePath& raster_path) {
+  int64 file_size;
+  if (base::GetFileSize(raster_path, &file_size) &&
+      file_size <= extensions::PrinterProviderAPI::kMaxDocumentSize) {
+    std::string data;
+    data.reserve(file_size);
+
+    if (base::ReadFileToString(raster_path, &data)) {
+      return scoped_refptr<base::RefCountedMemory>(
+          base::RefCountedString::TakeString(&data));
+    }
+  } else {
+    LOG(ERROR) << "Invalid raster file size.";
+  }
+  return scoped_refptr<base::RefCountedMemory>();
+}
+
+// Posts a task to read a file containing converted PWG raster data to the
+// worker pool.
+void ReadConvertedPWGRasterFile(
+    const ExtensionPrinterHandler::RefCountedMemoryCallback& callback,
+    bool success,
+    const base::FilePath& pwg_file_path) {
+  if (!success) {
+    callback.Run(scoped_refptr<base::RefCountedMemory>());
+    return;
+  }
+
+  base::PostTaskAndReplyWithResult(
+      base::WorkerPool::GetTaskRunner(true).get(), FROM_HERE,
+      base::Bind(&ReadConvertedPWGRasterFileOnWorkerThread, pwg_file_path),
+      callback);
+}
+
 }  // namespace
 
 ExtensionPrinterHandler::ExtensionPrinterHandler(
@@ -59,11 +109,13 @@
     const std::string& destination_id,
     const std::string& capability,
     const std::string& ticket_json,
+    const gfx::Size& page_size,
     const scoped_refptr<base::RefCountedMemory>& print_data,
     const PrinterHandler::PrintCallback& callback) {
-  extensions::PrinterProviderAPI::PrintJob print_job;
-  print_job.printer_id = destination_id;
-  print_job.ticket_json = ticket_json;
+  scoped_ptr<extensions::PrinterProviderAPI::PrintJob> print_job(
+      new extensions::PrinterProviderAPI::PrintJob());
+  print_job->printer_id = destination_id;
+  print_job->ticket_json = ticket_json;
 
   cloud_devices::CloudDeviceDescription printer_description;
   printer_description.InitFromString(capability);
@@ -74,20 +126,57 @@
   const bool kUsePdf = content_types.Contains(kContentTypePdf) ||
                        content_types.Contains(kContentTypeAll);
 
-  if (!kUsePdf) {
-    // TODO(tbarzic): Convert data to PWG raster if the printer does not support
-    // PDF.
+  if (kUsePdf) {
+    print_job->content_type = kContentTypePdf;
+    DispatchPrintJob(callback, print_job.Pass(), print_data);
+    return;
+  }
+
+  print_job->content_type = kContentTypePWGRaster;
+  ConvertToPWGRaster(print_data, printer_description, ticket_json, page_size,
+                     base::Bind(&ExtensionPrinterHandler::DispatchPrintJob,
+                                weak_ptr_factory_.GetWeakPtr(), callback,
+                                base::Passed(&print_job)));
+}
+
+void ExtensionPrinterHandler::ConvertToPWGRaster(
+    const scoped_refptr<base::RefCountedMemory>& data,
+    const cloud_devices::CloudDeviceDescription& printer_description,
+    const std::string& ticket_json,
+    const gfx::Size& page_size,
+    const ExtensionPrinterHandler::RefCountedMemoryCallback& callback) {
+  cloud_devices::CloudDeviceDescription ticket;
+  if (!ticket.InitFromString(ticket_json)) {
+    callback.Run(scoped_refptr<base::RefCountedMemory>());
+    return;
+  }
+
+  if (!pwg_raster_converter_) {
+    pwg_raster_converter_ = PWGRasterConverter::CreateDefault();
+  }
+  pwg_raster_converter_->Start(
+      data.get(),
+      PWGRasterConverter::GetConversionSettings(printer_description, page_size),
+      PWGRasterConverter::GetBitmapSettings(printer_description, ticket),
+      base::Bind(&ReadConvertedPWGRasterFile, callback));
+}
+
+void ExtensionPrinterHandler::DispatchPrintJob(
+    const PrinterHandler::PrintCallback& callback,
+    scoped_ptr<extensions::PrinterProviderAPI::PrintJob> print_job,
+    const scoped_refptr<base::RefCountedMemory>& print_data) {
+  if (!print_data) {
     WrapPrintCallback(callback, false, kInvalidDataPrintError);
     return;
   }
 
-  print_job.content_type = kContentTypePdf;
-  print_job.document_bytes = print_data;
+  print_job->document_bytes = print_data;
+
   extensions::PrinterProviderAPI::GetFactoryInstance()
       ->Get(browser_context_)
       ->DispatchPrintRequested(
-          print_job, base::Bind(&ExtensionPrinterHandler::WrapPrintCallback,
-                                weak_ptr_factory_.GetWeakPtr(), callback));
+          *print_job, base::Bind(&ExtensionPrinterHandler::WrapPrintCallback,
+                                 weak_ptr_factory_.GetWeakPtr(), callback));
 }
 
 void ExtensionPrinterHandler::WrapGetPrintersCallback(
diff --git a/chrome/browser/ui/webui/print_preview/extension_printer_handler.h b/chrome/browser/ui/webui/print_preview/extension_printer_handler.h
index 6ea9e01..6060da7 100644
--- a/chrome/browser/ui/webui/print_preview/extension_printer_handler.h
+++ b/chrome/browser/ui/webui/print_preview/extension_printer_handler.h
@@ -11,6 +11,7 @@
 #include "base/memory/scoped_ptr.h"
 #include "base/memory/weak_ptr.h"
 #include "chrome/browser/ui/webui/print_preview/printer_handler.h"
+#include "extensions/browser/api/printer_provider/printer_provider_api.h"
 
 namespace base {
 class DictionaryValue;
@@ -22,10 +23,25 @@
 class BrowserContext;
 }
 
+namespace cloud_devices {
+class CloudDeviceDescription;
+}
+
+namespace gfx {
+class Size;
+}
+
+namespace local_discovery {
+class PWGRasterConverter;
+}
+
 // Implementation of PrinterHandler interface backed by printerProvider
 // extension API.
 class ExtensionPrinterHandler : public PrinterHandler {
  public:
+  using RefCountedMemoryCallback =
+      base::Callback<void(const scoped_refptr<base::RefCountedMemory>&)>;
+
   explicit ExtensionPrinterHandler(content::BrowserContext* browser_context);
 
   ~ExtensionPrinterHandler() override;
@@ -41,10 +57,29 @@
   void StartPrint(const std::string& destination_id,
                   const std::string& capability,
                   const std::string& ticket_json,
+                  const gfx::Size& page_size,
                   const scoped_refptr<base::RefCountedMemory>& print_data,
                   const PrinterHandler::PrintCallback& callback) override;
 
  private:
+  // Converts |data| to PWG raster format (from PDF) for a printer described
+  // by |printer_description|.
+  // |callback| is called with the converted data.
+  void ConvertToPWGRaster(
+      const scoped_refptr<base::RefCountedMemory>& data,
+      const cloud_devices::CloudDeviceDescription& printer_description,
+      const std::string& ticket,
+      const gfx::Size& page_size,
+      const RefCountedMemoryCallback& callback);
+
+  // Sets print job document data and dispatches it using printerProvider API.
+  // TODO(tbarzic): Move PrinterProvider::PrintJob to it's own file so it can
+  // be forward-declared.
+  void DispatchPrintJob(
+      const PrinterHandler::PrintCallback& callback,
+      scoped_ptr<extensions::PrinterProviderAPI::PrintJob> print_job,
+      const scoped_refptr<base::RefCountedMemory>& data);
+
   // Methods used as wrappers to callbacks for extensions::PrinterProviderAPI
   // methods, primarily so the callbacks can be bound to this class' weak ptr.
   // They just propagate results to callbacks passed to them.
@@ -62,6 +97,8 @@
 
   content::BrowserContext* browser_context_;
 
+  scoped_ptr<local_discovery::PWGRasterConverter> pwg_raster_converter_;
+
   base::WeakPtrFactory<ExtensionPrinterHandler> weak_ptr_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(ExtensionPrinterHandler);
diff --git a/chrome/browser/ui/webui/print_preview/print_preview_handler.cc b/chrome/browser/ui/webui/print_preview/print_preview_handler.cc
index bcab1a9..e465e03 100644
--- a/chrome/browser/ui/webui/print_preview/print_preview_handler.cc
+++ b/chrome/browser/ui/webui/print_preview/print_preview_handler.cc
@@ -107,6 +107,7 @@
   INITIATOR_CLOSED,
   PRINT_WITH_CLOUD_PRINT,
   PRINT_WITH_PRIVET,
+  PRINT_WITH_EXTENSION,
   USERACTION_BUCKET_BOUNDARY
 };
 
@@ -884,14 +885,21 @@
 #endif
 
   if (print_with_extension) {
-    // TODO(tbarzic): Record UMA stats.
+    UMA_HISTOGRAM_COUNTS("PrintPreview.PageCount.PrintWithExtension",
+                         page_count);
+    ReportUserActionHistogram(PRINT_WITH_EXTENSION);
 
     std::string destination_id;
     std::string print_ticket;
     std::string capabilities;
+    int width = 0;
+    int height = 0;
     if (!settings->GetString(printing::kSettingDeviceName, &destination_id) ||
         !settings->GetString(printing::kSettingTicket, &print_ticket) ||
-        !settings->GetString(printing::kSettingCapabilities, &capabilities)) {
+        !settings->GetString(printing::kSettingCapabilities, &capabilities) ||
+        !settings->GetInteger(printing::kSettingPageWidth, &width) ||
+        !settings->GetInteger(printing::kSettingPageHeight, &height) ||
+        width <= 0 || height <= 0) {
       NOTREACHED();
       OnExtensionPrintResult(false, "FAILED");
       return;
@@ -907,9 +915,9 @@
 
     EnsureExtensionPrinterHandlerSet();
     extension_printer_handler_->StartPrint(
-        destination_id, capabilities, print_ticket, data,
-        base::Bind(&PrintPreviewHandler::OnExtensionPrintResult,
-                   base::Unretained(this)));
+        destination_id, capabilities, print_ticket, gfx::Size(width, height),
+        data, base::Bind(&PrintPreviewHandler::OnExtensionPrintResult,
+                         base::Unretained(this)));
     return;
   }
 
diff --git a/chrome/browser/ui/webui/print_preview/printer_handler.h b/chrome/browser/ui/webui/print_preview/printer_handler.h
index 92a9bf81..d51c22e 100644
--- a/chrome/browser/ui/webui/print_preview/printer_handler.h
+++ b/chrome/browser/ui/webui/print_preview/printer_handler.h
@@ -21,6 +21,10 @@
 class BrowserContext;
 }
 
+namespace gfx {
+class Size;
+}
+
 // Wrapper around PrinterProviderAPI to be used by print preview.
 // It makes request lifetime management easier, and hides details of more
 // complex operations like printing from the print preview handler.
@@ -58,12 +62,15 @@
   // |destination_id|: The printer to which print job should be sent.
   // |capability|: Capability reported by the printer.
   // |ticket_json|: The print job ticket as JSON string.
+  // |page_size|: The document page size.
   // |print_data|: The document bytes to print.
   // |callback| should be called in the response to the request.
+  // TODO(tbarzic): Page size should be extracted from print data.
   virtual void StartPrint(
       const std::string& destination_id,
       const std::string& capability,
       const std::string& ticket_json,
+      const gfx::Size& page_size,
       const scoped_refptr<base::RefCountedMemory>& print_data,
       const PrintCallback& callback) = 0;
 };
diff --git a/chrome/browser/ui/zoom/zoom_controller_browsertest.cc b/chrome/browser/ui/zoom/zoom_controller_browsertest.cc
index 378739d..a16336c1 100644
--- a/chrome/browser/ui/zoom/zoom_controller_browsertest.cc
+++ b/chrome/browser/ui/zoom/zoom_controller_browsertest.cc
@@ -8,6 +8,7 @@
 #include "base/process/kill.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_commands.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/ui/webui/signin/login_ui_test_utils.h"
 #include "chrome/browser/ui/zoom/chrome_zoom_level_prefs.h"
@@ -168,6 +169,57 @@
   EXPECT_FLOAT_EQ(new_zoom_level, zoom_controller->GetZoomLevel());
 }
 
+IN_PROC_BROWSER_TEST_F(ZoomControllerBrowserTest,
+                       ErrorPagesCanZoomAfterTabRestore) {
+  // This url is meant to cause a network error page to be loaded.
+  // Tests can't reach the network, so this test should continue
+  // to work even if the domain listed is someday registered.
+  GURL url("http://kjfhkjsdf.com");
+
+  TabStripModel* tab_strip = browser()->tab_strip_model();
+  ASSERT_TRUE(tab_strip);
+
+  ui_test_utils::NavigateToURLWithDisposition(
+      browser(), url, NEW_FOREGROUND_TAB,
+      ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
+  {
+    content::WebContents* web_contents = tab_strip->GetActiveWebContents();
+
+    EXPECT_EQ(
+        content::PAGE_TYPE_ERROR,
+        web_contents->GetController().GetLastCommittedEntry()->GetPageType());
+
+    content::WebContentsDestroyedWatcher destroyed_watcher(web_contents);
+    tab_strip->CloseWebContentsAt(tab_strip->active_index(),
+                                  TabStripModel::CLOSE_CREATE_HISTORICAL_TAB);
+    destroyed_watcher.Wait();
+  }
+  EXPECT_EQ(1, tab_strip->count());
+
+  content::WebContentsAddedObserver new_web_contents_observer;
+  chrome::RestoreTab(browser());
+  content::WebContents* web_contents =
+      new_web_contents_observer.GetWebContents();
+  content::WaitForLoadStop(web_contents);
+
+  EXPECT_EQ(2, tab_strip->count());
+
+  EXPECT_EQ(
+      content::PAGE_TYPE_ERROR,
+      web_contents->GetController().GetLastCommittedEntry()->GetPageType());
+
+  ZoomController* zoom_controller =
+      ZoomController::FromWebContents(web_contents);
+
+  double old_zoom_level = zoom_controller->GetZoomLevel();
+  double new_zoom_level = old_zoom_level + 0.5;
+
+  // The following attempt to change the zoom level for an error page should
+  // fail.
+  zoom_controller->SetZoomLevel(new_zoom_level);
+  EXPECT_FLOAT_EQ(new_zoom_level, zoom_controller->GetZoomLevel());
+}
+
 IN_PROC_BROWSER_TEST_F(ZoomControllerBrowserTest, Observe) {
   content::WebContents* web_contents =
       browser()->tab_strip_model()->GetActiveWebContents();
diff --git a/chrome/browser/web_resource/promo_resource_service.cc b/chrome/browser/web_resource/promo_resource_service.cc
index 11f0fb0..db05e93c 100644
--- a/chrome/browser/web_resource/promo_resource_service.cc
+++ b/chrome/browser/web_resource/promo_resource_service.cc
@@ -14,9 +14,9 @@
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/web_resource/notification_promo.h"
-#include "chrome/common/chrome_switches.h"
-#include "chrome/common/pref_names.h"
 #include "components/pref_registry/pref_registry_syncable.h"
+#include "components/web_resource/web_resource_pref_names.h"
+#include "components/web_resource/web_resource_switches.h"
 #include "content/public/browser/notification_service.h"
 #include "url/gurl.h"
 
diff --git a/chrome/browser_tests.isolate b/chrome/browser_tests.isolate
index 1a52ae674..18c0745 100644
--- a/chrome/browser_tests.isolate
+++ b/chrome/browser_tests.isolate
@@ -224,6 +224,7 @@
           '../ui/file_manager/image_loader/',
           '../ui/file_manager/integration_tests/',
           '../ui/webui/resources/js/',
+          '../third_party/analytics/',
           '../chrome/browser/resources/chromeos/wallpaper_manager/',
           '<(PRODUCT_DIR)/chromevox_test_data/',
           '<(PRODUCT_DIR)/content_shell.pak',
diff --git a/chrome/chrome.gyp b/chrome/chrome.gyp
index 4dfd621d..b284d4d 100644
--- a/chrome/chrome.gyp
+++ b/chrome/chrome.gyp
@@ -599,7 +599,6 @@
           'type': 'none',
           'dependencies': [
             'activity_type_ids_java',
-            'app_banner_metrics_ids_java',
             'chrome_resources.gyp:chrome_strings',
             'chrome_strings_grd',
             'chrome_version_java',
@@ -784,5 +783,41 @@
         },
       ],
     }],
+    ['syzyasan==1', {
+      'variables': {
+        'kasko_exe_dir': '<(DEPTH)/third_party/kasko',
+      },
+      'targets': [
+        {
+          'target_name': 'kasko_dll',
+          'type': 'none',
+          'outputs': [
+            '<(PRODUCT_DIR)/kasko.dll',
+            '<(PRODUCT_DIR)/kasko.dll.pdb',
+          ],
+          'copies': [
+            {
+              'destination': '<(PRODUCT_DIR)',
+              'files': [
+                '<(kasko_exe_dir)/kasko.dll',
+                '<(kasko_exe_dir)/kasko.dll.pdb',
+              ],
+            },
+          ],
+          'direct_dependent_settings': {
+            'msvs_settings': {
+              'VCLinkerTool': {
+                'AdditionalDependencies': [
+                  'kasko.dll.lib',
+                ],
+                'AdditionalLibraryDirectories': [
+                  '<(DEPTH)/third_party/kasko'
+                ],
+              },
+            },
+          },
+        },
+      ],
+    }],
   ],  # 'conditions'
 }
diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi
index 726c9a8..f05432d 100644
--- a/chrome/chrome_browser.gypi
+++ b/chrome/chrome_browser.gypi
@@ -33,10 +33,6 @@
       'browser/android/banners/app_banner_infobar_delegate.h',
       'browser/android/banners/app_banner_manager.cc',
       'browser/android/banners/app_banner_manager.h',
-      'browser/android/banners/app_banner_metrics_id_list.h',
-      'browser/android/banners/app_banner_metrics_ids.h',
-      'browser/android/banners/app_banner_utilities.cc',
-      'browser/android/banners/app_banner_utilities.h',
       'browser/android/bookmarks/bookmarks_bridge.cc',
       'browser/android/bookmarks/bookmarks_bridge.h',
       'browser/android/bookmarks/partner_bookmarks_reader.cc',
@@ -182,6 +178,8 @@
       'browser/autofill/risk_util.h',
       'browser/autofill/validation_rules_storage_factory.cc',
       'browser/autofill/validation_rules_storage_factory.h',
+      'browser/banners/app_banner_metrics.cc',
+      'browser/banners/app_banner_metrics.h',
       'browser/banners/app_banner_settings_helper.cc',
       'browser/banners/app_banner_settings_helper.h',
       'browser/bitmap_fetcher/bitmap_fetcher.cc',
@@ -504,6 +502,8 @@
       'browser/memory_details_mac.cc',
       'browser/memory_details_win.cc',
       'browser/native_window_notification_source.h',
+      'browser/net/predictor_tab_helper.cc',
+      'browser/net/predictor_tab_helper.h',
       'browser/omnibox/omnibox_log.cc',
       'browser/omnibox/omnibox_log.h',
       'browser/performance_monitor/performance_monitor.cc',
@@ -698,6 +698,8 @@
       'browser/media/protected_media_identifier_permission_context_factory.h',
       'browser/metrics/android_metrics_provider.cc',
       'browser/metrics/android_metrics_provider.h',
+      'browser/password_manager/account_chooser_infobar_delegate_android.cc',
+      'browser/password_manager/account_chooser_infobar_delegate_android.h',
       'browser/password_manager/generated_password_saved_infobar_delegate_android.cc',
       'browser/password_manager/generated_password_saved_infobar_delegate_android.h',
       'browser/sessions/in_memory_tab_restore_service.cc',
@@ -1005,8 +1007,6 @@
       'browser/media_galleries/win/snapshot_file_details.h',
       'browser/net/firefox_proxy_settings.cc',
       'browser/net/firefox_proxy_settings.h',
-      'browser/net/predictor_tab_helper.cc',
-      'browser/net/predictor_tab_helper.h',
       'browser/pdf/pdf_extension_util.cc',
       'browser/pdf/pdf_extension_util.h',
       'browser/power/process_power_collector.cc',
@@ -1521,11 +1521,12 @@
       'browser/history/chrome_history_client.h',
       'browser/history/chrome_history_client_factory.cc',
       'browser/history/chrome_history_client_factory.h',
+      'browser/history/content_visit_delegate.cc',
+      'browser/history/content_visit_delegate.h',
       'browser/history/delete_directive_handler.cc',
       'browser/history/delete_directive_handler.h',
       'browser/history/history_backend.cc',
       'browser/history/history_backend.h',
-      'browser/history/history_backend_android.cc',
       'browser/history/history_service.cc',
       'browser/history/history_service.h',
       'browser/history/history_service_factory.cc',
@@ -1631,6 +1632,7 @@
       'android/java/src/org/chromium/chrome/browser/VoiceSearchTabHelper.java',
       'android/java/src/org/chromium/chrome/browser/WebsiteSettingsPopup.java',
       'android/java/src/org/chromium/chrome/browser/infobar/AppBannerInfoBar.java',
+      'android/java/src/org/chromium/chrome/browser/infobar/AccountChooserInfoBar.java',
       'android/java/src/org/chromium/chrome/browser/infobar/AppBannerInfoBarDelegate.java',
       'android/java/src/org/chromium/chrome/browser/infobar/ConfirmInfoBarDelegate.java',
       'android/java/src/org/chromium/chrome/browser/infobar/DataReductionProxyInfoBarDelegate.java',
@@ -2769,6 +2771,8 @@
       'browser/sync/sync_error_controller.h',
       'browser/sync/sync_startup_tracker.cc',
       'browser/sync/sync_startup_tracker.h',
+      'browser/sync/sync_stopped_reporter.cc',
+      'browser/sync/sync_stopped_reporter.h',
       'browser/sync/sync_type_preference_provider.h',
     ],
     'chrome_browser_task_manager_sources': [
@@ -3625,19 +3629,6 @@
           'includes': [ '../build/android/java_cpp_enum.gypi' ],
         },
         {
-          # GN: //chrome/android:app_banner_metrics_ids_javagen
-          'target_name': 'app_banner_metrics_ids_java',
-          'type': 'none',
-          'sources': [
-            'android/java/AppBannerMetricsIds.template',
-          ],
-          'variables': {
-            'package_name': 'org/chromium/chrome/browser/banners',
-            'template_deps': ['browser/android/banners/app_banner_metrics_id_list.h'],
-          },
-          'includes': [ '../build/android/java_cpp_template.gypi' ],
-        },
-        {
           # GN: //chrome/android:resource_id_javagen
           'target_name': 'resource_id_java',
           'type': 'none',
diff --git a/chrome/chrome_browser_extensions.gypi b/chrome/chrome_browser_extensions.gypi
index 1d9fda9..caf0fc39 100644
--- a/chrome/chrome_browser_extensions.gypi
+++ b/chrome/chrome_browser_extensions.gypi
@@ -110,6 +110,8 @@
       'browser/extensions/activity_log/uma_policy.h',
       'browser/extensions/api/activity_log_private/activity_log_private_api.cc',
       'browser/extensions/api/activity_log_private/activity_log_private_api.h',
+      'browser/extensions/api/audio_modem/audio_modem_api.cc',
+      'browser/extensions/api/audio_modem/audio_modem_api.h',
       'browser/extensions/api/automation_internal/automation_action_adapter.h',
       'browser/extensions/api/automation_internal/automation_internal_api.cc',
       'browser/extensions/api/automation_internal/automation_internal_api.h',
diff --git a/chrome/chrome_browser_ui.gypi b/chrome/chrome_browser_ui.gypi
index 24e1a18..e520c87 100644
--- a/chrome/chrome_browser_ui.gypi
+++ b/chrome/chrome_browser_ui.gypi
@@ -801,6 +801,7 @@
       'browser/ui/passwords/password_ui_view.h',
       'browser/ui/passwords/save_password_refusal_combobox_model.cc',
       'browser/ui/passwords/save_password_refusal_combobox_model.h',
+      'browser/ui/platform_keys_certificate_selector_chromeos.h',
       'browser/ui/prefs/prefs_tab_helper.cc',
       'browser/ui/prefs/prefs_tab_helper.h',
       'browser/ui/process_singleton_dialog_linux.h',
@@ -878,6 +879,8 @@
       'browser/ui/user_manager.h',
       'browser/ui/validation_message_bubble.h',
       'browser/ui/view_ids.h',
+      'browser/ui/views/platform_keys_certificate_selector_chromeos.cc',
+      'browser/ui/views/platform_keys_certificate_selector_chromeos.h',
       'browser/ui/web_contents_sizer.h',
       'browser/ui/website_settings/permission_bubble_manager.cc',
       'browser/ui/website_settings/permission_bubble_manager.h',
@@ -903,8 +906,6 @@
       'browser/ui/webui/chromeos/cryptohome_ui.h',
       'browser/ui/webui/chromeos/cryptohome_web_ui_handler.cc',
       'browser/ui/webui/chromeos/cryptohome_web_ui_handler.h',
-      'browser/ui/webui/chromeos/device_log_ui.cc',
-      'browser/ui/webui/chromeos/device_log_ui.h',
       'browser/ui/webui/chromeos/drive_internals_ui.cc',
       'browser/ui/webui/chromeos/drive_internals_ui.h',
       'browser/ui/webui/chromeos/first_run/first_run_actor.cc',
@@ -1027,6 +1028,8 @@
       'browser/ui/webui/cookies_tree_model_util.h',
       'browser/ui/webui/crashes_ui.cc',
       'browser/ui/webui/crashes_ui.h',
+      'browser/ui/webui/device_log_ui.cc',
+      'browser/ui/webui/device_log_ui.h',
       'browser/ui/webui/domain_reliability_internals_ui.cc',
       'browser/ui/webui/domain_reliability_internals_ui.h',
       'browser/ui/webui/favicon_source.cc',
@@ -1107,6 +1110,8 @@
     'chrome_browser_ui_android_sources': [
       'browser/ui/android/autofill/credit_card_scanner_view_android.cc',
       'browser/ui/android/autofill/credit_card_scanner_view_android.h',
+      'browser/ui/android/infobars/account_chooser_infobar.cc',
+      'browser/ui/android/infobars/account_chooser_infobar.h',
       'browser/ui/android/infobars/generated_password_saved_infobar.cc',
       'browser/ui/android/infobars/generated_password_saved_infobar.h',
       'browser/ui/auto_login_infobar_delegate.cc',
@@ -1855,6 +1860,8 @@
       'browser/ui/views/infobars/confirm_infobar.h',
       'browser/ui/views/passwords/credentials_item_view.cc',
       'browser/ui/views/passwords/credentials_item_view.h',
+      'browser/ui/views/passwords/manage_credential_item_view.cc',
+      'browser/ui/views/passwords/manage_credential_item_view.h',
       'browser/ui/views/passwords/manage_password_items_view.cc',
       'browser/ui/views/passwords/manage_password_items_view.h',
       'browser/ui/views/passwords/manage_passwords_bubble_view.cc',
@@ -2003,6 +2010,8 @@
       'browser/ui/views/bookmarks/bookmark_menu_delegate.h',
       'browser/ui/views/bookmarks/bookmark_sync_promo_view.cc',
       'browser/ui/views/bookmarks/bookmark_sync_promo_view.h',
+      'browser/ui/views/certificate_selector.cc',
+      'browser/ui/views/certificate_selector.h',
       'browser/ui/views/certificate_viewer_win.cc',
       'browser/ui/views/chrome_javascript_native_dialog_factory_views.cc',
       'browser/ui/views/chrome_views_delegate_chromeos.cc',
@@ -2653,6 +2662,7 @@
         'common',
         'common_net',
         '../components/components.gyp:auto_login_parser',
+        '../components/components.gyp:device_event_log_component',
         '../components/components.gyp:dom_distiller_core',
         '../components/components.gyp:dom_distiller_webui',
         '../components/components.gyp:feedback_proto',
@@ -2673,7 +2683,6 @@
         '../third_party/cacheinvalidation/cacheinvalidation.gyp:cacheinvalidation_proto_cpp',
         '../third_party/icu/icu.gyp:icui18n',
         '../third_party/icu/icu.gyp:icuuc',
-        '../third_party/libusb/libusb.gyp:libusb',
         '../third_party/libxml/libxml.gyp:libxml',
         '../third_party/zlib/zlib.gyp:zlib',
         '../ui/accessibility/accessibility.gyp:accessibility',
@@ -2918,7 +2927,6 @@
           ],
           'dependencies!': [
              '../components/components.gyp:feedback_proto',
-             '../third_party/libusb/libusb.gyp:libusb',
              '../ui/events/events.gyp:events',
              'chrome_browser_ui_views.gyp:browser_ui_views',
           ],
@@ -3116,6 +3124,7 @@
         ['OS!="android" and OS!="ios"', {
           'dependencies': [
             '../device/bluetooth/bluetooth.gyp:device_bluetooth',
+            '../third_party/libusb/libusb.gyp:libusb',
           ],
           'sources': [
             '<@(chrome_browser_ui_non_mobile_sources)',
diff --git a/chrome/chrome_common.gypi b/chrome/chrome_common.gypi
index 1fb3eb58..60e38fb 100644
--- a/chrome/chrome_common.gypi
+++ b/chrome/chrome_common.gypi
@@ -115,8 +115,6 @@
       'common/v8_breakpad_support_win.h',
       'common/variations/experiment_labels.cc',
       'common/variations/experiment_labels.h',
-      'common/variations/uniformity_field_trials.cc',
-      'common/variations/uniformity_field_trials.h',
       'common/variations/variations_util.cc',
       'common/variations/variations_util.h',
       'common/web_application_info.cc',
diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi
index 44ded31..d5f62d8 100644
--- a/chrome/chrome_tests.gypi
+++ b/chrome/chrome_tests.gypi
@@ -1008,6 +1008,7 @@
     'chrome_interactive_ui_test_views_non_mac_sources': [
       'browser/ui/views/bookmarks/bookmark_bar_view_test.cc',
       'browser/ui/views/bookmarks/bookmark_bar_view_test_helper.h',
+      'browser/ui/views/certificate_selector_browsertest.cc',
       'browser/ui/views/constrained_window_views_browsertest.cc',
       'browser/ui/views/find_bar_controller_interactive_uitest.cc',
       'browser/ui/views/find_bar_host_interactive_uitest.cc',
@@ -1194,6 +1195,7 @@
       'test/perf/browser_perf_test.cc',
       'test/perf/browser_perf_test.h',
       'test/perf/mach_ports_performancetest.cc',
+      'test/perf/url_parse_perftest.cc',
     ],
     'chrome_driver_lib_sources': [
       '../third_party/webdriver/atoms.cc',
@@ -1447,6 +1449,7 @@
         'renderer',
         'test_support_common',
         '../content/app/resources/content_resources.gyp:content_resources',
+        '../crypto/crypto.gyp:crypto_test_support',
         '../google_apis/google_apis.gyp:google_apis_test_support',
         '../net/net.gyp:net',
         '../net/net.gyp:net_resources',
@@ -2875,58 +2878,6 @@
     },  # target 'load_library_perf_tests'
   ],
   'conditions': [
-    ['OS!="mac"', {
-      'targets': [
-        {
-          # This test appears to be a legacy target consisting of files not yet
-          # moved elsewhere.
-          #
-          # GN version: //chrome/test/perf
-          'target_name': 'perf_tests',
-          'type': 'executable',
-          'dependencies': [
-            '../base/base.gyp:base',
-            '../base/base.gyp:test_support_perf',
-            '../testing/gtest.gyp:gtest',
-            '../url/url.gyp:url_lib',
-          ],
-          'sources': [
-            # Note: Sources list duplicated in GN build.
-            'test/perf/perftests.cc',
-            'test/perf/url_parse_perftest.cc',
-          ],
-          'conditions': [
-            ['OS=="win"', {
-              'configurations': {
-                'Debug_Base': {
-                  'msvs_settings': {
-                    'VCLinkerTool': {
-                      'LinkIncremental': '<(msvs_large_module_debug_link_mode)',
-                    },
-                  },
-                },
-              },
-              'conditions': [
-                ['win_use_allocator_shim==1', {
-                  'dependencies': [
-                    '<(allocator_target)',
-                  ],
-                }],
-              ],
-            }],
-            ['os_posix == 1 and OS != "mac" and OS != "android"', {
-              'conditions': [
-                ['use_allocator!="none"', {
-                  'dependencies': [
-                    '../base/allocator/allocator.gyp:allocator',
-                  ],
-                }],
-              ],
-            }],
-          ],
-        },
-      ],
-    },],  # OS!="mac"
     ['OS == "android"', {
       'targets': [
         {
diff --git a/chrome/chrome_tests_unit.gypi b/chrome/chrome_tests_unit.gypi
index 6721be6..02ce0bd 100644
--- a/chrome/chrome_tests_unit.gypi
+++ b/chrome/chrome_tests_unit.gypi
@@ -160,6 +160,7 @@
       'browser/net/probe_message_unittest.cc',
       'browser/net/quota_policy_channel_id_store_unittest.cc',
       'browser/net/safe_search_util_unittest.cc',
+      'browser/net/spdyproxy/data_reduction_proxy_chrome_settings_unittest.cc',
       'browser/net/spdyproxy/data_reduction_proxy_settings_unittest_android.cc',
       'browser/net/ssl_config_service_manager_pref_unittest.cc',
       'browser/net/url_info_unittest.cc',
@@ -294,6 +295,7 @@
       'browser/sync/sessions/tab_node_pool_unittest.cc',
       'browser/sync/startup_controller_unittest.cc',
       'browser/sync/sync_startup_tracker_unittest.cc',
+      'browser/sync/sync_stopped_reporter_unittest.cc',
       'browser/sync/test/test_http_bridge_factory.cc',
       'browser/sync/test/test_http_bridge_factory.h',
       'browser/sync/test_profile_sync_service.cc',
@@ -656,6 +658,7 @@
       'browser/extensions/activity_log/hashed_ad_network_database_unittest.cc',
       'browser/extensions/activity_log/uma_policy_unittest.cc',
       'browser/extensions/api/activity_log_private/activity_log_private_api_unittest.cc',
+      'browser/extensions/api/audio_modem/audio_modem_api_unittest.cc',
       'browser/extensions/api/bookmarks/bookmark_api_helpers_unittest.cc',
       'browser/extensions/api/content_settings/content_settings_store_unittest.cc',
       'browser/extensions/api/content_settings/content_settings_unittest.cc',
@@ -2164,6 +2167,7 @@
           'sources': [ '<@(chrome_unit_tests_extensions_sources)' ],
           'dependencies': [
             'common/extensions/api/api.gyp:chrome_api',
+            '../components/components.gyp:audio_modem_test_support',
             '../extensions/extensions_resources.gyp:extensions_resources',
             '../extensions/extensions_strings.gyp:extensions_strings',
           ],
diff --git a/chrome/chrome_watcher/BUILD.gn b/chrome/chrome_watcher/BUILD.gn
index 32d73d9..376daaa 100644
--- a/chrome/chrome_watcher/BUILD.gn
+++ b/chrome/chrome_watcher/BUILD.gn
@@ -33,6 +33,7 @@
   ]
   deps = [
     ":chrome_watcher_resources",
+    ":client",
     "//base",
     "//components/browser_watcher",
   ]
diff --git a/chrome/chrome_watcher/DEPS b/chrome/chrome_watcher/DEPS
index e805bfc..9b1e8bd 100644
--- a/chrome/chrome_watcher/DEPS
+++ b/chrome/chrome_watcher/DEPS
@@ -1,4 +1,5 @@
 include_rules = [
   "+base",
   "+components/browser_watcher",
+  "+syzygy/kasko/api",
 ]
diff --git a/chrome/chrome_watcher/chrome_watcher.gypi b/chrome/chrome_watcher/chrome_watcher.gypi
index 067dbb6..146cb86 100644
--- a/chrome/chrome_watcher/chrome_watcher.gypi
+++ b/chrome/chrome_watcher/chrome_watcher.gypi
@@ -59,10 +59,18 @@
         'chrome_watcher_main.cc',
       ],
       'dependencies': [
+        'chrome_watcher_client',
         'chrome_watcher_resources',
         '../base/base.gyp:base',
         '../components/components.gyp:browser_watcher',
       ],
+      'conditions': [
+        ['syzyasan==1', {
+          'dependencies': [
+            'kasko_dll',
+          ],
+        }],
+      ],
       'msvs_settings': {
         'VCLinkerTool': {
           # Set /SUBSYSTEM:WINDOWS.
diff --git a/chrome/chrome_watcher/chrome_watcher_main.cc b/chrome/chrome_watcher/chrome_watcher_main.cc
index c88988fe..b67f13d66 100644
--- a/chrome/chrome_watcher/chrome_watcher_main.cc
+++ b/chrome/chrome_watcher/chrome_watcher_main.cc
@@ -8,6 +8,7 @@
 #include "base/bind.h"
 #include "base/bind_helpers.h"
 #include "base/command_line.h"
+#include "base/files/file_path.h"
 #include "base/logging_win.h"
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
@@ -20,11 +21,16 @@
 #include "base/template_util.h"
 #include "base/threading/thread.h"
 #include "base/time/time.h"
+#include "base/win/scoped_handle.h"
 #include "chrome/chrome_watcher/chrome_watcher_main_api.h"
 #include "components/browser_watcher/endsession_watcher_window_win.h"
 #include "components/browser_watcher/exit_code_watcher_win.h"
 #include "components/browser_watcher/exit_funnel_win.h"
 
+#ifdef SYZYASAN
+#include "syzygy/kasko/api/reporter.h"
+#endif
+
 namespace {
 
 // Use the same log facility as Chrome for convenience.
@@ -197,7 +203,8 @@
 // mangling.
 extern "C" int WatcherMain(const base::char16* registry_path,
                            HANDLE process_handle,
-                           HANDLE on_initialized_event_handle) {
+                           HANDLE on_initialized_event_handle,
+                           const base::char16* browser_data_directory) {
   base::Process process(process_handle);
   base::win::ScopedHandle on_initialized_event(on_initialized_event_handle);
 
@@ -212,6 +219,20 @@
   // chrome.exe in order to report its exit status.
   ::SetProcessShutdownParameters(0x100, SHUTDOWN_NORETRY);
 
+#ifdef SYZYASAN
+  bool launched_kasko = kasko::api::InitializeReporter(
+      GetKaskoEndpoint(process.Pid()).c_str(),
+      L"https://clients2.google.com/cr/staging_report",
+      base::FilePath(browser_data_directory)
+          .Append(L"Crash Reports")
+          .value()
+          .c_str(),
+      base::FilePath(browser_data_directory)
+          .Append(kPermanentlyFailedReportsSubdir)
+          .value()
+          .c_str());
+#endif  // SYZYASAN
+
   // Run a UI message loop on the main thread.
   base::MessageLoop msg_loop(base::MessageLoop::TYPE_UI);
   msg_loop.set_thread_name("WatcherMainThread");
@@ -225,6 +246,11 @@
 
   run_loop.Run();
 
+#ifdef SYZYASAN
+  if (launched_kasko)
+    kasko::api::ShutdownReporter();
+#endif  // SYZYASAN
+
   // Wind logging down.
   logging::LogEventProvider::Uninitialize();
 
diff --git a/chrome/chrome_watcher/chrome_watcher_main_api.cc b/chrome/chrome_watcher/chrome_watcher_main_api.cc
index 2c41cd5..e8b29e16 100644
--- a/chrome/chrome_watcher/chrome_watcher_main_api.cc
+++ b/chrome/chrome_watcher/chrome_watcher_main_api.cc
@@ -3,7 +3,14 @@
 // found in the LICENSE file.
 
 #include "chrome/chrome_watcher/chrome_watcher_main_api.h"
+#include "base/strings/string_number_conversions.h"
 
 const base::FilePath::CharType kChromeWatcherDll[] =
     FILE_PATH_LITERAL("chrome_watcher.dll");
 const char kChromeWatcherDLLEntrypoint[] = "WatcherMain";
+const base::FilePath::CharType kPermanentlyFailedReportsSubdir[] =
+    L"Crash Reports Fallback";
+
+base::string16 GetKaskoEndpoint(base::ProcessId client_process_id) {
+  return L"chrome_kasko_" + base::UintToString16(client_process_id);
+}
diff --git a/chrome/chrome_watcher/chrome_watcher_main_api.h b/chrome/chrome_watcher/chrome_watcher_main_api.h
index 22dbf9b..7b48f82f 100644
--- a/chrome/chrome_watcher/chrome_watcher_main_api.h
+++ b/chrome/chrome_watcher/chrome_watcher_main_api.h
@@ -7,20 +7,33 @@
 
 #include <Windows.h>
 #include "base/files/file_path.h"
+#include "base/process/process_handle.h"
 #include "base/strings/string16.h"
 
 // The name of the watcher DLL.
 extern const base::FilePath::CharType kChromeWatcherDll[];
 // The name of the watcher DLLs entrypoint function.
 extern const char kChromeWatcherDLLEntrypoint[];
+// The subdirectory of the browser data directory where permanently failed crash
+// reports will be stored.
+extern const base::FilePath::CharType kPermanentlyFailedReportsSubdir[];
 
 // The type of the watcher DLL's main entry point.
 // Watches |parent_process| and records its exit code under |registry_path| in
-// HKCU. |on_initialized_event| will be signaled once the process is fully
-// initialized. Takes ownership of |parent_process| and |on_initialized_event|.
+// HKCU. If SyzyASAN is enabled, a Kasko reporter process is also instantiated,
+// using |browser_data_directory| to store crash reports. |on_initialized_event|
+// will be signaled once the process is fully initialized. Takes ownership of
+// |parent_process| and |on_initialized_event|.
 typedef int (*ChromeWatcherMainFunction)(
     const base::char16* registry_path,
     HANDLE parent_process,
-    HANDLE on_initialized_event);
+    HANDLE on_initialized_event,
+    const base::char16* browser_data_directory);
+
+// Returns an RPC endpoint name for the identified client process. This method
+// may be invoked in both the client and the watcher process with the PID of the
+// client process to establish communication between the two using a common
+// endpoint name.
+base::string16 GetKaskoEndpoint(base::ProcessId client_process_id);
 
 #endif  // CHROME_CHROME_WATCHER_CHROME_WATCHER_MAIN_API_H_
diff --git a/chrome/common/BUILD.gn b/chrome/common/BUILD.gn
index b2b1e022..14b280e 100644
--- a/chrome/common/BUILD.gn
+++ b/chrome/common/BUILD.gn
@@ -312,6 +312,8 @@
     ]
   }
 
+  configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
+
   if (enable_extensions) {
     sources += [
       "extensions/extension_test_util.cc",
diff --git a/chrome/common/chrome_content_client.h b/chrome/common/chrome_content_client.h
index fd00d31..9e76754e 100644
--- a/chrome/common/chrome_content_client.h
+++ b/chrome/common/chrome_content_client.h
@@ -21,9 +21,9 @@
 
 class ChromeContentClient : public content::ContentClient {
  public:
-  static const char* const kPDFPluginName;
-  static const char* const kPDFPluginPath;
-  static const char* const kRemotingViewerPluginPath;
+  static const char kPDFPluginName[];
+  static const char kPDFPluginPath[];
+  static const char kRemotingViewerPluginPath[];
 
   // The methods below are called by child processes to set the function
   // pointers for built-in plugins. We avoid linking these plugins into
diff --git a/chrome/common/chrome_content_client_constants.cc b/chrome/common/chrome_content_client_constants.cc
index f0a59f8..29c05804 100644
--- a/chrome/common/chrome_content_client_constants.cc
+++ b/chrome/common/chrome_content_client_constants.cc
@@ -5,11 +5,10 @@
 #include "chrome/common/chrome_content_client.h"
 
 #if defined(GOOGLE_CHROME_BUILD)
-const char* const ChromeContentClient::kPDFPluginName = "Chrome PDF Viewer";
+const char ChromeContentClient::kPDFPluginName[] = "Chrome PDF Viewer";
 #else
-const char* const ChromeContentClient::kPDFPluginName = "Chromium PDF Viewer";
+const char ChromeContentClient::kPDFPluginName[] = "Chromium PDF Viewer";
 #endif
-const char* const ChromeContentClient::kPDFPluginPath =
-    "internal-pdf-viewer";
-const char* const ChromeContentClient::kRemotingViewerPluginPath =
+const char ChromeContentClient::kPDFPluginPath[] = "internal-pdf-viewer";
+const char ChromeContentClient::kRemotingViewerPluginPath[] =
     "internal-remoting-viewer";
diff --git a/chrome/common/chrome_paths.cc b/chrome/common/chrome_paths.cc
index 9537376..e4a0865 100644
--- a/chrome/common/chrome_paths.cc
+++ b/chrome/common/chrome_paths.cc
@@ -207,6 +207,16 @@
       cur = cur.Append(FILE_PATH_LITERAL("Crash Reports"));
       create_dir = true;
       break;
+#if defined(OS_WIN)
+    case chrome::DIR_WATCHER_DATA:
+      // The watcher data is always stored relative to the default user data
+      // directory.  This allows the watcher to be initialized before
+      // command-line options have been parsed.
+      if (!GetDefaultUserDataDirectory(&cur))
+        return false;
+      cur = cur.Append(FILE_PATH_LITERAL("Diagnostics"));
+      break;
+#endif
     case chrome::DIR_RESOURCES:
 #if defined(OS_MACOSX)
       cur = base::mac::FrameworkBundlePath();
diff --git a/chrome/common/chrome_paths.h b/chrome/common/chrome_paths.h
index 34c85fb..f2ebd7b 100644
--- a/chrome/common/chrome_paths.h
+++ b/chrome/common/chrome_paths.h
@@ -23,6 +23,10 @@
   DIR_LOGS,                     // Directory where logs should be written.
   DIR_USER_DATA,                // Directory where user data can be written.
   DIR_CRASH_DUMPS,              // Directory where crash dumps are written.
+#if defined(OS_WIN)
+  DIR_WATCHER_DATA,             // Directory where the Chrome watcher stores
+                                // data.
+#endif
   DIR_RESOURCES,                // Directory containing separate file resources
                                 // used by Chrome at runtime.
   DIR_INSPECTOR,                // Directory where web inspector is located.
diff --git a/chrome/common/chrome_switches.cc b/chrome/common/chrome_switches.cc
index 6a4f7d1..dcf4a253 100644
--- a/chrome/common/chrome_switches.cc
+++ b/chrome/common/chrome_switches.cc
@@ -270,7 +270,7 @@
     "disable-minimize-on-second-launcher-item-click";
 
 // Disables the new bookmark app system.
-const char kDisableNewBookmarkApps[] = "disable-new-bookmark-apps";
+const char kDisableNewBookmarkApps[]        = "disable-new-bookmark-apps";
 
 // Disables the new offline error page generated by NetErrorHelper for ChromeOS
 // and instead uses the old error page generated by OfflineResourceThrottle.
@@ -467,6 +467,9 @@
 // Enables the network-related benchmarking extensions.
 const char kEnableNetBenchmarking[]         = "enable-net-benchmarking";
 
+// Enables the new bookmark app system.
+const char kEnableNewBookmarkApps[]         = "enable-new-bookmark-apps";
+
 // Enables NPN with HTTP. It means NPN is enabled but SPDY won't be used.
 // HTTP is still used for all requests.
 const char kEnableNpnHttpOnly[]             = "enable-npn-http";
@@ -949,9 +952,6 @@
 // specified.
 const char kProfilingFlush[]                = "profiling-flush";
 
-// Specifies a custom URL for fetching NTP promo data.
-const char kPromoServerURL[]                = "promo-server-url";
-
 // Forces proxy auto-detection.
 const char kProxyAutoDetect[]               = "proxy-auto-detect";
 
diff --git a/chrome/common/chrome_switches.h b/chrome/common/chrome_switches.h
index 3d4d12de..7ab601ad 100644
--- a/chrome/common/chrome_switches.h
+++ b/chrome/common/chrome_switches.h
@@ -136,6 +136,7 @@
 extern const char kEnableMaterialDesignSettings[];
 extern const char kEnableNaCl[];
 extern const char kEnableNetBenchmarking[];
+extern const char kEnableNewBookmarkApps[];
 extern const char kEnableNpnHttpOnly[];
 extern const char kEnableOfflineAutoReload[];
 extern const char kEnableOfflineAutoReloadVisibleOnly[];
@@ -264,7 +265,6 @@
 extern const char kProfilingFile[];
 extern const char kProfilingFlush[];
 extern const char kProfilingOutputFile[];
-extern const char kPromoServerURL[];
 extern const char kProxyAutoDetect[];
 extern const char kProxyBypassList[];
 extern const char kProxyPacUrl[];
diff --git a/chrome/common/extensions/api/_api_features.json b/chrome/common/extensions/api/_api_features.json
index 17947a2..ca31e202 100644
--- a/chrome/common/extensions/api/_api_features.json
+++ b/chrome/common/extensions/api/_api_features.json
@@ -75,6 +75,10 @@
     "contexts": ["blessed_extension", "unblessed_extension", "content_script"],
     "matches": []
   },
+  "audioModem": {
+    "dependencies": ["permission:audioModem"],
+    "contexts": ["blessed_extension"]
+  },
   "automationInternal": {
     "internal": true,
     "dependencies": ["manifest:automation"],
@@ -462,10 +466,6 @@
     "dependencies": ["permission:idltest"],
     "contexts": ["blessed_extension"]
   },
-  "infobars": {
-    "dependencies": ["permission:infobars"],
-    "contexts": ["blessed_extension"]
-  },
   "inlineInstallPrivate": {
     "dependencies": ["permission:inlineInstallPrivate"],
     "contexts": ["blessed_extension"]
diff --git a/chrome/common/extensions/api/_permission_features.json b/chrome/common/extensions/api/_permission_features.json
index 6b11547..9ed71db 100644
--- a/chrome/common/extensions/api/_permission_features.json
+++ b/chrome/common/extensions/api/_permission_features.json
@@ -49,6 +49,23 @@
       "5107DE9024C329EEA9C9A72D94C16723790C6422"  // Apps Developer Tool Dev.
     ]
   },
+  "audioModem": [
+    {
+      "channel": "dev",
+      "extension_types": ["extension", "platform_app"]
+    },
+    {
+      "channel": "stable",
+      "extension_types": ["extension", "platform_app"],
+      "whitelist": [
+        "05EBA3051DFCA6AF17070AEE5FE8C66322FF4738",  // http://crbug.com/431978
+        "11B478CEC461C766A2DC1E5BEEB7970AE06DC9C2",  // http://crbug.com/458218
+        "0EFB879311E9EFBB7C45251F89EC655711B1F6ED",  // http://crbug.com/458218
+        "9193D3A51E2FE33B496CDA53EA330423166E7F02",  // http://crbug.com/458218
+        "F9119B8B18C7C82B51E7BC6FF816B694F2EC3E89"   // http://crbug.com/458218
+      ]
+    }
+  ],
   "autotestPrivate": {
     "channel": "stable",
     "extension_types": ["extension", "legacy_packaged_app"],
@@ -506,27 +523,6 @@
     "channel": "trunk",
     "extension_types": ["extension"]
   },
-  "infobars": [
-    {
-      "channel": "dev",
-      "extension_types": ["extension", "legacy_packaged_app", "platform_app"]
-    },
-    {
-      "channel": "stable",
-      "extension_types": ["extension"],
-      "whitelist": [
-        "496B6890097EB6E19809ADEADD095A8721FBB2E0"  // crbug.com/415852
-      ]
-    },
-    {
-      "channel": "stable",
-      "extension_types": ["extension"],
-      "whitelist": [
-        "E24F1786D842E91E74C27929B0B3715A4689A473"  // crbug.com/415852
-      ],
-      "location": "component"
-    }
-  ],
   "input": {
     "channel": "stable",
     "extension_types": ["extension", "legacy_packaged_app"],
diff --git a/chrome/common/extensions/api/audio_modem.idl b/chrome/common/extensions/api/audio_modem.idl
new file mode 100644
index 0000000..a306f7c2
--- /dev/null
+++ b/chrome/common/extensions/api/audio_modem.idl
@@ -0,0 +1,86 @@
+// 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.
+
+// Use the <code>chrome.audio_modem</code> API
+// to transmit and receive short tokens over audio.
+namespace audioModem {
+  // The audio bands supported.
+  enum Audioband {
+    // Audible (up to 3 kHz)
+    audible,
+    // Inaudible (18-20 kHz)
+    inaudible
+  };
+
+  // Details for how a token is encoded in audio.
+  dictionary TokenEncoding {
+    // The length of the tokens to transmit, in bytes.
+    // For now, apps must always use the same token length.
+    long tokenLength;
+    // Whether to use a 2-byte CRC checksum. Defaults to false.
+    boolean? crc;
+    // Whether to use a parity symbol. Defaults to false.
+    boolean? parity;
+  };
+
+  // Details of a transmit or receive request.
+  dictionary RequestParams {
+    // How long to transmit or receive for.
+    // The timeout has a maximum of 10 minutes for transmit,
+    // or 1 hour for receive.
+    long timeoutMillis;
+    // The audio band to use.
+    Audioband band;
+    // The token encoding details.
+    TokenEncoding encoding;
+  };
+
+  // Results of token decoding.
+  dictionary ReceivedToken {
+    // The token contents in raw bytes.
+    ArrayBuffer token;
+    // The audio band the token was heard on.
+    Audioband band;
+  };
+
+  // The result of a requested operation.
+  enum Status {
+    // The requested operation was processed successfully.
+    success,
+    // The request was invalid. See chrome.runtime.lastError for details.
+    invalidRequest,
+    // The requested audio band is already in use by another client.
+    // Eventually, simultaneous tokens will be time-sliced,
+    // and this error will no longer occur.
+    inUse,
+    // Audio encoding or decoding failed.
+    coderError
+  };
+
+  // A callback to report the status of a request.
+  callback StatusCallback = void(Status status);
+
+  interface Functions {
+    // Transmit a token. Only one can be transmitted at a time.
+    // Transmission of any previous tokens (by this app) will stop.
+    static void transmit(
+        RequestParams params, ArrayBuffer token, StatusCallback callback);
+    // Stop any active transmission on the specified band.
+    static void stopTransmit(Audioband band, StatusCallback callback);
+    // Start listening for audio tokens. For now,
+    // only one app will be able to listen at a time.
+    static void receive(RequestParams params, StatusCallback callback);
+    // Stop any active listening on the specified band.
+    static void stopReceive(Audioband band, StatusCallback callback);
+  };
+
+  interface Events {
+    // Audio tokens have been received.
+    static void onReceived(ReceivedToken[] tokens);
+    // Transmit could not be confirmed.
+    // The speaker volume might be too low.
+    static void onTransmitFail(Audioband band);
+  };
+};
+
diff --git a/chrome/common/extensions/api/copresence_private.idl b/chrome/common/extensions/api/copresence_private.idl
index 691cdab..6db3420 100644
--- a/chrome/common/extensions/api/copresence_private.idl
+++ b/chrome/common/extensions/api/copresence_private.idl
@@ -10,19 +10,26 @@
     boolean audible;
   };
 
+  dictionary TokenParameters {
+    long length;
+    boolean crc;
+    boolean parity;
+  };
+
   dictionary DecodeSamplesParameters {
     ArrayBuffer samples;
 
     boolean decodeAudible;
     boolean decodeInaudible;
 
-    long audibleTokenLength;
-    long inaudibleTokenLength;
+    TokenParameters audibleTokenParams;
+    TokenParameters inaudibleTokenParams;
   };
 
   dictionary EncodeTokenParameters {
     Token token;
     long repetitions;
+    TokenParameters tokenParams;
   };
 
   dictionary AudioParameters {
@@ -35,22 +42,26 @@
   interface Functions {
     // Send a boolean indicating whether our initialization was successful.
     static void sendInitialized(boolean success);
+
     // Sends an array of found tokens to Chrome.
-    static void sendFound(Token[] tokens);
+    static void sendFound(DOMString clientId, Token[] tokens);
+
     // Send an array buffer of samples encoded for the specified token.
-    static void sendSamples(Token token, ArrayBuffer samples);
-    // Send a boolean indicating whether we detected a broadcast or not.
-    static void sendDetect(boolean detected);
+    static void sendSamples(DOMString clientId,
+                            Token token,
+                            ArrayBuffer samples);
   };
 
   interface Events {
     // Fired to request audio configuration of the whisper.net library.
-    static void onConfigAudio(AudioParameters audioParams);
+    static void onConfigAudio(DOMString clientId, AudioParameters audioParams);
+
     // Fired to request encoding of the given token.
-    static void onEncodeTokenRequest(EncodeTokenParameters encodeParams);
+    static void onEncodeTokenRequest(DOMString clientId,
+                                     EncodeTokenParameters encodeParams);
+
     // Fired when we have new samples to decode.
-    static void onDecodeSamplesRequest(DecodeSamplesParameters decodeParams);
-    // Fired to request a DetectBroadcast.
-    static void onDetectBroadcastRequest();
+    static void onDecodeSamplesRequest(DOMString clientId,
+                                       DecodeSamplesParameters decodeParams);
   };
 };
diff --git a/chrome/common/extensions/api/enterprise_platform_keys.idl b/chrome/common/extensions/api/enterprise_platform_keys.idl
index 1fbc046..5133bf5 100644
--- a/chrome/common/extensions/api/enterprise_platform_keys.idl
+++ b/chrome/common/extensions/api/enterprise_platform_keys.idl
@@ -4,8 +4,9 @@
 
 // Use the <code>chrome.enterprise.platformKeys</code> API to generate
 // hardware-backed keys and to install certificates for these keys. The
-// certificates will be available to the platform and can, for example, be used
-// for TLS authentication and network access.
+// certificates will be managed by the platform and can be used for TLS
+// authentication, network access or by other extension through
+// $(ref:platformKeys chrome.platformKeys).
 [platforms = ("chromeos")]
 namespace enterprise.platformKeys {
   [nocompile, noinline_doc] dictionary Token {
diff --git a/chrome/common/extensions/api/file_manager_private.idl b/chrome/common/extensions/api/file_manager_private.idl
index 068fa0e..c597f1d 100644
--- a/chrome/common/extensions/api/file_manager_private.idl
+++ b/chrome/common/extensions/api/file_manager_private.idl
@@ -146,6 +146,27 @@
   can_view
 };
 
+// Names of properies for getEntryProperties().
+enum EntryPropertyName {
+  size,
+  modificationTime,
+  thumbnailUrl,
+  imageWidth,
+  imageHeight,
+  imageRotation,
+  pinned,
+  present,
+  hosted,
+  availableOffline,
+  availableWhenMetered,
+  dirty,
+  customIconUrl,
+  contentMimeType,
+  sharedWithMe,
+  shared,
+  externalFileUrl
+};
+
 // A file task represents an action that the file manager can perform over the
 // currently selected files. See
 // chrome/browser/chromeos/extensions/file_manager/file_tasks.h for details
@@ -173,10 +194,10 @@
 // Additional entry properties.
 dictionary EntryProperties {
   // Size of this file.
-  double? fileSize;
+  double? size;
 
   // Timestamp of entry update time, in milliseconds past the epoch.
-  double? lastModifiedTime;
+  double? modificationTime;
 
   // URL to the Drive thumbnail image for this file.
   DOMString? thumbnailUrl;
@@ -191,22 +212,22 @@
   long? imageRotation;
 
   // True if the file is pinned in cache.
-  boolean? isPinned;
+  boolean? pinned;
 
   // True if the file is present in cache.
-  boolean? isPresent;
+  boolean? present;
 
   // True if the file is hosted on a server instead of local.
-  boolean? isHosted;
+  boolean? hosted;
 
   // True if the file is available offline.
-  boolean? isAvailableOffline;
+  boolean? availableOffline;
 
   // True if the file is available on metered connection.
-  boolean? isAvailableWhenMetered;
+  boolean? availableWhenMetered;
 
   // True if the file has local change (has not been fully synced to the cloud).
-  boolean? isDirty;
+  boolean? dirty;
 
   // URL to the custom icon for this file.
   DOMString? customIconUrl;
@@ -662,9 +683,13 @@
 
   // Requests additional properties for files.
   // |fileUrls| list of URLs of files
-  // |callback|
+  // |names| list of requested properties by their names.
+  // |callback| Completion callback. May return less than requested properties
+  //     if some are not available. In the same time, it can return properties
+  //     which were not requested (if it's cheap to compute them).
   static void getEntryProperties(
       DOMString[] fileUrls,
+      EntryPropertyName[] names,
       GetEntryPropertiesCallback callback);
 
   // Pins/unpins a Drive file in the cache.
diff --git a/chrome/common/extensions/api/platform_keys.idl b/chrome/common/extensions/api/platform_keys.idl
index d4308c8..9c97ee5 100644
--- a/chrome/common/extensions/api/platform_keys.idl
+++ b/chrome/common/extensions/api/platform_keys.idl
@@ -2,10 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// Use the <code>chrome.platformKeys</code> API to use client certificates
-// managed by the platform.
+// Use the <code>chrome.platformKeys</code> API to access client certificates
+// managed by the platform. If the user or policy grants the permission, an
+// extension can use such a certficate in its custom authentication protocol.
+// E.g. this allows usage of platform managed certificates in third party VPNs
+// (see $(ref:vpnProvider chrome.vpnProvider)).
 namespace platformKeys {
-  dictionary Match {
+  [noinline_doc] dictionary Match {
      // The DER encoding of a X.509 certificate.
      ArrayBuffer certificate;
 
@@ -55,13 +58,17 @@
     boolean interactive;
   };
 
-  callback SelectCallback = void (Match[] certs);
+  // |matches|: The list of certificates that match the request, that the
+  // extension has permission for and, if <code>interactive</code> is true, that
+  // were selected by the user.
+  callback SelectCallback = void (Match[] matches);
 
   // The public and private
   // <a href="http://www.w3.org/TR/WebCryptoAPI/#dfn-CryptoKey">CryptoKey</a>
   // of a certificate which can only be used with
-  // <code>chrome.certs.subtleCrypto</code>. <code>privateKey</code> Might be
-  // null if this extension does not have access to it.
+  // $(ref:platformKeys.subtleCrypto).
+  // |privateKey|: Might be <code>null</code> if this extension does not have
+  //   access to it.
   callback GetKeyPairCallback = void (object publicKey, 
                                       optional object privateKey);
 
@@ -74,9 +81,6 @@
     // to the certificate.
     // The selected/filtered client certificates will be passed to
     // <code>callback</code>.
-    // |callback|: Will be called with the matching and, if
-    //   <code>interactive</code> is true, selected certificates that this
-    //   extension has access to.
     [nocompile] static void selectClientCertificates(
         SelectDetails details,
         SelectCallback callback);
@@ -84,15 +88,15 @@
     // Passes the key pair of <code>certificate</code> for usage with
     // $(ref:platformKeys.subtleCrypto) to <code>callback</code>.
     // |certificate|: The certificate of a $(ref:Match) returned by
-    //     $ref(selectClientCertificates).
-    // |params|: Determines signature/hash algorithm parameters additionally to
-    //     the parameters fixed by the key itself. The same parameters are
-    //     accepted as by WebCrypto's <code>importKey</code> function, e.g.
-    //     <code>RsaHashedImportParams</code> for a RSASSA-PKCS1-v1_5 key.
-    //     For RSASSA-PKCS1-v1_5 keys, additionally the parameters
-    //     <code>{ 'hash': { 'name': 'none' } }</code> are supported. The sign
-    //     function will then apply PKCS#1 v1.5 padding and but not hash the
-    //     given data.
+    // $(ref:selectClientCertificates).
+    // |parameters|: Determines signature/hash algorithm parameters additionally
+    //   to the parameters fixed by the key itself. The same parameters are
+    //   accepted as by WebCrypto's <code>importKey</code> function, e.g.
+    //   <code>RsaHashedImportParams</code> for a RSASSA-PKCS1-v1_5 key.
+    //   For RSASSA-PKCS1-v1_5 keys, additionally the parameters
+    //   <code>{ 'hash': { 'name': 'none' } }</code> are supported. The sign
+    //   function will then apply PKCS#1 v1.5 padding and but not hash the
+    //   given data.
     [nocompile] static void getKeyPair(ArrayBuffer certificate,
                                        object parameters,
                                        GetKeyPairCallback callback);
diff --git a/chrome/common/extensions/api/privacy.json b/chrome/common/extensions/api/privacy.json
index e3f71de..504b30c 100644
--- a/chrome/common/extensions/api/privacy.json
+++ b/chrome/common/extensions/api/privacy.json
@@ -16,6 +16,11 @@
             "$ref": "types.ChromeSetting",
             "value": ["networkPredictionEnabled", {"type":"boolean"}],
             "description": "If enabled, Chrome attempts to speed up your web browsing experience by pre-resolving DNS entries, prerendering sites (<code>&lt;link rel='prefetch' ...&gt;</code>), and preemptively opening TCP and SSL connections to servers.  This preference's value is a boolean, defaulting to <code>true</code>."
+          },
+          "webRTCMultipleRoutesEnabled": {
+            "$ref": "types.ChromeSetting",
+            "value": ["webRTCMultipleRoutesEnabled", {"type":"boolean"}],
+            "description": "If enabled, Chrome will explore all possible routing options when using WebRTC to find the most performant path, possibly exposing user's private IP address. Otherwise, WebRTC traffic will be routed the same way as regular HTTP. This preference's value is a boolean, defaulting to <code>true</code>."
           }
         }
       },
diff --git a/chrome/common/extensions/api/schemas.gypi b/chrome/common/extensions/api/schemas.gypi
index f8ac6010..f29dbc62 100644
--- a/chrome/common/extensions/api/schemas.gypi
+++ b/chrome/common/extensions/api/schemas.gypi
@@ -11,6 +11,7 @@
       'accessibility_features.json',
       'accessibility_private.json',
       'activity_log_private.json',
+      'audio_modem.idl',
       'automation.idl',
       'automation_internal.idl',
       'autotest_private.idl',
diff --git a/chrome/common/extensions/api/tab_capture.idl b/chrome/common/extensions/api/tab_capture.idl
index 978c235..c6489d6 100644
--- a/chrome/common/extensions/api/tab_capture.idl
+++ b/chrome/common/extensions/api/tab_capture.idl
@@ -71,7 +71,7 @@
   interface Events {
     // Event fired when the capture status of a tab changes.
     // This allows extension authors to keep track of the capture status of
-    // tabs to keep UI elements like page actions and infobars in sync.
+    // tabs to keep UI elements like page actions in sync.
     // |info| : CaptureInfo with new capture status for the tab.
     static void onStatusChanged(CaptureInfo info);
   };
diff --git a/chrome/common/extensions/api/webrtc_logging_private.idl b/chrome/common/extensions/api/webrtc_logging_private.idl
index 172035a5..5c72c95 100644
--- a/chrome/common/extensions/api/webrtc_logging_private.idl
+++ b/chrome/common/extensions/api/webrtc_logging_private.idl
@@ -67,6 +67,23 @@
                      DOMString securityOrigin,
                      GenericDoneCallback callback);
 
+    // Stores the current log without uploading. The log may stay around for
+    // as much as 5 days. The application has the option of supplying an id
+    // for uniquely identifying the log for later upload via a call to
+    // uploadStored().
+    static void store(RequestInfo request,
+                      DOMString securityOrigin,
+                      DOMString logId,
+                      GenericDoneCallback callback);
+
+    // Uploads a previously kept log that was stored via a call to store().
+    // The caller needs to know the logId as was originally provided in the
+    // call to store().
+    static void uploadStored(RequestInfo request,
+                             DOMString securityOrigin,
+                             DOMString logId,
+                             UploadDoneCallback callback);
+
     // Uploads the log and the RTP dumps, if they exist. Logging and RTP dumping
     // must be stopped before this function is called.
     static void upload(RequestInfo request,
diff --git a/chrome/common/extensions/docs/templates/public/apps/printerProvider.html b/chrome/common/extensions/docs/templates/public/apps/printerProvider.html
new file mode 100644
index 0000000..39ad95c
--- /dev/null
+++ b/chrome/common/extensions/docs/templates/public/apps/printerProvider.html
@@ -0,0 +1 @@
+{{+partials.standard_apps_api api:apis.apps.printerProvider/}}
diff --git a/chrome/common/extensions/docs/templates/public/extensions/platformKeys.html b/chrome/common/extensions/docs/templates/public/extensions/platformKeys.html
new file mode 100644
index 0000000..d4f2a0b9
--- /dev/null
+++ b/chrome/common/extensions/docs/templates/public/extensions/platformKeys.html
@@ -0,0 +1 @@
+{{+partials.standard_extensions_api api:apis.extensions.platformKeys/}}
diff --git a/chrome/common/extensions/docs/templates/public/extensions/printerProvider.html b/chrome/common/extensions/docs/templates/public/extensions/printerProvider.html
new file mode 100644
index 0000000..15259caa
--- /dev/null
+++ b/chrome/common/extensions/docs/templates/public/extensions/printerProvider.html
@@ -0,0 +1 @@
+{{+partials.standard_extensions_api api:apis.extensions.printerProvider/}}
diff --git a/chrome/common/extensions/permissions/chrome_api_permissions.cc b/chrome/common/extensions/permissions/chrome_api_permissions.cc
index bec99e9..50ec161 100644
--- a/chrome/common/extensions/permissions/chrome_api_permissions.cc
+++ b/chrome/common/extensions/permissions/chrome_api_permissions.cc
@@ -86,6 +86,7 @@
        "accessibilityPrivate",
        APIPermissionInfo::kFlagCannotBeOptional},
       {APIPermission::kActiveTab, "activeTab"},
+      {APIPermission::kAudioModem, "audioModem"},
       {APIPermission::kBookmark,
        "bookmarks",
        APIPermissionInfo::kFlagNone,
@@ -126,7 +127,6 @@
        IDS_EXTENSION_PROMPT_WARNING_HISTORY_WRITE,
        PermissionMessage::kBrowsingHistory},
       {APIPermission::kIdltest, "idltest"},
-      {APIPermission::kInfobars, "infobars"},
       {APIPermission::kInput,
        "input",
        APIPermissionInfo::kFlagNone,
diff --git a/chrome/common/extensions/permissions/permission_set_unittest.cc b/chrome/common/extensions/permissions/permission_set_unittest.cc
index dae5a7db..e101bff2 100644
--- a/chrome/common/extensions/permissions/permission_set_unittest.cc
+++ b/chrome/common/extensions/permissions/permission_set_unittest.cc
@@ -641,6 +641,7 @@
   skip.insert(APIPermission::kAlwaysOnTopWindows);
   skip.insert(APIPermission::kAppView);
   skip.insert(APIPermission::kAudio);
+  skip.insert(APIPermission::kAudioModem);
   skip.insert(APIPermission::kBrowsingData);
   skip.insert(APIPermission::kCastStreaming);
   skip.insert(APIPermission::kCommandsAccessibility);
@@ -741,7 +742,6 @@
   skip.insert(APIPermission::kGcdPrivate);
   skip.insert(APIPermission::kHotwordPrivate);
   skip.insert(APIPermission::kIdentityPrivate);
-  skip.insert(APIPermission::kInfobars);
   skip.insert(APIPermission::kInputMethodPrivate);
   skip.insert(APIPermission::kMediaPlayerPrivate);
   skip.insert(APIPermission::kMetricsPrivate);
diff --git a/chrome/common/net/BUILD.gn b/chrome/common/net/BUILD.gn
index 0794d869..b69512ca 100644
--- a/chrome/common/net/BUILD.gn
+++ b/chrome/common/net/BUILD.gn
@@ -57,7 +57,5 @@
     deps += [ "//third_party/boringssl" ]
   }
 
-  if (is_win) {
-    cflags = [ "/wd4267" ]
-  }
+  configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
 }
diff --git a/chrome/common/net/x509_certificate_model_openssl.cc b/chrome/common/net/x509_certificate_model_openssl.cc
index 32ce1a9e..fea0d5e 100644
--- a/chrome/common/net/x509_certificate_model_openssl.cc
+++ b/chrome/common/net/x509_certificate_model_openssl.cc
@@ -465,7 +465,7 @@
       {NS_OBJSIGN_CA, IDS_CERT_USAGE_OBJECT_SIGNER},
   };
 
-  crypto::ScopedOpenSSL<ASN1_BIT_STRING, ASN1_BIT_STRING_free>::Type value(
+  crypto::ScopedOpenSSL<ASN1_BIT_STRING, ASN1_BIT_STRING_free> value(
       reinterpret_cast<ASN1_BIT_STRING*>(X509V3_EXT_d2i(ex)));
   if (!value.get())
     return l10n_util::GetStringUTF8(IDS_CERT_EXTENSION_DUMP_ERROR);
@@ -488,7 +488,7 @@
       {KU_DECIPHER_ONLY, IDS_CERT_X509_KEY_USAGE_DECIPHER_ONLY},
   };
 
-  crypto::ScopedOpenSSL<ASN1_BIT_STRING, ASN1_BIT_STRING_free>::Type value(
+  crypto::ScopedOpenSSL<ASN1_BIT_STRING, ASN1_BIT_STRING_free> value(
       reinterpret_cast<ASN1_BIT_STRING*>(X509V3_EXT_d2i(ex)));
   if (!value.get())
     return l10n_util::GetStringUTF8(IDS_CERT_EXTENSION_DUMP_ERROR);
@@ -500,7 +500,7 @@
 
 std::string ProcessBasicConstraints(X509_EXTENSION* ex) {
   std::string rv;
-  crypto::ScopedOpenSSL<BASIC_CONSTRAINTS, BASIC_CONSTRAINTS_free>::Type value(
+  crypto::ScopedOpenSSL<BASIC_CONSTRAINTS, BASIC_CONSTRAINTS_free> value(
       reinterpret_cast<BASIC_CONSTRAINTS*>(X509V3_EXT_d2i(ex)));
   if (!value.get())
     return l10n_util::GetStringUTF8(IDS_CERT_EXTENSION_DUMP_ERROR);
@@ -525,8 +525,8 @@
 
 std::string ProcessExtKeyUsage(X509_EXTENSION* ex) {
   std::string rv;
-  crypto::ScopedOpenSSL<EXTENDED_KEY_USAGE, EXTENDED_KEY_USAGE_free>::Type
-      value(reinterpret_cast<EXTENDED_KEY_USAGE*>(X509V3_EXT_d2i(ex)));
+  crypto::ScopedOpenSSL<EXTENDED_KEY_USAGE, EXTENDED_KEY_USAGE_free> value(
+      reinterpret_cast<EXTENDED_KEY_USAGE*>(X509V3_EXT_d2i(ex)));
   if (!value.get())
     return l10n_util::GetStringUTF8(IDS_CERT_EXTENSION_DUMP_ERROR);
   for (size_t i = 0; i < sk_ASN1_OBJECT_num(value.get()); i++) {
@@ -674,7 +674,7 @@
 }
 
 std::string ProcessAltName(X509_EXTENSION* ex) {
-  crypto::ScopedOpenSSL<GENERAL_NAMES, GENERAL_NAMES_free>::Type alt_names(
+  crypto::ScopedOpenSSL<GENERAL_NAMES, GENERAL_NAMES_free> alt_names(
       reinterpret_cast<GENERAL_NAMES*>(X509V3_EXT_d2i(ex)));
   if (!alt_names.get())
     return l10n_util::GetStringUTF8(IDS_CERT_EXTENSION_DUMP_ERROR);
@@ -683,7 +683,7 @@
 }
 
 std::string ProcessSubjectKeyId(X509_EXTENSION* ex) {
-  crypto::ScopedOpenSSL<ASN1_OCTET_STRING, ASN1_OCTET_STRING_free>::Type value(
+  crypto::ScopedOpenSSL<ASN1_OCTET_STRING, ASN1_OCTET_STRING_free> value(
       reinterpret_cast<ASN1_OCTET_STRING*>(X509V3_EXT_d2i(ex)));
   if (!value.get())
     return l10n_util::GetStringUTF8(IDS_CERT_EXTENSION_DUMP_ERROR);
@@ -695,7 +695,7 @@
 
 std::string ProcessAuthKeyId(X509_EXTENSION* ex) {
   std::string rv;
-  crypto::ScopedOpenSSL<AUTHORITY_KEYID, AUTHORITY_KEYID_free>::Type value(
+  crypto::ScopedOpenSSL<AUTHORITY_KEYID, AUTHORITY_KEYID_free> value(
       reinterpret_cast<AUTHORITY_KEYID*>(X509V3_EXT_d2i(ex)));
   if (!value.get())
     return l10n_util::GetStringUTF8(IDS_CERT_EXTENSION_DUMP_ERROR);
@@ -751,8 +751,8 @@
 
 std::string ProcessCertificatePolicies(X509_EXTENSION* ex) {
   std::string rv;
-  crypto::ScopedOpenSSL<CERTIFICATEPOLICIES, CERTIFICATEPOLICIES_free>::Type
-      policies(reinterpret_cast<CERTIFICATEPOLICIES*>(X509V3_EXT_d2i(ex)));
+  crypto::ScopedOpenSSL<CERTIFICATEPOLICIES, CERTIFICATEPOLICIES_free> policies(
+      reinterpret_cast<CERTIFICATEPOLICIES*>(X509V3_EXT_d2i(ex)));
 
   if (!policies.get())
     return l10n_util::GetStringUTF8(IDS_CERT_EXTENSION_DUMP_ERROR);
@@ -822,8 +822,8 @@
   const int kDistPointRelativeName = 1;
 
   std::string rv;
-  crypto::ScopedOpenSSL<CRL_DIST_POINTS, CRL_DIST_POINTS_free>::Type
-      dist_points(reinterpret_cast<CRL_DIST_POINTS*>(X509V3_EXT_d2i(ex)));
+  crypto::ScopedOpenSSL<CRL_DIST_POINTS, CRL_DIST_POINTS_free> dist_points(
+      reinterpret_cast<CRL_DIST_POINTS*>(X509V3_EXT_d2i(ex)));
 
   if (!dist_points.get())
     return l10n_util::GetStringUTF8(IDS_CERT_EXTENSION_DUMP_ERROR);
@@ -863,8 +863,8 @@
 
 std::string ProcessAuthInfoAccess(X509_EXTENSION* ex) {
   std::string rv;
-  crypto::ScopedOpenSSL<AUTHORITY_INFO_ACCESS, AUTHORITY_INFO_ACCESS_free>::Type
-      aia(reinterpret_cast<AUTHORITY_INFO_ACCESS*>(X509V3_EXT_d2i(ex)));
+  crypto::ScopedOpenSSL<AUTHORITY_INFO_ACCESS, AUTHORITY_INFO_ACCESS_free> aia(
+      reinterpret_cast<AUTHORITY_INFO_ACCESS*>(X509V3_EXT_d2i(ex)));
 
   if (!aia.get())
     return l10n_util::GetStringUTF8(IDS_CERT_EXTENSION_DUMP_ERROR);
@@ -896,7 +896,7 @@
 
 std::string ProcessIA5StringData(ASN1_OCTET_STRING* asn1_string) {
   const unsigned char* data = ASN1_STRING_data(asn1_string);
-  crypto::ScopedOpenSSL<ASN1_IA5STRING, ASN1_IA5STRING_free>::Type ia5_string(
+  crypto::ScopedOpenSSL<ASN1_IA5STRING, ASN1_IA5STRING_free> ia5_string(
       d2i_ASN1_IA5STRING(NULL, &data, ASN1_STRING_length(asn1_string)));
 
   if (!ia5_string.get())
@@ -909,7 +909,7 @@
 
 std::string ProcessBMPStringData(ASN1_OCTET_STRING* asn1_string) {
   const unsigned char* data = ASN1_STRING_data(asn1_string);
-  crypto::ScopedOpenSSL<ASN1_BMPSTRING, ASN1_BMPSTRING_free>::Type bmp_string(
+  crypto::ScopedOpenSSL<ASN1_BMPSTRING, ASN1_BMPSTRING_free> bmp_string(
       d2i_ASN1_BMPSTRING(NULL, &data, ASN1_STRING_length(asn1_string)));
 
   if (!bmp_string.get())
@@ -1182,15 +1182,13 @@
 std::string ProcessSubjectPublicKeyInfo(
     net::X509Certificate::OSCertHandle cert_handle) {
   std::string rv;
-  crypto::ScopedOpenSSL<EVP_PKEY, EVP_PKEY_free>::Type public_key(
-      X509_get_pubkey(cert_handle));
+  crypto::ScopedEVP_PKEY public_key(X509_get_pubkey(cert_handle));
   if (!public_key.get())
     return rv;
   switch (EVP_PKEY_type(public_key.get()->type)) {
     case EVP_PKEY_RSA: {
-      crypto::ScopedOpenSSL<RSA, RSA_free>::Type rsa_key(
-          EVP_PKEY_get1_RSA(public_key.get()));
-      if (!rsa_key.get())
+      crypto::ScopedRSA rsa_key(EVP_PKEY_get1_RSA(public_key.get()));
+      if (!rsa_key)
         return rv;
       rv = l10n_util::GetStringFUTF8(
           IDS_CERT_RSA_PUBLIC_KEY_DUMP_FORMAT,
diff --git a/chrome/common/pref_names.cc b/chrome/common/pref_names.cc
index 3c9d33c..451d1cbf 100644
--- a/chrome/common/pref_names.cc
+++ b/chrome/common/pref_names.cc
@@ -1248,6 +1248,12 @@
 const char kCopresenceAnonymousDeviceId[] = "apps.copresence.unauth_device_id";
 #endif
 
+// Whether WebRTC should bind to individual NICs to explore all possible routing
+// options. Default is true.
+#if defined(ENABLE_WEBRTC)
+const char kWebRTCMultipleRoutesEnabled[] = "webrtc.multiple_routes_enabled";
+#endif
+
 // *************** LOCAL STATE ***************
 // These are attached to the machine/installation
 
@@ -1571,9 +1577,6 @@
 // True if a desktop sync session was found for this user.
 const char kNtpPromoDesktopSessionFound[] = "ntp.promo_desktop_session_found";
 
-// Last time of update of promo_resource_cache.
-const char kNtpPromoResourceCacheUpdate[] = "ntp.promo_resource_cache_update";
-
 // Which bookmarks folder should be visible on the new tab page v4.
 const char kNtpShownBookmarksFolder[] = "ntp.shown_bookmarks_folder";
 
@@ -1670,6 +1673,10 @@
     "googlegeolocationaccess.enabled";
 #endif
 
+// Boolean that specifies whether to enable the Google Now Launcher extension.
+// Note: This is not the notifications component gated by ENABLE_GOOGLE_NOW.
+const char kGoogleNowLauncherEnabled[] = "google_now_launcher.enabled";
+
 // The default audio capture device used by the Media content setting.
 const char kDefaultAudioCaptureDevice[] = "media.default_audio_capture_device";
 
diff --git a/chrome/common/pref_names.h b/chrome/common/pref_names.h
index 44502d0..319d971 100644
--- a/chrome/common/pref_names.h
+++ b/chrome/common/pref_names.h
@@ -419,6 +419,10 @@
 extern const char kCopresenceAnonymousDeviceId[];
 #endif
 
+#if defined(ENABLE_WEBRTC)
+extern const char kWebRTCMultipleRoutesEnabled[];
+#endif
+
 // Local state prefs. Please add Profile prefs above instead.
 extern const char kCertRevocationCheckingEnabled[];
 extern const char kCertRevocationCheckingRequiredLocalAnchors[];
@@ -550,7 +554,6 @@
 extern const char kNtpDateResourceServer[];
 extern const char kNtpMostVisitedURLsBlacklist[];
 extern const char kNtpPromoDesktopSessionFound[];
-extern const char kNtpPromoResourceCacheUpdate[];
 extern const char kNtpShownBookmarksFolder[];
 extern const char kNtpShownPage[];
 extern const char kNtpTipsResourceServer[];
@@ -592,6 +595,7 @@
 #if defined(ENABLE_GOOGLE_NOW)
 extern const char kGoogleGeolocationAccessEnabled[];
 #endif
+extern const char kGoogleNowLauncherEnabled[];
 
 extern const char kDefaultAudioCaptureDevice[];
 extern const char kDefaultVideoCaptureDevice[];
diff --git a/chrome/common/url_constants.cc b/chrome/common/url_constants.cc
index 82ed02b2..fa1e0a6f 100644
--- a/chrome/common/url_constants.cc
+++ b/chrome/common/url_constants.cc
@@ -172,6 +172,7 @@
 const char kChromeUICrashHost[] = "crash";
 const char kChromeUICreditsHost[] = "credits";
 const char kChromeUIDefaultHost[] = "version";
+const char kChromeUIDeviceLogHost[] = "device-log";
 const char kChromeUIDevicesHost[] = "devices";
 const char kChromeUIDevToolsHost[] = "devtools";
 const char kChromeUIDevToolsBundledPath[] = "bundled";
@@ -274,7 +275,6 @@
 const char kChromeUICertificateManagerHost[] = "certificate-manager";
 const char kChromeUIChooseMobileNetworkHost[] = "choose-mobile-network";
 const char kChromeUICryptohomeHost[] = "cryptohome";
-const char kChromeUIDeviceLogHost[] = "device-log";
 const char kChromeUIDiscardsHost[] = "discards";
 const char kChromeUIFirstRunHost[] = "first-run";
 const char kChromeUIIdleLogoutDialogHost[] = "idle-logout";
@@ -586,6 +586,7 @@
   kChromeUIComponentsHost,
   kChromeUICrashesHost,
   kChromeUICreditsHost,
+  kChromeUIDeviceLogHost,
   kChromeUIDNSHost,
   kChromeUIFlagsHost,
   kChromeUIHistoryHost,
@@ -642,7 +643,6 @@
   kChromeUICertificateManagerHost,
   kChromeUIChooseMobileNetworkHost,
   kChromeUICryptohomeHost,
-  kChromeUIDeviceLogHost,
   kChromeUIDiscardsHost,
   kChromeUIDriveInternalsHost,
   kChromeUIFirstRunHost,
diff --git a/chrome/common/url_constants.h b/chrome/common/url_constants.h
index 9278dfcc..7bcf524 100644
--- a/chrome/common/url_constants.h
+++ b/chrome/common/url_constants.h
@@ -164,6 +164,7 @@
 extern const char kChromeUICrashHost[];
 extern const char kChromeUICreditsHost[];
 extern const char kChromeUIDefaultHost[];
+extern const char kChromeUIDeviceLogHost[];
 extern const char kChromeUIDevicesHost[];
 extern const char kChromeUIDevToolsHost[];
 extern const char kChromeUIDevToolsBundledPath[];
@@ -266,7 +267,6 @@
 extern const char kChromeUICertificateManagerHost[];
 extern const char kChromeUIChooseMobileNetworkHost[];
 extern const char kChromeUICryptohomeHost[];
-extern const char kChromeUIDeviceLogHost[];
 extern const char kChromeUIDiagnosticsHost[];
 extern const char kChromeUIDiscardsHost[];
 extern const char kChromeUIFirstRunHost[];
diff --git a/chrome/common/variations/uniformity_field_trials.cc b/chrome/common/variations/uniformity_field_trials.cc
deleted file mode 100644
index 8202d13..0000000
--- a/chrome/common/variations/uniformity_field_trials.cc
+++ /dev/null
@@ -1,148 +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 "chrome/common/variations/uniformity_field_trials.h"
-
-#include <string>
-
-#include "base/metrics/field_trial.h"
-#include "base/strings/stringprintf.h"
-#include "base/time/time.h"
-#include "components/variations/variations_associated_data.h"
-
-namespace chrome_variations {
-
-namespace {
-
-const int MINIMIUM_ID = 3300000;
-
-const int UNIFORMITY_1_PERCENT_BASE  = MINIMIUM_ID;
-const int UNIFORMITY_1_PERCENT_LIMIT = UNIFORMITY_1_PERCENT_BASE + 100;
-const int UNIFORMITY_5_PERCENT_BASE  = UNIFORMITY_1_PERCENT_LIMIT;
-const int UNIFORMITY_5_PERCENT_LIMIT = UNIFORMITY_5_PERCENT_BASE + 20;
-const int UNIFORMITY_10_PERCENT_BASE  = UNIFORMITY_5_PERCENT_LIMIT;
-const int UNIFORMITY_10_PERCENT_LIMIT = UNIFORMITY_10_PERCENT_BASE + 10;
-const int UNIFORMITY_20_PERCENT_BASE  = UNIFORMITY_10_PERCENT_LIMIT;
-const int UNIFORMITY_20_PERCENT_LIMIT = UNIFORMITY_20_PERCENT_BASE + 5;
-const int UNIFORMITY_50_PERCENT_BASE  = UNIFORMITY_20_PERCENT_LIMIT;
-// A uniformity trial used to compare one-time-randomized and
-// session-randomized FieldTrials.
-const int UNIFORMITY_SESSION_RANDOMIZED_5_PERCENT_BASE  = 3300139;
-
-// Set up a uniformity field trial. |one_time_randomized| indicates if the
-// field trial is one-time randomized or session-randomized. |trial_name_string|
-// must contain a "%d" since the percentage of the group will be inserted in
-// the trial name. |num_trial_groups| must be a divisor of 100 (e.g. 5, 20)
-void SetupSingleUniformityFieldTrial(
-    base::FieldTrial::RandomizationType randomization_type,
-    const std::string& trial_name_string,
-    const variations::VariationID trial_base_id,
-    int num_trial_groups) {
-  // Probability per group remains constant for all uniformity trials, what
-  // changes is the probability divisor.
-  static const base::FieldTrial::Probability kProbabilityPerGroup = 1;
-  const std::string kDefaultGroupName = "default";
-  const base::FieldTrial::Probability divisor = num_trial_groups;
-
-  DCHECK_EQ(100 % num_trial_groups, 0);
-  const int group_percent = 100 / num_trial_groups;
-  const std::string trial_name = base::StringPrintf(trial_name_string.c_str(),
-                                                    group_percent);
-
-  DVLOG(1) << "Trial name = " << trial_name;
-
-  scoped_refptr<base::FieldTrial> trial(
-      base::FieldTrialList::FactoryGetFieldTrial(
-          trial_name, divisor, kDefaultGroupName, 2015, 1, 1,
-          randomization_type, NULL));
-  variations::AssociateGoogleVariationID(variations::GOOGLE_UPDATE_SERVICE,
-                                         trial_name, kDefaultGroupName,
-                                         trial_base_id);
-
-  // Loop starts with group 1 because the field trial automatically creates a
-  // default group, which would be group 0.
-  for (int group_number = 1; group_number < num_trial_groups; ++group_number) {
-    const std::string group_name =
-          base::StringPrintf("group_%02d", group_number);
-    DVLOG(1) << "    Group name = " << group_name;
-    trial->AppendGroup(group_name, kProbabilityPerGroup);
-    variations::AssociateGoogleVariationID(
-        variations::GOOGLE_UPDATE_SERVICE, trial_name, group_name,
-        static_cast<variations::VariationID>(trial_base_id + group_number));
-  }
-
-  // Now that all groups have been appended, call group() on the trial to
-  // ensure that our trial is registered. This resolves an off-by-one issue
-  // where the default group never gets chosen if we don't "use" the trial.
-  const int chosen_group = trial->group();
-  DVLOG(1) << "Chosen Group: " << chosen_group;
-}
-
-// Setup a 50% uniformity trial for new installs only. This is accomplished by
-// disabling the trial on clients that were installed before a specified date.
-void SetupNewInstallUniformityTrial(const base::Time install_date) {
-  const base::Time::Exploded kStartDate = {
-    2012, 11, 0, 6,  // Nov 6, 2012
-    0, 0, 0, 0       // 00:00:00.000
-  };
-  scoped_refptr<base::FieldTrial> trial(
-      base::FieldTrialList::FactoryGetFieldTrial(
-          "UMA-New-Install-Uniformity-Trial", 100, "Disabled",
-          2015, 1, 1, base::FieldTrial::ONE_TIME_RANDOMIZED, NULL));
-  trial->AppendGroup("Control", 50);
-  trial->AppendGroup("Experiment", 50);
-  const base::Time start_date = base::Time::FromLocalExploded(kStartDate);
-  if (install_date < start_date)
-    trial->Disable();
-  else
-    trial->group();
-}
-
-}  // namespace
-
-void SetupUniformityFieldTrials(const base::Time install_date) {
-  // The 100 percent field trial in which everyone is a member is a special
-  // case. It is useful to create as it permits viewing all UMA data in UIs
-  // and tools that require a field trial.
-  base::FieldTrial* trial =
-      base::FieldTrialList::CreateFieldTrial("UMA-Uniformity-Trial-100-Percent",
-                                             "group_01");
-  // Call |group()| on the trial to ensure its reported in metrics.
-  trial->group();
-
-  // One field trial will be created for each entry in this array. The i'th
-  // field trial will have |trial_sizes[i]| groups in it, including the default
-  // group. Each group will have a probability of 1/|trial_sizes[i]|.
-  const int num_trial_groups[] = { 100, 20, 10, 5, 2 };
-
-  // Declare our variation ID bases along side this array so we can loop over it
-  // and assign the IDs appropriately. So for example, the 1 percent experiments
-  // should have a size of 100 (100/100 = 1).
-  const variations::VariationID trial_base_ids[] = {
-    UNIFORMITY_1_PERCENT_BASE,
-    UNIFORMITY_5_PERCENT_BASE,
-    UNIFORMITY_10_PERCENT_BASE,
-    UNIFORMITY_20_PERCENT_BASE,
-    UNIFORMITY_50_PERCENT_BASE
-  };
-
-  const std::string kOneTimeRandomizedTrialName =
-      "UMA-Uniformity-Trial-%d-Percent";
-  for (size_t i = 0; i < arraysize(num_trial_groups); ++i) {
-    SetupSingleUniformityFieldTrial(base::FieldTrial::ONE_TIME_RANDOMIZED,
-                                    kOneTimeRandomizedTrialName,
-                                    trial_base_ids[i], num_trial_groups[i]);
-  }
-
-  // Setup a 5% session-randomized uniformity trial.
-  const std::string kSessionRandomizedTrialName =
-      "UMA-Session-Randomized-Uniformity-Trial-%d-Percent";
-  SetupSingleUniformityFieldTrial(
-      base::FieldTrial::SESSION_RANDOMIZED, kSessionRandomizedTrialName,
-      UNIFORMITY_SESSION_RANDOMIZED_5_PERCENT_BASE, 20);
-
-  SetupNewInstallUniformityTrial(install_date);
-}
-
-}  // namespace chrome_variations
diff --git a/chrome/common/variations/uniformity_field_trials.h b/chrome/common/variations/uniformity_field_trials.h
deleted file mode 100644
index a89734c..0000000
--- a/chrome/common/variations/uniformity_field_trials.h
+++ /dev/null
@@ -1,21 +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 CHROME_COMMON_VARIATIONS_UNIFORMITY_FIELD_TRIALS_H_
-#define CHROME_COMMON_VARIATIONS_UNIFORMITY_FIELD_TRIALS_H_
-
-namespace base {
-class Time;
-}
-
-namespace chrome_variations {
-
-// A collection of one-time-randomized and session-randomized field trials
-// intended to test the uniformity and correctness of the field trial control,
-// bucketing and reporting systems.
-void SetupUniformityFieldTrials(const base::Time install_date);
-
-}  // namespace chrome_variations
-
-#endif  // CHROME_COMMON_VARIATIONS_UNIFORMITY_FIELD_TRIALS_H_
diff --git a/chrome/installer/util/BUILD.gn b/chrome/installer/util/BUILD.gn
index a73f920..8dca7ce3 100644
--- a/chrome/installer/util/BUILD.gn
+++ b/chrome/installer/util/BUILD.gn
@@ -66,7 +66,7 @@
     include_dirs = [ "$root_gen_dir/installer_util_strings" ]
 
     # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
-    cflags = [ "/wd4267" ]
+    configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
 
     # TODO(GYP) hook up corresponding version of installer_util_nacl_win64 in GN
     sources += [
diff --git a/chrome/installer/util/google_update_util.cc b/chrome/installer/util/google_update_util.cc
index 5140146..3a8558f5 100644
--- a/chrome/installer/util/google_update_util.cc
+++ b/chrome/installer/util/google_update_util.cc
@@ -95,8 +95,7 @@
       base::LaunchProcess(cmd_string, base::LaunchOptions());
   if (!process.IsValid()) {
     PLOG(ERROR) << "Failed to launch (" << cmd_string << ")";
-  } else if (!base::WaitForExitCodeWithTimeout(process.Handle(), &exit_code,
-                                               timeout)) {
+  } else if (!process.WaitForExitWithTimeout(timeout, &exit_code)) {
     // The GetExitCodeProcess failed or timed-out.
     LOG(ERROR) <<"Command (" << cmd_string << ") is taking more than "
                << timeout.InMilliseconds() << " milliseconds to complete.";
diff --git a/chrome/renderer/chrome_content_renderer_client.cc b/chrome/renderer/chrome_content_renderer_client.cc
index ab0212949..631eafe0 100644
--- a/chrome/renderer/chrome_content_renderer_client.cc
+++ b/chrome/renderer/chrome_content_renderer_client.cc
@@ -269,6 +269,45 @@
 #endif
 
 #if defined(ENABLE_PLUGINS)
+// Presence of the poster param within plugin object tags.
+// These numeric values are used in UMA logs; do not change them.
+enum PosterParamPresence {
+  POSTER_PRESENCE_NO_PARAM_PPS_DISABLED = 0,
+  POSTER_PRESENCE_NO_PARAM_PPS_ENABLED = 1,
+  POSTER_PRESENCE_PARAM_EXISTS_PPS_DISABLED = 2,
+  POSTER_PRESENCE_PARAM_EXISTS_PPS_ENABLED = 3,
+  POSTER_PRESENCE_NUM_ITEMS
+};
+
+const char kPluginPowerSaverPosterParamPresenceHistogram[] =
+    "Plugin.PowerSaver.PosterParamPresence";
+
+void RecordPosterParamPresence(PosterParamPresence presence) {
+  UMA_HISTOGRAM_ENUMERATION(kPluginPowerSaverPosterParamPresenceHistogram,
+                            presence, POSTER_PRESENCE_NUM_ITEMS);
+}
+
+void TrackPosterParamPresence(const blink::WebPluginParams& params,
+                              bool power_saver_enabled) {
+  DCHECK_EQ(params.attributeNames.size(), params.attributeValues.size());
+
+  for (size_t i = 0; i < params.attributeNames.size(); ++i) {
+    if (params.attributeNames[i].utf8() == "poster") {
+      if (power_saver_enabled)
+        RecordPosterParamPresence(POSTER_PRESENCE_PARAM_EXISTS_PPS_ENABLED);
+      else
+        RecordPosterParamPresence(POSTER_PRESENCE_PARAM_EXISTS_PPS_DISABLED);
+
+      return;
+    }
+  }
+
+  if (power_saver_enabled)
+    RecordPosterParamPresence(POSTER_PRESENCE_NO_PARAM_PPS_ENABLED);
+  else
+    RecordPosterParamPresence(POSTER_PRESENCE_NO_PARAM_PPS_DISABLED);
+}
+
 GURL GetPluginInstancePosterImage(const blink::WebPluginParams& params,
                                   const GURL& page_base_url) {
   DCHECK_EQ(params.attributeNames.size(), params.attributeValues.size());
@@ -807,6 +846,9 @@
         bool blocked_for_background_tab =
             render_frame->IsHidden() && power_saver_enabled;
 
+        if (info.name == ASCIIToUTF16(content::kFlashPluginName))
+          TrackPosterParamPresence(params, power_saver_enabled);
+
         GURL poster_url;
         if (power_saver_enabled) {
           poster_url =
diff --git a/chrome/renderer/chrome_content_renderer_client.h b/chrome/renderer/chrome_content_renderer_client.h
index 81e2d55..908fac3 100644
--- a/chrome/renderer/chrome_content_renderer_client.h
+++ b/chrome/renderer/chrome_content_renderer_client.h
@@ -241,7 +241,6 @@
 #endif
 #if defined(ENABLE_PLUGINS)
   std::set<std::string> allowed_compositor_origins_;
-  std::set<std::string> allowed_video_decode_origins_;
 #endif
 };
 
diff --git a/chrome/renderer/media/chrome_key_systems.cc b/chrome/renderer/media/chrome_key_systems.cc
index db388ea..714c669 100644
--- a/chrome/renderer/media/chrome_key_systems.cc
+++ b/chrome/renderer/media/chrome_key_systems.cc
@@ -72,7 +72,8 @@
     return;
   }
 
-  KeySystemInfo info(kExternalClearKeyKeySystem);
+  KeySystemInfo info;
+  info.key_system = kExternalClearKeyKeySystem;
 
   info.supported_codecs = media::EME_CODEC_WEBM_ALL;
   info.supported_init_data_types = media::EME_INIT_DATA_TYPE_WEBM;
@@ -81,6 +82,16 @@
   info.supported_init_data_types |= media::EME_INIT_DATA_TYPE_CENC;
 #endif  // defined(USE_PROPRIETARY_CODECS)
 
+  // Persistent sessions are faked.
+  info.persistent_license_support = media::EME_SESSION_TYPE_SUPPORTED;
+  info.persistent_release_message_support =
+      media::EME_SESSION_TYPE_NOT_SUPPORTED;
+  // TODO(sandersd): Using ALWAYS_ENABLED prevents "not-allowed" from
+  // succeeding. Change this to REQUESTABLE once the state can be blocked.
+  // http://crbug.com/457482
+  info.persistent_state_support = media::EME_FEATURE_ALWAYS_ENABLED;
+  info.distinctive_identifier_support = media::EME_FEATURE_NOT_SUPPORTED;
+
   info.pepper_type = kExternalClearKeyPepperType;
 
   concrete_key_systems->push_back(info);
@@ -178,9 +189,29 @@
 #endif  // defined(USE_PROPRIETARY_CODECS)
   }
 
-  cdm::AddWidevineWithCodecs(cdm::WIDEVINE,
-                             supported_codecs,
-                             concrete_key_systems);
+  cdm::AddWidevineWithCodecs(
+      cdm::WIDEVINE, supported_codecs,
+#if defined(OS_CHROMEOS)
+      // Persistent licenses are supported if remote attestation succeeds.
+      media::EME_SESSION_TYPE_SUPPORTED_WITH_PERMISSION,
+      media::EME_SESSION_TYPE_NOT_SUPPORTED,  // Persistent release message.
+      // TODO(sandersd): Using ALWAYS_ENABLED prevents "not-allowed" from
+      // succeeding. Change this to REQUESTABLE once the state can be blocked.
+      // http://crbug.com/457482
+      media::EME_FEATURE_ALWAYS_ENABLED,  // Persistent state.
+      // A distinctive identifier will be available if remote attestation
+      // succeeds.
+      media::EME_FEATURE_REQUESTABLE_WITH_PERMISSION,
+#else   // (Desktop)
+      media::EME_SESSION_TYPE_NOT_SUPPORTED,  // Persistent license.
+      media::EME_SESSION_TYPE_NOT_SUPPORTED,  // Persistent release message.
+      // TODO(sandersd): Using ALWAYS_ENABLED prevents "not-allowed" from
+      // succeeding. Change this to REQUESTABLE once the state can be blocked.
+      // http://crbug.com/457482
+      media::EME_FEATURE_ALWAYS_ENABLED,  // Persistent state.
+      media::EME_FEATURE_NOT_SUPPORTED,   // Distinctive identifier.
+#endif  // defined(OS_CHROMEOS)
+      concrete_key_systems);
 }
 #endif  // defined(WIDEVINE_CDM_AVAILABLE)
 #endif  // defined(ENABLE_PEPPER_CDMS)
diff --git a/chrome/renderer/pepper/pepper_uma_host.cc b/chrome/renderer/pepper/pepper_uma_host.cc
index dc580e3..693c497 100644
--- a/chrome/renderer/pepper/pepper_uma_host.cc
+++ b/chrome/renderer/pepper/pepper_uma_host.cc
@@ -7,6 +7,7 @@
 #include "base/metrics/histogram.h"
 #include "base/sha1.h"
 #include "base/strings/string_number_conversions.h"
+#include "chrome/common/chrome_content_client.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/render_messages.h"
 #include "chrome/renderer/chrome_content_renderer_client.h"
@@ -30,11 +31,11 @@
 
 const char* const kPredefinedAllowedUMAOrigins[] = {
     "6EAED1924DB611B6EEF2A664BD077BE7EAD33B8F",  // see http://crbug.com/317833
-    "4EB74897CB187C7633357C2FE832E0AD6A44883A"   // see http://crbug.com/317833
+    "4EB74897CB187C7633357C2FE832E0AD6A44883A",  // see http://crbug.com/317833
 };
 
 const char* const kWhitelistedHistogramPrefixes[] = {
-    "22F67DA2061FFC4DC9A4974036348D9C38C22919"  // see http://crbug.com/390221
+    "22F67DA2061FFC4DC9A4974036348D9C38C22919",  // see http://crbug.com/390221
 };
 
 const char* const kWhitelistedPluginBaseNames[] = {
@@ -42,7 +43,7 @@
     kWidevineCdmAdapterFileName,  // see http://crbug.com/368743
                                   // and http://crbug.com/410630
 #endif
-    "libpdf.so"                   // see http://crbug.com/405305
+    ChromeContentClient::kPDFPluginPath,
 };
 
 std::string HashPrefix(const std::string& histogram) {
@@ -106,13 +107,12 @@
   }
 
   if (IsPluginWhitelisted() &&
-      allowed_histogram_prefixes_.find(HashPrefix(histogram)) !=
-          allowed_histogram_prefixes_.end()) {
+      ContainsKey(allowed_histogram_prefixes_, HashPrefix(histogram))) {
     return true;
   }
 
-  if (allowed_plugin_base_names_.find(plugin_base_name_.MaybeAsASCII()) !=
-      allowed_plugin_base_names_.end()) {
+  if (ContainsKey(allowed_plugin_base_names_,
+                  plugin_base_name_.MaybeAsASCII())) {
     return true;
   }
 
diff --git a/chrome/renderer/plugins/chrome_plugin_placeholder.cc b/chrome/renderer/plugins/chrome_plugin_placeholder.cc
index dcbeca8..375c3da 100644
--- a/chrome/renderer/plugins/chrome_plugin_placeholder.cc
+++ b/chrome/renderer/plugins/chrome_plugin_placeholder.cc
@@ -331,13 +331,15 @@
 
   content::ContextMenuParams params;
 
-  content::MenuItem name_item;
-  name_item.label = title_;
-  params.custom_items.push_back(name_item);
+  if (!title_.empty()) {
+    content::MenuItem name_item;
+    name_item.label = title_;
+    params.custom_items.push_back(name_item);
 
-  content::MenuItem separator_item;
-  separator_item.type = content::MenuItem::SEPARATOR;
-  params.custom_items.push_back(separator_item);
+    content::MenuItem separator_item;
+    separator_item.type = content::MenuItem::SEPARATOR;
+    params.custom_items.push_back(separator_item);
+  }
 
   if (!GetPluginInfo().path.value().empty()) {
     content::MenuItem run_item;
diff --git a/chrome/renderer/resources/extensions/automation/automation_node.js b/chrome/renderer/resources/extensions/automation/automation_node.js
index 709a45a..4c64485 100644
--- a/chrome/renderer/resources/extensions/automation/automation_node.js
+++ b/chrome/renderer/resources/extensions/automation/automation_node.js
@@ -728,18 +728,20 @@
 
     // TODO(dtseng): Make into set listing all hosting node roles.
     if (nodeData.role == schema.RoleType.webView) {
-      if (nodeImpl.pendingChildFrame === undefined)
+      if (nodeImpl.childTreeID !== nodeData.intAttributes.childTreeId)
         nodeImpl.pendingChildFrame = true;
 
       if (nodeImpl.pendingChildFrame) {
         nodeImpl.childTreeID = nodeData.intAttributes.childTreeId;
-        automationInternal.enableFrame(nodeImpl.childTreeID);
         automationUtil.storeTreeCallback(nodeImpl.childTreeID, function(root) {
           nodeImpl.pendingChildFrame = false;
           nodeImpl.childTree = root;
           privates(root).impl.hostTree = node;
+          if (root.attributes.docLoadingProgress == 1)
+            privates(root).impl.dispatchEvent(schema.EventType.loadComplete);
           nodeImpl.dispatchEvent(schema.EventType.childrenChanged);
         });
+        automationInternal.enableFrame(nodeImpl.childTreeID);
       }
     }
     for (var key in AutomationAttributeDefaults) {
diff --git a/chrome/renderer/web_apps.cc b/chrome/renderer/web_apps.cc
index 7f1500c..c842cbc 100644
--- a/chrome/renderer/web_apps.cc
+++ b/chrome/renderer/web_apps.cc
@@ -145,10 +145,16 @@
       //
       // Bookmark apps also support "apple-touch-icon" and
       // "apple-touch-icon-precomposed".
+#if defined(OS_CHROMEOS)
+      bool bookmark_apps_enabled = !base::CommandLine::ForCurrentProcess()->
+          HasSwitch(switches::kDisableNewBookmarkApps);
+#else
+      bool bookmark_apps_enabled = base::CommandLine::ForCurrentProcess()->
+          HasSwitch(switches::kEnableNewBookmarkApps);
+#endif
       if (LowerCaseEqualsASCII(rel, "icon") ||
           LowerCaseEqualsASCII(rel, "shortcut icon") ||
-          (!base::CommandLine::ForCurrentProcess()->HasSwitch(
-               switches::kDisableNewBookmarkApps) &&
+          (bookmark_apps_enabled &&
            (LowerCaseEqualsASCII(rel, "apple-touch-icon") ||
             LowerCaseEqualsASCII(rel, "apple-touch-icon-precomposed")))) {
         AddInstallIcon(elem, &app_info->icons);
diff --git a/chrome/service/BUILD.gn b/chrome/service/BUILD.gn
index 8ad6a13..fe26068 100644
--- a/chrome/service/BUILD.gn
+++ b/chrome/service/BUILD.gn
@@ -46,6 +46,8 @@
     "service_process_prefs.h",
   ]
 
+  configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
+
   deps = [
     "//chrome:strings",
     "//chrome/common",
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 45faaaf..17a6ecd 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -12,9 +12,6 @@
 # into the build.
 group("test") {
   testonly = true
-  deps = [
-    "//chrome/test/perf",
-  ]
 }
 
 # GYP version: chrome/chrome_tests_unit.gypi:test_support_common
@@ -244,6 +241,7 @@
       "//chrome:packed_resources",
       "//content/app/resources",
       "//crypto:platform",
+      "//crypto:test_support",
       "//google_apis:test_support",
       "//net",
       "//net:net_resources",
@@ -620,7 +618,6 @@
       "//chrome/browser",
       "//chrome/common/extensions/api",
       "//chrome/renderer",
-      "//chrome/test/perf",
       "//components/autofill/content/browser:risk_proto",
       "//components/autofill/content/browser:test_support",
       "//components/captive_portal:test_support",
@@ -1224,6 +1221,7 @@
 
     if (!is_ios) {
       deps += [
+        "//components/audio_modem:audio_modem_test_support",
         "//components/autofill/content/browser:test_support",
         "//components/metrics/proto",
         "//components/data_reduction_proxy/core/browser:test_support",
@@ -1516,7 +1514,7 @@
       # static for simplicity.
       deps += [ "//third_party/cld_2:cld2_static" ]
     }
-    if (is_desktop_linux && cpu_arch == "x64") {
+    if (is_desktop_linux && current_cpu == "x64") {
       # Only add this test for 64 bit builds because otherwise we need the 32
       # bit library on 64 bit systems when running this test.
       sources +=
@@ -1537,7 +1535,7 @@
         "//ui/aura:test_support",
       ]
     }
-    if (is_linux && is_chrome_branded && cpu_arch == "x86") {
+    if (is_linux && is_chrome_branded && current_cpu == "x86") {
       ldflags = [ "-Wl,--strip-debug" ]
     }
     if (is_mac) {
diff --git a/chrome/test/base/testing_profile.cc b/chrome/test/base/testing_profile.cc
index e2870aa1..75d2c5c 100644
--- a/chrome/test/base/testing_profile.cc
+++ b/chrome/test/base/testing_profile.cc
@@ -23,6 +23,7 @@
 #include "chrome/browser/favicon/favicon_service_factory.h"
 #include "chrome/browser/history/chrome_history_client.h"
 #include "chrome/browser/history/chrome_history_client_factory.h"
+#include "chrome/browser/history/content_visit_delegate.h"
 #include "chrome/browser/history/history_backend.h"
 #include "chrome/browser/history/history_service.h"
 #include "chrome/browser/history/history_service_factory.h"
@@ -214,9 +215,10 @@
 }
 
 KeyedService* BuildHistoryService(content::BrowserContext* context) {
-  Profile* profile = static_cast<Profile*>(context);
+  Profile* profile = Profile::FromBrowserContext(context);
   HistoryService* history_service = new HistoryService(
-      ChromeHistoryClientFactory::GetForProfile(profile), profile);
+      ChromeHistoryClientFactory::GetForProfile(profile),
+      scoped_ptr<history::VisitDelegate>(new ContentVisitDelegate(profile)));
   return history_service;
 }
 
@@ -694,7 +696,7 @@
   return this;
 }
 
-std::string TestingProfile::GetProfileUserName() {
+std::string TestingProfile::GetProfileUserName() const {
   return profile_name_;
 }
 
@@ -814,7 +816,7 @@
   policy::ProfilePolicyConnectorFactory::GetInstance()->SetServiceForTesting(
       this, profile_policy_connector_.get());
   CHECK_EQ(profile_policy_connector_.get(),
-           policy::ProfilePolicyConnectorFactory::GetForProfile(this));
+           policy::ProfilePolicyConnectorFactory::GetForBrowserContext(this));
 }
 
 PrefService* TestingProfile::GetPrefs() {
@@ -822,6 +824,11 @@
   return prefs_.get();
 }
 
+const PrefService* TestingProfile::GetPrefs() const {
+  DCHECK(prefs_);
+  return prefs_.get();
+}
+
 DownloadManagerDelegate* TestingProfile::GetDownloadManagerDelegate() {
   return NULL;
 }
diff --git a/chrome/test/base/testing_profile.h b/chrome/test/base/testing_profile.h
index 9b6faac..79a2e3b 100644
--- a/chrome/test/base/testing_profile.h
+++ b/chrome/test/base/testing_profile.h
@@ -237,7 +237,7 @@
   TestingProfile* AsTestingProfile() override;
 
   // Profile
-  std::string GetProfileUserName() override;
+  std::string GetProfileUserName() const override;
   ProfileType GetProfileType() const override;
 
   // DEPRECATED, because it's fragile to change a profile from non-incognito
@@ -275,6 +275,7 @@
   net::CookieMonster* GetCookieMonster();
 
   PrefService* GetPrefs() override;
+  const PrefService* GetPrefs() const override;
 
   net::URLRequestContextGetter* GetMediaRequestContext() override;
   net::URLRequestContextGetter* GetMediaRequestContextForRenderProcess(
diff --git a/chrome/test/chromedriver/run_buildbot_steps.py b/chrome/test/chromedriver/run_buildbot_steps.py
index 8aa9fc6..55a4caa 100755
--- a/chrome/test/chromedriver/run_buildbot_steps.py
+++ b/chrome/test/chromedriver/run_buildbot_steps.py
@@ -322,7 +322,7 @@
 
   fixed_issues = []
   query = ('https://code.google.com/p/chromedriver/issues/csv?'
-           'q=status%3AToBeReleased&colspec=ID%20Summary')
+           'q=label%%3AChromeDriver-%s&colspec=ID%%20Summary' % version)
   issues = StringIO.StringIO(_GetWebPageContent(query).split('\n', 1)[1])
   for issue in csv.reader(issues):
     if not issue:
diff --git a/chrome/test/data/android/download/download.txt b/chrome/test/data/android/download/download.txt
new file mode 100644
index 0000000..30d74d2
--- /dev/null
+++ b/chrome/test/data/android/download/download.txt
@@ -0,0 +1 @@
+test
\ No newline at end of file
diff --git a/chrome/test/data/android/download/test.gzip b/chrome/test/data/android/download/test.gzip
new file mode 100644
index 0000000..273c1a9
--- /dev/null
+++ b/chrome/test/data/android/download/test.gzip
@@ -0,0 +1 @@
+This is a test.
\ No newline at end of file
diff --git a/chrome/test/data/autofill/heuristics/input/bug_459132.html b/chrome/test/data/autofill/heuristics/input/bug_459132.html
new file mode 100644
index 0000000..9df9803
--- /dev/null
+++ b/chrome/test/data/autofill/heuristics/input/bug_459132.html
@@ -0,0 +1,51 @@
+<div id="fb_mod_personal" class="lh_fbMargin clearfix">
+  <div class="fieldwrapper wide">
+     <h1>Your personal data</h1>
+  </div>
+  <span class="overwriteMessage">Please note that any changes will not be saved to your profile.</span>
+
+  <div class="subtpl">
+    <div class="fieldwrapper">
+      <label for="title" class="title">Title</label>
+      <div id="title"><table class="dijit dijitReset dijitInline dijitLeft dijitDownArrowButton fb_protect title dijitSelect dijitValidationTextBox dijitSelectIncomplete dijitValidationTextBoxIncomplete dijitIncomplete" data-dojo-attach-point="_buttonNode,tableNode,focusNode,_popupStateNode" cellspacing="0" cellpadding="0" role="listbox" aria-haspopup="true" tabindex="0" id="lh_widget_form_KeySelect_0" aria-required="true" widgetid="lh_widget_form_KeySelect_0" aria-expanded="false" aria-invalid="true" style="-webkit-user-select: none;"><tbody role="presentation"><tr role="presentation"><td class="dijitReset dijitStretch dijitButtonContents" role="presentation"><div class="dijitReset dijitInputField dijitButtonText" data-dojo-attach-point="containerNode,textDirNode" role="presentation"><span role="option" class="dijitReset dijitInline dijitSelectLabel dijitValidationTextBoxLabel ">Please select</span></div><div class="dijitReset dijitValidationContainer"><input class="dijitReset dijitInputField dijitValidationIcon dijitValidationInner" value="Χ " type="text" tabindex="-1" readonly="readonly" role="presentation"></div><input type="hidden" name="Salutation_1" data-dojo-attach-point="valueNode" value=" " aria-hidden="true"></td><td class="dijitReset dijitRight dijitButtonNode dijitArrowButton dijitDownArrowButton dijitArrowButtonContainer" data-dojo-attach-point="titleNode" role="presentation"><input class="dijitReset dijitInputField dijitArrowButtonInner" value="▼ " type="text" tabindex="-1" readonly="readonly" role="presentation"></td></tr></tbody></table></div>
+    </div>
+  </div>
+
+  <div class="subtpl clear">
+    <div class="cl">
+      <div class="fieldwrapper">
+        <label for="firstname" class="firstname">First name</label>
+        <div id="firstname"><div class="dijit dijitReset dijitInline dijitLeft fb_protect firstname dijitTextBox dijitValidationTextBox dijitTextBoxIncomplete dijitValidationTextBoxIncomplete dijitIncomplete" id="widget_firstname" role="presentation" widgetid="firstname"><div class="dijitReset dijitValidationContainer"><input class="dijitReset dijitInputField dijitValidationIcon dijitValidationInner" value="Χ " type="text" tabindex="-1" readonly="readonly" role="presentation"></div><div class="dijitReset dijitInputField dijitInputContainer"><input class="dijitReset dijitInputInner" data-dojo-attach-point="textbox,focusNode" autocomplete="off" name="FirstName_1" type="text" tabindex="0" id="firstname" aria-required="true" maxlength="50" value="" aria-invalid="true"></div></div></div>
+      </div>
+    </div>
+    <div class="cl">
+      <div class="fieldwrapper">
+        <label for="lastname" class="lastname">Last name</label>
+        <div id="lastname"><div class="dijit dijitReset dijitInline dijitLeft fb_protect lastname dijitTextBox dijitValidationTextBox dijitTextBoxIncomplete dijitValidationTextBoxIncomplete dijitIncomplete" id="widget_lastname" role="presentation" widgetid="lastname"><div class="dijitReset dijitValidationContainer"><input class="dijitReset dijitInputField dijitValidationIcon dijitValidationInner" value="Χ " type="text" tabindex="-1" readonly="readonly" role="presentation"></div><div class="dijitReset dijitInputField dijitInputContainer"><input class="dijitReset dijitInputInner" data-dojo-attach-point="textbox,focusNode" autocomplete="off" name="LastName_1" type="text" tabindex="0" id="lastname" aria-required="true" maxlength="50" value="" aria-invalid="true"></div></div></div>
+      </div>
+    </div>
+  </div>
+
+  <div class="subtpl clear">
+    <div class="cl">
+      <div class="fieldwrapper">
+        <label for="email" class="email">Email</label>
+        <div id="email"><div class="dijit dijitReset dijitInline dijitLeft fb_protect email dijitTextBox dijitValidationTextBox dijitTextBoxIncomplete dijitValidationTextBoxIncomplete dijitIncomplete" id="widget_email" role="presentation" widgetid="email"><div class="dijitReset dijitValidationContainer"><input class="dijitReset dijitInputField dijitValidationIcon dijitValidationInner" value="Χ " type="text" tabindex="-1" readonly="readonly" role="presentation"></div><div class="dijitReset dijitInputField dijitInputContainer"><input class="dijitReset dijitInputInner" data-dojo-attach-point="textbox,focusNode" autocomplete="off" name="Email_1" type="text" tabindex="0" id="email" aria-required="true" value="" aria-invalid="true"></div></div></div>
+      </div>
+    </div>
+    <div class="cl">
+      <div class="fieldwrapper">
+        <label for="r_email" class="email">Re-enter email</label>
+        <div id="r_email"><div class="dijit dijitReset dijitInline dijitLeft dijitTextBox dijitValidationTextBox fb_protect email" id="widget_r_email" role="presentation" widgetid="r_email"><div class="dijitReset dijitValidationContainer"><input class="dijitReset dijitInputField dijitValidationIcon dijitValidationInner" value="Χ " type="text" tabindex="-1" readonly="readonly" role="presentation"></div><div class="dijitReset dijitInputField dijitInputContainer"><input class="dijitReset dijitInputInner" data-dojo-attach-point="textbox,focusNode" autocomplete="off" name="rEmail_1" type="text" tabindex="0" id="r_email" aria-required="true" value="" aria-invalid="false"></div></div></div>
+      </div>
+    </div>
+  </div>
+  <div class="subtpl clear">
+    <div class="cl">
+      <div class="fieldwrapper">
+        <label for="mam_number" class="mam_number">Miles &amp; More number <span class="optional"><span>&nbsp;</span>optional</span></label>
+        <div id="mam_number"><div class="dijit dijitReset dijitInline dijitLeft dijitTextBox dijitValidationTextBox fb_protect mam_number" id="widget_mam_number" role="presentation" widgetid="mam_number"><div class="dijitReset dijitValidationContainer"><input class="dijitReset dijitInputField dijitValidationIcon dijitValidationInner" value="Χ " type="text" tabindex="-1" readonly="readonly" role="presentation"></div><div class="dijitReset dijitInputField dijitInputContainer"><input class="dijitReset dijitInputInner" data-dojo-attach-point="textbox,focusNode" autocomplete="off" name="MamNumber_1" type="text" tabindex="0" id="mam_number" maxlength="15" value="" aria-invalid="false"></div></div></div>
+      </div>
+    </div>
+  </div>
+/div>
diff --git a/chrome/test/data/autofill/heuristics/output/05_checkout_overstock.com.out b/chrome/test/data/autofill/heuristics/output/05_checkout_overstock.com.out
index 8dd37d2a..f43cff3 100644
--- a/chrome/test/data/autofill/heuristics/output/05_checkout_overstock.com.out
+++ b/chrome/test/data/autofill/heuristics/output/05_checkout_overstock.com.out
@@ -18,12 +18,12 @@
 ADDRESS_HOME_STATE | state | State | CA | ShippingFirstName_1-default
 ADDRESS_HOME_ZIP | zip | Zip | 95014 | ShippingFirstName_1-default
 UNKNOWN_TYPE | product6496917 | Black-plated Tungsten Carbide Comfort Fit Band (8 mm) Options: 10 1 | GROUND6496917 | ShippingFirstName_1-default
-UNKNOWN_TYPE | CC | Credit Card Number | CreditCardValue | ShippingFirstName_1-default
+UNKNOWN_TYPE | CC |  | CreditCardValue | ShippingFirstName_1-default
 CREDIT_CARD_NUMBER | CC_number | Credit Card #: |  | ShippingFirstName_1-cc
 CREDIT_CARD_EXP_MONTH | exp_month | Expiration Date: | 0 | ShippingFirstName_1-cc
 CREDIT_CARD_EXP_4_DIGIT_YEAR | exp_year | Expiration Date: |  | ShippingFirstName_1-cc
-UNKNOWN_TYPE | CC | PayPal | PayPal | ShippingFirstName_1-default
-UNKNOWN_TYPE | CC | Use Bill Me Later | bml | ShippingFirstName_1-default
+UNKNOWN_TYPE | CC |  | PayPal | ShippingFirstName_1-default
+UNKNOWN_TYPE | CC |  | bml | ShippingFirstName_1-default
 UNKNOWN_TYPE | UsePromoCode | Use Promo Code | on | ShippingFirstName_1-default
 UNKNOWN_TYPE | PromoCode | See Terms |  | ShippingFirstName_1-default
 UNKNOWN_TYPE | UseGiftCards | Use Gift Card | on | ShippingFirstName_1-default
diff --git a/chrome/test/data/autofill/heuristics/output/11_register_macys.com.out b/chrome/test/data/autofill/heuristics/output/11_register_macys.com.out
index 838f7ee..a67d6be 100644
--- a/chrome/test/data/autofill/heuristics/output/11_register_macys.com.out
+++ b/chrome/test/data/autofill/heuristics/output/11_register_macys.com.out
@@ -8,12 +8,12 @@
 UNKNOWN_TYPE | Password | Password |  | FirstName_1-default
 UNKNOWN_TYPE | PasswordConfirm | Verify Password |  | FirstName_1-default
 UNKNOWN_TYPE | BirthMonth | Birth date | NOSELECTION | FirstName_1-default
-UNKNOWN_TYPE | BirthDay | Select the day on which you were born. | NOSELECTION | FirstName_1-default
-UNKNOWN_TYPE | BirthYear | Select the year on which you were born. | NOSELECTION | FirstName_1-default
+UNKNOWN_TYPE | BirthDay |  | NOSELECTION | FirstName_1-default
+UNKNOWN_TYPE | BirthYear |  | NOSELECTION | FirstName_1-default
 UNKNOWN_TYPE | Gender | Gender | NOSELECTION | FirstName_1-default
 UNKNOWN_TYPE | NewsLetter | We'll let you know about exclusive sales and events,both online and in-store. | NewsLetter | FirstName_1-default
 UNKNOWN_TYPE | MobileMarketing | Yes, please text me about exclusive sales and events, both online and in-store. We'll send your first text message within 48 hours. | MobileMarketing | FirstName_1-default
-PHONE_HOME_CITY_CODE | MobilePhoneAreaCode | Enter three number area code for your phone number. |  | FirstName_1-default
+PHONE_HOME_CITY_CODE | MobilePhoneAreaCode |  |  | FirstName_1-default
 PHONE_HOME_NUMBER | MobilePhoneExchangeNbr | - |  | FirstName_1-default
 PHONE_HOME_NUMBER | MobilePhoneSubscriberNbr | - |  | FirstName_1-default
 UNKNOWN_TYPE | addacard | Yes, I'd like to add my Macy's Card to my profile. | on | FirstName_1-default
diff --git a/chrome/test/data/autofill/heuristics/output/24_checkout_harryanddavid.com.out b/chrome/test/data/autofill/heuristics/output/24_checkout_harryanddavid.com.out
index 2105db0..ccb8fe6 100644
--- a/chrome/test/data/autofill/heuristics/output/24_checkout_harryanddavid.com.out
+++ b/chrome/test/data/autofill/heuristics/output/24_checkout_harryanddavid.com.out
@@ -29,7 +29,7 @@
 UNKNOWN_TYPE |  | Email address* | Χ  | typeOfAddress_1-default
 EMAIL_ADDRESS | email1 | Email address* |  | typeOfAddress_1-default
 UNKNOWN_TYPE |  | Confirm email address* | Χ  | typeOfAddress_1-default
-EMAIL_ADDRESS | logonId | Confirm email address* |  | logonId_1-default
+EMAIL_ADDRESS | logonId | Confirm email address* |  | typeOfAddress_1-default
 UNKNOWN_TYPE |  | Gift Card Number | Χ  | _1-default
 UNKNOWN_TYPE | GiftCardNumber | Gift Card Number |  | _1-default
 UNKNOWN_TYPE |  | PIN* | Χ  | _1-default
diff --git a/chrome/test/data/autofill/heuristics/output/bug_459132.out b/chrome/test/data/autofill/heuristics/output/bug_459132.out
new file mode 100644
index 0000000..86bf84a7
--- /dev/null
+++ b/chrome/test/data/autofill/heuristics/output/bug_459132.out
@@ -0,0 +1,12 @@
+UNKNOWN_TYPE |  | Please select | Χ  | _1-default
+UNKNOWN_TYPE |  | Please select | ▼  | _1-default
+UNKNOWN_TYPE |  | First name | Χ  | _1-default
+NAME_FIRST | FirstName_1 | First name |  | _1-default
+UNKNOWN_TYPE |  | Last name | Χ  | _1-default
+NAME_LAST | LastName_1 | Last name |  | _1-default
+UNKNOWN_TYPE |  | Email | Χ  | _1-default
+EMAIL_ADDRESS | Email_1 | Email |  | _1-default
+UNKNOWN_TYPE |  | Re-enter email | Χ  | _1-default
+EMAIL_ADDRESS | rEmail_1 | Re-enter email |  | _1-default
+UNKNOWN_TYPE |  | Miles & More number optional | Χ  | _1-default
+UNKNOWN_TYPE | MamNumber_1 | Miles & More number optional |  | _1-default
diff --git a/chrome/test/data/extensions/api_test/file_system_provider/thumbnail/test.js b/chrome/test/data/extensions/api_test/file_system_provider/thumbnail/test.js
index 087de223..01b58a5 100644
--- a/chrome/test/data/extensions/api_test/file_system_provider/thumbnail/test.js
+++ b/chrome/test/data/extensions/api_test/file_system_provider/thumbnail/test.js
@@ -163,17 +163,18 @@
           chrome.test.callbackPass(function(fileEntry) {
             chrome.fileManagerPrivate.getEntryProperties(
                 [fileEntry.toURL()],
+                ['thumbnailUrl', 'size', 'modificationTime'],
                 chrome.test.callbackPass(function(fileProperties) {
                   chrome.test.assertEq(1, fileProperties.length);
                   chrome.test.assertEq(
                       TESTING_WITH_VALID_THUMBNAIL_FILE.thumbnail,
                       fileProperties[0].thumbnailUrl);
                   chrome.test.assertEq(
-                      TESTING_WITH_VALID_THUMBNAIL_FILE.fileSize,
+                      TESTING_WITH_VALID_THUMBNAIL_FILE.size,
                       fileProperties[0].size);
                   chrome.test.assertEq(
                       TESTING_WITH_VALID_THUMBNAIL_FILE.modificationTime,
-                      new Date(fileProperties[0].lastModifiedTime));
+                      new Date(fileProperties[0].modificationTime));
                 }));
             }),
             function(error) {
@@ -190,6 +191,7 @@
           chrome.test.callbackPass(function(fileEntry) {
             chrome.fileManagerPrivate.getEntryProperties(
                 [fileEntry.toURL()],
+                ['thumbnailUrl'],
                 chrome.test.callbackPass(function(fileProperties) {
                   chrome.test.assertEq(1, fileProperties.length);
                   // The results for an entry is an empty dictionary in case of
@@ -201,6 +203,29 @@
             function(error) {
               chrome.test.fail(error.name);
             });
+    },
+
+    // Confirm that the thumbnail is not requested when not needed.
+    function getEntryPropertiesWithoutThumbnail() {
+      test_util.fileSystem.root.getFile(
+          TESTING_WITH_VALID_THUMBNAIL_FILE.name,
+          {create: false},
+          chrome.test.callbackPass(function(fileEntry) {
+            chrome.fileManagerPrivate.getEntryProperties(
+                [fileEntry.toURL()],
+                ['size'],
+                chrome.test.callbackPass(function(fileProperties) {
+                  chrome.test.assertEq(1, fileProperties.length);
+                  chrome.test.assertFalse(
+                      'thumbnailUrl' in fileProperties[0]);
+                  chrome.test.assertEq(
+                      TESTING_WITH_VALID_THUMBNAIL_FILE.size,
+                      fileProperties[0].size);
+                }));
+            }),
+            function(error) {
+              chrome.test.fail(error.name);
+            });
     }
   ]);
 }
diff --git a/chrome/test/data/extensions/api_test/management/test/launchType.js b/chrome/test/data/extensions/api_test/management/test/launchType.js
index b2d40f3..db35e6c7 100644
--- a/chrome/test/data/extensions/api_test/management/test/launchType.js
+++ b/chrome/test/data/extensions/api_test/management/test/launchType.js
@@ -32,6 +32,11 @@
   types.push("OPEN_AS_REGULAR_TAB");
   types.push("OPEN_AS_WINDOW");
 
+  if (navigator.userAgent.indexOf("CrOS") == -1) {
+    types.push("OPEN_AS_PINNED_TAB");
+    types.push("OPEN_FULL_SCREEN");
+  }
+
   return types;
 }
 
@@ -59,6 +64,13 @@
     } else {
       testSetLaunchType(app.id, type, null, function() {
         chrome.management.get(app.id, function(item) {
+          if (navigator.userAgent.indexOf("Mac") != -1) {
+            // In the current configuration, with the new bookmark app flow
+            // disabled, hosted apps set to open in a window on Mac will open
+            // instead in a tab.
+            if (item.type != 'packaged_app' && type == 'OPEN_AS_WINDOW')
+              type = 'OPEN_AS_REGULAR_TAB';
+          }
           assertEq(type, item.launchType);
           setNextLaunchType();
         });
diff --git a/chrome/test/data/extensions/api_test/networking/test.js b/chrome/test/data/extensions/api_test/networking/test.js
deleted file mode 100644
index 6bd2292..0000000
--- a/chrome/test/data/extensions/api_test/networking/test.js
+++ /dev/null
@@ -1,578 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Note: the expectations in this test are shared by both the Chrome OS and
-// Win/Mac (ServiceClient) implementations. TODO(stevenjb): Set up a way for
-// the test code to specify the correct expectations.
-
-var callbackPass = chrome.test.callbackPass;
-var callbackFail = chrome.test.callbackFail;
-var assertTrue = chrome.test.assertTrue;
-var assertFalse = chrome.test.assertFalse;
-var assertEq = chrome.test.assertEq;
-
-// Test properties for the verification API.
-var verificationProperties = {
-  "certificate": "certificate",
-  "intermediateCertificates": ["ica1", "ica2", "ica3"],
-  "publicKey": "cHVibGljX2tleQ==",  // Base64("public_key")
-  "nonce": "nonce",
-  "signedData": "c2lnbmVkX2RhdGE=",  // Base64("signed_data")
-  "deviceSerial": "device_serial",
-  "deviceSsid": "Device 0123",
-  "deviceBssid": "00:01:02:03:04:05"
-};
-
-var privateHelpers = {
-  // Watches for the states |expectedStates| in reverse order. If all states
-  // were observed in the right order, succeeds and calls |done|. If any
-  // unexpected state is observed, fails.
-  watchForStateChanges: function(network, expectedStates, done) {
-    var self = this;
-    var collectProperties = function(properties) {
-      var finishTest = function() {
-        chrome.networkingPrivate.onNetworksChanged.removeListener(
-            self.onNetworkChange);
-        done();
-      };
-      if (expectedStates.length > 0) {
-        var expectedState = expectedStates.pop();
-        assertEq(expectedState, properties.ConnectionState);
-        if (expectedStates.length == 0)
-          finishTest();
-      }
-    };
-    this.onNetworkChange = function(changes) {
-      assertEq([network], changes);
-      chrome.networkingPrivate.getProperties(
-          network,
-          callbackPass(collectProperties));
-    };
-    chrome.networkingPrivate.onNetworksChanged.addListener(
-        this.onNetworkChange);
-  },
-  listListener: function(expected, done) {
-    var self = this;
-    this.listenForChanges = function(list) {
-      assertEq(expected, list);
-      chrome.networkingPrivate.onNetworkListChanged.removeListener(
-          self.listenForChanges);
-      done();
-    };
-  },
-  watchForCaptivePortalState: function(expectedGuid,
-                                       expectedState,
-                                       done) {
-    var self = this;
-    this.onPortalDetectionCompleted = function(guid, state) {
-      assertEq(expectedGuid, guid);
-      assertEq(expectedState, state);
-      chrome.networkingPrivate.onPortalDetectionCompleted.removeListener(
-          self.onPortalDetectionCompleted);
-      done();
-    };
-    chrome.networkingPrivate.onPortalDetectionCompleted.addListener(
-        self.onPortalDetectionCompleted);
-  }
-};
-
-var availableTests = [
-  function startConnect() {
-    chrome.networkingPrivate.startConnect("stub_wifi2_guid", callbackPass());
-  },
-  function startDisconnect() {
-    // Must connect to a network before we can disconnect from it.
-    chrome.networkingPrivate.startConnect("stub_wifi2_guid", callbackPass(
-      function() {
-        chrome.networkingPrivate.startDisconnect("stub_wifi2_guid",
-                                                 callbackPass());
-      }));
-  },
-  function startConnectNonexistent() {
-    chrome.networkingPrivate.startConnect(
-      "nonexistent_path",
-      callbackFail("Error.InvalidNetworkGuid"));
-  },
-  function startDisconnectNonexistent() {
-    chrome.networkingPrivate.startDisconnect(
-      "nonexistent_path",
-      callbackFail("Error.InvalidNetworkGuid"));
-  },
-  function startGetPropertiesNonexistent() {
-    chrome.networkingPrivate.getProperties(
-      "nonexistent_path",
-      callbackFail("Error.InvalidNetworkGuid"));
-  },
-  function createNetwork() {
-    chrome.networkingPrivate.createNetwork(
-      false,  // shared
-      { "Type": "WiFi",
-        "GUID": "ignored_guid",
-        "WiFi": {
-          "SSID": "wifi_created",
-          "Security": "WEP-PSK"
-        }
-      },
-      callbackPass(function(guid) {
-        assertFalse(guid == "");
-        assertFalse(guid == "ignored_guid");
-        chrome.networkingPrivate.getProperties(
-          guid,
-          callbackPass(function(properties) {
-            assertEq("WiFi", properties.Type);
-            assertEq(guid, properties.GUID);
-            assertEq("wifi_created", properties.WiFi.SSID);
-            assertEq("WEP-PSK", properties.WiFi.Security);
-          }));
-      }));
-  },
-  function getNetworks() {
-    // Test 'type' and 'configured'.
-    chrome.networkingPrivate.getNetworks(
-      { "networkType": "WiFi", "configured": true },
-      callbackPass(function(result) {
-        assertEq([{
-          "Connectable": true,
-          "ConnectionState": "Connected",
-          "GUID": "stub_wifi1_guid",
-          "Name": "wifi1",
-          "Type": "WiFi",
-          "WiFi": {
-            "Security": "WEP-PSK",
-            "SignalStrength": 40
-          }
-        }, {
-          "GUID": "stub_wifi2_guid",
-          "Name": "wifi2_PSK",
-          "Type": "WiFi",
-          "WiFi": {
-            "Security": "WPA-PSK",
-          }
-        }], result);
-
-        // Test 'visible' (and 'configured').
-        chrome.networkingPrivate.getNetworks(
-          { "networkType": "WiFi", "visible": true, "configured": true },
-          callbackPass(function(result) {
-            assertEq([{
-              "Connectable": true,
-              "ConnectionState": "Connected",
-              "GUID": "stub_wifi1_guid",
-              "Name": "wifi1",
-              "Type": "WiFi",
-              "WiFi": {
-                "Security": "WEP-PSK",
-                "SignalStrength": 40
-              }
-            }], result);
-
-            // Test 'limit'.
-            chrome.networkingPrivate.getNetworks(
-              { "networkType": "All", "limit": 1 },
-              callbackPass(function(result) {
-                assertEq([{
-                  "ConnectionState": "Connected",
-                  "Ethernet": {
-                    "Authentication": "None"
-                  },
-                  "GUID": "stub_ethernet_guid",
-                  "Name": "eth0",
-                  "Type": "Ethernet"
-                }], result);
-              }));
-          }));
-      }));
-  },
-  function getVisibleNetworks() {
-    chrome.networkingPrivate.getVisibleNetworks(
-      "All",
-      callbackPass(function(result) {
-        assertEq([{
-                    "ConnectionState": "Connected",
-                    "Ethernet": {
-                      "Authentication": "None"
-                    },
-                    "GUID": "stub_ethernet_guid",
-                    "Name": "eth0",
-                    "Type": "Ethernet"
-                  },
-                  {
-                    "Connectable": true,
-                    "ConnectionState": "Connected",
-                    "GUID": "stub_wifi1_guid",
-                    "Name": "wifi1",
-                    "Type": "WiFi",
-                    "WiFi": {
-                      "Security": "WEP-PSK",
-                      "SignalStrength": 40
-                    }
-                  },
-                  {
-                    "Connectable": true,
-                    "ConnectionState": "Connected",
-                    "GUID": "stub_wimax_guid",
-                    "Name": "wimax",
-                    "Type": "WiMAX",
-                    "WiMAX": {
-                      "SignalStrength": 40
-                    }
-                  },
-                  {
-                    "ConnectionState": "Connected",
-                    "GUID": "stub_vpn1_guid",
-                    "Name": "vpn1",
-                    "Type": "VPN"
-                  },
-                  {
-                    "Connectable": true,
-                    "ConnectionState": "NotConnected",
-                    "GUID": "stub_wifi2_guid",
-                    "Name": "wifi2_PSK",
-                    "Type": "WiFi",
-                    "WiFi": {
-                      "Security": "WPA-PSK",
-                      "SignalStrength": 80
-                    }
-                  }], result);
-      }));
-  },
-  function getVisibleNetworksWifi() {
-    chrome.networkingPrivate.getVisibleNetworks(
-      "WiFi",
-      callbackPass(function(result) {
-        assertEq([{
-                    "Connectable": true,
-                    "ConnectionState": "Connected",
-                    "GUID": "stub_wifi1_guid",
-                    "Name": "wifi1",
-                    "Type": "WiFi",
-                    "WiFi": {
-                      "Security": "WEP-PSK",
-                      "SignalStrength": 40
-                    }
-                  },
-                  {
-                    "Connectable": true,
-                    "ConnectionState": "NotConnected",
-                    "GUID": "stub_wifi2_guid",
-                    "Name": "wifi2_PSK",
-                    "Type": "WiFi",
-                    "WiFi": {
-                      "Security": "WPA-PSK",
-                      "SignalStrength": 80
-                    }
-                  }
-                  ], result);
-      }));
-  },
-  function requestNetworkScan() {
-    // Connected or Connecting networks should be listed first, sorted by type.
-    var expected = ["stub_ethernet_guid",
-                    "stub_wifi1_guid",
-                    "stub_wimax_guid",
-                    "stub_vpn1_guid",
-                    "stub_wifi2_guid"];
-    var done = chrome.test.callbackAdded();
-    var listener = new privateHelpers.listListener(expected, done);
-    chrome.networkingPrivate.onNetworkListChanged.addListener(
-      listener.listenForChanges);
-    chrome.networkingPrivate.requestNetworkScan();
-  },
-  function getProperties() {
-    chrome.networkingPrivate.getProperties(
-      "stub_wifi1_guid",
-      callbackPass(function(result) {
-        assertEq({ "Connectable": true,
-                   "ConnectionState": "Connected",
-                   "GUID": "stub_wifi1_guid",
-                   "IPAddressConfigType": "Static",
-                   "IPConfigs": [{
-                     "Gateway": "0.0.0.1",
-                     "IPAddress": "0.0.0.0",
-                     "RoutingPrefix": 0,
-                     "Type": "IPv4"
-                   }],
-                   "MacAddress": "00:11:22:AA:BB:CC",
-                   "Name": "wifi1",
-                   "StaticIPConfig": {
-                     "IPAddress": "1.2.3.4",
-                     "Type": "IPv4"
-                   },
-                   "Type": "WiFi",
-                   "WiFi": {
-                     "HexSSID": "7769666931", // "wifi1"
-                     "Frequency": 2400,
-                     "FrequencyList": [2400],
-                     "SSID": "wifi1",
-                     "Security": "WEP-PSK",
-                     "SignalStrength": 40
-                   }
-                 }, result);
-      }));
-  },
-  function getPropertiesCellular() {
-    chrome.networkingPrivate.getProperties(
-      "stub_cellular1_guid",
-      callbackPass(function(result) {
-        assertEq({ "Cellular": {
-                     "ActivationState": "NotActivated",
-                     "AllowRoaming": false,
-                     "AutoConnect": true,
-                     "Carrier": "Cellular1_Carrier",
-                     "HomeProvider": {
-                       "country": "us",
-                       "name": "Cellular1_Provider"
-                     },
-                     "NetworkTechnology": "GSM",
-                     "RoamingState": "Home"
-                   },
-                   "ConnectionState": "NotConnected",
-                   "GUID": "stub_cellular1_guid",
-                   "Name": "cellular1",
-                   "Type": "Cellular"
-                 }, result);
-      }));
-  },
-  function getManagedProperties() {
-    chrome.networkingPrivate.getManagedProperties(
-      "stub_wifi2",
-      callbackPass(function(result) {
-        assertEq({
-                   "Connectable": true,
-                   "ConnectionState": "NotConnected",
-                   "GUID": "stub_wifi2",
-                   "Name": {
-                     "Active": "wifi2_PSK",
-                     "Effective": "UserPolicy",
-                     "UserPolicy": "My WiFi Network"
-                   },
-                   "Source": "UserPolicy",
-                   "Type": {
-                     "Active": "WiFi",
-                     "Effective": "UserPolicy",
-                     "UserPolicy": "WiFi"
-                   },
-                   "WiFi": {
-                     "AutoConnect": {
-                       "Active": false,
-                       "UserEditable": true
-                     },
-                     "HexSSID": {
-                       "Active": "77696669325F50534B", // "wifi2_PSK"
-                       "Effective": "UserPolicy",
-                       "UserPolicy": "77696669325F50534B"
-                     },
-                     "Frequency" : 5000,
-                     "FrequencyList" : [2400, 5000],
-                     "Passphrase": {
-                       "Effective": "UserSetting",
-                       "UserEditable": true,
-                       "UserSetting": "FAKE_CREDENTIAL_VPaJDV9x"
-                     },
-                     "SSID": {
-                       "Active": "wifi2_PSK",
-                       "Effective": "UserPolicy",
-                     },
-                     "Security": {
-                       "Active": "WPA-PSK",
-                       "Effective": "UserPolicy",
-                       "UserPolicy": "WPA-PSK"
-                     },
-                     "SignalStrength": 80,
-                   }
-                 }, result);
-      }));
-  },
-  function setWiFiProperties() {
-    var done = chrome.test.callbackAdded();
-    var network_guid = "stub_wifi1_guid";
-    chrome.networkingPrivate.getProperties(
-        network_guid,
-        callbackPass(function(result) {
-          assertEq(network_guid, result.GUID);
-          var new_properties = {
-            Priority: 1,
-            WiFi: {
-              AutoConnect: true
-            },
-            IPAddressConfigType: 'Static',
-            StaticIPConfig: {
-              IPAddress: '1.2.3.4'
-            }
-          };
-          chrome.networkingPrivate.setProperties(
-              network_guid,
-              new_properties,
-              callbackPass(function() {
-                chrome.networkingPrivate.getProperties(
-                    network_guid,
-                    callbackPass(function(result) {
-                      // Ensure that the GUID doesn't change.
-                      assertEq(network_guid, result.GUID);
-                      // Ensure that the properties were set.
-                      assertEq(1, result['Priority']);
-                      assertTrue('WiFi' in result);
-                      assertTrue('AutoConnect' in result['WiFi']);
-                      assertEq(true, result['WiFi']['AutoConnect']);
-                      assertTrue('StaticIPConfig' in result);
-                      assertEq('1.2.3.4',
-                               result['StaticIPConfig']['IPAddress']);
-                      done();
-                    }));
-              }));
-        }));
-  },
-  function setVPNProperties() {
-    var done = chrome.test.callbackAdded();
-    var network_guid = "stub_vpn1_guid";
-    chrome.networkingPrivate.getProperties(
-        network_guid,
-        callbackPass(function(result) {
-          assertEq(network_guid, result.GUID);
-          var new_properties = {
-            Priority: 1,
-            VPN: {
-              Host: 'vpn.host1'
-            }
-          };
-          chrome.networkingPrivate.setProperties(
-              network_guid,
-              new_properties,
-              callbackPass(function() {
-                chrome.networkingPrivate.getProperties(
-                    network_guid,
-                    callbackPass(function(result) {
-                      // Ensure that the properties were set.
-                      assertEq(1, result['Priority']);
-                      assertTrue('VPN' in result);
-                      assertTrue('Host' in result['VPN']);
-                      assertEq('vpn.host1', result['VPN']['Host']);
-                      // Ensure that the GUID doesn't change.
-                      assertEq(network_guid, result.GUID);
-                      done();
-                    }));
-              }));
-        }));
-  },
-  function getState() {
-    chrome.networkingPrivate.getState(
-      "stub_wifi2_guid",
-      callbackPass(function(result) {
-        assertEq({
-          "Connectable": true,
-          "ConnectionState": "NotConnected",
-          "GUID": "stub_wifi2_guid",
-          "Name": "wifi2_PSK",
-          "Type": "WiFi",
-          "WiFi": {
-            "Security": "WPA-PSK",
-            "SignalStrength": 80
-          }
-        }, result);
-      }));
-  },
-  function getStateNonExistent() {
-    chrome.networkingPrivate.getState(
-      'non_existent',
-      callbackFail('Error.InvalidNetworkGuid'));
-  },
-  function onNetworksChangedEventConnect() {
-    var network = "stub_wifi2_guid";
-    var done = chrome.test.callbackAdded();
-    var expectedStates = ["Connected"];
-    var listener =
-        new privateHelpers.watchForStateChanges(network, expectedStates, done);
-    chrome.networkingPrivate.startConnect(network, callbackPass());
-  },
-  function onNetworksChangedEventDisconnect() {
-    var network = "stub_wifi1_guid";
-    var done = chrome.test.callbackAdded();
-    var expectedStates = ["NotConnected"];
-    var listener =
-        new privateHelpers.watchForStateChanges(network, expectedStates, done);
-    chrome.networkingPrivate.startDisconnect(network, callbackPass());
-  },
-  function onNetworkListChangedEvent() {
-    // Connecting to wifi2 should set wifi1 to offline. Connected or Connecting
-    // networks should be listed first, sorted by type.
-    var expected = ["stub_ethernet_guid",
-                    "stub_wifi2_guid",
-                    "stub_wimax_guid",
-                    "stub_vpn1_guid",
-                    "stub_wifi1_guid"];
-    var done = chrome.test.callbackAdded();
-    var listener = new privateHelpers.listListener(expected, done);
-    chrome.networkingPrivate.onNetworkListChanged.addListener(
-      listener.listenForChanges);
-    var network = "stub_wifi2_guid";
-    chrome.networkingPrivate.startConnect(network, callbackPass());
-  },
-  function verifyDestination() {
-    chrome.networkingPrivate.verifyDestination(
-      verificationProperties,
-      callbackPass(function(isValid) {
-        assertTrue(isValid);
-      }));
-  },
-  function verifyAndEncryptCredentials() {
-    var network_guid = "stub_wifi2_guid";
-    chrome.networkingPrivate.verifyAndEncryptCredentials(
-      verificationProperties,
-      network_guid,
-      callbackPass(function(result) {
-        assertEq("encrypted_credentials", result);
-      }));
-  },
-  function verifyAndEncryptData() {
-    chrome.networkingPrivate.verifyAndEncryptData(
-      verificationProperties,
-      "data",
-      callbackPass(function(result) {
-        assertEq("encrypted_data", result);
-      }));
-  },
-  function setWifiTDLSEnabledState() {
-    chrome.networkingPrivate.setWifiTDLSEnabledState(
-      "aa:bb:cc:dd:ee:ff",
-      true,
-      callbackPass(function(result) {
-        assertEq("Connected", result);
-      }));
-  },
-  function getWifiTDLSStatus() {
-    chrome.networkingPrivate.getWifiTDLSStatus(
-      "aa:bb:cc:dd:ee:ff",
-      callbackPass(function(result) {
-        assertEq("Connected", result);
-      }));
-  },
-  function getCaptivePortalStatus() {
-    var networks = [['stub_ethernet_guid', 'Online'],
-                    ['stub_wifi1_guid', 'Offline'],
-                    ['stub_wifi2_guid', 'Portal'],
-                    ['stub_cellular1_guid', 'ProxyAuthRequired'],
-                    ['stub_vpn1_guid', 'Unknown']];
-    networks.forEach(function(network) {
-      var guid = network[0];
-      var expectedStatus = network[1];
-      chrome.networkingPrivate.getCaptivePortalStatus(
-        guid,
-        callbackPass(function(status) {
-          assertEq(expectedStatus, status);
-        }));
-    });
-  },
-  function captivePortalNotification() {
-    var done = chrome.test.callbackAdded();
-    var listener =
-        new privateHelpers.watchForCaptivePortalState(
-            'wifi_guid', 'Online', done);
-    chrome.test.sendMessage('notifyPortalDetectorObservers');
-  },
-];
-
-var testToRun = window.location.search.substring(1);
-chrome.test.runTests(availableTests.filter(function(op) {
-  return op.name == testToRun;
-}));
diff --git a/chrome/test/data/extensions/api_test/networking/main.html b/chrome/test/data/extensions/api_test/networking_private/chromeos/main.html
similarity index 100%
rename from chrome/test/data/extensions/api_test/networking/main.html
rename to chrome/test/data/extensions/api_test/networking_private/chromeos/main.html
diff --git a/chrome/test/data/extensions/api_test/networking/manifest.json b/chrome/test/data/extensions/api_test/networking_private/chromeos/manifest.json
similarity index 100%
rename from chrome/test/data/extensions/api_test/networking/manifest.json
rename to chrome/test/data/extensions/api_test/networking_private/chromeos/manifest.json
diff --git a/chrome/test/data/extensions/api_test/networking_private/chromeos/test.js b/chrome/test/data/extensions/api_test/networking_private/chromeos/test.js
new file mode 100644
index 0000000..d810af0
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/networking_private/chromeos/test.js
@@ -0,0 +1,589 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// The expectations in this test for the Chrome OS implementation. See
+// networking_private_chromeos_apitest.cc for more info.
+
+var callbackPass = chrome.test.callbackPass;
+var callbackFail = chrome.test.callbackFail;
+var assertTrue = chrome.test.assertTrue;
+var assertFalse = chrome.test.assertFalse;
+var assertEq = chrome.test.assertEq;
+
+// Test properties for the verification API.
+var verificationProperties = {
+  "certificate": "certificate",
+  "intermediateCertificates": ["ica1", "ica2", "ica3"],
+  "publicKey": "cHVibGljX2tleQ==",  // Base64("public_key")
+  "nonce": "nonce",
+  "signedData": "c2lnbmVkX2RhdGE=",  // Base64("signed_data")
+  "deviceSerial": "device_serial",
+  "deviceSsid": "Device 0123",
+  "deviceBssid": "00:01:02:03:04:05"
+};
+
+var privateHelpers = {
+  // Watches for the states |expectedStates| in reverse order. If all states
+  // were observed in the right order, succeeds and calls |done|. If any
+  // unexpected state is observed, fails.
+  watchForStateChanges: function(network, expectedStates, done) {
+    var self = this;
+    var collectProperties = function(properties) {
+      var finishTest = function() {
+        chrome.networkingPrivate.onNetworksChanged.removeListener(
+            self.onNetworkChange);
+        done();
+      };
+      if (expectedStates.length > 0) {
+        var expectedState = expectedStates.pop();
+        assertEq(expectedState, properties.ConnectionState);
+        if (expectedStates.length == 0)
+          finishTest();
+      }
+    };
+    this.onNetworkChange = function(changes) {
+      assertEq([network], changes);
+      chrome.networkingPrivate.getProperties(
+          network,
+          callbackPass(collectProperties));
+    };
+    chrome.networkingPrivate.onNetworksChanged.addListener(
+        this.onNetworkChange);
+  },
+  listListener: function(expected, done) {
+    var self = this;
+    this.listenForChanges = function(list) {
+      assertEq(expected, list);
+      chrome.networkingPrivate.onNetworkListChanged.removeListener(
+          self.listenForChanges);
+      done();
+    };
+  },
+  watchForCaptivePortalState: function(expectedGuid,
+                                       expectedState,
+                                       done) {
+    var self = this;
+    this.onPortalDetectionCompleted = function(guid, state) {
+      assertEq(expectedGuid, guid);
+      assertEq(expectedState, state);
+      chrome.networkingPrivate.onPortalDetectionCompleted.removeListener(
+          self.onPortalDetectionCompleted);
+      done();
+    };
+    chrome.networkingPrivate.onPortalDetectionCompleted.addListener(
+        self.onPortalDetectionCompleted);
+  }
+};
+
+var availableTests = [
+  function startConnect() {
+    chrome.networkingPrivate.startConnect("stub_wifi2_guid", callbackPass());
+  },
+  function startDisconnect() {
+    // Must connect to a network before we can disconnect from it.
+    chrome.networkingPrivate.startConnect("stub_wifi2_guid", callbackPass(
+      function() {
+        chrome.networkingPrivate.startDisconnect("stub_wifi2_guid",
+                                                 callbackPass());
+      }));
+  },
+  function startConnectNonexistent() {
+    chrome.networkingPrivate.startConnect(
+      "nonexistent_path",
+      callbackFail("Error.InvalidNetworkGuid"));
+  },
+  function startDisconnectNonexistent() {
+    chrome.networkingPrivate.startDisconnect(
+      "nonexistent_path",
+      callbackFail("Error.InvalidNetworkGuid"));
+  },
+  function startGetPropertiesNonexistent() {
+    chrome.networkingPrivate.getProperties(
+      "nonexistent_path",
+      callbackFail("Error.InvalidNetworkGuid"));
+  },
+  function createNetwork() {
+    chrome.networkingPrivate.createNetwork(
+      false,  // shared
+      { "Type": "WiFi",
+        "GUID": "ignored_guid",
+        "WiFi": {
+          "SSID": "wifi_created",
+          "Security": "WEP-PSK"
+        }
+      },
+      callbackPass(function(guid) {
+        assertFalse(guid == "");
+        assertFalse(guid == "ignored_guid");
+        chrome.networkingPrivate.getProperties(
+          guid,
+          callbackPass(function(properties) {
+            assertEq("WiFi", properties.Type);
+            assertEq(guid, properties.GUID);
+            assertEq("wifi_created", properties.WiFi.SSID);
+            assertEq("WEP-PSK", properties.WiFi.Security);
+          }));
+      }));
+  },
+  function getNetworks() {
+    // Test 'type' and 'configured'.
+    chrome.networkingPrivate.getNetworks(
+      { "networkType": "WiFi", "configured": true },
+      callbackPass(function(result) {
+        assertEq([{
+          "Connectable": true,
+          "ConnectionState": "Connected",
+          "GUID": "stub_wifi1_guid",
+          "Name": "wifi1",
+          "Type": "WiFi",
+          "Source":"User",
+          "WiFi": {
+            "Security": "WEP-PSK",
+            "SignalStrength": 40
+          }
+        }, {
+          "GUID": "stub_wifi2_guid",
+          "Name": "wifi2_PSK",
+          "Type": "WiFi",
+          "Source":"User",
+          "WiFi": {
+            "Security": "WPA-PSK",
+          }
+        }], result);
+
+        // Test 'visible' (and 'configured').
+        chrome.networkingPrivate.getNetworks(
+          { "networkType": "WiFi", "visible": true, "configured": true },
+          callbackPass(function(result) {
+            assertEq([{
+              "Connectable": true,
+              "ConnectionState": "Connected",
+              "GUID": "stub_wifi1_guid",
+              "Name": "wifi1",
+              "Source":"User",
+              "Type": "WiFi",
+              "WiFi": {
+                "Security": "WEP-PSK",
+                "SignalStrength": 40
+              }
+            }], result);
+
+            // Test 'limit'.
+            chrome.networkingPrivate.getNetworks(
+              { "networkType": "All", "limit": 1 },
+              callbackPass(function(result) {
+                assertEq([{
+                  "ConnectionState": "Connected",
+                  "Ethernet": {
+                    "Authentication": "None"
+                  },
+                  "GUID": "stub_ethernet_guid",
+                  "Name": "eth0",
+                  "Source":"Device",
+                  "Type": "Ethernet"
+                }], result);
+              }));
+          }));
+      }));
+  },
+  function getVisibleNetworks() {
+    chrome.networkingPrivate.getVisibleNetworks(
+      "All",
+      callbackPass(function(result) {
+        assertEq([{
+                    "ConnectionState": "Connected",
+                    "Ethernet": {
+                      "Authentication": "None"
+                    },
+                    "GUID": "stub_ethernet_guid",
+                    "Name": "eth0",
+                    "Source":"Device",
+                    "Type": "Ethernet"
+                  },
+                  {
+                    "Connectable": true,
+                    "ConnectionState": "Connected",
+                    "GUID": "stub_wifi1_guid",
+                    "Name": "wifi1",
+                    "Source": "User",
+                    "Type": "WiFi",
+                    "WiFi": {
+                      "Security": "WEP-PSK",
+                      "SignalStrength": 40
+                    }
+                  },
+                  {
+                    "Connectable": true,
+                    "ConnectionState": "Connected",
+                    "GUID": "stub_wimax_guid",
+                    "Name": "wimax",
+                    "Source":"User",
+                    "Type": "WiMAX",
+                    "WiMAX": {
+                      "SignalStrength": 40
+                    }
+                  },
+                  {
+                    "ConnectionState": "Connected",
+                    "GUID": "stub_vpn1_guid",
+                    "Name": "vpn1",
+                    "Source":"User",
+                    "Type": "VPN"
+                  },
+                  {
+                    "Connectable": true,
+                    "ConnectionState": "NotConnected",
+                    "GUID": "stub_wifi2_guid",
+                    "Name": "wifi2_PSK",
+                    "Source": "User",
+                    "Type": "WiFi",
+                    "WiFi": {
+                      "Security": "WPA-PSK",
+                      "SignalStrength": 80
+                    }
+                  }], result);
+      }));
+  },
+  function getVisibleNetworksWifi() {
+    chrome.networkingPrivate.getVisibleNetworks(
+      "WiFi",
+      callbackPass(function(result) {
+        assertEq([{
+                    "Connectable": true,
+                    "ConnectionState": "Connected",
+                    "GUID": "stub_wifi1_guid",
+                    "Name": "wifi1",
+                    "Source": "User",
+                    "Type": "WiFi",
+                    "WiFi": {
+                      "Security": "WEP-PSK",
+                      "SignalStrength": 40
+                    }
+                  },
+                  {
+                    "Connectable": true,
+                    "ConnectionState": "NotConnected",
+                    "GUID": "stub_wifi2_guid",
+                    "Name": "wifi2_PSK",
+                    "Source": "User",
+                    "Type": "WiFi",
+                    "WiFi": {
+                      "Security": "WPA-PSK",
+                      "SignalStrength": 80
+                    }
+                  }
+                  ], result);
+      }));
+  },
+  function requestNetworkScan() {
+    // Connected or Connecting networks should be listed first, sorted by type.
+    var expected = ["stub_ethernet_guid",
+                    "stub_wifi1_guid",
+                    "stub_wimax_guid",
+                    "stub_vpn1_guid",
+                    "stub_wifi2_guid"];
+    var done = chrome.test.callbackAdded();
+    var listener = new privateHelpers.listListener(expected, done);
+    chrome.networkingPrivate.onNetworkListChanged.addListener(
+      listener.listenForChanges);
+    chrome.networkingPrivate.requestNetworkScan();
+  },
+  function getProperties() {
+    chrome.networkingPrivate.getProperties(
+      "stub_wifi1_guid",
+      callbackPass(function(result) {
+        assertEq({ "Connectable": true,
+                   "ConnectionState": "Connected",
+                   "GUID": "stub_wifi1_guid",
+                   "IPAddressConfigType": "Static",
+                   "IPConfigs": [{
+                     "Gateway": "0.0.0.1",
+                     "IPAddress": "0.0.0.0",
+                     "RoutingPrefix": 0,
+                     "Type": "IPv4"
+                   }],
+                   "MacAddress": "00:11:22:AA:BB:CC",
+                   "Name": "wifi1",
+                   "StaticIPConfig": {
+                     "IPAddress": "1.2.3.4",
+                     "Type": "IPv4"
+                   },
+                   "Type": "WiFi",
+                   "WiFi": {
+                     "HexSSID": "7769666931", // "wifi1"
+                     "Frequency": 2400,
+                     "FrequencyList": [2400],
+                     "SSID": "wifi1",
+                     "Security": "WEP-PSK",
+                     "SignalStrength": 40
+                   }
+                 }, result);
+      }));
+  },
+  function getPropertiesCellular() {
+    chrome.networkingPrivate.getProperties(
+      "stub_cellular1_guid",
+      callbackPass(function(result) {
+        assertEq({ "Cellular": {
+                     "ActivationState": "NotActivated",
+                     "AllowRoaming": false,
+                     "AutoConnect": true,
+                     "Carrier": "Cellular1_Carrier",
+                     "HomeProvider": {
+                       "country": "us",
+                       "name": "Cellular1_Provider"
+                     },
+                     "NetworkTechnology": "GSM",
+                     "RoamingState": "Home"
+                   },
+                   "ConnectionState": "NotConnected",
+                   "GUID": "stub_cellular1_guid",
+                   "Name": "cellular1",
+                   "Type": "Cellular"
+                 }, result);
+      }));
+  },
+  function getManagedProperties() {
+    chrome.networkingPrivate.getManagedProperties(
+      "stub_wifi2",
+      callbackPass(function(result) {
+        assertEq({
+                   "Connectable": true,
+                   "ConnectionState": "NotConnected",
+                   "GUID": "stub_wifi2",
+                   "Name": {
+                     "Active": "wifi2_PSK",
+                     "Effective": "UserPolicy",
+                     "UserPolicy": "My WiFi Network"
+                   },
+                   "Source": "UserPolicy",
+                   "Type": {
+                     "Active": "WiFi",
+                     "Effective": "UserPolicy",
+                     "UserPolicy": "WiFi"
+                   },
+                   "WiFi": {
+                     "AutoConnect": {
+                       "Active": false,
+                       "UserEditable": true
+                     },
+                     "HexSSID": {
+                       "Active": "77696669325F50534B", // "wifi2_PSK"
+                       "Effective": "UserPolicy",
+                       "UserPolicy": "77696669325F50534B"
+                     },
+                     "Frequency" : 5000,
+                     "FrequencyList" : [2400, 5000],
+                     "Passphrase": {
+                       "Effective": "UserSetting",
+                       "UserEditable": true,
+                       "UserSetting": "FAKE_CREDENTIAL_VPaJDV9x"
+                     },
+                     "SSID": {
+                       "Active": "wifi2_PSK",
+                       "Effective": "UserPolicy",
+                     },
+                     "Security": {
+                       "Active": "WPA-PSK",
+                       "Effective": "UserPolicy",
+                       "UserPolicy": "WPA-PSK"
+                     },
+                     "SignalStrength": 80,
+                   }
+                 }, result);
+      }));
+  },
+  function setWiFiProperties() {
+    var done = chrome.test.callbackAdded();
+    var network_guid = "stub_wifi1_guid";
+    chrome.networkingPrivate.getProperties(
+        network_guid,
+        callbackPass(function(result) {
+          assertEq(network_guid, result.GUID);
+          var new_properties = {
+            Priority: 1,
+            WiFi: {
+              AutoConnect: true
+            },
+            IPAddressConfigType: 'Static',
+            StaticIPConfig: {
+              IPAddress: '1.2.3.4'
+            }
+          };
+          chrome.networkingPrivate.setProperties(
+              network_guid,
+              new_properties,
+              callbackPass(function() {
+                chrome.networkingPrivate.getProperties(
+                    network_guid,
+                    callbackPass(function(result) {
+                      // Ensure that the GUID doesn't change.
+                      assertEq(network_guid, result.GUID);
+                      // Ensure that the properties were set.
+                      assertEq(1, result['Priority']);
+                      assertTrue('WiFi' in result);
+                      assertTrue('AutoConnect' in result['WiFi']);
+                      assertEq(true, result['WiFi']['AutoConnect']);
+                      assertTrue('StaticIPConfig' in result);
+                      assertEq('1.2.3.4',
+                               result['StaticIPConfig']['IPAddress']);
+                      done();
+                    }));
+              }));
+        }));
+  },
+  function setVPNProperties() {
+    var done = chrome.test.callbackAdded();
+    var network_guid = "stub_vpn1_guid";
+    chrome.networkingPrivate.getProperties(
+        network_guid,
+        callbackPass(function(result) {
+          assertEq(network_guid, result.GUID);
+          var new_properties = {
+            Priority: 1,
+            VPN: {
+              Host: 'vpn.host1'
+            }
+          };
+          chrome.networkingPrivate.setProperties(
+              network_guid,
+              new_properties,
+              callbackPass(function() {
+                chrome.networkingPrivate.getProperties(
+                    network_guid,
+                    callbackPass(function(result) {
+                      // Ensure that the properties were set.
+                      assertEq(1, result['Priority']);
+                      assertTrue('VPN' in result);
+                      assertTrue('Host' in result['VPN']);
+                      assertEq('vpn.host1', result['VPN']['Host']);
+                      // Ensure that the GUID doesn't change.
+                      assertEq(network_guid, result.GUID);
+                      done();
+                    }));
+              }));
+        }));
+  },
+  function getState() {
+    chrome.networkingPrivate.getState(
+      "stub_wifi2_guid",
+      callbackPass(function(result) {
+        assertEq({
+          "Connectable": true,
+          "ConnectionState": "NotConnected",
+          "GUID": "stub_wifi2_guid",
+          "Name": "wifi2_PSK",
+          "Source": "User",
+          "Type": "WiFi",
+          "WiFi": {
+            "Security": "WPA-PSK",
+            "SignalStrength": 80
+          }
+        }, result);
+      }));
+  },
+  function getStateNonExistent() {
+    chrome.networkingPrivate.getState(
+      'non_existent',
+      callbackFail('Error.InvalidNetworkGuid'));
+  },
+  function onNetworksChangedEventConnect() {
+    var network = "stub_wifi2_guid";
+    var done = chrome.test.callbackAdded();
+    var expectedStates = ["Connected"];
+    var listener =
+        new privateHelpers.watchForStateChanges(network, expectedStates, done);
+    chrome.networkingPrivate.startConnect(network, callbackPass());
+  },
+  function onNetworksChangedEventDisconnect() {
+    var network = "stub_wifi1_guid";
+    var done = chrome.test.callbackAdded();
+    var expectedStates = ["NotConnected"];
+    var listener =
+        new privateHelpers.watchForStateChanges(network, expectedStates, done);
+    chrome.networkingPrivate.startDisconnect(network, callbackPass());
+  },
+  function onNetworkListChangedEvent() {
+    // Connecting to wifi2 should set wifi1 to offline. Connected or Connecting
+    // networks should be listed first, sorted by type.
+    var expected = ["stub_ethernet_guid",
+                    "stub_wifi2_guid",
+                    "stub_wimax_guid",
+                    "stub_vpn1_guid",
+                    "stub_wifi1_guid"];
+    var done = chrome.test.callbackAdded();
+    var listener = new privateHelpers.listListener(expected, done);
+    chrome.networkingPrivate.onNetworkListChanged.addListener(
+      listener.listenForChanges);
+    var network = "stub_wifi2_guid";
+    chrome.networkingPrivate.startConnect(network, callbackPass());
+  },
+  function verifyDestination() {
+    chrome.networkingPrivate.verifyDestination(
+      verificationProperties,
+      callbackPass(function(isValid) {
+        assertTrue(isValid);
+      }));
+  },
+  function verifyAndEncryptCredentials() {
+    var network_guid = "stub_wifi2_guid";
+    chrome.networkingPrivate.verifyAndEncryptCredentials(
+      verificationProperties,
+      network_guid,
+      callbackPass(function(result) {
+        assertEq("encrypted_credentials", result);
+      }));
+  },
+  function verifyAndEncryptData() {
+    chrome.networkingPrivate.verifyAndEncryptData(
+      verificationProperties,
+      "data",
+      callbackPass(function(result) {
+        assertEq("encrypted_data", result);
+      }));
+  },
+  function setWifiTDLSEnabledState() {
+    chrome.networkingPrivate.setWifiTDLSEnabledState(
+      "aa:bb:cc:dd:ee:ff",
+      true,
+      callbackPass(function(result) {
+        assertEq("Connected", result);
+      }));
+  },
+  function getWifiTDLSStatus() {
+    chrome.networkingPrivate.getWifiTDLSStatus(
+      "aa:bb:cc:dd:ee:ff",
+      callbackPass(function(result) {
+        assertEq("Connected", result);
+      }));
+  },
+  function getCaptivePortalStatus() {
+    var networks = [['stub_ethernet_guid', 'Online'],
+                    ['stub_wifi1_guid', 'Offline'],
+                    ['stub_wifi2_guid', 'Portal'],
+                    ['stub_cellular1_guid', 'ProxyAuthRequired'],
+                    ['stub_vpn1_guid', 'Unknown']];
+    networks.forEach(function(network) {
+      var guid = network[0];
+      var expectedStatus = network[1];
+      chrome.networkingPrivate.getCaptivePortalStatus(
+        guid,
+        callbackPass(function(status) {
+          assertEq(expectedStatus, status);
+        }));
+    });
+  },
+  function captivePortalNotification() {
+    var done = chrome.test.callbackAdded();
+    var listener =
+        new privateHelpers.watchForCaptivePortalState(
+            'wifi_guid', 'Online', done);
+    chrome.test.sendMessage('notifyPortalDetectorObservers');
+  },
+];
+
+var testToRun = window.location.search.substring(1);
+chrome.test.runTests(availableTests.filter(function(op) {
+  return op.name == testToRun;
+}));
diff --git a/chrome/test/data/extensions/api_test/networking/main.html b/chrome/test/data/extensions/api_test/networking_private/service_client/main.html
similarity index 100%
copy from chrome/test/data/extensions/api_test/networking/main.html
copy to chrome/test/data/extensions/api_test/networking_private/service_client/main.html
diff --git a/chrome/test/data/extensions/api_test/networking/manifest.json b/chrome/test/data/extensions/api_test/networking_private/service_client/manifest.json
similarity index 100%
copy from chrome/test/data/extensions/api_test/networking/manifest.json
copy to chrome/test/data/extensions/api_test/networking_private/service_client/manifest.json
diff --git a/chrome/test/data/extensions/api_test/networking_private/service_client/test.js b/chrome/test/data/extensions/api_test/networking_private/service_client/test.js
new file mode 100644
index 0000000..a434607
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/networking_private/service_client/test.js
@@ -0,0 +1,495 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// The expectations in this test are for the ServiceClient implementation.
+// Note: ServiceClient currently only implements WiFi networks. See
+// networking_private_service_client_apitest.cc for more info.
+
+var callbackPass = chrome.test.callbackPass;
+var callbackFail = chrome.test.callbackFail;
+var assertTrue = chrome.test.assertTrue;
+var assertFalse = chrome.test.assertFalse;
+var assertEq = chrome.test.assertEq;
+
+// Test properties for the verification API.
+var verificationProperties = {
+  "certificate": "certificate",
+  "intermediateCertificates": ["ica1", "ica2", "ica3"],
+  "publicKey": "cHVibGljX2tleQ==",  // Base64("public_key")
+  "nonce": "nonce",
+  "signedData": "c2lnbmVkX2RhdGE=",  // Base64("signed_data")
+  "deviceSerial": "device_serial",
+  "deviceSsid": "Device 0123",
+  "deviceBssid": "00:01:02:03:04:05"
+};
+
+var privateHelpers = {
+  // Watches for the states |expectedStates| in reverse order. If all states
+  // were observed in the right order, succeeds and calls |done|. If any
+  // unexpected state is observed, fails.
+  watchForStateChanges: function(network, expectedStates, done) {
+    var self = this;
+    var collectProperties = function(properties) {
+      var finishTest = function() {
+        chrome.networkingPrivate.onNetworksChanged.removeListener(
+            self.onNetworkChange);
+        done();
+      };
+      if (expectedStates.length > 0) {
+        var expectedState = expectedStates.pop();
+        assertEq(expectedState, properties.ConnectionState);
+        if (expectedStates.length == 0)
+          finishTest();
+      }
+    };
+    this.onNetworkChange = function(changes) {
+      assertEq([network], changes);
+      chrome.networkingPrivate.getProperties(
+          network,
+          callbackPass(collectProperties));
+    };
+    chrome.networkingPrivate.onNetworksChanged.addListener(
+        this.onNetworkChange);
+  },
+  listListener: function(expected, done) {
+    var self = this;
+    this.listenForChanges = function(list) {
+      assertEq(expected, list);
+      chrome.networkingPrivate.onNetworkListChanged.removeListener(
+          self.listenForChanges);
+      done();
+    };
+  },
+  watchForCaptivePortalState: function(expectedGuid,
+                                       expectedState,
+                                       done) {
+    var self = this;
+    this.onPortalDetectionCompleted = function(guid, state) {
+      assertEq(expectedGuid, guid);
+      assertEq(expectedState, state);
+      chrome.networkingPrivate.onPortalDetectionCompleted.removeListener(
+          self.onPortalDetectionCompleted);
+      done();
+    };
+    chrome.networkingPrivate.onPortalDetectionCompleted.addListener(
+        self.onPortalDetectionCompleted);
+  }
+};
+
+var availableTests = [
+  function startConnect() {
+    chrome.networkingPrivate.startConnect("stub_wifi2_guid", callbackPass());
+  },
+  function startDisconnect() {
+    // Must connect to a network before we can disconnect from it.
+    chrome.networkingPrivate.startConnect("stub_wifi2_guid", callbackPass(
+      function() {
+        chrome.networkingPrivate.startDisconnect("stub_wifi2_guid",
+                                                 callbackPass());
+      }));
+  },
+  function startConnectNonexistent() {
+    chrome.networkingPrivate.startConnect(
+      "nonexistent_path",
+      callbackFail("Error.InvalidNetworkGuid"));
+  },
+  function startDisconnectNonexistent() {
+    chrome.networkingPrivate.startDisconnect(
+      "nonexistent_path",
+      callbackFail("Error.InvalidNetworkGuid"));
+  },
+  function startGetPropertiesNonexistent() {
+    chrome.networkingPrivate.getProperties(
+      "nonexistent_path",
+      callbackFail("Error.InvalidNetworkGuid"));
+  },
+  function createNetwork() {
+    chrome.networkingPrivate.createNetwork(
+      false,  // shared
+      { "Type": "WiFi",
+        "GUID": "ignored_guid",
+        "WiFi": {
+          "SSID": "wifi_created",
+          "Security": "WEP-PSK"
+        }
+      },
+      callbackPass(function(guid) {
+        assertFalse(guid == "");
+        assertFalse(guid == "ignored_guid");
+        chrome.networkingPrivate.getProperties(
+          guid,
+          callbackPass(function(properties) {
+            assertEq("WiFi", properties.Type);
+            assertEq(guid, properties.GUID);
+            assertEq("wifi_created", properties.WiFi.SSID);
+            assertEq("WEP-PSK", properties.WiFi.Security);
+          }));
+      }));
+  },
+  function getNetworks() {
+    // Test 'type' and 'configured'.
+    chrome.networkingPrivate.getNetworks(
+      { "networkType": "WiFi", "configured": true },
+      callbackPass(function(result) {
+        assertEq([{
+          "Connectable": true,
+          "ConnectionState": "Connected",
+          "GUID": "stub_wifi1_guid",
+          "Name": "wifi1",
+          "Type": "WiFi",
+          "WiFi": {
+            "Security": "WEP-PSK",
+            "SignalStrength": 40
+          }
+        }, {
+          "Connectable": true,
+          "ConnectionState": "NotConnected",
+          "GUID": "stub_wifi2_guid",
+          "Name": "wifi2_PSK",
+          "Type": "WiFi",
+          "WiFi": {
+            "Security": "WPA-PSK",
+            "SignalStrength": 80
+          }
+        }], result);
+
+        // Test 'limit'.
+        chrome.networkingPrivate.getNetworks(
+          { "networkType": "All", "limit": 1 },
+          callbackPass(function(result) {
+            assertEq([{
+              "Connectable": true,
+              "ConnectionState": "Connected",
+              "GUID": "stub_wifi1_guid",
+              "Name": "wifi1",
+              "Type": "WiFi",
+              "WiFi": {
+                    "Security": "WEP-PSK",
+                "SignalStrength": 40
+              }
+            }], result);
+          }));
+      }));
+  },
+  function getVisibleNetworks() {
+    chrome.networkingPrivate.getVisibleNetworks(
+      "All",
+      callbackPass(function(result) {
+        assertEq([{
+                    "Connectable": true,
+                    "ConnectionState": "Connected",
+                    "GUID": "stub_wifi1_guid",
+                    "Name": "wifi1",
+                    "Type": "WiFi",
+                    "WiFi": {
+                      "Security": "WEP-PSK",
+                      "SignalStrength": 40
+                    }
+                  },
+                  {
+                    "Connectable": true,
+                    "ConnectionState": "NotConnected",
+                    "GUID": "stub_wifi2_guid",
+                    "Name": "wifi2_PSK",
+                    "Type": "WiFi",
+                    "WiFi": {
+                      "Security": "WPA-PSK",
+                      "SignalStrength": 80
+                    }
+                  }], result);
+      }));
+  },
+  function getVisibleNetworksWifi() {
+    chrome.networkingPrivate.getVisibleNetworks(
+      "WiFi",
+      callbackPass(function(result) {
+        assertEq([{
+                    "Connectable": true,
+                    "ConnectionState": "Connected",
+                    "GUID": "stub_wifi1_guid",
+                    "Name": "wifi1",
+                    "Type": "WiFi",
+                    "WiFi": {
+                      "Security": "WEP-PSK",
+                      "SignalStrength": 40
+                    }
+                  },
+                  {
+                    "Connectable": true,
+                    "ConnectionState": "NotConnected",
+                    "GUID": "stub_wifi2_guid",
+                    "Name": "wifi2_PSK",
+                    "Type": "WiFi",
+                    "WiFi": {
+                      "Security": "WPA-PSK",
+                      "SignalStrength": 80
+                    }
+                  }
+                  ], result);
+      }));
+  },
+  function requestNetworkScan() {
+    // Connected or Connecting networks should be listed first, sorted by type.
+    var expected = ["stub_wifi1_guid",
+                    "stub_wifi2_guid"];
+    var done = chrome.test.callbackAdded();
+    var listener = new privateHelpers.listListener(expected, done);
+    chrome.networkingPrivate.onNetworkListChanged.addListener(
+      listener.listenForChanges);
+    chrome.networkingPrivate.requestNetworkScan();
+  },
+  function getProperties() {
+    chrome.networkingPrivate.getProperties(
+      "stub_wifi1_guid",
+      callbackPass(function(result) {
+        assertEq({ "Connectable": true,
+                   "ConnectionState": "Connected",
+                   "GUID": "stub_wifi1_guid",
+                   "Name": "wifi1",
+                   "Type": "WiFi",
+                   "WiFi": {
+                     "HexSSID": "7769666931", // "wifi1"
+                     "SSID": "wifi1",
+                     "Security": "WEP-PSK",
+                     "SignalStrength": 40
+                   }
+                 }, result);
+      }));
+  },
+  function getManagedProperties() {
+    chrome.networkingPrivate.getManagedProperties(
+      "stub_wifi2",
+      callbackPass(function(result) {
+        assertEq({
+                   "Connectable": true,
+                   "ConnectionState": "NotConnected",
+                   "GUID": "stub_wifi2",
+                   "Name": {
+                     "Active": "wifi2_PSK",
+                     "Effective": "UserPolicy",
+                     "UserPolicy": "My WiFi Network"
+                   },
+                   "Source": "UserPolicy",
+                   "Type": {
+                     "Active": "WiFi",
+                     "Effective": "UserPolicy",
+                     "UserPolicy": "WiFi"
+                   },
+                   "WiFi": {
+                     "AutoConnect": {
+                       "Active": false,
+                       "UserEditable": true
+                     },
+                     "HexSSID": {
+                       "Active": "77696669325F50534B", // "wifi2_PSK"
+                       "Effective": "UserPolicy",
+                       "UserPolicy": "77696669325F50534B"
+                     },
+                     "Frequency" : 5000,
+                     "FrequencyList" : [2400, 5000],
+                     "Passphrase": {
+                       "Effective": "UserSetting",
+                       "UserEditable": true,
+                       "UserSetting": "FAKE_CREDENTIAL_VPaJDV9x"
+                     },
+                     "SSID": {
+                       "Active": "wifi2_PSK",
+                       "Effective": "UserPolicy",
+                     },
+                     "Security": {
+                       "Active": "WPA-PSK",
+                       "Effective": "UserPolicy",
+                       "UserPolicy": "WPA-PSK"
+                     },
+                     "SignalStrength": 80,
+                   }
+                 }, result);
+      }));
+  },
+  function setWiFiProperties() {
+    var done = chrome.test.callbackAdded();
+    var network_guid = "stub_wifi1_guid";
+    chrome.networkingPrivate.getProperties(
+        network_guid,
+        callbackPass(function(result) {
+          assertEq(network_guid, result.GUID);
+          var new_properties = {
+            Priority: 1,
+            WiFi: {
+              AutoConnect: true
+            },
+            IPAddressConfigType: 'Static',
+            StaticIPConfig: {
+              IPAddress: '1.2.3.4'
+            }
+          };
+          chrome.networkingPrivate.setProperties(
+              network_guid,
+              new_properties,
+              callbackPass(function() {
+                chrome.networkingPrivate.getProperties(
+                    network_guid,
+                    callbackPass(function(result) {
+                      // Ensure that the GUID doesn't change.
+                      assertEq(network_guid, result.GUID);
+                      // Ensure that the properties were set.
+                      assertEq(1, result['Priority']);
+                      assertTrue('WiFi' in result);
+                      assertTrue('AutoConnect' in result['WiFi']);
+                      assertEq(true, result['WiFi']['AutoConnect']);
+                      assertTrue('StaticIPConfig' in result);
+                      assertEq('1.2.3.4',
+                               result['StaticIPConfig']['IPAddress']);
+                      done();
+                    }));
+              }));
+        }));
+  },
+  function setVPNProperties() {
+    var done = chrome.test.callbackAdded();
+    var network_guid = "stub_vpn1_guid";
+    chrome.networkingPrivate.getProperties(
+        network_guid,
+        callbackPass(function(result) {
+          assertEq(network_guid, result.GUID);
+          var new_properties = {
+            Priority: 1,
+            VPN: {
+              Host: 'vpn.host1'
+            }
+          };
+          chrome.networkingPrivate.setProperties(
+              network_guid,
+              new_properties,
+              callbackPass(function() {
+                chrome.networkingPrivate.getProperties(
+                    network_guid,
+                    callbackPass(function(result) {
+                      // Ensure that the properties were set.
+                      assertEq(1, result['Priority']);
+                      assertTrue('VPN' in result);
+                      assertTrue('Host' in result['VPN']);
+                      assertEq('vpn.host1', result['VPN']['Host']);
+                      // Ensure that the GUID doesn't change.
+                      assertEq(network_guid, result.GUID);
+                      done();
+                    }));
+              }));
+        }));
+  },
+  function getState() {
+    chrome.networkingPrivate.getState(
+      "stub_wifi2_guid",
+      callbackPass(function(result) {
+        assertEq({
+          "Connectable": true,
+          "ConnectionState": "NotConnected",
+          "GUID": "stub_wifi2_guid",
+          "Name": "wifi2_PSK",
+          "Type": "WiFi",
+          "WiFi": {
+            "Security": "WPA-PSK",
+            "SignalStrength": 80
+          }
+        }, result);
+      }));
+  },
+  function getStateNonExistent() {
+    chrome.networkingPrivate.getState(
+      'non_existent',
+      callbackFail('Error.InvalidNetworkGuid'));
+  },
+  function onNetworksChangedEventConnect() {
+    var network = "stub_wifi2_guid";
+    var done = chrome.test.callbackAdded();
+    var expectedStates = ["Connected"];
+    var listener =
+        new privateHelpers.watchForStateChanges(network, expectedStates, done);
+    chrome.networkingPrivate.startConnect(network, callbackPass());
+  },
+  function onNetworksChangedEventDisconnect() {
+    var network = "stub_wifi1_guid";
+    var done = chrome.test.callbackAdded();
+    var expectedStates = ["NotConnected"];
+    var listener =
+        new privateHelpers.watchForStateChanges(network, expectedStates, done);
+    chrome.networkingPrivate.startDisconnect(network, callbackPass());
+  },
+  function onNetworkListChangedEvent() {
+    // Connecting to wifi2 should set wifi1 to offline. Connected or Connecting
+    // networks should be listed first, sorted by type.
+    var expected = ["stub_wifi2_guid",
+                    "stub_wifi1_guid"];
+    var done = chrome.test.callbackAdded();
+    var listener = new privateHelpers.listListener(expected, done);
+    chrome.networkingPrivate.onNetworkListChanged.addListener(
+      listener.listenForChanges);
+    var network = "stub_wifi2_guid";
+    chrome.networkingPrivate.startConnect(network, callbackPass());
+  },
+  function verifyDestination() {
+    chrome.networkingPrivate.verifyDestination(
+      verificationProperties,
+      callbackPass(function(isValid) {
+        assertTrue(isValid);
+      }));
+  },
+  function verifyAndEncryptCredentials() {
+    var network_guid = "stub_wifi2_guid";
+    chrome.networkingPrivate.verifyAndEncryptCredentials(
+      verificationProperties,
+      network_guid,
+      callbackPass(function(result) {
+        assertEq("encrypted_credentials", result);
+      }));
+  },
+  function verifyAndEncryptData() {
+    chrome.networkingPrivate.verifyAndEncryptData(
+      verificationProperties,
+      "data",
+      callbackPass(function(result) {
+        assertEq("encrypted_data", result);
+      }));
+  },
+  function setWifiTDLSEnabledState() {
+    chrome.networkingPrivate.setWifiTDLSEnabledState(
+      "aa:bb:cc:dd:ee:ff",
+      true,
+      callbackPass(function(result) {
+        assertEq("Connected", result);
+      }));
+  },
+  function getWifiTDLSStatus() {
+    chrome.networkingPrivate.getWifiTDLSStatus(
+      "aa:bb:cc:dd:ee:ff",
+      callbackPass(function(result) {
+        assertEq("Connected", result);
+      }));
+  },
+  function getCaptivePortalStatus() {
+    var networks = [['stub_wifi1_guid', 'Offline'],
+                    ['stub_wifi2_guid', 'Portal']];
+    networks.forEach(function(network) {
+      var guid = network[0];
+      var expectedStatus = network[1];
+      chrome.networkingPrivate.getCaptivePortalStatus(
+        guid,
+        callbackPass(function(status) {
+          assertEq(expectedStatus, status);
+        }));
+    });
+  },
+  function captivePortalNotification() {
+    var done = chrome.test.callbackAdded();
+    var listener =
+        new privateHelpers.watchForCaptivePortalState(
+            'wifi_guid', 'Online', done);
+    chrome.test.sendMessage('notifyPortalDetectorObservers');
+  },
+];
+
+var testToRun = window.location.search.substring(1);
+chrome.test.runTests(availableTests.filter(function(op) {
+  return op.name == testToRun;
+}));
diff --git a/chrome/test/data/extensions/api_test/platform_keys/basic.js b/chrome/test/data/extensions/api_test/platform_keys/basic.js
index 81d84fd..3f5c73574 100644
--- a/chrome/test/data/extensions/api_test/platform_keys/basic.js
+++ b/chrome/test/data/extensions/api_test/platform_keys/basic.js
@@ -4,11 +4,13 @@
 
 'use strict';
 
-var systemTokenEnabled = (location.href.indexOf("systemTokenEnabled") != -1);
+var systemTokenEnabled = (location.search.indexOf("systemTokenEnabled") != -1);
+var selectedTestSuite = location.hash.slice(1);
+console.log('[SELECTED TEST SUITE] ' + selectedTestSuite +
+            ', systemTokenEnable ' + systemTokenEnabled);
 
 var assertEq = chrome.test.assertEq;
 var assertTrue = chrome.test.assertTrue;
-var assertThrows = chrome.test.assertThrows;
 var fail = chrome.test.fail;
 var succeed = chrome.test.succeed;
 var callbackPass = chrome.test.callbackPass;
@@ -112,10 +114,9 @@
   return certs.sort(compareArrays);
 }
 
-function assertCertsSelected(request, expectedCerts, callback) {
+function assertCertsSelected(details, expectedCerts, callback) {
   chrome.platformKeys.selectClientCertificates(
-      {interactive: false, request: request},
-      callbackPass(function(actualMatches) {
+      details, callbackPass(function(actualMatches) {
         assertEq(expectedCerts.length, actualMatches.length,
                  'Number of stored certs not as expected');
         if (expectedCerts.length == actualMatches.length) {
@@ -190,32 +191,54 @@
   succeed();
 }
 
-function testSelectAllCerts() {
-  var requestAll = {
+var requestAll = {
+  certificateTypes: [],
+  certificateAuthorities: []
+};
+
+// Depends on |data|, thus it cannot be created immediately.
+function requestCA1() {
+  return {
     certificateTypes: [],
-    certificateAuthorities: []
+    certificateAuthorities: [data.client_1_issuer_dn.buffer]
   };
+}
+
+function testSelectAllCerts() {
   var expectedCerts = [data.client_1];
   if (systemTokenEnabled)
     expectedCerts.push(data.client_2);
-  assertCertsSelected(requestAll, expectedCerts);
+  assertCertsSelected({interactive: false, request: requestAll}, expectedCerts);
 }
 
 function testSelectCA1Certs() {
-  var requestCA1 = {
-    certificateTypes: [],
-    certificateAuthorities: [data.client_1_issuer_dn.buffer]
-  };
-  assertCertsSelected(requestCA1, [data.client_1]);
+  assertCertsSelected({interactive: false, request: requestCA1()},
+                      [data.client_1]);
+}
+
+function testSelectAllReturnsNoCerts() {
+  assertCertsSelected({interactive: false, request: requestAll},
+                      [] /* no certs selected */);
+}
+
+function testSelectAllReturnsClient1() {
+  assertCertsSelected({interactive: false, request: requestAll},
+                      [data.client_1]);
+}
+
+function testInteractiveSelectNoCerts() {
+  assertCertsSelected({interactive: true, request: requestAll},
+                      [] /* no certs selected */);
+}
+
+function testInteractiveSelectClient1() {
+  assertCertsSelected({interactive: true, request: requestAll},
+                      [data.client_1]);
 }
 
 function testMatchResult() {
-  var requestCA1 = {
-    certificateTypes: [],
-    certificateAuthorities: [data.client_1_issuer_dn.buffer]
-  };
   chrome.platformKeys.selectClientCertificates(
-      {interactive: false, request: requestCA1},
+      {interactive: false, request: requestCA1()},
       callbackPass(function(matches) {
         var expectedAlgorithm = {
           modulusLength: 2048,
@@ -282,7 +305,7 @@
       }));
 }
 
-function testSignSha1() {
+function testSignSha1Client1() {
   var keyParams = {
     // Algorithm names are case-insensitive.
     hash: {name: 'Sha-1'}
@@ -305,18 +328,86 @@
       }));
 }
 
-function runTests() {
-  var tests = [
-    testStaticMethods,
-    testSelectAllCerts,
-    testSelectCA1Certs,
-    testMatchResult,
-    testGetKeyPair,
-    testSignNoHash,
-    testSignSha1
-  ];
-
-  chrome.test.runTests(tests);
+// TODO(pneubeck): Test this by verifying that no private key is returned, once
+// that's implemented.
+function testSignFails(cert) {
+  var keyParams = {
+    hash: {name: 'SHA-1'}
+  };
+  var signParams = {
+    name: 'RSASSA-PKCS1-v1_5'
+  };
+  chrome.platformKeys.getKeyPair(
+      cert.buffer, keyParams, callbackPass(function(publicKey, privateKey) {
+        chrome.platformKeys.subtleCrypto()
+            .sign(signParams, privateKey, data.raw_data)
+            .then(function(signature) { fail('sign was expected to fail.'); },
+                  callbackPass(function(error) {
+                    assertTrue(error instanceof Error);
+                    assertEq(
+                        'The operation failed for an operation-specific reason',
+                        error.message);
+                  }));
+      }));
 }
 
-setUp(runTests);
+function testSignClient1Fails() {
+  testSignFails(data.client_1);
+}
+
+function testSignClient2Fails() {
+  testSignFails(data.client_2);
+}
+
+var testSuites = {
+  // These tests assume already granted permissions for client_1 and client_2.
+  // On interactive selectClientCertificates calls, the simulated user does not
+  // select any cert.
+  basicTests: function() {
+    var tests = [
+      testStaticMethods,
+      testSelectAllCerts,
+      testSelectCA1Certs,
+      testInteractiveSelectNoCerts,
+      testMatchResult,
+      testGetKeyPair,
+      testSignNoHash,
+      testSignSha1Client1,
+    ];
+
+    chrome.test.runTests(tests);
+  },
+
+  // This test suite starts without any granted permissions.
+  // On interactive selectClientCertificates calls, the simulated user selects
+  // client_1, if matching.
+  permissionTests: function() {
+    var tests = [
+      // Without permissions both sign attempts fail.
+      testSignClient1Fails,
+      testSignClient2Fails,
+
+      // Without permissions, non-interactive select calls return no certs.
+      testSelectAllReturnsNoCerts,
+
+      testInteractiveSelectClient1,
+      // Now that the permission for client_1 is granted.
+
+      // Verify that signing with client_1 is possible and with client_2 still
+      // fails.
+      testSignSha1Client1,
+      testSignClient2Fails,
+
+      // Verify that client_1 can still be selected interactively.
+      testInteractiveSelectClient1,
+
+      // Verify that client_1 but not client_2 is selected in non-interactive
+      // calls.
+      testSelectAllReturnsClient1,
+    ];
+
+    chrome.test.runTests(tests);
+  }
+};
+
+setUp(testSuites[selectedTestSuite]);
diff --git a/chrome/test/data/extensions/api_test/preference/standard/test.js b/chrome/test/data/extensions/api_test/preference/standard/test.js
index 972c2b98..905bdbf 100644
--- a/chrome/test/data/extensions/api_test/preference/standard/test.js
+++ b/chrome/test/data/extensions/api_test/preference/standard/test.js
@@ -9,7 +9,8 @@
   {
     root: chrome.privacy.network,
     preferences: [
-      'networkPredictionEnabled'
+      'networkPredictionEnabled',
+      'webRTCMultipleRoutesEnabled'
     ]
   },
   {
@@ -37,6 +38,13 @@
   },
 ];
 
+// Some preferences are only present on certain platforms or are hidden
+// behind flags and might not be present when this test runs.
+var possibly_missing_preferences = new Set([
+  'protectedContentEnabled',    // Windows/ChromeOS only
+  'webRTCMultipleRoutesEnabled' // requires ENABLE_WEBRTC=1
+]);
+
 function expect(expected, message) {
   return chrome.test.callbackPass(function(value) {
     chrome.test.assertEq(expected, value, message);
@@ -51,18 +59,14 @@
 }
 
 function prefGetter(pref) {
-  if (pref === 'protectedContentEnabled' && !this[pref]) {
-    // `protectedContentEnabled` is Windows/ChromeOS only, so it might not exist
-    // when this test runs, and that's pretty much OK.
+  if (possibly_missing_preferences.has(pref) && !this[pref]) {
     return true;
   }
   this[pref].get({}, expectFalse(pref));
 }
 
 function prefSetter(pref) {
-  if (pref === 'protectedContentEnabled' && !this[pref]) {
-    // `protectedContentEnabled` is Windows/ChromeOS only, so it might not exist
-    // when this test runs, and that's pretty much OK.
+  if (possibly_missing_preferences.has(pref) && !this[pref]) {
     return true;
   }
   this[pref].set({value: true}, chrome.test.callbackPass());
diff --git a/chrome/test/data/extensions/api_test/runtime/open_options_page/manifest.json b/chrome/test/data/extensions/api_test/runtime/open_options_page/manifest.json
new file mode 100644
index 0000000..ea13b2b
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/runtime/open_options_page/manifest.json
@@ -0,0 +1,12 @@
+{
+  "name": "chrome.runtime.openOptionsPage (success)",
+  "version": "0.1",
+  "manifest_version": 2,
+  "description": "Browser test for chrome.runtime.openOptionsPage, success case.",
+  "background": {
+    "scripts": ["test.js"]
+  },
+  "options_ui": {
+    "page": "options.html"
+  }
+}
diff --git a/chrome/test/data/extensions/api_test/runtime/open_options_page/options.html b/chrome/test/data/extensions/api_test/runtime/open_options_page/options.html
new file mode 100644
index 0000000..8f2356f7
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/runtime/open_options_page/options.html
@@ -0,0 +1 @@
+<script src="options.js"></script>
diff --git a/chrome/test/data/extensions/api_test/runtime/open_options_page/options.js b/chrome/test/data/extensions/api_test/runtime/open_options_page/options.js
new file mode 100644
index 0000000..9054ed56
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/runtime/open_options_page/options.js
@@ -0,0 +1,5 @@
+// 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.
+
+chrome.runtime.sendMessage('success');
diff --git a/chrome/test/data/extensions/api_test/runtime/open_options_page/test.js b/chrome/test/data/extensions/api_test/runtime/open_options_page/test.js
new file mode 100644
index 0000000..471da12
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/runtime/open_options_page/test.js
@@ -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.
+
+// browser_tests --gtest_filter=ExtensionApiTest.OpenOptionsPage
+
+var assertEq = chrome.test.assertEq;
+
+function test() {
+  chrome.test.listenOnce(chrome.runtime.onMessage, function(m, sender) {
+    assertEq('success', m);
+    assertEq(chrome.runtime.id, sender.id);
+    assertEq(chrome.runtime.getURL('options.html'), sender.url);
+  });
+  chrome.runtime.openOptionsPage();
+}
+
+chrome.test.runTests([test]);
diff --git a/chrome/test/data/extensions/api_test/runtime/open_options_page_error/manifest.json b/chrome/test/data/extensions/api_test/runtime/open_options_page_error/manifest.json
new file mode 100644
index 0000000..bb14c73
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/runtime/open_options_page_error/manifest.json
@@ -0,0 +1,9 @@
+{
+  "name": "chrome.runtime.openOptionsPage (error)",
+  "version": "0.1",
+  "manifest_version": 2,
+  "description": "Browser test for chrome.runtime.openOptionsPage, error case (no options page).",
+  "background": {
+    "scripts": ["test.js"]
+  }
+}
diff --git a/chrome/test/data/extensions/api_test/runtime/open_options_page_error/test.js b/chrome/test/data/extensions/api_test/runtime/open_options_page_error/test.js
new file mode 100644
index 0000000..66fb7900
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/runtime/open_options_page_error/test.js
@@ -0,0 +1,12 @@
+// 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.
+
+// browser_tests --gtest_filter=ExtensionApiTest.OpenOptionsPageError
+
+function test() {
+  chrome.runtime.openOptionsPage(
+      chrome.test.callbackFail('Could not create an options page.'));
+}
+
+chrome.test.runTests([test]);
diff --git a/chrome/test/data/extensions/api_test/window_open/spanning/background.js b/chrome/test/data/extensions/api_test/window_open/spanning/background.js
index 582b5b6..f851f63 100644
--- a/chrome/test/data/extensions/api_test/window_open/spanning/background.js
+++ b/chrome/test/data/extensions/api_test/window_open/spanning/background.js
@@ -13,8 +13,14 @@
     chrome.test.assertEq(1, windows[0].tabs.length);
     chrome.test.assertFalse(windows[0].tabs[0].incognito);
 
-    // The rest of the test continues in infobar.html.
-    chrome.infobars.show({tabId: windows[0].tabs[0].id, path: "infobar.html"});
+    chrome.windows.update(windows[0].id, {
+      focused: true
+    }, function() {
+      chrome.browserAction.openPopup(function(popupWindow) {
+        chrome.test.assertTrue(!!popupWindow);
+        // The rest of the test continues in popup.html.
+      });
+    });
   });
 }
 
@@ -23,14 +29,19 @@
   chrome.windows.getCurrent(function(normalWin) {
     chrome.test.assertFalse(normalWin.incognito);
     // Create an incognito window.
-    chrome.windows.create({ incognito: true }, function(incognitoWin) {
+    chrome.windows.create({
+      incognito: true,
+      focused: true
+    }, function(incognitoWin) {
       // Remove the normal window. We keep running because of the incognito
       // window.
       chrome.windows.remove(normalWin.id, function() {
         chrome.tabs.getAllInWindow(incognitoWin.id, function(tabs) {
           chrome.test.assertEq(1, tabs.length);
-          // The rest of the test continues in infobar.html.
-          chrome.infobars.show({tabId: tabs[0].id, path: "infobar.html"});
+          chrome.browserAction.openPopup(function(popupWindow) {
+            chrome.test.assertTrue(!!popupWindow);
+            // The rest of the test continues in popup.html.
+          });
         });
       });
     });
diff --git a/chrome/test/data/extensions/api_test/window_open/spanning/infobar.html b/chrome/test/data/extensions/api_test/window_open/spanning/infobar.html
deleted file mode 100644
index 48e30ac..0000000
--- a/chrome/test/data/extensions/api_test/window_open/spanning/infobar.html
+++ /dev/null
@@ -1,6 +0,0 @@
-<!--
- * Copyright (c) 2011 The Chromium Authors. All rights reserved.  Use of this
- * source code is governed by a BSD-style license that can be found in the
- * LICENSE file.
--->
-<script src="infobar.js"></script>
diff --git a/chrome/test/data/extensions/api_test/window_open/spanning/infobar.js b/chrome/test/data/extensions/api_test/window_open/spanning/infobar.js
deleted file mode 100644
index ecbc558..0000000
--- a/chrome/test/data/extensions/api_test/window_open/spanning/infobar.js
+++ /dev/null
@@ -1,9 +0,0 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-chrome.tabs.onCreated.addListener(function() {
-  chrome.tabs.onCreated.removeListener(arguments.callee);
-  chrome.extension.getBackgroundPage().verifyCreatedTab.apply(null, arguments);
-});
-window.open('foo.html');
diff --git a/chrome/test/data/extensions/api_test/window_open/spanning/manifest.json b/chrome/test/data/extensions/api_test/window_open/spanning/manifest.json
index fe49d28..585fe3d8 100644
--- a/chrome/test/data/extensions/api_test/window_open/spanning/manifest.json
+++ b/chrome/test/data/extensions/api_test/window_open/spanning/manifest.json
@@ -2,7 +2,10 @@
   "name": "window.open test - spanning",
   "version": "1",
   "manifest_version": 2,
-  "permissions": ["infobars", "tabs"],
+  "permissions": ["tabs"],
+  "browser_action": {
+    "default_popup": "popup.html"
+  },
   "background": {
     "scripts": ["background.js"]
   }
diff --git a/chrome/test/data/extensions/api_test/window_open/spanning/popup.html b/chrome/test/data/extensions/api_test/window_open/spanning/popup.html
new file mode 100644
index 0000000..4cd6831b
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/window_open/spanning/popup.html
@@ -0,0 +1,6 @@
+<!--
+ * Copyright (c) 2011 The Chromium Authors. All rights reserved.  Use of this
+ * source code is governed by a BSD-style license that can be found in the
+ * LICENSE file.
+-->
+<script src="popup.js"></script>
diff --git a/chrome/test/data/extensions/api_test/window_open/spanning/popup.js b/chrome/test/data/extensions/api_test/window_open/spanning/popup.js
new file mode 100644
index 0000000..cf5b2ca
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/window_open/spanning/popup.js
@@ -0,0 +1,10 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+chrome.tabs.onCreated.addListener(function onCreated(tab) {
+  chrome.tabs.onCreated.removeListener(onCreated);
+  chrome.extension.getBackgroundPage().verifyCreatedTab(tab);
+});
+window.open('foo.html');
+window.close();
diff --git a/chrome/test/data/extensions/platform_apps/extension_view/connect_api/main.html b/chrome/test/data/extensions/platform_apps/extension_view/connect_api/main.html
new file mode 100644
index 0000000..dbded87
--- /dev/null
+++ b/chrome/test/data/extensions/platform_apps/extension_view/connect_api/main.html
@@ -0,0 +1,12 @@
+<!doctype html>
+<!--
+ * 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.
+-->
+<html>
+<body>
+  <extensionview></extensionview>
+  <script src="main.js"></script>
+</body>
+</html>
\ No newline at end of file
diff --git a/chrome/test/data/extensions/platform_apps/extension_view/connect_api/main.js b/chrome/test/data/extensions/platform_apps/extension_view/connect_api/main.js
new file mode 100644
index 0000000..709f495
--- /dev/null
+++ b/chrome/test/data/extensions/platform_apps/extension_view/connect_api/main.js
@@ -0,0 +1,49 @@
+// 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.
+
+var checkExtension = function(element, expectedValue) {
+  chrome.test.assertEq(expectedValue, element.extension);
+};
+
+var checkSrc = function(element, expectedValue) {
+  chrome.test.assertEq(expectedValue, element.src);
+};
+
+onload = function() {
+  chrome.test.runTests([
+    function extensionView() {
+      var firstExtensionId = 'firstExtensionId';
+      var secondExtensionId = 'secondExtensionId';
+      var firstSrc = 'data:text/html,<body>One</body>';
+      var secondSrc = 'data:text/html,<body>One</body>';
+
+      var extensionview = document.querySelector('extensionview');
+      // Call connect with an initial extension Id and src.
+      extensionview.connect(firstExtensionId, firstSrc);
+      checkExtension(extensionview, firstExtensionId);
+      checkSrc(extensionview, firstSrc);
+
+      // Call connect with the same extension Id and src.
+      extensionview.connect(firstExtensionId, firstSrc);
+      checkExtension(extensionview, firstExtensionId);
+      checkSrc(extensionview, firstSrc);
+
+      // Call connect with the same extension Id and different src.
+      extensionview.connect(firstExtensionId, secondSrc);
+      checkExtension(extensionview, firstExtensionId);
+      checkSrc(extensionview, secondSrc);
+
+      // Call connect with a new extension Id and src.
+      extensionview.connect(secondExtensionId, firstSrc);
+      checkExtension(extensionview, secondExtensionId);
+      checkSrc(extensionview, firstSrc);
+
+      // Call setAttribute with a different src.
+      extensionview.setAttribute('src', secondSrc);
+      checkExtension(extensionview, secondExtensionId);
+      checkSrc(extensionview, secondSrc);
+      chrome.test.succeed();
+    }
+  ]);
+};
\ No newline at end of file
diff --git a/chrome/test/data/extensions/platform_apps/extension_view/connect_api/manifest.json b/chrome/test/data/extensions/platform_apps/extension_view/connect_api/manifest.json
new file mode 100644
index 0000000..caff1c0
--- /dev/null
+++ b/chrome/test/data/extensions/platform_apps/extension_view/connect_api/manifest.json
@@ -0,0 +1,12 @@
+{
+  "name": "Platform App Test: <extensionview> connect api call",
+  "version": "1",
+  "permissions": [
+    "extensionview"
+  ],
+  "app": {
+    "background": {
+      "scripts": ["test.js"]
+    }
+  }
+}
\ No newline at end of file
diff --git a/chrome/test/data/extensions/platform_apps/extension_view/connect_api/test.js b/chrome/test/data/extensions/platform_apps/extension_view/connect_api/test.js
new file mode 100644
index 0000000..a7480c0
--- /dev/null
+++ b/chrome/test/data/extensions/platform_apps/extension_view/connect_api/test.js
@@ -0,0 +1,7 @@
+// 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.
+
+chrome.app.runtime.onLaunched.addListener(function() {
+  chrome.app.window.create('main.html', {}, function () {});
+});
\ No newline at end of file
diff --git a/chrome/test/data/extensions/platform_apps/extension_view/extension_attribute/main.html b/chrome/test/data/extensions/platform_apps/extension_view/extension_attribute/main.html
new file mode 100644
index 0000000..dbded87
--- /dev/null
+++ b/chrome/test/data/extensions/platform_apps/extension_view/extension_attribute/main.html
@@ -0,0 +1,12 @@
+<!doctype html>
+<!--
+ * 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.
+-->
+<html>
+<body>
+  <extensionview></extensionview>
+  <script src="main.js"></script>
+</body>
+</html>
\ No newline at end of file
diff --git a/chrome/test/data/extensions/platform_apps/extension_view/extension_attribute/main.js b/chrome/test/data/extensions/platform_apps/extension_view/extension_attribute/main.js
new file mode 100644
index 0000000..eb99434d
--- /dev/null
+++ b/chrome/test/data/extensions/platform_apps/extension_view/extension_attribute/main.js
@@ -0,0 +1,26 @@
+// 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.
+
+var checkExtension = function(element, expectedValue) {
+  chrome.test.assertEq(expectedValue, element.extension);
+};
+
+onload = function() {
+  chrome.test.runTests([
+    function extensionView() {
+      // chrome.test.succeed();
+      var firstExtensionId = 'firstExtensionId';
+      var secondExtensionId = 'secondExtensionId';
+      var src = 'data:text/html,<body>One</body>';
+
+      var extensionview = document.querySelector('extensionview');
+      extensionview.connect(firstExtensionId, src);
+      checkExtension(extensionview, firstExtensionId);
+
+      extensionview.setAttribute('extension', secondExtensionId);
+      checkExtension(extensionview, firstExtensionId);
+      chrome.test.succeed();
+    }
+  ]);
+};
\ No newline at end of file
diff --git a/chrome/test/data/extensions/platform_apps/extension_view/extension_attribute/manifest.json b/chrome/test/data/extensions/platform_apps/extension_view/extension_attribute/manifest.json
new file mode 100644
index 0000000..b4d5aed
--- /dev/null
+++ b/chrome/test/data/extensions/platform_apps/extension_view/extension_attribute/manifest.json
@@ -0,0 +1,12 @@
+{
+  "name": "Platform App Test: <extensionview> extension",
+  "version": "1",
+  "permissions": [
+    "extensionview"
+  ],
+  "app": {
+    "background": {
+      "scripts": ["test.js"]
+    }
+  }
+}
\ No newline at end of file
diff --git a/chrome/test/data/extensions/platform_apps/extension_view/extension_attribute/test.js b/chrome/test/data/extensions/platform_apps/extension_view/extension_attribute/test.js
new file mode 100644
index 0000000..a7480c0
--- /dev/null
+++ b/chrome/test/data/extensions/platform_apps/extension_view/extension_attribute/test.js
@@ -0,0 +1,7 @@
+// 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.
+
+chrome.app.runtime.onLaunched.addListener(function() {
+  chrome.app.window.create('main.html', {}, function () {});
+});
\ No newline at end of file
diff --git a/chrome/test/data/extensions/platform_apps/extension_view/src_attribute/main.js b/chrome/test/data/extensions/platform_apps/extension_view/src_attribute/main.js
index 02e630f..91aa4a14 100644
--- a/chrome/test/data/extensions/platform_apps/extension_view/src_attribute/main.js
+++ b/chrome/test/data/extensions/platform_apps/extension_view/src_attribute/main.js
@@ -9,7 +9,6 @@
 onload = function() {
   chrome.test.runTests([
     function extensionView() {
-      chrome.test.succeed();
       var expectedSrcOne = 'data:text/html,<body>One</body>';
       var expectedSrcTwo = 'data:text/html,<body>Two</body>';
 
@@ -18,6 +17,7 @@
 
       extensionview.setAttribute('src', expectedSrcTwo);
       checkSrc(extensionview, expectedSrcTwo);
+      chrome.test.succeed();
     }
   ]);
 };
\ No newline at end of file
diff --git a/chrome/test/data/extensions/platform_apps/web_view/teardown/main.html b/chrome/test/data/extensions/platform_apps/web_view/simple/main.html
similarity index 100%
rename from chrome/test/data/extensions/platform_apps/web_view/teardown/main.html
rename to chrome/test/data/extensions/platform_apps/web_view/simple/main.html
diff --git a/chrome/test/data/extensions/platform_apps/web_view/simple/main.js b/chrome/test/data/extensions/platform_apps/web_view/simple/main.js
new file mode 100644
index 0000000..7e064839
--- /dev/null
+++ b/chrome/test/data/extensions/platform_apps/web_view/simple/main.js
@@ -0,0 +1,40 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+function CreateWebViewAndGuest(callback) {
+  var webview = document.createElement('webview');
+  var onLoadStop = function(e) {
+    chrome.test.sendMessage('WebViewTest.LAUNCHED');
+    webview.removeEventListener('loadstop', onLoadStop);
+    webview.removeEventListener('loadabort', onLoadAbort);
+    callback();
+  };
+  webview.addEventListener('loadstop', onLoadStop);
+
+  var onLoadAbort = function(e) {
+    chrome.test.sendMessage('WebViewTest.FAILURE');
+    webview.removeEventListener('loadstop', onLoadStop);
+    webview.removeEventListener('loadabort', onLoadAbort);
+  };
+  webview.src = 'data:text/html,<html><body>simple test</body></html>';
+  return webview;
+}
+
+onload = function() {
+  var webview = CreateWebViewAndGuest(function() {
+    webview.addEventListener('newwindow', function(e) {
+      var newwebview = document.createElement('webview');
+      newwebview.addEventListener('loadstop', function(e) {
+        chrome.test.sendMessage('WebViewTest.NEWWINDOW');
+      });
+      e.window.attach(newwebview);
+      document.body.appendChild(newwebview);
+    });
+
+    webview.addEventListener('loadstop', function(e) {
+      chrome.test.sendMessage('WebViewTest.LOADSTOP');
+    });
+  });
+  document.body.appendChild(webview);
+};
diff --git a/chrome/test/data/extensions/platform_apps/web_view/simple/manifest.json b/chrome/test/data/extensions/platform_apps/web_view/simple/manifest.json
new file mode 100644
index 0000000..2844633
--- /dev/null
+++ b/chrome/test/data/extensions/platform_apps/web_view/simple/manifest.json
@@ -0,0 +1,12 @@
+{
+  "name": "<webview> simple test.",
+  "version": "1",
+  "permissions": [
+    "webview"
+  ],
+  "app": {
+    "background": {
+      "scripts": ["test.js"]
+    }
+  }
+}
diff --git a/chrome/test/data/extensions/platform_apps/web_view/teardown/test.js b/chrome/test/data/extensions/platform_apps/web_view/simple/test.js
similarity index 100%
rename from chrome/test/data/extensions/platform_apps/web_view/teardown/test.js
rename to chrome/test/data/extensions/platform_apps/web_view/simple/test.js
diff --git a/chrome/test/data/extensions/platform_apps/web_view/teardown/main.js b/chrome/test/data/extensions/platform_apps/web_view/teardown/main.js
deleted file mode 100644
index 6e8ff3a..0000000
--- a/chrome/test/data/extensions/platform_apps/web_view/teardown/main.js
+++ /dev/null
@@ -1,13 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-onload = function() {
-  var webview = document.createElement('webview');
-  webview.addEventListener('loadstop', function(e) {
-    chrome.test.sendMessage('guest-loaded');
-  });
-  webview.setAttribute(
-      'src', 'data:text/html,<html><body>tear down test</body></html>');
-  document.body.appendChild(webview);
-};
diff --git a/chrome/test/data/extensions/platform_apps/web_view/teardown/manifest.json b/chrome/test/data/extensions/platform_apps/web_view/teardown/manifest.json
deleted file mode 100644
index 058c9d5..0000000
--- a/chrome/test/data/extensions/platform_apps/web_view/teardown/manifest.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
-  "name": "<webview> teardown test.",
-  "version": "1",
-  "permissions": [
-    "webview"
-  ],
-  "app": {
-    "background": {
-      "scripts": ["test.js"]
-    }
-  }
-}
diff --git a/chrome/test/data/policy/policy_test_cases.json b/chrome/test/data/policy/policy_test_cases.json
index 1e607bb..9c542f9 100644
--- a/chrome/test/data/policy/policy_test_cases.json
+++ b/chrome/test/data/policy/policy_test_cases.json
@@ -402,9 +402,9 @@
     ]
   },
 
-  "EnableWebBasedSignin": {
+  "EnableDeprecatedWebBasedSignin": {
     "os": ["win", "linux", "mac"],
-    "test_policy": { "EnableWebBasedSignin": false }
+    "test_policy": { "EnableDeprecatedWebBasedSignin": false }
   },
 
   "UserDataDir": {
diff --git a/chrome/test/data/push_messaging/push_test.js b/chrome/test/data/push_messaging/push_test.js
index f470030..be99f62 100644
--- a/chrome/test/data/push_messaging/push_test.js
+++ b/chrome/test/data/push_messaging/push_test.js
@@ -137,6 +137,15 @@
   });
 }
 
+function hasRegistration() {
+  navigator.serviceWorker.ready.then(function(swRegistration) {
+    return swRegistration.pushManager.getSubscription();
+  }).then(function(subscription) {
+    sendResultToTest(subscription ? 'true - registered'
+                                  : 'false - not registered');
+  }).catch(sendErrorToTest);
+}
+
 addEventListener('message', function(event) {
   var message = JSON.parse(event.data);
   if (message.type == 'push')
diff --git a/chrome/test/data/websocket/check-hsts.html b/chrome/test/data/websocket/check-hsts.html
new file mode 100644
index 0000000..46c1967
--- /dev/null
+++ b/chrome/test/data/websocket/check-hsts.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<title>Testing HSTS with WebSockets</title>
+<script>
+var ws_url = window.location.hash.substring(1);
+var ws = new WebSocket(ws_url);
+
+ws.onopen = function()
+{
+  // The fact that the WebSocket connect opened successfully means that SSL was
+  // used.
+  document.title = 'PASS';
+}
+
+ws.onclose = function(evt)
+{
+  if (!evt.wasClean)
+    document.title = 'FAIL';
+}
+</script>
diff --git a/chrome/test/data/websocket/set-hsts.html b/chrome/test/data/websocket/set-hsts.html
new file mode 100644
index 0000000..4f3f4e0
--- /dev/null
+++ b/chrome/test/data/websocket/set-hsts.html
@@ -0,0 +1 @@
+<title>SET</title>
diff --git a/chrome/test/data/websocket/set-hsts.html.mock-http-headers b/chrome/test/data/websocket/set-hsts.html.mock-http-headers
new file mode 100644
index 0000000..912b8b4
--- /dev/null
+++ b/chrome/test/data/websocket/set-hsts.html.mock-http-headers
@@ -0,0 +1,4 @@
+HTTP/1.1 200 OK
+Cache-Control: private
+Content-Type: text/html; charset=UTF-8
+Strict-Transport-Security: max-age=3600
diff --git a/chrome/test/data/webui/grid_test.html b/chrome/test/data/webui/grid_test.html
index 125aed9a..889af63 100644
--- a/chrome/test/data/webui/grid_test.html
+++ b/chrome/test/data/webui/grid_test.html
@@ -26,6 +26,7 @@
   assertEquals(0, columns);
 
   g.dataModel_ = new cr.ui.ArrayDataModel([0, 1, 2]);
+  g.horizontalPadding_ = 0;
   g.clientWidthWithoutScrollbar_ = 8;
   columns = g.getColumnCount_();
   // Client width is smaller than item width.
@@ -66,6 +67,18 @@
   columns = g.getColumnCount_();
   // Can not fit two columns due to margin on left and right side.
   assertEquals(1, columns);
+
+  g.measured_.marginRight = 0;
+  g.horizontalPadding_ = 2;
+  g.clientWidthWithoutScrollbar_ = 22;
+  columns = g.getColumnCount_();
+  // Can fit two columns as (22-2=)20px width is avaiable for grid items.
+  assertEquals(2, columns);
+
+  g.horizontalPadding_ = 3;
+  columns = g.getColumnCount_();
+  // Can not fit two columns due to bigger horizontal padding.
+  assertEquals(1, columns);
 }
 
 </script>
diff --git a/chrome/test/data/webui/print_preview.js b/chrome/test/data/webui/print_preview.js
index 55425bb..76d3aa87 100644
--- a/chrome/test/data/webui/print_preview.js
+++ b/chrome/test/data/webui/print_preview.js
@@ -113,7 +113,7 @@
    */
   setLocalDestinations: function() {
     var localDestsSetEvent =
-      new Event(print_preview.NativeLayer.EventType.LOCAL_DESTINATIONS_SET);
+        new Event(print_preview.NativeLayer.EventType.LOCAL_DESTINATIONS_SET);
     localDestsSetEvent.destinationInfos = this.localDestinationInfos_;
     this.nativeLayer_.dispatchEvent(localDestsSetEvent);
   },
@@ -125,7 +125,7 @@
    */
   setCapabilities: function(device) {
     var capsSetEvent =
-      new Event(print_preview.NativeLayer.EventType.CAPABILITIES_SET);
+        new Event(print_preview.NativeLayer.EventType.CAPABILITIES_SET);
     capsSetEvent.settingsInfo = device;
     this.nativeLayer_.dispatchEvent(capsSetEvent);
   },
@@ -338,6 +338,16 @@
   };
 }
 
+TEST_F('PrintPreviewWebUITest', 'TestPrintPreviewRestoreLocalDestination',
+    function() {
+  this.initialSettings_.serializedAppStateStr_ =
+      '{"version":2,"selectedDestinationId":"ID",' +
+      '"selectedDestinationOrigin":"local"}';
+  this.setInitialSettings();
+
+  testDone();
+});
+
 TEST_F('PrintPreviewWebUITest', 'TestSystemDialogLinkIsHiddenInAppKioskMode',
     function() {
   if (cr.isChromeOS) {
diff --git a/chrome/test/mini_installer/config/chrome_canary_installed.prop b/chrome/test/mini_installer/config/chrome_canary_installed.prop
new file mode 100644
index 0000000..f8b2855
--- /dev/null
+++ b/chrome/test/mini_installer/config/chrome_canary_installed.prop
@@ -0,0 +1,38 @@
+{
+  "Condition": "$SUPPORTS_SXS",
+  "Files": {
+    "$LOCAL_APPDATA\\$CHROME_DIR_SXS\\Application\\chrome.exe": {"exists": true},
+    "$LOCAL_APPDATA\\$CHROME_DIR_SXS\\Application\\$MINI_INSTALLER_FILE_VERSION\\chrome.dll":
+        {"exists": true},
+    "$LOCAL_APPDATA\\$CHROME_DIR_SXS\\Application\\$MINI_INSTALLER_FILE_VERSION\\chrome_elf.dll":
+        {"exists": true},
+    "$LOCAL_APPDATA\\$CHROME_DIR_SXS\\Application\\$MINI_INSTALLER_FILE_VERSION\\Installer\\chrome.7z":
+        {"exists": true},
+    "$LOCAL_APPDATA\\$CHROME_DIR_SXS\\Application\\$MINI_INSTALLER_FILE_VERSION\\Installer\\setup.exe":
+        {"exists": true},
+    "$LOCAL_APPDATA\\$CHROME_DIR_SXS\\Application\\$MINI_INSTALLER_FILE_VERSION\\$MINI_INSTALLER_FILE_VERSION.manifest":
+        {"exists": true}
+  },
+  "RegistryEntries": {
+    "HKEY_CURRENT_USER\\$CHROME_UPDATE_REGISTRY_SUBKEY_SXS": {
+      "exists": "required",
+      "values": {
+        "pv": {"type": "SZ", "data": "$MINI_INSTALLER_FILE_VERSION"}
+      }
+    },
+    "HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\$CHROME_LONG_NAME_SXS": {
+      "exists": "required",
+      "values": {
+        "UninstallString": {
+          "type": "SZ",
+          "data": "\"$LOCAL_APPDATA\\$CHROME_DIR_SXS\\Application\\$MINI_INSTALLER_FILE_VERSION\\Installer\\setup.exe\" --uninstall --chrome-sxs --verbose-logging"
+        },
+        "Version": {"type": "SZ", "data": "$MINI_INSTALLER_FILE_VERSION"}
+      }
+    },
+    "HKEY_CURRENT_USER\\Software\\Classes\\$CHROME_SHORT_NAME_SXS$USER_SPECIFIC_REGISTRY_SUFFIX": {
+      "condition": "$WINDOWS_VERSION >= $VERSION_WIN8",
+      "exists": "required"
+    }
+  }
+}
diff --git a/chrome/test/mini_installer/config/chrome_canary_inuse.prop b/chrome/test/mini_installer/config/chrome_canary_inuse.prop
new file mode 100644
index 0000000..cdbffc0
--- /dev/null
+++ b/chrome/test/mini_installer/config/chrome_canary_inuse.prop
@@ -0,0 +1,6 @@
+{
+  "Condition": "$SUPPORTS_SXS",
+  "Processes": {
+    "$LOCAL_APPDATA\\$CHROME_DIR_SXS\\Application\\chrome.exe": {"running": true}
+  }
+}
diff --git a/chrome/test/mini_installer/config/chrome_canary_no_pv.prop b/chrome/test/mini_installer/config/chrome_canary_no_pv.prop
new file mode 100644
index 0000000..6945853
--- /dev/null
+++ b/chrome/test/mini_installer/config/chrome_canary_no_pv.prop
@@ -0,0 +1,11 @@
+{
+  "Condition": "$SUPPORTS_SXS",
+  "RegistryEntries": {
+    "HKEY_CURRENT_USER\\$CHROME_UPDATE_REGISTRY_SUBKEY_SXS": {
+      "exists": "optional",
+      "values": {
+        "pv": {}
+      }
+    }
+  }
+}
diff --git a/chrome/test/mini_installer/config/chrome_canary_not_installed.prop b/chrome/test/mini_installer/config/chrome_canary_not_installed.prop
new file mode 100644
index 0000000..66b24ba9
--- /dev/null
+++ b/chrome/test/mini_installer/config/chrome_canary_not_installed.prop
@@ -0,0 +1,19 @@
+{
+  "Condition": "$SUPPORTS_SXS",
+  "Files": {
+    "$LOCAL_APPDATA\\$CHROME_DIR_SXS\\Application": {
+      "exists": false
+    }
+  },
+  "RegistryEntries": {
+    "HKEY_CURRENT_USER\\$CHROME_UPDATE_REGISTRY_SUBKEY_SXS": {
+      "exists": "forbidden"
+    },
+    "HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\$CHROME_LONG_NAME_SXS": {
+      "exists": "forbidden"
+    },
+    "HKEY_CURRENT_USER\\Software\\Classes\\$CHROME_SHORT_NAME_SXS$USER_SPECIFIC_REGISTRY_SUFFIX": {
+      "exists": "forbidden"
+    }
+  }
+}
diff --git a/chrome/test/mini_installer/config/chrome_canary_not_inuse.prop b/chrome/test/mini_installer/config/chrome_canary_not_inuse.prop
new file mode 100644
index 0000000..65b146a
--- /dev/null
+++ b/chrome/test/mini_installer/config/chrome_canary_not_inuse.prop
@@ -0,0 +1,8 @@
+{
+  "Condition": "$SUPPORTS_SXS",
+  "Processes": {
+    "$LOCAL_APPDATA\\$CHROME_DIR_SXS\\Application\\chrome.exe": {
+      "running": false
+    }
+  }
+}
diff --git a/chrome/test/mini_installer/config/chrome_system_installed.prop b/chrome/test/mini_installer/config/chrome_system_installed.prop
index 1a747f1..2a336d9 100644
--- a/chrome/test/mini_installer/config/chrome_system_installed.prop
+++ b/chrome/test/mini_installer/config/chrome_system_installed.prop
@@ -19,6 +19,12 @@
         "pv": {"type": "SZ", "data": "$MINI_INSTALLER_FILE_VERSION"}
       }
     },
+    "HKEY_LOCAL_MACHINE\\$BINARIES_UPDATE_REGISTRY_SUBKEY": {
+      "exists": "required",
+      "values": {
+        "pv": {"type": "SZ", "data": "$MINI_INSTALLER_FILE_VERSION"}
+      }
+    },
     "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\$CHROME_LONG_NAME": {
       "exists": "required",
       "values": {
diff --git a/chrome/test/mini_installer/config/chrome_system_not_installed.prop b/chrome/test/mini_installer/config/chrome_system_not_installed.prop
index eaebe44..e10910b 100644
--- a/chrome/test/mini_installer/config/chrome_system_not_installed.prop
+++ b/chrome/test/mini_installer/config/chrome_system_not_installed.prop
@@ -5,6 +5,8 @@
   "RegistryEntries": {
     "HKEY_LOCAL_MACHINE\\$CHROME_UPDATE_REGISTRY_SUBKEY": {
       "exists": "forbidden"},
+    "HKEY_LOCAL_MACHINE\\$BINARIES_UPDATE_REGISTRY_SUBKEY": {
+      "exists": "forbidden"},
     "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\$CHROME_LONG_NAME":
         {"exists": "forbidden"},
     "HKEY_LOCAL_MACHINE\\Software\\Classes\\$CHROME_SHORT_NAME": {
diff --git a/chrome/test/mini_installer/config/chrome_user_installed.prop b/chrome/test/mini_installer/config/chrome_user_installed.prop
index 3fdac9f..ed9f19e 100644
--- a/chrome/test/mini_installer/config/chrome_user_installed.prop
+++ b/chrome/test/mini_installer/config/chrome_user_installed.prop
@@ -19,6 +19,12 @@
         "pv": {"type": "SZ", "data": "$MINI_INSTALLER_FILE_VERSION"}
       }
     },
+    "HKEY_CURRENT_USER\\$BINARIES_UPDATE_REGISTRY_SUBKEY": {
+      "exists": "required",
+      "values": {
+        "pv": {"type": "SZ", "data": "$MINI_INSTALLER_FILE_VERSION"}
+      }
+    },
     "HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\$CHROME_LONG_NAME": {
       "exists": "required",
       "values": {
diff --git a/chrome/test/mini_installer/config/chrome_user_not_installed.prop b/chrome/test/mini_installer/config/chrome_user_not_installed.prop
index d7d33b1..7898f647 100644
--- a/chrome/test/mini_installer/config/chrome_user_not_installed.prop
+++ b/chrome/test/mini_installer/config/chrome_user_not_installed.prop
@@ -5,6 +5,8 @@
   "RegistryEntries": {
     "HKEY_CURRENT_USER\\$CHROME_UPDATE_REGISTRY_SUBKEY\\Commands":
         {"exists": "forbidden"},
+    "HKEY_CURRENT_USER\\$BINARIES_UPDATE_REGISTRY_SUBKEY":
+        {"exists": "forbidden"},
     "HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\$CHROME_LONG_NAME":
         {"exists": "forbidden"},
     "HKEY_CURRENT_USER\\Software\\Classes\\$CHROME_SHORT_NAME$USER_SPECIFIC_REGISTRY_SUFFIX": {
diff --git a/chrome/test/mini_installer/config/config.config b/chrome/test/mini_installer/config/config.config
index 2a8a24db..a663e9f 100644
--- a/chrome/test/mini_installer/config/config.config
+++ b/chrome/test/mini_installer/config/config.config
@@ -1,43 +1,80 @@
 {
   "states": [
     ["no_pv", ["chrome_user_no_pv.prop",
+               "chrome_canary_no_pv.prop",
                "chrome_system_no_pv.prop"]],
     ["clean", ["chrome_user_not_installed.prop",
+               "chrome_canary_not_installed.prop",
                "chrome_system_not_installed.prop",
                "chrome_user_not_inuse.prop",
+               "chrome_canary_not_inuse.prop",
                "chrome_system_not_inuse.prop"]],
     ["chrome_user_installed_not_inuse", ["chrome_user_installed.prop",
+                                         "chrome_canary_not_installed.prop",
                                          "chrome_system_not_installed.prop",
                                          "chrome_user_not_inuse.prop",
+                                         "chrome_canary_not_inuse.prop",
                                          "chrome_system_not_inuse.prop"]],
+    ["chrome_canary_installed_not_inuse", ["chrome_user_not_installed.prop",
+                                           "chrome_canary_installed.prop",
+                                           "chrome_system_not_installed.prop",
+                                           "chrome_user_not_inuse.prop",
+                                           "chrome_canary_not_inuse.prop",
+                                           "chrome_system_not_inuse.prop"]],
     ["chrome_system_installed_not_inuse", ["chrome_user_not_installed.prop",
+                                           "chrome_canary_not_installed.prop",
                                            "chrome_system_installed.prop",
                                            "chrome_user_not_inuse.prop",
+                                           "chrome_canary_not_inuse.prop",
                                            "chrome_system_not_inuse.prop"]],
+    ["chrome_user_and_canary_installed_not_inuse", ["chrome_user_installed.prop",
+                                         "chrome_canary_installed.prop",
+                                         "chrome_system_not_installed.prop",
+                                         "chrome_user_not_inuse.prop",
+                                         "chrome_canary_not_inuse.prop",
+                                         "chrome_system_not_inuse.prop"]],
     ["chrome_user_installed_inuse", ["chrome_user_installed.prop",
+                                     "chrome_canary_not_installed.prop",
                                      "chrome_system_not_installed.prop",
                                      "chrome_user_inuse.prop",
+                                     "chrome_canary_not_inuse.prop",
                                      "chrome_system_not_inuse.prop"]],
+    ["chrome_canary_installed_inuse", ["chrome_user_not_installed.prop",
+                                       "chrome_canary_installed.prop",
+                                       "chrome_system_not_installed.prop",
+                                       "chrome_user_not_inuse.prop",
+                                       "chrome_canary_inuse.prop",
+                                       "chrome_system_not_inuse.prop"]],
     ["chrome_system_installed_inuse", ["chrome_user_not_installed.prop",
+                                       "chrome_canary_not_installed.prop",
                                        "chrome_system_installed.prop",
                                        "chrome_user_not_inuse.prop",
+                                       "chrome_canary_not_inuse.prop",
                                        "chrome_system_inuse.prop"]]
   ],
   "actions": [
     ["install_chrome_user",
      "\"$MINI_INSTALLER\" --chrome --multi-install --verbose-logging --do-not-launch-chrome"],
+    ["install_chrome_canary",
+     "\"$MINI_INSTALLER\" --chrome-sxs --verbose-logging --do-not-launch-chrome"],
     ["install_chrome_system",
      "\"$MINI_INSTALLER\" --chrome --multi-install --verbose-logging --system-level --do-not-launch-chrome"],
     ["launch_chrome_user",
      "python launch_chrome.py \"$LOCAL_APPDATA\\$CHROME_DIR\\Application\\chrome.exe\""],
+    ["launch_chrome_canary",
+     "python launch_chrome.py \"$LOCAL_APPDATA\\$CHROME_DIR_SXS\\Application\\chrome.exe\""],
     ["launch_chrome_system",
      "python launch_chrome.py \"$PROGRAM_FILES\\$CHROME_DIR\\Application\\chrome.exe\""],
     ["quit_chrome_user",
      "python quit_chrome.py \"$LOCAL_APPDATA\\$CHROME_DIR\\Application\\chrome.exe\""],
+    ["quit_chrome_canary",
+     "python quit_chrome.py \"$LOCAL_APPDATA\\$CHROME_DIR_SXS\\Application\\chrome.exe\""],
     ["quit_chrome_system",
      "python quit_chrome.py \"$PROGRAM_FILES\\$CHROME_DIR\\Application\\chrome.exe\""],
     ["uninstall_chrome_user",
      "python uninstall_chrome.py --chrome-long-name=\"$CHROME_LONG_NAME\""],
+    ["uninstall_chrome_canary",
+     "python uninstall_chrome.py --chrome-long-name=\"$CHROME_LONG_NAME_SXS\""],
     ["uninstall_chrome_system",
      "python uninstall_chrome.py --chrome-long-name=\"$CHROME_LONG_NAME\" --system-level"]
   ],
@@ -51,12 +88,32 @@
       ]
     },
     {
+      "name": "ChromeCanary",
+      "condition": "$SUPPORTS_SXS",
+      "traversal": [
+        "no_pv",
+        "install_chrome_canary", "chrome_canary_installed_not_inuse",
+        "uninstall_chrome_canary", "clean"
+      ]
+    },
+    {
       "name": "ChromeSystemLevel",
       "traversal": [
         "no_pv",
         "install_chrome_system", "chrome_system_installed_not_inuse",
         "uninstall_chrome_system", "clean"
       ]
+    },
+    {
+      "name": "ChromeUserLevelWithCanary",
+      "condition": "$SUPPORTS_SXS",
+      "traversal": [
+        "no_pv",
+        "install_chrome_user", "chrome_user_installed_not_inuse",
+        "install_chrome_canary", "chrome_user_and_canary_installed_not_inuse",
+        "uninstall_chrome_user", "chrome_canary_installed_not_inuse",
+        "uninstall_chrome_canary", "clean"
+      ]
     }
   ]
 }
diff --git a/chrome/test/mini_installer/registry_verifier.py b/chrome/test/mini_installer/registry_verifier.py
index 20a09b9..bd790d5 100644
--- a/chrome/test/mini_installer/registry_verifier.py
+++ b/chrome/test/mini_installer/registry_verifier.py
@@ -71,13 +71,13 @@
                                    _winreg.KEY_QUERY_VALUE)
     except WindowsError:
       # Key doesn't exist. See that it matches the expectation.
-      assert expectation['exists'] is not 'required', ('Registry key %s is '
-                                                       'missing' % key)
+      assert expectation['exists'] != 'required', ('Registry key %s is '
+                                                   'missing' % key)
       # Values are not checked if the missing key's existence is optional.
       return
     # The key exists, see that it matches the expectation.
-    assert expectation['exists'] is not 'forbidden', ('Registry key %s exists' %
-                                                      key)
+    assert expectation['exists'] != 'forbidden', ('Registry key %s exists' %
+                                                  key)
 
     # Verify the expected values.
     if 'values' not in expectation:
diff --git a/chrome/test/mini_installer/test_installer.py b/chrome/test/mini_installer/test_installer.py
index a98f137..2b5c870b 100644
--- a/chrome/test/mini_installer/test_installer.py
+++ b/chrome/test/mini_installer/test_installer.py
@@ -233,13 +233,31 @@
           current_property[key].items() + value.items())
 
 
-def ParsePropertyFiles(directory, filenames):
+def FilterConditionalElem(elem, condition_name, variable_expander):
+  """Returns True if a conditional element should be processed.
+
+  Args:
+    elem: A dictionary.
+    condition_name: The name of the condition property in |elem|.
+    variable_expander: A variable expander used to evaluate conditions.
+
+  Returns:
+    True if |elem| should be processed.
+  """
+  if condition_name not in elem:
+    return True
+  condition = variable_expander.Expand(elem[condition_name])
+  return eval(condition, {'__builtins__': {'False': False, 'True': True}})
+
+
+def ParsePropertyFiles(directory, filenames, variable_expander):
   """Parses an array of .prop files.
 
   Args:
-    property_filenames: An array of Property filenames.
     directory: The directory where the Config file and all Property files
         reside in.
+    filenames: An array of Property filenames.
+    variable_expander: A variable expander used to evaluate conditions.
 
   Returns:
     A property dictionary created by merging all property dictionaries specified
@@ -249,11 +267,17 @@
   for filename in filenames:
     path = os.path.join(directory, filename)
     new_property = json.load(open(path))
+    if not FilterConditionalElem(new_property, 'Condition', variable_expander):
+      continue
+    # Remove any Condition from the propery dict before merging since it serves
+    # no purpose from here on out.
+    if 'Condition' in new_property:
+      del new_property['Condition']
     MergePropertyDictionaries(current_property, new_property)
   return current_property
 
 
-def ParseConfigFile(filename):
+def ParseConfigFile(filename, variable_expander):
   """Parses a .config file.
 
   Args:
@@ -268,9 +292,14 @@
 
   config = Config()
   config.tests = config_data['tests']
+  # Drop conditional tests that should not be run in the current configuration.
+  config.tests = filter(lambda t: FilterConditionalElem(t, 'condition',
+                                                        variable_expander),
+                        config.tests)
   for state_name, state_property_filenames in config_data['states']:
     config.states[state_name] = ParsePropertyFiles(directory,
-                                                   state_property_filenames)
+                                                   state_property_filenames,
+                                                   variable_expander)
   for action_name, action_command in config_data['actions']:
     config.actions[action_name] = action_command
   return config
@@ -306,9 +335,10 @@
 
   # Set the env var used by mini_installer.exe to decide to not show UI.
   os.environ['MINI_INSTALLER_TEST'] = '1'
-  config = ParseConfigFile(args.config)
 
   variable_expander = VariableExpander(mini_installer_path)
+  config = ParseConfigFile(args.config, variable_expander)
+
   RunCleanCommand(args.force_clean, variable_expander)
   for test in config.tests:
     # If tests were specified via |tests|, their names are formatted like so:
diff --git a/chrome/test/mini_installer/variable_expander.py b/chrome/test/mini_installer/variable_expander.py
index e965825..4bd7892d 100644
--- a/chrome/test/mini_installer/variable_expander.py
+++ b/chrome/test/mini_installer/variable_expander.py
@@ -61,76 +61,88 @@
 
     The constructor initializes a variable dictionary that maps variables to
     their values. These are the only acceptable variables:
-        * $PROGRAM_FILE: the unquoted path to the Program Files folder.
+        * $CHROME_DIR: the directory of Chrome (or Chromium) from the base
+            installation directory.
+        * $CHROME_HTML_PROG_ID: 'ChromeHTML' (or 'ChromiumHTM').
+        * $CHROME_LONG_NAME: 'Google Chrome' (or 'Chromium').
+        * $CHROME_LONG_NAME_SXS: 'Google Chrome SxS' if $SUPPORTS_SXS.
+        * $CHROME_SHORT_NAME: 'Chrome' (or 'Chromium').
+        * $CHROME_SHORT_NAME_SXS: 'ChromeCanary' if $SUPPORTS_SXS.
+        * $CHROME_UPDATE_REGISTRY_SUBKEY: the registry key, excluding the root
+            key, of Chrome for Google Update.
+        * $CHROME_UPDATE_REGISTRY_SUBKEY_SXS: the registry key, excluding the
+            root key, of Chrome SxS for Google Update.
         * $LOCAL_APPDATA: the unquoted path to the Local Application Data
             folder.
         * $MINI_INSTALLER: the unquoted path to the mini_installer.
         * $MINI_INSTALLER_FILE_VERSION: the file version of the mini_installer.
-        * $CHROME_SHORT_NAME: 'Chrome' (or 'Chromium').
-        * $CHROME_LONG_NAME: 'Google Chrome' (or 'Chromium').
-        * $CHROME_DIR: the directory of Chrome (or Chromium) from the base
-            installation directory.
-        * $CHROME_UPDATE_REGISTRY_SUBKEY: the registry key, excluding the root
-            key, of Chrome for Google Update.
-        * $CHROME_HTML_PROG_ID: 'ChromeHTML' (or 'ChromiumHTM').
+        * $PROGRAM_FILES: the unquoted path to the Program Files folder.
+        * $SUPPORTS_SXS: a boolean indicating whether or not SxS installs
+            are supported by the mini_installer under test.
         * $USER_SPECIFIC_REGISTRY_SUFFIX: the output from the function
             _GetUserSpecificRegistrySuffix().
-        * $WINDOWS_VERSION: a 2-tuple representing the current Windows version.
         * $VERSION_[XP/SERVER_2003/VISTA/WIN7/WIN8/WIN8_1/WIN10]: a 2-tuple
             representing the version of the corresponding OS.
+        * $WINDOWS_VERSION: a 2-tuple representing the current Windows version.
 
     Args:
       mini_installer_path: The path to mini_installer.exe.
     """
     mini_installer_abspath = os.path.abspath(mini_installer_path)
-    mini_installer_file_version = _GetFileVersion(mini_installer_abspath)
-    mini_installer_product_name = _GetProductName(mini_installer_abspath)
-    program_files_path = shell.SHGetFolderPath(0, shellcon.CSIDL_PROGRAM_FILES,
-                                               None, 0)
-    local_appdata_path = shell.SHGetFolderPath(0, shellcon.CSIDL_LOCAL_APPDATA,
-                                               None, 0)
-    user_specific_registry_suffix = _GetUserSpecificRegistrySuffix()
     windows_major_ver, windows_minor_ver, _, _, _ = win32api.GetVersionEx()
-    # This string will be converted to a tuple once injected in eval() through
-    # conditional checks. Tuples are compared lexicographically.
-    windows_version = '(%s, %s)' % (windows_major_ver, windows_minor_ver)
+    self._variable_mapping = {
+        'LOCAL_APPDATA': shell.SHGetFolderPath(0, shellcon.CSIDL_LOCAL_APPDATA,
+                                               None, 0),
+        'MINI_INSTALLER': mini_installer_abspath,
+        'MINI_INSTALLER_FILE_VERSION': _GetFileVersion(mini_installer_abspath),
+        'PROGRAM_FILES': shell.SHGetFolderPath(0, shellcon.CSIDL_PROGRAM_FILES,
+                                               None, 0),
+        'USER_SPECIFIC_REGISTRY_SUFFIX': _GetUserSpecificRegistrySuffix(),
+        'VERSION_SERVER_2003': '(5, 2)',
+        'VERSION_VISTA': '(6, 0)',
+        'VERSION_WIN10': '(10, 0)',
+        'VERSION_WIN7': '(6, 1)',
+        'VERSION_WIN8': '(6, 2)',
+        'VERSION_WIN8_1': '(6, 3)',
+        'VERSION_XP': '(5, 1)',
+        'WINDOWS_VERSION': '(%s, %s)' % (windows_major_ver, windows_minor_ver)
+    }
+
+    mini_installer_product_name = _GetProductName(mini_installer_abspath)
     if mini_installer_product_name == 'Google Chrome Installer':
-      chrome_short_name = 'Chrome'
-      chrome_long_name = 'Google Chrome'
-      chrome_dir = 'Google\\Chrome'
-      chrome_update_registry_subkey = ('Software\\Google\\Update\\Clients\\'
-                                       '{8A69D345-D564-463c-AFF1-A69D9E530F96}')
-      chrome_html_prog_id = 'ChromeHTML'
+      self._variable_mapping.update({
+          'BINARIES_UPDATE_REGISTRY_SUBKEY': (
+            'Software\\Google\\Update\\Clients\\'
+            '{4DC8B4CA-1BDA-483e-B5FA-D3C12E15B62D}'),
+          'CHROME_DIR': 'Google\\Chrome',
+          'CHROME_HTML_PROG_ID': 'ChromeHTML',
+          'CHROME_LONG_NAME': 'Google Chrome',
+          'CHROME_SHORT_NAME': 'Chrome',
+          'CHROME_UPDATE_REGISTRY_SUBKEY': (
+            'Software\\Google\\Update\\Clients\\'
+            '{8A69D345-D564-463c-AFF1-A69D9E530F96}'),
+          'SUPPORTS_SXS': True,
+          'CHROME_DIR_SXS': 'Google\\Chrome SxS',
+          'CHROME_LONG_NAME_SXS': 'Google Chrome SxS',
+          'CHROME_SHORT_NAME_SXS': 'ChromeCanary',
+          'CHROME_UPDATE_REGISTRY_SUBKEY_SXS': (
+            'Software\\Google\\Update\\Clients\\'
+            '{4ea16ac7-fd5a-47c3-875b-dbf4a2008c20}')
+      })
     elif mini_installer_product_name == 'Chromium Installer':
-      chrome_short_name = 'Chromium'
-      chrome_long_name = 'Chromium'
-      chrome_dir = 'Chromium'
-      chrome_update_registry_subkey = 'Software\\Chromium'
-      chrome_html_prog_id = 'ChromiumHTM'
+      self._variable_mapping.update({
+          'BINARIES_UPDATE_REGISTRY_SUBKEY': 'Software\\Chromium Binaries',
+          'CHROME_DIR': 'Chromium',
+          'CHROME_HTML_PROG_ID': 'ChromiumHTM',
+          'CHROME_LONG_NAME': 'Chromium',
+          'CHROME_SHORT_NAME': 'Chromium',
+          'CHROME_UPDATE_REGISTRY_SUBKEY': 'Software\\Chromium',
+          'SUPPORTS_SXS': False
+      })
     else:
       raise KeyError("Unknown mini_installer product name '%s'" %
                      mini_installer_product_name)
 
-    self._variable_mapping = {
-        'PROGRAM_FILES': program_files_path,
-        'LOCAL_APPDATA': local_appdata_path,
-        'MINI_INSTALLER': mini_installer_abspath,
-        'MINI_INSTALLER_FILE_VERSION': mini_installer_file_version,
-        'CHROME_SHORT_NAME': chrome_short_name,
-        'CHROME_LONG_NAME': chrome_long_name,
-        'CHROME_DIR': chrome_dir,
-        'CHROME_UPDATE_REGISTRY_SUBKEY': chrome_update_registry_subkey,
-        'CHROME_HTML_PROG_ID': chrome_html_prog_id,
-        'USER_SPECIFIC_REGISTRY_SUFFIX': user_specific_registry_suffix,
-        'WINDOWS_VERSION': windows_version,
-        'VERSION_XP': '(5, 1)',
-        'VERSION_SERVER_2003': '(5, 2)',
-        'VERSION_VISTA': '(6, 0)',
-        'VERSION_WIN7': '(6, 1)',
-        'VERSION_WIN8': '(6, 2)',
-        'VERSION_WIN8_1': '(6, 3)',
-        'VERSION_WIN10': '(10, 0)',
-    }
 
   def Expand(self, str):
     """Expands variables in the given string.
diff --git a/chrome/test/perf/BUILD.gn b/chrome/test/perf/BUILD.gn
deleted file mode 100644
index 3a313857..0000000
--- a/chrome/test/perf/BUILD.gn
+++ /dev/null
@@ -1,29 +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("//testing/test.gni")
-
-if (!is_win || link_chrome_on_windows) {
-  # This test appears to be a legacy target consisting of files not yet moved
-  # elsewhere.
-  test("perf") {
-    output_name = "perf_tests"
-
-    sources = [
-      "perftests.cc",
-      "url_parse_perftest.cc",
-    ]
-
-    deps = [
-      "//base",
-      "//base/allocator",
-      "//base/test:test_support",
-      "//testing/gtest",
-      "//url",
-    ]
-  }
-} else {
-  group("perf") {
-  }
-}
diff --git a/chrome/test/perf/perftests.cc b/chrome/test/perf/perftests.cc
deleted file mode 100644
index 709ffc82..0000000
--- a/chrome/test/perf/perftests.cc
+++ /dev/null
@@ -1,13 +0,0 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/message_loop/message_loop.h"
-#include "base/test/perf_test_suite.h"
-
-int main(int argc, char **argv) {
-  base::PerfTestSuite suite(argc, argv);
-  base::MessageLoop main_message_loop;
-
-  return suite.Run();
-}
diff --git a/chrome_elf/BUILD.gn b/chrome_elf/BUILD.gn
index b58c829..74a217f 100644
--- a/chrome_elf/BUILD.gn
+++ b/chrome_elf/BUILD.gn
@@ -32,7 +32,7 @@
     "/NODEFAULTLIB:user32.lib",
     "/DEF:" + rebase_path("chrome_elf.def"),
   ]
-  if (cpu_arch == "x86") {
+  if (current_cpu == "x86") {
     # Don"t set an x64 base address (to avoid breaking HE-ASLR).
     ldflags += [ "/BASE:0x01c20000" ]
   }
@@ -96,7 +96,7 @@
     configs += [ "//build/config/win:windowed" ]
     ldflags = [ "/DEF:" + rebase_path("chrome_redirects.def") ]
 
-    if (cpu_arch == "x86") {
+    if (current_cpu == "x86") {
       # Don't set an x64 base address (to avoid breaking HE-ASLR).
       ldflags += [ "/BASE:0x01c20000" ]
     }
diff --git a/chromecast/app/android/cast_jni_loader.cc b/chromecast/app/android/cast_jni_loader.cc
index d2cc9c7..98223f2 100644
--- a/chromecast/app/android/cast_jni_loader.cc
+++ b/chromecast/app/android/cast_jni_loader.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 #include "base/android/jni_android.h"
-#include "base/android/jni_onload_delegate.h"
+#include "base/bind.h"
 #include "chromecast/android/cast_jni_registrar.h"
 #include "chromecast/android/platform_jni_loader.h"
 #include "chromecast/app/cast_main_delegate.h"
@@ -13,32 +13,31 @@
 
 namespace {
 
-class CastJNIOnLoadDelegate : public base::android::JNIOnLoadDelegate {
- public:
-  bool RegisterJNI(JNIEnv* env) override {
-    // To be called only from the UI thread.  If loading the library is done on
-    // a separate thread, this should be moved elsewhere.
-    if (!chromecast::android::RegisterJni(env))
-      return false;
-    // Allow platform-specific implementations to perform more JNI registration.
-    if (!chromecast::android::PlatformRegisterJni(env))
-      return false;
-    return true;
-  }
+bool RegisterJNI(JNIEnv* env) {
+  // To be called only from the UI thread.  If loading the library is done on
+  // a separate thread, this should be moved elsewhere.
+  if (!chromecast::android::RegisterJni(env))
+    return false;
+  // Allow platform-specific implementations to perform more JNI registration.
+  if (!chromecast::android::PlatformRegisterJni(env))
+    return false;
+  return true;
+}
 
-  bool Init() override {
-    content::Compositor::Initialize();
-    content::SetContentMainDelegate(new chromecast::shell::CastMainDelegate);
-    return true;
-  }
-};
+bool Init() {
+  content::Compositor::Initialize();
+  content::SetContentMainDelegate(new chromecast::shell::CastMainDelegate);
+  return true;
+}
 
 }  // namespace
 
 // This is called by the VM when the shared library is first loaded.
 JNI_EXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
-  CastJNIOnLoadDelegate delegate;
-  if (!content::android::OnJNIOnLoad(vm, &delegate))
+  if (!content::android::OnJNIOnLoadRegisterJNI(
+          vm, base::Bind(&RegisterJNI)) ||
+      !content::android::OnJNIOnLoadInit(base::Bind(&Init)))
     return -1;
+
   return JNI_VERSION_1_4;
 }
diff --git a/chromecast/app/resources/shell_devtools_discovery_page.html b/chromecast/app/resources/shell_devtools_discovery_page.html
index 09bb9c4..f8d69d95 100644
--- a/chromecast/app/resources/shell_devtools_discovery_page.html
+++ b/chromecast/app/resources/shell_devtools_discovery_page.html
@@ -2,18 +2,6 @@
 <head>
 <title>Cast shell remote debugging</title>
 <style>
-  .local-ui-link {
-    background-color: #eee;
-    border: 1px solid #ccc;
-    color: #333;
-    display: block;
-    font-family: monospace;
-    font-size: 11px;
-    margin: 4px;
-    padding: 4px;
-    width: 100%;
-  }
-
   .help {
     font-size: 11px;
   }
@@ -50,15 +38,6 @@
     frontend_link.textContent = 'Remote Debugging (AppEngine)'
     frontend_link.href = metadata.devtoolsFrontendUrl;
     item_container.appendChild(frontend_link);
-
-    var devtools_protocol_link = document.createElement('textarea');
-    devtools_protocol_link.className = 'local-ui-link';
-    devtools_protocol_link.value = metadata.devtoolsFrontendUrl.replace(
-        "https://chrome-devtools-frontend.appspot.com",
-        "chrome-devtools://devtools/remote");
-    // Highlight text when clicked.
-    devtools_protocol_link.onclick = function() { this.select(); }
-    item_container.appendChild(devtools_protocol_link);
   } else {
     frontend_header.textContent += " (already has active debugging session)";
   }
@@ -69,16 +48,9 @@
 
 <h3>Help</h3>
 <div id="help">
-  Note: there are two debugging options presented for each page above. Either
-  is a valid way to initiate a remote debugging session.
-  <ul>
-    <li>For the first option (link), you may have to select the shield icon in
-        the address bar to establish a connection. See the <a
-        href="https://support.google.com/chrome/answer/1342714?hl=en">help
-        center</a> for more information.</li>
-    <li>For the second option (textarea), simply copy/paste the URL into
-        Chrome's URL bar.</li>
-  </ul>
+  You may have to select the shield icon in the address bar to establish a connection.
+  See the <a href="https://support.google.com/chrome/answer/1342714?hl=en">help
+  center</a> for more information.
 </div>
 
 </body>
diff --git a/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastCrashHandler.java b/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastCrashHandler.java
index ca25fb11..2bf92ff 100644
--- a/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastCrashHandler.java
+++ b/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastCrashHandler.java
@@ -14,19 +14,19 @@
 public final class CastCrashHandler {
 
     @CalledByNative
-    public static void removeCrashDumpsSync(String crashDumpPath, boolean isDebugBuild) {
-        new CastCrashUploader(crashDumpPath, isDebugBuild).removeCrashDumpsSync();
+    public static void removeCrashDumpsSync(String crashDumpPath, boolean uploadCrashToStaging) {
+        new CastCrashUploader(crashDumpPath, uploadCrashToStaging).removeCrashDumpsSync();
     }
 
     @CalledByNative
     public static void uploadCurrentProcessDumpSync(String crashDumpPath, String logFilePath,
-            boolean isDebugBuild) {
-        new CastCrashUploader(crashDumpPath, isDebugBuild)
+            boolean uploadCrashToStaging) {
+        new CastCrashUploader(crashDumpPath, uploadCrashToStaging)
                 .uploadCurrentProcessDumpSync(logFilePath);
     }
 
     @CalledByNative
-    public static void uploadCrashDumpsAsync(String crashDumpPath, boolean isDebugBuild) {
-        new CastCrashUploader(crashDumpPath, isDebugBuild).uploadRecentCrashesAsync();
+    public static void uploadCrashDumpsAsync(String crashDumpPath, boolean uploadCrashToStaging) {
+        new CastCrashUploader(crashDumpPath, uploadCrashToStaging).uploadRecentCrashesAsync();
     }
 }
diff --git a/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastCrashUploader.java b/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastCrashUploader.java
index d8f50fc8..39060953 100644
--- a/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastCrashUploader.java
+++ b/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastCrashUploader.java
@@ -42,11 +42,11 @@
     private final String mCrashDumpPath;
     private final String mCrashReportUploadUrl;
 
-    public CastCrashUploader(String crashDumpPath, boolean isDebugBuild) {
+    public CastCrashUploader(String crashDumpPath, boolean uploadCrashToStaging) {
         this.mCrashDumpPath = crashDumpPath;
-        mCrashReportUploadUrl = isDebugBuild ?
-                "http://clients2.google.com/cr/staging_report" :
-                "http://clients2.google.com/cr/report";
+        mCrashReportUploadUrl = uploadCrashToStaging
+                ? "http://clients2.google.com/cr/staging_report"
+                : "http://clients2.google.com/cr/report";
         mExecutorService = Executors.newFixedThreadPool(1);
     }
 
diff --git a/chromecast/browser/cast_browser_main_parts.cc b/chromecast/browser/cast_browser_main_parts.cc
index 7a6e600..8970be8 100644
--- a/chromecast/browser/cast_browser_main_parts.cc
+++ b/chromecast/browser/cast_browser_main_parts.cc
@@ -28,6 +28,7 @@
 #include "chromecast/common/cast_paths.h"
 #include "chromecast/common/chromecast_switches.h"
 #include "chromecast/common/platform_client_auth.h"
+#include "chromecast/net/connectivity_checker.h"
 #include "chromecast/net/network_change_notifier_cast.h"
 #include "chromecast/net/network_change_notifier_factory_cast.h"
 #include "content/public/browser/browser_thread.h"
@@ -95,6 +96,10 @@
 };
 
 DefaultCommandLineSwitch g_default_switches[] = {
+  // TODO(ddorwin): Develop a permanent solution. See http://crbug.com/394926.
+  // For now, disable unprefixed EME because the video behavior not be
+  // consistent with other clients.
+  { switches::kDisableEncryptedMedia, ""},
 #if defined(OS_ANDROID)
   { switches::kMediaDrmEnableNonCompositing, ""},
   { switches::kEnableOverlayFullscreenVideo, ""},
@@ -204,6 +209,11 @@
     ::media::SetBrowserCdmFactory(new media::CastBrowserCdmFactory);
 #endif  // !defined(OS_ANDROID)
 
+  cast_browser_process_->SetConnectivityChecker(
+      make_scoped_refptr(new ConnectivityChecker(
+          content::BrowserThread::GetMessageLoopProxyForThread(
+              content::BrowserThread::FILE))));
+
   url_request_context_factory_->InitializeOnUIThread();
 
   cast_browser_process_->SetBrowserContext(
@@ -225,15 +235,13 @@
       cast_browser_process_->pref_service(),
       cast_browser_process_->metrics_service_client(),
       url_request_context_factory_->GetSystemGetter()));
-
-  cast_browser_process_->metrics_service_client()
-      ->Initialize(cast_browser_process_->cast_service());
-
   cast_browser_process_->cast_service()->Initialize();
 
-  // Initializing network delegates must happen after cast service is
-  // initialized because CastNetworkDelegate may use components initialized by
-  // cast service.
+  // Initializing metrics service and network delegates must happen after cast
+  // service is intialized because CastMetricsServiceClient and
+  // CastNetworkDelegate may use components initialized by cast service.
+  cast_browser_process_->metrics_service_client()
+      ->Initialize(cast_browser_process_->cast_service());
   url_request_context_factory_->InitializeNetworkDelegates();
 }
 
diff --git a/chromecast/browser/cast_browser_process.cc b/chromecast/browser/cast_browser_process.cc
index 8712775e..ccd04ae9 100644
--- a/chromecast/browser/cast_browser_process.cc
+++ b/chromecast/browser/cast_browser_process.cc
@@ -8,9 +8,11 @@
 #include "base/prefs/pref_service.h"
 #include "chromecast/base/metrics/cast_metrics_helper.h"
 #include "chromecast/browser/cast_browser_context.h"
+#include "chromecast/browser/cast_resource_dispatcher_host_delegate.h"
 #include "chromecast/browser/devtools/remote_debugging_server.h"
 #include "chromecast/browser/metrics/cast_metrics_service_client.h"
 #include "chromecast/browser/service/cast_service.h"
+#include "chromecast/net/connectivity_checker.h"
 
 #if defined(OS_ANDROID)
 #include "components/crash/browser/crash_dump_manager_android.h"
@@ -82,6 +84,12 @@
   remote_debugging_server_.swap(remote_debugging_server);
 }
 
+void CastBrowserProcess::SetResourceDispatcherHostDelegate(
+    scoped_ptr<CastResourceDispatcherHostDelegate> delegate) {
+  DCHECK(!resource_dispatcher_host_delegate_);
+  resource_dispatcher_host_delegate_.swap(delegate);
+}
+
 #if defined(OS_ANDROID)
 void CastBrowserProcess::SetCrashDumpManager(
     scoped_ptr<breakpad::CrashDumpManager> crash_dump_manager) {
@@ -90,5 +98,11 @@
 }
 #endif  // defined(OS_ANDROID)
 
+void CastBrowserProcess::SetConnectivityChecker(
+    scoped_refptr<ConnectivityChecker> connectivity_checker) {
+  DCHECK(!connectivity_checker_);
+  connectivity_checker_.swap(connectivity_checker);
+}
+
 }  // namespace shell
 }  // namespace chromecast
diff --git a/chromecast/browser/cast_browser_process.h b/chromecast/browser/cast_browser_process.h
index 456e7af3..f4bec89 100644
--- a/chromecast/browser/cast_browser_process.h
+++ b/chromecast/browser/cast_browser_process.h
@@ -6,6 +6,7 @@
 #define CHROMECAST_BROWSER_CAST_BROWSER_PROCESS_H_
 
 #include "base/macros.h"
+#include "base/memory/ref_counted.h"
 #include "base/memory/scoped_ptr.h"
 
 class PrefService;
@@ -16,7 +17,7 @@
 
 namespace chromecast {
 class CastService;
-class WebCryptoServer;
+class ConnectivityChecker;
 
 namespace metrics {
 class CastMetricsHelper;
@@ -25,6 +26,7 @@
 
 namespace shell {
 class CastBrowserContext;
+class CastResourceDispatcherHostDelegate;
 class RemoteDebuggingServer;
 
 class CastBrowserProcess {
@@ -44,10 +46,14 @@
   void SetPrefService(scoped_ptr<PrefService> pref_service);
   void SetRemoteDebuggingServer(
       scoped_ptr<RemoteDebuggingServer> remote_debugging_server);
+  void SetResourceDispatcherHostDelegate(
+      scoped_ptr<CastResourceDispatcherHostDelegate> delegate);
 #if defined(OS_ANDROID)
   void SetCrashDumpManager(
       scoped_ptr<breakpad::CrashDumpManager> crash_dump_manager);
 #endif  // defined(OS_ANDROID)
+  void SetConnectivityChecker(
+      scoped_refptr<ConnectivityChecker> connectivity_checker);
 
   CastBrowserContext* browser_context() const { return browser_context_.get(); }
   CastService* cast_service() const { return cast_service_.get(); }
@@ -55,14 +61,24 @@
     return metrics_service_client_.get();
   }
   PrefService* pref_service() const { return pref_service_.get(); }
+  CastResourceDispatcherHostDelegate* resource_dispatcher_host_delegate()
+      const {
+    return resource_dispatcher_host_delegate_.get();
+  }
+  ConnectivityChecker* connectivity_checker() const {
+    return connectivity_checker_.get();
+  }
 
  private:
   // Note: The following order should match the order they are set in
   // CastBrowserMainParts.
   scoped_ptr<metrics::CastMetricsHelper> metrics_helper_;
   scoped_ptr<PrefService> pref_service_;
+  scoped_refptr<ConnectivityChecker> connectivity_checker_;
   scoped_ptr<CastBrowserContext> browser_context_;
   scoped_ptr<metrics::CastMetricsServiceClient> metrics_service_client_;
+  scoped_ptr<CastResourceDispatcherHostDelegate>
+      resource_dispatcher_host_delegate_;
 #if defined(OS_ANDROID)
   scoped_ptr<breakpad::CrashDumpManager> crash_dump_manager_;
 #endif  // defined(OS_ANDROID)
diff --git a/chromecast/browser/cast_content_browser_client.cc b/chromecast/browser/cast_content_browser_client.cc
index 57cbbf2..2ccdcd3d 100644
--- a/chromecast/browser/cast_content_browser_client.cc
+++ b/chromecast/browser/cast_content_browser_client.cc
@@ -15,6 +15,7 @@
 #include "chromecast/browser/cast_browser_main_parts.h"
 #include "chromecast/browser/cast_browser_process.h"
 #include "chromecast/browser/cast_network_delegate.h"
+#include "chromecast/browser/cast_resource_dispatcher_host_delegate.h"
 #include "chromecast/browser/devtools/cast_dev_tools_delegate.h"
 #include "chromecast/browser/geolocation/cast_access_token_store.h"
 #include "chromecast/browser/media/cma_message_filter_host.h"
@@ -28,6 +29,7 @@
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/certificate_request_result_type.h"
 #include "content/public/browser/render_process_host.h"
+#include "content/public/browser/resource_dispatcher_host.h"
 #include "content/public/common/content_descriptors.h"
 #include "content/public/common/content_switches.h"
 #include "content/public/common/url_constants.h"
@@ -73,6 +75,11 @@
       new media::CmaMessageFilterHost(host->GetID()));
   host->AddFilter(cma_message_filter.get());
 #endif  // !defined(OS_ANDROID)
+
+  auto extra_filters = PlatformGetBrowserMessageFilters();
+  for (auto const& filter : extra_filters) {
+    host->AddFilter(filter.get());
+  }
 }
 
 net::URLRequestContextGetter* CastContentBrowserClient::CreateRequestContext(
@@ -155,6 +162,13 @@
   prefs->allow_running_insecure_content = true;
 }
 
+void CastContentBrowserClient::ResourceDispatcherHostCreated() {
+  CastBrowserProcess::GetInstance()->SetResourceDispatcherHostDelegate(
+      make_scoped_ptr(new CastResourceDispatcherHostDelegate));
+  content::ResourceDispatcherHost::Get()->SetDelegate(
+      CastBrowserProcess::GetInstance()->resource_dispatcher_host_delegate());
+}
+
 std::string CastContentBrowserClient::GetApplicationLocale() {
   const std::string locale(base::i18n::GetConfiguredLocale());
   return locale.empty() ? "en-US" : locale;
diff --git a/chromecast/browser/cast_content_browser_client.h b/chromecast/browser/cast_content_browser_client.h
index 31d0eeb..ace72869 100644
--- a/chromecast/browser/cast_content_browser_client.h
+++ b/chromecast/browser/cast_content_browser_client.h
@@ -15,19 +15,29 @@
 class CrashHandlerHostLinux;
 }
 
+namespace content {
+class BrowserMessageFilter;
+}
+
 namespace chromecast {
 namespace shell {
 
 class CastBrowserMainParts;
 class URLRequestContextFactory;
 
-void PlatformAppendExtraCommandLineSwitches(base::CommandLine* command_line);
-
 class CastContentBrowserClient: public content::ContentBrowserClient {
  public:
   CastContentBrowserClient();
   ~CastContentBrowserClient() override;
 
+  // Appends extra command line arguments before launching a new process.
+  void PlatformAppendExtraCommandLineSwitches(base::CommandLine* command_line);
+
+  // Returns any BrowserMessageFilters from the platform implementation that
+  // should be added when launching a new render process.
+  std::vector<scoped_refptr<content::BrowserMessageFilter>>
+  PlatformGetBrowserMessageFilters();
+
   // content::ContentBrowserClient implementation:
   content::BrowserMainParts* CreateBrowserMainParts(
       const content::MainFunctionParams& parameters) override;
@@ -43,6 +53,7 @@
   content::AccessTokenStore* CreateAccessTokenStore() override;
   void OverrideWebkitPrefs(content::RenderViewHost* render_view_host,
                            content::WebPreferences* prefs) override;
+  void ResourceDispatcherHostCreated() override;
   std::string GetApplicationLocale() override;
   void AllowCertificateError(
       int render_process_id,
diff --git a/chromecast/browser/cast_content_browser_client_simple.cc b/chromecast/browser/cast_content_browser_client_simple.cc
index 132a2c9..34012b4 100644
--- a/chromecast/browser/cast_content_browser_client_simple.cc
+++ b/chromecast/browser/cast_content_browser_client_simple.cc
@@ -4,10 +4,18 @@
 
 #include "chromecast/browser/cast_content_browser_client.h"
 
+#include "content/public/browser/browser_message_filter.h"
+
 namespace chromecast {
 namespace shell {
 
-void PlatformAppendExtraCommandLineSwitches(base::CommandLine* command_line) {
+void CastContentBrowserClient::PlatformAppendExtraCommandLineSwitches(
+    base::CommandLine* command_line) {
+}
+
+std::vector<scoped_refptr<content::BrowserMessageFilter>>
+CastContentBrowserClient::PlatformGetBrowserMessageFilters() {
+  return std::vector<scoped_refptr<content::BrowserMessageFilter>>();
 }
 
 }  // namespace shell
diff --git a/chromecast/browser/cast_http_user_agent_settings.h b/chromecast/browser/cast_http_user_agent_settings.h
index 4c855b7a1..35a1a3a 100644
--- a/chromecast/browser/cast_http_user_agent_settings.h
+++ b/chromecast/browser/cast_http_user_agent_settings.h
@@ -7,7 +7,6 @@
 
 #include "base/compiler_specific.h"
 #include "base/macros.h"
-#include "base/prefs/pref_member.h"
 #include "net/url_request/http_user_agent_settings.h"
 
 namespace chromecast {
diff --git a/chromecast/browser/cast_resource_dispatcher_host_delegate.cc b/chromecast/browser/cast_resource_dispatcher_host_delegate.cc
new file mode 100644
index 0000000..9a935fb3
--- /dev/null
+++ b/chromecast/browser/cast_resource_dispatcher_host_delegate.cc
@@ -0,0 +1,26 @@
+// 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 "chromecast/browser/cast_resource_dispatcher_host_delegate.h"
+
+#include "chromecast/browser/cast_browser_process.h"
+#include "chromecast/net/connectivity_checker.h"
+#include "net/base/net_errors.h"
+#include "net/url_request/url_request.h"
+
+namespace chromecast {
+namespace shell {
+
+void CastResourceDispatcherHostDelegate::RequestComplete(
+    net::URLRequest* url_request) {
+  if (!url_request->status().is_success()) {
+    LOG(ERROR) << "Failed to load resource " << url_request->url()
+               << "; status:" << url_request->status().status() << ", error:"
+               << net::ErrorToShortString(url_request->status().error());
+    CastBrowserProcess::GetInstance()->connectivity_checker()->Check();
+  }
+}
+
+}  // namespace shell
+}  // namespace chromecast
diff --git a/chromecast/browser/cast_resource_dispatcher_host_delegate.h b/chromecast/browser/cast_resource_dispatcher_host_delegate.h
new file mode 100644
index 0000000..8295ccd0
--- /dev/null
+++ b/chromecast/browser/cast_resource_dispatcher_host_delegate.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 CHROMECAST_BROWSER_CAST_RESOURCE_DISPATCHER_HOST_DELEGATE_H_
+#define CHROMECAST_BROWSER_CAST_RESOURCE_DISPATCHER_HOST_DELEGATE_H_
+
+#include "base/macros.h"
+#include "content/public/browser/resource_dispatcher_host_delegate.h"
+
+namespace chromecast {
+namespace shell {
+
+class CastResourceDispatcherHostDelegate
+    : public content::ResourceDispatcherHostDelegate {
+ public:
+  CastResourceDispatcherHostDelegate() {}
+
+  // content::ResourceDispatcherHostDelegate implementation:
+  void RequestComplete(net::URLRequest* url_request) override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(CastResourceDispatcherHostDelegate);
+};
+
+}  // namespace shell
+}  // namespace chromecast
+
+#endif  // CHROMECAST_BROWSER_CAST_RESOURCE_DISPATCHER_HOST_DELEGATE_H_
diff --git a/chromecast/browser/metrics/cast_metrics_service_client.cc b/chromecast/browser/metrics/cast_metrics_service_client.cc
index 84192d7..3e97217 100644
--- a/chromecast/browser/metrics/cast_metrics_service_client.cc
+++ b/chromecast/browser/metrics/cast_metrics_service_client.cc
@@ -21,6 +21,7 @@
 #include "components/metrics/net/net_metrics_log_uploader.h"
 #include "components/metrics/net/network_metrics_provider.h"
 #include "components/metrics/profiler/profiler_metrics_provider.h"
+#include "components/metrics/url_constants.h"
 #include "content/public/common/content_switches.h"
 
 #if defined(OS_LINUX)
@@ -133,10 +134,8 @@
 
 scoped_ptr< ::metrics::MetricsLogUploader>
 CastMetricsServiceClient::CreateUploader(
-    const std::string& server_url,
-    const std::string& mime_type,
     const base::Callback<void(int)>& on_upload_complete) {
-  std::string uma_server_url(server_url);
+  std::string uma_server_url(::metrics::kDefaultMetricsServerUrl);
   base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
   if (command_line->HasSwitch(switches::kOverrideMetricsUploadUrl)) {
     uma_server_url.assign(
@@ -147,7 +146,7 @@
       new ::metrics::NetMetricsLogUploader(
           request_context_,
           uma_server_url,
-          mime_type,
+          ::metrics::kDefaultMetricsMimeType,
           on_upload_complete));
 }
 
diff --git a/chromecast/browser/metrics/cast_metrics_service_client.h b/chromecast/browser/metrics/cast_metrics_service_client.h
index af71a1c..72ceede 100644
--- a/chromecast/browser/metrics/cast_metrics_service_client.h
+++ b/chromecast/browser/metrics/cast_metrics_service_client.h
@@ -61,8 +61,6 @@
   void StartGatheringMetrics(const base::Closure& done_callback) override;
   void CollectFinalMetrics(const base::Closure& done_callback) override;
   scoped_ptr< ::metrics::MetricsLogUploader> CreateUploader(
-      const std::string& server_url,
-      const std::string& mime_type,
       const base::Callback<void(int)>& on_upload_complete) override;
 
   // Starts/stops the metrics service.
diff --git a/chromecast/browser/test/chromecast_browser_test_runner.cc b/chromecast/browser/test/chromecast_browser_test_runner.cc
index ff85ca7..587d745 100644
--- a/chromecast/browser/test/chromecast_browser_test_runner.cc
+++ b/chromecast/browser/test/chromecast_browser_test_runner.cc
@@ -16,6 +16,11 @@
 
 namespace {
 
+// Duplicate settings to avoid link dependency on chromecast/internal.
+const char kSwitchesAVSettingsUnixSocketPath[] = "av-settings-unix-socket-path";
+const char kSwitchesNoWifi[] = "no-wifi";
+
+const char kAvSettingsUnixSocketPath[] = "/tmp/avsettings";
 const char kTestTypeBrowser[] = "browser";
 
 class BrowserTestSuite : public content::ContentTestSuiteBase {
@@ -43,7 +48,10 @@
       base::CommandLine* command_line,
       const base::FilePath& temp_data_dir) override {
     // TODO(gunsch): handle temp_data_dir
+    command_line->AppendSwitch(kSwitchesNoWifi);
     command_line->AppendSwitchASCII(switches::kTestType, kTestTypeBrowser);
+    command_line->AppendSwitchASCII(kSwitchesAVSettingsUnixSocketPath,
+                                    kAvSettingsUnixSocketPath);
     return true;
   }
 
diff --git a/chromecast/chromecast.gyp b/chromecast/chromecast.gyp
index 8c3e246f..f5bf73d 100644
--- a/chromecast/chromecast.gyp
+++ b/chromecast/chromecast.gyp
@@ -10,6 +10,7 @@
     'chromium_code': 1,
     'chromecast_branding%': 'Chromium',
     'disable_display%': 0,
+    'use_chromecast_webui%': 0,
   },
   'includes': [
     'chromecast_tests.gypi',
@@ -25,10 +26,9 @@
     ],
   },
   'targets': [
-    # TODO(gunsch): Remove these fake targets once they're either added or no
+    # TODO(gunsch): Remove this fake target once it's either added or no
     # longer referenced from internal code.
     {'target_name': 'cast_media_audio', 'type': 'none'},
-    {'target_name': 'cast_port_impl', 'type': 'none'},
 
     {
       'target_name': 'cast_base',
@@ -52,6 +52,8 @@
         '../components/components.gyp:crash_component',
       ],
       'sources': [
+        'crash/cast_crash_keys.cc',
+        'crash/cast_crash_keys.h',
         'crash/cast_crash_reporter_client.cc',
         'crash/cast_crash_reporter_client.h',
       ],
@@ -71,6 +73,10 @@
       'target_name': 'cast_net',
       'type': '<(component)',
       'sources': [
+        'net/connectivity_checker.cc',
+        'net/connectivity_checker.h',
+        'net/net_switches.cc',
+        'net/net_switches.h',
         'net/network_change_notifier_cast.cc',
         'net/network_change_notifier_cast.h',
         'net/network_change_notifier_factory_cast.cc',
@@ -123,12 +129,28 @@
               '<(SHARED_INTERMEDIATE_DIR)/ui/strings/app_locale_settings_en-US.pak',
               '<(SHARED_INTERMEDIATE_DIR)/ui/strings/ui_strings_en-US.pak',
             ],
+            'conditions': [
+              ['chromecast_branding=="Chrome" and use_chromecast_webui==1', {
+                'pak_inputs': [
+                  '<(SHARED_INTERMEDIATE_DIR)/chromecast/app_resources.pak',
+                  '<(SHARED_INTERMEDIATE_DIR)/chromecast/cast_webui_resources.pak',
+                ],
+              }],
+            ],
             'pak_output': '<(PRODUCT_DIR)/assets/cast_shell.pak',
           },
           'includes': [ '../build/repack_action.gypi' ],
         },
       ],
-    },
+      'conditions': [
+        ['chromecast_branding=="Chrome" and use_chromecast_webui==1', {
+          'dependencies': [
+            'internal/chromecast_resources.gyp:chromecast_app_resources',
+            'internal/chromecast_resources.gyp:chromecast_webui_resources',
+          ],
+        }],
+      ],
+    },  # end of target 'cast_shell_pak'
     # This target contains all content-embedder implementation that is
     # non-platform-specific.
     {
@@ -137,6 +159,7 @@
       'dependencies': [
         'cast_base',
         'cast_crash_client',
+        'cast_net',
         'cast_shell_pak',
         'cast_shell_resources',
         'cast_version_header',
@@ -179,6 +202,8 @@
         'browser/cast_http_user_agent_settings.h',
         'browser/cast_network_delegate.cc',
         'browser/cast_network_delegate.h',
+        'browser/cast_resource_dispatcher_host_delegate.cc',
+        'browser/cast_resource_dispatcher_host_delegate.h',
         'browser/devtools/cast_dev_tools_delegate.cc',
         'browser/devtools/cast_dev_tools_delegate.h',
         'browser/devtools/remote_debugging_server.cc',
@@ -494,7 +519,6 @@
           'target_name': 'cast_shell_core',
           'type': '<(component)',
           'dependencies': [
-            'cast_net',
             'cast_shell_media',
             'cast_shell_common',
             'media/media.gyp:cast_media',
diff --git a/chromecast/crash/android/cast_crash_reporter_client_android.cc b/chromecast/crash/android/cast_crash_reporter_client_android.cc
index 2137faee..41717f8 100644
--- a/chromecast/crash/android/cast_crash_reporter_client_android.cc
+++ b/chromecast/crash/android/cast_crash_reporter_client_android.cc
@@ -11,6 +11,7 @@
 #include "chromecast/android/chromecast_config_android.h"
 #include "chromecast/common/global_descriptors.h"
 #include "chromecast/common/version.h"
+#include "chromecast/crash/cast_crash_keys.h"
 #include "content/public/common/content_switches.h"
 
 namespace chromecast {
@@ -55,6 +56,10 @@
   return true;
 }
 
+size_t CastCrashReporterClientAndroid::RegisterCrashKeys() {
+  return crash_keys::RegisterCastCrashKeys();
+}
+
 bool CastCrashReporterClientAndroid::GetCollectStatsConsent() {
   return android::ChromecastConfigAndroid::GetInstance()->CanSendUsageStats();
 }
diff --git a/chromecast/crash/android/cast_crash_reporter_client_android.h b/chromecast/crash/android/cast_crash_reporter_client_android.h
index 4f78334..e98eef7 100644
--- a/chromecast/crash/android/cast_crash_reporter_client_android.h
+++ b/chromecast/crash/android/cast_crash_reporter_client_android.h
@@ -25,6 +25,7 @@
   bool GetCollectStatsConsent() override;
   bool EnableBreakpadForProcess(
       const std::string& process_type) override;
+  size_t RegisterCrashKeys() override;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(CastCrashReporterClientAndroid);
diff --git a/chromecast/crash/android/crash_handler.cc b/chromecast/crash/android/crash_handler.cc
index a73e31352..4198764 100644
--- a/chromecast/crash/android/crash_handler.cc
+++ b/chromecast/crash/android/crash_handler.cc
@@ -5,12 +5,14 @@
 #include "chromecast/crash/android/crash_handler.h"
 
 #include <jni.h>
+#include <stdlib.h>
 #include <string>
 
 #include "base/android/jni_android.h"
 #include "base/android/jni_string.h"
 #include "base/files/file_path.h"
 #include "base/logging.h"
+#include "base/strings/string_number_conversions.h"
 #include "breakpad/src/client/linux/handler/exception_handler.h"
 #include "breakpad/src/client/linux/handler/minidump_descriptor.h"
 #include "chromecast/common/version.h"
@@ -40,6 +42,19 @@
   return false;
 }
 
+// Debug builds: always to crash-staging
+// Release builds: only to crash-staging for local/invalid build numbers
+bool UploadCrashToStaging() {
+#if CAST_IS_DEBUG_BUILD
+  return true;
+#else
+  int build_number;
+  if (base::StringToInt(CAST_BUILD_REVISION, &build_number))
+    return build_number == 0;
+  return true;
+#endif
+}
+
 }  // namespace
 
 namespace chromecast {
@@ -119,7 +134,7 @@
       env,
       crash_dump_path_java.obj(),
       log_file_path_java.obj(),
-      CAST_IS_DEBUG_BUILD);
+      UploadCrashToStaging());
 }
 
 void CrashHandler::UploadCrashDumpsAsync() {
@@ -129,7 +144,7 @@
                                              crash_dump_path_.value());
   Java_CastCrashHandler_uploadCrashDumpsAsync(env,
                                               crash_dump_path_java.obj(),
-                                              CAST_IS_DEBUG_BUILD);
+                                              UploadCrashToStaging());
 }
 
 void CrashHandler::RemoveCrashDumps() {
@@ -139,7 +154,7 @@
       base::android::ConvertUTF8ToJavaString(env,
                                              crash_dump_path_.value());
   Java_CastCrashHandler_removeCrashDumpsSync(
-      env, crash_dump_path_java.obj(), CAST_IS_DEBUG_BUILD);
+      env, crash_dump_path_java.obj(), UploadCrashToStaging());
 }
 
 }  // namespace chromecast
diff --git a/chromecast/crash/cast_crash_keys.cc b/chromecast/crash/cast_crash_keys.cc
new file mode 100644
index 0000000..6ca240f
--- /dev/null
+++ b/chromecast/crash/cast_crash_keys.cc
@@ -0,0 +1,53 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromecast/crash/cast_crash_keys.h"
+
+// TODO(kjoswiak): Potentially refactor chunk size info as well as non-cast
+// specific keys out and make shared with chrome/common/crash_keys.cc.
+namespace {
+
+// A small crash key, guaranteed to never be split into multiple pieces.
+const size_t kSmallSize = 63;
+
+// A medium crash key, which will be chunked on certain platforms but not
+// others. Guaranteed to never be more than four chunks.
+const size_t kMediumSize = kSmallSize * 4;
+
+// A large crash key, which will be chunked on all platforms. This should be
+// used sparingly.
+const size_t kLargeSize = kSmallSize * 16;
+
+// The maximum lengths specified by breakpad include the trailing NULL, so
+// the actual length of the string is one less.
+static const size_t kSingleChunkLength = 63;
+
+}
+
+namespace chromecast {
+namespace crash_keys {
+
+const char kLastApp[] = "last_app";
+const char kCurrentApp[] = "current_app";
+const char kPreviousApp[] = "previous_app";
+
+size_t RegisterCastCrashKeys() {
+  const base::debug::CrashKey fixed_keys[] = {
+    { kLastApp, kSmallSize },
+    { kCurrentApp, kSmallSize },
+    { kPreviousApp, kSmallSize },
+    // base/:
+    { "dm-usage", kSmallSize },
+    { "total-dm-usage", kSmallSize },
+    // content/:
+    { "ppapi_path", kMediumSize },
+    { "subresource_url", kLargeSize },
+  };
+
+  return base::debug::InitCrashKeys(fixed_keys, arraysize(fixed_keys),
+                                    kSingleChunkLength);
+}
+
+}  // namespace chromecast
+}  // namespace crash_keys
diff --git a/chromecast/crash/cast_crash_keys.h b/chromecast/crash/cast_crash_keys.h
new file mode 100644
index 0000000..2f1af20e
--- /dev/null
+++ b/chromecast/crash/cast_crash_keys.h
@@ -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.
+
+#ifndef CHROMECAST_CRASH_CAST_CRASH_KEYS_H_
+#define CHROMECAST_CRASH_CAST_CRASH_KEYS_H_
+
+#include "base/debug/crash_logging.h"
+
+namespace chromecast {
+namespace crash_keys {
+
+size_t RegisterCastCrashKeys();
+
+extern const char kCurrentApp[];
+extern const char kLastApp[];
+extern const char kPreviousApp[];
+
+}  // namespace chromecast
+}  // namespace crash_keys
+
+#endif  // CHROMECAST_CRASH_CAST_CRASH_KEYS_H_
diff --git a/chromecast/media/base/media_caps.h b/chromecast/media/base/media_caps.h
index 3590756..8e13798 100644
--- a/chromecast/media/base/media_caps.h
+++ b/chromecast/media/base/media_caps.h
@@ -16,4 +16,3 @@
 }  // namespace media
 
 #endif  // CHROMECAST_MEDIA_BASE_MEDIA_CAPS_
-
diff --git a/chromecast/net/connectivity_checker.cc b/chromecast/net/connectivity_checker.cc
new file mode 100644
index 0000000..e64ac47d
--- /dev/null
+++ b/chromecast/net/connectivity_checker.cc
@@ -0,0 +1,173 @@
+// 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 "chromecast/net/connectivity_checker.h"
+
+#include "base/command_line.h"
+#include "base/logging.h"
+#include "base/message_loop/message_loop.h"
+#include "chromecast/net/net_switches.h"
+#include "net/base/request_priority.h"
+#include "net/http/http_response_headers.h"
+#include "net/http/http_response_info.h"
+#include "net/http/http_status_code.h"
+#include "net/proxy/proxy_config.h"
+#include "net/proxy/proxy_config_service_fixed.h"
+#include "net/url_request/url_request_context.h"
+#include "net/url_request/url_request_context_builder.h"
+
+namespace chromecast {
+
+namespace {
+
+// How often connectivity checks are performed in seconds
+const unsigned int kConnectivityPeriodSeconds = 1;
+
+// Number of consecutive bad responses received before connectivity status is
+// changed to offline
+const unsigned int kNumBadResponses = 3;
+
+// Default url for connectivity checking.
+const char kDefaultConnectivityCheckUrl[] =
+    "https://clients3.google.com/generate_204";
+
+}  // namespace
+
+ConnectivityChecker::ConnectivityChecker(
+    const scoped_refptr<base::MessageLoopProxy>& loop_proxy)
+    : connectivity_observer_list_(
+          new ObserverListThreadSafe<ConnectivityObserver>()),
+      loop_proxy_(loop_proxy),
+      connected_(false),
+      bad_responses_(0) {
+  DCHECK(loop_proxy_.get());
+  loop_proxy->PostTask(FROM_HERE,
+                       base::Bind(&ConnectivityChecker::Initialize, this));
+}
+
+void ConnectivityChecker::Initialize() {
+  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+  base::CommandLine::StringType check_url_str =
+      command_line->GetSwitchValueNative(switches::kConnectivityCheckUrl);
+  connectivity_check_url_.reset(new GURL(
+      check_url_str.empty() ? kDefaultConnectivityCheckUrl : check_url_str));
+
+  net::URLRequestContextBuilder builder;
+  builder.set_proxy_config_service(
+      new net::ProxyConfigServiceFixed(net::ProxyConfig::CreateDirect()));
+  builder.DisableHttpCache();
+  url_request_context_.reset(builder.Build());
+
+  net::NetworkChangeNotifier::AddConnectionTypeObserver(this);
+  if (!net::NetworkChangeNotifier::IsOffline()) {
+    loop_proxy_->PostTask(FROM_HERE,
+                          base::Bind(&ConnectivityChecker::Check, this));
+  }
+}
+
+ConnectivityChecker::~ConnectivityChecker() {
+  DCHECK(loop_proxy_.get());
+  loop_proxy_->DeleteSoon(FROM_HERE, url_request_context_.release());
+}
+
+void ConnectivityChecker::AddConnectivityObserver(
+    ConnectivityObserver* observer) {
+  connectivity_observer_list_->AddObserver(observer);
+}
+
+void ConnectivityChecker::RemoveConnectivityObserver(
+    ConnectivityObserver* observer) {
+  connectivity_observer_list_->RemoveObserver(observer);
+}
+
+bool ConnectivityChecker::Connected() const {
+  return connected_;
+}
+
+void ConnectivityChecker::SetConnectivity(bool connected) {
+  if (connected_ == connected)
+    return;
+
+  connected_ = connected;
+  connectivity_observer_list_->Notify(
+      FROM_HERE, &ConnectivityObserver::OnConnectivityChanged, connected);
+  LOG(INFO) << "Global connection is: " << (connected ? "Up" : "Down");
+}
+
+void ConnectivityChecker::Check() {
+  if (!loop_proxy_->BelongsToCurrentThread()) {
+    loop_proxy_->PostTask(FROM_HERE,
+                          base::Bind(&ConnectivityChecker::Check, this));
+    return;
+  }
+  DCHECK(url_request_context_.get());
+
+  // If url_request_ is non-null, there is already a check going on. Don't
+  // start another.
+  if (url_request_.get())
+    return;
+
+  VLOG(2) << "Connectivity check: url=" << *connectivity_check_url_;
+  url_request_ = url_request_context_->CreateRequest(
+      *connectivity_check_url_, net::MAXIMUM_PRIORITY, this, NULL);
+  url_request_->set_method("HEAD");
+  url_request_->Start();
+}
+
+void ConnectivityChecker::OnConnectionTypeChanged(
+    net::NetworkChangeNotifier::ConnectionType type) {
+  Cancel();
+
+  // If there is no connection, set connectivity to false. Otherwise retest
+  // connectivity
+  if (type == net::NetworkChangeNotifier::CONNECTION_NONE) {
+    SetConnectivity(false);
+  } else {
+    Check();
+  }
+}
+
+void ConnectivityChecker::OnResponseStarted(net::URLRequest* request) {
+  int http_response_code =
+      (request->status().is_success() &&
+       request->response_info().headers.get() != NULL)
+          ? request->response_info().headers->response_code()
+          : net::HTTP_BAD_REQUEST;
+
+  // Clears resources.
+  url_request_.reset(NULL);  // URLRequest::Cancel() is called in destructor.
+
+  if (http_response_code < 400) {
+    bad_responses_ = 0;
+    SetConnectivity(true);
+    return;
+  }
+
+  ++bad_responses_;
+  if (bad_responses_ > kNumBadResponses) {
+    bad_responses_ = kNumBadResponses;
+    SetConnectivity(false);
+  }
+
+  // Check again
+  if (!net::NetworkChangeNotifier::IsOffline()) {
+    loop_proxy_->PostDelayedTask(
+        FROM_HERE, base::Bind(&ConnectivityChecker::Check, this),
+        base::TimeDelta::FromSeconds(kConnectivityPeriodSeconds));
+  }
+}
+
+void ConnectivityChecker::OnReadCompleted(net::URLRequest* request,
+                                          int bytes_read) {
+  NOTREACHED();
+}
+
+void ConnectivityChecker::Cancel() {
+  if (url_request_.get()) {
+    VLOG(2) << "Cancel connectivity check in progress";
+    url_request_.reset(NULL);  // URLRequest::Cancel() is called in destructor.
+  }
+}
+
+}  // namespace chromecast
diff --git a/chromecast/net/connectivity_checker.h b/chromecast/net/connectivity_checker.h
new file mode 100644
index 0000000..28bde9a
--- /dev/null
+++ b/chromecast/net/connectivity_checker.h
@@ -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.
+
+#ifndef CHROMECAST_NET_CONNECTIVITY_CHECKER_H_
+#define CHROMECAST_NET_CONNECTIVITY_CHECKER_H_
+
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "net/base/network_change_notifier.h"
+#include "net/url_request/url_request.h"
+
+class GURL;
+
+namespace base {
+class MessageLoopProxy;
+}
+
+namespace net {
+class URLRequestContext;
+}
+
+namespace chromecast {
+
+// Simple class to check network connectivity by sending a HEAD http request
+// to given url.
+class ConnectivityChecker
+    : public base::RefCountedThreadSafe<ConnectivityChecker>,
+      public net::URLRequest::Delegate,
+      public net::NetworkChangeNotifier::ConnectionTypeObserver {
+ public:
+  class ConnectivityObserver {
+   public:
+    // Will be called when internet connectivity changes
+    virtual void OnConnectivityChanged(bool connected) = 0;
+
+   protected:
+    ConnectivityObserver() {}
+    virtual ~ConnectivityObserver() {}
+
+   private:
+    DISALLOW_COPY_AND_ASSIGN(ConnectivityObserver);
+  };
+
+  explicit ConnectivityChecker(
+      const scoped_refptr<base::MessageLoopProxy>& loop_proxy);
+
+  void AddConnectivityObserver(ConnectivityObserver* observer);
+  void RemoveConnectivityObserver(ConnectivityObserver* observer);
+
+  // Returns if there is internet connectivity
+  bool Connected() const;
+
+  // Checks for connectivity
+  void Check();
+
+ protected:
+  ~ConnectivityChecker() override;
+
+ private:
+  friend class base::RefCountedThreadSafe<ConnectivityChecker>;
+
+  // UrlRequest::Delegate implementation:
+  void OnResponseStarted(net::URLRequest* request) override;
+  void OnReadCompleted(net::URLRequest* request, int bytes_read) override;
+
+  // Initializes ConnectivityChecker
+  void Initialize();
+
+  // NetworkChangeNotifier::ConnectionTypeObserver implementation:
+  void OnConnectionTypeChanged(
+      net::NetworkChangeNotifier::ConnectionType type) override;
+
+  // Cancels current connectivity checking in progress.
+  void Cancel();
+
+  // Sets connectivity and alerts observers if it has changed
+  void SetConnectivity(bool connected);
+
+  scoped_ptr<GURL> connectivity_check_url_;
+  scoped_ptr<net::URLRequestContext> url_request_context_;
+  scoped_ptr<net::URLRequest> url_request_;
+  const scoped_refptr<ObserverListThreadSafe<ConnectivityObserver> >
+      connectivity_observer_list_;
+  const scoped_refptr<base::MessageLoopProxy> loop_proxy_;
+  bool connected_;
+  unsigned int bad_responses_;
+
+  DISALLOW_COPY_AND_ASSIGN(ConnectivityChecker);
+};
+
+}  // namespace chromecast
+
+#endif  // CHROMECAST_NET_CONNECTIVITY_CHECKER_H_
diff --git a/chromecast/net/net_switches.cc b/chromecast/net/net_switches.cc
new file mode 100644
index 0000000..87f79b68
--- /dev/null
+++ b/chromecast/net/net_switches.cc
@@ -0,0 +1,13 @@
+// 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 "chromecast/net/net_switches.h"
+
+namespace switches {
+
+// Url for network connectivity checking. Default is
+// "https://clients3.google.com/generate_204".
+const char kConnectivityCheckUrl[] = "connectivity-check-url";
+
+}  // namespace switches
diff --git a/chromecast/net/net_switches.h b/chromecast/net/net_switches.h
new file mode 100644
index 0000000..dda9cac5
--- /dev/null
+++ b/chromecast/net/net_switches.h
@@ -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.
+
+#ifndef CHROMECAST_NET_NET_SWITCHES_H_
+#define CHROMECAST_NET_NET_SWITCHES_H_
+
+namespace switches {
+
+extern const char kConnectivityCheckUrl[];
+
+}  // namespace switches
+
+#endif  // CHROMECAST_NET_NET_SWITCHES_H_
diff --git a/chromecast/renderer/cast_content_renderer_client.cc b/chromecast/renderer/cast_content_renderer_client.cc
index 1f9cc976..188e21e5 100644
--- a/chromecast/renderer/cast_content_renderer_client.cc
+++ b/chromecast/renderer/cast_content_renderer_client.cc
@@ -19,6 +19,7 @@
 #include "content/public/renderer/render_view.h"
 #include "content/public/renderer/render_view_observer.h"
 #include "crypto/nss_util.h"
+#include "ipc/message_filter.h"
 #include "third_party/WebKit/public/platform/WebColor.h"
 #include "third_party/WebKit/public/web/WebSettings.h"
 #include "third_party/WebKit/public/web/WebView.h"
@@ -105,7 +106,8 @@
   PlatformPollFreemem();
 #endif
 
-  cast_observer_.reset(new CastRenderProcessObserver());
+  cast_observer_.reset(
+      new CastRenderProcessObserver(PlatformGetRendererMessageFilters()));
 
   prescient_networking_dispatcher_.reset(
       new network_hints::PrescientNetworkingDispatcher());
@@ -140,7 +142,8 @@
 #if !defined(OS_ANDROID)
 scoped_ptr<::media::RendererFactory>
 CastContentRendererClient::CreateMediaRendererFactory(
-    ::content::RenderFrame* render_frame) {
+    ::content::RenderFrame* render_frame,
+    const scoped_refptr<::media::MediaLog>& media_log) {
   const base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
   if (!cmd_line->HasSwitch(switches::kEnableCmaMediaPipeline))
     return nullptr;
diff --git a/chromecast/renderer/cast_content_renderer_client.h b/chromecast/renderer/cast_content_renderer_client.h
index 9f0dfbd..efdc3a5 100644
--- a/chromecast/renderer/cast_content_renderer_client.h
+++ b/chromecast/renderer/cast_content_renderer_client.h
@@ -10,6 +10,10 @@
 #include "base/macros.h"
 #include "content/public/renderer/content_renderer_client.h"
 
+namespace IPC {
+class MessageFilter;
+}
+
 namespace network_hints {
 class PrescientNetworkingDispatcher;
 }  // namespace network_hints
@@ -18,6 +22,7 @@
 namespace shell {
 class CastRenderProcessObserver;
 
+// Adds any platform-specific bindings to the current frame.
 void PlatformAddRendererNativeBindings(blink::WebLocalFrame* frame);
 
 class CastContentRendererClient : public content::ContentRendererClient {
@@ -25,6 +30,11 @@
   CastContentRendererClient();
   ~CastContentRendererClient() override;
 
+  // Returns any MessageFilters from the platform implementation that should
+  // be added to the render process.
+  std::vector<scoped_refptr<IPC::MessageFilter>>
+  PlatformGetRendererMessageFilters();
+
   // ContentRendererClient implementation:
   void RenderThreadStarted() override;
   void RenderViewCreated(content::RenderView* render_view) override;
@@ -32,7 +42,8 @@
       std::vector< ::media::KeySystemInfo>* key_systems) override;
 #if !defined(OS_ANDROID)
   scoped_ptr<media::RendererFactory> CreateMediaRendererFactory(
-      content::RenderFrame* render_frame) override;
+      content::RenderFrame* render_frame,
+      const scoped_refptr<media::MediaLog>& media_log) override;
 #endif
   blink::WebPrescientNetworking* GetPrescientNetworking() override;
   void DeferMediaLoad(content::RenderFrame* render_frame,
diff --git a/chromecast/renderer/cast_content_renderer_client_simple.cc b/chromecast/renderer/cast_content_renderer_client_simple.cc
index e8d40c6..5ad4fccb 100644
--- a/chromecast/renderer/cast_content_renderer_client_simple.cc
+++ b/chromecast/renderer/cast_content_renderer_client_simple.cc
@@ -4,11 +4,18 @@
 
 #include "chromecast/renderer/cast_content_renderer_client.h"
 
+#include "ipc/message_filter.h"
+
 namespace chromecast {
 namespace shell {
 
 void PlatformAddRendererNativeBindings(blink::WebLocalFrame* frame) {
 }
 
+std::vector<scoped_refptr<IPC::MessageFilter>>
+CastContentRendererClient::PlatformGetRendererMessageFilters() {
+  return std::vector<scoped_refptr<IPC::MessageFilter>>();
+}
+
 }  // namespace shell
 }  // namespace chromecast
diff --git a/chromecast/renderer/cast_render_process_observer.cc b/chromecast/renderer/cast_render_process_observer.cc
index 60638935..316fd616 100644
--- a/chromecast/renderer/cast_render_process_observer.cc
+++ b/chromecast/renderer/cast_render_process_observer.cc
@@ -10,7 +10,10 @@
 namespace chromecast {
 namespace shell {
 
-CastRenderProcessObserver::CastRenderProcessObserver() {
+CastRenderProcessObserver::CastRenderProcessObserver(
+    const std::vector<scoped_refptr<IPC::MessageFilter>>&
+        platform_message_filters)
+    : platform_message_filters_(platform_message_filters) {
   content::RenderThread* thread = content::RenderThread::Get();
   thread->AddObserver(this);
   CreateCustomFilters();
@@ -22,22 +25,32 @@
 }
 
 void CastRenderProcessObserver::CreateCustomFilters() {
-#if !defined(OS_ANDROID)
   content::RenderThread* thread = content::RenderThread::Get();
+#if !defined(OS_ANDROID)
   cma_message_filter_proxy_ =
       new media::CmaMessageFilterProxy(thread->GetIOMessageLoopProxy());
   thread->AddFilter(cma_message_filter_proxy_.get());
 #endif  // !defined(OS_ANDROID)
+  for (const auto& filter : platform_message_filters_) {
+    thread->AddFilter(filter.get());
+  }
 }
 
 void CastRenderProcessObserver::OnRenderProcessShutdown() {
-#if !defined(OS_ANDROID)
   content::RenderThread* thread = content::RenderThread::Get();
+#if !defined(OS_ANDROID)
   if (cma_message_filter_proxy_.get()) {
     thread->RemoveFilter(cma_message_filter_proxy_.get());
     cma_message_filter_proxy_ = NULL;
   }
 #endif  // !defined(OS_ANDROID)
+  for (auto& filter : platform_message_filters_) {
+    if (filter.get()) {
+      thread->RemoveFilter(filter.get());
+      filter = nullptr;
+    }
+  }
+  platform_message_filters_.clear();
 }
 
 }  // namespace shell
diff --git a/chromecast/renderer/cast_render_process_observer.h b/chromecast/renderer/cast_render_process_observer.h
index ca992bc9..0c28b5817 100644
--- a/chromecast/renderer/cast_render_process_observer.h
+++ b/chromecast/renderer/cast_render_process_observer.h
@@ -5,10 +5,16 @@
 #ifndef CHROMECAST_RENDERER_CAST_RENDER_PROCESS_OBSERVER_H_
 #define CHROMECAST_RENDERER_CAST_RENDER_PROCESS_OBSERVER_H_
 
+#include <vector>
+
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "content/public/renderer/render_process_observer.h"
 
+namespace IPC {
+class MessageFilter;
+}
+
 namespace chromecast {
 namespace media {
 class CmaMessageFilterProxy;
@@ -18,7 +24,9 @@
 
 class CastRenderProcessObserver : public content::RenderProcessObserver {
  public:
-  CastRenderProcessObserver();
+  CastRenderProcessObserver(
+      const std::vector<scoped_refptr<IPC::MessageFilter>>&
+          platform_message_filters);
   ~CastRenderProcessObserver() override;
 
  private:
@@ -30,6 +38,7 @@
 #if !defined(OS_ANDROID)
   scoped_refptr<media::CmaMessageFilterProxy> cma_message_filter_proxy_;
 #endif  // !defined(OS_ANDROID)
+  std::vector<scoped_refptr<IPC::MessageFilter>> platform_message_filters_;
 
   DISALLOW_COPY_AND_ASSIGN(CastRenderProcessObserver);
 };
diff --git a/chromecast/renderer/key_systems_cast.cc b/chromecast/renderer/key_systems_cast.cc
index 919ebbe8..7739d72 100644
--- a/chromecast/renderer/key_systems_cast.cc
+++ b/chromecast/renderer/key_systems_cast.cc
@@ -17,25 +17,31 @@
 namespace chromecast {
 namespace shell {
 
-void AddKeySystemWithCodecs(
-    const std::string& key_system_name,
-    std::vector< ::media::KeySystemInfo>* concrete_key_systems) {
-  ::media::KeySystemInfo info(key_system_name);
-  info.supported_codecs = ::media::EME_CODEC_MP4_ALL;
-  concrete_key_systems->push_back(info);
-}
-
 void AddChromecastKeySystems(
-    std::vector< ::media::KeySystemInfo>* key_systems_info) {
+    std::vector<::media::KeySystemInfo>* key_systems_info) {
 #if defined(WIDEVINE_CDM_AVAILABLE)
-  AddWidevineWithCodecs(cdm::WIDEVINE,
-                        ::media::EME_CODEC_MP4_ALL,
-                        key_systems_info);
+  AddWidevineWithCodecs(
+      cdm::WIDEVINE,
+      ::media::EME_CODEC_MP4_AAC | ::media::EME_CODEC_MP4_AVC1,
+      ::media::EME_SESSION_TYPE_NOT_SUPPORTED,  // Persistent license.
+      ::media::EME_SESSION_TYPE_NOT_SUPPORTED,  // Persistent release message.
+      ::media::EME_FEATURE_NOT_SUPPORTED,       // Persistent state.
+      ::media::EME_FEATURE_ALWAYS_ENABLED,      // Distinctive identifier.
+      key_systems_info);
 #endif
 
 #if defined(PLAYREADY_CDM_AVAILABLE)
-  AddKeySystemWithCodecs(media::kChromecastPlayreadyKeySystem,
-                         key_systems_info);
+  ::media::KeySystemInfo info;
+  info.key_system = ::media::kChromecastPlayreadyKeySystem;
+  info.supported_codecs =
+      ::media::EME_CODEC_MP4_AAC | ::media::EME_CODEC_MP4_AVC1;
+  info.supported_init_data_types = ::media::EME_INIT_DATA_TYPE_CENC;
+  info.persistent_license_support = ::media::EME_SESSION_TYPE_NOT_SUPPORTED;
+  info.persistent_release_message_support =
+      ::media::EME_SESSION_TYPE_NOT_SUPPORTED;
+  info.persistent_state_support = ::media::EME_FEATURE_ALWAYS_ENABLED;
+  info.distinctive_identifier_support = ::media::EME_FEATURE_ALWAYS_ENABLED;
+  key_systems_info->push_back(info);
 #endif
 }
 
diff --git a/chromecast/renderer/key_systems_cast.h b/chromecast/renderer/key_systems_cast.h
index 36b21d5d..bd3eec42 100644
--- a/chromecast/renderer/key_systems_cast.h
+++ b/chromecast/renderer/key_systems_cast.h
@@ -12,17 +12,12 @@
 namespace chromecast {
 namespace shell {
 
-// Adds a single key system by name.
-void AddKeySystemWithCodecs(
-    const std::string& key_system_name,
-    std::vector< ::media::KeySystemInfo>* concrete_key_systems);
-
 void AddChromecastKeySystems(
-    std::vector< ::media::KeySystemInfo>* key_systems_info);
+    std::vector<::media::KeySystemInfo>* key_systems_info);
 
 // TODO(gunsch): Remove when prefixed EME is removed.
 void AddChromecastPlatformKeySystems(
-    std::vector< ::media::KeySystemInfo>* key_systems_info);
+    std::vector<::media::KeySystemInfo>* key_systems_info);
 
 }  // namespace shell
 }  // namespace chromecast
diff --git a/chromeos/BUILD.gn b/chromeos/BUILD.gn
index 5e5c3b8..40823e6 100644
--- a/chromeos/BUILD.gn
+++ b/chromeos/BUILD.gn
@@ -25,6 +25,7 @@
     "//base:prefs",
     "//base/third_party/dynamic_annotations",
     "//chromeos/ime:gencode",
+    "//components/device_event_log",
     "//components/policy/proto",
     "//components/onc",
     "//crypto",
diff --git a/chromeos/CHROMEOS_LKGM b/chromeos/CHROMEOS_LKGM
index 394526c1..11fbb32 100644
--- a/chromeos/CHROMEOS_LKGM
+++ b/chromeos/CHROMEOS_LKGM
@@ -1 +1 @@
-6798.0.0
\ No newline at end of file
+6814.0.0
\ No newline at end of file
diff --git a/chromeos/DEPS b/chromeos/DEPS
index 8bc2f79..ecef880 100644
--- a/chromeos/DEPS
+++ b/chromeos/DEPS
@@ -2,6 +2,7 @@
 # low level Chrome OS system library that may be used by targets that need to
 # be kept as small as possible.
 include_rules = [
+  "+components/device_event_log",
   "+crypto",
   "+net",
   "+policy/proto",
diff --git a/chromeos/chromeos.gyp b/chromeos/chromeos.gyp
index 095c64e..00a41dd 100644
--- a/chromeos/chromeos.gyp
+++ b/chromeos/chromeos.gyp
@@ -260,10 +260,6 @@
       'dbus/update_engine_client.h',
       'dbus/volume_state.cc',
       'dbus/volume_state.h',
-      'device_event_log.cc',
-      'device_event_log.h',
-      'device_event_log_impl.cc',
-      'device_event_log_impl.h',
       'disks/disk_mount_manager.cc',
       'disks/disk_mount_manager.h',
       'geolocation/geoposition.cc',
@@ -463,7 +459,6 @@
       'dbus/shill_profile_client_unittest.cc',
       'dbus/shill_service_client_unittest.cc',
       'dbus/shill_third_party_vpn_driver_client_unittest.cc',
-      'device_event_log_impl_unittest.cc',
       'disks/disk_mount_manager_unittest.cc',
       'geolocation/simple_geolocation_unittest.cc',
       'login/auth/key_unittest.cc',
@@ -516,6 +511,7 @@
         '../build/linux/system.gyp:dbus',
         '../build/linux/system.gyp:ssl',
         '../components/components.gyp:cloud_policy_proto',
+        '../components/components.gyp:device_event_log_component',
         '../components/components.gyp:onc_component',
         '../crypto/crypto.gyp:crypto',
         '../dbus/dbus.gyp:dbus',
diff --git a/chromeos/chromeos_switches.cc b/chromeos/chromeos_switches.cc
index 735b0f5e..9549971 100644
--- a/chromeos/chromeos_switches.cc
+++ b/chromeos/chromeos_switches.cc
@@ -229,6 +229,7 @@
 
 // The memory pressure thresholds selection which is used to decide whether and
 // when a memory pressure event needs to get fired.
+const char kMemoryPressureExperimentName[] = "ChromeOSMemoryPressureHandling";
 const char kMemoryPressureHandlingOff[] = "memory-pressure-off";
 const char kMemoryPressureThresholds[] = "memory-pressure-thresholds";
 const char kConservativeThreshold[] = "conservative";
@@ -326,7 +327,7 @@
 bool MemoryPressureHandlingEnabled() {
   if ((base::CommandLine::ForCurrentProcess()->HasSwitch(
           chromeos::switches::kDisableMemoryPressureSystemChromeOS)) ||
-      (base::FieldTrialList::FindFullName(kMemoryPressureThresholds) ==
+      (base::FieldTrialList::FindFullName(kMemoryPressureExperimentName) ==
        kMemoryPressureHandlingOff))
     return false;
   return true;
@@ -337,7 +338,7 @@
   if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
           kMemoryPressureThresholds)) {
     const std::string group_name =
-        base::FieldTrialList::FindFullName(kMemoryPressureThresholds);
+        base::FieldTrialList::FindFullName(kMemoryPressureExperimentName);
     if (group_name == kConservativeThreshold)
       return base::MemoryPressureObserverChromeOS::THRESHOLD_CONSERVATIVE;
     if (group_name == kAggressiveCacheDiscardThreshold)
diff --git a/chromeos/dbus/dbus_thread_manager.cc b/chromeos/dbus/dbus_thread_manager.cc
index f74886b..03c620a 100644
--- a/chromeos/dbus/dbus_thread_manager.cc
+++ b/chromeos/dbus/dbus_thread_manager.cc
@@ -50,7 +50,6 @@
 #include "chromeos/dbus/sms_client.h"
 #include "chromeos/dbus/system_clock_client.h"
 #include "chromeos/dbus/update_engine_client.h"
-#include "chromeos/device_event_log.h"
 #include "dbus/bus.h"
 #include "dbus/dbus_statistics.h"
 
@@ -61,7 +60,6 @@
 
 DBusThreadManager::DBusThreadManager(scoped_ptr<DBusClientBundle> client_bundle)
     : client_bundle_(client_bundle.Pass()) {
-  device_event_log::Initialize(0 /* default max entries */);
   dbus::statistics::Initialize();
 
   if (client_bundle_->IsUsingAnyRealClient()) {
@@ -95,7 +93,6 @@
     dbus_thread_->Stop();
 
   dbus::statistics::Shutdown();
-  device_event_log::Shutdown();
 
   if (!g_dbus_thread_manager)
     return;  // Called form Shutdown() or local test instance.
diff --git a/chromeos/dbus/power_manager_client.cc b/chromeos/dbus/power_manager_client.cc
index 6840dcf..961ad4c 100644
--- a/chromeos/dbus/power_manager_client.cc
+++ b/chromeos/dbus/power_manager_client.cc
@@ -27,7 +27,7 @@
 #include "chromeos/dbus/power_manager/policy.pb.h"
 #include "chromeos/dbus/power_manager/power_supply_properties.pb.h"
 #include "chromeos/dbus/power_manager/suspend.pb.h"
-#include "chromeos/device_event_log.h"
+#include "components/device_event_log/device_event_log.h"
 #include "dbus/bus.h"
 #include "dbus/message.h"
 #include "dbus/object_path.h"
diff --git a/chromeos/dbus/shill_client_helper.cc b/chromeos/dbus/shill_client_helper.cc
index 6418015..81003553 100644
--- a/chromeos/dbus/shill_client_helper.cc
+++ b/chromeos/dbus/shill_client_helper.cc
@@ -7,7 +7,7 @@
 #include "base/bind.h"
 #include "base/callback_helpers.h"
 #include "base/values.h"
-#include "chromeos/device_event_log.h"
+#include "components/device_event_log/device_event_log.h"
 #include "dbus/message.h"
 #include "dbus/object_proxy.h"
 #include "dbus/values_util.h"
diff --git a/chromeos/dbus/shill_third_party_vpn_driver_client.cc b/chromeos/dbus/shill_third_party_vpn_driver_client.cc
index 3237f50..a1a761c 100644
--- a/chromeos/dbus/shill_third_party_vpn_driver_client.cc
+++ b/chromeos/dbus/shill_third_party_vpn_driver_client.cc
@@ -19,8 +19,8 @@
 const char* kSetParametersKeyList[] = {
     shill::kAddressParameterThirdPartyVpn,
     shill::kBroadcastAddressParameterThirdPartyVpn,
-    shill::kGatewayParameterThirdPartyVpn,
-    shill::kBypassTunnelForIpParameterThirdPartyVpn,
+    shill::kExclusionListParameterThirdPartyVpn,
+    shill::kInclusionListParameterThirdPartyVpn,
     shill::kSubnetPrefixParameterThirdPartyVpn,
     shill::kMtuParameterThirdPartyVpn,
     shill::kDomainSearchParameterThirdPartyVpn,
diff --git a/chromeos/device_event_log.cc b/chromeos/device_event_log.cc
deleted file mode 100644
index aaded58..0000000
--- a/chromeos/device_event_log.cc
+++ /dev/null
@@ -1,110 +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 "chromeos/device_event_log.h"
-
-#include <string>
-
-#include "base/logging.h"
-#include "chromeos/device_event_log_impl.h"
-
-namespace chromeos {
-
-namespace device_event_log {
-
-namespace {
-
-const size_t kDefaultMaxEntries = 4000;
-
-const int kSlowMethodThresholdMs = 10;
-const int kVerySlowMethodThresholdMs = 50;
-
-DeviceEventLogImpl* g_device_event_log = NULL;
-
-}  // namespace
-
-const LogLevel kDefaultLogLevel = LOG_LEVEL_EVENT;
-
-void Initialize(size_t max_entries) {
-  CHECK(!g_device_event_log);
-  if (max_entries == 0)
-    max_entries = kDefaultMaxEntries;
-  g_device_event_log = new DeviceEventLogImpl(max_entries);
-}
-
-void Shutdown() {
-  delete g_device_event_log;
-  g_device_event_log = NULL;
-}
-
-void AddEntry(const char* file,
-              int line,
-              LogType type,
-              LogLevel level,
-              const std::string& event) {
-  if (g_device_event_log) {
-    g_device_event_log->AddEntry(file, line, type, level, event);
-  } else {
-    DeviceEventLogImpl::SendToVLogOrErrorLog(file, line, type, level, event);
-  }
-}
-
-void AddEntryWithDescription(const char* file,
-                             int line,
-                             LogType type,
-                             LogLevel level,
-                             const std::string& event,
-                             const std::string& desc) {
-  std::string event_with_desc = event;
-  if (!desc.empty())
-    event_with_desc += ": " + desc;
-  AddEntry(file, line, type, level, event_with_desc);
-}
-
-std::string GetAsString(StringOrder order,
-                        const std::string& format,
-                        const std::string& types,
-                        LogLevel max_level,
-                        size_t max_events) {
-  if (!g_device_event_log)
-    return "DeviceEventLog not initialized.";
-  return g_device_event_log->GetAsString(order, format, types, max_level,
-                                         max_events);
-}
-
-namespace internal {
-
-DeviceEventLogInstance::DeviceEventLogInstance(const char* file,
-                                               int line,
-                                               device_event_log::LogType type,
-                                               device_event_log::LogLevel level)
-    : file_(file), line_(line), type_(type), level_(level) {
-}
-
-DeviceEventLogInstance::~DeviceEventLogInstance() {
-  device_event_log::AddEntry(file_, line_, type_, level_, stream_.str());
-}
-
-ScopedDeviceLogIfSlow::ScopedDeviceLogIfSlow(LogType type,
-                                             const char* file,
-                                             const std::string& name)
-    : file_(file), type_(type), name_(name) {
-}
-
-ScopedDeviceLogIfSlow::~ScopedDeviceLogIfSlow() {
-  if (timer_.Elapsed().InMilliseconds() >= kSlowMethodThresholdMs) {
-    LogLevel level(LOG_LEVEL_DEBUG);
-    if (timer_.Elapsed().InMilliseconds() >= kVerySlowMethodThresholdMs)
-      level = LOG_LEVEL_ERROR;
-    DEVICE_LOG(type_, level) << "@@@ Slow method: " << file_ << ":" << name_
-                             << ": " << timer_.Elapsed().InMilliseconds()
-                             << "ms";
-  }
-}
-
-}  // namespace internal
-
-}  // namespace device_event_log
-
-}  // namespace chromeos
diff --git a/chromeos/device_event_log.h b/chromeos/device_event_log.h
deleted file mode 100644
index a71f8d6..0000000
--- a/chromeos/device_event_log.h
+++ /dev/null
@@ -1,178 +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 CHROMEOS_DEVICE_EVENT_LOG_H_
-#define CHROMEOS_DEVICE_EVENT_LOG_H_
-
-#include <cstring>
-#include <sstream>
-
-#include "base/basictypes.h"
-#include "base/timer/elapsed_timer.h"
-#include "chromeos/chromeos_export.h"
-
-namespace chromeos {
-
-// These macros can be used to log chromeos device related events.
-// The following values should be used for |level| in these macros:
-//  ERROR Unexpected events, or device level failures. Use sparingly.
-//  USER  Events initiated directly by a user (or Chrome) action.
-//  EVENT Default event type.
-//  DEBUG Debugging details that are usually not interesting.
-// Examples:
-//  NET_LOG(EVENT) << "NetworkState Changed " << name << ": " << state;
-//  POWER_LOG(USER) << "Suspend requested";
-
-#define NET_LOG(level)                                       \
-  DEVICE_LOG(::chromeos::device_event_log::LOG_TYPE_NETWORK, \
-             ::chromeos::device_event_log::LOG_LEVEL_##level)
-#define POWER_LOG(level)                                   \
-  DEVICE_LOG(::chromeos::device_event_log::LOG_TYPE_POWER, \
-             ::chromeos::device_event_log::LOG_LEVEL_##level)
-#define LOGIN_LOG(level)                                   \
-  DEVICE_LOG(::chromeos::device_event_log::LOG_TYPE_LOGIN, \
-             ::chromeos::device_event_log::LOG_LEVEL_##level)
-
-// Generally prefer the above macros unless |type| or  |level| is not constant.
-
-#define DEVICE_LOG(type, level)                                   \
-  ::chromeos::device_event_log::internal::DeviceEventLogInstance( \
-      __FILE__, __LINE__, type, level).stream()
-
-// Declare {Type_LOG_IF_SLOW() at the top of a method to log slow methods
-// where "slow" is defined by kSlowMethodThresholdMs in the .cc file.
-#define SCOPED_NET_LOG_IF_SLOW() \
-  SCOPED_DEVICE_LOG_IF_SLOW(::chromeos::device_event_log::LOG_TYPE_NETWORK)
-
-// Generally prefer the above macros unless |type| is not constant.
-
-#define SCOPED_DEVICE_LOG_IF_SLOW(type)                         \
-  ::chromeos::device_event_log::internal::ScopedDeviceLogIfSlow \
-      scoped_device_log_if_slow(type, __FILE__, __func__)
-
-namespace device_event_log {
-
-// Used to specify the type of event. NOTE: Be sure to update LogTypeFromString
-// and GetLogTypeString when adding entries to this enum. Also consider
-// updating chrome://device-log (see device_log_ui.cc).
-enum LogType {
-  // Shill / network configuration related events.
-  LOG_TYPE_NETWORK,
-  // Power manager related events.
-  LOG_TYPE_POWER,
-  // Login related events.
-  LOG_TYPE_LOGIN,
-  // Used internally
-  LOG_TYPE_UNKNOWN
-};
-
-// Used to specify the detail level for logging. In GetAsString, used to
-// specify the maximum detail level (i.e. EVENT will include USER and ERROR).
-// See top-level comment for guidelines for each type.
-enum LogLevel {
-  LOG_LEVEL_ERROR = 0,
-  LOG_LEVEL_USER = 1,
-  LOG_LEVEL_EVENT = 2,
-  LOG_LEVEL_DEBUG = 3
-};
-
-// Used to specify which order to output event entries in GetAsString.
-enum StringOrder { OLDEST_FIRST, NEWEST_FIRST };
-
-// Initializes / shuts down device event logging. If |max_entries| = 0 the
-// default value will be used.
-CHROMEOS_EXPORT void Initialize(size_t max_entries);
-CHROMEOS_EXPORT void Shutdown();
-
-// If the global instance is initialized, adds an entry to it. Regardless of
-// whether the global instance was intitialzed, this logs the event to
-// LOG(ERROR) if |type| = ERROR or VLOG(1) otherwise.
-CHROMEOS_EXPORT void AddEntry(const char* file,
-                              int line,
-                              LogType type,
-                              LogLevel level,
-                              const std::string& event);
-
-// For backwards compatibility with network_event_log. Combines |event| and
-// |description| and calls AddEntry().
-CHROMEOS_EXPORT void AddEntryWithDescription(const char* file,
-                                             int line,
-                                             LogType type,
-                                             LogLevel level,
-                                             const std::string& event,
-                                             const std::string& description);
-
-// Outputs the log to a formatted string.
-// |order| determines which order to output the events.
-// |format| is a comma-separated string that determines which elements to show.
-//  e.g. "time,desc". Note: order of the strings does not affect the output.
-//  "time" - Include a timestamp.
-//  "file" - Include file and line number.
-//  "type" - Include the event type.
-//  "html" - Include html tags.
-//  "json" - Return JSON format dictionaries containing entries for timestamp,
-//           level, type, file, and event.
-// |types| lists the types included in the output. Prepend "non-" to disclude
-//  a type. e.g. "network,login" or "non-network". Use an empty string for
-//  all types.
-// |max_level| determines the maximum log level to be included in the output.
-// |max_events| limits how many events are output if > 0, otherwise all events
-//  are included.
-CHROMEOS_EXPORT std::string GetAsString(StringOrder order,
-                                        const std::string& format,
-                                        const std::string& types,
-                                        LogLevel max_level,
-                                        size_t max_events);
-
-CHROMEOS_EXPORT extern const LogLevel kDefaultLogLevel;
-
-namespace internal {
-
-// Implementation class for DEVICE_LOG macros. Provides a stream for creating
-// a log string and adds the event using device_event_log::AddEntry on
-// destruction.
-class CHROMEOS_EXPORT DeviceEventLogInstance {
- public:
-  DeviceEventLogInstance(const char* file,
-                         int line,
-                         device_event_log::LogType type,
-                         device_event_log::LogLevel level);
-  ~DeviceEventLogInstance();
-
-  std::ostream& stream() { return stream_; }
-
- private:
-  const char* file_;
-  const int line_;
-  device_event_log::LogType type_;
-  device_event_log::LogLevel level_;
-  std::ostringstream stream_;
-
-  DISALLOW_COPY_AND_ASSIGN(DeviceEventLogInstance);
-};
-
-// Implementation class for SCOPED_LOG_IF_SLOW macros. Tests the elapsed time on
-// destruction and adds a Debug or Error log entry if it exceeds the
-// corresponding expected maximum elapsed time.
-class CHROMEOS_EXPORT ScopedDeviceLogIfSlow {
- public:
-  ScopedDeviceLogIfSlow(LogType type,
-                        const char* file,
-                        const std::string& name);
-  ~ScopedDeviceLogIfSlow();
-
- private:
-  const char* file_;
-  LogType type_;
-  std::string name_;
-  base::ElapsedTimer timer_;
-};
-
-}  // namespace internal
-
-}  // namespace device_event_log
-
-}  // namespace chromeos
-
-#endif  // CHROMEOS_DEVICE_EVENT_LOG_H_
diff --git a/chromeos/device_event_log_impl.cc b/chromeos/device_event_log_impl.cc
deleted file mode 100644
index 8df670b..0000000
--- a/chromeos/device_event_log_impl.cc
+++ /dev/null
@@ -1,403 +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 "chromeos/device_event_log_impl.h"
-
-#include <cmath>
-#include <list>
-#include <set>
-
-#include "base/files/file_path.h"
-#include "base/json/json_string_value_serializer.h"
-#include "base/json/json_writer.h"
-#include "base/logging.h"
-#include "base/memory/scoped_ptr.h"
-#include "base/strings/string_tokenizer.h"
-#include "base/strings/string_util.h"
-#include "base/strings/stringprintf.h"
-#include "base/strings/utf_string_conversions.h"
-#include "base/values.h"
-#include "net/base/escape.h"
-
-namespace chromeos {
-
-namespace device_event_log {
-
-namespace {
-
-const char* kLogLevelName[] = {"Error", "User", "Event", "Debug"};
-
-const char* kLogTypeNetworkDesc = "Network";
-const char* kLogTypePowerDesc = "Power";
-const char* kLogTypeLoginDesc = "Login";
-
-std::string GetLogTypeString(LogType type) {
-  if (type == LOG_TYPE_NETWORK)
-    return kLogTypeNetworkDesc;
-  if (type == LOG_TYPE_POWER)
-    return kLogTypePowerDesc;
-  if (type == LOG_TYPE_LOGIN)
-    return kLogTypeLoginDesc;
-  NOTREACHED();
-  return "Unknown";
-}
-
-std::string DateAndTimeWithMicroseconds(const base::Time& time) {
-  base::Time::Exploded exploded;
-  time.LocalExplode(&exploded);
-  // base::Time::Exploded does not include microseconds, but sometimes we need
-  // microseconds, so append '.' + usecs to the end of the formatted string.
-  int usecs = static_cast<int>(fmod(time.ToDoubleT() * 1000000, 1000000));
-  return base::StringPrintf("%04d/%02d/%02d %02d:%02d:%02d.%06d", exploded.year,
-                            exploded.month, exploded.day_of_month,
-                            exploded.hour, exploded.minute, exploded.second,
-                            usecs);
-}
-
-std::string TimeWithSeconds(const base::Time& time) {
-  base::Time::Exploded exploded;
-  time.LocalExplode(&exploded);
-  return base::StringPrintf("%02d:%02d:%02d", exploded.hour, exploded.minute,
-                            exploded.second);
-}
-
-std::string TimeWithMillieconds(const base::Time& time) {
-  base::Time::Exploded exploded;
-  time.LocalExplode(&exploded);
-  return base::StringPrintf("%02d:%02d:%02d.%03d", exploded.hour,
-                            exploded.minute, exploded.second,
-                            exploded.millisecond);
-}
-
-// Defined below for easier review. TODO(stevenjb): Move implementation here.
-std::string GetHtmlText(LogLevel log_level, const std::string& event);
-
-std::string LogEntryToString(const DeviceEventLogImpl::LogEntry& log_entry,
-                             bool show_time,
-                             bool show_file,
-                             bool show_type,
-                             bool show_level,
-                             bool format_html) {
-  std::string line;
-  if (show_time)
-    line += "[" + TimeWithMillieconds(log_entry.time) + "] ";
-  if (show_type)
-    line += GetLogTypeString(log_entry.log_type) + ": ";
-  if (show_level) {
-    const char* kLevelDesc[] = {"ERROR", "USER", "EVENT", "DEBUG"};
-    line += base::StringPrintf("%s: ", kLevelDesc[log_entry.log_level]);
-  }
-  if (show_file) {
-    std::string filestr =
-        format_html ? net::EscapeForHTML(log_entry.file) : log_entry.file;
-    line += base::StringPrintf("%s:%d ", log_entry.file.c_str(),
-                               log_entry.file_line);
-  }
-  line += format_html ? GetHtmlText(log_entry.log_level, log_entry.event)
-                      : log_entry.event;
-  if (log_entry.count > 1)
-    line += base::StringPrintf(" (%d)", log_entry.count);
-  return line;
-}
-
-void LogEntryToDictionary(const DeviceEventLogImpl::LogEntry& log_entry,
-                          base::DictionaryValue* output) {
-  output->SetString("timestamp", DateAndTimeWithMicroseconds(log_entry.time));
-  output->SetString("timestampshort", TimeWithSeconds(log_entry.time));
-  output->SetString("level", kLogLevelName[log_entry.log_level]);
-  output->SetString("type", GetLogTypeString(log_entry.log_type));
-  output->SetString("file", base::StringPrintf("%s:%d ", log_entry.file.c_str(),
-                                               log_entry.file_line));
-  output->SetString("event", log_entry.event);
-}
-
-std::string LogEntryAsJSON(const DeviceEventLogImpl::LogEntry& log_entry) {
-  base::DictionaryValue entry_dict;
-  LogEntryToDictionary(log_entry, &entry_dict);
-  std::string json;
-  JSONStringValueSerializer serializer(&json);
-  if (!serializer.Serialize(entry_dict)) {
-    LOG(ERROR) << "Failed to serialize to JSON";
-  }
-  return json;
-}
-
-std::string GetHtmlText(LogLevel log_level, const std::string& event) {
-  std::string text;
-  if (log_level == LOG_LEVEL_DEBUG)
-    text += "<i>";
-  else if (log_level == LOG_LEVEL_USER)
-    text += "<b>";
-  else if (log_level == LOG_LEVEL_ERROR)
-    text += "<b><i>";
-
-  text += net::EscapeForHTML(event);
-
-  if (log_level == LOG_LEVEL_DEBUG)
-    text += "</i>";
-  else if (log_level == LOG_LEVEL_USER)
-    text += "</b>";
-  else if (log_level == LOG_LEVEL_ERROR)
-    text += "</i></b>";
-  return text;
-}
-
-void SendLogEntryToVLogOrErrorLog(
-    const DeviceEventLogImpl::LogEntry& log_entry) {
-  if (log_entry.log_level != LOG_LEVEL_ERROR && !VLOG_IS_ON(1))
-    return;
-  const bool show_time = true;
-  const bool show_file = true;
-  const bool show_type = true;
-  const bool show_level = false;
-  const bool format_html = false;
-  std::string output = LogEntryToString(log_entry, show_time, show_file,
-                                        show_type, show_level, format_html);
-  if (log_entry.log_level == LOG_LEVEL_ERROR)
-    LOG(ERROR) << output;
-  else
-    VLOG(1) << output;
-}
-
-bool LogEntryMatches(const DeviceEventLogImpl::LogEntry& first,
-                     const DeviceEventLogImpl::LogEntry& second) {
-  return first.file == second.file && first.file_line == second.file_line &&
-         first.log_level == second.log_level &&
-         first.log_type == second.log_type && first.event == second.event;
-}
-
-bool LogEntryMatchesTypes(const DeviceEventLogImpl::LogEntry& entry,
-                          const std::set<LogType>& include_types,
-                          const std::set<LogType>& exclude_types) {
-  if (include_types.empty() && exclude_types.empty())
-    return true;
-  if (!include_types.empty() && include_types.count(entry.log_type))
-    return true;
-  if (!exclude_types.empty() && !exclude_types.count(entry.log_type))
-    return true;
-  return false;
-}
-
-void GetFormat(const std::string& format_string,
-               bool* show_time,
-               bool* show_file,
-               bool* show_type,
-               bool* show_level,
-               bool* format_html,
-               bool* format_json) {
-  base::StringTokenizer tokens(format_string, ",");
-  *show_time = false;
-  *show_file = false;
-  *show_type = false;
-  *show_level = false;
-  *format_html = false;
-  *format_json = false;
-  while (tokens.GetNext()) {
-    std::string tok(tokens.token());
-    if (tok == "time")
-      *show_time = true;
-    if (tok == "file")
-      *show_file = true;
-    if (tok == "type")
-      *show_type = true;
-    if (tok == "level")
-      *show_level = true;
-    if (tok == "html")
-      *format_html = true;
-    if (tok == "json")
-      *format_json = true;
-  }
-}
-
-LogType LogTypeFromString(const std::string& desc) {
-  std::string desc_lc = base::StringToLowerASCII(desc);
-  if (desc_lc == "network")
-    return LOG_TYPE_NETWORK;
-  if (desc_lc == "power")
-    return LOG_TYPE_POWER;
-  if (desc_lc == "login")
-    return LOG_TYPE_LOGIN;
-  NOTREACHED() << "Unrecogized LogType: " << desc;
-  return LOG_TYPE_UNKNOWN;
-}
-
-void GetLogTypes(const std::string& types,
-                 std::set<LogType>* include_types,
-                 std::set<LogType>* exclude_types) {
-  base::StringTokenizer tokens(types, ",");
-  while (tokens.GetNext()) {
-    std::string tok(tokens.token());
-    if (tok.substr(0, 4) == "non-") {
-      LogType type = LogTypeFromString(tok.substr(4));
-      if (type != LOG_TYPE_UNKNOWN)
-        exclude_types->insert(type);
-    } else {
-      LogType type = LogTypeFromString(tok);
-      if (type != LOG_TYPE_UNKNOWN)
-        include_types->insert(type);
-    }
-  }
-}
-
-}  // namespace
-
-// static
-void DeviceEventLogImpl::SendToVLogOrErrorLog(const char* file,
-                                              int file_line,
-                                              LogType log_type,
-                                              LogLevel log_level,
-                                              const std::string& event) {
-  LogEntry entry(file, file_line, log_type, log_level, event);
-  SendLogEntryToVLogOrErrorLog(entry);
-}
-
-DeviceEventLogImpl::DeviceEventLogImpl(size_t max_entries)
-    : max_entries_(max_entries) {
-}
-
-DeviceEventLogImpl::~DeviceEventLogImpl() {
-}
-
-void DeviceEventLogImpl::AddEntry(const char* file,
-                                  int file_line,
-                                  LogType log_type,
-                                  LogLevel log_level,
-                                  const std::string& event) {
-  LogEntry entry(file, file_line, log_type, log_level, event);
-  AddLogEntry(entry);
-}
-
-void DeviceEventLogImpl::AddLogEntry(const LogEntry& entry) {
-  if (!entries_.empty()) {
-    LogEntry& last = entries_.back();
-    if (LogEntryMatches(last, entry)) {
-      // Update count and time for identical events to avoid log spam.
-      ++last.count;
-      last.log_level = std::min(last.log_level, entry.log_level);
-      last.time = base::Time::Now();
-      return;
-    }
-  }
-  if (entries_.size() >= max_entries_) {
-    const size_t max_error_entries = max_entries_ / 2;
-    // Remove the first (oldest) non-error entry, or the oldest entry if more
-    // than half the entries are errors.
-    size_t error_count = 0;
-    for (LogEntryList::iterator iter = entries_.begin(); iter != entries_.end();
-         ++iter) {
-      if (iter->log_level != LOG_LEVEL_ERROR) {
-        entries_.erase(iter);
-        break;
-      }
-      if (++error_count > max_error_entries) {
-        // Too many error entries, remove the oldest entry.
-        entries_.pop_front();
-        break;
-      }
-    }
-  }
-  entries_.push_back(entry);
-  SendLogEntryToVLogOrErrorLog(entry);
-}
-
-std::string DeviceEventLogImpl::GetAsString(StringOrder order,
-                                            const std::string& format,
-                                            const std::string& types,
-                                            LogLevel max_level,
-                                            size_t max_events) {
-  if (entries_.empty())
-    return "No Log Entries.";
-
-  bool show_time, show_file, show_type, show_level, format_html, format_json;
-  GetFormat(format, &show_time, &show_file, &show_type, &show_level,
-            &format_html, &format_json);
-
-  std::set<LogType> include_types, exclude_types;
-  GetLogTypes(types, &include_types, &exclude_types);
-
-  std::string result;
-  base::ListValue log_entries;
-  if (order == OLDEST_FIRST) {
-    size_t offset = 0;
-    if (max_events > 0 && max_events < entries_.size()) {
-      // Iterate backwards through the list skipping uninteresting entries to
-      // determine the first entry to include.
-      size_t shown_events = 0;
-      size_t num_entries = 0;
-      for (LogEntryList::const_reverse_iterator riter = entries_.rbegin();
-           riter != entries_.rend(); ++riter) {
-        ++num_entries;
-        if (!LogEntryMatchesTypes(*riter, include_types, exclude_types))
-          continue;
-        if (riter->log_level > max_level)
-          continue;
-        if (++shown_events >= max_events)
-          break;
-      }
-      offset = entries_.size() - num_entries;
-    }
-    for (LogEntryList::const_iterator iter = entries_.begin();
-         iter != entries_.end(); ++iter) {
-      if (offset > 0) {
-        --offset;
-        continue;
-      }
-      if (!LogEntryMatchesTypes(*iter, include_types, exclude_types))
-        continue;
-      if (iter->log_level > max_level)
-        continue;
-      if (format_json) {
-        log_entries.AppendString(LogEntryAsJSON(*iter));
-      } else {
-        result += LogEntryToString(*iter, show_time, show_file, show_type,
-                                   show_level, format_html);
-        result += "\n";
-      }
-    }
-  } else {
-    size_t nlines = 0;
-    // Iterate backwards through the list to show the most recent entries first.
-    for (LogEntryList::const_reverse_iterator riter = entries_.rbegin();
-         riter != entries_.rend(); ++riter) {
-      if (!LogEntryMatchesTypes(*riter, include_types, exclude_types))
-        continue;
-      if (riter->log_level > max_level)
-        continue;
-      if (format_json) {
-        log_entries.AppendString(LogEntryAsJSON(*riter));
-      } else {
-        result += LogEntryToString(*riter, show_time, show_file, show_type,
-                                   show_level, format_html);
-        result += "\n";
-      }
-      if (max_events > 0 && ++nlines >= max_events)
-        break;
-    }
-  }
-  if (format_json) {
-    JSONStringValueSerializer serializer(&result);
-    serializer.Serialize(log_entries);
-  }
-
-  return result;
-}
-
-DeviceEventLogImpl::LogEntry::LogEntry(const char* filedesc,
-                                       int file_line,
-                                       LogType log_type,
-                                       LogLevel log_level,
-                                       const std::string& event)
-    : file_line(file_line),
-      log_type(log_type),
-      log_level(log_level),
-      event(event),
-      time(base::Time::Now()),
-      count(1) {
-  if (filedesc)
-    file = base::FilePath(std::string(filedesc)).BaseName().value();
-}
-
-}  // namespace device_event_log
-
-}  // namespace chromeos
diff --git a/chromeos/device_event_log_impl.h b/chromeos/device_event_log_impl.h
deleted file mode 100644
index f18bff5..0000000
--- a/chromeos/device_event_log_impl.h
+++ /dev/null
@@ -1,83 +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 CHROMEOS_DEVICE_EVENT_LOG_IMPL_H_
-#define CHROMEOS_DEVICE_EVENT_LOG_IMPL_H_
-
-#include <list>
-#include <string>
-
-#include "base/time/time.h"
-#include "chromeos/chromeos_export.h"
-#include "chromeos/device_event_log.h"
-
-namespace chromeos {
-
-namespace device_event_log {
-
-class CHROMEOS_EXPORT DeviceEventLogImpl {
- public:
-  struct LogEntry {
-    LogEntry(const char* filedesc,
-             int file_line,
-             LogType log_type,
-             LogLevel log_level,
-             const std::string& event);
-
-    std::string file;
-    int file_line;
-    LogType log_type;
-    LogLevel log_level;
-    std::string event;
-    base::Time time;
-    int count;
-  };
-
-  explicit DeviceEventLogImpl(size_t max_entries);
-  ~DeviceEventLogImpl();
-
-  // Implements device_event_log::AddEntry.
-  void AddEntry(const char* file,
-                int file_line,
-                LogType type,
-                LogLevel level,
-                const std::string& event);
-
-  // Implements device_event_log::GetAsString.
-  std::string GetAsString(StringOrder order,
-                          const std::string& format,
-                          const std::string& types,
-                          LogLevel max_level,
-                          size_t max_events);
-
-  // Called from device_event_log::AddEntry if the global instance has not been
-  // created (or has already been destroyed). Logs to LOG(ERROR) or VLOG(1).
-  static void SendToVLogOrErrorLog(const char* file,
-                                   int file_line,
-                                   LogType type,
-                                   LogLevel log_level,
-                                   const std::string& event);
-
- private:
-  friend class DeviceEventLogTest;
-
-  typedef std::list<LogEntry> LogEntryList;
-
-  void AddLogEntry(const LogEntry& entry);
-
-  // For testing
-  size_t max_entries() const { return max_entries_; }
-  void set_max_entries_for_test(size_t entries) { max_entries_ = entries; }
-
-  size_t max_entries_;
-  LogEntryList entries_;
-
-  DISALLOW_COPY_AND_ASSIGN(DeviceEventLogImpl);
-};
-
-}  // namespace device_event_log
-
-}  // namespace chromeos
-
-#endif  // CHROMEOS_DEVICE_EVENT_LOG_IMPL_H_
diff --git a/chromeos/device_event_log_impl_unittest.cc b/chromeos/device_event_log_impl_unittest.cc
deleted file mode 100644
index 7c9e8c9..0000000
--- a/chromeos/device_event_log_impl_unittest.cc
+++ /dev/null
@@ -1,265 +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 "chromeos/device_event_log_impl.h"
-
-#include <algorithm>
-
-#include "base/basictypes.h"
-#include "base/compiler_specific.h"
-#include "base/format_macros.h"
-#include "base/memory/scoped_ptr.h"
-#include "base/strings/string_split.h"
-#include "base/strings/stringprintf.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace chromeos {
-
-namespace device_event_log {
-
-namespace {
-
-const size_t kDefaultMaxEvents = 100;
-LogLevel kDefaultLevel = LOG_LEVEL_EVENT;
-LogType kDefaultType = LOG_TYPE_NETWORK;
-
-}  // namespace
-
-class DeviceEventLogTest : public testing::Test {
- public:
-  DeviceEventLogTest() {}
-
-  void SetUp() override {
-    impl_.reset(new DeviceEventLogImpl(kDefaultMaxEvents));
-  }
-
-  void TearDown() override { impl_.reset(); }
-
- protected:
-  std::string SkipTime(const std::string& input) {
-    std::string output;
-    std::vector<std::string> lines;
-    base::SplitString(input, '\n', &lines);
-    for (size_t i = 0; i < lines.size(); ++i) {
-      size_t n = lines[i].find(']');
-      if (n != std::string::npos)
-        output += "[time] " + lines[i].substr(n + 2) + '\n';
-      else
-        output += lines[i];
-    }
-    return output;
-  }
-
-  size_t CountLines(const std::string& input) {
-    return std::count(input.begin(), input.end(), '\n');
-  }
-
-  std::string GetLogString(StringOrder order,
-                           const std::string& format,
-                           LogLevel max_level,
-                           size_t max_events) {
-    return impl_->GetAsString(order, format, "", max_level, max_events);
-  }
-
-  std::string GetOrderedString(StringOrder order, size_t max_events) {
-    return impl_->GetAsString(order, "file", "", kDefaultLevel, max_events);
-  }
-
-  std::string GetLogStringForType(const std::string& types) {
-    return impl_->GetAsString(OLDEST_FIRST, "type", types, kDefaultLevel, 0);
-  }
-
-  void AddNetworkEntry(const char* file,
-                       int line,
-                       LogLevel level,
-                       const std::string& event) {
-    impl_->AddEntry(file, line, kDefaultType, level, event);
-  }
-
-  void AddTestEvent(LogLevel level, const std::string& event) {
-    AddNetworkEntry("file", 0, level, event);
-  }
-
-  void AddEventType(LogType type, const std::string& event) {
-    impl_->AddEntry("file", 0, type, kDefaultLevel, event);
-  }
-
-  size_t GetMaxEntries() const { return impl_->max_entries(); }
-
-  scoped_ptr<DeviceEventLogImpl> impl_;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(DeviceEventLogTest);
-};
-
-TEST_F(DeviceEventLogTest, TestNetworkEvents) {
-  std::string output_none = GetOrderedString(OLDEST_FIRST, 0);
-  EXPECT_EQ("No Log Entries.", output_none);
-
-  LogLevel level = kDefaultLevel;
-  AddNetworkEntry("file1", 1, level, "event1");
-  AddNetworkEntry("file2", 2, level, "event2");
-  AddNetworkEntry("file3", 3, level, "event3");
-  AddNetworkEntry("file3", 3, level, "event3");
-
-  const std::string expected_output_oldest_first(
-      "file1:1 event1\n"
-      "file2:2 event2\n"
-      "file3:3 event3 (2)\n");
-  std::string output_oldest_first = GetOrderedString(OLDEST_FIRST, 0);
-  EXPECT_EQ(expected_output_oldest_first, output_oldest_first);
-
-  const std::string expected_output_oldest_first_short(
-      "file2:2 event2\n"
-      "file3:3 event3 (2)\n");
-  std::string output_oldest_first_short = GetOrderedString(OLDEST_FIRST, 2);
-  EXPECT_EQ(expected_output_oldest_first_short, output_oldest_first_short);
-
-  const std::string expected_output_newest_first(
-      "file3:3 event3 (2)\n"
-      "file2:2 event2\n"
-      "file1:1 event1\n");
-  std::string output_newest_first = GetOrderedString(NEWEST_FIRST, 0);
-  EXPECT_EQ(expected_output_newest_first, output_newest_first);
-
-  const std::string expected_output_newest_first_short(
-      "file3:3 event3 (2)\n"
-      "file2:2 event2\n");
-  std::string output_newest_first_short = GetOrderedString(NEWEST_FIRST, 2);
-  EXPECT_EQ(expected_output_newest_first_short, output_newest_first_short);
-}
-
-TEST_F(DeviceEventLogTest, TestMaxEntries) {
-  const size_t max_entries = GetMaxEntries();
-  const size_t entries_to_add = max_entries + 3;
-  for (size_t i = 0; i < entries_to_add; ++i) {
-    AddTestEvent(LOG_LEVEL_EVENT, base::StringPrintf("event_%" PRIuS, i));
-  }
-  std::string output = GetOrderedString(OLDEST_FIRST, 0);
-  size_t output_lines = CountLines(output);
-  EXPECT_EQ(max_entries, output_lines);
-}
-
-TEST_F(DeviceEventLogTest, TestStringFormat) {
-  AddTestEvent(LOG_LEVEL_ERROR, "event0");
-  EXPECT_EQ("file:0 event0\n",
-            GetLogString(OLDEST_FIRST, "file", kDefaultLevel, 1));
-  EXPECT_EQ("[time] event0\n",
-            SkipTime(GetLogString(OLDEST_FIRST, "time", kDefaultLevel, 1)));
-  EXPECT_EQ("event0\n", GetLogString(OLDEST_FIRST, "", kDefaultLevel, 1));
-  EXPECT_EQ("<b><i>event0</i></b>\n",
-            GetLogString(OLDEST_FIRST, "html", kDefaultLevel, 1));
-  EXPECT_EQ(
-      "[time] file:0 event0\n",
-      SkipTime(GetLogString(OLDEST_FIRST, "file,time", kDefaultLevel, 1)));
-
-  AddTestEvent(LOG_LEVEL_DEBUG, "event1");
-  EXPECT_EQ("[time] file:0 <i>event1</i>\n",
-            SkipTime(GetLogString(OLDEST_FIRST, "file,time,html",
-                                  LOG_LEVEL_DEBUG, 1)));
-
-  AddTestEvent(kDefaultLevel, "event2");
-  EXPECT_EQ("Network: file:0 event2\n",
-            GetLogString(OLDEST_FIRST, "file,type", kDefaultLevel, 1));
-}
-
-TEST_F(DeviceEventLogTest, TestLogLevel) {
-  AddTestEvent(LOG_LEVEL_ERROR, "error1");
-  AddTestEvent(LOG_LEVEL_ERROR, "error2");
-  AddTestEvent(LOG_LEVEL_EVENT, "event3");
-  AddTestEvent(LOG_LEVEL_ERROR, "error4");
-  AddTestEvent(LOG_LEVEL_EVENT, "event5");
-  AddTestEvent(LOG_LEVEL_DEBUG, "debug6");
-
-  std::string out;
-  out = GetLogString(OLDEST_FIRST, "", LOG_LEVEL_ERROR, 0);
-  EXPECT_EQ(3u, CountLines(out));
-  out = GetLogString(OLDEST_FIRST, "", LOG_LEVEL_EVENT, 0);
-  EXPECT_EQ(5u, CountLines(out));
-  out = GetLogString(OLDEST_FIRST, "", LOG_LEVEL_DEBUG, 0);
-  EXPECT_EQ(6u, CountLines(out));
-
-  // Test max_level. Get only the ERROR entries.
-  out = GetLogString(OLDEST_FIRST, "", LOG_LEVEL_ERROR, 0);
-  EXPECT_EQ("error1\nerror2\nerror4\n", out);
-}
-
-TEST_F(DeviceEventLogTest, TestMaxEvents) {
-  AddTestEvent(LOG_LEVEL_EVENT, "event1");
-  AddTestEvent(LOG_LEVEL_ERROR, "error2");
-  AddTestEvent(LOG_LEVEL_EVENT, "event3");
-  AddTestEvent(LOG_LEVEL_ERROR, "error4");
-  AddTestEvent(LOG_LEVEL_EVENT, "event5");
-
-  // Oldest first
-  EXPECT_EQ("error4\n", GetLogString(OLDEST_FIRST, "", LOG_LEVEL_ERROR, 1));
-
-  EXPECT_EQ("error2\nerror4\n",
-            GetLogString(OLDEST_FIRST, "", LOG_LEVEL_ERROR, 2));
-
-  EXPECT_EQ("event3\nerror4\nevent5\n",
-            GetLogString(OLDEST_FIRST, "", LOG_LEVEL_EVENT, 3));
-
-  EXPECT_EQ("error2\nevent3\nerror4\nevent5\n",
-            GetLogString(OLDEST_FIRST, "", LOG_LEVEL_EVENT, 4));
-
-  EXPECT_EQ("event1\nerror2\nevent3\nerror4\nevent5\n",
-            GetLogString(OLDEST_FIRST, "", LOG_LEVEL_EVENT, 5));
-
-  // Newest first
-  EXPECT_EQ("error4\n", GetLogString(NEWEST_FIRST, "", LOG_LEVEL_ERROR, 1));
-
-  EXPECT_EQ("error4\nerror2\n",
-            GetLogString(NEWEST_FIRST, "", LOG_LEVEL_ERROR, 2));
-
-  EXPECT_EQ("event5\nerror4\nevent3\n",
-            GetLogString(NEWEST_FIRST, "", LOG_LEVEL_EVENT, 3));
-
-  EXPECT_EQ("event5\nerror4\nevent3\nerror2\n",
-            GetLogString(NEWEST_FIRST, "", LOG_LEVEL_EVENT, 4));
-
-  EXPECT_EQ("event5\nerror4\nevent3\nerror2\nevent1\n",
-            GetLogString(NEWEST_FIRST, "", LOG_LEVEL_EVENT, 5));
-}
-
-TEST_F(DeviceEventLogTest, TestMaxErrors) {
-  const int kMaxTestEntries = 4;
-  impl_.reset(new DeviceEventLogImpl(kMaxTestEntries));
-  AddTestEvent(LOG_LEVEL_EVENT, "event1");
-  AddTestEvent(LOG_LEVEL_ERROR, "error2");
-  AddTestEvent(LOG_LEVEL_EVENT, "event3");
-  AddTestEvent(LOG_LEVEL_ERROR, "error4");
-  AddTestEvent(LOG_LEVEL_EVENT, "event5");
-  AddTestEvent(LOG_LEVEL_EVENT, "event6");
-  EXPECT_EQ("error2\nerror4\nevent5\nevent6\n",
-            GetLogString(OLDEST_FIRST, "", LOG_LEVEL_DEBUG, 0));
-}
-
-TEST_F(DeviceEventLogTest, TestType) {
-  AddEventType(LOG_TYPE_NETWORK, "event1");
-  AddEventType(LOG_TYPE_POWER, "event2");
-  AddEventType(LOG_TYPE_NETWORK, "event3");
-  AddEventType(LOG_TYPE_POWER, "event4");
-  AddEventType(LOG_TYPE_NETWORK, "event5");
-  AddEventType(LOG_TYPE_NETWORK, "event6");
-  EXPECT_EQ(
-      "Network: event1\nNetwork: event3\nNetwork: event5\nNetwork: event6\n",
-      GetLogStringForType("network"));
-  const std::string power_events("Power: event2\nPower: event4\n");
-  EXPECT_EQ(power_events, GetLogStringForType("power"));
-  EXPECT_EQ(power_events, GetLogStringForType("non-network"));
-  const std::string all_events(
-      "Network: event1\n"
-      "Power: event2\n"
-      "Network: event3\n"
-      "Power: event4\n"
-      "Network: event5\n"
-      "Network: event6\n");
-  EXPECT_EQ(all_events, GetLogStringForType("network,power"));
-  EXPECT_EQ(all_events, GetLogStringForType(""));
-}
-
-}  // namespace device_event_log
-
-}  // namespace chromeos
diff --git a/chromeos/login/auth/auth_attempt_state.h b/chromeos/login/auth/auth_attempt_state.h
index 04b0cbb..b99ce3601 100644
--- a/chromeos/login/auth/auth_attempt_state.h
+++ b/chromeos/login/auth/auth_attempt_state.h
@@ -7,6 +7,7 @@
 
 #include <string>
 
+#include "base/memory/weak_ptr.h"
 #include "chromeos/chromeos_export.h"
 #include "chromeos/login/auth/auth_status_consumer.h"
 #include "chromeos/login/auth/user_context.h"
@@ -19,7 +20,8 @@
 
 // Tracks the state associated with a single attempt to log in to chromium OS.
 // Enforces that methods are only called on the UI thread.
-class CHROMEOS_EXPORT AuthAttemptState {
+class CHROMEOS_EXPORT AuthAttemptState
+    : public base::SupportsWeakPtr<AuthAttemptState> {
  public:
   // Used to initialize for a login attempt.
   AuthAttemptState(const UserContext& user_context,
diff --git a/chromeos/login/auth/cryptohome_authenticator.cc b/chromeos/login/auth/cryptohome_authenticator.cc
index c9ca100..9a65e58 100644
--- a/chromeos/login/auth/cryptohome_authenticator.cc
+++ b/chromeos/login/auth/cryptohome_authenticator.cc
@@ -11,6 +11,7 @@
 #include "base/files/file_path.h"
 #include "base/location.h"
 #include "base/logging.h"
+#include "base/memory/weak_ptr.h"
 #include "chromeos/cryptohome/async_method_caller.h"
 #include "chromeos/cryptohome/cryptohome_parameters.h"
 #include "chromeos/cryptohome/homedir_methods.h"
@@ -53,7 +54,7 @@
 }
 
 // Records status and calls resolver->Resolve().
-void TriggerResolve(AuthAttemptState* attempt,
+void TriggerResolve(const base::WeakPtr<AuthAttemptState>& attempt,
                     scoped_refptr<CryptohomeAuthenticator> resolver,
                     bool success,
                     cryptohome::MountError return_code) {
@@ -62,7 +63,7 @@
 }
 
 // Records get hash status and calls resolver->Resolve().
-void TriggerResolveHash(AuthAttemptState* attempt,
+void TriggerResolveHash(const base::WeakPtr<AuthAttemptState>& attempt,
                         scoped_refptr<CryptohomeAuthenticator> resolver,
                         bool success,
                         const std::string& username_hash) {
@@ -76,7 +77,7 @@
 // Calls TriggerResolve while adding login time marker.
 void TriggerResolveWithLoginTimeMarker(
     const std::string& marker_name,
-    AuthAttemptState* attempt,
+    const base::WeakPtr<AuthAttemptState>& attempt,
     scoped_refptr<CryptohomeAuthenticator> resolver,
     bool success,
     cryptohome::MountError return_code) {
@@ -86,7 +87,7 @@
 
 // Records an error in accessing the user's cryptohome with the given key and
 // calls resolver->Resolve() after adding a login time marker.
-void RecordKeyErrorAndResolve(AuthAttemptState* attempt,
+void RecordKeyErrorAndResolve(const base::WeakPtr<AuthAttemptState>& attempt,
                               scoped_refptr<CryptohomeAuthenticator> resolver) {
   chromeos::LoginEventRecorder::Get()->AddLoginTimeMarker("CryptohomeMount-End",
                                                           false);
@@ -96,7 +97,7 @@
 }
 
 // Callback invoked when cryptohome's MountEx() method has finished.
-void OnMount(AuthAttemptState* attempt,
+void OnMount(const base::WeakPtr<AuthAttemptState>& attempt,
              scoped_refptr<CryptohomeAuthenticator> resolver,
              bool success,
              cryptohome::MountError return_code,
@@ -115,7 +116,7 @@
 // not be a plain text password. If the user provided a plain text password,
 // that password must be transformed to another key type (by salted hashing)
 // before calling this method.
-void DoMount(AuthAttemptState* attempt,
+void DoMount(const base::WeakPtr<AuthAttemptState>& attempt,
              scoped_refptr<CryptohomeAuthenticator> resolver,
              bool ephemeral,
              bool create_if_nonexistent) {
@@ -154,11 +155,11 @@
 // Callback invoked when the system salt has been retrieved. Transforms the key
 // in |attempt->user_context| using Chrome's default hashing algorithm and the
 // system salt, then calls MountEx().
-void OnGetSystemSalt(AuthAttemptState* attempt,
-                    scoped_refptr<CryptohomeAuthenticator> resolver,
-                    bool ephemeral,
-                    bool create_if_nonexistent,
-                    const std::string& system_salt) {
+void OnGetSystemSalt(const base::WeakPtr<AuthAttemptState>& attempt,
+                     scoped_refptr<CryptohomeAuthenticator> resolver,
+                     bool ephemeral,
+                     bool create_if_nonexistent,
+                     const std::string& system_salt) {
   DCHECK_EQ(Key::KEY_TYPE_PASSWORD_PLAIN,
             attempt->user_context.GetKey()->GetKeyType());
 
@@ -178,7 +179,7 @@
 //   algorithm and the system salt.
 // The resulting key is then passed to cryptohome's MountEx().
 void OnGetKeyDataEx(
-    AuthAttemptState* attempt,
+    const base::WeakPtr<AuthAttemptState>& attempt,
     scoped_refptr<CryptohomeAuthenticator> resolver,
     bool ephemeral,
     bool create_if_nonexistent,
@@ -249,7 +250,7 @@
 //   called to retrieve metadata indicating the hashing algorithm and salt that
 //   were used to generate the key for this user's cryptohome and the key is
 //   transformed accordingly before calling MountEx().
-void StartMount(AuthAttemptState* attempt,
+void StartMount(const base::WeakPtr<AuthAttemptState>& attempt,
                 scoped_refptr<CryptohomeAuthenticator> resolver,
                 bool ephemeral,
                 bool create_if_nonexistent) {
@@ -274,7 +275,7 @@
 
 // Calls cryptohome's mount method for guest and also get the user hash from
 // cryptohome.
-void MountGuestAndGetHash(AuthAttemptState* attempt,
+void MountGuestAndGetHash(const base::WeakPtr<AuthAttemptState>& attempt,
                           scoped_refptr<CryptohomeAuthenticator> resolver) {
   attempt->UsernameHashRequested();
   cryptohome::AsyncMethodCaller::GetInstance()->AsyncMountGuest(
@@ -288,7 +289,7 @@
 }
 
 // Calls cryptohome's MountPublic method
-void MountPublic(AuthAttemptState* attempt,
+void MountPublic(const base::WeakPtr<AuthAttemptState>& attempt,
                  scoped_refptr<CryptohomeAuthenticator> resolver,
                  int flags) {
   cryptohome::AsyncMethodCaller::GetInstance()->AsyncMountPublic(
@@ -304,7 +305,7 @@
 }
 
 // Calls cryptohome's key migration method.
-void Migrate(AuthAttemptState* attempt,
+void Migrate(const base::WeakPtr<AuthAttemptState>& attempt,
              scoped_refptr<CryptohomeAuthenticator> resolver,
              bool passing_old_hash,
              const std::string& old_password,
@@ -340,7 +341,7 @@
 }
 
 // Calls cryptohome's remove method.
-void Remove(AuthAttemptState* attempt,
+void Remove(const base::WeakPtr<AuthAttemptState>& attempt,
             scoped_refptr<CryptohomeAuthenticator> resolver) {
   chromeos::LoginEventRecorder::Get()->AddLoginTimeMarker(
       "CryptohomeRemove-Start", false);
@@ -353,7 +354,7 @@
 }
 
 // Calls cryptohome's key check method.
-void CheckKey(AuthAttemptState* attempt,
+void CheckKey(const base::WeakPtr<AuthAttemptState>& attempt,
               scoped_refptr<CryptohomeAuthenticator> resolver,
               const std::string& system_salt) {
   scoped_ptr<Key> key =
@@ -395,10 +396,9 @@
   // Reset the verified flag.
   owner_is_verified_ = false;
 
-  StartMount(current_state_.get(),
+  StartMount(current_state_->AsWeakPtr(),
              scoped_refptr<CryptohomeAuthenticator>(this),
-             false /* ephemeral */,
-             false /* create_if_nonexistent */);
+             false /* ephemeral */, false /* create_if_nonexistent */);
 }
 
 void CryptohomeAuthenticator::CompleteLogin(content::BrowserContext* context,
@@ -413,10 +413,9 @@
   // Reset the verified flag.
   owner_is_verified_ = false;
 
-  StartMount(current_state_.get(),
+  StartMount(current_state_->AsWeakPtr(),
              scoped_refptr<CryptohomeAuthenticator>(this),
-             false /* ephemeral */,
-             false /* create_if_nonexistent */);
+             false /* ephemeral */, false /* create_if_nonexistent */);
 
   // For login completion from extension, we just need to resolve the current
   // auth attempt state, the rest of OAuth related tasks will be done in
@@ -436,8 +435,7 @@
   remove_user_data_on_failure_ = false;
   check_key_attempted_ = true;
   SystemSaltGetter::Get()->GetSystemSalt(
-      base::Bind(&CheckKey,
-                 current_state_.get(),
+      base::Bind(&CheckKey, current_state_->AsWeakPtr(),
                  scoped_refptr<CryptohomeAuthenticator>(this)));
 }
 
@@ -451,10 +449,9 @@
                                             false,    // online_complete
                                             false));  // user_is_new
   remove_user_data_on_failure_ = false;
-  StartMount(current_state_.get(),
+  StartMount(current_state_->AsWeakPtr(),
              scoped_refptr<CryptohomeAuthenticator>(this),
-             false /* ephemeral */,
-             false /* create_if_nonexistent */);
+             false /* ephemeral */, false /* create_if_nonexistent */);
 }
 
 void CryptohomeAuthenticator::LoginOffTheRecord() {
@@ -467,7 +464,7 @@
                            false));  // user_is_new
   remove_user_data_on_failure_ = false;
   ephemeral_mount_attempted_ = true;
-  MountGuestAndGetHash(current_state_.get(),
+  MountGuestAndGetHash(current_state_->AsWeakPtr(),
                        scoped_refptr<CryptohomeAuthenticator>(this));
 }
 
@@ -482,9 +479,8 @@
                            false));  // user_is_new
   remove_user_data_on_failure_ = false;
   ephemeral_mount_attempted_ = true;
-  StartMount(current_state_.get(),
-             scoped_refptr<CryptohomeAuthenticator>(this),
-             true /* ephemeral */,
+  StartMount(current_state_->AsWeakPtr(),
+             scoped_refptr<CryptohomeAuthenticator>(this), true /* ephemeral */,
              true /* create_if_nonexistent */);
 }
 
@@ -503,12 +499,12 @@
 
   remove_user_data_on_failure_ = true;
   if (!use_guest_mount) {
-    MountPublic(current_state_.get(),
+    MountPublic(current_state_->AsWeakPtr(),
                 scoped_refptr<CryptohomeAuthenticator>(this),
                 cryptohome::CREATE_IF_MISSING);
   } else {
     ephemeral_mount_attempted_ = true;
-    MountGuestAndGetHash(current_state_.get(),
+    MountGuestAndGetHash(current_state_->AsWeakPtr(),
                          scoped_refptr<CryptohomeAuthenticator>(this));
   }
 }
@@ -559,32 +555,25 @@
     const std::string& old_password) {
   migrate_attempted_ = true;
   current_state_->ResetCryptohomeStatus();
-  SystemSaltGetter::Get()->GetSystemSalt(
-      base::Bind(&Migrate,
-                 current_state_.get(),
-                 scoped_refptr<CryptohomeAuthenticator>(this),
-                 true,
-                 old_password));
+  SystemSaltGetter::Get()->GetSystemSalt(base::Bind(
+      &Migrate, current_state_->AsWeakPtr(),
+      scoped_refptr<CryptohomeAuthenticator>(this), true, old_password));
 }
 
 void CryptohomeAuthenticator::RemoveEncryptedData() {
   remove_attempted_ = true;
   current_state_->ResetCryptohomeStatus();
   task_runner_->PostTask(
-      FROM_HERE,
-      base::Bind(&Remove,
-                 current_state_.get(),
-                 scoped_refptr<CryptohomeAuthenticator>(this)));
+      FROM_HERE, base::Bind(&Remove, current_state_->AsWeakPtr(),
+                            scoped_refptr<CryptohomeAuthenticator>(this)));
 }
 
 void CryptohomeAuthenticator::ResyncEncryptedData() {
   resync_attempted_ = true;
   current_state_->ResetCryptohomeStatus();
   task_runner_->PostTask(
-      FROM_HERE,
-      base::Bind(&Remove,
-                 current_state_.get(),
-                 scoped_refptr<CryptohomeAuthenticator>(this)));
+      FROM_HERE, base::Bind(&Remove, current_state_->AsWeakPtr(),
+                            scoped_refptr<CryptohomeAuthenticator>(this)));
 }
 
 bool CryptohomeAuthenticator::VerifyOwner() {
@@ -680,10 +669,9 @@
       create_if_nonexistent = true;
     case RECOVER_MOUNT:
       current_state_->ResetCryptohomeStatus();
-      StartMount(current_state_.get(),
+      StartMount(current_state_->AsWeakPtr(),
                  scoped_refptr<CryptohomeAuthenticator>(this),
-                 false /*ephemeral*/,
-                 create_if_nonexistent);
+                 false /*ephemeral*/, create_if_nonexistent);
       break;
     case NEED_OLD_PW:
       task_runner_->PostTask(
diff --git a/chromeos/login/login_state.cc b/chromeos/login/login_state.cc
index ae8403c..07a7f6b 100644
--- a/chromeos/login/login_state.cc
+++ b/chromeos/login/login_state.cc
@@ -8,7 +8,7 @@
 #include "base/logging.h"
 #include "base/sys_info.h"
 #include "chromeos/chromeos_switches.h"
-#include "chromeos/device_event_log.h"
+#include "components/device_event_log/device_event_log.h"
 
 namespace chromeos {
 
diff --git a/chromeos/network/managed_network_configuration_handler_impl.cc b/chromeos/network/managed_network_configuration_handler_impl.cc
index be67c522..424e9d2 100644
--- a/chromeos/network/managed_network_configuration_handler_impl.cc
+++ b/chromeos/network/managed_network_configuration_handler_impl.cc
@@ -51,8 +51,7 @@
 const char kNetworkAlreadyConfigured[] = "NetworkAlreadyConfigured";
 const char kPoliciesNotInitialized[] = "PoliciesNotInitialized";
 const char kProfileNotInitialized[] = "ProfileNotInitialized";
-const char kSetOnUnconfiguredNetwork[] = "SetCalledOnUnconfiguredNetwork";
-const char kUnknownProfilePath[] = "UnknownProfilePath";
+const char kUnconfiguredNetwork[] = "UnconfiguredNetwork";
 const char kUnknownNetwork[] = "UnknownNetwork";
 
 std::string ToDebugString(::onc::ONCSource source,
@@ -120,16 +119,14 @@
     InvokeErrorCallback(service_path, error_callback, kPoliciesNotInitialized);
     return;
   }
-  network_configuration_handler_->GetProperties(
+  network_configuration_handler_->GetShillProperties(
       service_path,
       base::Bind(
           &ManagedNetworkConfigurationHandlerImpl::GetPropertiesCallback,
           weak_ptr_factory_.GetWeakPtr(),
           base::Bind(
               &ManagedNetworkConfigurationHandlerImpl::SendManagedProperties,
-              weak_ptr_factory_.GetWeakPtr(),
-              userhash,
-              callback,
+              weak_ptr_factory_.GetWeakPtr(), userhash, callback,
               error_callback)),
       error_callback);
 }
@@ -200,15 +197,13 @@
     const std::string& service_path,
     const network_handler::DictionaryResultCallback& callback,
     const network_handler::ErrorCallback& error_callback) {
-  network_configuration_handler_->GetProperties(
+  network_configuration_handler_->GetShillProperties(
       service_path,
       base::Bind(
           &ManagedNetworkConfigurationHandlerImpl::GetPropertiesCallback,
           weak_ptr_factory_.GetWeakPtr(),
           base::Bind(&ManagedNetworkConfigurationHandlerImpl::SendProperties,
-                     weak_ptr_factory_.GetWeakPtr(),
-                     callback,
-                     error_callback)),
+                     weak_ptr_factory_.GetWeakPtr(), callback, error_callback)),
       error_callback);
 }
 
@@ -240,20 +235,16 @@
   }
 
   std::string guid = state->guid();
-  if (guid.empty()) {
-    // TODO(pneubeck): create an initial configuration in this case. As for
-    // CreateConfiguration, user settings from older ChromeOS versions have to
-    // determined here.
-    InvokeErrorCallback(
-        service_path, error_callback, kSetOnUnconfiguredNetwork);
-    return;
-  }
+  DCHECK(!guid.empty());
 
   const std::string& profile_path = state->profile_path();
   const NetworkProfile *profile =
       network_profile_handler_->GetProfileForPath(profile_path);
   if (!profile) {
-    InvokeErrorCallback(service_path, error_callback, kUnknownProfilePath);
+    // TODO(pneubeck): create an initial configuration in this case. As for
+    // CreateConfiguration, user settings from older ChromeOS versions have to
+    // be determined here.
+    InvokeErrorCallback(service_path, error_callback, kUnconfiguredNetwork);
     return;
   }
 
@@ -313,7 +304,7 @@
                                             network_policy,
                                             validated_user_settings.get()));
 
-  network_configuration_handler_->SetProperties(
+  network_configuration_handler_->SetShillProperties(
       service_path, *shill_dictionary,
       NetworkConfigurationObserver::SOURCE_USER_ACTION, callback,
       error_callback);
@@ -357,7 +348,7 @@
                                             NULL,  // no network policy
                                             &properties));
 
-  network_configuration_handler_->CreateConfiguration(
+  network_configuration_handler_->CreateShillConfiguration(
       *shill_dictionary, NetworkConfigurationObserver::SOURCE_USER_ACTION,
       callback, error_callback);
 }
@@ -503,7 +494,7 @@
 
 void ManagedNetworkConfigurationHandlerImpl::CreateConfigurationFromPolicy(
     const base::DictionaryValue& shill_properties) {
-  network_configuration_handler_->CreateConfiguration(
+  network_configuration_handler_->CreateShillConfiguration(
       shill_properties, NetworkConfigurationObserver::SOURCE_POLICY,
       base::Bind(
           &ManagedNetworkConfigurationHandlerImpl::OnPolicyAppliedToNetwork,
@@ -540,7 +531,7 @@
 
   shill_properties.MergeDictionary(&new_properties);
 
-  network_configuration_handler_->CreateConfiguration(
+  network_configuration_handler_->CreateShillConfiguration(
       shill_properties, NetworkConfigurationObserver::SOURCE_POLICY,
       base::Bind(
           &ManagedNetworkConfigurationHandlerImpl::OnPolicyAppliedToNetwork,
diff --git a/chromeos/network/managed_network_configuration_handler_unittest.cc b/chromeos/network/managed_network_configuration_handler_unittest.cc
index 85db89d..4d965eb 100644
--- a/chromeos/network/managed_network_configuration_handler_unittest.cc
+++ b/chromeos/network/managed_network_configuration_handler_unittest.cc
@@ -64,10 +64,26 @@
 MATCHER_P(IsEqualTo,
           value,
           std::string(negation ? "isn't" : "is") + " equal to " +
-          ValueToString(value)) {
+              ValueToString(value)) {
   return value->Equals(&arg);
 }
 
+// Match properties in |value| to |arg|. |arg| may contain extra properties).
+MATCHER_P(MatchesProperties,
+          value,
+          std::string(negation ? "does't match " : "matches ") +
+              ValueToString(value)) {
+  for (base::DictionaryValue::Iterator iter(*value); !iter.IsAtEnd();
+       iter.Advance()) {
+    const base::Value* property;
+    if (!arg.GetWithoutPathExpansion(iter.key(), &property) ||
+        !iter.value().Equals(property)) {
+      return false;
+    }
+  }
+  return true;
+}
+
 class ShillProfileTestClient {
  public:
   typedef ShillClientHelper::DictionaryValueCallbackWithoutStatus
@@ -865,8 +881,7 @@
   EXPECT_CALL(*mock_manager_client_,
               ConfigureServiceForProfile(
                   dbus::ObjectPath(kUser1ProfilePath),
-                  IsEqualTo(expected_shill_properties.get()),
-                  _, _));
+                  MatchesProperties(expected_shill_properties.get()), _, _));
 
   SetPolicy(::onc::ONC_SOURCE_USER_POLICY,
             kUser1,
diff --git a/chromeos/network/network_configuration_handler.cc b/chromeos/network/network_configuration_handler.cc
index 841398c3..3aa9960 100644
--- a/chromeos/network/network_configuration_handler.cc
+++ b/chromeos/network/network_configuration_handler.cc
@@ -9,6 +9,7 @@
 
 #include "base/bind.h"
 #include "base/format_macros.h"
+#include "base/guid.h"
 #include "base/json/json_writer.h"
 #include "base/logging.h"
 #include "base/memory/ref_counted.h"
@@ -21,10 +22,10 @@
 #include "chromeos/dbus/shill_profile_client.h"
 #include "chromeos/dbus/shill_service_client.h"
 #include "chromeos/network/network_device_handler.h"
-#include "chromeos/network/network_event_log.h"
 #include "chromeos/network/network_state.h"
 #include "chromeos/network/network_state_handler.h"
 #include "chromeos/network/shill_property_util.h"
+#include "components/device_event_log/device_event_log.h"
 #include "dbus/object_path.h"
 #include "third_party/cros_system_api/dbus/service_constants.h"
 
@@ -35,8 +36,8 @@
 // Strip surrounding "" from keys (if present).
 std::string StripQuotations(const std::string& in_str) {
   size_t len = in_str.length();
-  if (len >= 2 && in_str[0] == '"' && in_str[len-1] == '"')
-    return in_str.substr(1, len-2);
+  if (len >= 2 && in_str[0] == '"' && in_str[len - 1] == '"')
+    return in_str.substr(1, len - 2);
   return in_str;
 }
 
@@ -44,36 +45,9 @@
                          const network_handler::ErrorCallback& error_callback,
                          const std::string& error_name) {
   std::string error_msg = "Config Error: " + error_name;
-  NET_LOG_ERROR(error_msg, service_path);
-  network_handler::RunErrorCallback(
-      error_callback, service_path, error_name, error_msg);
-}
-
-void GetPropertiesCallback(
-    const network_handler::DictionaryResultCallback& callback,
-    const network_handler::ErrorCallback& error_callback,
-    const std::string& service_path,
-    DBusMethodCallStatus call_status,
-    const base::DictionaryValue& properties) {
-  if (call_status != DBUS_METHOD_CALL_SUCCESS) {
-    // Because network services are added and removed frequently, we will see
-    // failures regularly, so don't log these.
-    network_handler::RunErrorCallback(error_callback,
-                                      service_path,
-                                      network_handler::kDBusFailedError,
-                                      network_handler::kDBusFailedErrorMessage);
-    return;
-  }
-  if (callback.is_null())
-    return;
-
-  // Get the correct name from WifiHex if necessary.
-  scoped_ptr<base::DictionaryValue> properties_copy(properties.DeepCopy());
-  std::string name =
-      shill_property_util::GetNameFromProperties(service_path, properties);
-  if (!name.empty())
-    properties_copy->SetStringWithoutPathExpansion(shill::kNameProperty, name);
-  callback.Run(service_path, *properties_copy.get());
+  NET_LOG(ERROR) << error_msg << ": " << service_path;
+  network_handler::RunErrorCallback(error_callback, service_path, error_name,
+                                    error_msg);
 }
 
 void SetNetworkProfileErrorCallback(
@@ -83,20 +57,19 @@
     const std::string& dbus_error_name,
     const std::string& dbus_error_message) {
   network_handler::ShillErrorCallbackFunction(
-      "Config.SetNetworkProfile Failed: " + profile_path,
-      service_path, error_callback,
-      dbus_error_name, dbus_error_message);
+      "Config.SetNetworkProfile Failed: " + profile_path, service_path,
+      error_callback, dbus_error_name, dbus_error_message);
 }
 
 void LogConfigProperties(const std::string& desc,
                          const std::string& path,
                          const base::DictionaryValue& properties) {
-  for (base::DictionaryValue::Iterator iter(properties);
-       !iter.IsAtEnd(); iter.Advance()) {
+  for (base::DictionaryValue::Iterator iter(properties); !iter.IsAtEnd();
+       iter.Advance()) {
     std::string v = "******";
     if (!shill_property_util::IsPassphraseKey(iter.key()))
       base::JSONWriter::Write(&iter.value(), &v);
-    NET_LOG_DEBUG(desc,  path + "." + iter.key() + "=" + v);
+    NET_LOG(DEBUG) << desc << ": " << path + "." + iter.key() + "=" + v;
   }
 }
 
@@ -120,12 +93,12 @@
         guid_(guid),
         source_(source),
         callback_(callback),
-        error_callback_(error_callback) {
-  }
+        error_callback_(error_callback) {}
 
   void Run() {
-    DBusThreadManager::Get()->GetShillServiceClient()->
-        GetLoadableProfileEntries(
+    DBusThreadManager::Get()
+        ->GetShillServiceClient()
+        ->GetLoadableProfileEntries(
             dbus::ObjectPath(service_path_),
             base::Bind(&ProfileEntryDeleter::GetProfileEntriesToDeleteCallback,
                        AsWeakPtr()));
@@ -136,46 +109,45 @@
       DBusMethodCallStatus call_status,
       const base::DictionaryValue& profile_entries) {
     if (call_status != DBUS_METHOD_CALL_SUCCESS) {
-      InvokeErrorCallback(
-          service_path_, error_callback_, "GetLoadableProfileEntriesFailed");
+      InvokeErrorCallback(service_path_, error_callback_,
+                          "GetLoadableProfileEntriesFailed");
       // ProfileEntryDeleterCompleted will delete this.
       owner_->ProfileEntryDeleterCompleted(service_path_, guid_, source_,
                                            false /* failed */);
       return;
     }
 
-    for (base::DictionaryValue::Iterator iter(profile_entries);
-         !iter.IsAtEnd(); iter.Advance()) {
+    for (base::DictionaryValue::Iterator iter(profile_entries); !iter.IsAtEnd();
+         iter.Advance()) {
       std::string profile_path = StripQuotations(iter.key());
       std::string entry_path;
       iter.value().GetAsString(&entry_path);
       if (profile_path.empty() || entry_path.empty()) {
-        NET_LOG_ERROR("Failed to parse Profile Entry", base::StringPrintf(
-            "%s: %s", profile_path.c_str(), entry_path.c_str()));
+        NET_LOG(ERROR) << "Failed to parse Profile Entry: " << profile_path
+                       << ": " << entry_path;
         continue;
       }
       if (profile_delete_entries_.count(profile_path) != 0) {
-        NET_LOG_ERROR("Multiple Profile Entries", base::StringPrintf(
-            "%s: %s", profile_path.c_str(), entry_path.c_str()));
+        NET_LOG(ERROR) << "Multiple Profile Entries: " << profile_path << ": "
+                       << entry_path;
         continue;
       }
-      NET_LOG_DEBUG("Delete Profile Entry", base::StringPrintf(
-          "%s: %s", profile_path.c_str(), entry_path.c_str()));
+      NET_LOG(DEBUG) << "Delete Profile Entry: " << profile_path << ": "
+                     << entry_path;
       profile_delete_entries_[profile_path] = entry_path;
       DBusThreadManager::Get()->GetShillProfileClient()->DeleteEntry(
-          dbus::ObjectPath(profile_path),
-          entry_path,
+          dbus::ObjectPath(profile_path), entry_path,
           base::Bind(&ProfileEntryDeleter::ProfileEntryDeletedCallback,
                      AsWeakPtr(), profile_path, entry_path),
-          base::Bind(&ProfileEntryDeleter::ShillErrorCallback,
-                     AsWeakPtr(), profile_path, entry_path));
+          base::Bind(&ProfileEntryDeleter::ShillErrorCallback, AsWeakPtr(),
+                     profile_path, entry_path));
     }
   }
 
   void ProfileEntryDeletedCallback(const std::string& profile_path,
                                    const std::string& entry) {
-    NET_LOG_DEBUG("Profile Entry Deleted", base::StringPrintf(
-        "%s: %s", profile_path.c_str(), entry.c_str()));
+    NET_LOG(DEBUG) << "Profile Entry Deleted: " << profile_path << ": "
+                   << entry;
     profile_delete_entries_.erase(profile_path);
     if (!profile_delete_entries_.empty())
       return;
@@ -226,34 +198,50 @@
   observers_.RemoveObserver(observer);
 }
 
-void NetworkConfigurationHandler::GetProperties(
+void NetworkConfigurationHandler::GetShillProperties(
     const std::string& service_path,
     const network_handler::DictionaryResultCallback& callback,
-    const network_handler::ErrorCallback& error_callback) const {
-  NET_LOG_USER("GetProperties", service_path);
+    const network_handler::ErrorCallback& error_callback) {
+  NET_LOG(USER) << "GetProperties: " << service_path;
   DBusThreadManager::Get()->GetShillServiceClient()->GetProperties(
       dbus::ObjectPath(service_path),
-      base::Bind(&GetPropertiesCallback,
-                 callback, error_callback, service_path));
+      base::Bind(&NetworkConfigurationHandler::GetPropertiesCallback,
+                 AsWeakPtr(), callback, error_callback, service_path));
 }
 
-void NetworkConfigurationHandler::SetProperties(
+void NetworkConfigurationHandler::SetShillProperties(
     const std::string& service_path,
-    const base::DictionaryValue& properties,
+    const base::DictionaryValue& shill_properties,
     NetworkConfigurationObserver::Source source,
     const base::Closure& callback,
     const network_handler::ErrorCallback& error_callback) {
-  if (properties.empty()) {
+  if (shill_properties.empty()) {
     if (!callback.is_null())
       callback.Run();
     return;
   }
-  NET_LOG_USER("SetProperties", service_path);
-  LogConfigProperties("SetProperty", service_path, properties);
+  NET_LOG(USER) << "SetProperties: " << service_path;
 
-  scoped_ptr<base::DictionaryValue> properties_copy(properties.DeepCopy());
+  scoped_ptr<base::DictionaryValue> properties_to_set(
+      shill_properties.DeepCopy());
+
+  // Make sure that the GUID is saved to Shill when setting properties.
+  std::string guid;
+  properties_to_set->GetStringWithoutPathExpansion(shill::kGuidProperty, &guid);
+  if (guid.empty()) {
+    const NetworkState* network_state =
+        network_state_handler_->GetNetworkState(service_path);
+    guid = network_state ? network_state->guid() : base::GenerateGUID();
+    properties_to_set->SetStringWithoutPathExpansion(shill::kGuidProperty,
+                                                     guid);
+  }
+
+  LogConfigProperties("SetProperty", service_path, *properties_to_set);
+
+  scoped_ptr<base::DictionaryValue> properties_copy(
+      properties_to_set->DeepCopy());
   DBusThreadManager::Get()->GetShillServiceClient()->SetProperties(
-      dbus::ObjectPath(service_path), properties,
+      dbus::ObjectPath(service_path), *properties_to_set,
       base::Bind(&NetworkConfigurationHandler::SetPropertiesSuccessCallback,
                  AsWeakPtr(), service_path, base::Passed(&properties_copy),
                  source, callback),
@@ -262,11 +250,11 @@
 
   // If we set the StaticIPConfig property, request an IP config refresh
   // after calling SetProperties.
-  if (properties.HasKey(shill::kStaticIPConfigProperty))
+  if (properties_to_set->HasKey(shill::kStaticIPConfigProperty))
     RequestRefreshIPConfigs(service_path);
 }
 
-void NetworkConfigurationHandler::ClearProperties(
+void NetworkConfigurationHandler::ClearShillProperties(
     const std::string& service_path,
     const std::vector<std::string>& names,
     const base::Closure& callback,
@@ -276,49 +264,64 @@
       callback.Run();
     return;
   }
-  NET_LOG_USER("ClearProperties", service_path);
+  NET_LOG(USER) << "ClearProperties: " << service_path;
   for (std::vector<std::string>::const_iterator iter = names.begin();
        iter != names.end(); ++iter) {
-    NET_LOG_DEBUG("ClearProperty", service_path + "." + *iter);
+    NET_LOG(DEBUG) << "ClearProperty: " << service_path << "." << *iter;
   }
   DBusThreadManager::Get()->GetShillServiceClient()->ClearProperties(
-      dbus::ObjectPath(service_path),
-      names,
+      dbus::ObjectPath(service_path), names,
       base::Bind(&NetworkConfigurationHandler::ClearPropertiesSuccessCallback,
                  AsWeakPtr(), service_path, names, callback),
       base::Bind(&NetworkConfigurationHandler::ClearPropertiesErrorCallback,
                  AsWeakPtr(), service_path, error_callback));
 }
 
-void NetworkConfigurationHandler::CreateConfiguration(
-    const base::DictionaryValue& properties,
+void NetworkConfigurationHandler::CreateShillConfiguration(
+    const base::DictionaryValue& shill_properties,
     NetworkConfigurationObserver::Source source,
     const network_handler::StringResultCallback& callback,
     const network_handler::ErrorCallback& error_callback) {
   ShillManagerClient* manager =
       DBusThreadManager::Get()->GetShillManagerClient();
   std::string type;
-  properties.GetStringWithoutPathExpansion(shill::kTypeProperty, &type);
+  shill_properties.GetStringWithoutPathExpansion(shill::kTypeProperty, &type);
   DCHECK(!type.empty());
+
+  std::string network_id =
+      shill_property_util::GetNetworkIdFromProperties(shill_properties);
+
   if (NetworkTypePattern::Ethernet().MatchesType(type)) {
-    InvokeErrorCallback(
-        shill_property_util::GetNetworkIdFromProperties(properties),
-        error_callback,
-        "ConfigureServiceForProfile: Invalid type: " + type);
+    InvokeErrorCallback(network_id, error_callback,
+                        "ConfigureServiceForProfile: Invalid type: " + type);
     return;
   }
 
-  NET_LOG_USER("CreateConfiguration: " + type,
-               shill_property_util::GetNetworkIdFromProperties(properties));
-  LogConfigProperties("Configure", type, properties);
+  scoped_ptr<base::DictionaryValue> properties_to_set(
+      shill_properties.DeepCopy());
+
+  NET_LOG(USER) << "CreateConfiguration: " << type << ": " << network_id;
 
   std::string profile_path;
-  properties.GetStringWithoutPathExpansion(shill::kProfileProperty,
-                                           &profile_path);
+  properties_to_set->GetStringWithoutPathExpansion(shill::kProfileProperty,
+                                                   &profile_path);
   DCHECK(!profile_path.empty());
-  scoped_ptr<base::DictionaryValue> properties_copy(properties.DeepCopy());
+
+  // Make sure that the GUID is saved to Shill when configuring networks.
+  std::string guid;
+  properties_to_set->GetStringWithoutPathExpansion(shill::kGuidProperty, &guid);
+  if (guid.empty()) {
+    guid = base::GenerateGUID();
+    properties_to_set->SetStringWithoutPathExpansion(
+        ::onc::network_config::kGUID, guid);
+  }
+
+  LogConfigProperties("Configure", type, *properties_to_set);
+
+  scoped_ptr<base::DictionaryValue> properties_copy(
+      properties_to_set->DeepCopy());
   manager->ConfigureServiceForProfile(
-      dbus::ObjectPath(profile_path), properties,
+      dbus::ObjectPath(profile_path), *properties_to_set,
       base::Bind(&NetworkConfigurationHandler::RunCreateNetworkCallback,
                  AsWeakPtr(), profile_path, source,
                  base::Passed(&properties_copy), callback),
@@ -334,8 +337,8 @@
   // Service.Remove is not reliable. Instead, request the profile entries
   // for the service and remove each entry.
   if (ContainsKey(profile_entry_deleters_, service_path)) {
-    InvokeErrorCallback(
-        service_path, error_callback, "RemoveConfigurationInProgress");
+    InvokeErrorCallback(service_path, error_callback,
+                        "RemoveConfigurationInProgress");
     return;
   }
 
@@ -344,7 +347,7 @@
       network_state_handler_->GetNetworkState(service_path);
   if (network_state)
     guid = network_state->guid();
-  NET_LOG_USER("Remove Configuration", service_path);
+  NET_LOG(USER) << "Remove Configuration: " << service_path;
   ProfileEntryDeleter* deleter = new ProfileEntryDeleter(
       this, service_path, guid, source, callback, error_callback);
   profile_entry_deleters_[service_path] = deleter;
@@ -357,7 +360,8 @@
     NetworkConfigurationObserver::Source source,
     const base::Closure& callback,
     const network_handler::ErrorCallback& error_callback) {
-  NET_LOG_USER("SetNetworkProfile", service_path + ": " + profile_path);
+  NET_LOG(USER) << "SetNetworkProfile: " << service_path << ": "
+                << profile_path;
   base::StringValue profile_path_value(profile_path);
   DBusThreadManager::Get()->GetShillServiceClient()->SetProperty(
       dbus::ObjectPath(service_path), shill::kProfileProperty,
@@ -375,8 +379,8 @@
 }
 
 NetworkConfigurationHandler::~NetworkConfigurationHandler() {
-  STLDeleteContainerPairSecondPointers(
-      profile_entry_deleters_.begin(), profile_entry_deleters_.end());
+  STLDeleteContainerPairSecondPointers(profile_entry_deleters_.begin(),
+                                       profile_entry_deleters_.end());
 }
 
 void NetworkConfigurationHandler::Init(
@@ -432,6 +436,45 @@
       OnConfigurationProfileChanged(service_path, profile_path, source));
 }
 
+void NetworkConfigurationHandler::GetPropertiesCallback(
+    const network_handler::DictionaryResultCallback& callback,
+    const network_handler::ErrorCallback& error_callback,
+    const std::string& service_path,
+    DBusMethodCallStatus call_status,
+    const base::DictionaryValue& properties) {
+  if (call_status != DBUS_METHOD_CALL_SUCCESS) {
+    // Because network services are added and removed frequently, we will see
+    // failures regularly, so don't log these.
+    network_handler::RunErrorCallback(error_callback, service_path,
+                                      network_handler::kDBusFailedError,
+                                      network_handler::kDBusFailedErrorMessage);
+    return;
+  }
+  if (callback.is_null())
+    return;
+
+  // Get the correct name from WifiHex if necessary.
+  scoped_ptr<base::DictionaryValue> properties_copy(properties.DeepCopy());
+  std::string name =
+      shill_property_util::GetNameFromProperties(service_path, properties);
+  if (!name.empty())
+    properties_copy->SetStringWithoutPathExpansion(shill::kNameProperty, name);
+
+  // Get the GUID property from NetworkState if it is not set in Shill.
+  std::string guid;
+  properties.GetStringWithoutPathExpansion(::onc::network_config::kGUID, &guid);
+  if (guid.empty()) {
+    const NetworkState* network_state =
+        network_state_handler_->GetNetworkState(service_path);
+    if (network_state) {
+      properties_copy->SetStringWithoutPathExpansion(
+          ::onc::network_config::kGUID, network_state->guid());
+    }
+  }
+
+  callback.Run(service_path, *properties_copy.get());
+}
+
 void NetworkConfigurationHandler::SetPropertiesSuccessCallback(
     const std::string& service_path,
     scoped_ptr<base::DictionaryValue> set_properties,
@@ -456,8 +499,7 @@
     const std::string& dbus_error_name,
     const std::string& dbus_error_message) {
   network_handler::ShillErrorCallbackFunction(
-      "Config.SetProperties Failed",
-      service_path, error_callback,
+      "Config.SetProperties Failed", service_path, error_callback,
       dbus_error_name, dbus_error_message);
   // Some properties may have changed so request an update regardless.
   network_state_handler_->RequestUpdateForNetwork(service_path);
@@ -478,7 +520,8 @@
     if (!success) {
       // If a property was cleared that has never been set, the clear will fail.
       // We do not track which properties have been set, so just log the error.
-      NET_LOG_ERROR("ClearProperties Failed: " + names[i], service_path);
+      NET_LOG(ERROR) << "ClearProperties Failed: " << service_path << ": "
+                     << names[i];
     }
   }
 
@@ -493,8 +536,7 @@
     const std::string& dbus_error_name,
     const std::string& dbus_error_message) {
   network_handler::ShillErrorCallbackFunction(
-      "Config.ClearProperties Failed",
-      service_path, error_callback,
+      "Config.ClearProperties Failed", service_path, error_callback,
       dbus_error_name, dbus_error_message);
   // Some properties may have changed so request an update regardless.
   network_state_handler_->RequestUpdateForNetwork(service_path);
diff --git a/chromeos/network/network_configuration_handler.h b/chromeos/network/network_configuration_handler.h
index 4b404d8a..0ab68f0 100644
--- a/chromeos/network/network_configuration_handler.h
+++ b/chromeos/network/network_configuration_handler.h
@@ -66,20 +66,20 @@
 
   // Gets the properties of the network with id |service_path|. See note on
   // |callback| and |error_callback|, in class description above.
-  void GetProperties(
+  void GetShillProperties(
       const std::string& service_path,
       const network_handler::DictionaryResultCallback& callback,
-      const network_handler::ErrorCallback& error_callback) const;
+      const network_handler::ErrorCallback& error_callback);
 
   // Sets the properties of the network with id |service_path|. This means the
   // given properties will be merged with the existing settings, and it won't
   // clear any existing properties. See notes on |source| and callbacks in class
   // description above.
-  void SetProperties(const std::string& service_path,
-                     const base::DictionaryValue& properties,
-                     NetworkConfigurationObserver::Source source,
-                     const base::Closure& callback,
-                     const network_handler::ErrorCallback& error_callback);
+  void SetShillProperties(const std::string& service_path,
+                          const base::DictionaryValue& shill_properties,
+                          NetworkConfigurationObserver::Source source,
+                          const base::Closure& callback,
+                          const network_handler::ErrorCallback& error_callback);
 
   // Removes the properties with the given property paths. If any of them are
   // unable to be cleared, the |error_callback| will only be run once with
@@ -87,19 +87,22 @@
   // "errors" key of the error data, and the |callback| will not be run, even
   // though some of the properties may have been cleared. If there are no
   // errors, |callback| will be run.
-  void ClearProperties(const std::string& service_path,
-                       const std::vector<std::string>& property_paths,
-                       const base::Closure& callback,
-                       const network_handler::ErrorCallback& error_callback);
+  void ClearShillProperties(
+      const std::string& service_path,
+      const std::vector<std::string>& property_paths,
+      const base::Closure& callback,
+      const network_handler::ErrorCallback& error_callback);
 
   // Creates a network with the given |properties| in the specified Shill
   // profile, and returns the new service_path to |callback| if successful.
   // kProfileProperty must be set in |properties|. See notes on |source| and
   // callbacks in class description above. This may also be used to update an
   // existing matching configuration, see Shill documentation for
-  // Manager.ConfigureServiceForProfile.
-  void CreateConfiguration(
-      const base::DictionaryValue& properties,
+  // Manager.ConfigureServiceForProfile. NOTE: Normally
+  // ManagedNetworkConfigurationHandler should be used to call
+  // CreateConfiguration. This will set GUID if not provided.
+  void CreateShillConfiguration(
+      const base::DictionaryValue& shill_properties,
       NetworkConfigurationObserver::Source source,
       const network_handler::StringResultCallback& callback,
       const network_handler::ErrorCallback& error_callback);
@@ -125,7 +128,7 @@
       NetworkStateHandler* network_state_handler,
       NetworkDeviceHandler* network_device_handler);
 
- protected:
+ private:
   friend class ClientCertResolverTest;
   friend class NetworkHandler;
   friend class NetworkConfigurationHandlerTest;
@@ -159,7 +162,15 @@
                                   NetworkConfigurationObserver::Source source,
                                   const base::Closure& callback);
 
-  // Invoke the callback and inform NetworkStateHandler to request an update
+  // Set the Name and GUID properties correctly and Invoke |callback|.
+  void GetPropertiesCallback(
+      const network_handler::DictionaryResultCallback& callback,
+      const network_handler::ErrorCallback& error_callback,
+      const std::string& service_path,
+      DBusMethodCallStatus call_status,
+      const base::DictionaryValue& properties);
+
+  // Invoke |callback| and inform NetworkStateHandler to request an update
   // for the service after setting properties.
   void SetPropertiesSuccessCallback(
       const std::string& service_path,
@@ -172,7 +183,7 @@
       const std::string& dbus_error_name,
       const std::string& dbus_error_message);
 
-  // Invoke the callback and inform NetworkStateHandler to request an update
+  // Invoke |callback| and inform NetworkStateHandler to request an update
   // for the service after clearing properties.
   void ClearPropertiesSuccessCallback(
       const std::string& service_path,
diff --git a/chromeos/network/network_configuration_handler_unittest.cc b/chromeos/network/network_configuration_handler_unittest.cc
index 1b5b3bb..2772fc9 100644
--- a/chromeos/network/network_configuration_handler_unittest.cc
+++ b/chromeos/network/network_configuration_handler_unittest.cc
@@ -277,6 +277,14 @@
         service_path);
   }
 
+  void CreateConfiguration(const std::string& service_path,
+                           const base::DictionaryValue& properties) {
+    network_configuration_handler_->CreateShillConfiguration(
+        properties, NetworkConfigurationObserver::SOURCE_USER_ACTION,
+        base::Bind(&StringResultCallback, service_path),
+        base::Bind(&ErrorCallback, false, std::string()));
+  }
+
  protected:
   MockShillManagerClient* mock_manager_client_;
   MockShillProfileClient* mock_profile_client_;
@@ -310,7 +318,7 @@
   EXPECT_CALL(*mock_service_client_, GetProperties(_, _))
       .WillOnce(
           Invoke(this, &NetworkConfigurationHandlerTest::OnGetProperties));
-  network_configuration_handler_->GetProperties(
+  network_configuration_handler_->GetShillProperties(
       service_path,
       base::Bind(&DictionaryValueCallback, service_path, expected_json),
       base::Bind(&ErrorCallback, false, service_path));
@@ -330,7 +338,7 @@
   EXPECT_CALL(*mock_service_client_, SetProperties(_, _, _, _))
       .WillOnce(
           Invoke(this, &NetworkConfigurationHandlerTest::OnSetProperties));
-  network_configuration_handler_->SetProperties(
+  network_configuration_handler_->SetShillProperties(
       service_path, value, NetworkConfigurationObserver::SOURCE_USER_ACTION,
       base::Bind(&base::DoNothing),
       base::Bind(&ErrorCallback, false, service_path));
@@ -351,7 +359,7 @@
   EXPECT_CALL(*mock_service_client_, SetProperties(_, _, _, _))
       .WillOnce(
           Invoke(this, &NetworkConfigurationHandlerTest::OnSetProperties));
-  network_configuration_handler_->SetProperties(
+  network_configuration_handler_->SetShillProperties(
       service_path, value, NetworkConfigurationObserver::SOURCE_USER_ACTION,
       base::Bind(&base::DoNothing),
       base::Bind(&ErrorCallback, false, service_path));
@@ -363,7 +371,7 @@
   EXPECT_CALL(*mock_service_client_, ClearProperties(_, _, _, _))
       .WillOnce(
           Invoke(this, &NetworkConfigurationHandlerTest::OnClearProperties));
-  network_configuration_handler_->ClearProperties(
+  network_configuration_handler_->ClearShillProperties(
       service_path, values_to_clear, base::Bind(&base::DoNothing),
       base::Bind(&ErrorCallback, false, service_path));
   message_loop_.RunUntilIdle();
@@ -383,7 +391,7 @@
   EXPECT_CALL(*mock_service_client_, SetProperties(_, _, _, _))
       .WillOnce(
           Invoke(this, &NetworkConfigurationHandlerTest::OnSetProperties));
-  network_configuration_handler_->SetProperties(
+  network_configuration_handler_->SetShillProperties(
       service_path, value, NetworkConfigurationObserver::SOURCE_USER_ACTION,
       base::Bind(&base::DoNothing),
       base::Bind(&ErrorCallback, false, service_path));
@@ -395,7 +403,7 @@
   EXPECT_CALL(*mock_service_client_, ClearProperties(_, _, _, _))
       .WillOnce(Invoke(
           this, &NetworkConfigurationHandlerTest::OnClearPropertiesError));
-  network_configuration_handler_->ClearProperties(
+  network_configuration_handler_->ClearShillProperties(
       service_path, values_to_clear, base::Bind(&base::DoNothing),
       base::Bind(&ErrorCallback, true, service_path));
   message_loop_.RunUntilIdle();
@@ -417,10 +425,7 @@
               ConfigureServiceForProfile(dbus::ObjectPath(profile), _, _, _))
       .WillOnce(
           Invoke(this, &NetworkConfigurationHandlerTest::OnConfigureService));
-  network_configuration_handler_->CreateConfiguration(
-      value, NetworkConfigurationObserver::SOURCE_USER_ACTION,
-      base::Bind(&StringResultCallback, std::string("/service/2")),
-      base::Bind(&ErrorCallback, false, std::string()));
+  CreateConfiguration("/service/2", value);
   message_loop_.RunUntilIdle();
 }
 
@@ -533,7 +538,7 @@
     properties.SetStringWithoutPathExpansion(
         shill::kProfileProperty, NetworkProfileHandler::GetSharedProfilePath());
 
-    network_configuration_handler_->CreateConfiguration(
+    network_configuration_handler_->CreateShillConfiguration(
         properties, NetworkConfigurationObserver::SOURCE_USER_ACTION,
         base::Bind(
             &NetworkConfigurationHandlerStubTest::CreateConfigurationCallback,
@@ -588,7 +593,7 @@
                                                   test_identity);
   properties_to_set.SetStringWithoutPathExpansion(shill::kPassphraseProperty,
                                                   test_passphrase);
-  network_configuration_handler_->SetProperties(
+  network_configuration_handler_->SetShillProperties(
       service_path, properties_to_set,
       NetworkConfigurationObserver::SOURCE_USER_ACTION,
       base::Bind(&NetworkConfigurationHandlerStubTest::SuccessCallback,
@@ -610,7 +615,7 @@
   std::vector<std::string> properties_to_clear;
   properties_to_clear.push_back(shill::kIdentityProperty);
   properties_to_clear.push_back(shill::kPassphraseProperty);
-  network_configuration_handler_->ClearProperties(
+  network_configuration_handler_->ClearShillProperties(
       service_path, properties_to_clear,
       base::Bind(&NetworkConfigurationHandlerStubTest::SuccessCallback,
                  base::Unretained(this), "ClearProperties"),
@@ -635,7 +640,7 @@
   base::DictionaryValue properties_to_set;
   properties_to_set.SetStringWithoutPathExpansion(shill::kWifiHexSsid,
                                                   wifi_hex);
-  network_configuration_handler_->SetProperties(
+  network_configuration_handler_->SetShillProperties(
       service_path, properties_to_set,
       NetworkConfigurationObserver::SOURCE_USER_ACTION,
       base::Bind(&base::DoNothing),
@@ -647,7 +652,7 @@
   EXPECT_EQ(wifi_hex, wifi_hex_result);
 
   // Get Properties
-  network_configuration_handler_->GetProperties(
+  network_configuration_handler_->GetShillProperties(
       service_path,
       base::Bind(&NetworkConfigurationHandlerStubTest::GetPropertiesCallback,
                  base::Unretained(this)),
@@ -696,7 +701,7 @@
   base::DictionaryValue properties_to_set;
   properties_to_set.SetStringWithoutPathExpansion(shill::kPassphraseProperty,
                                                   test_passphrase);
-  network_configuration_handler_->SetProperties(
+  network_configuration_handler_->SetShillProperties(
       service_path, properties_to_set,
       NetworkConfigurationObserver::SOURCE_USER_ACTION,
       base::Bind(&base::DoNothing),
diff --git a/chromeos/network/network_connection_handler.cc b/chromeos/network/network_connection_handler.cc
index af9f26a..21d23be9e 100644
--- a/chromeos/network/network_connection_handler.cc
+++ b/chromeos/network/network_connection_handler.cc
@@ -293,7 +293,7 @@
   // Request additional properties to check. VerifyConfiguredAndConnect will
   // use only these properties, not cached properties, to ensure that they
   // are up to date after any recent configuration.
-  configuration_handler_->GetProperties(
+  configuration_handler_->GetShillProperties(
       service_path,
       base::Bind(&NetworkConnectionHandler::VerifyConfiguredAndConnect,
                  AsWeakPtr(), check_error_state),
@@ -504,16 +504,13 @@
 
   if (!config_properties.empty()) {
     NET_LOG_EVENT("Configuring Network", service_path);
-    configuration_handler_->SetProperties(
-        service_path,
-        config_properties,
+    configuration_handler_->SetShillProperties(
+        service_path, config_properties,
         NetworkConfigurationObserver::SOURCE_USER_ACTION,
-        base::Bind(&NetworkConnectionHandler::CallShillConnect,
-                   AsWeakPtr(),
+        base::Bind(&NetworkConnectionHandler::CallShillConnect, AsWeakPtr(),
                    service_path),
         base::Bind(&NetworkConnectionHandler::HandleConfigurationFailure,
-                   AsWeakPtr(),
-                   service_path));
+                   AsWeakPtr(), service_path));
     return;
   }
 
diff --git a/chromeos/network/network_device_handler_impl.cc b/chromeos/network/network_device_handler_impl.cc
index 54a414c..8755b567 100644
--- a/chromeos/network/network_device_handler_impl.cc
+++ b/chromeos/network/network_device_handler_impl.cc
@@ -13,10 +13,10 @@
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/shill_device_client.h"
 #include "chromeos/dbus/shill_ipconfig_client.h"
-#include "chromeos/device_event_log.h"
 #include "chromeos/network/device_state.h"
 #include "chromeos/network/network_handler_callbacks.h"
 #include "chromeos/network/network_state_handler.h"
+#include "components/device_event_log/device_event_log.h"
 #include "dbus/object_path.h"
 #include "third_party/cros_system_api/dbus/service_constants.h"
 
diff --git a/chromeos/network/network_event_log.h b/chromeos/network/network_event_log.h
index 104da008..7c2ba92 100644
--- a/chromeos/network/network_event_log.h
+++ b/chromeos/network/network_event_log.h
@@ -12,7 +12,7 @@
 #include "base/strings/stringprintf.h"
 #include "base/time/time.h"
 #include "chromeos/chromeos_export.h"
-#include "chromeos/device_event_log.h"
+#include "components/device_event_log/device_event_log.h"
 
 namespace base {
 class Value;
@@ -44,26 +44,26 @@
 
 // Errors
 #define NET_LOG_ERROR(event, desc) \
-  NET_LOG_LEVEL(::chromeos::device_event_log::LOG_LEVEL_ERROR, event, desc)
+  NET_LOG_LEVEL(::device_event_log::LOG_LEVEL_ERROR, event, desc)
 
 // Chrome initiated events, e.g. connection requests, scan requests,
 // configuration removal (either from the UI or from ONC policy application).
 #define NET_LOG_USER(event, desc) \
-  NET_LOG_LEVEL(::chromeos::device_event_log::LOG_LEVEL_USER, event, desc)
+  NET_LOG_LEVEL(::device_event_log::LOG_LEVEL_USER, event, desc)
 
 // Important events, e.g. state updates
 #define NET_LOG_EVENT(event, desc) \
-  NET_LOG_LEVEL(::chromeos::device_event_log::LOG_LEVEL_EVENT, event, desc)
+  NET_LOG_LEVEL(::device_event_log::LOG_LEVEL_EVENT, event, desc)
 
 // Non-essential debugging events
 #define NET_LOG_DEBUG(event, desc) \
-  NET_LOG_LEVEL(::chromeos::device_event_log::LOG_LEVEL_DEBUG, event, desc)
+  NET_LOG_LEVEL(::device_event_log::LOG_LEVEL_DEBUG, event, desc)
 
 // Macro to include file and line number info in the event log.
-#define NET_LOG_LEVEL(log_level, event, description)                      \
-  ::chromeos::device_event_log::AddEntryWithDescription(                  \
-      __FILE__, __LINE__, ::chromeos::device_event_log::LOG_TYPE_NETWORK, \
-      log_level, event, description)
+#define NET_LOG_LEVEL(log_level, event, description)                       \
+  ::device_event_log::AddEntryWithDescription(                             \
+      __FILE__, __LINE__, ::device_event_log::LOG_TYPE_NETWORK, log_level, \
+      event, description)
 
 }  // namespace network_event_log
 
diff --git a/chromeos/network/network_state.cc b/chromeos/network/network_state.cc
index 8bbb991..bd53eae 100644
--- a/chromeos/network/network_state.cc
+++ b/chromeos/network/network_state.cc
@@ -7,12 +7,12 @@
 #include "base/stl_util.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/stringprintf.h"
-#include "chromeos/device_event_log.h"
 #include "chromeos/network/network_profile_handler.h"
 #include "chromeos/network/network_type_pattern.h"
 #include "chromeos/network/network_util.h"
 #include "chromeos/network/onc/onc_utils.h"
 #include "chromeos/network/shill_property_util.h"
+#include "components/device_event_log/device_event_log.h"
 #include "third_party/cros_system_api/dbus/service_constants.h"
 
 namespace {
@@ -202,6 +202,8 @@
   dictionary->SetStringWithoutPathExpansion(shill::kGuidProperty, guid());
   dictionary->SetStringWithoutPathExpansion(shill::kSecurityClassProperty,
                                             security_class());
+  dictionary->SetStringWithoutPathExpansion(shill::kProfileProperty,
+                                            profile_path());
 
   if (visible()) {
     if (!error().empty())
diff --git a/chromeos/network/network_util.cc b/chromeos/network/network_util.cc
index 1d84adf..ea6e3e2 100644
--- a/chromeos/network/network_util.cc
+++ b/chromeos/network/network_util.cc
@@ -7,8 +7,11 @@
 #include "base/strings/string_tokenizer.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
+#include "chromeos/login/login_state.h"
+#include "chromeos/network/managed_network_configuration_handler.h"
 #include "chromeos/network/network_state.h"
 #include "chromeos/network/network_state_handler.h"
+#include "chromeos/network/network_ui_data.h"
 #include "chromeos/network/onc/onc_signature.h"
 #include "chromeos/network/onc/onc_translation_tables.h"
 #include "chromeos/network/onc/onc_translator.h"
@@ -146,9 +149,18 @@
   base::DictionaryValue shill_dictionary;
   network->GetStateProperties(&shill_dictionary);
 
+  // NetworkState is always associated with the primary user profile, regardless
+  // of what profile is associated with the page that calls this method. We do
+  // not expose any sensitive properties in the resulting dictionary, it is
+  // only used to show connection state and icons.
+  std::string user_id_hash = chromeos::LoginState::Get()->primary_user_hash();
+  ::onc::ONCSource onc_source = ::onc::ONC_SOURCE_NONE;
+  NetworkHandler::Get()
+      ->managed_network_configuration_handler()
+      ->FindPolicyByGUID(user_id_hash, network->guid(), &onc_source);
+
   scoped_ptr<base::DictionaryValue> onc_dictionary =
-      TranslateShillServiceToONCPart(shill_dictionary,
-                                     ::onc::ONC_SOURCE_UNKNOWN,
+      TranslateShillServiceToONCPart(shill_dictionary, onc_source,
                                      &onc::kNetworkWithStateSignature);
   return onc_dictionary.Pass();
 }
@@ -164,18 +176,15 @@
       pattern, configured_only, visible_only, limit, &network_states);
 
   scoped_ptr<base::ListValue> network_properties_list(new base::ListValue);
-  for (NetworkStateHandler::NetworkStateList::iterator it =
-           network_states.begin();
-       it != network_states.end();
-       ++it) {
+  for (const NetworkState* state : network_states) {
     scoped_ptr<base::DictionaryValue> onc_dictionary =
-        TranslateNetworkStateToONC(*it);
+        TranslateNetworkStateToONC(state);
 
     if (debugging_properties) {
-      onc_dictionary->SetBoolean("connectable", (*it)->connectable());
-      onc_dictionary->SetBoolean("visible", (*it)->visible());
-      onc_dictionary->SetString("profile_path", (*it)->profile_path());
-      onc_dictionary->SetString("service_path", (*it)->path());
+      onc_dictionary->SetBoolean("connectable", state->connectable());
+      onc_dictionary->SetBoolean("visible", state->visible());
+      onc_dictionary->SetString("profile_path", state->profile_path());
+      onc_dictionary->SetString("service_path", state->path());
     }
 
     network_properties_list->Append(onc_dictionary.release());
diff --git a/chromeos/network/shill_property_util.cc b/chromeos/network/shill_property_util.cc
index 798245990..941630c 100644
--- a/chromeos/network/shill_property_util.cc
+++ b/chromeos/network/shill_property_util.cc
@@ -12,9 +12,9 @@
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversion_utils.h"
 #include "base/values.h"
-#include "chromeos/device_event_log.h"
 #include "chromeos/network/network_ui_data.h"
 #include "chromeos/network/onc/onc_utils.h"
+#include "components/device_event_log/device_event_log.h"
 #include "third_party/cros_system_api/dbus/service_constants.h"
 
 namespace chromeos {
diff --git a/components/BUILD.gn b/components/BUILD.gn
index 97417d0..7312e280 100644
--- a/components/BUILD.gn
+++ b/components/BUILD.gn
@@ -33,6 +33,7 @@
     "//components/crx_file",
     "//components/data_reduction_proxy/core/browser",
     "//components/data_reduction_proxy/core/common",
+    "//components/device_event_log",
     "//components/dom_distiller/core",
     "//components/domain_reliability",
     "//components/enhanced_bookmarks",
@@ -237,6 +238,7 @@
       "//components/crx_file:unit_tests",
       "//components/data_reduction_proxy/core/browser:unit_tests",
       "//components/data_reduction_proxy/core/common:unit_tests",
+      "//components/device_event_log:unit_tests",
       "//components/dom_distiller/core:unit_tests",
       "//components/domain_reliability:unit_tests",
       "//components/favicon_base:unit_tests",
diff --git a/components/OWNERS b/components/OWNERS
index c1f0cd7..d563137 100644
--- a/components/OWNERS
+++ b/components/OWNERS
@@ -6,6 +6,10 @@
 
 per-file app_modal*=avi@chromium.org
 
+per-file audio_modem.gypi=ckehoe@chromium.org
+per-file audio_modem.gypi=rkc@chromium.org
+per-file audio_modem.gypi=xiyuan@chromium.org
+
 # Can not match autofill* due to crbug.com/397984
 per-file autofill.gypi=estade@chromium.org
 per-file autofill.gypi=isherman@chromium.org
@@ -141,6 +145,9 @@
 per-file onc.gypi=pneubeck@chromium.org
 per-file onc.gypi=stevenjb@chromium.org
 
+per-file open_from_clipboard.gypi=jif@chromium.org
+per-file open_from_clipboard_strings.grdp=jif@chromium.org
+
 per-file packed_ct_ev_whitelist.gypi=eranm@chromium.org
 per-file packed_ct_ev_whitelist.gypi=rsleevi@chromium.org
 
@@ -264,7 +271,10 @@
 per-file webp_transcode.gypi=droger@chromium.org
 per-file webp_transcode.gypi=sdefresne@chromium.org
 
+per-file web_resource.gypi=achuith@chromium.org
 per-file web_resource.gypi=asvitkine@chromium.org
+per-file web_resource.gypi=dbeam@chromium.org
+per-file web_resource.gypi=rsesek@chromium.org
 per-file web_resource.gypi=stevet@chromium.org
 
 per-file wifi.gypi=mef@chromium.org
diff --git a/components/audio_modem.gypi b/components/audio_modem.gypi
index 79d2dbe0..7d2219b 100644
--- a/components/audio_modem.gypi
+++ b/components/audio_modem.gypi
@@ -43,6 +43,8 @@
       'sources': [
         'audio_modem/test/random_samples.cc',
         'audio_modem/test/random_samples.h',
+        'audio_modem/test/stub_modem.cc',
+        'audio_modem/test/stub_modem.h',
         'audio_modem/test/stub_whispernet_client.cc',
         'audio_modem/test/stub_whispernet_client.h',
       ],
diff --git a/components/audio_modem/BUILD.gn b/components/audio_modem/BUILD.gn
index 6676e64..93c9fd658 100644
--- a/components/audio_modem/BUILD.gn
+++ b/components/audio_modem/BUILD.gn
@@ -27,3 +27,19 @@
     "//third_party/webrtc/common_audio",
   ]
 }
+
+static_library("audio_modem_test_support") {
+  sources = [
+    "test/random_samples.cc",
+    "test/random_samples.h",
+    "test/stub_modem.cc",
+    "test/stub_modem.h",
+    "test/stub_whispernet_client.cc",
+    "test/stub_whispernet_client.h",
+  ]
+
+  deps = [
+    "//base",
+    "//media",
+  ]
+}
diff --git a/components/audio_modem/audio_recorder_impl.cc b/components/audio_modem/audio_recorder_impl.cc
index 9be772e0..b694c39 100644
--- a/components/audio_modem/audio_recorder_impl.cc
+++ b/components/audio_modem/audio_recorder_impl.cc
@@ -54,7 +54,6 @@
 AudioRecorderImpl::AudioRecorderImpl()
     : is_recording_(false),
       stream_(nullptr),
-      temp_conversion_buffer_(nullptr),
       total_buffer_frames_(0),
       buffer_frame_index_(0) {
 }
@@ -103,20 +102,8 @@
           : media::AudioManager::Get()->GetInputStreamParameters(
                 media::AudioManagerBase::kDefaultDeviceId);
 
-  const media::AudioParameters dest_params(params.format(),
-                                           kDefaultChannelLayout,
-                                           kDefaultSampleRate,
-                                           kDefaultBitsPerSample,
-                                           params.frames_per_buffer(),
-                                           media::AudioParameters::NO_EFFECTS);
-
-  converter_.reset(new media::AudioConverter(
-      params, dest_params, params.sample_rate() == dest_params.sample_rate()));
-  converter_->AddInput(this);
-
-  total_buffer_frames_ = kProcessIntervalMs * dest_params.sample_rate() / 1000;
-  buffer_ =
-      media::AudioBus::Create(dest_params.channels(), total_buffer_frames_);
+  total_buffer_frames_ = kProcessIntervalMs * params.sample_rate() / 1000;
+  buffer_ = media::AudioBus::Create(params.channels(), total_buffer_frames_);
   buffer_frame_index_ = 0;
 
   stream_ = input_stream_for_testing_
@@ -141,7 +128,6 @@
     return;
 
   VLOG(3) << "Starting recording.";
-  converter_->Reset();
   stream_->Start(this);
   is_recording_ = true;
 }
@@ -176,46 +162,27 @@
                                const media::AudioBus* source,
                                uint32 /* hardware_delay_bytes */,
                                double /* volume */) {
-  temp_conversion_buffer_ = source;
-  while (temp_conversion_buffer_) {
-    // source->frames() == source_params.frames_per_buffer(), so we only have
-    // one chunk of data in the source; correspondingly set the destination
-    // size to one chunk.
-    // TODO(rkc): Optimize this to directly write into buffer_ so we can avoid
-    // the copy into this buffer and then the copy back into buffer_.
-    scoped_ptr<media::AudioBus> converted_source =
-        media::AudioBus::Create(kDefaultChannels, converter_->ChunkSize());
+  // source->frames() == source_params.frames_per_buffer(), so we only have
+  // one chunk of data in the source; correspondingly set the destination
+  // size to one chunk.
 
-    // Convert accumulated samples into converted_source. Note: One call may not
-    // be enough to consume the samples from |source|.  The converter may have
-    // accumulated samples over time due to a fractional input:output sample
-    // rate ratio.  Since |source| is ephemeral, Convert() must be called until
-    // |source| is at least buffered into the converter.  Once |source| is
-    // consumed during ProvideInput(), |temp_conversion_buffer_| will be set to
-    // NULL, which will break the conversion loop.
-    converter_->Convert(converted_source.get());
+  int remaining_buffer_frames = buffer_->frames() - buffer_frame_index_;
+  int frames_to_copy = std::min(remaining_buffer_frames, source->frames());
+  source->CopyPartialFramesTo(0, frames_to_copy, buffer_frame_index_,
+                              buffer_.get());
+  buffer_frame_index_ += frames_to_copy;
 
-    int remaining_buffer_frames = buffer_->frames() - buffer_frame_index_;
-    int frames_to_copy =
-        std::min(remaining_buffer_frames, converted_source->frames());
-    converted_source->CopyPartialFramesTo(
-        0, frames_to_copy, buffer_frame_index_, buffer_.get());
-    buffer_frame_index_ += frames_to_copy;
+  // Buffer full, send it for processing.
+  if (buffer_->frames() == buffer_frame_index_) {
+    ProcessSamples(buffer_.Pass(), decode_callback_);
+    buffer_ = media::AudioBus::Create(kDefaultChannels, total_buffer_frames_);
+    buffer_frame_index_ = 0;
 
-    // Buffer full, send it for processing.
-    if (buffer_->frames() == buffer_frame_index_) {
-      ProcessSamples(buffer_.Pass(), decode_callback_);
-      buffer_ = media::AudioBus::Create(kDefaultChannels, total_buffer_frames_);
-      buffer_frame_index_ = 0;
-
-      // Copy any remaining frames in the source to our buffer.
-      int remaining_source_frames = converted_source->frames() - frames_to_copy;
-      converted_source->CopyPartialFramesTo(frames_to_copy,
-                                            remaining_source_frames,
-                                            buffer_frame_index_,
-                                            buffer_.get());
-      buffer_frame_index_ += remaining_source_frames;
-    }
+    // Copy any remaining frames in the source to our buffer.
+    int remaining_source_frames = source->frames() - frames_to_copy;
+    source->CopyPartialFramesTo(frames_to_copy, remaining_source_frames,
+                                buffer_frame_index_, buffer_.get());
+    buffer_frame_index_ += remaining_source_frames;
   }
 }
 
@@ -227,15 +194,6 @@
                  base::Unretained(this)));
 }
 
-double AudioRecorderImpl::ProvideInput(media::AudioBus* dest,
-                                       base::TimeDelta /* buffer_delay */) {
-  DCHECK(temp_conversion_buffer_);
-  DCHECK_LE(temp_conversion_buffer_->frames(), dest->frames());
-  temp_conversion_buffer_->CopyTo(dest);
-  temp_conversion_buffer_ = nullptr;
-  return 1.0;
-}
-
 void AudioRecorderImpl::FlushAudioLoopForTesting() {
   if (media::AudioManager::Get()->GetTaskRunner()->BelongsToCurrentThread())
     return;
diff --git a/components/audio_modem/audio_recorder_impl.h b/components/audio_modem/audio_recorder_impl.h
index 5da0b1c..d5f567f0 100644
--- a/components/audio_modem/audio_recorder_impl.h
+++ b/components/audio_modem/audio_recorder_impl.h
@@ -13,7 +13,6 @@
 #include "components/audio_modem/audio_recorder.h"
 #include "media/audio/audio_io.h"
 #include "media/audio/audio_parameters.h"
-#include "media/base/audio_converter.h"
 
 namespace base {
 class MessageLoop;
@@ -28,8 +27,7 @@
 // The AudioRecorder class will record audio until told to stop.
 class AudioRecorderImpl final
     : public AudioRecorder,
-      public media::AudioInputStream::AudioInputCallback,
-      public media::AudioConverter::InputCallback {
+      public media::AudioInputStream::AudioInputCallback {
  public:
   using RecordedSamplesCallback = base::Callback<void(const std::string&)>;
 
@@ -79,10 +77,6 @@
               double volume) override;
   void OnError(media::AudioInputStream* stream) override;
 
-  // AudioConverter::InputCallback overrides:
-  double ProvideInput(media::AudioBus* dest,
-                      base::TimeDelta buffer_delay) override;
-
   // Flushes the audio loop, making sure that any queued operations are
   // performed.
   void FlushAudioLoopForTesting();
@@ -93,17 +87,12 @@
 
   RecordedSamplesCallback decode_callback_;
 
-  // ProvideInput will use this buffer as its source.
-  const media::AudioBus* temp_conversion_buffer_;
-
   // Outside of the ctor/Initialize method, only access the next variables on
   // the recording thread.
   scoped_ptr<media::AudioBus> buffer_;
   int total_buffer_frames_;
   int buffer_frame_index_;
 
-  scoped_ptr<media::AudioConverter> converter_;
-
   scoped_ptr<media::AudioInputStream> input_stream_for_testing_;
   scoped_ptr<media::AudioParameters> params_for_testing_;
 
diff --git a/components/audio_modem/audio_recorder_unittest.cc b/components/audio_modem/audio_recorder_unittest.cc
index a05ad7f5..0dd089ae 100644
--- a/components/audio_modem/audio_recorder_unittest.cc
+++ b/components/audio_modem/audio_recorder_unittest.cc
@@ -20,6 +20,8 @@
 
 namespace {
 
+const size_t kSomeNumber = 0x9999;
+
 class TestAudioInputStream : public media::AudioInputStream {
  public:
   TestAudioInputStream(const media::AudioParameters& params,
@@ -87,23 +89,24 @@
   }
 
   void CreateSimpleRecorder() {
-    DeleteRecorder();
-    recorder_ = new AudioRecorderImpl();
-    recorder_->Initialize(
-        base::Bind(&AudioRecorderTest::DecodeSamples, base::Unretained(this)));
+    // If we have input devices, we'll create a recorder which uses a real
+    // input stream, if not, we'll create a recorder which uses our mock input
+    // stream.
+    if (media::AudioManager::Get()->HasAudioInputDevices()) {
+      DeleteRecorder();
+      recorder_ = new AudioRecorderImpl();
+      recorder_->Initialize(base::Bind(&AudioRecorderTest::DecodeSamples,
+                                       base::Unretained(this)));
+    } else {
+      CreateRecorder(kSomeNumber);
+    }
   }
 
-  void CreateRecorder(size_t channels,
-                      size_t sample_rate,
-                      size_t bits_per_sample,
-                      size_t samples) {
+  void CreateRecorder(size_t samples) {
     DeleteRecorder();
-    params_.Reset(media::AudioParameters::AUDIO_PCM_LOW_LATENCY,
-                  kDefaultChannelLayout,
-                  channels,
-                  sample_rate,
-                  bits_per_sample,
-                  4096);
+
+    params_ = media::AudioManager::Get()->GetInputStreamParameters(
+        media::AudioManagerBase::kDefaultDeviceId);
 
     channel_data_.clear();
     channel_data_.push_back(GenerateSamples(0x1337, samples));
@@ -193,17 +196,7 @@
   content::TestBrowserThreadBundle thread_bundle_;
 };
 
-// TODO(rkc): These tests are broken on all platforms.
-// On Windows and Mac, we cannot use non-OS params. The tests need to be
-// rewritten to use the params provided to us by the audio manager
-// rather than setting our own params.
-// On Linux, there is a memory leak in the audio code during initialization.
-#define MAYBE_BasicRecordAndStop DISABLED_BasicRecordAndStop
-#define MAYBE_OutOfOrderRecordAndStopMultiple \
-    DISABLED_OutOfOrderRecordAndStopMultiple
-#define MAYBE_RecordingEndToEnd DISABLED_RecordingEndToEnd
-
-TEST_F(AudioRecorderTest, MAYBE_BasicRecordAndStop) {
+TEST_F(AudioRecorderTest, BasicRecordAndStop) {
   CreateSimpleRecorder();
 
   recorder_->Record();
@@ -224,6 +217,15 @@
   DeleteRecorder();
 }
 
+// http://crbug.com/460685
+#if defined(OS_MACOSX)
+#define MAYBE_OutOfOrderRecordAndStopMultiple \
+  DISABLED_OutOfOrderRecordAndStopMultiple
+#else
+#define MAYBE_OutOfOrderRecordAndStopMultiple \
+  OutOfOrderRecordAndStopMultiple
+#endif
+
 TEST_F(AudioRecorderTest, MAYBE_OutOfOrderRecordAndStopMultiple) {
   CreateSimpleRecorder();
 
@@ -243,10 +245,9 @@
   DeleteRecorder();
 }
 
-TEST_F(AudioRecorderTest, MAYBE_RecordingEndToEnd) {
+TEST_F(AudioRecorderTest, RecordingEndToEnd) {
   const int kNumSamples = 48000 * 3;
-  CreateRecorder(
-      kDefaultChannels, kDefaultSampleRate, kDefaultBitsPerSample, kNumSamples);
+  CreateRecorder(kNumSamples);
 
   RecordAndVerifySamples();
 
diff --git a/components/audio_modem/modem_impl.cc b/components/audio_modem/modem_impl.cc
index 293b1f9..23d75d0a 100644
--- a/components/audio_modem/modem_impl.cc
+++ b/components/audio_modem/modem_impl.cc
@@ -92,8 +92,6 @@
       switches::kAudioModemEnableInaudibleBroadcast, true);
   player_[AUDIBLE] = nullptr;
   player_[INAUDIBLE] = nullptr;
-  token_length_[0] = 0;
-  token_length_[1] = 0;
 
   samples_caches_.resize(2);
   samples_caches_[AUDIBLE] = new SamplesMap(kMaxSamples);
@@ -191,21 +189,21 @@
 }
 
 void ModemImpl::SetToken(AudioType type,
-                                const std::string& url_safe_token) {
+                         const std::string& url_safe_token) {
   DCHECK(type == AUDIBLE || type == INAUDIBLE);
   std::string token = FromUrlSafe(url_safe_token);
   if (samples_caches_[type]->Get(token) == samples_caches_[type]->end()) {
-    client_->EncodeToken(token, type);
+    client_->EncodeToken(token, type, token_params_);
   } else {
     UpdateToken(type, token);
   }
 }
 
-const std::string ModemImpl::GetToken(AudioType type) {
+const std::string ModemImpl::GetToken(AudioType type) const {
   return playing_token_[type];
 }
 
-bool ModemImpl::IsPlayingTokenHeard(AudioType type) {
+bool ModemImpl::IsPlayingTokenHeard(AudioType type) const {
   base::TimeDelta tokenTimeout =
       base::TimeDelta::FromMilliseconds(kTokenTimeoutMs);
 
@@ -217,8 +215,12 @@
   return base::Time::Now() - heard_own_token_[type] < tokenTimeout;
 }
 
-void ModemImpl::SetTokenLength(AudioType type, size_t token_length) {
-  token_length_[type] = token_length;
+void ModemImpl::SetTokenParams(AudioType type, const TokenParameters& params) {
+  DCHECK_GT(params.length, 0u);
+  token_params_[type] = params;
+
+  // TODO(ckehoe): Make whispernet handle different token lengths
+  // simultaneously without reinitializing the decoder over and over.
 }
 
 // static
@@ -294,11 +296,11 @@
       should_be_recording_[INAUDIBLE] || should_be_playing_[INAUDIBLE];
 
   if (decode_audible && decode_inaudible) {
-    client_->DecodeSamples(BOTH, samples, token_length_);
+    client_->DecodeSamples(BOTH, samples, token_params_);
   } else if (decode_audible) {
-    client_->DecodeSamples(AUDIBLE, samples, token_length_);
+    client_->DecodeSamples(AUDIBLE, samples, token_params_);
   } else if (decode_inaudible) {
-    client_->DecodeSamples(INAUDIBLE, samples, token_length_);
+    client_->DecodeSamples(INAUDIBLE, samples, token_params_);
   }
 }
 
diff --git a/components/audio_modem/modem_impl.h b/components/audio_modem/modem_impl.h
index 108d4bb..ec6beb5 100644
--- a/components/audio_modem/modem_impl.h
+++ b/components/audio_modem/modem_impl.h
@@ -45,9 +45,9 @@
   void StartRecording(AudioType type) override;
   void StopRecording(AudioType type) override;
   void SetToken(AudioType type, const std::string& url_safe_token) override;
-  const std::string GetToken(AudioType type) override;
-  bool IsPlayingTokenHeard(AudioType type) override;
-  void SetTokenLength(AudioType type, size_t token_length) override;
+  const std::string GetToken(AudioType type) const override;
+  bool IsPlayingTokenHeard(AudioType type) const override;
+  void SetTokenParams(AudioType type, const TokenParameters& params) override;
 
   void set_player_for_testing(AudioType type, AudioPlayer* player) {
     player_[type] = player;
@@ -108,7 +108,7 @@
 
   // Indexed using enum AudioType.
   std::string playing_token_[2];
-  size_t token_length_[2];
+  TokenParameters token_params_[2];
   base::Time started_playing_[2];
   base::Time heard_own_token_[2];
 
diff --git a/components/audio_modem/public/audio_modem_types.h b/components/audio_modem/public/audio_modem_types.h
index 32c6757..036a6a1 100644
--- a/components/audio_modem/public/audio_modem_types.h
+++ b/components/audio_modem/public/audio_modem_types.h
@@ -50,6 +50,22 @@
   bool audible;
 };
 
+// Struct to hold the encoding parameters for tokens.
+// Parity is on by default.
+struct TokenParameters {
+  TokenParameters() : TokenParameters(0, false, true) {}
+
+  explicit TokenParameters(size_t length)
+      : TokenParameters(length, false, true) {}
+
+  TokenParameters(size_t length, bool crc, bool parity)
+      : length(length), crc(crc), parity(parity) {}
+
+  size_t length;
+  bool crc;
+  bool parity;
+};
+
 // Callback to pass around found tokens.
 using TokensCallback = base::Callback<void(const std::vector<AudioToken>&)>;
 
diff --git a/components/audio_modem/public/modem.h b/components/audio_modem/public/modem.h
index 304614c4..09ab399 100644
--- a/components/audio_modem/public/modem.h
+++ b/components/audio_modem/public/modem.h
@@ -31,11 +31,12 @@
   virtual void SetToken(AudioType type,
                         const std::string& url_safe_token) = 0;
 
-  virtual const std::string GetToken(AudioType type) = 0;
+  virtual const std::string GetToken(AudioType type) const = 0;
 
-  virtual bool IsPlayingTokenHeard(AudioType type) = 0;
+  virtual bool IsPlayingTokenHeard(AudioType type) const = 0;
 
-  virtual void SetTokenLength(AudioType type, size_t token_length) = 0;
+  virtual void SetTokenParams(AudioType type,
+                              const TokenParameters& params) = 0;
 
   static scoped_ptr<Modem> Create();
 };
diff --git a/components/audio_modem/public/whispernet_client.h b/components/audio_modem/public/whispernet_client.h
index c7219c8..48d1efa9 100644
--- a/components/audio_modem/public/whispernet_client.h
+++ b/components/audio_modem/public/whispernet_client.h
@@ -34,27 +34,24 @@
   virtual void Initialize(const SuccessCallback& init_callback) = 0;
 
   // Fires an event to request a token encode.
-  virtual void EncodeToken(const std::string& token, AudioType type) = 0;
+  virtual void EncodeToken(const std::string& token,
+                           AudioType type,
+                           const TokenParameters token_params[2]) = 0;
   // Fires an event to request a decode for the given samples.
   virtual void DecodeSamples(AudioType type,
                              const std::string& samples,
-                             const size_t token_length[2]) = 0;
-  // Fires an event to request detection of a whispernet broadcast.
-  virtual void DetectBroadcast() = 0;
+                             const TokenParameters token_params[2]) = 0;
 
   // Callback registration methods. The modem will set these to receive data.
   virtual void RegisterTokensCallback(
       const TokensCallback& tokens_callback) = 0;
   virtual void RegisterSamplesCallback(
       const SamplesCallback& samples_callback) = 0;
-  virtual void RegisterDetectBroadcastCallback(
-      const SuccessCallback& db_callback) = 0;
 
   // Don't cache these callbacks, as they may become invalid at any time.
   // Always invoke callbacks directly through these accessors.
   virtual TokensCallback GetTokensCallback() = 0;
   virtual SamplesCallback GetSamplesCallback() = 0;
-  virtual SuccessCallback GetDetectBroadcastCallback() = 0;
   virtual SuccessCallback GetInitializedCallback() = 0;
 
   virtual ~WhispernetClient() {}
diff --git a/components/audio_modem/test/stub_modem.cc b/components/audio_modem/test/stub_modem.cc
new file mode 100644
index 0000000..ea0fb08
--- /dev/null
+++ b/components/audio_modem/test/stub_modem.cc
@@ -0,0 +1,69 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/audio_modem/test/stub_modem.h"
+
+#include "base/base64.h"
+#include "base/logging.h"
+
+namespace audio_modem {
+
+StubModem::StubModem() {
+  playing_[AUDIBLE] = false;
+  playing_[INAUDIBLE] = false;
+  recording_[AUDIBLE] = false;
+  recording_[INAUDIBLE] = false;
+}
+
+StubModem::~StubModem() {}
+
+void StubModem::Initialize(WhispernetClient* whispernet_client,
+                           const TokensCallback& tokens_cb) {
+  tokens_callback_ = tokens_cb;
+}
+
+void StubModem::StartPlaying(AudioType type) {
+  playing_[type] = true;
+}
+
+void StubModem::StopPlaying(AudioType type) {
+  playing_[type] = false;
+}
+
+void StubModem::StartRecording(AudioType type) {
+  recording_[type] = true;
+}
+
+void StubModem::StopRecording(AudioType type) {
+  recording_[type] = false;
+}
+
+const std::string StubModem::GetToken(AudioType type) const {
+  return std::string();
+}
+
+bool StubModem::IsPlayingTokenHeard(AudioType type) const {
+  return false;
+}
+
+bool StubModem::IsRecording(AudioType type) const {
+  return recording_[type];
+}
+
+bool StubModem::IsPlaying(AudioType type) const {
+  return playing_[type];
+}
+
+void StubModem::DeliverTokens(const std::vector<AudioToken>& tokens) {
+  std::vector<AudioToken> encoded_tokens;
+  for (const AudioToken& token : tokens) {
+    std::string encoded_token;
+    base::Base64Encode(token.token, & encoded_token);
+    encoded_tokens.push_back(AudioToken(encoded_token, token.audible));
+  }
+  DCHECK_EQ(tokens.size(), encoded_tokens.size());
+  tokens_callback_.Run(encoded_tokens);
+}
+
+}  // namespace audio_modem
diff --git a/components/audio_modem/test/stub_modem.h b/components/audio_modem/test/stub_modem.h
new file mode 100644
index 0000000..e76f5e35
--- /dev/null
+++ b/components/audio_modem/test/stub_modem.h
@@ -0,0 +1,51 @@
+// 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 COMPONENTS_AUDIO_MODEM_TEST_STUB_MODEM_H_
+#define COMPONENTS_AUDIO_MODEM_TEST_STUB_MODEM_H_
+
+#include <string>
+#include <vector>
+
+#include "base/callback.h"
+#include "components/audio_modem/public/modem.h"
+
+namespace audio_modem {
+
+class StubModem final : public Modem {
+ public:
+  StubModem();
+  ~StubModem() override;
+
+  // AudioManager overrides:
+  void Initialize(WhispernetClient* whispernet_client,
+                  const TokensCallback& tokens_cb) override;
+  void StartPlaying(AudioType type) override;
+  void StopPlaying(AudioType type) override;
+  void StartRecording(AudioType type) override;
+  void StopRecording(AudioType type) override;
+  void SetToken(AudioType type, const std::string& url_unsafe_token) override {}
+  const std::string GetToken(AudioType type) const override;
+  bool IsPlayingTokenHeard(AudioType type) const override;
+  void SetTokenParams(AudioType type, const TokenParameters& params) override {}
+
+  bool IsRecording(AudioType type) const;
+  bool IsPlaying(AudioType type) const;
+
+  // Encodes tokens as base64 and then delivers them.
+  void DeliverTokens(const std::vector<AudioToken>& tokens);
+
+ private:
+  // Indexed using enum AudioType.
+  bool playing_[2];
+  bool recording_[2];
+
+  TokensCallback tokens_callback_;
+
+  DISALLOW_COPY_AND_ASSIGN(StubModem);
+};
+
+}  // namespace audio_modem
+
+#endif  // COMPONENTS_AUDIO_MODEM_TEST_STUB_MODEM_H_
diff --git a/components/audio_modem/test/stub_whispernet_client.cc b/components/audio_modem/test/stub_whispernet_client.cc
index 9b0a50c..e2763ce 100644
--- a/components/audio_modem/test/stub_whispernet_client.cc
+++ b/components/audio_modem/test/stub_whispernet_client.cc
@@ -18,14 +18,16 @@
 void StubWhispernetClient::Initialize(const SuccessCallback& init_callback) {}
 
 void StubWhispernetClient::EncodeToken(const std::string& token,
-                                       AudioType type) {
+                                       AudioType type,
+                                       const TokenParameters token_params[2]) {
   if (!samples_cb_.is_null())
     samples_cb_.Run(type, token, samples_);
 }
 
-void StubWhispernetClient::DecodeSamples(AudioType type,
-                                         const std::string& samples,
-                                         const size_t token_length[2]) {
+void StubWhispernetClient::DecodeSamples(
+    AudioType type,
+    const std::string& samples,
+    const TokenParameters token_params[2]) {
   if (!tokens_cb_.is_null())
     tokens_cb_.Run(tokens_);
 }
@@ -48,10 +50,6 @@
   return samples_cb_;
 }
 
-SuccessCallback StubWhispernetClient::GetDetectBroadcastCallback() {
-  return SuccessCallback();
-}
-
 SuccessCallback StubWhispernetClient::GetInitializedCallback() {
   return SuccessCallback();
 }
diff --git a/components/audio_modem/test/stub_whispernet_client.h b/components/audio_modem/test/stub_whispernet_client.h
index 3225863c..9a0e8c6 100644
--- a/components/audio_modem/test/stub_whispernet_client.h
+++ b/components/audio_modem/test/stub_whispernet_client.h
@@ -30,20 +30,18 @@
 
   void Initialize(const SuccessCallback& init_callback) override;
 
-  void EncodeToken(const std::string& token, AudioType type) override;
+  void EncodeToken(const std::string& token,
+                   AudioType type,
+                   const TokenParameters token_params[2]) override;
   void DecodeSamples(AudioType type,
                      const std::string& samples,
-                     const size_t token_length[2]) override;
-  void DetectBroadcast() override {}
+                     const TokenParameters token_params[2]) override;
 
   void RegisterTokensCallback(const TokensCallback& tokens_cb) override;
   void RegisterSamplesCallback(const SamplesCallback& samples_cb) override;
-  void RegisterDetectBroadcastCallback(
-      const SuccessCallback& /* db_cb */) override {}
 
   TokensCallback GetTokensCallback() override;
   SamplesCallback GetSamplesCallback() override;
-  SuccessCallback GetDetectBroadcastCallback() override;
   SuccessCallback GetInitializedCallback() override;
 
  private:
diff --git a/components/autofill/content/browser/content_autofill_driver.cc b/components/autofill/content/browser/content_autofill_driver.cc
index 0bcd98b..5574f32 100644
--- a/components/autofill/content/browser/content_autofill_driver.cc
+++ b/components/autofill/content/browser/content_autofill_driver.cc
@@ -101,8 +101,8 @@
   if (!RendererIsAvailable())
     return;
 
-  std::vector<FormDataPredictions> type_predictions;
-  FormStructure::GetFieldTypePredictions(forms, &type_predictions);
+  std::vector<FormDataPredictions> type_predictions =
+      FormStructure::GetFieldTypePredictions(forms);
   render_frame_host_->Send(new AutofillMsg_FieldTypePredictionsAvailable(
       render_frame_host_->GetRoutingID(), type_predictions));
 }
diff --git a/components/autofill/content/browser/content_autofill_driver_unittest.cc b/components/autofill/content/browser/content_autofill_driver_unittest.cc
index 0ade6e6..c3763326 100644
--- a/components/autofill/content/browser/content_autofill_driver_unittest.cc
+++ b/components/autofill/content/browser/content_autofill_driver_unittest.cc
@@ -30,7 +30,7 @@
 
 namespace {
 
-const std::string kAppLocale = "en-US";
+const char kAppLocale[] = "en-US";
 const AutofillManager::AutofillDownloadManagerState kDownloadState =
     AutofillManager::DISABLE_AUTOFILL_DOWNLOAD_MANAGER;
 
@@ -272,8 +272,8 @@
   test::CreateTestAddressFormData(&form);
   FormStructure form_structure(form);
   std::vector<FormStructure*> forms(1, &form_structure);
-  std::vector<FormDataPredictions> expected_type_predictions;
-  FormStructure::GetFieldTypePredictions(forms, &expected_type_predictions);
+  std::vector<FormDataPredictions> expected_type_predictions =
+      FormStructure::GetFieldTypePredictions(forms);
   driver_->SendAutofillTypePredictionsToRenderer(forms);
 
   std::vector<FormDataPredictions> output_type_predictions;
diff --git a/components/autofill/content/renderer/BUILD.gn b/components/autofill/content/renderer/BUILD.gn
index 89706ab..bd0c7b21 100644
--- a/components/autofill/content/renderer/BUILD.gn
+++ b/components/autofill/content/renderer/BUILD.gn
@@ -38,8 +38,6 @@
     "//ui/base",
   ]
 
-  if (is_win) {
-    # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
-    cflags = [ "/wd4267" ]
-  }
+  # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+  configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
 }
diff --git a/components/autofill/content/renderer/form_autofill_util.cc b/components/autofill/content/renderer/form_autofill_util.cc
index 9bc1dbf..c5d30307 100644
--- a/components/autofill/content/renderer/form_autofill_util.cc
+++ b/components/autofill/content/renderer/form_autofill_util.cc
@@ -567,15 +567,7 @@
     // If we didn't find a label from the table, check for div table case if we
     // haven't already.
     inferred_label = InferLabelFromDivTable(element);
-    if (!inferred_label.empty())
-      return inferred_label;
   }
-
-  // Not all web pages put useful info in the title attribute, thus use this as
-  // a last resort.
-  CR_DEFINE_STATIC_LOCAL(WebString, kTitle, ("title"));
-  if (element.hasAttribute(kTitle))
-    inferred_label = element.getAttribute(kTitle);
   return inferred_label;
 }
 
diff --git a/components/autofill/content/renderer/password_autofill_agent.h b/components/autofill/content/renderer/password_autofill_agent.h
index 0b6865f..69d28c1 100644
--- a/components/autofill/content/renderer/password_autofill_agent.h
+++ b/components/autofill/content/renderer/password_autofill_agent.h
@@ -20,7 +20,6 @@
 class WebInputElement;
 class WebKeyboardEvent;
 class WebSecurityOrigin;
-class WebView;
 }
 
 namespace autofill {
@@ -239,9 +238,6 @@
   // Used for UMA stats.
   OtherPossibleUsernamesUsage usernames_usage_;
 
-  // Pointer to the WebView. Used to access page scale factor.
-  blink::WebView* web_view_;
-
   // Set if the user might be submitting a password form on the current page,
   // but the submit may still fail (i.e. doesn't pass JavaScript validation).
   scoped_ptr<PasswordForm> provisionally_saved_form_;
diff --git a/components/autofill/core/browser/BUILD.gn b/components/autofill/core/browser/BUILD.gn
index dd040a0..5c937b0e 100644
--- a/components/autofill/core/browser/BUILD.gn
+++ b/components/autofill/core/browser/BUILD.gn
@@ -175,6 +175,7 @@
     "//url",
   ]
 
+  configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
   public_configs = [ ":autofill_browser_config" ]
 
   if (autofill_enable_sync) {
@@ -189,11 +190,6 @@
       "webdata/autofill_wallet_syncable_service.h",
     ]
   }
-
-  if (is_win) {
-    # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
-    cflags = [ "/wd4267" ]
-  }
 }
 
 static_library("test_support") {
diff --git a/components/autofill/core/browser/autofill_external_delegate.cc b/components/autofill/core/browser/autofill_external_delegate.cc
index 85e6cdf..f600b652 100644
--- a/components/autofill/core/browser/autofill_external_delegate.cc
+++ b/components/autofill/core/browser/autofill_external_delegate.cc
@@ -185,7 +185,9 @@
 
 void AutofillExternalDelegate::OnPopupShown() {
   manager_->DidShowSuggestions(
-      has_suggestion_ && !has_shown_popup_for_current_edit_);
+      has_suggestion_ && !has_shown_popup_for_current_edit_,
+      query_form_,
+      query_field_);
   has_shown_popup_for_current_edit_ |= has_suggestion_;
 }
 
diff --git a/components/autofill/core/browser/autofill_manager.cc b/components/autofill/core/browser/autofill_manager.cc
index 688ddb4d..ca6399a 100644
--- a/components/autofill/core/browser/autofill_manager.cc
+++ b/components/autofill/core/browser/autofill_manager.cc
@@ -357,6 +357,9 @@
   if (submitted_form->IsAutofillable())
     ImportFormData(*submitted_form);
 
+  address_form_event_logger_->OnDidSubmitForm();
+  credit_card_form_event_logger_->OnDidSubmitForm();
+
   // Only upload server statistics and UMA metrics if at least some local data
   // is available to use as a baseline.
   const std::vector<AutofillProfile*>& profiles = personal_data_->GetProfiles();
@@ -566,6 +569,46 @@
   }
 }
 
+void AutofillManager::FillOrPreviewCreditCardForm(
+    AutofillDriver::RendererFormDataAction action,
+    int query_id,
+    const FormData& form,
+    const FormFieldData& field,
+    const CreditCard& credit_card,
+    size_t variant) {
+  if (action == AutofillDriver::FORM_DATA_ACTION_FILL) {
+    if (credit_card.record_type() == CreditCard::MASKED_SERVER_CARD) {
+      unmasking_card_ = credit_card;
+      unmasking_query_id_ = query_id;
+      unmasking_form_ = form;
+      unmasking_field_ = field;
+      real_pan_client_.Prepare();
+      client()->ShowUnmaskPrompt(unmasking_card_,
+                                 weak_ptr_factory_.GetWeakPtr());
+      credit_card_form_event_logger_->OnDidSelectMaskedServerCardSuggestion();
+      return;
+    }
+    credit_card_form_event_logger_->OnDidFillSuggestion(credit_card);
+  }
+
+  FillOrPreviewDataModelForm(action, query_id, form, field, credit_card,
+                             variant, true /* is_credit_card */);
+}
+
+void AutofillManager::FillOrPreviewProfileForm(
+    AutofillDriver::RendererFormDataAction action,
+    int query_id,
+    const FormData& form,
+    const FormFieldData& field,
+    const AutofillProfile& profile,
+    size_t variant) {
+  if (action == AutofillDriver::FORM_DATA_ACTION_FILL)
+    address_form_event_logger_->OnDidFillSuggestion(profile);
+
+  FillOrPreviewDataModelForm(action, query_id, form, field, profile, variant,
+                             false /* is_credit_card */);
+}
+
 void AutofillManager::FillOrPreviewForm(
     AutofillDriver::RendererFormDataAction action,
     int query_id,
@@ -575,35 +618,19 @@
   if (!IsValidFormData(form) || !IsValidFormFieldData(field))
     return;
 
-  const AutofillDataModel* data_model = NULL;
-  size_t variant = 0;
-  bool is_credit_card = false;
   // NOTE: RefreshDataModels may invalidate |data_model| because it causes the
   // PersonalDataManager to reload Mac address book entries. Thus it must come
-  // before GetProfileOrCreditCard.
-  if (!RefreshDataModels() ||
-      !driver_->RendererIsAvailable() ||
-      !GetProfileOrCreditCard(
-          unique_id, &data_model, &variant, &is_credit_card)) {
+  // before GetProfile or GetCreditCard.
+  if (!RefreshDataModels() || !driver_->RendererIsAvailable())
     return;
-  }
 
-  if (is_credit_card && action == AutofillDriver::FORM_DATA_ACTION_FILL) {
-    const CreditCard* card = static_cast<const CreditCard*>(data_model);
-    if (card->record_type() == CreditCard::MASKED_SERVER_CARD) {
-      unmasking_card_ = *card;
-      unmasking_query_id_ = query_id;
-      unmasking_form_ = form;
-      unmasking_field_ = field;
-      real_pan_client_.Prepare();
-      client()->ShowUnmaskPrompt(unmasking_card_,
-                                 weak_ptr_factory_.GetWeakPtr());
-      return;
-    }
-  }
-
-  FillOrPreviewDataModelForm(action, query_id, form, field, data_model, variant,
-                             is_credit_card);
+  size_t variant = 0;
+  const CreditCard* credit_card = nullptr;
+  const AutofillProfile* profile = nullptr;
+  if (GetCreditCard(unique_id, &credit_card))
+    FillOrPreviewCreditCardForm(action, query_id, form, field, *credit_card, 0);
+  else if (GetProfile(unique_id, &profile, &variant))
+    FillOrPreviewProfileForm(action, query_id, form, field, *profile, variant);
 }
 
 void AutofillManager::FillCreditCardForm(int query_id,
@@ -616,7 +643,7 @@
   }
 
   FillOrPreviewDataModelForm(AutofillDriver::FORM_DATA_ACTION_FILL, query_id,
-                             form, field, &credit_card, 0, true);
+                             form, field, credit_card, 0, true);
 }
 
 void AutofillManager::OnDidPreviewAutofillFormData() {
@@ -638,9 +665,15 @@
   UpdateInitialInteractionTimestamp(timestamp);
 }
 
-void AutofillManager::DidShowSuggestions(bool is_new_popup) {
+void AutofillManager::DidShowSuggestions(bool is_new_popup,
+                                         const FormData& form,
+                                         const FormFieldData& field) {
   if (test_delegate_)
     test_delegate_->DidShowSuggestions();
+  FormStructure* form_structure = NULL;
+  AutofillField* autofill_field = NULL;
+  if (!GetCachedFormAndField(form, field, &form_structure, &autofill_field))
+    return;
 
   if (is_new_popup) {
     AutofillMetrics::LogUserHappinessMetric(AutofillMetrics::SUGGESTIONS_SHOWN);
@@ -650,6 +683,11 @@
       AutofillMetrics::LogUserHappinessMetric(
           AutofillMetrics::SUGGESTIONS_SHOWN_ONCE);
     }
+
+    if (autofill_field->Type().group() == CREDIT_CARD)
+      credit_card_form_event_logger_->OnDidShowSuggestions();
+    else
+      address_form_event_logger_->OnDidShowSuggestions();
   }
 }
 
@@ -662,11 +700,15 @@
 }
 
 void AutofillManager::RemoveAutofillProfileOrCreditCard(int unique_id) {
-  const AutofillDataModel* data_model = NULL;
+  std::string guid;
   size_t variant = 0;
-  bool unused_is_credit_card = false;
-  if (!GetProfileOrCreditCard(
-          unique_id, &data_model, &variant, &unused_is_credit_card)) {
+  const CreditCard* credit_card = nullptr;
+  const AutofillProfile* profile = nullptr;
+  if (GetCreditCard(unique_id, &credit_card)) {
+    guid = credit_card->guid();
+  } else if (GetProfile(unique_id, &profile, &variant)) {
+    guid = profile->guid();
+  } else {
     NOTREACHED();
     return;
   }
@@ -677,7 +719,7 @@
   if (variant != 0)
     return;
 
-  personal_data_->RemoveByGUID(data_model->guid());
+  personal_data_->RemoveByGUID(guid);
 }
 
 void AutofillManager::RemoveAutocompleteEntry(const base::string16& name,
@@ -722,9 +764,7 @@
 
 void AutofillManager::OnUnmaskResponse(const UnmaskResponse& response) {
   unmask_response_ = response;
-  // TODO(estade): use month/year.
-  real_pan_client_.UnmaskCard(unmasking_card_, base::UTF16ToASCII(response.cvc),
-                              response.risk_data);
+  real_pan_client_.UnmaskCard(unmasking_card_, response);
 }
 
 void AutofillManager::OnUnmaskPromptClosed() {
@@ -740,6 +780,7 @@
 
 void AutofillManager::OnDidGetRealPan(const std::string& real_pan) {
   if (!real_pan.empty()) {
+    credit_card_form_event_logger_->OnDidFillSuggestion(unmasking_card_);
     unmasking_card_.set_record_type(CreditCard::FULL_SERVER_CARD);
     unmasking_card_.SetNumber(base::UTF8ToUTF16(real_pan));
     if (unmask_response_.should_store_pan)
@@ -954,32 +995,41 @@
   return true;
 }
 
-bool AutofillManager::GetProfileOrCreditCard(
-    int unique_id,
-    const AutofillDataModel** data_model,
-    size_t* variant,
-    bool* is_credit_card) const {
+bool AutofillManager::IsCreditCard(int unique_id) {
   // Unpack the |unique_id| into component parts.
   SuggestionBackendID credit_card_id;
   SuggestionBackendID profile_id;
   SplitFrontendID(unique_id, &credit_card_id, &profile_id);
   DCHECK(!base::IsValidGUID(credit_card_id.guid) ||
          !base::IsValidGUID(profile_id.guid));
-  *is_credit_card = false;
+  return base::IsValidGUID(credit_card_id.guid);
+}
 
-  // Find the profile that matches the |profile_guid|, if one is specified.
-  // Otherwise find the credit card that matches the |credit_card_guid|,
-  // if specified.
+bool AutofillManager::GetProfile(int unique_id,
+                                 const AutofillProfile** profile,
+                                 size_t* variant) {
+  // Unpack the |unique_id| into component parts.
+  SuggestionBackendID credit_card_id;
+  SuggestionBackendID profile_id;
+  SplitFrontendID(unique_id, &credit_card_id, &profile_id);
+  *profile = NULL;
   if (base::IsValidGUID(profile_id.guid)) {
-    *data_model = personal_data_->GetProfileByGUID(profile_id.guid);
+    *profile = personal_data_->GetProfileByGUID(profile_id.guid);
     *variant = profile_id.variant;
-  } else if (base::IsValidGUID(credit_card_id.guid)) {
-    *data_model = personal_data_->GetCreditCardByGUID(credit_card_id.guid);
-    *variant = credit_card_id.variant;
-    *is_credit_card = true;
   }
+  return !!*profile;
+}
 
-  return !!*data_model;
+bool AutofillManager::GetCreditCard(int unique_id,
+                                    const CreditCard** credit_card) {
+  // Unpack the |unique_id| into component parts.
+  SuggestionBackendID credit_card_id;
+  SuggestionBackendID profile_id;
+  SplitFrontendID(unique_id, &credit_card_id, &profile_id);
+  *credit_card = NULL;
+  if (base::IsValidGUID(credit_card_id.guid))
+    *credit_card = personal_data_->GetCreditCardByGUID(credit_card_id.guid);
+  return !!*credit_card;
 }
 
 void AutofillManager::FillOrPreviewDataModelForm(
@@ -987,7 +1037,7 @@
     int query_id,
     const FormData& form,
     const FormFieldData& field,
-    const AutofillDataModel* data_model,
+    const AutofillDataModel& data_model,
     size_t variant,
     bool is_credit_card) {
   FormStructure* form_structure = NULL;
@@ -1003,22 +1053,19 @@
   base::string16 profile_full_name;
   std::string profile_language_code;
   if (!is_credit_card) {
-    profile_full_name = data_model->GetInfo(
+    profile_full_name = data_model.GetInfo(
         AutofillType(NAME_FULL), app_locale_);
     profile_language_code =
-        static_cast<const AutofillProfile*>(data_model)->language_code();
+        static_cast<const AutofillProfile*>(&data_model)->language_code();
   }
 
-  if (action == AutofillDriver::FORM_DATA_ACTION_FILL)
-    personal_data_->RecordUseOf(*data_model);
-
   // If the relevant section is auto-filled, we should fill |field| but not the
   // rest of the form.
   if (SectionIsAutofilled(*form_structure, form, autofill_field->section())) {
     for (std::vector<FormFieldData>::iterator iter = result.fields.begin();
          iter != result.fields.end(); ++iter) {
       if (iter->SameFieldAs(field)) {
-        base::string16 value = data_model->GetInfoForVariant(
+        base::string16 value = data_model.GetInfoForVariant(
             autofill_field->Type(), variant, app_locale_);
         if (AutofillField::FillFormField(*autofill_field,
                                          value,
@@ -1041,6 +1088,11 @@
       }
     }
 
+    // Note that this may invalidate |data_model|, particularly if it is a Mac
+    // address book entry.
+    if (action == AutofillDriver::FORM_DATA_ACTION_FILL)
+      personal_data_->RecordUseOf(data_model);
+
     driver_->SendFormDataToRenderer(query_id, action, result);
     return;
   }
@@ -1070,15 +1122,15 @@
         field_group_type == initiating_group_type) {
       use_variant = variant;
     }
-    base::string16 value = data_model->GetInfoForVariant(
+    base::string16 value = data_model.GetInfoForVariant(
         cached_field->Type(), use_variant, app_locale_);
     if (is_credit_card &&
         cached_field->Type().GetStorableType() ==
             CREDIT_CARD_VERIFICATION_CODE) {
-      // If this is |unmasking_card_|, |unmask_response_,cvc| should be
+      // If this is |unmasking_card_|, |unmask_response_.cvc| should be
       // non-empty and vice versa.
       value = unmask_response_.cvc;
-      DCHECK_EQ(&unmasking_card_ == data_model, value.empty());
+      DCHECK_EQ(&unmasking_card_ == &data_model, !value.empty());
     }
 
     // Must match ForEachMatchingFormField() in form_autofill_util.cc.
@@ -1116,6 +1168,11 @@
   if (autofilled_form_signatures_.size() > kMaxRecentFormSignaturesToRemember)
     autofilled_form_signatures_.pop_back();
 
+  // Note that this may invalidate |data_model|, particularly if it is a Mac
+  // address book entry.
+  if (action == AutofillDriver::FORM_DATA_ACTION_FILL)
+    personal_data_->RecordUseOf(data_model);
+
   driver_->SendFormDataToRenderer(query_id, action, result);
 }
 
diff --git a/components/autofill/core/browser/autofill_manager.h b/components/autofill/core/browser/autofill_manager.h
index 6cd5169..e9bbb44 100644
--- a/components/autofill/core/browser/autofill_manager.h
+++ b/components/autofill/core/browser/autofill_manager.h
@@ -117,7 +117,9 @@
                                   const FormData& form,
                                   const FormFieldData& field,
                                   const CreditCard& credit_card);
-  void DidShowSuggestions(bool is_new_popup);
+  void DidShowSuggestions(bool is_new_popup,
+                          const FormData& form,
+                          const FormFieldData& field);
   void OnDidFillAutofillFormData(const base::TimeTicks& timestamp);
   void OnDidPreviewAutofillFormData();
 
@@ -240,22 +242,46 @@
   // Returns false if Autofill is disabled or if no Autofill data is available.
   bool RefreshDataModels();
 
-  // Unpacks |unique_id| and fills |form_group| and |variant| with the
-  // appropriate data source and variant index. Sets |is_credit_card| to true
-  // if |data_model| points to a CreditCard data model, false if it's a
-  // profile data model.
-  // Returns false if the unpacked id cannot be found.
-  bool GetProfileOrCreditCard(int unique_id,
-                              const AutofillDataModel** data_model,
-                              size_t* variant,
-                              bool* is_credit_card) const WARN_UNUSED_RESULT;
+  // Returns true if the unique_id refers to a credit card and false if
+  // it refers to a profile.
+  bool IsCreditCard(int unique_id);
+
+  // Gets the profile referred by |unique_id| and populates |variant|
+  // based on it. Returns true if the profile exists.
+  bool GetProfile(int unique_id,
+                  const AutofillProfile** profile,
+                  size_t* variant);
+
+  // Gets the credit card referred by |unique_id| and populates |variant|
+  // based on it. Returns true if the credit card exists.
+  bool GetCreditCard(int unique_id, const CreditCard** credit_card);
+
+  // Fills or previews the credit card form.
+  // Assumes the form and field are valid.
+  void FillOrPreviewCreditCardForm(
+      AutofillDriver::RendererFormDataAction action,
+      int query_id,
+      const FormData& form,
+      const FormFieldData& field,
+      const CreditCard& credit_card,
+      size_t variant);
+
+  // Fills or previews the profile form.
+  // Assumes the form and field are valid.
+  void FillOrPreviewProfileForm(
+      AutofillDriver::RendererFormDataAction action,
+      int query_id,
+      const FormData& form,
+      const FormFieldData& field,
+      const AutofillProfile& profile,
+      size_t variant);
 
   // Fills or previews |data_model| in the |form|.
   void FillOrPreviewDataModelForm(AutofillDriver::RendererFormDataAction action,
                                   int query_id,
                                   const FormData& form,
                                   const FormFieldData& field,
-                                  const AutofillDataModel* data_model,
+                                  const AutofillDataModel& data_model,
                                   size_t variant,
                                   bool is_credit_card);
 
@@ -400,8 +426,13 @@
                            DeterminePossibleFieldTypesForUploadStressTest);
   FRIEND_TEST_ALL_PREFIXES(AutofillManagerTest,
                            DisabledAutofillDispatchesError);
+  FRIEND_TEST_ALL_PREFIXES(AutofillMetricsTest, AddressFilledFormEvents);
+  FRIEND_TEST_ALL_PREFIXES(AutofillMetricsTest, AddressSubmittedFormEvents);
   FRIEND_TEST_ALL_PREFIXES(AutofillMetricsTest, AddressSuggestionsCount);
   FRIEND_TEST_ALL_PREFIXES(AutofillMetricsTest, AutofillIsEnabledAtPageLoad);
+  FRIEND_TEST_ALL_PREFIXES(AutofillMetricsTest, CreditCardSelectedFormEvents);
+  FRIEND_TEST_ALL_PREFIXES(AutofillMetricsTest, CreditCardFilledFormEvents);
+  FRIEND_TEST_ALL_PREFIXES(AutofillMetricsTest, CreditCardSubmittedFormEvents);
   FRIEND_TEST_ALL_PREFIXES(AutofillMetricsTest, DeveloperEngagement);
   FRIEND_TEST_ALL_PREFIXES(AutofillMetricsTest, FormFillDuration);
   FRIEND_TEST_ALL_PREFIXES(AutofillMetricsTest,
diff --git a/components/autofill/core/browser/autofill_metrics.cc b/components/autofill/core/browser/autofill_metrics.cc
index 60ce8bc..d1e7d9d 100644
--- a/components/autofill/core/browser/autofill_metrics.cc
+++ b/components/autofill/core/browser/autofill_metrics.cc
@@ -476,7 +476,13 @@
     : is_for_credit_card_(is_for_credit_card),
       is_server_data_available_(false),
       is_local_data_available_(false),
-      has_logged_interacted_(false) {};
+      has_logged_interacted_(false),
+      has_logged_suggestions_shown_(false),
+      has_logged_masked_server_card_suggestion_selected_(false),
+      has_logged_suggestion_filled_(false),
+      has_logged_submitted_(false),
+      logged_suggestion_filled_was_server_data_(false),
+      logged_suggestion_filled_was_masked_server_card_(false) {};
 
 void AutofillMetrics::FormEventLogger::OnDidInteractWithAutofillableForm() {
   if (!has_logged_interacted_) {
@@ -485,6 +491,92 @@
   }
 }
 
+void AutofillMetrics::FormEventLogger::OnDidShowSuggestions() {
+  Log(AutofillMetrics::FORM_EVENT_SUGGESTIONS_SHOWN);
+  if (!has_logged_suggestions_shown_) {
+    has_logged_suggestions_shown_ = true;
+    Log(AutofillMetrics::FORM_EVENT_SUGGESTIONS_SHOWN_ONCE);
+  }
+}
+
+void AutofillMetrics::FormEventLogger::OnDidSelectMaskedServerCardSuggestion() {
+  DCHECK(is_for_credit_card_);
+  Log(AutofillMetrics::FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SELECTED);
+  if (!has_logged_masked_server_card_suggestion_selected_) {
+    has_logged_masked_server_card_suggestion_selected_ = true;
+    Log(AutofillMetrics
+            ::FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SELECTED_ONCE);
+  }
+}
+
+void AutofillMetrics::FormEventLogger::OnDidFillSuggestion(
+    const CreditCard& credit_card) {
+  DCHECK(is_for_credit_card_);
+  if (credit_card.record_type() == CreditCard::MASKED_SERVER_CARD)
+    Log(AutofillMetrics::FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_FILLED);
+  else if (credit_card.record_type() == CreditCard::FULL_SERVER_CARD)
+    Log(AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_FILLED);
+  else
+    Log(AutofillMetrics::FORM_EVENT_LOCAL_SUGGESTION_FILLED);
+
+  if (!has_logged_suggestion_filled_) {
+    has_logged_suggestion_filled_ = true;
+    logged_suggestion_filled_was_server_data_ =
+        credit_card.record_type() == CreditCard::MASKED_SERVER_CARD ||
+        credit_card.record_type() == CreditCard::FULL_SERVER_CARD;
+    logged_suggestion_filled_was_masked_server_card_ =
+        credit_card.record_type() == CreditCard::MASKED_SERVER_CARD;
+    if (credit_card.record_type() == CreditCard::MASKED_SERVER_CARD) {
+      Log(AutofillMetrics
+              ::FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_FILLED_ONCE);
+    } else if (credit_card.record_type() == CreditCard::FULL_SERVER_CARD) {
+      Log(AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_FILLED_ONCE);
+    } else {
+      Log(AutofillMetrics::FORM_EVENT_LOCAL_SUGGESTION_FILLED_ONCE);
+    }
+  }
+}
+
+void AutofillMetrics::FormEventLogger::OnDidFillSuggestion(
+    const AutofillProfile& profile) {
+  DCHECK(!is_for_credit_card_);
+  if (profile.record_type() == AutofillProfile::SERVER_PROFILE)
+    Log(AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_FILLED);
+  else
+    Log(AutofillMetrics::FORM_EVENT_LOCAL_SUGGESTION_FILLED);
+
+  if (!has_logged_suggestion_filled_) {
+    has_logged_suggestion_filled_ = true;
+    logged_suggestion_filled_was_server_data_ =
+        profile.record_type() == AutofillProfile::SERVER_PROFILE;
+    Log(profile.record_type() == AutofillProfile::SERVER_PROFILE
+        ? AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_FILLED_ONCE
+        : AutofillMetrics::FORM_EVENT_LOCAL_SUGGESTION_FILLED_ONCE);
+  }
+}
+
+void AutofillMetrics::FormEventLogger::OnDidSubmitForm() {
+  // Not logging this kind of form if we haven't logged a user interaction.
+  if (!has_logged_interacted_)
+    return;
+
+  // Not logging twice.
+  if (has_logged_submitted_)
+    return;
+  has_logged_submitted_ = true;
+
+  if (!has_logged_suggestion_filled_) {
+    Log(AutofillMetrics::FORM_EVENT_NO_SUGGESTION_SUBMITTED_ONCE);
+  } else if (logged_suggestion_filled_was_masked_server_card_) {
+    Log(AutofillMetrics
+            ::FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SUBMITTED_ONCE);
+  } else if (logged_suggestion_filled_was_server_data_) {
+    Log(AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_SUBMITTED_ONCE);
+  } else {
+    Log(AutofillMetrics::FORM_EVENT_LOCAL_SUGGESTION_SUBMITTED_ONCE);
+  }
+}
+
 void AutofillMetrics::FormEventLogger::Log(FormEvent event) const {
   DCHECK_LT(event, NUM_FORM_EVENTS);
   std::string name("Autofill.FormEvents.");
diff --git a/components/autofill/core/browser/autofill_metrics.h b/components/autofill/core/browser/autofill_metrics.h
index e0aeab8d2c..c77794a 100644
--- a/components/autofill/core/browser/autofill_metrics.h
+++ b/components/autofill/core/browser/autofill_metrics.h
@@ -10,6 +10,8 @@
 
 #include "base/basictypes.h"
 #include "components/autofill/core/browser/autofill_client.h"
+#include "components/autofill/core/browser/autofill_profile.h"
+#include "components/autofill/core/browser/credit_card.h"
 #include "components/autofill/core/browser/field_types.h"
 
 namespace base {
@@ -249,10 +251,37 @@
     // User interacted with a field of this kind of form. Logged only once per
     // page load.
     FORM_EVENT_INTERACTED_ONCE = 0,
-    // TODO(waltercacau): Remove the 2 here once we add more events.
-    //   This circumvents an assertion that gets triggered when you have
-    //   only one bucket.
-    NUM_FORM_EVENTS = 2,
+    // A dropdown with suggestions was shown.
+    FORM_EVENT_SUGGESTIONS_SHOWN,
+    // Same as above, but recoreded only once per page load.
+    FORM_EVENT_SUGGESTIONS_SHOWN_ONCE,
+    // A local suggestion was used to fill the form.
+    FORM_EVENT_LOCAL_SUGGESTION_FILLED,
+    // A server suggestion was used to fill the form.
+    // When dealing with credit cards, this means a full server card was used
+    // to fill.
+    FORM_EVENT_SERVER_SUGGESTION_FILLED,
+    // A masked server card suggestion was used to fill the form.
+    FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_FILLED,
+    // A suggestion was used to fill the form. The origin type (local or server
+    // or masked server card) of the first selected within a page load will
+    // determine which of the following two will be fired.
+    FORM_EVENT_LOCAL_SUGGESTION_FILLED_ONCE,
+    FORM_EVENT_SERVER_SUGGESTION_FILLED_ONCE,
+    FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_FILLED_ONCE,
+    // A form was submitted. Depending on the user filling a local, server,
+    // masked server card or no suggestion one of the following will be
+    // triggered. Only one of the following four will be triggered per page
+    // load.
+    FORM_EVENT_NO_SUGGESTION_SUBMITTED_ONCE,
+    FORM_EVENT_LOCAL_SUGGESTION_SUBMITTED_ONCE,
+    FORM_EVENT_SERVER_SUGGESTION_SUBMITTED_ONCE,
+    FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SUBMITTED_ONCE,
+    // A masked server card suggestion was selected to fill the form.
+    FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SELECTED,
+    // Same as above but only triggered once per page load.
+    FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SELECTED_ONCE,
+    NUM_FORM_EVENTS,
   };
 
   // For measuring the network request time of various Wallet API calls. See
@@ -438,27 +467,41 @@
    public:
     FormEventLogger(bool is_for_credit_card);
 
-    // Should be called when the user interacted with an autofillable form.
-    void OnDidInteractWithAutofillableForm();
-
-    // Sets if server data is available or not. If not called assumed false.
     inline void set_is_server_data_available(bool is_server_data_available) {
       is_server_data_available_ = is_server_data_available;
     }
 
-    // Sets if server data is available or not. If not called assumed false.
     inline void set_is_local_data_available(bool is_local_data_available) {
       is_local_data_available_ = is_local_data_available;
     }
 
+    void OnDidInteractWithAutofillableForm();
+
+    void OnDidShowSuggestions();
+
+    void OnDidSelectMaskedServerCardSuggestion();
+
+    // In case of masked cards, caller must make sure this gets called before
+    // the card is upgraded to a full card.
+    void OnDidFillSuggestion(const CreditCard& credit_card);
+
+    void OnDidFillSuggestion(const AutofillProfile& profile);
+
+    void OnDidSubmitForm();
+
    private:
-    // Logs a form event.
     void Log(FormEvent event) const;
 
     bool is_for_credit_card_;
     bool is_server_data_available_;
     bool is_local_data_available_;
     bool has_logged_interacted_;
+    bool has_logged_suggestions_shown_;
+    bool has_logged_masked_server_card_suggestion_selected_;
+    bool has_logged_suggestion_filled_;
+    bool has_logged_submitted_;
+    bool logged_suggestion_filled_was_server_data_;
+    bool logged_suggestion_filled_was_masked_server_card_;
   };
 
  private:
diff --git a/components/autofill/core/browser/autofill_metrics_unittest.cc b/components/autofill/core/browser/autofill_metrics_unittest.cc
index c7c9589f..f249d08 100644
--- a/components/autofill/core/browser/autofill_metrics_unittest.cc
+++ b/components/autofill/core/browser/autofill_metrics_unittest.cc
@@ -19,6 +19,7 @@
 #include "components/autofill/core/browser/personal_data_manager.h"
 #include "components/autofill/core/browser/test_autofill_client.h"
 #include "components/autofill/core/browser/test_autofill_driver.h"
+#include "components/autofill/core/browser/wallet/real_pan_wallet_client.h"
 #include "components/autofill/core/browser/webdata/autofill_webdata_service.h"
 #include "components/autofill/core/common/autofill_pref_names.h"
 #include "components/autofill/core/common/form_data.h"
@@ -135,6 +136,7 @@
       CreditCard* credit_card = new CreditCard(
           CreditCard::MASKED_SERVER_CARD, "server_id");
       credit_card->set_guid("10000000-0000-0000-0000-000000000002");
+      credit_card->SetTypeForMaskedCard(kDiscoverCard);
       server_credit_cards_.push_back(credit_card);
     }
     if (include_full_server_credit_card) {
@@ -921,8 +923,8 @@
   }
 }
 
-// Test that we log interacted form event for credit cards only once.
-TEST_F(AutofillMetricsTest, CreditCardInteractedOnce) {
+// Test that we log interacted form event for credit cards related.
+TEST_F(AutofillMetricsTest, CreditCardInteractedFormEvents) {
   // Set up our form data.
   FormData form;
   form.name = ASCIIToUTF16("TestForm");
@@ -973,8 +975,445 @@
   }
 }
 
-// Test that we log interacted form event for address only once.
-TEST_F(AutofillMetricsTest, AddressInteractedOnce) {
+// Test that we log suggestion shown form events for credit cards.
+TEST_F(AutofillMetricsTest, CreditCardShownFormEvents) {
+  // Set up our form data.
+  FormData form;
+  form.name = ASCIIToUTF16("TestForm");
+  form.origin = GURL("http://example.com/form.html");
+  form.action = GURL("http://example.com/submit.html");
+  form.user_submitted = true;
+
+  FormFieldData field;
+  std::vector<ServerFieldType> field_types;
+  test::CreateTestFormField("Month", "card_month", "", "text", &field);
+  form.fields.push_back(field);
+  field_types.push_back(CREDIT_CARD_EXP_MONTH);
+  test::CreateTestFormField("Year", "card_year", "", "text", &field);
+  form.fields.push_back(field);
+  field_types.push_back(CREDIT_CARD_EXP_2_DIGIT_YEAR);
+  test::CreateTestFormField("Credit card", "card", "", "text", &field);
+  form.fields.push_back(field);
+  field_types.push_back(CREDIT_CARD_NUMBER);
+
+  // Simulate having seen this form on page load.
+  // |form_structure| will be owned by |autofill_manager_|.
+  autofill_manager_->AddSeenForm(form, field_types, field_types);
+
+  {
+    // Simulating new popup being shown.
+    base::HistogramTester histogram_tester;
+    autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form, field);
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.CreditCard",
+        AutofillMetrics::FORM_EVENT_SUGGESTIONS_SHOWN, 1);
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.CreditCard",
+        AutofillMetrics::FORM_EVENT_SUGGESTIONS_SHOWN_ONCE, 1);
+  }
+
+  // Reset the autofill manager state.
+  autofill_manager_->Reset();
+  autofill_manager_->AddSeenForm(form, field_types, field_types);
+
+  {
+    // Simulating two popups in the same page load.
+    base::HistogramTester histogram_tester;
+    autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form, field);
+    autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form, field);
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.CreditCard",
+        AutofillMetrics::FORM_EVENT_SUGGESTIONS_SHOWN, 2);
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.CreditCard",
+        AutofillMetrics::FORM_EVENT_SUGGESTIONS_SHOWN_ONCE, 1);
+  }
+
+  // Reset the autofill manager state.
+  autofill_manager_->Reset();
+  autofill_manager_->AddSeenForm(form, field_types, field_types);
+
+  {
+    // Simulating same popup being refreshed.
+    base::HistogramTester histogram_tester;
+    autofill_manager_->DidShowSuggestions(false /* is_new_popup */, form,
+                                          field);
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.CreditCard",
+        AutofillMetrics::FORM_EVENT_SUGGESTIONS_SHOWN, 0);
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.CreditCard",
+        AutofillMetrics::FORM_EVENT_SUGGESTIONS_SHOWN_ONCE, 0);
+  }
+}
+
+// Test that we log selected form event for credit cards.
+TEST_F(AutofillMetricsTest, CreditCardSelectedFormEvents) {
+  autofill_client_.GetPrefs()->SetBoolean(
+      prefs::kAutofillWalletSyncExperimentEnabled, true);
+  // Creating all kinds of cards.
+  personal_data_->RecreateCreditCards(
+      true /* include_local_credit_card */,
+      true /* include_masked_server_credit_card */,
+      true /* include_full_server_credit_card */);
+  // Set up our form data.
+  FormData form;
+  form.name = ASCIIToUTF16("TestForm");
+  form.origin = GURL("http://example.com/form.html");
+  form.action = GURL("http://example.com/submit.html");
+  form.user_submitted = true;
+
+  FormFieldData field;
+  std::vector<ServerFieldType> field_types;
+  test::CreateTestFormField("Month", "card_month", "", "text", &field);
+  form.fields.push_back(field);
+  field_types.push_back(CREDIT_CARD_EXP_MONTH);
+  test::CreateTestFormField("Year", "card_year", "", "text", &field);
+  form.fields.push_back(field);
+  field_types.push_back(CREDIT_CARD_EXP_2_DIGIT_YEAR);
+  test::CreateTestFormField("Credit card", "card", "", "text", &field);
+  form.fields.push_back(field);
+  field_types.push_back(CREDIT_CARD_NUMBER);
+
+  // Simulate having seen this form on page load.
+  // |form_structure| will be owned by |autofill_manager_|.
+  autofill_manager_->AddSeenForm(form, field_types, field_types);
+
+  {
+    // Simulating selecting a masked card server suggestion.
+    base::HistogramTester histogram_tester;
+    SuggestionBackendID guid(
+        "10000000-0000-0000-0000-000000000002", 0); // masked server card
+    autofill_manager_->FillOrPreviewForm(
+        AutofillDriver::FORM_DATA_ACTION_FILL,
+        0, form, form.fields.front(),
+        autofill_manager_->MakeFrontendID(guid, SuggestionBackendID()));
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.CreditCard",
+        AutofillMetrics::FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SELECTED, 1);
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.CreditCard",
+        AutofillMetrics::FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SELECTED_ONCE,
+        1);
+  }
+
+  // Reset the autofill manager state.
+  autofill_manager_->Reset();
+  autofill_manager_->AddSeenForm(form, field_types, field_types);
+
+  {
+    // Simulating selecting multiple times a masked card server.
+    base::HistogramTester histogram_tester;
+    SuggestionBackendID guid(
+        "10000000-0000-0000-0000-000000000002", 0); // masked server card
+    autofill_manager_->FillOrPreviewForm(
+        AutofillDriver::FORM_DATA_ACTION_FILL,
+        0, form, form.fields.front(),
+        autofill_manager_->MakeFrontendID(guid, SuggestionBackendID()));
+    autofill_manager_->FillOrPreviewForm(
+        AutofillDriver::FORM_DATA_ACTION_FILL,
+        0, form, form.fields.front(),
+        autofill_manager_->MakeFrontendID(guid, SuggestionBackendID()));
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.CreditCard",
+        AutofillMetrics::FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SELECTED, 2);
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.CreditCard",
+        AutofillMetrics::FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SELECTED_ONCE,
+        1);
+  }
+}
+
+// Test that we log filled form events for credit cards.
+TEST_F(AutofillMetricsTest, CreditCardFilledFormEvents) {
+  autofill_client_.GetPrefs()->SetBoolean(
+      prefs::kAutofillWalletSyncExperimentEnabled, true);
+  // Creating all kinds of cards.
+  personal_data_->RecreateCreditCards(
+      true /* include_local_credit_card */,
+      true /* include_masked_server_credit_card */,
+      true /* include_full_server_credit_card */);
+  // Set up our form data.
+  FormData form;
+  form.name = ASCIIToUTF16("TestForm");
+  form.origin = GURL("http://example.com/form.html");
+  form.action = GURL("http://example.com/submit.html");
+  form.user_submitted = true;
+
+  FormFieldData field;
+  std::vector<ServerFieldType> field_types;
+  test::CreateTestFormField("Month", "card_month", "", "text", &field);
+  form.fields.push_back(field);
+  field_types.push_back(CREDIT_CARD_EXP_MONTH);
+  test::CreateTestFormField("Year", "card_year", "", "text", &field);
+  form.fields.push_back(field);
+  field_types.push_back(CREDIT_CARD_EXP_2_DIGIT_YEAR);
+  test::CreateTestFormField("Credit card", "card", "", "text", &field);
+  form.fields.push_back(field);
+  field_types.push_back(CREDIT_CARD_NUMBER);
+
+  // Simulate having seen this form on page load.
+  // |form_structure| will be owned by |autofill_manager_|.
+  autofill_manager_->AddSeenForm(form, field_types, field_types);
+
+  {
+    // Simulating filling a local card suggestion.
+    base::HistogramTester histogram_tester;
+    SuggestionBackendID guid(
+        "10000000-0000-0000-0000-000000000001", 0); // local card
+    autofill_manager_->FillOrPreviewForm(
+        AutofillDriver::FORM_DATA_ACTION_FILL,
+        0, form, form.fields.front(),
+        autofill_manager_->MakeFrontendID(guid, SuggestionBackendID()));
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.CreditCard",
+        AutofillMetrics::FORM_EVENT_LOCAL_SUGGESTION_FILLED, 1);
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.CreditCard",
+        AutofillMetrics::FORM_EVENT_LOCAL_SUGGESTION_FILLED_ONCE, 1);
+  }
+
+  // Reset the autofill manager state.
+  autofill_manager_->Reset();
+  autofill_manager_->AddSeenForm(form, field_types, field_types);
+
+  {
+    // Simulating filling a masked card server suggestion.
+    base::HistogramTester histogram_tester;
+    SuggestionBackendID guid(
+        "10000000-0000-0000-0000-000000000002", 0); // masked server card
+    autofill_manager_->FillOrPreviewForm(
+        AutofillDriver::FORM_DATA_ACTION_FILL,
+        0, form, form.fields.front(),
+        autofill_manager_->MakeFrontendID(guid, SuggestionBackendID()));
+    autofill_manager_->OnDidGetRealPan("6011000990139424");
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.CreditCard",
+        AutofillMetrics::FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_FILLED, 1);
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.CreditCard",
+        AutofillMetrics::FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_FILLED_ONCE,
+        1);
+  }
+
+  // Recreating cards as the previous test should have upgraded the masked
+  // card to a full card.
+  personal_data_->RecreateCreditCards(
+      true /* include_local_credit_card */,
+      true /* include_masked_server_credit_card */,
+      true /* include_full_server_credit_card */);
+
+  // Reset the autofill manager state.
+  autofill_manager_->Reset();
+  autofill_manager_->AddSeenForm(form, field_types, field_types);
+
+  {
+    // Simulating filling a full card server suggestion.
+    base::HistogramTester histogram_tester;
+    SuggestionBackendID guid(
+        "10000000-0000-0000-0000-000000000003", 0); // full server card
+    autofill_manager_->FillOrPreviewForm(
+        AutofillDriver::FORM_DATA_ACTION_FILL,
+        0, form, form.fields.front(),
+        autofill_manager_->MakeFrontendID(guid, SuggestionBackendID()));
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.CreditCard",
+        AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_FILLED, 1);
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.CreditCard",
+        AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_FILLED_ONCE, 1);
+  }
+
+  // Reset the autofill manager state.
+  autofill_manager_->Reset();
+  autofill_manager_->AddSeenForm(form, field_types, field_types);
+
+  {
+    // Simulating filling multiple times.
+    base::HistogramTester histogram_tester;
+    SuggestionBackendID guid(
+        "10000000-0000-0000-0000-000000000001", 0); // local card
+    autofill_manager_->FillOrPreviewForm(
+        AutofillDriver::FORM_DATA_ACTION_FILL,
+        0, form, form.fields.front(),
+        autofill_manager_->MakeFrontendID(guid, SuggestionBackendID()));
+    autofill_manager_->FillOrPreviewForm(
+        AutofillDriver::FORM_DATA_ACTION_FILL,
+        0, form, form.fields.front(),
+        autofill_manager_->MakeFrontendID(guid, SuggestionBackendID()));
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.CreditCard",
+        AutofillMetrics::FORM_EVENT_LOCAL_SUGGESTION_FILLED, 2);
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.CreditCard",
+        AutofillMetrics::FORM_EVENT_LOCAL_SUGGESTION_FILLED_ONCE, 1);
+  }
+}
+
+// Test that we log submitted form events for credit cards.
+TEST_F(AutofillMetricsTest, CreditCardSubmittedFormEvents) {
+  autofill_client_.GetPrefs()->SetBoolean(
+      prefs::kAutofillWalletSyncExperimentEnabled, true);
+  // Creating all kinds of cards.
+  personal_data_->RecreateCreditCards(
+      true /* include_local_credit_card */,
+      true /* include_masked_server_credit_card */,
+      true /* include_full_server_credit_card */);
+  // Set up our form data.
+  FormData form;
+  form.name = ASCIIToUTF16("TestForm");
+  form.origin = GURL("http://example.com/form.html");
+  form.action = GURL("http://example.com/submit.html");
+  form.user_submitted = true;
+
+  FormFieldData field;
+  std::vector<ServerFieldType> field_types;
+  test::CreateTestFormField("Month", "card_month", "", "text", &field);
+  form.fields.push_back(field);
+  field_types.push_back(CREDIT_CARD_EXP_MONTH);
+  test::CreateTestFormField("Year", "card_year", "", "text", &field);
+  form.fields.push_back(field);
+  field_types.push_back(CREDIT_CARD_EXP_2_DIGIT_YEAR);
+  test::CreateTestFormField("Credit card", "card", "", "text", &field);
+  form.fields.push_back(field);
+  field_types.push_back(CREDIT_CARD_NUMBER);
+
+  // Simulate having seen this form on page load.
+  // |form_structure| will be owned by |autofill_manager_|.
+  autofill_manager_->AddSeenForm(form, field_types, field_types);
+
+  {
+    // Simulating submission with no filled data.
+    base::HistogramTester histogram_tester;
+    autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::Rect(),
+                                                false);
+    autofill_manager_->FormSubmitted(form, TimeTicks::Now());
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.CreditCard",
+        AutofillMetrics::FORM_EVENT_NO_SUGGESTION_SUBMITTED_ONCE, 1);
+  }
+
+  // Reset the autofill manager state.
+  autofill_manager_->Reset();
+  autofill_manager_->AddSeenForm(form, field_types, field_types);
+
+  {
+    // Simulating submission with filled local data.
+    base::HistogramTester histogram_tester;
+    autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::Rect(),
+                                                false);
+    SuggestionBackendID guid(
+        "10000000-0000-0000-0000-000000000001", 0); // local card
+    autofill_manager_->FillOrPreviewForm(
+        AutofillDriver::FORM_DATA_ACTION_FILL,
+        0, form, form.fields.front(),
+        autofill_manager_->MakeFrontendID(guid, SuggestionBackendID()));
+    autofill_manager_->FormSubmitted(form, TimeTicks::Now());
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.CreditCard",
+        AutofillMetrics::FORM_EVENT_LOCAL_SUGGESTION_SUBMITTED_ONCE, 1);
+  }
+
+  // Reset the autofill manager state.
+  autofill_manager_->Reset();
+  autofill_manager_->AddSeenForm(form, field_types, field_types);
+
+  {
+    // Simulating submission with filled server data.
+    base::HistogramTester histogram_tester;
+    autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::Rect(),
+                                                false);
+    SuggestionBackendID guid(
+        "10000000-0000-0000-0000-000000000003", 0); // full server card
+    autofill_manager_->FillOrPreviewForm(
+        AutofillDriver::FORM_DATA_ACTION_FILL,
+        0, form, form.fields.front(),
+        autofill_manager_->MakeFrontendID(guid, SuggestionBackendID()));
+    autofill_manager_->FormSubmitted(form, TimeTicks::Now());
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.CreditCard",
+        AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_SUBMITTED_ONCE, 1);
+  }
+
+  // Reset the autofill manager state.
+  autofill_manager_->Reset();
+  autofill_manager_->AddSeenForm(form, field_types, field_types);
+
+  {
+    // Simulating submission with a masked card server suggestion.
+    base::HistogramTester histogram_tester;
+    SuggestionBackendID guid(
+        "10000000-0000-0000-0000-000000000002", 0); // masked server card
+    autofill_manager_->FillOrPreviewForm(
+        AutofillDriver::FORM_DATA_ACTION_FILL,
+        0, form, form.fields.front(),
+        autofill_manager_->MakeFrontendID(guid, SuggestionBackendID()));
+    autofill_manager_->OnDidGetRealPan("6011000990139424");
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.CreditCard",
+        AutofillMetrics::FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_FILLED, 1);
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.CreditCard",
+        AutofillMetrics::FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_FILLED_ONCE,
+        1);
+  }
+
+  // Recreating cards as the previous test should have upgraded the masked
+  // card to a full card.
+  personal_data_->RecreateCreditCards(
+      true /* include_local_credit_card */,
+      true /* include_masked_server_credit_card */,
+      true /* include_full_server_credit_card */);
+
+  // Reset the autofill manager state.
+  autofill_manager_->Reset();
+  autofill_manager_->AddSeenForm(form, field_types, field_types);
+
+  {
+    // Simulating multiple submissions.
+    base::HistogramTester histogram_tester;
+    autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::Rect(),
+                                                false);
+    autofill_manager_->FormSubmitted(form, TimeTicks::Now());
+    autofill_manager_->FormSubmitted(form, TimeTicks::Now());
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.CreditCard",
+        AutofillMetrics::FORM_EVENT_NO_SUGGESTION_SUBMITTED_ONCE, 1);
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.CreditCard",
+        AutofillMetrics::FORM_EVENT_LOCAL_SUGGESTION_SUBMITTED_ONCE, 0);
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.CreditCard",
+        AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_SUBMITTED_ONCE, 0);
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.CreditCard",
+        AutofillMetrics
+            ::FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SUBMITTED_ONCE,
+        0);
+  }
+
+  // Reset the autofill manager state.
+  autofill_manager_->Reset();
+  autofill_manager_->AddSeenForm(form, field_types, field_types);
+
+  {
+    // Simulating submission without previous interaction.
+    base::HistogramTester histogram_tester;
+    autofill_manager_->FormSubmitted(form, TimeTicks::Now());
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.CreditCard",
+        AutofillMetrics::FORM_EVENT_NO_SUGGESTION_SUBMITTED_ONCE, 0);
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.CreditCard",
+        AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_SUBMITTED_ONCE, 0);
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.CreditCard",
+        AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_SUBMITTED_ONCE, 0);
+  }
+}
+
+// Test that we log interacted form events for address.
+TEST_F(AutofillMetricsTest, AddressInteractedFormEvents) {
   // Set up our form data.
   FormData form;
   form.name = ASCIIToUTF16("TestForm");
@@ -1025,6 +1464,299 @@
   }
 }
 
+// Test that we log suggestion shown form events for address.
+TEST_F(AutofillMetricsTest, AddressShownFormEvents) {
+  // Creating all kinds of profiles.
+  personal_data_->RecreateProfiles(true /* include_local_profile */,
+                                   true /* include_server_profile */);
+  // Set up our form data.
+  FormData form;
+  form.name = ASCIIToUTF16("TestForm");
+  form.origin = GURL("http://example.com/form.html");
+  form.action = GURL("http://example.com/submit.html");
+  form.user_submitted = true;
+
+  FormFieldData field;
+  std::vector<ServerFieldType> field_types;
+  test::CreateTestFormField("State", "state", "", "text", &field);
+  form.fields.push_back(field);
+  field_types.push_back(ADDRESS_HOME_STATE);
+  test::CreateTestFormField("City", "city", "", "text", &field);
+  form.fields.push_back(field);
+  field_types.push_back(ADDRESS_HOME_CITY);
+  test::CreateTestFormField("Street", "street", "", "text", &field);
+  form.fields.push_back(field);
+  field_types.push_back(ADDRESS_HOME_STREET_ADDRESS);
+
+  // Simulate having seen this form on page load.
+  // |form_structure| will be owned by |autofill_manager_|.
+  autofill_manager_->AddSeenForm(form, field_types, field_types);
+
+  {
+    // Simulating new popup being shown.
+    base::HistogramTester histogram_tester;
+    autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form, field);
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.Address",
+        AutofillMetrics::FORM_EVENT_SUGGESTIONS_SHOWN, 1);
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.Address",
+        AutofillMetrics::FORM_EVENT_SUGGESTIONS_SHOWN_ONCE, 1);
+  }
+
+  // Reset the autofill manager state.
+  autofill_manager_->Reset();
+  autofill_manager_->AddSeenForm(form, field_types, field_types);
+
+  {
+    // Simulating two popups in the same page load.
+    base::HistogramTester histogram_tester;
+    autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form, field);
+    autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form, field);
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.Address",
+        AutofillMetrics::FORM_EVENT_SUGGESTIONS_SHOWN, 2);
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.Address",
+        AutofillMetrics::FORM_EVENT_SUGGESTIONS_SHOWN_ONCE, 1);
+  }
+
+  // Reset the autofill manager state.
+  autofill_manager_->Reset();
+  autofill_manager_->AddSeenForm(form, field_types, field_types);
+
+  {
+    // Simulating same popup being refreshed.
+    base::HistogramTester histogram_tester;
+    autofill_manager_->DidShowSuggestions(false /* is_new_popup */, form,
+                                          field);
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.Address",
+        AutofillMetrics::FORM_EVENT_SUGGESTIONS_SHOWN, 0);
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.Address",
+        AutofillMetrics::FORM_EVENT_SUGGESTIONS_SHOWN_ONCE, 0);
+  }
+}
+
+// Test that we log filled form events for address.
+TEST_F(AutofillMetricsTest, AddressFilledFormEvents) {
+  autofill_client_.GetPrefs()->SetBoolean(
+      prefs::kAutofillWalletSyncExperimentEnabled, true);
+  // Creating all kinds of profiles.
+  personal_data_->RecreateProfiles(true /* include_local_profile */,
+                                   true /* include_server_profile */);
+  // Set up our form data.
+  FormData form;
+  form.name = ASCIIToUTF16("TestForm");
+  form.origin = GURL("http://example.com/form.html");
+  form.action = GURL("http://example.com/submit.html");
+  form.user_submitted = true;
+
+  FormFieldData field;
+  std::vector<ServerFieldType> field_types;
+  test::CreateTestFormField("State", "state", "", "text", &field);
+  form.fields.push_back(field);
+  field_types.push_back(ADDRESS_HOME_STATE);
+  test::CreateTestFormField("City", "city", "", "text", &field);
+  form.fields.push_back(field);
+  field_types.push_back(ADDRESS_HOME_CITY);
+  test::CreateTestFormField("Street", "street", "", "text", &field);
+  form.fields.push_back(field);
+  field_types.push_back(ADDRESS_HOME_STREET_ADDRESS);
+
+  // Simulate having seen this form on page load.
+  // |form_structure| will be owned by |autofill_manager_|.
+  autofill_manager_->AddSeenForm(form, field_types, field_types);
+
+  {
+    // Simulating selecting/filling a local profile suggestion.
+    base::HistogramTester histogram_tester;
+    SuggestionBackendID guid(
+        "00000000-0000-0000-0000-000000000001", 0); // local profile
+    autofill_manager_->FillOrPreviewForm(
+        AutofillDriver::FORM_DATA_ACTION_FILL,
+        0, form, form.fields.front(),
+        autofill_manager_->MakeFrontendID(SuggestionBackendID(), guid));
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.Address",
+        AutofillMetrics::FORM_EVENT_LOCAL_SUGGESTION_FILLED, 1);
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.Address",
+        AutofillMetrics::FORM_EVENT_LOCAL_SUGGESTION_FILLED_ONCE, 1);
+  }
+
+  // Reset the autofill manager state.
+  autofill_manager_->Reset();
+  autofill_manager_->AddSeenForm(form, field_types, field_types);
+
+  {
+    // Simulating selecting/filling a server profile suggestion.
+    base::HistogramTester histogram_tester;
+    SuggestionBackendID guid(
+        "00000000-0000-0000-0000-000000000002", 0); // server profile
+    autofill_manager_->FillOrPreviewForm(
+        AutofillDriver::FORM_DATA_ACTION_FILL,
+        0, form, form.fields.front(),
+        autofill_manager_->MakeFrontendID(SuggestionBackendID(), guid));
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.Address",
+        AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_FILLED, 1);
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.Address",
+        AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_FILLED_ONCE, 1);
+  }
+
+  // Reset the autofill manager state.
+  autofill_manager_->Reset();
+  autofill_manager_->AddSeenForm(form, field_types, field_types);
+
+  {
+    // Simulating selecting/filling a local profile suggestion.
+    base::HistogramTester histogram_tester;
+    SuggestionBackendID guid(
+        "00000000-0000-0000-0000-000000000001", 0); // local profile
+    autofill_manager_->FillOrPreviewForm(
+        AutofillDriver::FORM_DATA_ACTION_FILL,
+        0, form, form.fields.front(),
+        autofill_manager_->MakeFrontendID(SuggestionBackendID(), guid));
+    autofill_manager_->FillOrPreviewForm(
+        AutofillDriver::FORM_DATA_ACTION_FILL,
+        0, form, form.fields.front(),
+        autofill_manager_->MakeFrontendID(SuggestionBackendID(), guid));
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.Address",
+        AutofillMetrics::FORM_EVENT_LOCAL_SUGGESTION_FILLED, 2);
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.Address",
+        AutofillMetrics::FORM_EVENT_LOCAL_SUGGESTION_FILLED_ONCE, 1);
+  }
+}
+
+// Test that we log submitted form events for address.
+TEST_F(AutofillMetricsTest, AddressSubmittedFormEvents) {
+  autofill_client_.GetPrefs()->SetBoolean(
+      prefs::kAutofillWalletSyncExperimentEnabled, true);
+  // Creating all kinds of profiles.
+  personal_data_->RecreateProfiles(true /* include_local_profile */,
+                                   true /* include_server_profile */);
+  // Set up our form data.
+  FormData form;
+  form.name = ASCIIToUTF16("TestForm");
+  form.origin = GURL("http://example.com/form.html");
+  form.action = GURL("http://example.com/submit.html");
+  form.user_submitted = true;
+
+  FormFieldData field;
+  std::vector<ServerFieldType> field_types;
+  test::CreateTestFormField("State", "state", "", "text", &field);
+  form.fields.push_back(field);
+  field_types.push_back(ADDRESS_HOME_STATE);
+  test::CreateTestFormField("City", "city", "", "text", &field);
+  form.fields.push_back(field);
+  field_types.push_back(ADDRESS_HOME_CITY);
+  test::CreateTestFormField("Street", "street", "", "text", &field);
+  form.fields.push_back(field);
+  field_types.push_back(ADDRESS_HOME_STREET_ADDRESS);
+
+  // Simulate having seen this form on page load.
+  // |form_structure| will be owned by |autofill_manager_|.
+  autofill_manager_->AddSeenForm(form, field_types, field_types);
+
+  {
+    // Simulating submission with no filled data.
+    base::HistogramTester histogram_tester;
+    autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::Rect(),
+                                                false);
+    autofill_manager_->FormSubmitted(form, TimeTicks::Now());
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.Address",
+        AutofillMetrics::FORM_EVENT_NO_SUGGESTION_SUBMITTED_ONCE, 1);
+  }
+
+  // Reset the autofill manager state.
+  autofill_manager_->Reset();
+  autofill_manager_->AddSeenForm(form, field_types, field_types);
+
+  {
+    // Simulating submission with filled local data.
+    base::HistogramTester histogram_tester;
+    autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::Rect(),
+                                                false);
+    SuggestionBackendID guid(
+        "00000000-0000-0000-0000-000000000001", 0); // local profile
+    autofill_manager_->FillOrPreviewForm(
+        AutofillDriver::FORM_DATA_ACTION_FILL,
+        0, form, form.fields.front(),
+        autofill_manager_->MakeFrontendID(SuggestionBackendID(), guid));
+    autofill_manager_->FormSubmitted(form, TimeTicks::Now());
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.Address",
+        AutofillMetrics::FORM_EVENT_LOCAL_SUGGESTION_SUBMITTED_ONCE, 1);
+  }
+
+  // Reset the autofill manager state.
+  autofill_manager_->Reset();
+  autofill_manager_->AddSeenForm(form, field_types, field_types);
+
+  {
+    // Simulating submission with filled server data.
+    base::HistogramTester histogram_tester;
+    autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::Rect(),
+                                                false);
+    SuggestionBackendID guid(
+        "00000000-0000-0000-0000-000000000002", 0); // server profile
+    autofill_manager_->FillOrPreviewForm(
+        AutofillDriver::FORM_DATA_ACTION_FILL,
+        0, form, form.fields.front(),
+        autofill_manager_->MakeFrontendID(SuggestionBackendID(), guid));
+    autofill_manager_->FormSubmitted(form, TimeTicks::Now());
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.Address",
+        AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_SUBMITTED_ONCE, 1);
+  }
+
+  // Reset the autofill manager state.
+  autofill_manager_->Reset();
+  autofill_manager_->AddSeenForm(form, field_types, field_types);
+
+  {
+    // Simulating multiple submissions.
+    base::HistogramTester histogram_tester;
+    autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::Rect(),
+                                                false);
+    autofill_manager_->FormSubmitted(form, TimeTicks::Now());
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.Address",
+        AutofillMetrics::FORM_EVENT_NO_SUGGESTION_SUBMITTED_ONCE, 1);
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.Address",
+        AutofillMetrics::FORM_EVENT_LOCAL_SUGGESTION_SUBMITTED_ONCE, 0);
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.Address",
+        AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_SUBMITTED_ONCE, 0);
+  }
+
+  // Reset the autofill manager state.
+  autofill_manager_->Reset();
+  autofill_manager_->AddSeenForm(form, field_types, field_types);
+
+  {
+    // Simulating submission without previous interaction.
+    base::HistogramTester histogram_tester;
+    autofill_manager_->FormSubmitted(form, TimeTicks::Now());
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.Address",
+        AutofillMetrics::FORM_EVENT_NO_SUGGESTION_SUBMITTED_ONCE, 0);
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.Address",
+        AutofillMetrics::FORM_EVENT_LOCAL_SUGGESTION_SUBMITTED_ONCE, 0);
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.Address",
+        AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_SUBMITTED_ONCE, 0);
+  }
+}
+
 // Test that we log interacted form event for credit cards only once.
 TEST_F(AutofillMetricsTest, CreditCardFormEventsAreSegmented) {
   autofill_client_.GetPrefs()->SetBoolean(
@@ -1413,8 +2145,8 @@
   // keystrokes in a single field).
   {
     base::HistogramTester histogram_tester;
-    autofill_manager_->DidShowSuggestions(true);
-    autofill_manager_->DidShowSuggestions(false);
+    autofill_manager_->DidShowSuggestions(true, form, field);
+    autofill_manager_->DidShowSuggestions(false, form, field);
     histogram_tester.ExpectBucketCount("Autofill.UserHappiness",
                                        AutofillMetrics::SUGGESTIONS_SHOWN, 1);
     histogram_tester.ExpectBucketCount(
@@ -1424,7 +2156,7 @@
   // Simulate suggestions shown for a different field.
   {
     base::HistogramTester histogram_tester;
-    autofill_manager_->DidShowSuggestions(true);
+    autofill_manager_->DidShowSuggestions(true, form, form.fields[1]);
     histogram_tester.ExpectUniqueSample("Autofill.UserHappiness",
                                         AutofillMetrics::SUGGESTIONS_SHOWN, 1);
   }
diff --git a/components/autofill/core/browser/credit_card.cc b/components/autofill/core/browser/credit_card.cc
index b51ecd5..34b78b2 100644
--- a/components/autofill/core/browser/credit_card.cc
+++ b/components/autofill/core/browser/credit_card.cc
@@ -185,6 +185,9 @@
 base::string16 CreditCard::TypeForDisplay(const std::string& type) {
   if (kGenericCard == type)
     return l10n_util::GetStringUTF16(IDS_AUTOFILL_CC_GENERIC);
+  if (kAmericanExpressCard == type)
+    return l10n_util::GetStringUTF16(IDS_AUTOFILL_CC_AMEX_SHORT);
+
   return ::autofill::TypeForFill(type);
 }
 
diff --git a/components/autofill/core/browser/form_structure.cc b/components/autofill/core/browser/form_structure.cc
index c546a11..6a8ef7c4e 100644
--- a/components/autofill/core/browser/form_structure.cc
+++ b/components/autofill/core/browser/form_structure.cc
@@ -624,17 +624,17 @@
 }
 
 // static
-void FormStructure::GetFieldTypePredictions(
-    const std::vector<FormStructure*>& form_structures,
-    std::vector<FormDataPredictions>* forms) {
-  forms->clear();
-  forms->reserve(form_structures.size());
+std::vector<FormDataPredictions> FormStructure::GetFieldTypePredictions(
+    const std::vector<FormStructure*>& form_structures) {
+  std::vector<FormDataPredictions> forms;
+  forms.reserve(form_structures.size());
   for (size_t i = 0; i < form_structures.size(); ++i) {
     FormStructure* form_structure = form_structures[i];
     FormDataPredictions form;
     form.data.name = form_structure->form_name_;
     form.data.origin = form_structure->source_url_;
     form.data.action = form_structure->target_url_;
+    form.data.is_form_tag = form_structure->is_form_tag_;
     form.signature = form_structure->FormSignature();
 
     for (std::vector<AutofillField*>::const_iterator field =
@@ -652,8 +652,9 @@
       form.fields.push_back(annotated_field);
     }
 
-    forms->push_back(form);
+    forms.push_back(form);
   }
+  return forms;
 }
 
 std::string FormStructure::FormSignature() const {
@@ -1208,8 +1209,11 @@
       if (AutofillType(current_type).group() == PHONE_HOME)
         already_saw_current_type = false;
 
-      // Ignore non-focusable field while inferring boundaries between sections.
-      if (!field->is_focusable)
+      // Ignore non-focusable field and presentation role fields while inferring
+      // boundaries between sections.
+      bool ignored_field = !field->is_focusable ||
+          field->role == FormFieldData::ROLE_ATTRIBUTE_PRESENTATION;
+      if (ignored_field)
         already_saw_current_type = false;
 
       // Some forms have adjacent fields of the same type.  Two common examples:
@@ -1224,20 +1228,23 @@
       if (current_type == previous_type)
         already_saw_current_type = false;
 
-      previous_type = current_type;
-
       if (current_type != UNKNOWN_TYPE && already_saw_current_type) {
         // We reached the end of a section, so start a new section.
         seen_types.clear();
         current_section = field->unique_name();
       }
 
-      // Only consider a type "seen" if it was focusable. Some forms have
+      // Only consider a type "seen" if it was not ignored. Some forms have
       // sections for different locales, only one of which is enabled at a
       // time. Each section may duplicate some information (e.g. postal code)
       // and we don't want that to cause section splits.
-      if (field->is_focusable)
+      // Also only set |previous_type| when the field was not ignored. This
+      // prevents ignored fields from breaking up fields that are otherwise
+      // adjacent.
+      if (!ignored_field) {
         seen_types.insert(current_type);
+        previous_type = current_type;
+      }
 
       field->set_section(base::UTF16ToUTF8(current_section));
     }
diff --git a/components/autofill/core/browser/form_structure.h b/components/autofill/core/browser/form_structure.h
index 119117a..2234719 100644
--- a/components/autofill/core/browser/form_structure.h
+++ b/components/autofill/core/browser/form_structure.h
@@ -76,11 +76,10 @@
   static void ParseQueryResponse(const std::string& response_xml,
                                  const std::vector<FormStructure*>& forms);
 
-  // Fills |forms| with the details from the given |form_structures| and their
-  // fields' predicted types.
-  static void GetFieldTypePredictions(
-      const std::vector<FormStructure*>& form_structures,
-      std::vector<FormDataPredictions>* forms);
+  // Returns predictions using the details from the given |form_structures| and
+  // their fields' predicted types.
+  static std::vector<FormDataPredictions> GetFieldTypePredictions(
+      const std::vector<FormStructure*>& form_structures);
 
   // The unique signature for this form, composed of the target url domain,
   // the form name, and the form field names in a 64-bit hash.
diff --git a/components/autofill/core/browser/personal_data_manager.cc b/components/autofill/core/browser/personal_data_manager.cc
index 7f5599a..ea3cea7 100644
--- a/components/autofill/core/browser/personal_data_manager.cc
+++ b/components/autofill/core/browser/personal_data_manager.cc
@@ -217,6 +217,18 @@
   return a.second < b.second;
 }
 
+// Ranks two data models according to their recency of use. Currently this will
+// place all server (Wallet) cards and addresses below all locally saved ones,
+// which is probably not what we want. TODO(estade): figure out relative ranking
+// of server data.
+bool RankByMfu(const AutofillDataModel* a, const AutofillDataModel* b) {
+  if (a->use_count() != b->use_count())
+    return a->use_count() > b->use_count();
+
+  // Ties are broken by MRU.
+  return a->use_date() > b->use_date();
+}
+
 }  // namespace
 
 PersonalDataManager::PersonalDataManager(const std::string& app_locale)
@@ -717,13 +729,16 @@
   base::string16 field_contents_canon =
       AutofillProfile::CanonicalizeProfileString(field_contents);
 
+  std::vector<AutofillProfile*> profiles = GetProfiles(true);
+  std::sort(profiles.begin(), profiles.end(), RankByMfu);
+
   if (field_is_autofilled) {
     // This field was previously autofilled. In this case, suggesting results
     // based on prefix is useless since it will be the same thing. Instead,
     // check for a field that may have multiple possible values (for example,
     // multiple names for the same address) and suggest the alternates. This
     // allows for easy correction of the data.
-    for (AutofillProfile* profile : GetProfiles(true)) {
+    for (AutofillProfile* profile : profiles) {
       std::vector<base::string16> values =
           GetMultiInfoInOneLine(profile, type, app_locale_);
 
@@ -750,7 +765,7 @@
   } else {
     // Match based on a prefix search.
     std::vector<AutofillProfile*> matched_profiles;
-    for (AutofillProfile* profile : GetProfiles(true)) {
+    for (AutofillProfile* profile : profiles) {
       std::vector<base::string16> values =
           GetMultiInfoInOneLine(profile, type, app_locale_);
       for (size_t i = 0; i < values.size(); i++) {
@@ -826,6 +841,8 @@
     }
   }
 
+  cards_to_suggest.sort(RankByMfu);
+
   std::vector<Suggestion> suggestions;
   for (const CreditCard* credit_card : cards_to_suggest) {
     // Make a new suggestion.
diff --git a/components/autofill/core/browser/personal_data_manager.h b/components/autofill/core/browser/personal_data_manager.h
index a63b318..f1fc9c17 100644
--- a/components/autofill/core/browser/personal_data_manager.h
+++ b/components/autofill/core/browser/personal_data_manager.h
@@ -82,7 +82,8 @@
                       scoped_ptr<CreditCard>* credit_card);
 
   // Called to indicate |data_model| was used (to fill in a form). Updates
-  // the database accordingly.
+  // the database accordingly. Can invalidate |data_model|, particularly if
+  // it's a Mac address book entry.
   virtual void RecordUseOf(const AutofillDataModel& data_model);
 
   // Saves |imported_profile| to the WebDB if it exists. Returns the guid of
diff --git a/components/autofill/core/browser/personal_data_manager_unittest.cc b/components/autofill/core/browser/personal_data_manager_unittest.cc
index 8461db5..1ef66b5 100644
--- a/components/autofill/core/browser/personal_data_manager_unittest.cc
+++ b/components/autofill/core/browser/personal_data_manager_unittest.cc
@@ -2709,21 +2709,22 @@
 TEST_F(PersonalDataManagerTest, GetCreditCardSuggestions) {
   EnableWalletCardImport();
 
-  // These GUIDs are reverse alphabetical to make validating expectations
-  // easier.
   CreditCard credit_card0("287151C8-6AB1-487C-9095-28E80BE5DA15",
                           "https://www.example.com");
   test::SetCreditCardInfo(&credit_card0,
       "Clyde Barrow", "347666888555" /* American Express */, "04", "2015");
+  credit_card0.set_use_count(2);
   personal_data_->AddCreditCard(credit_card0);
 
   CreditCard credit_card1("1141084B-72D7-4B73-90CF-3D6AC154673B",
                           "https://www.example.com");
+  credit_card1.set_use_count(3);
   test::SetCreditCardInfo(&credit_card1, "John Dillinger", "", "01", "2010");
   personal_data_->AddCreditCard(credit_card1);
 
   CreditCard credit_card2("002149C1-EE28-4213-A3B9-DA243FFF021B",
                           "https://www.example.com");
+  credit_card2.set_use_count(1);
   test::SetCreditCardInfo(&credit_card2,
       "Bonnie Parker", "518765432109" /* Mastercard */, "", "");
   personal_data_->AddCreditCard(credit_card2);
@@ -2737,21 +2738,22 @@
       personal_data_->GetCreditCardSuggestions(
           AutofillType(CREDIT_CARD_NAME), base::string16());
   ASSERT_EQ(3U, suggestions.size());
-  EXPECT_EQ(ASCIIToUTF16("Clyde Barrow"), suggestions[2].value);
-  EXPECT_EQ(ASCIIToUTF16("*8555"), suggestions[2].label);
-  EXPECT_EQ(ASCIIToUTF16("John Dillinger"), suggestions[1].value);
-  EXPECT_EQ(base::string16(), suggestions[1].label);
-  EXPECT_EQ(ASCIIToUTF16("Bonnie Parker"), suggestions[0].value);
-  EXPECT_EQ(ASCIIToUTF16("*2109"), suggestions[0].label);
+  // Ordered by MFU.
+  EXPECT_EQ(ASCIIToUTF16("Clyde Barrow"), suggestions[1].value);
+  EXPECT_EQ(ASCIIToUTF16("*8555"), suggestions[1].label);
+  EXPECT_EQ(ASCIIToUTF16("John Dillinger"), suggestions[0].value);
+  EXPECT_EQ(base::string16(), suggestions[0].label);
+  EXPECT_EQ(ASCIIToUTF16("Bonnie Parker"), suggestions[2].value);
+  EXPECT_EQ(ASCIIToUTF16("*2109"), suggestions[2].label);
 
   // Sublabel is expiration date when filling card number.
   suggestions = personal_data_->GetCreditCardSuggestions(
       AutofillType(CREDIT_CARD_NUMBER), base::string16());
   ASSERT_EQ(2U, suggestions.size());
-  EXPECT_EQ(ASCIIToUTF16("American Express - 8555"), suggestions[1].value);
-  EXPECT_EQ(ASCIIToUTF16("04/15"), suggestions[1].label);
-  EXPECT_EQ(ASCIIToUTF16("MasterCard - 2109"), suggestions[0].value);
-  EXPECT_EQ(base::string16(), suggestions[0].label);
+  EXPECT_EQ(ASCIIToUTF16("Amex - 8555"), suggestions[0].value);
+  EXPECT_EQ(ASCIIToUTF16("04/15"), suggestions[0].label);
+  EXPECT_EQ(ASCIIToUTF16("MasterCard - 2109"), suggestions[1].value);
+  EXPECT_EQ(base::string16(), suggestions[1].label);
 
   // Add some server cards. If there are local dupes, the locals should be
   // hidden.
@@ -2799,7 +2801,7 @@
   EXPECT_EQ(ASCIIToUTF16("MasterCard - 2109"), suggestions[0].value);
   EXPECT_EQ(ASCIIToUTF16("Visa - 9012"), suggestions[1].value);
   EXPECT_EQ(ASCIIToUTF16("Visa - 2109"), suggestions[2].value);
-  EXPECT_EQ(ASCIIToUTF16("American Express - 8555"), suggestions[3].value);
+  EXPECT_EQ(ASCIIToUTF16("Amex - 8555"), suggestions[3].value);
 
   // Make sure a server card can be a dupe of more than one local card.
   CreditCard credit_card3("4141084B-72D7-4B73-90CF-3D6AC154673B",
diff --git a/components/autofill/core/browser/test_autofill_client.cc b/components/autofill/core/browser/test_autofill_client.cc
index e193a3a..abcfb53 100644
--- a/components/autofill/core/browser/test_autofill_client.cc
+++ b/components/autofill/core/browser/test_autofill_client.cc
@@ -8,7 +8,9 @@
 
 namespace autofill {
 
-TestAutofillClient::TestAutofillClient() {
+TestAutofillClient::TestAutofillClient()
+    : token_service_(new FakeOAuth2TokenService()),
+      identity_provider_(new FakeIdentityProvider(token_service_.get())) {
 }
 TestAutofillClient::~TestAutofillClient() {
 }
@@ -26,7 +28,7 @@
 }
 
 IdentityProvider* TestAutofillClient::GetIdentityProvider() {
-  return nullptr;
+  return identity_provider_.get();
 }
 
 void TestAutofillClient::HideRequestAutocompleteDialog() {
diff --git a/components/autofill/core/browser/test_autofill_client.h b/components/autofill/core/browser/test_autofill_client.h
index 571d04e..1d5ab7ae 100644
--- a/components/autofill/core/browser/test_autofill_client.h
+++ b/components/autofill/core/browser/test_autofill_client.h
@@ -10,6 +10,8 @@
 #include "base/memory/scoped_ptr.h"
 #include "base/prefs/pref_service.h"
 #include "components/autofill/core/browser/autofill_client.h"
+#include "google_apis/gaia/fake_identity_provider.h"
+#include "google_apis/gaia/fake_oauth2_token_service.h"
 
 namespace autofill {
 
@@ -58,6 +60,8 @@
  private:
   // NULL by default.
   scoped_ptr<PrefService> prefs_;
+  scoped_ptr<FakeOAuth2TokenService> token_service_;
+  scoped_ptr<FakeIdentityProvider> identity_provider_;
 
   DISALLOW_COPY_AND_ASSIGN(TestAutofillClient);
 };
diff --git a/components/autofill/core/browser/wallet/real_pan_wallet_client.cc b/components/autofill/core/browser/wallet/real_pan_wallet_client.cc
index 5a12b3e3..913f604 100644
--- a/components/autofill/core/browser/wallet/real_pan_wallet_client.cc
+++ b/components/autofill/core/browser/wallet/real_pan_wallet_client.cc
@@ -5,12 +5,16 @@
 #include "components/autofill/core/browser/wallet/real_pan_wallet_client.h"
 
 #include "base/bind.h"
+#include "base/command_line.h"
 #include "base/json/json_reader.h"
 #include "base/json/json_writer.h"
 #include "base/memory/scoped_ptr.h"
+#include "base/strings/string_number_conversions.h"
 #include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
 #include "base/values.h"
 #include "components/autofill/core/browser/credit_card.h"
+#include "components/autofill/core/common/autofill_switches.h"
 #include "google_apis/gaia/identity_provider.h"
 #include "net/base/escape.h"
 #include "net/http/http_status_code.h"
@@ -26,14 +30,53 @@
     "requestContentType=application/json; charset=utf-8&request=%s"
     "&s7e_13_cvc=%s";
 
-const char kUnmaskCardRequestUrl[] =
-    "https://sandbox.google.com/payments/apis-secure/creditcardservice"
-    "/getrealpan?s7e_suffix=chromewallet";
+const char kUnmaskCardRequestHost[] = "https://wallet.google.com";
+const char kUnmaskCardRequestHostSandbox[] = "https://sandbox.google.com";
+const char kUnmaskCardRequestPath[] =
+    "payments/apis-secure/creditcardservice/getrealpan?s7e_suffix=chromewallet";
 
 const char kTokenServiceConsumerId[] = "real_pan_wallet_client";
 const char kWalletOAuth2Scope[] =
     "https://www.googleapis.com/auth/wallet.chrome";
 
+// This is mostly copied from wallet_service_url.cc, which is currently in
+// content/, hence inaccessible from here.
+bool IsWalletProductionEnabled() {
+  // If the command line flag exists, it takes precedence.
+  const base::CommandLine* command_line =
+      base::CommandLine::ForCurrentProcess();
+  std::string sandbox_enabled(
+      command_line->GetSwitchValueASCII(switches::kWalletServiceUseSandbox));
+  if (!sandbox_enabled.empty())
+    return sandbox_enabled != "1";
+
+#if defined(ENABLE_PROD_WALLET_SERVICE)
+  return true;
+#else
+  return false;
+#endif
+}
+
+GURL GetUnmaskCardRequestUrl() {
+  if (base::CommandLine::ForCurrentProcess()->HasSwitch("sync-url")) {
+    if (IsWalletProductionEnabled()) {
+      LOG(ERROR) << "You are using production Wallet but you specified a "
+                    "--sync-url. You likely want to disable the sync sandbox "
+                    "or switch to sandbox Wallet. Both are controlled in "
+                    "about:flags.";
+    }
+  } else if (!IsWalletProductionEnabled()) {
+    LOG(ERROR) << "You are using sandbox Wallet but you didn't specify a "
+                  "--sync-url. You likely want to enable the sync sandbox "
+                  "or switch to production Wallet. Both are controlled in "
+                  "about:flags.";
+  }
+
+  GURL base(IsWalletProductionEnabled() ? kUnmaskCardRequestHost
+                                        : kUnmaskCardRequestHostSandbox);
+  return base.Resolve(kUnmaskCardRequestPath);
+}
+
 }  // namespace
 
 RealPanWalletClient::RealPanWalletClient(
@@ -54,28 +97,35 @@
     StartTokenFetch();
 }
 
-void RealPanWalletClient::UnmaskCard(const CreditCard& card,
-                                     const std::string& cvc,
-                                     const std::string& risk_data) {
+void RealPanWalletClient::UnmaskCard(
+    const CreditCard& card,
+    const CardUnmaskDelegate::UnmaskResponse& response) {
   DCHECK_EQ(CreditCard::MASKED_SERVER_CARD, card.record_type());
 
   request_.reset(net::URLFetcher::Create(
-      0, GURL(kUnmaskCardRequestUrl), net::URLFetcher::POST, this));
+      0, GetUnmaskCardRequestUrl(), net::URLFetcher::POST, this));
   request_->SetRequestContext(context_getter_.get());
 
   base::DictionaryValue request_dict;
   request_dict.SetString("encrypted_cvc", "__param:s7e_13_cvc");
   request_dict.SetString("credit_card_id", card.server_id());
-  request_dict.SetString("risk_data_base64", risk_data);
+  request_dict.SetString("risk_data_base64", response.risk_data);
   request_dict.Set("context", make_scoped_ptr(new base::DictionaryValue()));
 
+  int value = 0;
+  if (base::StringToInt(response.exp_month, &value))
+    request_dict.SetInteger("expiration_month", value);
+  if (base::StringToInt(response.exp_year, &value))
+    request_dict.SetInteger("expiration_year", value);
+
   std::string json_request;
   base::JSONWriter::Write(&request_dict, &json_request);
-  std::string post_body = base::StringPrintf(kUnmaskCardRequestFormat,
-      net::EscapeUrlEncodedData(json_request, true).c_str(),
-      net::EscapeUrlEncodedData(cvc, true).c_str());
+  std::string post_body =
+      base::StringPrintf(kUnmaskCardRequestFormat,
+                         net::EscapeUrlEncodedData(json_request, true).c_str(),
+                         net::EscapeUrlEncodedData(
+                             base::UTF16ToASCII(response.cvc), true).c_str());
   request_->SetUploadData("application/x-www-form-urlencoded", post_body);
-  // TODO(estade): remove this when possible.
   request_->AddExtraRequestHeader(
       net::HttpRequestHeaders::kAcceptEncoding + std::string(": chunked;q=0"));
 
diff --git a/components/autofill/core/browser/wallet/real_pan_wallet_client.h b/components/autofill/core/browser/wallet/real_pan_wallet_client.h
index c2e0e6bc..f1fc05c 100644
--- a/components/autofill/core/browser/wallet/real_pan_wallet_client.h
+++ b/components/autofill/core/browser/wallet/real_pan_wallet_client.h
@@ -7,6 +7,7 @@
 
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
+#include "components/autofill/core/browser/card_unmask_delegate.h"
 #include "google_apis/gaia/oauth2_token_service.h"
 #include "net/url_request/url_fetcher_delegate.h"
 
@@ -53,8 +54,7 @@
 
   // The user has attempted to unmask a card with the given cvc.
   void UnmaskCard(const CreditCard& card,
-                  const std::string& cvc,
-                  const std::string& risk_data);
+                  const CardUnmaskDelegate::UnmaskResponse& response);
 
   // Cancels and clears the current |request_|.
   void CancelRequest();
diff --git a/components/autofill_strings.grdp b/components/autofill_strings.grdp
index 3236c54..fadd9b0 100644
--- a/components/autofill_strings.grdp
+++ b/components/autofill_strings.grdp
@@ -10,9 +10,12 @@
   <message name="IDS_AUTOFILL_WARNING_INSECURE_CONNECTION" desc="Warning text to show when credit card autofill is disabled because the website is not using a secure connection.">
     Automatic credit card filling is disabled because this form does not use a secure connection.
   </message>
-  <message name="IDS_AUTOFILL_CC_AMEX" desc="American Express credit card name.">
+  <message name="IDS_AUTOFILL_CC_AMEX" desc="Full American Express credit card name.">
     American Express
   </message>
+  <message name="IDS_AUTOFILL_CC_AMEX_SHORT" desc="Shorter name for American Express credit card.">
+    Amex
+  </message>
   <message name="IDS_AUTOFILL_CC_DINERS" desc="Diners Club credit card name.">
     Diners Club
   </message>
diff --git a/components/bookmarks.gypi b/components/bookmarks.gypi
index fdf382d..c697e02f 100644
--- a/components/bookmarks.gypi
+++ b/components/bookmarks.gypi
@@ -13,7 +13,9 @@
       ],
       'dependencies': [
         '../base/base.gyp:base',
+        '../base/base.gyp:base_i18n',
         '../net/net.gyp:net',
+        '../third_party/icu/icu.gyp:icui18n',
         '../third_party/icu/icu.gyp:icuuc',
         '../ui/base/ui_base.gyp:ui_base',
         '../ui/gfx/gfx.gyp:gfx',
diff --git a/components/bookmarks/browser/BUILD.gn b/components/bookmarks/browser/BUILD.gn
index fb770f4..091b20a 100644
--- a/components/bookmarks/browser/BUILD.gn
+++ b/components/bookmarks/browser/BUILD.gn
@@ -70,6 +70,8 @@
     "bookmark_utils_unittest.cc",
   ]
 
+  configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
+
   deps = [
     ":browser",
     "//base:prefs",
diff --git a/components/bookmarks/browser/bookmark_node_data_views.cc b/components/bookmarks/browser/bookmark_node_data_views.cc
index 64cd1f6..809113d 100644
--- a/components/bookmarks/browser/bookmark_node_data_views.cc
+++ b/components/bookmarks/browser/bookmark_node_data_views.cc
@@ -56,7 +56,6 @@
     }
   } else {
     // See if there is a URL on the clipboard.
-    Element element;
     GURL url;
     base::string16 title;
     if (data.GetURLAndTitle(
diff --git a/components/browser_watcher/BUILD.gn b/components/browser_watcher/BUILD.gn
index b65a408..1f782c2a0 100644
--- a/components/browser_watcher/BUILD.gn
+++ b/components/browser_watcher/BUILD.gn
@@ -39,6 +39,7 @@
     "watcher_client_win_unittest.cc",
     "watcher_metrics_provider_win_unittest.cc",
   ]
+  configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
   deps = [
     ":browser_watcher",
     ":browser_watcher_client",
diff --git a/components/cdm/renderer/android_key_systems.cc b/components/cdm/renderer/android_key_systems.cc
index 83a0d04..d1e0f2a 100644
--- a/components/cdm/renderer/android_key_systems.cc
+++ b/components/cdm/renderer/android_key_systems.cc
@@ -43,13 +43,22 @@
     AddWidevineWithCodecs(
         WIDEVINE,
         static_cast<SupportedCodecs>(response.compositing_codecs),
+        media::EME_SESSION_TYPE_NOT_SUPPORTED,  // Persistent license.
+        media::EME_SESSION_TYPE_NOT_SUPPORTED,  // Persistent release message.
+        media::EME_FEATURE_NOT_SUPPORTED,       // Persistent state.
+        media::EME_FEATURE_ALWAYS_ENABLED,      // Distinctive identifier.
         concrete_key_systems);
   }
 
   if (response.non_compositing_codecs != media::EME_CODEC_NONE) {
+    // TODO(ddorwin): Remove with unprefixed. http://crbug.com/249976
     AddWidevineWithCodecs(
         WIDEVINE_HR_NON_COMPOSITING,
         static_cast<SupportedCodecs>(response.non_compositing_codecs),
+        media::EME_SESSION_TYPE_NOT_SUPPORTED,  // Persistent license.
+        media::EME_SESSION_TYPE_NOT_SUPPORTED,  // Persistent release message.
+        media::EME_FEATURE_NOT_SUPPORTED,       // Persistent state.
+        media::EME_FEATURE_ALWAYS_ENABLED,      // Distinctive identifier.
         concrete_key_systems);
   }
 }
@@ -64,7 +73,8 @@
        it != key_system_names.end(); ++it) {
     SupportedKeySystemResponse response = QueryKeySystemSupport(*it);
     if (response.compositing_codecs != media::EME_CODEC_NONE) {
-      KeySystemInfo info(*it);
+      KeySystemInfo info;
+      info.key_system = *it;
       info.supported_codecs = response.compositing_codecs;
       // Here we assume that support for a container implies support for the
       // associated initialization data type. KeySystems handles validating
@@ -75,6 +85,12 @@
       if (response.compositing_codecs & media::EME_CODEC_MP4_ALL)
         info.supported_init_data_types |= media::EME_INIT_DATA_TYPE_CENC;
 #endif  // defined(USE_PROPRIETARY_CODECS)
+      // Assume the worst case (from a user point of view).
+      info.persistent_license_support = media::EME_SESSION_TYPE_NOT_SUPPORTED;
+      info.persistent_release_message_support =
+          media::EME_SESSION_TYPE_NOT_SUPPORTED;
+      info.persistent_state_support = media::EME_FEATURE_ALWAYS_ENABLED;
+      info.distinctive_identifier_support = media::EME_FEATURE_ALWAYS_ENABLED;
       concrete_key_systems->push_back(info);
     }
   }
diff --git a/components/cdm/renderer/widevine_key_systems.cc b/components/cdm/renderer/widevine_key_systems.cc
index f803e8e..037df1b 100644
--- a/components/cdm/renderer/widevine_key_systems.cc
+++ b/components/cdm/renderer/widevine_key_systems.cc
@@ -26,10 +26,16 @@
   return name.substr(0u, last_period);
 }
 
-void AddWidevineWithCodecs(WidevineCdmType widevine_cdm_type,
-                           SupportedCodecs supported_codecs,
-                           std::vector<KeySystemInfo>* concrete_key_systems) {
-  KeySystemInfo info(kWidevineKeySystem);
+void AddWidevineWithCodecs(
+    WidevineCdmType widevine_cdm_type,
+    SupportedCodecs supported_codecs,
+    media::EmeSessionTypeSupport persistent_license_support,
+    media::EmeSessionTypeSupport persistent_release_message_support,
+    media::EmeFeatureSupport persistent_state_support,
+    media::EmeFeatureSupport distinctive_identifier_support,
+    std::vector<KeySystemInfo>* concrete_key_systems) {
+  KeySystemInfo info;
+  info.key_system = kWidevineKeySystem;
 
   switch (widevine_cdm_type) {
     case WIDEVINE:
@@ -60,6 +66,11 @@
     info.supported_init_data_types |= media::EME_INIT_DATA_TYPE_CENC;
 #endif  // defined(USE_PROPRIETARY_CODECS)
 
+  info.persistent_license_support = persistent_license_support;
+  info.persistent_release_message_support = persistent_release_message_support;
+  info.persistent_state_support = persistent_state_support;
+  info.distinctive_identifier_support = distinctive_identifier_support;
+
 #if defined(ENABLE_PEPPER_CDMS)
   info.pepper_type = kWidevineCdmPluginMimeType;
 #endif  // defined(ENABLE_PEPPER_CDMS)
diff --git a/components/cdm/renderer/widevine_key_systems.h b/components/cdm/renderer/widevine_key_systems.h
index 1d42b3b..15c55daf8 100644
--- a/components/cdm/renderer/widevine_key_systems.h
+++ b/components/cdm/renderer/widevine_key_systems.h
@@ -22,6 +22,10 @@
 void AddWidevineWithCodecs(
     WidevineCdmType widevine_cdm_type,
     media::SupportedCodecs supported_codecs,
+    media::EmeSessionTypeSupport persistent_license_support,
+    media::EmeSessionTypeSupport persistent_release_message_support,
+    media::EmeFeatureSupport persistent_state_support,
+    media::EmeFeatureSupport distinctive_identifier_support,
     std::vector<media::KeySystemInfo>* concrete_key_systems);
 
 }  // namespace cdm
diff --git a/components/components.gyp b/components/components.gyp
index 974d2924..db6d9b0 100644
--- a/components/components.gyp
+++ b/components/components.gyp
@@ -21,6 +21,7 @@
     'cronet.gypi',
     'crx_file.gypi',
     'data_reduction_proxy.gypi',
+    'device_event_log.gypi',
     'network_hints.gypi',
     'dom_distiller.gypi',
     'domain_reliability.gypi',
@@ -83,6 +84,7 @@
     }],
     ['OS == "ios"', {
       'includes': [
+        'open_from_clipboard.gypi',
         'webp_transcode.gypi',
       ],
     }],
diff --git a/components/components_strings.grd b/components/components_strings.grd
index 35d1c59..e48591a 100644
--- a/components/components_strings.grd
+++ b/components/components_strings.grd
@@ -174,6 +174,7 @@
       <part file="dom_distiller_strings.grdp" />
       <part file="error_page_strings.grdp" />
       <part file="omnibox_strings.grdp" />
+      <part file="open_from_clipboard_strings.grdp" />
       <part file="password_manager_strings.grdp" />
       <part file="pdf_strings.grdp" />
       <part file="policy_strings.grdp" />
diff --git a/components/components_tests.gyp b/components/components_tests.gyp
index 3287eada..cfd77f5 100644
--- a/components/components_tests.gyp
+++ b/components/components_tests.gyp
@@ -136,6 +136,7 @@
             'data_reduction_proxy/core/common/data_reduction_proxy_event_store_unittest.cc',
             'data_reduction_proxy/core/common/data_reduction_proxy_headers_unittest.cc',
             'data_reduction_proxy/core/common/data_reduction_proxy_params_unittest.cc',
+            'device_event_log/device_event_log_impl_unittest.cc',
             'dom_distiller/core/article_entry_unittest.cc',
             'dom_distiller/core/distilled_content_store_unittest.cc',
             'dom_distiller/core/distilled_page_prefs_unittests.cc',
@@ -391,6 +392,7 @@
             'components.gyp:data_reduction_proxy_core_browser',
             'components.gyp:data_reduction_proxy_core_common',
             'components.gyp:data_reduction_proxy_test_support',
+            'components.gyp:device_event_log_component',
             'components.gyp:distilled_page_proto',
             'components.gyp:dom_distiller_core',
             'components.gyp:dom_distiller_test_support',
diff --git a/components/content_settings/core/browser/BUILD.gn b/components/content_settings/core/browser/BUILD.gn
index 89f3ec88..a321cbb 100644
--- a/components/content_settings/core/browser/BUILD.gn
+++ b/components/content_settings/core/browser/BUILD.gn
@@ -40,6 +40,8 @@
     "//net",
     "//url",
   ]
+
+  configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
 }
 
 source_set("unit_tests") {
diff --git a/components/copresence/handlers/audio/audio_directive_handler_impl.cc b/components/copresence/handlers/audio/audio_directive_handler_impl.cc
index 16b0269..d91b4d8f 100644
--- a/components/copresence/handlers/audio/audio_directive_handler_impl.cc
+++ b/components/copresence/handlers/audio/audio_directive_handler_impl.cc
@@ -22,6 +22,7 @@
 
 using audio_modem::AUDIBLE;
 using audio_modem::INAUDIBLE;
+using audio_modem::TokenParameters;
 
 namespace copresence {
 
@@ -102,12 +103,13 @@
       DCHECK_GT(token_length, 0u);
       switch (instruction.medium()) {
         case AUDIO_ULTRASOUND_PASSBAND:
-          audio_modem_->SetTokenLength(INAUDIBLE, token_length);
+          audio_modem_->SetTokenParams(INAUDIBLE,
+                                       TokenParameters(token_length));
           transmits_lists_[INAUDIBLE]->AddDirective(op_id, directive);
           audio_modem_->SetToken(INAUDIBLE, instruction.token_id());
           break;
         case AUDIO_AUDIBLE_DTMF:
-          audio_modem_->SetTokenLength(AUDIBLE, token_length);
+          audio_modem_->SetTokenParams(AUDIBLE, TokenParameters(token_length));
           transmits_lists_[AUDIBLE]->AddDirective(op_id, directive);
           audio_modem_->SetToken(AUDIBLE, instruction.token_id());
           break;
@@ -123,11 +125,12 @@
       DCHECK_GT(token_length, 0u);
       switch (instruction.medium()) {
         case AUDIO_ULTRASOUND_PASSBAND:
-          audio_modem_->SetTokenLength(INAUDIBLE, token_length);
+          audio_modem_->SetTokenParams(INAUDIBLE,
+                                       TokenParameters(token_length));
           receives_lists_[INAUDIBLE]->AddDirective(op_id, directive);
           break;
         case AUDIO_AUDIBLE_DTMF:
-          audio_modem_->SetTokenLength(AUDIBLE, token_length);
+          audio_modem_->SetTokenParams(AUDIBLE, TokenParameters(token_length));
           receives_lists_[AUDIBLE]->AddDirective(op_id, directive);
           break;
         default:
diff --git a/components/copresence/handlers/audio/audio_directive_handler_unittest.cc b/components/copresence/handlers/audio/audio_directive_handler_unittest.cc
index d414dd2..24da3ca 100644
--- a/components/copresence/handlers/audio/audio_directive_handler_unittest.cc
+++ b/components/copresence/handlers/audio/audio_directive_handler_unittest.cc
@@ -12,6 +12,7 @@
 #include "base/timer/mock_timer.h"
 #include "components/audio_modem/public/modem.h"
 #include "components/audio_modem/test/random_samples.h"
+#include "components/audio_modem/test/stub_modem.h"
 #include "components/copresence/handlers/audio/audio_directive_handler_impl.h"
 #include "components/copresence/handlers/audio/tick_clock_ref_counted.h"
 #include "components/copresence/proto/data.pb.h"
@@ -20,6 +21,7 @@
 using audio_modem::AUDIBLE;
 using audio_modem::AudioType;
 using audio_modem::INAUDIBLE;
+using audio_modem::StubModem;
 
 namespace copresence {
 
@@ -39,34 +41,6 @@
 
 }  // namespace
 
-class StubModem final : public audio_modem::Modem {
- public:
-  StubModem() {}
-  ~StubModem() override {}
-
-  // AudioManager overrides:
-  void Initialize(audio_modem::WhispernetClient* whispernet_client,
-                  const audio_modem::TokensCallback& tokens_cb) override {}
-  void StartPlaying(AudioType type) override { playing_[type] = true; }
-  void StopPlaying(AudioType type) override { playing_[type] = false; }
-  void StartRecording(AudioType type) override { recording_[type] = true; }
-  void StopRecording(AudioType type) override { recording_[type] = false; }
-  void SetToken(AudioType type, const std::string& url_unsafe_token) override {}
-  const std::string GetToken(AudioType type) override { return std::string(); }
-  bool IsPlayingTokenHeard(AudioType type) override { return false; }
-  void SetTokenLength(AudioType type, size_t token_length) override {}
-
-  bool IsRecording(AudioType type) { return recording_[type]; }
-  bool IsPlaying(AudioType type) { return playing_[type]; }
-
- private:
-  // Indexed using enum AudioType.
-  bool playing_[2];
-  bool recording_[2];
-
-  DISALLOW_COPY_AND_ASSIGN(StubModem);
-};
-
 class AudioDirectiveHandlerTest : public testing::Test {
  public:
   AudioDirectiveHandlerTest() {
diff --git a/components/cronet.gypi b/components/cronet.gypi
index f492d41..e7d68388 100644
--- a/components/cronet.gypi
+++ b/components/cronet.gypi
@@ -13,6 +13,7 @@
           'sources': [
             'cronet/android/java/src/org/chromium/net/CronetHistogramManager.java',
             'cronet/android/java/src/org/chromium/net/CronetLibraryLoader.java',
+            'cronet/android/java/src/org/chromium/net/CronetUploadDataStream.java',
             'cronet/android/java/src/org/chromium/net/CronetUrlRequest.java',
             'cronet/android/java/src/org/chromium/net/CronetUrlRequestContext.java',
             'cronet/android/java/src/org/chromium/net/ChromiumUrlRequest.java',
@@ -124,12 +125,14 @@
             'cronet/android/cronet_histogram_manager.h',
             'cronet/android/cronet_library_loader.cc',
             'cronet/android/cronet_library_loader.h',
+            'cronet/android/cronet_upload_data_stream_adapter.cc',
+            'cronet/android/cronet_upload_data_stream_adapter.h',
+            'cronet/android/cronet_upload_data_stream_delegate.cc',
+            'cronet/android/cronet_upload_data_stream_delegate.h',
             'cronet/android/cronet_url_request.cc',
             'cronet/android/cronet_url_request.h',
             'cronet/android/cronet_url_request_adapter.cc',
             'cronet/android/cronet_url_request_adapter.h',
-            'cronet/android/cronet_url_request_context.cc',
-            'cronet/android/cronet_url_request_context.h',
             'cronet/android/cronet_url_request_context_adapter.cc',
             'cronet/android/cronet_url_request_context_adapter.h',
             'cronet/android/url_request_adapter.cc',
@@ -200,6 +203,8 @@
               '**/HttpUrlRequest*.java',
               '**/ResponseInfo.java',
               '**/ResponseTooLargeException.java',
+              '**/UploadDataProvider.java',
+              '**/UploadDataSink.java',
               '**/UrlRequest.java',
               '**/UrlRequestContext.java',
               '**/UrlRequestContextConfig.java',
@@ -235,6 +240,7 @@
               '**/CronetHistogramManager.java',
               '**/CronetResponseInfo.java',
               '**/CronetLibraryLoader.java',
+              '**/CronetUploadDataStream.java',
               '**/CronetUrlRequest.java',
               '**/CronetUrlRequestContext.java',
               '**/CronetUrlRequestFactory.java',
@@ -306,6 +312,7 @@
             'cronet/android/test/src/org/chromium/net/MockUrlRequestJobFactory.java',
             'cronet/android/test/src/org/chromium/net/NativeTestServer.java',
             'cronet/android/test/src/org/chromium/net/NetworkChangeNotifierUtil.java',
+            'cronet/android/test/src/org/chromium/net/TestUploadDataStreamHandler.java',
           ],
           'variables': {
             'jni_gen_package': 'cronet_tests',
@@ -321,6 +328,8 @@
             'cronet/android/test/mock_url_request_job_factory.h',
             'cronet/android/test/native_test_server.cc',
             'cronet/android/test/native_test_server.h',
+            'cronet/android/test/test_upload_data_stream_handler.cc',
+            'cronet/android/test/test_upload_data_stream_handler.h',
             'cronet/android/test/network_change_notifier_util.cc',
             'cronet/android/test/network_change_notifier_util.h',
           ],
diff --git a/components/cronet/android/cronet_library_loader.cc b/components/cronet/android/cronet_library_loader.cc
index af0ac41..d02142b 100644
--- a/components/cronet/android/cronet_library_loader.cc
+++ b/components/cronet/android/cronet_library_loader.cc
@@ -15,8 +15,9 @@
 #include "components/cronet/android/chromium_url_request.h"
 #include "components/cronet/android/chromium_url_request_context.h"
 #include "components/cronet/android/cronet_histogram_manager.h"
+#include "components/cronet/android/cronet_upload_data_stream_delegate.h"
 #include "components/cronet/android/cronet_url_request.h"
-#include "components/cronet/android/cronet_url_request_context.h"
+#include "components/cronet/android/cronet_url_request_context_adapter.h"
 #include "jni/CronetLibraryLoader_jni.h"
 #include "net/android/net_jni_registrar.h"
 #include "net/android/network_change_notifier_factory_android.h"
@@ -37,8 +38,11 @@
     {"ChromiumUrlRequestContext", ChromiumUrlRequestContextRegisterJni},
     {"CronetHistogramManager", CronetHistogramManagerRegisterJni},
     {"CronetLibraryLoader", RegisterNativesImpl},
+    {"CronetUploadDataStreamDelegate",
+     CronetUploadDataStreamDelegateRegisterJni},
     {"CronetUrlRequest", CronetUrlRequestRegisterJni},
-    {"CronetUrlRequestContext", CronetUrlRequestContextRegisterJni},
+    {"CronetUrlRequestContextAdapter",
+     CronetUrlRequestContextAdapterRegisterJni},
     {"NetAndroid", net::android::RegisterJni},
     {"UrlAndroid", url::android::RegisterJni},
 };
diff --git a/components/cronet/android/cronet_upload_data_stream_adapter.cc b/components/cronet/android/cronet_upload_data_stream_adapter.cc
new file mode 100644
index 0000000..05ca1ce4
--- /dev/null
+++ b/components/cronet/android/cronet_upload_data_stream_adapter.cc
@@ -0,0 +1,139 @@
+// 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 "components/cronet/android/cronet_upload_data_stream_adapter.h"
+
+#include "net/base/io_buffer.h"
+#include "net/base/net_errors.h"
+
+namespace cronet {
+
+CronetUploadDataStreamAdapter::CronetUploadDataStreamAdapter(Delegate* delegate,
+                                                             int64 size)
+    : UploadDataStream(size < 0, 0),
+      size_(size),
+      waiting_on_read_(false),
+      read_in_progress_(false),
+      waiting_on_rewind_(false),
+      rewind_in_progress_(false),
+      at_front_of_stream_(true),
+      delegate_(delegate),
+      weak_factory_(this) {
+}
+
+CronetUploadDataStreamAdapter::~CronetUploadDataStreamAdapter() {
+  delegate_->OnAdapterDestroyed();
+}
+
+int CronetUploadDataStreamAdapter::InitInternal() {
+  // ResetInternal should have been called before init, if the adapter was in
+  // use.
+  DCHECK(!waiting_on_read_);
+  DCHECK(!waiting_on_rewind_);
+
+  if (!weak_factory_.HasWeakPtrs())
+    delegate_->InitializeOnNetworkThread(weak_factory_.GetWeakPtr());
+
+  // Set size of non-chunked uploads.
+  if (size_ >= 0)
+    SetSize(static_cast<uint64>(size_));
+
+  // If already at the front of the stream, nothing to do.
+  if (at_front_of_stream_) {
+    // Being at the front of the stream implies there's no read or rewind in
+    // progress.
+    DCHECK(!read_in_progress_);
+    DCHECK(!rewind_in_progress_);
+    return net::OK;
+  }
+
+  // Otherwise, the request is now waiting for the stream to be rewound.
+  waiting_on_rewind_ = true;
+
+  // Start rewinding the stream if no operation is in progress.
+  if (!read_in_progress_ && !rewind_in_progress_)
+    StartRewind();
+  return net::ERR_IO_PENDING;
+}
+
+int CronetUploadDataStreamAdapter::ReadInternal(net::IOBuffer* buf,
+                                                int buf_len) {
+  // All pending operations should have completed before a read can start.
+  DCHECK(!waiting_on_read_);
+  DCHECK(!read_in_progress_);
+  DCHECK(!waiting_on_rewind_);
+  DCHECK(!rewind_in_progress_);
+
+  DCHECK(buf);
+  DCHECK_GT(buf_len, 0);
+
+  read_in_progress_ = true;
+  waiting_on_read_ = true;
+  at_front_of_stream_ = false;
+  delegate_->Read(buf, buf_len);
+  return net::ERR_IO_PENDING;
+}
+
+void CronetUploadDataStreamAdapter::ResetInternal() {
+  // Consumer is not waiting on any operation.  Note that the active operation,
+  // if any, will continue.
+  waiting_on_read_ = false;
+  waiting_on_rewind_ = false;
+}
+
+void CronetUploadDataStreamAdapter::OnReadSuccess(int bytes_read,
+                                                  bool final_chunk) {
+  DCHECK(read_in_progress_);
+  DCHECK(!rewind_in_progress_);
+  DCHECK(bytes_read > 0 || (final_chunk && bytes_read == 0));
+  DCHECK(!is_chunked() || !final_chunk);
+
+  read_in_progress_ = false;
+
+  if (waiting_on_rewind_) {
+    DCHECK(!waiting_on_read_);
+    // Since a read just completed, can't be at the front of the stream.
+    StartRewind();
+    return;
+  }
+  // ResetInternal has been called, but still waiting on InitInternal.
+  if (!waiting_on_read_)
+    return;
+
+  waiting_on_read_ = false;
+  if (final_chunk)
+    SetIsFinalChunk();
+  OnReadCompleted(bytes_read);
+}
+
+void CronetUploadDataStreamAdapter::OnRewindSuccess() {
+  DCHECK(!waiting_on_read_);
+  DCHECK(!read_in_progress_);
+  DCHECK(rewind_in_progress_);
+  DCHECK(!at_front_of_stream_);
+
+  rewind_in_progress_ = false;
+  at_front_of_stream_ = true;
+
+  // Possible that ResetInternal was called since the rewind was started, but
+  // InitInternal has not been.
+  if (!waiting_on_rewind_)
+    return;
+
+  waiting_on_rewind_ = false;
+  OnInitCompleted(net::OK);
+}
+
+void CronetUploadDataStreamAdapter::StartRewind() {
+  DCHECK(!waiting_on_read_);
+  DCHECK(!read_in_progress_);
+  DCHECK(waiting_on_rewind_);
+  DCHECK(!rewind_in_progress_);
+  DCHECK(!at_front_of_stream_);
+
+  rewind_in_progress_ = true;
+  delegate_->Rewind();
+}
+
+}  // namespace cronet
diff --git a/components/cronet/android/cronet_upload_data_stream_adapter.h b/components/cronet/android/cronet_upload_data_stream_adapter.h
new file mode 100644
index 0000000..ae64314
--- /dev/null
+++ b/components/cronet/android/cronet_upload_data_stream_adapter.h
@@ -0,0 +1,111 @@
+// 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 COMPONENTS_CRONET_ANDROID_CRONET_UPLOAD_DATA_STREAM_ADAPTER_H_
+#define COMPONENTS_CRONET_ANDROID_CRONET_UPLOAD_DATA_STREAM_ADAPTER_H_
+
+#include "base/basictypes.h"
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "net/base/upload_data_stream.h"
+
+namespace net {
+class IOBuffer;
+}  // namespace net
+
+namespace cronet {
+
+// The CronetUploadDataStreamAdapter is created on a Java thread, but
+// afterwards, lives and is deleted on the network thread. It's responsible for
+// invoking UploadDataStream's callbacks, and ensuring only one read/rewind
+// request sent to Java is outstanding at a time. The main complexity is around
+// Reset/Initialize calls while there's a pending read or rewind.
+class CronetUploadDataStreamAdapter : public net::UploadDataStream {
+ public:
+  class Delegate {
+   public:
+    // Called once during initial setup on the network thread, called before
+    // all other methods.
+    virtual void InitializeOnNetworkThread(
+        base::WeakPtr<CronetUploadDataStreamAdapter> adapter) = 0;
+
+    // Called for each read request. Delegate must respond by calling
+    // OnReadSuccess on the network thread asynchronous, or failing the request.
+    // Only called when there's no other pending read or rewind operation.
+    virtual void Read(net::IOBuffer* buffer, int buf_len) = 0;
+
+    // Called to rewind the stream. Not called when already at the start of the
+    // stream. The delegate must respond by calling OnRewindSuccess
+    // asynchronously on the network thread, or failing the request. Only called
+    // when there's no other pending read or rewind operation.
+    virtual void Rewind() = 0;
+
+    // Called when the adapter is destroyed. May be called when there's a
+    // pending read or rewind operation. The Delegate is then responsible for
+    // destroying itself.
+    virtual void OnAdapterDestroyed() = 0;
+
+   protected:
+    Delegate() {}
+    virtual ~Delegate() {}
+
+   private:
+    DISALLOW_COPY_AND_ASSIGN(Delegate);
+  };
+
+  CronetUploadDataStreamAdapter(Delegate* delegate, int64 size);
+  ~CronetUploadDataStreamAdapter() override;
+
+  // Failure is handled at the Java layer. These two success callbacks are
+  // invoked by Java UploadDataSink upon completion of the operation.
+  void OnReadSuccess(int bytes_read, bool final_chunk);
+  void OnRewindSuccess();
+
+ private:
+  // net::UploadDataStream implementation:
+  int InitInternal() override;
+  int ReadInternal(net::IOBuffer* buf, int buf_len) override;
+  void ResetInternal() override;
+
+  // Starts rewinding the stream. Only called when not already at the front of
+  // the stream, and no operation is pending. Completes asynchronously.
+  void StartRewind();
+
+  // Size of the upload. -1 if chunked.
+  const int64 size_;
+
+  // True if ReadInternal has been called, the read hasn't completed, and there
+  // hasn't been a ResetInternal call yet.
+  bool waiting_on_read_;
+  // True if there's a read operation in progress. This will always be true
+  // when |waiting_on_read_| is true. This will only be set to false once it
+  // completes, even though ResetInternal may have been called since the read
+  // started.
+  bool read_in_progress_;
+
+  // True if InitInternal has been called, the rewind hasn't completed, and
+  // there hasn't been a ResetInternal call yet. Note that this may be true
+  // even when the rewind hasn't yet started, if there's a read in progress.
+  bool waiting_on_rewind_;
+  // True if there's a rewind operation in progress. Rewinding will only start
+  // when |waiting_on_rewind_| is true, and |read_in_progress_| is false. This
+  // will only be set to false once it completes, even though ResetInternal may
+  // have been called since the rewind started.
+  bool rewind_in_progress_;
+
+  // Set to false when a read starts, true when a rewind completes.
+  bool at_front_of_stream_;
+
+  Delegate* const delegate_;
+
+  // Vends pointers on the network thread, though created on a Java thread.
+  base::WeakPtrFactory<CronetUploadDataStreamAdapter> weak_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(CronetUploadDataStreamAdapter);
+};
+
+}  // namespace cronet
+
+#endif  // COMPONENTS_CRONET_ANDROID_CRONET_UPLOAD_DATA_STREAM_ADAPTER_H_
diff --git a/components/cronet/android/cronet_upload_data_stream_delegate.cc b/components/cronet/android/cronet_upload_data_stream_delegate.cc
new file mode 100644
index 0000000..323714eb
--- /dev/null
+++ b/components/cronet/android/cronet_upload_data_stream_delegate.cc
@@ -0,0 +1,147 @@
+// 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 "components/cronet/android/cronet_upload_data_stream_delegate.h"
+
+#include <string>
+
+#include "base/android/jni_android.h"
+#include "base/android/jni_string.h"
+#include "base/bind.h"
+#include "base/message_loop/message_loop_proxy.h"
+#include "base/single_thread_task_runner.h"
+#include "components/cronet/android/cronet_url_request_adapter.h"
+#include "jni/CronetUploadDataStream_jni.h"
+
+using base::android::ConvertUTF8ToJavaString;
+
+namespace cronet {
+
+CronetUploadDataStreamDelegate::CronetUploadDataStreamDelegate(
+    JNIEnv* env,
+    jobject jupload_data_stream) {
+  jupload_data_stream_.Reset(env, jupload_data_stream);
+}
+
+CronetUploadDataStreamDelegate::~CronetUploadDataStreamDelegate() {
+}
+
+void CronetUploadDataStreamDelegate::InitializeOnNetworkThread(
+    base::WeakPtr<CronetUploadDataStreamAdapter> adapter) {
+  DCHECK(!adapter_);
+  DCHECK(!network_task_runner_.get());
+
+  adapter_ = adapter;
+  network_task_runner_ = base::MessageLoopProxy::current();
+  DCHECK(network_task_runner_);
+}
+
+void CronetUploadDataStreamDelegate::Read(net::IOBuffer* buffer, int buf_len) {
+  DCHECK(adapter_);
+  DCHECK(network_task_runner_);
+  DCHECK(network_task_runner_->BelongsToCurrentThread());
+  DCHECK_GT(buf_len, 0);
+  DCHECK(!buffer_.get());
+  buffer_ = buffer;
+
+  // TODO(mmenke):  Consider preserving the java buffer across reads, when the
+  // IOBuffer's data pointer and its length are unchanged.
+  JNIEnv* env = base::android::AttachCurrentThread();
+  base::android::ScopedJavaLocalRef<jobject> java_buffer(
+      env, env->NewDirectByteBuffer(buffer->data(), buf_len));
+  Java_CronetUploadDataStream_readData(env, jupload_data_stream_.obj(),
+                                       java_buffer.obj());
+}
+
+void CronetUploadDataStreamDelegate::Rewind() {
+  DCHECK(adapter_);
+  DCHECK(network_task_runner_->BelongsToCurrentThread());
+
+  JNIEnv* env = base::android::AttachCurrentThread();
+  Java_CronetUploadDataStream_rewind(env, jupload_data_stream_.obj());
+}
+
+void CronetUploadDataStreamDelegate::OnAdapterDestroyed() {
+  DCHECK(adapter_);
+  DCHECK(network_task_runner_->BelongsToCurrentThread());
+
+  JNIEnv* env = base::android::AttachCurrentThread();
+  Java_CronetUploadDataStream_onAdapterDestroyed(env,
+                                                 jupload_data_stream_.obj());
+}
+
+void CronetUploadDataStreamDelegate::OnReadSucceeded(JNIEnv* env,
+                                                     jobject jcaller,
+                                                     int bytes_read,
+                                                     bool final_chunk) {
+  DCHECK(!network_task_runner_->BelongsToCurrentThread());
+  DCHECK(bytes_read > 0 || (final_chunk && bytes_read == 0));
+
+  buffer_ = nullptr;
+  DCHECK(network_task_runner_->PostTask(
+      FROM_HERE, base::Bind(&CronetUploadDataStreamAdapter::OnReadSuccess,
+                            adapter_, bytes_read, final_chunk)));
+}
+
+void CronetUploadDataStreamDelegate::OnRewindSucceeded(JNIEnv* env,
+                                                       jobject jcaller) {
+  DCHECK(!network_task_runner_->BelongsToCurrentThread());
+
+  network_task_runner_->PostTask(
+      FROM_HERE,
+      base::Bind(&CronetUploadDataStreamAdapter::OnRewindSuccess, adapter_));
+}
+
+bool CronetUploadDataStreamDelegateRegisterJni(JNIEnv* env) {
+  return RegisterNativesImpl(env);
+}
+
+static jlong AttachUploadDataToRequest(JNIEnv* env,
+                                       jobject jupload_data_stream,
+                                       jlong jcronet_url_request_adapter,
+                                       jlong jlength) {
+  CronetURLRequestAdapter* request_adapter =
+      reinterpret_cast<CronetURLRequestAdapter*>(jcronet_url_request_adapter);
+  DCHECK(request_adapter != nullptr);
+
+  CronetUploadDataStreamDelegate* delegate =
+      new CronetUploadDataStreamDelegate(env, jupload_data_stream);
+
+  scoped_ptr<CronetUploadDataStreamAdapter> upload_adapter(
+      new CronetUploadDataStreamAdapter(delegate, jlength));
+
+  request_adapter->SetUpload(upload_adapter.Pass());
+
+  return reinterpret_cast<jlong>(delegate);
+}
+
+static jlong CreateDelegateForTesting(JNIEnv* env,
+                                      jobject jupload_data_stream) {
+  CronetUploadDataStreamDelegate* delegate =
+      new CronetUploadDataStreamDelegate(env, jupload_data_stream);
+  return reinterpret_cast<jlong>(delegate);
+}
+
+static jlong CreateAdapterForTesting(JNIEnv* env,
+                                     jobject jupload_data_stream,
+                                     jlong jlength,
+                                     jlong jdelegate) {
+  CronetUploadDataStreamDelegate* delegate =
+      reinterpret_cast<CronetUploadDataStreamDelegate*>(jdelegate);
+  CronetUploadDataStreamAdapter* upload_adapter =
+      new CronetUploadDataStreamAdapter(delegate, jlength);
+  return reinterpret_cast<jlong>(upload_adapter);
+}
+
+static void DestroyDelegate(JNIEnv* env,
+                            jclass jcronet_url_request_adapter,
+                            jlong jupload_data_stream_delegate) {
+  CronetUploadDataStreamDelegate* delegate =
+      reinterpret_cast<CronetUploadDataStreamDelegate*>(
+          jupload_data_stream_delegate);
+  DCHECK(delegate != nullptr);
+  delete delegate;
+}
+
+}  // namespace cronet
diff --git a/components/cronet/android/cronet_upload_data_stream_delegate.h b/components/cronet/android/cronet_upload_data_stream_delegate.h
new file mode 100644
index 0000000..1c3b9b22
--- /dev/null
+++ b/components/cronet/android/cronet_upload_data_stream_delegate.h
@@ -0,0 +1,77 @@
+// 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 COMPONENTS_CRONET_ANDROID_CRONET_UPLOAD_DATA_STREAM_DELEGATE_H_
+#define COMPONENTS_CRONET_ANDROID_CRONET_UPLOAD_DATA_STREAM_DELEGATE_H_
+
+#include <jni.h>
+
+#include "base/android/scoped_java_ref.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "components/cronet/android/cronet_upload_data_stream_adapter.h"
+#include "net/base/io_buffer.h"
+
+namespace base {
+class SingleThreadTaskRunner;
+}  // namespace base
+
+namespace cronet {
+
+// The Delegate holds onto a reference to the IOBuffer that is currently being
+// written to in Java, so may not be deleted until any read operation in Java
+// has completed.
+//
+// The Delegate is owned by the Java CronetUploadDataStream, and also owns a
+// reference to it. The Delegate is only destroyed after the URLRequest
+// destroys the adapter and the CronetUploadDataStream has no read operation
+// pending, at which point it also releases its reference to the
+// CronetUploadDataStream.
+//
+// Failures don't go through the delegate, but directly to the Java request
+// object, since normally reads aren't allowed to fail during an upload.
+class CronetUploadDataStreamDelegate
+    : public CronetUploadDataStreamAdapter::Delegate {
+ public:
+  CronetUploadDataStreamDelegate(JNIEnv* env, jobject jupload_data_stream);
+  ~CronetUploadDataStreamDelegate() override;
+
+  // CronetUploadDataStreamAdapter::Delegate implementation.  Called on network
+  // thread.
+  void InitializeOnNetworkThread(
+      base::WeakPtr<CronetUploadDataStreamAdapter> adapter) override;
+  void Read(net::IOBuffer* buffer, int buf_len) override;
+  void Rewind() override;
+  void OnAdapterDestroyed() override;
+
+  // Callbacks from Java, called on some Java thread.
+  void OnReadSucceeded(JNIEnv* env,
+                       jobject obj,
+                       int bytes_read,
+                       bool final_chunk);
+  void OnRewindSucceeded(JNIEnv* env, jobject obj);
+
+ private:
+  // Initialized on construction, effectively constant.
+  base::android::ScopedJavaGlobalRef<jobject> jupload_data_stream_;
+
+  // These are initialized in InitializeOnNetworkThread, so are safe to access
+  // during Java callbacks, which all happen after initialization.
+  scoped_refptr<base::SingleThreadTaskRunner> network_task_runner_;
+  base::WeakPtr<CronetUploadDataStreamAdapter> adapter_;
+
+  // Used to keep the read buffer alive until the callback from Java has been
+  // received.
+  scoped_refptr<net::IOBuffer> buffer_;
+
+  DISALLOW_COPY_AND_ASSIGN(CronetUploadDataStreamDelegate);
+};
+
+// Explicitly register static JNI functions.
+bool CronetUploadDataStreamDelegateRegisterJni(JNIEnv* env);
+
+}  // namespace cronet
+
+#endif  // COMPONENTS_CRONET_ANDROID_CRONET_UPLOAD_DATA_STREAM_DELEGATE_H_
diff --git a/components/cronet/android/cronet_url_request_adapter.cc b/components/cronet/android/cronet_url_request_adapter.cc
index 49922d2..80e1c63 100644
--- a/components/cronet/android/cronet_url_request_adapter.cc
+++ b/components/cronet/android/cronet_url_request_adapter.cc
@@ -58,6 +58,13 @@
   return context_->IsOnNetworkThread();
 }
 
+void CronetURLRequestAdapter::SetUpload(
+    scoped_ptr<net::UploadDataStream> upload) {
+  DCHECK(!IsOnNetworkThread());
+  DCHECK(!upload_);
+  upload_ = upload.Pass();
+}
+
 void CronetURLRequestAdapter::Start() {
   DCHECK(IsOnNetworkThread());
   VLOG(1) << "Starting chromium request: "
@@ -69,6 +76,8 @@
   url_request_->set_method(initial_method_);
   url_request_->SetExtraRequestHeaders(initial_request_headers_);
   url_request_->SetPriority(initial_priority_);
+  if (upload_)
+    url_request_->set_upload(upload_.Pass());
   url_request_->Start();
 }
 
diff --git a/components/cronet/android/cronet_url_request_adapter.h b/components/cronet/android/cronet_url_request_adapter.h
index eb0f62f..0072611 100644
--- a/components/cronet/android/cronet_url_request_adapter.h
+++ b/components/cronet/android/cronet_url_request_adapter.h
@@ -23,6 +23,7 @@
 namespace net {
 class GrowableIOBuffer;
 class HttpResponseHeaders;
+class UploadDataStream;
 }  // namespace net
 
 namespace cronet {
@@ -80,6 +81,9 @@
   // Adds a header to the request before it starts.
   void AddRequestHeader(const std::string& name, const std::string& value);
 
+  // Adds a request body to the request before it starts.
+  void SetUpload(scoped_ptr<net::UploadDataStream> upload);
+
   // Methods called on any thread.
 
   // Posts tasks to network thread.
@@ -145,6 +149,7 @@
   std::string initial_method_;
   int load_flags_;
   net::HttpRequestHeaders initial_request_headers_;
+  scoped_ptr<net::UploadDataStream> upload_;
 
   scoped_refptr<net::IOBufferWithSize> read_buffer_;
   scoped_ptr<net::URLRequest> url_request_;
diff --git a/components/cronet/android/cronet_url_request_context.cc b/components/cronet/android/cronet_url_request_context.cc
deleted file mode 100644
index bae0380..0000000
--- a/components/cronet/android/cronet_url_request_context.cc
+++ /dev/null
@@ -1,117 +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 "components/cronet/android/cronet_url_request_context.h"
-
-#include <string>
-
-#include "base/android/jni_android.h"
-#include "base/android/jni_string.h"
-#include "base/android/scoped_java_ref.h"
-#include "base/logging.h"
-#include "base/memory/scoped_ptr.h"
-#include "base/metrics/statistics_recorder.h"
-#include "base/values.h"
-#include "components/cronet/android/cronet_url_request.h"
-#include "components/cronet/android/cronet_url_request_adapter.h"
-#include "components/cronet/android/cronet_url_request_context_adapter.h"
-#include "components/cronet/url_request_context_config.h"
-#include "jni/CronetUrlRequestContext_jni.h"
-
-namespace cronet {
-
-// Init network thread on Java side.
-void initJavaNetworkThread(
-    const base::android::ScopedJavaGlobalRef<jobject>& jowner) {
-  JNIEnv* env = base::android::AttachCurrentThread();
-  Java_CronetUrlRequestContext_initNetworkThread(env, jowner.obj());
-}
-
-// Explicitly register static JNI functions.
-bool CronetUrlRequestContextRegisterJni(JNIEnv* env) {
-  return RegisterNativesImpl(env);
-}
-
-// Creates RequestContextAdater if config is valid URLRequestContextConfig,
-// returns 0 otherwise.
-static jlong CreateRequestContextAdapter(JNIEnv* env,
-                                         jobject jcaller,
-                                         jobject japp_context,
-                                         jstring jconfig) {
-  std::string config_string =
-      base::android::ConvertJavaStringToUTF8(env, jconfig);
-  scoped_ptr<URLRequestContextConfig> context_config(
-      new URLRequestContextConfig());
-  if (!context_config->LoadFromJSON(config_string))
-    return 0;
-
-  CronetURLRequestContextAdapter* context_adapter =
-      new CronetURLRequestContextAdapter(context_config.Pass());
-  return reinterpret_cast<jlong>(context_adapter);
-}
-
-// Destroys native objects.
-static void DestroyRequestContextAdapter(JNIEnv* env,
-                                         jobject jcaller,
-                                         jlong jurl_request_context_adapter) {
-  DCHECK(jurl_request_context_adapter);
-  CronetURLRequestContextAdapter* context_adapter =
-      reinterpret_cast<CronetURLRequestContextAdapter*>(
-          jurl_request_context_adapter);
-  context_adapter->Destroy();
-}
-
-// Starts recording NetLog into file with |fileName|.
-static void StartNetLogToFile(JNIEnv* env,
-                              jobject jcaller,
-                              jlong jurl_request_context_adapter,
-                              jstring jfile_name) {
-  if (jurl_request_context_adapter == 0)
-    return;
-  CronetURLRequestContextAdapter* context_adapter =
-      reinterpret_cast<CronetURLRequestContextAdapter*>(
-          jurl_request_context_adapter);
-  std::string file_name =
-      base::android::ConvertJavaStringToUTF8(env, jfile_name);
-  context_adapter->StartNetLogToFile(file_name);
-}
-
-// Stops recording NetLog.
-static void StopNetLog(JNIEnv* env,
-                       jobject jcaller,
-                       jlong jurl_request_context_adapter) {
-  if (jurl_request_context_adapter == 0)
-    return;
-  CronetURLRequestContextAdapter* context_adapter =
-      reinterpret_cast<CronetURLRequestContextAdapter*>(
-          jurl_request_context_adapter);
-  context_adapter->StopNetLog();
-}
-
-static jint SetMinLogLevel(JNIEnv* env, jobject jcaller, jint jlog_level) {
-  jint old_log_level = static_cast<jint>(logging::GetMinLogLevel());
-  // MinLogLevel is global, shared by all URLRequestContexts.
-  logging::SetMinLogLevel(static_cast<int>(jlog_level));
-  return old_log_level;
-}
-
-// Called on application's main Java thread.
-static void InitRequestContextOnMainThread(JNIEnv* env,
-                                           jobject jcaller,
-                                           jlong jurl_request_context_adapter) {
-  if (jurl_request_context_adapter == 0)
-    return;
-
-  CronetURLRequestContextAdapter* context_adapter =
-      reinterpret_cast<CronetURLRequestContextAdapter*>(
-          jurl_request_context_adapter);
-
-  base::android::ScopedJavaGlobalRef<jobject> jcaller_ref;
-  jcaller_ref.Reset(env, jcaller);
-  base::Closure init_java_network_thread = base::Bind(&initJavaNetworkThread,
-                                                      jcaller_ref);
-  context_adapter->InitRequestContextOnMainThread(init_java_network_thread);
-}
-
-}  // namespace cronet
diff --git a/components/cronet/android/cronet_url_request_context.h b/components/cronet/android/cronet_url_request_context.h
deleted file mode 100644
index 72a87fe..0000000
--- a/components/cronet/android/cronet_url_request_context.h
+++ /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.
-
-#ifndef COMPONENTS_CRONET_ANDROID_CRONET_URL_REQUEST_CONTEXT_H_
-#define COMPONENTS_CRONET_ANDROID_CRONET_URL_REQUEST_CONTEXT_H_
-
-#include <jni.h>
-
-namespace cronet {
-
-bool CronetUrlRequestContextRegisterJni(JNIEnv* env);
-
-}  // namespace cronet
-
-#endif  // COMPONENTS_CRONET_ANDROID_CRONET_URL_REQUEST_CONTEXT_H_
diff --git a/components/cronet/android/cronet_url_request_context_adapter.cc b/components/cronet/android/cronet_url_request_context_adapter.cc
index 3c2cfa8..39c585a8 100644
--- a/components/cronet/android/cronet_url_request_context_adapter.cc
+++ b/components/cronet/android/cronet_url_request_context_adapter.cc
@@ -4,26 +4,24 @@
 
 #include "components/cronet/android/cronet_url_request_context_adapter.h"
 
+#include "base/android/jni_android.h"
+#include "base/android/jni_string.h"
 #include "base/bind.h"
 #include "base/files/file_util.h"
+#include "base/logging.h"
 #include "base/single_thread_task_runner.h"
+#include "base/values.h"
 #include "components/cronet/url_request_context_config.h"
+#include "jni/CronetUrlRequestContext_jni.h"
 #include "net/base/load_flags.h"
 #include "net/base/net_errors.h"
 #include "net/base/net_log_logger.h"
 #include "net/base/network_delegate_impl.h"
-#include "net/cert/cert_verifier.h"
 #include "net/http/http_auth_handler_factory.h"
-#include "net/http/http_network_layer.h"
-#include "net/http/http_server_properties.h"
 #include "net/proxy/proxy_config_service_fixed.h"
 #include "net/proxy/proxy_service.h"
-#include "net/ssl/ssl_config_service_defaults.h"
-#include "net/url_request/static_http_user_agent_settings.h"
 #include "net/url_request/url_request_context.h"
 #include "net/url_request/url_request_context_builder.h"
-#include "net/url_request/url_request_context_storage.h"
-#include "net/url_request/url_request_job_factory_impl.h"
 
 namespace {
 
@@ -108,6 +106,11 @@
 
 namespace cronet {
 
+// Explicitly register static JNI functions.
+bool CronetUrlRequestContextAdapterRegisterJni(JNIEnv* env) {
+  return RegisterNativesImpl(env);
+}
+
 CronetURLRequestContextAdapter::CronetURLRequestContextAdapter(
     scoped_ptr<URLRequestContextConfig> context_config)
     : network_thread_(new base::Thread("network")),
@@ -125,20 +128,23 @@
 }
 
 void CronetURLRequestContextAdapter::InitRequestContextOnMainThread(
-    const base::Closure& java_init_network_thread) {
+    JNIEnv* env,
+    jobject jcaller) {
+  base::android::ScopedJavaGlobalRef<jobject> jcaller_ref;
+  jcaller_ref.Reset(env, jcaller);
   proxy_config_service_.reset(net::ProxyService::CreateSystemProxyConfigService(
       GetNetworkTaskRunner(), nullptr));
   GetNetworkTaskRunner()->PostTask(
       FROM_HERE,
       base::Bind(&CronetURLRequestContextAdapter::InitializeOnNetworkThread,
-                 base::Unretained(this),
-                 Passed(&context_config_),
-                 java_init_network_thread));
+                 base::Unretained(this), Passed(&context_config_),
+                 jcaller_ref));
 }
 
 void CronetURLRequestContextAdapter::InitializeOnNetworkThread(
     scoped_ptr<URLRequestContextConfig> config,
-    const base::Closure& java_init_network_thread) {
+    const base::android::ScopedJavaGlobalRef<jobject>&
+        jcronet_url_request_context) {
   DCHECK(GetNetworkTaskRunner()->BelongsToCurrentThread());
   DCHECK(!is_context_initialized_);
   // TODO(mmenke):  Add method to have the builder enable SPDY.
@@ -199,7 +205,9 @@
     }
   }
 
-  java_init_network_thread.Run();
+  JNIEnv* env = base::android::AttachCurrentThread();
+  Java_CronetUrlRequestContext_initNetworkThread(
+      env, jcronet_url_request_context.obj());
 
   is_context_initialized_ = true;
   while (!tasks_waiting_for_context_.empty()) {
@@ -208,7 +216,7 @@
   }
 }
 
-void CronetURLRequestContextAdapter::Destroy() {
+void CronetURLRequestContextAdapter::Destroy(JNIEnv* env, jobject jcaller) {
   DCHECK(!GetNetworkTaskRunner()->BelongsToCurrentThread());
   // Stick network_thread_ in a local, as |this| may be destroyed from the
   // network thread before delete network_thread is called.
@@ -254,17 +262,18 @@
   return network_thread_->task_runner();
 }
 
-void CronetURLRequestContextAdapter::StartNetLogToFile(
-    const std::string& file_name) {
+void CronetURLRequestContextAdapter::StartNetLogToFile(JNIEnv* env,
+                                                       jobject jcaller,
+                                                       jstring jfile_name) {
   GetNetworkTaskRunner()->PostTask(
       FROM_HERE,
       base::Bind(
           &CronetURLRequestContextAdapter::StartNetLogToFileOnNetworkThread,
           base::Unretained(this),
-          file_name));
+          base::android::ConvertJavaStringToUTF8(env, jfile_name)));
 }
 
-void CronetURLRequestContextAdapter::StopNetLog() {
+void CronetURLRequestContextAdapter::StopNetLog(JNIEnv* env, jobject jcaller) {
   GetNetworkTaskRunner()->PostTask(
       FROM_HERE,
       base::Bind(&CronetURLRequestContextAdapter::StopNetLogOnNetworkThread,
@@ -296,4 +305,29 @@
   }
 }
 
+// Creates RequestContextAdater if config is valid URLRequestContextConfig,
+// returns 0 otherwise.
+static jlong CreateRequestContextAdapter(JNIEnv* env,
+                                         jclass jcaller,
+                                         jobject japp_context,
+                                         jstring jconfig) {
+  std::string config_string =
+      base::android::ConvertJavaStringToUTF8(env, jconfig);
+  scoped_ptr<URLRequestContextConfig> context_config(
+      new URLRequestContextConfig());
+  if (!context_config->LoadFromJSON(config_string))
+    return 0;
+
+  CronetURLRequestContextAdapter* context_adapter =
+      new CronetURLRequestContextAdapter(context_config.Pass());
+  return reinterpret_cast<jlong>(context_adapter);
+}
+
+static jint SetMinLogLevel(JNIEnv* env, jclass jcaller, jint jlog_level) {
+  jint old_log_level = static_cast<jint>(logging::GetMinLogLevel());
+  // MinLogLevel is global, shared by all URLRequestContexts.
+  logging::SetMinLogLevel(static_cast<int>(jlog_level));
+  return old_log_level;
+}
+
 }  // namespace cronet
diff --git a/components/cronet/android/cronet_url_request_context_adapter.h b/components/cronet/android/cronet_url_request_context_adapter.h
index 31de6d9..da44d0f 100644
--- a/components/cronet/android/cronet_url_request_context_adapter.h
+++ b/components/cronet/android/cronet_url_request_context_adapter.h
@@ -5,16 +5,17 @@
 #ifndef COMPONENTS_CRONET_ANDROID_CRONET_URL_REQUEST_CONTEXT_ADAPTER_H_
 #define COMPONENTS_CRONET_ANDROID_CRONET_URL_REQUEST_CONTEXT_ADAPTER_H_
 
+#include <jni.h>
+
 #include <queue>
 #include <string>
 
+#include "base/android/scoped_java_ref.h"
 #include "base/callback.h"
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/scoped_ptr.h"
 #include "base/threading/thread.h"
-#include "net/base/net_log.h"
-#include "net/base/network_change_notifier.h"
 
 namespace base {
 class SingleThreadTaskRunner;
@@ -30,6 +31,8 @@
 
 struct URLRequestContextConfig;
 
+bool CronetUrlRequestContextAdapterRegisterJni(JNIEnv* env);
+
 // Adapter between Java CronetUrlRequestContext and net::URLRequestContext.
 class CronetURLRequestContextAdapter {
  public:
@@ -39,12 +42,11 @@
   ~CronetURLRequestContextAdapter();
 
   // Called on main Java thread to initialize URLRequestContext.
-  void InitRequestContextOnMainThread(
-      const base::Closure& java_init_network_thread);
+  void InitRequestContextOnMainThread(JNIEnv* env, jobject jcaller);
 
   // Releases all resources for the request context and deletes the object.
   // Blocks until network thread is destroyed after running all pending tasks.
-  void Destroy();
+  void Destroy(JNIEnv* env, jobject jcaller);
 
   // Posts a task that might depend on the context being initialized
   // to the network thread.
@@ -55,9 +57,9 @@
 
   net::URLRequestContext* GetURLRequestContext();
 
-  void StartNetLogToFile(const std::string& file_name);
+  void StartNetLogToFile(JNIEnv* env, jobject jcaller, jstring jfile_name);
 
-  void StopNetLog();
+  void StopNetLog(JNIEnv* env, jobject jcaller);
 
   // Default net::LOAD flags used to create requests.
   int default_load_flags() const { return default_load_flags_; }
@@ -67,9 +69,9 @@
 
  private:
   // Initializes |context_| on the Network thread.
-  void InitializeOnNetworkThread(
-      scoped_ptr<URLRequestContextConfig> config,
-      const base::Closure& java_init_network_thread);
+  void InitializeOnNetworkThread(scoped_ptr<URLRequestContextConfig> config,
+                                 const base::android::ScopedJavaGlobalRef<
+                                     jobject>& jcronet_url_request_context);
 
   // Runs a task that might depend on the context being initialized.
   // This method should only be run on the network thread.
diff --git a/components/cronet/android/java/src/org/chromium/net/CronetUploadDataStream.java b/components/cronet/android/java/src/org/chromium/net/CronetUploadDataStream.java
new file mode 100644
index 0000000..731623d
--- /dev/null
+++ b/components/cronet/android/java/src/org/chromium/net/CronetUploadDataStream.java
@@ -0,0 +1,302 @@
+// 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.net;
+
+import org.chromium.base.CalledByNative;
+import org.chromium.base.JNINamespace;
+import org.chromium.base.NativeClassQualifiedName;
+
+import java.nio.ByteBuffer;
+import java.util.concurrent.Executor;
+
+/**
+ * Pass an upload body to a UrlRequest using an UploadDataProvider.
+ */
+@JNINamespace("cronet")
+final class CronetUploadDataStream implements UploadDataSink {
+    // These are never changed, once a request starts.
+    private final Executor mExecutor;
+    private final UploadDataProvider mDataProvider;
+    private final long mLength;
+    private CronetUrlRequest mRequest;
+
+    // Reusable read task, to reduce redundant memory allocation.
+    private final Runnable mReadTask = new Runnable() {
+        @Override
+        public void run() {
+            synchronized (mLock) {
+                if (mReading || mRewinding || mByteBuffer == null
+                        || mUploadDataStreamDelegate == 0) {
+                    throw new IllegalStateException(
+                            "Unexpected readData call.");
+                }
+                mReading = true;
+            }
+            try {
+                mDataProvider.read(CronetUploadDataStream.this, mByteBuffer);
+            } catch (Exception exception) {
+                onError(exception);
+            }
+        }
+    };
+
+    // ByteBuffer created in the native code and passed to
+    // UploadDataProvider for reading. It is only valid from the
+    // call to mDataProvider.read until onError or onReadSucceeded.
+    private ByteBuffer mByteBuffer = null;
+
+    // Lock that protects all subsequent variables. The delegate has to be
+    // protected to ensure safe shutdown, mReading and mRewinding are protected
+    // to robustly detect getting read/rewind results more often than expected.
+    private final Object mLock = new Object();
+
+    // Native adapter delegate object, owned by the CronetUploadDataStream.
+    // It's only deleted after the native UploadDataStream object is destroyed.
+    // All access to the delegate is synchronized, for safe usage and cleanup.
+    private long mUploadDataStreamDelegate = 0;
+
+    private boolean mReading = false;
+    private boolean mRewinding = false;
+    private boolean mDestroyDelegatePostponed = false;
+
+    /**
+     * Constructs a CronetUploadDataStream.
+     * @param dataProvider the UploadDataProvider to read data from.
+     * @param executor the Executor to execute UploadDataProvider tasks.
+     */
+    public CronetUploadDataStream(UploadDataProvider dataProvider,
+            Executor executor) {
+        mExecutor = executor;
+        mDataProvider = dataProvider;
+        mLength = mDataProvider.getLength();
+        if (mLength < 0) {
+            // TODO(mmenke):  Add tests and remove this line.
+            throw new IllegalArgumentException(
+                    "Chunked uploads not supported.");
+        }
+    }
+
+    /**
+     * Called by native code to make the UploadDataProvider read data into
+     * {@code byteBuffer}.
+     */
+    @SuppressWarnings("unused")
+    @CalledByNative
+    void readData(ByteBuffer byteBuffer) {
+        mByteBuffer = byteBuffer;
+        mExecutor.execute(mReadTask);
+    }
+
+    // TODO(mmenke): Consider implementing a cancel method.
+    // currently wait for any pending read to complete.
+
+    /**
+     * Called by native code to make the UploadDataProvider rewind upload data.
+     */
+    @SuppressWarnings("unused")
+    @CalledByNative
+    void rewind() {
+        Runnable task = new Runnable() {
+            @Override
+            public void run() {
+                synchronized (mLock) {
+                    if (mReading || mRewinding
+                            || mUploadDataStreamDelegate == 0) {
+                        throw new IllegalStateException(
+                                "Unexpected rewind call.");
+                    }
+                    mRewinding = true;
+                }
+                try {
+                    mDataProvider.rewind(CronetUploadDataStream.this);
+                } catch (Exception exception) {
+                    onError(exception);
+                }
+            }
+        };
+        mExecutor.execute(task);
+    }
+
+    /**
+     * Called by native code to destroy the native adapter delegate, when the
+     * adapter is destroyed.
+     */
+    @SuppressWarnings("unused")
+    @CalledByNative
+    void onAdapterDestroyed() {
+        Runnable task = new Runnable() {
+            @Override
+            public void run() {
+                destroyDelegate();
+            }
+        };
+
+        mExecutor.execute(task);
+    }
+
+    /**
+     * Helper method called when an exception occurred. This method resets
+     * states and propagates the error to the request.
+     */
+    private void onError(Exception exception) {
+        synchronized (mLock) {
+            if (!mReading && !mRewinding) {
+                throw new IllegalStateException(
+                        "There is no read or rewind in progress.");
+            }
+            mReading = false;
+            mRewinding = false;
+            mByteBuffer = null;
+            destroyDelegateIfPostponed();
+        }
+
+        // Just fail the request - simpler to fail directly, and
+        // UploadDataStream only supports failing during initialization, not
+        // while reading. This should be safe, even if we deleted the adapter,
+        // because in that case, the request has already been cancelled.
+        mRequest.onUploadException(exception);
+    }
+
+    @Override
+    public void onReadSucceeded(boolean lastChunk) {
+        synchronized (mLock) {
+            if (!mReading) {
+                throw new IllegalStateException("Non-existent read succeeded.");
+            }
+            if (lastChunk && mLength >= 0) {
+                throw new IllegalArgumentException(
+                        "Non-chunked upload can't have last chunk");
+            }
+            int bytesRead = mByteBuffer.position();
+
+            mByteBuffer = null;
+            mReading = false;
+
+            destroyDelegateIfPostponed();
+            // Request may been canceled already.
+            if (mUploadDataStreamDelegate == 0) {
+                return;
+            }
+            nativeOnReadSucceeded(mUploadDataStreamDelegate, bytesRead,
+                    lastChunk);
+        }
+    }
+
+    @Override
+    public void onReadError(Exception exception) {
+        synchronized (mLock) {
+            if (!mReading) {
+                throw new IllegalStateException("Non-existent read failed.");
+            }
+            onError(exception);
+        }
+    }
+
+    @Override
+    public void onRewindSucceeded() {
+        synchronized (mLock) {
+            if (!mRewinding) {
+                throw new IllegalStateException(
+                        "Non-existent rewind succeeded.");
+            }
+            mRewinding = false;
+            // Request may been canceled already.
+            if (mUploadDataStreamDelegate == 0) {
+                return;
+            }
+            nativeOnRewindSucceeded(mUploadDataStreamDelegate);
+        }
+    }
+
+    @Override
+    public void onRewindError(Exception exception) {
+        synchronized (mLock) {
+            if (!mRewinding) {
+                throw new IllegalStateException("Non-existent rewind failed.");
+            }
+            onError(exception);
+        }
+    }
+
+    /**
+     * The delegate is owned by the CronetUploadDataStream, so it can be
+     * destroyed safely when there is no pending read; however, destruction is
+     * initiated by the destruction of the native UploadDataStream.
+     */
+    private void destroyDelegate() {
+        synchronized (mLock) {
+            if (mReading) {
+                // Wait for the read to complete before destroy the delegate.
+                mDestroyDelegatePostponed = true;
+                return;
+            }
+            if (mUploadDataStreamDelegate == 0) {
+                return;
+            }
+            nativeDestroyDelegate(mUploadDataStreamDelegate);
+            mUploadDataStreamDelegate = 0;
+        }
+    }
+
+    /**
+     * Destroy the native delegate if the destruction is postponed due to a
+     * pending read, which has since completed. Caller needs to be on executor
+     * thread.
+     */
+    private void destroyDelegateIfPostponed() {
+        synchronized (mLock) {
+            if (mReading) {
+                throw new IllegalStateException(
+                        "Method should not be called when read has not completed.");
+            }
+            if (mDestroyDelegatePostponed) {
+                destroyDelegate();
+            }
+        }
+    }
+
+    /**
+     * Creates native objects and attaches them to the underlying request
+     * adapter object.
+     * TODO(mmenke):  If more types of native upload streams are needed, create
+     * an interface with just this method, to minimize CronetURLRequest's
+     * dependencies on each upload stream type.
+     */
+    void attachToRequest(CronetUrlRequest request, long requestAdapter) {
+        mRequest = request;
+        mUploadDataStreamDelegate =
+                nativeAttachUploadDataToRequest(requestAdapter, mLength);
+    }
+
+    /**
+     * Creates a native UploadDataStreamDelegate and UploadDataStreamAdapter
+     * for testing.
+     * @return the address of the native CronetUploadDataStreamAdapter object.
+     */
+    public long createAdapterForTesting() {
+        mUploadDataStreamDelegate = nativeCreateDelegateForTesting();
+        return nativeCreateAdapterForTesting(mLength, mUploadDataStreamDelegate);
+    }
+
+    // Native methods are implemented in upload_data_stream_adapter.cc.
+
+    private native long nativeAttachUploadDataToRequest(long urlRequestAdapter,
+            long length);
+
+    private native long nativeCreateDelegateForTesting();
+
+    private native long nativeCreateAdapterForTesting(long length,
+            long delegate);
+
+    @NativeClassQualifiedName("CronetUploadDataStreamDelegate")
+    private native void nativeOnReadSucceeded(long nativePtr,
+            int bytesRead, boolean finalChunk);
+
+    @NativeClassQualifiedName("CronetUploadDataStreamDelegate")
+    private native void nativeOnRewindSucceeded(long nativePtr);
+
+    private static native void nativeDestroyDelegate(
+            long uploadDataStreamDelegate);
+}
diff --git a/components/cronet/android/java/src/org/chromium/net/CronetUrlRequest.java b/components/cronet/android/java/src/org/chromium/net/CronetUrlRequest.java
index 1a085b8..b7e6905f 100644
--- a/components/cronet/android/java/src/org/chromium/net/CronetUrlRequest.java
+++ b/components/cronet/android/java/src/org/chromium/net/CronetUrlRequest.java
@@ -57,6 +57,8 @@
     private String mInitialMethod;
     private final HeadersList mRequestHeaders = new HeadersList();
 
+    private CronetUploadDataStream mUploadDataStream;
+
     private NativeResponseInfo mResponseInfo;
 
     /*
@@ -258,28 +260,55 @@
     }
 
     @Override
+    public void setUploadDataProvider(UploadDataProvider uploadDataProvider, Executor executor) {
+        if (uploadDataProvider == null) {
+            throw new NullPointerException("Invalid UploadDataProvider.");
+        }
+        if (mInitialMethod == null) {
+            mInitialMethod = "POST";
+        }
+        mUploadDataStream = new CronetUploadDataStream(uploadDataProvider, executor);
+    }
+
+    @Override
     public void start() {
         synchronized (mUrlRequestAdapterLock) {
             checkNotStarted();
-            mUrlRequestAdapter = nativeCreateRequestAdapter(
-                    mRequestContext.getUrlRequestContextAdapter(),
-                    mInitialUrl,
-                    mPriority);
-            mRequestContext.onRequestStarted(this);
-            if (mInitialMethod != null) {
-                if (!nativeSetHttpMethod(mUrlRequestAdapter, mInitialMethod)) {
-                    destroyRequestAdapter();
-                    throw new IllegalArgumentException("Invalid http method "
-                            + mInitialMethod);
+
+            try {
+                mUrlRequestAdapter = nativeCreateRequestAdapter(
+                        mRequestContext.getUrlRequestContextAdapter(), mInitialUrl, mPriority);
+                mRequestContext.onRequestStarted(this);
+                if (mInitialMethod != null) {
+                    if (!nativeSetHttpMethod(mUrlRequestAdapter, mInitialMethod)) {
+                        throw new IllegalArgumentException("Invalid http method " + mInitialMethod);
+                    }
                 }
-            }
-            for (Pair<String, String> header : mRequestHeaders) {
-                if (!nativeAddHeader(mUrlRequestAdapter, header.first,
-                        header.second)) {
-                    destroyRequestAdapter();
-                    throw new IllegalArgumentException("Invalid header "
-                            + header.first + "=" + header.second);
+
+                boolean hasContentType = false;
+                for (Pair<String, String> header : mRequestHeaders) {
+                    if (header.first.equalsIgnoreCase("Content-Type")
+                            && !header.second.isEmpty()) {
+                        hasContentType = true;
+                    }
+                    if (!nativeAddHeader(mUrlRequestAdapter, header.first, header.second)) {
+                        destroyRequestAdapter();
+                        throw new IllegalArgumentException(
+                                "Invalid header " + header.first + "=" + header.second);
+                    }
                 }
+                if (mUploadDataStream != null) {
+                    if (!hasContentType) {
+                        throw new IllegalArgumentException(
+                                "Requests with upload data must have a Content-Type.");
+                    }
+                    mUploadDataStream.attachToRequest(this, mUrlRequestAdapter);
+                }
+            } catch (RuntimeException e) {
+                // If there's an exception, cleanup and then throw the
+                // exception to the caller.
+                destroyRequestAdapter();
+                throw e;
             }
             if (mDisableCache) {
                 nativeDisableCache(mUrlRequestAdapter);
@@ -425,6 +454,28 @@
         }
     }
 
+    /**
+     * Called when UploadDataProvider encounters an error.
+     */
+    void onUploadException(Exception e) {
+        UrlRequestException uploadError =
+                new UrlRequestException("Exception received from UploadDataProvider", e);
+        Log.e(CronetUrlRequestContext.LOG_TAG, "Exception in upload method", e);
+        // Do not call into listener if request is canceled.
+        synchronized (mUrlRequestAdapterLock) {
+            if (isCanceled()) {
+                return;
+            }
+            cancel();
+        }
+        try {
+            mListener.onFailed(this, mResponseInfo, uploadError);
+        } catch (Exception failException) {
+            Log.e(CronetUrlRequestContext.LOG_TAG, "Exception notifying of failed upload",
+                    failException);
+        }
+    }
+
     ////////////////////////////////////////////////
     // Private methods called by the native code.
     // Always called on network thread.
diff --git a/components/cronet/android/java/src/org/chromium/net/CronetUrlRequestContext.java b/components/cronet/android/java/src/org/chromium/net/CronetUrlRequestContext.java
index 40c91d2..03f4944 100644
--- a/components/cronet/android/java/src/org/chromium/net/CronetUrlRequestContext.java
+++ b/components/cronet/android/java/src/org/chromium/net/CronetUrlRequestContext.java
@@ -14,6 +14,7 @@
 
 import org.chromium.base.CalledByNative;
 import org.chromium.base.JNINamespace;
+import org.chromium.base.NativeClassQualifiedName;
 
 import java.util.concurrent.Executor;
 import java.util.concurrent.atomic.AtomicInteger;
@@ -112,7 +113,7 @@
             if (!haveRequestContextAdapter()) {
                 return;
             }
-            nativeDestroyRequestContextAdapter(mUrlRequestContextAdapter);
+            nativeDestroy(mUrlRequestContextAdapter);
             mUrlRequestContextAdapter = 0;
         }
     }
@@ -194,19 +195,21 @@
     }
 
     // Native methods are implemented in cronet_url_request_context.cc.
-    private native long nativeCreateRequestContextAdapter(Context context,
-            String config);
+    private static native long nativeCreateRequestContextAdapter(
+            Context context, String config);
 
-    private native void nativeDestroyRequestContextAdapter(
-            long urlRequestContextAdapter);
+    private static native int nativeSetMinLogLevel(int loggingLevel);
 
-    private native void nativeStartNetLogToFile(
-            long urlRequestContextAdapter, String fileName);
+    @NativeClassQualifiedName("CronetURLRequestContextAdapter")
+    private native void nativeDestroy(long nativePtr);
 
-    private native void nativeStopNetLog(long urlRequestContextAdapter);
+    @NativeClassQualifiedName("CronetURLRequestContextAdapter")
+    private native void nativeStartNetLogToFile(long nativePtr,
+            String fileName);
 
-    private native int nativeSetMinLogLevel(int loggingLevel);
+    @NativeClassQualifiedName("CronetURLRequestContextAdapter")
+    private native void nativeStopNetLog(long nativePtr);
 
-    private native void nativeInitRequestContextOnMainThread(
-            long urlRequestContextAdapter);
+    @NativeClassQualifiedName("CronetURLRequestContextAdapter")
+    private native void nativeInitRequestContextOnMainThread(long nativePtr);
 }
diff --git a/components/cronet/android/java/src/org/chromium/net/UploadDataProvider.java b/components/cronet/android/java/src/org/chromium/net/UploadDataProvider.java
new file mode 100644
index 0000000..37e6b06
--- /dev/null
+++ b/components/cronet/android/java/src/org/chromium/net/UploadDataProvider.java
@@ -0,0 +1,69 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.net;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+/**
+ * Interface allowing the embedder to provide an upload body to
+ * {@link UrlRequest}. It supports both non-chunked (size known in advanced) and
+ * chunked (size not known in advance) uploads. Be aware that not all servers
+ * support chunked uploads.
+ *
+ * <p>An upload is either always chunked, across multiple uploads if the data
+ * ends up being sent more than once, or never chunked.
+ */
+public interface UploadDataProvider {
+    /**
+     * @return if this is a non-chunked upload, returns the length of the
+     *         upload. Must always return -1 if this is a chunked upload.
+     */
+    public long getLength();
+
+    /**
+     * Reads upload data into {@code byteBuffer}. Upon completion, the buffer's
+     * position is updated to the end of the bytes that were read. The buffer's
+     * limit is not changed. Each call of this method must be followed be a
+     * single call, either synchronous or asynchronous, to
+     * {@code uploadDataSink}: {@link UploadDataSink#onReadSucceeded} on success
+     * or {@link UploadDataSink#onReadError} on failure. Neither read nor rewind
+     * will be called until one of those methods or the other is called. Even if
+     * the associated {@link UrlRequest} is cancelled, one or the other must
+     * still be called before resources can be safely freed. Throwing an
+     * exception will also result in resources being freed and the request being
+     * errored out.
+     *
+     * @param uploadDataSink The object to notify when the read has completed,
+     *            successfully or otherwise.
+     * @param byteBuffer The buffer to copy the read bytes into.
+     * @throws IOException if any IOException occurred during the process.
+     */
+    public void read(UploadDataSink uploadDataSink, ByteBuffer byteBuffer)
+            throws IOException;
+
+    /**
+     * Rewinds upload data. Each call must be followed be a single
+     * call, either synchronous or asynchronous, to {@code uploadDataSink}:
+     * {@link UploadDataSink#onRewindSucceeded} on success or
+     * {@link UploadDataSink#onRewindError} on failure. Neither read nor rewind
+     * will be called until one of those methods or the other is called.
+     * Even if the associated {@link UrlRequest} is cancelled, one or the other
+     * must still be called before resources can be safely freed. Throwing an
+     * exception will also result in resources being freed and the request being
+     * errored out.
+     *
+     * <p>If rewinding is not supported, this should call
+     * {@link UploadDataSink#onRewindError}. Note that rewinding is required to
+     * follow redirects that preserve the upload body, and for retrying when the
+     * server times out stale sockets.
+     *
+     * @param uploadDataSink The object to notify when the rewind operation has
+     *         completed, successfully or otherwise.
+     * @throws IOException if any IOException occurred during the process.
+     */
+    public void rewind(UploadDataSink uploadDataSink)
+            throws IOException;
+}
diff --git a/components/cronet/android/java/src/org/chromium/net/UploadDataSink.java b/components/cronet/android/java/src/org/chromium/net/UploadDataSink.java
new file mode 100644
index 0000000..213ccefe
--- /dev/null
+++ b/components/cronet/android/java/src/org/chromium/net/UploadDataSink.java
@@ -0,0 +1,36 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.net;
+
+/**
+ * Interface with callbacks methods for {@link UploadDataProvider}. All methods
+ * may be called synchronously or asynchronously, on any thread.
+ */
+public interface UploadDataSink {
+    /**
+     * Called by {@link UploadDataProvider} when a read succeeds.
+     * @param finalChunk For chunked uploads, {@code true} if this is the final
+     *     read. It must be {@code false} for non-chunked uploads.
+     */
+    public void onReadSucceeded(boolean finalChunk);
+
+    /**
+     * Called by {@link UploadDataProvider} when a read fails.
+     * @param exception Exception passed on to the embedder.
+     */
+    public void onReadError(Exception exception);
+
+    /**
+     * Called by {@link UploadDataProvider} when a rewind succeeds.
+     */
+    public void onRewindSucceeded();
+
+    /**
+     * Called by {@link UploadDataProvider} when a rewind fails, or if rewinding
+     * uploads is not supported.
+     * @param exception Exception passed on to the embedder.
+     */
+    public void onRewindError(Exception exception);
+}
diff --git a/components/cronet/android/java/src/org/chromium/net/UrlRequest.java b/components/cronet/android/java/src/org/chromium/net/UrlRequest.java
index 54b72a19..01f1a0e 100644
--- a/components/cronet/android/java/src/org/chromium/net/UrlRequest.java
+++ b/components/cronet/android/java/src/org/chromium/net/UrlRequest.java
@@ -4,6 +4,8 @@
 
 package org.chromium.net;
 
+import java.util.concurrent.Executor;
+
 /**
  * HTTP request (GET, PUT or POST).
  * Note:  All methods must be called on the Executor passed in during creation.
@@ -45,6 +47,19 @@
     public void addHeader(String header, String value);
 
     /**
+     * Sets upload data. Must be done before request has started. May only be
+     * invoked once per request. Switches method to "POST" if not explicitly
+     * set. Starting the request will throw an exception if a Content-Type
+     * header is not set.
+     *
+     * @param uploadDataProvider responsible for providing the upload data.
+     * @param executor All {@code uploadDataProvider} methods will be called
+     *     using this {@code Executor}. May optionally be the same
+     *     {@code Executor} the request itself is using.
+     */
+    public void setUploadDataProvider(UploadDataProvider uploadDataProvider, Executor executor);
+
+    /**
      * Starts the request, all callbacks go to listener. May only be called
      * once. May not be called if cancel has been called on the request.
      */
diff --git a/components/cronet/android/test/cronet_test_jni.cc b/components/cronet/android/test/cronet_test_jni.cc
index 4a35300..73f775f0 100644
--- a/components/cronet/android/test/cronet_test_jni.cc
+++ b/components/cronet/android/test/cronet_test_jni.cc
@@ -11,13 +11,16 @@
 #include "mock_url_request_job_factory.h"
 #include "native_test_server.h"
 #include "network_change_notifier_util.h"
+#include "test_upload_data_stream_handler.h"
 
 namespace {
 
 const base::android::RegistrationMethod kCronetTestsRegisteredMethods[] = {
-  {"MockUrlRequestJobFactory", cronet::RegisterMockUrlRequestJobFactory},
-  {"RegisterNativeTestServer", cronet::RegisterNativeTestServer},
-  {"NetworkChangeNotifierUtil", cronet::RegisterNetworkChangeNotifierUtil},
+    {"MockUrlRequestJobFactory", cronet::RegisterMockUrlRequestJobFactory},
+    {"RegisterNativeTestServer", cronet::RegisterNativeTestServer},
+    {"NetworkChangeNotifierUtil", cronet::RegisterNetworkChangeNotifierUtil},
+    {"TestUploadDataStreamHandlerRegisterJni",
+     cronet::TestUploadDataStreamHandlerRegisterJni},
 };
 
 }  // namespace
diff --git a/components/cronet/android/test/javatests/src/org/chromium/net/CronetUploadTest.java b/components/cronet/android/test/javatests/src/org/chromium/net/CronetUploadTest.java
new file mode 100644
index 0000000..218cbf6b
--- /dev/null
+++ b/components/cronet/android/test/javatests/src/org/chromium/net/CronetUploadTest.java
@@ -0,0 +1,298 @@
+// 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.net;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.chromium.base.test.util.Feature;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+/**
+ * Tests that directly drive {@code CronetUploadDataStream} and
+ * {@code UploadDataProvider} to simulate different ordering of reset, init,
+ * read, and rewind calls.
+ */
+public class CronetUploadTest extends CronetTestBase {
+    private TestDrivenDataProvider mDataProvider;
+    private CronetUploadDataStream mUploadDataStream;
+    private TestUploadDataStreamHandler mHandler;
+    // Address of native CronetUploadDataStreamAdapter object.
+    private long mAdapter = 0;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        launchCronetTestApp();
+        ExecutorService executor = Executors.newSingleThreadExecutor();
+        List<byte[]> reads = Arrays.asList("hello".getBytes());
+        mDataProvider = new TestDrivenDataProvider(executor, reads);
+        mUploadDataStream = new CronetUploadDataStream(mDataProvider, executor);
+        mAdapter = mUploadDataStream.createAdapterForTesting();
+        mHandler = new TestUploadDataStreamHandler(mAdapter);
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        // Destroy handler's native objects.
+        mHandler.destroyNativeObjects();
+        super.tearDown();
+    }
+
+    /**
+     * Tests that after some data is read, init triggers a rewind, and that
+     * before the rewind completes, init blocks.
+     */
+    @SmallTest
+    @Feature({"Cronet"})
+    public void testInitTriggersRewindAndInitBeforeRewindCompletes()
+            throws Exception {
+        // Init completes synchronously and read succeeds.
+        assertTrue(mHandler.init());
+        mHandler.read();
+        mDataProvider.waitForReadRequest();
+        mHandler.checkReadCallbackNotInvoked();
+        mDataProvider.onReadSucceeded(mUploadDataStream);
+        mHandler.waitForReadComplete();
+        mDataProvider.assertReadNotPending();
+        assertEquals(0, mDataProvider.getNumRewindCalls());
+        assertEquals(1, mDataProvider.getNumReadCalls());
+        assertEquals("hello", mHandler.getData());
+
+        // Reset and then init, which should trigger a rewind.
+        mHandler.reset();
+        assertEquals("", mHandler.getData());
+        assertFalse(mHandler.init());
+        mDataProvider.waitForRewindRequest();
+        mHandler.checkInitCallbackNotInvoked();
+
+        // Before rewind completes, reset and init should block.
+        mHandler.reset();
+        assertFalse(mHandler.init());
+
+        // Signal rewind completes, and wait for init to complete.
+        mHandler.checkInitCallbackNotInvoked();
+        mDataProvider.onRewindSucceeded(mUploadDataStream);
+        mHandler.waitForInitComplete();
+        mDataProvider.assertRewindNotPending();
+
+        // Read should complete successfully since init has completed.
+        mHandler.read();
+        mDataProvider.waitForReadRequest();
+        mHandler.checkReadCallbackNotInvoked();
+        mDataProvider.onReadSucceeded(mUploadDataStream);
+        mHandler.waitForReadComplete();
+        mDataProvider.assertReadNotPending();
+        assertEquals(1, mDataProvider.getNumRewindCalls());
+        assertEquals(2, mDataProvider.getNumReadCalls());
+        assertEquals("hello", mHandler.getData());
+    }
+
+    /**
+     * Tests that after some data is read, init triggers a rewind, and that
+     * after the rewind completes, init does not block.
+     */
+    @SmallTest
+    @Feature({"Cronet"})
+    public void testInitTriggersRewindAndInitAfterRewindCompletes()
+            throws Exception {
+        // Init completes synchronously and read succeeds.
+        assertTrue(mHandler.init());
+        mHandler.read();
+        mDataProvider.waitForReadRequest();
+        mHandler.checkReadCallbackNotInvoked();
+        mDataProvider.onReadSucceeded(mUploadDataStream);
+        mHandler.waitForReadComplete();
+        mDataProvider.assertReadNotPending();
+        assertEquals(0, mDataProvider.getNumRewindCalls());
+        assertEquals(1, mDataProvider.getNumReadCalls());
+        assertEquals("hello", mHandler.getData());
+
+        // Reset and then init, which should trigger a rewind.
+        mHandler.reset();
+        assertEquals("", mHandler.getData());
+        assertFalse(mHandler.init());
+        mDataProvider.waitForRewindRequest();
+        mHandler.checkInitCallbackNotInvoked();
+
+        // Signal rewind completes, and wait for init to complete.
+        mDataProvider.onRewindSucceeded(mUploadDataStream);
+        mHandler.waitForInitComplete();
+        mDataProvider.assertRewindNotPending();
+
+        // Reset and init should not block, since rewind has completed.
+        mHandler.reset();
+        assertTrue(mHandler.init());
+
+        // Read should complete successfully since init has completed.
+        mHandler.read();
+        mDataProvider.waitForReadRequest();
+        mHandler.checkReadCallbackNotInvoked();
+        mDataProvider.onReadSucceeded(mUploadDataStream);
+        mHandler.waitForReadComplete();
+        mDataProvider.assertReadNotPending();
+        assertEquals(1, mDataProvider.getNumRewindCalls());
+        assertEquals(2, mDataProvider.getNumReadCalls());
+        assertEquals("hello", mHandler.getData());
+    }
+
+    /**
+     * Tests that if init before read completes, a rewind is triggered when
+     * read completes.
+     */
+    @SmallTest
+    @Feature({"Cronet"})
+    public void testReadCompleteTriggerRewind() throws Exception {
+        // Reset and init before read completes.
+        assertTrue(mHandler.init());
+        mHandler.read();
+        mDataProvider.waitForReadRequest();
+        mHandler.checkReadCallbackNotInvoked();
+        mHandler.reset();
+        // Init should return asynchronously, since there is a pending read.
+        assertFalse(mHandler.init());
+        mDataProvider.assertRewindNotPending();
+        mHandler.checkInitCallbackNotInvoked();
+        assertEquals(0, mDataProvider.getNumRewindCalls());
+        assertEquals(1, mDataProvider.getNumReadCalls());
+        assertEquals("", mHandler.getData());
+
+        // Read completes should trigger a rewind.
+        mDataProvider.onReadSucceeded(mUploadDataStream);
+        mDataProvider.waitForRewindRequest();
+        mHandler.checkInitCallbackNotInvoked();
+        mDataProvider.onRewindSucceeded(mUploadDataStream);
+        mHandler.waitForInitComplete();
+        mDataProvider.assertRewindNotPending();
+        assertEquals(1, mDataProvider.getNumRewindCalls());
+        assertEquals(1, mDataProvider.getNumReadCalls());
+        assertEquals("", mHandler.getData());
+    }
+
+    /**
+     * Tests that when init again after rewind completes, no additional rewind
+     * is triggered. This test is the same as testReadCompleteTriggerRewind
+     * except that this test invokes reset and init again in the end.
+     */
+    @SmallTest
+    @Feature({"Cronet"})
+    public void testReadCompleteTriggerRewindOnlyOneRewind() throws Exception {
+        testReadCompleteTriggerRewind();
+        // Reset and Init again, no rewind should happen.
+        mHandler.reset();
+        assertTrue(mHandler.init());
+        mDataProvider.assertRewindNotPending();
+        assertEquals(1, mDataProvider.getNumRewindCalls());
+        assertEquals(1, mDataProvider.getNumReadCalls());
+        assertEquals("", mHandler.getData());
+    }
+
+    /**
+     * Tests that if reset before read completes, no rewind is triggered, and
+     * that a following init triggers rewind.
+     */
+    @SmallTest
+    @Feature({"Cronet"})
+    public void testResetBeforeReadCompleteAndInitTriggerRewind()
+            throws Exception {
+        // Reset before read completes. Rewind is not triggered.
+        assertTrue(mHandler.init());
+        mHandler.read();
+        mDataProvider.waitForReadRequest();
+        mHandler.checkReadCallbackNotInvoked();
+        mHandler.reset();
+        mDataProvider.onReadSucceeded(mUploadDataStream);
+        mDataProvider.assertRewindNotPending();
+        assertEquals(0, mDataProvider.getNumRewindCalls());
+        assertEquals(1, mDataProvider.getNumReadCalls());
+        assertEquals("", mHandler.getData());
+
+        // Init should trigger a rewind.
+        assertFalse(mHandler.init());
+        mDataProvider.waitForRewindRequest();
+        mHandler.checkInitCallbackNotInvoked();
+        mDataProvider.onRewindSucceeded(mUploadDataStream);
+        mHandler.waitForInitComplete();
+        mDataProvider.assertRewindNotPending();
+        assertEquals(1, mDataProvider.getNumRewindCalls());
+        assertEquals(1, mDataProvider.getNumReadCalls());
+        assertEquals("", mHandler.getData());
+    }
+
+    /**
+     * Tests that there is no crash when native CronetUploadDataStreamAdapter is
+     * destroyed while read is pending. The test is racy since read could
+     * complete either before or after onDestroyAdapter() is called in
+     * CronetUploadDataStream. However, the test should pass either way, though
+     * we are interested in the latter case.
+     */
+    @SmallTest
+    @Feature({"Cronet"})
+    public void testDestroyAdapterBeforeReadComplete()
+            throws Exception {
+        // Start a read and wait for it to be pending.
+        assertTrue(mHandler.init());
+        mHandler.read();
+        mDataProvider.waitForReadRequest();
+        mHandler.checkReadCallbackNotInvoked();
+
+        // Destroy the C++ object, which should trigger the Java
+        // onAdapterDestroyed() which should block until the read completes.
+        mAdapter = 0;
+
+        // Make the read complete should not encounter a crash.
+        mDataProvider.onReadSucceeded(mUploadDataStream);
+
+        assertEquals(0, mDataProvider.getNumRewindCalls());
+        assertEquals(1, mDataProvider.getNumReadCalls());
+    }
+
+    /**
+     * Tests that there is no crash when native CronetUploadDataStreamAdapter is
+     * destroyed while rewind is pending. The test is racy since rewind could
+     * complete either before or after onDestroyAdapter() is called in
+     * CronetUploadDataStream. However, the test should pass either way, though
+     * we are interested in the latter case.
+     */
+    @SmallTest
+    @Feature({"Cronet"})
+    public void testDestroyAdapterBeforeRewindComplete()
+            throws Exception {
+        // Start a read and wait for it to complete.
+        assertTrue(mHandler.init());
+        mHandler.read();
+        mDataProvider.waitForReadRequest();
+        mHandler.checkReadCallbackNotInvoked();
+        mDataProvider.onReadSucceeded(mUploadDataStream);
+        mHandler.waitForReadComplete();
+        mDataProvider.assertReadNotPending();
+        assertEquals(0, mDataProvider.getNumRewindCalls());
+        assertEquals(1, mDataProvider.getNumReadCalls());
+        assertEquals("hello", mHandler.getData());
+
+        // Reset and then init, which should trigger a rewind.
+        mHandler.reset();
+        assertEquals("", mHandler.getData());
+        assertFalse(mHandler.init());
+        mDataProvider.waitForRewindRequest();
+        mHandler.checkInitCallbackNotInvoked();
+
+        // Destroy the C++ object, which should trigger the Java
+        // onAdapterDestroyed().
+        mAdapter = 0;
+
+        // Signal rewind completes, and wait for init to complete.
+        mHandler.checkInitCallbackNotInvoked();
+        mDataProvider.onRewindSucceeded(mUploadDataStream);
+        mHandler.waitForInitComplete();
+        mDataProvider.assertRewindNotPending();
+
+        assertEquals(1, mDataProvider.getNumRewindCalls());
+        assertEquals(1, mDataProvider.getNumReadCalls());
+    }
+}
diff --git a/components/cronet/android/test/javatests/src/org/chromium/net/CronetUrlRequestTest.java b/components/cronet/android/test/javatests/src/org/chromium/net/CronetUrlRequestTest.java
index b83cba3..d3f93ee 100644
--- a/components/cronet/android/test/javatests/src/org/chromium/net/CronetUrlRequestTest.java
+++ b/components/cronet/android/test/javatests/src/org/chromium/net/CronetUrlRequestTest.java
@@ -447,6 +447,385 @@
         assertEquals(listener.mResponseStep, ResponseStep.ON_RESPONSE_STARTED);
     }
 
+    @SmallTest
+    @Feature({"Cronet"})
+    public void testUploadSetDataProvider() throws Exception {
+        TestUrlRequestListener listener = new TestUrlRequestListener();
+        UrlRequest urlRequest = mActivity.mUrlRequestContext.createRequest(
+                NativeTestServer.getEchoBodyURL(), listener, listener.getExecutor());
+
+        try {
+            urlRequest.setUploadDataProvider(null, listener.getExecutor());
+            fail("Exception not thrown");
+        } catch (NullPointerException e) {
+            assertEquals("Invalid UploadDataProvider.", e.getMessage());
+        }
+
+        TestUploadDataProvider dataProvider = new TestUploadDataProvider(
+                TestUploadDataProvider.SuccessCallbackMode.SYNC, listener.getExecutor());
+        urlRequest.setUploadDataProvider(dataProvider, listener.getExecutor());
+        try {
+            urlRequest.start();
+            fail("Exception not thrown");
+        } catch (IllegalArgumentException e) {
+            assertEquals("Requests with upload data must have a Content-Type.", e.getMessage());
+        }
+    }
+
+    @SmallTest
+    @Feature({"Cronet"})
+    public void testUploadEmptyBodySync() throws Exception {
+        TestUrlRequestListener listener = new TestUrlRequestListener();
+        UrlRequest urlRequest = mActivity.mUrlRequestContext.createRequest(
+                NativeTestServer.getEchoBodyURL(), listener, listener.getExecutor());
+
+        TestUploadDataProvider dataProvider = new TestUploadDataProvider(
+                TestUploadDataProvider.SuccessCallbackMode.SYNC, listener.getExecutor());
+        urlRequest.setUploadDataProvider(dataProvider, listener.getExecutor());
+        urlRequest.addHeader("Content-Type", "useless/string");
+        urlRequest.start();
+        listener.blockForDone();
+
+        assertEquals(0, dataProvider.getLength());
+        assertEquals(0, dataProvider.getNumReadCalls());
+        assertEquals(0, dataProvider.getNumRewindCalls());
+
+        assertEquals(200, listener.mResponseInfo.getHttpStatusCode());
+        assertEquals("", listener.mResponseAsString);
+    }
+
+    @SmallTest
+    @Feature({"Cronet"})
+    public void testUploadSync() throws Exception {
+        TestUrlRequestListener listener = new TestUrlRequestListener();
+        UrlRequest urlRequest = mActivity.mUrlRequestContext.createRequest(
+                NativeTestServer.getEchoBodyURL(), listener, listener.getExecutor());
+
+        TestUploadDataProvider dataProvider = new TestUploadDataProvider(
+                TestUploadDataProvider.SuccessCallbackMode.SYNC, listener.getExecutor());
+        dataProvider.addRead("test".getBytes());
+        urlRequest.setUploadDataProvider(dataProvider, listener.getExecutor());
+        urlRequest.addHeader("Content-Type", "useless/string");
+        urlRequest.start();
+        listener.blockForDone();
+
+        assertEquals(4, dataProvider.getLength());
+        assertEquals(1, dataProvider.getNumReadCalls());
+        assertEquals(0, dataProvider.getNumRewindCalls());
+
+        assertEquals(200, listener.mResponseInfo.getHttpStatusCode());
+        assertEquals("test", listener.mResponseAsString);
+    }
+
+    @SmallTest
+    @Feature({"Cronet"})
+    public void testUploadMultiplePiecesSync() throws Exception {
+        TestUrlRequestListener listener = new TestUrlRequestListener();
+        UrlRequest urlRequest = mActivity.mUrlRequestContext.createRequest(
+                NativeTestServer.getEchoBodyURL(), listener, listener.getExecutor());
+
+        TestUploadDataProvider dataProvider = new TestUploadDataProvider(
+                TestUploadDataProvider.SuccessCallbackMode.SYNC, listener.getExecutor());
+        dataProvider.addRead("Y".getBytes());
+        dataProvider.addRead("et ".getBytes());
+        dataProvider.addRead("another ".getBytes());
+        dataProvider.addRead("test".getBytes());
+
+        urlRequest.setUploadDataProvider(dataProvider, listener.getExecutor());
+        urlRequest.addHeader("Content-Type", "useless/string");
+        urlRequest.start();
+        listener.blockForDone();
+
+        assertEquals(16, dataProvider.getLength());
+        assertEquals(4, dataProvider.getNumReadCalls());
+        assertEquals(0, dataProvider.getNumRewindCalls());
+
+        assertEquals(200, listener.mResponseInfo.getHttpStatusCode());
+        assertEquals("Yet another test", listener.mResponseAsString);
+    }
+
+    @SmallTest
+    @Feature({"Cronet"})
+    public void testUploadMultiplePiecesAsync() throws Exception {
+        TestUrlRequestListener listener = new TestUrlRequestListener();
+        UrlRequest urlRequest = mActivity.mUrlRequestContext.createRequest(
+                NativeTestServer.getEchoBodyURL(), listener, listener.getExecutor());
+
+        TestUploadDataProvider dataProvider = new TestUploadDataProvider(
+                TestUploadDataProvider.SuccessCallbackMode.ASYNC, listener.getExecutor());
+        dataProvider.addRead("Y".getBytes());
+        dataProvider.addRead("et ".getBytes());
+        dataProvider.addRead("another ".getBytes());
+        dataProvider.addRead("test".getBytes());
+
+        urlRequest.setUploadDataProvider(dataProvider, listener.getExecutor());
+        urlRequest.addHeader("Content-Type", "useless/string");
+        urlRequest.start();
+        listener.blockForDone();
+
+        assertEquals(16, dataProvider.getLength());
+        assertEquals(4, dataProvider.getNumReadCalls());
+        assertEquals(0, dataProvider.getNumRewindCalls());
+
+        assertEquals(200, listener.mResponseInfo.getHttpStatusCode());
+        assertEquals("Yet another test", listener.mResponseAsString);
+    }
+
+    @SmallTest
+    @Feature({"Cronet"})
+    public void testUploadChangesDefaultMethod() throws Exception {
+        TestUrlRequestListener listener = new TestUrlRequestListener();
+        UrlRequest urlRequest = mActivity.mUrlRequestContext.createRequest(
+                NativeTestServer.getEchoMethodURL(), listener, listener.getExecutor());
+
+        TestUploadDataProvider dataProvider = new TestUploadDataProvider(
+                TestUploadDataProvider.SuccessCallbackMode.SYNC, listener.getExecutor());
+        dataProvider.addRead("test".getBytes());
+        urlRequest.setUploadDataProvider(dataProvider, listener.getExecutor());
+        urlRequest.addHeader("Content-Type", "useless/string");
+        urlRequest.start();
+        listener.blockForDone();
+
+        assertEquals(200, listener.mResponseInfo.getHttpStatusCode());
+        assertEquals("POST", listener.mResponseAsString);
+    }
+
+    @SmallTest
+    @Feature({"Cronet"})
+    public void testUploadWithSetMethod() throws Exception {
+        TestUrlRequestListener listener = new TestUrlRequestListener();
+        UrlRequest urlRequest = mActivity.mUrlRequestContext.createRequest(
+                NativeTestServer.getEchoMethodURL(), listener, listener.getExecutor());
+
+        final String method = "PUT";
+        urlRequest.setHttpMethod(method);
+
+        TestUploadDataProvider dataProvider = new TestUploadDataProvider(
+                TestUploadDataProvider.SuccessCallbackMode.SYNC, listener.getExecutor());
+        dataProvider.addRead("test".getBytes());
+        urlRequest.setUploadDataProvider(dataProvider, listener.getExecutor());
+        urlRequest.addHeader("Content-Type", "useless/string");
+        urlRequest.start();
+        listener.blockForDone();
+
+        assertEquals(200, listener.mResponseInfo.getHttpStatusCode());
+        assertEquals("PUT", listener.mResponseAsString);
+    }
+
+    @SmallTest
+    @Feature({"Cronet"})
+    public void testUploadRedirectSync() throws Exception {
+        TestUrlRequestListener listener = new TestUrlRequestListener();
+        UrlRequest urlRequest = mActivity.mUrlRequestContext.createRequest(
+                NativeTestServer.getRedirectToEchoBody(), listener, listener.getExecutor());
+
+        TestUploadDataProvider dataProvider = new TestUploadDataProvider(
+                TestUploadDataProvider.SuccessCallbackMode.SYNC, listener.getExecutor());
+        dataProvider.addRead("test".getBytes());
+        urlRequest.setUploadDataProvider(dataProvider, listener.getExecutor());
+        urlRequest.addHeader("Content-Type", "useless/string");
+        urlRequest.start();
+        listener.blockForDone();
+
+        // 1 read call before the rewind, 1 after.
+        assertEquals(2, dataProvider.getNumReadCalls());
+        assertEquals(1, dataProvider.getNumRewindCalls());
+
+        assertEquals(200, listener.mResponseInfo.getHttpStatusCode());
+        assertEquals("test", listener.mResponseAsString);
+    }
+
+    @SmallTest
+    @Feature({"Cronet"})
+    public void testUploadRedirectAsync() throws Exception {
+        TestUrlRequestListener listener = new TestUrlRequestListener();
+        UrlRequest urlRequest = mActivity.mUrlRequestContext.createRequest(
+                NativeTestServer.getRedirectToEchoBody(), listener, listener.getExecutor());
+
+        TestUploadDataProvider dataProvider = new TestUploadDataProvider(
+                TestUploadDataProvider.SuccessCallbackMode.ASYNC, listener.getExecutor());
+        dataProvider.addRead("test".getBytes());
+        urlRequest.setUploadDataProvider(dataProvider, listener.getExecutor());
+        urlRequest.addHeader("Content-Type", "useless/string");
+        urlRequest.start();
+        listener.blockForDone();
+
+        // 1 read call before the rewind, 1 after.
+        assertEquals(2, dataProvider.getNumReadCalls());
+        assertEquals(1, dataProvider.getNumRewindCalls());
+
+        assertEquals(200, listener.mResponseInfo.getHttpStatusCode());
+        assertEquals("test", listener.mResponseAsString);
+    }
+
+    @SmallTest
+    @Feature({"Cronet"})
+    public void testUploadReadFailSync() throws Exception {
+        TestUrlRequestListener listener = new TestUrlRequestListener();
+        UrlRequest urlRequest = mActivity.mUrlRequestContext.createRequest(
+                NativeTestServer.getEchoBodyURL(), listener, listener.getExecutor());
+
+        TestUploadDataProvider dataProvider = new TestUploadDataProvider(
+                TestUploadDataProvider.SuccessCallbackMode.SYNC, listener.getExecutor());
+        dataProvider.setReadFailure(0, TestUploadDataProvider.FailMode.CALLBACK_SYNC);
+        // This will never be read, but if the length is 0, read may never be
+        // called.
+        dataProvider.addRead("test".getBytes());
+        urlRequest.setUploadDataProvider(dataProvider, listener.getExecutor());
+        urlRequest.addHeader("Content-Type", "useless/string");
+        urlRequest.start();
+        listener.blockForDone();
+
+        assertEquals(1, dataProvider.getNumReadCalls());
+        assertEquals(0, dataProvider.getNumRewindCalls());
+
+        assertEquals("Exception received from UploadDataProvider", listener.mError.getMessage());
+        assertEquals("Sync read failure", listener.mError.getCause().getMessage());
+        assertEquals(null, listener.mResponseInfo);
+    }
+
+    @SmallTest
+    @Feature({"Cronet"})
+    public void testUploadReadFailAsync() throws Exception {
+        TestUrlRequestListener listener = new TestUrlRequestListener();
+        UrlRequest urlRequest = mActivity.mUrlRequestContext.createRequest(
+                NativeTestServer.getEchoBodyURL(), listener, listener.getExecutor());
+
+        TestUploadDataProvider dataProvider = new TestUploadDataProvider(
+                TestUploadDataProvider.SuccessCallbackMode.SYNC, listener.getExecutor());
+        dataProvider.setReadFailure(0, TestUploadDataProvider.FailMode.CALLBACK_ASYNC);
+        // This will never be read, but if the length is 0, read may never be
+        // called.
+        dataProvider.addRead("test".getBytes());
+        urlRequest.setUploadDataProvider(dataProvider, listener.getExecutor());
+        urlRequest.addHeader("Content-Type", "useless/string");
+        urlRequest.start();
+        listener.blockForDone();
+
+        assertEquals(1, dataProvider.getNumReadCalls());
+        assertEquals(0, dataProvider.getNumRewindCalls());
+
+        assertEquals("Exception received from UploadDataProvider", listener.mError.getMessage());
+        assertEquals("Async read failure", listener.mError.getCause().getMessage());
+        assertEquals(null, listener.mResponseInfo);
+    }
+
+    @SmallTest
+    @Feature({"Cronet"})
+    public void testUploadReadFailThrown() throws Exception {
+        TestUrlRequestListener listener = new TestUrlRequestListener();
+        UrlRequest urlRequest = mActivity.mUrlRequestContext.createRequest(
+                NativeTestServer.getEchoBodyURL(), listener, listener.getExecutor());
+
+        TestUploadDataProvider dataProvider = new TestUploadDataProvider(
+                TestUploadDataProvider.SuccessCallbackMode.SYNC, listener.getExecutor());
+        dataProvider.setReadFailure(0, TestUploadDataProvider.FailMode.THROWN);
+        // This will never be read, but if the length is 0, read may never be
+        // called.
+        dataProvider.addRead("test".getBytes());
+        urlRequest.setUploadDataProvider(dataProvider, listener.getExecutor());
+        urlRequest.addHeader("Content-Type", "useless/string");
+        urlRequest.start();
+        listener.blockForDone();
+
+        assertEquals(1, dataProvider.getNumReadCalls());
+        assertEquals(0, dataProvider.getNumRewindCalls());
+
+        assertEquals("Exception received from UploadDataProvider", listener.mError.getMessage());
+        assertEquals("Thrown read failure", listener.mError.getCause().getMessage());
+        assertEquals(null, listener.mResponseInfo);
+    }
+
+    @SmallTest
+    @Feature({"Cronet"})
+    public void testUploadRewindFailSync() throws Exception {
+        TestUrlRequestListener listener = new TestUrlRequestListener();
+        UrlRequest urlRequest = mActivity.mUrlRequestContext.createRequest(
+                NativeTestServer.getRedirectToEchoBody(), listener, listener.getExecutor());
+
+        TestUploadDataProvider dataProvider = new TestUploadDataProvider(
+                TestUploadDataProvider.SuccessCallbackMode.SYNC, listener.getExecutor());
+        dataProvider.setRewindFailure(TestUploadDataProvider.FailMode.CALLBACK_SYNC);
+        dataProvider.addRead("test".getBytes());
+        urlRequest.setUploadDataProvider(dataProvider, listener.getExecutor());
+        urlRequest.addHeader("Content-Type", "useless/string");
+        urlRequest.start();
+        listener.blockForDone();
+
+        assertEquals(1, dataProvider.getNumReadCalls());
+        assertEquals(1, dataProvider.getNumRewindCalls());
+
+        assertEquals("Exception received from UploadDataProvider", listener.mError.getMessage());
+        assertEquals("Sync rewind failure", listener.mError.getCause().getMessage());
+        assertEquals(null, listener.mResponseInfo);
+    }
+
+    @SmallTest
+    @Feature({"Cronet"})
+    public void testUploadRewindFailAsync() throws Exception {
+        TestUrlRequestListener listener = new TestUrlRequestListener();
+        UrlRequest urlRequest = mActivity.mUrlRequestContext.createRequest(
+                NativeTestServer.getRedirectToEchoBody(), listener, listener.getExecutor());
+
+        TestUploadDataProvider dataProvider = new TestUploadDataProvider(
+                TestUploadDataProvider.SuccessCallbackMode.ASYNC, listener.getExecutor());
+        dataProvider.setRewindFailure(TestUploadDataProvider.FailMode.CALLBACK_ASYNC);
+        dataProvider.addRead("test".getBytes());
+        urlRequest.setUploadDataProvider(dataProvider, listener.getExecutor());
+        urlRequest.addHeader("Content-Type", "useless/string");
+        urlRequest.start();
+        listener.blockForDone();
+
+        assertEquals(1, dataProvider.getNumReadCalls());
+        assertEquals(1, dataProvider.getNumRewindCalls());
+
+        assertEquals("Exception received from UploadDataProvider", listener.mError.getMessage());
+        assertEquals("Async rewind failure", listener.mError.getCause().getMessage());
+        assertEquals(null, listener.mResponseInfo);
+    }
+
+    @SmallTest
+    @Feature({"Cronet"})
+    public void testUploadRewindFailThrown() throws Exception {
+        TestUrlRequestListener listener = new TestUrlRequestListener();
+        UrlRequest urlRequest = mActivity.mUrlRequestContext.createRequest(
+                NativeTestServer.getRedirectToEchoBody(), listener, listener.getExecutor());
+
+        TestUploadDataProvider dataProvider = new TestUploadDataProvider(
+                TestUploadDataProvider.SuccessCallbackMode.SYNC, listener.getExecutor());
+        dataProvider.setRewindFailure(TestUploadDataProvider.FailMode.THROWN);
+        dataProvider.addRead("test".getBytes());
+        urlRequest.setUploadDataProvider(dataProvider, listener.getExecutor());
+        urlRequest.addHeader("Content-Type", "useless/string");
+        urlRequest.start();
+        listener.blockForDone();
+
+        assertEquals(1, dataProvider.getNumReadCalls());
+        assertEquals(1, dataProvider.getNumRewindCalls());
+
+        assertEquals("Exception received from UploadDataProvider", listener.mError.getMessage());
+        assertEquals("Thrown rewind failure", listener.mError.getCause().getMessage());
+        assertEquals(null, listener.mResponseInfo);
+    }
+
+    @SmallTest
+    @Feature({"Cronet"})
+    public void testUploadChunkedNotSupported() throws Exception {
+        TestUrlRequestListener listener = new TestUrlRequestListener();
+        UrlRequest urlRequest = mActivity.mUrlRequestContext.createRequest(
+                NativeTestServer.getRedirectToEchoBody(), listener, listener.getExecutor());
+
+        TestUploadDataProvider dataProvider = new TestUploadDataProvider(
+                TestUploadDataProvider.SuccessCallbackMode.SYNC, listener.getExecutor());
+        dataProvider.setChunked(true);
+        try {
+            urlRequest.setUploadDataProvider(dataProvider, listener.getExecutor());
+            fail("Exception not thrown");
+        } catch (IllegalArgumentException e) {
+            assertEquals("Chunked uploads not supported.", e.getMessage());
+        }
+    }
+
     private void throwOrCancel(FailureType failureType, ResponseStep failureStep,
             boolean expectResponseInfo, boolean expectError) {
         TestUrlRequestListener listener = new TestUrlRequestListener();
diff --git a/components/cronet/android/test/javatests/src/org/chromium/net/TestDataProvider.java b/components/cronet/android/test/javatests/src/org/chromium/net/TestDataProvider.java
new file mode 100644
index 0000000..7175fbe
--- /dev/null
+++ b/components/cronet/android/test/javatests/src/org/chromium/net/TestDataProvider.java
@@ -0,0 +1,245 @@
+// 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.net;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.concurrent.Executor;
+
+/**
+ * An UploadDataProvider implementation used in tests.
+ */
+class TestUploadDataProvider implements UploadDataProvider {
+    // Indicates whether all success callbacks are synchronous or asynchronous.
+    // Doesn't apply to errors.
+    enum SuccessCallbackMode {
+        SYNC,
+        ASYNC
+    };
+
+    // Indicates whether failures should throw exceptions, invoke callbacks
+    // synchronously, or invoke callback asynchronously.
+    enum FailMode {
+        NONE,
+        THROWN,
+        CALLBACK_SYNC,
+        CALLBACK_ASYNC
+    };
+
+    private ArrayList<byte[]> mReads = new ArrayList<byte[]>();
+    private final SuccessCallbackMode mSuccessCallbackMode;
+    private final Executor mExecutor;
+
+    private boolean mChunked = false;
+
+    // Index of read to fail on.
+    private int mReadFailIndex = -1;
+    // Indicates how to fail on a read.
+    private FailMode mReadFailMode = FailMode.NONE;
+
+    private FailMode mRewindFailMode = FailMode.NONE;
+
+    private int mNumReadCalls = 0;
+    private int mNumRewindCalls = 0;
+
+    private int mNextRead = 0;
+    private boolean mStarted = false;
+    private boolean mReadPending = false;
+    private boolean mRewindPending = false;
+    // Used to ensure there are no read/rewind requests after a failure.
+    private boolean mFailed = false;
+
+    TestUploadDataProvider(SuccessCallbackMode successCallbackMode,
+            Executor executor) {
+        mSuccessCallbackMode = successCallbackMode;
+        mExecutor = executor;
+    }
+
+    // Adds the result to be returned by a successful read request.  The
+    // returned bytes must all fit within the read buffer provided by Cronet.
+    // After a rewind, if there is one, all reads will be repeated.
+    public void addRead(byte[] read) {
+        if (mStarted) {
+            throw new IllegalStateException("Adding bytes after read");
+        }
+        mReads.add(read);
+    }
+
+    public void setReadFailure(int readFailIndex, FailMode readFailMode) {
+        mReadFailIndex = readFailIndex;
+        mReadFailMode = readFailMode;
+    }
+
+    public void setRewindFailure(FailMode rewindFailMode) {
+        mRewindFailMode = rewindFailMode;
+    }
+
+    public void setChunked(boolean chunked) {
+        mChunked = chunked;
+    }
+
+    public int getNumReadCalls() {
+        return mNumReadCalls;
+    }
+
+    public int getNumRewindCalls() {
+        return mNumRewindCalls;
+    }
+
+    /**
+     * Returns the cumulative length of all data added by calls to addRead.
+     */
+    @Override
+    public long getLength() {
+        if (mChunked) {
+            return -1;
+        }
+        long length = 0;
+        for (byte[] read : mReads) {
+            length += read.length;
+        }
+        return length;
+    }
+
+    @Override
+    public void read(final UploadDataSink uploadDataSink,
+            final ByteBuffer byteBuffer) throws IOException {
+        int currentReadCall = mNumReadCalls;
+        ++mNumReadCalls;
+        assertIdle();
+
+        if (maybeFailRead(currentReadCall, uploadDataSink)) {
+            mFailed = true;
+            return;
+        }
+
+        mReadPending = true;
+        mStarted = true;
+
+        final boolean finalChunk = (mChunked && mNextRead == mReads.size());
+        if (mNextRead < mReads.size()) {
+            if ((byteBuffer.limit() - byteBuffer.position())
+                    < mReads.get(mNextRead).length) {
+                throw new IllegalStateException(
+                        "Read buffer smaller than expected.");
+            }
+            byteBuffer.put(mReads.get(mNextRead));
+            ++mNextRead;
+        } else if (!finalChunk) {
+            throw new IllegalStateException(
+                    "Too many reads: " + mNextRead);
+        }
+
+        Runnable completeRunnable = new Runnable() {
+            @Override
+            public void run() {
+                mReadPending = false;
+                uploadDataSink.onReadSucceeded(finalChunk);
+            }
+        };
+        if (mSuccessCallbackMode == SuccessCallbackMode.SYNC) {
+            completeRunnable.run();
+        } else {
+            mExecutor.execute(completeRunnable);
+        }
+    }
+
+    public void rewind(final UploadDataSink uploadDataSink) throws IOException {
+        ++mNumRewindCalls;
+        assertIdle();
+
+        if (maybeFailRewind(uploadDataSink)) {
+            mFailed = true;
+            return;
+        }
+
+        if (mNextRead == 0) {
+            // Should never try and rewind when rewinding does nothing.
+            throw new IllegalStateException(
+                    "Unexpected rewind when already at beginning");
+        }
+
+        mRewindPending = true;
+        mNextRead = 0;
+
+        Runnable completeRunnable = new Runnable() {
+            @Override
+            public void run() {
+                mRewindPending = false;
+                uploadDataSink.onRewindSucceeded();
+            }
+        };
+        if (mSuccessCallbackMode == SuccessCallbackMode.SYNC) {
+            completeRunnable.run();
+        } else {
+            mExecutor.execute(completeRunnable);
+        }
+    }
+
+    private void assertIdle() {
+        if (mReadPending) {
+            throw new IllegalStateException("Unexpected operation during read");
+        }
+        if (mRewindPending) {
+            throw new IllegalStateException(
+                    "Unexpected operation during rewind");
+        }
+        if (mFailed) {
+            throw new IllegalStateException(
+                    "Unexpected operation after failure");
+        }
+    }
+
+    private boolean maybeFailRead(int readIndex,
+            final UploadDataSink uploadDataSink) {
+        if (readIndex != mReadFailIndex)
+            return false;
+
+        switch (mReadFailMode) {
+            case THROWN:
+                throw new IllegalStateException("Thrown read failure");
+            case CALLBACK_SYNC:
+                uploadDataSink.onReadError(
+                        new IllegalStateException("Sync read failure"));
+                return true;
+            case CALLBACK_ASYNC:
+                Runnable errorRunnable = new Runnable() {
+                    @Override
+                    public void run() {
+                        uploadDataSink.onReadError(
+                                new IllegalStateException("Async read failure"));
+                    }
+                };
+                mExecutor.execute(errorRunnable);
+                return true;
+            default:
+                return false;
+        }
+    }
+
+    private boolean maybeFailRewind(final UploadDataSink uploadDataSink) {
+        switch (mRewindFailMode) {
+            case THROWN:
+                throw new IllegalStateException("Thrown rewind failure");
+            case CALLBACK_SYNC:
+                uploadDataSink.onRewindError(
+                        new IllegalStateException("Sync rewind failure"));
+                return true;
+            case CALLBACK_ASYNC:
+                Runnable errorRunnable = new Runnable() {
+                    @Override
+                    public void run() {
+                        uploadDataSink.onRewindError(new IllegalStateException(
+                                "Async rewind failure"));
+                    }
+                };
+                mExecutor.execute(errorRunnable);
+                return true;
+            default:
+                return false;
+        }
+    }
+}
diff --git a/components/cronet/android/test/javatests/src/org/chromium/net/TestDrivenDataProvider.java b/components/cronet/android/test/javatests/src/org/chromium/net/TestDrivenDataProvider.java
new file mode 100644
index 0000000..eff5acf
--- /dev/null
+++ b/components/cronet/android/test/javatests/src/org/chromium/net/TestDrivenDataProvider.java
@@ -0,0 +1,192 @@
+// 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.net;
+
+import android.os.ConditionVariable;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.List;
+import java.util.concurrent.Executor;
+
+/**
+ * An UploadDataProvider that allows tests to invoke {@code onReadSucceeded}
+ * and {@code onRewindSucceeded} on the UploadDataSink directly.
+ * Chunked mode is not supported here, since the main interest is to test
+ * different order of init/read/rewind calls.
+ */
+class TestDrivenDataProvider implements UploadDataProvider {
+    private final Executor mExecutor;
+    private final List<byte[]> mReads;
+    private final ConditionVariable mWaitForReadRequest =
+            new ConditionVariable();
+    private final ConditionVariable mWaitForRewindRequest =
+            new ConditionVariable();
+    // Lock used to synchronize access to mReadPending and mRewindPending.
+    private final Object mLock = new Object();
+
+    private int mNextRead = 0;
+
+    // Only accessible when holding mLock.
+
+    private boolean mReadPending = false;
+    private boolean mRewindPending = false;
+    private int mNumRewindCalls = 0;
+    private int mNumReadCalls = 0;
+
+    /**
+     * Constructor.
+     * @param Executor executor. Executor to run callbacks of UploadDataSink.
+     * @param List<byte[]> reads. Results to be returned by successful read
+     *            requests. Returned bytes must all fit within the read buffer
+     *            provided by Cronet. After a rewind, if there is one, all reads
+     *            will be repeated.
+     */
+    TestDrivenDataProvider(Executor executor, List<byte[]> reads) {
+        mExecutor = executor;
+        mReads = reads;
+    }
+
+    // Called by UploadDataSink on the main thread.
+    @Override
+    public long getLength() {
+        long length = 0;
+        for (byte[] read : mReads) {
+            length += read.length;
+        }
+        return length;
+    }
+
+    // Called by UploadDataSink on the executor thread.
+    @Override
+    public void read(final UploadDataSink uploadDataSink,
+                     final ByteBuffer byteBuffer) throws IOException {
+        synchronized (mLock) {
+            ++mNumReadCalls;
+            assertIdle();
+
+            mReadPending = true;
+            if (mNextRead != mReads.size()) {
+                if ((byteBuffer.limit() - byteBuffer.position())
+                        < mReads.get(mNextRead).length) {
+                    throw new IllegalStateException("Read buffer smaller than expected.");
+                }
+                byteBuffer.put(mReads.get(mNextRead));
+                ++mNextRead;
+            } else {
+                throw new IllegalStateException("Too many reads: " + mNextRead);
+            }
+            mWaitForReadRequest.open();
+        }
+    }
+
+    // Called by UploadDataSink on the executor thread.
+    @Override
+    public void rewind(final UploadDataSink uploadDataSink) throws IOException {
+        synchronized (mLock) {
+            ++mNumRewindCalls;
+            assertIdle();
+
+            if (mNextRead == 0) {
+                // Should never try and rewind when rewinding does nothing.
+                throw new IllegalStateException(
+                        "Unexpected rewind when already at beginning");
+            }
+            mRewindPending = true;
+            mNextRead = 0;
+            mWaitForRewindRequest.open();
+        }
+    }
+
+    // Called by test fixture on the main thread.
+    public void onReadSucceeded(final UploadDataSink uploadDataSink) {
+        Runnable completeRunnable = new Runnable() {
+            @Override
+            public void run() {
+                synchronized (mLock) {
+                    if (!mReadPending) {
+                        throw new IllegalStateException("No read pending.");
+                    }
+                    mReadPending = false;
+                    uploadDataSink.onReadSucceeded(false);
+                }
+            }
+        };
+        mExecutor.execute(completeRunnable);
+    }
+
+
+    // Called by test fixture on the main thread.
+    public void onRewindSucceeded(final UploadDataSink uploadDataSink) {
+        Runnable completeRunnable = new Runnable() {
+            @Override
+            public void run() {
+                synchronized (mLock) {
+                    if (!mRewindPending) {
+                        throw new IllegalStateException("No rewind pending.");
+                    }
+                    mRewindPending = false;
+                    uploadDataSink.onRewindSucceeded();
+                }
+            }
+        };
+        mExecutor.execute(completeRunnable);
+    }
+
+    // Called by test fixture on the main thread.
+    public int getNumReadCalls() {
+        synchronized (mLock) {
+            return mNumReadCalls;
+        }
+    }
+
+    // Called by test fixture on the main thread.
+    public int getNumRewindCalls() {
+        synchronized (mLock) {
+            return mNumRewindCalls;
+        }
+    }
+
+    // Called by test fixture on the main thread.
+    public void waitForReadRequest() {
+        mWaitForReadRequest.block();
+    }
+
+    // Called by test fixture on the main thread.
+    public void resetWaitForReadRequest() {
+        mWaitForReadRequest.close();
+    }
+
+    // Called by test fixture on the main thread.
+    public void waitForRewindRequest() {
+        mWaitForRewindRequest.block();
+    }
+
+    // Called by test fixture on the main thread.
+    public void assertReadNotPending() {
+        synchronized (mLock) {
+            if (mReadPending) {
+                throw new IllegalStateException("Read is pending.");
+            }
+        }
+    }
+
+    // Called by test fixture on the main thread.
+    public void assertRewindNotPending() {
+        synchronized (mLock) {
+            if (mRewindPending) {
+                throw new IllegalStateException("Rewind is pending.");
+            }
+        }
+    }
+
+    /**
+     * Helper method to ensure no read or rewind is in progress.
+     */
+    private void assertIdle() {
+        assertReadNotPending();
+        assertRewindNotPending();
+    }
+}
diff --git a/components/cronet/android/test/src/org/chromium/net/TestUploadDataStreamHandler.java b/components/cronet/android/test/src/org/chromium/net/TestUploadDataStreamHandler.java
new file mode 100644
index 0000000..9b7bb21
--- /dev/null
+++ b/components/cronet/android/test/src/org/chromium/net/TestUploadDataStreamHandler.java
@@ -0,0 +1,170 @@
+// 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.net;
+
+import android.os.ConditionVariable;
+
+import junit.framework.Assert;
+
+import org.chromium.base.CalledByNative;
+import org.chromium.base.JNINamespace;
+import org.chromium.base.NativeClassQualifiedName;
+
+/**
+ * A wrapper class on top of the native net::UploadDataStream. This class is
+ * used in tests to drive the native UploadDataStream directly.
+ */
+@JNINamespace("cronet")
+public final class TestUploadDataStreamHandler {
+    private long mTestUploadDataStreamHandler;
+    private ConditionVariable mWaitInitCalled = new ConditionVariable();
+    private ConditionVariable mWaitInitComplete = new ConditionVariable();
+    private ConditionVariable mWaitReadComplete = new ConditionVariable();
+    private ConditionVariable mWaitResetComplete = new ConditionVariable();
+    // Waits for checkIfInitCallbackInvoked() returns result asynchronously.
+    private ConditionVariable mWaitCheckInit = new ConditionVariable();
+    // Waits for checkIfReadCallbackInvoked() returns result asynchronously.
+    private ConditionVariable mWaitCheckRead = new ConditionVariable();
+    // If true, init completes synchronously.
+    private boolean mInitCompletedSynchronously = false;
+    private String mData = "";
+
+    public TestUploadDataStreamHandler(final long uploadDataStream) {
+        mTestUploadDataStreamHandler =
+                nativeCreateTestUploadDataStreamHandler(uploadDataStream);
+    }
+
+    public void destroyNativeObjects() {
+        nativeDestroy(mTestUploadDataStreamHandler);
+        mTestUploadDataStreamHandler = 0;
+    }
+
+    /**
+     * Init and returns whether init completes synchronously.
+     */
+    public boolean init() {
+        mData = "";
+        nativeInit(mTestUploadDataStreamHandler);
+        mWaitInitCalled.block();
+        mWaitInitCalled.close();
+        return mInitCompletedSynchronously;
+    }
+
+    public void read() {
+        nativeRead(mTestUploadDataStreamHandler);
+    }
+
+    public void reset() {
+        mData = "";
+        nativeReset(mTestUploadDataStreamHandler);
+        mWaitResetComplete.block();
+        mWaitResetComplete.close();
+    }
+
+    /**
+     * Checks that {@link #onInitCompleted} has not invoked asynchronously
+     * by the native UploadDataStream.
+     */
+    public void checkInitCallbackNotInvoked() {
+        nativeCheckInitCallbackNotInvoked(mTestUploadDataStreamHandler);
+        mWaitCheckInit.block();
+        mWaitCheckInit.close();
+    }
+
+    /**
+     * Checks that {@link #onReadCompleted} has not been invoked asynchronously
+     * by the native UploadDataStream.
+     */
+    public void checkReadCallbackNotInvoked() {
+        nativeCheckReadCallbackNotInvoked(mTestUploadDataStreamHandler);
+        mWaitCheckRead.block();
+        mWaitCheckRead.close();
+    }
+
+    public String getData() {
+        return mData;
+    }
+
+    public void waitForReadComplete() {
+        mWaitReadComplete.block();
+        mWaitReadComplete.close();
+    }
+
+    public void waitForInitComplete() {
+        mWaitInitComplete.block();
+        mWaitInitComplete.close();
+    }
+
+    // Called on network thread.
+    @CalledByNative
+    private void onInitCalled(int res) {
+        if (res == 0) {
+            mInitCompletedSynchronously = true;
+        } else {
+            mInitCompletedSynchronously = false;
+        }
+        mWaitInitCalled.open();
+    }
+
+    // Called on network thread.
+    @CalledByNative
+    private void onReadCompleted(int bytesRead, String data) {
+        mData = data;
+        mWaitReadComplete.open();
+    }
+
+    // Called on network thread.
+    @CalledByNative
+    private void onInitCompleted(int res) {
+        // If init() completed synchronously, waitForInitComplete() will
+        // not be invoked in the test, so skip mWaitInitComplete.open().
+        if (!mInitCompletedSynchronously) {
+            mWaitInitComplete.open();
+        }
+    }
+
+    // Called on network thread.
+    @CalledByNative
+    private void onResetCompleted() {
+        mWaitResetComplete.open();
+    }
+
+    // Called on network thread.
+    @CalledByNative
+    private void onCheckInitCallbackNotInvoked(boolean initCallbackNotInvoked) {
+        Assert.assertTrue(initCallbackNotInvoked);
+        mWaitCheckInit.open();
+    }
+
+    // Called on network thread.
+    @CalledByNative
+    private void onCheckReadCallbackNotInvoked(boolean readCallbackNotInvoked) {
+        Assert.assertTrue(readCallbackNotInvoked);
+        mWaitCheckRead.open();
+    }
+
+    @NativeClassQualifiedName("TestUploadDataStreamHandler")
+    private native void nativeInit(long nativePtr);
+
+    @NativeClassQualifiedName("TestUploadDataStreamHandler")
+    private native void nativeRead(long nativePtr);
+
+    @NativeClassQualifiedName("TestUploadDataStreamHandler")
+    private native void nativeReset(long nativePtr);
+
+    @NativeClassQualifiedName("TestUploadDataStreamHandler")
+    private native void nativeCheckInitCallbackNotInvoked(
+            long nativePtr);
+
+    @NativeClassQualifiedName("TestUploadDataStreamHandler")
+    private native void nativeCheckReadCallbackNotInvoked(
+            long nativePtr);
+
+    @NativeClassQualifiedName("TestUploadDataStreamHandler")
+    private native void nativeDestroy(long nativePtr);
+
+    private native long nativeCreateTestUploadDataStreamHandler(
+            long uploadDataStream);
+}
diff --git a/components/cronet/android/test/test_upload_data_stream_handler.cc b/components/cronet/android/test/test_upload_data_stream_handler.cc
new file mode 100644
index 0000000..786caae
--- /dev/null
+++ b/components/cronet/android/test/test_upload_data_stream_handler.cc
@@ -0,0 +1,186 @@
+// 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 "test_upload_data_stream_handler.h"
+
+#include <string>
+
+#include "base/android/jni_android.h"
+#include "base/android/jni_string.h"
+#include "base/bind.h"
+#include "base/message_loop/message_loop_proxy.h"
+#include "jni/TestUploadDataStreamHandler_jni.h"
+#include "net/base/net_errors.h"
+
+namespace cronet {
+
+static const size_t kReadBufferSize = 32768;
+
+TestUploadDataStreamHandler::TestUploadDataStreamHandler(
+    scoped_ptr<net::UploadDataStream> upload_data_stream,
+    JNIEnv* env,
+    jobject jtest_upload_data_stream_handler)
+    : init_callback_invoked_(false),
+      read_callback_invoked_(false),
+      bytes_read_(0),
+      network_thread_(new base::Thread("network")) {
+  upload_data_stream_ = upload_data_stream.Pass();
+  base::Thread::Options options;
+  options.message_loop_type = base::MessageLoop::TYPE_IO;
+  network_thread_->StartWithOptions(options);
+  jtest_upload_data_stream_handler_.Reset(env,
+                                          jtest_upload_data_stream_handler);
+}
+
+TestUploadDataStreamHandler::~TestUploadDataStreamHandler() {
+}
+
+void TestUploadDataStreamHandler::Destroy(JNIEnv* env, jobject jcaller) {
+  DCHECK(!network_thread_->task_runner()->BelongsToCurrentThread());
+  // Stick network_thread_ in a local, so |this| may be destroyed from the
+  // network thread before the network thread is destroyed.
+  scoped_ptr<base::Thread> network_thread = network_thread_.Pass();
+  network_thread->task_runner()->DeleteSoon(FROM_HERE, this);
+  // Deleting thread stops it after all tasks are completed.
+  network_thread.reset();
+}
+
+void TestUploadDataStreamHandler::OnInitCompleted(int res) {
+  DCHECK(network_thread_->task_runner()->BelongsToCurrentThread());
+  init_callback_invoked_ = true;
+  JNIEnv* env = base::android::AttachCurrentThread();
+  cronet::Java_TestUploadDataStreamHandler_onInitCompleted(
+      env, jtest_upload_data_stream_handler_.obj(), res);
+}
+
+void TestUploadDataStreamHandler::OnReadCompleted(int res) {
+  DCHECK(network_thread_->task_runner()->BelongsToCurrentThread());
+  read_callback_invoked_ = true;
+  bytes_read_ = res;
+  NotifyJavaReadCompleted();
+}
+
+void TestUploadDataStreamHandler::Init(JNIEnv* env, jobject jcaller) {
+  DCHECK(!network_thread_->task_runner()->BelongsToCurrentThread());
+  network_thread_->task_runner()->PostTask(
+      FROM_HERE, base::Bind(&TestUploadDataStreamHandler::InitOnNetworkThread,
+                            base::Unretained(this)));
+}
+
+void TestUploadDataStreamHandler::Read(JNIEnv* env, jobject jcaller) {
+  DCHECK(!network_thread_->task_runner()->BelongsToCurrentThread());
+  network_thread_->task_runner()->PostTask(
+      FROM_HERE, base::Bind(&TestUploadDataStreamHandler::ReadOnNetworkThread,
+                            base::Unretained(this)));
+}
+
+void TestUploadDataStreamHandler::Reset(JNIEnv* env, jobject jcaller) {
+  DCHECK(!network_thread_->task_runner()->BelongsToCurrentThread());
+  network_thread_->task_runner()->PostTask(
+      FROM_HERE, base::Bind(&TestUploadDataStreamHandler::ResetOnNetworkThread,
+                            base::Unretained(this)));
+}
+
+void TestUploadDataStreamHandler::CheckInitCallbackNotInvoked(JNIEnv* env,
+                                                              jobject jcaller) {
+  DCHECK(!network_thread_->task_runner()->BelongsToCurrentThread());
+  network_thread_->task_runner()->PostTask(
+      FROM_HERE, base::Bind(&TestUploadDataStreamHandler::
+                                CheckInitCallbackNotInvokedOnNetworkThread,
+                            base::Unretained(this)));
+}
+
+void TestUploadDataStreamHandler::CheckReadCallbackNotInvoked(JNIEnv* env,
+                                                              jobject jcaller) {
+  DCHECK(!network_thread_->task_runner()->BelongsToCurrentThread());
+  network_thread_->task_runner()->PostTask(
+      FROM_HERE, base::Bind(&TestUploadDataStreamHandler::
+                                CheckReadCallbackNotInvokedOnNetworkThread,
+                            base::Unretained(this)));
+}
+
+void TestUploadDataStreamHandler::InitOnNetworkThread() {
+  DCHECK(network_thread_->task_runner()->BelongsToCurrentThread());
+  init_callback_invoked_ = false;
+  read_buffer_ = nullptr;
+  bytes_read_ = 0;
+  int res = upload_data_stream_->Init(base::Bind(
+      &TestUploadDataStreamHandler::OnInitCompleted, base::Unretained(this)));
+  JNIEnv* env = base::android::AttachCurrentThread();
+  cronet::Java_TestUploadDataStreamHandler_onInitCalled(
+      env, jtest_upload_data_stream_handler_.obj(), res);
+
+  if (res == net::OK) {
+    cronet::Java_TestUploadDataStreamHandler_onInitCompleted(
+        env, jtest_upload_data_stream_handler_.obj(), res);
+  }
+}
+
+void TestUploadDataStreamHandler::ReadOnNetworkThread() {
+  DCHECK(network_thread_->task_runner()->BelongsToCurrentThread());
+  read_callback_invoked_ = false;
+  if (!read_buffer_.get())
+    read_buffer_ = new net::IOBufferWithSize(kReadBufferSize);
+
+  int bytes_read = upload_data_stream_->Read(
+      read_buffer_.get(), kReadBufferSize,
+      base::Bind(&TestUploadDataStreamHandler::OnReadCompleted,
+                 base::Unretained(this)));
+  if (bytes_read == net::OK) {
+    bytes_read_ = bytes_read;
+    NotifyJavaReadCompleted();
+  }
+}
+
+void TestUploadDataStreamHandler::ResetOnNetworkThread() {
+  DCHECK(network_thread_->task_runner()->BelongsToCurrentThread());
+  read_buffer_ = nullptr;
+  bytes_read_ = 0;
+  upload_data_stream_->Reset();
+  JNIEnv* env = base::android::AttachCurrentThread();
+  cronet::Java_TestUploadDataStreamHandler_onResetCompleted(
+      env, jtest_upload_data_stream_handler_.obj());
+}
+
+void TestUploadDataStreamHandler::CheckInitCallbackNotInvokedOnNetworkThread() {
+  DCHECK(network_thread_->task_runner()->BelongsToCurrentThread());
+  JNIEnv* env = base::android::AttachCurrentThread();
+  cronet::Java_TestUploadDataStreamHandler_onCheckInitCallbackNotInvoked(
+      env, jtest_upload_data_stream_handler_.obj(), !init_callback_invoked_);
+}
+
+void TestUploadDataStreamHandler::CheckReadCallbackNotInvokedOnNetworkThread() {
+  DCHECK(network_thread_->task_runner()->BelongsToCurrentThread());
+  JNIEnv* env = base::android::AttachCurrentThread();
+  cronet::Java_TestUploadDataStreamHandler_onCheckReadCallbackNotInvoked(
+      env, jtest_upload_data_stream_handler_.obj(), !read_callback_invoked_);
+}
+
+void TestUploadDataStreamHandler::NotifyJavaReadCompleted() {
+  DCHECK(network_thread_->task_runner()->BelongsToCurrentThread());
+  JNIEnv* env = base::android::AttachCurrentThread();
+  std::string data_read = "";
+  if (read_buffer_.get() && bytes_read_ > 0)
+    data_read = std::string(read_buffer_->data(), bytes_read_);
+  cronet::Java_TestUploadDataStreamHandler_onReadCompleted(
+      env, jtest_upload_data_stream_handler_.obj(), bytes_read_,
+      base::android::ConvertUTF8ToJavaString(env, data_read).Release());
+}
+
+static jlong CreateTestUploadDataStreamHandler(
+    JNIEnv* env,
+    jobject jtest_upload_data_stream_handler,
+    jlong jupload_data_stream) {
+  scoped_ptr<net::UploadDataStream> upload_data_stream(
+      reinterpret_cast<net::UploadDataStream*>(jupload_data_stream));
+  TestUploadDataStreamHandler* handler = new TestUploadDataStreamHandler(
+      upload_data_stream.Pass(), env, jtest_upload_data_stream_handler);
+  return reinterpret_cast<jlong>(handler);
+}
+
+bool TestUploadDataStreamHandlerRegisterJni(JNIEnv* env) {
+  return RegisterNativesImpl(env);
+}
+
+}  // namespace cronet
diff --git a/components/cronet/android/test/test_upload_data_stream_handler.h b/components/cronet/android/test/test_upload_data_stream_handler.h
new file mode 100644
index 0000000..dddfbf4a
--- /dev/null
+++ b/components/cronet/android/test/test_upload_data_stream_handler.h
@@ -0,0 +1,99 @@
+// 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 COMPONENTS_CRONET_ANDROID_TEST_TEST_UPLOAD_DATA_STREAM_HANDLER_H_
+#define COMPONENTS_CRONET_ANDROID_TEST_TEST_UPLOAD_DATA_STREAM_HANDLER_H_
+
+#include <jni.h>
+
+#include "base/android/scoped_java_ref.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/threading/thread.h"
+#include "net/base/io_buffer.h"
+#include "net/base/upload_data_stream.h"
+
+namespace cronet {
+
+/**
+ * This class allows a net::UploadDataStream to be driven directly from
+ * Java, for use in tests.
+ */
+class TestUploadDataStreamHandler {
+ public:
+  TestUploadDataStreamHandler(
+      scoped_ptr<net::UploadDataStream> upload_data_stream,
+      JNIEnv* env,
+      jobject jtest_upload_data_stream_handler);
+
+  ~TestUploadDataStreamHandler();
+
+  // Destroys |network_thread_| created by this class.
+  void Destroy(JNIEnv* env, jobject jcaller);
+
+  // Posts a task to |network_thread_| to call the corresponding method of
+  // net::UploadDataStream on |upload_data_stream_|.
+
+  void Init(JNIEnv* env, jobject jcaller);
+  void Read(JNIEnv* env, jobject jcaller);
+  void Reset(JNIEnv* env, jobject jcaller);
+
+  // Posts a task to |network_thread_| to check whether init complete callback
+  // has been invoked by net::UploadDataStream asynchronously, and notifies the
+  // Java side of the result.
+  void CheckInitCallbackNotInvoked(JNIEnv* env, jobject jcaller);
+  // Posts a task to |network_thread_| to check whether read complete callback
+  // has been invoked by net::UploadDataStream asynchronously, and notifies the
+  // Java side of the result.
+  void CheckReadCallbackNotInvoked(JNIEnv* env, jobject jcaller);
+
+ private:
+  // Complete callbacks that are passed to the |upload_data_stream_|.
+  void OnInitCompleted(int res);
+  void OnReadCompleted(int res);
+
+  // Helper methods that run corresponding task on |network_thread_|.
+
+  void InitOnNetworkThread();
+  void ReadOnNetworkThread();
+  void ResetOnNetworkThread();
+  void CheckInitCallbackNotInvokedOnNetworkThread();
+  void CheckReadCallbackNotInvokedOnNetworkThread();
+
+  // Notify the Java TestUploadDataStreamHandler that read has completed.
+  void NotifyJavaReadCompleted();
+
+  // True if |OnInitCompleted| callback has been invoked. It is set to false
+  // when init or reset is called again. Created on a Java thread, but is only
+  // accessed from |network_thread_|.
+  bool init_callback_invoked_;
+  // True if |OnReadCompleted| callback has been invoked. It is set to false
+  // when init or reset is called again. Created on a Java thread, but is only
+  // accessed from |network_thread_|.
+  bool read_callback_invoked_;
+  // Indicates the number of bytes read. It is reset to 0 when init, reset, or
+  // read is called again. Created on a Java thread, but is only accessed from
+  // |network_thread_|.
+  int bytes_read_;
+
+  // Created and destroyed on the same Java thread. This is where methods of
+  // net::UploadDataStream run on.
+  scoped_ptr<base::Thread> network_thread_;
+  // Created on a Java thread. Accessed only on |network_thread_|.
+  scoped_ptr<net::UploadDataStream> upload_data_stream_;
+  // Created and accessed only on |network_thread_|.
+  scoped_refptr<net::IOBufferWithSize> read_buffer_;
+  // A Java reference pointer for calling methods on the Java
+  // TestUploadDataStreamHandler object. Initialized during construction.
+  base::android::ScopedJavaGlobalRef<jobject> jtest_upload_data_stream_handler_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestUploadDataStreamHandler);
+};
+
+bool TestUploadDataStreamHandlerRegisterJni(JNIEnv* env);
+
+}  // namespace cronet
+
+#endif  // COMPONENTS_CRONET_ANDROID_TEST_TEST_UPLOAD_DATA_STREAM_HANDLER_H_
diff --git a/components/data_reduction_proxy.gypi b/components/data_reduction_proxy.gypi
index a4bed9d..5184706 100644
--- a/components/data_reduction_proxy.gypi
+++ b/components/data_reduction_proxy.gypi
@@ -78,6 +78,8 @@
         'data_reduction_proxy/core/browser/data_reduction_proxy_prefs.h',
         'data_reduction_proxy/core/browser/data_reduction_proxy_request_options.cc',
         'data_reduction_proxy/core/browser/data_reduction_proxy_request_options.h',
+        'data_reduction_proxy/core/browser/data_reduction_proxy_service.cc',
+        'data_reduction_proxy/core/browser/data_reduction_proxy_service.h',
         'data_reduction_proxy/core/browser/data_reduction_proxy_settings.cc',
         'data_reduction_proxy/core/browser/data_reduction_proxy_settings.h',
         'data_reduction_proxy/core/browser/data_reduction_proxy_statistics_prefs.cc',
@@ -139,6 +141,8 @@
         'data_reduction_proxy/core/browser/data_reduction_proxy_configurator_test_utils.h',
         'data_reduction_proxy/core/browser/data_reduction_proxy_settings_test_utils.cc',
         'data_reduction_proxy/core/browser/data_reduction_proxy_settings_test_utils.h',
+        'data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.cc',
+        'data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.h',
         'data_reduction_proxy/core/common/data_reduction_proxy_headers_test_utils.cc',
         'data_reduction_proxy/core/common/data_reduction_proxy_headers_test_utils.h',
         'data_reduction_proxy/core/common/data_reduction_proxy_params_test_utils.cc',
diff --git a/components/data_reduction_proxy/content/browser/BUILD.gn b/components/data_reduction_proxy/content/browser/BUILD.gn
index 1320fbb..99e971e 100644
--- a/components/data_reduction_proxy/content/browser/BUILD.gn
+++ b/components/data_reduction_proxy/content/browser/BUILD.gn
@@ -35,7 +35,9 @@
 
     deps = [
       ":browser",
+      "//skia",
       "//testing/gtest",
+      "//third_party/mojo/src/mojo/public/cpp/bindings",
     ]
   }
 }
diff --git a/components/data_reduction_proxy/content/browser/data_reduction_proxy_debug_resource_throttle.cc b/components/data_reduction_proxy/content/browser/data_reduction_proxy_debug_resource_throttle.cc
index 60d31bcd..f7bdb96 100644
--- a/components/data_reduction_proxy/content/browser/data_reduction_proxy_debug_resource_throttle.cc
+++ b/components/data_reduction_proxy/content/browser/data_reduction_proxy_debug_resource_throttle.cc
@@ -4,6 +4,7 @@
 
 #include "components/data_reduction_proxy/content/browser/data_reduction_proxy_debug_resource_throttle.h"
 
+#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_config.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_debug_ui_service.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_params.h"
@@ -30,13 +31,12 @@
   if (io_data && io_data->IsEnabled() &&
       data_reduction_proxy::DataReductionProxyParams::
           WarnIfNoDataReductionProxy()) {
-    DCHECK(io_data->params());
     DCHECK(io_data->debug_ui_service());
     DCHECK(request);
     return scoped_ptr<DataReductionProxyDebugResourceThrottle>(
-        new DataReductionProxyDebugResourceThrottle(request, resource_type,
-                                                    io_data->debug_ui_service(),
-                                                    io_data->params()));
+        new DataReductionProxyDebugResourceThrottle(
+            request, resource_type, io_data->debug_ui_service(),
+            io_data->config()->params()));
   }
   return nullptr;
 }
diff --git a/components/data_reduction_proxy/core/browser/BUILD.gn b/components/data_reduction_proxy/core/browser/BUILD.gn
index 451076e..c1e75c3 100644
--- a/components/data_reduction_proxy/core/browser/BUILD.gn
+++ b/components/data_reduction_proxy/core/browser/BUILD.gn
@@ -25,6 +25,8 @@
     "data_reduction_proxy_prefs.h",
     "data_reduction_proxy_request_options.cc",
     "data_reduction_proxy_request_options.h",
+    "data_reduction_proxy_service.cc",
+    "data_reduction_proxy_service.h",
     "data_reduction_proxy_settings.cc",
     "data_reduction_proxy_settings.h",
     "data_reduction_proxy_statistics_prefs.cc",
@@ -58,6 +60,8 @@
     "data_reduction_proxy_configurator_test_utils.h",
     "data_reduction_proxy_settings_test_utils.cc",
     "data_reduction_proxy_settings_test_utils.h",
+    "data_reduction_proxy_test_utils.cc",
+    "data_reduction_proxy_test_utils.h",
   ]
 
   public_deps = [
@@ -90,6 +94,8 @@
     "data_reduction_proxy_usage_stats_unittest.cc",
   ]
 
+  configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
+
   deps = [
     ":browser",
     ":test_support",
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_protocol_unittest.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_protocol_unittest.cc
index bb187b1..8907c65 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_protocol_unittest.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_protocol_unittest.cc
@@ -8,17 +8,15 @@
 
 #include "base/memory/ref_counted.h"
 #include "base/memory/scoped_ptr.h"
-#include "base/message_loop/message_loop.h"
-#include "base/message_loop/message_loop_proxy.h"
 #include "base/metrics/field_trial.h"
 #include "base/run_loop.h"
 #include "base/strings/stringprintf.h"
+#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_config_test_utils.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_interceptor.h"
-#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.h"
+#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_usage_stats.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_event_store.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_headers.h"
-#include "components/data_reduction_proxy/core/common/data_reduction_proxy_headers_test_utils.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_params_test_utils.h"
 #include "net/base/completion_callback.h"
 #include "net/base/host_port_pair.h"
@@ -43,17 +41,16 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
 
-using net::HttpResponseHeaders;
 using net::HostPortPair;
+using net::HttpResponseHeaders;
 using net::MockRead;
 using net::MockWrite;
 using net::ProxyRetryInfoMap;
 using net::ProxyService;
 using net::StaticSocketDataProvider;
 using net::TestDelegate;
-using net::URLRequest;
 using net::TestURLRequestContext;
-
+using net::URLRequest;
 
 namespace data_reduction_proxy {
 
@@ -81,9 +78,6 @@
 class DataReductionProxyProtocolTest : public testing::Test {
  public:
   DataReductionProxyProtocolTest() : http_user_agent_settings_("", "") {
-    settings_.reset(
-        new DataReductionProxySettings(CreateDataReductionProxyParams()));
-    proxy_params_.reset(CreateDataReductionProxyParams().release());
     simple_interceptor_.reset(new SimpleURLRequestInterceptor());
     net::URLRequestFilter::GetInstance()->AddHostnameInterceptor(
         "http", "www.google.com", simple_interceptor_.Pass());
@@ -93,25 +87,21 @@
     // URLRequestJobs may post clean-up tasks on destruction.
     net::URLRequestFilter::GetInstance()->RemoveHostnameHandler(
             "http", "www.google.com");
-    base::RunLoop().RunUntilIdle();
-  }
-
-  scoped_ptr<TestDataReductionProxyParams> CreateDataReductionProxyParams() {
-    return scoped_ptr<TestDataReductionProxyParams>(
-        new TestDataReductionProxyParams(
-            DataReductionProxyParams::kAllowed |
-            DataReductionProxyParams::kFallbackAllowed |
-            DataReductionProxyParams::kPromoAllowed,
-            TestDataReductionProxyParams::HAS_EVERYTHING &
-            ~TestDataReductionProxyParams::HAS_DEV_ORIGIN &
-            ~TestDataReductionProxyParams::HAS_DEV_FALLBACK_ORIGIN))
-        .Pass();
+    test_context_->RunUntilIdle();
   }
 
   void SetUp() override {
+    test_context_.reset(new DataReductionProxyTestContext(
+        DataReductionProxyParams::kAllowed |
+            DataReductionProxyParams::kFallbackAllowed |
+            DataReductionProxyParams::kPromoAllowed,
+        TestDataReductionProxyParams::HAS_EVERYTHING &
+            ~TestDataReductionProxyParams::HAS_DEV_ORIGIN &
+            ~TestDataReductionProxyParams::HAS_DEV_FALLBACK_ORIGIN,
+        DataReductionProxyTestContext::DEFAULT_TEST_CONTEXT_OPTIONS));
     network_change_notifier_.reset(net::NetworkChangeNotifier::CreateMock());
     net::NetworkChangeNotifier::SetTestNotificationsOnly(true);
-    base::RunLoop().RunUntilIdle();
+    test_context_->RunUntilIdle();
   }
 
   // Sets up the |TestURLRequestContext| with the provided |ProxyService|.
@@ -128,14 +118,14 @@
     // to requests.
     context_->set_http_user_agent_settings(&http_user_agent_settings_);
     usage_stats_.reset(new DataReductionProxyUsageStats(
-       settings_->params(), settings_.get(),
-       base::MessageLoopProxy::current()));
+        test_context_->config()->params(),
+        test_context_->data_reduction_proxy_service()->GetWeakPtr(),
+        test_context_->task_runner()));
 
-    event_store_.reset(
-        new DataReductionProxyEventStore(base::MessageLoopProxy::current()));
     DataReductionProxyInterceptor* interceptor =
-        new DataReductionProxyInterceptor(
-            settings_->params(), usage_stats_.get(), event_store_.get());
+        new DataReductionProxyInterceptor(test_context_->config()->params(),
+                                          usage_stats_.get(),
+                                          test_context_->event_store());
     scoped_ptr<net::URLRequestJobFactoryImpl> job_factory_impl(
         new net::URLRequestJobFactoryImpl());
     job_factory_.reset(new net::URLRequestInterceptingJobFactory(
@@ -333,17 +323,14 @@
   }
 
  protected:
-  base::MessageLoopForIO loop_;
   scoped_ptr<net::NetworkChangeNotifier> network_change_notifier_;
 
   scoped_ptr<net::URLRequestInterceptor> simple_interceptor_;
   net::MockClientSocketFactory mock_socket_factory_;
   scoped_ptr<net::TestNetworkDelegate> network_delegate_;
   scoped_ptr<ProxyService> proxy_service_;
-  scoped_ptr<TestDataReductionProxyParams> proxy_params_;
-  scoped_ptr<DataReductionProxySettings> settings_;
+  scoped_ptr<DataReductionProxyTestContext> test_context_;
   scoped_ptr<DataReductionProxyUsageStats> usage_stats_;
-  scoped_ptr<DataReductionProxyEventStore> event_store_;
   net::StaticHttpUserAgentSettings http_user_agent_settings_;
 
   scoped_ptr<net::URLRequestInterceptingJobFactory> job_factory_;
@@ -757,8 +744,9 @@
       BYPASS_EVENT_TYPE_SHORT
     },
   };
-  std::string primary = proxy_params_->DefaultOrigin();
-  std::string fallback = proxy_params_->DefaultFallbackOrigin();
+  std::string primary = test_context_->config()->test_params()->DefaultOrigin();
+  std::string fallback =
+      test_context_->config()->test_params()->DefaultFallbackOrigin();
   for (size_t i = 0; i < arraysize(tests); ++i) {
     ConfigureTestDependencies(ProxyService::CreateFixedFromPacResult(
         net::ProxyServer::FromURI(
@@ -782,8 +770,9 @@
 
 TEST_F(DataReductionProxyProtocolTest,
        RelaxedMissingViaHeaderOtherBypassLogic) {
-  std::string primary = proxy_params_->DefaultOrigin();
-  std::string fallback = proxy_params_->DefaultFallbackOrigin();
+  std::string primary = test_context_->config()->test_params()->DefaultOrigin();
+  std::string fallback =
+      test_context_->config()->test_params()->DefaultFallbackOrigin();
   base::FieldTrialList field_trial_list(new BadEntropyProvider());
   base::FieldTrialList::CreateFieldTrial(
       "DataReductionProxyRemoveMissingViaHeaderOtherBypass", "Relaxed");
@@ -823,7 +812,7 @@
   // The first response after a network change is missing the DRP via header, so
   // this should cause a bypass.
   net::NetworkChangeNotifier::NotifyObserversOfIPAddressChangeForTests();
-  base::RunLoop().RunUntilIdle();
+  test_context_->RunUntilIdle();
   TestProxyFallback("GET",
                     "HTTP/1.1 200 OK\r\n\r\n",
                     true /* expected_retry */,
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config.cc
index 573a235..2328d26 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config.cc
@@ -11,6 +11,7 @@
 #include "base/single_thread_task_runner.h"
 #include "base/strings/string_util.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_configurator.h"
+#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_service.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_event_store.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_params.h"
 #include "net/base/load_flags.h"
@@ -47,47 +48,71 @@
 namespace data_reduction_proxy {
 
 DataReductionProxyConfig::DataReductionProxyConfig(
-    scoped_ptr<DataReductionProxyParams> params)
+    scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
+    scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner,
+    net::NetLog* net_log,
+    scoped_ptr<DataReductionProxyParams> params,
+    DataReductionProxyConfigurator* configurator,
+    DataReductionProxyEventStore* event_store)
     : restricted_by_carrier_(false),
       disabled_on_vpn_(false),
       unreachable_(false),
       enabled_by_user_(false),
       alternative_enabled_by_user_(false),
-      net_log_(nullptr),
-      url_request_context_getter_(nullptr),
-      configurator_(nullptr),
-      event_store_(nullptr) {
-  params_.reset(params.release());
+      params_(params.release()),
+      io_task_runner_(io_task_runner),
+      ui_task_runner_(ui_task_runner),
+      net_log_(net_log),
+      configurator_(configurator),
+      event_store_(event_store) {
+  DCHECK(io_task_runner);
+  DCHECK(ui_task_runner);
+  DCHECK(configurator);
+  DCHECK(event_store);
+  InitOnIOThread();
 }
 
 DataReductionProxyConfig::~DataReductionProxyConfig() {
   net::NetworkChangeNotifier::RemoveIPAddressObserver(this);
 }
 
-void DataReductionProxyConfig::InitDataReductionProxyConfig(
-    scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
-    net::NetLog* net_log,
-    net::URLRequestContextGetter* url_request_context_getter,
-    DataReductionProxyConfigurator* configurator,
-    DataReductionProxyEventStore* event_store) {
-  DCHECK(io_task_runner);
-  DCHECK(configurator);
-  DCHECK(event_store);
-  DCHECK(io_task_runner->BelongsToCurrentThread());
-  io_task_runner_ = io_task_runner;
-  net_log_ = net_log;
-  url_request_context_getter_ = url_request_context_getter;
-  configurator_ = configurator;
-  event_store_ = event_store;
-  net::NetworkChangeNotifier::AddIPAddressObserver(this);
+void DataReductionProxyConfig::SetDataReductionProxyService(
+    base::WeakPtr<DataReductionProxyService> data_reduction_proxy_service) {
+  data_reduction_proxy_service_ = data_reduction_proxy_service;
 }
 
-void DataReductionProxyConfig::SetProxyConfigs(bool enabled,
-                                               bool alternative_enabled,
-                                               bool restricted,
-                                               bool at_startup) {
-  DCHECK(configurator_);
+void DataReductionProxyConfig::SetProxyPrefs(bool enabled,
+                                             bool alternative_enabled,
+                                             bool at_startup) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  io_task_runner_->PostTask(
+      FROM_HERE, base::Bind(&DataReductionProxyConfig::SetProxyConfigOnIOThread,
+                            base::Unretained(this), enabled,
+                            alternative_enabled, at_startup));
+}
 
+void DataReductionProxyConfig::SetProxyConfigOnIOThread(
+    bool enabled, bool alternative_enabled, bool at_startup) {
+  enabled_by_user_ = enabled;
+  alternative_enabled_by_user_ = alternative_enabled;
+  UpdateConfigurator(enabled_by_user_, alternative_enabled_by_user_,
+                     restricted_by_carrier_, at_startup);
+
+  // Check if the proxy has been restricted explicitly by the carrier.
+  if (enabled &&
+      !(alternative_enabled && !params()->alternative_fallback_allowed())) {
+    ui_task_runner_->PostTask(
+        FROM_HERE, base::Bind(&DataReductionProxyConfig::StartProbe,
+                              base::Unretained(this)));
+  }
+}
+
+void DataReductionProxyConfig::UpdateConfigurator(bool enabled,
+                                                  bool alternative_enabled,
+                                                  bool restricted,
+                                                  bool at_startup) {
+  DCHECK(io_task_runner_->BelongsToCurrentThread());
+  DCHECK(configurator_);
   LogProxyState(enabled, restricted, at_startup);
   // The alternative is only configured if the standard configuration is
   // is enabled.
@@ -126,11 +151,17 @@
                << (at_startup ? kAtStartup : kByUser);
 }
 
-void DataReductionProxyConfig::OnURLFetchComplete(
-    const net::URLFetcher* source) {
-  DCHECK(source == fetcher_.get());
-  net::URLRequestStatus status = source->GetStatus();
+void DataReductionProxyConfig::HandleProbeResponse(
+    const std::string& response, const net::URLRequestStatus& status) {
+  DCHECK(ui_task_runner_->BelongsToCurrentThread());
+  io_task_runner_->PostTask(
+      FROM_HERE,
+      base::Bind(&DataReductionProxyConfig::HandleProbeResponseOnIOThread,
+                 base::Unretained(this), response, status));
+}
 
+void DataReductionProxyConfig::HandleProbeResponseOnIOThread(
+    const std::string& response, const net::URLRequestStatus& status) {
   if (event_store_) {
     event_store_->EndCanaryRequest(bound_net_log_, status.error());
   }
@@ -147,9 +178,6 @@
                                 std::abs(status.error()));
   }
 
-  std::string response;
-  source->GetResponseAsString(&response);
-
   if ("OK" == response.substr(0, 2)) {
     DVLOG(1) << "The data reduction proxy is unrestricted.";
 
@@ -159,8 +187,8 @@
         // the network operator had blocked the canary and restricted the user.
         // The current network doesn't block the canary, so don't restrict the
         // proxy configurations.
-        SetProxyConfigs(true /* enabled */, false /* alternative_enabled */,
-                        false /* restricted */, false /* at_startup */);
+        UpdateConfigurator(true /* enabled */, false /* alternative_enabled */,
+                           false /* restricted */, false /* at_startup */);
         RecordProbeURLFetchResult(SUCCEEDED_PROXY_ENABLED);
       } else {
         RecordProbeURLFetchResult(SUCCEEDED_PROXY_ALREADY_ENABLED);
@@ -174,8 +202,8 @@
   if (enabled_by_user_) {
     if (!restricted_by_carrier_) {
       // Restrict the proxy.
-      SetProxyConfigs(true /* enabled */, false /* alternative_enabled */,
-                      true /* restricted */, false /* at_startup */);
+      UpdateConfigurator(true /* enabled */, false /* alternative_enabled */,
+                         true /* restricted */, false /* at_startup */);
       RecordProbeURLFetchResult(FAILED_PROXY_DISABLED);
     } else {
       RecordProbeURLFetchResult(FAILED_PROXY_ALREADY_DISABLED);
@@ -185,6 +213,7 @@
 }
 
 void DataReductionProxyConfig::OnIPAddressChanged() {
+  DCHECK(io_task_runner_->BelongsToCurrentThread());
   if (enabled_by_user_) {
     DCHECK(params()->allowed());
     RecordNetworkChangeEvent(IP_CHANGED);
@@ -194,10 +223,28 @@
         !params()->alternative_fallback_allowed()) {
       return;
     }
-    ProbeWhetherDataReductionProxyIsAvailable();
+
+    ui_task_runner_->PostTask(
+        FROM_HERE, base::Bind(&DataReductionProxyConfig::StartProbe,
+                              base::Unretained(this)));
   }
 }
 
+void DataReductionProxyConfig::InitOnIOThread() {
+  if (!io_task_runner_->BelongsToCurrentThread()) {
+    io_task_runner_->PostTask(
+        FROM_HERE, base::Bind(&DataReductionProxyConfig::InitOnIOThread,
+                              base::Unretained(this)));
+    return;
+  }
+
+  if (!params_->allowed())
+    return;
+
+  AddDefaultProxyBypassRules();
+  net::NetworkChangeNotifier::AddIPAddressObserver(this);
+}
+
 void DataReductionProxyConfig::AddDefaultProxyBypassRules() {
   // localhost
   DCHECK(configurator_);
@@ -230,33 +277,19 @@
                             PROBE_URL_FETCH_RESULT_COUNT);
 }
 
-net::URLFetcher* DataReductionProxyConfig::GetURLFetcherForProbe() {
-  net::URLFetcher* fetcher =
-      net::URLFetcher::Create(params_->probe_url(), net::URLFetcher::GET, this);
-  fetcher->SetLoadFlags(net::LOAD_DISABLE_CACHE | net::LOAD_BYPASS_PROXY);
-  DCHECK(url_request_context_getter_);
-  fetcher->SetRequestContext(url_request_context_getter_);
-  // Configure max retries to be at most kMaxRetries times for 5xx errors.
-  static const int kMaxRetries = 5;
-  fetcher->SetMaxRetriesOn5xx(kMaxRetries);
-  fetcher->SetAutomaticallyRetryOnNetworkChanges(kMaxRetries);
-  return fetcher;
-}
-
-void DataReductionProxyConfig::ProbeWhetherDataReductionProxyIsAvailable() {
-  net::URLFetcher* fetcher = GetURLFetcherForProbe();
-  if (!fetcher)
-    return;
-  fetcher_.reset(fetcher);
-
+void DataReductionProxyConfig::StartProbe() {
+  DCHECK(ui_task_runner_->BelongsToCurrentThread());
   bound_net_log_ = net::BoundNetLog::Make(
       net_log_, net::NetLog::SOURCE_DATA_REDUCTION_PROXY);
-  if (event_store_) {
-    event_store_->BeginCanaryRequest(bound_net_log_,
-                                     fetcher_->GetOriginalURL());
-  }
+  if (data_reduction_proxy_service_) {
+    if (event_store_)
+      event_store_->BeginCanaryRequest(bound_net_log_, params_->probe_url());
 
-  fetcher_->Start();
+    data_reduction_proxy_service_->CheckProbeURL(
+        params_->probe_url(),
+        base::Bind(&DataReductionProxyConfig::HandleProbeResponse,
+                   base::Unretained(this)));
+  }
 }
 
 void DataReductionProxyConfig::GetNetworkList(
@@ -278,15 +311,15 @@
             interface_name.begin(),
             interface_name.begin() + vpn_interface_name_prefix.size(),
             vpn_interface_name_prefix.c_str())) {
-      SetProxyConfigs(false, alternative_enabled_by_user_, false, false);
+      UpdateConfigurator(false, alternative_enabled_by_user_, false, false);
       disabled_on_vpn_ = true;
       RecordNetworkChangeEvent(DISABLED_ON_VPN);
       return true;
     }
   }
   if (disabled_on_vpn_) {
-    SetProxyConfigs(enabled_by_user_, alternative_enabled_by_user_,
-                    restricted_by_carrier_, false);
+    UpdateConfigurator(enabled_by_user_, alternative_enabled_by_user_,
+                       restricted_by_carrier_, false);
   }
   disabled_on_vpn_ = false;
   return false;
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config.h b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config.h
index ac7f7581..09cfd28 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config.h
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config.h
@@ -13,7 +13,6 @@
 #include "net/base/net_log.h"
 #include "net/base/net_util.h"
 #include "net/base/network_change_notifier.h"
-#include "net/url_request/url_fetcher_delegate.h"
 
 namespace base {
 class SingleThreadTaskRunner;
@@ -21,8 +20,7 @@
 
 namespace net {
 class NetLog;
-class URLFetcher;
-class URLRequestContextGetter;
+class URLRequestStatus;
 }
 
 namespace data_reduction_proxy {
@@ -30,6 +28,7 @@
 class DataReductionProxyConfigurator;
 class DataReductionProxyEventStore;
 class DataReductionProxyParams;
+class DataReductionProxyService;
 
 // Values of the UMA DataReductionProxy.ProbeURL histogram.
 // This enum must remain synchronized with
@@ -61,45 +60,43 @@
 // This object lives on the IO thread and all of its methods are expected to be
 // called from there.
 class DataReductionProxyConfig
-    : public net::URLFetcherDelegate,
-      public net::NetworkChangeNotifier::IPAddressObserver {
+    : public net::NetworkChangeNotifier::IPAddressObserver {
  public:
-  // DataReductionProxyConfig will take ownership of |params|.
-  DataReductionProxyConfig(scoped_ptr<DataReductionProxyParams> params);
-  ~DataReductionProxyConfig() override;
-
-  // Initializes the Data Reduction Proxy config with an |io_task_runner|,
-  // |net_log|, a |UrlRequestContextGetter| for canary probes, a
-  // |DataReductionProxyConfigurator| for setting the proxy configuration, and a
-  // |DataReductionProxyEventStore| for logging event changes. The caller must
-  // ensure that all parameters remain alive for the lifetime of the
-  // |DataReductionProxyConfig| instance.
-  void InitDataReductionProxyConfig(
+  // The caller must ensure that all parameters remain alive for the lifetime
+  // of the |DataReductionProxyConfig| instance, with the exception of |params|
+  // which this instance will own.
+  DataReductionProxyConfig(
       scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
+      scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner,
       net::NetLog* net_log,
-      net::URLRequestContextGetter* url_request_context_getter,
+      scoped_ptr<DataReductionProxyParams> params,
       DataReductionProxyConfigurator* configurator,
       DataReductionProxyEventStore* event_store);
+  ~DataReductionProxyConfig() override;
 
   // Returns the underlying |DataReductionProxyParams| instance.
   DataReductionProxyParams* params() const {
     return params_.get();
   }
 
- protected:
-  // Virtualized for testing. Returns a fetcher for the probe to check if OK for
-  // the proxy to use TLS.
-  virtual net::URLFetcher* GetURLFetcherForProbe();
+  void SetDataReductionProxyService(
+      base::WeakPtr<DataReductionProxyService> data_reduction_proxy_service);
 
+  // This method expects to run on the UI thread. It permits the data reduction
+  // proxy configuration to change based on changes initiated by the user.
+  virtual void SetProxyPrefs(bool enabled,
+                             bool alternative_enabled,
+                             bool at_startup);
+
+ protected:
   // Sets the proxy configs, enabling or disabling the proxy according to
   // the value of |enabled| and |alternative_enabled|. Use the alternative
   // configuration only if |enabled| and |alternative_enabled| are true. If
   // |restricted| is true, only enable the fallback proxy. |at_startup| is true
   // when this method is called from InitDataReductionProxySettings.
-  virtual void SetProxyConfigs(bool enabled,
-                               bool alternative_enabled,
-                               bool restricted,
-                               bool at_startup);
+  void SetProxyConfigOnIOThread(bool enabled,
+                                bool alternative_enabled,
+                                bool at_startup);
 
   // Writes a warning to the log that is used in backend processing of
   // customer feedback. Virtual so tests can mock it for verification.
@@ -116,26 +113,39 @@
 
  private:
   friend class DataReductionProxyConfigTest;
+  friend class MockDataReductionProxyConfig;
+  friend class TestDataReductionProxyConfig;
   FRIEND_TEST_ALL_PREFIXES(DataReductionProxyConfigTest,
                            TestOnIPAddressChanged);
-  FRIEND_TEST_ALL_PREFIXES(DataReductionProxyConfigTest, TestSetProxyConfigs);
   FRIEND_TEST_ALL_PREFIXES(DataReductionProxyConfigTest,
                            TestSetProxyConfigsHoldback);
 
-  // net::URLFetcherDelegate:
-  void OnURLFetchComplete(const net::URLFetcher* source) override;
-
   // NetworkChangeNotifier::IPAddressObserver:
   void OnIPAddressChanged() override;
 
+  // Performs initialization on the IO thread.
+  void InitOnIOThread();
+
+  // Updates the Data Reduction Proxy configurator with the current config.
+  virtual void UpdateConfigurator(bool enabled,
+                                  bool alternative_enabled,
+                                  bool restricted,
+                                  bool at_startup);
+
+  // Begins a probe request to determine if the Data Reduction Proxy is
+  // permitted to use the HTTPS proxy servers.
+  void StartProbe();
+
+  // Parses the probe responses and appropriately configures the Data Reduction
+  // Proxy rules.
+  virtual void HandleProbeResponse(const std::string& response,
+                                   const net::URLRequestStatus& status);
+  virtual void HandleProbeResponseOnIOThread(
+      const std::string& response, const net::URLRequestStatus& status);
+
   // Adds the default proxy bypass rules for the Data Reduction Proxy.
   void AddDefaultProxyBypassRules();
 
-  // Requests the proxy probe URL, if one is set.  If unable to do so, disables
-  // the proxy, if enabled. Otherwise enables the proxy if disabled by a probe
-  // failure.
-  void ProbeWhetherDataReductionProxyIsAvailable();
-
   // Disables use of the Data Reduction Proxy on VPNs. Returns true if the
   // Data Reduction Proxy has been disabled.
   bool DisableIfVPN();
@@ -153,6 +163,10 @@
   // IO thread.
   scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;
 
+  // |ui_task_runner_| should be the task runner for running operations on the
+  // UI thread.
+  scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner_;
+
   // The caller must ensure that the |net_log_|, if set, outlives this instance.
   // It is used to create new instances of |bound_net_log_| on canary
   // requests. |bound_net_log_| permits the correlation of the begin and end
@@ -161,9 +175,6 @@
   net::NetLog* net_log_;
   net::BoundNetLog bound_net_log_;
 
-  // Used to retrieve a URLRequestContext for performing the canary check.
-  net::URLRequestContextGetter* url_request_context_getter_;
-
   // The caller must ensure that the |configurator_| outlives this instance.
   DataReductionProxyConfigurator* configurator_;
 
@@ -172,8 +183,10 @@
 
   base::ThreadChecker thread_checker_;
 
-  // The URLFetcher being used for the canary check.
-  scoped_ptr<net::URLFetcher> fetcher_;
+  // A weak pointer to a |DataReductionProxyService| to perform probe requests.
+  // The weak pointer is required since the |DataReductionProxyService| is
+  // destroyed before this instance of the |DataReductionProxyConfig|.
+  base::WeakPtr<DataReductionProxyService> data_reduction_proxy_service_;
 
   DISALLOW_COPY_AND_ASSIGN(DataReductionProxyConfig);
 };
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_test_utils.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_test_utils.cc
index 03ba9d6..7e6d12c3 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_test_utils.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_test_utils.cc
@@ -10,45 +10,90 @@
 #include "net/url_request/url_request_test_util.h"
 #include "testing/gmock/include/gmock/gmock.h"
 
+using testing::_;
+
 namespace data_reduction_proxy {
 
-MockDataReductionProxyConfig::MockDataReductionProxyConfig()
-    : MockDataReductionProxyConfig(DataReductionProxyParams::kAllowed |
-                                   DataReductionProxyParams::kFallbackAllowed |
-                                   DataReductionProxyParams::kPromoAllowed) {
+TestDataReductionProxyConfig::TestDataReductionProxyConfig(
+    int params_flags,
+    unsigned int params_definitions,
+    scoped_refptr<base::SingleThreadTaskRunner> task_runner,
+    net::NetLog* net_log,
+    DataReductionProxyConfigurator* configurator,
+    DataReductionProxyEventStore* event_store)
+    : DataReductionProxyConfig(
+        task_runner, task_runner, net_log,
+        make_scoped_ptr(
+            new TestDataReductionProxyParams(params_flags, params_definitions))
+            .Pass(),
+        configurator, event_store) {
+  network_interfaces_.reset(new net::NetworkInterfaceList());
 }
 
-MockDataReductionProxyConfig::MockDataReductionProxyConfig(int flags)
-    : DataReductionProxyConfig(
-          scoped_ptr<TestDataReductionProxyParams>(
-              new TestDataReductionProxyParams(
-                  flags,
-                  TestDataReductionProxyParams::HAS_EVERYTHING &
-                      ~TestDataReductionProxyParams::HAS_DEV_ORIGIN &
-                      ~TestDataReductionProxyParams::HAS_DEV_FALLBACK_ORIGIN))
-              .Pass()) {
-  network_interfaces_.reset(new net::NetworkInterfaceList());
+TestDataReductionProxyConfig::~TestDataReductionProxyConfig() {
+}
+
+void TestDataReductionProxyConfig::GetNetworkList(
+    net::NetworkInterfaceList* interfaces,
+    int policy) {
+  for (size_t i = 0; i < network_interfaces_->size(); ++i)
+    interfaces->push_back(network_interfaces_->at(i));
+}
+
+void TestDataReductionProxyConfig::ResetParamFlagsForTest(int flags) {
+  params_ = make_scoped_ptr(
+                new TestDataReductionProxyParams(
+                    flags,
+                    TestDataReductionProxyParams::HAS_EVERYTHING &
+                        ~TestDataReductionProxyParams::HAS_DEV_ORIGIN &
+                        ~TestDataReductionProxyParams::HAS_DEV_FALLBACK_ORIGIN))
+                .Pass();
+}
+
+TestDataReductionProxyParams* TestDataReductionProxyConfig::test_params() {
+  return static_cast<TestDataReductionProxyParams*>(params_.get());
+}
+
+void TestDataReductionProxyConfig::SetStateForTest(
+    bool enabled_by_user,
+    bool alternative_enabled_by_user,
+    bool restricted_by_carrier,
+    bool at_startup) {
+  enabled_by_user_ = enabled_by_user;
+  alternative_enabled_by_user_ = alternative_enabled_by_user;
+  restricted_by_carrier_ = restricted_by_carrier;
+  UpdateConfigurator(enabled_by_user_, alternative_enabled_by_user_,
+                     restricted_by_carrier_, at_startup);
+}
+
+MockDataReductionProxyConfig::MockDataReductionProxyConfig(
+    int params_flags,
+    unsigned int params_definitions,
+    scoped_refptr<base::SingleThreadTaskRunner> task_runner,
+    net::NetLog* net_log,
+    DataReductionProxyConfigurator* configurator,
+    DataReductionProxyEventStore* event_store)
+    : TestDataReductionProxyConfig(
+        params_flags, params_definitions, task_runner, net_log, configurator,
+        event_store) {
 }
 
 MockDataReductionProxyConfig::~MockDataReductionProxyConfig() {
 }
 
-void MockDataReductionProxyConfig::SetProxyConfigs(bool enabled,
-                                                   bool alternative_enabled,
-                                                   bool restricted,
-                                                   bool at_startup) {
+void MockDataReductionProxyConfig::UpdateConfigurator(bool enabled,
+                                                      bool alternative_enabled,
+                                                      bool restricted,
+                                                      bool at_startup) {
   EXPECT_CALL(*this, LogProxyState(enabled, restricted, at_startup)).Times(1);
-  DataReductionProxyConfig::SetProxyConfigs(enabled, alternative_enabled,
-                                            restricted, at_startup);
+  DataReductionProxyConfig::UpdateConfigurator(enabled, alternative_enabled,
+                                               restricted, at_startup);
 }
 
-void MockDataReductionProxyConfig::GetNetworkList(
-    net::NetworkInterfaceList* interfaces,
-    int policy) {
-  if (!network_interfaces_.get())
-    return;
-  for (size_t i = 0; i < network_interfaces_->size(); ++i)
-    interfaces->push_back(network_interfaces_->at(i));
+void MockDataReductionProxyConfig::HandleProbeResponse(
+    const std::string& response,
+    const net::URLRequestStatus& status) {
+  DataReductionProxyConfig::HandleProbeResponse(response, status);
 }
 
 }  // namespace data_reduction_proxy
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_test_utils.h b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_test_utils.h
index e28ee88d..a15d529 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_test_utils.h
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_test_utils.h
@@ -5,36 +5,56 @@
 #ifndef COMPONENTS_DATA_REDUCTION_PROXY_CORE_BROWSER_DATA_REDUCTION_PROXY_CONFIG_TEST_UTILS_H_
 #define COMPONENTS_DATA_REDUCTION_PROXY_CORE_BROWSER_DATA_REDUCTION_PROXY_CONFIG_TEST_UTILS_H_
 
+#include "base/memory/ref_counted.h"
 #include "base/memory/scoped_ptr.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_config.h"
 #include "net/base/net_util.h"
 #include "testing/gmock/include/gmock/gmock.h"
 
+namespace base {
+class SingleThreadTaskRunner;
+}
+
 namespace net {
-class URLFetcher;
+class NetLog;
 }
 
 namespace data_reduction_proxy {
 
-class MockDataReductionProxyConfig : public DataReductionProxyConfig {
+class DataReductionProxyConfigurator;
+class DataReductionProxyEventStore;
+class TestDataReductionProxyParams;
+
+// Test version of |DataReductionProxyConfig|, which uses an underlying
+// |TestDataReductionProxyParams| to permit overriding of default values
+// returning from |DataReductionProxyParams|, as well as exposing methods to
+// change the underlying state.
+class TestDataReductionProxyConfig : public DataReductionProxyConfig {
  public:
-  MockDataReductionProxyConfig();
-  MockDataReductionProxyConfig(int flags);
-  ~MockDataReductionProxyConfig() override;
+  // Creates a |TestDataReductionProxyConfig| with the provided |params_flags|.
+  TestDataReductionProxyConfig(
+      int params_flags,
+      unsigned int params_definitions,
+      scoped_refptr<base::SingleThreadTaskRunner> network_task_runner,
+      net::NetLog* net_log,
+      DataReductionProxyConfigurator* configurator,
+      DataReductionProxyEventStore* event_store);
+  ~TestDataReductionProxyConfig() override;
 
-  MOCK_METHOD0(GetURLFetcherForProbe, net::URLFetcher*());
-  MOCK_METHOD1(RecordProbeURLFetchResult, void(ProbeURLFetchResult result));
-  MOCK_METHOD3(LogProxyState,
-               void(bool enabled, bool restricted, bool at_startup));
+  void GetNetworkList(net::NetworkInterfaceList* interfaces,
+                      int policy) override;
 
-  // SetProxyConfigs should always call LogProxyState exactly once.
-  virtual void SetProxyConfigs(bool enabled,
-                               bool alternative_enabled,
-                               bool restricted,
-                               bool at_startup) override;
+  // Allows tests to reset the params being used for configuration.
+  void ResetParamFlagsForTest(int flags);
 
-  virtual void GetNetworkList(net::NetworkInterfaceList* interfaces,
-                              int policy) override;
+  // Retrieves the test params being used for the configuration.
+  TestDataReductionProxyParams* test_params();
+
+  // Allows tests to set the internal state.
+  void SetStateForTest(bool enabled_by_user,
+                       bool alternative_enabled_by_user,
+                       bool restricted_by_carrier,
+                       bool at_startup);
 
   net::NetworkInterfaceList* interfaces() {
     return network_interfaces_.get();
@@ -44,6 +64,38 @@
   scoped_ptr<net::NetworkInterfaceList> network_interfaces_;
 };
 
+// A |TestDataReductionProxyConfig| which permits mocking of methods for
+// testing.
+class MockDataReductionProxyConfig : public TestDataReductionProxyConfig {
+ public:
+  // Creates a |MockDataReductionProxyConfig| with the provided |params_flags|.
+  MockDataReductionProxyConfig(
+      int params_flags,
+      unsigned int params_definitions,
+      scoped_refptr<base::SingleThreadTaskRunner> network_task_runner,
+      net::NetLog* net_log,
+      DataReductionProxyConfigurator* configurator,
+      DataReductionProxyEventStore* event_store);
+  ~MockDataReductionProxyConfig();
+
+  MOCK_METHOD1(RecordProbeURLFetchResult, void(ProbeURLFetchResult result));
+  MOCK_METHOD3(LogProxyState,
+               void(bool enabled, bool restricted, bool at_startup));
+  MOCK_METHOD3(SetProxyPrefs,
+               void(bool enabled, bool alternative_enabled, bool at_startup));
+
+  // UpdateConfigurator should always call LogProxyState exactly once.
+  void UpdateConfigurator(bool enabled,
+                          bool alternative_enabled,
+                          bool restricted,
+                          bool at_startup) override;
+
+  // HandleProbeResponse should always call RecordProbeURLFetchResult exactly
+  // once.
+  void HandleProbeResponse(const std::string& response,
+                           const net::URLRequestStatus& status) override;
+};
+
 }  // namespace data_reduction_proxy
 
 #endif  // COMPONENTS_DATA_REDUCTION_PROXY_CORE_BROWSER_DATA_REDUCTION_PROXY_CONFIG_TEST_UTILS_H_
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_unittest.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_unittest.cc
index 61e2616..c6c5ab6 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_unittest.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_unittest.cc
@@ -5,10 +5,10 @@
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_config.h"
 
 #include "base/command_line.h"
-#include "base/message_loop/message_loop.h"
-#include "base/test/test_simple_task_runner.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_config_test_utils.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_configurator_test_utils.h"
+#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_service.h"
+#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_event_store.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_params_test_utils.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_switches.h"
@@ -38,11 +38,15 @@
   ~DataReductionProxyConfigTest() override {}
 
   void SetUp() override {
-    task_runner_ = new base::TestSimpleTaskRunner();
-    request_context_ = new net::TestURLRequestContextGetter(task_runner_.get());
-    event_store_.reset(new DataReductionProxyEventStore(task_runner_.get()));
-    configurator_.reset(new TestDataReductionProxyConfigurator(
-        task_runner_.get(), &net_log_, event_store_.get()));
+    test_context_.reset(new DataReductionProxyTestContext(
+        DataReductionProxyParams::kAllowed |
+            DataReductionProxyParams::kFallbackAllowed |
+            DataReductionProxyParams::kPromoAllowed,
+        TestDataReductionProxyParams::HAS_EVERYTHING &
+            ~TestDataReductionProxyParams::HAS_DEV_ORIGIN &
+            ~TestDataReductionProxyParams::HAS_DEV_FALLBACK_ORIGIN,
+        DataReductionProxyTestContext::USE_MOCK_CONFIG |
+            DataReductionProxyTestContext::USE_TEST_CONFIGURATOR));
 
     ResetSettings(true, true, false, true, false);
 
@@ -71,14 +75,8 @@
       flags |= DataReductionProxyParams::kPromoAllowed;
     if (holdback)
       flags |= DataReductionProxyParams::kHoldback;
-    config_.reset(new MockDataReductionProxyConfig(flags));
-    MockDataReductionProxyConfig* config =
-        static_cast<MockDataReductionProxyConfig*>(config_.get());
-    EXPECT_CALL(*config, GetURLFetcherForProbe()).Times(0);
-    EXPECT_CALL(*config, LogProxyState(_, _, _)).Times(0);
-    config_->InitDataReductionProxyConfig(
-        task_runner_.get(), &net_log_, request_context_.get(),
-        configurator_.get(), event_store_.get());
+    config()->ResetParamFlagsForTest(flags);
+    EXPECT_CALL(*config(), LogProxyState(_, _, _)).Times(0);
   }
 
   void SetProbeResult(const std::string& test_url,
@@ -87,17 +85,9 @@
                       bool success,
                       int expected_calls) {
     if (0 == expected_calls) {
-      EXPECT_CALL(*config(), GetURLFetcherForProbe()).Times(0);
       EXPECT_CALL(*config(), RecordProbeURLFetchResult(_)).Times(0);
     } else {
       EXPECT_CALL(*config(), RecordProbeURLFetchResult(result)).Times(1);
-      EXPECT_CALL(*config(), GetURLFetcherForProbe())
-          .Times(expected_calls)
-          .WillRepeatedly(Return(new net::FakeURLFetcher(
-              GURL(test_url), config(), response,
-              success ? net::HTTP_OK : net::HTTP_INTERNAL_SERVER_ERROR,
-              success ? net::URLRequestStatus::SUCCESS
-                      : net::URLRequestStatus::FAILED)));
     }
   }
 
@@ -110,17 +100,37 @@
     ASSERT_EQ(expected_enabled, configurator()->enabled());
   }
 
+  class TestResponder {
+   public:
+    void ExecuteCallback(FetcherResponseCallback callback) {
+      callback.Run(response, status);
+    }
+
+    std::string response;
+    net::URLRequestStatus status;
+  };
+
   void CheckProbeOnIPChange(const std::string& probe_url,
                             const std::string& response,
                             bool request_succeeded,
                             bool expected_restricted,
                             bool expected_fallback_restricted) {
     SetProbeResult(probe_url, response,
-                   FetchResult(!config_->restricted_by_carrier_,
+                   FetchResult(!config()->restricted_by_carrier_,
                                request_succeeded && (response == "OK")),
                    request_succeeded, 1);
-    config_->OnIPAddressChanged();
-    base::MessageLoop::current()->RunUntilIdle();
+    MockDataReductionProxyService* service =
+        test_context_->data_reduction_proxy_service();
+    TestResponder responder;
+    responder.response = response;
+    responder.status =
+        net::URLRequestStatus(net::URLRequestStatus::SUCCESS, net::OK);
+    EXPECT_CALL(*service, CheckProbeURL(_, _))
+        .Times(1)
+        .WillRepeatedly(testing::WithArgs<1>(
+            testing::Invoke(&responder, &TestResponder::ExecuteCallback)));
+    config()->OnIPAddressChanged();
+    test_context_->RunUntilIdle();
     CheckProxyConfigs(true, expected_restricted, expected_fallback_restricted);
   }
 
@@ -135,31 +145,23 @@
     return FAILED_PROXY_ALREADY_DISABLED;
   }
 
-  void CheckInitDataReductionProxy(bool enabled_at_startup) {
-    config_->InitDataReductionProxyConfig(
-        task_runner_.get(), &net_log_, request_context_.get(),
-        configurator_.get(), event_store_.get());
-
-    base::MessageLoop::current()->RunUntilIdle();
+  void RunUntilIdle() {
+    test_context_->RunUntilIdle();
   }
 
-  MockDataReductionProxyConfig* config() { return config_.get(); }
+  MockDataReductionProxyConfig* config() {
+    return test_context_->mock_config();
+  }
 
   TestDataReductionProxyConfigurator* configurator() {
-    return configurator_.get();
+    return test_context_->test_configurator();
   }
 
   TestDataReductionProxyParams* params() { return expected_params_.get(); }
 
  private:
-  scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
-  scoped_refptr<net::TestURLRequestContextGetter> request_context_;
-  scoped_ptr<TestDataReductionProxyConfigurator> configurator_;
-  scoped_ptr<MockDataReductionProxyConfig> config_;
+  scoped_ptr<DataReductionProxyTestContext> test_context_;
   scoped_ptr<TestDataReductionProxyParams> expected_params_;
-  base::Time last_update_time_;
-  net::CapturingNetLog net_log_;
-  scoped_ptr<DataReductionProxyEventStore> event_store_;
 };
 
 TEST_F(DataReductionProxyConfigTest, TestGetDataReductionProxyOrigin) {
@@ -189,7 +191,7 @@
             proxies[1]);
 }
 
-TEST_F(DataReductionProxyConfigTest, TestSetProxyConfigs) {
+TEST_F(DataReductionProxyConfigTest, TestUpdateConfigurator) {
   base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
       switches::kDataReductionProxyAlt, params()->DefaultAltOrigin());
   base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
@@ -199,7 +201,7 @@
       switches::kDataReductionSSLProxy, params()->DefaultSSLOrigin());
   ResetSettings(true, true, true, true, false);
 
-  config()->SetProxyConfigs(true, true, false, false);
+  config()->UpdateConfigurator(true, true, false, false);
   EXPECT_TRUE(configurator()->enabled());
 
   EXPECT_EQ(
@@ -217,7 +219,7 @@
             net::ProxyServer::FromURI(configurator()->ssl_origin(),
                                       net::ProxyServer::SCHEME_HTTP));
 
-  config()->SetProxyConfigs(true, false, false, false);
+  config()->UpdateConfigurator(true, false, false, false);
   EXPECT_TRUE(configurator()->enabled());
   EXPECT_TRUE(
       net::HostPortPair::FromString(params()->DefaultOrigin())
@@ -227,23 +229,23 @@
                       configurator()->fallback_origin())));
   EXPECT_TRUE(configurator()->ssl_origin().empty());
 
-  config()->SetProxyConfigs(false, true, false, false);
+  config()->UpdateConfigurator(false, true, false, false);
   EXPECT_FALSE(configurator()->enabled());
   EXPECT_TRUE(configurator()->origin().empty());
   EXPECT_TRUE(configurator()->fallback_origin().empty());
   EXPECT_TRUE(configurator()->ssl_origin().empty());
 
-  config()->SetProxyConfigs(false, false, false, false);
+  config()->UpdateConfigurator(false, false, false, false);
   EXPECT_FALSE(configurator()->enabled());
   EXPECT_TRUE(configurator()->origin().empty());
   EXPECT_TRUE(configurator()->fallback_origin().empty());
   EXPECT_TRUE(configurator()->ssl_origin().empty());
 }
 
-TEST_F(DataReductionProxyConfigTest, TestSetProxyConfigsHoldback) {
+TEST_F(DataReductionProxyConfigTest, TestUpdateConfiguratorHoldback) {
   ResetSettings(true, true, true, true, true);
 
-  config()->SetProxyConfigs(true, true, false, false);
+  config()->UpdateConfigurator(true, true, false, false);
   EXPECT_FALSE(configurator()->enabled());
   EXPECT_EQ("", configurator()->origin());
   EXPECT_EQ("", configurator()->fallback_origin());
@@ -251,11 +253,10 @@
 }
 
 TEST_F(DataReductionProxyConfigTest, TestOnIPAddressChanged) {
-  base::MessageLoopForUI loop;
   // The proxy is enabled initially.
   config()->enabled_by_user_ = true;
   config()->restricted_by_carrier_ = false;
-  config()->SetProxyConfigs(true, false, false, true);
+  config()->UpdateConfigurator(true, false, false, true);
   // IP address change triggers a probe that succeeds. Proxy remains
   // unrestricted.
   CheckProbeOnIPChange(kProbeURLWithOKResponse, "OK", true, false, false);
@@ -277,7 +278,7 @@
       net::IP_ADDRESS_ATTRIBUTE_NONE /* ip address attribute */
       ));
   config()->OnIPAddressChanged();
-  base::MessageLoop::current()->RunUntilIdle();
+  RunUntilIdle();
   CheckProxyConfigs(false, false, false);
 
   // Check that the proxy is re-enabled if a non-VPN connection is later used.
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_interceptor.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_interceptor.cc
index 0283163..5c8655ce 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_interceptor.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_interceptor.cc
@@ -21,11 +21,10 @@
     DataReductionProxyParams* params,
     DataReductionProxyUsageStats* stats,
     DataReductionProxyEventStore* event_store)
-    : params_(params),
-      usage_stats_(stats),
-      event_store_(event_store),
+    : usage_stats_(stats),
       bypass_protocol_(
-          new DataReductionProxyBypassProtocol(params, event_store)) {}
+          new DataReductionProxyBypassProtocol(params, event_store)) {
+}
 
 DataReductionProxyInterceptor::~DataReductionProxyInterceptor() {
 }
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_interceptor.h b/components/data_reduction_proxy/core/browser/data_reduction_proxy_interceptor.h
index a0cb781..b82c94c 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_interceptor.h
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_interceptor.h
@@ -43,15 +43,9 @@
       net::NetworkDelegate* network_delegate) const override;
 
  private:
-  // Must outlive |this|.
-  DataReductionProxyParams* params_;
-
   // Must outlive |this| if non-NULL.
   DataReductionProxyUsageStats* usage_stats_;
 
-  // Must outlive |this|.
-  DataReductionProxyEventStore* event_store_;
-
   // Object responsible for identifying cases when a response should cause the
   // data reduction proxy to be bypassed, and for triggering proxy bypasses in
   // these cases.
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_interceptor_unittest.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_interceptor_unittest.cc
index c8ef3b8..dbebf7602 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_interceptor_unittest.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_interceptor_unittest.cc
@@ -11,12 +11,11 @@
 #include "base/memory/scoped_ptr.h"
 #include "base/message_loop/message_loop.h"
 #include "base/run_loop.h"
+#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_config_test_utils.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.h"
-#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.h"
-#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.h"
+#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_params.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_params_test_utils.h"
-#include "components/data_reduction_proxy/core/common/data_reduction_proxy_switches.h"
 #include "net/base/capturing_net_log.h"
 #include "net/base/request_priority.h"
 #include "net/http/http_response_headers.h"
@@ -166,9 +165,9 @@
   }
 
   ~DataReductionProxyInterceptorWithServerTest() override {
-    io_data_->ShutdownOnUIThread();
+    test_context_->io_data()->ShutdownOnUIThread();
     // URLRequestJobs may post clean-up tasks on destruction.
-    base::RunLoop().RunUntilIdle();
+    test_context_->RunUntilIdle();
   }
 
   void SetUp() override {
@@ -183,41 +182,29 @@
     ASSERT_TRUE(proxy_.InitializeAndWaitUntilReady());
     ASSERT_TRUE(direct_.InitializeAndWaitUntilReady());
 
-    // Owned by settings_.
-    scoped_ptr<TestDataReductionProxyParams> params;
-    params.reset(new TestDataReductionProxyParams(
+    test_context_.reset(new DataReductionProxyTestContext(
         DataReductionProxyParams::kAllowed,
         TestDataReductionProxyParams::HAS_EVERYTHING &
             ~TestDataReductionProxyParams::HAS_DEV_ORIGIN &
-            ~TestDataReductionProxyParams::HAS_DEV_FALLBACK_ORIGIN));
+            ~TestDataReductionProxyParams::HAS_DEV_FALLBACK_ORIGIN,
+        DataReductionProxyTestContext::DEFAULT_TEST_CONTEXT_OPTIONS));
     std::string spec;
     base::TrimString(proxy_.GetURL("/").spec(), "/", &spec);
-    params->set_origin(net::ProxyServer::FromURI(
-        spec, net::ProxyServer::SCHEME_HTTP));
-    std::string proxy_name = params->origin().ToURI();
+    test_context_->config()->test_params()->set_origin(
+        net::ProxyServer::FromURI(spec, net::ProxyServer::SCHEME_HTTP));
+    std::string proxy_name =
+        test_context_->config()->params()->origin().ToURI();
     proxy_service_.reset(
         net::ProxyService::CreateFixedFromPacResult(
             "PROXY " + proxy_name + "; DIRECT"));
 
     context_.set_proxy_service(proxy_service_.get());
 
-    settings_.reset(new DataReductionProxySettings(params.Pass()));
-    io_data_.reset(
-        new DataReductionProxyIOData(
-            data_reduction_proxy::Client::UNKNOWN,
-            scoped_ptr<
-                data_reduction_proxy::DataReductionProxyStatisticsPrefs>(),
-            settings_.get(),
-            &net_log_,
-            loop_.message_loop_proxy(),
-            loop_.message_loop_proxy()));
-
     scoped_ptr<net::URLRequestJobFactoryImpl> job_factory_impl(
         new net::URLRequestJobFactoryImpl());
-    job_factory_.reset(
-        new net::URLRequestInterceptingJobFactory(
-            job_factory_impl.Pass(),
-            io_data_->CreateInterceptor()));
+    job_factory_.reset(new net::URLRequestInterceptingJobFactory(
+        job_factory_impl.Pass(),
+        test_context_->io_data()->CreateInterceptor()));
     context_.set_job_factory(job_factory_.get());
     context_.Init();
   }
@@ -231,7 +218,6 @@
   }
 
  private:
-  base::MessageLoopForIO loop_;
   net::CapturingNetLog net_log_;
   net::TestNetworkDelegate network_delegate_;
   net::TestURLRequestContext context_;
@@ -239,8 +225,7 @@
   net::test_server::EmbeddedTestServer direct_;
   scoped_ptr<net::ProxyService> proxy_service_;
   scoped_ptr<net::URLRequestJobFactory> job_factory_;
-  scoped_ptr<DataReductionProxySettings> settings_;
-  scoped_ptr<DataReductionProxyIOData> io_data_;
+  scoped_ptr<DataReductionProxyTestContext> test_context_;
 };
 
 TEST_F(DataReductionProxyInterceptorWithServerTest, TestBypass) {
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.cc
index 88d15d6..636eb80d 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.cc
@@ -9,11 +9,11 @@
 #include "base/prefs/pref_member.h"
 #include "base/single_thread_task_runner.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_protocol.h"
+#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_config.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_configurator.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_delegate.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_interceptor.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_network_delegate.h"
-#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_statistics_prefs.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_usage_stats.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_event_store.h"
@@ -25,48 +25,42 @@
 
 DataReductionProxyIOData::DataReductionProxyIOData(
     const Client& client,
-    scoped_ptr<DataReductionProxyStatisticsPrefs> statistics_prefs,
-    DataReductionProxySettings* settings,
+    int param_flags,
     net::NetLog* net_log,
     scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
-    scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner)
+    scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner,
+    bool enable_quic)
     : client_(client),
-      temporary_statistics_prefs_(statistics_prefs.Pass()),
-      settings_(settings),
       net_log_(net_log),
       io_task_runner_(io_task_runner),
       ui_task_runner_(ui_task_runner),
-      shutdown_on_ui_(false),
-      initialized_(false),
-      network_delegate_created_(false) {
-  DCHECK(settings);
+      shutdown_on_ui_(false) {
   DCHECK(net_log);
   DCHECK(io_task_runner_.get());
   DCHECK(ui_task_runner_.get());
-  params_ = settings->params()->Clone();
+  scoped_ptr<DataReductionProxyParams> params(
+      new DataReductionProxyParams(param_flags));
+  params->EnableQuic(enable_quic);
   request_options_.reset(new DataReductionProxyRequestOptions(
-      client_, params_.get(), io_task_runner_));
+      client_, params.get(), io_task_runner_));
+  request_options_->Init();
   event_store_.reset(new DataReductionProxyEventStore(ui_task_runner));
   configurator_.reset(new DataReductionProxyConfigurator(
       io_task_runner, net_log, event_store_.get()));
   proxy_delegate_.reset(
-      new data_reduction_proxy::DataReductionProxyDelegate(
-          request_options_.get(), params_.get()));
-  if (temporary_statistics_prefs_)
-    statistics_prefs_ = temporary_statistics_prefs_->GetWeakPtr();
+      new DataReductionProxyDelegate(request_options_.get(), params.get()));
+  config_.reset(new DataReductionProxyConfig(
+      io_task_runner_, ui_task_runner_, net_log, params.Pass(),
+      configurator_.get(), event_store_.get()));
+}
+
+DataReductionProxyIOData::DataReductionProxyIOData() : shutdown_on_ui_(false) {
 }
 
 DataReductionProxyIOData::~DataReductionProxyIOData() {
   DCHECK(shutdown_on_ui_);
 }
 
-void DataReductionProxyIOData::Init() {
-  DCHECK(!initialized_);
-  DCHECK(!network_delegate_created_);
-  initialized_ = true;
-  request_options_->Init();
-}
-
 void DataReductionProxyIOData::InitOnUIThread(PrefService* pref_service) {
   DCHECK(ui_task_runner_->BelongsToCurrentThread());
   enabled_.Init(prefs::kDataReductionProxyEnabled, pref_service);
@@ -80,14 +74,10 @@
   shutdown_on_ui_ = true;
 }
 
-void DataReductionProxyIOData::SetDataReductionProxyStatisticsPrefs(
-    base::WeakPtr<DataReductionProxyStatisticsPrefs> statistics_prefs) {
-  statistics_prefs_ = statistics_prefs;
-}
-
-scoped_ptr<DataReductionProxyStatisticsPrefs>
-DataReductionProxyIOData::PassStatisticsPrefs() {
-  return temporary_statistics_prefs_.Pass();
+void DataReductionProxyIOData::SetDataReductionProxyService(
+    base::WeakPtr<DataReductionProxyService> data_reduction_proxy_service) {
+  service_ = data_reduction_proxy_service;
+  config()->SetDataReductionProxyService(data_reduction_proxy_service);
 }
 
 bool DataReductionProxyIOData::IsEnabled() const {
@@ -101,7 +91,7 @@
 DataReductionProxyIOData::CreateInterceptor() {
   DCHECK(io_task_runner_->BelongsToCurrentThread());
   return make_scoped_ptr(new DataReductionProxyInterceptor(
-      params_.get(), usage_stats_.get(), event_store_.get()));
+      config_->params(), usage_stats_.get(), event_store_.get()));
 }
 
 scoped_ptr<DataReductionProxyNetworkDelegate>
@@ -111,15 +101,14 @@
   DCHECK(io_task_runner_->BelongsToCurrentThread());
   scoped_ptr<DataReductionProxyNetworkDelegate> network_delegate(
       new DataReductionProxyNetworkDelegate(
-          wrapped_network_delegate.Pass(), params_.get(),
+          wrapped_network_delegate.Pass(), config_->params(),
           request_options_.get(), configurator_.get()));
   if (track_proxy_bypass_statistics && !usage_stats_) {
     usage_stats_.reset(new data_reduction_proxy::DataReductionProxyUsageStats(
-        params_.get(), settings_, ui_task_runner_));
-    network_delegate->InitStatisticsPrefsAndUMA(
-        ui_task_runner_, statistics_prefs_, &enabled_, usage_stats_.get());
+        config_->params(), service_, ui_task_runner_));
+    network_delegate->InitIODataAndUMA(ui_task_runner_, this, &enabled_,
+                                       usage_stats_.get());
   }
-  network_delegate_created_ = true;
   return network_delegate.Pass();
 }
 
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.h b/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.h
index 1a84aa64..5fbd0ad 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.h
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.h
@@ -20,10 +20,10 @@
 
 namespace data_reduction_proxy {
 
+class DataReductionProxyConfig;
 class DataReductionProxyConfigurator;
 class DataReductionProxyEventStore;
-class DataReductionProxyParams;
-class DataReductionProxySettings;
+class DataReductionProxyService;
 class DataReductionProxyStatisticsPrefs;
 class DataReductionProxyUsageStats;
 
@@ -31,26 +31,19 @@
 // the IO thread.
 class DataReductionProxyIOData {
  public:
-  // Constructs a DataReductionProxyIOData object and takes ownership of
-  // |params| and |statistics_prefs|. |params| contains information about the
-  // DNS names used by the proxy, and allowable configurations.
-  // |statistics_prefs| maintains compression statistics during use of the
-  // proxy. |settings| provides a UI hook to signal when the proxy is
-  // unavailable. Only |statistics_prefs.get()| may be null.
+  // Constructs a DataReductionProxyIOData object. |param_flags| is used to
+  // set information about the DNS names used by the proxy, and allowable
+  // configurations.
   DataReductionProxyIOData(
       const Client& client,
-      scoped_ptr<DataReductionProxyStatisticsPrefs> statistics_prefs,
-      DataReductionProxySettings* settings,
+      int param_flags,
       net::NetLog* net_log,
       scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
-      scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner);
+      scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner,
+      bool enable_quic);
 
   virtual ~DataReductionProxyIOData();
 
-  // Initializes IOData objects on the IO thread. Must be called before using
-  // members.
-  void Init();
-
   // Initializes preferences, including a preference to track whether the
   // Data Reduction Proxy is enabled.
   void InitOnUIThread(PrefService* pref_service);
@@ -58,11 +51,9 @@
   // Destroys the statistics preferences.
   void ShutdownOnUIThread();
 
-  void SetDataReductionProxyStatisticsPrefs(
-      base::WeakPtr<DataReductionProxyStatisticsPrefs> statistics_prefs);
-
-  // Passes ownership of |statistics_prefs_|.
-  scoped_ptr<DataReductionProxyStatisticsPrefs> PassStatisticsPrefs();
+  // Sets the Data Reduction Proxy service after it has been created.
+  void SetDataReductionProxyService(
+      base::WeakPtr<DataReductionProxyService> data_reduction_proxy_service);
 
   // Creates an interceptor suitable for following the Data Reduction Proxy
   // bypass protocol.
@@ -83,6 +74,10 @@
     return configurator_.get();
   }
 
+  DataReductionProxyConfig* config() const {
+    return config_.get();
+  }
+
   DataReductionProxyEventStore* event_store() const {
     return event_store_.get();
   }
@@ -99,15 +94,15 @@
     return net_log_;
   }
 
+  base::WeakPtr<DataReductionProxyService> service() const {
+    return service_;
+  }
+
   // Used for testing.
   DataReductionProxyUsageStats* usage_stats() const {
     return usage_stats_.get();
   }
 
-  DataReductionProxyParams* params() const {
-    return params_.get();
-  }
-
   DataReductionProxyDebugUIService* debug_ui_service() const {
     return debug_ui_service_.get();
   }
@@ -118,18 +113,16 @@
   }
 
  private:
+  friend class TestDataReductionProxyIOData;
+
+  // Used for testing.
+  DataReductionProxyIOData();
+
   // The type of Data Reduction Proxy client.
   Client client_;
 
   // Parameters including DNS names and allowable configurations.
-  scoped_ptr<DataReductionProxyParams> params_;
-
-  // Tracker of compression statistics to be displayed to the user.
-  base::WeakPtr<DataReductionProxyStatisticsPrefs> statistics_prefs_;
-
-  // |temporary_statistics_prefs_| is used only until DataReductionProxySettings
-  // initialization and is dead after.
-  scoped_ptr<DataReductionProxyStatisticsPrefs> temporary_statistics_prefs_;
+  scoped_ptr<DataReductionProxyConfig> config_;
 
   // Holds the DataReductionProxyDebugUIManager for Data Reduction Proxy bypass
   // interstitials.
@@ -145,8 +138,8 @@
   // request.
   scoped_ptr<DataReductionProxyDelegate> proxy_delegate_;
 
-  // User-facing settings object.
-  DataReductionProxySettings* settings_;
+  // Data Reduction Proxy objects with a UI based lifetime.
+  base::WeakPtr<DataReductionProxyService> service_;
 
   // Tracker of various metrics to be reported in UMA.
   scoped_ptr<DataReductionProxyUsageStats> usage_stats_;
@@ -163,8 +156,6 @@
 
   // Used
   bool shutdown_on_ui_;
-  bool initialized_;
-  bool network_delegate_created_;
 
   // Preference that determines if the Data Reduction Proxy has been enabled
   // by the user. In practice, this can be overridden by the command line.
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data_unittest.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data_unittest.cc
index 75506f0..e877e85 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data_unittest.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data_unittest.cc
@@ -13,9 +13,7 @@
 #include "base/time/time.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_prefs.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.h"
-#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.h"
-#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_statistics_prefs.h"
-#include "components/data_reduction_proxy/core/common/data_reduction_proxy_params_test_utils.h"
+#include "components/data_reduction_proxy/core/common/data_reduction_proxy_params.h"
 #include "net/base/net_log.h"
 #include "net/url_request/url_request_interceptor.h"
 #include "net/url_request/url_request_test_util.h"
@@ -54,13 +52,6 @@
 class DataReductionProxyIODataTest : public testing::Test {
  public:
   void SetUp() override {
-    scoped_ptr<TestDataReductionProxyParams> params;
-    params.reset(new TestDataReductionProxyParams(
-        TestDataReductionProxyParams::kAllowed,
-        TestDataReductionProxyParams::HAS_EVERYTHING &
-            ~TestDataReductionProxyParams::HAS_DEV_ORIGIN &
-            ~TestDataReductionProxyParams::HAS_DEV_FALLBACK_ORIGIN));
-    settings_.reset(new DataReductionProxySettings(params.Pass()));
     RegisterSimpleProfilePrefs(prefs_.registry());
   }
 
@@ -88,29 +79,18 @@
     return &prefs_;
   }
 
-  DataReductionProxySettings* settings() const {
-    return settings_.get();
-  }
-
  private:
   base::MessageLoopForIO loop_;
   net::TestDelegate delegate_;
   net::TestURLRequestContext context_;
   net::NetLog net_log_;
   TestingPrefServiceSimple prefs_;
-  scoped_ptr<DataReductionProxySettings> settings_;
 };
 
 TEST_F(DataReductionProxyIODataTest, TestConstruction) {
-  DataReductionProxyStatisticsPrefs* statistics_prefs =
-      new DataReductionProxyStatisticsPrefs(
-          prefs(), message_loop_proxy(), base::TimeDelta());
-  scoped_ptr<DataReductionProxyIOData> io_data(
-      new DataReductionProxyIOData(
-          Client::UNKNOWN, make_scoped_ptr(statistics_prefs), settings(),
-          net_log(), message_loop_proxy(), message_loop_proxy()));
-  settings()->SetDataReductionProxyStatisticsPrefs(
-      io_data->PassStatisticsPrefs());
+  scoped_ptr<DataReductionProxyIOData> io_data(new DataReductionProxyIOData(
+      Client::UNKNOWN, DataReductionProxyParams::kAllowed, net_log(),
+      message_loop_proxy(), message_loop_proxy(), false /* enable_quic */));
 
   // Check that io_data creates an interceptor. Such an interceptor is
   // thoroughly tested by DataReductionProxyInterceptoTest.
@@ -118,10 +98,6 @@
       io_data->CreateInterceptor();
   EXPECT_NE(nullptr, interceptor.get());
 
-  // At this point io_data->statistics_prefs() should be set and compression
-  // statistics logging should be enabled.
-  EXPECT_EQ(statistics_prefs, settings()->statistics_prefs().get());
-
   // When creating a network delegate, expect that it properly wraps a
   // network delegate. Such a network delegate is thoroughly tested by
   // DataReductionProxyNetworkDelegateTest.
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_network_delegate.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_network_delegate.cc
index 9df2ef5..1db8852 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_network_delegate.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_network_delegate.cc
@@ -12,7 +12,9 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/time/time.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_configurator.h"
+#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.h"
+#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_service.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_statistics_prefs.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_usage_stats.h"
 #include "net/base/load_flags.h"
@@ -93,15 +95,15 @@
 DataReductionProxyNetworkDelegate::~DataReductionProxyNetworkDelegate() {
 }
 
-void DataReductionProxyNetworkDelegate::InitStatisticsPrefsAndUMA(
+void DataReductionProxyNetworkDelegate::InitIODataAndUMA(
     scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner,
-    const base::WeakPtr<DataReductionProxyStatisticsPrefs>& statistics_prefs,
+    DataReductionProxyIOData* io_data,
     BooleanPrefMember* data_reduction_proxy_enabled,
     DataReductionProxyUsageStats* usage_stats) {
   DCHECK(data_reduction_proxy_enabled);
   DCHECK(usage_stats);
   ui_task_runner_ = ui_task_runner;
-  data_reduction_proxy_statistics_prefs_ = statistics_prefs;
+  data_reduction_proxy_io_data_ = io_data;
   data_reduction_proxy_enabled_ = data_reduction_proxy_enabled;
   data_reduction_proxy_usage_stats_ = usage_stats;
 }
@@ -251,17 +253,20 @@
     int original_content_length,
     bool data_reduction_proxy_enabled,
     DataReductionProxyRequestType request_type) {
-  if (data_reduction_proxy_statistics_prefs_) {
-    int64 total_received = data_reduction_proxy_statistics_prefs_->GetInt64(
+  if (data_reduction_proxy_io_data_ &&
+      data_reduction_proxy_io_data_->service()) {
+    DataReductionProxyStatisticsPrefs* data_reduction_proxy_statistics_prefs =
+        data_reduction_proxy_io_data_->service()->statistics_prefs();
+    int64 total_received = data_reduction_proxy_statistics_prefs->GetInt64(
         data_reduction_proxy::prefs::kHttpReceivedContentLength);
-    int64 total_original = data_reduction_proxy_statistics_prefs_->GetInt64(
+    int64 total_original = data_reduction_proxy_statistics_prefs->GetInt64(
         data_reduction_proxy::prefs::kHttpOriginalContentLength);
     total_received += received_content_length;
     total_original += original_content_length;
-    data_reduction_proxy_statistics_prefs_->SetInt64(
+    data_reduction_proxy_statistics_prefs->SetInt64(
         data_reduction_proxy::prefs::kHttpReceivedContentLength,
         total_received);
-    data_reduction_proxy_statistics_prefs_->SetInt64(
+    data_reduction_proxy_statistics_prefs->SetInt64(
         data_reduction_proxy::prefs::kHttpOriginalContentLength,
         total_original);
 
@@ -271,7 +276,7 @@
         data_reduction_proxy_enabled,
         request_type,
         base::Time::Now(),
-        data_reduction_proxy_statistics_prefs_.get());
+        data_reduction_proxy_statistics_prefs);
   }
 }
 
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_network_delegate.h b/components/data_reduction_proxy/core/browser/data_reduction_proxy_network_delegate.h
index 116cdc72..b31a9622 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_network_delegate.h
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_network_delegate.h
@@ -40,9 +40,9 @@
 namespace data_reduction_proxy {
 
 class DataReductionProxyConfigurator;
+class DataReductionProxyIOData;
 class DataReductionProxyParams;
 class DataReductionProxyRequestOptions;
-class DataReductionProxyStatisticsPrefs;
 class DataReductionProxyUsageStats;
 
 // DataReductionProxyNetworkDelegate is a LayeredNetworkDelegate that wraps a
@@ -68,9 +68,9 @@
 
   // Initializes member variables to record data reduction proxy prefs and
   // report UMA.
-  void InitStatisticsPrefsAndUMA(
+  void InitIODataAndUMA(
       scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner,
-      const base::WeakPtr<DataReductionProxyStatisticsPrefs>& statistics_prefs,
+      DataReductionProxyIOData* io_data,
       BooleanPrefMember* data_reduction_proxy_enabled,
       DataReductionProxyUsageStats* usage_stats);
 
@@ -142,16 +142,14 @@
   // Weak, owned by our owner.
   BooleanPrefMember* data_reduction_proxy_enabled_;
 
-  // Must outlive this DataReductionProxyNetworkDelegate.
+  // All raw Data Reduction Proxy pointers must outlive |this|.
   DataReductionProxyParams* data_reduction_proxy_params_;
 
-  // Must outlive this DataReductionProxyNetworkDelegate.
   DataReductionProxyUsageStats* data_reduction_proxy_usage_stats_;
 
   DataReductionProxyRequestOptions* data_reduction_proxy_request_options_;
 
-  base::WeakPtr<DataReductionProxyStatisticsPrefs>
-      data_reduction_proxy_statistics_prefs_;
+  DataReductionProxyIOData* data_reduction_proxy_io_data_;
 
   const DataReductionProxyConfigurator* configurator_;
 
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_network_delegate_unittest.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_network_delegate_unittest.cc
index 5e7514d9..d78f797 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_network_delegate_unittest.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_network_delegate_unittest.cc
@@ -6,21 +6,18 @@
 
 #include "base/bind.h"
 #include "base/bind_helpers.h"
-#include "base/message_loop/message_loop.h"
 #include "base/metrics/field_trial.h"
 #include "base/prefs/pref_registry_simple.h"
 #include "base/prefs/testing_pref_service.h"
-#include "base/run_loop.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/test/histogram_tester.h"
-#include "base/test/test_simple_task_runner.h"
 #include "base/time/time.h"
-#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_configurator.h"
+#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_config_test_utils.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_metrics.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_prefs.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_statistics_prefs.h"
-#include "components/data_reduction_proxy/core/common/data_reduction_proxy_event_store.h"
+#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_headers_test_utils.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_params_test_utils.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_pref_names.h"
@@ -80,27 +77,25 @@
     EXPECT_TRUE(test_job_factory_.SetProtocolHandler(url::kHttpScheme,
                                                      test_job_interceptor_));
     context_.set_job_factory(&test_job_factory_);
-    event_store_.reset(new DataReductionProxyEventStore(message_loop_proxy()));
 
-    params_.reset(
-        new TestDataReductionProxyParams(
+    test_context_.reset(
+        new DataReductionProxyTestContext(
             DataReductionProxyParams::kAllowed |
-            DataReductionProxyParams::kFallbackAllowed |
-            DataReductionProxyParams::kPromoAllowed,
+                DataReductionProxyParams::kFallbackAllowed |
+                DataReductionProxyParams::kPromoAllowed,
             TestDataReductionProxyParams::HAS_EVERYTHING &
-            ~TestDataReductionProxyParams::HAS_DEV_ORIGIN &
-            ~TestDataReductionProxyParams::HAS_DEV_FALLBACK_ORIGIN));
+                ~TestDataReductionProxyParams::HAS_DEV_ORIGIN &
+                ~TestDataReductionProxyParams::HAS_DEV_FALLBACK_ORIGIN,
+            DataReductionProxyTestContext::DEFAULT_TEST_CONTEXT_OPTIONS));
 
-    request_options_.reset(new DataReductionProxyRequestOptions(
-        kClient, params_.get(), message_loop_proxy()));
-
-    configurator_.reset(new DataReductionProxyConfigurator(
-        message_loop_proxy(), net_log(), event_store()));
+    request_options_.reset(
+        new DataReductionProxyRequestOptions(
+        kClient, params(), test_context_->task_runner()));
 
     data_reduction_proxy_network_delegate_.reset(
         new DataReductionProxyNetworkDelegate(
             scoped_ptr<net::NetworkDelegate>(new TestNetworkDelegate()),
-            params_.get(), request_options_.get(), configurator_.get()));
+            params(), request_options_.get(), test_context_->configurator()));
   }
 
   const net::ProxyConfig& GetProxyConfig() const {
@@ -128,7 +123,7 @@
     request->set_received_response_content_length(response_content_length);
 
     request->Start();
-    base::RunLoop().Run();
+    test_context_->RunUntilIdle();
 
     if (!raw_response_headers.empty())
       EXPECT_TRUE(request->response_headers() != NULL);
@@ -141,26 +136,27 @@
     context_.set_network_delegate(network_delegate_);
   }
 
-  const scoped_refptr<base::MessageLoopProxy> message_loop_proxy() {
-    return loop_.message_loop_proxy();
+  TestDataReductionProxyParams* params() const {
+    return test_context_->config()->test_params();
   }
 
-  net::NetLog* net_log() {
-    return &net_log_;
+  TestDataReductionProxyIOData* io_data() const {
+    return test_context_->io_data();
   }
 
-  DataReductionProxyEventStore* event_store() {
-    return event_store_.get();
+  TestingPrefServiceSimple* pref_service() const {
+    return test_context_->pref_service();
   }
 
-  scoped_ptr<TestDataReductionProxyParams> params_;
+  DataReductionProxyStatisticsPrefs* statistics_prefs() const {
+    return test_context_->data_reduction_proxy_service()->statistics_prefs();
+  }
+
   scoped_ptr<DataReductionProxyRequestOptions> request_options_;
-  scoped_ptr<DataReductionProxyConfigurator> configurator_;
   scoped_ptr<DataReductionProxyNetworkDelegate>
       data_reduction_proxy_network_delegate_;
 
  private:
-  base::MessageLoopForIO loop_;
   net::TestURLRequestContext context_;
   net::TestDelegate delegate_;
   // |test_job_interceptor_| is owned by |test_job_factory_|.
@@ -169,8 +165,7 @@
 
   net::ProxyConfig config_;
   net::NetworkDelegate* network_delegate_;
-  net::CapturingNetLog net_log_;
-  scoped_ptr<DataReductionProxyEventStore> event_store_;
+  scoped_ptr<DataReductionProxyTestContext> test_context_;
 };
 
 TEST_F(DataReductionProxyNetworkDelegateTest, AuthenticationTest) {
@@ -180,7 +175,7 @@
 
   net::ProxyInfo data_reduction_proxy_info;
   std::string data_reduction_proxy;
-  base::TrimString(params_->DefaultOrigin(), "/", &data_reduction_proxy);
+  base::TrimString(params()->DefaultOrigin(), "/", &data_reduction_proxy);
   data_reduction_proxy_info.UseNamedProxy(data_reduction_proxy);
 
   net::HttpRequestHeaders headers;
@@ -278,11 +273,11 @@
   // Data reduction proxy info
   net::ProxyInfo data_reduction_proxy_info;
   std::string data_reduction_proxy;
-  base::TrimString(params_->DefaultOrigin(), "/", &data_reduction_proxy);
+  base::TrimString(params()->DefaultOrigin(), "/", &data_reduction_proxy);
   data_reduction_proxy_info.UsePacString(
       "PROXY " +
       net::ProxyServer::FromURI(
-          params_->DefaultOrigin(),
+          params()->DefaultOrigin(),
           net::ProxyServer::SCHEME_HTTP).host_port_pair().ToString() +
       "; DIRECT");
   EXPECT_FALSE(data_reduction_proxy_info.is_empty());
@@ -319,7 +314,7 @@
   // Another proxy is used. It should be used afterwards.
   result.Use(other_proxy_info);
   OnResolveProxyHandler(url, load_flags, data_reduction_proxy_config,
-                        empty_proxy_retry_info, params_.get(), &result);
+                        empty_proxy_retry_info, params(), &result);
   EXPECT_EQ(other_proxy_info.proxy_server(), result.proxy_server());
 
   // A direct connection is used. The data reduction proxy should be used
@@ -328,7 +323,7 @@
   result.Use(direct_proxy_info);
   net::ProxyConfig::ID prev_id = result.config_id();
   OnResolveProxyHandler(url, load_flags, data_reduction_proxy_config,
-                        empty_proxy_retry_info, params_.get(), &result);
+                        empty_proxy_retry_info, params(), &result);
   EXPECT_EQ(data_reduction_proxy_info.proxy_server(), result.proxy_server());
   // Only the proxy list should be updated, not he proxy info.
   EXPECT_EQ(result.config_id(), prev_id);
@@ -339,7 +334,7 @@
   prev_id = result.config_id();
   OnResolveProxyHandler(url, load_flags, data_reduction_proxy_config,
                         data_reduction_proxy_retry_info,
-                        params_.get(), &result);
+                        params(), &result);
   EXPECT_TRUE(result.proxy_server().is_direct());
   EXPECT_EQ(result.config_id(), prev_id);
 
@@ -347,37 +342,37 @@
   result.UseDirect();
   OnResolveProxyHandler(GURL("ws://echo.websocket.org/"),
                         load_flags, data_reduction_proxy_config,
-                        empty_proxy_retry_info, params_.get(), &result);
+                        empty_proxy_retry_info, params(), &result);
   EXPECT_TRUE(result.is_direct());
 
   OnResolveProxyHandler(GURL("wss://echo.websocket.org/"),
                         load_flags, data_reduction_proxy_config,
-                        empty_proxy_retry_info, params_.get(), &result);
+                        empty_proxy_retry_info, params(), &result);
   EXPECT_TRUE(result.is_direct());
 
   // Without DataCompressionProxyCriticalBypass Finch trial set, the
   // BYPASS_DATA_REDUCTION_PROXY load flag should be ignored.
   OnResolveProxyHandler(url, load_flags, data_reduction_proxy_config,
-                        empty_proxy_retry_info, params_.get(),
+                        empty_proxy_retry_info, params(),
                         &result);
   EXPECT_FALSE(result.is_direct());
 
   OnResolveProxyHandler(url, load_flags, data_reduction_proxy_config,
                         empty_proxy_retry_info,
-                        params_.get(), &other_proxy_info);
+                        params(), &other_proxy_info);
   EXPECT_FALSE(other_proxy_info.is_direct());
 
   load_flags |= net::LOAD_BYPASS_DATA_REDUCTION_PROXY;
 
   result.UseDirect();
   OnResolveProxyHandler(url, load_flags, data_reduction_proxy_config,
-                        empty_proxy_retry_info, params_.get(),
+                        empty_proxy_retry_info, params(),
                         &result);
   EXPECT_FALSE(result.is_direct());
 
   OnResolveProxyHandler(url, load_flags, data_reduction_proxy_config,
                         empty_proxy_retry_info,
-                        params_.get(), &other_proxy_info);
+                        params(), &other_proxy_info);
   EXPECT_FALSE(other_proxy_info.is_direct());
 
   // With Finch trial set, should only bypass if LOAD flag is set and the
@@ -392,12 +387,12 @@
 
   result.UseDirect();
   OnResolveProxyHandler(url, load_flags, data_reduction_proxy_config,
-                        empty_proxy_retry_info, params_.get(),
+                        empty_proxy_retry_info, params(),
                         &result);
   EXPECT_FALSE(result.is_direct());
 
   OnResolveProxyHandler(url, load_flags, data_reduction_proxy_config,
-                        empty_proxy_retry_info, params_.get(),
+                        empty_proxy_retry_info, params(),
                         &other_proxy_info);
   EXPECT_FALSE(other_proxy_info.is_direct());
 
@@ -405,12 +400,12 @@
 
   result.UseDirect();
   OnResolveProxyHandler(url, load_flags, data_reduction_proxy_config,
-                        empty_proxy_retry_info, params_.get(),
+                        empty_proxy_retry_info, params(),
                         &result);
   EXPECT_TRUE(result.is_direct());
 
   OnResolveProxyHandler(url, load_flags, data_reduction_proxy_config,
-                        empty_proxy_retry_info, params_.get(),
+                        empty_proxy_retry_info, params(),
                         &other_proxy_info);
   EXPECT_FALSE(other_proxy_info.is_direct());
 }
@@ -419,49 +414,41 @@
   const int64 kOriginalLength = 200;
   const int64 kReceivedLength = 100;
 
-  TestingPrefServiceSimple pref_service;
-  scoped_ptr<DataReductionProxyStatisticsPrefs> statistics_prefs(
-      new DataReductionProxyStatisticsPrefs(
-          &pref_service,
-          scoped_refptr<base::TestSimpleTaskRunner>(
-              new base::TestSimpleTaskRunner()),
-          base::TimeDelta()));
-  PrefRegistrySimple* registry = pref_service.registry();
+  PrefRegistrySimple* registry = pref_service()->registry();
   data_reduction_proxy::RegisterSimpleProfilePrefs(registry);
 
-  data_reduction_proxy_network_delegate_->
-      data_reduction_proxy_statistics_prefs_ =
-          statistics_prefs->GetWeakPtr();
+  data_reduction_proxy_network_delegate_->data_reduction_proxy_io_data_ =
+      io_data();
 
   data_reduction_proxy_network_delegate_->UpdateContentLengthPrefs(
       kReceivedLength, kOriginalLength,
-      pref_service.GetBoolean(
+      pref_service()->GetBoolean(
           data_reduction_proxy::prefs::kDataReductionProxyEnabled),
       UNKNOWN_TYPE);
 
   EXPECT_EQ(kReceivedLength,
-            statistics_prefs->GetInt64(
+            statistics_prefs()->GetInt64(
                 data_reduction_proxy::prefs::kHttpReceivedContentLength));
-  EXPECT_FALSE(pref_service.GetBoolean(
+  EXPECT_FALSE(pref_service()->GetBoolean(
       data_reduction_proxy::prefs::kDataReductionProxyEnabled));
   EXPECT_EQ(kOriginalLength,
-            statistics_prefs->GetInt64(
+            statistics_prefs()->GetInt64(
                 data_reduction_proxy::prefs::kHttpOriginalContentLength));
 
   // Record the same numbers again, and total lengths should be doubled.
   data_reduction_proxy_network_delegate_->UpdateContentLengthPrefs(
       kReceivedLength, kOriginalLength,
-      pref_service.GetBoolean(
+      pref_service()->GetBoolean(
           data_reduction_proxy::prefs::kDataReductionProxyEnabled),
       UNKNOWN_TYPE);
 
   EXPECT_EQ(kReceivedLength * 2,
-            statistics_prefs->GetInt64(
+            statistics_prefs()->GetInt64(
                 data_reduction_proxy::prefs::kHttpReceivedContentLength));
-  EXPECT_FALSE(pref_service.GetBoolean(
+  EXPECT_FALSE(pref_service()->GetBoolean(
       data_reduction_proxy::prefs::kDataReductionProxyEnabled));
   EXPECT_EQ(kOriginalLength * 2,
-            statistics_prefs->GetInt64(
+            statistics_prefs()->GetInt64(
                 data_reduction_proxy::prefs::kHttpOriginalContentLength));
 }
 
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.cc
index 568fafc8..16d8690 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.cc
@@ -4,12 +4,11 @@
 
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.h"
 
-#include <vector>
-
 #include "base/bind.h"
 #include "base/command_line.h"
 #include "base/single_thread_task_runner.h"
 #include "base/strings/string_split.h"
+#include "base/strings/string_tokenizer.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
@@ -30,12 +29,19 @@
 
 namespace data_reduction_proxy {
 
+namespace {
+std::string FormatOption(const std::string& name, const std::string& value) {
+  return name + "=" + value;
+}
+}  //namespace
+
 const char kSessionHeaderOption[] = "ps";
 const char kCredentialsHeaderOption[] = "sid";
 const char kBuildNumberHeaderOption[] = "b";
 const char kPatchNumberHeaderOption[] = "p";
 const char kClientHeaderOption[] = "c";
 const char kLoFiHeaderOption[] = "q";
+const char kExperimentsOption[] = "exp";
 
 // The empty version for the authentication protocol. Currently used by
 // Android webview.
@@ -67,9 +73,9 @@
     DataReductionProxyParams* params,
     scoped_refptr<base::SingleThreadTaskRunner> network_task_runner)
     : client_(GetString(client)),
-      version_(ChromiumVersion()),
       data_reduction_proxy_params_(params),
       network_task_runner_(network_task_runner) {
+  GetChromiumBuildAndPatch(ChromiumVersion(), &build_, &patch_);
 }
 
 DataReductionProxyRequestOptions::DataReductionProxyRequestOptions(
@@ -78,9 +84,9 @@
     DataReductionProxyParams* params,
     scoped_refptr<base::SingleThreadTaskRunner> network_task_runner)
     : client_(GetString(client)),
-      version_(version),
       data_reduction_proxy_params_(params),
       network_task_runner_(network_task_runner) {
+  GetChromiumBuildAndPatch(version, &build_, &patch_);
 }
 
 DataReductionProxyRequestOptions::~DataReductionProxyRequestOptions() {
@@ -91,6 +97,7 @@
   UpdateCredentials();
   UpdateLoFi();
   UpdateVersion();
+  UpdateExperiments();
 }
 
 std::string DataReductionProxyRequestOptions::ChromiumVersion() const {
@@ -114,34 +121,40 @@
 }
 
 void DataReductionProxyRequestOptions::UpdateVersion() {
-  std::string build_number;
-  std::string patch_number;
-  GetChromiumBuildAndPatch(version_, &build_number, &patch_number);
-  if (!build_number.empty() && !patch_number.empty()) {
-    header_options_[kBuildNumberHeaderOption] = build_number;
-    header_options_[kPatchNumberHeaderOption] = patch_number;
-  }
-  if (!client_.empty())
-    header_options_[kClientHeaderOption] = client_;
+  GetChromiumBuildAndPatch(version_, &build_, &patch_);
   RegenerateRequestHeaderValue();
 }
 
 void DataReductionProxyRequestOptions::UpdateLoFi() {
   // LoFi was not enabled, but now is. Add the header option.
-  if (header_options_.find(kLoFiHeaderOption) == header_options_.end() &&
+  if (lofi_.empty() &&
       DataReductionProxyParams::IsLoFiEnabled()) {
-    header_options_[kLoFiHeaderOption] = "low";
+    lofi_ = "low";
     RegenerateRequestHeaderValue();
     return;
   }
   // LoFi was enabled, but no longer is. Remove the header option.
-  if (header_options_.find(kLoFiHeaderOption) != header_options_.end() &&
-      !DataReductionProxyParams::IsLoFiEnabled()) {
-    header_options_.erase(kLoFiHeaderOption);
+  if (!lofi_.empty() && !DataReductionProxyParams::IsLoFiEnabled()) {
+    lofi_ = std::string();
     RegenerateRequestHeaderValue();
   }
 }
 
+void DataReductionProxyRequestOptions::UpdateExperiments() {
+  std::string experiments =
+      base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
+          data_reduction_proxy::switches::kDataReductionProxyExperiment);
+  if (experiments.empty())
+    return;
+  base::StringTokenizer experiment_tokenizer(experiments, ", ");
+  experiment_tokenizer.set_quote_chars("\"");
+  while (experiment_tokenizer.GetNext()) {
+    if (!experiment_tokenizer.token().empty())
+      experiments_.push_back(experiment_tokenizer.token());
+  }
+  RegenerateRequestHeaderValue();
+}
+
 // static
 base::string16 DataReductionProxyRequestOptions::AuthHashForSalt(
     int64 salt,
@@ -228,9 +241,7 @@
   std::string session;
   std::string credentials;
   last_credentials_update_time_ = Now();
-  ComputeCredentials(last_credentials_update_time_, &session, &credentials);
-  header_options_[kSessionHeaderOption] = session;
-  header_options_[kCredentialsHeaderOption] = credentials;
+  ComputeCredentials(last_credentials_update_time_, &session_, &credentials_);
   RegenerateRequestHeaderValue();
 }
 
@@ -277,12 +288,18 @@
 }
 
 void DataReductionProxyRequestOptions::RegenerateRequestHeaderValue() {
-  std::vector <std::string> options;
-  for (std::map<std::string, std::string>::iterator
-       it = header_options_.begin(); it != header_options_.end(); ++it) {
-    options.push_back(it->first + "=" + it->second);
-  }
-  header_value_ = JoinString(options, ", ");
+  header_value_ = FormatOption(kSessionHeaderOption, session_)
+                + ", " + FormatOption(kCredentialsHeaderOption, credentials_)
+                + (client_.empty() ?
+                       "" : ", " + FormatOption(kClientHeaderOption, client_))
+                + (build_.empty() || patch_.empty() ?
+                       "" :
+                       ", " + FormatOption(kBuildNumberHeaderOption, build_)
+                       + ", " + FormatOption(kPatchNumberHeaderOption, patch_))
+                + (lofi_.empty() ?
+                       "" : ", " + FormatOption(kLoFiHeaderOption, lofi_));
+  for (const auto& experiment : experiments_)
+    header_value_ += ", " + FormatOption(kExperimentsOption, experiment);
 }
 
 }  // namespace data_reduction_proxy
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.h b/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.h
index 1a9ef79..e11f1de 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.h
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.h
@@ -5,8 +5,8 @@
 #ifndef COMPONENTS_DATA_REDUCTION_PROXY_CORE_BROWSER_DATA_REDUCTION_PROXY_AUTH_REQUEST_HANDLER_H_
 #define COMPONENTS_DATA_REDUCTION_PROXY_CORE_BROWSER_DATA_REDUCTION_PROXY_AUTH_REQUEST_HANDLER_H_
 
-#include <map>
 #include <string>
+#include <vector>
 
 #include "base/gtest_prod_util.h"
 #include "base/memory/ref_counted.h"
@@ -33,6 +33,7 @@
 extern const char kPatchNumberHeaderOption[];
 extern const char kClientHeaderOption[];
 extern const char kLoFiHeaderOption[];
+extern const char kExperimentsOption[];
 
 #if defined(OS_ANDROID)
 extern const char kAndroidWebViewProtocolVersion[];
@@ -74,8 +75,8 @@
   virtual ~DataReductionProxyRequestOptions();
 
   // Sets |key_| to the default key and initializes the credentials, version,
-  // client, and lo-fi header values in |header_options_|. Generates the
-  // |header_value_| string which is concatenated to the Chrome-proxy header.
+  // client, and lo-fi header values. Generates the |header_value_| string,
+  // which is concatenated to the Chrome-proxy header.
   void Init();
 
   // Adds a 'Chrome-Proxy' header to |request_headers| with the data reduction
@@ -127,17 +128,7 @@
 
  private:
   FRIEND_TEST_ALL_PREFIXES(DataReductionProxyRequestOptionsTest,
-                           AuthorizationOnIOThread);
-  FRIEND_TEST_ALL_PREFIXES(DataReductionProxyRequestOptionsTest,
-                           AuthorizationIgnoresEmptyKey);
-  FRIEND_TEST_ALL_PREFIXES(DataReductionProxyRequestOptionsTest,
-                           AuthorizationBogusVersion);
-  FRIEND_TEST_ALL_PREFIXES(DataReductionProxyRequestOptionsTest,
                            AuthHashForSalt);
-  FRIEND_TEST_ALL_PREFIXES(DataReductionProxyRequestOptionsTest,
-                           AuthorizationLoFi);
-  FRIEND_TEST_ALL_PREFIXES(DataReductionProxyRequestOptionsTest,
-                           AuthorizationLoFiOffThenOn);
 
   // Returns the version of Chromium that is being used.
   std::string ChromiumVersion() const;
@@ -148,20 +139,23 @@
                                 std::string* build,
                                 std::string* patch) const;
 
-  // Uses |version_| and |client_| to update values in |header_options_|.
+  // Updates client type, build, and patch.
   void UpdateVersion();
 
-  // Updates the value of LoFi in |header_options_| and regenerates the header
-  // if necessary.
+  // Updates the value of LoFi and regenerates the header if necessary.
   void UpdateLoFi();
 
+  // Update the value of the experiments to be run and regenerate the header if
+  // necessary.
+  void UpdateExperiments();
+
   // Generates a session ID and credentials suitable for authenticating with
   // the data reduction proxy.
   void ComputeCredentials(const base::Time& now,
                           std::string* session,
                           std::string* credentials);
 
-  // Generates and updates the session ID and credentials in |header_options_|.
+  // Generates and updates the session ID and credentials.
   void UpdateCredentials();
 
   // Adds authentication headers only if |expects_ssl| is true and
@@ -176,8 +170,7 @@
   // Chrome-proxy header.
   void RegenerateRequestHeaderValue();
 
-  // Map and string of the request options to be added to the header.
-  std::map<std::string, std::string> header_options_;
+  // The Chrome-Proxy header value.
   std::string header_value_;
 
   // Authentication state.
@@ -186,6 +179,12 @@
   // Name of the client and version of the data reduction proxy protocol to use.
   std::string client_;
   std::string version_;
+  std::string session_;
+  std::string credentials_;
+  std::string build_;
+  std::string patch_;
+  std::string lofi_;
+  std::vector<std::string> experiments_;
 
   // The last time the session was updated. Used to ensure that a session is
   // never used for more than twenty-four hours.
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options_unittest.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options_unittest.cc
index cf5bf46..196b071e 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options_unittest.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options_unittest.cc
@@ -2,7 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.h"
 
 #include "base/command_line.h"
@@ -89,7 +88,9 @@
       : DataReductionProxyRequestOptions(
             client, version, params, loop_proxy) {}
 
-  std::string GetDefaultKey() const override { return kTestKey; }
+  std::string GetDefaultKey() const override {
+    return kTestKey;
+  }
 
   base::Time Now() const override {
     return base::Time::UnixEpoch() + now_offset_;
@@ -111,257 +112,107 @@
   base::TimeDelta now_offset_;
 };
 
+void SetHeaderExpectations(const std::string& session,
+                           const std::string& credentials,
+                           const std::string& client,
+                           const std::string& build,
+                           const std::string& patch,
+                           const std::string& lofi,
+                           const std::vector<std::string> experiments,
+                           std::string* expected_header) {
+  std::vector<std::string> expected_options;
+  if (!session.empty()) {
+    expected_options.push_back(
+        std::string(kSessionHeaderOption) + "=" + session);
+  }
+  if (!credentials.empty()) {
+    expected_options.push_back(
+        std::string(kCredentialsHeaderOption) + "=" + credentials);
+  }
+  if (!client.empty()) {
+    expected_options.push_back(
+        std::string(kClientHeaderOption) + "=" + client);
+  }
+  if (!build.empty()) {
+    expected_options.push_back(
+        std::string(kBuildNumberHeaderOption) + "=" + build);
+  }
+  if (!patch.empty()) {
+    expected_options.push_back(
+        std::string(kPatchNumberHeaderOption) + "=" + patch);
+  }
+  if (!lofi.empty()) {
+    expected_options.push_back(
+        std::string(kLoFiHeaderOption) + "=" + lofi);
+  }
+  for (const auto& experiment : experiments) {
+    expected_options.push_back(
+        std::string(kExperimentsOption) + "=" + experiment);
+  }
+  if (!expected_options.empty())
+    *expected_header = JoinString(expected_options, ", ");
+}
+
 }  // namespace
 
 class DataReductionProxyRequestOptionsTest : public testing::Test {
  public:
-  DataReductionProxyRequestOptionsTest()
-      : loop_proxy_(base::MessageLoopProxy::current().get()) {
+  DataReductionProxyRequestOptionsTest() {
+    params_.reset(
+        new TestDataReductionProxyParams(
+            DataReductionProxyParams::kAllowed |
+            DataReductionProxyParams::kFallbackAllowed |
+            DataReductionProxyParams::kPromoAllowed,
+            TestDataReductionProxyParams::HAS_EVERYTHING &
+            ~TestDataReductionProxyParams::HAS_DEV_ORIGIN &
+            ~TestDataReductionProxyParams::HAS_DEV_FALLBACK_ORIGIN));
   }
 
-  std::map<std::string, std::string> ParseHeader(std::string header) {
-    std::map<std::string, std::string> header_options;
-    size_t pos = 0;
-    std::string name;
-    std::string value;
-    std::string equals_delimiter = "=";
-    std::string comma_delimiter = ", ";
-    while ((pos = header.find(equals_delimiter)) != std::string::npos) {
-        name = header.substr(0, pos);
-        header.erase(0, pos + equals_delimiter.length());
-        pos = header.find(comma_delimiter);
-        if (pos != std::string::npos) {
-          value = header.substr(0, pos);
-          header.erase(0, pos + comma_delimiter.length());
-          header_options[name] = value;
-        }
+  void CreateRequestOptions(const std::string& version) {
+    request_options_.reset(
+        new TestDataReductionProxyRequestOptions(
+            kClient, version, params(), loop_proxy()));
+    request_options_->Init();
+  }
+
+  TestDataReductionProxyParams* params() {
+    return params_.get();
+  }
+
+  base::MessageLoopProxy* loop_proxy() {
+    return base::MessageLoopProxy::current().get();
+  }
+
+  TestDataReductionProxyRequestOptions* request_options() {
+    return request_options_.get();
+  }
+
+  void VerifyExpectedHeader(const std::string& proxy_uri,
+                            const std::string& expected_header) {
+    base::RunLoop().RunUntilIdle();
+    net::HttpRequestHeaders headers;
+    request_options_->MaybeAddRequestHeader(
+        NULL,
+        proxy_uri.empty() ? net::ProxyServer() :
+            net::ProxyServer::FromURI(proxy_uri, net::ProxyServer::SCHEME_HTTP),
+        &headers);
+    if (expected_header.empty()) {
+      EXPECT_FALSE(headers.HasHeader(kChromeProxyHeader));
+      return;
     }
-
-    if (!header.empty())
-      header_options[name] = header;
-
-    return header_options;
+    EXPECT_TRUE(headers.HasHeader(kChromeProxyHeader));
+    std::string header_value;
+    headers.GetHeader(kChromeProxyHeader, &header_value);
+    EXPECT_EQ(expected_header, header_value);
   }
 
+ private:
   // Required for MessageLoopProxy::current().
   base::MessageLoopForUI loop_;
-  base::MessageLoopProxy* loop_proxy_;
+  scoped_ptr<TestDataReductionProxyParams> params_;
+  scoped_ptr<TestDataReductionProxyRequestOptions> request_options_;
 };
 
-TEST_F(DataReductionProxyRequestOptionsTest, AuthorizationOnIOThread) {
-  std::map<std::string, std::string> kExpectedHeader;
-  kExpectedHeader[kSessionHeaderOption] = kExpectedSession2;
-  kExpectedHeader[kCredentialsHeaderOption] = kExpectedCredentials2;
-  kExpectedHeader[kBuildNumberHeaderOption] = "2";
-  kExpectedHeader[kPatchNumberHeaderOption] =  "3";
-  kExpectedHeader[kClientHeaderOption] = kClientStr;
-
-  std::map<std::string, std::string> kExpectedHeader2;
-  kExpectedHeader2[kSessionHeaderOption] =
-      "86401-1633771873-1633771873-1633771873";
-  kExpectedHeader2[kCredentialsHeaderOption] =
-      "d7c1c34ef6b90303b01c48a6c1db6419";
-  kExpectedHeader2[kBuildNumberHeaderOption] = "2";
-  kExpectedHeader2[kPatchNumberHeaderOption] = "3";
-  kExpectedHeader2[kClientHeaderOption] = kClientStr;
-
-  scoped_ptr<TestDataReductionProxyParams> params;
-  params.reset(
-      new TestDataReductionProxyParams(
-          DataReductionProxyParams::kAllowed |
-          DataReductionProxyParams::kFallbackAllowed |
-          DataReductionProxyParams::kPromoAllowed,
-          TestDataReductionProxyParams::HAS_EVERYTHING &
-          ~TestDataReductionProxyParams::HAS_DEV_ORIGIN &
-          ~TestDataReductionProxyParams::HAS_DEV_FALLBACK_ORIGIN));
-  // loop_proxy_ is just the current message loop. This means loop_proxy_
-  // is the network thread used by DataReductionProxyRequestOptions.
-  TestDataReductionProxyRequestOptions request_options(kClient,
-                                                       kVersion,
-                                                       params.get(),
-                                                       loop_proxy_);
-  request_options.Init();
-  base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(kExpectedBuild,
-            request_options.header_options_[kBuildNumberHeaderOption]);
-  EXPECT_EQ(kExpectedPatch,
-            request_options.header_options_[kPatchNumberHeaderOption]);
-  EXPECT_EQ(request_options.key_, kTestKey);
-  EXPECT_EQ(kExpectedCredentials,
-            request_options.header_options_[kCredentialsHeaderOption]);
-  EXPECT_EQ(kExpectedSession,
-            request_options.header_options_[kSessionHeaderOption]);
-  EXPECT_EQ(kClientStr, request_options.header_options_[kClientHeaderOption]);
-
-  // Now set a key.
-  request_options.SetKeyOnIO(kTestKey2);
-  base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(kTestKey2, request_options.key_);
-  EXPECT_EQ(kExpectedCredentials2,
-            request_options.header_options_[kCredentialsHeaderOption]);
-  EXPECT_EQ(kExpectedSession2,
-            request_options.header_options_[kSessionHeaderOption]);
-
-  // Don't write headers if the proxy is invalid.
-  net::HttpRequestHeaders headers;
-  request_options.MaybeAddRequestHeader(NULL, net::ProxyServer(), &headers);
-  EXPECT_FALSE(headers.HasHeader(kChromeProxyHeader));
-
-  // Don't write headers with a valid proxy, that's not a data reduction proxy.
-  request_options.MaybeAddRequestHeader(
-      NULL,
-      net::ProxyServer::FromURI(kOtherProxy, net::ProxyServer::SCHEME_HTTP),
-      &headers);
-  EXPECT_FALSE(headers.HasHeader(kChromeProxyHeader));
-
-  // Don't write headers with a valid data reduction ssl proxy.
-  request_options.MaybeAddRequestHeader(
-      NULL,
-      net::ProxyServer::FromURI(params->DefaultSSLOrigin(),
-                                net::ProxyServer::SCHEME_HTTP),
-      &headers);
-  EXPECT_FALSE(headers.HasHeader(kChromeProxyHeader));
-
-  // Write headers with a valid data reduction proxy.
-  request_options.MaybeAddRequestHeader(
-      NULL,
-      net::ProxyServer::FromURI(params->DefaultOrigin(),
-                                net::ProxyServer::SCHEME_HTTP),
-      &headers);
-  EXPECT_TRUE(headers.HasHeader(kChromeProxyHeader));
-  std::string header_value;
-  headers.GetHeader(kChromeProxyHeader, &header_value);
-  EXPECT_EQ(kExpectedHeader, ParseHeader(header_value));
-
-  // Write headers with a valid data reduction ssl proxy when one is expected.
-  net::HttpRequestHeaders ssl_headers;
-  request_options.MaybeAddProxyTunnelRequestHandler(
-      net::ProxyServer::FromURI(
-        params->DefaultSSLOrigin(),
-        net::ProxyServer::SCHEME_HTTP).host_port_pair(),
-      &ssl_headers);
-  EXPECT_TRUE(ssl_headers.HasHeader(kChromeProxyHeader));
-  std::string ssl_header_value;
-  ssl_headers.GetHeader(kChromeProxyHeader, &ssl_header_value);
-  EXPECT_EQ(kExpectedHeader, ParseHeader(ssl_header_value));
-
-  // Fast forward 24 hours. The header should be the same.
-  request_options.set_offset(base::TimeDelta::FromSeconds(24 * 60 * 60));
-  net::HttpRequestHeaders headers2;
-  // Write headers with a valid data reduction proxy.
-  request_options.MaybeAddRequestHeader(
-      NULL,
-      net::ProxyServer::FromURI(params->DefaultOrigin(),
-                                net::ProxyServer::SCHEME_HTTP),
-      &headers2);
-  EXPECT_TRUE(headers2.HasHeader(kChromeProxyHeader));
-  std::string header_value2;
-  headers2.GetHeader(kChromeProxyHeader, &header_value2);
-  EXPECT_EQ(kExpectedHeader, ParseHeader(header_value2));
-
-  // Fast forward one more second. The header should be new.
-  request_options.set_offset(base::TimeDelta::FromSeconds(24 * 60 * 60 + 1));
-  net::HttpRequestHeaders headers3;
-  // Write headers with a valid data reduction proxy.
-  request_options.MaybeAddRequestHeader(
-      NULL,
-      net::ProxyServer::FromURI(params->DefaultOrigin(),
-                                net::ProxyServer::SCHEME_HTTP),
-      &headers3);
-  EXPECT_TRUE(headers3.HasHeader(kChromeProxyHeader));
-  std::string header_value3;
-  headers3.GetHeader(kChromeProxyHeader, &header_value3);
-  EXPECT_EQ(kExpectedHeader2, ParseHeader(header_value3));
-}
-
-TEST_F(DataReductionProxyRequestOptionsTest, AuthorizationIgnoresEmptyKey) {
-scoped_ptr<TestDataReductionProxyParams> params;
-  params.reset(
-      new TestDataReductionProxyParams(
-          DataReductionProxyParams::kAllowed |
-          DataReductionProxyParams::kFallbackAllowed |
-          DataReductionProxyParams::kPromoAllowed,
-          TestDataReductionProxyParams::HAS_EVERYTHING &
-          ~TestDataReductionProxyParams::HAS_DEV_ORIGIN &
-          ~TestDataReductionProxyParams::HAS_DEV_FALLBACK_ORIGIN));
-  // loop_proxy_ is just the current message loop. This means loop_proxy_
-  // is the network thread used by DataReductionProxyRequestOptions.
-  TestDataReductionProxyRequestOptions request_options(kClient,
-                                                       kVersion,
-                                                       params.get(),
-                                                       loop_proxy_);
-  request_options.Init();
-  base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(kExpectedBuild,
-            request_options.header_options_[kBuildNumberHeaderOption]);
-  EXPECT_EQ(kExpectedPatch,
-            request_options.header_options_[kPatchNumberHeaderOption]);
-  EXPECT_EQ(request_options.key_, kTestKey);
-  EXPECT_EQ(kExpectedCredentials,
-            request_options.header_options_[kCredentialsHeaderOption]);
-  EXPECT_EQ(kExpectedSession,
-            request_options.header_options_[kSessionHeaderOption]);
-  EXPECT_EQ(kClientStr, request_options.header_options_[kClientHeaderOption]);
-
-  // Now set an empty key. The auth handler should ignore that, and the key
-  // remains |kTestKey|.
-  request_options.SetKeyOnIO("");
-  base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(request_options.key_, kTestKey);
-  EXPECT_EQ(kExpectedCredentials,
-            request_options.header_options_[kCredentialsHeaderOption]);
-  EXPECT_EQ(kExpectedSession,
-            request_options.header_options_[kSessionHeaderOption]);
-}
-
-TEST_F(DataReductionProxyRequestOptionsTest, AuthorizationBogusVersion) {
-  std::map<std::string, std::string> kExpectedHeader;
-  kExpectedHeader[kSessionHeaderOption] = kExpectedSession2;
-  kExpectedHeader[kCredentialsHeaderOption] = kExpectedCredentials2;
-  kExpectedHeader[kClientHeaderOption] = kClientStr;
-
-  scoped_ptr<TestDataReductionProxyParams> params;
-  params.reset(
-      new TestDataReductionProxyParams(
-          DataReductionProxyParams::kAllowed |
-          DataReductionProxyParams::kFallbackAllowed |
-          DataReductionProxyParams::kPromoAllowed,
-          TestDataReductionProxyParams::HAS_EVERYTHING &
-          ~TestDataReductionProxyParams::HAS_DEV_ORIGIN &
-          ~TestDataReductionProxyParams::HAS_DEV_FALLBACK_ORIGIN));
-  TestDataReductionProxyRequestOptions request_options(kClient,
-                                                       kBogusVersion,
-                                                       params.get(),
-                                                       loop_proxy_);
-  request_options.Init();
-  EXPECT_TRUE(request_options.header_options_.find(kBuildNumberHeaderOption) ==
-                  request_options.header_options_.end());
-  EXPECT_TRUE(request_options.header_options_.find(kPatchNumberHeaderOption) ==
-                  request_options.header_options_.end());
-
-  // Now set a key.
-  request_options.SetKeyOnIO(kTestKey2);
-  base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(kTestKey2, request_options.key_);
-  EXPECT_EQ(kExpectedCredentials2,
-            request_options.header_options_[kCredentialsHeaderOption]);
-  EXPECT_EQ(kExpectedSession2,
-            request_options.header_options_[kSessionHeaderOption]);
-
-  net::HttpRequestHeaders headers;
-  // Write headers with a valid data reduction proxy.
-  request_options.MaybeAddRequestHeader(
-      NULL,
-      net::ProxyServer::FromURI(
-          params->DefaultOrigin(),
-          net::ProxyServer::SCHEME_HTTP),
-      &headers);
-  EXPECT_TRUE(headers.HasHeader(kChromeProxyHeader));
-  std::string header_value;
-  headers.GetHeader(kChromeProxyHeader, &header_value);
-  EXPECT_EQ(kExpectedHeader, ParseHeader(header_value));
-}
-
 TEST_F(DataReductionProxyRequestOptionsTest, AuthHashForSalt) {
   std::string salt = "8675309"; // Jenny's number to test the hash generator.
   std::string salted_key = salt + kDataReductionProxyKey + salt;
@@ -371,105 +222,125 @@
                 8675309, kDataReductionProxyKey));
 }
 
-TEST_F(DataReductionProxyRequestOptionsTest, AuthorizationLoFi) {
-  std::map<std::string, std::string> kExpectedHeader;
-  kExpectedHeader[kSessionHeaderOption] = kExpectedSession;
-  kExpectedHeader[kCredentialsHeaderOption] = kExpectedCredentials;
-  kExpectedHeader[kClientHeaderOption] = kClientStr;
-  kExpectedHeader[kLoFiHeaderOption] = "low";
+TEST_F(DataReductionProxyRequestOptionsTest, AuthorizationOnIOThread) {
+  std::string expected_header;
+  SetHeaderExpectations(kExpectedSession2, kExpectedCredentials2, kClientStr,
+                        kExpectedBuild, kExpectedPatch, std::string(),
+                        std::vector<std::string>(), &expected_header);
 
-  scoped_ptr<TestDataReductionProxyParams> params;
-  params.reset(
-      new TestDataReductionProxyParams(
-          DataReductionProxyParams::kAllowed |
-          DataReductionProxyParams::kFallbackAllowed |
-          DataReductionProxyParams::kPromoAllowed,
-          TestDataReductionProxyParams::HAS_EVERYTHING &
-          ~TestDataReductionProxyParams::HAS_DEV_ORIGIN &
-          ~TestDataReductionProxyParams::HAS_DEV_FALLBACK_ORIGIN));
+  std::string expected_header2;
+  SetHeaderExpectations("86401-1633771873-1633771873-1633771873",
+                        "d7c1c34ef6b90303b01c48a6c1db6419", kClientStr,
+                        kExpectedBuild, kExpectedPatch, std::string(),
+                        std::vector<std::string>(), &expected_header2);
+
+  CreateRequestOptions(kVersion);
+  base::RunLoop().RunUntilIdle();
+
+  // Now set a key.
+  request_options()->SetKeyOnIO(kTestKey2);
+
+  // Don't write headers if the proxy is invalid.
+  VerifyExpectedHeader(std::string(), std::string());
+
+  // Don't write headers with a valid proxy, that's not a data reduction proxy.
+  VerifyExpectedHeader(kOtherProxy, std::string());
+
+  // Don't write headers with a valid data reduction ssl proxy.
+  VerifyExpectedHeader(params()->DefaultSSLOrigin(), std::string());
+
+  // Write headers with a valid data reduction proxy.
+  VerifyExpectedHeader(params()->DefaultOrigin(), expected_header);
+
+  // Write headers with a valid data reduction ssl proxy when one is expected.
+  net::HttpRequestHeaders ssl_headers;
+  request_options()->MaybeAddProxyTunnelRequestHandler(
+      net::ProxyServer::FromURI(
+        params()->DefaultSSLOrigin(),
+        net::ProxyServer::SCHEME_HTTP).host_port_pair(),
+      &ssl_headers);
+  EXPECT_TRUE(ssl_headers.HasHeader(kChromeProxyHeader));
+  std::string ssl_header_value;
+  ssl_headers.GetHeader(kChromeProxyHeader, &ssl_header_value);
+  EXPECT_EQ(expected_header, ssl_header_value);
+
+  // Fast forward 24 hours. The header should be the same.
+  request_options()->set_offset(base::TimeDelta::FromSeconds(24 * 60 * 60));
+  VerifyExpectedHeader(params()->DefaultOrigin(), expected_header);
+
+  // Fast forward one more second. The header should be new.
+  request_options()->set_offset(base::TimeDelta::FromSeconds(24 * 60 * 60 + 1));
+  VerifyExpectedHeader(params()->DefaultOrigin(), expected_header2);
+}
+
+TEST_F(DataReductionProxyRequestOptionsTest, AuthorizationIgnoresEmptyKey) {
+  std::string expected_header;
+  SetHeaderExpectations(kExpectedSession, kExpectedCredentials, kClientStr,
+                        kExpectedBuild, kExpectedPatch, std::string(),
+                        std::vector<std::string>(), &expected_header);
+  CreateRequestOptions(kVersion);
+  VerifyExpectedHeader(params()->DefaultOrigin(), expected_header);
+
+  // Now set an empty key. The auth handler should ignore that, and the key
+  // remains |kTestKey|.
+  request_options()->SetKeyOnIO(std::string());
+  VerifyExpectedHeader(params()->DefaultOrigin(), expected_header);
+}
+
+TEST_F(DataReductionProxyRequestOptionsTest, AuthorizationBogusVersion) {
+  std::string expected_header;
+  SetHeaderExpectations(kExpectedSession2, kExpectedCredentials2, kClientStr,
+                        std::string(), std::string(), std::string(),
+                        std::vector<std::string>(), &expected_header);
+
+  CreateRequestOptions(kBogusVersion);
+
+  // Now set a key.
+  request_options()->SetKeyOnIO(kTestKey2);
+  VerifyExpectedHeader(params()->DefaultOrigin(), expected_header);
+}
+
+TEST_F(DataReductionProxyRequestOptionsTest, AuthorizationLoFi) {
+  std::string expected_header;
+  SetHeaderExpectations(kExpectedSession, kExpectedCredentials, kClientStr,
+                        std::string(), std::string(), "low",
+                        std::vector<std::string>(), &expected_header);
 
   base::CommandLine::ForCurrentProcess()->AppendSwitch(
       data_reduction_proxy::switches::kEnableDataReductionProxyLoFi);
 
-  TestDataReductionProxyRequestOptions request_options(kClient,
-                                                       kBogusVersion,
-                                                       params.get(),
-                                                       loop_proxy_);
-  request_options.Init();
-  base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(kTestKey, request_options.key_);
-  EXPECT_EQ(kExpectedCredentials,
-            request_options.header_options_[kCredentialsHeaderOption]);
-  EXPECT_EQ(kExpectedSession,
-            request_options.header_options_[kSessionHeaderOption]);
-
-  net::HttpRequestHeaders headers;
-  // Write headers with a valid data reduction proxy.
-  request_options.MaybeAddRequestHeader(
-      NULL,
-      net::ProxyServer::FromURI(params->DefaultOrigin(),
-                                net::ProxyServer::SCHEME_HTTP),
-      &headers);
-  EXPECT_TRUE(headers.HasHeader(kChromeProxyHeader));
-  std::string header_value;
-  headers.GetHeader(kChromeProxyHeader, &header_value);
-  EXPECT_EQ(kExpectedHeader, ParseHeader(header_value));
+  CreateRequestOptions(kBogusVersion);
+  VerifyExpectedHeader(params()->DefaultOrigin(), expected_header);
 }
 
-TEST_F(DataReductionProxyRequestOptionsTest, AuthorizationLoFiOffThenOn) {
-  std::map<std::string, std::string> kExpectedHeader;
-  kExpectedHeader[kSessionHeaderOption] = kExpectedSession;
-  kExpectedHeader[kCredentialsHeaderOption] = kExpectedCredentials;
-  kExpectedHeader[kClientHeaderOption] = kClientStr;
-
-  scoped_ptr<TestDataReductionProxyParams> params;
-  params.reset(
-      new TestDataReductionProxyParams(
-          DataReductionProxyParams::kAllowed |
-          DataReductionProxyParams::kFallbackAllowed |
-          DataReductionProxyParams::kPromoAllowed,
-          TestDataReductionProxyParams::HAS_EVERYTHING &
-          ~TestDataReductionProxyParams::HAS_DEV_ORIGIN &
-          ~TestDataReductionProxyParams::HAS_DEV_FALLBACK_ORIGIN));
-
-  TestDataReductionProxyRequestOptions request_options(kClient,
-                                                       kBogusVersion,
-                                                       params.get(),
-                                                       loop_proxy_);
-  request_options.Init();
-  base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(kTestKey, request_options.key_);
-  EXPECT_EQ(kExpectedCredentials,
-            request_options.header_options_[kCredentialsHeaderOption]);
-  EXPECT_EQ(kExpectedSession,
-            request_options.header_options_[kSessionHeaderOption]);
-
-  net::HttpRequestHeaders headers;
-  // Write headers with a valid data reduction proxy.
-  request_options.MaybeAddRequestHeader(
-      NULL,
-      net::ProxyServer::FromURI(params->DefaultOrigin(),
-                                net::ProxyServer::SCHEME_HTTP),
-      &headers);
-  EXPECT_TRUE(headers.HasHeader(kChromeProxyHeader));
-  std::string header_value;
-  headers.GetHeader(kChromeProxyHeader, &header_value);
-  EXPECT_EQ(kExpectedHeader, ParseHeader(header_value));
-
+TEST_F(DataReductionProxyRequestOptionsTest, LoFiOn) {
   // Add the LoFi command line switch.
   base::CommandLine::ForCurrentProcess()->AppendSwitch(
       data_reduction_proxy::switches::kEnableDataReductionProxyLoFi);
-  kExpectedHeader[kLoFiHeaderOption] = "low";
 
-   // Write headers with a valid data reduction proxy.
-   request_options.MaybeAddRequestHeader(
-       NULL,
-       net::ProxyServer::FromURI(params->DefaultOrigin(),
-                                 net::ProxyServer::SCHEME_HTTP),
-       &headers);
-   EXPECT_TRUE(headers.HasHeader(kChromeProxyHeader));
-   headers.GetHeader(kChromeProxyHeader, &header_value);
-   EXPECT_EQ(kExpectedHeader, ParseHeader(header_value));
+  std::string expected_header;
+  SetHeaderExpectations(kExpectedSession, kExpectedCredentials, kClientStr,
+                        std::string(), std::string(), "low",
+                        std::vector<std::string>(), &expected_header);
+
+  CreateRequestOptions(kBogusVersion);
+  VerifyExpectedHeader(params()->DefaultOrigin(), expected_header);
+}
+
+TEST_F(DataReductionProxyRequestOptionsTest, ParseExperiments) {
+  base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
+      data_reduction_proxy::switches::kDataReductionProxyExperiment,
+      "staging,\"foo,bar\"");
+  std::vector<std::string> expected_experiments;
+  expected_experiments.push_back("staging");
+  expected_experiments.push_back("\"foo,bar\"");
+  std::string expected_header;
+  SetHeaderExpectations(kExpectedSession, kExpectedCredentials, kClientStr,
+                        std::string(), std::string(), std::string(),
+                        expected_experiments, &expected_header);
+
+  CreateRequestOptions(kBogusVersion);
+  VerifyExpectedHeader(params()->DefaultOrigin(), expected_header);
 }
 
 }  // namespace data_reduction_proxy
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_service.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_service.cc
new file mode 100644
index 0000000..08ff314
--- /dev/null
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_service.cc
@@ -0,0 +1,85 @@
+// 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 "components/data_reduction_proxy/core/browser/data_reduction_proxy_service.h"
+
+#include "base/sequenced_task_runner.h"
+#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_statistics_prefs.h"
+#include "net/base/load_flags.h"
+#include "net/url_request/url_fetcher.h"
+#include "net/url_request/url_request_status.h"
+
+namespace data_reduction_proxy {
+
+DataReductionProxyService::DataReductionProxyService(
+    scoped_ptr<DataReductionProxyStatisticsPrefs> statistics_prefs,
+    DataReductionProxySettings* settings,
+    net::URLRequestContextGetter* request_context_getter)
+    : url_request_context_getter_(request_context_getter),
+      settings_(settings),
+      weak_factory_(this) {
+  DCHECK(settings);
+  statistics_prefs_ = statistics_prefs.Pass();
+}
+
+DataReductionProxyService::~DataReductionProxyService() {
+}
+
+void DataReductionProxyService::Shutdown() {
+  DCHECK(CalledOnValidThread());
+  weak_factory_.InvalidateWeakPtrs();
+}
+
+void DataReductionProxyService::EnableCompressionStatisticsLogging(
+    PrefService* prefs,
+    scoped_refptr<base::SequencedTaskRunner> ui_task_runner,
+    const base::TimeDelta& commit_delay) {
+  DCHECK(!statistics_prefs_);
+  statistics_prefs_.reset(new DataReductionProxyStatisticsPrefs(
+      prefs, ui_task_runner, commit_delay));
+}
+
+base::WeakPtr<DataReductionProxyService>
+DataReductionProxyService::GetWeakPtr() {
+  DCHECK(CalledOnValidThread());
+  return weak_factory_.GetWeakPtr();
+}
+
+void DataReductionProxyService::OnURLFetchComplete(
+    const net::URLFetcher* source) {
+  DCHECK(source == fetcher_.get());
+  net::URLRequestStatus status = source->GetStatus();
+
+  std::string response;
+  source->GetResponseAsString(&response);
+
+  fetcher_callback_.Run(response, status);
+}
+
+net::URLFetcher* DataReductionProxyService::GetURLFetcherForProbe(
+    const GURL& probe_url) {
+  net::URLFetcher* fetcher =
+      net::URLFetcher::Create(probe_url, net::URLFetcher::GET, this);
+  fetcher->SetLoadFlags(net::LOAD_DISABLE_CACHE | net::LOAD_BYPASS_PROXY);
+  DCHECK(url_request_context_getter_);
+  fetcher->SetRequestContext(url_request_context_getter_);
+  // Configure max retries to be at most kMaxRetries times for 5xx errors.
+  static const int kMaxRetries = 5;
+  fetcher->SetMaxRetriesOn5xx(kMaxRetries);
+  fetcher->SetAutomaticallyRetryOnNetworkChanges(kMaxRetries);
+  return fetcher;
+}
+
+void DataReductionProxyService::CheckProbeURL(
+    const GURL& probe_url, FetcherResponseCallback fetcher_callback) {
+  DCHECK(CalledOnValidThread());
+  net::URLFetcher* fetcher = GetURLFetcherForProbe(probe_url);
+  if (!fetcher)
+    return;
+  fetcher_.reset(fetcher);
+  fetcher_callback_ = fetcher_callback;
+  fetcher_->Start();
+}
+
+}  // namespace data_reduction_proxy
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_service.h b/components/data_reduction_proxy/core/browser/data_reduction_proxy_service.h
new file mode 100644
index 0000000..1017e813
--- /dev/null
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_service.h
@@ -0,0 +1,106 @@
+// 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 COMPONENTS_DATA_REDUCTION_PROXY_CORE_BROWSER_DATA_REDUCTION_PROXY_SERVICE_H_
+#define COMPONENTS_DATA_REDUCTION_PROXY_CORE_BROWSER_DATA_REDUCTION_PROXY_SERVICE_H_
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/threading/non_thread_safe.h"
+#include "net/url_request/url_fetcher_delegate.h"
+
+class GURL;
+class PrefService;
+
+namespace base {
+class SequencedTaskRunner;
+class TimeDelta;
+}
+
+namespace net {
+class URLFetcher;
+class URLRequestContextGetter;
+class URLRequestStatus;
+}
+
+namespace data_reduction_proxy {
+
+typedef base::Callback<void(const std::string&, const net::URLRequestStatus&)>
+    FetcherResponseCallback;
+
+class DataReductionProxySettings;
+class DataReductionProxyStatisticsPrefs;
+
+
+// Contains and initializes all Data Reduction Proxy objects that have a
+// lifetime based on the UI thread.
+class DataReductionProxyService : public base::NonThreadSafe,
+                                  public net::URLFetcherDelegate {
+ public:
+  // The caller must ensure that |settings| and |request_context| remain alive
+  // for the lifetime of the |DataReductionProxyService| instance. This instance
+  // will take ownership of |statistics_prefs|.
+  // TODO(jeremyim): DataReductionProxyService should own
+  // DataReductionProxySettings and not vice versa.
+  DataReductionProxyService(
+      scoped_ptr<DataReductionProxyStatisticsPrefs> statistics_prefs,
+      DataReductionProxySettings* settings,
+      net::URLRequestContextGetter* request_context_getter);
+
+  ~DataReductionProxyService() override;
+
+  void Shutdown();
+
+  // Requests the given |probe_url|. Upon completion, returns the results to the
+  // caller via the |fetcher_callback|. Virtualized for unit testing.
+  virtual void CheckProbeURL(const GURL& probe_url,
+                             FetcherResponseCallback fetcher_callback);
+
+  // Constructs statistics prefs. This should not be called if a valid
+  // statistics prefs is passed into the constructor.
+  void EnableCompressionStatisticsLogging(
+      PrefService* prefs,
+      scoped_refptr<base::SequencedTaskRunner> ui_task_runner,
+      const base::TimeDelta& commit_delay);
+
+  DataReductionProxyStatisticsPrefs* statistics_prefs() const {
+    return statistics_prefs_.get();
+  }
+
+  DataReductionProxySettings* settings() const {
+    return settings_;
+  }
+
+  base::WeakPtr<DataReductionProxyService> GetWeakPtr();
+
+ protected:
+  // Virtualized for testing. Returns a fetcher for the probe to check if OK for
+  // the proxy to use TLS.
+  virtual net::URLFetcher* GetURLFetcherForProbe(const GURL& probe_url);
+
+ private:
+  // net::URLFetcherDelegate:
+  void OnURLFetchComplete(const net::URLFetcher* source) override;
+
+  net::URLRequestContextGetter* url_request_context_getter_;
+
+  // The URLFetcher being used for the canary check.
+  scoped_ptr<net::URLFetcher> fetcher_;
+  FetcherResponseCallback fetcher_callback_;
+
+  // Tracks compression statistics to be displayed to the user.
+  scoped_ptr<DataReductionProxyStatisticsPrefs> statistics_prefs_;
+
+  DataReductionProxySettings* settings_;
+
+  base::WeakPtrFactory<DataReductionProxyService> weak_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(DataReductionProxyService);
+};
+
+}  // namespace data_reduction_proxy
+
+#endif  // COMPONENTS_DATA_REDUCTION_PROXY_CORE_BROWSER_DATA_REDUCTION_PROXY_SERVICE_H_
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.cc
index 7fdb3212..081c49f5 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.cc
@@ -6,66 +6,24 @@
 
 #include "base/bind.h"
 #include "base/command_line.h"
-#include "base/metrics/field_trial.h"
 #include "base/metrics/histogram.h"
-#include "base/metrics/sparse_histogram.h"
 #include "base/prefs/pref_member.h"
 #include "base/prefs/pref_service.h"
-#include "base/prefs/scoped_user_pref_update.h"
 #include "base/strings/string_number_conversions.h"
-#include "base/strings/string_util.h"
-#include "base/strings/stringprintf.h"
-#include "base/strings/utf_string_conversions.h"
 #include "base/values.h"
-#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_configurator.h"
-#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_usage_stats.h"
-#include "components/data_reduction_proxy/core/common/data_reduction_proxy_event_store.h"
+#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_config.h"
+#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.h"
+#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_service.h"
+#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_statistics_prefs.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_params.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_pref_names.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_switches.h"
-#include "net/base/host_port_pair.h"
-#include "net/base/load_flags.h"
-#include "net/base/net_errors.h"
-#include "net/base/net_util.h"
-#include "net/http/http_network_session.h"
-#include "net/http/http_response_headers.h"
-#include "net/proxy/proxy_server.h"
-#include "net/url_request/url_fetcher.h"
-#include "net/url_request/url_fetcher_delegate.h"
-#include "net/url_request/url_request_context_getter.h"
-#include "net/url_request/url_request_status.h"
-#include "url/gurl.h"
-
-
-using base::StringPrintf;
 
 namespace {
-// Values of the UMA DataReductionProxy.NetworkChangeEvents histograms.
-// This enum must remain synchronized with the enum of the same
-// name in metrics/histograms/histograms.xml.
-enum DataReductionProxyNetworkChangeEvent {
-  IP_CHANGED = 0, // The client IP address changed.
-  DISABLED_ON_VPN = 1, // The proxy is disabled because a VPN is running.
-  CHANGE_EVENT_COUNT = 2 // This must always be last.
-};
-
 // Key of the UMA DataReductionProxy.StartupState histogram.
 const char kUMAProxyStartupStateHistogram[] =
     "DataReductionProxy.StartupState";
 
-// Key of the UMA DataReductionProxy.ProbeURL histogram.
-const char kUMAProxyProbeURL[] = "DataReductionProxy.ProbeURL";
-
-// Key of the UMA DataReductionProxy.ProbeURLNetError histogram.
-const char kUMAProxyProbeURLNetError[] = "DataReductionProxy.ProbeURLNetError";
-
-// Record a network change event.
-void RecordNetworkChangeEvent(DataReductionProxyNetworkChangeEvent event) {
-  UMA_HISTOGRAM_ENUMERATION("DataReductionProxy.NetworkChangeEvents",
-                            event,
-                            CHANGE_EVENT_COUNT);
-}
-
 int64 GetInt64PrefValue(const base::ListValue& list_value, size_t index) {
   int64 val = 0;
   std::string pref_value;
@@ -89,25 +47,19 @@
 
 namespace data_reduction_proxy {
 
-DataReductionProxySettings::DataReductionProxySettings(
-    scoped_ptr<DataReductionProxyParams> params)
-    : restricted_by_carrier_(false),
-      enabled_by_user_(false),
-      disabled_on_vpn_(false),
-      unreachable_(false),
+DataReductionProxySettings::DataReductionProxySettings()
+    : unreachable_(false),
+      allowed_(false),
+      alternative_allowed_(false),
+      promo_allowed_(false),
       prefs_(NULL),
-      url_request_context_getter_(NULL),
-      net_log_(NULL),
       event_store_(NULL),
-      configurator_(NULL) {
-  DCHECK(params.get());
-  config_.reset(new DataReductionProxyConfig(params.Pass()));
+      config_(nullptr) {
 }
 
 DataReductionProxySettings::~DataReductionProxySettings() {
-  if (params()->allowed())
+  if (allowed_)
     spdy_proxy_auth_enabled_.Destroy();
-  net::NetworkChangeNotifier::RemoveIPAddressObserver(this);
 }
 
 void DataReductionProxySettings::InitPrefMembers() {
@@ -125,53 +77,31 @@
           base::Unretained(this)));
 }
 
+void DataReductionProxySettings::UpdateConfigValues() {
+  DCHECK(config_);
+  allowed_ = config_->params()->allowed();
+  alternative_allowed_ = config_->params()->alternative_allowed();
+  promo_allowed_ = config_->params()->promo_allowed();
+  primary_origin_ = config_->params()->origin().ToURI();
+}
+
 void DataReductionProxySettings::InitDataReductionProxySettings(
     PrefService* prefs,
-    scoped_ptr<DataReductionProxyStatisticsPrefs> statistics_prefs,
-    net::URLRequestContextGetter* url_request_context_getter,
-    net::NetLog* net_log,
-    DataReductionProxyEventStore* event_store) {
+    DataReductionProxyIOData* io_data,
+    scoped_ptr<DataReductionProxyService> data_reduction_proxy_service) {
   DCHECK(thread_checker_.CalledOnValidThread());
   DCHECK(prefs);
-  DCHECK(!statistics_prefs_);
-  DCHECK(url_request_context_getter);
-  DCHECK(event_store);
+  DCHECK(io_data);
+  DCHECK(io_data->config());
+  DCHECK(io_data->event_store());
+  DCHECK(data_reduction_proxy_service.get());
   prefs_ = prefs;
-  statistics_prefs_ = statistics_prefs.Pass();
-  url_request_context_getter_ = url_request_context_getter;
-  net_log_ = net_log;
-  event_store_ = event_store;
+  config_ = io_data->config();
+  event_store_ = io_data->event_store();
+  data_reduction_proxy_service_ = data_reduction_proxy_service.Pass();
   InitPrefMembers();
+  UpdateConfigValues();
   RecordDataReductionInit();
-
-  // Disable the proxy if it is not allowed to be used.
-  if (!params()->allowed())
-    return;
-
-  AddDefaultProxyBypassRules();
-  net::NetworkChangeNotifier::AddIPAddressObserver(this);
-}
-
-void DataReductionProxySettings::SetDataReductionProxyStatisticsPrefs(
-    scoped_ptr<DataReductionProxyStatisticsPrefs> statistics_prefs) {
-  statistics_prefs_ = statistics_prefs.Pass();
-}
-
-void DataReductionProxySettings::EnableCompressionStatisticsLogging(
-    PrefService* prefs,
-    scoped_refptr<base::SequencedTaskRunner> ui_task_runner,
-    const base::TimeDelta& commit_delay) {
-  DCHECK(!statistics_prefs_);
-  statistics_prefs_.reset(
-      new DataReductionProxyStatisticsPrefs(
-          prefs, ui_task_runner, commit_delay));
-}
-
-base::WeakPtr<DataReductionProxyStatisticsPrefs>
-DataReductionProxySettings::statistics_prefs() {
-  if (statistics_prefs_)
-    return statistics_prefs_->GetWeakPtr();
-  return base::WeakPtr<DataReductionProxyStatisticsPrefs>();
 }
 
 void DataReductionProxySettings::SetOnDataReductionEnabledCallback(
@@ -180,12 +110,6 @@
   on_data_reduction_proxy_enabled_.Run(IsDataReductionProxyEnabled());
 }
 
-void DataReductionProxySettings::SetProxyConfigurator(
-    DataReductionProxyConfigurator* configurator) {
-  DCHECK(configurator);
-  configurator_ = configurator;
-}
-
 bool DataReductionProxySettings::IsDataReductionProxyEnabled() {
   return spdy_proxy_auth_enabled_.GetValue() || IsEnabledOnCommandLine();
 }
@@ -202,7 +126,7 @@
 void DataReductionProxySettings::SetDataReductionProxyEnabled(bool enabled) {
   DCHECK(thread_checker_.CalledOnValidThread());
   // Prevent configuring the proxy when it is not allowed to be used.
-  if (!params()->allowed())
+  if (!allowed_)
     return;
 
   if (spdy_proxy_auth_enabled_.GetValue() != enabled) {
@@ -215,7 +139,7 @@
     bool enabled) {
   DCHECK(thread_checker_.CalledOnValidThread());
   // Prevent configuring the proxy when it is not allowed to be used.
-  if (!params()->alternative_allowed())
+  if (!alternative_allowed_)
     return;
   if (data_reduction_proxy_alternative_enabled_.GetValue() != enabled) {
     data_reduction_proxy_alternative_enabled_.SetValue(enabled);
@@ -225,9 +149,10 @@
 
 int64 DataReductionProxySettings::GetDataReductionLastUpdateTime() {
   DCHECK(thread_checker_.CalledOnValidThread());
-  DCHECK(statistics_prefs_);
+  DCHECK(data_reduction_proxy_service_->statistics_prefs());
   int64 last_update_internal =
-      statistics_prefs_->GetInt64(prefs::kDailyHttpContentLengthLastUpdateDate);
+      data_reduction_proxy_service_->statistics_prefs()->GetInt64(
+          prefs::kDailyHttpContentLengthLastUpdateDate);
   base::Time last_update = base::Time::FromInternalValue(last_update_internal);
   return static_cast<int64>(last_update.ToJsTime());
 }
@@ -253,157 +178,36 @@
   return GetDailyContentLengths(prefs::kDailyHttpReceivedContentLength);
 }
 
-void DataReductionProxySettings::OnURLFetchComplete(
-    const net::URLFetcher* source) {
-  DCHECK(thread_checker_.CalledOnValidThread());
-
-  DCHECK(source == fetcher_.get());
-  net::URLRequestStatus status = source->GetStatus();
-
-  if (event_store_) {
-    event_store_->EndCanaryRequest(bound_net_log_, status.error());
-  }
-
-  if (status.status() == net::URLRequestStatus::FAILED) {
-    if (status.error() == net::ERR_INTERNET_DISCONNECTED) {
-      RecordProbeURLFetchResult(INTERNET_DISCONNECTED);
-      return;
-    }
-    // TODO(bengr): Remove once we understand the reasons probes are failing.
-    // Probe errors are either due to fetcher-level errors or modified
-    // responses. This only tracks the former.
-    UMA_HISTOGRAM_SPARSE_SLOWLY(
-        kUMAProxyProbeURLNetError, std::abs(status.error()));
-  }
-
-  std::string response;
-  source->GetResponseAsString(&response);
-
-  if ("OK" == response.substr(0, 2)) {
-    DVLOG(1) << "The data reduction proxy is unrestricted.";
-
-    if (enabled_by_user_) {
-      if (restricted_by_carrier_) {
-        // The user enabled the proxy, but sometime previously in the session,
-        // the network operator had blocked the canary and restricted the user.
-        // The current network doesn't block the canary, so don't restrict the
-        // proxy configurations.
-        SetProxyConfigs(true /* enabled */,
-                        IsDataReductionProxyAlternativeEnabled(),
-                        false /* restricted */,
-                        false /* at_startup */);
-        RecordProbeURLFetchResult(SUCCEEDED_PROXY_ENABLED);
-      } else {
-        RecordProbeURLFetchResult(SUCCEEDED_PROXY_ALREADY_ENABLED);
-      }
-    }
-    restricted_by_carrier_ = false;
-    return;
-  }
-  DVLOG(1) << "The data reduction proxy is restricted to the configured "
-           << "fallback proxy.";
-  if (enabled_by_user_) {
-    if (!restricted_by_carrier_) {
-      // Restrict the proxy.
-      SetProxyConfigs(true /* enabled */,
-                      IsDataReductionProxyAlternativeEnabled(),
-                      true /* restricted */,
-                      false /* at_startup */);
-      RecordProbeURLFetchResult(FAILED_PROXY_DISABLED);
-    } else {
-      RecordProbeURLFetchResult(FAILED_PROXY_ALREADY_DISABLED);
-    }
-  }
-  restricted_by_carrier_ = true;
-}
-
 PrefService* DataReductionProxySettings::GetOriginalProfilePrefs() {
   DCHECK(thread_checker_.CalledOnValidThread());
   return prefs_;
 }
 
-void DataReductionProxySettings::AddDefaultProxyBypassRules() {
-  // localhost
-  DCHECK(configurator_);
-  configurator_->AddHostPatternToBypass("<local>");
-  // RFC6890 loopback addresses.
-  // TODO(tbansal): Remove this once crbug/446705 is fixed.
-  configurator_->AddHostPatternToBypass("127.0.0.0/8");
-
-  // RFC6890 current network (only valid as source address).
-  configurator_->AddHostPatternToBypass("0.0.0.0/8");
-
-  // RFC1918 private addresses.
-  configurator_->AddHostPatternToBypass("10.0.0.0/8");
-  configurator_->AddHostPatternToBypass("172.16.0.0/12");
-  configurator_->AddHostPatternToBypass("192.168.0.0/16");
-
-  // RFC3513 unspecified address.
-  configurator_->AddHostPatternToBypass("::/128");
-
-  // RFC4193 private addresses.
-  configurator_->AddHostPatternToBypass("fc00::/7");
-  // IPV6 probe addresses.
-  configurator_->AddHostPatternToBypass("*-ds.metric.gstatic.com");
-  configurator_->AddHostPatternToBypass("*-v4.metric.gstatic.com");
-}
-
-void DataReductionProxySettings::LogProxyState(
-    bool enabled, bool restricted, bool at_startup) {
-  // This must stay a LOG(WARNING); the output is used in processing customer
-  // feedback.
-  const char kAtStartup[] = "at startup";
-  const char kByUser[] = "by user action";
-  const char kOn[] = "ON";
-  const char kOff[] = "OFF";
-  const char kRestricted[] = "(Restricted)";
-  const char kUnrestricted[] = "(Unrestricted)";
-
-  std::string annotated_on =
-      kOn + std::string(" ") + (restricted ? kRestricted : kUnrestricted);
-
-  LOG(WARNING) << "SPDY proxy " << (enabled ? annotated_on : kOff)
-               << " " << (at_startup ? kAtStartup : kByUser);
-}
-
-void DataReductionProxySettings::OnIPAddressChanged() {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  if (enabled_by_user_) {
-    DCHECK(params()->allowed());
-    RecordNetworkChangeEvent(IP_CHANGED);
-    if (DisableIfVPN())
-      return;
-    if (IsDataReductionProxyAlternativeEnabled() &&
-        !params()->alternative_fallback_allowed()) {
-      return;
-    }
-    ProbeWhetherDataReductionProxyIsAvailable();
-  }
-}
-
 void DataReductionProxySettings::OnProxyEnabledPrefChange() {
   DCHECK(thread_checker_.CalledOnValidThread());
   if (!on_data_reduction_proxy_enabled_.is_null())
     on_data_reduction_proxy_enabled_.Run(IsDataReductionProxyEnabled());
-  if (!params()->allowed())
+  if (!allowed_)
     return;
   MaybeActivateDataReductionProxy(false);
 }
 
 void DataReductionProxySettings::OnProxyAlternativeEnabledPrefChange() {
   DCHECK(thread_checker_.CalledOnValidThread());
-  if (!params()->alternative_allowed())
+  if (!alternative_allowed_)
     return;
   MaybeActivateDataReductionProxy(false);
 }
 
 void DataReductionProxySettings::ResetDataReductionStatistics() {
   DCHECK(thread_checker_.CalledOnValidThread());
-  DCHECK(statistics_prefs_);
+  DCHECK(data_reduction_proxy_service_->statistics_prefs());
   base::ListValue* original_update =
-      statistics_prefs_->GetList(prefs::kDailyHttpOriginalContentLength);
+      data_reduction_proxy_service_->statistics_prefs()->GetList(
+          prefs::kDailyHttpOriginalContentLength);
   base::ListValue* received_update =
-      statistics_prefs_->GetList(prefs::kDailyHttpReceivedContentLength);
+      data_reduction_proxy_service_->statistics_prefs()->GetList(
+          prefs::kDailyHttpReceivedContentLength);
   original_update->Clear();
   received_update->Clear();
   for (size_t i = 0; i < kNumDaysInHistory; ++i) {
@@ -429,54 +233,15 @@
     ResetDataReductionStatistics();
   }
   // Configure use of the data reduction proxy if it is enabled.
-  enabled_by_user_= IsDataReductionProxyEnabled();
-  SetProxyConfigs(enabled_by_user_ && !disabled_on_vpn_,
-                  IsDataReductionProxyAlternativeEnabled(),
-                  restricted_by_carrier_,
-                  at_startup);
-
-  // Check if the proxy has been restricted explicitly by the carrier.
-  if (enabled_by_user_ && !disabled_on_vpn_ &&
-      !(IsDataReductionProxyAlternativeEnabled() &&
-        !params()->alternative_fallback_allowed())) {
-    ProbeWhetherDataReductionProxyIsAvailable();
-  }
-}
-
-void DataReductionProxySettings::SetProxyConfigs(bool enabled,
-                                                 bool alternative_enabled,
-                                                 bool restricted,
-                                                 bool at_startup) {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  DCHECK(configurator_);
-
-  LogProxyState(enabled, restricted, at_startup);
-  // The alternative is only configured if the standard configuration is
-  // is enabled.
-  if (enabled & !params()->holdback()) {
-    if (alternative_enabled) {
-      configurator_->Enable(restricted,
-                            !params()->alternative_fallback_allowed(),
-                            params()->alt_origin().ToURI(),
-                            std::string(),
-                            params()->ssl_origin().ToURI());
-    } else {
-      configurator_->Enable(restricted,
-                            !params()->fallback_allowed(),
-                            params()->origin().ToURI(),
-                            params()->fallback_origin().ToURI(),
-                            std::string());
-    }
-  } else {
-    configurator_->Disable();
-  }
+  config_->SetProxyPrefs(IsDataReductionProxyEnabled(),
+                         IsDataReductionProxyAlternativeEnabled(), at_startup);
 }
 
 // Metrics methods
 void DataReductionProxySettings::RecordDataReductionInit() {
   DCHECK(thread_checker_.CalledOnValidThread());
   ProxyStartupState state = PROXY_NOT_AVAILABLE;
-  if (params()->allowed()) {
+  if (allowed_) {
     if (IsDataReductionProxyEnabled())
       state = PROXY_ENABLED;
     else
@@ -486,31 +251,19 @@
   RecordStartupState(state);
 }
 
-void DataReductionProxySettings::RecordProbeURLFetchResult(
-    ProbeURLFetchResult result) {
-  UMA_HISTOGRAM_ENUMERATION(kUMAProxyProbeURL,
-                            result,
-                            PROBE_URL_FETCH_RESULT_COUNT);
-}
-
 void DataReductionProxySettings::RecordStartupState(ProxyStartupState state) {
   UMA_HISTOGRAM_ENUMERATION(kUMAProxyStartupStateHistogram,
                             state,
                             PROXY_STARTUP_STATE_COUNT);
 }
 
-void DataReductionProxySettings::GetNetworkList(
-    net::NetworkInterfaceList* interfaces,
-    int policy) {
-  net::GetNetworkList(interfaces, policy);
-}
-
 DataReductionProxySettings::ContentLengthList
 DataReductionProxySettings::GetDailyContentLengths(const char* pref_name) {
   DCHECK(thread_checker_.CalledOnValidThread());
   DataReductionProxySettings::ContentLengthList content_lengths;
-  DCHECK(statistics_prefs_);
-  const base::ListValue* list_value = statistics_prefs_->GetList(pref_name);
+  DCHECK(data_reduction_proxy_service_->statistics_prefs());
+  const base::ListValue* list_value =
+      data_reduction_proxy_service_->statistics_prefs()->GetList(pref_name);
   if (list_value->GetSize() == kNumDaysInHistory) {
     for (size_t i = 0; i < kNumDaysInHistory; ++i) {
       content_lengths.push_back(GetInt64PrefValue(*list_value, i));
@@ -526,12 +279,14 @@
     int64* last_update_time) {
   DCHECK(thread_checker_.CalledOnValidThread());
   DCHECK_LE(days, kNumDaysInHistory);
-  DCHECK(statistics_prefs_);
+  DCHECK(data_reduction_proxy_service_->statistics_prefs());
 
   const base::ListValue* original_list =
-      statistics_prefs_->GetList(prefs::kDailyHttpOriginalContentLength);
+      data_reduction_proxy_service_->statistics_prefs()->GetList(
+          prefs::kDailyHttpOriginalContentLength);
   const base::ListValue* received_list =
-      statistics_prefs_->GetList(prefs::kDailyHttpReceivedContentLength);
+      data_reduction_proxy_service_->statistics_prefs()->GetList(
+          prefs::kDailyHttpReceivedContentLength);
 
   if (original_list->GetSize() != kNumDaysInHistory ||
       received_list->GetSize() != kNumDaysInHistory) {
@@ -552,80 +307,8 @@
   *original_content_length = orig;
   *received_content_length = recv;
   *last_update_time =
-      statistics_prefs_->GetInt64(prefs::kDailyHttpContentLengthLastUpdateDate);
-}
-
-net::URLFetcher* DataReductionProxySettings::GetBaseURLFetcher(
-    const GURL& gurl,
-    int load_flags) {
-
-  net::URLFetcher* fetcher = net::URLFetcher::Create(gurl,
-                                                     net::URLFetcher::GET,
-                                                     this);
-  fetcher->SetLoadFlags(load_flags);
-  DCHECK(url_request_context_getter_);
-  fetcher->SetRequestContext(url_request_context_getter_);
-  // Configure max retries to be at most kMaxRetries times for 5xx errors.
-  static const int kMaxRetries = 5;
-  fetcher->SetMaxRetriesOn5xx(kMaxRetries);
-  fetcher->SetAutomaticallyRetryOnNetworkChanges(kMaxRetries);
-  return fetcher;
-}
-
-
-net::URLFetcher*
-DataReductionProxySettings::GetURLFetcherForAvailabilityCheck() {
-  return GetBaseURLFetcher(params()->probe_url(),
-                           net::LOAD_DISABLE_CACHE | net::LOAD_BYPASS_PROXY);
-}
-
-
-void DataReductionProxySettings::ProbeWhetherDataReductionProxyIsAvailable() {
-  net::URLFetcher* fetcher = GetURLFetcherForAvailabilityCheck();
-  if (!fetcher)
-    return;
-  fetcher_.reset(fetcher);
-
-  bound_net_log_ = net::BoundNetLog::Make(
-      net_log_, net::NetLog::SOURCE_DATA_REDUCTION_PROXY);
-  if (event_store_) {
-    event_store_->BeginCanaryRequest(bound_net_log_,
-                                     fetcher_->GetOriginalURL());
-  }
-
-  fetcher_->Start();
-}
-
-bool DataReductionProxySettings::DisableIfVPN() {
-  net::NetworkInterfaceList network_interfaces;
-  GetNetworkList(&network_interfaces, 0);
-  // VPNs use a "tun" interface, so the presence of a "tun" interface indicates
-  // a VPN is in use.
-  // TODO(kundaji): Verify this works on Windows.
-  const std::string vpn_interface_name_prefix = "tun";
-  for (size_t i = 0; i < network_interfaces.size(); ++i) {
-    std::string interface_name = network_interfaces[i].name;
-    if (LowerCaseEqualsASCII(
-        interface_name.begin(),
-        interface_name.begin() + vpn_interface_name_prefix.size(),
-        vpn_interface_name_prefix.c_str())) {
-      SetProxyConfigs(false,
-                      IsDataReductionProxyAlternativeEnabled(),
-                      false,
-                      false);
-      disabled_on_vpn_ = true;
-      RecordNetworkChangeEvent(DISABLED_ON_VPN);
-      return true;
-    }
-  }
-  if (disabled_on_vpn_) {
-    SetProxyConfigs(enabled_by_user_,
-                    IsDataReductionProxyAlternativeEnabled(),
-                    restricted_by_carrier_,
-                    false);
-  }
-  disabled_on_vpn_ = false;
-  return false;
+      data_reduction_proxy_service_->statistics_prefs()->GetInt64(
+          prefs::kDailyHttpContentLengthLastUpdateDate);
 }
 
 }  // namespace data_reduction_proxy
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.h b/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.h
index 61539beb..f5b70a4 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.h
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.h
@@ -14,28 +14,16 @@
 #include "base/memory/scoped_ptr.h"
 #include "base/prefs/pref_member.h"
 #include "base/threading/thread_checker.h"
-#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_config.h"
-#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_configurator.h"
-#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_statistics_prefs.h"
-#include "components/data_reduction_proxy/core/common/data_reduction_proxy_params.h"
-#include "net/base/net_log.h"
-#include "net/base/net_util.h"
-#include "net/base/network_change_notifier.h"
-#include "net/url_request/url_fetcher_delegate.h"
+#include "url/gurl.h"
 
 class PrefService;
 
-namespace net {
-class HostPortPair;
-class HttpNetworkSession;
-class HttpResponseHeaders;
-class URLFetcher;
-class URLRequestContextGetter;
-}
-
 namespace data_reduction_proxy {
 
+class DataReductionProxyConfig;
 class DataReductionProxyEventStore;
+class DataReductionProxyIOData;
+class DataReductionProxyService;
 class DataReductionProxyStatisticsPrefs;
 
 // The number of days of bandwidth usage statistics that are tracked.
@@ -63,38 +51,20 @@
 // be called from there.
 // TODO(marq): Convert this to be a KeyedService with an
 // associated factory class, and refactor the Java call sites accordingly.
-class DataReductionProxySettings
-    : public net::URLFetcherDelegate,
-      public net::NetworkChangeNotifier::IPAddressObserver {
+class DataReductionProxySettings {
  public:
   typedef std::vector<long long> ContentLengthList;
 
-  static bool IsProxyKeySetOnCommandLine();
+  DataReductionProxySettings();
+  virtual ~DataReductionProxySettings();
 
-  DataReductionProxySettings(scoped_ptr<DataReductionProxyParams> params);
-  ~DataReductionProxySettings() override;
-
-  DataReductionProxyParams* params() const {
-    return config_->params();
-  }
-
-  // Initializes the data reduction proxy with profile and local state prefs,
-  // and a |UrlRequestContextGetter| for canary probes. The caller must ensure
-  // that all parameters remain alive for the lifetime of the
-  // |DataReductionProxySettings| instance.
+  // Initializes the data reduction proxy with profile prefs and a
+  // |DataReductionProxyIOData|. The caller must ensure that all parameters
+  // remain alive for the lifetime of the |DataReductionProxySettings| instance.
   void InitDataReductionProxySettings(
       PrefService* prefs,
-      scoped_ptr<DataReductionProxyStatisticsPrefs> statistics_prefs,
-      net::URLRequestContextGetter* url_request_context_getter,
-      net::NetLog* net_log,
-      DataReductionProxyEventStore* event_store);
-
-  // Constructs statistics prefs. This should not be called if a valid
-  // statistics prefs is passed into the constructor.
-  void EnableCompressionStatisticsLogging(
-      PrefService* prefs,
-      scoped_refptr<base::SequencedTaskRunner> ui_task_runner,
-      const base::TimeDelta& commit_delay);
+      DataReductionProxyIOData* io_data,
+      scoped_ptr<DataReductionProxyService> data_reduction_proxy_service);
 
   base::WeakPtr<DataReductionProxyStatisticsPrefs> statistics_prefs();
 
@@ -103,11 +73,6 @@
   void SetOnDataReductionEnabledCallback(
       const base::Callback<void(bool)>& on_data_reduction_proxy_enabled);
 
-  // Sets the logic the embedder uses to set the networking configuration that
-  // causes traffic to be proxied.
-  void SetProxyConfigurator(
-      DataReductionProxyConfigurator* configurator);
-
   // Returns true if the proxy is enabled.
   bool IsDataReductionProxyEnabled();
 
@@ -117,9 +82,7 @@
   // Returns true if the proxy is managed by an adminstrator's policy.
   bool IsDataReductionProxyManaged();
 
-  // Enables or disables the data reduction proxy. If a probe URL is available,
-  // and a probe request fails at some point, the proxy won't be used until a
-  // probe succeeds.
+  // Enables or disables the data reduction proxy.
   void SetDataReductionProxyEnabled(bool enabled);
 
   // Enables or disables the alternative data reduction proxy configuration.
@@ -155,11 +118,7 @@
 
   ContentLengthList GetDailyContentLengths(const char* pref_name);
 
-  // net::URLFetcherDelegate:
-  void OnURLFetchComplete(const net::URLFetcher* source) override;
-
-  // Configures data reduction proxy and makes a request to the probe URL to
-  // determine server availability. |at_startup| is true when this method is
+  // Configures data reduction proxy. |at_startup| is true when this method is
   // called in response to creating or loading a new profile.
   void MaybeActivateDataReductionProxy(bool at_startup);
 
@@ -169,67 +128,59 @@
     return event_store_;
   }
 
-  // Used for testing.
-  void SetDataReductionProxyStatisticsPrefs(
-      scoped_ptr<DataReductionProxyStatisticsPrefs> statistics_prefs);
+  // Returns true if the data reduction proxy configuration may be used.
+  bool Allowed() const {
+    return allowed_;
+  }
+
+  // Returns true if the alternative data reduction proxy configuration may be
+  // used.
+  bool AlternativeAllowed() const {
+    return alternative_allowed_;
+  }
+
+  // Returns true if the data reduction proxy promo may be shown.
+  // This is idependent of whether the data reduction proxy is allowed.
+  bool PromoAllowed() const {
+    return promo_allowed_;
+  }
+
+  // The data reduction proxy primary origin
+  const std::string PrimaryOrigin() const {
+    return primary_origin_;
+  }
+
+  DataReductionProxyService* data_reduction_proxy_service() {
+    return data_reduction_proxy_service_.get();
+  }
+
+  // Permits changing the underlying |DataReductionProxyConfig| without running
+  // the initialization loop.
+  void ResetConfigForTest(DataReductionProxyConfig* config) {
+    config_ = config;
+  }
 
  protected:
   void InitPrefMembers();
 
-  // Returns a fetcher for the probe to check if OK for the proxy to use SPDY.
-  // Virtual for testing.
-  virtual net::URLFetcher* GetURLFetcherForAvailabilityCheck();
+  void UpdateConfigValues();
 
   // Virtualized for unit test support.
   virtual PrefService* GetOriginalProfilePrefs();
 
-  // Sets the proxy configs, enabling or disabling the proxy according to
-  // the value of |enabled| and |alternative_enabled|. Use the alternative
-  // configuration only if |enabled| and |alternative_enabled| are true. If
-  // |restricted| is true, only enable the fallback proxy. |at_startup| is true
-  // when this method is called from InitDataReductionProxySettings.
-  virtual void SetProxyConfigs(bool enabled,
-                               bool alternative_enabled,
-                               bool restricted,
-                               bool at_startup);
-
   // Metrics method. Subclasses should override if they wish to provide
   // alternatives.
   virtual void RecordDataReductionInit();
 
-  virtual void AddDefaultProxyBypassRules();
-
-  // Writes a warning to the log that is used in backend processing of
-  // customer feedback. Virtual so tests can mock it for verification.
-  virtual void LogProxyState(bool enabled, bool restricted, bool at_startup);
-
-  // Virtualized for mocking. Records UMA containing the result of requesting
-  // the probe URL.
-  virtual void RecordProbeURLFetchResult(
-      data_reduction_proxy::ProbeURLFetchResult result);
-
   // Virtualized for mocking. Records UMA specifying whether the proxy was
   // enabled or disabled at startup.
   virtual void RecordStartupState(
       data_reduction_proxy::ProxyStartupState state);
 
-  // Virtualized for mocking. Returns the list of network interfaces in use.
-  virtual void GetNetworkList(net::NetworkInterfaceList* interfaces,
-                              int policy);
-
-  DataReductionProxyConfigurator* configurator() {
-    return configurator_;
-  }
-
  private:
   friend class DataReductionProxySettingsTestBase;
   friend class DataReductionProxySettingsTest;
-  FRIEND_TEST_ALL_PREFIXES(DataReductionProxySettingsTest,
-                           TestAuthenticationInit);
-  FRIEND_TEST_ALL_PREFIXES(DataReductionProxySettingsTest,
-                           TestAuthHashGeneration);
-  FRIEND_TEST_ALL_PREFIXES(DataReductionProxySettingsTest,
-                           TestAuthHashGenerationWithOriginSetViaSwitch);
+  friend class DataReductionProxyTestContext;
   FRIEND_TEST_ALL_PREFIXES(DataReductionProxySettingsTest,
                            TestResetDataReductionStatistics);
   FRIEND_TEST_ALL_PREFIXES(DataReductionProxySettingsTest,
@@ -241,78 +192,45 @@
   FRIEND_TEST_ALL_PREFIXES(DataReductionProxySettingsTest,
                            TestMaybeActivateDataReductionProxy);
   FRIEND_TEST_ALL_PREFIXES(DataReductionProxySettingsTest,
-                           TestOnIPAddressChanged);
-  FRIEND_TEST_ALL_PREFIXES(DataReductionProxySettingsTest,
                            TestOnProxyEnabledPrefChange);
   FRIEND_TEST_ALL_PREFIXES(DataReductionProxySettingsTest,
                            TestInitDataReductionProxyOn);
   FRIEND_TEST_ALL_PREFIXES(DataReductionProxySettingsTest,
                            TestInitDataReductionProxyOff);
   FRIEND_TEST_ALL_PREFIXES(DataReductionProxySettingsTest,
-                           TestBypassList);
-  FRIEND_TEST_ALL_PREFIXES(DataReductionProxySettingsTest,
                            CheckInitMetricsWhenNotAllowed);
-  FRIEND_TEST_ALL_PREFIXES(DataReductionProxySettingsTest,
-                           TestSetProxyConfigs);
-  FRIEND_TEST_ALL_PREFIXES(DataReductionProxySettingsTest,
-                           TestSetProxyConfigsHoldback);
-
-  // NetworkChangeNotifier::IPAddressObserver:
-  void OnIPAddressChanged() override;
 
   void OnProxyEnabledPrefChange();
   void OnProxyAlternativeEnabledPrefChange();
 
   void ResetDataReductionStatistics();
 
-  // Requests the proxy probe URL, if one is set.  If unable to do so, disables
-  // the proxy, if enabled. Otherwise enables the proxy if disabled by a probe
-  // failure.
-  void ProbeWhetherDataReductionProxyIsAvailable();
-
-  // Disables use of the data reduction proxy on VPNs. Returns true if the
-  // data reduction proxy has been disabled.
-  bool DisableIfVPN();
-
-  // Generic method to get a URL fetcher.
-  net::URLFetcher* GetBaseURLFetcher(const GURL& gurl, int load_flags);
-
-  std::string key_;
-  bool restricted_by_carrier_;
-  bool enabled_by_user_;
-  bool disabled_on_vpn_;
   bool unreachable_;
 
-  scoped_ptr<net::URLFetcher> fetcher_;
-
-  // A new BoundNetLog is created for each canary check so that we can correlate
-  // the request begin/end phases.
-  net::BoundNetLog bound_net_log_;
+  // The following values are cached in order to access the values on the
+  // correct thread.
+  bool allowed_;
+  bool alternative_allowed_;
+  bool promo_allowed_;
+  std::string primary_origin_;
 
   BooleanPrefMember spdy_proxy_auth_enabled_;
   BooleanPrefMember data_reduction_proxy_alternative_enabled_;
 
+  scoped_ptr<DataReductionProxyService> data_reduction_proxy_service_;
+
   PrefService* prefs_;
-  scoped_ptr<DataReductionProxyStatisticsPrefs> statistics_prefs_;
-
-  net::URLRequestContextGetter* url_request_context_getter_;
-
-  // The caller must ensure that the |net_log_|, if set, outlives this instance.
-  // It is used to create new instances of |bound_net_log_| on canary
-  // requests.
-  net::NetLog* net_log_;
 
   // The caller must ensure that the |event_store_| outlives this instance.
   DataReductionProxyEventStore* event_store_;
 
+  // The caller must ensure that the |config_| outlives this instance.
+  DataReductionProxyConfig* config_;
+
   base::Callback<void(bool)> on_data_reduction_proxy_enabled_;
 
-  DataReductionProxyConfigurator* configurator_;
-
   base::ThreadChecker thread_checker_;
 
-  scoped_ptr<DataReductionProxyConfig> config_;
-
   DISALLOW_COPY_AND_ASSIGN(DataReductionProxySettings);
 };
 
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings_test_utils.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings_test_utils.cc
index bab18899..ee292895 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings_test_utils.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings_test_utils.cc
@@ -6,17 +6,17 @@
 
 #include "base/bind.h"
 #include "base/command_line.h"
-#include "base/message_loop/message_loop.h"
 #include "base/prefs/pref_registry_simple.h"
 #include "base/prefs/scoped_user_pref_update.h"
 #include "base/strings/string_number_conversions.h"
-#include "base/test/test_simple_task_runner.h"
 #include "base/time/time.h"
+#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_config_test_utils.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_configurator_test_utils.h"
-#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_prefs.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_statistics_prefs.h"
-#include "components/data_reduction_proxy/core/common/data_reduction_proxy_event_store.h"
+#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_headers_test_utils.h"
+#include "components/data_reduction_proxy/core/common/data_reduction_proxy_params.h"
+#include "components/data_reduction_proxy/core/common/data_reduction_proxy_params_test_utils.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_pref_names.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_switches.h"
 
@@ -26,29 +26,12 @@
 
 namespace {
 
-const char kProbeURLWithOKResponse[] = "http://ok.org/";
-
 const char kProxy[] = "proxy";
 
 }  // namespace
 
 namespace data_reduction_proxy {
 
-namespace {
-
-ProbeURLFetchResult FetchResult(bool enabled, bool success) {
-  if (enabled) {
-    if (success)
-      return SUCCEEDED_PROXY_ALREADY_ENABLED;
-    return FAILED_PROXY_DISABLED;
-  }
-  if (success)
-    return SUCCEEDED_PROXY_ENABLED;
-  return FAILED_PROXY_ALREADY_DISABLED;
-}
-
-}  // namespace
-
 DataReductionProxySettingsTestBase::DataReductionProxySettingsTestBase()
     : testing::Test() {
 }
@@ -57,7 +40,17 @@
 
 // testing::Test implementation:
 void DataReductionProxySettingsTestBase::SetUp() {
-  PrefRegistrySimple* registry = pref_service_.registry();
+  test_context_.reset(new DataReductionProxyTestContext(
+      DataReductionProxyParams::kAllowed |
+          DataReductionProxyParams::kFallbackAllowed |
+          DataReductionProxyParams::kPromoAllowed,
+      TestDataReductionProxyParams::HAS_EVERYTHING &
+          ~TestDataReductionProxyParams::HAS_DEV_ORIGIN &
+          ~TestDataReductionProxyParams::HAS_DEV_FALLBACK_ORIGIN,
+      DataReductionProxyTestContext::USE_MOCK_CONFIG |
+          DataReductionProxyTestContext::SKIP_SETTINGS_INITIALIZATION));
+
+  PrefRegistrySimple* registry = test_context_->pref_service()->registry();
   registry->RegisterListPref(prefs::kDailyHttpOriginalContentLength);
   registry->RegisterListPref(prefs::kDailyHttpReceivedContentLength);
   registry->RegisterInt64Pref(prefs::kDailyHttpContentLengthLastUpdateDate,
@@ -68,16 +61,12 @@
   registry->RegisterBooleanPref(prefs::kDataReductionProxyWasEnabledBefore,
                                 false);
 
-  event_store_.reset(new DataReductionProxyEventStore(
-      scoped_refptr<base::TestSimpleTaskRunner>(
-          new base::TestSimpleTaskRunner())));
-
   //AddProxyToCommandLine();
   ResetSettings(true, true, false, true, false);
 
-  ListPrefUpdate original_update(&pref_service_,
+  ListPrefUpdate original_update(test_context_->pref_service(),
                                  prefs::kDailyHttpOriginalContentLength);
-  ListPrefUpdate received_update(&pref_service_,
+  ListPrefUpdate received_update(test_context_->pref_service(),
                                  prefs::kDailyHttpReceivedContentLength);
   for (int64 i = 0; i < kNumDaysInHistory; i++) {
     original_update->Insert(0,
@@ -85,24 +74,9 @@
     received_update->Insert(0, new base::StringValue(base::Int64ToString(i)));
   }
   last_update_time_ = base::Time::Now().LocalMidnight();
-  scoped_ptr<DataReductionProxyStatisticsPrefs> statistics_prefs(
-      new DataReductionProxyStatisticsPrefs(
-          &pref_service_,
-          scoped_refptr<base::TestSimpleTaskRunner>(
-              new base::TestSimpleTaskRunner()),
-              base::TimeDelta()));
-  statistics_prefs->SetInt64(
+  settings_->data_reduction_proxy_service()->statistics_prefs()->SetInt64(
       prefs::kDailyHttpContentLengthLastUpdateDate,
       last_update_time_.ToInternalValue());
-  settings_->SetDataReductionProxyStatisticsPrefs(
-        statistics_prefs.Pass());
-  expected_params_.reset(new TestDataReductionProxyParams(
-      DataReductionProxyParams::kAllowed |
-      DataReductionProxyParams::kFallbackAllowed |
-      DataReductionProxyParams::kPromoAllowed,
-      TestDataReductionProxyParams::HAS_EVERYTHING &
-      ~TestDataReductionProxyParams::HAS_DEV_ORIGIN &
-      ~TestDataReductionProxyParams::HAS_DEV_FALLBACK_ORIGIN));
 }
 
 template <class C>
@@ -123,20 +97,19 @@
   if (holdback)
     flags |= DataReductionProxyParams::kHoldback;
   MockDataReductionProxySettings<C>* settings =
-      new MockDataReductionProxySettings<C>(flags);
+      new MockDataReductionProxySettings<C>();
+  settings->config_ = test_context_->config();
+  settings->data_reduction_proxy_service_ =
+      test_context_->CreateDataReductionProxyService();
+  test_context_->config()->ResetParamFlagsForTest(flags);
+  settings->UpdateConfigValues();
   EXPECT_CALL(*settings, GetOriginalProfilePrefs())
       .Times(AnyNumber())
-      .WillRepeatedly(Return(&pref_service_));
+      .WillRepeatedly(Return(test_context_->pref_service()));
   EXPECT_CALL(*settings, GetLocalStatePrefs())
       .Times(AnyNumber())
-      .WillRepeatedly(Return(&pref_service_));
-  EXPECT_CALL(*settings, GetURLFetcherForAvailabilityCheck()).Times(0);
-  EXPECT_CALL(*settings, LogProxyState(_, _, _)).Times(0);
+      .WillRepeatedly(Return(test_context_->pref_service()));
   settings_.reset(settings);
-  configurator_.reset(new TestDataReductionProxyConfigurator(
-      scoped_refptr<base::TestSimpleTaskRunner>(
-          new base::TestSimpleTaskRunner()), &net_log_, event_store_.get()));
-  settings_->configurator_ = configurator_.get();
 }
 
 // Explicitly generate required instantiations.
@@ -148,145 +121,44 @@
     bool promo_allowed,
     bool holdback);
 
-template <class C>
-void DataReductionProxySettingsTestBase::SetProbeResult(
-    const std::string& test_url,
-    const std::string& response,
-    ProbeURLFetchResult result,
-    bool success,
-    int expected_calls)  {
-  MockDataReductionProxySettings<C>* settings =
-      static_cast<MockDataReductionProxySettings<C>*>(settings_.get());
-  if (0 == expected_calls) {
-    EXPECT_CALL(*settings, GetURLFetcherForAvailabilityCheck()).Times(0);
-    EXPECT_CALL(*settings, RecordProbeURLFetchResult(_)).Times(0);
-  } else {
-    EXPECT_CALL(*settings, RecordProbeURLFetchResult(result)).Times(1);
-    EXPECT_CALL(*settings, GetURLFetcherForAvailabilityCheck())
-        .Times(expected_calls)
-        .WillRepeatedly(Return(new net::FakeURLFetcher(
-            GURL(test_url),
-            settings,
-            response,
-            success ? net::HTTP_OK : net::HTTP_INTERNAL_SERVER_ERROR,
-            success ? net::URLRequestStatus::SUCCESS :
-                      net::URLRequestStatus::FAILED)));
-  }
-}
-
-// Explicitly generate required instantiations.
-template void
-DataReductionProxySettingsTestBase::SetProbeResult<DataReductionProxySettings>(
-    const std::string& test_url,
-    const std::string& response,
-    ProbeURLFetchResult result,
-    bool success,
-    int expected_calls);
-
-void DataReductionProxySettingsTestBase::CheckProxyConfigs(
+void DataReductionProxySettingsTestBase::ExpectSetProxyPrefs(
     bool expected_enabled,
-    bool expected_restricted,
-    bool expected_fallback_restricted) {
-  TestDataReductionProxyConfigurator* config =
-      static_cast<TestDataReductionProxyConfigurator*>(
-          settings_->configurator_);
-  ASSERT_EQ(expected_restricted, config->restricted());
-  ASSERT_EQ(expected_fallback_restricted, config->fallback_restricted());
-  ASSERT_EQ(expected_enabled, config->enabled());
-}
-
-void DataReductionProxySettingsTestBase::CheckProbe(
-    bool initially_enabled,
-    const std::string& probe_url,
-    const std::string& response,
-    bool request_succeeded,
-    bool expected_enabled,
-    bool expected_restricted,
-    bool expected_fallback_restricted) {
-  pref_service_.SetBoolean(prefs::kDataReductionProxyEnabled,
-                           initially_enabled);
-  if (initially_enabled)
-    settings_->enabled_by_user_ = true;
-  settings_->restricted_by_carrier_ = false;
-  SetProbeResult(probe_url,
-                 response,
-                 FetchResult(initially_enabled,
-                             request_succeeded && (response == "OK")),
-                 request_succeeded,
-                 initially_enabled ? 1 : 0);
-  settings_->MaybeActivateDataReductionProxy(false);
-  base::MessageLoop::current()->RunUntilIdle();
-  CheckProxyConfigs(expected_enabled,
-                    expected_restricted,
-                    expected_fallback_restricted);
-}
-
-void DataReductionProxySettingsTestBase::CheckProbeOnIPChange(
-    const std::string& probe_url,
-    const std::string& response,
-    bool request_succeeded,
-    bool expected_restricted,
-    bool expected_fallback_restricted) {
-  SetProbeResult(probe_url,
-                 response,
-                 FetchResult(!settings_->restricted_by_carrier_,
-                             request_succeeded && (response == "OK")),
-                 request_succeeded,
-                 1);
-  settings_->OnIPAddressChanged();
-  base::MessageLoop::current()->RunUntilIdle();
-  CheckProxyConfigs(true, expected_restricted, expected_fallback_restricted);
+    bool expected_alternate_enabled,
+    bool expected_at_startup) {
+  MockDataReductionProxyConfig* config =
+      static_cast<MockDataReductionProxyConfig*>(test_context_->config());
+  EXPECT_CALL(*config,
+              SetProxyPrefs(expected_enabled, expected_alternate_enabled,
+                            expected_at_startup));
 }
 
 void DataReductionProxySettingsTestBase::CheckOnPrefChange(
     bool enabled,
     bool expected_enabled,
     bool managed) {
-  // Always have a sucessful probe for pref change tests.
-  SetProbeResult(kProbeURLWithOKResponse,
-                 "OK",
-                 FetchResult(enabled, true),
-                 true,
-                 expected_enabled ? 1 : 0);
+  ExpectSetProxyPrefs(expected_enabled, false, false);
   if (managed) {
-    pref_service_.SetManagedPref(prefs::kDataReductionProxyEnabled,
-                                 new base::FundamentalValue(enabled));
+    test_context_->pref_service()->SetManagedPref(
+        prefs::kDataReductionProxyEnabled, new base::FundamentalValue(enabled));
   } else {
-    pref_service_.SetBoolean(prefs::kDataReductionProxyEnabled, enabled);
+    test_context_->pref_service()->SetBoolean(prefs::kDataReductionProxyEnabled,
+                                              enabled);
   }
-  base::MessageLoop::current()->RunUntilIdle();
+  test_context_->RunUntilIdle();
   // Never expect the proxy to be restricted for pref change tests.
-  CheckProxyConfigs(expected_enabled, false, false);
 }
 
 void DataReductionProxySettingsTestBase::CheckInitDataReductionProxy(
     bool enabled_at_startup) {
-  base::MessageLoopForUI loop;
-  scoped_ptr<DataReductionProxyConfigurator> configurator(
-      new TestDataReductionProxyConfigurator(
-          scoped_refptr<base::TestSimpleTaskRunner>(
-              new base::TestSimpleTaskRunner()), &net_log_,
-              event_store_.get()));
-  settings_->SetProxyConfigurator(configurator.get());
-  scoped_refptr<net::TestURLRequestContextGetter> request_context =
-      new net::TestURLRequestContextGetter(base::MessageLoopProxy::current());
-
-  // Delete statistics prefs, otherwise the DCHECK in
-  // InitDataReductionProxySettings fails.
-  settings_->SetDataReductionProxyStatisticsPrefs(
-      scoped_ptr<data_reduction_proxy::DataReductionProxyStatisticsPrefs>());
   settings_->InitDataReductionProxySettings(
-      &pref_service_,
-      scoped_ptr<data_reduction_proxy::DataReductionProxyStatisticsPrefs>(),
-      request_context.get(),
-      &net_log_,
-      event_store_.get());
+      test_context_->pref_service(), test_context_->io_data(),
+      test_context_->CreateDataReductionProxyService());
   settings_->SetOnDataReductionEnabledCallback(
       base::Bind(&DataReductionProxySettingsTestBase::
                  RegisterSyntheticFieldTrialCallback,
                  base::Unretained(this)));
 
-  base::MessageLoop::current()->RunUntilIdle();
+  test_context_->RunUntilIdle();
   EXPECT_EQ(enabled_at_startup, proxy_enabled_);
 }
 
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings_test_utils.h b/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings_test_utils.h
index ab8b06c..ced835e2 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings_test_utils.h
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings_test_utils.h
@@ -8,7 +8,6 @@
 #include "base/memory/scoped_ptr.h"
 #include "base/prefs/testing_pref_service.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.h"
-#include "components/data_reduction_proxy/core/common/data_reduction_proxy_params_test_utils.h"
 #include "net/base/capturing_net_log.h"
 #include "net/base/net_util.h"
 #include "net/url_request/test_url_fetcher_factory.h"
@@ -23,52 +22,18 @@
 
 class DataReductionProxyConfigurator;
 class DataReductionProxyStatisticsPrefs;
+class DataReductionProxyTestContext;
+class MockDataReductionProxyConfig;
 
 template <class C>
 class MockDataReductionProxySettings : public C {
  public:
-  MockDataReductionProxySettings<C>() : DataReductionProxySettings(
-      scoped_ptr<TestDataReductionProxyParams>(new TestDataReductionProxyParams(
-          DataReductionProxyParams::kAllowed |
-          DataReductionProxyParams::kFallbackAllowed |
-          DataReductionProxyParams::kPromoAllowed,
-          TestDataReductionProxyParams::HAS_EVERYTHING &
-          ~TestDataReductionProxyParams::HAS_DEV_ORIGIN &
-          ~TestDataReductionProxyParams::HAS_DEV_FALLBACK_ORIGIN)).Pass()) {}
-  MockDataReductionProxySettings<C>(int flags)
-      : C(scoped_ptr<TestDataReductionProxyParams>(
-          new TestDataReductionProxyParams(flags,
-              TestDataReductionProxyParams::HAS_EVERYTHING &
-              ~TestDataReductionProxyParams::HAS_DEV_ORIGIN &
-              ~TestDataReductionProxyParams::HAS_DEV_FALLBACK_ORIGIN))
-          .Pass()) {}
-  MOCK_METHOD0(GetURLFetcherForAvailabilityCheck, net::URLFetcher*());
+  MockDataReductionProxySettings<C>() : C() {
+  }
   MOCK_METHOD0(GetOriginalProfilePrefs, PrefService*());
   MOCK_METHOD0(GetLocalStatePrefs, PrefService*());
-  MOCK_METHOD3(LogProxyState, void(
-      bool enabled, bool restricted, bool at_startup));
-  MOCK_METHOD1(RecordProbeURLFetchResult,
-               void(ProbeURLFetchResult result));
   MOCK_METHOD1(RecordStartupState,
                void(ProxyStartupState state));
-
-  // SetProxyConfigs should always call LogProxyState exactly once.
-  virtual void SetProxyConfigs(bool enabled,
-                               bool alternative_enabled,
-                               bool restricted,
-                               bool at_startup) override {
-    EXPECT_CALL(*this, LogProxyState(enabled, restricted, at_startup)).Times(1);
-    C::SetProxyConfigs(enabled, alternative_enabled, restricted, at_startup);
-  }
-  virtual void GetNetworkList(net::NetworkInterfaceList* interfaces,
-                              int policy) override {
-    if (!network_interfaces_.get())
-      return;
-    for (size_t i = 0; i < network_interfaces_->size(); ++i)
-      interfaces->push_back(network_interfaces_->at(i));
-  }
-
-  scoped_ptr<net::NetworkInterfaceList> network_interfaces_;
 };
 
 class DataReductionProxySettingsTestBase : public testing::Test {
@@ -97,33 +62,15 @@
                              bool promo_allowed,
                              bool holdback) = 0;
 
-  template <class C> void SetProbeResult(
-      const std::string& test_url,
-      const std::string& response,
-      ProbeURLFetchResult state,
-      bool success,
-      int expected_calls);
-  virtual void SetProbeResult(const std::string& test_url,
-                              const std::string& response,
-                              ProbeURLFetchResult result,
-                              bool success,
-                              int expected_calls) = 0;
+  void ExpectSetProxyPrefs(bool expected_enabled,
+                           bool expected_alternate_enabled,
+                           bool expected_at_startup);
 
-  void CheckProxyConfigs(bool expected_enabled,
-                         bool expected_restricted,
-                         bool expected_fallback_restricted);
-  void CheckProbe(bool initially_enabled,
-                  const std::string& probe_url,
-                  const std::string& response,
-                  bool request_success,
-                  bool expected_enabled,
-                  bool expected_restricted,
-                  bool expected_fallback_restricted);
-  void CheckProbeOnIPChange(const std::string& probe_url,
-                            const std::string& response,
-                            bool request_success,
-                            bool expected_enabled,
-                            bool expected_fallback_restricted);
+  void CheckMaybeActivateDataReductionProxy(bool initially_enabled,
+                                            bool request_succeeded,
+                                            bool expected_enabled,
+                                            bool expected_restricted,
+                                            bool expected_fallback_restricted);
   void CheckOnPrefChange(bool enabled, bool expected_enabled, bool managed);
   void InitWithStatisticsPrefs();
   void CheckInitDataReductionProxy(bool enabled_at_startup);
@@ -131,14 +78,10 @@
     proxy_enabled_ = proxy_enabled;
   }
 
-  TestingPrefServiceSimple pref_service_;
-  scoped_ptr<DataReductionProxyConfigurator> configurator_;
+  scoped_ptr<DataReductionProxyTestContext> test_context_;
   scoped_ptr<DataReductionProxySettings> settings_;
-  scoped_ptr<TestDataReductionProxyParams> expected_params_;
   base::Time last_update_time_;
   bool proxy_enabled_;
-  net::CapturingNetLog net_log_;
-  scoped_ptr<DataReductionProxyEventStore> event_store_;
 };
 
 // Test implementations should be subclasses of an instantiation of this
@@ -157,19 +100,6 @@
     return DataReductionProxySettingsTestBase::ResetSettings<C>(
         allowed, fallback_allowed, alt_allowed, promo_allowed, holdback);
   }
-
-  virtual void SetProbeResult(const std::string& test_url,
-                              const std::string& response,
-                              ProbeURLFetchResult result,
-                              bool success,
-                              int expected_calls) override {
-    return DataReductionProxySettingsTestBase::SetProbeResult<C>(
-        test_url,
-        response,
-        result,
-        success,
-        expected_calls);
-  }
 };
 
 }  // namespace data_reduction_proxy
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings_unittest.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings_unittest.cc
index b588554..93fec58 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings_unittest.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings_unittest.cc
@@ -6,11 +6,13 @@
 
 #include "base/command_line.h"
 #include "base/md5.h"
-#include "base/message_loop/message_loop.h"
+#include "base/metrics/field_trial.h"
 #include "base/strings/utf_string_conversions.h"
-#include "base/test/test_simple_task_runner.h"
+#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_config_test_utils.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_configurator_test_utils.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_settings_test_utils.h"
+#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_statistics_prefs.h"
+#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_params.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_pref_names.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_switches.h"
@@ -19,136 +21,41 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-namespace {
-
-const char kProbeURLWithOKResponse[] = "http://ok.org/";
-const char kProbeURLWithBadResponse[] = "http://bad.org/";
-const char kProbeURLWithNoResponse[] = "http://no.org/";
-
-}  // namespace
-
 namespace data_reduction_proxy {
 
-class DataReductionProxyStatisticsPrefs;
+class BadEntropyProvider : public base::FieldTrial::EntropyProvider {
+ public:
+  ~BadEntropyProvider() override {}
+
+  double GetEntropyForTrial(const std::string& trial_name,
+                            uint32 randomization_seed) const override {
+    return 0.5;
+  }
+};
 
 class DataReductionProxySettingsTest
     : public ConcreteDataReductionProxySettingsTest<
           DataReductionProxySettings> {
+ public:
+  void CheckMaybeActivateDataReductionProxy(bool initially_enabled,
+                                            bool request_succeeded,
+                                            bool expected_enabled,
+                                            bool expected_restricted,
+                                            bool expected_fallback_restricted) {
+    test_context_->pref_service()->SetBoolean(prefs::kDataReductionProxyEnabled,
+                                              initially_enabled);
+    test_context_->config()->SetStateForTest(initially_enabled, false,
+                                             !request_succeeded, false);
+    ExpectSetProxyPrefs(expected_enabled, false, false);
+    settings_->MaybeActivateDataReductionProxy(false);
+    test_context_->RunUntilIdle();
+  }
 };
 
-TEST_F(DataReductionProxySettingsTest, TestGetDataReductionProxyOrigin) {
-  // SetUp() adds the origin to the command line, which should be returned here.
-  std::string result =
-      settings_->params()->origin().ToURI();
-  EXPECT_EQ(expected_params_->DefaultOrigin(), result);
-}
-
-TEST_F(DataReductionProxySettingsTest, TestGetDataReductionProxyDevOrigin) {
-  base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
-      switches::kDataReductionProxyDev, expected_params_->DefaultDevOrigin());
-  ResetSettings(true, true, false, true, false);
-  std::string result =
-      settings_->params()->origin().ToURI();
-  EXPECT_EQ(expected_params_->DefaultDevOrigin(), result);
-}
-
-
-TEST_F(DataReductionProxySettingsTest, TestGetDataReductionProxies) {
-  DataReductionProxyParams::DataReductionProxyList proxies =
-      expected_params_->GetAllowedProxies();
-
-  unsigned int expected_proxy_size = 2u;
-  EXPECT_EQ(expected_proxy_size, proxies.size());
-
-  EXPECT_EQ(net::ProxyServer::FromURI(expected_params_->DefaultOrigin(),
-                                      net::ProxyServer::SCHEME_HTTP),
-            proxies[0]);
-  EXPECT_EQ(net::ProxyServer::FromURI(expected_params_->DefaultFallbackOrigin(),
-                                      net::ProxyServer::SCHEME_HTTP),
-            proxies[1]);
-}
-
-TEST_F(DataReductionProxySettingsTest, TestSetProxyConfigs) {
-  TestDataReductionProxyParams drp_params(
-      DataReductionProxyParams::kAllowed |
-      DataReductionProxyParams::kFallbackAllowed |
-      DataReductionProxyParams::kPromoAllowed,
-      TestDataReductionProxyParams::HAS_EVERYTHING &
-      ~TestDataReductionProxyParams::HAS_DEV_ORIGIN &
-      ~TestDataReductionProxyParams::HAS_DEV_FALLBACK_ORIGIN);
-  base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
-      switches::kDataReductionProxyAlt, drp_params.DefaultAltOrigin());
-  base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
-      switches::kDataReductionProxyAltFallback,
-      drp_params.DefaultAltFallbackOrigin());
-  base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
-      switches::kDataReductionSSLProxy, drp_params.DefaultSSLOrigin());
-  ResetSettings(true, true, true, true, false);
-  TestDataReductionProxyConfigurator* configurator =
-      static_cast<TestDataReductionProxyConfigurator*>(
-          settings_->configurator());
-
-  settings_->SetProxyConfigs(true, true, false, false);
-  EXPECT_TRUE(configurator->enabled());
-  EXPECT_EQ(net::ProxyServer::FromURI(expected_params_->DefaultAltOrigin(),
-                                      net::ProxyServer::SCHEME_HTTP),
-            net::ProxyServer::FromURI(configurator->origin(),
-                                      net::ProxyServer::SCHEME_HTTP));
-  EXPECT_TRUE(
-      net::ProxyServer::FromURI(expected_params_->DefaultAltFallbackOrigin(),
-                                net::ProxyServer::SCHEME_HTTP).is_valid());
-  EXPECT_TRUE(configurator->fallback_origin().empty());
-  EXPECT_EQ(net::ProxyServer::FromURI(expected_params_->DefaultSSLOrigin(),
-                                      net::ProxyServer::SCHEME_HTTP),
-            net::ProxyServer::FromURI(configurator->ssl_origin(),
-                                      net::ProxyServer::SCHEME_HTTP));
-
-  settings_->SetProxyConfigs(true, false, false, false);
-  EXPECT_TRUE(configurator->enabled());
-  EXPECT_EQ(net::ProxyServer::FromURI(drp_params.DefaultOrigin(),
-                                      net::ProxyServer::SCHEME_HTTP),
-            net::ProxyServer::FromURI(configurator->origin(),
-                                      net::ProxyServer::SCHEME_HTTP));
-  EXPECT_EQ(net::ProxyServer::FromURI(drp_params.DefaultFallbackOrigin(),
-                                      net::ProxyServer::SCHEME_HTTP),
-            net::ProxyServer::FromURI(
-                configurator->fallback_origin(),
-                net::ProxyServer::SCHEME_HTTP));
-  EXPECT_TRUE(configurator->ssl_origin().empty());
-
-  settings_->SetProxyConfigs(false, true, false, false);
-  EXPECT_FALSE(configurator->enabled());
-  EXPECT_TRUE(configurator->origin().empty());
-  EXPECT_TRUE(configurator->fallback_origin().empty());
-  EXPECT_TRUE(configurator->ssl_origin().empty());
-
-  settings_->SetProxyConfigs(false, false, false, false);
-  EXPECT_FALSE(configurator->enabled());
-  EXPECT_TRUE(configurator->origin().empty());
-  EXPECT_TRUE(configurator->fallback_origin().empty());
-  EXPECT_TRUE(configurator->ssl_origin().empty());
-}
-
-TEST_F(DataReductionProxySettingsTest, TestSetProxyConfigsHoldback) {
-  ResetSettings(true, true, true, true, true);
-  TestDataReductionProxyConfigurator* configurator =
-      static_cast<TestDataReductionProxyConfigurator*>(
-          settings_->configurator());
-
-   // Holdback.
-  settings_->SetProxyConfigs(true, true, false, false);
-  EXPECT_FALSE(configurator->enabled());
-  EXPECT_EQ("", configurator->origin());
-  EXPECT_EQ("", configurator->fallback_origin());
-  EXPECT_EQ("", configurator->ssl_origin());
-}
-
 TEST_F(DataReductionProxySettingsTest, TestIsProxyEnabledOrManaged) {
   settings_->InitPrefMembers();
-  base::MessageLoopForUI loop;
   // The proxy is disabled initially.
-  settings_->enabled_by_user_ = false;
-  settings_->SetProxyConfigs(false, false, false, false);
+  test_context_->config()->SetStateForTest(false, false, false, false);
 
   EXPECT_FALSE(settings_->IsDataReductionProxyEnabled());
   EXPECT_FALSE(settings_->IsDataReductionProxyManaged());
@@ -161,7 +68,7 @@
   EXPECT_TRUE(settings_->IsDataReductionProxyEnabled());
   EXPECT_TRUE(settings_->IsDataReductionProxyManaged());
 
-  base::MessageLoop::current()->RunUntilIdle();
+  test_context_->RunUntilIdle();
 }
 
 TEST_F(DataReductionProxySettingsTest, TestResetDataReductionStatistics) {
@@ -241,121 +148,22 @@
       settings_->GetOriginalProfilePrefs());
 
   // TODO(bengr): Test enabling/disabling while a probe is outstanding.
-  base::MessageLoopForUI loop;
   // The proxy is enabled and unrestructed initially.
   // Request succeeded but with bad response, expect proxy to be restricted.
-  CheckProbe(true,
-             kProbeURLWithBadResponse,
-             "Bad",
-             true,
-             true,
-             true,
-             false);
+  CheckMaybeActivateDataReductionProxy(true, true, true, true, false);
   // Request succeeded with valid response, expect proxy to be unrestricted.
-  CheckProbe(true,
-             kProbeURLWithOKResponse,
-             "OK",
-             true,
-             true,
-             false,
-             false);
+  CheckMaybeActivateDataReductionProxy(true, true, true, false, false);
   // Request failed, expect proxy to be enabled but restricted.
-  CheckProbe(true,
-             kProbeURLWithNoResponse,
-             "",
-             false,
-             true,
-             true,
-             false);
+  CheckMaybeActivateDataReductionProxy(true, false, true, true, false);
   // The proxy is disabled initially. Probes should not be emitted to change
   // state.
-  CheckProbe(false,
-             kProbeURLWithOKResponse,
-             "OK",
-             true,
-             false,
-             false,
-             false);
-}
-
-TEST_F(DataReductionProxySettingsTest, TestOnIPAddressChanged) {
-  base::MessageLoopForUI loop;
-  // The proxy is enabled initially.
-  pref_service_.SetBoolean(prefs::kDataReductionProxyEnabled, true);
-  settings_->spdy_proxy_auth_enabled_.Init(
-      prefs::kDataReductionProxyEnabled,
-      settings_->GetOriginalProfilePrefs());
-  settings_->data_reduction_proxy_alternative_enabled_.Init(
-      prefs::kDataReductionProxyAltEnabled,
-      settings_->GetOriginalProfilePrefs());
-  settings_->enabled_by_user_ = true;
-  settings_->restricted_by_carrier_ = false;
-  settings_->SetProxyConfigs(true, false, false, true);
-  // IP address change triggers a probe that succeeds. Proxy remains
-  // unrestricted.
-  CheckProbeOnIPChange(kProbeURLWithOKResponse,
-                       "OK",
-                       true,
-                       false,
-                       false);
-  // IP address change triggers a probe that fails. Proxy is restricted.
-  CheckProbeOnIPChange(kProbeURLWithBadResponse,
-                       "Bad",
-                       true,
-                       true,
-                       false);
-  // IP address change triggers a probe that fails. Proxy remains restricted.
-  CheckProbeOnIPChange(kProbeURLWithBadResponse,
-                       "Bad",
-                       true,
-                       true,
-                       false);
-  // IP address change triggers a probe that succeeds. Proxy is unrestricted.
-  CheckProbeOnIPChange(kProbeURLWithOKResponse,
-                       "OK",
-                       true,
-                       false,
-                       false);
-  // Simulate a VPN connection. The proxy should be disabled.
-  MockSettings* settings = static_cast<MockSettings*>(settings_.get());
-  settings->network_interfaces_.reset(new net::NetworkInterfaceList());
-  settings->network_interfaces_->push_back(net::NetworkInterface(
-      "tun0", /* network interface name */
-      "tun0", /* network interface friendly name */
-      0,      /* interface index */
-      net::NetworkChangeNotifier::CONNECTION_WIFI,
-      net::IPAddressNumber(),        /* IP address */
-      0,                             /* network prefix */
-      net::IP_ADDRESS_ATTRIBUTE_NONE /* ip address attribute */
-      ));
-  settings_->OnIPAddressChanged();
-  base::MessageLoop::current()->RunUntilIdle();
-  CheckProxyConfigs(false, false, false);
-
-  // Check that the proxy is re-enabled if a non-VPN connection is later used.
-  settings->network_interfaces_.reset(new net::NetworkInterfaceList());
-  settings->network_interfaces_->push_back(net::NetworkInterface(
-      "eth0", /* network interface name */
-      "eth0", /* network interface friendly name */
-      0,      /* interface index */
-      net::NetworkChangeNotifier::CONNECTION_WIFI,
-      net::IPAddressNumber(),
-      0,                             /* network prefix */
-      net::IP_ADDRESS_ATTRIBUTE_NONE /* ip address attribute */
-      ));
-  CheckProbeOnIPChange(kProbeURLWithOKResponse,
-                       "OK",
-                       true,
-                       false,
-                       false);
+  CheckMaybeActivateDataReductionProxy(false, true, false, false, false);
 }
 
 TEST_F(DataReductionProxySettingsTest, TestOnProxyEnabledPrefChange) {
   settings_->InitPrefMembers();
-  base::MessageLoopForUI loop;
   // The proxy is enabled initially.
-  settings_->enabled_by_user_ = true;
-  settings_->SetProxyConfigs(true, false, false, true);
+  test_context_->config()->SetStateForTest(true, false, false, true);
   // The pref is disabled, so correspondingly should be the proxy.
   CheckOnPrefChange(false, false, false);
   // The pref is enabled, so correspondingly should be the proxy.
@@ -366,7 +174,8 @@
   MockSettings* settings = static_cast<MockSettings*>(settings_.get());
   EXPECT_CALL(*settings, RecordStartupState(PROXY_ENABLED));
 
-  pref_service_.SetBoolean(prefs::kDataReductionProxyEnabled, true);
+  test_context_->pref_service()->SetBoolean(prefs::kDataReductionProxyEnabled,
+                                            true);
   CheckInitDataReductionProxy(true);
 }
 
@@ -376,7 +185,8 @@
   MockSettings* settings = static_cast<MockSettings*>(settings_.get());
   EXPECT_CALL(*settings, RecordStartupState(PROXY_DISABLED));
 
-  pref_service_.SetBoolean(prefs::kDataReductionProxyEnabled, false);
+  test_context_->pref_service()->SetBoolean(prefs::kDataReductionProxyEnabled,
+                                            false);
   CheckInitDataReductionProxy(false);
 }
 
@@ -406,35 +216,62 @@
 TEST_F(DataReductionProxySettingsTest, CheckInitMetricsWhenNotAllowed) {
   // No call to |AddProxyToCommandLine()| was made, so the proxy feature
   // should be unavailable.
-  base::MessageLoopForUI loop;
   // Clear the command line. Setting flags can force the proxy to be allowed.
   base::CommandLine::ForCurrentProcess()->InitFromArgv(0, NULL);
 
   ResetSettings(false, false, false, false, false);
   MockSettings* settings = static_cast<MockSettings*>(settings_.get());
-  EXPECT_FALSE(settings->params()->allowed());
+  EXPECT_FALSE(settings->allowed_);
   EXPECT_CALL(*settings, RecordStartupState(PROXY_NOT_AVAILABLE));
 
-  scoped_ptr<DataReductionProxyConfigurator> configurator(
-      new TestDataReductionProxyConfigurator(
-          scoped_refptr<base::TestSimpleTaskRunner>(
-              new base::TestSimpleTaskRunner()),
-          &net_log_, event_store_.get()));
-  settings_->SetProxyConfigurator(configurator.get());
-  scoped_refptr<net::TestURLRequestContextGetter> request_context =
-      new net::TestURLRequestContextGetter(base::MessageLoopProxy::current());
   settings_->InitDataReductionProxySettings(
-      &pref_service_,
-      scoped_ptr<DataReductionProxyStatisticsPrefs>(),
-      request_context.get(),
-      &net_log_,
-      event_store_.get());
+      test_context_->pref_service(), test_context_->io_data(),
+      test_context_->CreateDataReductionProxyService());
   settings_->SetOnDataReductionEnabledCallback(
       base::Bind(&DataReductionProxySettingsTestBase::
                  RegisterSyntheticFieldTrialCallback,
                  base::Unretained(this)));
 
-  base::MessageLoop::current()->RunUntilIdle();
+  test_context_->RunUntilIdle();
+}
+
+TEST_F(DataReductionProxySettingsTest, CheckQUICFieldTrials) {
+  for (int i = 0; i < 2; ++i) {
+    bool enable_quic = i == 0;
+    // No call to |AddProxyToCommandLine()| was made, so the proxy feature
+    // should be unavailable.
+    // Clear the command line. Setting flags can force the proxy to be allowed.
+    base::CommandLine::ForCurrentProcess()->InitFromArgv(0, NULL);
+
+    ResetSettings(false, false, false, false, false);
+    MockSettings* settings = static_cast<MockSettings*>(settings_.get());
+    EXPECT_FALSE(settings->Allowed());
+    EXPECT_CALL(*settings, RecordStartupState(PROXY_NOT_AVAILABLE));
+
+    settings_->InitDataReductionProxySettings(
+         test_context_->pref_service(), test_context_->io_data(),
+         test_context_->CreateDataReductionProxyService());
+
+    base::FieldTrialList field_trial_list(new BadEntropyProvider());
+    if (enable_quic) {
+      base::FieldTrialList::CreateFieldTrial(
+          DataReductionProxyParams::GetQuicFieldTrialName(),
+          "Enabled");
+    } else {
+      base::FieldTrialList::CreateFieldTrial(
+          DataReductionProxyParams::GetQuicFieldTrialName(),
+          "Disabled");
+    }
+    test_context_->config()->params()->EnableQuic(enable_quic);
+
+    settings_->SetOnDataReductionEnabledCallback(
+        base::Bind(&DataReductionProxySettingsTestBase::
+                   RegisterSyntheticFieldTrialCallback,
+                   base::Unretained(this)));
+
+    EXPECT_EQ(enable_quic,
+              test_context_->config()->params()->origin().is_quic()) << i;
+  }
 }
 
 }  // namespace data_reduction_proxy
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.cc
new file mode 100644
index 0000000..cb081ace
--- /dev/null
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.cc
@@ -0,0 +1,156 @@
+// 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 "components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.h"
+
+#include "base/message_loop/message_loop.h"
+#include "base/prefs/testing_pref_service.h"
+#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_config_test_utils.h"
+#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_configurator_test_utils.h"
+#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_statistics_prefs.h"
+#include "components/data_reduction_proxy/core/common/data_reduction_proxy_event_store.h"
+#include "components/data_reduction_proxy/core/common/data_reduction_proxy_params.h"
+#include "components/data_reduction_proxy/core/common/data_reduction_proxy_params_test_utils.h"
+#include "net/url_request/url_request_test_util.h"
+
+namespace data_reduction_proxy {
+
+MockDataReductionProxyService::MockDataReductionProxyService(
+    scoped_ptr<DataReductionProxyStatisticsPrefs> statistics_prefs,
+    DataReductionProxySettings* settings,
+    net::URLRequestContextGetter* request_context)
+    : DataReductionProxyService(
+        statistics_prefs.Pass(), settings, request_context) {
+}
+
+MockDataReductionProxyService::~MockDataReductionProxyService() {
+}
+
+TestDataReductionProxyIOData::TestDataReductionProxyIOData(
+    scoped_refptr<base::SingleThreadTaskRunner> task_runner,
+    scoped_ptr<TestDataReductionProxyConfig> config,
+    scoped_ptr<DataReductionProxyEventStore> event_store,
+    scoped_ptr<DataReductionProxyRequestOptions> request_options,
+    scoped_ptr<DataReductionProxyConfigurator> configurator)
+    : DataReductionProxyIOData() {
+  config_ = config.Pass();
+  event_store_ = event_store.Pass();
+  request_options_ = request_options.Pass();
+  configurator_ = configurator.Pass();
+  io_task_runner_ = task_runner;
+  ui_task_runner_ = task_runner;
+}
+
+TestDataReductionProxyIOData::~TestDataReductionProxyIOData() {
+  shutdown_on_ui_ = true;
+}
+
+DataReductionProxyTestContext::DataReductionProxyTestContext(
+    int params_flags,
+    unsigned int params_definitions,
+    unsigned int test_context_flags)
+    : test_context_flags_(test_context_flags),
+      task_runner_(base::MessageLoopProxy::current()),
+      request_context_(scoped_refptr<net::URLRequestContextGetter>(
+          new net::TestURLRequestContextGetter(task_runner_))),
+      service_(nullptr) {
+  scoped_ptr<DataReductionProxyEventStore> event_store =
+      make_scoped_ptr(new DataReductionProxyEventStore(task_runner_));
+  scoped_ptr<DataReductionProxyConfigurator> configurator;
+  if (test_context_flags_ &
+      DataReductionProxyTestContext::USE_TEST_CONFIGURATOR) {
+    configurator = make_scoped_ptr(new TestDataReductionProxyConfigurator(
+        task_runner_, &net_log_, event_store.get()));
+  } else {
+    configurator = make_scoped_ptr(new DataReductionProxyConfigurator(
+        task_runner_, &net_log_, event_store.get()));
+  }
+
+  scoped_ptr<TestDataReductionProxyConfig> config;
+  if (test_context_flags_ & DataReductionProxyTestContext::USE_MOCK_CONFIG) {
+    config.reset(new MockDataReductionProxyConfig(
+        params_flags, params_definitions, task_runner_, &net_log_,
+        configurator.get(), event_store.get()));
+  } else {
+    config.reset(new TestDataReductionProxyConfig(
+        params_flags, params_definitions, task_runner_, &net_log_,
+        configurator.get(), event_store.get()));
+  }
+  scoped_ptr<DataReductionProxyRequestOptions> request_options =
+      make_scoped_ptr(new DataReductionProxyRequestOptions(
+          Client::UNKNOWN, config->params(), task_runner_));
+  settings_.reset(new DataReductionProxySettings());
+
+  io_data_.reset(new TestDataReductionProxyIOData(
+      task_runner_.get(), config.Pass(), event_store.Pass(),
+      request_options.Pass(), configurator.Pass()));
+  if (!(test_context_flags_ &
+        DataReductionProxyTestContext::SKIP_SETTINGS_INITIALIZATION)) {
+    scoped_ptr<DataReductionProxyStatisticsPrefs> statistics_prefs =
+        make_scoped_ptr(new DataReductionProxyStatisticsPrefs(
+            &simple_pref_service_, task_runner_, base::TimeDelta()));
+    scoped_ptr<MockDataReductionProxyService> data_reduction_proxy_service =
+        make_scoped_ptr(new MockDataReductionProxyService(
+            statistics_prefs.Pass(), settings_.get(), request_context_.get()));
+    service_ = data_reduction_proxy_service.get();
+    settings_->data_reduction_proxy_service_ =
+        data_reduction_proxy_service.Pass();
+    io_data_->SetDataReductionProxyService(service_->GetWeakPtr());
+  }
+
+  settings_->config_ = io_data_->config();
+}
+
+DataReductionProxyTestContext::~DataReductionProxyTestContext() {
+}
+
+void DataReductionProxyTestContext::RunUntilIdle() {
+  base::MessageLoop::current()->RunUntilIdle();
+}
+
+void DataReductionProxyTestContext::InitSettings() {
+  DCHECK(test_context_flags_ &
+         DataReductionProxyTestContext::SKIP_SETTINGS_INITIALIZATION);
+  settings_->InitDataReductionProxySettings(
+      &simple_pref_service_, io_data_.get(), CreateDataReductionProxyService());
+  io_data_->SetDataReductionProxyService(
+      settings_->data_reduction_proxy_service()->GetWeakPtr());
+}
+
+scoped_ptr<MockDataReductionProxyService>
+DataReductionProxyTestContext::CreateDataReductionProxyService() {
+  DCHECK(test_context_flags_ &
+         DataReductionProxyTestContext::SKIP_SETTINGS_INITIALIZATION);
+  scoped_ptr<DataReductionProxyStatisticsPrefs> statistics_prefs =
+      make_scoped_ptr(new DataReductionProxyStatisticsPrefs(
+          &simple_pref_service_, task_runner_, base::TimeDelta()));
+  scoped_ptr<MockDataReductionProxyService> service =
+      make_scoped_ptr(new MockDataReductionProxyService(
+          statistics_prefs.Pass(), settings_.get(), request_context_.get()));
+  return service.Pass();
+}
+
+TestDataReductionProxyConfigurator*
+DataReductionProxyTestContext::test_configurator() const {
+  DCHECK(test_context_flags_ &
+         DataReductionProxyTestContext::USE_TEST_CONFIGURATOR);
+  return reinterpret_cast<TestDataReductionProxyConfigurator*>(
+      io_data_->configurator());
+}
+
+MockDataReductionProxyConfig* DataReductionProxyTestContext::mock_config()
+    const {
+  DCHECK(test_context_flags_ & DataReductionProxyTestContext::USE_MOCK_CONFIG);
+  return reinterpret_cast<MockDataReductionProxyConfig*>(io_data_->config());
+}
+
+MockDataReductionProxyService*
+DataReductionProxyTestContext::data_reduction_proxy_service()
+    const {
+  DCHECK(!(test_context_flags_ &
+           DataReductionProxyTestContext::SKIP_SETTINGS_INITIALIZATION));
+  return service_;
+}
+
+}  // namespace data_reduction_proxy
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.h b/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.h
new file mode 100644
index 0000000..3c0b30e2
--- /dev/null
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.h
@@ -0,0 +1,179 @@
+// 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 COMPONENTS_DATA_REDUCTION_PROXY_CORE_BROWSER_DATA_REDUCTION_PROXY_TEST_UTILS_H_
+#define COMPONENTS_DATA_REDUCTION_PROXY_CORE_BROWSER_DATA_REDUCTION_PROXY_TEST_UTILS_H_
+
+#include "base/macros.h"
+#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.h"
+#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_service.h"
+#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_settings_test_utils.h"
+#include "net/base/capturing_net_log.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+class TestingPrefServiceSimple;
+
+namespace base {
+class MessageLoopForUI;
+class SingleThreadTaskRunner;
+}
+
+namespace net {
+class NetLog;
+class URLRequestContextGetter;
+}
+
+namespace data_reduction_proxy {
+
+class DataReductionProxyConfigurator;
+class DataReductionProxyEventStore;
+class DataReductionProxyRequestOptions;
+class DataReductionProxySettings;
+class DataReductionProxyStatisticsPrefs;
+class MockDataReductionProxyConfig;
+class TestDataReductionProxyConfig;
+class TestDataReductionProxyConfigurator;
+
+// Test version of |DataReductionProxyService|, which permits mocking of various
+// methods.
+class MockDataReductionProxyService : public DataReductionProxyService {
+ public:
+  MockDataReductionProxyService(
+      scoped_ptr<DataReductionProxyStatisticsPrefs> statistics_prefs,
+      DataReductionProxySettings* settings,
+      net::URLRequestContextGetter* request_context);
+  ~MockDataReductionProxyService() override;
+
+  MOCK_METHOD2(CheckProbeURL,
+      void(const GURL& probe_url, FetcherResponseCallback fetcher_callback));
+};
+
+// Test version of |DataReductionProxyIOData|, which bypasses initialization in
+// the constructor in favor of explicitly passing in its owned classes. This
+// permits the use of test/mock versions of those classes.
+class TestDataReductionProxyIOData : public DataReductionProxyIOData {
+ public:
+  TestDataReductionProxyIOData(
+      scoped_refptr<base::SingleThreadTaskRunner> task_runner,
+      scoped_ptr<TestDataReductionProxyConfig> config,
+      scoped_ptr<DataReductionProxyEventStore> event_store,
+      scoped_ptr<DataReductionProxyRequestOptions> request_options,
+      scoped_ptr<DataReductionProxyConfigurator> configurator);
+  ~TestDataReductionProxyIOData() override;
+
+  DataReductionProxyConfigurator* configurator() const {
+    return configurator_.get();
+  }
+};
+
+// Builds a test version of the Data Reduction Proxy stack for use in tests.
+// Takes in various |TestContextOptions| which controls the behavior of the
+// underlying objects.
+class DataReductionProxyTestContext {
+ public:
+  static const unsigned int DEFAULT_TEST_CONTEXT_OPTIONS = 0;
+
+  enum TestContextOptions {
+    // Permits mocking of the underlying |DataReductionProxyConfig|.
+    USE_MOCK_CONFIG = 0x1,
+    // Uses a |TestDataReductionProxyConfigurator| to record proxy configuration
+    // changes.
+    USE_TEST_CONFIGURATOR = 0x2,
+    // Construct, but do not initialize the |DataReductionProxySettings| object.
+    // Primarily used for testing of the |DataReductionProxySettings| object
+    // itself.
+    SKIP_SETTINGS_INITIALIZATION = 0x4,
+  };
+
+  // Creates a new DataReductionProxyTestContext. |params_flags| controls what
+  // is enabled in the underlying |DataReductionProxyParams|.
+  // |params_definitions| is used to control the HasNames enum for the
+  // underlying |TestDataReductionProxyParams|. |test_context_flags| is the
+  // |TestContextOptions| enum to control what underlying objects are created.
+  explicit DataReductionProxyTestContext(int params_flags,
+                                         unsigned int params_definitions,
+                                         unsigned int test_context_flags);
+
+  virtual ~DataReductionProxyTestContext();
+
+  // Waits while executing all tasks on the current SingleThreadTaskRunner.
+  void RunUntilIdle();
+
+  // Initializes the |DataReductionProxySettings| object. Can only be called if
+  // |SKIP_SETTINGS_INITIALIZATION| was specified.
+  void InitSettings();
+
+  // Creates a |MockDataReductionProxyService| object. Can only be called if
+  // |SKIP_SETTINGS_INITIALIZATION| was specified.
+  scoped_ptr<MockDataReductionProxyService> CreateDataReductionProxyService();
+
+  // Returns the underlying |TestDataReductionProxyConfigurator|. This can only
+  // be called if |USE_TEST_CONFIGURATOR| was specified.
+  TestDataReductionProxyConfigurator* test_configurator() const;
+
+  // Returns the underlying |MockDataReductionProxyConfig|. This can only be
+  // called if |USE_MOCK_CONFIG| was specified.
+  MockDataReductionProxyConfig* mock_config() const;
+
+  // Returns the underlying |MockDataReductionProxyService|. This can only
+  // be called if |SKIP_SETTINGS_INITIALIZATION| was not specified.
+  MockDataReductionProxyService* data_reduction_proxy_service() const;
+
+  scoped_refptr<base::SingleThreadTaskRunner> task_runner() const {
+    return task_runner_;
+  }
+
+  TestingPrefServiceSimple* pref_service() {
+    return &simple_pref_service_;
+  }
+
+  net::NetLog* net_log() {
+    return &net_log_;
+  }
+
+  net::URLRequestContextGetter* request_context() const {
+    return request_context_.get();
+  }
+
+  DataReductionProxyEventStore* event_store() const {
+    return io_data_->event_store();
+  }
+
+  DataReductionProxyConfigurator* configurator() const {
+    return io_data_->configurator();
+  }
+
+  TestDataReductionProxyConfig* config() const {
+    return reinterpret_cast<TestDataReductionProxyConfig*>(io_data_->config());
+  }
+
+  TestDataReductionProxyIOData* io_data() const {
+    return io_data_.get();
+  }
+
+  DataReductionProxySettings* settings() const {
+    return settings_.get();
+  }
+
+ private:
+  unsigned int test_context_flags_;
+
+  base::MessageLoopForIO loop_;
+
+  scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+  TestingPrefServiceSimple simple_pref_service_;
+  net::CapturingNetLog net_log_;
+  scoped_refptr<net::URLRequestContextGetter> request_context_;
+
+  scoped_ptr<TestDataReductionProxyIOData> io_data_;
+  scoped_ptr<DataReductionProxySettings> settings_;
+
+  MockDataReductionProxyService* service_;
+
+  DISALLOW_COPY_AND_ASSIGN(DataReductionProxyTestContext);
+};
+
+}  // namespace data_reduction_proxy
+
+#endif  // COMPONENTS_DATA_REDUCTION_PROXY_CORE_BROWSER_DATA_REDUCTION_PROXY_TEST_UTILS_H_
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_usage_stats.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_usage_stats.cc
index ef14fbd..4fb9547c 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_usage_stats.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_usage_stats.cc
@@ -10,6 +10,7 @@
 #include "base/metrics/sparse_histogram.h"
 #include "base/prefs/pref_member.h"
 #include "base/single_thread_task_runner.h"
+#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_service.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_tamper_detection.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_headers.h"
@@ -104,10 +105,10 @@
 
 DataReductionProxyUsageStats::DataReductionProxyUsageStats(
     DataReductionProxyParams* params,
-    DataReductionProxySettings* settings,
+    base::WeakPtr<DataReductionProxyService> service,
     const scoped_refptr<base::SingleThreadTaskRunner>& ui_task_runner)
     : data_reduction_proxy_params_(params),
-      settings_(settings),
+      service_(service),
       last_bypass_type_(BYPASS_EVENT_TYPE_MAX),
       triggering_request_(true),
       ui_task_runner_(ui_task_runner),
@@ -115,7 +116,6 @@
       proxy_net_errors_count_(0),
       unavailable_(false) {
   DCHECK(params);
-  DCHECK(settings);
   NetworkChangeNotifier::AddNetworkChangeObserver(this);
 };
 
@@ -386,7 +386,8 @@
 void DataReductionProxyUsageStats::NotifyUnavailabilityOnUIThread(
     bool unavailable) {
   DCHECK(ui_task_runner_->BelongsToCurrentThread());
-  settings_->SetUnreachable(unavailable);
+  if (service_)
+    service_->settings()->SetUnreachable(unavailable);
 }
 
 void DataReductionProxyUsageStats::RecordBypassedBytes(
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_usage_stats.h b/components/data_reduction_proxy/core/browser/data_reduction_proxy_usage_stats.h
index 5e159e7e..8636d693 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_usage_stats.h
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_usage_stats.h
@@ -6,6 +6,7 @@
 #define COMPONENTS_DATA_REDUCTION_PROXY_CORE_BROWSER_DATA_REDUCTION_PROXY_USAGE_STATS_H_
 
 #include "base/callback.h"
+#include "base/memory/weak_ptr.h"
 #include "base/prefs/pref_member.h"
 #include "base/single_thread_task_runner.h"
 #include "base/threading/thread_checker.h"
@@ -23,7 +24,7 @@
 
 namespace data_reduction_proxy {
 
-class DataReductionProxySettings;
+class DataReductionProxyService;
 
 // TODO(bengr): Rename as DataReductionProxyBypassStats.
 class DataReductionProxyUsageStats
@@ -45,13 +46,12 @@
       bool is_primary,
       const net::HttpResponseHeaders* headers);
 
-  // |params| outlives this class instance. |settings| provides a hook to inform
+  // |params| outlives this class instance. |service| provides a hook to inform
   // the user that the Data Reduction Proxy is unreachable, which occurs on the
-  // UI thread, hence the |ui_task_runner|. |settings| and |params| must not be
-  // null.
+  // UI thread, hence the |ui_task_runner|. |params| must not be null.
   DataReductionProxyUsageStats(
       DataReductionProxyParams* params,
-      DataReductionProxySettings* settings,
+      base::WeakPtr<DataReductionProxyService> service,
       const scoped_refptr<base::SingleThreadTaskRunner>& ui_task_runner);
 
   ~DataReductionProxyUsageStats() override;
@@ -137,7 +137,7 @@
 
   DataReductionProxyParams* data_reduction_proxy_params_;
 
-  DataReductionProxySettings* settings_;
+  base::WeakPtr<DataReductionProxyService> service_;
 
   // The last reason for bypass as determined by
   // MaybeBypassProxyAndPrepareToRetry
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_usage_stats_unittest.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_usage_stats_unittest.cc
index 3da049e..8683a27 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_usage_stats_unittest.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_usage_stats_unittest.cc
@@ -13,13 +13,15 @@
 #include "base/metrics/histogram.h"
 #include "base/prefs/testing_pref_service.h"
 #include "base/test/histogram_tester.h"
+#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_config_test_utils.h"
+#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_configurator.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_network_delegate.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_prefs.h"
-#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.h"
-#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_statistics_prefs.h"
+#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_headers_test_utils.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_params_test_utils.h"
+#include "components/data_reduction_proxy/core/common/data_reduction_proxy_pref_names.h"
 #include "net/base/host_port_pair.h"
 #include "net/base/load_flags.h"
 #include "net/base/net_errors.h"
@@ -77,8 +79,7 @@
 class DataReductionProxyUsageStatsTest : public testing::Test {
  public:
   DataReductionProxyUsageStatsTest()
-      : loop_proxy_(base::MessageLoopProxy::current().get()),
-        context_(true) {
+      : context_(true) {
     context_.Init();
 
     // The |test_job_factory_| takes ownership of the interceptor.
@@ -88,11 +89,17 @@
 
     context_.set_job_factory(&test_job_factory_);
 
+    test_context_.reset(
+        new DataReductionProxyTestContext(
+            DataReductionProxyParams::kAllowed |
+                DataReductionProxyParams::kFallbackAllowed |
+                DataReductionProxyParams::kPromoAllowed,
+            TestDataReductionProxyParams::HAS_EVERYTHING &
+                ~TestDataReductionProxyParams::HAS_DEV_ORIGIN &
+                ~TestDataReductionProxyParams::HAS_DEV_FALLBACK_ORIGIN,
+            DataReductionProxyTestContext::DEFAULT_TEST_CONTEXT_OPTIONS));
     mock_url_request_ = context_.CreateRequest(GURL(), net::IDLE, &delegate_,
                                                NULL);
-    settings_.reset(new DataReductionProxySettings(
-        scoped_ptr<DataReductionProxyParamsMock>(
-            new DataReductionProxyParamsMock()).Pass()));
   }
 
   scoped_ptr<net::URLRequest> CreateURLRequestWithResponseHeaders(
@@ -111,20 +118,16 @@
     // Configure the interceptor to use the test job to handle the next request.
     test_job_interceptor_->set_main_intercept_job(test_job.get());
     fake_request->Start();
-    base::MessageLoop::current()->RunUntilIdle();
+    test_context_->RunUntilIdle();
 
     EXPECT_TRUE(fake_request->response_headers() != NULL);
     return fake_request.Pass();
   }
 
   bool IsUnreachable() const {
-    return settings_->IsDataReductionProxyUnreachable();
+    return test_context_->settings()->IsDataReductionProxyUnreachable();
   }
 
-  // Required for base::MessageLoopProxy::current().
-  base::MessageLoopForUI loop_;
-  base::MessageLoopProxy* loop_proxy_;
-
  protected:
   net::TestURLRequestContext context_;
   net::TestDelegate delegate_;
@@ -133,7 +136,7 @@
   // |test_job_interceptor_| is owned by |test_job_factory_|.
   net::TestJobInterceptor* test_job_interceptor_;
   net::URLRequestJobFactoryImpl test_job_factory_;
-  scoped_ptr<DataReductionProxySettings> settings_;
+  scoped_ptr<DataReductionProxyTestContext> test_context_;
 };
 
 TEST_F(DataReductionProxyUsageStatsTest, IsDataReductionProxyUnreachable) {
@@ -178,13 +181,15 @@
         .WillRepeatedly(testing::Return(test_case.was_proxy_used));
 
     scoped_ptr<DataReductionProxyUsageStats> usage_stats(
-        new DataReductionProxyUsageStats(&mock_params_, settings_.get(),
-                                         loop_proxy_));
+        new DataReductionProxyUsageStats(
+            &mock_params_,
+            test_context_->data_reduction_proxy_service()->GetWeakPtr(),
+            test_context_->task_runner()));
 
     usage_stats->OnProxyFallback(fallback_proxy_server,
                                  net::ERR_PROXY_CONNECTION_FAILED);
     usage_stats->OnUrlRequestCompleted(mock_url_request_.get(), false);
-    base::MessageLoop::current()->RunUntilIdle();
+    test_context_->RunUntilIdle();
 
     EXPECT_EQ(test_case.is_unreachable, IsUnreachable());
   }
@@ -194,8 +199,10 @@
   net::ProxyServer fallback_proxy_server =
       net::ProxyServer::FromURI("foo.com", net::ProxyServer::SCHEME_HTTP);
   scoped_ptr<DataReductionProxyUsageStats> usage_stats(
-      new DataReductionProxyUsageStats(&mock_params_, settings_.get(),
-                                       loop_proxy_));
+      new DataReductionProxyUsageStats(
+          &mock_params_,
+          test_context_->data_reduction_proxy_service()->GetWeakPtr(),
+          test_context_->task_runner()));
   EXPECT_CALL(mock_params_, IsDataReductionProxy(testing::_, testing::_))
       .WillOnce(testing::Return(true));
   EXPECT_CALL(mock_params_,
@@ -205,12 +212,12 @@
   // proxy falls back
   usage_stats->OnProxyFallback(fallback_proxy_server,
                                net::ERR_PROXY_CONNECTION_FAILED);
-  base::MessageLoop::current()->RunUntilIdle();
+  test_context_->RunUntilIdle();
   EXPECT_TRUE(IsUnreachable());
 
   // proxy succeeds
   usage_stats->OnUrlRequestCompleted(mock_url_request_.get(), false);
-  base::MessageLoop::current()->RunUntilIdle();
+  test_context_->RunUntilIdle();
   EXPECT_FALSE(IsUnreachable());
 }
 
@@ -218,8 +225,10 @@
   net::ProxyServer fallback_proxy_server =
       net::ProxyServer::FromURI("foo.com", net::ProxyServer::SCHEME_HTTP);
   scoped_ptr<DataReductionProxyUsageStats> usage_stats(
-      new DataReductionProxyUsageStats(&mock_params_, settings_.get(),
-                                       loop_proxy_));
+      new DataReductionProxyUsageStats(
+          &mock_params_,
+          test_context_->data_reduction_proxy_service()->GetWeakPtr(),
+          test_context_->task_runner()));
   EXPECT_CALL(mock_params_,
               WasDataReductionProxyUsed(mock_url_request_.get(), testing::_))
       .WillOnce(testing::Return(true));
@@ -228,7 +237,7 @@
 
   // Proxy succeeds.
   usage_stats->OnUrlRequestCompleted(mock_url_request_.get(), false);
-  base::MessageLoop::current()->RunUntilIdle();
+  test_context_->RunUntilIdle();
   EXPECT_FALSE(IsUnreachable());
 
   // Then proxy falls back indefinitely.
@@ -240,7 +249,7 @@
                                net::ERR_PROXY_CONNECTION_FAILED);
   usage_stats->OnProxyFallback(fallback_proxy_server,
                                net::ERR_PROXY_CONNECTION_FAILED);
-  base::MessageLoop::current()->RunUntilIdle();
+  test_context_->RunUntilIdle();
   EXPECT_TRUE(IsUnreachable());
 }
 
@@ -423,8 +432,10 @@
   for (size_t i = 0; i < arraysize(test_cases); ++i) {
     base::HistogramTester histogram_tester;
     scoped_ptr<DataReductionProxyUsageStats> usage_stats(
-        new DataReductionProxyUsageStats(&mock_params_, settings_.get(),
-                                         loop_proxy_));
+        new DataReductionProxyUsageStats(
+            &mock_params_,
+            test_context_->data_reduction_proxy_service()->GetWeakPtr(),
+            test_context_->task_runner()));
 
     std::string raw_headers(test_cases[i].headers);
     HeadersToRaw(&raw_headers);
@@ -492,8 +503,10 @@
   for (size_t i = 0; i < arraysize(test_cases); ++i) {
     base::HistogramTester histogram_tester;
     scoped_ptr<DataReductionProxyUsageStats> usage_stats(
-        new DataReductionProxyUsageStats(&mock_params_, settings_.get(),
-                                         loop_proxy_));
+        new DataReductionProxyUsageStats(
+            &mock_params_,
+            test_context_->data_reduction_proxy_service()->GetWeakPtr(),
+            test_context_->task_runner()));
 
     std::string raw_headers("HTTP/1.1 200 OK\n"
                             "Via: 1.1 Chrome-Compression-Proxy\n");
@@ -562,8 +575,8 @@
       : context_(true) {}
 
   ~DataReductionProxyUsageStatsEndToEndTest() override {
-    io_data_->ShutdownOnUIThread();
-    base::MessageLoop::current()->RunUntilIdle();
+    test_context_->io_data()->ShutdownOnUIThread();
+    test_context_->RunUntilIdle();
   }
 
   void SetUp() override {
@@ -571,34 +584,21 @@
     // test bypassed bytes due to proxy fallbacks. This way, a test just needs
     // to cause one proxy fallback in order for the data reduction proxy to be
     // fully bypassed.
-    settings_.reset(new DataReductionProxySettings(
-        scoped_ptr<DataReductionProxyParams>(new TestDataReductionProxyParams(
-            TestDataReductionProxyParams::kAllowed,
-            TestDataReductionProxyParams::HAS_ORIGIN))));
-    RegisterSimpleProfilePrefs(simple_prefs_.registry());
+    test_context_.reset(new DataReductionProxyTestContext(
+        DataReductionProxyParams::kAllowed,
+        TestDataReductionProxyParams::HAS_ORIGIN,
+        DataReductionProxyTestContext::SKIP_SETTINGS_INITIALIZATION));
+    TestingPrefServiceSimple* simple_prefs = test_context_->pref_service();
+    RegisterSimpleProfilePrefs(simple_prefs->registry());
 
     BooleanPrefMember enabled;
-    enabled.Init(prefs::kDataReductionProxyEnabled, &simple_prefs_);
+    enabled.Init(prefs::kDataReductionProxyEnabled, simple_prefs);
     enabled.SetValue(true);
     enabled.Destroy();
 
-    scoped_ptr<DataReductionProxyStatisticsPrefs> statistics_prefs(
-        new DataReductionProxyStatisticsPrefs(
-            &simple_prefs_, loop_.message_loop_proxy(), base::TimeDelta()));
-    io_data_.reset(new DataReductionProxyIOData(
-        Client::UNKNOWN, statistics_prefs.Pass(), settings_.get(), &net_log_,
-        loop_.message_loop_proxy(), loop_.message_loop_proxy()));
+    test_context_->InitSettings();
 
-    settings_->SetProxyConfigurator(io_data_->configurator());
-    context_getter_ = new net::TrivialURLRequestContextGetter(
-        &context_, loop_.message_loop_proxy());
-    settings_->InitDataReductionProxySettings(
-        &simple_prefs_, io_data_->PassStatisticsPrefs(), context_getter_.get(),
-        &net_log_, io_data_->event_store());
-    io_data_->SetDataReductionProxyStatisticsPrefs(
-        settings_->statistics_prefs());
-
-    network_delegate_ = io_data_->CreateNetworkDelegate(
+    network_delegate_ = test_context_->io_data()->CreateNetworkDelegate(
         scoped_ptr<net::NetworkDelegate>(new net::TestNetworkDelegate()), true);
     context_.set_network_delegate(network_delegate_.get());
 
@@ -607,14 +607,14 @@
     job_factory_.reset(new net::URLRequestInterceptingJobFactory(
         scoped_ptr<net::URLRequestJobFactory>(
             new net::URLRequestJobFactoryImpl()),
-        io_data_->CreateInterceptor().Pass()));
+        test_context_->io_data()->CreateInterceptor().Pass()));
     context_.set_job_factory(job_factory_.get());
 
-    io_data_->InitOnUIThread(&simple_prefs_);
-    io_data_->configurator()->Enable(false, true,
-                                     settings_->params()->origin().ToURI(),
-                                     std::string(), std::string());
-    base::MessageLoop::current()->RunUntilIdle();
+    test_context_->io_data()->InitOnUIThread(simple_prefs);
+    test_context_->configurator()->Enable(false, true,
+                                          params()->origin().ToURI(),
+                                          std::string(), std::string());
+    test_context_->RunUntilIdle();
   }
 
   // Create and execute a fake request using the data reduction proxy stack.
@@ -662,7 +662,7 @@
     request->set_method("GET");
     request->SetLoadFlags(net::LOAD_NORMAL);
     request->Start();
-    base::MessageLoop::current()->RunUntilIdle();
+    test_context_->RunUntilIdle();
   }
 
   void set_proxy_service(net::ProxyService* proxy_service) {
@@ -674,7 +674,11 @@
   }
 
   const DataReductionProxySettings* settings() const {
-    return settings_.get();
+    return test_context_->settings();
+  }
+
+  const DataReductionProxyParams* params() const {
+    return test_context_->config()->test_params();
   }
 
   void ClearBadProxies() {
@@ -739,17 +743,12 @@
   }
 
  private:
-  base::MessageLoopForIO loop_;
   net::TestDelegate delegate_;
-  net::NetLog net_log_;
-  TestingPrefServiceSimple simple_prefs_;
   net::MockClientSocketFactory mock_socket_factory_;
-  scoped_ptr<DataReductionProxyIOData> io_data_;
   scoped_ptr<DataReductionProxyNetworkDelegate> network_delegate_;
   scoped_ptr<net::URLRequestJobFactory> job_factory_;
-  scoped_ptr<DataReductionProxySettings> settings_;
   net::TestURLRequestContext context_;
-  scoped_refptr<net::URLRequestContextGetter> context_getter_;
+  scoped_ptr<DataReductionProxyTestContext> test_context_;
 };
 
 TEST_F(DataReductionProxyUsageStatsEndToEndTest, BypassedBytesNoRetry) {
@@ -942,7 +941,7 @@
   // Make the data reduction proxy host fail to resolve.
   scoped_ptr<net::MockHostResolver> host_resolver(new net::MockHostResolver());
   host_resolver->rules()->AddSimulatedFailure(
-      settings()->params()->origin().host_port_pair().host());
+      params()->origin().host_port_pair().host());
   set_host_resolver(host_resolver.get());
   InitializeContext();
 
diff --git a/components/data_reduction_proxy/core/common/data_reduction_proxy_params.cc b/components/data_reduction_proxy/core/common/data_reduction_proxy_params.cc
index 9bbec29..b3422c8 100644
--- a/components/data_reduction_proxy/core/common/data_reduction_proxy_params.cc
+++ b/components/data_reduction_proxy/core/common/data_reduction_proxy_params.cc
@@ -30,7 +30,8 @@
 namespace {
 
 const char kEnabled[] = "Enabled";
-const char kDefaultOrigin[] = "https://proxy.googlezip.net:443";
+const char kDefaultSpdyOrigin[] = "https://proxy.googlezip.net:443";
+const char kDefaultQuicOrigin[] = "quic://proxy.googlezip.net:443";
 const char kDevOrigin[] = "https://proxy-dev.googlezip.net:443";
 const char kDevFallbackOrigin[] = "proxy-dev.googlezip.net:80";
 const char kDefaultFallbackOrigin[] = "compress.googlezip.net:80";
@@ -44,6 +45,8 @@
 const char kDefaultWarmupUrl[] = "http://www.gstatic.com/generate_204";
 
 const char kAndroidOneIdentifier[] = "sprout";
+
+const char kQuicFieldTrial[] = "DataReductionProxyUseQuic";
 }  // namespace
 
 namespace data_reduction_proxy {
@@ -120,6 +123,24 @@
   return url.SchemeIs(url::kHttpScheme);
 }
 
+// static
+bool DataReductionProxyParams::IsIncludedInQuicFieldTrial() {
+  return FieldTrialList::FindFullName(kQuicFieldTrial) == kEnabled;
+}
+
+// static
+std::string DataReductionProxyParams::GetQuicFieldTrialName() {
+  return kQuicFieldTrial;
+}
+
+void DataReductionProxyParams::EnableQuic(bool enable) {
+  quic_enabled_ = enable;
+  DCHECK(!quic_enabled_ || IsIncludedInQuicFieldTrial());
+  if (override_quic_origin_.empty() && quic_enabled_)
+    origin_ = net::ProxyServer::FromURI(kDefaultQuicOrigin,
+                                        net::ProxyServer::SCHEME_HTTP);
+}
+
 DataReductionProxyTypeInfo::DataReductionProxyTypeInfo()
     : proxy_servers(),
       is_fallback(false),
@@ -138,6 +159,7 @@
           (flags & kAlternativeFallbackAllowed) == kAlternativeFallbackAllowed),
       promo_allowed_((flags & kPromoAllowed) == kPromoAllowed),
       holdback_((flags & kHoldback) == kHoldback),
+      quic_enabled_(false),
       configured_on_command_line_(false) {
   bool result = Init(
       allowed_, fallback_allowed_, alt_allowed_, alt_fallback_allowed_);
@@ -164,6 +186,8 @@
       alt_fallback_allowed_(other.alt_fallback_allowed_),
       promo_allowed_(other.promo_allowed_),
       holdback_(other.holdback_),
+      quic_enabled_(other.quic_enabled_),
+      override_quic_origin_(other.override_quic_origin_),
       configured_on_command_line_(other.configured_on_command_line_) {
 }
 
@@ -196,6 +220,7 @@
           (flags & kAlternativeFallbackAllowed) == kAlternativeFallbackAllowed),
       promo_allowed_((flags & kPromoAllowed) == kPromoAllowed),
       holdback_((flags & kHoldback) == kHoldback),
+      quic_enabled_(false),
       configured_on_command_line_(false) {
   if (should_call_init) {
     bool result = Init(
@@ -314,6 +339,7 @@
   // command line.
   if (origin.empty())
     origin = GetDefaultDevOrigin();
+  override_quic_origin_ = origin;
   if (origin.empty())
     origin = GetDefaultOrigin();
   if (fallback_origin.empty())
@@ -542,7 +568,8 @@
 
 // TODO(kundaji): Remove tests for macro definitions.
 std::string DataReductionProxyParams::GetDefaultOrigin() const {
-  return kDefaultOrigin;
+  return quic_enabled_ ?
+      kDefaultQuicOrigin : kDefaultSpdyOrigin;
 }
 
 std::string DataReductionProxyParams::GetDefaultFallbackOrigin() const {
diff --git a/components/data_reduction_proxy/core/common/data_reduction_proxy_params.h b/components/data_reduction_proxy/core/common/data_reduction_proxy_params.h
index 98676a7..da4905b 100644
--- a/components/data_reduction_proxy/core/common/data_reduction_proxy_params.h
+++ b/components/data_reduction_proxy/core/common/data_reduction_proxy_params.h
@@ -111,6 +111,15 @@
   // provided |url|.
   static bool CanProxyURLScheme(const GURL& url);
 
+  // Returns true if this client is part of a field trial that sets the origin
+  // proxy server as quic://proxy.googlezip.net.
+  static bool IsIncludedInQuicFieldTrial();
+
+  static std::string GetQuicFieldTrialName();
+
+  // If true, uses QUIC instead of SPDY to connect to proxies that use TLS.
+  void EnableQuic(bool enable);
+
   // Constructs configuration parameters. If |kAllowed|, then the standard
   // data reduction proxy configuration is allowed to be used. If
   // |kfallbackAllowed| a fallback proxy can be used if the primary proxy is
@@ -333,6 +342,8 @@
   bool alt_fallback_allowed_;
   bool promo_allowed_;
   bool holdback_;
+  bool quic_enabled_;
+  std::string override_quic_origin_;
 
   bool configured_on_command_line_;
 };
diff --git a/components/data_reduction_proxy/core/common/data_reduction_proxy_switches.cc b/components/data_reduction_proxy/core/common/data_reduction_proxy_switches.cc
index 8132901..33f7df4 100644
--- a/components/data_reduction_proxy/core/common/data_reduction_proxy_switches.cc
+++ b/components/data_reduction_proxy/core/common/data_reduction_proxy_switches.cc
@@ -20,6 +20,11 @@
 // The origin of the data reduction proxy dev.
 const char kDataReductionProxyDev[]      = "spdy-proxy-dev-auth-origin";
 
+// The name of a Data Reduction Proxy experiment to run. These experiments are
+// defined by the proxy server. Use --force-fieldtrials for Data Reduction
+// Proxy field trials.
+const char kDataReductionProxyExperiment[] = "data-reduction-proxy-experiment";
+
 // The origin of the data reduction proxy fallback.
 const char kDataReductionProxyFallback[] = "spdy-proxy-auth-fallback";
 
diff --git a/components/data_reduction_proxy/core/common/data_reduction_proxy_switches.h b/components/data_reduction_proxy/core/common/data_reduction_proxy_switches.h
index d3520040..6f682b2 100644
--- a/components/data_reduction_proxy/core/common/data_reduction_proxy_switches.h
+++ b/components/data_reduction_proxy/core/common/data_reduction_proxy_switches.h
@@ -15,6 +15,7 @@
 extern const char kDataReductionProxyAlt[];
 extern const char kDataReductionProxyAltFallback[];
 extern const char kDataReductionProxyDev[];
+extern const char kDataReductionProxyExperiment[];
 extern const char kDataReductionProxyFallback[];
 extern const char kDataReductionProxyKey[];
 extern const char kDataReductionProxyProbeURL[];
@@ -27,6 +28,7 @@
 extern const char kEnableDataReductionProxyLoFi[];
 extern const char kEnableDataReductionProxyBypassWarning[];
 
+
 }  // namespace switches
 }  // namespace data_reduction_proxy
 
diff --git a/components/device_event_log.gypi b/components/device_event_log.gypi
new file mode 100644
index 0000000..09eda0e
--- /dev/null
+++ b/components/device_event_log.gypi
@@ -0,0 +1,28 @@
+# 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.
+
+{
+  'targets': [
+    {
+      'target_name': 'device_event_log_component',
+      'type': '<(component)',
+      'dependencies': [
+        '../base/base.gyp:base',
+        '../net/net.gyp:net',
+      ],
+      'include_dirs': [
+        '..',
+      ],
+      'defines': [
+        'DEVICE_EVENT_LOG_IMPLEMENTATION',
+      ],
+      'sources': [
+        'device_event_log/device_event_log.cc',
+        'device_event_log/device_event_log.h',
+        'device_event_log/device_event_log_impl.cc',
+        'device_event_log/device_event_log_impl.h',
+      ],
+    },
+  ],
+}
diff --git a/components/device_event_log/BUILD.gn b/components/device_event_log/BUILD.gn
new file mode 100644
index 0000000..0b1103b
--- /dev/null
+++ b/components/device_event_log/BUILD.gn
@@ -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.
+
+source_set("device_event_log") {
+  sources = [
+    "device_event_log.cc",
+    "device_event_log.h",
+    "device_event_log_impl.cc",
+    "device_event_log_impl.h",
+  ]
+
+  deps = [
+    "//base",
+    "//net",
+  ]
+}
+
+source_set("unit_tests") {
+  testonly = true
+  sources = [
+    "device_event_log_impl_unittest.cc",
+  ]
+
+  deps = [
+    ":device_event_log",
+    "//testing/gtest",
+  ]
+}
diff --git a/components/device_event_log/DEPS b/components/device_event_log/DEPS
new file mode 100644
index 0000000..8fa9d48
--- /dev/null
+++ b/components/device_event_log/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+  "+net",
+]
diff --git a/components/device_event_log/OWNERS b/components/device_event_log/OWNERS
new file mode 100644
index 0000000..d313f84
--- /dev/null
+++ b/components/device_event_log/OWNERS
@@ -0,0 +1,2 @@
+stevenjb@chromium.org
+reillyg@chromium.org
diff --git a/components/device_event_log/device_event_log.cc b/components/device_event_log/device_event_log.cc
new file mode 100644
index 0000000..0d52b6a
--- /dev/null
+++ b/components/device_event_log/device_event_log.cc
@@ -0,0 +1,106 @@
+// 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 "components/device_event_log/device_event_log.h"
+
+#include <string>
+
+#include "base/logging.h"
+#include "components/device_event_log/device_event_log_impl.h"
+
+namespace device_event_log {
+
+namespace {
+
+const size_t kDefaultMaxEntries = 4000;
+
+const int kSlowMethodThresholdMs = 10;
+const int kVerySlowMethodThresholdMs = 50;
+
+DeviceEventLogImpl* g_device_event_log = NULL;
+
+}  // namespace
+
+const LogLevel kDefaultLogLevel = LOG_LEVEL_EVENT;
+
+void Initialize(size_t max_entries) {
+  CHECK(!g_device_event_log);
+  if (max_entries == 0)
+    max_entries = kDefaultMaxEntries;
+  g_device_event_log = new DeviceEventLogImpl(max_entries);
+}
+
+void Shutdown() {
+  delete g_device_event_log;
+  g_device_event_log = NULL;
+}
+
+void AddEntry(const char* file,
+              int line,
+              LogType type,
+              LogLevel level,
+              const std::string& event) {
+  if (g_device_event_log) {
+    g_device_event_log->AddEntry(file, line, type, level, event);
+  } else {
+    DeviceEventLogImpl::SendToVLogOrErrorLog(file, line, type, level, event);
+  }
+}
+
+void AddEntryWithDescription(const char* file,
+                             int line,
+                             LogType type,
+                             LogLevel level,
+                             const std::string& event,
+                             const std::string& desc) {
+  std::string event_with_desc = event;
+  if (!desc.empty())
+    event_with_desc += ": " + desc;
+  AddEntry(file, line, type, level, event_with_desc);
+}
+
+std::string GetAsString(StringOrder order,
+                        const std::string& format,
+                        const std::string& types,
+                        LogLevel max_level,
+                        size_t max_events) {
+  if (!g_device_event_log)
+    return "DeviceEventLog not initialized.";
+  return g_device_event_log->GetAsString(order, format, types, max_level,
+                                         max_events);
+}
+
+namespace internal {
+
+DeviceEventLogInstance::DeviceEventLogInstance(const char* file,
+                                               int line,
+                                               device_event_log::LogType type,
+                                               device_event_log::LogLevel level)
+    : file_(file), line_(line), type_(type), level_(level) {
+}
+
+DeviceEventLogInstance::~DeviceEventLogInstance() {
+  device_event_log::AddEntry(file_, line_, type_, level_, stream_.str());
+}
+
+ScopedDeviceLogIfSlow::ScopedDeviceLogIfSlow(LogType type,
+                                             const char* file,
+                                             const std::string& name)
+    : file_(file), type_(type), name_(name) {
+}
+
+ScopedDeviceLogIfSlow::~ScopedDeviceLogIfSlow() {
+  if (timer_.Elapsed().InMilliseconds() >= kSlowMethodThresholdMs) {
+    LogLevel level(LOG_LEVEL_DEBUG);
+    if (timer_.Elapsed().InMilliseconds() >= kVerySlowMethodThresholdMs)
+      level = LOG_LEVEL_ERROR;
+    DEVICE_LOG(type_, level) << "@@@ Slow method: " << file_ << ":" << name_
+                             << ": " << timer_.Elapsed().InMilliseconds()
+                             << "ms";
+  }
+}
+
+}  // namespace internal
+
+}  // namespace device_event_log
diff --git a/components/device_event_log/device_event_log.h b/components/device_event_log/device_event_log.h
new file mode 100644
index 0000000..cc733e4
--- /dev/null
+++ b/components/device_event_log/device_event_log.h
@@ -0,0 +1,175 @@
+// 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 COMPONENTS_DEVICE_EVENT_LOG_DEVICE_EVENT_LOG_H_
+#define COMPONENTS_DEVICE_EVENT_LOG_DEVICE_EVENT_LOG_H_
+
+#include <cstring>
+#include <sstream>
+
+#include "base/basictypes.h"
+#include "base/timer/elapsed_timer.h"
+#include "components/device_event_log/device_event_log_export.h"
+
+// These macros can be used to log device related events.
+// The following values should be used for |level| in these macros:
+//  ERROR Unexpected events, or device level failures. Use sparingly.
+//  USER  Events initiated directly by a user (or Chrome) action.
+//  EVENT Default event type.
+//  DEBUG Debugging details that are usually not interesting.
+// Examples:
+//  NET_LOG(EVENT) << "NetworkState Changed " << name << ": " << state;
+//  POWER_LOG(USER) << "Suspend requested";
+
+#define NET_LOG(level)                             \
+  DEVICE_LOG(::device_event_log::LOG_TYPE_NETWORK, \
+             ::device_event_log::LOG_LEVEL_##level)
+#define POWER_LOG(level)                         \
+  DEVICE_LOG(::device_event_log::LOG_TYPE_POWER, \
+             ::device_event_log::LOG_LEVEL_##level)
+#define LOGIN_LOG(level)                         \
+  DEVICE_LOG(::device_event_log::LOG_TYPE_LOGIN, \
+             ::device_event_log::LOG_LEVEL_##level)
+
+// Generally prefer the above macros unless |type| or  |level| is not constant.
+
+#define DEVICE_LOG(type, level)                                            \
+  ::device_event_log::internal::DeviceEventLogInstance(__FILE__, __LINE__, \
+                                                       type, level).stream()
+
+// Declare {Type_LOG_IF_SLOW() at the top of a method to log slow methods
+// where "slow" is defined by kSlowMethodThresholdMs in the .cc file.
+#define SCOPED_NET_LOG_IF_SLOW() \
+  SCOPED_DEVICE_LOG_IF_SLOW(::device_event_log::LOG_TYPE_NETWORK)
+
+// Generally prefer the above macros unless |type| is not constant.
+
+#define SCOPED_DEVICE_LOG_IF_SLOW(type)               \
+  ::device_event_log::internal::ScopedDeviceLogIfSlow \
+      scoped_device_log_if_slow(type, __FILE__, __func__)
+
+namespace device_event_log {
+
+// Used to specify the type of event. NOTE: Be sure to update LogTypeFromString
+// and GetLogTypeString when adding entries to this enum. Also consider
+// updating chrome://device-log (see device_log_ui.cc).
+enum LogType {
+  // Shill / network configuration related events.
+  LOG_TYPE_NETWORK,
+  // Power manager related events.
+  LOG_TYPE_POWER,
+  // Login related events.
+  LOG_TYPE_LOGIN,
+  // Used internally
+  LOG_TYPE_UNKNOWN
+};
+
+// Used to specify the detail level for logging. In GetAsString, used to
+// specify the maximum detail level (i.e. EVENT will include USER and ERROR).
+// See top-level comment for guidelines for each type.
+enum LogLevel {
+  LOG_LEVEL_ERROR = 0,
+  LOG_LEVEL_USER = 1,
+  LOG_LEVEL_EVENT = 2,
+  LOG_LEVEL_DEBUG = 3
+};
+
+// Used to specify which order to output event entries in GetAsString.
+enum StringOrder { OLDEST_FIRST, NEWEST_FIRST };
+
+// Initializes / shuts down device event logging. If |max_entries| = 0 the
+// default value will be used.
+DEVICE_EVENT_LOG_EXPORT void Initialize(size_t max_entries);
+DEVICE_EVENT_LOG_EXPORT void Shutdown();
+
+// If the global instance is initialized, adds an entry to it. Regardless of
+// whether the global instance was intitialzed, this logs the event to
+// LOG(ERROR) if |type| = ERROR or VLOG(1) otherwise.
+DEVICE_EVENT_LOG_EXPORT void AddEntry(const char* file,
+                                      int line,
+                                      LogType type,
+                                      LogLevel level,
+                                      const std::string& event);
+
+// For backwards compatibility with network_event_log. Combines |event| and
+// |description| and calls AddEntry().
+DEVICE_EVENT_LOG_EXPORT void AddEntryWithDescription(
+    const char* file,
+    int line,
+    LogType type,
+    LogLevel level,
+    const std::string& event,
+    const std::string& description);
+
+// Outputs the log to a formatted string.
+// |order| determines which order to output the events.
+// |format| is a comma-separated string that determines which elements to show.
+//  e.g. "time,desc". Note: order of the strings does not affect the output.
+//  "time" - Include a timestamp.
+//  "file" - Include file and line number.
+//  "type" - Include the event type.
+//  "html" - Include html tags.
+//  "json" - Return JSON format dictionaries containing entries for timestamp,
+//           level, type, file, and event.
+// |types| lists the types included in the output. Prepend "non-" to disclude
+//  a type. e.g. "network,login" or "non-network". Use an empty string for
+//  all types.
+// |max_level| determines the maximum log level to be included in the output.
+// |max_events| limits how many events are output if > 0, otherwise all events
+//  are included.
+DEVICE_EVENT_LOG_EXPORT std::string GetAsString(StringOrder order,
+                                                const std::string& format,
+                                                const std::string& types,
+                                                LogLevel max_level,
+                                                size_t max_events);
+
+DEVICE_EVENT_LOG_EXPORT extern const LogLevel kDefaultLogLevel;
+
+namespace internal {
+
+// Implementation class for DEVICE_LOG macros. Provides a stream for creating
+// a log string and adds the event using device_event_log::AddEntry on
+// destruction.
+class DEVICE_EVENT_LOG_EXPORT DeviceEventLogInstance {
+ public:
+  DeviceEventLogInstance(const char* file,
+                         int line,
+                         device_event_log::LogType type,
+                         device_event_log::LogLevel level);
+  ~DeviceEventLogInstance();
+
+  std::ostream& stream() { return stream_; }
+
+ private:
+  const char* file_;
+  const int line_;
+  device_event_log::LogType type_;
+  device_event_log::LogLevel level_;
+  std::ostringstream stream_;
+
+  DISALLOW_COPY_AND_ASSIGN(DeviceEventLogInstance);
+};
+
+// Implementation class for SCOPED_LOG_IF_SLOW macros. Tests the elapsed time on
+// destruction and adds a Debug or Error log entry if it exceeds the
+// corresponding expected maximum elapsed time.
+class DEVICE_EVENT_LOG_EXPORT ScopedDeviceLogIfSlow {
+ public:
+  ScopedDeviceLogIfSlow(LogType type,
+                        const char* file,
+                        const std::string& name);
+  ~ScopedDeviceLogIfSlow();
+
+ private:
+  const char* file_;
+  LogType type_;
+  std::string name_;
+  base::ElapsedTimer timer_;
+};
+
+}  // namespace internal
+
+}  // namespace device_event_log
+
+#endif  // DEVICE_EVENT_LOG_DEVICE_EVENT_LOG_H_
diff --git a/components/device_event_log/device_event_log_export.h b/components/device_event_log/device_event_log_export.h
new file mode 100644
index 0000000..fda2016
--- /dev/null
+++ b/components/device_event_log/device_event_log_export.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 COMPONENTS_DEVICE_EVENT_LOG_DEVICE_EVENT_LOG_EXPORT_H_
+#define COMPONENTS_DEVICE_EVENT_LOG_DEVICE_EVENT_LOG_EXPORT_H_
+
+#if defined(COMPONENT_BUILD)
+#if defined(WIN32)
+
+#if defined(DEVICE_EVENT_LOG_IMPLEMENTATION)
+#define DEVICE_EVENT_LOG_EXPORT __declspec(dllexport)
+#else
+#define DEVICE_EVENT_LOG_EXPORT __declspec(dllimport)
+#endif  // defined(DEVICE_EVENT_LOG_IMPLEMENTATION)
+
+#else  // defined(WIN32)
+#if defined(DEVICE_EVENT_LOG_IMPLEMENTATION)
+#define DEVICE_EVENT_LOG_EXPORT __attribute__((visibility("default")))
+#else
+#define DEVICE_EVENT_LOG_EXPORT
+#endif
+#endif
+
+#else  // defined(COMPONENT_BUILD)
+#define DEVICE_EVENT_LOG_EXPORT
+#endif
+
+#endif  // COMPONENTS_DEVICE_EVENT_LOG_DEVICE_EVENT_LOG_EXPORT_H_
diff --git a/components/device_event_log/device_event_log_impl.cc b/components/device_event_log/device_event_log_impl.cc
new file mode 100644
index 0000000..b260991
--- /dev/null
+++ b/components/device_event_log/device_event_log_impl.cc
@@ -0,0 +1,401 @@
+// 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 "components/device_event_log/device_event_log_impl.h"
+
+#include <cmath>
+#include <list>
+#include <set>
+
+#include "base/containers/adapters.h"
+#include "base/json/json_string_value_serializer.h"
+#include "base/json/json_writer.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/string_tokenizer.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/values.h"
+#include "net/base/escape.h"
+
+namespace device_event_log {
+
+namespace {
+
+const char* kLogLevelName[] = {"Error", "User", "Event", "Debug"};
+
+const char* kLogTypeNetworkDesc = "Network";
+const char* kLogTypePowerDesc = "Power";
+const char* kLogTypeLoginDesc = "Login";
+
+std::string GetLogTypeString(LogType type) {
+  if (type == LOG_TYPE_NETWORK)
+    return kLogTypeNetworkDesc;
+  if (type == LOG_TYPE_POWER)
+    return kLogTypePowerDesc;
+  if (type == LOG_TYPE_LOGIN)
+    return kLogTypeLoginDesc;
+  NOTREACHED();
+  return "Unknown";
+}
+
+std::string DateAndTimeWithMicroseconds(const base::Time& time) {
+  base::Time::Exploded exploded;
+  time.LocalExplode(&exploded);
+  // base::Time::Exploded does not include microseconds, but sometimes we need
+  // microseconds, so append '.' + usecs to the end of the formatted string.
+  int usecs = static_cast<int>(fmod(time.ToDoubleT() * 1000000, 1000000));
+  return base::StringPrintf("%04d/%02d/%02d %02d:%02d:%02d.%06d", exploded.year,
+                            exploded.month, exploded.day_of_month,
+                            exploded.hour, exploded.minute, exploded.second,
+                            usecs);
+}
+
+std::string TimeWithSeconds(const base::Time& time) {
+  base::Time::Exploded exploded;
+  time.LocalExplode(&exploded);
+  return base::StringPrintf("%02d:%02d:%02d", exploded.hour, exploded.minute,
+                            exploded.second);
+}
+
+std::string TimeWithMillieconds(const base::Time& time) {
+  base::Time::Exploded exploded;
+  time.LocalExplode(&exploded);
+  return base::StringPrintf("%02d:%02d:%02d.%03d", exploded.hour,
+                            exploded.minute, exploded.second,
+                            exploded.millisecond);
+}
+
+// Defined below for easier review. TODO(stevenjb): Move implementation here.
+std::string GetHtmlText(LogLevel log_level, const std::string& event);
+
+std::string LogEntryToString(const DeviceEventLogImpl::LogEntry& log_entry,
+                             bool show_time,
+                             bool show_file,
+                             bool show_type,
+                             bool show_level,
+                             bool format_html) {
+  std::string line;
+  if (show_time)
+    line += "[" + TimeWithMillieconds(log_entry.time) + "] ";
+  if (show_type)
+    line += GetLogTypeString(log_entry.log_type) + ": ";
+  if (show_level) {
+    const char* kLevelDesc[] = {"ERROR", "USER", "EVENT", "DEBUG"};
+    line += base::StringPrintf("%s: ", kLevelDesc[log_entry.log_level]);
+  }
+  if (show_file) {
+    std::string filestr =
+        format_html ? net::EscapeForHTML(log_entry.file) : log_entry.file;
+    line += base::StringPrintf("%s:%d ", log_entry.file.c_str(),
+                               log_entry.file_line);
+  }
+  line += format_html ? GetHtmlText(log_entry.log_level, log_entry.event)
+                      : log_entry.event;
+  if (log_entry.count > 1)
+    line += base::StringPrintf(" (%d)", log_entry.count);
+  return line;
+}
+
+void LogEntryToDictionary(const DeviceEventLogImpl::LogEntry& log_entry,
+                          base::DictionaryValue* output) {
+  output->SetString("timestamp", DateAndTimeWithMicroseconds(log_entry.time));
+  output->SetString("timestampshort", TimeWithSeconds(log_entry.time));
+  output->SetString("level", kLogLevelName[log_entry.log_level]);
+  output->SetString("type", GetLogTypeString(log_entry.log_type));
+  output->SetString("file", base::StringPrintf("%s:%d ", log_entry.file.c_str(),
+                                               log_entry.file_line));
+  output->SetString("event", log_entry.event);
+}
+
+std::string LogEntryAsJSON(const DeviceEventLogImpl::LogEntry& log_entry) {
+  base::DictionaryValue entry_dict;
+  LogEntryToDictionary(log_entry, &entry_dict);
+  std::string json;
+  JSONStringValueSerializer serializer(&json);
+  if (!serializer.Serialize(entry_dict)) {
+    LOG(ERROR) << "Failed to serialize to JSON";
+  }
+  return json;
+}
+
+std::string GetHtmlText(LogLevel log_level, const std::string& event) {
+  std::string text;
+  if (log_level == LOG_LEVEL_DEBUG)
+    text += "<i>";
+  else if (log_level == LOG_LEVEL_USER)
+    text += "<b>";
+  else if (log_level == LOG_LEVEL_ERROR)
+    text += "<b><i>";
+
+  text += net::EscapeForHTML(event);
+
+  if (log_level == LOG_LEVEL_DEBUG)
+    text += "</i>";
+  else if (log_level == LOG_LEVEL_USER)
+    text += "</b>";
+  else if (log_level == LOG_LEVEL_ERROR)
+    text += "</i></b>";
+  return text;
+}
+
+void SendLogEntryToVLogOrErrorLog(
+    const DeviceEventLogImpl::LogEntry& log_entry) {
+  if (log_entry.log_level != LOG_LEVEL_ERROR && !VLOG_IS_ON(1))
+    return;
+  const bool show_time = true;
+  const bool show_file = true;
+  const bool show_type = true;
+  const bool show_level = false;
+  const bool format_html = false;
+  std::string output = LogEntryToString(log_entry, show_time, show_file,
+                                        show_type, show_level, format_html);
+  if (log_entry.log_level == LOG_LEVEL_ERROR)
+    LOG(ERROR) << output;
+  else
+    VLOG(1) << output;
+}
+
+bool LogEntryMatches(const DeviceEventLogImpl::LogEntry& first,
+                     const DeviceEventLogImpl::LogEntry& second) {
+  return first.file == second.file && first.file_line == second.file_line &&
+         first.log_level == second.log_level &&
+         first.log_type == second.log_type && first.event == second.event;
+}
+
+bool LogEntryMatchesTypes(const DeviceEventLogImpl::LogEntry& entry,
+                          const std::set<LogType>& include_types,
+                          const std::set<LogType>& exclude_types) {
+  if (include_types.empty() && exclude_types.empty())
+    return true;
+  if (!include_types.empty() && include_types.count(entry.log_type))
+    return true;
+  if (!exclude_types.empty() && !exclude_types.count(entry.log_type))
+    return true;
+  return false;
+}
+
+void GetFormat(const std::string& format_string,
+               bool* show_time,
+               bool* show_file,
+               bool* show_type,
+               bool* show_level,
+               bool* format_html,
+               bool* format_json) {
+  base::StringTokenizer tokens(format_string, ",");
+  *show_time = false;
+  *show_file = false;
+  *show_type = false;
+  *show_level = false;
+  *format_html = false;
+  *format_json = false;
+  while (tokens.GetNext()) {
+    std::string tok(tokens.token());
+    if (tok == "time")
+      *show_time = true;
+    if (tok == "file")
+      *show_file = true;
+    if (tok == "type")
+      *show_type = true;
+    if (tok == "level")
+      *show_level = true;
+    if (tok == "html")
+      *format_html = true;
+    if (tok == "json")
+      *format_json = true;
+  }
+}
+
+LogType LogTypeFromString(const std::string& desc) {
+  std::string desc_lc = base::StringToLowerASCII(desc);
+  if (desc_lc == "network")
+    return LOG_TYPE_NETWORK;
+  if (desc_lc == "power")
+    return LOG_TYPE_POWER;
+  if (desc_lc == "login")
+    return LOG_TYPE_LOGIN;
+  NOTREACHED() << "Unrecogized LogType: " << desc;
+  return LOG_TYPE_UNKNOWN;
+}
+
+void GetLogTypes(const std::string& types,
+                 std::set<LogType>* include_types,
+                 std::set<LogType>* exclude_types) {
+  base::StringTokenizer tokens(types, ",");
+  while (tokens.GetNext()) {
+    std::string tok(tokens.token());
+    if (tok.substr(0, 4) == "non-") {
+      LogType type = LogTypeFromString(tok.substr(4));
+      if (type != LOG_TYPE_UNKNOWN)
+        exclude_types->insert(type);
+    } else {
+      LogType type = LogTypeFromString(tok);
+      if (type != LOG_TYPE_UNKNOWN)
+        include_types->insert(type);
+    }
+  }
+}
+
+}  // namespace
+
+// static
+void DeviceEventLogImpl::SendToVLogOrErrorLog(const char* file,
+                                              int file_line,
+                                              LogType log_type,
+                                              LogLevel log_level,
+                                              const std::string& event) {
+  LogEntry entry(file, file_line, log_type, log_level, event);
+  SendLogEntryToVLogOrErrorLog(entry);
+}
+
+DeviceEventLogImpl::DeviceEventLogImpl(size_t max_entries)
+    : max_entries_(max_entries) {
+}
+
+DeviceEventLogImpl::~DeviceEventLogImpl() {
+}
+
+void DeviceEventLogImpl::AddEntry(const char* file,
+                                  int file_line,
+                                  LogType log_type,
+                                  LogLevel log_level,
+                                  const std::string& event) {
+  LogEntry entry(file, file_line, log_type, log_level, event);
+  AddLogEntry(entry);
+}
+
+void DeviceEventLogImpl::AddLogEntry(const LogEntry& entry) {
+  if (!entries_.empty()) {
+    LogEntry& last = entries_.back();
+    if (LogEntryMatches(last, entry)) {
+      // Update count and time for identical events to avoid log spam.
+      ++last.count;
+      last.log_level = std::min(last.log_level, entry.log_level);
+      last.time = base::Time::Now();
+      return;
+    }
+  }
+  if (entries_.size() >= max_entries_) {
+    const size_t max_error_entries = max_entries_ / 2;
+    // Remove the first (oldest) non-error entry, or the oldest entry if more
+    // than half the entries are errors.
+    size_t error_count = 0;
+    for (LogEntryList::iterator iter = entries_.begin(); iter != entries_.end();
+         ++iter) {
+      if (iter->log_level != LOG_LEVEL_ERROR) {
+        entries_.erase(iter);
+        break;
+      }
+      if (++error_count > max_error_entries) {
+        // Too many error entries, remove the oldest entry.
+        entries_.pop_front();
+        break;
+      }
+    }
+  }
+  entries_.push_back(entry);
+  SendLogEntryToVLogOrErrorLog(entry);
+}
+
+std::string DeviceEventLogImpl::GetAsString(StringOrder order,
+                                            const std::string& format,
+                                            const std::string& types,
+                                            LogLevel max_level,
+                                            size_t max_events) {
+  if (entries_.empty())
+    return "No Log Entries.";
+
+  bool show_time, show_file, show_type, show_level, format_html, format_json;
+  GetFormat(format, &show_time, &show_file, &show_type, &show_level,
+            &format_html, &format_json);
+
+  std::set<LogType> include_types, exclude_types;
+  GetLogTypes(types, &include_types, &exclude_types);
+
+  std::string result;
+  base::ListValue log_entries;
+  if (order == OLDEST_FIRST) {
+    size_t offset = 0;
+    if (max_events > 0 && max_events < entries_.size()) {
+      // Iterate backwards through the list skipping uninteresting entries to
+      // determine the first entry to include.
+      size_t shown_events = 0;
+      size_t num_entries = 0;
+      for (const LogEntry& entry : base::Reversed(entries_)) {
+        ++num_entries;
+        if (!LogEntryMatchesTypes(entry, include_types, exclude_types))
+          continue;
+        if (entry.log_level > max_level)
+          continue;
+        if (++shown_events >= max_events)
+          break;
+      }
+      offset = entries_.size() - num_entries;
+    }
+    for (const LogEntry& entry : entries_) {
+      if (offset > 0) {
+        --offset;
+        continue;
+      }
+      if (!LogEntryMatchesTypes(entry, include_types, exclude_types))
+        continue;
+      if (entry.log_level > max_level)
+        continue;
+      if (format_json) {
+        log_entries.AppendString(LogEntryAsJSON(entry));
+      } else {
+        result += LogEntryToString(entry, show_time, show_file, show_type,
+                                   show_level, format_html);
+        result += "\n";
+      }
+    }
+  } else {
+    size_t nlines = 0;
+    // Iterate backwards through the list to show the most recent entries first.
+    for (const LogEntry& entry : base::Reversed(entries_)) {
+      if (!LogEntryMatchesTypes(entry, include_types, exclude_types))
+        continue;
+      if (entry.log_level > max_level)
+        continue;
+      if (format_json) {
+        log_entries.AppendString(LogEntryAsJSON(entry));
+      } else {
+        result += LogEntryToString(entry, show_time, show_file, show_type,
+                                   show_level, format_html);
+        result += "\n";
+      }
+      if (max_events > 0 && ++nlines >= max_events)
+        break;
+    }
+  }
+  if (format_json) {
+    JSONStringValueSerializer serializer(&result);
+    serializer.Serialize(log_entries);
+  }
+
+  return result;
+}
+
+DeviceEventLogImpl::LogEntry::LogEntry(const char* filedesc,
+                                       int file_line,
+                                       LogType log_type,
+                                       LogLevel log_level,
+                                       const std::string& event)
+    : file_line(file_line),
+      log_type(log_type),
+      log_level(log_level),
+      event(event),
+      time(base::Time::Now()),
+      count(1) {
+  if (filedesc) {
+    file = filedesc;
+    size_t last_slash_pos = file.find_last_of("\\/");
+    if (last_slash_pos != std::string::npos) {
+      file.erase(0, last_slash_pos + 1);
+    }
+  }
+}
+
+}  // namespace device_event_log
diff --git a/components/device_event_log/device_event_log_impl.h b/components/device_event_log/device_event_log_impl.h
new file mode 100644
index 0000000..8b7e11a
--- /dev/null
+++ b/components/device_event_log/device_event_log_impl.h
@@ -0,0 +1,79 @@
+// 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 COMPONENTS_DEVICE_EVENT_LOG_DEVICE_EVENT_LOG_IMPL_H_
+#define COMPONENTS_DEVICE_EVENT_LOG_DEVICE_EVENT_LOG_IMPL_H_
+
+#include <list>
+#include <string>
+
+#include "base/time/time.h"
+#include "components/device_event_log/device_event_log.h"
+#include "components/device_event_log/device_event_log_export.h"
+
+namespace device_event_log {
+
+class DEVICE_EVENT_LOG_EXPORT DeviceEventLogImpl {
+ public:
+  struct LogEntry {
+    LogEntry(const char* filedesc,
+             int file_line,
+             LogType log_type,
+             LogLevel log_level,
+             const std::string& event);
+
+    std::string file;
+    int file_line;
+    LogType log_type;
+    LogLevel log_level;
+    std::string event;
+    base::Time time;
+    int count;
+  };
+
+  explicit DeviceEventLogImpl(size_t max_entries);
+  ~DeviceEventLogImpl();
+
+  // Implements device_event_log::AddEntry.
+  void AddEntry(const char* file,
+                int file_line,
+                LogType type,
+                LogLevel level,
+                const std::string& event);
+
+  // Implements device_event_log::GetAsString.
+  std::string GetAsString(StringOrder order,
+                          const std::string& format,
+                          const std::string& types,
+                          LogLevel max_level,
+                          size_t max_events);
+
+  // Called from device_event_log::AddEntry if the global instance has not been
+  // created (or has already been destroyed). Logs to LOG(ERROR) or VLOG(1).
+  static void SendToVLogOrErrorLog(const char* file,
+                                   int file_line,
+                                   LogType type,
+                                   LogLevel log_level,
+                                   const std::string& event);
+
+ private:
+  friend class DeviceEventLogTest;
+
+  typedef std::list<LogEntry> LogEntryList;
+
+  void AddLogEntry(const LogEntry& entry);
+
+  // For testing
+  size_t max_entries() const { return max_entries_; }
+  void set_max_entries_for_test(size_t entries) { max_entries_ = entries; }
+
+  size_t max_entries_;
+  LogEntryList entries_;
+
+  DISALLOW_COPY_AND_ASSIGN(DeviceEventLogImpl);
+};
+
+}  // namespace device_event_log
+
+#endif  // COMPONENTS_DEVICE_EVENT_LOG_DEVICE_EVENT_LOG_IMPL_H_
diff --git a/components/device_event_log/device_event_log_impl_unittest.cc b/components/device_event_log/device_event_log_impl_unittest.cc
new file mode 100644
index 0000000..b6eff23
--- /dev/null
+++ b/components/device_event_log/device_event_log_impl_unittest.cc
@@ -0,0 +1,261 @@
+// 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 "components/device_event_log/device_event_log_impl.h"
+
+#include <algorithm>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/format_macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/string_split.h"
+#include "base/strings/stringprintf.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace device_event_log {
+
+namespace {
+
+const size_t kDefaultMaxEvents = 100;
+LogLevel kDefaultLevel = LOG_LEVEL_EVENT;
+LogType kDefaultType = LOG_TYPE_NETWORK;
+
+}  // namespace
+
+class DeviceEventLogTest : public testing::Test {
+ public:
+  DeviceEventLogTest() {}
+
+  void SetUp() override {
+    impl_.reset(new DeviceEventLogImpl(kDefaultMaxEvents));
+  }
+
+  void TearDown() override { impl_.reset(); }
+
+ protected:
+  std::string SkipTime(const std::string& input) {
+    std::string output;
+    std::vector<std::string> lines;
+    base::SplitString(input, '\n', &lines);
+    for (size_t i = 0; i < lines.size(); ++i) {
+      size_t n = lines[i].find(']');
+      if (n != std::string::npos)
+        output += "[time] " + lines[i].substr(n + 2) + '\n';
+      else
+        output += lines[i];
+    }
+    return output;
+  }
+
+  size_t CountLines(const std::string& input) {
+    return std::count(input.begin(), input.end(), '\n');
+  }
+
+  std::string GetLogString(StringOrder order,
+                           const std::string& format,
+                           LogLevel max_level,
+                           size_t max_events) {
+    return impl_->GetAsString(order, format, "", max_level, max_events);
+  }
+
+  std::string GetOrderedString(StringOrder order, size_t max_events) {
+    return impl_->GetAsString(order, "file", "", kDefaultLevel, max_events);
+  }
+
+  std::string GetLogStringForType(const std::string& types) {
+    return impl_->GetAsString(OLDEST_FIRST, "type", types, kDefaultLevel, 0);
+  }
+
+  void AddNetworkEntry(const char* file,
+                       int line,
+                       LogLevel level,
+                       const std::string& event) {
+    impl_->AddEntry(file, line, kDefaultType, level, event);
+  }
+
+  void AddTestEvent(LogLevel level, const std::string& event) {
+    AddNetworkEntry("file", 0, level, event);
+  }
+
+  void AddEventType(LogType type, const std::string& event) {
+    impl_->AddEntry("file", 0, type, kDefaultLevel, event);
+  }
+
+  size_t GetMaxEntries() const { return impl_->max_entries(); }
+
+  scoped_ptr<DeviceEventLogImpl> impl_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(DeviceEventLogTest);
+};
+
+TEST_F(DeviceEventLogTest, TestNetworkEvents) {
+  std::string output_none = GetOrderedString(OLDEST_FIRST, 0);
+  EXPECT_EQ("No Log Entries.", output_none);
+
+  LogLevel level = kDefaultLevel;
+  AddNetworkEntry("file1", 1, level, "event1");
+  AddNetworkEntry("file2", 2, level, "event2");
+  AddNetworkEntry("file3", 3, level, "event3");
+  AddNetworkEntry("file3", 3, level, "event3");
+
+  const std::string expected_output_oldest_first(
+      "file1:1 event1\n"
+      "file2:2 event2\n"
+      "file3:3 event3 (2)\n");
+  std::string output_oldest_first = GetOrderedString(OLDEST_FIRST, 0);
+  EXPECT_EQ(expected_output_oldest_first, output_oldest_first);
+
+  const std::string expected_output_oldest_first_short(
+      "file2:2 event2\n"
+      "file3:3 event3 (2)\n");
+  std::string output_oldest_first_short = GetOrderedString(OLDEST_FIRST, 2);
+  EXPECT_EQ(expected_output_oldest_first_short, output_oldest_first_short);
+
+  const std::string expected_output_newest_first(
+      "file3:3 event3 (2)\n"
+      "file2:2 event2\n"
+      "file1:1 event1\n");
+  std::string output_newest_first = GetOrderedString(NEWEST_FIRST, 0);
+  EXPECT_EQ(expected_output_newest_first, output_newest_first);
+
+  const std::string expected_output_newest_first_short(
+      "file3:3 event3 (2)\n"
+      "file2:2 event2\n");
+  std::string output_newest_first_short = GetOrderedString(NEWEST_FIRST, 2);
+  EXPECT_EQ(expected_output_newest_first_short, output_newest_first_short);
+}
+
+TEST_F(DeviceEventLogTest, TestMaxEntries) {
+  const size_t max_entries = GetMaxEntries();
+  const size_t entries_to_add = max_entries + 3;
+  for (size_t i = 0; i < entries_to_add; ++i) {
+    AddTestEvent(LOG_LEVEL_EVENT, base::StringPrintf("event_%" PRIuS, i));
+  }
+  std::string output = GetOrderedString(OLDEST_FIRST, 0);
+  size_t output_lines = CountLines(output);
+  EXPECT_EQ(max_entries, output_lines);
+}
+
+TEST_F(DeviceEventLogTest, TestStringFormat) {
+  AddTestEvent(LOG_LEVEL_ERROR, "event0");
+  EXPECT_EQ("file:0 event0\n",
+            GetLogString(OLDEST_FIRST, "file", kDefaultLevel, 1));
+  EXPECT_EQ("[time] event0\n",
+            SkipTime(GetLogString(OLDEST_FIRST, "time", kDefaultLevel, 1)));
+  EXPECT_EQ("event0\n", GetLogString(OLDEST_FIRST, "", kDefaultLevel, 1));
+  EXPECT_EQ("<b><i>event0</i></b>\n",
+            GetLogString(OLDEST_FIRST, "html", kDefaultLevel, 1));
+  EXPECT_EQ(
+      "[time] file:0 event0\n",
+      SkipTime(GetLogString(OLDEST_FIRST, "file,time", kDefaultLevel, 1)));
+
+  AddTestEvent(LOG_LEVEL_DEBUG, "event1");
+  EXPECT_EQ("[time] file:0 <i>event1</i>\n",
+            SkipTime(GetLogString(OLDEST_FIRST, "file,time,html",
+                                  LOG_LEVEL_DEBUG, 1)));
+
+  AddTestEvent(kDefaultLevel, "event2");
+  EXPECT_EQ("Network: file:0 event2\n",
+            GetLogString(OLDEST_FIRST, "file,type", kDefaultLevel, 1));
+}
+
+TEST_F(DeviceEventLogTest, TestLogLevel) {
+  AddTestEvent(LOG_LEVEL_ERROR, "error1");
+  AddTestEvent(LOG_LEVEL_ERROR, "error2");
+  AddTestEvent(LOG_LEVEL_EVENT, "event3");
+  AddTestEvent(LOG_LEVEL_ERROR, "error4");
+  AddTestEvent(LOG_LEVEL_EVENT, "event5");
+  AddTestEvent(LOG_LEVEL_DEBUG, "debug6");
+
+  std::string out;
+  out = GetLogString(OLDEST_FIRST, "", LOG_LEVEL_ERROR, 0);
+  EXPECT_EQ(3u, CountLines(out));
+  out = GetLogString(OLDEST_FIRST, "", LOG_LEVEL_EVENT, 0);
+  EXPECT_EQ(5u, CountLines(out));
+  out = GetLogString(OLDEST_FIRST, "", LOG_LEVEL_DEBUG, 0);
+  EXPECT_EQ(6u, CountLines(out));
+
+  // Test max_level. Get only the ERROR entries.
+  out = GetLogString(OLDEST_FIRST, "", LOG_LEVEL_ERROR, 0);
+  EXPECT_EQ("error1\nerror2\nerror4\n", out);
+}
+
+TEST_F(DeviceEventLogTest, TestMaxEvents) {
+  AddTestEvent(LOG_LEVEL_EVENT, "event1");
+  AddTestEvent(LOG_LEVEL_ERROR, "error2");
+  AddTestEvent(LOG_LEVEL_EVENT, "event3");
+  AddTestEvent(LOG_LEVEL_ERROR, "error4");
+  AddTestEvent(LOG_LEVEL_EVENT, "event5");
+
+  // Oldest first
+  EXPECT_EQ("error4\n", GetLogString(OLDEST_FIRST, "", LOG_LEVEL_ERROR, 1));
+
+  EXPECT_EQ("error2\nerror4\n",
+            GetLogString(OLDEST_FIRST, "", LOG_LEVEL_ERROR, 2));
+
+  EXPECT_EQ("event3\nerror4\nevent5\n",
+            GetLogString(OLDEST_FIRST, "", LOG_LEVEL_EVENT, 3));
+
+  EXPECT_EQ("error2\nevent3\nerror4\nevent5\n",
+            GetLogString(OLDEST_FIRST, "", LOG_LEVEL_EVENT, 4));
+
+  EXPECT_EQ("event1\nerror2\nevent3\nerror4\nevent5\n",
+            GetLogString(OLDEST_FIRST, "", LOG_LEVEL_EVENT, 5));
+
+  // Newest first
+  EXPECT_EQ("error4\n", GetLogString(NEWEST_FIRST, "", LOG_LEVEL_ERROR, 1));
+
+  EXPECT_EQ("error4\nerror2\n",
+            GetLogString(NEWEST_FIRST, "", LOG_LEVEL_ERROR, 2));
+
+  EXPECT_EQ("event5\nerror4\nevent3\n",
+            GetLogString(NEWEST_FIRST, "", LOG_LEVEL_EVENT, 3));
+
+  EXPECT_EQ("event5\nerror4\nevent3\nerror2\n",
+            GetLogString(NEWEST_FIRST, "", LOG_LEVEL_EVENT, 4));
+
+  EXPECT_EQ("event5\nerror4\nevent3\nerror2\nevent1\n",
+            GetLogString(NEWEST_FIRST, "", LOG_LEVEL_EVENT, 5));
+}
+
+TEST_F(DeviceEventLogTest, TestMaxErrors) {
+  const int kMaxTestEntries = 4;
+  impl_.reset(new DeviceEventLogImpl(kMaxTestEntries));
+  AddTestEvent(LOG_LEVEL_EVENT, "event1");
+  AddTestEvent(LOG_LEVEL_ERROR, "error2");
+  AddTestEvent(LOG_LEVEL_EVENT, "event3");
+  AddTestEvent(LOG_LEVEL_ERROR, "error4");
+  AddTestEvent(LOG_LEVEL_EVENT, "event5");
+  AddTestEvent(LOG_LEVEL_EVENT, "event6");
+  EXPECT_EQ("error2\nerror4\nevent5\nevent6\n",
+            GetLogString(OLDEST_FIRST, "", LOG_LEVEL_DEBUG, 0));
+}
+
+TEST_F(DeviceEventLogTest, TestType) {
+  AddEventType(LOG_TYPE_NETWORK, "event1");
+  AddEventType(LOG_TYPE_POWER, "event2");
+  AddEventType(LOG_TYPE_NETWORK, "event3");
+  AddEventType(LOG_TYPE_POWER, "event4");
+  AddEventType(LOG_TYPE_NETWORK, "event5");
+  AddEventType(LOG_TYPE_NETWORK, "event6");
+  EXPECT_EQ(
+      "Network: event1\nNetwork: event3\nNetwork: event5\nNetwork: event6\n",
+      GetLogStringForType("network"));
+  const std::string power_events("Power: event2\nPower: event4\n");
+  EXPECT_EQ(power_events, GetLogStringForType("power"));
+  EXPECT_EQ(power_events, GetLogStringForType("non-network"));
+  const std::string all_events(
+      "Network: event1\n"
+      "Power: event2\n"
+      "Network: event3\n"
+      "Power: event4\n"
+      "Network: event5\n"
+      "Network: event6\n");
+  EXPECT_EQ(all_events, GetLogStringForType("network,power"));
+  EXPECT_EQ(all_events, GetLogStringForType(""));
+}
+
+}  // namespace device_event_log
diff --git a/components/domain_reliability/BUILD.gn b/components/domain_reliability/BUILD.gn
index 0f941a21..9950a296 100644
--- a/components/domain_reliability/BUILD.gn
+++ b/components/domain_reliability/BUILD.gn
@@ -86,6 +86,8 @@
     "util_unittest.cc",
   ]
 
+  configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
+
   deps = [
     ":domain_reliability",
     "//base",
diff --git a/components/domain_reliability/baked_in_configs/mail_google_com.json b/components/domain_reliability/baked_in_configs/mail_google_com.json
index 4d43913..3dc51d4 100644
--- a/components/domain_reliability/baked_in_configs/mail_google_com.json
+++ b/components/domain_reliability/baked_in_configs/mail_google_com.json
@@ -1,5 +1,5 @@
 {
-  "config_version": "mail-google-com $Revision: #5 $",
+  "config_version": "mail-google-com $Revision: #6 $",
   "config_valid_until": 1446336000,
   "monitored_domain": "mail.google.com",
   "collectors": [
@@ -27,6 +27,12 @@
       "failure_sample_rate": 1.00
     },
     {
+      "resource_name": "gmail-persistent",
+      "url_patterns": ["http*://mail.google.com/*/channel/bind*"],
+      "success_sample_rate": 0.05,
+      "failure_sample_rate": 1.00
+    },
+    {
       "resource_name": "gmail-other",
       "url_patterns": ["*"],
       "success_sample_rate": 0.05,
diff --git a/components/domain_reliability/config.cc b/components/domain_reliability/config.cc
index 0898ff30..612f0bd 100644
--- a/components/domain_reliability/config.cc
+++ b/components/domain_reliability/config.cc
@@ -94,13 +94,12 @@
     const base::StringPiece& json) {
   scoped_ptr<base::Value> value(base::JSONReader::Read(json));
   base::JSONValueConverter<DomainReliabilityConfig> converter;
-  DomainReliabilityConfig* config = new DomainReliabilityConfig();
+  scoped_ptr<DomainReliabilityConfig> config(new DomainReliabilityConfig());
 
   // If we can parse and convert the JSON into a valid config, return that.
-  if (value && converter.Convert(*value, config) && config->IsValid())
-    return scoped_ptr<const DomainReliabilityConfig>(config);
-  else
-    return scoped_ptr<const DomainReliabilityConfig>();
+  if (value && converter.Convert(*value, config.get()) && config->IsValid())
+    return config.Pass();
+  return scoped_ptr<const DomainReliabilityConfig>();
 }
 
 bool DomainReliabilityConfig::IsValid() const {
diff --git a/components/domain_reliability/monitor.cc b/components/domain_reliability/monitor.cc
index ef9bc75..070b62de 100644
--- a/components/domain_reliability/monitor.cc
+++ b/components/domain_reliability/monitor.cc
@@ -21,8 +21,8 @@
 
 DomainReliabilityMonitor::DomainReliabilityMonitor(
     const std::string& upload_reporter_string,
-    scoped_refptr<base::SingleThreadTaskRunner> pref_thread,
-    scoped_refptr<base::SingleThreadTaskRunner> network_thread)
+    const scoped_refptr<base::SingleThreadTaskRunner>& pref_thread,
+    const scoped_refptr<base::SingleThreadTaskRunner>& network_thread)
     : time_(new ActualTime()),
       upload_reporter_string_(upload_reporter_string),
       scheduler_params_(
@@ -39,8 +39,8 @@
 
 DomainReliabilityMonitor::DomainReliabilityMonitor(
     const std::string& upload_reporter_string,
-    scoped_refptr<base::SingleThreadTaskRunner> pref_thread,
-    scoped_refptr<base::SingleThreadTaskRunner> network_thread,
+    const scoped_refptr<base::SingleThreadTaskRunner>& pref_thread,
+    const scoped_refptr<base::SingleThreadTaskRunner>& network_thread,
     scoped_ptr<MockableTime> time)
     : time_(time.Pass()),
       upload_reporter_string_(upload_reporter_string),
@@ -90,7 +90,8 @@
 }
 
 void DomainReliabilityMonitor::InitURLRequestContext(
-    scoped_refptr<net::URLRequestContextGetter> url_request_context_getter) {
+    const scoped_refptr<net::URLRequestContextGetter>&
+        url_request_context_getter) {
   DCHECK(OnNetworkThread());
   DCHECK(moved_to_network_thread_);
 
@@ -114,10 +115,12 @@
 
   base::Time now = base::Time::Now();
   for (size_t i = 0; kBakedInJsonConfigs[i]; ++i) {
-    std::string json(kBakedInJsonConfigs[i]);
+    base::StringPiece json(kBakedInJsonConfigs[i]);
     scoped_ptr<const DomainReliabilityConfig> config =
         DomainReliabilityConfig::FromJSON(json);
-    if (config && config->IsExpired(now)) {
+    if (!config) {
+      continue;
+    } else if (config->IsExpired(now)) {
       LOG(WARNING) << "Baked-in Domain Reliability config for "
                    << config->domain << " is expired.";
       continue;
diff --git a/components/domain_reliability/monitor.h b/components/domain_reliability/monitor.h
index d2bcd6dd..d897692 100644
--- a/components/domain_reliability/monitor.h
+++ b/components/domain_reliability/monitor.h
@@ -49,14 +49,14 @@
   // on which requests will actually be monitored and reported.
   DomainReliabilityMonitor(
       const std::string& upload_reporter_string,
-      scoped_refptr<base::SingleThreadTaskRunner> pref_thread,
-      scoped_refptr<base::SingleThreadTaskRunner> network_thread);
+      const scoped_refptr<base::SingleThreadTaskRunner>& pref_thread,
+      const scoped_refptr<base::SingleThreadTaskRunner>& network_thread);
 
   // Same, but specifies a mock interface for time functions for testing.
   DomainReliabilityMonitor(
       const std::string& upload_reporter_string,
-      scoped_refptr<base::SingleThreadTaskRunner> pref_thread,
-      scoped_refptr<base::SingleThreadTaskRunner> network_thread,
+      const scoped_refptr<base::SingleThreadTaskRunner>& pref_thread,
+      const scoped_refptr<base::SingleThreadTaskRunner>& network_thread,
       scoped_ptr<MockableTime> time);
 
   // Must be called from the pref thread if |MoveToNetworkThread| was not
@@ -78,7 +78,8 @@
 
   // Same, but for unittests where the Getter is readily available.
   void InitURLRequestContext(
-      scoped_refptr<net::URLRequestContextGetter> url_request_context_getter);
+      const scoped_refptr<net::URLRequestContextGetter>&
+          url_request_context_getter);
 
   // Populates the monitor with contexts that were configured at compile time.
   void AddBakedInConfigs();
diff --git a/components/favicon_base.gypi b/components/favicon_base.gypi
index 97f5aeb..0ea6331 100644
--- a/components/favicon_base.gypi
+++ b/components/favicon_base.gypi
@@ -16,6 +16,7 @@
         '../skia/skia.gyp:skia',
         '../ui/base/ui_base.gyp:ui_base',
         '../ui/gfx/gfx.gyp:gfx',
+        '../ui/gfx/gfx.gyp:gfx_geometry',
         '../url/url.gyp:url_lib',
       ],
       'sources': [
diff --git a/components/favicon_base/BUILD.gn b/components/favicon_base/BUILD.gn
index 57c42a4..07daa0a 100644
--- a/components/favicon_base/BUILD.gn
+++ b/components/favicon_base/BUILD.gn
@@ -24,6 +24,7 @@
     "//skia",
     "//ui/base",
     "//ui/gfx",
+    "//ui/gfx/geometry",
     "//url",
   ]
 }
@@ -39,5 +40,6 @@
     "//testing/gtest",
     "//ui/base",
     "//ui/gfx",
+    "//ui/gfx/geometry",
   ]
 }
diff --git a/components/gcm_driver/android/java/src/org/chromium/components/gcm_driver/GCMDriver.java b/components/gcm_driver/android/java/src/org/chromium/components/gcm_driver/GCMDriver.java
index 867a097..29c8b4d 100644
--- a/components/gcm_driver/android/java/src/org/chromium/components/gcm_driver/GCMDriver.java
+++ b/components/gcm_driver/android/java/src/org/chromium/components/gcm_driver/GCMDriver.java
@@ -37,10 +37,12 @@
 
     private long mNativeGCMDriverAndroid;
     private final Context mContext;
+    private final GoogleCloudMessagingV2 mGcm;
 
     private GCMDriver(long nativeGCMDriverAndroid, Context context) {
         mNativeGCMDriverAndroid = nativeGCMDriverAndroid;
         mContext = context;
+        mGcm = new GoogleCloudMessagingV2(context);
     }
 
     /**
@@ -72,18 +74,16 @@
     }
 
     @CalledByNative
-    private void register(final String appId, final String[] senderIds) {
+    private void register(final String appId, final String senderId) {
         new AsyncTask<Void, Void, String>() {
             @Override
             protected String doInBackground(Void... voids) {
-                // TODO(johnme): Should check if GMS is installed on the device first. Ditto below.
                 try {
                     String subtype = appId;
-                    GoogleCloudMessagingV2 gcm = new GoogleCloudMessagingV2(mContext);
-                    String registrationId = gcm.register(subtype, senderIds);
+                    String registrationId = mGcm.subscribe(senderId, subtype, null);
                     return registrationId;
                 } catch (IOException ex) {
-                    Log.w(TAG, "GCMv2 registration failed for " + appId, ex);
+                    Log.w(TAG, "GCM subscription failed for " + appId + ", " + senderId, ex);
                     return "";
                 }
             }
@@ -96,17 +96,16 @@
     }
 
     @CalledByNative
-    private void unregister(final String appId) {
+    private void unregister(final String appId, final String senderId) {
         new AsyncTask<Void, Void, Boolean>() {
             @Override
             protected Boolean doInBackground(Void... voids) {
                 try {
                     String subtype = appId;
-                    GoogleCloudMessagingV2 gcm = new GoogleCloudMessagingV2(mContext);
-                    gcm.unregister(subtype);
+                    mGcm.unsubscribe(senderId, subtype, null);
                     return true;
                 } catch (IOException ex) {
-                    Log.w(TAG, "GCMv2 unregistration failed for " + appId, ex);
+                    Log.w(TAG, "GCM unsubscription failed for " + appId + ", " + senderId, ex);
                     return false;
                 }
             }
diff --git a/components/gcm_driver/android/java/src/org/chromium/components/gcm_driver/GoogleCloudMessagingV2.java b/components/gcm_driver/android/java/src/org/chromium/components/gcm_driver/GoogleCloudMessagingV2.java
index d8a82534..6840602 100644
--- a/components/gcm_driver/android/java/src/org/chromium/components/gcm_driver/GoogleCloudMessagingV2.java
+++ b/components/gcm_driver/android/java/src/org/chromium/components/gcm_driver/GoogleCloudMessagingV2.java
@@ -7,6 +7,9 @@
 import android.app.PendingIntent;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
@@ -19,29 +22,25 @@
 
 
 /**
- * Temporary code for sending subtypes when (un)registering with GCM.
+ * Temporary code for sending subtypes when (un)subscribing with GCM.
  * Subtypes are experimental and may change without notice!
  * TODO(johnme): Remove this file, once we switch to the GMS client library.
  */
 public class GoogleCloudMessagingV2 {
 
-    // Inlined com.google.android.gms.common.GooglePlayServicesUtil.GOOGLE_PLAY_SERVICES_PACKAGE
-    // since this class mustn't depend on the GMS client library.
     private static final String GOOGLE_PLAY_SERVICES_PACKAGE = "com.google.android.gms";
     private static final long REGISTER_TIMEOUT = 5000;
-    private static final String ACTION_C2DM_REGISTER =
-            "com.google.android.c2dm.intent.REGISTER";
-    private static final String ACTION_C2DM_UNREGISTER =
-            "com.google.android.c2dm.intent.UNREGISTER";
+    private static final String ACTION_C2DM_REGISTER = "com.google.android.c2dm.intent.REGISTER";
     private static final String C2DM_EXTRA_ERROR = "error";
     private static final String INTENT_PARAM_APP = "app";
     private static final String ERROR_MAIN_THREAD = "MAIN_THREAD";
     private static final String ERROR_SERVICE_NOT_AVAILABLE = "SERVICE_NOT_AVAILABLE";
+    private static final String EXTRA_DELETE = "delete";
     private static final String EXTRA_REGISTRATION_ID = "registration_id";
-    private static final String EXTRA_UNREGISTERED = "unregistered";
     private static final String EXTRA_SENDER = "sender";
     private static final String EXTRA_MESSENGER = "google.messenger";
     private static final String EXTRA_SUBTYPE = "subtype";
+    private static final String EXTRA_SUBSCRIPTION = "subscription";
 
     private Context mContext;
     private PendingIntent mAppPendingIntent = null;
@@ -51,80 +50,132 @@
         mContext = context;
     }
 
+    public String subscribe(String source, String subtype, Bundle data) throws IOException {
+        if (data == null) {
+            data = new Bundle();
+        }
+        data.putString(EXTRA_SUBTYPE, subtype);
+        Bundle result = subscribe(source, data);
+        return result.getString(EXTRA_REGISTRATION_ID);
+    }
+
+    public void unsubscribe(String source, String subtype, Bundle data) throws IOException {
+        if (data == null) {
+            data = new Bundle();
+        }
+        data.putString(EXTRA_SUBTYPE, subtype);
+        unsubscribe(source, data);
+        return;
+    }
+
     /**
-     * Register the application for GCM and return the registration ID. You must call this once,
-     * when your application is installed, and send the returned registration ID to the server.
+     * Subscribe to receive GCM messages from a specific source.
      * <p>
-     * This is a blocking call&mdash;you shouldn't call it from the UI thread.
-     * <p>
-     * Repeated calls to this method will return the original registration ID.
-     * <p>
-     * If you want to modify the list of senders, you must call {@code unregister()} first.
-     * <p>
-     * Most applications use a single sender ID. You may use multiple senders if different
-     * servers may send messages to the app or for testing.
+     * Source Types:
+     * <ul>
+     * <li>Sender ID - if you have multiple senders you can call this method
+     * for each additional sender. Each sender can use the corresponding
+     * {@link #REGISTRATION_ID} returned in the bundle to send messages
+     * from the server.</li>
+     * <li>Cloud Pub/Sub topic - You can subscribe to a topic and receive
+     * notifications from the owner of that topic, when something changes.
+     * For more information see
+     * <a href="https://cloud.google.com/pubsub">Cloud Pub/Sub</a>.</li>
+     * </ul>
+     * This function is blocking and should not be called on the main thread.
      *
-     * @param senderIds list of project numbers or Google accounts identifying who is allowed to
-     *   send messages to this application.
-     * @return registration id
+     * @param source of the desired notifications.
+     * @param data (optional) additional information.
+     * @return Bundle containing subscription information including {@link #REGISTRATION_ID}
+     * @throws IOException if the request fails.
      */
-    public String register(String subtype, String... senderIds) throws IOException {
+    public Bundle subscribe(String source, Bundle data) throws IOException {
+        if (data == null) {
+            data = new Bundle();
+        }
+        // Expected by older versions of GMS and servlet
+        data.putString(EXTRA_SENDER, source);
+        // New name of the sender parameter
+        data.putString(EXTRA_SUBSCRIPTION, source);
+        // DB buster for older versions of GCM.
+        if (data.getString(EXTRA_SUBTYPE) == null) {
+            data.putString(EXTRA_SUBTYPE, source);
+        }
+
+        Intent resultIntent = registerRpc(data);
+        getExtraOrThrow(resultIntent, EXTRA_REGISTRATION_ID);
+        return resultIntent.getExtras();
+    }
+
+    /**
+     * Unsubscribe from a source to stop receiving messages from it.
+     * <p>
+     * This function is blocking and should not be called on the main thread.
+     *
+     * @param source to unsubscribe
+     * @param data (optional) additional information.
+     * @throws IOException if the request fails.
+     */
+    public void unsubscribe(String source, Bundle data) throws IOException {
+        if (data == null) {
+            data = new Bundle();
+        }
+        // Use the register servlet, with 'delete=true'.
+        // Registration service returns a registration_id on success - or an error code.
+        data.putString(EXTRA_DELETE, "1");
+        subscribe(source, data);
+    }
+
+    private Intent registerRpc(Bundle data) throws IOException {
         if (Looper.getMainLooper() == Looper.myLooper()) {
             throw new IOException(ERROR_MAIN_THREAD);
         }
+        if (getGcmVersion() < 0) {
+            throw new IOException("Google Play Services missing");
+        }
+        if (data == null) {
+            data = new Bundle();
+        }
 
-        final BlockingQueue<Intent> registerResult = new LinkedBlockingQueue<Intent>();
-        Handler registrationHandler = new Handler(Looper.getMainLooper()) {
+        final BlockingQueue<Intent> responseResult = new LinkedBlockingQueue<Intent>();
+        Handler responseHandler = new Handler(Looper.getMainLooper()) {
             @Override
             public void handleMessage(Message msg) {
                 Intent res = (Intent) msg.obj;
-                registerResult.add(res);
+                responseResult.add(res);
             }
         };
-        Messenger messenger = new Messenger(registrationHandler);
+        Messenger responseMessenger = new Messenger(responseHandler);
 
-        internalRegister(messenger, subtype, senderIds);
-
+        Intent intent = new Intent(ACTION_C2DM_REGISTER);
+        intent.setPackage(GOOGLE_PLAY_SERVICES_PACKAGE);
+        setPackageNameExtra(intent);
+        intent.putExtras(data);
+        intent.putExtra(EXTRA_MESSENGER, responseMessenger);
+        mContext.startService(intent);
         try {
-            Intent regIntent = registerResult.poll(REGISTER_TIMEOUT, TimeUnit.MILLISECONDS);
-            if (regIntent == null) {
-                throw new IOException(ERROR_SERVICE_NOT_AVAILABLE);
-            }
-            String registrationId = regIntent.getStringExtra(EXTRA_REGISTRATION_ID);
-            // registration succeeded
-            if (registrationId != null) {
-                return registrationId;
-            }
-            String err = regIntent.getStringExtra(C2DM_EXTRA_ERROR);
-            if (err != null) {
-                throw new IOException(err);
-            } else {
-                throw new IOException(ERROR_SERVICE_NOT_AVAILABLE);
-            }
+            return responseResult.poll(REGISTER_TIMEOUT, TimeUnit.MILLISECONDS);
         } catch (InterruptedException e) {
             throw new IOException(e.getMessage());
         }
     }
 
-    private void internalRegister(Messenger messenger, String subtype, String... senderIds) {
-        Intent intent = new Intent(ACTION_C2DM_REGISTER);
-        intent.setPackage(GOOGLE_PLAY_SERVICES_PACKAGE);
-        if (subtype != null) intent.putExtra("subtype", subtype);
-        intent.putExtra(EXTRA_MESSENGER, messenger);
-        setPackageNameExtra(intent);
-        intent.putExtra(EXTRA_SENDER, getFlatSenderIds(senderIds));
-        mContext.startService(intent);
-    }
+    private String getExtraOrThrow(Intent intent, String extraKey) throws IOException {
+        if (intent == null) {
+            throw new IOException(ERROR_SERVICE_NOT_AVAILABLE);
+        }
 
-    private String getFlatSenderIds(String... senderIds) {
-        if (senderIds == null || senderIds.length == 0) {
-            throw new IllegalArgumentException("No senderIds");
+        String extraValue = intent.getStringExtra(extraKey);
+        if (extraValue != null) {
+            return extraValue;
         }
-        StringBuilder builder = new StringBuilder(senderIds[0]);
-        for (int i = 1; i < senderIds.length; i++) {
-            builder.append(',').append(senderIds[i]);
+
+        String err = intent.getStringExtra(C2DM_EXTRA_ERROR);
+        if (err != null) {
+            throw new IOException(err);
+        } else {
+            throw new IOException(ERROR_SERVICE_NOT_AVAILABLE);
         }
-        return builder.toString();
     }
 
     private void setPackageNameExtra(Intent intent) {
@@ -140,63 +191,14 @@
         intent.putExtra(INTENT_PARAM_APP, mAppPendingIntent);
     }
 
-    /**
-     * Unregister the application. Calling {@code unregister()} stops any
-     * messages from the server.
-     * <p>
-     * This is a blocking call&mdash;you shouldn't call it from the UI thread.
-     * <p>
-     * You should rarely (if ever) need to call this method. Not only is it
-     * expensive in terms of resources, but it invalidates your registration ID,
-     * which you should never change unnecessarily. A better approach is to simply
-     * have your server stop sending messages. Only use unregister if you want
-     * to change your sender ID.
-     *
-     * @throws IOException if we can't connect to server to unregister.
-     */
-    public void unregister(String subtype) throws IOException {
-        if (Looper.getMainLooper() == Looper.myLooper()) {
-            throw new IOException(ERROR_MAIN_THREAD);
-        }
-        final BlockingQueue<Intent> registerResult = new LinkedBlockingQueue<Intent>();
-        Handler registrationHandler = new Handler(Looper.getMainLooper()) {
-            @Override
-            public void handleMessage(Message msg) {
-                Intent res = (Intent) msg.obj;
-                registerResult.add(res);
-            }
-        };
-        Messenger messenger = new Messenger(registrationHandler);
-        internalUnregister(messenger, subtype);
+    private int getGcmVersion() {
+        PackageManager pm = mContext.getPackageManager();
         try {
-            Intent regIntent = registerResult.poll(REGISTER_TIMEOUT, TimeUnit.MILLISECONDS);
-            if (regIntent == null) {
-                throw new IOException(ERROR_SERVICE_NOT_AVAILABLE);
-            }
-            String unregistered = regIntent.getStringExtra(EXTRA_UNREGISTERED);
-            if (unregistered != null) {
-                // All done
-                return;
-            }
-            String err = regIntent.getStringExtra(C2DM_EXTRA_ERROR);
-            if (err != null) {
-                throw new IOException(err);
-            } else {
-                throw new IOException(ERROR_SERVICE_NOT_AVAILABLE);
-            }
-        } catch (InterruptedException e) {
-            throw new IOException(e.getMessage());
+            PackageInfo packageInfo = pm.getPackageInfo(GOOGLE_PLAY_SERVICES_PACKAGE, 0);
+            return packageInfo.versionCode;
+        } catch (PackageManager.NameNotFoundException e) {
+            // No problem
         }
-    }
-
-    private void internalUnregister(Messenger messenger, String subtype) {
-        Intent intent = new Intent(ACTION_C2DM_UNREGISTER);
-        intent.setPackage(GOOGLE_PLAY_SERVICES_PACKAGE);
-        if (subtype != null) {
-            intent.putExtra("subtype", subtype);
-        }
-        intent.putExtra(EXTRA_MESSENGER, messenger);
-        setPackageNameExtra(intent);
-        mContext.startService(intent);
+        return -1;
     }
 }
diff --git a/components/gcm_driver/gcm_driver.cc b/components/gcm_driver/gcm_driver.cc
index a82e7b1b..080ba52 100644
--- a/components/gcm_driver/gcm_driver.cc
+++ b/components/gcm_driver/gcm_driver.cc
@@ -70,6 +70,20 @@
 
 void GCMDriver::Unregister(const std::string& app_id,
                            const UnregisterCallback& callback) {
+  UnregisterInternal(app_id, nullptr /* sender_id */, callback);
+}
+
+void GCMDriver::UnregisterWithSenderId(
+    const std::string& app_id,
+    const std::string& sender_id,
+    const UnregisterCallback& callback) {
+  DCHECK(!sender_id.empty());
+  UnregisterInternal(app_id, &sender_id, callback);
+}
+
+void GCMDriver::UnregisterInternal(const std::string& app_id,
+                                   const std::string* sender_id,
+                                   const UnregisterCallback& callback) {
   DCHECK(!app_id.empty());
   DCHECK(!callback.is_null());
 
@@ -88,7 +102,10 @@
 
   unregister_callbacks_[app_id] = callback;
 
-  UnregisterImpl(app_id);
+  if (sender_id)
+    UnregisterWithSenderIdImpl(app_id, *sender_id);
+  else
+    UnregisterImpl(app_id);
 }
 
 void GCMDriver::Send(const std::string& app_id,
@@ -117,6 +134,11 @@
   SendImpl(app_id, receiver_id, message);
 }
 
+void GCMDriver::UnregisterWithSenderIdImpl(const std::string& app_id,
+                                           const std::string& sender_id) {
+  NOTREACHED();
+}
+
 void GCMDriver::RegisterFinished(const std::string& app_id,
                                  const std::string& registration_id,
                                  GCMClient::Result result) {
diff --git a/components/gcm_driver/gcm_driver.h b/components/gcm_driver/gcm_driver.h
index 04fb5366..fb20f3ac 100644
--- a/components/gcm_driver/gcm_driver.h
+++ b/components/gcm_driver/gcm_driver.h
@@ -37,8 +37,9 @@
   GCMDriver();
   virtual ~GCMDriver();
 
-  // Registers |sender_id| for an app. A registration ID will be returned by
-  // the GCM server.
+  // Registers |sender_ids| for an app. A registration ID will be returned by
+  // the GCM server. On Android, only a single sender ID is supported, but
+  // instead multiple simultaneous registrations are allowed.
   // |app_id|: application ID.
   // |sender_ids|: list of IDs of the servers that are allowed to send the
   //               messages to the application. These IDs are assigned by the
@@ -48,12 +49,22 @@
                 const std::vector<std::string>& sender_ids,
                 const RegisterCallback& callback);
 
-  // Unregisters an app from using GCM.
+  // Unregisters all sender_ids for an app. Only works on non-Android.
   // |app_id|: application ID.
   // |callback|: to be called once the asynchronous operation is done.
   void Unregister(const std::string& app_id,
                   const UnregisterCallback& callback);
 
+  // Unregisters an (app_id, sender_id) pair from using GCM. Only works on
+  // Android.
+  // TODO(jianli): Switch to using GCM's unsubscribe API.
+  // |app_id|: application ID.
+  // |sender_id|: the sender ID that was passed when registering.
+  // |callback|: to be called once the asynchronous operation is done.
+  void UnregisterWithSenderId(const std::string& app_id,
+                              const std::string& sender_id,
+                              const UnregisterCallback& callback);
+
   // Sends a message to a given receiver.
   // |app_id|: application ID.
   // |receiver_id|: registration ID of the receiver party.
@@ -146,6 +157,10 @@
   // Platform-specific implementation of Unregister.
   virtual void UnregisterImpl(const std::string& app_id) = 0;
 
+  // Platform-specific implementation of UnregisterWithSenderId.
+  virtual void UnregisterWithSenderIdImpl(const std::string& app_id,
+                                          const std::string& sender_id);
+
   // Platform-specific implementation of Send.
   virtual void SendImpl(const std::string& app_id,
                         const std::string& receiver_id,
@@ -170,6 +185,11 @@
   void ClearCallbacks();
 
  private:
+  // Common code shared by Unregister and UnregisterWithSenderId.
+  void UnregisterInternal(const std::string& app_id,
+                          const std::string* sender_id,
+                          const UnregisterCallback& callback);
+
   // Called after unregistration completes in order to trigger the pending
   // registration.
   void RegisterAfterUnregister(
diff --git a/components/gcm_driver/gcm_driver_android.cc b/components/gcm_driver/gcm_driver_android.cc
index 93b2f91..2f82fbe 100644
--- a/components/gcm_driver/gcm_driver_android.cc
+++ b/components/gcm_driver/gcm_driver_android.cc
@@ -169,20 +169,26 @@
 }
 
 void GCMDriverAndroid::RegisterImpl(
-    const std::string& app_id,
-    const std::vector<std::string>& sender_ids) {
+    const std::string& app_id, const std::vector<std::string>& sender_ids) {
+  DCHECK_EQ(1u, sender_ids.size());
   JNIEnv* env = AttachCurrentThread();
   Java_GCMDriver_register(
       env, java_ref_.obj(),
       ConvertUTF8ToJavaString(env, app_id).Release(),
-      ToJavaArrayOfStrings(env, sender_ids).obj());
+      ConvertUTF8ToJavaString(env, sender_ids[0]).Release());
 }
 
 void GCMDriverAndroid::UnregisterImpl(const std::string& app_id) {
+  NOTREACHED();
+}
+
+void GCMDriverAndroid::UnregisterWithSenderIdImpl(
+    const std::string& app_id, const std::string& sender_id) {
   JNIEnv* env = AttachCurrentThread();
   Java_GCMDriver_unregister(
       env, java_ref_.obj(),
-      ConvertUTF8ToJavaString(env, app_id).Release());
+      ConvertUTF8ToJavaString(env, app_id).Release(),
+      ConvertUTF8ToJavaString(env, sender_id).Release());
 }
 
 void GCMDriverAndroid::SendImpl(const std::string& app_id,
diff --git a/components/gcm_driver/gcm_driver_android.h b/components/gcm_driver/gcm_driver_android.h
index d26ed677..41c47b5 100644
--- a/components/gcm_driver/gcm_driver_android.h
+++ b/components/gcm_driver/gcm_driver_android.h
@@ -71,6 +71,8 @@
   void RegisterImpl(const std::string& app_id,
                     const std::vector<std::string>& sender_ids) override;
   void UnregisterImpl(const std::string& app_id) override;
+  void UnregisterWithSenderIdImpl(const std::string& app_id,
+                                  const std::string& sender_id) override;
   void SendImpl(const std::string& app_id,
                 const std::string& receiver_id,
                 const GCMClient::OutgoingMessage& message) override;
diff --git a/components/gcm_driver/gcm_driver_desktop_unittest.cc b/components/gcm_driver/gcm_driver_desktop_unittest.cc
index e20081e..ab9d1a3 100644
--- a/components/gcm_driver/gcm_driver_desktop_unittest.cc
+++ b/components/gcm_driver/gcm_driver_desktop_unittest.cc
@@ -295,8 +295,8 @@
   base::RunLoop run_loop;
   async_operation_completed_callback_ = run_loop.QuitClosure();
   driver_->Unregister(app_id,
-                       base::Bind(&GCMDriverTest::UnregisterCompleted,
-                                  base::Unretained(this)));
+                      base::Bind(&GCMDriverTest::UnregisterCompleted,
+                                 base::Unretained(this)));
   if (wait_to_finish == WAIT)
     run_loop.Run();
 }
diff --git a/components/google/core/browser/google_url_tracker.h b/components/google/core/browser/google_url_tracker.h
index ad30b3a..d59b85a 100644
--- a/components/google/core/browser/google_url_tracker.h
+++ b/components/google/core/browser/google_url_tracker.h
@@ -16,7 +16,6 @@
 #include "net/url_request/url_fetcher_delegate.h"
 #include "url/gurl.h"
 
-class GoogleURLTrackerNavigationHelper;
 class PrefService;
 
 namespace infobars {
diff --git a/components/history.gypi b/components/history.gypi
index 4d6474c2..384fc9ca 100644
--- a/components/history.gypi
+++ b/components/history.gypi
@@ -85,6 +85,8 @@
         'history/core/browser/url_utils.h',
         'history/core/browser/visit_database.cc',
         'history/core/browser/visit_database.h',
+        'history/core/browser/visit_delegate.cc',
+        'history/core/browser/visit_delegate.h',
         'history/core/browser/visit_filter.cc',
         'history/core/browser/visit_filter.h',
         'history/core/browser/visit_tracker.cc',
diff --git a/components/history/core/browser/BUILD.gn b/components/history/core/browser/BUILD.gn
index 437eaeb..5e8b4f49e 100644
--- a/components/history/core/browser/BUILD.gn
+++ b/components/history/core/browser/BUILD.gn
@@ -62,6 +62,8 @@
     "url_utils.h",
     "visit_database.cc",
     "visit_database.h",
+    "visit_delegate.cc",
+    "visit_delegate.h",
     "visit_filter.cc",
     "visit_filter.h",
     "visit_tracker.cc",
@@ -109,6 +111,8 @@
       "android/visit_sql_handler.h",
     ]
   }
+
+  configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
 }
 
 proto_library("proto") {
diff --git a/components/history/core/browser/url_database.h b/components/history/core/browser/url_database.h
index fb3d2f0..c6b35295 100644
--- a/components/history/core/browser/url_database.h
+++ b/components/history/core/browser/url_database.h
@@ -137,7 +137,7 @@
    public:
     URLEnumerator();
 
-    // Retreives the next url. Returns false if no more urls are available
+    // Retrieves the next url. Returns false if no more urls are available.
     bool GetNextURL(URLRow* r);
 
    private:
@@ -153,8 +153,6 @@
   // more than 3 times.
   bool InitURLEnumeratorForSignificant(URLEnumerator* enumerator);
 
-  // Favicons ------------------------------------------------------------------
-
   // Autocomplete --------------------------------------------------------------
 
   // Fills the given array with URLs matching the given prefix.  They will be
diff --git a/components/history/core/browser/visit_delegate.cc b/components/history/core/browser/visit_delegate.cc
new file mode 100644
index 0000000..5df16c5c
--- /dev/null
+++ b/components/history/core/browser/visit_delegate.cc
@@ -0,0 +1,15 @@
+// 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 "components/history/core/browser/visit_delegate.h"
+
+namespace history {
+
+VisitDelegate::VisitDelegate() {
+}
+
+VisitDelegate::~VisitDelegate() {
+}
+
+}  // namespace history
diff --git a/components/history/core/browser/visit_delegate.h b/components/history/core/browser/visit_delegate.h
new file mode 100644
index 0000000..a60e7a2
--- /dev/null
+++ b/components/history/core/browser/visit_delegate.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 COMPONENTS_HISTORY_CORE_BROWSER_VISIT_DELEGATE_H_
+#define COMPONENTS_HISTORY_CORE_BROWSER_VISIT_DELEGATE_H_
+
+#include <vector>
+
+#include "base/macros.h"
+
+class GURL;
+class HistoryService;
+
+namespace history {
+
+// VisitDelegate gets notified about URLs recorded as visited by the
+// HistoryService.
+class VisitDelegate {
+ public:
+  VisitDelegate();
+  virtual ~VisitDelegate();
+
+  // Called once HistoryService initialization is complete. Returns true if the
+  // initialization succeeded, false otherwise.
+  virtual bool Init(HistoryService* history_service) = 0;
+
+  // Called when an URL is recorded by HistoryService.
+  virtual void AddURL(const GURL& url) = 0;
+
+  // Called when a list of URLs are recorded by HistoryService.
+  virtual void AddURLs(const std::vector<GURL>& urls) = 0;
+
+  // Called when a list of URLs are removed from HistoryService.
+  virtual void DeleteURLs(const std::vector<GURL>& urls) = 0;
+
+  // Called when all URLs are removed from HistoryService.
+  virtual void DeleteAllURLs() = 0;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(VisitDelegate);
+};
+
+}  // namespace history
+
+#endif  // COMPONENTS_HISTORY_CORE_BROWSER_VISIT_DELEGATE_H_
diff --git a/components/keyed_service/content/BUILD.gn b/components/keyed_service/content/BUILD.gn
index b75f864a..6d3d5b6 100644
--- a/components/keyed_service/content/BUILD.gn
+++ b/components/keyed_service/content/BUILD.gn
@@ -19,11 +19,10 @@
     "refcounted_browser_context_keyed_service_factory.h",
   ]
 
+  # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+  configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
+
   defines = [ "KEYED_SERVICE_IMPLEMENTATION" ]
-  if (is_win) {
-    # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
-    cflags = [ "/wd4267" ]
-  }
 
   deps = [
     "//base",
diff --git a/components/keyed_service/content/browser_context_dependency_manager.cc b/components/keyed_service/content/browser_context_dependency_manager.cc
index 7e00023..cc112d3 100644
--- a/components/keyed_service/content/browser_context_dependency_manager.cc
+++ b/components/keyed_service/content/browser_context_dependency_manager.cc
@@ -21,6 +21,9 @@
 void BrowserContextDependencyManager::RegisterProfilePrefsForServices(
     const content::BrowserContext* context,
     user_prefs::PrefRegistrySyncable* pref_registry) {
+  TRACE_EVENT0(
+     "browser",
+     "BrowserContextDependencyManager::RegisterProfilePrefsForServices");
   RegisterPrefsForServices(context, pref_registry);
 }
 
diff --git a/components/keyed_service/core/BUILD.gn b/components/keyed_service/core/BUILD.gn
index 2d8670e..8585cbb 100644
--- a/components/keyed_service/core/BUILD.gn
+++ b/components/keyed_service/core/BUILD.gn
@@ -26,14 +26,11 @@
     "service_access_types.h",
   ]
 
+  configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
+
   defines = [ "KEYED_SERVICE_IMPLEMENTATION" ]
 
   deps = [
     "//base",
   ]
-
-  if (is_win) {
-    # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
-    cflags = [ "/wd4267" ]
-  }
 }
diff --git a/components/metrics.gypi b/components/metrics.gypi
index c723a15..9a8f19de 100644
--- a/components/metrics.gypi
+++ b/components/metrics.gypi
@@ -66,6 +66,8 @@
         'metrics/metrics_switches.h',
         'metrics/persisted_logs.cc',
         'metrics/persisted_logs.h',
+        'metrics/url_constants.cc',
+        'metrics/url_constants.h',
       ],
       'conditions': [
         ['chromeos==1', {
diff --git a/components/metrics/BUILD.gn b/components/metrics/BUILD.gn
index 69d0af41..7f4b9b5 100644
--- a/components/metrics/BUILD.gn
+++ b/components/metrics/BUILD.gn
@@ -48,6 +48,8 @@
     "metrics_switches.h",
     "persisted_logs.cc",
     "persisted_logs.h",
+    "url_constants.cc",
+    "url_constants.h",
   ]
 
   public_deps = [
diff --git a/components/metrics/metrics_service.cc b/components/metrics/metrics_service.cc
index 18ec2e5..962d836 100644
--- a/components/metrics/metrics_service.cc
+++ b/components/metrics/metrics_service.cc
@@ -227,12 +227,6 @@
 // Interval, in minutes, between state saves.
 const int kSaveStateIntervalMinutes = 5;
 
-// The metrics server's URL.
-const char kServerUrl[] = "https://clients4.google.com/uma/v2";
-
-// The MIME type for the uploaded metrics data.
-const char kMimeType[] = "application/vnd.chrome.uma";
-
 enum ResponseStatus {
   UNKNOWN_FAILURE,
   SUCCESS,
@@ -1021,7 +1015,6 @@
 
   if (!log_uploader_) {
     log_uploader_ = client_->CreateUploader(
-        kServerUrl, kMimeType,
         base::Bind(&MetricsService::OnLogUploadComplete,
                    self_ptr_factory_.GetWeakPtr()));
   }
diff --git a/components/metrics/metrics_service_client.h b/components/metrics/metrics_service_client.h
index 4289bae..fa31c5e 100644
--- a/components/metrics/metrics_service_client.h
+++ b/components/metrics/metrics_service_client.h
@@ -64,8 +64,6 @@
   // Creates a MetricsLogUploader with the specified parameters (see comments on
   // MetricsLogUploader for details).
   virtual scoped_ptr<MetricsLogUploader> CreateUploader(
-      const std::string& server_url,
-      const std::string& mime_type,
       const base::Callback<void(int)>& on_upload_complete) = 0;
 
   // Returns the name of a key under HKEY_CURRENT_USER that can be used to store
diff --git a/components/metrics/net/wifi_access_point_info_provider_chromeos.cc b/components/metrics/net/wifi_access_point_info_provider_chromeos.cc
index cb0adf1..16471bc 100644
--- a/components/metrics/net/wifi_access_point_info_provider_chromeos.cc
+++ b/components/metrics/net/wifi_access_point_info_provider_chromeos.cc
@@ -51,10 +51,9 @@
     return;
 
   // Retrieve access point info for wifi connection.
-  NetworkHandler::Get()->network_configuration_handler()->GetProperties(
+  NetworkHandler::Get()->network_configuration_handler()->GetShillProperties(
       default_network->path(),
-      base::Bind(&WifiAccessPointInfoProviderChromeos::ParseInfo,
-                 AsWeakPtr()),
+      base::Bind(&WifiAccessPointInfoProviderChromeos::ParseInfo, AsWeakPtr()),
       chromeos::network_handler::ErrorCallback());
 }
 
diff --git a/components/metrics/test_metrics_service_client.cc b/components/metrics/test_metrics_service_client.cc
index c60db0b..53cb1d9 100644
--- a/components/metrics/test_metrics_service_client.cc
+++ b/components/metrics/test_metrics_service_client.cc
@@ -65,8 +65,6 @@
 }
 
 scoped_ptr<MetricsLogUploader> TestMetricsServiceClient::CreateUploader(
-    const std::string& server_url,
-    const std::string& mime_type,
     const base::Callback<void(int)>& on_upload_complete) {
   return scoped_ptr<MetricsLogUploader>();
 }
diff --git a/components/metrics/test_metrics_service_client.h b/components/metrics/test_metrics_service_client.h
index c46ac4e..14c55183 100644
--- a/components/metrics/test_metrics_service_client.h
+++ b/components/metrics/test_metrics_service_client.h
@@ -32,8 +32,6 @@
   void StartGatheringMetrics(const base::Closure& done_callback) override;
   void CollectFinalMetrics(const base::Closure& done_callback) override;
   scoped_ptr<MetricsLogUploader> CreateUploader(
-      const std::string& server_url,
-      const std::string& mime_type,
       const base::Callback<void(int)>& on_upload_complete) override;
 
   const std::string& get_client_id() const { return client_id_; }
diff --git a/components/metrics/url_constants.cc b/components/metrics/url_constants.cc
new file mode 100644
index 0000000..55d9e13
--- /dev/null
+++ b/components/metrics/url_constants.cc
@@ -0,0 +1,13 @@
+// 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 "components/metrics/url_constants.h"
+
+namespace metrics {
+
+const char kDefaultMetricsServerUrl[] = "https://clients4.google.com/uma/v2";
+const char kDefaultMetricsMimeType[] = "application/vnd.chrome.uma";
+
+} // namespace metrics
+
diff --git a/components/metrics/url_constants.h b/components/metrics/url_constants.h
new file mode 100644
index 0000000..b52ddef
--- /dev/null
+++ b/components/metrics/url_constants.h
@@ -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.
+
+#ifndef COMPONENTS_METRICS_URL_CONSTANTS_H_
+#define COMPONENTS_METRICS_URL_CONSTANTS_H_
+
+namespace metrics {
+
+// The default metrics server's URL.
+extern const char kDefaultMetricsServerUrl[];
+
+// The default MIME type for the uploaded metrics data.
+extern const char kDefaultMetricsMimeType[];
+
+} // namespace metrics
+
+#endif // COMPONENTS_METRICS_URL_CONSTANTS_H_
diff --git a/components/omnibox/omnibox_field_trial.cc b/components/omnibox/omnibox_field_trial.cc
index 74f1100..6dd2302 100644
--- a/components/omnibox/omnibox_field_trial.cc
+++ b/components/omnibox/omnibox_field_trial.cc
@@ -289,6 +289,37 @@
   return bookmark_value;
 }
 
+bool OmniboxFieldTrial::HQPExperimentalScoringEnabled() {
+  return variations::GetVariationParamValue(
+      kBundledExperimentFieldTrialName,
+      kHQPExperimentalScoringEnabledParam) == "true";
+}
+
+std::string OmniboxFieldTrial::HQPExperimentalScoringBuckets() {
+  if (!HQPExperimentalScoringEnabled())
+    return "";
+
+  return variations::GetVariationParamValue(
+      kBundledExperimentFieldTrialName,
+      kHQPExperimentalScoringBucketsParam);
+}
+
+float OmniboxFieldTrial::HQPExperimentalTopicalityThreshold() {
+  if (!HQPExperimentalScoringEnabled())
+    return -1;
+
+  std::string topicality_threhold_str =
+    variations::GetVariationParamValue(
+        kBundledExperimentFieldTrialName,
+        kHQPExperimentalScoringTopicalityThresholdParam);
+
+  double topicality_threshold;
+  if (!base::StringToDouble(topicality_threhold_str, &topicality_threshold))
+    return -1;
+
+  return static_cast<float>(topicality_threshold);
+}
+
 bool OmniboxFieldTrial::HQPAllowMatchInTLDValue() {
   return variations::GetVariationParamValue(
       kBundledExperimentFieldTrialName,
@@ -381,6 +412,14 @@
 const char OmniboxFieldTrial::kHUPNewScoringVisitedCountScoreBucketsParam[] =
     "VisitedCountScoreBuckets";
 
+const char OmniboxFieldTrial::kHQPExperimentalScoringEnabledParam[] =
+    "HQPExperimentalScoringEnabled";
+const char OmniboxFieldTrial::kHQPExperimentalScoringBucketsParam[] =
+    "HQPExperimentalScoringBuckets";
+const char
+    OmniboxFieldTrial::kHQPExperimentalScoringTopicalityThresholdParam[] =
+      "HQPExperimentalScoringTopicalityThreshold";
+
 // static
 int OmniboxFieldTrial::kDefaultMinimumTimeBetweenSuggestQueriesMs = 100;
 
diff --git a/components/omnibox/omnibox_field_trial.h b/components/omnibox/omnibox_field_trial.h
index 5a439e2..4eebf644 100644
--- a/components/omnibox/omnibox_field_trial.h
+++ b/components/omnibox/omnibox_field_trial.h
@@ -270,6 +270,28 @@
                                         int* polling_delay_ms);
 
   // ---------------------------------------------------------
+  // For HQP scoring related experiments to control the topicality and scoring
+  // ranges of relevancy scores.
+
+  // Returns true if HQP experimental scoring is enabled. Returns false if
+  // |kHQPExperimentalScoringEnabledParam| is not specified in the field trial.
+  static bool HQPExperimentalScoringEnabled();
+
+  // Returns the scoring buckets for HQP experiments. Returns empty string
+  // in case |kHQPExperimentalScoringBucketsParam| or
+  // |kHQPExperimentalScoringEnabledParam| is not specified in the
+  // field trial. Scoring buckets are stored in string form giving mapping from
+  // (topicality_score, frequency_score) to final relevance score.
+  // Please see GetRelevancyScore() under
+  // chrome/browser/history::ScoredHistoryMatch for details.
+  static std::string HQPExperimentalScoringBuckets();
+
+  // Returns the topicality threshold for HQP experiments. Returns -1 if
+  // |kHQPExperimentalScoringTopicalityThresholdParam| or
+  // |kHQPExperimentalScoringEnabledParam| is not specified in the field trial.
+  static float HQPExperimentalTopicalityThreshold();
+
+  // ---------------------------------------------------------
   // Exposed publicly for the sake of unittests.
   static const char kBundledExperimentFieldTrialName[];
   // Rule names used by the bundled experiment.
@@ -298,6 +320,11 @@
   static const char kHUPNewScoringVisitedCountHalfLifeTimeParam[];
   static const char kHUPNewScoringVisitedCountScoreBucketsParam[];
 
+  // Parameter names used by the HQP experimental scoring experiments.
+  static const char kHQPExperimentalScoringEnabledParam[];
+  static const char kHQPExperimentalScoringBucketsParam[];
+  static const char kHQPExperimentalScoringTopicalityThresholdParam[];
+
   // The amount of time to wait before sending a new suggest request after the
   // previous one unless overridden by a field trial parameter.
   // Non-const because some unittests modify this value.
diff --git a/components/open_from_clipboard.gypi b/components/open_from_clipboard.gypi
new file mode 100644
index 0000000..103e6d0
--- /dev/null
+++ b/components/open_from_clipboard.gypi
@@ -0,0 +1,28 @@
+# 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.
+
+{
+  'targets': [
+    {
+      'target_name': 'open_from_clipboard',
+      'type': 'static_library',
+      'dependencies': [
+        '../base/base.gyp:base',
+        '../url/url.gyp:url_lib',
+        'components_strings.gyp:components_strings',
+        'omnibox',
+      ],
+      'include_dirs': [
+        '..',
+      ],
+      'sources': [
+        'open_from_clipboard/clipboard_recent_content.h',
+        'open_from_clipboard/clipboard_recent_content_ios.h',
+        'open_from_clipboard/clipboard_recent_content_ios.mm',
+        'open_from_clipboard/clipboard_url_provider.cc',
+        'open_from_clipboard/clipboard_url_provider.h',
+      ],
+    },
+  ],
+}
diff --git a/components/open_from_clipboard/DEPS b/components/open_from_clipboard/DEPS
new file mode 100644
index 0000000..30a8913
--- /dev/null
+++ b/components/open_from_clipboard/DEPS
@@ -0,0 +1,5 @@
+include_rules = [
+  "+components/omnibox",
+  "+grit/components_strings.h",
+  "+ui/base/l10n",
+]
diff --git a/components/open_from_clipboard/OWNERS b/components/open_from_clipboard/OWNERS
new file mode 100644
index 0000000..48eb126
--- /dev/null
+++ b/components/open_from_clipboard/OWNERS
@@ -0,0 +1 @@
+jif@chromium.org
\ No newline at end of file
diff --git a/components/open_from_clipboard/clipboard_recent_content.h b/components/open_from_clipboard/clipboard_recent_content.h
new file mode 100644
index 0000000..31c274c
--- /dev/null
+++ b/components/open_from_clipboard/clipboard_recent_content.h
@@ -0,0 +1,43 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_OPEN_FROM_CLIPBOARD_CLIPBOARD_RECENT_CONTENT_H_
+#define COMPONENTS_OPEN_FROM_CLIPBOARD_CLIPBOARD_RECENT_CONTENT_H_
+
+#include <string>
+
+#include "base/macros.h"
+
+class GURL;
+
+// Helper class returning an URL if the content of the clipboard can be turned
+// into an URL, and if it estimates that the content of the clipboard is not too
+// old.
+class ClipboardRecentContent {
+ public:
+  // Returns an instance of the ClipboardContent singleton.
+  static ClipboardRecentContent* GetInstance();
+
+  // Returns true if the clipboard contains a recent URL, and copies it in
+  // |url|. Otherwise, returns false. |url| must not be null.
+  virtual bool GetRecentURLFromClipboard(GURL* url) const = 0;
+
+  // Sets which URL scheme this app can be opened with. Used by
+  // ClipboardRecentContent to determine whether or not the clipboard contains
+  // a relevant URL. |application_scheme| may be empty.
+  void set_application_scheme(const std::string& application_scheme) {
+    application_scheme_ = application_scheme;
+  }
+
+ protected:
+  ClipboardRecentContent() {}
+  virtual ~ClipboardRecentContent() {}
+  // Contains the URL scheme opening the app. May be empty.
+  std::string application_scheme_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ClipboardRecentContent);
+};
+
+#endif  // COMPONENTS_OPEN_FROM_CLIPBOARD_CLIPBOARD_RECENT_CONTENT_H_
diff --git a/components/open_from_clipboard/clipboard_recent_content_ios.h b/components/open_from_clipboard/clipboard_recent_content_ios.h
new file mode 100644
index 0000000..5c3ff46
--- /dev/null
+++ b/components/open_from_clipboard/clipboard_recent_content_ios.h
@@ -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.
+
+#ifndef COMPONENTS_OPEN_FROM_CLIPBOARD_CLIPBOARD_RECENT_CONTENT_IOS_H_
+#define COMPONENTS_OPEN_FROM_CLIPBOARD_CLIPBOARD_RECENT_CONTENT_IOS_H_
+
+#include "base/mac/scoped_nsobject.h"
+#include "components/open_from_clipboard/clipboard_recent_content.h"
+#include "url/gurl.h"
+
+@class NSDate;
+@class PasteboardNotificationListenerBridge;
+
+template <typename T>
+struct DefaultSingletonTraits;
+
+// IOS implementation of ClipboardRecentContent
+class ClipboardRecentContentIOS : public ClipboardRecentContent {
+ public:
+  static ClipboardRecentContentIOS* GetInstance();
+  // Notifies that the content of the pasteboard may have changed.
+  void PasteboardChanged();
+
+  // ClipboardRecentContent implementation.
+  bool GetRecentURLFromClipboard(GURL* url) const override;
+
+ private:
+  friend struct DefaultSingletonTraits<ClipboardRecentContentIOS>;
+
+  ClipboardRecentContentIOS();
+  ~ClipboardRecentContentIOS() override;
+  // Loads information from the user defaults about the latest clipboard entry.
+  void LoadFromUserDefaults();
+  // Saves information to the user defaults about the latest clipboard entry.
+  void SaveToUserDefaults();
+  // Returns the URL contained in the clipboard (if any).
+  GURL URLFromPasteboard();
+
+  // The pasteboard's change count. Increases everytime the pasteboard changes.
+  NSInteger lastPasteboardChangeCount_;
+  // Estimation of the date when the pasteboard changed.
+  base::scoped_nsobject<NSDate> lastPasteboardChangeDate_;
+  // Cache of the GURL contained in the pasteboard (if any).
+  GURL urlFromPasteboardCache_;
+  // Bridge to receive notification when the pasteboard changes.
+  base::scoped_nsobject<PasteboardNotificationListenerBridge>
+      notificationBridge_;
+
+  DISALLOW_COPY_AND_ASSIGN(ClipboardRecentContentIOS);
+};
+
+#endif  // COMPONENTS_OPEN_FROM_CLIPBOARD_CLIPBOARD_RECENT_CONTENT_IOS_H_
diff --git a/components/open_from_clipboard/clipboard_recent_content_ios.mm b/components/open_from_clipboard/clipboard_recent_content_ios.mm
new file mode 100644
index 0000000..9ec6f07
--- /dev/null
+++ b/components/open_from_clipboard/clipboard_recent_content_ios.mm
@@ -0,0 +1,162 @@
+// 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 "components/open_from_clipboard/clipboard_recent_content_ios.h"
+
+#import <UIKit/UIKit.h>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/memory/singleton.h"
+#include "base/metrics/user_metrics.h"
+#include "base/strings/sys_string_conversions.h"
+#include "url/gurl.h"
+#include "url/url_constants.h"
+
+ClipboardRecentContent* ClipboardRecentContent::GetInstance() {
+  return ClipboardRecentContentIOS::GetInstance();
+}
+
+// Bridge that forwards pasteboard change notifications to its delegate.
+@interface PasteboardNotificationListenerBridge : NSObject {
+  ClipboardRecentContentIOS* _delegate;
+}
+@end
+
+@implementation PasteboardNotificationListenerBridge
+
+- (id)initWithDelegate:(ClipboardRecentContentIOS*)delegate {
+  DCHECK(delegate);
+  self = [super init];
+  if (self) {
+    _delegate = delegate;
+    [[NSNotificationCenter defaultCenter]
+        addObserver:self
+           selector:@selector(pasteboardChangedNotification:)
+               name:UIPasteboardChangedNotification
+             object:[UIPasteboard generalPasteboard]];
+    [[NSNotificationCenter defaultCenter]
+        addObserver:self
+           selector:@selector(pasteboardChangedNotification:)
+               name:UIApplicationDidBecomeActiveNotification
+             object:nil];
+  }
+  return self;
+}
+
+- (void)dealloc {
+  [[NSNotificationCenter defaultCenter]
+      removeObserver:self
+                name:UIPasteboardChangedNotification
+              object:[UIPasteboard generalPasteboard]];
+  [[NSNotificationCenter defaultCenter]
+      removeObserver:self
+                name:UIApplicationDidBecomeActiveNotification
+              object:[UIPasteboard generalPasteboard]];
+  [super dealloc];
+}
+
+- (void)pasteboardChangedNotification:(NSNotification*)notification {
+  _delegate->PasteboardChanged();
+}
+
+@end
+
+namespace {
+// Key used to store the pasteboard's current change count. If when resuming
+// chrome the pasteboard's change count is different from the stored one, then
+// it means that the pasteboard's content has changed.
+NSString* kPasteboardChangeCountKey = @"PasteboardChangeCount";
+// Key used to store the last date at which it was detected that the pasteboard
+// changed. It is used to evaluate the age of the pasteboard's content.
+NSString* kPasteboardChangeDateKey = @"PasteboardChangeDate";
+NSTimeInterval kMaximumAgeOfClipboardInSeconds = 6 * 60 * 60;
+// Schemes accepted by the ClipboardRecentContentIOS.
+const char* kAuthorizedSchemes[] = {
+    url::kHttpScheme,
+    url::kHttpsScheme,
+    url::kDataScheme,
+    url::kAboutScheme,
+};
+}  // namespace
+
+ClipboardRecentContentIOS* ClipboardRecentContentIOS::GetInstance() {
+  return Singleton<ClipboardRecentContentIOS>::get();
+}
+
+bool ClipboardRecentContentIOS::GetRecentURLFromClipboard(GURL* url) const {
+  DCHECK(url);
+  if (-[lastPasteboardChangeDate_ timeIntervalSinceNow] >
+      kMaximumAgeOfClipboardInSeconds) {
+    return false;
+  }
+  if (urlFromPasteboardCache_.is_valid()) {
+    *url = urlFromPasteboardCache_;
+    return true;
+  }
+  return false;
+}
+
+void ClipboardRecentContentIOS::PasteboardChanged() {
+  if ([UIPasteboard generalPasteboard].changeCount !=
+      lastPasteboardChangeCount_) {
+    urlFromPasteboardCache_ = URLFromPasteboard();
+    if (!urlFromPasteboardCache_.is_empty()) {
+      base::RecordAction(
+          base::UserMetricsAction("MobileOmniboxClipboardChanged"));
+    }
+    lastPasteboardChangeDate_.reset([[NSDate date] retain]);
+    lastPasteboardChangeCount_ = [UIPasteboard generalPasteboard].changeCount;
+    SaveToUserDefaults();
+  }
+}
+
+ClipboardRecentContentIOS::ClipboardRecentContentIOS()
+    : ClipboardRecentContent() {
+  urlFromPasteboardCache_ = URLFromPasteboard();
+  LoadFromUserDefaults();
+  if ([UIPasteboard generalPasteboard].changeCount !=
+      lastPasteboardChangeCount_) {
+    PasteboardChanged();
+  }
+  notificationBridge_.reset(
+      [[PasteboardNotificationListenerBridge alloc] initWithDelegate:this]);
+}
+
+ClipboardRecentContentIOS::~ClipboardRecentContentIOS() {
+}
+
+GURL ClipboardRecentContentIOS::URLFromPasteboard() {
+  const std::string clipboard =
+      base::SysNSStringToUTF8([[UIPasteboard generalPasteboard] string]);
+  GURL gurl = GURL(clipboard);
+  if (gurl.is_valid()) {
+    for (size_t i = 0; i < arraysize(kAuthorizedSchemes); ++i) {
+      if (gurl.SchemeIs(kAuthorizedSchemes[i])) {
+        return gurl;
+      }
+    }
+    if (!application_scheme_.empty() &&
+        gurl.SchemeIs(application_scheme_.c_str())) {
+      return gurl;
+    }
+  }
+  return GURL::EmptyGURL();
+}
+
+void ClipboardRecentContentIOS::LoadFromUserDefaults() {
+  lastPasteboardChangeCount_ = [[NSUserDefaults standardUserDefaults]
+      integerForKey:kPasteboardChangeCountKey];
+  lastPasteboardChangeDate_.reset([[[NSUserDefaults standardUserDefaults]
+      objectForKey:kPasteboardChangeDateKey] retain]);
+  DCHECK(!lastPasteboardChangeDate_ ||
+         [lastPasteboardChangeDate_ isKindOfClass:[NSDate class]]);
+}
+
+void ClipboardRecentContentIOS::SaveToUserDefaults() {
+  [[NSUserDefaults standardUserDefaults] setInteger:lastPasteboardChangeCount_
+                                             forKey:kPasteboardChangeCountKey];
+  [[NSUserDefaults standardUserDefaults] setObject:lastPasteboardChangeDate_
+                                            forKey:kPasteboardChangeDateKey];
+}
diff --git a/components/open_from_clipboard/clipboard_url_provider.cc b/components/open_from_clipboard/clipboard_url_provider.cc
new file mode 100644
index 0000000..33fdf38
--- /dev/null
+++ b/components/open_from_clipboard/clipboard_url_provider.cc
@@ -0,0 +1,59 @@
+// 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 "components/open_from_clipboard/clipboard_url_provider.h"
+
+#include "base/logging.h"
+#include "base/strings/utf_string_conversions.h"
+#include "components/omnibox/autocomplete_input.h"
+#include "components/open_from_clipboard/clipboard_recent_content.h"
+#include "grit/components_strings.h"
+#include "ui/base/l10n/l10n_util.h"
+
+namespace {
+// Score defining the priority of the suggestion. 1600 guarantees that the
+// suggestion will always be first. See table in autocomplete_provider.h.
+const int kClipboardURLProviderRelevance = 1600;
+}  // namespace
+
+ClipboardURLProvider::ClipboardURLProvider(
+    ClipboardRecentContent* clipboard_recent_content)
+    : AutocompleteProvider(AutocompleteProvider::TYPE_SHORTCUTS),
+      clipboard_recent_content_(clipboard_recent_content) {
+  DCHECK(clipboard_recent_content_);
+}
+
+ClipboardURLProvider::~ClipboardURLProvider() {
+}
+
+void ClipboardURLProvider::Start(const AutocompleteInput& input,
+                                 bool minimal_changes,
+                                 bool called_due_to_focus) {
+  matches_.clear();
+  // Attempt to add an AutocompleteMatch only if the user has not entered
+  // anything in the omnibox.
+  if (input.cursor_position() == base::string16::npos) {
+    GURL url;
+    if (clipboard_recent_content_->GetRecentURLFromClipboard(&url)) {
+      DCHECK(url.is_valid());
+      AutocompleteMatch match(this, kClipboardURLProviderRelevance, false,
+                              AutocompleteMatchType::NAVSUGGEST_PERSONALIZED);
+      match.allowed_to_be_default_match = true;
+      match.destination_url = url;
+      match.contents.assign(base::UTF8ToUTF16(url.spec()));
+      AutocompleteMatch::ClassifyLocationInString(
+          base::string16::npos, 0, match.contents.length(),
+          ACMatchClassification::URL, &match.contents_class);
+
+      match.description.assign(
+          l10n_util::GetStringUTF16(IDS_LINK_FROM_CLIPBOARD));
+      AutocompleteMatch::ClassifyLocationInString(
+          base::string16::npos, 0, match.description.length(),
+          ACMatchClassification::URL, &match.description_class);
+
+      matches_.push_back(match);
+    }
+  }
+  done_ = true;
+}
diff --git a/components/open_from_clipboard/clipboard_url_provider.h b/components/open_from_clipboard/clipboard_url_provider.h
new file mode 100644
index 0000000..38122cc
--- /dev/null
+++ b/components/open_from_clipboard/clipboard_url_provider.h
@@ -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.
+
+#ifndef COMPONENTS_OPEN_FROM_CLIPBOARD_CLIPBOARD_URL_PROVIDER_H_
+#define COMPONENTS_OPEN_FROM_CLIPBOARD_CLIPBOARD_URL_PROVIDER_H_
+
+#include "components/omnibox/autocomplete_provider.h"
+
+class ClipboardRecentContent;
+
+// Autocomplete provider offering content based on the clipboard's content.
+class ClipboardURLProvider : public AutocompleteProvider {
+ public:
+  ClipboardURLProvider(ClipboardRecentContent* clipboard_recent_content);
+  void Start(const AutocompleteInput& input,
+             bool minimal_changes,
+             bool called_due_to_focus) override;
+
+ private:
+  ~ClipboardURLProvider() override;
+  ClipboardRecentContent* clipboard_recent_content_;
+
+  DISALLOW_COPY_AND_ASSIGN(ClipboardURLProvider);
+};
+
+#endif  // COMPONENTS_OPEN_FROM_CLIPBOARD_CLIPBOARD_URL_PROVIDER_H_
diff --git a/components/open_from_clipboard_strings.grdp b/components/open_from_clipboard_strings.grdp
new file mode 100644
index 0000000..c6ec2b5
--- /dev/null
+++ b/components/open_from_clipboard_strings.grdp
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<grit-part>
+  <message name="IDS_LINK_FROM_CLIPBOARD" desc="The label in the omnibox dropdown explaining that the link has been extracted from the user's clipboard. [Length: 21em]">
+    Link you copied
+  </message>
+</grit-part>
diff --git a/components/ownership/BUILD.gn b/components/ownership/BUILD.gn
index 0432b94..5acce97 100644
--- a/components/ownership/BUILD.gn
+++ b/components/ownership/BUILD.gn
@@ -32,6 +32,8 @@
     "owner_key_util_impl_unittest.cc",
   ]
 
+  configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
+
   deps = [
     ":ownership",
     "//testing/gtest",
diff --git a/components/packed_ct_ev_whitelist/BUILD.gn b/components/packed_ct_ev_whitelist/BUILD.gn
index ae9561ea..c3ca36c 100644
--- a/components/packed_ct_ev_whitelist/BUILD.gn
+++ b/components/packed_ct_ev_whitelist/BUILD.gn
@@ -10,16 +10,13 @@
     "packed_ct_ev_whitelist.h",
   ]
 
+  configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
+
   deps = [
     "//base",
     "//content",
     "//net",
   ]
-
-  if (is_win) {
-    # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
-    cflags = [ "/wd4267" ]
-  }
 }
 
 source_set("unit_tests") {
diff --git a/components/pairing/bluetooth_host_pairing_controller.cc b/components/pairing/bluetooth_host_pairing_controller.cc
index 5a8c8aa..5ffd2f5 100644
--- a/components/pairing/bluetooth_host_pairing_controller.cc
+++ b/components/pairing/bluetooth_host_pairing_controller.cc
@@ -59,7 +59,6 @@
     : current_stage_(STAGE_NONE),
       update_status_(UPDATE_STATUS_UNKNOWN),
       enrollment_status_(ENROLLMENT_STATUS_UNKNOWN),
-      device_(NULL),
       proto_decoder_(new ProtoDecoder(this)),
       ptr_factory_(this) {
 }
diff --git a/components/pairing/bluetooth_host_pairing_controller.h b/components/pairing/bluetooth_host_pairing_controller.h
index 9c50f5c1..b99af24d 100644
--- a/components/pairing/bluetooth_host_pairing_controller.h
+++ b/components/pairing/bluetooth_host_pairing_controller.h
@@ -103,7 +103,6 @@
   EnrollmentStatus enrollment_status_;
 
   scoped_refptr<device::BluetoothAdapter> adapter_;
-  device::BluetoothDevice* device_;
   scoped_refptr<device::BluetoothSocket> service_socket_;
   scoped_refptr<device::BluetoothSocket> controller_socket_;
   scoped_ptr<ProtoDecoder> proto_decoder_;
diff --git a/components/password_manager/content/browser/content_password_manager_driver.cc b/components/password_manager/content/browser/content_password_manager_driver.cc
index 5f70c80d..80b53b2 100644
--- a/components/password_manager/content/browser/content_password_manager_driver.cc
+++ b/components/password_manager/content/browser/content_password_manager_driver.cc
@@ -29,7 +29,7 @@
     : render_frame_host_(render_frame_host),
       client_(client),
       password_generation_manager_(client, this),
-      password_autofill_manager_(client, this, autofill_client),
+      password_autofill_manager_(this, autofill_client),
       next_free_key_(0) {
 }
 
diff --git a/components/password_manager/content/browser/credential_manager_dispatcher.cc b/components/password_manager/content/browser/credential_manager_dispatcher.cc
index 0a120127..ba7ea40 100644
--- a/components/password_manager/content/browser/credential_manager_dispatcher.cc
+++ b/components/password_manager/content/browser/credential_manager_dispatcher.cc
@@ -99,6 +99,11 @@
                         zero_click_form_to_return->federation_url.is_empty()
                             ? CredentialType::CREDENTIAL_TYPE_LOCAL
                             : CredentialType::CREDENTIAL_TYPE_FEDERATED);
+    auto it = std::find(local_results.begin(), local_results.end(),
+                        zero_click_form_to_return);
+    DCHECK(it != local_results.end());
+    std::swap(*it, local_results[0]);
+    dispatcher_->client()->NotifyUserAutoSignin(local_results.Pass());
     dispatcher_->SendCredential(id_, info);
     return;
   }
@@ -225,10 +230,10 @@
       client_, GetDriver(), *form, this));
 }
 
-void CredentialManagerDispatcher::OnProvisionalSaveComplete(
-    CredentialSourceType type) {
+void CredentialManagerDispatcher::OnProvisionalSaveComplete() {
   DCHECK(form_manager_);
-  client_->PromptUserToSavePassword(form_manager_.Pass(), type);
+  client_->PromptUserToSavePassword(
+      form_manager_.Pass(), CredentialSourceType::CREDENTIAL_SOURCE_API);
 }
 
 void CredentialManagerDispatcher::OnNotifySignedOut(int request_id) {
diff --git a/components/password_manager/content/browser/credential_manager_dispatcher.h b/components/password_manager/content/browser/credential_manager_dispatcher.h
index b13d6fe..87b5f66 100644
--- a/components/password_manager/content/browser/credential_manager_dispatcher.h
+++ b/components/password_manager/content/browser/credential_manager_dispatcher.h
@@ -24,7 +24,6 @@
 }
 
 namespace password_manager {
-enum class CredentialSourceType;
 class CredentialManagerPasswordFormManager;
 class PasswordManagerClient;
 class PasswordManagerDriver;
@@ -37,7 +36,7 @@
                               PasswordManagerClient* client);
   ~CredentialManagerDispatcher() override;
 
-  void OnProvisionalSaveComplete(CredentialSourceType type);
+  void OnProvisionalSaveComplete();
 
   // Called in response to an IPC from the renderer, triggered by a page's call
   // to 'navigator.credentials.notifyFailedSignIn'.
diff --git a/components/password_manager/content/browser/credential_manager_dispatcher_unittest.cc b/components/password_manager/content/browser/credential_manager_dispatcher_unittest.cc
index 706f4348..20085fe 100644
--- a/components/password_manager/content/browser/credential_manager_dispatcher_unittest.cc
+++ b/components/password_manager/content/browser/credential_manager_dispatcher_unittest.cc
@@ -36,9 +36,9 @@
     : public password_manager::StubPasswordManagerClient {
  public:
   TestPasswordManagerClient(password_manager::PasswordStore* store)
-      : did_pass_credential_source_type_(false),
-        did_prompt_user_to_save_(false),
+      : did_prompt_user_to_save_(false),
         did_prompt_user_to_choose_(false),
+        did_prompt_auto_signin_(false),
         is_off_the_record_(false),
         store_(store) {
     prefs_.registry()->RegisterBooleanPref(
@@ -56,11 +56,9 @@
       scoped_ptr<password_manager::PasswordFormManager> manager,
       password_manager::CredentialSourceType type) override {
     did_prompt_user_to_save_ = true;
-    did_pass_credential_source_type_ =
-        (type == password_manager::CredentialSourceType::
-                     CREDENTIAL_SOURCE_PASSWORD_MANAGER ||
-         type == password_manager::CredentialSourceType::CREDENTIAL_SOURCE_API);
-    manager_.reset(manager.release());
+    EXPECT_EQ(password_manager::CredentialSourceType::CREDENTIAL_SOURCE_API,
+              type);
+    manager_.swap(manager);
     return true;
   }
 
@@ -82,13 +80,17 @@
     return true;
   }
 
+  void NotifyUserAutoSignin(
+      ScopedVector<autofill::PasswordForm> local_forms) override {
+    EXPECT_FALSE(local_forms.empty());
+    did_prompt_auto_signin_ = true;
+  }
+
   bool IsOffTheRecord() const override { return is_off_the_record_; }
 
-  bool did_pass_credential_source_type() const {
-    return did_pass_credential_source_type_;
-  }
   bool did_prompt_user_to_save() const { return did_prompt_user_to_save_; }
   bool did_prompt_user_to_choose() const { return did_prompt_user_to_choose_; }
+  bool did_prompt_auto_signin() const { return did_prompt_auto_signin_; }
 
   password_manager::PasswordFormManager* pending_manager() const {
     return manager_.get();
@@ -105,12 +107,14 @@
 
  private:
   TestingPrefServiceSimple prefs_;
-  bool did_pass_credential_source_type_;
   bool did_prompt_user_to_save_;
   bool did_prompt_user_to_choose_;
+  bool did_prompt_auto_signin_;
   bool is_off_the_record_;
   password_manager::PasswordStore* store_;
   scoped_ptr<password_manager::PasswordFormManager> manager_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestPasswordManagerClient);
 };
 
 class TestCredentialManagerDispatcher
@@ -238,7 +242,6 @@
   RunAllPendingTasks();
 
   EXPECT_TRUE(client_->did_prompt_user_to_save());
-  EXPECT_TRUE(client_->did_pass_credential_source_type());
   EXPECT_TRUE(client_->pending_manager()->HasCompletedMatching());
 
   autofill::PasswordForm new_form =
@@ -313,6 +316,7 @@
   EXPECT_EQ(CredentialType::CREDENTIAL_TYPE_EMPTY, get<1>(param).type);
   process()->sink().ClearMessages();
   EXPECT_FALSE(client_->did_prompt_user_to_choose());
+  EXPECT_FALSE(client_->did_prompt_auto_signin());
 }
 
 TEST_F(CredentialManagerDispatcherTest,
@@ -333,6 +337,7 @@
   EXPECT_EQ(CredentialType::CREDENTIAL_TYPE_EMPTY, get<1>(param).type);
   process()->sink().ClearMessages();
   EXPECT_FALSE(client_->did_prompt_user_to_choose());
+  EXPECT_FALSE(client_->did_prompt_auto_signin());
 }
 
 TEST_F(CredentialManagerDispatcherTest,
@@ -350,6 +355,7 @@
       process()->sink().GetFirstMessageMatching(kMsgID);
   EXPECT_TRUE(message);
   EXPECT_TRUE(client_->did_prompt_user_to_choose());
+  EXPECT_FALSE(client_->did_prompt_auto_signin());
 }
 
 TEST_F(
@@ -365,6 +371,7 @@
       process()->sink().GetFirstMessageMatching(kMsgID);
   EXPECT_TRUE(message);
   EXPECT_FALSE(client_->did_prompt_user_to_choose());
+  EXPECT_FALSE(client_->did_prompt_auto_signin());
   CredentialManagerMsg_SendCredential::Param send_param;
   CredentialManagerMsg_SendCredential::Read(message, &send_param);
   EXPECT_EQ(CredentialType::CREDENTIAL_TYPE_EMPTY, get<1>(send_param).type);
@@ -384,6 +391,7 @@
       process()->sink().GetFirstMessageMatching(kMsgID);
   EXPECT_TRUE(message);
   EXPECT_FALSE(client_->did_prompt_user_to_choose());
+  EXPECT_TRUE(client_->did_prompt_auto_signin());
   CredentialManagerMsg_SendCredential::Param send_param;
   CredentialManagerMsg_SendCredential::Read(message, &send_param);
   EXPECT_EQ(CredentialType::CREDENTIAL_TYPE_LOCAL, get<1>(send_param).type);
@@ -404,6 +412,7 @@
       process()->sink().GetFirstMessageMatching(kMsgID);
   EXPECT_TRUE(message);
   EXPECT_FALSE(client_->did_prompt_user_to_choose());
+  EXPECT_FALSE(client_->did_prompt_auto_signin());
   CredentialManagerMsg_SendCredential::Param send_param;
   CredentialManagerMsg_SendCredential::Read(message, &send_param);
 
@@ -427,6 +436,7 @@
       process()->sink().GetFirstMessageMatching(kMsgID);
   EXPECT_TRUE(message);
   EXPECT_FALSE(client_->did_prompt_user_to_choose());
+  EXPECT_TRUE(client_->did_prompt_auto_signin());
   CredentialManagerMsg_SendCredential::Param send_param;
   CredentialManagerMsg_SendCredential::Read(message, &send_param);
 
@@ -454,6 +464,7 @@
       process()->sink().GetFirstMessageMatching(kMsgID);
   EXPECT_TRUE(message);
   EXPECT_FALSE(client_->did_prompt_user_to_choose());
+  EXPECT_FALSE(client_->did_prompt_auto_signin());
   CredentialManagerMsg_SendCredential::Param send_param;
   CredentialManagerMsg_SendCredential::Read(message, &send_param);
 
@@ -481,6 +492,7 @@
   EXPECT_EQ(blink::WebCredentialManagerError::ErrorTypePendingRequest,
             get<1>(reject_param));
   EXPECT_FALSE(client_->did_prompt_user_to_choose());
+  EXPECT_FALSE(client_->did_prompt_auto_signin());
 
   process()->sink().ClearMessages();
 
@@ -496,6 +508,7 @@
   EXPECT_NE(CredentialType::CREDENTIAL_TYPE_EMPTY, get<1>(send_param).type);
   process()->sink().ClearMessages();
   EXPECT_TRUE(client_->did_prompt_user_to_choose());
+  EXPECT_FALSE(client_->did_prompt_auto_signin());
 }
 
 TEST_F(CredentialManagerDispatcherTest, ResetSkipZeroClickAfterPrompt) {
@@ -531,6 +544,7 @@
   // credential (and have the global zero-click flag enabled), we make sure that
   // they'll be logged in again next time.
   EXPECT_TRUE(client_->did_prompt_user_to_choose());
+  EXPECT_FALSE(client_->did_prompt_auto_signin());
   passwords = store_->stored_passwords();
   EXPECT_EQ(2U, passwords.size());
   EXPECT_EQ(1U, passwords[form_.signon_realm].size());
@@ -539,7 +553,7 @@
   EXPECT_TRUE(passwords[cross_origin_form_.signon_realm][0].skip_zero_click);
 }
 
-TEST_F(CredentialManagerDispatcherTest, IncognitoRequestCredential) {
+TEST_F(CredentialManagerDispatcherTest, IncognitoZeroClickRequestCredential) {
   client_->set_off_the_record(true);
   store_->AddLogin(form_);
 
@@ -556,6 +570,7 @@
   CredentialManagerMsg_SendCredential::Read(message, &param);
   EXPECT_EQ(CredentialType::CREDENTIAL_TYPE_EMPTY, get<1>(param).type);
   EXPECT_FALSE(client_->did_prompt_user_to_choose());
+  EXPECT_FALSE(client_->did_prompt_auto_signin());
 }
 
 }  // namespace password_manager
diff --git a/components/password_manager/content/browser/credential_manager_password_form_manager.cc b/components/password_manager/content/browser/credential_manager_password_form_manager.cc
index 15e1351..66539bb2 100644
--- a/components/password_manager/content/browser/credential_manager_password_form_manager.cc
+++ b/components/password_manager/content/browser/credential_manager_password_form_manager.cc
@@ -39,8 +39,7 @@
   PasswordForm provisionally_saved_form(observed_form());
   provisionally_saved_form.preferred = true;
   ProvisionallySave(provisionally_saved_form, IGNORE_OTHER_POSSIBLE_USERNAMES);
-  dispatcher_->OnProvisionalSaveComplete(
-      CredentialSourceType::CREDENTIAL_SOURCE_API);
+  dispatcher_->OnProvisionalSaveComplete();
 }
 
 }  // namespace password_manager
diff --git a/components/password_manager/core/browser/BUILD.gn b/components/password_manager/core/browser/BUILD.gn
index d6601bf7..3d06f74 100644
--- a/components/password_manager/core/browser/BUILD.gn
+++ b/components/password_manager/core/browser/BUILD.gn
@@ -99,11 +99,10 @@
     # TODO(blundell): Provide the iOS login DB implementation and then
     # also exclude the POSIX one from iOS. http://crbug.com/341429
     sources -= [ "login_database_posix.cc" ]
-  } else if (is_win) {
-    # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
-    cflags = [ "/wd4267" ]
   }
 
+  # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+  configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
   public_configs = [ ":password_manager_config" ]
 
   # Sync (not supported in Android WebView).
diff --git a/components/password_manager/core/browser/password_autofill_manager.cc b/components/password_manager/core/browser/password_autofill_manager.cc
index c67bd8d..3095dd3 100644
--- a/components/password_manager/core/browser/password_autofill_manager.cc
+++ b/components/password_manager/core/browser/password_autofill_manager.cc
@@ -15,7 +15,6 @@
 #include "components/autofill/core/browser/suggestion.h"
 #include "components/autofill/core/common/autofill_constants.h"
 #include "components/autofill/core/common/autofill_data_validation.h"
-#include "components/password_manager/core/browser/password_manager_client.h"
 #include "components/password_manager/core/browser/password_manager_driver.h"
 #include "components/strings/grit/components_strings.h"
 #include "grit/components_strings.h"
@@ -91,11 +90,9 @@
 // PasswordAutofillManager, public:
 
 PasswordAutofillManager::PasswordAutofillManager(
-    PasswordManagerClient* password_manager_client,
     PasswordManagerDriver* password_manager_driver,
     autofill::AutofillClient* autofill_client)
-    : password_manager_client_(password_manager_client),
-      password_manager_driver_(password_manager_driver),
+    : password_manager_driver_(password_manager_driver),
       autofill_client_(autofill_client),
       weak_ptr_factory_(this) {
 }
diff --git a/components/password_manager/core/browser/password_autofill_manager.h b/components/password_manager/core/browser/password_autofill_manager.h
index a699d547..dfe0400 100644
--- a/components/password_manager/core/browser/password_autofill_manager.h
+++ b/components/password_manager/core/browser/password_autofill_manager.h
@@ -19,14 +19,12 @@
 
 namespace password_manager {
 
-class PasswordManagerClient;
 class PasswordManagerDriver;
 
 // This class is responsible for filling password forms.
 class PasswordAutofillManager : public autofill::AutofillPopupDelegate {
  public:
-  PasswordAutofillManager(PasswordManagerClient* password_manager_client,
-                          PasswordManagerDriver* password_manager_driver,
+  PasswordAutofillManager(PasswordManagerDriver* password_manager_driver,
                           autofill::AutofillClient* autofill_client);
   virtual ~PasswordAutofillManager();
 
@@ -91,9 +89,6 @@
   // right password info in |login_to_password_info_|.
   int form_data_key_;
 
-  // Provides embedder-level operations on passwords. Must outlive |this|.
-  PasswordManagerClient* const password_manager_client_;  // weak
-
   // The driver that owns |this|.
   PasswordManagerDriver* password_manager_driver_;
 
diff --git a/components/password_manager/core/browser/password_autofill_manager_unittest.cc b/components/password_manager/core/browser/password_autofill_manager_unittest.cc
index 81235d63..2e9df20 100644
--- a/components/password_manager/core/browser/password_autofill_manager_unittest.cc
+++ b/components/password_manager/core/browser/password_autofill_manager_unittest.cc
@@ -94,8 +94,8 @@
   void InitializePasswordAutofillManager(
       TestPasswordManagerClient* client,
       autofill::AutofillClient* autofill_client) {
-    password_autofill_manager_.reset(new PasswordAutofillManager(
-        client, client->mock_driver(), autofill_client));
+    password_autofill_manager_.reset(
+        new PasswordAutofillManager(client->mock_driver(), autofill_client));
     password_autofill_manager_->OnAddPasswordFormMapping(fill_data_id_,
                                                          fill_data_);
   }
diff --git a/components/password_manager/core/browser/password_generation_manager_unittest.cc b/components/password_manager/core/browser/password_generation_manager_unittest.cc
index 01bb377..6f34c40 100644
--- a/components/password_manager/core/browser/password_generation_manager_unittest.cc
+++ b/components/password_manager/core/browser/password_generation_manager_unittest.cc
@@ -34,7 +34,7 @@
   TestPasswordManagerDriver(PasswordManagerClient* client)
       : password_manager_(client),
         password_generation_manager_(client, this),
-        password_autofill_manager_(client, this, NULL) {}
+        password_autofill_manager_(this, NULL) {}
   ~TestPasswordManagerDriver() override {}
 
   // PasswordManagerDriver implementation.
diff --git a/components/password_manager/core/browser/password_manager_client.h b/components/password_manager/core/browser/password_manager_client.h
index 737c50a..c119d237 100644
--- a/components/password_manager/core/browser/password_manager_client.h
+++ b/components/password_manager/core/browser/password_manager_client.h
@@ -98,6 +98,13 @@
       const GURL& origin,
       base::Callback<void(const CredentialInfo&)> callback) = 0;
 
+  // Informs the embedder that automatic signing in just happened. The form
+  // returned to the site is |local_forms[0]|. |local_forms| and
+  // |federated_forms| contain all the local and federated credentials for the
+  // site.
+  virtual void NotifyUserAutoSignin(
+      ScopedVector<autofill::PasswordForm> local_forms) = 0;
+
   // Called when a password is saved in an automated fashion. Embedder may
   // inform the user that this save has occured.
   virtual void AutomaticPasswordSave(
diff --git a/components/password_manager/core/browser/password_manager_metrics_util.h b/components/password_manager/core/browser/password_manager_metrics_util.h
index d4ea6c3..eb771b00b 100644
--- a/components/password_manager/core/browser/password_manager_metrics_util.h
+++ b/components/password_manager/core/browser/password_manager_metrics_util.h
@@ -30,6 +30,7 @@
   MANUAL_BLACKLISTED,
   AUTOMATIC_GENERATED_PASSWORD_CONFIRMATION,
   AUTOMATIC_CREDENTIAL_REQUEST,
+  AUTOMATIC_SIGNIN_TOAST,
   NUM_DISPLAY_DISPOSITIONS
 };
 
@@ -48,6 +49,7 @@
   CLICKED_CREDENTIAL,
   CLICKED_COLLECT_URL,
   CLICKED_DO_NOT_COLLECT_URL,
+  AUTO_SIGNIN_TOAST_TIMEOUT,
   NUM_UI_RESPONSES,
 
   // If we add the omnibox icon _without_ intending to display the bubble,
diff --git a/components/password_manager/core/browser/password_manager_unittest.cc b/components/password_manager/core/browser/password_manager_unittest.cc
index c85940a08..36ef7afa 100644
--- a/components/password_manager/core/browser/password_manager_unittest.cc
+++ b/components/password_manager/core/browser/password_manager_unittest.cc
@@ -111,7 +111,7 @@
 
     manager_.reset(new TestPasswordManager(&client_));
     password_autofill_manager_.reset(
-        new PasswordAutofillManager(&client_, client_.GetDriver(), NULL));
+        new PasswordAutofillManager(client_.GetDriver(), NULL));
 
     EXPECT_CALL(driver_, GetPasswordManager())
         .WillRepeatedly(Return(manager_.get()));
diff --git a/components/password_manager/core/browser/password_store.cc b/components/password_manager/core/browser/password_store.cc
index f1f3ab5..8a5792c 100644
--- a/components/password_manager/core/browser/password_store.cc
+++ b/components/password_manager/core/browser/password_store.cc
@@ -12,7 +12,6 @@
 #include "base/stl_util.h"
 #include "components/autofill/core/common/password_form.h"
 #include "components/password_manager/core/browser/password_store_consumer.h"
-#include "components/password_manager/core/browser/password_syncable_service.h"
 
 #if defined(PASSWORD_MANAGER_ENABLE_SYNC)
 #include "components/password_manager/core/browser/password_syncable_service.h"
diff --git a/components/password_manager/core/browser/stub_password_manager_client.cc b/components/password_manager/core/browser/stub_password_manager_client.cc
index 42a9b03..09587d87 100644
--- a/components/password_manager/core/browser/stub_password_manager_client.cc
+++ b/components/password_manager/core/browser/stub_password_manager_client.cc
@@ -44,6 +44,10 @@
   return false;
 }
 
+void StubPasswordManagerClient::NotifyUserAutoSignin(
+    ScopedVector<autofill::PasswordForm> local_forms) {
+}
+
 void StubPasswordManagerClient::AutomaticPasswordSave(
     scoped_ptr<PasswordFormManager> saved_manager) {
 }
diff --git a/components/password_manager/core/browser/stub_password_manager_client.h b/components/password_manager/core/browser/stub_password_manager_client.h
index 3d5c3c5..4fd82d74 100644
--- a/components/password_manager/core/browser/stub_password_manager_client.h
+++ b/components/password_manager/core/browser/stub_password_manager_client.h
@@ -31,6 +31,8 @@
       const GURL& origin,
       base::Callback<void(const password_manager::CredentialInfo&)> callback)
       override;
+  void NotifyUserAutoSignin(
+      ScopedVector<autofill::PasswordForm> local_forms) override;
   void AutomaticPasswordSave(
       scoped_ptr<PasswordFormManager> saved_manager) override;
   PrefService* GetPrefs() override;
diff --git a/components/password_manager/core/common/password_manager_ui.h b/components/password_manager/core/common/password_manager_ui.h
index eb10225..b8fe37c2 100644
--- a/components/password_manager/core/common/password_manager_ui.h
+++ b/components/password_manager/core/common/password_manager_ui.h
@@ -34,6 +34,10 @@
   // The site has asked user to choose a credential.
   CREDENTIAL_REQUEST_STATE,
 
+  // The user was auto signed in to the site. The icon and the auto-signin toast
+  // should be visible.
+  AUTO_SIGNIN_STATE,
+
   // Password manager failed to detect the form, bubble should outlive next
   // navigation.
   ASK_USER_REPORT_URL_BUBBLE_SHOWN_BEFORE_TRANSITION_STATE,
diff --git a/components/pdf/renderer/BUILD.gn b/components/pdf/renderer/BUILD.gn
index 3ec8040e..3a1554e 100644
--- a/components/pdf/renderer/BUILD.gn
+++ b/components/pdf/renderer/BUILD.gn
@@ -14,6 +14,9 @@
     "ppb_pdf_impl.h",
   ]
 
+  # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+  configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
+
   deps = [
     "//base",
     "//components/pdf/common",
@@ -26,8 +29,4 @@
     "//v8",
     "//third_party/WebKit/public:blink_minimal",
   ]
-
-  if (is_win) {
-    cflags = [ "/wd4267" ]  # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
-  }
 }
diff --git a/components/policy/BUILD.gn b/components/policy/BUILD.gn
index 632cbe67..c8266de 100644
--- a/components/policy/BUILD.gn
+++ b/components/policy/BUILD.gn
@@ -89,7 +89,7 @@
       app_resources_path,
     ]
 
-    if (os != "android") {
+    if (current_os != "android") {
       outputs -= [
         app_restrictions_path,
         app_resources_path,
@@ -111,7 +111,7 @@
           rebase_path(app_restrictions_path, root_build_dir),
       "--app-restrictions-resources=" +
           rebase_path(app_resources_path, root_build_dir),
-      os,
+      current_os,
       chromeos_flag,
       rebase_path("resources/policy_templates.json", root_build_dir),
     ]
diff --git a/components/policy/core/browser/BUILD.gn b/components/policy/core/browser/BUILD.gn
index 6de6120..d9f1a630 100644
--- a/components/policy/core/browser/BUILD.gn
+++ b/components/policy/core/browser/BUILD.gn
@@ -54,7 +54,6 @@
     ]
 
     deps += [
-      "//components/autofill/core/browser",
       "//components/autofill/core/common",
       "//components/policy",
       "//components/policy/proto",
diff --git a/components/policy/core/common/policy_loader_ios_unittest.mm b/components/policy/core/common/policy_loader_ios_unittest.mm
index f77893a4..f22d37f 100644
--- a/components/policy/core/common/policy_loader_ios_unittest.mm
+++ b/components/policy/core/common/policy_loader_ios_unittest.mm
@@ -7,7 +7,6 @@
 #include "base/basictypes.h"
 #include "base/callback.h"
 #include "base/files/file_path.h"
-#include "base/ios/ios_util.h"
 #include "base/strings/sys_string_conversions.h"
 #include "base/test/test_simple_task_runner.h"
 #include "base/values.h"
diff --git a/components/policy/core/common/policy_service_stub.h b/components/policy/core/common/policy_service_stub.h
index 532bf521..c049b0d 100644
--- a/components/policy/core/common/policy_service_stub.h
+++ b/components/policy/core/common/policy_service_stub.h
@@ -17,20 +17,20 @@
 class POLICY_EXPORT PolicyServiceStub : public PolicyService {
  public:
   PolicyServiceStub();
-  virtual ~PolicyServiceStub();
+  ~PolicyServiceStub() override;
 
-  virtual void AddObserver(PolicyDomain domain,
-                           Observer* observer) override;
+  void AddObserver(PolicyDomain domain,
+                   Observer* observer) override;
 
-  virtual void RemoveObserver(PolicyDomain domain,
-                              Observer* observer) override;
+  void RemoveObserver(PolicyDomain domain,
+                      Observer* observer) override;
 
-  virtual const PolicyMap& GetPolicies(
+  const PolicyMap& GetPolicies(
       const PolicyNamespace& ns) const override;
 
-  virtual bool IsInitializationComplete(PolicyDomain domain) const override;
+  bool IsInitializationComplete(PolicyDomain domain) const override;
 
-  virtual void RefreshPolicies(const base::Closure& callback) override;
+  void RefreshPolicies(const base::Closure& callback) override;
  private:
   const PolicyMap kEmpty_;
 
diff --git a/components/policy/policy_browser.gypi b/components/policy/policy_browser.gypi
index 987da77..dc42169e 100644
--- a/components/policy/policy_browser.gypi
+++ b/components/policy/policy_browser.gypi
@@ -34,7 +34,8 @@
     # GN version: //components/policy/core/browser
     ['configuration_policy==1', {
       'dependencies': [
-        'autofill_core_browser',
+        '../third_party/icu/icu.gyp:icui18n',
+        '../third_party/icu/icu.gyp:icuuc',
         'autofill_core_common',
         'cloud_policy_proto',
         'policy',
diff --git a/components/policy/resources/policy_templates.json b/components/policy/resources/policy_templates.json
index 906542f..9f18e244 100644
--- a/components/policy/resources/policy_templates.json
+++ b/components/policy/resources/policy_templates.json
@@ -1192,7 +1192,7 @@
       If you set this policy, you can configure whether a user is allowed to sign in to <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph>. Setting this policy to 'False' will prevent apps and extensions that use the chrome.identity API from functioning, so you may want to use SyncDisabled instead.''',
     },
     {
-      'name': 'EnableWebBasedSignin',
+      'name': 'EnableDeprecatedWebBasedSignin',
       'type': 'main',
       'schema': { 'type': 'boolean' },
       'supported_on': ['chrome.*:35-42'],
@@ -1206,8 +1206,7 @@
       'caption': '''Enables the old web-based signin''',
       'desc': '''Enables the old web-based signin flow.
 
-      This setting should no longer be needed as of Chrome 41 and support will be removed entirely in Chrome 43.
-      This setting will be renamed in Chrome 42 as part of the deprecation process.
+      This setting was named EnableWebBasedSignin prior to Chrome 42, and support for it will be removed entirely in Chrome 43.
 
       This setting is useful for enterprise customers who are using SSO solutions that are not compatible with the new inline signin flow yet.
       If you enable this setting, the old web-based signin flow would be used.
diff --git a/components/printing/renderer/BUILD.gn b/components/printing/renderer/BUILD.gn
index 768c95b..c31bb8e 100644
--- a/components/printing/renderer/BUILD.gn
+++ b/components/printing/renderer/BUILD.gn
@@ -12,6 +12,9 @@
     "print_web_view_helper_pdf_win.cc",
   ]
 
+  # TODO(dgn): crbug.com/167187 fix size_t to int truncations.
+  configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
+
   deps = [
     "//base",
     "//components/printing/common:printing_common",
@@ -23,9 +26,4 @@
     "//third_party/WebKit/public:blink",
     "//ui/base",
   ]
-
-  if (is_win) {
-    # TODO(dgn): crbug.com/167187 fix size_t to int truncations.
-    cflags = [ "/wd4267" ]
-  }
 }
diff --git a/components/proximity_auth/BUILD.gn b/components/proximity_auth/BUILD.gn
index a859826..84f827b 100644
--- a/components/proximity_auth/BUILD.gn
+++ b/components/proximity_auth/BUILD.gn
@@ -54,6 +54,8 @@
     "wire_message_unittest.cc",
   ]
 
+  configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
+
   deps = [
     ":proximity_auth",
     "cryptauth:unit_tests",
diff --git a/components/renderer_context_menu/render_view_context_menu_base.cc b/components/renderer_context_menu/render_view_context_menu_base.cc
index cda6c46..d6472df 100644
--- a/components/renderer_context_menu/render_view_context_menu_base.cc
+++ b/components/renderer_context_menu/render_view_context_menu_base.cc
@@ -15,6 +15,9 @@
 #include "content/public/browser/render_widget_host_view.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/menu_item.h"
+#if defined(ENABLE_EXTENSIONS)
+#include "extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.h"
+#endif
 #include "third_party/WebKit/public/web/WebContextMenuData.h"
 
 using blink::WebContextMenuData;
@@ -126,6 +129,17 @@
   }
 }
 
+content::WebContents* GetWebContentsToUse(content::WebContents* web_contents) {
+// If we're viewing in a MimeHandlerViewGuest, use its embedder WebContents.
+#if defined(ENABLE_EXTENSIONS)
+  auto guest_view =
+      extensions::MimeHandlerViewGuest::FromWebContents(web_contents);
+  if (guest_view)
+    return guest_view->embedder_web_contents();
+#endif
+  return web_contents;
+}
+
 }  // namespace
 
 // static
@@ -155,6 +169,7 @@
     const content::ContextMenuParams& params)
     : params_(params),
       source_web_contents_(WebContents::FromRenderFrameHost(render_frame_host)),
+      embedder_web_contents_(GetWebContentsToUse(source_web_contents_)),
       browser_context_(source_web_contents_->GetBrowserContext()),
       menu_model_(this),
       render_frame_id_(render_frame_host->GetRoutingID()),
diff --git a/components/renderer_context_menu/render_view_context_menu_base.h b/components/renderer_context_menu/render_view_context_menu_base.h
index 779e8bb..d6d7bbd 100644
--- a/components/renderer_context_menu/render_view_context_menu_base.h
+++ b/components/renderer_context_menu/render_view_context_menu_base.h
@@ -163,6 +163,10 @@
 
   content::ContextMenuParams params_;
   content::WebContents* source_web_contents_;
+  // In the case of a MimeHandlerView this will point to the WebContents that
+  // embeds the MimeHandlerViewGuest. Otherwise this will be the same as
+  // source_web_contents_.
+  content::WebContents* embedder_web_contents_;
   content::BrowserContext* browser_context_;
 
   ui::SimpleMenuModel menu_model_;
diff --git a/components/search_engines/prepopulated_engines.json b/components/search_engines/prepopulated_engines.json
index eb0e8df..8037f57 100644
--- a/components/search_engines/prepopulated_engines.json
+++ b/components/search_engines/prepopulated_engines.json
@@ -30,7 +30,7 @@
 
     // Increment this if you change the data in ways that mean users with
     // existing data should get a new version.
-    "kCurrentDataVersion": 81
+    "kCurrentDataVersion": 82
   },
 
   // The following engines are included in country lists and are added to the
@@ -587,6 +587,8 @@
       "favicon_url": "http://yandex.st/lego/_/pDu9OWAQKB0s2J9IojKpiS_Eho.ico",
       "search_url": "http://yandex.ru/yandsearch?text={searchTerms}",
       "suggest_url": "http://suggest.yandex.net/suggest-ff.cgi?part={searchTerms}",
+      "image_url": "http://yandex.ru/images/search/?rpt=imageview",
+      "image_url_post_params": "upfile={google:imageThumbnail},original_width={google:imageOriginalWidth},original_height={google:imageOriginalHeight},prg=1",
       "type": "SEARCH_ENGINE_YANDEX",
       "id": 15
     },
@@ -597,6 +599,8 @@
       "favicon_url": "http://yastatic.net/islands-icons/_/6jyHGXR8-HAc8oJ1bU8qMUQQz_g.ico",
       "search_url": "http://www.yandex.com.tr/yandsearch?text={searchTerms}",
       "suggest_url": "http://suggest.yandex.com.tr/suggest-ff.cgi?part={searchTerms}",
+      "image_url": "http://yandex.com.tr/gorsel/search?rpt=imageview",
+      "image_url_post_params": "upfile={google:imageThumbnail},original_width={google:imageOriginalWidth},original_height={google:imageOriginalHeight},prg=1",
       "type": "SEARCH_ENGINE_YANDEX",
       "id": 15
     },
@@ -607,6 +611,8 @@
       "favicon_url": "http://yastatic.net/islands-icons/_/aKnllxm-gQhidpzbZqub7qe641g.ico",
       "search_url": "http://yandex.ua/yandsearch?text={searchTerms}",
       "suggest_url": "http://suggest.yandex.ua/suggest-ff.cgi?part={searchTerms}",
+      "image_url": "http://yandex.ua/images/search/?rpt=imageview",
+      "image_url_post_params": "upfile={google:imageThumbnail},original_width={google:imageOriginalWidth},original_height={google:imageOriginalHeight},prg=1",
       "type": "SEARCH_ENGINE_YANDEX",
       "id": 15
     },
diff --git a/components/search_engines/template_url.cc b/components/search_engines/template_url.cc
index a9b6c9b6..0071218 100644
--- a/components/search_engines/template_url.cc
+++ b/components/search_engines/template_url.cc
@@ -323,11 +323,10 @@
   // Encodes the post parameters.
   std::string* post_data = &post_content->second;
   post_data->clear();
-  for (PostParams::const_iterator param = post_params.begin();
-       param != post_params.end(); ++param) {
-    DCHECK(!param->first.empty());
-    net::AddMultipartValueForUpload(param->first, param->second, boundary,
-                                    std::string(), post_data);
+  for (const auto& param : post_params) {
+    DCHECK(!param.name.empty());
+    net::AddMultipartValueForUpload(param.name, param.value, boundary,
+                                    param.content_type, post_data);
   }
   net::AddMultipartFinalDelimiterForUpload(boundary, post_data);
   return true;
@@ -714,7 +713,8 @@
       size_t replacements_size = replacements->size();
       if (IsTemplateParameterString(value))
         ParseParameter(0, value.length() - 1, &value, replacements);
-      post_params->push_back(std::make_pair(parts[0], value));
+      PostParam param = { parts[0], value };
+      post_params->push_back(param);
       // If there was a replacement added, points its index to last added
       // PostParam.
       if (replacements->size() > replacements_size) {
@@ -797,8 +797,8 @@
   size_t pos = replacement.index;
   if (replacement.is_post_param) {
     DCHECK_LT(pos, post_params_.size());
-    DCHECK(!post_params_[pos].first.empty());
-    post_params_[pos].second = value;
+    DCHECK(!post_params_[pos].name.empty());
+    post_params_[pos].value = value;
   } else {
     url->insert(pos, name.empty() ? value : (name + "=" + value + "&"));
   }
@@ -1097,6 +1097,7 @@
       case GOOGLE_IMAGE_THUMBNAIL:
         HandleReplacement(
             std::string(), search_terms_args.image_thumbnail_content, *i, &url);
+        post_params_[i->index].content_type = "image/jpeg";
         break;
 
       case GOOGLE_IMAGE_URL:
diff --git a/components/search_engines/template_url.h b/components/search_engines/template_url.h
index a599793..840278a 100644
--- a/components/search_engines/template_url.h
+++ b/components/search_engines/template_url.h
@@ -285,6 +285,7 @@
 
  private:
   friend class TemplateURL;
+  friend class TemplateURLTest;
   FRIEND_TEST_ALL_PREFIXES(TemplateURLTest, SetPrepopulatedAndParse);
   FRIEND_TEST_ALL_PREFIXES(TemplateURLTest, ParseParameterKnown);
   FRIEND_TEST_ALL_PREFIXES(TemplateURLTest, ParseParameterUnknown);
@@ -344,10 +345,15 @@
     bool is_post_param;
   };
 
+  // Stores a single parameter for a POST.
+  struct PostParam {
+    std::string name;
+    std::string value;
+    std::string content_type;
+  };
+
   // The list of elements to replace.
   typedef std::vector<struct Replacement> Replacements;
-  // Type to store <key, value> pairs for POST URLs.
-  typedef std::pair<std::string, std::string> PostParam;
   typedef std::vector<PostParam> PostParams;
 
   // TemplateURLRef internally caches values to make replacement quick. This
diff --git a/components/search_engines/template_url_unittest.cc b/components/search_engines/template_url_unittest.cc
index b16e7b42..83a7867 100644
--- a/components/search_engines/template_url_unittest.cc
+++ b/components/search_engines/template_url_unittest.cc
@@ -23,6 +23,12 @@
   void CheckSuggestBaseURL(const std::string& base_url,
                            const std::string& base_suggest_url) const;
 
+  static void ExpectPostParamIs(
+      const TemplateURLRef::PostParam& param,
+      const std::string& name,
+      const std::string& value,
+      const std::string& content_type = std::string());
+
   TestingSearchTermsData search_terms_data_;
 };
 
@@ -33,6 +39,16 @@
   EXPECT_EQ(base_suggest_url, search_terms_data.GoogleBaseSuggestURLValue());
 }
 
+// static
+void TemplateURLTest::ExpectPostParamIs(const TemplateURLRef::PostParam& param,
+                                        const std::string& name,
+                                        const std::string& value,
+                                        const std::string& content_type) {
+  EXPECT_EQ(name, param.name);
+  EXPECT_EQ(value, param.value);
+  EXPECT_EQ(content_type, param.content_type);
+}
+
 TEST_F(TemplateURLTest, Defaults) {
   TemplateURLData data;
   EXPECT_FALSE(data.show_in_default_list);
@@ -158,10 +174,9 @@
   const TemplateURLRef::PostParams& bad_post_params =
       url_bad.image_url_ref().post_params_;
   ASSERT_EQ(2U, bad_post_params.size());
-  EXPECT_EQ("unknown_template", bad_post_params[0].first);
-  EXPECT_EQ("{UnknownTemplate}", bad_post_params[0].second);
-  EXPECT_EQ("bad_value", bad_post_params[1].first);
-  EXPECT_EQ("bad{value}", bad_post_params[1].second);
+  ExpectPostParamIs(bad_post_params[0], "unknown_template",
+                    "{UnknownTemplate}");
+  ExpectPostParamIs(bad_post_params[1], "bad_value", "bad{value}");
 
   // Try to parse valid post parameters.
   data.image_url_post_params = kValidPostParamsString;
@@ -203,26 +218,24 @@
           static_cast<size_t>(i - post_params.begin())) {
         switch (j->type) {
           case TemplateURLRef::GOOGLE_IMAGE_ORIGINAL_WIDTH:
-            EXPECT_EQ("width", i->first);
-            EXPECT_EQ(
-                base::IntToString(search_args.image_original_size.width()),
-                i->second);
+            ExpectPostParamIs(*i, "width",
+                              base::IntToString(
+                                   search_args.image_original_size.width()));
             break;
           case TemplateURLRef::GOOGLE_IMAGE_SEARCH_SOURCE:
-            EXPECT_EQ("sbisrc", i->first);
-            EXPECT_EQ(search_terms_data.GoogleImageSearchSource(), i->second);
+            ExpectPostParamIs(*i, "sbisrc",
+                              search_terms_data.GoogleImageSearchSource());
             break;
           case TemplateURLRef::GOOGLE_IMAGE_THUMBNAIL:
-            EXPECT_EQ("image_content", i->first);
-            EXPECT_EQ(search_args.image_thumbnail_content, i->second);
+            ExpectPostParamIs(*i, "image_content",
+                              search_args.image_thumbnail_content,
+                              "image/jpeg");
             break;
           case TemplateURLRef::GOOGLE_IMAGE_URL:
-            EXPECT_EQ("image_url", i->first);
-            EXPECT_EQ(search_args.image_url.spec(), i->second);
+            ExpectPostParamIs(*i, "image_url", search_args.image_url.spec());
             break;
           case TemplateURLRef::LANGUAGE:
-            EXPECT_EQ("language", i->first);
-            EXPECT_EQ("en", i->second);
+            ExpectPostParamIs(*i, "language", "en");
             break;
           default:
             ADD_FAILURE();  // Should never go here.
@@ -232,14 +245,10 @@
     }
     if (j != replacements.end())
       continue;
-    if (i->first == "empty_param") {
-      EXPECT_TRUE(i->second.empty());
-    } else if (i->first == "sbisrc") {
-      EXPECT_FALSE(i->second.empty());
-    } else {
-      EXPECT_EQ("constant_param", i->first);
-      EXPECT_EQ("constant", i->second);
-    }
+    if (i->name == "empty_param")
+      ExpectPostParamIs(*i, "empty_param", std::string());
+    else
+      ExpectPostParamIs(*i, "constant_param", "constant");
   }
 }
 
diff --git a/components/signin/core/browser/BUILD.gn b/components/signin/core/browser/BUILD.gn
index 4f4f7767..819b418 100644
--- a/components/signin/core/browser/BUILD.gn
+++ b/components/signin/core/browser/BUILD.gn
@@ -43,6 +43,8 @@
     "webdata/token_web_data.h",
   ]
 
+  configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
+
   deps = [
     "//base",
     "//crypto",
@@ -67,11 +69,6 @@
   if (is_chromeos) {
     sources -= [ "signin_manager.cc" ]
   }
-
-  if (is_win) {
-    # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
-    cflags = [ "/wd4267" ]
-  }
 }
 
 static_library("test_support") {
diff --git a/components/sync_driver/data_type_manager_impl.cc b/components/sync_driver/data_type_manager_impl.cc
index 07f79c4..dfffa3c 100644
--- a/components/sync_driver/data_type_manager_impl.cc
+++ b/components/sync_driver/data_type_manager_impl.cc
@@ -13,6 +13,7 @@
 #include "base/compiler_specific.h"
 #include "base/logging.h"
 #include "base/metrics/histogram.h"
+#include "base/profiler/scoped_tracker.h"
 #include "base/strings/stringprintf.h"
 #include "base/trace_event/trace_event.h"
 #include "components/sync_driver/data_type_controller.h"
@@ -349,12 +350,23 @@
     syncer::ModelTypeSet high_priority_types_before,
     syncer::ModelTypeSet first_sync_types,
     syncer::ModelTypeSet failed_configuration_types) {
+  // TODO(erikchen): Remove ScopedTracker below once https://crbug.com/458406
+  // is fixed.
+  tracked_objects::ScopedTracker tracking_profile1(
+      FROM_HERE_WITH_EXPLICIT_FUNCTION(
+          "458406 DataTypeManagerImpl::DownloadReady"));
+
   DCHECK(state_ == DOWNLOAD_PENDING || state_ == CONFIGURING);
 
   // Persistence errors are reset after each backend configuration attempt
   // during which they would have been purged.
   data_type_status_table_.ResetPersistenceErrorsFrom(types_to_download);
 
+  // TODO(erikchen): Remove ScopedTracker below once https://crbug.com/458406
+  // is fixed.
+  tracked_objects::ScopedTracker tracking_profile2(
+      FROM_HERE_WITH_EXPLICIT_FUNCTION(
+          "458406 DataTypeManagerImpl::DownloadReady::NeedsReconfigure"));
   // Ignore |failed_configuration_types| if we need to reconfigure
   // anyway.
   if (needs_reconfigure_) {
@@ -363,6 +375,11 @@
     return;
   }
 
+  // TODO(erikchen): Remove ScopedTracker below once https://crbug.com/458406
+  // is fixed.
+  tracked_objects::ScopedTracker tracking_profile3(
+      FROM_HERE_WITH_EXPLICIT_FUNCTION(
+          "458406 DataTypeManagerImpl::DownloadReady::UnrecoverableError"));
   if (!failed_configuration_types.Empty()) {
     if (!unrecoverable_error_method_.is_null())
       unrecoverable_error_method_.Run();
@@ -383,6 +400,11 @@
 
   state_ = CONFIGURING;
 
+  // TODO(erikchen): Remove ScopedTracker below once https://crbug.com/458406
+  // is fixed.
+  tracked_objects::ScopedTracker tracking_profile4(
+      FROM_HERE_WITH_EXPLICIT_FUNCTION(
+          "458406 DataTypeManagerImpl::DownloadReady::Associate"));
   // Pop and associate download-ready types.
   syncer::ModelTypeSet ready_types = types_to_download;
   CHECK(!download_types_queue_.empty());
@@ -401,6 +423,11 @@
   if (association_types_queue_.size() == 1u)
     StartNextAssociation();
 
+  // TODO(erikchen): Remove ScopedTracker below once https://crbug.com/458406
+  // is fixed.
+  tracked_objects::ScopedTracker tracking_profile5(
+      FROM_HERE_WITH_EXPLICIT_FUNCTION(
+          "458406 DataTypeManagerImpl::DownloadReady::ConfigureDataTypes"));
   // Download types of low priority while configuring types of high priority.
   if (!new_types_to_download.Empty()) {
     configurer_->ConfigureDataTypes(
diff --git a/components/ui/zoom/zoom_controller.cc b/components/ui/zoom/zoom_controller.cc
index 1a68fe4..b471b7e 100644
--- a/components/ui/zoom/zoom_controller.cc
+++ b/components/ui/zoom/zoom_controller.cc
@@ -95,6 +95,10 @@
 
   // Do not actually rescale the page in manual mode.
   if (zoom_mode_ == ZOOM_MODE_MANUAL) {
+    // If the zoom level hasn't changed, early out to avoid sending an event.
+    if (zoom_level_ == zoom_level)
+      return true;
+
     double old_zoom_level = zoom_level_;
     zoom_level_ = zoom_level;
 
diff --git a/components/url_fixer/BUILD.gn b/components/url_fixer/BUILD.gn
index bf29f35..93eac1f3 100644
--- a/components/url_fixer/BUILD.gn
+++ b/components/url_fixer/BUILD.gn
@@ -8,13 +8,11 @@
     "url_fixer.h",
   ]
 
+  # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+  configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
+
   deps = [
     "//base",
     "//net",
   ]
-
-  if (is_win) {
-    # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
-    cflags = [ "/wd4267" ]
-  }
 }
diff --git a/components/url_matcher/BUILD.gn b/components/url_matcher/BUILD.gn
index 83f95d3..7df189b 100644
--- a/components/url_matcher/BUILD.gn
+++ b/components/url_matcher/BUILD.gn
@@ -29,4 +29,6 @@
     "//third_party/re2",
     "//url",
   ]
+
+  configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
 }
diff --git a/components/variations/processed_study.cc b/components/variations/processed_study.cc
index 26721c3f6..5f065d8 100644
--- a/components/variations/processed_study.cc
+++ b/components/variations/processed_study.cc
@@ -14,10 +14,12 @@
 
 namespace {
 
-// Validates the sanity of |study| and computes the total probability.
+// Validates the sanity of |study| and computes the total probability and
+// whether all assignments are to a single group.
 bool ValidateStudyAndComputeTotalProbability(
     const Study& study,
-    base::FieldTrial::Probability* total_probability) {
+    base::FieldTrial::Probability* total_probability,
+    bool* all_assignments_to_one_group) {
   // At the moment, a missing default_experiment_name makes the study invalid.
   if (study.default_experiment_name().empty()) {
     DVLOG(1) << study.name() << " has no default experiment defined.";
@@ -39,21 +41,27 @@
   const std::string& default_group_name = study.default_experiment_name();
   base::FieldTrial::Probability divisor = 0;
 
+  bool multiple_assigned_groups = false;
   bool found_default_group = false;
   std::set<std::string> experiment_names;
   for (int i = 0; i < study.experiment_size(); ++i) {
-    if (study.experiment(i).name().empty()) {
+    const Study_Experiment& experiment = study.experiment(i);
+    if (experiment.name().empty()) {
       DVLOG(1) << study.name() << " is missing experiment " << i << " name";
       return false;
     }
-    if (!experiment_names.insert(study.experiment(i).name()).second) {
+    if (!experiment_names.insert(experiment.name()).second) {
       DVLOG(1) << study.name() << " has a repeated experiment name "
                << study.experiment(i).name();
       return false;
     }
 
-    if (!study.experiment(i).has_forcing_flag())
-      divisor += study.experiment(i).probability_weight();
+    if (!experiment.has_forcing_flag() && experiment.probability_weight() > 0) {
+      // If |divisor| is not 0, there was at least one prior non-zero group.
+      if (divisor != 0)
+        multiple_assigned_groups = true;
+      divisor += experiment.probability_weight();
+    }
     if (study.experiment(i).name() == default_group_name)
       found_default_group = true;
   }
@@ -67,15 +75,18 @@
   }
 
   *total_probability = divisor;
+  *all_assignments_to_one_group = !multiple_assigned_groups;
   return true;
 }
 
 
 }  // namespace
 
-
 ProcessedStudy::ProcessedStudy()
-    : study_(NULL), total_probability_(0), is_expired_(false) {
+    : study_(NULL),
+      total_probability_(0),
+      all_assignments_to_one_group_(false),
+      is_expired_(false) {
 }
 
 ProcessedStudy::~ProcessedStudy() {
@@ -83,12 +94,16 @@
 
 bool ProcessedStudy::Init(const Study* study, bool is_expired) {
   base::FieldTrial::Probability total_probability = 0;
-  if (!ValidateStudyAndComputeTotalProbability(*study, &total_probability))
+  bool all_assignments_to_one_group = false;
+  if (!ValidateStudyAndComputeTotalProbability(*study, &total_probability,
+                                               &all_assignments_to_one_group)) {
     return false;
+  }
 
   study_ = study;
   is_expired_ = is_expired;
   total_probability_ = total_probability;
+  all_assignments_to_one_group_ = all_assignments_to_one_group;
   return true;
 }
 
diff --git a/components/variations/processed_study.h b/components/variations/processed_study.h
index f6229795..c9650fd 100644
--- a/components/variations/processed_study.h
+++ b/components/variations/processed_study.h
@@ -29,6 +29,10 @@
     return total_probability_;
   }
 
+  bool all_assignments_to_one_group() const {
+    return all_assignments_to_one_group_;
+  }
+
   bool is_expired() const { return is_expired_; }
 
   // Gets the index of the experiment with the given |name|. Returns -1 if no
@@ -47,6 +51,9 @@
   // Computed total group probability for the study.
   base::FieldTrial::Probability total_probability_;
 
+  // Whether all assignments are to a single group.
+  bool all_assignments_to_one_group_;
+
   // Whether the study is expired.
   bool is_expired_;
 };
diff --git a/components/variations/variations_seed_processor.cc b/components/variations/variations_seed_processor.cc
index 378f544..9d7300de 100644
--- a/components/variations/variations_seed_processor.cc
+++ b/components/variations/variations_seed_processor.cc
@@ -144,7 +144,11 @@
   base::FieldTrial::RandomizationType randomization_type =
       base::FieldTrial::SESSION_RANDOMIZED;
   if (study.has_consistency() &&
-      study.consistency() == Study_Consistency_PERMANENT) {
+      study.consistency() == Study_Consistency_PERMANENT &&
+      // If all assignments are to a single group, no need to enable one time
+      // randomization (which is more expensive to compute), since the result
+      // will be the same.
+      !processed_study.all_assignments_to_one_group()) {
     randomization_type = base::FieldTrial::ONE_TIME_RANDOMIZED;
     if (study.has_randomization_seed())
       randomization_seed = study.randomization_seed();
diff --git a/components/variations/variations_seed_processor_unittest.cc b/components/variations/variations_seed_processor_unittest.cc
index d1f76ccb..147bf60 100644
--- a/components/variations/variations_seed_processor_unittest.cc
+++ b/components/variations/variations_seed_processor_unittest.cc
@@ -325,6 +325,7 @@
   ProcessedStudy processed_study;
   EXPECT_TRUE(processed_study.Init(&study, false));
   EXPECT_EQ(300, processed_study.total_probability());
+  EXPECT_FALSE(processed_study.all_assignments_to_one_group());
 
   // Min version checks.
   study.mutable_filter()->set_min_version("1.2.3.*");
@@ -360,6 +361,36 @@
   EXPECT_FALSE(processed_study.Init(&study, false));
 }
 
+TEST_F(VariationsSeedProcessorTest, ProcessedStudyAllAssignmentsToOneGroup) {
+  Study study;
+  study.set_default_experiment_name("def");
+  AddExperiment("def", 100, &study);
+
+  ProcessedStudy processed_study;
+  EXPECT_TRUE(processed_study.Init(&study, false));
+  EXPECT_TRUE(processed_study.all_assignments_to_one_group());
+
+  AddExperiment("abc", 0, &study);
+  AddExperiment("flag", 0, &study)->set_forcing_flag(kForcingFlag1);
+  EXPECT_TRUE(processed_study.Init(&study, false));
+  EXPECT_TRUE(processed_study.all_assignments_to_one_group());
+
+  AddExperiment("xyz", 1, &study);
+  EXPECT_TRUE(processed_study.Init(&study, false));
+  EXPECT_FALSE(processed_study.all_assignments_to_one_group());
+
+  // Try with default group and first group being at 0.
+  Study study2;
+  study2.set_default_experiment_name("def");
+  AddExperiment("def", 0, &study2);
+  AddExperiment("xyz", 34, &study2);
+  EXPECT_TRUE(processed_study.Init(&study2, false));
+  EXPECT_TRUE(processed_study.all_assignments_to_one_group());
+  AddExperiment("abc", 12, &study2);
+  EXPECT_TRUE(processed_study.Init(&study2, false));
+  EXPECT_FALSE(processed_study.all_assignments_to_one_group());
+}
+
 TEST_F(VariationsSeedProcessorTest, VariationParams) {
   base::FieldTrialList field_trial_list(NULL);
 
diff --git a/components/wallpaper/wallpaper_manager_base.cc b/components/wallpaper/wallpaper_manager_base.cc
index 28c7fa70..c8ef5343 100644
--- a/components/wallpaper/wallpaper_manager_base.cc
+++ b/components/wallpaper/wallpaper_manager_base.cc
@@ -1017,6 +1017,10 @@
       chromeos::switches::kGuestWallpaperSmall);
   guest_large_wallpaper_file_ = command_line->GetSwitchValuePath(
       chromeos::switches::kGuestWallpaperLarge);
+  child_small_wallpaper_file_ = command_line->GetSwitchValuePath(
+      chromeos::switches::kChildWallpaperSmall);
+  child_large_wallpaper_file_ = command_line->GetSwitchValuePath(
+      chromeos::switches::kChildWallpaperLarge);
   default_wallpaper_image_.reset();
 }
 
diff --git a/components/wallpaper/wallpaper_manager_base.h b/components/wallpaper/wallpaper_manager_base.h
index 99226e8..4607466 100644
--- a/components/wallpaper/wallpaper_manager_base.h
+++ b/components/wallpaper/wallpaper_manager_base.h
@@ -605,6 +605,9 @@
   base::FilePath guest_small_wallpaper_file_;
   base::FilePath guest_large_wallpaper_file_;
 
+  base::FilePath child_small_wallpaper_file_;
+  base::FilePath child_large_wallpaper_file_;
+
   // Current decoded default image is stored in cache.
   scoped_ptr<user_manager::UserImage> default_wallpaper_image_;
 
diff --git a/components/web_cache/browser/BUILD.gn b/components/web_cache/browser/BUILD.gn
index d001973..b9ba85bd 100644
--- a/components/web_cache/browser/BUILD.gn
+++ b/components/web_cache/browser/BUILD.gn
@@ -9,6 +9,8 @@
     "web_cache_manager.h",
   ]
 
+  configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
+
   deps = [
     "//base",
     "//components/web_cache/common",
diff --git a/components/web_resource.gypi b/components/web_resource.gypi
index d89095f..b300556 100644
--- a/components/web_resource.gypi
+++ b/components/web_resource.gypi
@@ -24,6 +24,8 @@
         'web_resource/web_resource_pref_names.h',
         'web_resource/web_resource_service.cc',
         'web_resource/web_resource_service.h',
+        'web_resource/web_resource_switches.cc',
+        'web_resource/web_resource_switches.h',
       ],
     },
     {
diff --git a/components/web_resource/BUILD.gn b/components/web_resource/BUILD.gn
index 6d27dca..6be678d 100644
--- a/components/web_resource/BUILD.gn
+++ b/components/web_resource/BUILD.gn
@@ -12,6 +12,8 @@
     "web_resource_pref_names.h",
     "web_resource_service.cc",
     "web_resource_service.h",
+    "web_resource_switches.cc",
+    "web_resource_switches.h",
   ]
 
   deps = [
diff --git a/components/web_resource/web_resource_pref_names.cc b/components/web_resource/web_resource_pref_names.cc
index 5fb4df7..51ae76f 100644
--- a/components/web_resource/web_resource_pref_names.cc
+++ b/components/web_resource/web_resource_pref_names.cc
@@ -9,4 +9,7 @@
 // A boolean pref of the EULA accepted flag.
 const char kEulaAccepted[] = "EulaAccepted";
 
+// Last time of update of promo_resource_cache.
+const char kNtpPromoResourceCacheUpdate[] = "ntp.promo_resource_cache_update";
+
 }  // namespace prefs
diff --git a/components/web_resource/web_resource_pref_names.h b/components/web_resource/web_resource_pref_names.h
index 5aad2b6..0fa4f43 100644
--- a/components/web_resource/web_resource_pref_names.h
+++ b/components/web_resource/web_resource_pref_names.h
@@ -8,6 +8,7 @@
 namespace prefs {
 
 extern const char kEulaAccepted[];
+extern const char kNtpPromoResourceCacheUpdate[];
 
 }  // namespace prefs
 
diff --git a/components/web_resource/web_resource_switches.cc b/components/web_resource/web_resource_switches.cc
new file mode 100644
index 0000000..8a1f9a7
--- /dev/null
+++ b/components/web_resource/web_resource_switches.cc
@@ -0,0 +1,12 @@
+// 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 "components/web_resource/web_resource_switches.h"
+
+namespace switches {
+
+// Specifies a custom URL for fetching NTP promo data.
+const char kPromoServerURL[] = "promo-server-url";
+
+}  // namespace switches
diff --git a/components/web_resource/web_resource_switches.h b/components/web_resource/web_resource_switches.h
new file mode 100644
index 0000000..e1de796
--- /dev/null
+++ b/components/web_resource/web_resource_switches.h
@@ -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.
+
+#ifndef COMPONENTS_WEB_RESOURCE_WEB_RESOURCE_SWITCHES_H_
+#define COMPONENTS_WEB_RESOURCE_WEB_RESOURCE_SWITCHES_H_
+
+namespace switches {
+
+extern const char kPromoServerURL[];
+
+}  // namespace switches
+
+#endif  // COMPONENTS_WEB_RESOURCE_WEB_RESOURCE_SWITCHES_H_
diff --git a/components/wifi/fake_wifi_service.cc b/components/wifi/fake_wifi_service.cc
index 7246d9b..a35d002 100644
--- a/components/wifi/fake_wifi_service.cc
+++ b/components/wifi/fake_wifi_service.cc
@@ -5,7 +5,6 @@
 #include "components/wifi/fake_wifi_service.h"
 
 #include "base/bind.h"
-#include "base/json/json_reader.h"
 #include "base/message_loop/message_loop.h"
 #include "components/onc/onc_constants.h"
 
@@ -23,25 +22,6 @@
     network_properties.ssid = "wifi1";
     network_properties.security = onc::wifi::kWEP_PSK;
     network_properties.signal_strength = 40;
-    network_properties.json_extra =
-      "{"
-      "  \"MacAddress\": \"00:11:22:AA:BB:CC\","
-      "  \"IPAddressConfigType\": \"Static\","
-      "  \"IPConfigs\": [{"
-      "     \"Gateway\": \"0.0.0.1\","
-      "     \"IPAddress\": \"0.0.0.0\","
-      "     \"RoutingPrefix\": 0,"
-      "     \"Type\": \"IPv4\""
-      "  }],"
-      "  \"StaticIPConfig\": {"
-      "     \"IPAddress\": \"1.2.3.4\","
-      "     \"Type\": \"IPv4\""
-      "  },"
-      "  \"WiFi\": {"
-      "    \"Frequency\": 2400,"
-      "    \"FrequencyList\": [2400]"
-      "  }"
-      "}";
     networks_.push_back(network_properties);
   }
   {
diff --git a/components/wifi/network_properties.cc b/components/wifi/network_properties.cc
index 49aeb69..68af452 100644
--- a/components/wifi/network_properties.cc
+++ b/components/wifi/network_properties.cc
@@ -4,7 +4,6 @@
 
 #include "components/wifi/network_properties.h"
 
-#include "base/json/json_reader.h"
 #include "base/message_loop/message_loop.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/stringprintf.h"
@@ -60,14 +59,6 @@
   }
   value->Set(onc::network_type::kWiFi, wifi.release());
 
-  if (!network_list && !json_extra.empty()) {
-    base::Value* value_extra = base::JSONReader::Read(json_extra);
-    CHECK(value_extra);
-    base::DictionaryValue* value_dictionary;
-    if (value_extra->GetAsDictionary(&value_dictionary))
-      value->MergeDictionary(value_dictionary);
-  }
-
   return value.Pass();
 }
 
diff --git a/components/wifi/network_properties.h b/components/wifi/network_properties.h
index 8e4d179b..8483e98 100644
--- a/components/wifi/network_properties.h
+++ b/components/wifi/network_properties.h
@@ -48,8 +48,6 @@
   Frequency frequency;
   FrequencySet frequency_set;
 
-  std::string json_extra;  // Extra JSON properties for unit tests
-
   scoped_ptr<base::DictionaryValue> ToValue(bool network_list) const;
   // Updates only properties set in |value|.
   bool UpdateFromValue(const base::DictionaryValue& value);
diff --git a/components/wifi_sync/wifi_credential_syncable_service.cc b/components/wifi_sync/wifi_credential_syncable_service.cc
index e4023019..c8f65dd5 100644
--- a/components/wifi_sync/wifi_credential_syncable_service.cc
+++ b/components/wifi_sync/wifi_credential_syncable_service.cc
@@ -203,7 +203,15 @@
     return false;
   }
 
-  // TODO(quiche): Handle case where network already exists.
+  const SsidAndSecurityClass network_id(
+      credential.ssid(), credential.security_class());
+  if (synced_networks_and_passphrases_.find(network_id) !=
+      synced_networks_and_passphrases_.end()) {
+    // TODO(quiche): If passphrase has changed, submit this to sync as
+    // an ACTION_UPDATE. crbug.com/431436
+    return false;
+  }
+
   syncer::SyncChangeList change_list;
   syncer::SyncError sync_error;
   sync_pb::EntitySpecifics wifi_credential_specifics;
@@ -218,6 +226,7 @@
     return false;
   }
 
+  synced_networks_and_passphrases_[network_id] = credential.passphrase();
   return true;
 }
 
diff --git a/components/wifi_sync/wifi_credential_syncable_service.h b/components/wifi_sync/wifi_credential_syncable_service.h
index d0868e8..5113704 100644
--- a/components/wifi_sync/wifi_credential_syncable_service.h
+++ b/components/wifi_sync/wifi_credential_syncable_service.h
@@ -5,17 +5,20 @@
 #ifndef COMPONENTS_WIFI_SYNC_WIFI_CREDENTIAL_SYNCABLE_SERVICE_H_
 #define COMPONENTS_WIFI_SYNC_WIFI_CREDENTIAL_SYNCABLE_SERVICE_H_
 
+#include <map>
 #include <string>
+#include <utility>
 
 #include "base/macros.h"
 #include "base/memory/scoped_ptr.h"
 #include "components/keyed_service/core/keyed_service.h"
 #include "components/wifi_sync/wifi_config_delegate.h"
+#include "components/wifi_sync/wifi_credential.h"
+#include "components/wifi_sync/wifi_security_class.h"
 #include "sync/api/sync_change_processor.h"
 #include "sync/api/syncable_service.h"
 
 namespace wifi_sync {
-class WifiCredential;
 
 // KeyedService that synchronizes WiFi credentials between local settings,
 // and Chrome Sync.
@@ -62,6 +65,11 @@
                            const WifiCredential& credential);
 
  private:
+  using SsidAndSecurityClass =
+      std::pair<WifiCredential::SsidBytes, WifiSecurityClass>;
+  using SsidAndSecurityClassToPassphrase =
+      std::map<SsidAndSecurityClass, std::string>;
+
   // The syncer::ModelType that this SyncableService processes and
   // generates updates for.
   static const syncer::ModelType kModelType;
@@ -73,6 +81,11 @@
   // Chrome Sync.
   scoped_ptr<syncer::SyncChangeProcessor> sync_processor_;
 
+  // The networks and passphrases that are already known by Chrome
+  // Sync. All synced networks must be included in this map, even if
+  // they do not use passphrases.
+  SsidAndSecurityClassToPassphrase synced_networks_and_passphrases_;
+
   DISALLOW_COPY_AND_ASSIGN(WifiCredentialSyncableService);
 };
 
diff --git a/components/wifi_sync/wifi_credential_syncable_service_unittest.cc b/components/wifi_sync/wifi_credential_syncable_service_unittest.cc
index d5de955..13c8092 100644
--- a/components/wifi_sync/wifi_credential_syncable_service_unittest.cc
+++ b/components/wifi_sync/wifi_credential_syncable_service_unittest.cc
@@ -31,6 +31,7 @@
 
 namespace {
 
+const char kSsid[] = "fake-ssid";
 const char kSsidNonUtf8[] = "\xc0";
 
 // Fake implementation of WifiConfigDelegate, which provides the
@@ -283,4 +284,56 @@
   EXPECT_EQ(1, change_processor_changes_size());
 }
 
+TEST_F(WifiCredentialSyncableServiceTest,
+       AddToSyncedNetworksDifferentSecurityClassesSuccess) {
+  StartSyncing();
+  EXPECT_TRUE(AddToSyncedNetworks(
+      "fake-item-id", MakeCredential(kSsidNonUtf8, SECURITY_CLASS_NONE, "")));
+  EXPECT_TRUE(AddToSyncedNetworks(
+      "fake-item-id-2",
+      MakeCredential(kSsidNonUtf8, SECURITY_CLASS_WEP, "")));
+  EXPECT_EQ(2, change_processor_changes_size());
+}
+
+TEST_F(WifiCredentialSyncableServiceTest,
+       AddToSyncedNetworksDifferentSsidsSuccess) {
+  StartSyncing();
+  EXPECT_TRUE(AddToSyncedNetworks(
+      "fake-item-id", MakeCredential(kSsidNonUtf8, SECURITY_CLASS_NONE, "")));
+  EXPECT_TRUE(AddToSyncedNetworks(
+      "fake-item-id-2", MakeCredential(kSsid, SECURITY_CLASS_NONE, "")));
+  EXPECT_EQ(2, change_processor_changes_size());
+}
+
+TEST_F(WifiCredentialSyncableServiceTest,
+       AddToSyncedNetworksDuplicateAddPskNetwork) {
+  const std::string passphrase("psk-passphrase");
+  StartSyncing();
+  EXPECT_TRUE(
+      AddToSyncedNetworks(
+          "fake-item-id",
+          MakeCredential(kSsidNonUtf8, SECURITY_CLASS_PSK, passphrase)));
+  EXPECT_EQ(1, change_processor_changes_size());
+  EXPECT_FALSE(
+      AddToSyncedNetworks(
+          "fake-item-id",
+          MakeCredential(kSsidNonUtf8, SECURITY_CLASS_PSK, passphrase)));
+  EXPECT_EQ(1, change_processor_changes_size());
+}
+
+TEST_F(WifiCredentialSyncableServiceTest,
+       AddToSyncedNetworksDuplicateAddOpenNetwork) {
+  StartSyncing();
+  EXPECT_TRUE(
+      AddToSyncedNetworks(
+          "fake-item-id",
+          MakeCredential(kSsidNonUtf8, SECURITY_CLASS_NONE, "")));
+  EXPECT_EQ(1, change_processor_changes_size());
+  EXPECT_FALSE(
+      AddToSyncedNetworks(
+          "fake-item-id",
+          MakeCredential(kSsidNonUtf8, SECURITY_CLASS_NONE, "")));
+  EXPECT_EQ(1, change_processor_changes_size());
+}
+
 }  // namespace wifi_sync
diff --git a/content/app/android/content_jni_onload.cc b/content/app/android/content_jni_onload.cc
index ab95965..27c7b166 100644
--- a/content/app/android/content_jni_onload.cc
+++ b/content/app/android/content_jni_onload.cc
@@ -7,8 +7,8 @@
 #include <vector>
 
 #include "base/android/base_jni_onload.h"
-#include "base/android/jni_onload_delegate.h"
 #include "base/android/library_loader/library_loader_hooks.h"
+#include "base/bind.h"
 #include "content/app/android/library_loader_hooks.h"
 #include "content/public/app/content_main.h"
 
@@ -17,18 +17,11 @@
 
 namespace {
 
-class ContentJNIOnLoadDelegate
-    : public base::android::JNIOnLoadDelegate {
- public:
-  bool RegisterJNI(JNIEnv* env) override;
-  bool Init() override;
-};
-
-bool ContentJNIOnLoadDelegate::RegisterJNI(JNIEnv* env) {
+bool RegisterJNI(JNIEnv* env) {
   return content::EnsureJniRegistered(env);
 }
 
-bool ContentJNIOnLoadDelegate::Init() {
+bool Init() {
   base::android::SetLibraryLoadedHook(&content::LibraryLoaded);
   return true;
 }
@@ -36,13 +29,20 @@
 }  // namespace
 
 
-bool OnJNIOnLoad(JavaVM* vm,
-                 base::android::JNIOnLoadDelegate* delegate) {
-  std::vector<base::android::JNIOnLoadDelegate*> delegates;
-  ContentJNIOnLoadDelegate content_delegate;
-  delegates.push_back(delegate);
-  delegates.push_back(&content_delegate);
-  return base::android::OnJNIOnLoad(vm, &delegates);
+bool OnJNIOnLoadRegisterJNI(
+    JavaVM* vm,
+    base::android::RegisterCallback callback) {
+  std::vector<base::android::RegisterCallback> callbacks;
+  callbacks.push_back(callback);
+  callbacks.push_back(base::Bind(&RegisterJNI));
+  return base::android::OnJNIOnLoadRegisterJNI(vm, callbacks);
+}
+
+bool OnJNIOnLoadInit(base::android::InitCallback callback) {
+  std::vector<base::android::InitCallback> callbacks;
+  callbacks.push_back(callback);
+  callbacks.push_back(base::Bind(&Init));
+  return base::android::OnJNIOnLoadInit(callbacks);
 }
 
 }  // namespace android
diff --git a/content/browser/accessibility/accessibility_ipc_error_browsertest.cc b/content/browser/accessibility/accessibility_ipc_error_browsertest.cc
index 5702a33..da6921f6 100644
--- a/content/browser/accessibility/accessibility_ipc_error_browsertest.cc
+++ b/content/browser/accessibility/accessibility_ipc_error_browsertest.cc
@@ -107,7 +107,7 @@
 
   // Get the accessibility tree, ensure it reflects the final state of the
   // document.
-  const ui::AXNode* root = tree->GetRoot();
+  const ui::AXNode* root = tree->root();
 
   // Use this for debugging if the test fails.
   VLOG(1) << tree->ToString();
diff --git a/content/browser/accessibility/accessibility_ui.cc b/content/browser/accessibility/accessibility_ui.cc
index a8a5d97..3bfa6ee6 100644
--- a/content/browser/accessibility/accessibility_ui.cc
+++ b/content/browser/accessibility/accessibility_ui.cc
@@ -66,7 +66,7 @@
 base::DictionaryValue* BuildTargetDescriptor(RenderViewHost* rvh) {
   WebContentsImpl* web_contents = static_cast<WebContentsImpl*>(
       WebContents::FromRenderViewHost(rvh));
-  AccessibilityMode accessibility_mode = web_contents->GetAccessibilityMode();
+  AccessibilityMode accessibility_mode = AccessibilityModeOff;
 
   std::string title;
   GURL url;
@@ -80,6 +80,7 @@
     NavigationEntry* entry = controller.GetVisibleEntry();
     if (entry != NULL && entry->GetURL().is_valid())
       favicon_url = entry->GetFavicon().url;
+    accessibility_mode = web_contents->GetAccessibilityMode();
   }
 
   return BuildTargetDescriptor(url,
diff --git a/content/browser/accessibility/browser_accessibility_manager.cc b/content/browser/accessibility/browser_accessibility_manager.cc
index 34a82257..8f61b904 100644
--- a/content/browser/accessibility/browser_accessibility_manager.cc
+++ b/content/browser/accessibility/browser_accessibility_manager.cc
@@ -114,7 +114,7 @@
   }
 
   if (!focus_)
-    SetFocus(tree_->GetRoot(), false);
+    SetFocus(tree_->root(), false);
 }
 
 // static
@@ -128,7 +128,7 @@
 }
 
 BrowserAccessibility* BrowserAccessibilityManager::GetRoot() {
-  return GetFromAXNode(tree_->GetRoot());
+  return GetFromAXNode(tree_->root());
 }
 
 BrowserAccessibility* BrowserAccessibilityManager::GetFromAXNode(
@@ -198,7 +198,7 @@
 
     // Set focus to the root if it's not anywhere else.
     if (!focus_) {
-      SetFocus(tree_->GetRoot(), false);
+      SetFocus(tree_->root(), false);
       should_send_initial_focus = true;
     }
   }
@@ -420,8 +420,8 @@
 
 void BrowserAccessibilityManager::OnNodeWillBeDeleted(ui::AXNode* node) {
   if (node == focus_ && tree_) {
-    if (node != tree_->GetRoot())
-      SetFocus(tree_->GetRoot(), false);
+    if (node != tree_->root())
+      SetFocus(tree_->root(), false);
     else
       focus_ = NULL;
   }
@@ -471,7 +471,7 @@
       tree_->CreateTreeSource());
   ui::AXTreeSerializer<const ui::AXNode*> serializer(tree_source.get());
   ui::AXTreeUpdate update;
-  serializer.SerializeChanges(tree_->GetRoot(), &update);
+  serializer.SerializeChanges(tree_->root(), &update);
   return update;
 }
 
diff --git a/content/browser/accessibility/browser_accessibility_manager_win.cc b/content/browser/accessibility/browser_accessibility_manager_win.cc
index 7d48d25..750d1311 100644
--- a/content/browser/accessibility/browser_accessibility_manager_win.cc
+++ b/content/browser/accessibility/browser_accessibility_manager_win.cc
@@ -176,7 +176,7 @@
 
   // Try to fire a focus event on the root first and then the focused node.
   // This will clear focus_event_on_root_needed_ if successful.
-  if (focus_ != tree_->GetRoot())
+  if (focus_ != tree_->root())
     NotifyAccessibilityEvent(ui::AX_EVENT_FOCUS, GetRoot());
   BrowserAccessibilityManager::OnWindowFocused();
 }
diff --git a/content/browser/accessibility/cross_platform_accessibility_browsertest.cc b/content/browser/accessibility/cross_platform_accessibility_browsertest.cc
index 8f83ed9..c39318a 100644
--- a/content/browser/accessibility/cross_platform_accessibility_browsertest.cc
+++ b/content/browser/accessibility/cross_platform_accessibility_browsertest.cc
@@ -142,7 +142,7 @@
   GURL url(url_str);
   NavigateToURL(shell(), url);
   const ui::AXTree& tree = GetAXTree();
-  const ui::AXNode* root = tree.GetRoot();
+  const ui::AXNode* root = tree.root();
 
   // Check properties of the root element of the tree.
   EXPECT_STREQ(url_str,
@@ -213,7 +213,7 @@
   NavigateToURL(shell(), url);
 
   const ui::AXTree& tree = GetAXTree();
-  const ui::AXNode* root = tree.GetRoot();
+  const ui::AXNode* root = tree.root();
   ASSERT_EQ(1, root->child_count());
   const ui::AXNode* body = root->ChildAtIndex(0);
   ASSERT_EQ(1, body->child_count());
@@ -245,7 +245,7 @@
   NavigateToURL(shell(), url);
 
   const ui::AXTree& tree = GetAXTree();
-  const ui::AXNode* root = tree.GetRoot();
+  const ui::AXNode* root = tree.root();
   ASSERT_EQ(1, root->child_count());
   const ui::AXNode* body = root->ChildAtIndex(0);
   ASSERT_EQ(1, body->child_count());
@@ -275,7 +275,7 @@
   NavigateToURL(shell(), url);
 
   const ui::AXTree& tree = GetAXTree();
-  const ui::AXNode* root = tree.GetRoot();
+  const ui::AXNode* root = tree.root();
   ASSERT_EQ(1, root->child_count());
   const ui::AXNode* table = root->ChildAtIndex(0);
   EXPECT_EQ(ui::AX_ROLE_TABLE, table->data().role);
@@ -324,7 +324,7 @@
   NavigateToURL(shell(), url);
 
   const ui::AXTree& tree = GetAXTree();
-  const ui::AXNode* root = tree.GetRoot();
+  const ui::AXNode* root = tree.root();
   base::hash_set<int> ids;
   RecursiveAssertUniqueIds(root, &ids);
 }
@@ -346,7 +346,7 @@
   NavigateToURL(shell(), url);
 
   const ui::AXTree& tree = GetAXTree();
-  const ui::AXNode* root = tree.GetRoot();
+  const ui::AXNode* root = tree.root();
   ASSERT_EQ(1, root->child_count());
   const ui::AXNode* body = root->ChildAtIndex(0);
   ASSERT_EQ(3, body->child_count());
@@ -397,7 +397,7 @@
   NavigateToURL(shell(), url);
 
   const ui::AXTree& tree = GetAXTree();
-  const ui::AXNode* root = tree.GetRoot();
+  const ui::AXNode* root = tree.root();
   base::hash_set<int> ids;
   RecursiveAssertUniqueIds(root, &ids);
 }
@@ -425,7 +425,7 @@
   NavigateToURL(shell(), url);
 
   const ui::AXTree& tree = GetAXTree();
-  const ui::AXNode* root = tree.GetRoot();
+  const ui::AXNode* root = tree.root();
   const ui::AXNode* table = root->ChildAtIndex(0);
   EXPECT_EQ(ui::AX_ROLE_TABLE, table->data().role);
   ASSERT_GE(table->child_count(), 5);
@@ -488,7 +488,7 @@
   GURL url(url_str);
   NavigateToURL(shell(), url);
   const ui::AXTree& tree = GetAXTree();
-  const ui::AXNode* root = tree.GetRoot();
+  const ui::AXNode* root = tree.root();
   ASSERT_EQ(1, root->child_count());
   const ui::AXNode* textbox = root->ChildAtIndex(0);
   EXPECT_EQ(true, GetBoolAttr(textbox, ui::AX_ATTR_CAN_SET_VALUE));
diff --git a/content/browser/appcache/appcache_database.cc b/content/browser/appcache/appcache_database.cc
index 3e502813..985173b 100644
--- a/content/browser/appcache/appcache_database.cc
+++ b/content/browser/appcache/appcache_database.cc
@@ -210,6 +210,7 @@
 }
 
 AppCacheDatabase::~AppCacheDatabase() {
+  CommitLazyLastAccessTimes();
 }
 
 void AppCacheDatabase::Disable() {
@@ -392,7 +393,7 @@
   return true;
 }
 
-bool AppCacheDatabase::UpdateGroupLastAccessTime(
+bool AppCacheDatabase::UpdateLastAccessTime(
     int64 group_id, base::Time time) {
   if (!LazyOpen(true))
     return false;
@@ -404,7 +405,30 @@
   statement.BindInt64(0, time.ToInternalValue());
   statement.BindInt64(1, group_id);
 
-  return statement.Run() && db_->GetLastChangeCount();
+  return statement.Run();
+}
+
+bool AppCacheDatabase::LazyUpdateLastAccessTime(
+    int64 group_id, base::Time time) {
+  if (!LazyOpen(true))
+    return false;
+  lazy_last_access_times_[group_id] = time;
+  return true;
+}
+
+bool AppCacheDatabase::CommitLazyLastAccessTimes() {
+  if (lazy_last_access_times_.empty())
+    return true;
+  if (!LazyOpen(false))
+    return false;
+
+  sql::Transaction transaction(db_.get());
+  if (!transaction.Begin())
+    return false;
+  for (const auto& pair : lazy_last_access_times_)
+    UpdateLastAccessTime(pair.first, pair.second);
+  lazy_last_access_times_.clear();
+  return transaction.Commit();
 }
 
 bool AppCacheDatabase::InsertGroup(const GroupRecord* record) {
@@ -924,8 +948,14 @@
   record->manifest_url = GURL(statement.ColumnString(2));
   record->creation_time =
       base::Time::FromInternalValue(statement.ColumnInt64(3));
-  record->last_access_time =
-      base::Time::FromInternalValue(statement.ColumnInt64(4));
+
+  const auto found = lazy_last_access_times_.find(record->group_id);
+  if (found != lazy_last_access_times_.end()) {
+    record->last_access_time = found->second;
+  } else {
+    record->last_access_time =
+        base::Time::FromInternalValue(statement.ColumnInt64(4));
+  }
 }
 
 void AppCacheDatabase::ReadCacheRecord(
diff --git a/content/browser/appcache/appcache_database.h b/content/browser/appcache/appcache_database.h
index ee1c52d8..ccfd891a 100644
--- a/content/browser/appcache/appcache_database.h
+++ b/content/browser/appcache/appcache_database.h
@@ -116,8 +116,11 @@
   bool FindGroupsForOrigin(
       const GURL& origin, std::vector<GroupRecord>* records);
   bool FindGroupForCache(int64 cache_id, GroupRecord* record);
-  bool UpdateGroupLastAccessTime(
+  bool UpdateLastAccessTime(
       int64 group_id, base::Time last_access_time);
+  bool LazyUpdateLastAccessTime(
+      int64 group_id, base::Time last_access_time);
+  bool CommitLazyLastAccessTimes();  // The destructor calls this too.
   bool InsertGroup(const GroupRecord* record);
   bool DeleteGroup(int64 group_id);
 
@@ -220,6 +223,7 @@
   base::FilePath db_file_path_;
   scoped_ptr<sql::Connection> db_;
   scoped_ptr<sql::MetaTable> meta_table_;
+  std::map<int64, base::Time> lazy_last_access_times_;
   bool is_disabled_;
   bool is_recreating_;
   bool was_corruption_detected_;
diff --git a/content/browser/appcache/appcache_interceptor.cc b/content/browser/appcache/appcache_interceptor.cc
index 4ca2c69..4910880 100644
--- a/content/browser/appcache/appcache_interceptor.cc
+++ b/content/browser/appcache/appcache_interceptor.cc
@@ -4,6 +4,7 @@
 
 #include "content/browser/appcache/appcache_interceptor.h"
 
+#include "base/profiler/scoped_tracker.h"
 #include "content/browser/appcache/appcache_backend_impl.h"
 #include "content/browser/appcache/appcache_host.h"
 #include "content/browser/appcache/appcache_request_handler.h"
@@ -34,6 +35,10 @@
     int host_id,
     ResourceType resource_type,
     bool should_reset_appcache) {
+  // TODO(pkasting): Remove ScopedTracker below once crbug.com/456331 is fixed.
+  tracked_objects::ScopedTracker tracking_profile(
+      FROM_HERE_WITH_EXPLICIT_FUNCTION(
+          "456331 AppCacheInterceptor::SetExtraRequestInfo"));
   if (!service || (host_id == kAppCacheNoHostId))
     return;
 
diff --git a/content/browser/appcache/appcache_response.cc b/content/browser/appcache/appcache_response.cc
index c38d3afa..d53f941 100644
--- a/content/browser/appcache/appcache_response.cc
+++ b/content/browser/appcache/appcache_response.cc
@@ -22,10 +22,7 @@
 namespace {
 
 // Disk cache entry data indices.
-enum {
-  kResponseInfoIndex,
-  kResponseContentIndex
-};
+enum { kResponseInfoIndex, kResponseContentIndex, kResponseMetadataIndex };
 
 // An IOBuffer that wraps a pickle's data. Ownership of the
 // pickle is transfered to the WrappedPickleIOBuffer object.
@@ -139,15 +136,51 @@
   OnIOComplete(result);
 }
 
+void AppCacheResponseIO::OpenEntryIfNeeded() {
+  int rv;
+  AppCacheDiskCacheInterface::Entry** entry_ptr = NULL;
+  if (entry_) {
+    rv = net::OK;
+  } else if (!disk_cache_) {
+    rv = net::ERR_FAILED;
+  } else {
+    entry_ptr = new AppCacheDiskCacheInterface::Entry*;
+    open_callback_ =
+        base::Bind(&AppCacheResponseIO::OpenEntryCallback,
+                   weak_factory_.GetWeakPtr(), base::Owned(entry_ptr));
+    rv = disk_cache_->OpenEntry(response_id_, entry_ptr, open_callback_);
+  }
+
+  if (rv != net::ERR_IO_PENDING)
+    OpenEntryCallback(entry_ptr, rv);
+}
+
+void AppCacheResponseIO::OpenEntryCallback(
+    AppCacheDiskCacheInterface::Entry** entry, int rv) {
+  DCHECK(info_buffer_.get() || buffer_.get());
+
+  if (!open_callback_.is_null()) {
+    if (rv == net::OK) {
+      DCHECK(entry);
+      entry_ = *entry;
+    }
+    open_callback_.Reset();
+  }
+  OnOpenEntryComplete();
+}
+
 
 // AppCacheResponseReader ----------------------------------------------
 
 AppCacheResponseReader::AppCacheResponseReader(
-    int64 response_id, int64 group_id, AppCacheDiskCacheInterface* disk_cache)
+    int64 response_id,
+    int64 group_id,
+    AppCacheDiskCacheInterface* disk_cache)
     : AppCacheResponseIO(response_id, group_id, disk_cache),
       range_offset_(0),
       range_length_(kint32max),
       read_position_(0),
+      reading_metadata_size_(0),
       weak_factory_(this) {
 }
 
@@ -165,15 +198,10 @@
 
   info_buffer_ = info_buf;
   callback_ = callback;  // cleared on completion
-  OpenEntryIfNeededAndContinue();
+  OpenEntryIfNeeded();
 }
 
 void AppCacheResponseReader::ContinueReadInfo() {
-  if (!entry_)  {
-    ScheduleIOCompletionCallback(net::ERR_CACHE_MISS);
-    return;
-  }
-
   int size = entry_->GetSize(kResponseInfoIndex);
   if (size <= 0) {
     ScheduleIOCompletionCallback(net::ERR_CACHE_MISS);
@@ -196,15 +224,10 @@
   buffer_ = buf;
   buffer_len_ = buf_len;
   callback_ = callback;  // cleared on completion
-  OpenEntryIfNeededAndContinue();
+  OpenEntryIfNeeded();
 }
 
 void AppCacheResponseReader::ContinueReadData() {
-  if (!entry_)  {
-    ScheduleIOCompletionCallback(net::ERR_CACHE_MISS);
-    return;
-  }
-
   if (read_position_ + buffer_len_ > range_length_) {
     // TODO(michaeln): What about integer overflows?
     DCHECK(range_length_ >= read_position_);
@@ -224,7 +247,11 @@
 
 void AppCacheResponseReader::OnIOComplete(int result) {
   if (result >= 0) {
-    if (info_buffer_.get()) {
+    if (reading_metadata_size_) {
+      DCHECK(reading_metadata_size_ == result);
+      DCHECK(info_buffer_->http_info->metadata);
+      reading_metadata_size_ = 0;
+    } else if (info_buffer_.get()) {
       // Deserialize the http info structure, ensuring we got headers.
       Pickle pickle(buffer_->data(), result);
       scoped_ptr<net::HttpResponseInfo> info(new net::HttpResponseInfo);
@@ -241,6 +268,16 @@
       DCHECK(entry_);
       info_buffer_->response_data_size =
           entry_->GetSize(kResponseContentIndex);
+
+      int64 metadata_size = entry_->GetSize(kResponseMetadataIndex);
+      if (metadata_size > 0) {
+        reading_metadata_size_ = metadata_size;
+        info_buffer_->http_info->metadata =
+            new net::IOBufferWithSize(metadata_size);
+        ReadRaw(kResponseMetadataIndex, 0,
+                info_buffer_->http_info->metadata.get(), metadata_size);
+        return;
+      }
     } else {
       read_position_ += result;
     }
@@ -248,37 +285,11 @@
   InvokeUserCompletionCallback(result);
 }
 
-void AppCacheResponseReader::OpenEntryIfNeededAndContinue() {
-  int rv;
-  AppCacheDiskCacheInterface::Entry** entry_ptr = NULL;
-  if (entry_) {
-    rv = net::OK;
-  } else if (!disk_cache_) {
-    rv = net::ERR_FAILED;
-  } else {
-    entry_ptr = new AppCacheDiskCacheInterface::Entry*;
-    open_callback_ =
-        base::Bind(&AppCacheResponseReader::OnOpenEntryComplete,
-                   weak_factory_.GetWeakPtr(), base::Owned(entry_ptr));
-    rv = disk_cache_->OpenEntry(response_id_, entry_ptr, open_callback_);
+void AppCacheResponseReader::OnOpenEntryComplete() {
+  if (!entry_)  {
+    ScheduleIOCompletionCallback(net::ERR_CACHE_MISS);
+    return;
   }
-
-  if (rv != net::ERR_IO_PENDING)
-    OnOpenEntryComplete(entry_ptr, rv);
-}
-
-void AppCacheResponseReader::OnOpenEntryComplete(
-    AppCacheDiskCacheInterface::Entry** entry, int rv) {
-  DCHECK(info_buffer_.get() || buffer_.get());
-
-  if (!open_callback_.is_null()) {
-    if (rv == net::OK) {
-      DCHECK(entry);
-      entry_ = *entry;
-    }
-    open_callback_.Reset();
-  }
-
   if (info_buffer_.get())
     ContinueReadInfo();
   else
@@ -426,4 +437,47 @@
     ContinueWriteData();
 }
 
+// AppCacheResponseMetadataWriter ----------------------------------------------
+
+AppCacheResponseMetadataWriter::AppCacheResponseMetadataWriter(
+    int64 response_id,
+    int64 group_id,
+    AppCacheDiskCacheInterface* disk_cache)
+    : AppCacheResponseIO(response_id, group_id, disk_cache),
+      write_amount_(0),
+      weak_factory_(this) {
+}
+
+AppCacheResponseMetadataWriter::~AppCacheResponseMetadataWriter() {
+}
+
+void AppCacheResponseMetadataWriter::WriteMetadata(
+    net::IOBuffer* buf,
+    int buf_len,
+    const net::CompletionCallback& callback) {
+  DCHECK(!callback.is_null());
+  DCHECK(!IsIOPending());
+  DCHECK(buf);
+  DCHECK(buf_len >= 0);
+  DCHECK(!buffer_.get());
+
+  buffer_ = buf;
+  write_amount_ = buf_len;
+  callback_ = callback;  // cleared on completion
+  OpenEntryIfNeeded();
+}
+
+void AppCacheResponseMetadataWriter::OnOpenEntryComplete() {
+  if (!entry_) {
+    ScheduleIOCompletionCallback(net::ERR_FAILED);
+    return;
+  }
+  WriteRaw(kResponseMetadataIndex, 0, buffer_.get(), write_amount_);
+}
+
+void AppCacheResponseMetadataWriter::OnIOComplete(int result) {
+  DCHECK(result < 0 || write_amount_ == result);
+  InvokeUserCompletionCallback(result);
+}
+
 }  // namespace content
diff --git a/content/browser/appcache/appcache_response.h b/content/browser/appcache/appcache_response.h
index 4b7a32c..8d6c790 100644
--- a/content/browser/appcache/appcache_response.h
+++ b/content/browser/appcache/appcache_response.h
@@ -106,12 +106,14 @@
                      AppCacheDiskCacheInterface* disk_cache);
 
   virtual void OnIOComplete(int result) = 0;
+  virtual void OnOpenEntryComplete() {}
 
   bool IsIOPending() { return !callback_.is_null(); }
   void ScheduleIOCompletionCallback(int result);
   void InvokeUserCompletionCallback(int result);
   void ReadRaw(int index, int offset, net::IOBuffer* buf, int buf_len);
   void WriteRaw(int index, int offset, net::IOBuffer* buf, int buf_len);
+  void OpenEntryIfNeeded();
 
   const int64 response_id_;
   const int64 group_id_;
@@ -121,10 +123,12 @@
   scoped_refptr<net::IOBuffer> buffer_;
   int buffer_len_;
   net::CompletionCallback callback_;
+  net::CompletionCallback open_callback_;
   base::WeakPtrFactory<AppCacheResponseIO> weak_factory_;
 
  private:
   void OnRawIOComplete(int result);
+  void OpenEntryCallback(AppCacheDiskCacheInterface::Entry** entry, int rv);
 };
 
 // Reads existing response data from storage. If the object is deleted
@@ -178,15 +182,14 @@
                          AppCacheDiskCacheInterface* disk_cache);
 
   void OnIOComplete(int result) override;
+  void OnOpenEntryComplete() override;
   void ContinueReadInfo();
   void ContinueReadData();
-  void OpenEntryIfNeededAndContinue();
-  void OnOpenEntryComplete(AppCacheDiskCacheInterface::Entry** entry, int rv);
 
   int range_offset_;
   int range_length_;
   int read_position_;
-  net::CompletionCallback open_callback_;
+  int reading_metadata_size_;
   base::WeakPtrFactory<AppCacheResponseReader> weak_factory_;
 };
 
@@ -257,6 +260,47 @@
   base::WeakPtrFactory<AppCacheResponseWriter> weak_factory_;
 };
 
+// Writes metadata of the existing response to storage. If the object is deleted
+// and there is a write in progress, the implementation will return
+// immediately but will take care of any side effect of cancelling the
+// operation. In other words, instances are safe to delete at will.
+class CONTENT_EXPORT AppCacheResponseMetadataWriter
+    : public AppCacheResponseIO {
+ public:
+  ~AppCacheResponseMetadataWriter() override;
+
+  // Writes metadata to storage. Always returns the result of the write
+  // asynchronously through the 'callback'. Returns the number of bytes written
+  // or a net:: error code. Guaranteed to not perform partial writes.
+  // The writer acquires a reference to the provided 'buf' until completion at
+  // which time the callback is invoked with either a negative error code or
+  // the number of bytes written. The 'callback' is a required parameter.
+  // The contents of 'buf' are not modified.
+  // Should only be called where there is no WriteMetadata operation in
+  // progress.
+  void WriteMetadata(net::IOBuffer* buf,
+                     int buf_len,
+                     const net::CompletionCallback& callback);
+
+  // Returns true if there is a write pending.
+  bool IsWritePending() { return IsIOPending(); }
+
+ protected:
+  friend class AppCacheStorageImpl;
+  friend class content::MockAppCacheStorage;
+  // Should only be constructed by the storage class and derivatives.
+  AppCacheResponseMetadataWriter(int64 response_id,
+                                 int64 group_id,
+                                 AppCacheDiskCacheInterface* disk_cache);
+
+ private:
+  void OnIOComplete(int result) override;
+  void OnOpenEntryComplete() override;
+
+  int write_amount_;
+  base::WeakPtrFactory<AppCacheResponseMetadataWriter> weak_factory_;
+};
+
 }  // namespace content
 
 #endif  // CONTENT_BROWSER_APPCACHE_APPCACHE_RESPONSE_H_
diff --git a/content/browser/appcache/appcache_response_unittest.cc b/content/browser/appcache/appcache_response_unittest.cc
index 815e697..5f91f92f 100644
--- a/content/browser/appcache/appcache_response_unittest.cc
+++ b/content/browser/appcache/appcache_response_unittest.cc
@@ -191,6 +191,16 @@
                                   base::Unretained(this)));
   }
 
+  void WriteResponseMetadata(scoped_refptr<IOBuffer> io_buffer, int buf_len) {
+    EXPECT_FALSE(metadata_writer_->IsWritePending());
+    write_buffer_ = io_buffer;
+    expected_write_result_ = buf_len;
+    metadata_writer_->WriteMetadata(
+        write_buffer_.get(), buf_len,
+        base::Bind(&AppCacheResponseTest::OnMetadataWriteComplete,
+                   base::Unretained(this)));
+  }
+
   void ReadResponseBody(scoped_refptr<IOBuffer> io_buffer, int buf_len) {
     EXPECT_FALSE(reader_->IsReadPending());
     read_buffer_ = io_buffer;
@@ -220,6 +230,12 @@
     ScheduleNextTask();
   }
 
+  void OnMetadataWriteComplete(int result) {
+    EXPECT_FALSE(metadata_writer_->IsWritePending());
+    EXPECT_EQ(expected_write_result_, result);
+    ScheduleNextTask();
+  }
+
   void OnReadInfoComplete(int result) {
     EXPECT_FALSE(reader_->IsReadPending());
     EXPECT_EQ(expected_read_result_, result);
@@ -373,6 +389,87 @@
     TestFinished();
   }
 
+  // Metadata -------------------------------------------------
+  void Metadata() {
+    // This tests involves multiple async steps.
+    // 1. Write a response headers and body to storage
+    //   a. headers
+    //   b. body
+    // 2. Write metadata "Metadata First" using AppCacheResponseMetadataWriter.
+    // 3. Check metadata was written.
+    // 4. Write metadata "Second".
+    // 5. Check metadata was written and was truncated .
+    // 6. Write metadata "".
+    // 7. Check metadata was deleted.
+
+    // Push tasks in reverse order.
+    PushNextTask(base::Bind(&AppCacheResponseTest::Metadata_VerifyMetadata,
+                            base::Unretained(this), ""));
+    PushNextTask(base::Bind(&AppCacheResponseTest::Metadata_LoadResponseInfo,
+                            base::Unretained(this)));
+    PushNextTask(base::Bind(&AppCacheResponseTest::Metadata_WriteMetadata,
+                            base::Unretained(this), ""));
+    PushNextTask(base::Bind(&AppCacheResponseTest::Metadata_VerifyMetadata,
+                            base::Unretained(this), "Second"));
+    PushNextTask(base::Bind(&AppCacheResponseTest::Metadata_LoadResponseInfo,
+                            base::Unretained(this)));
+    PushNextTask(base::Bind(&AppCacheResponseTest::Metadata_WriteMetadata,
+                            base::Unretained(this), "Second"));
+    PushNextTask(base::Bind(&AppCacheResponseTest::Metadata_VerifyMetadata,
+                            base::Unretained(this), "Metadata First"));
+    PushNextTask(base::Bind(&AppCacheResponseTest::Metadata_LoadResponseInfo,
+                            base::Unretained(this)));
+    PushNextTask(base::Bind(&AppCacheResponseTest::Metadata_WriteMetadata,
+                            base::Unretained(this), "Metadata First"));
+    PushNextTask(base::Bind(&AppCacheResponseTest::Metadata_ResetWriter,
+                            base::Unretained(this)));
+    writer_.reset(service_->storage()->CreateResponseWriter(GURL(), 0));
+    written_response_id_ = writer_->response_id();
+    WriteBasicResponse();
+  }
+
+  void Metadata_ResetWriter() {
+    writer_.reset();
+    ScheduleNextTask();
+  }
+
+  void Metadata_WriteMetadata(const char* metadata) {
+    metadata_writer_.reset(service_->storage()->CreateResponseMetadataWriter(
+        0, written_response_id_));
+    scoped_refptr<IOBuffer> buffer(new WrappedIOBuffer(metadata));
+    WriteResponseMetadata(buffer.get(), strlen(metadata));
+  }
+
+  void Metadata_LoadResponseInfo() {
+    metadata_writer_.reset();
+    storage_delegate_.reset(new MockStorageDelegate(this));
+    service_->storage()->LoadResponseInfo(GURL(), 0, written_response_id_,
+                                          storage_delegate_.get());
+  }
+
+  void Metadata_VerifyMetadata(const char* metadata) {
+    EXPECT_EQ(written_response_id_, storage_delegate_->loaded_info_id_);
+    EXPECT_TRUE(storage_delegate_->loaded_info_.get());
+    const net::HttpResponseInfo* read_head =
+        storage_delegate_->loaded_info_->http_response_info();
+    EXPECT_TRUE(read_head);
+    const int metadata_size = strlen(metadata);
+    if (metadata_size) {
+      EXPECT_TRUE(read_head->metadata.get());
+      EXPECT_EQ(metadata_size, read_head->metadata->size());
+      EXPECT_EQ(0,
+                memcmp(metadata, read_head->metadata->data(), metadata_size));
+    } else {
+      EXPECT_FALSE(read_head->metadata.get());
+    }
+    EXPECT_TRUE(CompareHttpResponseInfos(
+        write_info_buffer_->http_info.get(),
+        storage_delegate_->loaded_info_->http_response_info()));
+    EXPECT_EQ(basic_response_size(),
+              storage_delegate_->loaded_info_->response_data_size());
+    ScheduleNextTask();
+  }
+
   // AmountWritten ----------------------------------------------------
 
   void AmountWritten() {
@@ -667,6 +764,7 @@
 
   int64 written_response_id_;
   scoped_ptr<AppCacheResponseWriter> writer_;
+  scoped_ptr<AppCacheResponseMetadataWriter> metadata_writer_;
   scoped_refptr<HttpResponseInfoIOBuffer> write_info_buffer_;
   scoped_refptr<IOBuffer> write_buffer_;
   int expected_write_result_;
@@ -692,6 +790,10 @@
   RunTestOnIOThread(&AppCacheResponseTest::LoadResponseInfo_Hit);
 }
 
+TEST_F(AppCacheResponseTest, Metadata) {
+  RunTestOnIOThread(&AppCacheResponseTest::Metadata);
+}
+
 TEST_F(AppCacheResponseTest, AmountWritten) {
   RunTestOnIOThread(&AppCacheResponseTest::AmountWritten);
 }
diff --git a/content/browser/appcache/appcache_storage.h b/content/browser/appcache/appcache_storage.h
index edc059b..854b0d1 100644
--- a/content/browser/appcache/appcache_storage.h
+++ b/content/browser/appcache/appcache_storage.h
@@ -26,6 +26,7 @@
 class AppCacheEntry;
 class AppCacheGroup;
 class AppCacheQuotaClientTest;
+class AppCacheResponseMetadataWriter;
 class AppCacheResponseReader;
 class AppCacheResponseTest;
 class AppCacheResponseWriter;
@@ -169,6 +170,11 @@
   virtual AppCacheResponseWriter* CreateResponseWriter(
       const GURL& manifest_url, int64 group_id) = 0;
 
+  // Creates a metadata writer to write metadata of response to storage.
+  virtual AppCacheResponseMetadataWriter* CreateResponseMetadataWriter(
+      int64 group_id,
+      int64 response_id) = 0;
+
   // Schedules the lazy deletion of responses and saves the ids
   // persistently such that the responses will be deleted upon restart
   // if they aren't deleted prior to shutdown.
diff --git a/content/browser/appcache/appcache_storage_impl.cc b/content/browser/appcache/appcache_storage_impl.cc
index 5df1d3e..3653488 100644
--- a/content/browser/appcache/appcache_storage_impl.cc
+++ b/content/browser/appcache/appcache_storage_impl.cc
@@ -14,6 +14,7 @@
 #include "base/files/file_util.h"
 #include "base/logging.h"
 #include "base/message_loop/message_loop.h"
+#include "base/profiler/scoped_tracker.h"
 #include "base/single_thread_task_runner.h"
 #include "base/stl_util.h"
 #include "base/strings/string_util.h"
@@ -285,6 +286,8 @@
 };
 
 void AppCacheStorageImpl::InitTask::Run() {
+  tracked_objects::ScopedTracker tracking_profile(
+      FROM_HERE_WITH_EXPLICIT_FUNCTION("AppCacheStorageImpl::InitTask"));
   // If there is no sql database, ensure there is no disk cache either.
   if (!db_file_path_.empty() &&
       !base::PathExists(db_file_path_) &&
@@ -512,14 +515,16 @@
 };
 
 void AppCacheStorageImpl::CacheLoadTask::Run() {
+  tracked_objects::ScopedTracker tracking_profile(
+      FROM_HERE_WITH_EXPLICIT_FUNCTION("AppCacheStorageImpl::CacheLoadTask"));
   success_ =
       database_->FindCache(cache_id_, &cache_record_) &&
       database_->FindGroup(cache_record_.group_id, &group_record_) &&
       FindRelatedCacheRecords(cache_id_);
 
   if (success_)
-    database_->UpdateGroupLastAccessTime(group_record_.group_id,
-                                         base::Time::Now());
+    database_->LazyUpdateLastAccessTime(group_record_.group_id,
+                                        base::Time::Now());
 }
 
 void AppCacheStorageImpl::CacheLoadTask::RunCompleted() {
@@ -527,6 +532,7 @@
   scoped_refptr<AppCache> cache;
   scoped_refptr<AppCacheGroup> group;
   if (success_ && !storage_->is_disabled()) {
+    storage_->LazilyCommitLastAccessTimes();
     DCHECK(cache_record_.cache_id == cache_id_);
     CreateCacheAndGroupFromRecords(&cache, &group);
   }
@@ -554,14 +560,16 @@
 };
 
 void AppCacheStorageImpl::GroupLoadTask::Run() {
+  tracked_objects::ScopedTracker tracking_profile(
+      FROM_HERE_WITH_EXPLICIT_FUNCTION("AppCacheStorageImpl::GroupLoadTask"));
   success_ =
       database_->FindGroupForManifestUrl(manifest_url_, &group_record_) &&
       database_->FindCacheForGroup(group_record_.group_id, &cache_record_) &&
       FindRelatedCacheRecords(cache_record_.cache_id);
 
   if (success_)
-    database_->UpdateGroupLastAccessTime(group_record_.group_id,
-                                         base::Time::Now());
+    database_->LazyUpdateLastAccessTime(group_record_.group_id,
+                                        base::Time::Now());
 }
 
 void AppCacheStorageImpl::GroupLoadTask::RunCompleted() {
@@ -570,6 +578,7 @@
   scoped_refptr<AppCache> cache;
   if (!storage_->is_disabled()) {
     if (success_) {
+      storage_->LazilyCommitLastAccessTimes();
       DCHECK(group_record_.manifest_url == manifest_url_);
       CreateCacheAndGroupFromRecords(&cache, &group);
     } else {
@@ -690,8 +699,8 @@
     DCHECK(group_record_.manifest_url == existing_group.manifest_url);
     DCHECK(group_record_.origin == existing_group.origin);
 
-    database_->UpdateGroupLastAccessTime(group_record_.group_id,
-                                         base::Time::Now());
+    database_->UpdateLastAccessTime(group_record_.group_id,
+                                    base::Time::Now());
 
     AppCacheDatabase::CacheRecord cache;
     if (database_->FindCacheForGroup(group_record_.group_id, &cache)) {
@@ -931,6 +940,9 @@
 };
 
 void AppCacheStorageImpl::FindMainResponseTask::Run() {
+  tracked_objects::ScopedTracker tracking_profile(
+      FROM_HERE_WITH_EXPLICIT_FUNCTION(
+          "AppCacheStorageImpl::FindMainResponseTask"));
   // NOTE: The heuristics around choosing amoungst multiple candidates
   // is underspecified, and just plain not fully understood. This needs
   // to be refined.
@@ -1286,12 +1298,12 @@
   database_->DeleteDeletableResponseIds(response_ids_);
 }
 
-// UpdateGroupLastAccessTimeTask -------
+// LazyUpdateLastAccessTimeTask -------
 
-class AppCacheStorageImpl::UpdateGroupLastAccessTimeTask
+class AppCacheStorageImpl::LazyUpdateLastAccessTimeTask
     : public DatabaseTask {
  public:
-  UpdateGroupLastAccessTimeTask(
+  LazyUpdateLastAccessTimeTask(
       AppCacheStorageImpl* storage, AppCacheGroup* group, base::Time time)
       : DatabaseTask(storage), group_id_(group->group_id()),
         last_access_time_(time) {
@@ -1300,19 +1312,46 @@
 
   // DatabaseTask:
   void Run() override;
+  void RunCompleted() override;
 
  protected:
-  ~UpdateGroupLastAccessTimeTask() override {}
+  ~LazyUpdateLastAccessTimeTask() override {}
 
  private:
   int64 group_id_;
   base::Time last_access_time_;
 };
 
-void AppCacheStorageImpl::UpdateGroupLastAccessTimeTask::Run() {
-  database_->UpdateGroupLastAccessTime(group_id_, last_access_time_);
+void AppCacheStorageImpl::LazyUpdateLastAccessTimeTask::Run() {
+  tracked_objects::ScopedTracker tracking_profile(
+      FROM_HERE_WITH_EXPLICIT_FUNCTION(
+          "AppCacheStorageImpl::LazyUpdateLastAccessTimeTask"));
+  database_->LazyUpdateLastAccessTime(group_id_, last_access_time_);
 }
 
+void AppCacheStorageImpl::LazyUpdateLastAccessTimeTask::RunCompleted() {
+  storage_->LazilyCommitLastAccessTimes();
+}
+
+// CommitLastAccessTimesTask -------
+
+class AppCacheStorageImpl::CommitLastAccessTimesTask
+    : public DatabaseTask {
+ public:
+  CommitLastAccessTimesTask(AppCacheStorageImpl* storage)
+      : DatabaseTask(storage) {}
+
+  // DatabaseTask:
+  void Run() override {
+    tracked_objects::ScopedTracker tracking_profile(
+        FROM_HERE_WITH_EXPLICIT_FUNCTION(
+            "AppCacheStorageImpl::CommitLastAccessTimesTask"));
+    database_->CommitLazyLastAccessTimes();
+  }
+
+ protected:
+  ~CommitLastAccessTimesTask() override {}
+};
 
 // AppCacheStorageImpl ---------------------------------------------------
 
@@ -1400,7 +1439,7 @@
     delegate->OnCacheLoaded(cache, id);
     if (cache->owning_group()) {
       scoped_refptr<DatabaseTask> update_task(
-          new UpdateGroupLastAccessTimeTask(
+          new LazyUpdateLastAccessTimeTask(
               this, cache->owning_group(), base::Time::Now()));
       update_task->Schedule();
     }
@@ -1429,7 +1468,7 @@
   if (group) {
     delegate->OnGroupLoaded(group, manifest_url);
     scoped_refptr<DatabaseTask> update_task(
-        new UpdateGroupLastAccessTimeTask(
+        new LazyUpdateLastAccessTimeTask(
             this, group, base::Time::Now()));
     update_task->Schedule();
     return;
@@ -1643,6 +1682,13 @@
   return new AppCacheResponseWriter(NewResponseId(), group_id, disk_cache());
 }
 
+AppCacheResponseMetadataWriter*
+AppCacheStorageImpl::CreateResponseMetadataWriter(int64 group_id,
+                                                  int64 response_id) {
+  return new AppCacheResponseMetadataWriter(response_id, group_id,
+                                            disk_cache());
+}
+
 void AppCacheStorageImpl::DoomResponses(
     const GURL& manifest_url, const std::vector<int64>& response_ids) {
   if (response_ids.empty())
@@ -1691,12 +1737,12 @@
 
 void AppCacheStorageImpl::ScheduleDeleteOneResponse() {
   DCHECK(!is_response_deletion_scheduled_);
-  const base::TimeDelta kDelay = base::TimeDelta::FromMilliseconds(10);
+  const base::TimeDelta kBriefDelay = base::TimeDelta::FromMilliseconds(10);
   base::MessageLoop::current()->PostDelayedTask(
       FROM_HERE,
       base::Bind(&AppCacheStorageImpl::DeleteOneResponse,
                  weak_factory_.GetWeakPtr()),
-      kDelay);
+      kBriefDelay);
   is_response_deletion_scheduled_ = true;
 }
 
@@ -1863,4 +1909,22 @@
   // note: 'this' may be deleted at this point.
 }
 
+void AppCacheStorageImpl::LazilyCommitLastAccessTimes() {
+  if (lazy_commit_timer_.IsRunning())
+    return;
+  const base::TimeDelta kDelay = base::TimeDelta::FromMinutes(5);
+  lazy_commit_timer_.Start(
+      FROM_HERE, kDelay,
+      base::Bind(&AppCacheStorageImpl::OnLazyCommitTimer,
+                 weak_factory_.GetWeakPtr()));
+}
+
+void AppCacheStorageImpl::OnLazyCommitTimer() {
+  lazy_commit_timer_.Stop();
+  if (is_disabled())
+    return;
+  scoped_refptr<DatabaseTask> task(new CommitLastAccessTimesTask(this));
+  task->Schedule();
+}
+
 }  // namespace content
diff --git a/content/browser/appcache/appcache_storage_impl.h b/content/browser/appcache/appcache_storage_impl.h
index ca6d76b..4a8ebcc3 100644
--- a/content/browser/appcache/appcache_storage_impl.h
+++ b/content/browser/appcache/appcache_storage_impl.h
@@ -15,6 +15,7 @@
 #include "base/files/file_path.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
+#include "base/timer/timer.h"
 #include "content/browser/appcache/appcache_database.h"
 #include "content/browser/appcache/appcache_disk_cache.h"
 #include "content/browser/appcache/appcache_storage.h"
@@ -64,6 +65,9 @@
                                                int64 response_id) override;
   AppCacheResponseWriter* CreateResponseWriter(const GURL& manifest_url,
                                                int64 group_id) override;
+  AppCacheResponseMetadataWriter* CreateResponseMetadataWriter(
+      int64 group_id,
+      int64 response_id) override;
   void DoomResponses(const GURL& manifest_url,
                      const std::vector<int64>& response_ids) override;
   void DeleteResponses(const GURL& manifest_url,
@@ -87,7 +91,8 @@
   class GetDeletableResponseIdsTask;
   class InsertDeletableResponseIdsTask;
   class DeleteDeletableResponseIdsTask;
-  class UpdateGroupLastAccessTimeTask;
+  class LazyUpdateLastAccessTimeTask;
+  class CommitLastAccessTimesTask;
 
   typedef std::deque<DatabaseTask*> DatabaseTaskQueue;
   typedef std::map<int64, CacheLoadTask*> PendingCacheLoads;
@@ -111,12 +116,13 @@
   void StartDeletingResponses(const std::vector<int64>& response_ids);
   void ScheduleDeleteOneResponse();
   void DeleteOneResponse();
-
   void OnDeletedOneResponse(int rv);
   void OnDiskCacheInitialized(int rv);
   void DeleteAndStartOver();
   void DeleteAndStartOverPart2();
   void CallScheduleReinitialize();
+  void LazilyCommitLastAccessTimes();
+  void OnLazyCommitTimer();
 
   // Sometimes we can respond without having to query the database.
   bool FindResponseForMainRequestInGroup(
@@ -168,6 +174,7 @@
   bool is_disabled_;
 
   scoped_ptr<AppCacheDiskCache> disk_cache_;
+  base::OneShotTimer<AppCacheStorageImpl> lazy_commit_timer_;
 
   // Used to short-circuit certain operations without having to schedule
   // any tasks on the background database thread.
diff --git a/content/browser/appcache/appcache_url_request_job.cc b/content/browser/appcache/appcache_url_request_job.cc
index 6436c65..e253f4a 100644
--- a/content/browser/appcache/appcache_url_request_job.cc
+++ b/content/browser/appcache/appcache_url_request_job.cc
@@ -382,11 +382,6 @@
 }
 
 net::LoadState AppCacheURLRequestJob::GetLoadState() const {
-  // TODO(pkasting): Remove ScopedTracker below once crbug.com/455952 is
-  // fixed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "455952 AppCacheURLRequestJob::GetLoadState"));
   if (!has_been_started())
     return net::LOAD_STATE_IDLE;
   if (!has_delivery_orders())
diff --git a/content/browser/appcache/mock_appcache_storage.cc b/content/browser/appcache/mock_appcache_storage.cc
index 91630f7d..2a337a0e 100644
--- a/content/browser/appcache/mock_appcache_storage.cc
+++ b/content/browser/appcache/mock_appcache_storage.cc
@@ -165,6 +165,13 @@
   return new AppCacheResponseWriter(NewResponseId(),  group_id, disk_cache());
 }
 
+AppCacheResponseMetadataWriter*
+MockAppCacheStorage::CreateResponseMetadataWriter(int64 group_id,
+                                                  int64 response_id) {
+  return new AppCacheResponseMetadataWriter(response_id, group_id,
+                                            disk_cache());
+}
+
 void MockAppCacheStorage::DoomResponses(
     const GURL& manifest_url, const std::vector<int64>& response_ids) {
   DeleteResponses(manifest_url, response_ids);
diff --git a/content/browser/appcache/mock_appcache_storage.h b/content/browser/appcache/mock_appcache_storage.h
index a6ece43..7803ff7c 100644
--- a/content/browser/appcache/mock_appcache_storage.h
+++ b/content/browser/appcache/mock_appcache_storage.h
@@ -72,6 +72,9 @@
                                                int64 response_id) override;
   AppCacheResponseWriter* CreateResponseWriter(const GURL& manifest_url,
                                                int64 group_id) override;
+  AppCacheResponseMetadataWriter* CreateResponseMetadataWriter(
+      int64 group_id,
+      int64 response_id) override;
   void DoomResponses(const GURL& manifest_url,
                      const std::vector<int64>& response_ids) override;
   void DeleteResponses(const GURL& manifest_url,
diff --git a/content/browser/bluetooth/bluetooth_dispatcher_host.cc b/content/browser/bluetooth/bluetooth_dispatcher_host.cc
index 79941ef..c054e142 100644
--- a/content/browser/bluetooth/bluetooth_dispatcher_host.cc
+++ b/content/browser/bluetooth/bluetooth_dispatcher_host.cc
@@ -73,7 +73,12 @@
       // TODO(scheib): Filter devices by services: crbug.com/440594
       // TODO(scheib): Device selection UI: crbug.com/436280
       // TODO(scheib): Utilize BluetoothAdapter::Observer::DeviceAdded/Removed.
-      BluetoothAdapter::DeviceList devices = adapter_->GetDevices();
+      BluetoothAdapter::DeviceList devices;
+      if (adapter_.get())
+        devices = adapter_->GetDevices();
+      else
+        DLOG(WARNING) << "No BluetoothAdapter. Can't serve requestDevice.";
+
       if (devices.begin() == devices.end()) {
         Send(new BluetoothMsg_RequestDeviceError(thread_id, request_id,
                                                  BluetoothError::NOT_FOUND));
diff --git a/content/browser/browser_main_loop.cc b/content/browser/browser_main_loop.cc
index 4d578a2..2409bc7 100644
--- a/content/browser/browser_main_loop.cc
+++ b/content/browser/browser_main_loop.cc
@@ -487,13 +487,6 @@
 
   InitializeMainThread();
 
-#if defined(OS_CHROMEOS)
-  if (chromeos::switches::MemoryPressureHandlingEnabled()) {
-    memory_pressure_observer_.reset(new base::MemoryPressureObserverChromeOS(
-        chromeos::switches::GetMemoryPressureThresholds()));
-  }
-#endif
-
   {
     TRACE_EVENT0("startup", "BrowserMainLoop::Subsystem:SystemMonitor");
     system_monitor_.reset(new base::SystemMonitor);
@@ -599,6 +592,13 @@
     result_code_ = parts_->PreCreateThreads();
   }
 
+#if defined(OS_CHROMEOS)
+  if (chromeos::switches::MemoryPressureHandlingEnabled()) {
+    memory_pressure_observer_.reset(new base::MemoryPressureObserverChromeOS(
+        chromeos::switches::GetMemoryPressureThresholds()));
+  }
+#endif
+
 #if defined(ENABLE_PLUGINS)
   // Prior to any processing happening on the io thread, we create the
   // plugin service as it is predominantly used from the io thread,
diff --git a/content/browser/browser_plugin/browser_plugin_embedder.cc b/content/browser/browser_plugin/browser_plugin_embedder.cc
index d6c06fd1..826167f 100644
--- a/content/browser/browser_plugin/browser_plugin_embedder.cc
+++ b/content/browser/browser_plugin/browser_plugin_embedder.cc
@@ -4,24 +4,15 @@
 
 #include "content/browser/browser_plugin/browser_plugin_embedder.h"
 
-#include "base/values.h"
 #include "content/browser/browser_plugin/browser_plugin_guest.h"
 #include "content/browser/renderer_host/render_view_host_impl.h"
 #include "content/browser/web_contents/web_contents_impl.h"
-#include "content/common/browser_plugin/browser_plugin_constants.h"
 #include "content/common/browser_plugin/browser_plugin_messages.h"
 #include "content/common/drag_messages.h"
-#include "content/common/gpu/gpu_messages.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_plugin_guest_manager.h"
-#include "content/public/browser/content_browser_client.h"
 #include "content/public/browser/native_web_keyboard_event.h"
 #include "content/public/browser/render_view_host.h"
-#include "content/public/browser/user_metrics.h"
-#include "content/public/common/content_switches.h"
-#include "content/public/common/result_codes.h"
-#include "content/public/common/url_constants.h"
-#include "net/base/escape.h"
 #include "third_party/WebKit/public/web/WebFindOptions.h"
 #include "ui/events/keycodes/keyboard_codes.h"
 
@@ -59,13 +50,9 @@
   guest_drag_ending_ = false;
 }
 
-WebContentsImpl* BrowserPluginEmbedder::GetWebContents() const {
-  return static_cast<WebContentsImpl*>(web_contents());
-}
-
 BrowserPluginGuestManager*
 BrowserPluginEmbedder::GetBrowserPluginGuestManager() const {
-  return GetWebContents()->GetBrowserContext()->GetGuestManager();
+  return web_contents()->GetBrowserContext()->GetGuestManager();
 }
 
 void BrowserPluginEmbedder::ClearGuestDragStateIfApplicable() {
@@ -93,8 +80,8 @@
 
 void BrowserPluginEmbedder::DidSendScreenRects() {
   GetBrowserPluginGuestManager()->ForEachGuest(
-          GetWebContents(), base::Bind(
-              &BrowserPluginEmbedder::DidSendScreenRectsCallback));
+      web_contents(),
+      base::Bind(&BrowserPluginEmbedder::DidSendScreenRectsCallback));
 }
 
 bool BrowserPluginEmbedder::OnMessageReceived(const IPC::Message& message) {
@@ -150,13 +137,15 @@
   // routing ID. See http://crbug.com/436339.
   WebContents* guest_web_contents =
       GetBrowserPluginGuestManager()->GetGuestByInstanceID(
-          GetWebContents()->GetRenderProcessHost()->GetID(),
+          web_contents()->GetRenderProcessHost()->GetID(),
           browser_plugin_instance_id);
   if (!guest_web_contents)
     return;
   BrowserPluginGuest* guest = static_cast<WebContentsImpl*>(guest_web_contents)
                                   ->GetBrowserPluginGuest();
-  guest->Attach(browser_plugin_instance_id, GetWebContents(), params);
+  guest->Attach(browser_plugin_instance_id,
+                static_cast<WebContentsImpl*>(web_contents()),
+                params);
 }
 
 bool BrowserPluginEmbedder::HandleKeyboardEvent(
@@ -168,7 +157,7 @@
 
   bool event_consumed = false;
   GetBrowserPluginGuestManager()->ForEachGuest(
-      GetWebContents(),
+      web_contents(),
       base::Bind(&BrowserPluginEmbedder::UnlockMouseIfNecessaryCallback,
                  &event_consumed));
 
@@ -179,7 +168,7 @@
                                  const base::string16& search_text,
                                  const blink::WebFindOptions& options) {
   return GetBrowserPluginGuestManager()->ForEachGuest(
-      GetWebContents(),
+      web_contents(),
       base::Bind(&BrowserPluginEmbedder::FindInGuest,
                  request_id,
                  search_text,
@@ -188,7 +177,7 @@
 
 bool BrowserPluginEmbedder::StopFinding(StopFindAction action) {
   return GetBrowserPluginGuestManager()->ForEachGuest(
-      GetWebContents(),
+      web_contents(),
       base::Bind(&BrowserPluginEmbedder::StopFindingInGuest, action));
 }
 
diff --git a/content/browser/browser_plugin/browser_plugin_embedder.h b/content/browser/browser_plugin/browser_plugin_embedder.h
index 921ab15..7153fd1 100644
--- a/content/browser/browser_plugin/browser_plugin_embedder.h
+++ b/content/browser/browser_plugin/browser_plugin_embedder.h
@@ -14,27 +14,18 @@
 #ifndef CONTENT_BROWSER_BROWSER_PLUGIN_BROWSER_PLUGIN_EMBEDDER_H_
 #define CONTENT_BROWSER_BROWSER_PLUGIN_BROWSER_PLUGIN_EMBEDDER_H_
 
-#include <map>
-
 #include "base/memory/weak_ptr.h"
-#include "base/values.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_contents_observer.h"
 #include "third_party/WebKit/public/web/WebDragOperation.h"
 
 struct BrowserPluginHostMsg_Attach_Params;
-struct BrowserPluginHostMsg_ResizeGuest_Params;
-
-namespace gfx {
-class Point;
-}
 
 namespace content {
 
 class BrowserPluginGuest;
 class BrowserPluginGuestManager;
 class RenderWidgetHostImpl;
-class WebContentsImpl;
 struct NativeWebKeyboardEvent;
 
 class CONTENT_EXPORT BrowserPluginEmbedder : public WebContentsObserver {
@@ -43,9 +34,6 @@
 
   static BrowserPluginEmbedder* Create(WebContentsImpl* web_contents);
 
-  // Returns this embedder's WebContentsImpl.
-  WebContentsImpl* GetWebContents() const;
-
   // Called when embedder's |rwh| has sent screen rects to renderer.
   void DidSendScreenRects();
 
@@ -57,8 +45,6 @@
   void DragSourceEndedAt(int client_x, int client_y, int screen_x,
       int screen_y, blink::WebDragOperation operation);
 
-  void OnUpdateDragCursor(bool* handled);
-
   void DragEnteredGuest(BrowserPluginGuest* guest);
 
   void DragLeftGuest(BrowserPluginGuest* guest);
@@ -98,12 +84,11 @@
   static bool StopFindingInGuest(StopFindAction action, WebContents* guest);
 
   // Message handlers.
+
   void OnAttach(RenderFrameHost* render_frame_host,
                 int instance_id,
                 const BrowserPluginHostMsg_Attach_Params& params);
-  void OnPluginAtPositionResponse(int instance_id,
-                                  int request_id,
-                                  const gfx::Point& position);
+  void OnUpdateDragCursor(bool* handled);
 
   // Used to correctly update the cursor when dragging over a guest, and to
   // handle a race condition when dropping onto the guest that started the drag
diff --git a/content/browser/browser_url_handler_impl.cc b/content/browser/browser_url_handler_impl.cc
index 7ea7ff7..438feb3 100644
--- a/content/browser/browser_url_handler_impl.cc
+++ b/content/browser/browser_url_handler_impl.cc
@@ -88,7 +88,8 @@
   return Singleton<BrowserURLHandlerImpl>::get();
 }
 
-BrowserURLHandlerImpl::BrowserURLHandlerImpl() {
+BrowserURLHandlerImpl::BrowserURLHandlerImpl() :
+    fixup_handler_(null_handler()) {
   AddHandlerPair(&DebugURLHandler, BrowserURLHandlerImpl::null_handler());
 
   GetContentClient()->browser()->BrowserURLHandlerCreated(this);
@@ -100,6 +101,11 @@
 BrowserURLHandlerImpl::~BrowserURLHandlerImpl() {
 }
 
+void BrowserURLHandlerImpl::SetFixupHandler(URLHandler handler) {
+  DCHECK_EQ(null_handler(), fixup_handler_);
+  fixup_handler_ = handler;
+}
+
 void BrowserURLHandlerImpl::AddHandlerPair(URLHandler handler,
                                            URLHandler reverse_handler) {
   url_handlers_.push_back(HandlerPair(handler, reverse_handler));
@@ -118,6 +124,13 @@
   }
 }
 
+void BrowserURLHandlerImpl::FixupURLBeforeRewrite(
+    GURL* url,
+    BrowserContext* browser_context) {
+  if (fixup_handler_)
+    fixup_handler_(url, browser_context);
+}
+
 bool BrowserURLHandlerImpl::ReverseURLRewrite(
     GURL* url, const GURL& original, BrowserContext* browser_context) {
   for (size_t i = 0; i < url_handlers_.size(); ++i) {
diff --git a/content/browser/browser_url_handler_impl.h b/content/browser/browser_url_handler_impl.h
index 240554b7..4084418a 100644
--- a/content/browser/browser_url_handler_impl.h
+++ b/content/browser/browser_url_handler_impl.h
@@ -26,9 +26,13 @@
   void RewriteURLIfNecessary(GURL* url,
                              BrowserContext* browser_context,
                              bool* reverse_on_redirect) override;
+  void SetFixupHandler(URLHandler handler) override;
   // Add the specified handler pair to the list of URL handlers.
   void AddHandlerPair(URLHandler handler, URLHandler reverse_handler) override;
 
+  // Fixes up the URL before rewriting occurs.
+  void FixupURLBeforeRewrite(GURL* url, BrowserContext* browser_context);
+
   // Reverses the rewriting that was done for |original| using the new |url|.
   bool ReverseURLRewrite(GURL* url, const GURL& original,
                          BrowserContext* browser_context);
@@ -39,6 +43,9 @@
   ~BrowserURLHandlerImpl() override;
   friend struct DefaultSingletonTraits<BrowserURLHandlerImpl>;
 
+  // A URLHandler to run in a preliminary phase, before rewriting is done.
+  URLHandler fixup_handler_;
+
   // The list of known URLHandlers, optionally with reverse-rewriters.
   typedef std::pair<URLHandler, URLHandler> HandlerPair;
   std::vector<HandlerPair> url_handlers_;
diff --git a/content/browser/compositor/browser_compositor_output_surface_proxy.cc b/content/browser/compositor/browser_compositor_output_surface_proxy.cc
deleted file mode 100644
index 27fe55a7..0000000
--- a/content/browser/compositor/browser_compositor_output_surface_proxy.cc
+++ /dev/null
@@ -1,58 +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 "content/browser/compositor/browser_compositor_output_surface_proxy.h"
-
-#include "base/bind.h"
-#include "content/browser/compositor/browser_compositor_output_surface.h"
-#include "content/browser/gpu/browser_gpu_channel_host_factory.h"
-#include "content/common/gpu/gpu_messages.h"
-
-namespace content {
-
-BrowserCompositorOutputSurfaceProxy::BrowserCompositorOutputSurfaceProxy(
-    IDMap<BrowserCompositorOutputSurface>* surface_map)
-    : surface_map_(surface_map),
-      connected_to_gpu_process_host_id_(0) {}
-
-BrowserCompositorOutputSurfaceProxy::~BrowserCompositorOutputSurfaceProxy() {}
-
-void BrowserCompositorOutputSurfaceProxy::ConnectToGpuProcessHost(
-    base::SingleThreadTaskRunner* compositor_thread_task_runner) {
-  BrowserGpuChannelHostFactory* factory =
-      BrowserGpuChannelHostFactory::instance();
-
-  int gpu_process_host_id = factory->GpuProcessHostId();
-  if (connected_to_gpu_process_host_id_ == gpu_process_host_id)
-    return;
-
-  const uint32 kMessagesToFilter[] = { GpuHostMsg_UpdateVSyncParameters::ID };
-  factory->SetHandlerForControlMessages(
-      kMessagesToFilter,
-      arraysize(kMessagesToFilter),
-      base::Bind(&BrowserCompositorOutputSurfaceProxy::
-                     OnMessageReceivedOnCompositorThread,
-                 this),
-      compositor_thread_task_runner);
-  connected_to_gpu_process_host_id_ = gpu_process_host_id;
-}
-
-void BrowserCompositorOutputSurfaceProxy::OnMessageReceivedOnCompositorThread(
-    const IPC::Message& message) {
-  IPC_BEGIN_MESSAGE_MAP(BrowserCompositorOutputSurfaceProxy, message)
-      IPC_MESSAGE_HANDLER(GpuHostMsg_UpdateVSyncParameters,
-                          OnUpdateVSyncParametersOnCompositorThread);
-  IPC_END_MESSAGE_MAP()
-}
-
-void
-BrowserCompositorOutputSurfaceProxy::OnUpdateVSyncParametersOnCompositorThread(
-    int surface_id,
-    base::TimeTicks timebase,
-    base::TimeDelta interval) {
-  BrowserCompositorOutputSurface* surface = surface_map_->Lookup(surface_id);
-  if (surface)
-    surface->OnUpdateVSyncParametersFromGpu(timebase, interval);
-}
-}  // namespace content
diff --git a/content/browser/compositor/browser_compositor_output_surface_proxy.h b/content/browser/compositor/browser_compositor_output_surface_proxy.h
deleted file mode 100644
index 644516e..0000000
--- a/content/browser/compositor/browser_compositor_output_surface_proxy.h
+++ /dev/null
@@ -1,51 +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 CONTENT_BROWSER_COMPOSITOR_BROWSER_COMPOSITOR_OUTPUT_SURFACE_PROXY_H_
-#define CONTENT_BROWSER_COMPOSITOR_BROWSER_COMPOSITOR_OUTPUT_SURFACE_PROXY_H_
-
-#include "base/id_map.h"
-#include "base/memory/ref_counted.h"
-#include "base/time/time.h"
-#include "content/common/content_export.h"
-
-namespace base { class SingleThreadTaskRunner; }
-
-namespace IPC { class Message; }
-
-namespace content {
-class BrowserCompositorOutputSurface;
-
-// Directs vsync updates to the appropriate BrowserCompositorOutputSurface.
-class CONTENT_EXPORT BrowserCompositorOutputSurfaceProxy
-    : public base::RefCountedThreadSafe<BrowserCompositorOutputSurfaceProxy> {
- public:
-  BrowserCompositorOutputSurfaceProxy(
-      IDMap<BrowserCompositorOutputSurface>* surface_map);
-
-  // Call this before each OutputSurface is created to ensure that the
-  // proxy is connected to the current host.
-  void ConnectToGpuProcessHost(
-      base::SingleThreadTaskRunner* compositor_thread_task_runner);
-
- private:
-  friend class base::RefCountedThreadSafe<BrowserCompositorOutputSurfaceProxy>;
-  friend class SoftwareBrowserCompositorOutputSurface;
-  ~BrowserCompositorOutputSurfaceProxy();
-
-  void OnMessageReceivedOnCompositorThread(const IPC::Message& message);
-
-  void OnUpdateVSyncParametersOnCompositorThread(int surface_id,
-                                                 base::TimeTicks timebase,
-                                                 base::TimeDelta interval);
-
-  IDMap<BrowserCompositorOutputSurface>* surface_map_;
-  int connected_to_gpu_process_host_id_;
-
-  DISALLOW_COPY_AND_ASSIGN(BrowserCompositorOutputSurfaceProxy);
-};
-
-}  // namespace content
-
-#endif  // CONTENT_BROWSER_COMPOSITOR_BROWSER_COMPOSITOR_OUTPUT_SURFACE_PROXY_H_
diff --git a/content/browser/compositor/buffer_queue.cc b/content/browser/compositor/buffer_queue.cc
index c215827..e094972 100644
--- a/content/browser/compositor/buffer_queue.cc
+++ b/content/browser/compositor/buffer_queue.cc
@@ -173,6 +173,11 @@
           size_, gpu::ImageFactory::ImageFormatToGpuMemoryBufferFormat(
                      internalformat_),
           surface_id_));
+  if (!buffer) {
+    gl->DeleteTextures(1, &texture);
+    DLOG(ERROR) << "Failed to allocate GPU memory buffer";
+    return AllocatedSurface();
+  }
 
   unsigned int id = gl->CreateImageCHROMIUM(
       buffer->AsClientBuffer(), size_.width(), size_.height(), internalformat_);
diff --git a/content/browser/compositor/gpu_browser_compositor_output_surface.cc b/content/browser/compositor/gpu_browser_compositor_output_surface.cc
index b4da74cd..f6c1663 100644
--- a/content/browser/compositor/gpu_browser_compositor_output_surface.cc
+++ b/content/browser/compositor/gpu_browser_compositor_output_surface.cc
@@ -30,7 +30,10 @@
 #endif
       swap_buffers_completion_callback_(
           base::Bind(&GpuBrowserCompositorOutputSurface::OnSwapBuffersCompleted,
-                     base::Unretained(this))) {
+                     base::Unretained(this))),
+      update_vsync_parameters_callback_(base::Bind(
+          &BrowserCompositorOutputSurface::OnUpdateVSyncParametersFromGpu,
+          base::Unretained(this))) {
   overlay_candidate_validator_ = overlay_candidate_validator.Pass();
 }
 
@@ -54,6 +57,8 @@
 
   GetCommandBufferProxy()->SetSwapBuffersCompletionCallback(
       swap_buffers_completion_callback_.callback());
+  GetCommandBufferProxy()->SetUpdateVSyncParametersCallback(
+      update_vsync_parameters_callback_.callback());
   return true;
 }
 
diff --git a/content/browser/compositor/gpu_browser_compositor_output_surface.h b/content/browser/compositor/gpu_browser_compositor_output_surface.h
index b1d7e18..48fdeca 100644
--- a/content/browser/compositor/gpu_browser_compositor_output_surface.h
+++ b/content/browser/compositor/gpu_browser_compositor_output_surface.h
@@ -51,6 +51,9 @@
 
   base::CancelableCallback<void(const std::vector<ui::LatencyInfo>&)>
       swap_buffers_completion_callback_;
+  base::CancelableCallback<void(base::TimeTicks timebase,
+                                base::TimeDelta interval)>
+      update_vsync_parameters_callback_;
 
   DISALLOW_COPY_AND_ASSIGN(GpuBrowserCompositorOutputSurface);
 };
diff --git a/content/browser/compositor/gpu_process_transport_factory.cc b/content/browser/compositor/gpu_process_transport_factory.cc
index b9f65d5c..c10e1eb 100644
--- a/content/browser/compositor/gpu_process_transport_factory.cc
+++ b/content/browser/compositor/gpu_process_transport_factory.cc
@@ -18,7 +18,6 @@
 #include "cc/surfaces/surface_display_output_surface.h"
 #include "cc/surfaces/surface_manager.h"
 #include "content/browser/compositor/browser_compositor_output_surface.h"
-#include "content/browser/compositor/browser_compositor_output_surface_proxy.h"
 #include "content/browser/compositor/gpu_browser_compositor_output_surface.h"
 #include "content/browser/compositor/gpu_surfaceless_browser_compositor_output_surface.h"
 #include "content/browser/compositor/reflector_impl.h"
@@ -72,9 +71,6 @@
 GpuProcessTransportFactory::GpuProcessTransportFactory()
     : next_surface_id_namespace_(1u),
       callback_factory_(this) {
-  output_surface_proxy_ = new BrowserCompositorOutputSurfaceProxy(
-      &output_surface_map_);
-
   if (UseSurfacesEnabled())
     surface_manager_ = make_scoped_ptr(new cc::SurfaceManager);
 }
@@ -185,18 +181,6 @@
   UMA_HISTOGRAM_BOOLEAN("Aura.CreatedGpuBrowserCompositor",
                         !!context_provider.get());
 
-  if (context_provider.get()) {
-    scoped_refptr<base::SingleThreadTaskRunner> compositor_thread_task_runner =
-        GetCompositorMessageLoop();
-    if (!compositor_thread_task_runner.get())
-      compositor_thread_task_runner = base::MessageLoopProxy::current();
-
-    // Here we know the GpuProcessHost has been set up, because we created a
-    // context.
-    output_surface_proxy_->ConnectToGpuProcessHost(
-        compositor_thread_task_runner.get());
-  }
-
   if (UseSurfacesEnabled()) {
     // This gets a bit confusing. Here we have a ContextProvider configured to
     // render directly to this widget. We need to make an OnscreenDisplayClient
@@ -207,7 +191,6 @@
     if (!context_provider.get()) {
       display_surface =
           make_scoped_ptr(new SoftwareBrowserCompositorOutputSurface(
-              output_surface_proxy_,
               CreateSoftwareOutputDevice(compositor.get()),
               data->surface_id,
               &output_surface_map_,
@@ -245,7 +228,6 @@
 
     scoped_ptr<SoftwareBrowserCompositorOutputSurface> surface(
         new SoftwareBrowserCompositorOutputSurface(
-            output_surface_proxy_,
             CreateSoftwareOutputDevice(compositor.get()),
             data->surface_id,
             &output_surface_map_,
diff --git a/content/browser/compositor/gpu_process_transport_factory.h b/content/browser/compositor/gpu_process_transport_factory.h
index 90c9f4d..961a6e3 100644
--- a/content/browser/compositor/gpu_process_transport_factory.h
+++ b/content/browser/compositor/gpu_process_transport_factory.h
@@ -26,7 +26,6 @@
 
 namespace content {
 class BrowserCompositorOutputSurface;
-class BrowserCompositorOutputSurfaceProxy;
 class CompositorSwapClient;
 class ContextProviderCommandBuffer;
 class ReflectorImpl;
@@ -98,8 +97,6 @@
   // thread.
   IDMap<BrowserCompositorOutputSurface> output_surface_map_;
 
-  scoped_refptr<BrowserCompositorOutputSurfaceProxy> output_surface_proxy_;
-
   base::WeakPtrFactory<GpuProcessTransportFactory> callback_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(GpuProcessTransportFactory);
diff --git a/content/browser/compositor/software_browser_compositor_output_surface.cc b/content/browser/compositor/software_browser_compositor_output_surface.cc
index 671d8134..7ba98d3 100644
--- a/content/browser/compositor/software_browser_compositor_output_surface.cc
+++ b/content/browser/compositor/software_browser_compositor_output_surface.cc
@@ -10,7 +10,6 @@
 #include "cc/output/compositor_frame.h"
 #include "cc/output/output_surface_client.h"
 #include "cc/output/software_output_device.h"
-#include "content/browser/compositor/browser_compositor_output_surface_proxy.h"
 #include "content/browser/renderer_host/render_widget_host_impl.h"
 #include "ui/events/latency_info.h"
 #include "ui/gfx/vsync_provider.h"
@@ -18,7 +17,6 @@
 namespace content {
 
 SoftwareBrowserCompositorOutputSurface::SoftwareBrowserCompositorOutputSurface(
-    scoped_refptr<BrowserCompositorOutputSurfaceProxy> surface_proxy,
     scoped_ptr<cc::SoftwareOutputDevice> software_device,
     int surface_id,
     IDMap<BrowserCompositorOutputSurface>* output_surface_map,
@@ -27,10 +25,12 @@
                                      surface_id,
                                      output_surface_map,
                                      vsync_manager),
-      output_surface_proxy_(surface_proxy) {}
+      weak_factory_(this) {
+}
 
 SoftwareBrowserCompositorOutputSurface::
-    ~SoftwareBrowserCompositorOutputSurface() {}
+    ~SoftwareBrowserCompositorOutputSurface() {
+}
 
 void SoftwareBrowserCompositorOutputSurface::SwapBuffers(
     cc::CompositorFrame* frame) {
@@ -46,11 +46,9 @@
 
   gfx::VSyncProvider* vsync_provider = software_device()->GetVSyncProvider();
   if (vsync_provider) {
-    vsync_provider->GetVSyncParameters(
-        base::Bind(&BrowserCompositorOutputSurfaceProxy::
-                        OnUpdateVSyncParametersOnCompositorThread,
-                   output_surface_proxy_,
-                   surface_id_));
+    vsync_provider->GetVSyncParameters(base::Bind(
+        &BrowserCompositorOutputSurface::OnUpdateVSyncParametersFromGpu,
+        weak_factory_.GetWeakPtr()));
   }
   PostSwapBuffersComplete();
   client_->DidSwapBuffers();
diff --git a/content/browser/compositor/software_browser_compositor_output_surface.h b/content/browser/compositor/software_browser_compositor_output_surface.h
index 6b8c474..5ad0c2b 100644
--- a/content/browser/compositor/software_browser_compositor_output_surface.h
+++ b/content/browser/compositor/software_browser_compositor_output_surface.h
@@ -19,13 +19,10 @@
 
 namespace content {
 
-class BrowserCompositorOutputSurfaceProxy;
-
 class CONTENT_EXPORT SoftwareBrowserCompositorOutputSurface
     : public BrowserCompositorOutputSurface {
  public:
   SoftwareBrowserCompositorOutputSurface(
-      scoped_refptr<BrowserCompositorOutputSurfaceProxy> surface_proxy,
       scoped_ptr<cc::SoftwareOutputDevice> software_device,
       int surface_id,
       IDMap<BrowserCompositorOutputSurface>* output_surface_map,
@@ -42,9 +39,7 @@
   bool ShouldNotShowFramesAfterRecycle() const override;
 #endif
 
-  // On the software path we need to explicitly call the proxy to update the
-  // VSync parameters.
-  scoped_refptr<BrowserCompositorOutputSurfaceProxy> output_surface_proxy_;
+  base::WeakPtrFactory<SoftwareBrowserCompositorOutputSurface> weak_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(SoftwareBrowserCompositorOutputSurface);
 };
diff --git a/content/browser/compositor/software_browser_compositor_output_surface_unittest.cc b/content/browser/compositor/software_browser_compositor_output_surface_unittest.cc
index 904fca2d..7da7053 100644
--- a/content/browser/compositor/software_browser_compositor_output_surface_unittest.cc
+++ b/content/browser/compositor/software_browser_compositor_output_surface_unittest.cc
@@ -5,7 +5,6 @@
 #include "base/message_loop/message_loop.h"
 #include "cc/output/compositor_frame.h"
 #include "cc/test/fake_output_surface_client.h"
-#include "content/browser/compositor/browser_compositor_output_surface_proxy.h"
 #include "content/browser/compositor/software_browser_compositor_output_surface.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/compositor/compositor.h"
@@ -73,7 +72,6 @@
   scoped_ptr<ui::Compositor> compositor_;
 
   IDMap<content::BrowserCompositorOutputSurface> surface_map_;
-  scoped_refptr<content::BrowserCompositorOutputSurfaceProxy> surface_proxy_;
 
   DISALLOW_COPY_AND_ASSIGN(SoftwareBrowserCompositorOutputSurfaceTest);
 };
@@ -96,8 +94,6 @@
   compositor_.reset(new ui::Compositor(gfx::kNullAcceleratedWidget,
                                        context_factory,
                                        base::MessageLoopProxy::current()));
-  surface_proxy_ =
-      new content::BrowserCompositorOutputSurfaceProxy(&surface_map_);
 }
 
 void SoftwareBrowserCompositorOutputSurfaceTest::TearDown() {
@@ -115,7 +111,6 @@
     scoped_ptr<cc::SoftwareOutputDevice> device) {
   return scoped_ptr<content::BrowserCompositorOutputSurface>(
       new content::SoftwareBrowserCompositorOutputSurface(
-          surface_proxy_,
           device.Pass(),
           1,
           &surface_map_,
diff --git a/content/browser/devtools/devtools_frontend_host_impl.cc b/content/browser/devtools/devtools_frontend_host_impl.cc
index 6d3c6589..cef0ecb 100644
--- a/content/browser/devtools/devtools_frontend_host_impl.cc
+++ b/content/browser/devtools/devtools_frontend_host_impl.cc
@@ -4,6 +4,8 @@
 
 #include "content/browser/devtools/devtools_frontend_host_impl.h"
 
+#include "content/browser/frame_host/render_frame_host_impl.h"
+#include "content/browser/renderer_host/render_widget_host_impl.h"
 #include "content/common/devtools_messages.h"
 #include "content/public/browser/navigation_entry.h"
 #include "content/public/browser/render_frame_host.h"
@@ -31,6 +33,11 @@
 DevToolsFrontendHostImpl::~DevToolsFrontendHostImpl() {
 }
 
+void DevToolsFrontendHostImpl::RenderFrameCreated(RenderFrameHost* rfh) {
+  static_cast<RenderFrameHostImpl*>(rfh)->GetRenderWidgetHost()->
+      DisableElasticOverscroll();
+}
+
 bool DevToolsFrontendHostImpl::OnMessageReceived(
     const IPC::Message& message,
     RenderFrameHost* render_frame_host) {
diff --git a/content/browser/devtools/devtools_frontend_host_impl.h b/content/browser/devtools/devtools_frontend_host_impl.h
index ee616e5..9f65ee4 100644
--- a/content/browser/devtools/devtools_frontend_host_impl.h
+++ b/content/browser/devtools/devtools_frontend_host_impl.h
@@ -21,6 +21,7 @@
   // WebContentsObserver overrides.
   bool OnMessageReceived(const IPC::Message& message,
                          RenderFrameHost* render_frame_host) override;
+  void RenderFrameCreated(RenderFrameHost* render_frame_host) override;
 
   void OnDispatchOnInspectorBackend(const std::string& message);
   void OnDispatchOnEmbedder(const std::string& message);
diff --git a/content/browser/devtools/protocol/input_handler.cc b/content/browser/devtools/protocol/input_handler.cc
index 05cff61..d28a1e4 100644
--- a/content/browser/devtools/protocol/input_handler.cc
+++ b/content/browser/devtools/protocol/input_handler.cc
@@ -25,6 +25,9 @@
   host_ = host;
 }
 
+void InputHandler::SetClient(scoped_ptr<DevToolsProtocolClient> client) {
+}
+
 Response InputHandler::EmulateTouchFromMouseEvent(const std::string& type,
                                                   int x,
                                                   int y,
@@ -106,6 +109,40 @@
   return Response::OK();
 }
 
+Response InputHandler::SynthesizePinchGesture(
+    DevToolsCommandId command_id,
+    int x,
+    int y,
+    double scale_factor,
+    const int* relative_speed,
+    const std::string* gesture_source_type) {
+  return Response::InternalError("Not yet implemented");
+}
+
+Response InputHandler::SynthesizeScrollGesture(
+    DevToolsCommandId command_id,
+    int x,
+    int y,
+    const int* x_distance,
+    const int* y_distance,
+    const int* x_overscroll,
+    const int* y_overscroll,
+    const bool* prevent_fling,
+    const int* speed,
+    const std::string* gesture_source_type) {
+  return Response::InternalError("Not yet implemented");
+}
+
+Response InputHandler::SynthesizeTapGesture(
+    DevToolsCommandId command_id,
+    int x,
+    int y,
+    const int* duration,
+    const int* tap_count,
+    const std::string* gesture_source_type) {
+  return Response::InternalError("Not yet implemented");
+}
+
 }  // namespace input
 }  // namespace devtools
 }  // namespace content
diff --git a/content/browser/devtools/protocol/input_handler.h b/content/browser/devtools/protocol/input_handler.h
index edb71832..062ad1e 100644
--- a/content/browser/devtools/protocol/input_handler.h
+++ b/content/browser/devtools/protocol/input_handler.h
@@ -22,6 +22,7 @@
   virtual ~InputHandler();
 
   void SetRenderViewHost(RenderViewHostImpl* host);
+  void SetClient(scoped_ptr<DevToolsProtocolClient> client);
 
   Response EmulateTouchFromMouseEvent(const std::string& type,
                                       int x,
@@ -33,13 +34,38 @@
                                       int* modifiers,
                                       int* click_count);
 
+  Response SynthesizePinchGesture(DevToolsCommandId command_id,
+                                  int x,
+                                  int y,
+                                  double scale_factor,
+                                  const int* relative_speed,
+                                  const std::string* gesture_source_type);
+
+  Response SynthesizeScrollGesture(DevToolsCommandId command_id,
+                                   int x,
+                                   int y,
+                                   const int* x_distance,
+                                   const int* y_distance,
+                                   const int* x_overscroll,
+                                   const int* y_overscroll,
+                                   const bool* prevent_fling,
+                                   const int* speed,
+                                   const std::string* gesture_source_type);
+
+  Response SynthesizeTapGesture(DevToolsCommandId command_id,
+                                int x,
+                                int y,
+                                const int* duration,
+                                const int* tap_count,
+                                const std::string* gesture_source_type);
+
  private:
   RenderViewHostImpl* host_;
 
   DISALLOW_COPY_AND_ASSIGN(InputHandler);
 };
 
-}  // namespace inpue
+}  // namespace input
 }  // namespace devtools
 }  // namespace content
 
diff --git a/content/browser/dom_storage/dom_storage_area.cc b/content/browser/dom_storage/dom_storage_area.cc
index 90a55a0..a2611eb 100644
--- a/content/browser/dom_storage/dom_storage_area.cc
+++ b/content/browser/dom_storage/dom_storage_area.cc
@@ -4,10 +4,13 @@
 
 #include "content/browser/dom_storage/dom_storage_area.h"
 
+#include <algorithm>
+
 #include "base/bind.h"
 #include "base/location.h"
 #include "base/logging.h"
 #include "base/metrics/histogram.h"
+#include "base/process/process_info.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/time/time.h"
 #include "content/browser/dom_storage/dom_storage_namespace.h"
@@ -25,13 +28,44 @@
 
 namespace content {
 
-static const int kCommitTimerSeconds = 1;
+namespace {
 
-DOMStorageArea::CommitBatch::CommitBatch()
-  : clear_all_first(false) {
+// Delay for a moment after a value is set in anticipation
+// of other values being set, so changes are batched.
+const int kCommitDefaultDelaySecs = 5;
+
+// To avoid excessive IO we apply limits to the amount of data being written
+// and the frequency of writes. The specific values used are somewhat arbitrary.
+const int kMaxBytesPerDay = kPerStorageAreaQuota * 2;
+const int kMaxCommitsPerHour = 6;
+
+}  // namespace
+
+DOMStorageArea::RateLimiter::RateLimiter(size_t desired_rate,
+                                         base::TimeDelta time_quantum)
+    : rate_(desired_rate), samples_(0), time_quantum_(time_quantum) {
+  DCHECK_GT(desired_rate, 0ul);
+}
+
+base::TimeDelta DOMStorageArea::RateLimiter::ComputeTimeNeeded() const {
+  return time_quantum_.multiply_by(samples_ / rate_);
+}
+
+base::TimeDelta DOMStorageArea::RateLimiter::ComputeDelayNeeded(
+    const base::TimeDelta elapsed_time) const {
+  base::TimeDelta time_needed = ComputeTimeNeeded();
+  if (time_needed > elapsed_time)
+    return time_needed - elapsed_time;
+  return base::TimeDelta();
+}
+
+DOMStorageArea::CommitBatch::CommitBatch() : clear_all_first(false) {
 }
 DOMStorageArea::CommitBatch::~CommitBatch() {}
 
+size_t DOMStorageArea::CommitBatch::GetDataSize() const {
+  return DOMStorageMap::CountBytes(changed_values);
+}
 
 // static
 const base::FilePath::CharType DOMStorageArea::kDatabaseFileExtension[] =
@@ -55,17 +89,21 @@
   return storage::GetOriginFromIdentifier(origin_id);
 }
 
-DOMStorageArea::DOMStorageArea(
-    const GURL& origin, const base::FilePath& directory,
-    DOMStorageTaskRunner* task_runner)
-    : namespace_id_(kLocalStorageNamespaceId), origin_(origin),
+DOMStorageArea::DOMStorageArea(const GURL& origin,
+                               const base::FilePath& directory,
+                               DOMStorageTaskRunner* task_runner)
+    : namespace_id_(kLocalStorageNamespaceId),
+      origin_(origin),
       directory_(directory),
       task_runner_(task_runner),
       map_(new DOMStorageMap(kPerStorageAreaQuota +
                              kPerStorageAreaOverQuotaAllowance)),
       is_initial_import_done_(true),
       is_shutdown_(false),
-      commit_batches_in_flight_(0) {
+      commit_batches_in_flight_(0),
+      start_time_(base::TimeTicks::Now()),
+      data_rate_limiter_(kMaxBytesPerDay, base::TimeDelta::FromHours(24)),
+      commit_rate_limiter_(kMaxCommitsPerHour, base::TimeDelta::FromHours(1)) {
   if (!directory.empty()) {
     base::FilePath path = directory.Append(DatabaseFileNameFromOrigin(origin_));
     backing_.reset(new LocalStorageDatabaseAdapter(path));
@@ -73,12 +111,11 @@
   }
 }
 
-DOMStorageArea::DOMStorageArea(
-    int64 namespace_id,
-    const std::string& persistent_namespace_id,
-    const GURL& origin,
-    SessionStorageDatabase* session_storage_backing,
-    DOMStorageTaskRunner* task_runner)
+DOMStorageArea::DOMStorageArea(int64 namespace_id,
+                               const std::string& persistent_namespace_id,
+                               const GURL& origin,
+                               SessionStorageDatabase* session_storage_backing,
+                               DOMStorageTaskRunner* task_runner)
     : namespace_id_(namespace_id),
       persistent_namespace_id_(persistent_namespace_id),
       origin_(origin),
@@ -88,7 +125,10 @@
       session_storage_backing_(session_storage_backing),
       is_initial_import_done_(true),
       is_shutdown_(false),
-      commit_batches_in_flight_(0) {
+      commit_batches_in_flight_(0),
+      start_time_(base::TimeTicks::Now()),
+      data_rate_limiter_(kMaxBytesPerDay, base::TimeDelta::FromHours(24)),
+      commit_rate_limiter_(kMaxCommitsPerHour, base::TimeDelta::FromHours(1)) {
   DCHECK(namespace_id != kLocalStorageNamespaceId);
   if (session_storage_backing) {
     backing_.reset(new SessionStorageDatabaseAdapter(
@@ -137,7 +177,8 @@
   if (!map_->HasOneRef())
     map_ = map_->DeepCopy();
   bool success = map_->SetItem(key, value, old_value);
-  if (success && backing_) {
+  if (success && backing_ &&
+      (old_value->is_null() || old_value->string() != value)) {
     CommitBatch* commit_batch = CreateCommitBatchIfNeeded();
     commit_batch->changed_values[key] = base::NullableString16(value, false);
   }
@@ -212,19 +253,21 @@
   copy->is_initial_import_done_ = true;
 
   // All the uncommitted changes to this area need to happen before the actual
-  // shallow copy is made (scheduled by the upper layer). Another OnCommitTimer
-  // call might be in the event queue at this point, but it's handled gracefully
-  // when it fires.
+  // shallow copy is made (scheduled by the upper layer sometime after return).
   if (commit_batch_)
-    OnCommitTimer();
+    ScheduleImmediateCommit();
   return copy;
 }
 
 bool DOMStorageArea::HasUncommittedChanges() const {
-  DCHECK(!is_shutdown_);
   return commit_batch_.get() || commit_batches_in_flight_;
 }
 
+void DOMStorageArea::ScheduleImmediateCommit() {
+  DCHECK(HasUncommittedChanges());
+  PostCommitTask();
+}
+
 void DOMStorageArea::DeleteOrigin() {
   DCHECK(!is_shutdown_);
   // This function shouldn't be called for sessionStorage.
@@ -327,25 +370,44 @@
     // started after the commits have happened.
     if (!commit_batches_in_flight_) {
       task_runner_->PostDelayedTask(
-          FROM_HERE,
-          base::Bind(&DOMStorageArea::OnCommitTimer, this),
-          base::TimeDelta::FromSeconds(kCommitTimerSeconds));
+          FROM_HERE, base::Bind(&DOMStorageArea::OnCommitTimer, this),
+          ComputeCommitDelay());
     }
   }
   return commit_batch_.get();
 }
 
+base::TimeDelta DOMStorageArea::ComputeCommitDelay() const {
+  base::TimeDelta elapsed_time = base::TimeTicks::Now() - start_time_;
+  base::TimeDelta delay = std::max(
+      base::TimeDelta::FromSeconds(kCommitDefaultDelaySecs),
+      std::max(commit_rate_limiter_.ComputeDelayNeeded(elapsed_time),
+               data_rate_limiter_.ComputeDelayNeeded(elapsed_time)));
+  UMA_HISTOGRAM_LONG_TIMES("LocalStorage.CommitDelay", delay);
+  return delay;
+}
+
 void DOMStorageArea::OnCommitTimer() {
   if (is_shutdown_)
     return;
 
-  DCHECK(backing_.get());
-
-  // It's possible that there is nothing to commit, since a shallow copy occured
-  // before the timer fired.
+  // It's possible that there is nothing to commit if an immediate
+  // commit occured after the timer was scheduled but before it fired.
   if (!commit_batch_)
     return;
 
+  PostCommitTask();
+}
+
+void DOMStorageArea::PostCommitTask() {
+  if (is_shutdown_ || !commit_batch_)
+    return;
+
+  DCHECK(backing_.get());
+
+  commit_rate_limiter_.add_samples(1);
+  data_rate_limiter_.add_samples(commit_batch_->GetDataSize());
+
   // This method executes on the primary sequence, we schedule
   // a task for immediate execution on the commit sequence.
   DCHECK(task_runner_->IsRunningOnPrimarySequence());
@@ -362,7 +424,7 @@
   // This method executes on the commit sequence.
   DCHECK(task_runner_->IsRunningOnCommitSequence());
   backing_->CommitChanges(commit_batch->clear_all_first,
-                                         commit_batch->changed_values);
+                          commit_batch->changed_values);
   // TODO(michaeln): what if CommitChanges returns false (e.g., we're trying to
   // commit to a DB which is in an inconsistent state?)
   task_runner_->PostTask(
@@ -379,9 +441,8 @@
   if (commit_batch_.get() && !commit_batches_in_flight_) {
     // More changes have accrued, restart the timer.
     task_runner_->PostDelayedTask(
-        FROM_HERE,
-        base::Bind(&DOMStorageArea::OnCommitTimer, this),
-        base::TimeDelta::FromSeconds(kCommitTimerSeconds));
+        FROM_HERE, base::Bind(&DOMStorageArea::OnCommitTimer, this),
+        ComputeCommitDelay());
   }
 }
 
diff --git a/content/browser/dom_storage/dom_storage_area.h b/content/browser/dom_storage/dom_storage_area.h
index ca28be1..d094789 100644
--- a/content/browser/dom_storage/dom_storage_area.h
+++ b/content/browser/dom_storage/dom_storage_area.h
@@ -5,6 +5,8 @@
 #ifndef CONTENT_BROWSER_DOM_STORAGE_DOM_STORAGE_AREA_H_
 #define CONTENT_BROWSER_DOM_STORAGE_DOM_STORAGE_AREA_H_
 
+#include <string>
+
 #include "base/files/file_path.h"
 #include "base/gtest_prod_util.h"
 #include "base/memory/ref_counted.h"
@@ -65,6 +67,7 @@
       const std::string& destination_persistent_namespace_id);
 
   bool HasUncommittedChanges() const;
+  void ScheduleImmediateCommit();
 
   // Similar to Clear() but more optimized for just deleting
   // without raising events.
@@ -92,14 +95,42 @@
   FRIEND_TEST_ALL_PREFIXES(DOMStorageAreaTest, CommitChangesAtShutdown);
   FRIEND_TEST_ALL_PREFIXES(DOMStorageAreaTest, DeleteOrigin);
   FRIEND_TEST_ALL_PREFIXES(DOMStorageAreaTest, PurgeMemory);
+  FRIEND_TEST_ALL_PREFIXES(DOMStorageAreaTest, RateLimiter);
   FRIEND_TEST_ALL_PREFIXES(DOMStorageContextImplTest, PersistentIds);
   friend class base::RefCountedThreadSafe<DOMStorageArea>;
 
+  // Used to rate limit commits.
+  class CONTENT_EXPORT RateLimiter {
+   public:
+    RateLimiter(size_t desired_rate, base::TimeDelta time_quantum);
+
+    void add_samples(size_t samples) {
+      samples_ += samples;
+    }
+
+    // Computes the total time needed to process the total samples seen
+    // at the desired rate.
+    base::TimeDelta ComputeTimeNeeded() const;
+
+    // Given the elapsed time since the start of the rate limiting session,
+    // computes the delay needed to mimic having processed the total samples
+    // seen at the desired rate.
+    base::TimeDelta ComputeDelayNeeded(
+        const base::TimeDelta elapsed_time) const;
+
+   private:
+    float rate_;
+    float samples_;
+    base::TimeDelta time_quantum_;
+  };
+
   struct CommitBatch {
     bool clear_all_first;
     DOMStorageValuesMap changed_values;
+
     CommitBatch();
     ~CommitBatch();
+    size_t GetDataSize() const;
   };
 
   ~DOMStorageArea();
@@ -114,8 +145,10 @@
   // task sequence when complete.
   CommitBatch* CreateCommitBatchIfNeeded();
   void OnCommitTimer();
+  void PostCommitTask();
   void CommitChanges(const CommitBatch* commit_batch);
   void OnCommitComplete();
+  base::TimeDelta ComputeCommitDelay() const;
 
   void ShutdownInCommitSequence();
 
@@ -131,6 +164,11 @@
   bool is_shutdown_;
   scoped_ptr<CommitBatch> commit_batch_;
   int commit_batches_in_flight_;
+  base::TimeTicks start_time_;
+  RateLimiter data_rate_limiter_;
+  RateLimiter commit_rate_limiter_;
+
+  DISALLOW_COPY_AND_ASSIGN(DOMStorageArea);
 };
 
 }  // namespace content
diff --git a/content/browser/dom_storage/dom_storage_area_unittest.cc b/content/browser/dom_storage/dom_storage_area_unittest.cc
index c074cc07..11155909 100644
--- a/content/browser/dom_storage/dom_storage_area_unittest.cc
+++ b/content/browser/dom_storage/dom_storage_area_unittest.cc
@@ -472,4 +472,51 @@
           base::FilePath().AppendASCII(".extensiononly")));
 }
 
+TEST_F(DOMStorageAreaTest, RateLimiter) {
+  // Limit to 1000 samples per second
+  DOMStorageArea::RateLimiter rate_limiter(
+      1000, base::TimeDelta::FromSeconds(1));
+
+  // No samples have been added so no time/delay should be needed.
+  EXPECT_EQ(base::TimeDelta(),
+            rate_limiter.ComputeTimeNeeded());
+  EXPECT_EQ(base::TimeDelta(),
+            rate_limiter.ComputeDelayNeeded(base::TimeDelta()));
+  EXPECT_EQ(base::TimeDelta(),
+            rate_limiter.ComputeDelayNeeded(base::TimeDelta::FromDays(1)));
+
+  // Add a seconds worth of samples.
+  rate_limiter.add_samples(1000);
+  EXPECT_EQ(base::TimeDelta::FromSeconds(1),
+            rate_limiter.ComputeTimeNeeded());
+  EXPECT_EQ(base::TimeDelta::FromSeconds(1),
+            rate_limiter.ComputeDelayNeeded(base::TimeDelta()));
+  EXPECT_EQ(base::TimeDelta(),
+            rate_limiter.ComputeDelayNeeded(base::TimeDelta::FromSeconds(1)));
+  EXPECT_EQ(base::TimeDelta::FromMilliseconds(250),
+            rate_limiter.ComputeDelayNeeded(
+                base::TimeDelta::FromMilliseconds(750)));
+  EXPECT_EQ(base::TimeDelta(),
+            rate_limiter.ComputeDelayNeeded(
+                base::TimeDelta::FromDays(1)));
+
+  // And another half seconds worth.
+  rate_limiter.add_samples(500);
+  EXPECT_EQ(base::TimeDelta::FromMilliseconds(1500),
+            rate_limiter.ComputeTimeNeeded());
+  EXPECT_EQ(base::TimeDelta::FromMilliseconds(1500),
+            rate_limiter.ComputeDelayNeeded(base::TimeDelta()));
+  EXPECT_EQ(base::TimeDelta::FromMilliseconds(500),
+            rate_limiter.ComputeDelayNeeded(base::TimeDelta::FromSeconds(1)));
+  EXPECT_EQ(base::TimeDelta::FromMilliseconds(750),
+            rate_limiter.ComputeDelayNeeded(
+                base::TimeDelta::FromMilliseconds(750)));
+  EXPECT_EQ(base::TimeDelta(),
+            rate_limiter.ComputeDelayNeeded(
+                base::TimeDelta::FromMilliseconds(1500)));
+  EXPECT_EQ(base::TimeDelta(),
+            rate_limiter.ComputeDelayNeeded(
+                base::TimeDelta::FromDays(1)));
+}
+
 }  // namespace content
diff --git a/content/browser/dom_storage/dom_storage_namespace.cc b/content/browser/dom_storage/dom_storage_namespace.cc
index 75bfdaa..fc85d83 100644
--- a/content/browser/dom_storage/dom_storage_namespace.cc
+++ b/content/browser/dom_storage/dom_storage_namespace.cc
@@ -123,16 +123,23 @@
     return;  // We can't purge w/o backing on disk.
   AreaMap::iterator it = areas_.begin();
   while (it != areas_.end()) {
-    // Leave it alone if changes are pending
-    if (it->second.area_->HasUncommittedChanges()) {
+    const AreaHolder& holder = it->second;
+
+    // We can't purge if there are changes pending.
+    if (holder.area_->HasUncommittedChanges()) {
+      if (holder.open_count_ == 0) {
+         // Schedule an immediate commit so the next time we're asked to purge,
+         // we can drop it from memory.
+         holder.area_->ScheduleImmediateCommit();
+      }
       ++it;
       continue;
     }
 
     // If not in use, we can shut it down and remove
     // it from our collection entirely.
-    if (it->second.open_count_ == 0) {
-      it->second.area_->Shutdown();
+    if (holder.open_count_ == 0) {
+      holder.area_->Shutdown();
       areas_.erase(it++);
       continue;
     }
@@ -140,7 +147,7 @@
     if (option == PURGE_AGGRESSIVE) {
       // If aggressive is true, we clear caches and such
       // for opened areas.
-      it->second.area_->PurgeMemory();
+      holder.area_->PurgeMemory();
     }
 
     ++it;
diff --git a/content/browser/download/download_browsertest.cc b/content/browser/download/download_browsertest.cc
index a2e75241..b39e04eb 100644
--- a/content/browser/download/download_browsertest.cc
+++ b/content/browser/download/download_browsertest.cc
@@ -854,6 +854,7 @@
   plugin_info.name = base::ASCIIToUTF16(kTestPluginName);
   plugin_info.mime_types.push_back(
       WebPluginMimeType(kTestMimeType, kTestFileType, ""));
+  plugin_info.type = WebPluginInfo::PLUGIN_TYPE_PEPPER_IN_PROCESS;
   PluginServiceImpl::GetInstance()->RegisterInternalPlugin(plugin_info, false);
 
   // The following is served with a Content-Type of application/octet-stream.
diff --git a/content/browser/frame_host/frame_navigation_entry.cc b/content/browser/frame_host/frame_navigation_entry.cc
new file mode 100644
index 0000000..712fc63d
--- /dev/null
+++ b/content/browser/frame_host/frame_navigation_entry.cc
@@ -0,0 +1,21 @@
+// 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 "content/browser/frame_host/frame_navigation_entry.h"
+
+namespace content {
+
+FrameNavigationEntry::FrameNavigationEntry() {
+}
+
+FrameNavigationEntry::FrameNavigationEntry(SiteInstanceImpl* site_instance,
+                                           const GURL& url,
+                                           const Referrer& referrer)
+    : site_instance_(site_instance), url_(url), referrer_(referrer) {
+}
+
+FrameNavigationEntry::~FrameNavigationEntry() {
+}
+
+}  // namespace content
diff --git a/content/browser/frame_host/frame_navigation_entry.h b/content/browser/frame_host/frame_navigation_entry.h
new file mode 100644
index 0000000..622b682
--- /dev/null
+++ b/content/browser/frame_host/frame_navigation_entry.h
@@ -0,0 +1,64 @@
+// 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 CONTENT_BROWSER_FRAME_HOST_FRAME_NAVIGATION_ENTRY_H_
+#define CONTENT_BROWSER_FRAME_HOST_FRAME_NAVIGATION_ENTRY_H_
+
+#include "base/basictypes.h"
+#include "base/memory/ref_counted.h"
+#include "content/browser/site_instance_impl.h"
+#include "content/public/common/referrer.h"
+
+namespace content {
+
+// Represents a session history item for a particular frame.
+//
+// This class is currently owned by a single NavigationEntry and only tracks the
+// main frame.
+//
+// TODO(creis): Keep a tree of FrameNavigationEntries in each NavigationEntry,
+// one per frame.  FrameNavigationEntries may be shared across NavigationEntries
+// if the frame hasn't changed.
+class CONTENT_EXPORT FrameNavigationEntry {
+ public:
+  FrameNavigationEntry();
+  FrameNavigationEntry(SiteInstanceImpl* site_instance,
+                       const GURL& url,
+                       const Referrer& referrer);
+  virtual ~FrameNavigationEntry();
+
+  // The SiteInstance responsible for rendering this frame.  All frames sharing
+  // a SiteInstance must live in the same process.  This is a refcounted pointer
+  // that keeps the SiteInstance (not necessarily the process) alive as long as
+  // this object remains in the session history.
+  void set_site_instance(SiteInstanceImpl* site_instance) {
+    site_instance_ = site_instance;
+  }
+  SiteInstanceImpl* site_instance() const { return site_instance_.get(); }
+
+  // The actual URL loaded in the frame.  This is in contrast to the virtual
+  // URL, which is shown to the user.
+  // TODO(creis): Move virtual URL and related members to FrameNavigationEntry.
+  void set_url(const GURL& url) { url_ = url; }
+  const GURL& url() const { return url_; }
+
+  // The referring URL.  Can be empty.
+  void set_referrer(const Referrer& referrer) { referrer_ = referrer; }
+  const Referrer& referrer() const { return referrer_; }
+
+ private:
+  // TODO(creis): These fields have implications for session restore.  This is
+  // currently managed by NavigationEntry, but the logic will move here.
+
+  // See the accessors above for descriptions.
+  scoped_refptr<SiteInstanceImpl> site_instance_;
+  GURL url_;
+  Referrer referrer_;
+
+  // Copy and assignment is explicitly allowed for this class.
+};
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_FRAME_HOST_FRAME_NAVIGATION_ENTRY_H_
diff --git a/content/browser/frame_host/frame_tree.cc b/content/browser/frame_host/frame_tree.cc
index 7b0a1a7c..f3b43e3a4 100644
--- a/content/browser/frame_host/frame_tree.cc
+++ b/content/browser/frame_host/frame_tree.cc
@@ -150,6 +150,8 @@
   // it is in the same SiteInstance as the parent frame. Ensure that the process
   // which requested a child frame to be added is the same as the process of the
   // parent node.
+  // We return nullptr if this is not the case, which can happen in a race if an
+  // old RFH sends a CreateChildFrame message as we're swapping to a new RFH.
   if (parent->current_frame_host()->GetProcess()->GetID() != process_id)
     return nullptr;
 
diff --git a/content/browser/frame_host/frame_tree.h b/content/browser/frame_host/frame_tree.h
index d49e40a0..e9b5d3d 100644
--- a/content/browser/frame_host/frame_tree.h
+++ b/content/browser/frame_host/frame_tree.h
@@ -70,6 +70,10 @@
   void ForEach(const base::Callback<bool(FrameTreeNode*)>& on_node) const;
 
   // Frame tree manipulation routines.
+  // |process_id| is required to disambiguate |new_routing_id|, and it must
+  // match the process of the |parent| node.  Otherwise this method returns
+  // nullptr.  Passing MSG_ROUTING_NONE for |new_routing_id| will allocate a new
+  // routing ID for the new frame.
   RenderFrameHostImpl* AddFrame(FrameTreeNode* parent,
                                 int process_id,
                                 int new_routing_id,
diff --git a/content/browser/frame_host/navigation_controller_impl.cc b/content/browser/frame_host/navigation_controller_impl.cc
index 5c3f046e..0658787 100644
--- a/content/browser/frame_host/navigation_controller_impl.cc
+++ b/content/browser/frame_host/navigation_controller_impl.cc
@@ -37,6 +37,7 @@
 #include "content/public/browser/user_metrics.h"
 #include "content/public/common/content_client.h"
 #include "content/public/common/content_constants.h"
+#include "content/public/common/content_switches.h"
 #include "net/base/escape.h"
 #include "net/base/mime_util.h"
 #include "net/base/net_util.h"
@@ -165,11 +166,17 @@
       bool is_renderer_initiated,
       const std::string& extra_headers,
       BrowserContext* browser_context) {
+  // Fix up the given URL before letting it be rewritten, so that any minor
+  // cleanup (e.g., removing leading dots) will not lead to a virtual URL.
+  GURL dest_url(url);
+  BrowserURLHandlerImpl::GetInstance()->FixupURLBeforeRewrite(&dest_url,
+                                                              browser_context);
+
   // Allow the browser URL handler to rewrite the URL. This will, for example,
   // remove "view-source:" from the beginning of the URL to get the URL that
   // will actually be loaded. This real URL won't be shown to the user, just
   // used internally.
-  GURL loaded_url(url);
+  GURL loaded_url(dest_url);
   bool reverse_on_redirect = false;
   BrowserURLHandlerImpl::GetInstance()->RewriteURLIfNecessary(
       &loaded_url, browser_context, &reverse_on_redirect);
@@ -183,8 +190,8 @@
       base::string16(),
       transition,
       is_renderer_initiated);
-  entry->SetVirtualURL(url);
-  entry->set_user_typed_url(url);
+  entry->SetVirtualURL(dest_url);
+  entry->set_user_typed_url(dest_url);
   entry->set_update_virtual_url_with_url(reverse_on_redirect);
   entry->set_extra_headers(extra_headers);
   return entry;
@@ -284,8 +291,7 @@
   if (transient_entry_index_ != -1) {
     // If an interstitial is showing, treat a reload as a navigation to the
     // transient entry's URL.
-    NavigationEntryImpl* transient_entry =
-        NavigationEntryImpl::FromNavigationEntry(GetTransientEntry());
+    NavigationEntryImpl* transient_entry = GetTransientEntry();
     if (!transient_entry)
       return;
     LoadURL(transient_entry->GetURL(),
@@ -309,8 +315,7 @@
     DiscardNonCommittedEntriesInternal();
     current_index = GetCurrentEntryIndex();
     if (current_index != -1) {
-      entry = NavigationEntryImpl::FromNavigationEntry(
-          GetEntryAtIndex(current_index));
+      entry = GetEntryAtIndex(current_index);
     }
   }
 
@@ -426,7 +431,7 @@
       Details<NavigationEntry>(entry));
 }
 
-NavigationEntry* NavigationControllerImpl::GetActiveEntry() const {
+NavigationEntryImpl* NavigationControllerImpl::GetActiveEntry() const {
   if (transient_entry_index_ != -1)
     return entries_[transient_entry_index_].get();
   if (pending_entry_)
@@ -434,7 +439,7 @@
   return GetLastCommittedEntry();
 }
 
-NavigationEntry* NavigationControllerImpl::GetVisibleEntry() const {
+NavigationEntryImpl* NavigationControllerImpl::GetVisibleEntry() const {
   if (transient_entry_index_ != -1)
     return entries_[transient_entry_index_].get();
   // The pending entry is safe to return for new (non-history), browser-
@@ -475,7 +480,7 @@
   return last_committed_entry_index_;
 }
 
-NavigationEntry* NavigationControllerImpl::GetLastCommittedEntry() const {
+NavigationEntryImpl* NavigationControllerImpl::GetLastCommittedEntry() const {
   if (last_committed_entry_index_ == -1)
     return NULL;
   return entries_[last_committed_entry_index_].get();
@@ -499,12 +504,12 @@
   return static_cast<int>(entries_.size());
 }
 
-NavigationEntry* NavigationControllerImpl::GetEntryAtIndex(
+NavigationEntryImpl* NavigationControllerImpl::GetEntryAtIndex(
     int index) const {
   return entries_.at(index).get();
 }
 
-NavigationEntry* NavigationControllerImpl::GetEntryAtOffset(
+NavigationEntryImpl* NavigationControllerImpl::GetEntryAtOffset(
     int offset) const {
   int index = GetIndexForOffset(offset);
   if (index < 0 || index >= GetEntryCount())
@@ -767,7 +772,7 @@
 }
 
 bool NavigationControllerImpl::RendererDidNavigate(
-    RenderFrameHost* rfh,
+    RenderFrameHostImpl* rfh,
     const FrameHostMsg_DidCommitProvisionalLoad_Params& params,
     LoadCommittedDetails* details) {
   is_initial_navigation_ = false;
@@ -852,8 +857,7 @@
   // All committed entries should have nonempty content state so WebKit doesn't
   // get confused when we go back to them (see the function for details).
   DCHECK(params.page_state.IsValid());
-  NavigationEntryImpl* active_entry =
-      NavigationEntryImpl::FromNavigationEntry(GetLastCommittedEntry());
+  NavigationEntryImpl* active_entry = GetLastCommittedEntry();
   active_entry->SetTimestamp(timestamp);
   active_entry->SetHttpStatusCode(params.http_status_code);
   active_entry->SetPageState(params.page_state);
@@ -879,8 +883,7 @@
 
   // Remember the bindings the renderer process has at this point, so that
   // we do not grant this entry additional bindings if we come back to it.
-  active_entry->SetBindings(
-      static_cast<RenderFrameHostImpl*>(rfh)->GetEnabledBindings());
+  active_entry->SetBindings(rfh->GetEnabledBindings());
 
   // Now prep the rest of the details for the notification and broadcast.
   details->entry = active_entry;
@@ -894,7 +897,7 @@
 }
 
 NavigationType NavigationControllerImpl::ClassifyNavigation(
-    RenderFrameHost* rfh,
+    RenderFrameHostImpl* rfh,
     const FrameHostMsg_DidCommitProvisionalLoad_Params& params) const {
   if (params.page_id == -1) {
     // TODO(nasko, creis):  An out-of-process child frame has no way of
@@ -991,8 +994,7 @@
       temp.append(",");
     }
     GURL url(temp);
-    static_cast<RenderFrameHostImpl*>(rfh)->render_view_host()->Send(
-        new ViewMsg_TempCrashWithData(url));
+    rfh->render_view_host()->Send(new ViewMsg_TempCrashWithData(url));
     return NAVIGATION_TYPE_NAV_IGNORE;
   }
   NavigationEntryImpl* existing_entry = entries_[existing_entry_index].get();
@@ -1037,7 +1039,7 @@
 }
 
 void NavigationControllerImpl::RendererDidNavigateToNewPage(
-    RenderFrameHost* rfh,
+    RenderFrameHostImpl* rfh,
     const FrameHostMsg_DidCommitProvisionalLoad_Params& params,
     bool replace_entry) {
   NavigationEntryImpl* new_entry;
@@ -1050,10 +1052,6 @@
        pending_entry_->site_instance() == rfh->GetSiteInstance())) {
     new_entry = new NavigationEntryImpl(*pending_entry_);
 
-    // Don't use the page type from the pending entry. Some interstitial page
-    // may have set the type to interstitial. Once we commit, however, the page
-    // type must always be normal.
-    new_entry->set_page_type(PAGE_TYPE_NORMAL);
     update_virtual_url = new_entry->update_virtual_url_with_url();
   } else {
     new_entry = new NavigationEntryImpl;
@@ -1074,8 +1072,11 @@
     update_virtual_url = needs_update;
   }
 
-  if (params.url_is_unreachable)
-    new_entry->set_page_type(PAGE_TYPE_ERROR);
+  // Don't use the page type from the pending entry. Some interstitial page
+  // may have set the type to interstitial. Once we commit, however, the page
+  // type must always be normal or error.
+  new_entry->set_page_type(params.url_is_unreachable ? PAGE_TYPE_ERROR
+                                                     : PAGE_TYPE_NORMAL);
   new_entry->SetURL(params.url);
   if (update_virtual_url)
     UpdateVirtualURLToURL(new_entry, params.url);
@@ -1111,7 +1112,7 @@
 }
 
 void NavigationControllerImpl::RendererDidNavigateToExistingPage(
-    RenderFrameHost* rfh,
+    RenderFrameHostImpl* rfh,
     const FrameHostMsg_DidCommitProvisionalLoad_Params& params) {
   // We should only get here for main frame navigations.
   DCHECK(ui::PageTransitionIsMainFrame(params.transition));
@@ -1126,6 +1127,8 @@
   NavigationEntryImpl* entry = entries_[entry_index].get();
 
   // The URL may have changed due to redirects.
+  entry->set_page_type(params.url_is_unreachable ? PAGE_TYPE_ERROR
+                                                 : PAGE_TYPE_NORMAL);
   entry->SetURL(params.url);
   entry->SetReferrer(params.referrer);
   if (entry->update_virtual_url_with_url())
@@ -1164,7 +1167,7 @@
 }
 
 void NavigationControllerImpl::RendererDidNavigateToSamePage(
-    RenderFrameHost* rfh,
+    RenderFrameHostImpl* rfh,
     const FrameHostMsg_DidCommitProvisionalLoad_Params& params) {
   // This mode implies we have a pending entry that's the same as an existing
   // entry for this page ID. This entry is guaranteed to exist by
@@ -1178,6 +1181,8 @@
   existing_entry->set_unique_id(pending_entry_->GetUniqueID());
 
   // The URL may have changed due to redirects.
+  existing_entry->set_page_type(params.url_is_unreachable ? PAGE_TYPE_ERROR
+                                                          : PAGE_TYPE_NORMAL);
   if (existing_entry->update_virtual_url_with_url())
     UpdateVirtualURLToURL(existing_entry, params.url);
   existing_entry->SetURL(params.url);
@@ -1191,7 +1196,7 @@
 }
 
 void NavigationControllerImpl::RendererDidNavigateInPage(
-    RenderFrameHost* rfh,
+    RenderFrameHostImpl* rfh,
     const FrameHostMsg_DidCommitProvisionalLoad_Params& params,
     bool* did_replace_entry) {
   DCHECK(ui::PageTransitionIsMainFrame(params.transition)) <<
@@ -1204,6 +1209,8 @@
   // entry and it will be the same page as the new navigation (minus the
   // reference fragments, of course).  We'll update the URL of the existing
   // entry without pruning the forward history.
+  existing_entry->set_page_type(params.url_is_unreachable ? PAGE_TYPE_ERROR
+                                                          : PAGE_TYPE_NORMAL);
   existing_entry->SetURL(params.url);
   if (existing_entry->update_virtual_url_with_url())
     UpdateVirtualURLToURL(existing_entry, params.url);
@@ -1223,11 +1230,26 @@
 }
 
 void NavigationControllerImpl::RendererDidNavigateNewSubframe(
-    RenderFrameHost* rfh,
+    RenderFrameHostImpl* rfh,
     const FrameHostMsg_DidCommitProvisionalLoad_Params& params) {
-  if (ui::PageTransitionCoreTypeIs(params.transition,
-                                   ui::PAGE_TRANSITION_AUTO_SUBFRAME)) {
-    // This is not user-initiated. Ignore.
+  if (!ui::PageTransitionCoreTypeIs(params.transition,
+                                    ui::PAGE_TRANSITION_MANUAL_SUBFRAME)) {
+    // There was a comment here that said, "This is not user-initiated. Ignore."
+    // But this makes no sense; non-user-initiated navigations should be
+    // determined to be of type NAVIGATION_TYPE_AUTO_SUBFRAME and sent to
+    // RendererDidNavigateAutoSubframe below.
+    //
+    // This if clause dates back to https://codereview.chromium.org/115919 and
+    // the handling of immediate redirects. TODO(avi): Is this still valid? I'm
+    // pretty sure that's there's nothing left of that code and that we should
+    // take this out.
+    //
+    // Except for cross-process iframes; this doesn't work yet for them.
+    if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kSitePerProcess)) {
+      NOTREACHED();
+    }
+
     DiscardNonCommittedEntriesInternal();
     return;
   }
@@ -1238,15 +1260,18 @@
   // band with the actual navigations.
   DCHECK(GetLastCommittedEntry()) << "ClassifyNavigation should guarantee "
                                   << "that a last committed entry exists.";
-  NavigationEntryImpl* new_entry = new NavigationEntryImpl(
-      *NavigationEntryImpl::FromNavigationEntry(GetLastCommittedEntry()));
+  NavigationEntryImpl* new_entry =
+      new NavigationEntryImpl(*GetLastCommittedEntry());
   new_entry->SetPageID(params.page_id);
   InsertOrReplaceEntry(new_entry, false);
 }
 
 bool NavigationControllerImpl::RendererDidNavigateAutoSubframe(
-    RenderFrameHost* rfh,
+    RenderFrameHostImpl* rfh,
     const FrameHostMsg_DidCommitProvisionalLoad_Params& params) {
+  DCHECK(ui::PageTransitionCoreTypeIs(params.transition,
+                                      ui::PAGE_TRANSITION_AUTO_SUBFRAME));
+
   // We're guaranteed to have a previously committed entry, and we now need to
   // handle navigation inside of a subframe in it without creating a new entry.
   DCHECK(GetLastCommittedEntry());
@@ -1367,8 +1392,7 @@
   // Copy the max page id map from the old tab to the new tab. This ensures that
   // new and existing navigations in the tab's current SiteInstances are
   // identified properly.
-  NavigationEntryImpl* last_committed =
-      NavigationEntryImpl::FromNavigationEntry(GetLastCommittedEntry());
+  NavigationEntryImpl* last_committed = GetLastCommittedEntry();
   int32 site_max_page_id =
       delegate_->GetMaxPageIDForSiteInstance(last_committed->site_instance());
   delegate_->CopyMaxPageIDsFrom(source->delegate()->GetWebContents());
@@ -1532,7 +1556,7 @@
   }
 }
 
-NavigationEntry* NavigationControllerImpl::GetPendingEntry() const {
+NavigationEntryImpl* NavigationControllerImpl::GetPendingEntry() const {
   return pending_entry_;
 }
 
@@ -1756,7 +1780,7 @@
   return -1;
 }
 
-NavigationEntry* NavigationControllerImpl::GetTransientEntry() const {
+NavigationEntryImpl* NavigationControllerImpl::GetTransientEntry() const {
   if (transient_entry_index_ == -1)
     return NULL;
   return entries_[transient_entry_index_].get();
diff --git a/content/browser/frame_host/navigation_controller_impl.h b/content/browser/frame_host/navigation_controller_impl.h
index a646646..c7df11d 100644
--- a/content/browser/frame_host/navigation_controller_impl.h
+++ b/content/browser/frame_host/navigation_controller_impl.h
@@ -12,6 +12,7 @@
 #include "base/time/time.h"
 #include "build/build_config.h"
 #include "content/browser/frame_host/navigation_controller_delegate.h"
+#include "content/browser/frame_host/navigation_entry_impl.h"
 #include "content/browser/ssl/ssl_manager.h"
 #include "content/public/browser/navigation_controller.h"
 #include "content/public/browser/navigation_type.h"
@@ -19,8 +20,7 @@
 struct FrameHostMsg_DidCommitProvisionalLoad_Params;
 
 namespace content {
-class NavigationEntryImpl;
-class RenderViewHost;
+class RenderFrameHostImpl;
 class NavigationEntryScreenshotManager;
 class SiteInstance;
 struct LoadCommittedDetails;
@@ -40,19 +40,19 @@
   void Restore(int selected_navigation,
                RestoreType type,
                std::vector<NavigationEntry*>* entries) override;
-  NavigationEntry* GetActiveEntry() const override;
-  NavigationEntry* GetVisibleEntry() const override;
+  NavigationEntryImpl* GetActiveEntry() const override;
+  NavigationEntryImpl* GetVisibleEntry() const override;
   int GetCurrentEntryIndex() const override;
-  NavigationEntry* GetLastCommittedEntry() const override;
+  NavigationEntryImpl* GetLastCommittedEntry() const override;
   int GetLastCommittedEntryIndex() const override;
   bool CanViewSource() const override;
   int GetEntryCount() const override;
-  NavigationEntry* GetEntryAtIndex(int index) const override;
-  NavigationEntry* GetEntryAtOffset(int offset) const override;
+  NavigationEntryImpl* GetEntryAtIndex(int index) const override;
+  NavigationEntryImpl* GetEntryAtOffset(int offset) const override;
   void DiscardNonCommittedEntries() override;
-  NavigationEntry* GetPendingEntry() const override;
+  NavigationEntryImpl* GetPendingEntry() const override;
   int GetPendingEntryIndex() const override;
-  NavigationEntry* GetTransientEntry() const override;
+  NavigationEntryImpl* GetTransientEntry() const override;
   void SetTransientEntry(NavigationEntry* entry) override;
   void LoadURL(const GURL& url,
                const Referrer& referrer,
@@ -134,7 +134,7 @@
   // In the case that nothing has changed, the details structure is undefined
   // and it will return false.
   bool RendererDidNavigate(
-      RenderFrameHost* rfh,
+      RenderFrameHostImpl* rfh,
       const FrameHostMsg_DidCommitProvisionalLoad_Params& params,
       LoadCommittedDetails* details);
 
@@ -228,7 +228,7 @@
 
   // Classifies the given renderer navigation (see the NavigationType enum).
   NavigationType ClassifyNavigation(
-      RenderFrameHost* rfh,
+      RenderFrameHostImpl* rfh,
       const FrameHostMsg_DidCommitProvisionalLoad_Params& params) const;
 
   // Causes the controller to load the specified entry. The function assumes
@@ -249,24 +249,24 @@
   // whether the last entry has been replaced or not.
   // See LoadCommittedDetails.did_replace_entry.
   void RendererDidNavigateToNewPage(
-      RenderFrameHost* rfh,
+      RenderFrameHostImpl* rfh,
       const FrameHostMsg_DidCommitProvisionalLoad_Params& params,
       bool replace_entry);
   void RendererDidNavigateToExistingPage(
-      RenderFrameHost* rfh,
+      RenderFrameHostImpl* rfh,
       const FrameHostMsg_DidCommitProvisionalLoad_Params& params);
   void RendererDidNavigateToSamePage(
-      RenderFrameHost* rfh,
+      RenderFrameHostImpl* rfh,
       const FrameHostMsg_DidCommitProvisionalLoad_Params& params);
   void RendererDidNavigateInPage(
-      RenderFrameHost* rfh,
+      RenderFrameHostImpl* rfh,
       const FrameHostMsg_DidCommitProvisionalLoad_Params& params,
       bool* did_replace_entry);
   void RendererDidNavigateNewSubframe(
-      RenderFrameHost* rfh,
+      RenderFrameHostImpl* rfh,
       const FrameHostMsg_DidCommitProvisionalLoad_Params& params);
   bool RendererDidNavigateAutoSubframe(
-      RenderFrameHost* rfh,
+      RenderFrameHostImpl* rfh,
       const FrameHostMsg_DidCommitProvisionalLoad_Params& params);
 
   // Helper function for code shared between Reload() and ReloadIgnoringCache().
diff --git a/content/browser/frame_host/navigation_controller_impl_browsertest.cc b/content/browser/frame_host/navigation_controller_impl_browsertest.cc
index fa7bff0..9024ff6 100644
--- a/content/browser/frame_host/navigation_controller_impl_browsertest.cc
+++ b/content/browser/frame_host/navigation_controller_impl_browsertest.cc
@@ -172,6 +172,7 @@
 
 struct FrameNavigateParamsCapturer : public WebContentsObserver {
  public:
+  // Observes navigation for the specified |node|.
   explicit FrameNavigateParamsCapturer(FrameTreeNode* node)
       : WebContentsObserver(
             node->current_frame_host()->delegate()->GetAsWebContents()),
@@ -217,6 +218,64 @@
   scoped_refptr<MessageLoopRunner> message_loop_runner_;
 };
 
+struct LoadCommittedCapturer : public WebContentsObserver {
+ public:
+  // Observes the load commit for the specified |node|.
+  explicit LoadCommittedCapturer(FrameTreeNode* node)
+      : WebContentsObserver(
+            node->current_frame_host()->delegate()->GetAsWebContents()),
+        frame_tree_node_id_(node->frame_tree_node_id()),
+        message_loop_runner_(new MessageLoopRunner) {}
+
+  // Observes the load commit for the next created frame in the specified
+  // |web_contents|.
+  explicit LoadCommittedCapturer(WebContents* web_contents)
+      : WebContentsObserver(web_contents),
+        frame_tree_node_id_(0),
+        message_loop_runner_(new MessageLoopRunner) {}
+
+  void Wait() {
+    message_loop_runner_->Run();
+  }
+
+  ui::PageTransition transition_type() const {
+    return transition_type_;
+  }
+
+ private:
+  void RenderFrameCreated(RenderFrameHost* render_frame_host) override {
+    // If this object was created with a specified tree frame node, there
+    // shouldn't be any frames being created.
+    DCHECK_EQ(0, frame_tree_node_id_);
+    RenderFrameHostImpl* rfh =
+        static_cast<RenderFrameHostImpl*>(render_frame_host);
+    frame_tree_node_id_ = rfh->frame_tree_node()->frame_tree_node_id();
+  }
+
+  void DidCommitProvisionalLoadForFrame(
+      RenderFrameHost* render_frame_host,
+      const GURL& url,
+      ui::PageTransition transition_type) override {
+    DCHECK_NE(0, frame_tree_node_id_);
+    RenderFrameHostImpl* rfh =
+        static_cast<RenderFrameHostImpl*>(render_frame_host);
+    if (rfh->frame_tree_node()->frame_tree_node_id() != frame_tree_node_id_)
+      return;
+
+    transition_type_ = transition_type;
+    message_loop_runner_->Quit();
+  }
+
+  // The id of the FrameTreeNode whose navigations to observe.
+  int frame_tree_node_id_;
+
+  // The transition_type of the last navigation.
+  ui::PageTransition transition_type_;
+
+  // The MessageLoopRunner used to spin the message loop.
+  scoped_refptr<MessageLoopRunner> message_loop_runner_;
+};
+
 }  // namespace
 
 // Verify that the distinction between manual and auto subframes is properly set
@@ -224,8 +283,8 @@
 // in two different enums; http://crbug.com/453555.
 IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest,
                        ManualAndAutoSubframeNavigationClassification) {
-  GURL main_url(
-      embedded_test_server()->GetURL("/frame_tree/page_with_one_frame.html"));
+  GURL main_url(embedded_test_server()->GetURL(
+      "/navigation_controller/page_with_iframe.html"));
   NavigateToURL(shell(), main_url);
 
   // It is safe to obtain the root frame tree node here, as it doesn't change.
@@ -239,8 +298,8 @@
   {
     // Navigate the iframe to a new URL; expect a manual subframe transition.
     FrameNavigateParamsCapturer capturer(root->child_at(0));
-    GURL frame_url(
-        embedded_test_server()->GetURL("/frame_tree/2-1.html"));
+    GURL frame_url(embedded_test_server()->GetURL(
+        "/navigation_controller/simple_page_1.html"));
     NavigateFrameToURL(root->child_at(0), frame_url);
     capturer.Wait();
     EXPECT_EQ(ui::PAGE_TRANSITION_MANUAL_SUBFRAME,
@@ -249,7 +308,7 @@
   }
 
   {
-    // History navigations should result in an auto subframe transition.
+    // Do a history navigation; expect an auto subframe transition.
     FrameNavigateParamsCapturer capturer(root->child_at(0));
     shell()->web_contents()->GetController().GoBack();
     capturer.Wait();
@@ -258,7 +317,7 @@
   }
 
   {
-    // History navigations should result in an auto subframe transition.
+    // Do a history navigation; expect an auto subframe transition.
     FrameNavigateParamsCapturer capturer(root->child_at(0));
     shell()->web_contents()->GetController().GoForward();
     capturer.Wait();
@@ -269,14 +328,65 @@
   {
     // Navigate the iframe to a new URL; expect a manual subframe transition.
     FrameNavigateParamsCapturer capturer(root->child_at(0));
-    GURL frame_url(
-        embedded_test_server()->GetURL("/frame_tree/2-3.html"));
+    GURL frame_url(embedded_test_server()->GetURL(
+        "/navigation_controller/simple_page_2.html"));
     NavigateFrameToURL(root->child_at(0), frame_url);
     capturer.Wait();
     EXPECT_EQ(ui::PAGE_TRANSITION_MANUAL_SUBFRAME,
               capturer.params().transition);
     EXPECT_EQ(NAVIGATION_TYPE_NEW_SUBFRAME, capturer.details().type);
   }
+
+  {
+    // Use location.assign(); expect a manual subframe transition.
+    FrameNavigateParamsCapturer capturer(root->child_at(0));
+    GURL frame_url(embedded_test_server()->GetURL(
+        "/navigation_controller/simple_page_1.html"));
+    std::string script = "location.assign('" + frame_url.spec() + "')";
+    EXPECT_TRUE(content::ExecuteScript(root->child_at(0)->current_frame_host(),
+                                       script));
+    capturer.Wait();
+    EXPECT_EQ(ui::PAGE_TRANSITION_MANUAL_SUBFRAME,
+              capturer.params().transition);
+    EXPECT_EQ(NAVIGATION_TYPE_NEW_SUBFRAME, capturer.details().type);
+  }
+
+  {
+    // Use location.replace(); expect an auto subframe transition. (Replacements
+    // aren't "navigation" so we only see the frame load committing.)
+    LoadCommittedCapturer capturer(root->child_at(0));
+    GURL frame_url(embedded_test_server()->GetURL(
+        "/navigation_controller/simple_page_2.html"));
+    std::string script = "location.replace('" + frame_url.spec() + "')";
+    EXPECT_TRUE(content::ExecuteScript(root->child_at(0)->current_frame_host(),
+                                       script));
+    capturer.Wait();
+    EXPECT_EQ(ui::PAGE_TRANSITION_AUTO_SUBFRAME, capturer.transition_type());
+  }
+
+  {
+    // Reload the subframe; expect an auto subframe transition. (Reloads aren't
+    // "navigation" so we only see the frame load committing.)
+    LoadCommittedCapturer capturer(root->child_at(0));
+    EXPECT_TRUE(content::ExecuteScript(root->child_at(0)->current_frame_host(),
+                                       "location.reload()"));
+    capturer.Wait();
+    EXPECT_EQ(ui::PAGE_TRANSITION_AUTO_SUBFRAME, capturer.transition_type());
+  }
+
+  {
+    // Create an iframe; expect an auto subframe transition. (Initial frame
+    // creation isn't "navigation" so we only see the frame load committing.)
+    LoadCommittedCapturer capturer(shell()->web_contents());
+    GURL frame_url(embedded_test_server()->GetURL(
+        "/navigation_controller/simple_page_1.html"));
+    std::string script = "var iframe = document.createElement('iframe');"
+                         "iframe.src = '" + frame_url.spec() + "';"
+                         "document.body.appendChild(iframe);";
+    EXPECT_TRUE(content::ExecuteScript(root->current_frame_host(), script));
+    capturer.Wait();
+    EXPECT_EQ(ui::PAGE_TRANSITION_AUTO_SUBFRAME, capturer.transition_type());
+  }
 }
 
 }  // namespace content
diff --git a/content/browser/frame_host/navigation_controller_impl_unittest.cc b/content/browser/frame_host/navigation_controller_impl_unittest.cc
index c02ff99..3492196 100644
--- a/content/browser/frame_host/navigation_controller_impl_unittest.cc
+++ b/content/browser/frame_host/navigation_controller_impl_unittest.cc
@@ -30,6 +30,7 @@
 #include "content/public/browser/web_contents_observer.h"
 #include "content/public/common/content_switches.h"
 #include "content/public/common/page_state.h"
+#include "content/public/common/page_type.h"
 #include "content/public/common/url_constants.h"
 #include "content/public/test/mock_render_process_host.h"
 #include "content/public/test/test_notification_tracker.h"
@@ -257,10 +258,6 @@
                      Source<NavigationController>(controller));
 }
 
-SiteInstance* GetSiteInstanceFromEntry(NavigationEntry* entry) {
-  return NavigationEntryImpl::FromNavigationEntry(entry)->site_instance();
-}
-
 class TestWebContentsDelegate : public WebContentsDelegate {
  public:
   explicit TestWebContentsDelegate() :
@@ -425,8 +422,7 @@
   EXPECT_FALSE(controller.CanGoBack());
   EXPECT_FALSE(controller.CanGoForward());
   EXPECT_EQ(contents()->GetMaxPageID(), 0);
-  EXPECT_EQ(0, NavigationEntryImpl::FromNavigationEntry(
-      controller.GetLastCommittedEntry())->bindings());
+  EXPECT_EQ(0, controller.GetLastCommittedEntry()->bindings());
 
   // The timestamp should have been set.
   EXPECT_FALSE(controller.GetVisibleEntry()->GetTimestamp().is_null());
@@ -560,9 +556,7 @@
   load_params.transferred_global_request_id = GlobalRequestID(2, 3);
 
   controller.LoadURLWithParams(load_params);
-  NavigationEntryImpl* entry =
-      NavigationEntryImpl::FromNavigationEntry(
-          controller.GetPendingEntry());
+  NavigationEntryImpl* entry = controller.GetPendingEntry();
 
   // The timestamp should not have been set yet.
   ASSERT_TRUE(entry);
@@ -582,9 +576,7 @@
   load_params.override_user_agent = NavigationController::UA_OVERRIDE_FALSE;
 
   controller.LoadURLWithParams(load_params);
-  NavigationEntryImpl* entry =
-      NavigationEntryImpl::FromNavigationEntry(
-          controller.GetPendingEntry());
+  NavigationEntryImpl* entry = controller.GetPendingEntry();
 
   CheckNavigationEntryMatchLoadParams(load_params, entry);
 }
@@ -608,9 +600,7 @@
   load_params.browser_initiated_post_data = data.get();
 
   controller.LoadURLWithParams(load_params);
-  NavigationEntryImpl* entry =
-      NavigationEntryImpl::FromNavigationEntry(
-          controller.GetPendingEntry());
+  NavigationEntryImpl* entry = controller.GetPendingEntry();
 
   CheckNavigationEntryMatchLoadParams(load_params, entry);
 }
@@ -891,8 +881,7 @@
   EXPECT_EQ(0U, notifications.size());
   EXPECT_EQ(0, controller.GetPendingEntryIndex());
   EXPECT_EQ(1, controller.GetLastCommittedEntryIndex());
-  EXPECT_EQ(2, NavigationEntryImpl::FromNavigationEntry(
-                controller.GetPendingEntry())->bindings());
+  EXPECT_EQ(2, controller.GetPendingEntry()->bindings());
 
   // Before that commits, do a new navigation.
   const GURL kNewURL("http://foo/bee");
@@ -906,8 +895,7 @@
   EXPECT_EQ(-1, controller.GetPendingEntryIndex());
   EXPECT_EQ(2, controller.GetLastCommittedEntryIndex());
   EXPECT_EQ(kNewURL, controller.GetVisibleEntry()->GetURL());
-  EXPECT_EQ(0, NavigationEntryImpl::FromNavigationEntry(
-                controller.GetLastCommittedEntry())->bindings());
+  EXPECT_EQ(0, controller.GetLastCommittedEntry()->bindings());
 }
 
 // Tests navigating to an existing URL when there is a pending new navigation.
@@ -1131,8 +1119,7 @@
   controller.LoadURL(
       url1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
   EXPECT_EQ(NavigationEntryImpl::kInvalidBindings,
-            NavigationEntryImpl::FromNavigationEntry(
-                controller.GetPendingEntry())->bindings());
+            controller.GetPendingEntry()->bindings());
 
   // Commit.
   TestRenderFrameHost* orig_rfh = contents()->GetMainFrame();
@@ -1140,8 +1127,7 @@
   orig_rfh->SendNavigate(0, url1);
   EXPECT_EQ(controller.GetEntryCount(), 1);
   EXPECT_EQ(0, controller.GetLastCommittedEntryIndex());
-  EXPECT_EQ(0, NavigationEntryImpl::FromNavigationEntry(
-      controller.GetLastCommittedEntry())->bindings());
+  EXPECT_EQ(0, controller.GetLastCommittedEntry()->bindings());
 
   // Manually increase the number of active frames in the SiteInstance
   // that orig_rfh belongs to, to prevent it from being destroyed when
@@ -1163,16 +1149,14 @@
   EXPECT_EQ(controller.GetEntryCount(), 2);
   EXPECT_EQ(1, controller.GetLastCommittedEntryIndex());
   EXPECT_TRUE(controller.CanGoBack());
-  EXPECT_EQ(1, NavigationEntryImpl::FromNavigationEntry(
-      controller.GetLastCommittedEntry())->bindings());
+  EXPECT_EQ(1, controller.GetLastCommittedEntry()->bindings());
 
   // Going back, the first entry should still appear unprivileged.
   controller.GoBack();
   new_rfh->PrepareForCommit(url1);
   orig_rfh->SendNavigate(0, url1);
   EXPECT_EQ(0, controller.GetLastCommittedEntryIndex());
-  EXPECT_EQ(0, NavigationEntryImpl::FromNavigationEntry(
-      controller.GetLastCommittedEntry())->bindings());
+  EXPECT_EQ(0, controller.GetLastCommittedEntry()->bindings());
 }
 
 TEST_F(NavigationControllerTest, Reload) {
@@ -1276,8 +1260,7 @@
   ASSERT_TRUE(controller.GetVisibleEntry());
 
   // Make the entry believe its RenderProcessHost is a guest.
-  NavigationEntryImpl* entry1 =
-      NavigationEntryImpl::FromNavigationEntry(controller.GetVisibleEntry());
+  NavigationEntryImpl* entry1 = controller.GetVisibleEntry();
   reinterpret_cast<MockRenderProcessHost*>(
       entry1->site_instance()->GetProcess())->set_is_isolated_guest(true);
 
@@ -1290,8 +1273,7 @@
   EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
   EXPECT_EQ(controller.GetPendingEntryIndex(), 0);
 
-  NavigationEntryImpl* entry2 =
-      NavigationEntryImpl::FromNavigationEntry(controller.GetPendingEntry());
+  NavigationEntryImpl* entry2 = controller.GetPendingEntry();
   EXPECT_EQ(entry1, entry2);
 }
 
@@ -1385,8 +1367,7 @@
   GlobalRequestID transfer_id(3, 4);
 
   // Set non-persisted values on the pending entry.
-  NavigationEntryImpl* pending_entry =
-      NavigationEntryImpl::FromNavigationEntry(controller.GetPendingEntry());
+  NavigationEntryImpl* pending_entry = controller.GetPendingEntry();
   pending_entry->SetBrowserInitiatedPostData(post_data.get());
   pending_entry->set_is_renderer_initiated(true);
   pending_entry->set_transferred_global_request_id(transfer_id);
@@ -1403,9 +1384,7 @@
 
   // Certain values that are only used for pending entries get reset after
   // commit.
-  NavigationEntryImpl* committed_entry =
-      NavigationEntryImpl::FromNavigationEntry(
-          controller.GetLastCommittedEntry());
+  NavigationEntryImpl* committed_entry = controller.GetLastCommittedEntry();
   EXPECT_FALSE(committed_entry->GetBrowserInitiatedPostData());
   EXPECT_FALSE(committed_entry->is_renderer_initiated());
   EXPECT_EQ(GlobalRequestID(-1, -1),
@@ -1426,17 +1405,14 @@
   redirects.push_back(GURL("http://foo2"));
 
   // Set redirects on the pending entry.
-  NavigationEntryImpl* pending_entry =
-      NavigationEntryImpl::FromNavigationEntry(controller.GetPendingEntry());
+  NavigationEntryImpl* pending_entry = controller.GetPendingEntry();
   pending_entry->SetRedirectChain(redirects);
   EXPECT_EQ(1U, pending_entry->GetRedirectChain().size());
   EXPECT_EQ(GURL("http://foo2"), pending_entry->GetRedirectChain()[0]);
 
   // Normal navigation will preserve redirects in the committed entry.
   main_test_rfh()->SendNavigateWithRedirects(0, url1, redirects);
-  NavigationEntryImpl* committed_entry =
-      NavigationEntryImpl::FromNavigationEntry(
-          controller.GetLastCommittedEntry());
+  NavigationEntryImpl* committed_entry = controller.GetLastCommittedEntry();
   ASSERT_EQ(1U, committed_entry->GetRedirectChain().size());
   EXPECT_EQ(GURL("http://foo2"), committed_entry->GetRedirectChain()[0]);
 }
@@ -1607,8 +1583,7 @@
   // We know all the entries have the same site instance, so we can just grab
   // a random one for looking up other entries.
   SiteInstance* site_instance =
-      NavigationEntryImpl::FromNavigationEntry(
-          controller.GetLastCommittedEntry())->site_instance();
+      controller.GetLastCommittedEntry()->site_instance();
 
   // That second URL should be the last committed and it should have gotten the
   // new title.
@@ -2058,6 +2033,7 @@
   const GURL url3("http://foo3");
   params.page_id = 2;
   params.url = url3;
+  params.transition = ui::PAGE_TRANSITION_MANUAL_SUBFRAME;
   EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params,
                                              &details));
   EXPECT_EQ(1U, navigation_entry_committed_counter_);
@@ -2067,8 +2043,9 @@
 
   // Go back one.
   controller.GoBack();
-  params.url = url2;
   params.page_id = 1;
+  params.url = url2;
+  params.transition = ui::PAGE_TRANSITION_AUTO_SUBFRAME;
   EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params,
                                              &details));
   EXPECT_EQ(1U, navigation_entry_committed_counter_);
@@ -2080,8 +2057,9 @@
 
   // Go back one more.
   controller.GoBack();
-  params.url = url1;
   params.page_id = 0;
+  params.url = url1;
+  params.transition = ui::PAGE_TRANSITION_AUTO_SUBFRAME;
   EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params,
                                              &details));
   EXPECT_EQ(1U, navigation_entry_committed_counter_);
@@ -2478,10 +2456,8 @@
   // and no SiteInstance.
   ASSERT_EQ(1, our_controller.GetEntryCount());
   EXPECT_EQ(NavigationEntryImpl::RESTORE_LAST_SESSION_EXITED_CLEANLY,
-            NavigationEntryImpl::FromNavigationEntry(
-                our_controller.GetEntryAtIndex(0))->restore_type());
-  EXPECT_FALSE(NavigationEntryImpl::FromNavigationEntry(
-      our_controller.GetEntryAtIndex(0))->site_instance());
+            our_controller.GetEntryAtIndex(0)->restore_type());
+  EXPECT_FALSE(our_controller.GetEntryAtIndex(0)->site_instance());
 
   // After navigating, we should have one entry, and it should be "pending".
   // It should now have a SiteInstance and no restore_type.
@@ -2491,10 +2467,8 @@
             our_controller.GetPendingEntry());
   EXPECT_EQ(0, our_controller.GetEntryAtIndex(0)->GetPageID());
   EXPECT_EQ(NavigationEntryImpl::RESTORE_NONE,
-            NavigationEntryImpl::FromNavigationEntry
-                (our_controller.GetEntryAtIndex(0))->restore_type());
-  EXPECT_TRUE(NavigationEntryImpl::FromNavigationEntry(
-      our_controller.GetEntryAtIndex(0))->site_instance());
+            our_controller.GetEntryAtIndex(0)->restore_type());
+  EXPECT_TRUE(our_controller.GetEntryAtIndex(0)->site_instance());
 
   // Timestamp should remain the same before the navigation finishes.
   EXPECT_EQ(timestamp, our_controller.GetEntryAtIndex(0)->GetTimestamp());
@@ -2518,13 +2492,11 @@
   EXPECT_EQ(1, our_controller.GetEntryCount());
   EXPECT_EQ(0, our_controller.GetLastCommittedEntryIndex());
   EXPECT_FALSE(our_controller.GetPendingEntry());
-  EXPECT_EQ(url,
-            NavigationEntryImpl::FromNavigationEntry(
-                our_controller.GetLastCommittedEntry())->site_instance()->
-                    GetSiteURL());
+  EXPECT_EQ(
+      url,
+      our_controller.GetLastCommittedEntry()->site_instance()->GetSiteURL());
   EXPECT_EQ(NavigationEntryImpl::RESTORE_NONE,
-            NavigationEntryImpl::FromNavigationEntry(
-                our_controller.GetEntryAtIndex(0))->restore_type());
+            our_controller.GetEntryAtIndex(0)->restore_type());
 
   // Timestamp should have been updated.
   EXPECT_GE(our_controller.GetEntryAtIndex(0)->GetTimestamp(), timestamp);
@@ -2553,10 +2525,8 @@
   // Before navigating to the restored entry, it should have a restore_type
   // and no SiteInstance.
   EXPECT_EQ(NavigationEntryImpl::RESTORE_LAST_SESSION_EXITED_CLEANLY,
-            NavigationEntryImpl::FromNavigationEntry(
-                our_controller.GetEntryAtIndex(0))->restore_type());
-  EXPECT_FALSE(NavigationEntryImpl::FromNavigationEntry(
-      our_controller.GetEntryAtIndex(0))->site_instance());
+            our_controller.GetEntryAtIndex(0)->restore_type());
+  EXPECT_FALSE(our_controller.GetEntryAtIndex(0)->site_instance());
 
   // After navigating, we should have one entry, and it should be "pending".
   // It should now have a SiteInstance and no restore_type.
@@ -2566,10 +2536,8 @@
             our_controller.GetPendingEntry());
   EXPECT_EQ(0, our_controller.GetEntryAtIndex(0)->GetPageID());
   EXPECT_EQ(NavigationEntryImpl::RESTORE_NONE,
-            NavigationEntryImpl::FromNavigationEntry(
-                our_controller.GetEntryAtIndex(0))->restore_type());
-  EXPECT_TRUE(NavigationEntryImpl::FromNavigationEntry(
-      our_controller.GetEntryAtIndex(0))->site_instance());
+            our_controller.GetEntryAtIndex(0)->restore_type());
+  EXPECT_TRUE(our_controller.GetEntryAtIndex(0)->site_instance());
 
   // This pending navigation may have caused a different navigation to fail,
   // which causes the pending entry to be cleared.
@@ -2599,13 +2567,11 @@
   EXPECT_EQ(1, our_controller.GetEntryCount());
   EXPECT_EQ(0, our_controller.GetLastCommittedEntryIndex());
   EXPECT_FALSE(our_controller.GetPendingEntry());
-  EXPECT_EQ(url,
-            NavigationEntryImpl::FromNavigationEntry(
-                our_controller.GetLastCommittedEntry())->site_instance()->
-                    GetSiteURL());
+  EXPECT_EQ(
+      url,
+      our_controller.GetLastCommittedEntry()->site_instance()->GetSiteURL());
   EXPECT_EQ(NavigationEntryImpl::RESTORE_NONE,
-            NavigationEntryImpl::FromNavigationEntry(
-                our_controller.GetEntryAtIndex(0))->restore_type());
+            our_controller.GetEntryAtIndex(0)->restore_type());
 }
 
 // Make sure that the page type and stuff is correct after an interstitial.
@@ -2622,8 +2588,7 @@
   const GURL url2("http://bar");
   controller.LoadURL(
       url1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
-  NavigationEntryImpl::FromNavigationEntry(controller.GetPendingEntry())->
-      set_page_type(PAGE_TYPE_INTERSTITIAL);
+  controller.GetPendingEntry()->set_page_type(PAGE_TYPE_INTERSTITIAL);
 
   // At this point the interstitial will be displayed and the load will still
   // be pending. If the user continues, the load will commit.
@@ -2963,9 +2928,7 @@
 
   EXPECT_EQ(url1_fixed, controller.GetPendingEntry()->GetURL());
   EXPECT_EQ(url1, controller.GetPendingEntry()->GetVirtualURL());
-  EXPECT_TRUE(
-      NavigationEntryImpl::FromNavigationEntry(controller.GetPendingEntry())->
-          is_renderer_initiated());
+  EXPECT_TRUE(controller.GetPendingEntry()->is_renderer_initiated());
 
   // If the user clicks another link, we should replace the pending entry.
   navigator->DidStartProvisionalLoad(main_test_rfh(), url2, false);
@@ -2987,12 +2950,9 @@
   // We should remember if the pending entry will replace the current one.
   // http://crbug.com/308444.
   navigator->DidStartProvisionalLoad(main_test_rfh(), url1, false);
-  NavigationEntryImpl::FromNavigationEntry(controller.GetPendingEntry())->
-      set_should_replace_entry(true);
+  controller.GetPendingEntry()->set_should_replace_entry(true);
   navigator->DidStartProvisionalLoad(main_test_rfh(), url2, false);
-  EXPECT_TRUE(
-      NavigationEntryImpl::FromNavigationEntry(controller.GetPendingEntry())->
-          should_replace_entry());
+  EXPECT_TRUE(controller.GetPendingEntry()->should_replace_entry());
   // TODO(nasko): Until OnNavigate is moved to RenderFrameHost, we need
   // to go through the RenderViewHost. The TestRenderViewHost routes navigations
   // to the main frame.
@@ -3027,9 +2987,7 @@
   controller.LoadURLWithParams(load_url_params);
   EXPECT_EQ(url0, controller.GetVisibleEntry()->GetURL());
   EXPECT_EQ(url1, controller.GetPendingEntry()->GetURL());
-  EXPECT_TRUE(
-      NavigationEntryImpl::FromNavigationEntry(controller.GetPendingEntry())->
-          is_renderer_initiated());
+  EXPECT_TRUE(controller.GetPendingEntry()->is_renderer_initiated());
 
   // After commit, both visible should be updated, there should be no pending
   // entry, and we should no longer treat the entry as renderer-initiated.
@@ -3037,9 +2995,7 @@
   main_test_rfh()->SendNavigate(1, url1);
   EXPECT_EQ(url1, controller.GetVisibleEntry()->GetURL());
   EXPECT_FALSE(controller.GetPendingEntry());
-  EXPECT_FALSE(
-      NavigationEntryImpl::FromNavigationEntry(
-          controller.GetLastCommittedEntry())->is_renderer_initiated());
+  EXPECT_FALSE(controller.GetLastCommittedEntry()->is_renderer_initiated());
 
   notifications.Reset();
 }
@@ -3064,9 +3020,7 @@
   controller.LoadURLWithParams(load_url_params);
   EXPECT_EQ(url, controller.GetVisibleEntry()->GetURL());
   EXPECT_EQ(url, controller.GetPendingEntry()->GetURL());
-  EXPECT_TRUE(
-      NavigationEntryImpl::FromNavigationEntry(controller.GetPendingEntry())->
-          is_renderer_initiated());
+  EXPECT_TRUE(controller.GetPendingEntry()->is_renderer_initiated());
   EXPECT_TRUE(controller.IsInitialNavigation());
   EXPECT_FALSE(contents()->HasAccessedInitialDocument());
 
@@ -3104,9 +3058,7 @@
   controller.LoadURLWithParams(load_url_params);
   EXPECT_EQ(url, controller.GetVisibleEntry()->GetURL());
   EXPECT_EQ(url, controller.GetPendingEntry()->GetURL());
-  EXPECT_FALSE(
-      NavigationEntryImpl::FromNavigationEntry(controller.GetPendingEntry())->
-          is_renderer_initiated());
+  EXPECT_FALSE(controller.GetPendingEntry()->is_renderer_initiated());
   EXPECT_TRUE(controller.IsInitialNavigation());
   EXPECT_FALSE(contents()->HasAccessedInitialDocument());
 
@@ -3155,9 +3107,7 @@
   controller.LoadURLWithParams(load_url_params);
   EXPECT_EQ(url, controller.GetVisibleEntry()->GetURL());
   EXPECT_EQ(url, controller.GetPendingEntry()->GetURL());
-  EXPECT_TRUE(
-      NavigationEntryImpl::FromNavigationEntry(controller.GetPendingEntry())->
-          is_renderer_initiated());
+  EXPECT_TRUE(controller.GetPendingEntry()->is_renderer_initiated());
   EXPECT_TRUE(controller.IsInitialNavigation());
   EXPECT_FALSE(contents()->HasAccessedInitialDocument());
 
@@ -3201,9 +3151,7 @@
   load_url_params.is_renderer_initiated = true;
   controller.LoadURLWithParams(load_url_params);
   EXPECT_EQ(url1, controller.GetVisibleEntry()->GetURL());
-  EXPECT_TRUE(
-      NavigationEntryImpl::FromNavigationEntry(controller.GetPendingEntry())->
-          is_renderer_initiated());
+  EXPECT_TRUE(controller.GetPendingEntry()->is_renderer_initiated());
   EXPECT_TRUE(controller.IsInitialNavigation());
   EXPECT_FALSE(contents()->HasAccessedInitialDocument());
 
@@ -3534,7 +3482,7 @@
   // The max page ID map should be copied over and updated with the max page ID
   // from the current tab.
   SiteInstance* instance1 =
-      GetSiteInstanceFromEntry(other_controller.GetEntryAtIndex(0));
+      other_controller.GetEntryAtIndex(0)->site_instance();
   EXPECT_EQ(0, other_contents->GetMaxPageIDForSiteInstance(instance1));
 
   // Ensure the SessionStorageNamespaceMaps are the same size and have
@@ -3569,10 +3517,8 @@
   NavigateAndCommit(url2);
 
   // First two entries should have the same SiteInstance.
-  SiteInstance* instance1 =
-      GetSiteInstanceFromEntry(controller.GetEntryAtIndex(0));
-  SiteInstance* instance2 =
-      GetSiteInstanceFromEntry(controller.GetEntryAtIndex(1));
+  SiteInstance* instance1 = controller.GetEntryAtIndex(0)->site_instance();
+  SiteInstance* instance2 = controller.GetEntryAtIndex(1)->site_instance();
   EXPECT_EQ(instance1, instance2);
   EXPECT_EQ(0, controller.GetEntryAtIndex(0)->GetPageID());
   EXPECT_EQ(1, controller.GetEntryAtIndex(1)->GetPageID());
@@ -3601,7 +3547,7 @@
   // A new SiteInstance in a different BrowsingInstance should be used for the
   // new tab.
   SiteInstance* instance3 =
-      GetSiteInstanceFromEntry(other_controller.GetEntryAtIndex(2));
+      other_controller.GetEntryAtIndex(2)->site_instance();
   EXPECT_NE(instance3, instance1);
   EXPECT_FALSE(instance3->IsRelatedSiteInstance(instance1));
 
@@ -3643,7 +3589,7 @@
   // The max page ID map should be copied over and updated with the max page ID
   // from the current tab.
   SiteInstance* instance1 =
-      GetSiteInstanceFromEntry(other_controller.GetEntryAtIndex(1));
+      other_controller.GetEntryAtIndex(1)->site_instance();
   EXPECT_EQ(0, other_contents->GetMaxPageIDForSiteInstance(instance1));
 }
 
@@ -3679,7 +3625,7 @@
   // The max page ID map should be copied over and updated with the max page ID
   // from the current tab.
   SiteInstance* instance1 =
-      GetSiteInstanceFromEntry(other_controller.GetEntryAtIndex(2));
+      other_controller.GetEntryAtIndex(2)->site_instance();
   EXPECT_EQ(0, other_contents->GetMaxPageIDForSiteInstance(instance1));
 }
 
@@ -3717,7 +3663,7 @@
   // The max page ID map should be copied over and updated with the max page ID
   // from the current tab.
   SiteInstance* instance1 =
-      GetSiteInstanceFromEntry(other_controller.GetEntryAtIndex(2));
+      other_controller.GetEntryAtIndex(2)->site_instance();
   EXPECT_EQ(0, other_contents->GetMaxPageIDForSiteInstance(instance1));
 }
 
@@ -3760,7 +3706,7 @@
   // The max page ID map should be copied over and updated with the max page ID
   // from the current tab.
   SiteInstance* instance1 =
-      GetSiteInstanceFromEntry(other_controller.GetEntryAtIndex(0));
+      other_controller.GetEntryAtIndex(0)->site_instance();
   EXPECT_EQ(0, other_contents->GetMaxPageIDForSiteInstance(instance1));
 }
 
@@ -3808,7 +3754,7 @@
   // The max page ID map should be copied over and updated with the max page ID
   // from the current tab.
   SiteInstance* instance1 =
-      GetSiteInstanceFromEntry(other_controller.GetEntryAtIndex(1));
+      other_controller.GetEntryAtIndex(1)->site_instance();
   EXPECT_EQ(0, other_contents->GetMaxPageIDForSiteInstance(instance1));
 }
 
@@ -3844,7 +3790,7 @@
   // The max page ID map should be copied over and updated with the max page ID
   // from the current tab.
   SiteInstance* instance1 =
-      GetSiteInstanceFromEntry(other_controller.GetEntryAtIndex(2));
+      other_controller.GetEntryAtIndex(2)->site_instance();
   EXPECT_EQ(0, other_contents->GetMaxPageIDForSiteInstance(instance1));
 }
 
@@ -3909,10 +3855,8 @@
   NavigateAndCommit(url2);
 
   // First two entries should have the same SiteInstance.
-  SiteInstance* instance1 =
-      GetSiteInstanceFromEntry(controller.GetEntryAtIndex(0));
-  SiteInstance* instance2 =
-      GetSiteInstanceFromEntry(controller.GetEntryAtIndex(1));
+  SiteInstance* instance1 = controller.GetEntryAtIndex(0)->site_instance();
+  SiteInstance* instance2 = controller.GetEntryAtIndex(1)->site_instance();
   EXPECT_EQ(instance1, instance2);
   EXPECT_EQ(0, controller.GetEntryAtIndex(0)->GetPageID());
   EXPECT_EQ(1, controller.GetEntryAtIndex(1)->GetPageID());
@@ -3939,7 +3883,7 @@
   // A new SiteInstance in a different BrowsingInstance should be used for the
   // new tab.
   SiteInstance* instance3 =
-      GetSiteInstanceFromEntry(other_controller.GetEntryAtIndex(1));
+      other_controller.GetEntryAtIndex(1)->site_instance();
   EXPECT_NE(instance3, instance1);
   EXPECT_FALSE(instance3->IsRelatedSiteInstance(instance1));
 
@@ -4341,28 +4285,24 @@
       new MockScreenshotManager(&controller);
   controller.SetScreenshotManager(screenshot_manager);
   for (int i = 0; i < controller.GetEntryCount(); ++i) {
-    entry = NavigationEntryImpl::FromNavigationEntry(
-        controller.GetEntryAtIndex(i));
+    entry = controller.GetEntryAtIndex(i);
     screenshot_manager->TakeScreenshotFor(entry);
     EXPECT_TRUE(entry->screenshot().get());
   }
 
   NavigateAndCommit(GURL("https://foo/"));
   EXPECT_EQ(13, controller.GetEntryCount());
-  entry = NavigationEntryImpl::FromNavigationEntry(
-      controller.GetEntryAtIndex(11));
+  entry = controller.GetEntryAtIndex(11);
   screenshot_manager->TakeScreenshotFor(entry);
 
   for (int i = 0; i < 2; ++i) {
-    entry = NavigationEntryImpl::FromNavigationEntry(
-        controller.GetEntryAtIndex(i));
+    entry = controller.GetEntryAtIndex(i);
     EXPECT_FALSE(entry->screenshot().get()) << "Screenshot " << i
                                             << " not purged";
   }
 
   for (int i = 2; i < controller.GetEntryCount() - 1; ++i) {
-    entry = NavigationEntryImpl::FromNavigationEntry(
-        controller.GetEntryAtIndex(i));
+    entry = controller.GetEntryAtIndex(i);
     EXPECT_TRUE(entry->screenshot().get()) << "Screenshot not found for " << i;
   }
 
@@ -4371,14 +4311,12 @@
   contents()->CommitPendingNavigation();
   EXPECT_EQ(5, controller.GetCurrentEntryIndex());
   for (int i = 0; i < controller.GetEntryCount() - 1; ++i) {
-    entry = NavigationEntryImpl::FromNavigationEntry(
-        controller.GetEntryAtIndex(i));
+    entry = controller.GetEntryAtIndex(i);
     screenshot_manager->TakeScreenshotFor(entry);
   }
 
   for (int i = 10; i <= 12; ++i) {
-    entry = NavigationEntryImpl::FromNavigationEntry(
-        controller.GetEntryAtIndex(i));
+    entry = controller.GetEntryAtIndex(i);
     EXPECT_FALSE(entry->screenshot().get()) << "Screenshot " << i
                                             << " not purged";
     screenshot_manager->TakeScreenshotFor(entry);
@@ -4389,14 +4327,12 @@
   contents()->CommitPendingNavigation();
   EXPECT_EQ(7, controller.GetCurrentEntryIndex());
   for (int i = 0; i < controller.GetEntryCount() - 1; ++i) {
-    entry = NavigationEntryImpl::FromNavigationEntry(
-        controller.GetEntryAtIndex(i));
+    entry = controller.GetEntryAtIndex(i);
     screenshot_manager->TakeScreenshotFor(entry);
   }
 
   for (int i = 0; i < 2; ++i) {
-    entry = NavigationEntryImpl::FromNavigationEntry(
-        controller.GetEntryAtIndex(i));
+    entry = controller.GetEntryAtIndex(i);
     EXPECT_FALSE(entry->screenshot().get()) << "Screenshot " << i
                                             << " not purged";
   }
@@ -4407,8 +4343,7 @@
   controller.ClearAllScreenshots();
   EXPECT_EQ(0, screenshot_manager->GetScreenshotCount());
   for (int i = 0; i < controller.GetEntryCount(); ++i) {
-    entry = NavigationEntryImpl::FromNavigationEntry(
-        controller.GetEntryAtIndex(i));
+    entry = controller.GetEntryAtIndex(i);
     EXPECT_FALSE(entry->screenshot().get()) << "Screenshot " << i
                                             << " not cleared";
   }
@@ -4472,9 +4407,7 @@
 
   // Verify that the pending entry correctly indicates that the session history
   // should be cleared.
-  NavigationEntryImpl* entry =
-      NavigationEntryImpl::FromNavigationEntry(
-          controller.GetPendingEntry());
+  NavigationEntryImpl* entry = controller.GetPendingEntry();
   ASSERT_TRUE(entry);
   EXPECT_TRUE(entry->should_clear_history_list());
 
@@ -4534,4 +4467,61 @@
   EXPECT_EQ(0, delegate->repost_form_warning_count());
 }
 
+TEST_F(NavigationControllerTest, UnreachableURLGivesErrorPage) {
+  GURL url("http://foo");
+  FrameHostMsg_DidCommitProvisionalLoad_Params params;
+  params.page_id = 1;
+  params.url = url;
+  params.transition = ui::PAGE_TRANSITION_LINK;
+  params.gesture = NavigationGestureUser;
+  params.page_state = PageState::CreateFromURL(url);
+  params.was_within_same_page = false;
+  params.is_post = true;
+  params.post_id = 2;
+  params.url_is_unreachable = true;
+  // Navigate to new page
+  {
+    LoadCommittedDetails details;
+    controller_impl().RendererDidNavigate(main_test_rfh(), params, &details);
+    EXPECT_EQ(PAGE_TYPE_ERROR,
+              controller_impl().GetLastCommittedEntry()->GetPageType());
+    EXPECT_EQ(NAVIGATION_TYPE_NEW_PAGE, details.type);
+  }
+
+  // Navigate to existing page.
+  {
+    LoadCommittedDetails details;
+    controller_impl().RendererDidNavigate(main_test_rfh(), params, &details);
+    EXPECT_EQ(PAGE_TYPE_ERROR,
+              controller_impl().GetLastCommittedEntry()->GetPageType());
+    EXPECT_EQ(NAVIGATION_TYPE_EXISTING_PAGE, details.type);
+  }
+
+  // Navigate to same page.
+  // Note: The call to LoadURL() creates a pending entry in order to trigger the
+  // same-page transition.
+  controller_impl().LoadURL(
+      url, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
+  params.transition = ui::PAGE_TRANSITION_TYPED;
+  {
+    LoadCommittedDetails details;
+    controller_impl().RendererDidNavigate(main_test_rfh(), params, &details);
+    EXPECT_EQ(PAGE_TYPE_ERROR,
+              controller_impl().GetLastCommittedEntry()->GetPageType());
+    EXPECT_EQ(NAVIGATION_TYPE_SAME_PAGE, details.type);
+  }
+
+  // Navigate in page.
+  params.url = GURL("http://foo#foo");
+  params.transition = ui::PAGE_TRANSITION_LINK;
+  params.was_within_same_page = true;
+  {
+    LoadCommittedDetails details;
+    controller_impl().RendererDidNavigate(main_test_rfh(), params, &details);
+    EXPECT_EQ(PAGE_TYPE_ERROR,
+              controller_impl().GetLastCommittedEntry()->GetPageType());
+    EXPECT_EQ(NAVIGATION_TYPE_IN_PAGE, details.type);
+  }
+}
+
 }  // namespace content
diff --git a/content/browser/frame_host/navigation_entry_impl.cc b/content/browser/frame_host/navigation_entry_impl.cc
index 849871a..7e39192 100644
--- a/content/browser/frame_host/navigation_entry_impl.cc
+++ b/content/browser/frame_host/navigation_entry_impl.cc
@@ -48,12 +48,10 @@
                                          const base::string16& title,
                                          ui::PageTransition transition_type,
                                          bool is_renderer_initiated)
-    : unique_id_(GetUniqueIDInConstructor()),
-      site_instance_(instance),
+    : frame_entry_(instance, url, referrer),
+      unique_id_(GetUniqueIDInConstructor()),
       bindings_(kInvalidBindings),
       page_type_(PAGE_TYPE_NORMAL),
-      url_(url),
-      referrer_(referrer),
       update_virtual_url_with_url_(false),
       title_(title),
       page_id_(page_id),
@@ -82,12 +80,12 @@
 }
 
 void NavigationEntryImpl::SetURL(const GURL& url) {
-  url_ = url;
+  frame_entry_.set_url(url);
   cached_display_title_.clear();
 }
 
 const GURL& NavigationEntryImpl::GetURL() const {
-  return url_;
+  return frame_entry_.url();
 }
 
 void NavigationEntryImpl::SetBaseURLForDataURL(const GURL& url) {
@@ -99,20 +97,20 @@
 }
 
 void NavigationEntryImpl::SetReferrer(const Referrer& referrer) {
-  referrer_ = referrer;
+  frame_entry_.set_referrer(referrer);
 }
 
 const Referrer& NavigationEntryImpl::GetReferrer() const {
-  return referrer_;
+  return frame_entry_.referrer();
 }
 
 void NavigationEntryImpl::SetVirtualURL(const GURL& url) {
-  virtual_url_ = (url == url_) ? GURL() : url;
+  virtual_url_ = (url == GetURL()) ? GURL() : url;
   cached_display_title_.clear();
 }
 
 const GURL& NavigationEntryImpl::GetVirtualURL() const {
-  return virtual_url_.is_empty() ? url_ : virtual_url_;
+  return virtual_url_.is_empty() ? GetURL() : virtual_url_;
 }
 
 void NavigationEntryImpl::SetTitle(const base::string16& title) {
@@ -141,7 +139,7 @@
 }
 
 void NavigationEntryImpl::set_site_instance(SiteInstanceImpl* site_instance) {
-  site_instance_ = site_instance;
+  frame_entry_.set_site_instance(site_instance);
 }
 
 void NavigationEntryImpl::set_source_site_instance(
@@ -172,12 +170,12 @@
   base::string16 title;
   if (!virtual_url_.is_empty()) {
     title = net::FormatUrl(virtual_url_, languages);
-  } else if (!url_.is_empty()) {
-    title = net::FormatUrl(url_, languages);
+  } else if (!GetURL().is_empty()) {
+    title = net::FormatUrl(GetURL(), languages);
   }
 
   // For file:// URLs use the filename as the title, not the full path.
-  if (url_.SchemeIsFile()) {
+  if (GetURL().SchemeIsFile()) {
     base::string16::size_type slashpos = title.rfind('/');
     if (slashpos != base::string16::npos)
       title = title.substr(slashpos + 1);
diff --git a/content/browser/frame_host/navigation_entry_impl.h b/content/browser/frame_host/navigation_entry_impl.h
index f458bc0..8fadcb3 100644
--- a/content/browser/frame_host/navigation_entry_impl.h
+++ b/content/browser/frame_host/navigation_entry_impl.h
@@ -8,6 +8,7 @@
 #include "base/basictypes.h"
 #include "base/memory/ref_counted.h"
 #include "base/time/time.h"
+#include "content/browser/frame_host/frame_navigation_entry.h"
 #include "content/browser/site_instance_impl.h"
 #include "content/public/browser/favicon_status.h"
 #include "content/public/browser/global_request_id.h"
@@ -97,15 +98,15 @@
     unique_id_ = unique_id;
   }
 
-  // The SiteInstance tells us how to share sub-processes. This is a reference
-  // counted pointer to a shared site instance.
+  // The SiteInstance represents which pages must share processes. This is a
+  // reference counted pointer to a shared SiteInstance.
   //
   // Note that the SiteInstance should usually not be changed after it is set,
   // but this may happen if the NavigationEntry was cloned and needs to use a
   // different SiteInstance.
   void set_site_instance(SiteInstanceImpl* site_instance);
   SiteInstanceImpl* site_instance() const {
-    return site_instance_.get();
+    return frame_entry_.site_instance();
   }
 
   // The |source_site_instance| is used to identify the SiteInstance of the
@@ -243,14 +244,15 @@
   // state_serializer.cc appropriately.
   // WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
 
+  // The FrameNavigationEntry for the main frame.
+  // TODO(creis): Make this a tree with nodes for each frame in the page.
+  FrameNavigationEntry frame_entry_;
+
   // See the accessors above for descriptions.
   int unique_id_;
-  scoped_refptr<SiteInstanceImpl> site_instance_;
   // TODO(creis): Persist bindings_. http://crbug.com/173672.
   int bindings_;
   PageType page_type_;
-  GURL url_;
-  Referrer referrer_;
   GURL virtual_url_;
   bool update_virtual_url_with_url_;
   base::string16 title_;
diff --git a/content/browser/frame_host/navigation_entry_screenshot_manager.cc b/content/browser/frame_host/navigation_entry_screenshot_manager.cc
index 780f3c0..121898b 100644
--- a/content/browser/frame_host/navigation_entry_screenshot_manager.cc
+++ b/content/browser/frame_host/navigation_entry_screenshot_manager.cc
@@ -78,8 +78,7 @@
   if (!overscroll_enabled)
     return;
 
-  NavigationEntryImpl* entry =
-      NavigationEntryImpl::FromNavigationEntry(owner_->GetLastCommittedEntry());
+  NavigationEntryImpl* entry = owner_->GetLastCommittedEntry();
   if (!entry)
     return;
 
@@ -109,8 +108,7 @@
 void NavigationEntryScreenshotManager::ClearAllScreenshots() {
   int count = owner_->GetEntryCount();
   for (int i = 0; i < count; ++i) {
-    ClearScreenshot(NavigationEntryImpl::FromNavigationEntry(
-        owner_->GetEntryAtIndex(i)));
+    ClearScreenshot(owner_->GetEntryAtIndex(i));
   }
   DCHECK_EQ(GetScreenshotCount(), 0);
 }
@@ -142,9 +140,9 @@
   NavigationEntryImpl* entry = NULL;
   int entry_count = owner_->GetEntryCount();
   for (int i = 0; i < entry_count; ++i) {
-    NavigationEntry* iter = owner_->GetEntryAtIndex(i);
+    NavigationEntryImpl* iter = owner_->GetEntryAtIndex(i);
     if (iter->GetUniqueID() == unique_id) {
-      entry = NavigationEntryImpl::FromNavigationEntry(iter);
+      entry = iter;
       break;
     }
   }
@@ -173,8 +171,7 @@
   int screenshot_count = 0;
   int entry_count = owner_->GetEntryCount();
   for (int i = 0; i < entry_count; ++i) {
-    NavigationEntryImpl* entry =
-        NavigationEntryImpl::FromNavigationEntry(owner_->GetEntryAtIndex(i));
+    NavigationEntryImpl* entry = owner_->GetEntryAtIndex(i);
     if (entry->screenshot().get())
       screenshot_count++;
   }
@@ -187,9 +184,9 @@
   NavigationEntryImpl* entry = NULL;
   int entry_count = owner_->GetEntryCount();
   for (int i = 0; i < entry_count; ++i) {
-    NavigationEntry* iter = owner_->GetEntryAtIndex(i);
+    NavigationEntryImpl* iter = owner_->GetEntryAtIndex(i);
     if (iter->GetUniqueID() == unique_id) {
-      entry = NavigationEntryImpl::FromNavigationEntry(iter);
+      entry = iter;
       break;
     }
   }
@@ -224,8 +221,7 @@
   const int current = owner_->GetCurrentEntryIndex();
   const int num_entries = owner_->GetEntryCount();
   int available_slots = kMaxScreenshots;
-  if (NavigationEntryImpl::FromNavigationEntry(owner_->GetEntryAtIndex(current))
-          ->screenshot().get()) {
+  if (owner_->GetEntryAtIndex(current)->screenshot().get()) {
     --available_slots;
   }
 
@@ -242,16 +238,14 @@
   int forward = current + 1;
   while (available_slots > 0 && (back >= 0 || forward < num_entries)) {
     if (back >= 0) {
-      NavigationEntryImpl* entry = NavigationEntryImpl::FromNavigationEntry(
-          owner_->GetEntryAtIndex(back));
+      NavigationEntryImpl* entry = owner_->GetEntryAtIndex(back);
       if (entry->screenshot().get())
         --available_slots;
       --back;
     }
 
     if (available_slots > 0 && forward < num_entries) {
-      NavigationEntryImpl* entry = NavigationEntryImpl::FromNavigationEntry(
-          owner_->GetEntryAtIndex(forward));
+      NavigationEntryImpl* entry = owner_->GetEntryAtIndex(forward);
       if (entry->screenshot().get())
         --available_slots;
       ++forward;
@@ -261,16 +255,14 @@
   // Purge any screenshot at |back| or lower indices, and |forward| or higher
   // indices.
   while (screenshot_count > kMaxScreenshots && back >= 0) {
-    NavigationEntryImpl* entry = NavigationEntryImpl::FromNavigationEntry(
-        owner_->GetEntryAtIndex(back));
+    NavigationEntryImpl* entry = owner_->GetEntryAtIndex(back);
     if (ClearScreenshot(entry))
       --screenshot_count;
     --back;
   }
 
   while (screenshot_count > kMaxScreenshots && forward < num_entries) {
-    NavigationEntryImpl* entry = NavigationEntryImpl::FromNavigationEntry(
-        owner_->GetEntryAtIndex(forward));
+    NavigationEntryImpl* entry = owner_->GetEntryAtIndex(forward);
     if (ClearScreenshot(entry))
       --screenshot_count;
     ++forward;
diff --git a/content/browser/frame_host/navigation_request.h b/content/browser/frame_host/navigation_request.h
index b49d692..948dbd1d 100644
--- a/content/browser/frame_host/navigation_request.h
+++ b/content/browser/frame_host/navigation_request.h
@@ -78,6 +78,8 @@
 
   const CommonNavigationParams& common_params() const { return common_params_; }
 
+  const BeginNavigationParams& begin_params() const { return begin_params_; }
+
   const CommitNavigationParams& commit_params() const { return commit_params_; }
 
   NavigationURLLoader* loader_for_testing() const { return loader_.get(); }
diff --git a/content/browser/frame_host/navigator.cc b/content/browser/frame_host/navigator.cc
index 774b8c5..00e193fc 100644
--- a/content/browser/frame_host/navigator.cc
+++ b/content/browser/frame_host/navigator.cc
@@ -15,7 +15,7 @@
 }
 
 bool Navigator::NavigateToPendingEntry(
-    RenderFrameHostImpl* render_frame_host,
+    FrameTreeNode* frame_tree_node,
     NavigationController::ReloadType reload_type) {
   return false;
 }
diff --git a/content/browser/frame_host/navigator.h b/content/browser/frame_host/navigator.h
index a69dfdf..7390426a 100644
--- a/content/browser/frame_host/navigator.h
+++ b/content/browser/frame_host/navigator.h
@@ -85,7 +85,7 @@
   // NavigationController know about each other. This will be possible once
   // initialization of Navigator and NavigationController is properly done.
   virtual bool NavigateToPendingEntry(
-      RenderFrameHostImpl* render_frame_host,
+      FrameTreeNode* frame_tree_node,
       NavigationController::ReloadType reload_type);
 
 
diff --git a/content/browser/frame_host/navigator_impl.cc b/content/browser/frame_host/navigator_impl.cc
index e4afbe62..d31a65a 100644
--- a/content/browser/frame_host/navigator_impl.cc
+++ b/content/browser/frame_host/navigator_impl.cc
@@ -191,8 +191,7 @@
   render_process_host->FilterURL(false, &validated_url);
 
   bool is_main_frame = render_frame_host->frame_tree_node()->IsMainFrame();
-  NavigationEntryImpl* pending_entry =
-      NavigationEntryImpl::FromNavigationEntry(controller_->GetPendingEntry());
+  NavigationEntryImpl* pending_entry = controller_->GetPendingEntry();
   if (is_main_frame) {
     // If there is no browser-initiated pending entry for this navigation and it
     // is not for the error URL, create a pending entry using the current
@@ -321,7 +320,7 @@
 }
 
 bool NavigatorImpl::NavigateToEntry(
-    RenderFrameHostImpl* render_frame_host,
+    FrameTreeNode* frame_tree_node,
     const NavigationEntryImpl& entry,
     NavigationController::ReloadType reload_type) {
   TRACE_EVENT0("browser,navigation", "NavigatorImpl::NavigateToEntry");
@@ -340,18 +339,14 @@
   // capture the time needed for the RenderFrameHost initialization.
   base::TimeTicks navigation_start = base::TimeTicks::Now();
 
-  RenderFrameHostManager* manager =
-      render_frame_host->frame_tree_node()->render_manager();
+  RenderFrameHostManager* manager = frame_tree_node->render_manager();
 
   // PlzNavigate: the RenderFrameHosts are no longer asked to navigate.
   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
           switches::kEnableBrowserSideNavigation)) {
     navigation_data_.reset(new NavigationMetricsData(
         navigation_start, entry.GetURL(), entry.restore_type()));
-    RequestNavigation(render_frame_host->frame_tree_node(),
-                      entry,
-                      reload_type,
-                      navigation_start);
+    RequestNavigation(frame_tree_node, entry, reload_type, navigation_start);
     return true;
   }
 
@@ -369,7 +364,7 @@
 
   // Notify observers that we will navigate in this RenderFrame.
   if (delegate_) {
-    delegate_->AboutToNavigateRenderFrame(render_frame_host,
+    delegate_->AboutToNavigateRenderFrame(frame_tree_node->current_frame_host(),
                                           dest_render_frame_host);
   }
 
@@ -423,12 +418,10 @@
 }
 
 bool NavigatorImpl::NavigateToPendingEntry(
-    RenderFrameHostImpl* render_frame_host,
+    FrameTreeNode* frame_tree_node,
     NavigationController::ReloadType reload_type) {
-  return NavigateToEntry(
-      render_frame_host,
-      *NavigationEntryImpl::FromNavigationEntry(controller_->GetPendingEntry()),
-      reload_type);
+  return NavigateToEntry(frame_tree_node, *controller_->GetPendingEntry(),
+                         reload_type);
 }
 
 void NavigatorImpl::DidNavigate(
@@ -703,14 +696,27 @@
     const CommonNavigationParams& common_params,
     const BeginNavigationParams& begin_params,
     scoped_refptr<ResourceRequestBody> body) {
+  // This is a renderer-initiated navigation.
   CHECK(base::CommandLine::ForCurrentProcess()->HasSwitch(
       switches::kEnableBrowserSideNavigation));
   DCHECK(frame_tree_node);
 
-  // This is a renderer-initiated navigation, so generate a new
-  // NavigationRequest and store it in the map.
-  // TODO(clamy): Renderer-initiated navigations should not always cancel the
-  // current one.
+  NavigationRequest* ongoing_navigation_request =
+      navigation_request_map_.get(frame_tree_node->frame_tree_node_id());
+
+  // The renderer-initiated navigation request is ignored iff a) there is an
+  // ongoing request b) which is browser or user-initiated and c) the renderer
+  // request is not user-initiated.
+  if (ongoing_navigation_request &&
+      (ongoing_navigation_request->browser_initiated() ||
+       ongoing_navigation_request->begin_params().has_user_gesture) &&
+      !begin_params.has_user_gesture) {
+    return;
+  }
+
+  // In all other cases the current navigation, if any, is canceled and a new
+  // NavigationRequest is created and stored in the map. Actual cancellation
+  // happens when the existing request map entry is replaced and destroyed.
   scoped_ptr<NavigationRequest> navigation_request =
       NavigationRequest::CreateRendererInitiated(
           frame_tree_node, common_params, begin_params, body);
diff --git a/content/browser/frame_host/navigator_impl.h b/content/browser/frame_host/navigator_impl.h
index 7d4622d..bf075ff 100644
--- a/content/browser/frame_host/navigator_impl.h
+++ b/content/browser/frame_host/navigator_impl.h
@@ -51,7 +51,7 @@
                    const FrameHostMsg_DidCommitProvisionalLoad_Params&
                        input_params) override;
   bool NavigateToPendingEntry(
-      RenderFrameHostImpl* render_frame_host,
+      FrameTreeNode* frame_tree_node,
       NavigationController::ReloadType reload_type) override;
   void RequestOpenURL(RenderFrameHostImpl* render_frame_host,
                       const GURL& url,
@@ -101,7 +101,7 @@
   // Navigates to the given entry, which must be the pending entry.  Private
   // because all callers should use NavigateToPendingEntry.
   bool NavigateToEntry(
-      RenderFrameHostImpl* render_frame_host,
+      FrameTreeNode* frame_tree_node,
       const NavigationEntryImpl& entry,
       NavigationController::ReloadType reload_type);
 
diff --git a/content/browser/frame_host/navigator_impl_unittest.cc b/content/browser/frame_host/navigator_impl_unittest.cc
index e31ce22eb..82a49874 100644
--- a/content/browser/frame_host/navigator_impl_unittest.cc
+++ b/content/browser/frame_host/navigator_impl_unittest.cc
@@ -139,8 +139,8 @@
   EXPECT_FALSE(GetNavigationRequestForFrameTreeNode(node));
   EXPECT_FALSE(node->render_manager()->pending_frame_host());
 
-  // The main RFH should not have been changed, and the renderer should have
-  // been initialized.
+  // The main RenderFrameHost should not have been changed, and the renderer
+  // should have been initialized.
   EXPECT_EQ(site_instance_id, main_test_rfh()->GetSiteInstance()->GetId());
   EXPECT_TRUE(main_test_rfh()->IsRenderFrameLive());
 
@@ -161,9 +161,9 @@
   contents()->NavigateAndCommit(kUrl1);
   EXPECT_TRUE(main_test_rfh()->IsRenderFrameLive());
 
-  // Start a renderer-initiated navigation.
+  // Start a renderer-initiated non-user-initiated navigation.
   process()->sink().ClearMessages();
-  main_test_rfh()->SendBeginNavigationWithURL(kUrl2);
+  main_test_rfh()->SendBeginNavigationWithURL(kUrl2, false);
   FrameTreeNode* node = main_test_rfh()->frame_tree_node();
   NavigationRequest* request = GetNavigationRequestForFrameTreeNode(node);
   ASSERT_TRUE(request);
@@ -171,6 +171,7 @@
   // The navigation is immediately started as there's no need to wait for
   // beforeUnload to be executed.
   EXPECT_EQ(NavigationRequest::STARTED, request->state());
+  EXPECT_FALSE(request->begin_params().has_user_gesture);
   EXPECT_EQ(kUrl2, request->common_params().url);
   EXPECT_FALSE(request->browser_initiated());
   EXPECT_FALSE(GetSpeculativeRenderFrameHost(node));
@@ -375,9 +376,12 @@
   // Navigate to a different site.
   process()->sink().ClearMessages();
   RequestNavigation(node, kUrl2);
-  main_test_rfh()->SendBeforeUnloadACK(true);
   NavigationRequest* main_request = GetNavigationRequestForFrameTreeNode(node);
   ASSERT_TRUE(main_request);
+  EXPECT_FALSE(GetSpeculativeRenderFrameHost(node));
+
+  // Receive the beforeUnload ACK.
+  main_test_rfh()->SendBeforeUnloadACK(true);
   EXPECT_TRUE(GetSpeculativeRenderFrameHost(node));
 
   scoped_refptr<ResourceResponse> response(new ResourceResponse);
@@ -430,7 +434,7 @@
   EXPECT_EQ(1, GetLoaderForNavigationRequest(main_request)->redirect_count());
   EXPECT_FALSE(GetSpeculativeRenderFrameHost(node));
 
-  // Request the RenderFrameHost to commit.
+  // Have the RenderFrameHost commit the navigation.
   response = new ResourceResponse;
   GetLoaderForNavigationRequest(main_request)->CallOnResponseStarted(
       response, MakeEmptyStream());
@@ -475,8 +479,9 @@
   EXPECT_TRUE(request1->browser_initiated());
   base::WeakPtr<TestNavigationURLLoader> loader1 =
       GetLoaderForNavigationRequest(request1)->AsWeakPtr();
+  EXPECT_TRUE(loader1);
 
-  // Confirm a speculative RFH was created.
+  // Confirm a speculative RenderFrameHost was created.
   TestRenderFrameHost* speculative_rfh = GetSpeculativeRenderFrameHost(node);
   ASSERT_TRUE(speculative_rfh);
   int32 site_instance_id_1 = speculative_rfh->GetSiteInstance()->GetId();
@@ -494,13 +499,13 @@
   // Confirm that the first loader got destroyed.
   EXPECT_FALSE(loader1);
 
-  // Confirm that a new speculative RFH was created.
+  // Confirm that a new speculative RenderFrameHost was created.
   speculative_rfh = GetSpeculativeRenderFrameHost(node);
   ASSERT_TRUE(speculative_rfh);
   int32 site_instance_id_2 = speculative_rfh->GetSiteInstance()->GetId();
   EXPECT_NE(site_instance_id_1, site_instance_id_2);
 
-  // Request the RenderFrameHost to commit.
+  // Have the RenderFrameHost commit the navigation.
   scoped_refptr<ResourceResponse> response(new ResourceResponse);
   GetLoaderForNavigationRequest(request2)->CallOnResponseStarted(
       response, MakeEmptyStream());
@@ -515,10 +520,220 @@
   EXPECT_EQ(kUrl2_site, main_test_rfh()->GetSiteInstance()->GetSiteURL());
   EXPECT_EQ(kUrl2, contents()->GetLastCommittedURL());
 
-  // Confirm that the committed RFH is the latest speculative one.
+  // Confirm that the committed RenderFrameHost is the latest speculative one.
   EXPECT_EQ(site_instance_id_2, main_test_rfh()->GetSiteInstance()->GetId());
 }
 
+// PlzNavigate: Test that a browser-initiated navigation is canceled if a
+// renderer-initiated user-initiated request has been issued in the meantime.
+TEST_F(NavigatorTestWithBrowserSideNavigation,
+       RendererUserInitiatedNavigationCancel) {
+  const GURL kUrl0("http://www.wikipedia.org/");
+  const GURL kUrl1("http://www.chromium.org/");
+  const GURL kUrl2("http://www.google.com/");
+
+  // Initialization.
+  contents()->NavigateAndCommit(kUrl0);
+  FrameTreeNode* node = main_test_rfh()->frame_tree_node();
+
+  // Start a browser-initiated navigation to the 1st URL and receive its
+  // beforeUnload ACK.
+  process()->sink().ClearMessages();
+  RequestNavigation(node, kUrl1);
+  main_test_rfh()->SendBeforeUnloadACK(true);
+  NavigationRequest* request1 = GetNavigationRequestForFrameTreeNode(node);
+  ASSERT_TRUE(request1);
+  EXPECT_EQ(kUrl1, request1->common_params().url);
+  EXPECT_TRUE(request1->browser_initiated());
+  base::WeakPtr<TestNavigationURLLoader> loader1 =
+      GetLoaderForNavigationRequest(request1)->AsWeakPtr();
+  EXPECT_TRUE(loader1);
+
+  // Confirm a speculative RenderFrameHost was created.
+  TestRenderFrameHost* speculative_rfh = GetSpeculativeRenderFrameHost(node);
+  ASSERT_TRUE(speculative_rfh);
+  int32 site_instance_id_1 = speculative_rfh->GetSiteInstance()->GetId();
+
+  // Now receive a renderer-initiated user-initiated request. It should replace
+  // the current NavigationRequest.
+  main_test_rfh()->SendBeginNavigationWithURL(kUrl2, true);
+  NavigationRequest* request2 = GetNavigationRequestForFrameTreeNode(node);
+  ASSERT_TRUE(request2);
+  EXPECT_EQ(kUrl2, request2->common_params().url);
+  EXPECT_FALSE(request2->browser_initiated());
+  EXPECT_TRUE(request2->begin_params().has_user_gesture);
+
+  // Confirm that the first loader got destroyed.
+  EXPECT_FALSE(loader1);
+
+  // Confirm that a new speculative RenderFrameHost was created.
+  speculative_rfh = GetSpeculativeRenderFrameHost(node);
+  ASSERT_TRUE(speculative_rfh);
+  int32 site_instance_id_2 = speculative_rfh->GetSiteInstance()->GetId();
+  EXPECT_NE(site_instance_id_1, site_instance_id_2);
+
+  // Have the RenderFrameHost commit the navigation.
+  scoped_refptr<ResourceResponse> response(new ResourceResponse);
+  GetLoaderForNavigationRequest(request2)
+      ->CallOnResponseStarted(response, MakeEmptyStream());
+  EXPECT_TRUE(DidRenderFrameHostRequestCommit(speculative_rfh));
+  EXPECT_FALSE(DidRenderFrameHostRequestCommit(main_test_rfh()));
+
+  // Commit the navigation.
+  speculative_rfh->SendNavigate(0, kUrl2);
+
+  // Confirm that the commit corresponds to the new request.
+  ASSERT_TRUE(main_test_rfh());
+  EXPECT_EQ(kUrl2, contents()->GetLastCommittedURL());
+
+  // Confirm that the committed RenderFrameHost is the latest speculative one.
+  EXPECT_EQ(site_instance_id_2, main_test_rfh()->GetSiteInstance()->GetId());
+}
+
+// PlzNavigate: Test that a renderer-initiated user-initiated navigation is NOT
+// canceled if a renderer-initiated non-user-initiated request is issued in the
+// meantime.
+TEST_F(NavigatorTestWithBrowserSideNavigation,
+       RendererNonUserInitiatedNavigationDoesntCancelRendererUserInitiated) {
+  const GURL kUrl0("http://www.wikipedia.org/");
+  const GURL kUrl1("http://www.chromium.org/");
+  const GURL kUrl2("http://www.google.com/");
+
+  // Initialization.
+  contents()->NavigateAndCommit(kUrl0);
+  FrameTreeNode* node = main_test_rfh()->frame_tree_node();
+
+  // Start a renderer-initiated user-initiated navigation to the 1st URL.
+  process()->sink().ClearMessages();
+  main_test_rfh()->SendBeginNavigationWithURL(kUrl1, true);
+  NavigationRequest* request1 = GetNavigationRequestForFrameTreeNode(node);
+  ASSERT_TRUE(request1);
+  EXPECT_EQ(kUrl1, request1->common_params().url);
+  EXPECT_FALSE(request1->browser_initiated());
+  EXPECT_TRUE(request1->begin_params().has_user_gesture);
+  EXPECT_TRUE(GetSpeculativeRenderFrameHost(node));
+
+  // Now receive a renderer-initiated non-user-initiated request. Nothing should
+  // change.
+  main_test_rfh()->SendBeginNavigationWithURL(kUrl2, false);
+  NavigationRequest* request2 = GetNavigationRequestForFrameTreeNode(node);
+  ASSERT_TRUE(request2);
+  EXPECT_EQ(request1, request2);
+  EXPECT_EQ(kUrl1, request2->common_params().url);
+  EXPECT_FALSE(request2->browser_initiated());
+  EXPECT_TRUE(request2->begin_params().has_user_gesture);
+  TestRenderFrameHost* speculative_rfh = GetSpeculativeRenderFrameHost(node);
+  ASSERT_TRUE(speculative_rfh);
+
+  // Have the RenderFrameHost commit the navigation.
+  scoped_refptr<ResourceResponse> response(new ResourceResponse);
+  GetLoaderForNavigationRequest(request2)
+      ->CallOnResponseStarted(response, MakeEmptyStream());
+  EXPECT_TRUE(DidRenderFrameHostRequestCommit(speculative_rfh));
+  EXPECT_FALSE(DidRenderFrameHostRequestCommit(main_test_rfh()));
+
+  // Commit the navigation.
+  speculative_rfh->SendNavigate(0, kUrl1);
+  EXPECT_EQ(kUrl1, contents()->GetLastCommittedURL());
+}
+
+// PlzNavigate: Test that a browser-initiated navigation is NOT canceled if a
+// renderer-initiated non-user-initiated request is issued in the meantime.
+TEST_F(NavigatorTestWithBrowserSideNavigation,
+       RendererNonUserInitiatedNavigationDoesntCancelBrowserInitiated) {
+  const GURL kUrl0("http://www.wikipedia.org/");
+  const GURL kUrl1("http://www.chromium.org/");
+  const GURL kUrl2("http://www.google.com/");
+
+  // Initialization.
+  contents()->NavigateAndCommit(kUrl0);
+  FrameTreeNode* node = main_test_rfh()->frame_tree_node();
+
+  // Start a browser-initiated navigation to the 1st URL.
+  process()->sink().ClearMessages();
+  RequestNavigation(node, kUrl1);
+  NavigationRequest* request1 = GetNavigationRequestForFrameTreeNode(node);
+  ASSERT_TRUE(request1);
+  EXPECT_EQ(kUrl1, request1->common_params().url);
+  EXPECT_TRUE(request1->browser_initiated());
+  EXPECT_FALSE(GetSpeculativeRenderFrameHost(node));
+
+  // Now receive a renderer-initiated non-user-initiated request. Nothing should
+  // change.
+  main_test_rfh()->SendBeginNavigationWithURL(kUrl2, false);
+  NavigationRequest* request2 = GetNavigationRequestForFrameTreeNode(node);
+  ASSERT_TRUE(request2);
+  EXPECT_EQ(request1, request2);
+  EXPECT_EQ(kUrl1, request2->common_params().url);
+  EXPECT_TRUE(request2->browser_initiated());
+  EXPECT_FALSE(GetSpeculativeRenderFrameHost(node));
+
+  // Now receive the beforeUnload ACK from the still ongoing navigation.
+  main_test_rfh()->SendBeforeUnloadACK(true);
+  TestRenderFrameHost* speculative_rfh = GetSpeculativeRenderFrameHost(node);
+  ASSERT_TRUE(speculative_rfh);
+
+  // Have the RenderFrameHost commit the navigation.
+  scoped_refptr<ResourceResponse> response(new ResourceResponse);
+  GetLoaderForNavigationRequest(request2)
+      ->CallOnResponseStarted(response, MakeEmptyStream());
+  EXPECT_TRUE(DidRenderFrameHostRequestCommit(speculative_rfh));
+  EXPECT_FALSE(DidRenderFrameHostRequestCommit(main_test_rfh()));
+
+  // Commit the navigation.
+  speculative_rfh->SendNavigate(0, kUrl1);
+  EXPECT_EQ(kUrl1, contents()->GetLastCommittedURL());
+}
+
+// PlzNavigate: Test that a renderer-initiated non-user-initiated navigation is
+// canceled if a another similar request is issued in the meantime.
+TEST_F(NavigatorTestWithBrowserSideNavigation,
+       RendererNonUserInitiatedNavigationCancelSimilarNavigation) {
+  const GURL kUrl0("http://www.wikipedia.org/");
+  const GURL kUrl1("http://www.chromium.org/");
+  const GURL kUrl2("http://www.google.com/");
+
+  // Initialization.
+  contents()->NavigateAndCommit(kUrl0);
+  FrameTreeNode* node = main_test_rfh()->frame_tree_node();
+
+  // Start a renderer-initiated non-user-initiated navigation to the 1st URL.
+  process()->sink().ClearMessages();
+  main_test_rfh()->SendBeginNavigationWithURL(kUrl1, false);
+  NavigationRequest* request1 = GetNavigationRequestForFrameTreeNode(node);
+  ASSERT_TRUE(request1);
+  EXPECT_EQ(kUrl1, request1->common_params().url);
+  EXPECT_FALSE(request1->browser_initiated());
+  EXPECT_FALSE(request1->begin_params().has_user_gesture);
+  EXPECT_TRUE(GetSpeculativeRenderFrameHost(node));
+  base::WeakPtr<TestNavigationURLLoader> loader1 =
+      GetLoaderForNavigationRequest(request1)->AsWeakPtr();
+  EXPECT_TRUE(loader1);
+
+  // Now receive a 2nd similar request that should replace the current one.
+  main_test_rfh()->SendBeginNavigationWithURL(kUrl2, false);
+  NavigationRequest* request2 = GetNavigationRequestForFrameTreeNode(node);
+  EXPECT_EQ(kUrl2, request2->common_params().url);
+  EXPECT_FALSE(request2->browser_initiated());
+  EXPECT_FALSE(request2->begin_params().has_user_gesture);
+  TestRenderFrameHost* speculative_rfh = GetSpeculativeRenderFrameHost(node);
+  ASSERT_TRUE(speculative_rfh);
+
+  // Confirm that the first loader got destroyed.
+  EXPECT_FALSE(loader1);
+
+  // Have the RenderFrameHost commit the navigation.
+  scoped_refptr<ResourceResponse> response(new ResourceResponse);
+  GetLoaderForNavigationRequest(request2)
+      ->CallOnResponseStarted(response, MakeEmptyStream());
+  EXPECT_TRUE(DidRenderFrameHostRequestCommit(speculative_rfh));
+  EXPECT_FALSE(DidRenderFrameHostRequestCommit(main_test_rfh()));
+
+  // Commit the navigation.
+  speculative_rfh->SendNavigate(0, kUrl2);
+  EXPECT_EQ(kUrl2, contents()->GetLastCommittedURL());
+}
+
 // PlzNavigate: Test that a reload navigation is properly signaled to the
 // RenderFrame when the navigation can commit. A speculative RenderFrameHost
 // should not be created at any step.
@@ -567,6 +782,9 @@
   const GURL kUrl("http://google.com/");
   process()->sink().ClearMessages();
   RequestNavigation(node, kUrl);
+  EXPECT_FALSE(GetSpeculativeRenderFrameHost(node));
+
+  // Receive the beforeUnload ACK.
   main_test_rfh()->SendBeforeUnloadACK(true);
   TestRenderFrameHost* speculative_rfh = GetSpeculativeRenderFrameHost(node);
   ASSERT_TRUE(speculative_rfh);
@@ -608,6 +826,9 @@
   const GURL kUrl("http://google.com/");
   process()->sink().ClearMessages();
   RequestNavigation(node, kUrl);
+  EXPECT_FALSE(GetSpeculativeRenderFrameHost(node));
+
+  // Receive the beforeUnload ACK.
   main_test_rfh()->SendBeforeUnloadACK(true);
   TestRenderFrameHost* speculative_rfh = GetSpeculativeRenderFrameHost(node);
   int32 site_instance_id = speculative_rfh->GetSiteInstance()->GetId();
@@ -634,8 +855,8 @@
   speculative_rfh = GetSpeculativeRenderFrameHost(node);
   ASSERT_TRUE(speculative_rfh);
 
-  // For now, ensure that the speculative RFH does not change after the
-  // redirect.
+  // For now, ensure that the speculative RenderFrameHost does not change after
+  // the redirect.
   // TODO(carlosk): once the speculative RenderFrameHost updates with redirects
   // this next check will be changed to verify that it actually happens.
   EXPECT_EQ(site_instance_id, speculative_rfh->GetSiteInstance()->GetId());
@@ -698,6 +919,8 @@
       ->sink()
       .ClearMessages();
   RequestNavigation(node, kUrl1);
+  EXPECT_FALSE(GetSpeculativeRenderFrameHost(node));
+
   main_test_rfh()->SendBeforeUnloadACK(true);
   EXPECT_EQ(rfh1, GetSpeculativeRenderFrameHost(node));
   EXPECT_NE(RenderFrameHostImpl::STATE_DEFAULT,
diff --git a/content/browser/gamepad/gamepad_provider.cc b/content/browser/gamepad/gamepad_provider.cc
index caa4cc8..f3493b4 100644
--- a/content/browser/gamepad/gamepad_provider.cc
+++ b/content/browser/gamepad/gamepad_provider.cc
@@ -10,6 +10,7 @@
 #include "base/logging.h"
 #include "base/message_loop/message_loop.h"
 #include "base/message_loop/message_loop_proxy.h"
+#include "base/third_party/dynamic_annotations/dynamic_annotations.h"
 #include "base/threading/thread.h"
 #include "base/threading/thread_restrictions.h"
 #include "content/browser/gamepad/gamepad_data_fetcher.h"
diff --git a/content/browser/gpu/browser_gpu_channel_host_factory.cc b/content/browser/gpu/browser_gpu_channel_host_factory.cc
index 71ce450..0eee6d4 100644
--- a/content/browser/gpu/browser_gpu_channel_host_factory.cc
+++ b/content/browser/gpu/browser_gpu_channel_host_factory.cc
@@ -422,29 +422,6 @@
     host->AddFilter(filter.get());
 }
 
-void BrowserGpuChannelHostFactory::SetHandlerForControlMessages(
-      const uint32* message_ids,
-      size_t num_messages,
-      const base::Callback<void(const IPC::Message&)>& handler,
-      base::TaskRunner* target_task_runner) {
-  DCHECK(gpu_host_id_)
-      << "Do not call"
-      << " BrowserGpuChannelHostFactory::SetHandlerForControlMessages()"
-      << " until the GpuProcessHost has been set up.";
-
-  scoped_refptr<IPC::ForwardingMessageFilter> filter =
-      new IPC::ForwardingMessageFilter(message_ids,
-                                       num_messages,
-                                       target_task_runner);
-  filter->AddRoute(MSG_ROUTING_CONTROL, handler);
-
-  GetIOLoopProxy()->PostTask(
-      FROM_HERE,
-      base::Bind(&BrowserGpuChannelHostFactory::AddFilterOnIO,
-                 gpu_host_id_,
-                 filter));
-}
-
 bool BrowserGpuChannelHostFactory::IsGpuMemoryBufferConfigurationSupported(
     gfx::GpuMemoryBuffer::Format format,
     gfx::GpuMemoryBuffer::Usage usage) {
diff --git a/content/browser/gpu/browser_gpu_channel_host_factory.h b/content/browser/gpu/browser_gpu_channel_host_factory.h
index 33c7fb04..9524f663 100644
--- a/content/browser/gpu/browser_gpu_channel_host_factory.h
+++ b/content/browser/gpu/browser_gpu_channel_host_factory.h
@@ -56,14 +56,6 @@
                               int client_id,
                               int32 sync_point) override;
 
-  // Specify a task runner and callback to be used for a set of messages. The
-  // callback will be set up on the current GpuProcessHost, identified by
-  // GpuProcessHostId().
-  virtual void SetHandlerForControlMessages(
-      const uint32* message_ids,
-      size_t num_messages,
-      const base::Callback<void(const IPC::Message&)>& handler,
-      base::TaskRunner* target_task_runner);
   int GpuProcessHostId() { return gpu_host_id_; }
 #if !defined(OS_ANDROID)
   GpuChannelHost* EstablishGpuChannelSync(
diff --git a/content/browser/gpu/compositor_util.cc b/content/browser/gpu/compositor_util.cc
index c75571b9..8c32b6f 100644
--- a/content/browser/gpu/compositor_util.cc
+++ b/content/browser/gpu/compositor_util.cc
@@ -295,6 +295,8 @@
 bool IsThreadedGpuRasterizationEnabled() {
   if (!IsImplSidePaintingEnabled())
     return false;
+  if (!IsGpuRasterizationEnabled() && !IsForceGpuRasterizationEnabled())
+    return false;
 
   const base::CommandLine& command_line =
       *base::CommandLine::ForCurrentProcess();
diff --git a/content/browser/indexed_db/indexed_db_backing_store.cc b/content/browser/indexed_db/indexed_db_backing_store.cc
index 51f52c37..4ae9ed4 100644
--- a/content/browser/indexed_db/indexed_db_backing_store.cc
+++ b/content/browser/indexed_db/indexed_db_backing_store.cc
@@ -4402,4 +4402,12 @@
       last_modified_(last_modified) {
 }
 
+IndexedDBBackingStore::Transaction::WriteDescriptor::WriteDescriptor(
+    const WriteDescriptor& other) = default;
+IndexedDBBackingStore::Transaction::WriteDescriptor::~WriteDescriptor() =
+    default;
+IndexedDBBackingStore::Transaction::WriteDescriptor&
+    IndexedDBBackingStore::Transaction::WriteDescriptor::
+    operator=(const WriteDescriptor& other) = default;
+
 }  // namespace content
diff --git a/content/browser/indexed_db/indexed_db_backing_store.h b/content/browser/indexed_db/indexed_db_backing_store.h
index 7c16b4e..d7e5dbc 100644
--- a/content/browser/indexed_db/indexed_db_backing_store.h
+++ b/content/browser/indexed_db/indexed_db_backing_store.h
@@ -167,7 +167,7 @@
     typedef std::vector<std::pair<BlobEntryKey, std::string> >
         BlobEntryKeyValuePairVec;
 
-    class WriteDescriptor {
+    class CONTENT_EXPORT WriteDescriptor {
      public:
       WriteDescriptor(const GURL& url,
                       int64_t key,
@@ -177,6 +177,9 @@
                       int64_t key,
                       int64_t size,
                       base::Time last_modified);
+      WriteDescriptor(const WriteDescriptor& other);
+      ~WriteDescriptor();
+      WriteDescriptor& operator=(const WriteDescriptor& other);
 
       bool is_file() const { return is_file_; }
       const GURL& url() const {
diff --git a/content/browser/indexed_db/indexed_db_blob_info.cc b/content/browser/indexed_db/indexed_db_blob_info.cc
index e4b1a3a..d61de75 100644
--- a/content/browser/indexed_db/indexed_db_blob_info.cc
+++ b/content/browser/indexed_db/indexed_db_blob_info.cc
@@ -49,7 +49,12 @@
     : is_file_(true), type_(type), size_(-1), file_name_(file_name), key_(key) {
 }
 
-IndexedDBBlobInfo::~IndexedDBBlobInfo() {}
+IndexedDBBlobInfo::IndexedDBBlobInfo(const IndexedDBBlobInfo& other) = default;
+
+IndexedDBBlobInfo::~IndexedDBBlobInfo() = default;
+
+IndexedDBBlobInfo& IndexedDBBlobInfo::operator=(
+    const IndexedDBBlobInfo& other) = default;
 
 void IndexedDBBlobInfo::set_size(int64 size) {
   DCHECK_EQ(-1, size_);
diff --git a/content/browser/indexed_db/indexed_db_blob_info.h b/content/browser/indexed_db/indexed_db_blob_info.h
index 02d039d..60da707 100644
--- a/content/browser/indexed_db/indexed_db_blob_info.h
+++ b/content/browser/indexed_db/indexed_db_blob_info.h
@@ -33,7 +33,9 @@
                     const base::string16& type,
                     const base::string16& file_name);
 
+  IndexedDBBlobInfo(const IndexedDBBlobInfo& other);
   ~IndexedDBBlobInfo();
+  IndexedDBBlobInfo& operator=(const IndexedDBBlobInfo& other);
 
   bool is_file() const { return is_file_; }
   const std::string& uuid() const { return uuid_; }
diff --git a/content/browser/indexed_db/indexed_db_browsertest.cc b/content/browser/indexed_db/indexed_db_browsertest.cc
index e2b419aa..81cd9c0 100644
--- a/content/browser/indexed_db/indexed_db_browsertest.cc
+++ b/content/browser/indexed_db/indexed_db_browsertest.cc
@@ -441,6 +441,35 @@
   EXPECT_EQ(0, RequestDiskUsage());
 }
 
+IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, DiskFullOnCommit) {
+  // Ignore several preceding transactions:
+  // * The test calls deleteDatabase() which opens the backing store:
+  //   #1: IndexedDBBackingStore::OpenBackingStore
+  //       => IndexedDBBackingStore::SetUpMetadata
+  //   #2: IndexedDBBackingStore::OpenBackingStore
+  //       => IndexedDBBackingStore::CleanUpBlobJournal (no-op)
+  // * Then deletes the database:
+  //   #3: IndexedDBFactoryImpl::DeleteDatabase
+  //       => IndexedDBDatabase::Create
+  //       => IndexedDBBackingStore::CreateIDBDatabaseMetaData
+  //   #4: IndexedDBFactoryImpl::DeleteDatabase
+  //       => IndexedDBDatabase::DeleteDatabase
+  //       => IndexedDBBackingStore::DeleteDatabase
+  //       => IndexedDBBackingStore::CleanUpBlobJournal (no-op)
+  // * The test calls open(), to create a new database:
+  //   #5: IndexedDBFactoryImpl::Open
+  //       => IndexedDBDatabase::Create
+  //       => IndexedDBBackingStore::CreateIDBDatabaseMetaData
+  //   #6: IndexedDBTransaction::Commit - initial "versionchange" transaction
+  // * Once the connection is opened, the test runs:
+  //   #7: IndexedDBTransaction::Commit - the test's "readwrite" transaction)
+  const int instance_num = 7;
+  const int call_num = 1;
+  FailOperation(FAIL_CLASS_LEVELDB_TRANSACTION, FAIL_METHOD_COMMIT_DISK_FULL,
+                instance_num, call_num);
+  SimpleTest(GetTestUrl("indexeddb", "disk_full_on_commit.html"));
+}
+
 namespace {
 
 static void CompactIndexedDBBackingStore(
diff --git a/content/browser/indexed_db/indexed_db_database_error.cc b/content/browser/indexed_db/indexed_db_database_error.cc
new file mode 100644
index 0000000..ff05a77
--- /dev/null
+++ b/content/browser/indexed_db/indexed_db_database_error.cc
@@ -0,0 +1,28 @@
+// 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 "content/browser/indexed_db/indexed_db_database_error.h"
+
+namespace content {
+
+IndexedDBDatabaseError::IndexedDBDatabaseError(uint16 code) : code_(code) {
+}
+
+IndexedDBDatabaseError::IndexedDBDatabaseError() = default;
+
+IndexedDBDatabaseError::IndexedDBDatabaseError(uint16 code, const char* message)
+    : code_(code), message_(base::ASCIIToUTF16(message)) {
+}
+
+IndexedDBDatabaseError::IndexedDBDatabaseError(uint16 code,
+                                               const base::string16& message)
+    : code_(code), message_(message) {
+}
+
+IndexedDBDatabaseError::~IndexedDBDatabaseError() = default;
+
+IndexedDBDatabaseError& IndexedDBDatabaseError::operator=(
+    const IndexedDBDatabaseError& rhs) = default;
+
+}  // namespace content
diff --git a/content/browser/indexed_db/indexed_db_database_error.h b/content/browser/indexed_db/indexed_db_database_error.h
index 25b5ea6..7b233f0 100644
--- a/content/browser/indexed_db/indexed_db_database_error.h
+++ b/content/browser/indexed_db/indexed_db_database_error.h
@@ -13,21 +13,20 @@
 
 class IndexedDBDatabaseError {
  public:
-  explicit IndexedDBDatabaseError(uint16 code) : code_(code) {}
-  IndexedDBDatabaseError(uint16 code, const char* message)
-      : code_(code), message_(base::ASCIIToUTF16(message)) {}
-  IndexedDBDatabaseError(uint16 code, const base::string16& message)
-      : code_(code), message_(message) {}
-  ~IndexedDBDatabaseError() {}
+  IndexedDBDatabaseError();
+  explicit IndexedDBDatabaseError(uint16 code);
+  IndexedDBDatabaseError(uint16 code, const char* message);
+  IndexedDBDatabaseError(uint16 code, const base::string16& message);
+  ~IndexedDBDatabaseError();
+
+  IndexedDBDatabaseError& operator=(const IndexedDBDatabaseError& rhs);
 
   uint16 code() const { return code_; }
   const base::string16& message() const { return message_; }
 
  private:
-  const uint16 code_;
-  const base::string16 message_;
-
-  DISALLOW_COPY_AND_ASSIGN(IndexedDBDatabaseError);
+  uint16 code_ = 0;
+  base::string16 message_;
 };
 
 }  // namespace content
diff --git a/content/browser/indexed_db/indexed_db_metadata.cc b/content/browser/indexed_db/indexed_db_metadata.cc
index 47b7219..92c4c31 100644
--- a/content/browser/indexed_db/indexed_db_metadata.cc
+++ b/content/browser/indexed_db/indexed_db_metadata.cc
@@ -6,6 +6,28 @@
 
 namespace content {
 
+IndexedDBIndexMetadata::IndexedDBIndexMetadata() = default;
+
+IndexedDBIndexMetadata::IndexedDBIndexMetadata(const base::string16& name,
+                                               int64 id,
+                                               const IndexedDBKeyPath& key_path,
+                                               bool unique,
+                                               bool multi_entry)
+    : name(name),
+      id(id),
+      key_path(key_path),
+      unique(unique),
+      multi_entry(multi_entry) {
+}
+
+IndexedDBIndexMetadata::IndexedDBIndexMetadata(
+    const IndexedDBIndexMetadata& other) = default;
+
+IndexedDBIndexMetadata::~IndexedDBIndexMetadata() = default;
+
+IndexedDBIndexMetadata& IndexedDBIndexMetadata::operator=(
+    const IndexedDBIndexMetadata& other) = default;
+
 IndexedDBObjectStoreMetadata::IndexedDBObjectStoreMetadata(
     const base::string16& name,
     int64 id,
@@ -18,11 +40,19 @@
       auto_increment(auto_increment),
       max_index_id(max_index_id) {}
 
-IndexedDBObjectStoreMetadata::IndexedDBObjectStoreMetadata() {}
-IndexedDBObjectStoreMetadata::~IndexedDBObjectStoreMetadata() {}
+IndexedDBObjectStoreMetadata::IndexedDBObjectStoreMetadata() = default;
+
+IndexedDBObjectStoreMetadata::IndexedDBObjectStoreMetadata(
+    const IndexedDBObjectStoreMetadata& other) = default;
+
+IndexedDBObjectStoreMetadata::~IndexedDBObjectStoreMetadata() = default;
+
+IndexedDBObjectStoreMetadata& IndexedDBObjectStoreMetadata::operator=(
+    const IndexedDBObjectStoreMetadata& other) = default;
 
 IndexedDBDatabaseMetadata::IndexedDBDatabaseMetadata()
     : int_version(NO_INT_VERSION) {}
+
 IndexedDBDatabaseMetadata::IndexedDBDatabaseMetadata(
     const base::string16& name,
     int64 id,
@@ -35,6 +65,12 @@
       int_version(int_version),
       max_object_store_id(max_object_store_id) {}
 
-IndexedDBDatabaseMetadata::~IndexedDBDatabaseMetadata() {}
+IndexedDBDatabaseMetadata::IndexedDBDatabaseMetadata(
+    const IndexedDBDatabaseMetadata& other) = default;
+
+IndexedDBDatabaseMetadata::~IndexedDBDatabaseMetadata() = default;
+
+IndexedDBDatabaseMetadata& IndexedDBDatabaseMetadata::operator=(
+    IndexedDBDatabaseMetadata& other) = default;
 
 }  // namespace content
diff --git a/content/browser/indexed_db/indexed_db_metadata.h b/content/browser/indexed_db/indexed_db_metadata.h
index a597616..e294868f 100644
--- a/content/browser/indexed_db/indexed_db_metadata.h
+++ b/content/browser/indexed_db/indexed_db_metadata.h
@@ -13,20 +13,19 @@
 
 namespace content {
 
-struct IndexedDBIndexMetadata {
+struct CONTENT_EXPORT IndexedDBIndexMetadata {
   static const int64 kInvalidId = -1;
 
-  IndexedDBIndexMetadata() {}
+  IndexedDBIndexMetadata();
   IndexedDBIndexMetadata(const base::string16& name,
                          int64 id,
                          const IndexedDBKeyPath& key_path,
                          bool unique,
-                         bool multi_entry)
-      : name(name),
-        id(id),
-        key_path(key_path),
-        unique(unique),
-        multi_entry(multi_entry) {}
+                         bool multi_entry);
+  IndexedDBIndexMetadata(const IndexedDBIndexMetadata& other);
+  ~IndexedDBIndexMetadata();
+  IndexedDBIndexMetadata& operator=(const IndexedDBIndexMetadata& other);
+
   base::string16 name;
   int64 id;
   IndexedDBKeyPath key_path;
@@ -45,7 +44,11 @@
                                const IndexedDBKeyPath& key_path,
                                bool auto_increment,
                                int64 max_index_id);
+  IndexedDBObjectStoreMetadata(const IndexedDBObjectStoreMetadata& other);
   ~IndexedDBObjectStoreMetadata();
+  IndexedDBObjectStoreMetadata& operator=(
+      const IndexedDBObjectStoreMetadata& other);
+
   base::string16 name;
   int64 id;
   IndexedDBKeyPath key_path;
@@ -70,7 +73,9 @@
                             const base::string16& version,
                             int64 int_version,
                             int64 max_object_store_id);
+  IndexedDBDatabaseMetadata(const IndexedDBDatabaseMetadata& other);
   ~IndexedDBDatabaseMetadata();
+  IndexedDBDatabaseMetadata& operator=(IndexedDBDatabaseMetadata& other);
 
   base::string16 name;
   int64 id;
diff --git a/content/browser/indexed_db/indexed_db_transaction.cc b/content/browser/indexed_db/indexed_db_transaction.cc
index 6609e024..88491de 100644
--- a/content/browser/indexed_db/indexed_db_transaction.cc
+++ b/content/browser/indexed_db/indexed_db_transaction.cc
@@ -15,6 +15,7 @@
 #include "content/browser/indexed_db/indexed_db_tracing.h"
 #include "content/browser/indexed_db/indexed_db_transaction_coordinator.h"
 #include "third_party/WebKit/public/platform/WebIDBDatabaseException.h"
+#include "third_party/leveldatabase/env_chromium.h"
 
 namespace content {
 
@@ -318,10 +319,17 @@
     while (!abort_task_stack_.empty())
       abort_task_stack_.pop().Run(NULL);
 
-    callbacks_->OnAbort(
-        id_,
-        IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionUnknownError,
-                               "Internal error committing transaction."));
+    IndexedDBDatabaseError error;
+    if (leveldb_env::IndicatesDiskFull(s)) {
+      error = IndexedDBDatabaseError(
+          blink::WebIDBDatabaseExceptionQuotaError,
+          "Encountered disk full while committing transaction.");
+    } else {
+      error = IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionUnknownError,
+                                     "Internal error committing transaction.");
+    }
+    callbacks_->OnAbort(id_, error);
+
     database_->TransactionFinished(this, false);
     database_->TransactionCommitFailed(s);
   }
diff --git a/content/browser/indexed_db/indexed_db_value.cc b/content/browser/indexed_db/indexed_db_value.cc
index 2843521..e8be18a 100644
--- a/content/browser/indexed_db/indexed_db_value.cc
+++ b/content/browser/indexed_db/indexed_db_value.cc
@@ -8,13 +8,16 @@
 
 namespace content {
 
-IndexedDBValue::IndexedDBValue() {}
+IndexedDBValue::IndexedDBValue() = default;
 IndexedDBValue::IndexedDBValue(
     const std::string& input_bits,
     const std::vector<IndexedDBBlobInfo>& input_blob_info)
     : bits(input_bits), blob_info(input_blob_info) {
   DCHECK(!input_blob_info.size() || input_bits.size());
 }
-IndexedDBValue::~IndexedDBValue() {}
+IndexedDBValue::IndexedDBValue(const IndexedDBValue& other) = default;
+IndexedDBValue::~IndexedDBValue() = default;
+IndexedDBValue& IndexedDBValue::operator=(const IndexedDBValue& other) =
+    default;
 
 }  // namespace content
diff --git a/content/browser/indexed_db/indexed_db_value.h b/content/browser/indexed_db/indexed_db_value.h
index d313038d..7b2377a 100644
--- a/content/browser/indexed_db/indexed_db_value.h
+++ b/content/browser/indexed_db/indexed_db_value.h
@@ -18,7 +18,9 @@
   IndexedDBValue();
   IndexedDBValue(const std::string& input_bits,
                  const std::vector<IndexedDBBlobInfo>& input_blob_info);
+  IndexedDBValue(const IndexedDBValue& other);
   ~IndexedDBValue();
+  IndexedDBValue& operator=(const IndexedDBValue& other);
 
   void swap(IndexedDBValue& value) {
     bits.swap(value.bits);
diff --git a/content/browser/indexed_db/leveldb/leveldb_database.cc b/content/browser/indexed_db/leveldb/leveldb_database.cc
index 51a8aea..c78ec0d 100644
--- a/content/browser/indexed_db/leveldb/leveldb_database.cc
+++ b/content/browser/indexed_db/leveldb/leveldb_database.cc
@@ -103,7 +103,12 @@
   options.create_if_missing = true;
   options.paranoid_checks = true;
   options.filter_policy = filter_policy->get();
+#if defined(OS_CHROMEOS)
+  // Disabled on CrOS until crbug.com/460568 is fixed.
+  options.reuse_logs = false;
+#else
   options.reuse_logs = true;
+#endif
   options.compression = leveldb::kSnappyCompression;
 
   // For info about the troubles we've run into with this parameter, see:
@@ -183,7 +188,7 @@
 static void ParseAndHistogramIOErrorDetails(const std::string& histogram_name,
                                             const leveldb::Status& s) {
   leveldb_env::MethodID method;
-  int error = -1;
+  base::File::Error error = base::File::FILE_OK;
   leveldb_env::ErrorParsingResult result =
       leveldb_env::ParseMethodAndError(s, &method, &error);
   if (result == leveldb_env::NONE)
@@ -209,15 +214,6 @@
         -base::File::FILE_ERROR_MAX,
         -base::File::FILE_ERROR_MAX + 1,
         base::HistogramBase::kUmaTargetedHistogramFlag)->Add(-error);
-  } else if (result == leveldb_env::METHOD_AND_ERRNO) {
-    error_histogram_name.append(std::string(".Errno.") +
-                                leveldb_env::MethodIDToString(method));
-    base::LinearHistogram::FactoryGet(
-        error_histogram_name,
-        1,
-        ERANGE + 1,
-        ERANGE + 2,
-        base::HistogramBase::kUmaTargetedHistogramFlag)->Add(error);
   }
 }
 
diff --git a/content/browser/indexed_db/mock_browsertest_indexed_db_class_factory.cc b/content/browser/indexed_db/mock_browsertest_indexed_db_class_factory.cc
index b447429..5cade12b 100644
--- a/content/browser/indexed_db/mock_browsertest_indexed_db_class_factory.cc
+++ b/content/browser/indexed_db/mock_browsertest_indexed_db_class_factory.cc
@@ -8,6 +8,7 @@
 #include "content/browser/indexed_db/leveldb/leveldb_iterator_impl.h"
 #include "content/browser/indexed_db/leveldb/leveldb_transaction.h"
 #include "content/browser/indexed_db/mock_browsertest_indexed_db_class_factory.h"
+#include "third_party/leveldatabase/env_chromium.h"
 #include "third_party/leveldatabase/src/include/leveldb/status.h"
 
 namespace {
@@ -64,10 +65,18 @@
   }
 
   leveldb::Status Commit() override {
-    if (fail_method_ != FAIL_METHOD_COMMIT ||
+    if ((fail_method_ != FAIL_METHOD_COMMIT &&
+         fail_method_ != FAIL_METHOD_COMMIT_DISK_FULL) ||
         ++current_call_num_ != fail_on_call_num_)
       return LevelDBTransaction::Commit();
 
+    // TODO(jsbell): Consider parameterizing the failure mode.
+    if (fail_method_ == FAIL_METHOD_COMMIT_DISK_FULL) {
+      return leveldb_env::MakeIOError("dummy filename", "Disk Full",
+                                      leveldb_env::kWritableFileAppend,
+                                      base::File::FILE_ERROR_NO_SPACE);
+    }
+
     return leveldb::Status::Corruption("Corrupted for the test");
   }
 
diff --git a/content/browser/indexed_db/mock_browsertest_indexed_db_class_factory.h b/content/browser/indexed_db/mock_browsertest_indexed_db_class_factory.h
index 9e40b60..6e6f6ae0 100644
--- a/content/browser/indexed_db/mock_browsertest_indexed_db_class_factory.h
+++ b/content/browser/indexed_db/mock_browsertest_indexed_db_class_factory.h
@@ -23,6 +23,7 @@
 enum FailMethod {
   FAIL_METHOD_NOTHING,
   FAIL_METHOD_COMMIT,
+  FAIL_METHOD_COMMIT_DISK_FULL,
   FAIL_METHOD_GET,
   FAIL_METHOD_SEEK,
 };
diff --git a/content/browser/loader/resource_dispatcher_host_impl.cc b/content/browser/loader/resource_dispatcher_host_impl.cc
index 85bbcc724..6c4da44 100644
--- a/content/browser/loader/resource_dispatcher_host_impl.cc
+++ b/content/browser/loader/resource_dispatcher_host_impl.cc
@@ -977,10 +977,6 @@
     int routing_id,
     int request_id,
     const ResourceHostMsg_Request& request_data) {
-  // TODO(pkasting): Remove ScopedTracker below once crbug.com/456331 is fixed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "456331 ResourceDispatcherHostImpl::OnRequestResource"));
   // When logging time-to-network only care about main frame and non-transfer
   // navigations.
   if (request_data.resource_type == RESOURCE_TYPE_MAIN_FRAME &&
@@ -1096,10 +1092,6 @@
     const ResourceHostMsg_Request& request_data,
     IPC::Message* sync_result,  // only valid for sync
     int route_id) {
-  // TODO(pkasting): Remove ScopedTracker below once crbug.com/456331 is fixed.
-  tracked_objects::ScopedTracker tracking_profile1(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "456331 ResourceDispatcherHostImpl::BeginRequest1"));
   int process_type = filter_->process_type();
   int child_id = filter_->child_id();
 
@@ -1119,36 +1111,25 @@
 
   // If the request that's coming in is being transferred from another process,
   // we want to reuse and resume the old loader rather than start a new one.
-  {
-    // TODO(pkasting): Remove ScopedTracker below once crbug.com/456331 is
-    // fixed.
-    tracked_objects::ScopedTracker tracking_profile2(
-        FROM_HERE_WITH_EXPLICIT_FUNCTION(
-            "456331 ResourceDispatcherHostImpl::BeginRequest2"));
-    LoaderMap::iterator it = pending_loaders_.find(
-        GlobalRequestID(request_data.transferred_request_child_id,
-                        request_data.transferred_request_request_id));
-    if (it != pending_loaders_.end()) {
-      // If the request is transferring to a new process, we can update our
-      // state and let it resume with its existing ResourceHandlers.
-      if (it->second->is_transferring()) {
-        linked_ptr<ResourceLoader> deferred_loader = it->second;
-        UpdateRequestForTransfer(child_id, route_id, request_id,
-                                 request_data, deferred_loader);
+  LoaderMap::iterator it = pending_loaders_.find(
+      GlobalRequestID(request_data.transferred_request_child_id,
+                      request_data.transferred_request_request_id));
+  if (it != pending_loaders_.end()) {
+    // If the request is transferring to a new process, we can update our
+    // state and let it resume with its existing ResourceHandlers.
+    if (it->second->is_transferring()) {
+      linked_ptr<ResourceLoader> deferred_loader = it->second;
+      UpdateRequestForTransfer(child_id, route_id, request_id,
+                                request_data, deferred_loader);
 
-        deferred_loader->CompleteTransfer();
-      } else {
-        RecordAction(base::UserMetricsAction("BadMessageTerminate_RDH"));
-        filter_->BadMessageReceived();
-      }
-      return;
+      deferred_loader->CompleteTransfer();
+    } else {
+      RecordAction(base::UserMetricsAction("BadMessageTerminate_RDH"));
+      filter_->BadMessageReceived();
     }
+    return;
   }
 
-  // TODO(pkasting): Remove ScopedTracker below once crbug.com/456331 is fixed.
-  tracked_objects::ScopedTracker tracking_profile3(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "456331 ResourceDispatcherHostImpl::BeginRequest3"));
   ResourceContext* resource_context = NULL;
   net::URLRequestContext* request_context = NULL;
   filter_->GetContexts(request_data, &resource_context, &request_context);
@@ -1171,10 +1152,6 @@
     return;
   }
 
-  // TODO(pkasting): Remove ScopedTracker below once crbug.com/456331 is fixed.
-  tracked_objects::ScopedTracker tracking_profile4(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "456331 ResourceDispatcherHostImpl::BeginRequest4"));
   // Construct the request.
   net::CookieStore* cookie_store =
       GetContentClient()->browser()->OverrideCookieStoreForRenderProcess(
@@ -1201,10 +1178,6 @@
   headers.AddHeadersFromString(request_data.headers);
   new_request->SetExtraRequestHeaders(headers);
 
-  // TODO(pkasting): Remove ScopedTracker below once crbug.com/456331 is fixed.
-  tracked_objects::ScopedTracker tracking_profile5(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "456331 ResourceDispatcherHostImpl::BeginRequest5"));
   storage::BlobStorageContext* blob_context =
       GetBlobStorageContext(filter_->blob_storage_context());
   // Resolve elements from request_body and prepare upload data.
@@ -1228,10 +1201,6 @@
             .get()));
   }
 
-  // TODO(pkasting): Remove ScopedTracker below once crbug.com/456331 is fixed.
-  tracked_objects::ScopedTracker tracking_profile6(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "456331 ResourceDispatcherHostImpl::BeginRequest6"));
   bool allow_download = request_data.allow_download &&
       IsResourceTypeFrame(request_data.resource_type);
   bool do_not_prompt_for_login = request_data.do_not_prompt_for_login;
@@ -1307,10 +1276,6 @@
             new_request->url()));
   }
 
-  // TODO(pkasting): Remove ScopedTracker below once crbug.com/456331 is fixed.
-  tracked_objects::ScopedTracker tracking_profile7(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "456331 ResourceDispatcherHostImpl::BeginRequest7"));
   // Initialize the service worker handler for the request. We don't use
   // ServiceWorker for synchronous loads to avoid renderer deadlocks.
   ServiceWorkerRequestHandler::InitializeHandler(
@@ -1351,6 +1316,10 @@
     int process_type,
     int child_id,
     ResourceContext* resource_context) {
+  // TODO(pkasting): Remove ScopedTracker below once crbug.com/456331 is fixed.
+  tracked_objects::ScopedTracker tracking_profile(
+      FROM_HERE_WITH_EXPLICIT_FUNCTION(
+          "456331 ResourceDispatcherHostImpl::CreateResourceHandler"));
   // Construct the IPC resource handler.
   scoped_ptr<ResourceHandler> handler;
   if (sync_result) {
@@ -2065,9 +2034,9 @@
     scoped_ptr<net::URLRequest> request,
     scoped_ptr<ResourceHandler> handler) {
   // TODO(pkasting): Remove ScopedTracker below once crbug.com/456331 is fixed.
-  tracked_objects::ScopedTracker tracking_profile(
+  tracked_objects::ScopedTracker tracking_profile1(
       FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "456331 ResourceDispatcherHostImpl::BeginRequestInternal"));
+          "456331 ResourceDispatcherHostImpl::BeginRequestInternal1"));
   DCHECK(!request->is_pending());
   ResourceRequestInfoImpl* info =
       ResourceRequestInfoImpl::ForRequest(request.get());
@@ -2085,6 +2054,11 @@
   // bound, abort it right away.
   OustandingRequestsStats stats = IncrementOutstandingRequestsMemory(1, *info);
   if (stats.memory_cost > max_outstanding_requests_cost_per_process_) {
+    // TODO(pkasting): Remove ScopedTracker below once crbug.com/456331 is
+    // fixed.
+    tracked_objects::ScopedTracker tracking_profile2(
+        FROM_HERE_WITH_EXPLICIT_FUNCTION(
+            "456331 ResourceDispatcherHostImpl::BeginRequestInternal2"));
     // We call "CancelWithError()" as a way of setting the net::URLRequest's
     // status -- it has no effect beyond this, since the request hasn't started.
     request->CancelWithError(net::ERR_INSUFFICIENT_RESOURCES);
@@ -2103,6 +2077,10 @@
     return;
   }
 
+  // TODO(pkasting): Remove ScopedTracker below once crbug.com/456331 is fixed.
+  tracked_objects::ScopedTracker tracking_profile3(
+      FROM_HERE_WITH_EXPLICIT_FUNCTION(
+          "456331 ResourceDispatcherHostImpl::BeginRequestInternal3"));
   linked_ptr<ResourceLoader> loader(
       new ResourceLoader(request.Pass(), handler.Pass(), this));
 
@@ -2120,6 +2098,10 @@
 void ResourceDispatcherHostImpl::StartLoading(
     ResourceRequestInfoImpl* info,
     const linked_ptr<ResourceLoader>& loader) {
+  // TODO(pkasting): Remove ScopedTracker below once crbug.com/456331 is fixed.
+  tracked_objects::ScopedTracker tracking_profile(
+      FROM_HERE_WITH_EXPLICIT_FUNCTION(
+          "456331 ResourceDispatcherHostImpl::StartLoading"));
   pending_loaders_[info->GetGlobalRequestID()] = loader;
 
   loader->StartRequest();
@@ -2183,13 +2165,35 @@
   for (const auto& loader : pending_loaders_) {
     // Also poll for upload progress on this timer and send upload progress ipc
     // messages to the plugin process.
-    loader.second->ReportUploadProgress();
+    {
+      // TODO(pkasting): Remove ScopedTracker below once crbug.com/455952 is
+      // fixed.
+      tracked_objects::ScopedTracker tracking_profile1(
+          FROM_HERE_WITH_EXPLICIT_FUNCTION(
+              "455952 ResourceDispatcherHostImpl::GetLoadInfoForAllRoutes1"));
+      loader.second->ReportUploadProgress();
+    }
 
     net::URLRequest* request = loader.second->request();
-    net::UploadProgress upload_progress = request->GetUploadProgress();
+    net::UploadProgress upload_progress;
+    {
+      // TODO(pkasting): Remove ScopedTracker below once crbug.com/455952 is
+      // fixed.
+      tracked_objects::ScopedTracker tracking_profile2(
+          FROM_HERE_WITH_EXPLICIT_FUNCTION(
+              "455952 ResourceDispatcherHostImpl::GetLoadInfoForAllRoutes2"));
+      upload_progress = request->GetUploadProgress();
+    }
     LoadInfo load_info;
     load_info.url = request->url();
-    load_info.load_state = request->GetLoadState();
+    {
+      // TODO(pkasting): Remove ScopedTracker below once crbug.com/455952 is
+      // fixed.
+      tracked_objects::ScopedTracker tracking_profile3(
+          FROM_HERE_WITH_EXPLICIT_FUNCTION(
+              "455952 ResourceDispatcherHostImpl::GetLoadInfoForAllRoutes3"));
+      load_info.load_state = request->GetLoadState();
+    }
     load_info.upload_size = upload_progress.size();
     load_info.upload_position = upload_progress.position();
 
@@ -2208,7 +2212,7 @@
   // fixed.
   tracked_objects::ScopedTracker tracking_profile(
       FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "455952 ResourceDispatcherHostImpl::UpdateLoadStates"));
+          "455952 ResourceDispatcherHostImpl::UpdateLoadInfo"));
 
   scoped_ptr<LoadInfoMap> info_map(GetLoadInfoForAllRoutes());
 
diff --git a/content/browser/loader/resource_loader.cc b/content/browser/loader/resource_loader.cc
index f1157bda..b462a68 100644
--- a/content/browser/loader/resource_loader.cc
+++ b/content/browser/loader/resource_loader.cc
@@ -146,11 +146,6 @@
 }
 
 void ResourceLoader::ReportUploadProgress() {
-  // TODO(pkasting): Remove ScopedTracker below once crbug.com/455952 is
-  // fixed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "455952 ResourceLoader::ReportUploadProgress"));
   if (waiting_for_upload_progress_ack_)
     return;  // Send one progress event at a time.
 
diff --git a/content/browser/media/android/media_drm_credential_manager.cc b/content/browser/media/android/media_drm_credential_manager.cc
index 70fc967b..e419b04 100644
--- a/content/browser/media/android/media_drm_credential_manager.cc
+++ b/content/browser/media/android/media_drm_credential_manager.cc
@@ -87,6 +87,7 @@
   media_drm_bridge_.reset();
 }
 
+// TODO(ddorwin): The key system should be passed in. http://crbug.com/459400
 bool MediaDrmCredentialManager::ResetCredentialsInternal(
     SecurityLevel security_level) {
   media_drm_bridge_ =
diff --git a/content/browser/media/android/media_drm_credential_manager.h b/content/browser/media/android/media_drm_credential_manager.h
index f2cb0fa1..cf16cea 100644
--- a/content/browser/media/android/media_drm_credential_manager.h
+++ b/content/browser/media/android/media_drm_credential_manager.h
@@ -22,6 +22,10 @@
   typedef base::Callback<void(bool)> ResetCredentialsCB;
 
   // Called to reset the DRM credentials. (for Java)
+  // Only clears credentials for Widevine.
+  // TODO(ddorwin): This should accept a key system parameter so that this is
+  // clear to the caller, which can call it repeatedly as necessary.
+  // http://crbug.com/459400
   static void ResetCredentials(JNIEnv* env, jclass clazz, jobject callback);
 
   // Called to reset the DRM credentials. The result is returned in the
diff --git a/content/browser/media/capture/desktop_capture_device_aura_unittest.cc b/content/browser/media/capture/desktop_capture_device_aura_unittest.cc
index 6fcd680c..ff540ad 100644
--- a/content/browser/media/capture/desktop_capture_device_aura_unittest.cc
+++ b/content/browser/media/capture/desktop_capture_device_aura_unittest.cc
@@ -35,21 +35,21 @@
 
 class MockDeviceClient : public media::VideoCaptureDevice::Client {
  public:
-  MOCK_METHOD2(ReserveOutputBuffer,
-               scoped_refptr<Buffer>(media::VideoFrame::Format format,
-                                     const gfx::Size& dimensions));
-  MOCK_METHOD1(OnError, void(const std::string& reason));
   MOCK_METHOD5(OnIncomingCapturedData,
                void(const uint8* data,
                     int length,
                     const media::VideoCaptureFormat& frame_format,
                     int rotation,
-                    base::TimeTicks timestamp));
+                    const base::TimeTicks& timestamp));
+  MOCK_METHOD2(ReserveOutputBuffer,
+               scoped_refptr<Buffer>(media::VideoFrame::Format format,
+                                     const gfx::Size& dimensions));
   MOCK_METHOD4(OnIncomingCapturedVideoFrame,
                void(const scoped_refptr<Buffer>& buffer,
                     const media::VideoCaptureFormat& buffer_format,
                     const scoped_refptr<media::VideoFrame>& frame,
-                    base::TimeTicks timestamp));
+                    const base::TimeTicks& timestamp));
+  MOCK_METHOD1(OnError, void(const std::string& reason));
 };
 
 // Test harness that sets up a minimal environment with necessary stubs.
diff --git a/content/browser/media/capture/desktop_capture_device_unittest.cc b/content/browser/media/capture/desktop_capture_device_unittest.cc
index d0365cf..842b57e 100644
--- a/content/browser/media/capture/desktop_capture_device_unittest.cc
+++ b/content/browser/media/capture/desktop_capture_device_unittest.cc
@@ -53,21 +53,21 @@
 
 class MockDeviceClient : public media::VideoCaptureDevice::Client {
  public:
-  MOCK_METHOD2(ReserveOutputBuffer,
-               scoped_refptr<Buffer>(media::VideoFrame::Format format,
-                                     const gfx::Size& dimensions));
-  MOCK_METHOD1(OnError, void(const std::string& reason));
   MOCK_METHOD5(OnIncomingCapturedData,
                void(const uint8* data,
                     int length,
                     const media::VideoCaptureFormat& frame_format,
                     int rotation,
-                    base::TimeTicks timestamp));
+                    const base::TimeTicks& timestamp));
+  MOCK_METHOD2(ReserveOutputBuffer,
+               scoped_refptr<Buffer>(media::VideoFrame::Format format,
+                                     const gfx::Size& dimensions));
   MOCK_METHOD4(OnIncomingCapturedVideoFrame,
                void(const scoped_refptr<Buffer>& buffer,
                     const media::VideoCaptureFormat& buffer_format,
                     const scoped_refptr<media::VideoFrame>& frame,
-                    base::TimeTicks timestamp));
+                    const base::TimeTicks& timestamp));
+  MOCK_METHOD1(OnError, void(const std::string& reason));
 };
 
 // Creates a DesktopFrame that has the first pixel bytes set to
diff --git a/content/browser/media/capture/web_contents_video_capture_device_unittest.cc b/content/browser/media/capture/web_contents_video_capture_device_unittest.cc
index 3e506fec..b71d47c5b 100644
--- a/content/browser/media/capture/web_contents_video_capture_device_unittest.cc
+++ b/content/browser/media/capture/web_contents_video_capture_device_unittest.cc
@@ -315,6 +315,14 @@
   }
   ~StubClient() override {}
 
+  void OnIncomingCapturedData(const uint8* data,
+                              int length,
+                              const media::VideoCaptureFormat& frame_format,
+                              int rotation,
+                              const base::TimeTicks& timestamp) override {
+    FAIL();
+  }
+
   scoped_refptr<media::VideoCaptureDevice::Client::Buffer> ReserveOutputBuffer(
       media::VideoFrame::Format format,
       const gfx::Size& dimensions) override {
@@ -330,22 +338,14 @@
     size_t size;
     buffer_pool_->GetBufferInfo(buffer_id, &data, &size);
     return scoped_refptr<media::VideoCaptureDevice::Client::Buffer>(
-        new PoolBuffer(buffer_pool_, buffer_id, data, size));
-  }
-
-  void OnIncomingCapturedData(const uint8* data,
-                              int length,
-                              const media::VideoCaptureFormat& frame_format,
-                              int rotation,
-                              base::TimeTicks timestamp) override {
-    FAIL();
+        new AutoReleaseBuffer(buffer_pool_, buffer_id, data, size));
   }
 
   void OnIncomingCapturedVideoFrame(
       const scoped_refptr<Buffer>& buffer,
       const media::VideoCaptureFormat& buffer_format,
       const scoped_refptr<media::VideoFrame>& frame,
-      base::TimeTicks timestamp) override {
+      const base::TimeTicks& timestamp) override {
     EXPECT_EQ(gfx::Size(kTestWidth, kTestHeight), buffer_format.frame_size);
     EXPECT_EQ(media::PIXEL_FORMAT_I420, buffer_format.pixel_format);
     EXPECT_EQ(media::VideoFrame::I420, frame->format());
@@ -361,17 +361,29 @@
   void OnError(const std::string& reason) override { error_callback_.Run(); }
 
  private:
-  class PoolBuffer : public media::VideoCaptureDevice::Client::Buffer {
+  class AutoReleaseBuffer : public media::VideoCaptureDevice::Client::Buffer {
    public:
-    PoolBuffer(const scoped_refptr<VideoCaptureBufferPool>& pool,
+    AutoReleaseBuffer(const scoped_refptr<VideoCaptureBufferPool>& pool,
                int buffer_id,
                void* data,
                size_t size)
-        : Buffer(buffer_id, data, size), pool_(pool) {}
+        : pool_(pool),
+          id_(buffer_id),
+          data_(data),
+          size_(size) {
+      DCHECK(pool_.get());
+    }
+    int id() const override { return id_; }
+    void* data() const override { return data_; }
+    size_t size() const override { return size_; }
 
    private:
-    ~PoolBuffer() override { pool_->RelinquishProducerReservation(id()); }
+    ~AutoReleaseBuffer() override { pool_->RelinquishProducerReservation(id_); }
+
     const scoped_refptr<VideoCaptureBufferPool> pool_;
+    const int id_;
+    void* const data_;
+    const size_t size_;
   };
 
   scoped_refptr<VideoCaptureBufferPool> buffer_pool_;
diff --git a/content/browser/media/midi_host.cc b/content/browser/media/midi_host.cc
index 37e5411..b8fcdd0b 100644
--- a/content/browser/media/midi_host.cc
+++ b/content/browser/media/midi_host.cc
@@ -145,6 +145,14 @@
   Send(new MidiMsg_AddOutputPort(info));
 }
 
+void MidiHost::SetInputPortState(uint32 port, media::MidiPortState state) {
+  Send(new MidiMsg_SetInputPortState(port, state));
+}
+
+void MidiHost::SetOutputPortState(uint32 port, media::MidiPortState state) {
+  Send(new MidiMsg_SetOutputPortState(port, state));
+}
+
 void MidiHost::ReceiveMidiData(
     uint32 port,
     const uint8* data,
diff --git a/content/browser/media/midi_host.h b/content/browser/media/midi_host.h
index 3a469bf93b..9d9c01f 100644
--- a/content/browser/media/midi_host.h
+++ b/content/browser/media/midi_host.h
@@ -16,6 +16,7 @@
 #include "content/public/browser/browser_message_filter.h"
 #include "content/public/browser/browser_thread.h"
 #include "media/midi/midi_manager.h"
+#include "media/midi/midi_port_info.h"
 
 namespace media {
 class MidiManager;
@@ -39,6 +40,8 @@
   void CompleteStartSession(media::MidiResult result) override;
   void AddInputPort(const media::MidiPortInfo& info) override;
   void AddOutputPort(const media::MidiPortInfo& info) override;
+  void SetInputPortState(uint32 port, media::MidiPortState state) override;
+  void SetOutputPortState(uint32 port, media::MidiPortState state) override;
   void ReceiveMidiData(uint32 port,
                        const uint8* data,
                        size_t length,
diff --git a/content/browser/message_port_message_filter.cc b/content/browser/message_port_message_filter.cc
index c263344..97c72b4 100644
--- a/content/browser/message_port_message_filter.cc
+++ b/content/browser/message_port_message_filter.cc
@@ -6,6 +6,7 @@
 
 #include "content/browser/message_port_service.h"
 #include "content/common/message_port_messages.h"
+#include "content/common/view_messages.h"
 
 namespace content {
 
@@ -92,6 +93,16 @@
   }
 }
 
+void MessagePortMessageFilter::RouteMessageEventWithMessagePorts(
+    int routing_id,
+    const ViewMsg_PostMessage_Params& params) {
+  ViewMsg_PostMessage_Params new_params(params);
+  UpdateMessagePortsWithNewRoutes(params.message_port_ids,
+                                  &new_params.new_routing_ids);
+  Send(new ViewMsg_PostMessageEvent(routing_id, new_params));
+}
+
+
 void MessagePortMessageFilter::OnCreateMessagePort(int *route_id,
                                                    int* message_port_id) {
   *route_id = next_routing_id_.Run();
diff --git a/content/browser/message_port_message_filter.h b/content/browser/message_port_message_filter.h
index 5e0286a..ec6fba0 100644
--- a/content/browser/message_port_message_filter.h
+++ b/content/browser/message_port_message_filter.h
@@ -15,6 +15,8 @@
 #undef SendMessage
 #endif
 
+struct ViewMsg_PostMessage_Params;
+
 namespace content {
 
 // Filter for MessagePort related IPC messages (creating and destroying a
@@ -48,6 +50,10 @@
       const std::vector<int>& message_port_ids,
       std::vector<int>* new_routing_ids);
 
+  void RouteMessageEventWithMessagePorts(
+      int routing_id,
+      const ViewMsg_PostMessage_Params& params);
+
  protected:
   // This is protected, so we can define sub classes for testing.
   ~MessagePortMessageFilter() override;
diff --git a/content/browser/message_port_service.cc b/content/browser/message_port_service.cc
index 382f5b6..9ee4d70 100644
--- a/content/browser/message_port_service.cc
+++ b/content/browser/message_port_service.cc
@@ -63,6 +63,7 @@
 void MessagePortService::UpdateMessagePort(int message_port_id,
                                            MessagePortDelegate* delegate,
                                            int routing_id) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
   if (!message_ports_.count(message_port_id)) {
     NOTREACHED();
     return;
@@ -75,6 +76,7 @@
 
 void MessagePortService::OnMessagePortDelegateClosing(
     MessagePortDelegate* delegate) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
   // Check if the (possibly) crashed process had any message ports.
   for (MessagePorts::iterator iter = message_ports_.begin();
        iter != message_ports_.end();) {
@@ -88,7 +90,7 @@
 void MessagePortService::Create(int route_id,
                                 MessagePortDelegate* delegate,
                                 int* message_port_id) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
   *message_port_id = ++next_message_port_id_;
 
   MessagePort port;
@@ -103,6 +105,7 @@
 }
 
 void MessagePortService::Destroy(int message_port_id) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
   if (!message_ports_.count(message_port_id)) {
     NOTREACHED();
     return;
@@ -115,6 +118,7 @@
 
 void MessagePortService::Entangle(int local_message_port_id,
                                   int remote_message_port_id) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
   if (!message_ports_.count(local_message_port_id) ||
       !message_ports_.count(remote_message_port_id)) {
     NOTREACHED();
@@ -131,6 +135,7 @@
     int sender_message_port_id,
     const base::string16& message,
     const std::vector<int>& sent_message_port_ids) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
   if (!message_ports_.count(sender_message_port_id)) {
     NOTREACHED();
     return;
@@ -194,6 +199,7 @@
 }
 
 void MessagePortService::QueueMessages(int message_port_id) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
   if (!message_ports_.count(message_port_id)) {
     NOTREACHED();
     return;
@@ -210,6 +216,7 @@
 void MessagePortService::SendQueuedMessages(
     int message_port_id,
     const QueuedMessages& queued_messages) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
   if (!message_ports_.count(message_port_id)) {
     NOTREACHED();
     return;
@@ -239,6 +246,7 @@
 }
 
 void MessagePortService::SendQueuedMessagesIfPossible(int message_port_id) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
   if (!message_ports_.count(message_port_id)) {
     NOTREACHED();
     return;
@@ -256,6 +264,7 @@
 }
 
 void MessagePortService::HoldMessages(int message_port_id) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
   if (!message_ports_.count(message_port_id)) {
     NOTREACHED();
     return;
@@ -270,6 +279,7 @@
 }
 
 void MessagePortService::ClosePort(int message_port_id) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
   if (!message_ports_.count(message_port_id)) {
     NOTREACHED();
     return;
@@ -289,6 +299,7 @@
 }
 
 void MessagePortService::ReleaseMessages(int message_port_id) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
   if (!message_ports_.count(message_port_id)) {
     NOTREACHED();
     return;
diff --git a/content/browser/net/sqlite_persistent_cookie_store.cc b/content/browser/net/sqlite_persistent_cookie_store.cc
index c1eb1cc4..cb2f98ea 100644
--- a/content/browser/net/sqlite_persistent_cookie_store.cc
+++ b/content/browser/net/sqlite_persistent_cookie_store.cc
@@ -21,7 +21,6 @@
 #include "base/memory/scoped_ptr.h"
 #include "base/metrics/field_trial.h"
 #include "base/metrics/histogram.h"
-#include "base/profiler/scoped_tracker.h"
 #include "base/sequenced_task_runner.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
@@ -508,11 +507,6 @@
 void SQLitePersistentCookieStore::Backend::CompleteLoadForKeyInForeground(
     const LoadedCallback& loaded_callback,
     bool load_success) {
-  // TODO(pkasting): Remove ScopedTracker below once crbug.com/456373 is fixed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "456373 SQLitePersistentCookieStore::Backend::"
-          "CompleteLoadForKeyInForeground"));
   DCHECK(client_task_runner_->RunsTasksOnCurrentThread());
 
   Notify(loaded_callback, load_success);
@@ -537,10 +531,6 @@
 }
 
 void SQLitePersistentCookieStore::Backend::ReportMetrics() {
-  // TODO(pkasting): Remove ScopedTracker below once crbug.com/457528 is fixed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "457528 SQLitePersistentCookieStore::Backend::ReportMetrics"));
   PostBackgroundTask(FROM_HERE, base::Bind(
       &SQLitePersistentCookieStore::Backend::ReportMetricsInBackground, this));
 
diff --git a/content/browser/plugin_service_impl.cc b/content/browser/plugin_service_impl.cc
index 42ac63b..e431095 100644
--- a/content/browser/plugin_service_impl.cc
+++ b/content/browser/plugin_service_impl.cc
@@ -190,15 +190,6 @@
 
   if (command_line->HasSwitch(switches::kDisablePluginsDiscovery))
     PluginList::Singleton()->DisablePluginsDiscovery();
-#if defined(OS_WIN) || defined(OS_MACOSX)
-  npapi_plugins_enabled_ = command_line->HasSwitch(switches::kEnableNpapi);
-  NPAPIPluginStatus status =
-      npapi_plugins_enabled_ ? NPAPI_STATUS_ENABLED : NPAPI_STATUS_DISABLED;
-#else
-  NPAPIPluginStatus status = NPAPI_STATUS_UNSUPPORTED;
-#endif
-  UMA_HISTOGRAM_ENUMERATION("Plugin.NPAPIStatus", status,
-                            NPAPI_STATUS_ENUM_COUNT);
 }
 
 void PluginServiceImpl::StartWatchingPlugins() {
@@ -779,8 +770,9 @@
 void PluginServiceImpl::RegisterInternalPlugin(
     const WebPluginInfo& info,
     bool add_at_beginning) {
-  if (!NPAPIPluginsSupported() &&
-      info.type == WebPluginInfo::PLUGIN_TYPE_NPAPI) {
+  // Internal plugins should never be NPAPI.
+  CHECK_NE(info.type, WebPluginInfo::PLUGIN_TYPE_NPAPI);
+  if (info.type == WebPluginInfo::PLUGIN_TYPE_NPAPI) {
     DVLOG(0) << "Don't register NPAPI plugins when they're not supported";
     return;
   }
@@ -797,6 +789,22 @@
 }
 
 bool PluginServiceImpl::NPAPIPluginsSupported() {
+  static bool command_line_checked = false;
+
+  if (!command_line_checked) {
+#if defined(OS_WIN) || defined(OS_MACOSX)
+    const base::CommandLine* command_line =
+        base::CommandLine::ForCurrentProcess();
+    npapi_plugins_enabled_ = command_line->HasSwitch(switches::kEnableNpapi);
+    NPAPIPluginStatus status =
+        npapi_plugins_enabled_ ? NPAPI_STATUS_ENABLED : NPAPI_STATUS_DISABLED;
+#else
+    NPAPIPluginStatus status = NPAPI_STATUS_UNSUPPORTED;
+#endif
+    UMA_HISTOGRAM_ENUMERATION("Plugin.NPAPIStatus", status,
+        NPAPI_STATUS_ENUM_COUNT);
+  }
+
   return npapi_plugins_enabled_;
 }
 
diff --git a/content/browser/ppapi_plugin_process_host.cc b/content/browser/ppapi_plugin_process_host.cc
index 608d6c9..480296f 100644
--- a/content/browser/ppapi_plugin_process_host.cc
+++ b/content/browser/ppapi_plugin_process_host.cc
@@ -199,6 +199,24 @@
 }
 
 // static
+void PpapiPluginProcessHost::OnPluginInstanceThrottleStateChange(
+    int plugin_process_id,
+    int32 pp_instance,
+    bool is_throttled) {
+  for (PpapiPluginProcessHostIterator iter; !iter.Done(); ++iter) {
+    if (iter->process_.get() &&
+        iter->process_->GetData().id == plugin_process_id) {
+      // Found the plugin.
+      iter->host_impl_->OnThrottleStateChanged(pp_instance, is_throttled);
+      return;
+    }
+  }
+  // Note: It's possible that the plugin process has already been deleted by
+  // the time this message is received. For example, it could have crashed.
+  // That's OK, we can just ignore this message.
+}
+
+// static
 void PpapiPluginProcessHost::FindByName(
     const base::string16& name,
     std::vector<PpapiPluginProcessHost*>* hosts) {
diff --git a/content/browser/ppapi_plugin_process_host.h b/content/browser/ppapi_plugin_process_host.h
index 3f90d302..e4c58a9b 100644
--- a/content/browser/ppapi_plugin_process_host.h
+++ b/content/browser/ppapi_plugin_process_host.h
@@ -90,6 +90,11 @@
   static void DidDeleteOutOfProcessInstance(int plugin_process_id,
                                             int32 pp_instance);
 
+  // Notification that a Plugin instance has been throttled or unthrottled.
+  static void OnPluginInstanceThrottleStateChange(int plugin_process_id,
+                                                  int32 pp_instance,
+                                                  bool is_throttled);
+
   // Returns the instances that match the specified process name.
   // It can only be called on the IO thread.
   static void FindByName(const base::string16& name,
diff --git a/content/browser/push_messaging/push_messaging_message_filter.cc b/content/browser/push_messaging/push_messaging_message_filter.cc
index 8e151d7..7aaa7903 100644
--- a/content/browser/push_messaging/push_messaging_message_filter.cc
+++ b/content/browser/push_messaging/push_messaging_message_filter.cc
@@ -7,6 +7,7 @@
 #include <string>
 
 #include "base/bind.h"
+#include "base/bind_helpers.h"
 #include "base/logging.h"
 #include "base/metrics/histogram.h"
 #include "base/strings/string_number_conversions.h"
@@ -19,49 +20,171 @@
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/push_messaging_service.h"
 #include "content/public/common/child_process_host.h"
+#include "content/public/common/push_messaging_status.h"
 #include "third_party/WebKit/public/platform/modules/push_messaging/WebPushPermissionStatus.h"
 
 namespace content {
+
+const char kPushSenderIdServiceWorkerKey[] = "push_sender_id";
+const char kPushRegistrationIdServiceWorkerKey[] = "push_registration_id";
+
 namespace {
 
+// These UMA methods are only called from IO thread, but it would be acceptable
+// (even though slightly racy) to call them from UI thread as well, see
+// https://groups.google.com/a/chromium.org/d/msg/chromium-dev/FNzZRJtN2aw/Aw0CWAXJJ1kJ
 void RecordRegistrationStatus(PushRegistrationStatus status) {
-  // Called from both UI and IO threads. Slightly racy, but acceptable, see
-  // https://groups.google.com/a/chromium.org/d/msg/chromium-dev/FNzZRJtN2aw/Aw0CWAXJJ1kJ
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
   UMA_HISTOGRAM_ENUMERATION("PushMessaging.RegistrationStatus",
                             status,
                             PUSH_REGISTRATION_STATUS_LAST + 1);
 }
 
-const char kPushRegistrationIdServiceWorkerKey[] =
-    "push_registration_id";
-const char kSenderIdServiceWorkerKey[] =
-    "push_sender_id";
+void RecordUnregistrationStatus(PushUnregistrationStatus status) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  UMA_HISTOGRAM_ENUMERATION("PushMessaging.UnregistrationStatus",
+                            status,
+                            PUSH_UNREGISTRATION_STATUS_LAST + 1);
+}
+
+void RecordGetRegistrationStatus(PushGetRegistrationStatus status) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  UMA_HISTOGRAM_ENUMERATION("PushMessaging.GetRegistrationStatus",
+                            status,
+                            PUSH_GETREGISTRATION_STATUS_LAST + 1);
+}
 
 }  // namespace
 
+struct PushMessagingMessageFilter::RegisterData {
+  RegisterData();
+  RegisterData(const RegisterData& other) = default;
+  bool FromDocument() const;
+  int request_id;
+  GURL requesting_origin;
+  int64 service_worker_registration_id;
+  // The following two members should only be read if FromDocument() is true.
+  int render_frame_id;
+  bool user_visible_only;
+};
+
+
+// Inner core of this message filter which lives on the UI thread.
+class PushMessagingMessageFilter::Core {
+ public:
+  Core(const base::WeakPtr<PushMessagingMessageFilter>& io_parent,
+       int render_process_id);
+
+  // Public Register methods on UI thread --------------------------------------
+
+  // Called via PostTask from IO thread.
+  void RegisterOnUI(const RegisterData& data, const std::string& sender_id);
+
+  // Public Unregister methods on UI thread ------------------------------------
+
+  // Called via PostTask from IO thread.
+  void UnregisterFromService(int request_id,
+                             int64 service_worker_registration_id,
+                             const GURL& requesting_origin,
+                             const std::string& sender_id);
+
+  // Public GetPermission methods on UI thread ---------------------------------
+
+  // Called via PostTask from IO thread.
+  void GetPermissionStatusOnUI(const GURL& requesting_origin, int request_id);
+
+  // Public helper methods on UI thread ----------------------------------------
+
+  // Called (directly) from both the UI and IO threads.
+  bool is_incognito() const { return is_incognito_; }
+
+  // Returns a push messaging service. May return null.
+  PushMessagingService* service();
+
+ private:
+  friend struct BrowserThread::DeleteOnThread<BrowserThread::UI>;
+  friend class base::DeleteHelper<Core>;
+
+  ~Core();
+
+  // Private Register methods on UI thread -------------------------------------
+
+  void DidRegister(const RegisterData& data,
+                   const std::string& push_registration_id,
+                   PushRegistrationStatus status);
+
+  // Private Unregister methods on UI thread -----------------------------------
+
+  void DidUnregisterFromService(int request_id,
+                                int64 service_worker_registration_id,
+                                PushUnregistrationStatus unregistration_status);
+
+  // Private helper methods on UI thread ---------------------------------------
+
+  void Send(IPC::Message* message);
+
+  // Outer part of this message filter which lives on the IO thread.
+  base::WeakPtr<PushMessagingMessageFilter> io_parent_;
+
+  int render_process_id_;
+
+  bool is_incognito_;
+
+  base::WeakPtrFactory<Core> weak_factory_ui_to_ui_;
+
+  DISALLOW_COPY_AND_ASSIGN(Core);
+};
+
+
 PushMessagingMessageFilter::RegisterData::RegisterData()
     : request_id(0),
       service_worker_registration_id(0),
       render_frame_id(ChildProcessHost::kInvalidUniqueID),
-      user_visible_only(false) {}
+      user_visible_only(false) {
+}
 
 bool PushMessagingMessageFilter::RegisterData::FromDocument() const {
   return render_frame_id != ChildProcessHost::kInvalidUniqueID;
 }
 
+PushMessagingMessageFilter::Core::Core(
+    const base::WeakPtr<PushMessagingMessageFilter>& io_parent,
+    int render_process_id)
+    : io_parent_(io_parent),
+      render_process_id_(render_process_id),
+      weak_factory_ui_to_ui_(this) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  RenderProcessHost* process_host =
+      RenderProcessHost::FromID(render_process_id_);  // Can't be null yet.
+  is_incognito_ = process_host->GetBrowserContext()->IsOffTheRecord();
+}
+
+PushMessagingMessageFilter::Core::~Core() {}
+
 PushMessagingMessageFilter::PushMessagingMessageFilter(
     int render_process_id,
     ServiceWorkerContextWrapper* service_worker_context)
     : BrowserMessageFilter(PushMessagingMsgStart),
-      render_process_id_(render_process_id),
       service_worker_context_(service_worker_context),
-      service_(NULL),
-      weak_factory_io_to_io_(this),
-      weak_factory_ui_to_ui_(this) {
+      weak_factory_io_to_io_(this) {
+  // Although this class is used only on the IO thread, it is constructed on UI.
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  // Normally, it would be unsafe to obtain a weak pointer from the UI thread,
+  // but it's ok in the constructor since we can't be destroyed before our
+  // constructor finishes.
+  ui_core_.reset(new Core(weak_factory_io_to_io_.GetWeakPtr(),
+                          render_process_id));
+  PushMessagingService* push_service = ui_core_->service();
+  if (push_service)
+    push_endpoint_ = push_service->GetPushEndpoint();
 }
 
 PushMessagingMessageFilter::~PushMessagingMessageFilter() {}
 
+void PushMessagingMessageFilter::OnDestruct() const {
+  BrowserThread::DeleteOnIOThread::Destruct(this);
+}
+
 bool PushMessagingMessageFilter::OnMessageReceived(
     const IPC::Message& message) {
   bool handled = true;
@@ -80,6 +203,10 @@
   return handled;
 }
 
+// Register methods on both IO and UI threads, merged in order of use from
+// PushMessagingMessageFilter and Core.
+// -----------------------------------------------------------------------------
+
 void PushMessagingMessageFilter::OnRegisterFromDocument(
     int render_frame_id,
     int request_id,
@@ -88,32 +215,27 @@
     int64 service_worker_registration_id) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   // TODO(mvanouwerkerk): Validate arguments?
-  ServiceWorkerRegistration* service_worker_registration =
-      service_worker_context_->context()->GetLiveRegistration(
-          service_worker_registration_id);
-  DCHECK(service_worker_registration);
-  if (!service_worker_registration ||
-      !service_worker_registration->active_version()) {
-    PushRegistrationStatus status = PUSH_REGISTRATION_STATUS_NO_SERVICE_WORKER;
-    Send(new PushMessagingMsg_RegisterFromDocumentError(render_frame_id,
-                                                        request_id, status));
-    RecordRegistrationStatus(status);
-    return;
-  }
-
   // TODO(peter): Persist |user_visible_only| in Service Worker storage.
-
   RegisterData data;
   data.request_id = request_id;
-  data.requesting_origin = service_worker_registration->pattern().GetOrigin();
   data.service_worker_registration_id = service_worker_registration_id;
   data.render_frame_id = render_frame_id;
   data.user_visible_only = user_visible_only;
 
+  ServiceWorkerRegistration* service_worker_registration =
+      service_worker_context_->context()->GetLiveRegistration(
+          service_worker_registration_id);
+  if (!service_worker_registration ||
+      !service_worker_registration->active_version()) {
+    SendRegisterError(data, PUSH_REGISTRATION_STATUS_NO_SERVICE_WORKER);
+    return;
+  }
+  data.requesting_origin = service_worker_registration->pattern().GetOrigin();
+
   service_worker_context_->context()->storage()->StoreUserData(
       service_worker_registration_id,
       data.requesting_origin,
-      kSenderIdServiceWorkerKey,
+      kPushSenderIdServiceWorkerKey,
       sender_id,
       base::Bind(&PushMessagingMessageFilter::DidPersistSenderId,
                  weak_factory_io_to_io_.GetWeakPtr(),
@@ -124,39 +246,21 @@
     int request_id,
     int64 service_worker_registration_id) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  ServiceWorkerRegistration* service_worker_registration =
-      service_worker_context_->context()->GetLiveRegistration(
-          service_worker_registration_id);
-  DCHECK(service_worker_registration);
-  if (!service_worker_registration)
-    return;
-
   RegisterData data;
   data.request_id = request_id;
-  data.requesting_origin = service_worker_registration->pattern().GetOrigin();
   data.service_worker_registration_id = service_worker_registration_id;
 
-  // This sender_id will be ignored; instead it will be fetched from storage.
-  CheckForExistingRegistration(data, "" /* sender_id */);
-}
-
-void PushMessagingMessageFilter::OnGetPermissionStatus(
-    int request_id,
-    int64 service_worker_registration_id) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
   ServiceWorkerRegistration* service_worker_registration =
       service_worker_context_->context()->GetLiveRegistration(
           service_worker_registration_id);
-  DCHECK(service_worker_registration);
-  if (!service_worker_registration)
+  if (!service_worker_registration) {
+    SendRegisterError(data, PUSH_REGISTRATION_STATUS_NO_SERVICE_WORKER);
     return;
+  }
+  data.requesting_origin = service_worker_registration->pattern().GetOrigin();
 
-  BrowserThread::PostTask(
-      BrowserThread::UI, FROM_HERE,
-      base::Bind(&PushMessagingMessageFilter::GetPermissionStatusOnUI,
-                 this,
-                 service_worker_registration->pattern().GetOrigin(),
-                 request_id));
+  // This sender_id will be ignored; instead it will be fetched from storage.
+  CheckForExistingRegistration(data, std::string() /* sender_id */);
 }
 
 void PushMessagingMessageFilter::DidPersistSenderId(
@@ -178,8 +282,7 @@
       data.service_worker_registration_id,
       kPushRegistrationIdServiceWorkerKey,
       base::Bind(&PushMessagingMessageFilter::DidCheckForExistingRegistration,
-                 weak_factory_io_to_io_.GetWeakPtr(),
-                 data, sender_id));
+                 weak_factory_io_to_io_.GetWeakPtr(), data, sender_id));
 }
 
 void PushMessagingMessageFilter::DidCheckForExistingRegistration(
@@ -189,11 +292,8 @@
     ServiceWorkerStatusCode service_worker_status) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   if (service_worker_status == SERVICE_WORKER_OK) {
-    BrowserThread::PostTask(
-        BrowserThread::UI, FROM_HERE,
-        base::Bind(&PushMessagingMessageFilter::SendRegisterSuccessOnUI,
-                   this, data, PUSH_REGISTRATION_STATUS_SUCCESS_FROM_CACHE,
-                   push_registration_id));
+    SendRegisterSuccess(data, PUSH_REGISTRATION_STATUS_SUCCESS_FROM_CACHE,
+                        push_registration_id);
     return;
   }
   // TODO(johnme): The spec allows the register algorithm to reject with an
@@ -204,12 +304,12 @@
   if (data.FromDocument()) {
     BrowserThread::PostTask(
         BrowserThread::UI, FROM_HERE,
-        base::Bind(&PushMessagingMessageFilter::RegisterOnUI,
-                   this, data, sender_id));
+        base::Bind(&Core::RegisterOnUI, base::Unretained(ui_core_.get()),
+                   data, sender_id));
   } else {
     service_worker_context_->context()->storage()->GetUserData(
         data.service_worker_registration_id,
-        kSenderIdServiceWorkerKey,
+        kPushSenderIdServiceWorkerKey,
         base::Bind(&PushMessagingMessageFilter::DidGetSenderIdFromStorage,
                    weak_factory_io_to_io_.GetWeakPtr(), data));
   }
@@ -226,67 +326,76 @@
   }
   BrowserThread::PostTask(
       BrowserThread::UI, FROM_HERE,
-      base::Bind(&PushMessagingMessageFilter::RegisterOnUI,
-                 this, data, sender_id));
+      base::Bind(&Core::RegisterOnUI, base::Unretained(ui_core_.get()),
+                 data, sender_id));
 }
 
-void PushMessagingMessageFilter::RegisterOnUI(
-    const RegisterData& data,
+void PushMessagingMessageFilter::Core::RegisterOnUI(
+    const PushMessagingMessageFilter::RegisterData& data,
     const std::string& sender_id) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  if (!service()) {
-    SendRegisterError(data, PUSH_REGISTRATION_STATUS_SERVICE_NOT_AVAILABLE);
+  PushMessagingService* push_service = service();
+  if (!push_service) {
+    if (!is_incognito()) {
+      // TODO(johnme): Might be better not to expose the API in this case.
+      BrowserThread::PostTask(
+          BrowserThread::IO, FROM_HERE,
+          base::Bind(&PushMessagingMessageFilter::SendRegisterError,
+                     io_parent_,
+                     data, PUSH_REGISTRATION_STATUS_SERVICE_NOT_AVAILABLE));
+    } else {
+      // Prevent websites from detecting incognito mode, by emulating what would
+      // have happened if we had a PushMessagingService available.
+      if (!data.FromDocument() || !data.user_visible_only) {
+        // Throw a permission denied error under the same circumstances.
+        BrowserThread::PostTask(
+            BrowserThread::IO, FROM_HERE,
+            base::Bind(&PushMessagingMessageFilter::SendRegisterError,
+                       io_parent_,
+                       data,
+                       PUSH_REGISTRATION_STATUS_INCOGNITO_PERMISSION_DENIED));
+      }
+      // Else leave the promise hanging forever, to simulate a user ignoring the
+      // infobar. TODO(johnme): Simulate the user dismissing the infobar after a
+      // random time period.
+    }
     return;
   }
 
   if (data.FromDocument()) {
-    service()->RegisterFromDocument(
+    push_service->RegisterFromDocument(
         data.requesting_origin, data.service_worker_registration_id, sender_id,
         render_process_id_, data.render_frame_id, data.user_visible_only,
-        base::Bind(&PushMessagingMessageFilter::DidRegister,
-                   weak_factory_ui_to_ui_.GetWeakPtr(), data));
+        base::Bind(&Core::DidRegister, weak_factory_ui_to_ui_.GetWeakPtr(),
+                   data));
   } else {
-    service()->RegisterFromWorker(
+    push_service->RegisterFromWorker(
         data.requesting_origin, data.service_worker_registration_id, sender_id,
-        base::Bind(&PushMessagingMessageFilter::DidRegister,
-                   weak_factory_ui_to_ui_.GetWeakPtr(), data));
+        base::Bind(&Core::DidRegister, weak_factory_ui_to_ui_.GetWeakPtr(),
+                   data));
   }
 }
 
-void PushMessagingMessageFilter::GetPermissionStatusOnUI(
-    const GURL& requesting_origin,
-    int request_id) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  if (!service()) {
-    Send(new PushMessagingMsg_GetPermissionStatusError(request_id));
-    return;
-  }
-  GURL embedding_origin = requesting_origin;
-  blink::WebPushPermissionStatus permission_status =
-      service()->GetPermissionStatus(requesting_origin, embedding_origin);
-  Send(new PushMessagingMsg_GetPermissionStatusSuccess(request_id,
-                                                       permission_status));
-}
-
-void PushMessagingMessageFilter::DidRegister(
+void PushMessagingMessageFilter::Core::DidRegister(
     const RegisterData& data,
     const std::string& push_registration_id,
     PushRegistrationStatus status) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   if (status == PUSH_REGISTRATION_STATUS_SUCCESS_FROM_PUSH_SERVICE) {
-    GURL push_endpoint(service()->GetPushEndpoint());
     BrowserThread::PostTask(
         BrowserThread::IO, FROM_HERE,
         base::Bind(&PushMessagingMessageFilter::PersistRegistrationOnIO,
-                   this, data, push_endpoint, push_registration_id));
+                   io_parent_, data, push_registration_id));
   } else {
-    SendRegisterError(data, status);
+    BrowserThread::PostTask(
+        BrowserThread::IO, FROM_HERE,
+        base::Bind(&PushMessagingMessageFilter::SendRegisterError, io_parent_,
+                   data, status));
   }
 }
 
 void PushMessagingMessageFilter::PersistRegistrationOnIO(
     const RegisterData& data,
-    const GURL& push_endpoint,
     const std::string& push_registration_id) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   service_worker_context_->context()->storage()->StoreUserData(
@@ -296,19 +405,18 @@
       push_registration_id,
       base::Bind(&PushMessagingMessageFilter::DidPersistRegistrationOnIO,
                  weak_factory_io_to_io_.GetWeakPtr(),
-                 data, push_endpoint, push_registration_id));
+                 data, push_registration_id));
 }
 
 void PushMessagingMessageFilter::DidPersistRegistrationOnIO(
     const RegisterData& data,
-    const GURL& push_endpoint,
     const std::string& push_registration_id,
     ServiceWorkerStatusCode service_worker_status) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   if (service_worker_status == SERVICE_WORKER_OK) {
     SendRegisterSuccess(data,
                         PUSH_REGISTRATION_STATUS_SUCCESS_FROM_PUSH_SERVICE,
-                        push_endpoint, push_registration_id);
+                        push_registration_id);
   } else {
     // TODO(johnme): Unregister, so PushMessagingServiceImpl can decrease count.
     SendRegisterError(data, PUSH_REGISTRATION_STATUS_STORAGE_ERROR);
@@ -317,13 +425,14 @@
 
 void PushMessagingMessageFilter::SendRegisterError(
     const RegisterData& data, PushRegistrationStatus status) {
-  // May be called from both IO and UI threads.
+  // Only called from IO thread, but would be safe to call from UI thread.
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
   if (data.FromDocument()) {
     Send(new PushMessagingMsg_RegisterFromDocumentError(
-      data.render_frame_id, data.request_id, status));
+        data.render_frame_id, data.request_id, status));
   } else {
     Send(new PushMessagingMsg_RegisterFromWorkerError(
-      data.request_id, status));
+        data.request_id, status));
   }
   RecordRegistrationStatus(status);
 }
@@ -331,28 +440,30 @@
 void PushMessagingMessageFilter::SendRegisterSuccess(
     const RegisterData& data,
     PushRegistrationStatus status,
-    const GURL& push_endpoint,
     const std::string& push_registration_id) {
-  // May be called from both IO and UI threads.
+  // Only called from IO thread, but would be safe to call from UI thread.
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  if (push_endpoint_.is_empty()) {
+    // This shouldn't be possible in incognito mode, since we've already checked
+    // that we have an existing registration. Hence it's ok to throw an error.
+    DCHECK(!ui_core_->is_incognito());
+    SendRegisterError(data, PUSH_REGISTRATION_STATUS_SERVICE_NOT_AVAILABLE);
+    return;
+  }
   if (data.FromDocument()) {
     Send(new PushMessagingMsg_RegisterFromDocumentSuccess(
         data.render_frame_id,
-        data.request_id, push_endpoint, push_registration_id));
+        data.request_id, push_endpoint_, push_registration_id));
   } else {
     Send(new PushMessagingMsg_RegisterFromWorkerSuccess(
-        data.request_id, push_endpoint, push_registration_id));
+        data.request_id, push_endpoint_, push_registration_id));
   }
   RecordRegistrationStatus(status);
 }
 
-void PushMessagingMessageFilter::SendRegisterSuccessOnUI(
-    const RegisterData& data,
-    PushRegistrationStatus status,
-    const std::string& push_registration_id) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  GURL push_endpoint(service()->GetPushEndpoint());
-  SendRegisterSuccess(data, status, push_endpoint, push_registration_id);
-}
+// Unregister methods on both IO and UI threads, merged in order of use from
+// PushMessagingMessageFilter and Core.
+// -----------------------------------------------------------------------------
 
 void PushMessagingMessageFilter::OnUnregister(
     int request_id, int64 service_worker_registration_id) {
@@ -360,25 +471,54 @@
   ServiceWorkerRegistration* service_worker_registration =
       service_worker_context_->context()->GetLiveRegistration(
           service_worker_registration_id);
-  DCHECK(service_worker_registration);
-  if (!service_worker_registration)
+  if (!service_worker_registration) {
+    DidUnregister(request_id, PUSH_UNREGISTRATION_STATUS_NO_SERVICE_WORKER);
     return;
+  }
 
   service_worker_context_->context()->storage()->GetUserData(
       service_worker_registration_id,
       kPushRegistrationIdServiceWorkerKey,
-      base::Bind(&PushMessagingMessageFilter::DoUnregister,
-                 weak_factory_io_to_io_.GetWeakPtr(),
-                 request_id,
-                 service_worker_registration_id,
-                 service_worker_registration->pattern().GetOrigin()));
+      base::Bind(
+          &PushMessagingMessageFilter::UnregisterHavingGottenPushRegistrationId,
+          weak_factory_io_to_io_.GetWeakPtr(), request_id,
+          service_worker_registration_id,
+          service_worker_registration->pattern().GetOrigin()));
 }
 
-void PushMessagingMessageFilter::DoUnregister(
+void PushMessagingMessageFilter::UnregisterHavingGottenPushRegistrationId(
     int request_id,
     int64 service_worker_registration_id,
     const GURL& requesting_origin,
-    const std::string& push_registration_id,
+    const std::string& push_registration_id,  // Unused, we just want the status
+    ServiceWorkerStatusCode service_worker_status) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+
+  if (service_worker_status == SERVICE_WORKER_OK) {
+    service_worker_context_->context()->storage()->GetUserData(
+        service_worker_registration_id,
+        kPushSenderIdServiceWorkerKey,
+        base::Bind(
+            &PushMessagingMessageFilter::UnregisterHavingGottenSenderId,
+            weak_factory_io_to_io_.GetWeakPtr(),
+            request_id,
+            service_worker_registration_id,
+            requesting_origin));
+  } else {
+    // Errors are handled the same, whether we were trying to get the
+    // push_registration_id or the sender_id.
+    UnregisterHavingGottenSenderId(request_id, service_worker_registration_id,
+                                   requesting_origin,
+                                   std::string() /* sender_id */,
+                                   service_worker_status);
+  }
+}
+
+void PushMessagingMessageFilter::UnregisterHavingGottenSenderId(
+    int request_id,
+    int64 service_worker_registration_id,
+    const GURL& requesting_origin,
+    const std::string& sender_id,
     ServiceWorkerStatusCode service_worker_status) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
@@ -386,22 +526,21 @@
     case SERVICE_WORKER_OK:
       BrowserThread::PostTask(
           BrowserThread::UI, FROM_HERE,
-          base::Bind(&PushMessagingMessageFilter::UnregisterFromService,
-                     this,
-                     request_id,
-                     service_worker_registration_id,
-                     requesting_origin));
-      return;
+          base::Bind(&Core::UnregisterFromService,
+                     base::Unretained(ui_core_.get()), request_id,
+                     service_worker_registration_id, requesting_origin,
+                     sender_id));
+      break;
     case SERVICE_WORKER_ERROR_NOT_FOUND:
       // We did not find a registration, stop here and notify the renderer that
       // it was a success even though we did not unregister.
       DidUnregister(request_id,
                     PUSH_UNREGISTRATION_STATUS_SUCCESS_WAS_NOT_REGISTERED);
-      return;
+      break;
     case SERVICE_WORKER_ERROR_FAILED:
       DidUnregister(request_id,
-                    PUSH_UNREGISTRATION_STATUS_UNKNOWN_ERROR);
-      return;
+                    PUSH_UNREGISTRATION_STATUS_STORAGE_ERROR);
+      break;
     case SERVICE_WORKER_ERROR_ABORT:
     case SERVICE_WORKER_ERROR_START_WORKER_FAILED:
     case SERVICE_WORKER_ERROR_PROCESS_NOT_FOUND:
@@ -415,86 +554,134 @@
     case SERVICE_WORKER_ERROR_STATE:
       NOTREACHED() << "Got unexpected error code: " << service_worker_status
                    << " " << ServiceWorkerStatusToString(service_worker_status);
-      DidUnregister(request_id, PUSH_UNREGISTRATION_STATUS_UNKNOWN_ERROR);
-      return;
+      DidUnregister(request_id, PUSH_UNREGISTRATION_STATUS_STORAGE_ERROR);
+      break;
   }
 }
 
-void PushMessagingMessageFilter::UnregisterFromService(
+void PushMessagingMessageFilter::Core::UnregisterFromService(
     int request_id,
     int64 service_worker_registration_id,
-    const GURL& requesting_origin) {
+    const GURL& requesting_origin,
+    const std::string& sender_id) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  if (!service()) {
-    DidUnregister(request_id, PUSH_UNREGISTRATION_STATUS_UNKNOWN_ERROR);
+  PushMessagingService* push_service = service();
+  if (!push_service) {
+    // This shouldn't be possible in incognito mode, since we've already checked
+    // that we have an existing registration. Hence it's ok to throw an error.
+    DCHECK(!is_incognito());
+    BrowserThread::PostTask(
+        BrowserThread::IO, FROM_HERE,
+        base::Bind(&PushMessagingMessageFilter::DidUnregister, io_parent_,
+                   request_id,
+                   PUSH_UNREGISTRATION_STATUS_SERVICE_NOT_AVAILABLE));
     return;
   }
 
-  service()->Unregister(
-      requesting_origin, service_worker_registration_id,
+  push_service->Unregister(
+      requesting_origin, service_worker_registration_id, sender_id,
       false /* retry_on_failure */,
-      base::Bind(&PushMessagingMessageFilter::DidUnregisterFromService,
+      base::Bind(&Core::DidUnregisterFromService,
                  weak_factory_ui_to_ui_.GetWeakPtr(),
-                 request_id,
-                 service_worker_registration_id));
+                 request_id, service_worker_registration_id));
 }
 
-void PushMessagingMessageFilter::DidUnregisterFromService(
+void PushMessagingMessageFilter::Core::DidUnregisterFromService(
     int request_id,
     int64 service_worker_registration_id,
     PushUnregistrationStatus unregistration_status) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   switch (unregistration_status) {
-    case PUSH_UNREGISTRATION_STATUS_SUCCESS_UNREGISTER:
-      break;
-    case PUSH_UNREGISTRATION_STATUS_SUCCESS_WILL_RETRY_NETWORK_ERROR:
-      NOTREACHED();
-      break;
+    case PUSH_UNREGISTRATION_STATUS_SUCCESS_UNREGISTERED:
     case PUSH_UNREGISTRATION_STATUS_SUCCESS_WAS_NOT_REGISTERED:
+    case PUSH_UNREGISTRATION_STATUS_PENDING_WILL_RETRY_NETWORK_ERROR:
+      BrowserThread::PostTask(
+          BrowserThread::IO, FROM_HERE,
+          base::Bind(&PushMessagingMessageFilter::ClearRegistrationData,
+                     io_parent_, request_id, service_worker_registration_id,
+                     unregistration_status));
+      break;
+    case PUSH_UNREGISTRATION_STATUS_NO_SERVICE_WORKER:
+    case PUSH_UNREGISTRATION_STATUS_SERVICE_NOT_AVAILABLE:
+    case PUSH_UNREGISTRATION_STATUS_SERVICE_ERROR:
+    case PUSH_UNREGISTRATION_STATUS_STORAGE_ERROR:
     case PUSH_UNREGISTRATION_STATUS_NETWORK_ERROR:
-    case PUSH_UNREGISTRATION_STATUS_UNKNOWN_ERROR:
-      DidUnregister(request_id, unregistration_status);
-      return;
+      BrowserThread::PostTask(
+          BrowserThread::IO, FROM_HERE,
+          base::Bind(&PushMessagingMessageFilter::DidUnregister, io_parent_,
+                     request_id, unregistration_status));
+      break;
+  }
+}
+
+void PushMessagingMessageFilter::ClearRegistrationData(
+    int request_id,
+    int64 service_worker_registration_id,
+    PushUnregistrationStatus unregistration_status) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+
+  service_worker_context_->context()->storage()->ClearUserData(
+      service_worker_registration_id,
+      kPushRegistrationIdServiceWorkerKey,
+      base::Bind(&PushMessagingMessageFilter::DidClearRegistrationData,
+                 weak_factory_io_to_io_.GetWeakPtr(),
+                 request_id, unregistration_status));
+}
+
+void PushMessagingMessageFilter::DidClearRegistrationData(
+    int request_id,
+    PushUnregistrationStatus unregistration_status,
+    ServiceWorkerStatusCode service_worker_status) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+
+  if (service_worker_status != SERVICE_WORKER_OK &&
+      service_worker_status != SERVICE_WORKER_ERROR_NOT_FOUND) {
+    unregistration_status = PUSH_UNREGISTRATION_STATUS_STORAGE_ERROR;
+    DLOG(WARNING) << "Got unexpected error code: " << service_worker_status
+                  << " " << ServiceWorkerStatusToString(service_worker_status);
   }
 
-  BrowserThread::PostTask(
-      BrowserThread::IO, FROM_HERE,
-      base::Bind(&PushMessagingMessageFilter::ClearRegistrationData,
-                 this,
-                 service_worker_registration_id,
-                 base::Bind(&PushMessagingMessageFilter::DidUnregister,
-                            weak_factory_io_to_io_.GetWeakPtr(),
-                            request_id,
-                            unregistration_status)));
+  DidUnregister(request_id, unregistration_status);
 }
 
 void PushMessagingMessageFilter::DidUnregister(
     int request_id,
     PushUnregistrationStatus unregistration_status) {
+  // Only called from IO thread, but would be safe to call from UI thread.
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  RecordUnregistrationStatus(unregistration_status);
+  blink::WebPushError::ErrorType blinkError =
+      blink::WebPushError::ErrorTypeUnknown;
   switch (unregistration_status) {
-    case PUSH_UNREGISTRATION_STATUS_SUCCESS_UNREGISTER:
-    case PUSH_UNREGISTRATION_STATUS_SUCCESS_WILL_RETRY_NETWORK_ERROR:
+    case PUSH_UNREGISTRATION_STATUS_SUCCESS_UNREGISTERED:
       Send(new PushMessagingMsg_UnregisterSuccess(request_id, true));
       return;
     case PUSH_UNREGISTRATION_STATUS_SUCCESS_WAS_NOT_REGISTERED:
       Send(new PushMessagingMsg_UnregisterSuccess(request_id, false));
       return;
+    case PUSH_UNREGISTRATION_STATUS_PENDING_WILL_RETRY_NETWORK_ERROR:
+      NOTREACHED();
+      return;
+    case PUSH_UNREGISTRATION_STATUS_NO_SERVICE_WORKER:
+    case PUSH_UNREGISTRATION_STATUS_SERVICE_NOT_AVAILABLE:
+    case PUSH_UNREGISTRATION_STATUS_SERVICE_ERROR:
+    case PUSH_UNREGISTRATION_STATUS_STORAGE_ERROR:
+      blinkError = blink::WebPushError::ErrorTypeAbort;
+      break;
     case PUSH_UNREGISTRATION_STATUS_NETWORK_ERROR:
-      Send(new PushMessagingMsg_UnregisterError(
-          request_id,
-          blink::WebPushError::ErrorTypeNetwork,
-          "Failed to connect to the push server."));
-      return;
-    case PUSH_UNREGISTRATION_STATUS_UNKNOWN_ERROR:
-      Send(new PushMessagingMsg_UnregisterError(
-          request_id,
-          blink::WebPushError::ErrorTypeUnknown,
-          "Unexpected error while trying to unregister from the push server."));
-      return;
+      blinkError = blink::WebPushError::ErrorTypeNetwork;
+      break;
   }
+  Send(new PushMessagingMsg_UnregisterError(
+       request_id, blinkError,
+       PushUnregistrationStatusToString(unregistration_status)));
 }
 
+// GetRegistration methods on both IO and UI threads, merged in order of use
+// from PushMessagingMessageFilter and Core.
+// -----------------------------------------------------------------------------
+
 void PushMessagingMessageFilter::OnGetRegistration(
     int request_id,
     int64 service_worker_registration_id) {
@@ -504,8 +691,7 @@
       service_worker_registration_id,
       kPushRegistrationIdServiceWorkerKey,
       base::Bind(&PushMessagingMessageFilter::DidGetRegistration,
-                 weak_factory_io_to_io_.GetWeakPtr(),
-                 request_id));
+                 weak_factory_io_to_io_.GetWeakPtr(), request_id));
 }
 
 void PushMessagingMessageFilter::DidGetRegistration(
@@ -514,20 +700,27 @@
     ServiceWorkerStatusCode service_worker_status) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   PushGetRegistrationStatus get_status =
-      PUSH_GETREGISTRATION_STATUS_SERVICE_WORKER_ERROR;
+      PUSH_GETREGISTRATION_STATUS_STORAGE_ERROR;
   switch (service_worker_status) {
     case SERVICE_WORKER_OK:
-      BrowserThread::PostTask(
-          BrowserThread::UI, FROM_HERE,
-          base::Bind(
-              &PushMessagingMessageFilter::SendGetRegistrationSuccessOnUI,
-              this, request_id, push_registration_id));
+      if (push_endpoint_.is_empty()) {
+        // Return not found in incognito mode, so websites can't detect it.
+        get_status =
+            ui_core_->is_incognito()
+            ? PUSH_GETREGISTRATION_STATUS_INCOGNITO_REGISTRATION_NOT_FOUND
+            : PUSH_GETREGISTRATION_STATUS_SERVICE_NOT_AVAILABLE;
+        break;
+      }
+      Send(new PushMessagingMsg_GetRegistrationSuccess(request_id,
+                                                       push_endpoint_,
+                                                       push_registration_id));
+      RecordGetRegistrationStatus(PUSH_GETREGISTRATION_STATUS_SUCCESS);
       return;
     case SERVICE_WORKER_ERROR_NOT_FOUND:
       get_status = PUSH_GETREGISTRATION_STATUS_REGISTRATION_NOT_FOUND;
       break;
     case SERVICE_WORKER_ERROR_FAILED:
-      get_status = PUSH_GETREGISTRATION_STATUS_SERVICE_WORKER_ERROR;
+      get_status = PUSH_GETREGISTRATION_STATUS_STORAGE_ERROR;
       break;
     case SERVICE_WORKER_ERROR_ABORT:
     case SERVICE_WORKER_ERROR_START_WORKER_FAILED:
@@ -542,57 +735,80 @@
     case SERVICE_WORKER_ERROR_STATE:
       NOTREACHED() << "Got unexpected error code: " << service_worker_status
                    << " " << ServiceWorkerStatusToString(service_worker_status);
-      get_status = PUSH_GETREGISTRATION_STATUS_SERVICE_WORKER_ERROR;
+      get_status = PUSH_GETREGISTRATION_STATUS_STORAGE_ERROR;
       break;
   }
   Send(new PushMessagingMsg_GetRegistrationError(request_id, get_status));
-  // TODO(johnme): RecordGetRegistrationStatus(status); ?
+  RecordGetRegistrationStatus(get_status);
 }
 
-void PushMessagingMessageFilter::SendGetRegistrationSuccessOnUI(
+// GetPermission methods on both IO and UI threads, merged in order of use from
+// PushMessagingMessageFilter and Core.
+// -----------------------------------------------------------------------------
+
+void PushMessagingMessageFilter::OnGetPermissionStatus(
     int request_id,
-    const std::string& push_registration_id) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  GURL push_endpoint(service()->GetPushEndpoint());
-  Send(new PushMessagingMsg_GetRegistrationSuccess(request_id, push_endpoint,
-                                                   push_registration_id));
-}
-
-void PushMessagingMessageFilter::ClearRegistrationData(
-    int64 service_worker_registration_id,
-    const base::Closure& closure) {
+    int64 service_worker_registration_id) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
-
-  service_worker_context_->context()->storage()->ClearUserData(
-      service_worker_registration_id,
-      kPushRegistrationIdServiceWorkerKey,
-      base::Bind(&PushMessagingMessageFilter::DidClearRegistrationData,
-                 weak_factory_io_to_io_.GetWeakPtr(),
-                 closure));
-}
-
-void PushMessagingMessageFilter::DidClearRegistrationData(
-    const base::Closure& closure,
-    ServiceWorkerStatusCode service_worker_status) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-
-  DCHECK(service_worker_status == SERVICE_WORKER_OK)
-      << "Got unexpected error code: " << service_worker_status
-      << " " << ServiceWorkerStatusToString(service_worker_status);
-
-  closure.Run();
-}
-
-PushMessagingService* PushMessagingMessageFilter::service() {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  if (!service_) {
-    RenderProcessHost* process_host =
-        RenderProcessHost::FromID(render_process_id_);
-    if (!process_host)
-      return NULL;
-    service_ = process_host->GetBrowserContext()->GetPushMessagingService();
+  ServiceWorkerRegistration* service_worker_registration =
+      service_worker_context_->context()->GetLiveRegistration(
+          service_worker_registration_id);
+  if (!service_worker_registration) {
+    Send(new PushMessagingMsg_GetPermissionStatusError(request_id));
+    return;
   }
-  return service_;
+
+  BrowserThread::PostTask(
+      BrowserThread::UI, FROM_HERE,
+      base::Bind(&Core::GetPermissionStatusOnUI,
+                 base::Unretained(ui_core_.get()),
+                 service_worker_registration->pattern().GetOrigin(),
+                 request_id));
+}
+
+void PushMessagingMessageFilter::Core::GetPermissionStatusOnUI(
+    const GURL& requesting_origin,
+    int request_id) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  blink::WebPushPermissionStatus permission_status;
+  PushMessagingService* push_service = service();
+  if (push_service) {
+    GURL embedding_origin = requesting_origin;
+    permission_status =
+        push_service->GetPermissionStatus(requesting_origin, embedding_origin);
+  } else if (is_incognito()) {
+    // Return default, so the website can't detect incognito mode.
+    permission_status = blink::WebPushPermissionStatusDefault;
+  } else {
+    Send(new PushMessagingMsg_GetPermissionStatusError(request_id));
+    return;
+  }
+  Send(new PushMessagingMsg_GetPermissionStatusSuccess(request_id,
+                                                       permission_status));
+}
+
+// Helper methods on both IO and UI threads, merged from
+// PushMessagingMessageFilter and Core.
+// -----------------------------------------------------------------------------
+
+void PushMessagingMessageFilter::Core::Send(IPC::Message* message) {
+  BrowserThread::PostTask(
+      BrowserThread::IO, FROM_HERE,
+      base::Bind(&PushMessagingMessageFilter::SendIPC, io_parent_,
+                 base::Passed(make_scoped_ptr(message))));
+}
+
+void PushMessagingMessageFilter::SendIPC(scoped_ptr<IPC::Message> message) {
+  Send(message.release());
+}
+
+PushMessagingService* PushMessagingMessageFilter::Core::service() {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  RenderProcessHost* process_host =
+      RenderProcessHost::FromID(render_process_id_);
+  return process_host
+         ? process_host->GetBrowserContext()->GetPushMessagingService()
+         : nullptr;
 }
 
 }  // namespace content
diff --git a/content/browser/push_messaging/push_messaging_message_filter.h b/content/browser/push_messaging/push_messaging_message_filter.h
index d14bf471..3e12f4bd 100644
--- a/content/browser/push_messaging/push_messaging_message_filter.h
+++ b/content/browser/push_messaging/push_messaging_message_filter.h
@@ -8,6 +8,7 @@
 #include <string>
 
 #include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
 #include "base/memory/weak_ptr.h"
 #include "content/common/service_worker/service_worker_status_code.h"
 #include "content/public/browser/browser_message_filter.h"
@@ -21,6 +22,9 @@
 class PushMessagingService;
 class ServiceWorkerContextWrapper;
 
+extern const char kPushSenderIdServiceWorkerKey[];
+extern const char kPushRegistrationIdServiceWorkerKey[];
+
 class PushMessagingMessageFilter : public BrowserMessageFilter {
  public:
   PushMessagingMessageFilter(
@@ -28,23 +32,20 @@
       ServiceWorkerContextWrapper* service_worker_context);
 
  private:
-  struct RegisterData {
-    RegisterData();
-    RegisterData(const RegisterData& other) = default;
-    bool FromDocument() const;
-    int request_id;
-    GURL requesting_origin;
-    int64 service_worker_registration_id;
-    // The following two members should only be read if FromDocument() is true.
-    int render_frame_id;
-    bool user_visible_only;
-  };
+  struct RegisterData;
+  class Core;
+
+  friend class BrowserThread;
+  friend class base::DeleteHelper<PushMessagingMessageFilter>;
 
   ~PushMessagingMessageFilter() override;
 
   // BrowserMessageFilter implementation.
+  void OnDestruct() const override;
   bool OnMessageReceived(const IPC::Message& message) override;
 
+  // Register methods on IO thread ---------------------------------------------
+
   void OnRegisterFromDocument(int render_frame_id,
                               int request_id,
                               const std::string& sender_id,
@@ -54,9 +55,6 @@
   void OnRegisterFromWorker(int request_id,
                             int64 service_worker_registration_id);
 
-  void OnGetPermissionStatus(int request_id,
-                             int64 service_worker_registration_id);
-
   void DidPersistSenderId(const RegisterData& data,
                           const std::string& sender_id,
                           ServiceWorkerStatusCode service_worker_status);
@@ -77,102 +75,82 @@
                                  const std::string& sender_id,
                                  ServiceWorkerStatusCode service_worker_status);
 
-  void RegisterOnUI(const RegisterData& data,
-                    const std::string& sender_id);
-
-  void GetPermissionStatusOnUI(const GURL& requesting_origin, int request_id);
-
-  void DidRegister(const RegisterData& data,
-                   const std::string& push_registration_id,
-                   PushRegistrationStatus status);
-
+  // Called via PostTask from UI thread.
   void PersistRegistrationOnIO(const RegisterData& data,
-                               const GURL& push_endpoint,
                                const std::string& push_registration_id);
 
   void DidPersistRegistrationOnIO(
-    const RegisterData& data,
-    const GURL& push_endpoint,
-    const std::string& push_registration_id,
-    ServiceWorkerStatusCode service_worker_status);
+      const RegisterData& data,
+      const std::string& push_registration_id,
+      ServiceWorkerStatusCode service_worker_status);
 
+  // Called both from IO thread, and via PostTask from UI thread.
   void SendRegisterError(const RegisterData& data,
                          PushRegistrationStatus status);
+  // Called both from IO thread, and via PostTask from UI thread.
   void SendRegisterSuccess(const RegisterData& data,
                            PushRegistrationStatus status,
-                           const GURL& push_endpoint,
                            const std::string& push_registration_id);
-  void SendRegisterSuccessOnUI(const RegisterData& data,
-                               PushRegistrationStatus status,
-                               const std::string& push_registration_id);
 
-  // Unregister methods --------------------------------------------------------
+  // Unregister methods on IO thread -------------------------------------------
 
-  void OnUnregister(int request_id,
-                    int64 service_worker_registration_id);
+  void OnUnregister(int request_id, int64 service_worker_registration_id);
 
-  void DoUnregister(int request_id,
-                    int64 service_worker_registration_id,
-                    const GURL& requesting_origin,
-                    const std::string& push_registration_id,
-                    ServiceWorkerStatusCode service_worker_status);
+  void UnregisterHavingGottenPushRegistrationId(
+      int request_id,
+      int64 service_worker_registration_id,
+      const GURL& requesting_origin,
+      const std::string& push_registration_id,
+      ServiceWorkerStatusCode service_worker_status);
 
-  void UnregisterFromService(int request_id,
+  void UnregisterHavingGottenSenderId(
+      int request_id,
+      int64 service_worker_registration_id,
+      const GURL& requesting_origin,
+      const std::string& sender_id,
+      ServiceWorkerStatusCode service_worker_status);
+
+  // Called via PostTask from UI thread.
+  void ClearRegistrationData(int request_id,
                              int64 service_worker_registration_id,
-                             const GURL& requesting_origin);
+                             PushUnregistrationStatus unregistration_status);
 
-  void DidUnregisterFromService(int request_id,
-                                int64 service_worker_registration_id,
-                                PushUnregistrationStatus unregistration_status);
+  void DidClearRegistrationData(int request_id,
+                                PushUnregistrationStatus unregistration_status,
+                                ServiceWorkerStatusCode service_worker_status);
 
+  // Called both from IO thread, and via PostTask from UI thread.
   void DidUnregister(int request_id,
                      PushUnregistrationStatus unregistration_status);
 
+  // GetRegistration methods on IO thread --------------------------------------
 
-  // GetRegistration methods ---------------------------------------------------
-
-  void OnGetRegistration(int request_id,
-                         int64 service_worker_registration_id);
+  void OnGetRegistration(int request_id, int64 service_worker_registration_id);
 
   void DidGetRegistration(int request_id,
                           const std::string& push_registration_id,
                           ServiceWorkerStatusCode status);
 
-  void SendGetRegistrationSuccessOnUI(int request_id,
-                                      const std::string& push_registration_id);
+  // GetPermission methods on IO thread ----------------------------------------
 
-  // Helper methods ------------------------------------------------------------
+  void OnGetPermissionStatus(int request_id,
+                             int64 service_worker_registration_id);
 
-  // Clear the registration information related to
-  // |service_worker_registration_id| and call |closure| when done.
-  // The caller MUST check that there is a registration information before
-  // calling this method if this is something that it needs to know.
-  void ClearRegistrationData(int64 service_worker_registration_id,
-                             const base::Closure& closure);
+  // Helper methods on IO thread -----------------------------------------------
 
-  void DidClearRegistrationData(
-      const base::Closure& closure,
-      ServiceWorkerStatusCode service_worker_status_code);
+  // Called via PostTask from UI thread.
+  void SendIPC(scoped_ptr<IPC::Message> message);
 
-  // Returns a push messaging service. The embedder owns the service, and is
-  // responsible for ensuring that it outlives RenderProcessHost. It's valid to
-  // return NULL. Must be called on the UI thread.
-  PushMessagingService* service();
+  // Inner core of this message filter which lives on the UI thread.
+  scoped_ptr<Core, BrowserThread::DeleteOnUIThread> ui_core_;
 
-  int render_process_id_;
   scoped_refptr<ServiceWorkerContextWrapper> service_worker_context_;
 
-  // Owned by the content embedder's browsing context.
-  PushMessagingService* service_;
+  // Empty if no PushMessagingService was available when constructed.
+  GURL push_endpoint_;
 
-  // Should only be used for asynchronous calls on the IO thread with external
-  // dependencies that might outlive this class e.g. ServiceWorkerStorage.
   base::WeakPtrFactory<PushMessagingMessageFilter> weak_factory_io_to_io_;
 
-  // TODO(johnme): Remove this, it seems unsafe since this class could be
-  // destroyed on the IO thread while the callback runs on the UI thread.
-  base::WeakPtrFactory<PushMessagingMessageFilter> weak_factory_ui_to_ui_;
-
   DISALLOW_COPY_AND_ASSIGN(PushMessagingMessageFilter);
 };
 
diff --git a/content/browser/renderer_host/clipboard_message_filter.cc b/content/browser/renderer_host/clipboard_message_filter.cc
index a297b0a..5cb0d9c8 100644
--- a/content/browser/renderer_host/clipboard_message_filter.cc
+++ b/content/browser/renderer_host/clipboard_message_filter.cc
@@ -236,9 +236,9 @@
 }
 
 void ClipboardMessageFilter::OnWriteBookmark(ui::ClipboardType clipboard_type,
-                                             const GURL& url,
+                                             const std::string& url,
                                              const base::string16& title) {
-  clipboard_writer_->WriteBookmark(title, url.spec());
+  clipboard_writer_->WriteBookmark(title, url);
 }
 
 void ClipboardMessageFilter::OnWriteImage(ui::ClipboardType clipboard_type,
diff --git a/content/browser/renderer_host/clipboard_message_filter.h b/content/browser/renderer_host/clipboard_message_filter.h
index 3f01e868..f563ed5 100644
--- a/content/browser/renderer_host/clipboard_message_filter.h
+++ b/content/browser/renderer_host/clipboard_message_filter.h
@@ -71,7 +71,7 @@
   void OnWriteCustomData(ui::ClipboardType clipboard_type,
                          const std::map<base::string16, base::string16>& data);
   void OnWriteBookmark(ui::ClipboardType clipboard_type,
-                       const GURL& url,
+                       const std::string& url,
                        const base::string16& title);
   void OnWriteImage(ui::ClipboardType clipboard_type,
                     const gfx::Size& size,
diff --git a/content/browser/renderer_host/compositor_impl_android.h b/content/browser/renderer_host/compositor_impl_android.h
index c9ed34d9..b3f5a7e 100644
--- a/content/browser/renderer_host/compositor_impl_android.h
+++ b/content/browser/renderer_host/compositor_impl_android.h
@@ -67,6 +67,7 @@
   void WillBeginMainFrame() override {}
   void DidBeginMainFrame() override {}
   void BeginMainFrame(const cc::BeginFrameArgs& args) override {}
+  void BeginMainFrameNotExpectedSoon() override {}
   void Layout() override;
   void ApplyViewportDeltas(const gfx::Vector2dF& inner_delta,
                            const gfx::Vector2dF& outer_delta,
diff --git a/content/browser/renderer_host/input/synthetic_gesture_target_aura.cc b/content/browser/renderer_host/input/synthetic_gesture_target_aura.cc
index a7626d4..ba187b0 100644
--- a/content/browser/renderer_host/input/synthetic_gesture_target_aura.cc
+++ b/content/browser/renderer_host/input/synthetic_gesture_target_aura.cc
@@ -10,6 +10,7 @@
 #include "ui/aura/window.h"
 #include "ui/aura/window_tree_host.h"
 #include "ui/events/event_processor.h"
+#include "ui/events/event_utils.h"
 #include "ui/events/gesture_detection/gesture_configuration.h"
 
 using blink::WebTouchEvent;
@@ -47,8 +48,8 @@
       const blink::WebMouseWheelEvent& web_wheel,
       const ui::LatencyInfo&) {
   gfx::Point location(web_wheel.x, web_wheel.y);
-  ui::MouseEvent mouse_event(
-      ui::ET_MOUSEWHEEL, location, location, ui::EF_NONE, ui::EF_NONE);
+  ui::MouseEvent mouse_event(ui::ET_MOUSEWHEEL, location, location,
+                             ui::EventTimeForNow(), ui::EF_NONE, ui::EF_NONE);
   ui::MouseWheelEvent wheel_event(
       mouse_event, web_wheel.deltaX, web_wheel.deltaY);
 
@@ -117,7 +118,8 @@
   gfx::Point location(web_mouse.x, web_mouse.y);
   ui::EventType event_type = WebMouseEventTypeToEventType(web_mouse.type);
   int flags = WebMouseEventButtonToFlags(web_mouse.button);
-  ui::MouseEvent mouse_event(event_type, location, location, flags, flags);
+  ui::MouseEvent mouse_event(event_type, location, location,
+                             ui::EventTimeForNow(), flags, flags);
 
   aura::Window* window = GetWindow();
   mouse_event.ConvertLocationToTarget(window, window->GetRootWindow());
diff --git a/content/browser/renderer_host/media/media_stream_manager.cc b/content/browser/renderer_host/media/media_stream_manager.cc
index 0d104af..419ef21 100644
--- a/content/browser/renderer_host/media/media_stream_manager.cc
+++ b/content/browser/renderer_host/media/media_stream_manager.cc
@@ -1622,10 +1622,6 @@
 }
 
 void MediaStreamManager::InitializeDeviceManagersOnIOThread() {
-  // TODO(pkasting): Remove ScopedTracker below once crbug.com/457525 is fixed.
-  tracked_objects::ScopedTracker tracking_profile1(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "457525 MediaStreamManager::InitializeDeviceManagersOnIOThread1"));
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   if (device_task_runner_.get())
     return;
@@ -1645,10 +1641,6 @@
     audio_input_device_manager()->UseFakeDevice();
   }
 
-  // TODO(pkasting): Remove ScopedTracker below once crbug.com/457525 is fixed.
-  tracked_objects::ScopedTracker tracking_profile2(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "457525 MediaStreamManager::InitializeDeviceManagersOnIOThread2"));
   video_capture_manager_ =
       new VideoCaptureManager(media::VideoCaptureDeviceFactory::CreateFactory(
           BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI)));
@@ -1657,10 +1649,14 @@
   // buggy third party Direct Show modules, http://crbug.com/428958.
   video_capture_thread_.init_com_with_mta(false);
   // TODO(pkasting): Remove ScopedTracker below once crbug.com/457525 is fixed.
-  tracked_objects::ScopedTracker tracking_profile3(
+  tracked_objects::ScopedTracker tracking_profile1(
       FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "457525 MediaStreamManager::InitializeDeviceManagersOnIOThread3"));
+          "457525 MediaStreamManager::InitializeDeviceManagersOnIOThread1"));
   CHECK(video_capture_thread_.Start());
+  // TODO(pkasting): Remove ScopedTracker below once crbug.com/457525 is fixed.
+  tracked_objects::ScopedTracker tracking_profile2(
+      FROM_HERE_WITH_EXPLICIT_FUNCTION(
+          "457525 MediaStreamManager::InitializeDeviceManagersOnIOThread2"));
   video_capture_manager_->Register(this,
                                    video_capture_thread_.message_loop_proxy());
 #else
diff --git a/content/browser/renderer_host/media/video_capture_controller.cc b/content/browser/renderer_host/media/video_capture_controller.cc
index 207ef7cb..7f4a1f60 100644
--- a/content/browser/renderer_host/media/video_capture_controller.cc
+++ b/content/browser/renderer_host/media/video_capture_controller.cc
@@ -40,20 +40,31 @@
         name, \
         (height) ? ((width) * 100) / (height) : kInfiniteRatio);
 
-class PoolBuffer : public media::VideoCaptureDevice::Client::Buffer {
+// Class combining a Client::Buffer interface implementation and a pool buffer
+// implementation to guarantee proper cleanup on destruction on our side.
+class AutoReleaseBuffer : public media::VideoCaptureDevice::Client::Buffer {
  public:
-  PoolBuffer(const scoped_refptr<VideoCaptureBufferPool>& pool,
-             int buffer_id,
-             void* data,
-             size_t size)
-      : Buffer(buffer_id, data, size), pool_(pool) {
+  AutoReleaseBuffer(const scoped_refptr<VideoCaptureBufferPool>& pool,
+                    int buffer_id,
+                    void* data,
+                    size_t size)
+      : pool_(pool),
+        id_(buffer_id),
+        data_(data),
+        size_(size) {
     DCHECK(pool_.get());
   }
+  int id() const override { return id_; }
+  void* data() const override { return data_; }
+  size_t size() const override { return size_; }
 
  private:
-  ~PoolBuffer() override { pool_->RelinquishProducerReservation(id()); }
+  ~AutoReleaseBuffer() override { pool_->RelinquishProducerReservation(id_); }
 
   const scoped_refptr<VideoCaptureBufferPool> pool_;
+  const int id_;
+  void* const data_;
+  const size_t size_;
 };
 
 class SyncPointClientImpl : public media::VideoFrame::SyncPointClient {
@@ -155,25 +166,22 @@
   ~VideoCaptureDeviceClient() override;
 
   // VideoCaptureDevice::Client implementation.
-  scoped_refptr<Buffer> ReserveOutputBuffer(media::VideoFrame::Format format,
-                                            const gfx::Size& size) override;
   void OnIncomingCapturedData(const uint8* data,
                               int length,
                               const VideoCaptureFormat& frame_format,
                               int rotation,
-                              base::TimeTicks timestamp) override;
+                              const base::TimeTicks& timestamp) override;
+  scoped_refptr<Buffer> ReserveOutputBuffer(media::VideoFrame::Format format,
+                                            const gfx::Size& size) override;
   void OnIncomingCapturedVideoFrame(
       const scoped_refptr<Buffer>& buffer,
       const VideoCaptureFormat& buffer_format,
       const scoped_refptr<media::VideoFrame>& frame,
-      base::TimeTicks timestamp) override;
+      const base::TimeTicks& timestamp) override;
   void OnError(const std::string& reason) override;
   void OnLog(const std::string& message) override;
 
  private:
-  scoped_refptr<Buffer> DoReserveOutputBuffer(media::VideoFrame::Format format,
-                                              const gfx::Size& dimensions);
-
   // The controller to which we post events.
   const base::WeakPtr<VideoCaptureController> controller_;
 
@@ -341,8 +349,38 @@
 scoped_refptr<media::VideoCaptureDevice::Client::Buffer>
 VideoCaptureController::VideoCaptureDeviceClient::ReserveOutputBuffer(
     media::VideoFrame::Format format,
-    const gfx::Size& size) {
-  return DoReserveOutputBuffer(format, size);
+    const gfx::Size& dimensions) {
+  size_t frame_bytes = 0;
+  if (format == media::VideoFrame::NATIVE_TEXTURE) {
+    DCHECK_EQ(dimensions.width(), 0);
+    DCHECK_EQ(dimensions.height(), 0);
+  } else {
+    // The capture pipeline expects I420 for now.
+    DCHECK_EQ(format, media::VideoFrame::I420)
+        << " Non-I420 output buffer format " << format << " requested";
+    frame_bytes = media::VideoFrame::AllocationSize(format, dimensions);
+  }
+
+  int buffer_id_to_drop = VideoCaptureBufferPool::kInvalidId;
+  int buffer_id =
+      buffer_pool_->ReserveForProducer(frame_bytes, &buffer_id_to_drop);
+  if (buffer_id == VideoCaptureBufferPool::kInvalidId)
+    return NULL;
+  void* data;
+  size_t size;
+  buffer_pool_->GetBufferInfo(buffer_id, &data, &size);
+
+  scoped_refptr<media::VideoCaptureDevice::Client::Buffer> output_buffer(
+      new AutoReleaseBuffer(buffer_pool_, buffer_id, data, size));
+
+  if (buffer_id_to_drop != VideoCaptureBufferPool::kInvalidId) {
+    BrowserThread::PostTask(BrowserThread::IO,
+        FROM_HERE,
+        base::Bind(&VideoCaptureController::DoBufferDestroyedOnIOThread,
+                   controller_, buffer_id_to_drop));
+  }
+
+  return output_buffer;
 }
 
 void VideoCaptureController::VideoCaptureDeviceClient::OnIncomingCapturedData(
@@ -350,7 +388,7 @@
     int length,
     const VideoCaptureFormat& frame_format,
     int rotation,
-    base::TimeTicks timestamp) {
+    const base::TimeTicks& timestamp) {
   TRACE_EVENT0("video", "VideoCaptureController::OnIncomingCapturedData");
 
   if (last_captured_pixel_format_ != frame_format.pixel_format) {
@@ -392,9 +430,8 @@
     return;
   }
 
-  scoped_refptr<Buffer> buffer =
-      DoReserveOutputBuffer(media::VideoFrame::I420, dimensions);
-
+  scoped_refptr<Buffer> buffer = ReserveOutputBuffer(media::VideoFrame::I420,
+                                                     dimensions);
   if (!buffer.get())
     return;
   uint8* yplane = NULL;
@@ -469,6 +506,10 @@
       NOTREACHED();
   }
 
+  // The input |length| can be greater than the required buffer size because of
+  // paddings and/or alignments, but it cannot be less.
+  DCHECK_GE(static_cast<size_t>(length), frame_format.ImageAllocationSize());
+
   if (libyuv::ConvertToI420(data,
                             length,
                             yplane,
@@ -526,7 +567,7 @@
     const scoped_refptr<Buffer>& buffer,
     const VideoCaptureFormat& buffer_format,
     const scoped_refptr<media::VideoFrame>& frame,
-    base::TimeTicks timestamp) {
+    const base::TimeTicks& timestamp) {
   BrowserThread::PostTask(
       BrowserThread::IO,
       FROM_HERE,
@@ -558,43 +599,6 @@
   MediaStreamManager::SendMessageToNativeLog("Video capture: " + message);
 }
 
-scoped_refptr<media::VideoCaptureDevice::Client::Buffer>
-VideoCaptureController::VideoCaptureDeviceClient::DoReserveOutputBuffer(
-    media::VideoFrame::Format format,
-    const gfx::Size& dimensions) {
-  size_t frame_bytes = 0;
-  if (format == media::VideoFrame::NATIVE_TEXTURE) {
-    DCHECK_EQ(dimensions.width(), 0);
-    DCHECK_EQ(dimensions.height(), 0);
-  } else {
-    // The capture pipeline expects I420 for now.
-    DCHECK_EQ(format, media::VideoFrame::I420)
-        << "Non-I420 output buffer format " << format << " requested";
-    frame_bytes = media::VideoFrame::AllocationSize(format, dimensions);
-  }
-
-  int buffer_id_to_drop = VideoCaptureBufferPool::kInvalidId;
-  int buffer_id =
-      buffer_pool_->ReserveForProducer(frame_bytes, &buffer_id_to_drop);
-  if (buffer_id == VideoCaptureBufferPool::kInvalidId)
-    return NULL;
-  void* data;
-  size_t size;
-  buffer_pool_->GetBufferInfo(buffer_id, &data, &size);
-
-  scoped_refptr<media::VideoCaptureDevice::Client::Buffer> output_buffer(
-      new PoolBuffer(buffer_pool_, buffer_id, data, size));
-
-  if (buffer_id_to_drop != VideoCaptureBufferPool::kInvalidId) {
-    BrowserThread::PostTask(BrowserThread::IO,
-        FROM_HERE,
-        base::Bind(&VideoCaptureController::DoBufferDestroyedOnIOThread,
-                   controller_, buffer_id_to_drop));
-  }
-
-  return output_buffer;
-}
-
 VideoCaptureController::~VideoCaptureController() {
   STLDeleteContainerPointers(controller_clients_.begin(),
                              controller_clients_.end());
@@ -604,15 +608,13 @@
     const scoped_refptr<media::VideoCaptureDevice::Client::Buffer>& buffer,
     const media::VideoCaptureFormat& buffer_format,
     const scoped_refptr<media::VideoFrame>& frame,
-    base::TimeTicks timestamp) {
+    const base::TimeTicks& timestamp) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   DCHECK_NE(buffer->id(), VideoCaptureBufferPool::kInvalidId);
 
   int count = 0;
   if (state_ == VIDEO_CAPTURE_STATE_STARTED) {
-    for (ControllerClients::iterator client_it = controller_clients_.begin();
-         client_it != controller_clients_.end(); ++client_it) {
-      ControllerClient* client = *client_it;
+    for (const auto& client : controller_clients_) {
       if (client->session_closed || client->paused)
         continue;
 
diff --git a/content/browser/renderer_host/media/video_capture_controller.h b/content/browser/renderer_host/media/video_capture_controller.h
index 720213e..5d0d9ac6 100644
--- a/content/browser/renderer_host/media/video_capture_controller.h
+++ b/content/browser/renderer_host/media/video_capture_controller.h
@@ -135,21 +135,19 @@
       const scoped_refptr<media::VideoCaptureDevice::Client::Buffer>& buffer,
       const media::VideoCaptureFormat& format,
       const scoped_refptr<media::VideoFrame>& frame,
-      base::TimeTicks timestamp);
+      const base::TimeTicks& timestamp);
   void DoErrorOnIOThread();
   void DoDeviceStoppedOnIOThread();
   void DoBufferDestroyedOnIOThread(int buffer_id_to_drop);
 
   // Find a client of |id| and |handler| in |clients|.
-  ControllerClient* FindClient(
-      const VideoCaptureControllerID& id,
-      VideoCaptureControllerEventHandler* handler,
-      const ControllerClients& clients);
+  ControllerClient* FindClient(const VideoCaptureControllerID& id,
+                               VideoCaptureControllerEventHandler* handler,
+                               const ControllerClients& clients);
 
   // Find a client of |session_id| in |clients|.
-  ControllerClient* FindClient(
-      int session_id,
-      const ControllerClients& clients);
+  ControllerClient* FindClient(int session_id,
+                               const ControllerClients& clients);
 
   // The pool of shared-memory buffers used for capturing.
   const scoped_refptr<VideoCaptureBufferPool> buffer_pool_;
diff --git a/content/browser/renderer_host/media/video_capture_controller_unittest.cc b/content/browser/renderer_host/media/video_capture_controller_unittest.cc
index 45d43c9..d8ac2a24 100644
--- a/content/browser/renderer_host/media/video_capture_controller_unittest.cc
+++ b/content/browser/renderer_host/media/video_capture_controller_unittest.cc
@@ -141,7 +141,6 @@
   }
 
   scoped_refptr<media::VideoFrame> WrapMailboxBuffer(
-      const scoped_refptr<media::VideoCaptureDevice::Client::Buffer>& buffer,
       scoped_ptr<gpu::MailboxHolder> holder,
       const media::VideoFrame::ReleaseMailboxCB& release_cb,
       gfx::Size dimensions) {
@@ -285,7 +284,7 @@
 
   media::VideoCaptureParams session_1 = session_100;
 
-  gfx::Size capture_resolution(444, 200);
+  const gfx::Size capture_resolution(444, 200);
 
   // The device format needn't match the VideoCaptureParams (the camera can do
   // what it wants). Pick something random.
@@ -503,8 +502,7 @@
         media::VideoCaptureFormat(capture_resolution,
                                   device_format.frame_rate,
                                   media::PIXEL_FORMAT_TEXTURE),
-        WrapMailboxBuffer(buffer,
-                          make_scoped_ptr(new gpu::MailboxHolder(
+        WrapMailboxBuffer(make_scoped_ptr(new gpu::MailboxHolder(
                               gpu::Mailbox(), 0, mailbox_syncpoints[i])),
                           base::Bind(&CacheSyncPoint, &release_syncpoints[i]),
                           capture_resolution),
@@ -628,12 +626,22 @@
   Mock::VerifyAndClearExpectations(client_b_.get());
 }
 
+// Tests that buffer-based capture API accepts all memory-backed pixel formats.
 TEST_F(VideoCaptureControllerTest, DataCaptureInEachVideoFormatInSequence) {
-  // This Test will skip PIXEL_FORMAT_TEXTURE and PIXEL_FORMAT_UNKNOWN
+  // The usual ReserveOutputBuffer() -> OnIncomingCapturedVideoFrame() cannot
+  // be used since it does not accept all pixel formats. The memory backed
+  // buffer OnIncomingCapturedData() is used instead, with a dummy scratchpad
+  // buffer.
+  const size_t kScratchpadSizeInBytes = 400;
+  unsigned char data[kScratchpadSizeInBytes];
+  const gfx::Size capture_resolution(10, 10);
+  ASSERT_GE(kScratchpadSizeInBytes, capture_resolution.GetArea() * 4u)
+      << "Scratchpad is too small to hold the largest pixel format (ARGB).";
+  // This Test skips PIXEL_FORMAT_TEXTURE and PIXEL_FORMAT_UNKNOWN.
   for (int format = 0; format < media::PIXEL_FORMAT_TEXTURE; ++format) {
     media::VideoCaptureParams params;
     params.requested_format = media::VideoCaptureFormat(
-        gfx::Size(320, 240), 30, media::VideoPixelFormat(format));
+        capture_resolution, 30, media::VideoPixelFormat(format));
 
     const gfx::Size capture_resolution(320, 240);
 
@@ -646,21 +654,14 @@
                            100,
                            params);
     ASSERT_EQ(1, controller_->GetClientCount());
-
-    // Now, simulate an incoming captured buffer from the capture device.
-    scoped_refptr<media::VideoCaptureDevice::Client::Buffer> buffer;
-    buffer =
-        device_->ReserveOutputBuffer(media::VideoFrame::I420,
-                                     capture_resolution);
-    ASSERT_TRUE(buffer.get());
-
-    // Captured a new video frame.
     device_->OnIncomingCapturedData(
-              static_cast<unsigned char*>(buffer.get()->data()),
-              buffer.get()->size(),
-              params.requested_format,
-              0,
-              base::TimeTicks());
+      data,
+      params.requested_format.ImageAllocationSize(),
+      params.requested_format,
+      0 /* rotation */,
+      base::TimeTicks());
+    EXPECT_EQ(100, controller_->RemoveClient(route, client_a_.get()));
+    Mock::VerifyAndClearExpectations(client_a_.get());
   }
 }
 
diff --git a/content/browser/renderer_host/pepper/browser_ppapi_host_impl.cc b/content/browser/renderer_host/pepper/browser_ppapi_host_impl.cc
index d9539ee5..3011603 100644
--- a/content/browser/renderer_host/pepper/browser_ppapi_host_impl.cc
+++ b/content/browser/renderer_host/pepper/browser_ppapi_host_impl.cc
@@ -85,22 +85,22 @@
 }
 
 bool BrowserPpapiHostImpl::IsValidInstance(PP_Instance instance) const {
-  return instance_map_.find(instance) != instance_map_.end();
+  return instance_map_.contains(instance);
 }
 
 bool BrowserPpapiHostImpl::GetRenderFrameIDsForInstance(
     PP_Instance instance,
     int* render_process_id,
     int* render_frame_id) const {
-  InstanceMap::const_iterator found = instance_map_.find(instance);
-  if (found == instance_map_.end()) {
+  auto* data = instance_map_.get(instance);
+  if (data == nullptr) {
     *render_process_id = 0;
     *render_frame_id = 0;
     return false;
   }
 
-  *render_process_id = found->second.render_process_id;
-  *render_frame_id = found->second.render_frame_id;
+  *render_process_id = data->renderer_data.render_process_id;
+  *render_frame_id = data->renderer_data.render_frame_id;
   return true;
 }
 
@@ -117,17 +117,17 @@
 }
 
 GURL BrowserPpapiHostImpl::GetDocumentURLForInstance(PP_Instance instance) {
-  InstanceMap::const_iterator found = instance_map_.find(instance);
-  if (found == instance_map_.end())
+  auto* data = instance_map_.get(instance);
+  if (data == nullptr)
     return GURL();
-  return found->second.document_url;
+  return data->renderer_data.document_url;
 }
 
 GURL BrowserPpapiHostImpl::GetPluginURLForInstance(PP_Instance instance) {
-  InstanceMap::const_iterator found = instance_map_.find(instance);
-  if (found == instance_map_.end())
+  auto* data = instance_map_.get(instance);
+  if (data == nullptr)
     return GURL();
-  return found->second.plugin_url;
+  return data->renderer_data.plugin_url;
 }
 
 void BrowserPpapiHostImpl::SetOnKeepaliveCallback(
@@ -137,18 +137,45 @@
 
 void BrowserPpapiHostImpl::AddInstance(
     PP_Instance instance,
-    const PepperRendererInstanceData& instance_data) {
-  DCHECK(instance_map_.find(instance) == instance_map_.end());
-  instance_map_[instance] = instance_data;
+    const PepperRendererInstanceData& renderer_instance_data) {
+  DCHECK(!instance_map_.contains(instance));
+  instance_map_.add(instance,
+                    make_scoped_ptr(new InstanceData(renderer_instance_data)));
 }
 
 void BrowserPpapiHostImpl::DeleteInstance(PP_Instance instance) {
-  InstanceMap::iterator found = instance_map_.find(instance);
-  if (found == instance_map_.end()) {
-    NOTREACHED();
-    return;
+  int erased = instance_map_.erase(instance);
+  DCHECK_EQ(1, erased);
+}
+
+void BrowserPpapiHostImpl::AddInstanceObserver(PP_Instance instance,
+                                               InstanceObserver* observer) {
+  instance_map_.get(instance)->observer_list.AddObserver(observer);
+}
+
+void BrowserPpapiHostImpl::RemoveInstanceObserver(PP_Instance instance,
+                                                  InstanceObserver* observer) {
+  auto* data = instance_map_.get(instance);
+  if (data)
+    data->observer_list.RemoveObserver(observer);
+}
+
+void BrowserPpapiHostImpl::OnThrottleStateChanged(PP_Instance instance,
+                                                  bool is_throttled) {
+  auto* data = instance_map_.get(instance);
+  if (data) {
+    data->is_throttled = is_throttled;
+    FOR_EACH_OBSERVER(InstanceObserver, data->observer_list,
+                      OnThrottleStateChanged(is_throttled));
   }
-  instance_map_.erase(found);
+}
+
+bool BrowserPpapiHostImpl::IsThrottled(PP_Instance instance) const {
+  auto* data = instance_map_.get(instance);
+  if (data)
+    return data->is_throttled;
+
+  return false;
 }
 
 BrowserPpapiHostImpl::HostMessageFilter::HostMessageFilter(
@@ -192,6 +219,14 @@
   UMA_HISTOGRAM_SPARSE_SLOWLY("Pepper.InterfaceUsed", hash);
 }
 
+BrowserPpapiHostImpl::InstanceData::InstanceData(
+    const PepperRendererInstanceData& renderer_data)
+    : renderer_data(renderer_data), is_throttled(false) {
+}
+
+BrowserPpapiHostImpl::InstanceData::~InstanceData() {
+}
+
 void BrowserPpapiHostImpl::OnKeepalive() {
   // An instance has been active. The on_keepalive_callback_ will be
   // used to permit the content embedder to handle this, e.g. by tracking
@@ -206,12 +241,15 @@
 
   BrowserPpapiHost::OnKeepaliveInstanceData instance_data(instance_map_.size());
 
-  InstanceMap::iterator instance = instance_map_.begin();
+  auto instance = instance_map_.begin();
   int i = 0;
   while (instance != instance_map_.end()) {
-    instance_data[i].render_process_id = instance->second.render_process_id;
-    instance_data[i].render_frame_id = instance->second.render_frame_id;
-    instance_data[i].document_url = instance->second.document_url;
+    instance_data[i].render_process_id =
+        instance->second->renderer_data.render_process_id;
+    instance_data[i].render_frame_id =
+        instance->second->renderer_data.render_frame_id;
+    instance_data[i].document_url =
+        instance->second->renderer_data.document_url;
     ++instance;
     ++i;
   }
diff --git a/content/browser/renderer_host/pepper/browser_ppapi_host_impl.h b/content/browser/renderer_host/pepper/browser_ppapi_host_impl.h
index 101e0eba..b0445c8 100644
--- a/content/browser/renderer_host/pepper/browser_ppapi_host_impl.h
+++ b/content/browser/renderer_host/pepper/browser_ppapi_host_impl.h
@@ -10,9 +10,11 @@
 
 #include "base/basictypes.h"
 #include "base/compiler_specific.h"
+#include "base/containers/scoped_ptr_hash_map.h"
 #include "base/files/file_path.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
+#include "base/observer_list.h"
 #include "base/process/process.h"
 #include "content/browser/renderer_host/pepper/content_browser_pepper_host_factory.h"
 #include "content/browser/renderer_host/pepper/ssl_context_helper.h"
@@ -31,6 +33,13 @@
 
 class CONTENT_EXPORT BrowserPpapiHostImpl : public BrowserPpapiHost {
  public:
+  class InstanceObserver {
+   public:
+    // Called when the plugin instance is throttled or unthrottled because of
+    // the Plugin Power Saver feature. Invoked on the IO thread.
+    virtual void OnThrottleStateChanged(bool is_throttled) = 0;
+  };
+
   // The creator is responsible for calling set_plugin_process as soon as it is
   // known (we start the process asynchronously so it won't be known when this
    // object is created).
@@ -70,9 +79,15 @@
   // or destroyed. They allow us to maintain a mapping of PP_Instance to data
   // associated with the instance including view IDs in the browser process.
   void AddInstance(PP_Instance instance,
-                   const PepperRendererInstanceData& instance_data);
+                   const PepperRendererInstanceData& renderer_instance_data);
   void DeleteInstance(PP_Instance instance);
 
+  void AddInstanceObserver(PP_Instance instance, InstanceObserver* observer);
+  void RemoveInstanceObserver(PP_Instance instance, InstanceObserver* observer);
+
+  void OnThrottleStateChanged(PP_Instance instance, bool is_throttled);
+  bool IsThrottled(PP_Instance instance) const;
+
   scoped_refptr<IPC::MessageFilter> message_filter() {
     return message_filter_;
   }
@@ -108,6 +123,16 @@
     BrowserPpapiHostImpl* browser_ppapi_host_impl_;
   };
 
+  struct InstanceData {
+    InstanceData(const PepperRendererInstanceData& renderer_data);
+    ~InstanceData();
+
+    PepperRendererInstanceData renderer_data;
+    bool is_throttled;
+
+    ObserverList<InstanceObserver> observer_list;
+  };
+
   // Reports plugin activity to the callback set with SetOnKeepaliveCallback.
   void OnKeepalive();
 
@@ -126,10 +151,8 @@
 
   scoped_refptr<SSLContextHelper> ssl_context_helper_;
 
-  // Tracks all PP_Instances in this plugin and associated renderer-related
-  // data.
-  typedef std::map<PP_Instance, PepperRendererInstanceData> InstanceMap;
-  InstanceMap instance_map_;
+  // Tracks all PP_Instances in this plugin and associated data.
+  base::ScopedPtrHashMap<PP_Instance, InstanceData> instance_map_;
 
   scoped_refptr<HostMessageFilter> message_filter_;
 
diff --git a/content/browser/renderer_host/pepper/pepper_tcp_socket_message_filter.cc b/content/browser/renderer_host/pepper/pepper_tcp_socket_message_filter.cc
index b1437a4..f323623b 100644
--- a/content/browser/renderer_host/pepper/pepper_tcp_socket_message_filter.cc
+++ b/content/browser/renderer_host/pepper/pepper_tcp_socket_message_filter.cc
@@ -10,7 +10,6 @@
 #include "base/logging.h"
 #include "base/profiler/scoped_tracker.h"
 #include "build/build_config.h"
-#include "content/browser/renderer_host/pepper/browser_ppapi_host_impl.h"
 #include "content/browser/renderer_host/pepper/content_browser_pepper_host_factory.h"
 #include "content/browser/renderer_host/pepper/pepper_socket_utils.h"
 #include "content/public/browser/browser_context.h"
@@ -58,7 +57,7 @@
       external_plugin_(host->external_plugin()),
       render_process_id_(0),
       render_frame_id_(0),
-      ppapi_host_(host->GetPpapiHost()),
+      host_(host),
       factory_(factory),
       instance_(instance),
       state_(TCPSocketState::INITIAL),
@@ -70,9 +69,12 @@
       address_index_(0),
       socket_(new net::TCPSocket(NULL, net::NetLog::Source())),
       ssl_context_helper_(host->ssl_context_helper()),
-      pending_accept_(false) {
+      pending_accept_(false),
+      pending_read_on_unthrottle_(false),
+      pending_read_net_result_(0) {
   DCHECK(host);
   ++g_num_instances;
+  host_->AddInstanceObserver(instance_, this);
   if (!host->GetRenderFrameIDsForInstance(
           instance, &render_process_id_, &render_frame_id_)) {
     NOTREACHED();
@@ -88,7 +90,7 @@
       external_plugin_(host->external_plugin()),
       render_process_id_(0),
       render_frame_id_(0),
-      ppapi_host_(host->GetPpapiHost()),
+      host_(host),
       factory_(NULL),
       instance_(instance),
       state_(TCPSocketState::CONNECTED),
@@ -100,11 +102,14 @@
       address_index_(0),
       socket_(socket.Pass()),
       ssl_context_helper_(host->ssl_context_helper()),
-      pending_accept_(false) {
+      pending_accept_(false),
+      pending_read_on_unthrottle_(false),
+      pending_read_net_result_(0) {
   DCHECK(host);
   DCHECK_NE(version, ppapi::TCP_SOCKET_VERSION_1_0);
 
   ++g_num_instances;
+  host_->AddInstanceObserver(instance_, this);
   if (!host->GetRenderFrameIDsForInstance(
           instance, &render_process_id_, &render_frame_id_)) {
     NOTREACHED();
@@ -112,6 +117,7 @@
 }
 
 PepperTCPSocketMessageFilter::~PepperTCPSocketMessageFilter() {
+  host_->RemoveInstanceObserver(instance_, this);
   if (socket_)
     socket_->Close();
   if (ssl_socket_)
@@ -170,6 +176,16 @@
   return PP_ERROR_FAILED;
 }
 
+void PepperTCPSocketMessageFilter::OnThrottleStateChanged(bool is_throttled) {
+  if (pending_read_on_unthrottle_ && !is_throttled) {
+    DCHECK(read_buffer_);
+    OnReadCompleted(pending_read_reply_message_context_,
+                    pending_read_net_result_);
+    DCHECK(!read_buffer_);
+    pending_read_on_unthrottle_ = false;
+  }
+}
+
 int32_t PepperTCPSocketMessageFilter::OnMsgBind(
     const ppapi::host::HostMessageContext* context,
     const PP_NetAddress_Private& net_addr) {
@@ -857,6 +873,13 @@
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   DCHECK(read_buffer_.get());
 
+  if (host_->IsThrottled(instance_)) {
+    pending_read_on_unthrottle_ = true;
+    pending_read_reply_message_context_ = context;
+    pending_read_net_result_ = net_result;
+    return;
+  }
+
   if (net_result > 0) {
     SendReadReply(
         context, PP_OK, std::string(read_buffer_->data(), net_result));
@@ -942,7 +965,8 @@
     SendAcceptError(context, PP_ERROR_NOSPACE);
     return;
   }
-  int pending_host_id = ppapi_host_->AddPendingResourceHost(host.Pass());
+  int pending_host_id =
+      host_->GetPpapiHost()->AddPendingResourceHost(host.Pass());
   if (pending_host_id)
     SendAcceptReply(context, PP_OK, pending_host_id, local_addr, remote_addr);
   else
diff --git a/content/browser/renderer_host/pepper/pepper_tcp_socket_message_filter.h b/content/browser/renderer_host/pepper/pepper_tcp_socket_message_filter.h
index d0e672aa..e88a1e64 100644
--- a/content/browser/renderer_host/pepper/pepper_tcp_socket_message_filter.h
+++ b/content/browser/renderer_host/pepper/pepper_tcp_socket_message_filter.h
@@ -12,6 +12,7 @@
 #include "base/compiler_specific.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/scoped_ptr.h"
+#include "content/browser/renderer_host/pepper/browser_ppapi_host_impl.h"
 #include "content/browser/renderer_host/pepper/ssl_context_helper.h"
 #include "content/common/content_export.h"
 #include "net/base/address_list.h"
@@ -47,7 +48,8 @@
 class ResourceContext;
 
 class CONTENT_EXPORT PepperTCPSocketMessageFilter
-    : public ppapi::host::ResourceMessageFilter {
+    : public ppapi::host::ResourceMessageFilter,
+      public BrowserPpapiHostImpl::InstanceObserver {
  public:
   PepperTCPSocketMessageFilter(ContentBrowserPepperHostFactory* factory,
                                BrowserPpapiHostImpl* host,
@@ -78,6 +80,9 @@
       const IPC::Message& msg,
       ppapi::host::HostMessageContext* context) override;
 
+  // BrowserPpapiHostImpl::InstanceObserver overrides.
+  void OnThrottleStateChanged(bool is_throttled) override;
+
   int32_t OnMsgBind(const ppapi::host::HostMessageContext* context,
                     const PP_NetAddress_Private& net_addr);
   int32_t OnMsgConnect(const ppapi::host::HostMessageContext* context,
@@ -176,7 +181,7 @@
 
   // The following fields are used only on the IO thread.
   // Non-owning ptr.
-  ppapi::host::PpapiHost* ppapi_host_;
+  BrowserPpapiHostImpl* host_;
   // Non-owning ptr.
   ContentBrowserPepperHostFactory* factory_;
   PP_Instance instance_;
@@ -226,6 +231,12 @@
   scoped_ptr<net::TCPSocket> accepted_socket_;
   net::IPEndPoint accepted_address_;
 
+  // If the plugin is throttled, we defer completing socket reads until
+  // the plugin is unthrottled.
+  bool pending_read_on_unthrottle_;
+  ppapi::host::ReplyMessageContext pending_read_reply_message_context_;
+  int pending_read_net_result_;
+
   DISALLOW_COPY_AND_ASSIGN(PepperTCPSocketMessageFilter);
 };
 
diff --git a/content/browser/renderer_host/render_message_filter.cc b/content/browser/renderer_host/render_message_filter.cc
index c208323..bcf1aca 100644
--- a/content/browser/renderer_host/render_message_filter.cc
+++ b/content/browser/renderer_host/render_message_filter.cc
@@ -395,6 +395,8 @@
                         OnDidDeleteOutOfProcessPepperInstance)
     IPC_MESSAGE_HANDLER(ViewHostMsg_OpenChannelToPpapiBroker,
                         OnOpenChannelToPpapiBroker)
+    IPC_MESSAGE_HANDLER(ViewHostMsg_PluginInstanceThrottleStateChange,
+                        OnPluginInstanceThrottleStateChange)
 #endif
 #if defined(OS_MACOSX)
     IPC_MESSAGE_HANDLER_GENERIC(ViewHostMsg_SwapCompositorFrame,
@@ -817,6 +819,15 @@
       path,
       new OpenChannelToPpapiBrokerCallback(this, routing_id));
 }
+
+void RenderMessageFilter::OnPluginInstanceThrottleStateChange(
+    int plugin_child_id,
+    int32 pp_instance,
+    bool is_throttled) {
+  // Feature is only implemented for non-external Plugins.
+  PpapiPluginProcessHost::OnPluginInstanceThrottleStateChange(
+      plugin_child_id, pp_instance, is_throttled);
+}
 #endif  // defined(ENABLE_PLUGINS)
 
 void RenderMessageFilter::OnGenerateRoutingID(int* route_id) {
@@ -977,7 +988,7 @@
 
 void RenderMessageFilter::OnCacheableMetadataAvailable(
     const GURL& url,
-    double expected_response_time,
+    base::Time expected_response_time,
     const std::vector<char>& data) {
   net::HttpCache* cache = request_context_->GetURLRequestContext()->
       http_transaction_factory()->GetCache();
diff --git a/content/browser/renderer_host/render_message_filter.h b/content/browser/renderer_host/render_message_filter.h
index 8969992e..d8582e6a 100644
--- a/content/browser/renderer_host/render_message_filter.h
+++ b/content/browser/renderer_host/render_message_filter.h
@@ -205,6 +205,9 @@
                                              bool is_external);
   void OnOpenChannelToPpapiBroker(int routing_id,
                                   const base::FilePath& path);
+  void OnPluginInstanceThrottleStateChange(int plugin_child_id,
+                                           int32 pp_instance,
+                                           bool is_throttled);
 #endif  // defined(ENABLE_PLUGINS)
   void OnGenerateRoutingID(int* route_id);
   void OnDownloadUrl(int render_view_id,
@@ -244,7 +247,7 @@
       base::SharedMemoryHandle* handle);
 
   void OnCacheableMetadataAvailable(const GURL& url,
-                                    double expected_response_time,
+                                    base::Time expected_response_time,
                                     const std::vector<char>& data);
   void OnKeygen(uint32 key_size_index, const std::string& challenge_string,
                 const GURL& url, IPC::Message* reply_msg);
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc
index 68b50c7..e36c0fd 100644
--- a/content/browser/renderer_host/render_process_host_impl.cc
+++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -1260,6 +1260,7 @@
     switches::kEnablePinch,
     switches::kEnablePreciseMemoryInfo,
     switches::kEnablePushMessagePayload,
+    switches::kEnablePushMessagingHasPermission,
     switches::kEnableRendererMojoChannel,
     switches::kEnableSeccompFilterSandbox,
     switches::kEnableSkiaBenchmarking,
@@ -2207,17 +2208,27 @@
     return;
 #endif  // OS_WIN
 
+#if defined(OS_WIN)
+  // Same as below, but bound to an experiment (http://crbug.com/458594)
+  // initially on Windows. Enabled by default in the asbence of field trials to
+  // get coverage on the perf waterfall.
+  base::FieldTrial* trial =
+      base::FieldTrialList::Find("BackgroundRendererProcesses");
+  if (!trial || (trial->group_name() != "Disallow" &&
+                 trial->group_name() != "AllowBackgroundModeFromRenderer")) {
+    child_process_launcher_->SetProcessBackgrounded(backgrounded);
+  }
+#else
+  // Control the background state from the browser process, otherwise the task
+  // telling the renderer to "unbackground" itself may be preempted by other
+  // tasks executing at lowered priority ahead of it or simply by not being
+  // swiftly scheduled by the OS per the low process priority
+  // (http://crbug.com/398103).
+  child_process_launcher_->SetProcessBackgrounded(backgrounded);
+#endif  // OS_WIN
+
   // Notify the child process of background state.
   Send(new ChildProcessMsg_SetProcessBackgrounded(backgrounded));
-
-#if !defined(OS_WIN)
-  // Backgrounding may require elevated privileges not available to renderer
-  // processes, so control backgrounding from the process host.
-
-  // Windows Vista+ has a fancy process backgrounding mode that can only be set
-  // from within the process.
-  child_process_launcher_->SetProcessBackgrounded(backgrounded);
-#endif  // !OS_WIN
 }
 
 void RenderProcessHostImpl::OnProcessLaunched() {
diff --git a/content/browser/renderer_host/render_widget_host_impl.cc b/content/browser/renderer_host/render_widget_host_impl.cc
index 4d50c1c..727a14b 100644
--- a/content/browser/renderer_host/render_widget_host_impl.cc
+++ b/content/browser/renderer_host/render_widget_host_impl.cc
@@ -880,6 +880,10 @@
   RendererIsResponsive();
 }
 
+void RenderWidgetHostImpl::DisableElasticOverscroll() {
+  // TODO(dgozman): Send an IPC handled by InputHandlerProxy.
+}
+
 void RenderWidgetHostImpl::ForwardMouseEvent(const WebMouseEvent& mouse_event) {
   ForwardMouseEventWithLatencyInfo(mouse_event, ui::LatencyInfo());
 }
diff --git a/content/browser/renderer_host/render_widget_host_impl.h b/content/browser/renderer_host/render_widget_host_impl.h
index a5136ce..7e965c7 100644
--- a/content/browser/renderer_host/render_widget_host_impl.h
+++ b/content/browser/renderer_host/render_widget_host_impl.h
@@ -276,6 +276,9 @@
   // responsive.
   void StopHangMonitorTimeout();
 
+  // Disables elastic overscroll effect if enabled.
+  void DisableElasticOverscroll();
+
   // Forwards the given message to the renderer. These are called by the view
   // when it has received a message.
   void ForwardGestureEventWithLatencyInfo(
diff --git a/content/browser/renderer_host/render_widget_host_latency_tracker.cc b/content/browser/renderer_host/render_widget_host_latency_tracker.cc
index 2d738c4..9cbd986 100644
--- a/content/browser/renderer_host/render_widget_host_latency_tracker.cc
+++ b/content/browser/renderer_host/render_widget_host_latency_tracker.cc
@@ -135,6 +135,20 @@
   }
 }
 
+// Long scroll latency component that is mostly under 200ms.
+#define UMA_HISTOGRAM_SCROLL_LATENCY_LONG(name, start, end)        \
+  UMA_HISTOGRAM_CUSTOM_COUNTS(                                     \
+    name,                                                          \
+    (end.event_time - start.event_time).InMicroseconds(),          \
+    1000, 200000, 50)
+
+// Short scroll latency component that is mostly under 50ms.
+#define UMA_HISTOGRAM_SCROLL_LATENCY_SHORT(name, start, end)       \
+  UMA_HISTOGRAM_CUSTOM_COUNTS(                                     \
+    name,                                                          \
+    (end.event_time - start.event_time).InMicroseconds(),          \
+    1, 50000, 50)
+
 void ComputeScrollLatencyHistograms(
     const LatencyInfo::LatencyComponent& swap_component,
     int64 latency_component_id,
@@ -171,6 +185,67 @@
             .InMicroseconds(),
         1, 1000000, 100);
   }
+
+  // TODO(miletus): Add validation for making sure the following components
+  // are present and their event times are legit.
+  LatencyInfo::LatencyComponent rendering_scheduled_component;
+  bool rendering_scheduled_on_main = latency.FindLatency(
+      ui::INPUT_EVENT_LATENCY_RENDERING_SCHEDULED_MAIN_COMPONENT,
+      0, &rendering_scheduled_component);
+
+  if (!rendering_scheduled_on_main) {
+    if (!latency.FindLatency(
+            ui::INPUT_EVENT_LATENCY_RENDERING_SCHEDULED_IMPL_COMPONENT,
+            0, &rendering_scheduled_component))
+      return;
+  }
+
+  if (rendering_scheduled_on_main) {
+    UMA_HISTOGRAM_SCROLL_LATENCY_LONG(
+        "Event.Latency.ScrollUpdate.TouchToHandled_Main",
+        original_component, rendering_scheduled_component);
+  } else {
+    UMA_HISTOGRAM_SCROLL_LATENCY_LONG(
+        "Event.Latency.ScrollUpdate.TouchToHandled_Impl",
+        original_component, rendering_scheduled_component);
+  }
+
+  LatencyInfo::LatencyComponent renderer_swap_component;
+  if (!latency.FindLatency(ui::INPUT_EVENT_LATENCY_RENDERER_SWAP_COMPONENT,
+                           0, &renderer_swap_component))
+    return;
+
+  if (rendering_scheduled_on_main) {
+    UMA_HISTOGRAM_SCROLL_LATENCY_LONG(
+        "Event.Latency.ScrollUpdate.HandledToRendererSwap_Main",
+        rendering_scheduled_component, renderer_swap_component);
+  } else {
+    UMA_HISTOGRAM_SCROLL_LATENCY_LONG(
+        "Event.Latency.ScrollUpdate.HandledToRendererSwap_Impl",
+        rendering_scheduled_component, renderer_swap_component);
+  }
+
+  LatencyInfo::LatencyComponent browser_received_swap_component;
+  if (!latency.FindLatency(
+          ui::INPUT_EVENT_BROWSER_RECEIVED_RENDERER_SWAP_COMPONENT,
+          0, &browser_received_swap_component))
+    return;
+
+  UMA_HISTOGRAM_SCROLL_LATENCY_SHORT(
+      "Event.Latency.ScrollUpdate.RendererSwapToBrowserNotified",
+      renderer_swap_component, browser_received_swap_component);
+
+  LatencyInfo::LatencyComponent gpu_swap_component;
+  if (!latency.FindLatency(ui::INPUT_EVENT_GPU_SWAP_BUFFER_COMPONENT,
+                           0, &gpu_swap_component))
+    return;
+
+  UMA_HISTOGRAM_SCROLL_LATENCY_LONG(
+      "Event.Latency.ScrollUpdate.BrowserNotifiedToBeforeGpuSwap",
+      browser_received_swap_component, gpu_swap_component);
+
+  UMA_HISTOGRAM_SCROLL_LATENCY_SHORT("Event.Latency.ScrollUpdate.GpuSwap",
+                                     gpu_swap_component, swap_component);
 }
 
 // LatencyComponents generated in the renderer must have component IDs
@@ -264,7 +339,9 @@
 
   // Latency ends when it is acked but does not cause render scheduling.
   bool rendering_scheduled = latency->FindLatency(
-      ui::INPUT_EVENT_LATENCY_RENDERING_SCHEDULED_COMPONENT, 0, NULL);
+      ui::INPUT_EVENT_LATENCY_RENDERING_SCHEDULED_MAIN_COMPONENT, 0, nullptr);
+  rendering_scheduled |= latency->FindLatency(
+      ui::INPUT_EVENT_LATENCY_RENDERING_SCHEDULED_IMPL_COMPONENT, 0, nullptr);
 
   if (WebInputEvent::isGestureEventType(event.type)) {
     if (!rendering_scheduled) {
diff --git a/content/browser/renderer_host/render_widget_host_view_aura.cc b/content/browser/renderer_host/render_widget_host_view_aura.cc
index 270453d..63c1609 100644
--- a/content/browser/renderer_host/render_widget_host_view_aura.cc
+++ b/content/browser/renderer_host/render_widget_host_view_aura.cc
@@ -1163,22 +1163,42 @@
 }
 
 void RenderWidgetHostViewAura::ProcessAckedTouchEvent(
-    const TouchEventWithLatencyInfo& touch, InputEventAckState ack_result) {
+    const TouchEventWithLatencyInfo& touch,
+    InputEventAckState ack_result) {
   ScopedVector<ui::TouchEvent> events;
-  if (!MakeUITouchEventsFromWebTouchEvents(touch, &events,
-                                           SCREEN_COORDINATES))
-    return;
-
   aura::WindowTreeHost* host = window_->GetHost();
   // |host| is NULL during tests.
   if (!host)
     return;
 
-  ui::EventResult result = (ack_result ==
-      INPUT_EVENT_ACK_STATE_CONSUMED) ? ui::ER_HANDLED : ui::ER_UNHANDLED;
-  for (ScopedVector<ui::TouchEvent>::iterator iter = events.begin(),
-      end = events.end(); iter != end; ++iter) {
-    host->dispatcher()->ProcessedTouchEvent((*iter), window_, result);
+  ui::EventResult result = (ack_result == INPUT_EVENT_ACK_STATE_CONSUMED)
+                               ? ui::ER_HANDLED
+                               : ui::ER_UNHANDLED;
+
+  blink::WebTouchPoint::State required_state;
+  switch (touch.event.type) {
+    case blink::WebInputEvent::TouchStart:
+      required_state = blink::WebTouchPoint::StatePressed;
+      break;
+    case blink::WebInputEvent::TouchEnd:
+      required_state = blink::WebTouchPoint::StateReleased;
+      break;
+    case blink::WebInputEvent::TouchMove:
+      required_state = blink::WebTouchPoint::StateMoved;
+      break;
+    case blink::WebInputEvent::TouchCancel:
+      required_state = blink::WebTouchPoint::StateCancelled;
+      break;
+    default:
+      required_state = blink::WebTouchPoint::StateUndefined;
+      NOTREACHED();
+      break;
+  }
+
+  // Only send acks for changed touches.
+  for (size_t i = 0; i < touch.event.touchesLength; ++i) {
+    if (touch.event.touches[i].state == required_state)
+      host->dispatcher()->ProcessedTouchEvent(window_, result);
   }
 }
 
@@ -1650,11 +1670,11 @@
   host_->CandidateWindowHidden();
 }
 
-bool RenderWidgetHostViewAura::IsEditingCommandEnabled(int command_id) {
+bool RenderWidgetHostViewAura::IsEditCommandEnabled(int command_id) {
   return false;
 }
 
-void RenderWidgetHostViewAura::ExecuteEditingCommand(int command_id) {
+void RenderWidgetHostViewAura::SetEditCommandForNextKeyEvent(int command_id) {
 }
 
 ////////////////////////////////////////////////////////////////////////////////
diff --git a/content/browser/renderer_host/render_widget_host_view_aura.h b/content/browser/renderer_host/render_widget_host_view_aura.h
index f98221c..d3e72e2 100644
--- a/content/browser/renderer_host/render_widget_host_view_aura.h
+++ b/content/browser/renderer_host/render_widget_host_view_aura.h
@@ -263,8 +263,8 @@
   void OnCandidateWindowShown() override;
   void OnCandidateWindowUpdated() override;
   void OnCandidateWindowHidden() override;
-  bool IsEditingCommandEnabled(int command_id) override;
-  void ExecuteEditingCommand(int command_id) override;
+  bool IsEditCommandEnabled(int command_id) override;
+  void SetEditCommandForNextKeyEvent(int command_id) override;
 
   // Overridden from gfx::DisplayObserver:
   void OnDisplayAdded(const gfx::Display& new_display) override;
diff --git a/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc b/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc
index d643e4f..9a6042d4 100644
--- a/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc
+++ b/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc
@@ -43,6 +43,7 @@
 #include "ui/aura/env.h"
 #include "ui/aura/layout_manager.h"
 #include "ui/aura/test/aura_test_helper.h"
+#include "ui/aura/test/aura_test_utils.h"
 #include "ui/aura/test/test_cursor_client.h"
 #include "ui/aura/test/test_screen.h"
 #include "ui/aura/test/test_window_delegate.h"
@@ -208,6 +209,26 @@
   base::Callback<void(bool)> callback_;
 };
 
+class FakeWindowEventDispatcher : public aura::WindowEventDispatcher {
+ public:
+  FakeWindowEventDispatcher(aura::WindowTreeHost* host)
+      : WindowEventDispatcher(host),
+        processed_touch_event_count_(0) {}
+
+  void ProcessedTouchEvent(aura::Window* window,
+                           ui::EventResult result) override {
+    WindowEventDispatcher::ProcessedTouchEvent(window, result);
+    processed_touch_event_count_++;
+  }
+
+  size_t processed_touch_event_count() {
+    return processed_touch_event_count_;
+  }
+
+ private:
+  size_t processed_touch_event_count_;
+};
+
 class FakeRenderWidgetHostViewAura : public RenderWidgetHostViewAura {
  public:
   FakeRenderWidgetHostViewAura(RenderWidgetHost* widget,
@@ -215,6 +236,12 @@
       : RenderWidgetHostViewAura(widget, is_guest_view_hack),
         has_resize_lock_(false) {}
 
+  void UseFakeDispatcher() {
+    dispatcher_ = new FakeWindowEventDispatcher(window()->GetHost());
+    scoped_ptr<aura::WindowEventDispatcher> dispatcher(dispatcher_);
+    aura::test::SetHostDispatcher(window()->GetHost(), dispatcher.Pass());
+  }
+
   ~FakeRenderWidgetHostViewAura() override {}
 
   scoped_ptr<ResizeLock> DelegatedFrameHostCreateResizeLock(
@@ -285,6 +312,7 @@
   scoped_ptr<cc::CopyOutputRequest> last_copy_request_;
   // null if there are 0 active touch points.
   scoped_ptr<blink::WebTouchEvent> touch_event_;
+  FakeWindowEventDispatcher* dispatcher_;
 };
 
 // A layout manager that always resizes a child to the root window size.
@@ -971,9 +999,9 @@
   sink_->ClearMessages();
 
   // Simulates the mouse press.
-  ui::MouseEvent mouse_event(ui::ET_MOUSE_PRESSED,
-                             gfx::Point(), gfx::Point(),
-                             ui::EF_LEFT_MOUSE_BUTTON, 0);
+  ui::MouseEvent mouse_event(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(),
+                             ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
+                             0);
   view_->OnMouseEvent(&mouse_event);
 
   EXPECT_FALSE(view_->has_composition_text_);
@@ -3119,9 +3147,8 @@
   sink_->ClearMessages();
 
   // Simulates the mouse wheel event with ctrl modifier applied.
-  ui::MouseWheelEvent event(gfx::Vector2d(1, 1),
-                             gfx::Point(), gfx::Point(),
-                             ui::EF_CONTROL_DOWN, 0);
+  ui::MouseWheelEvent event(gfx::Vector2d(1, 1), gfx::Point(), gfx::Point(),
+                            ui::EventTimeForNow(), ui::EF_CONTROL_DOWN, 0);
   view_->OnMouseEvent(&event);
 
   const WebInputEvent* input_event =
@@ -3139,7 +3166,7 @@
 
   // Simulates the mouse wheel event with no modifier applied.
   event = ui::MouseWheelEvent(gfx::Vector2d(1, 1), gfx::Point(), gfx::Point(),
-      ui::EF_NONE, 0);
+                              ui::EventTimeForNow(), ui::EF_NONE, 0);
 
   view_->OnMouseEvent(&event);
 
@@ -3165,4 +3192,27 @@
   EXPECT_TRUE(wheel_event->canScroll);
 }
 
+// Ensures that the mapping from ui::TouchEvent to blink::WebTouchEvent doesn't
+// lose track of the number of acks required.
+TEST_F(RenderWidgetHostViewAuraTest, CorrectNumberOfAcksAreDispatched) {
+  view_->InitAsFullscreen(parent_view_);
+  view_->Show();
+  view_->UseFakeDispatcher();
+
+  ui::TouchEvent press1(
+      ui::ET_TOUCH_PRESSED, gfx::Point(30, 30), 0, ui::EventTimeForNow());
+
+  view_->OnTouchEvent(&press1);
+  SendInputEventACK(blink::WebInputEvent::TouchStart,
+                    INPUT_EVENT_ACK_STATE_CONSUMED);
+
+  ui::TouchEvent press2(
+      ui::ET_TOUCH_PRESSED, gfx::Point(20, 20), 1, ui::EventTimeForNow());
+  view_->OnTouchEvent(&press2);
+  SendInputEventACK(blink::WebInputEvent::TouchStart,
+                    INPUT_EVENT_ACK_STATE_CONSUMED);
+
+  EXPECT_EQ(2U, view_->dispatcher_->processed_touch_event_count());
+}
+
 }  // namespace content
diff --git a/content/browser/renderer_host/render_widget_host_view_mac.mm b/content/browser/renderer_host/render_widget_host_view_mac.mm
index 0b3b836b..a477fe08 100644
--- a/content/browser/renderer_host/render_widget_host_view_mac.mm
+++ b/content/browser/renderer_host/render_widget_host_view_mac.mm
@@ -2277,6 +2277,12 @@
 }
 
 - (void)smartMagnifyWithEvent:(NSEvent*)event {
+  const WebGestureEvent& smartMagnifyEvent =
+      WebInputEventFactory::gestureEvent(event, self);
+  if (renderWidgetHostView_ && renderWidgetHostView_->render_widget_host_) {
+    renderWidgetHostView_->render_widget_host_->ForwardGestureEvent(
+        smartMagnifyEvent);
+  }
 }
 
 // This is invoked only on 10.8 or newer when the user taps a word using
diff --git a/content/browser/resources/service_worker/serviceworker_internals.html b/content/browser/resources/service_worker/serviceworker_internals.html
index 8c994c1..a16ac65 100644
--- a/content/browser/resources/service_worker/serviceworker_internals.html
+++ b/content/browser/resources/service_worker/serviceworker_internals.html
@@ -126,7 +126,7 @@
                 jsvalues=".checked:$this.debug_on_start">
           </span>
           <span>
-            Opens the DevTools window for ServiceWorker on start for debugging.
+            Open DevTools window and pause JavaScript execution on Service Worker startup for debugging.
           </span>
         </label>
       </div>
diff --git a/content/browser/service_worker/embedded_worker_registry.cc b/content/browser/service_worker/embedded_worker_registry.cc
index fa8197a..0ff12fd80 100644
--- a/content/browser/service_worker/embedded_worker_registry.cc
+++ b/content/browser/service_worker/embedded_worker_registry.cc
@@ -48,13 +48,14 @@
               new EmbeddedWorkerMsg_StopWorker(embedded_worker_id));
 }
 
-bool EmbeddedWorkerRegistry::OnMessageReceived(const IPC::Message& message) {
+bool EmbeddedWorkerRegistry::OnMessageReceived(const IPC::Message& message,
+                                               int process_id) {
   // TODO(kinuko): Move all EmbeddedWorker message handling from
   // ServiceWorkerDispatcherHost.
 
   WorkerInstanceMap::iterator found = worker_map_.find(message.routing_id());
   DCHECK(found != worker_map_.end());
-  if (found == worker_map_.end())
+  if (found == worker_map_.end() || found->second->process_id() != process_id)
     return false;
   return found->second->OnMessageReceived(message);
 }
@@ -245,6 +246,9 @@
 ServiceWorkerStatusCode EmbeddedWorkerRegistry::SendStartWorker(
     scoped_ptr<EmbeddedWorkerMsg_StartWorker_Params> params,
     int process_id) {
+  if (!context_)
+    return SERVICE_WORKER_ERROR_ABORT;
+
   // The ServiceWorkerDispatcherHost is supposed to be created when the process
   // is created, and keep an entry in process_sender_map_ for its whole
   // lifetime.
diff --git a/content/browser/service_worker/embedded_worker_registry.h b/content/browser/service_worker/embedded_worker_registry.h
index 2567b20..f17f1c36 100644
--- a/content/browser/service_worker/embedded_worker_registry.h
+++ b/content/browser/service_worker/embedded_worker_registry.h
@@ -50,7 +50,7 @@
       const base::WeakPtr<ServiceWorkerContextCore>& context,
       EmbeddedWorkerRegistry* old_registry);
 
-  bool OnMessageReceived(const IPC::Message& message);
+  bool OnMessageReceived(const IPC::Message& message, int process_id);
 
   // Creates and removes a new worker instance entry for bookkeeping.
   // This doesn't actually start or stop the worker.
diff --git a/content/browser/service_worker/embedded_worker_test_helper.cc b/content/browser/service_worker/embedded_worker_test_helper.cc
index 7a8468f..e28ee728 100644
--- a/content/browser/service_worker/embedded_worker_test_helper.cc
+++ b/content/browser/service_worker/embedded_worker_test_helper.cc
@@ -215,7 +215,7 @@
 
 void EmbeddedWorkerTestHelper::SimulateSend(
     IPC::Message* message) {
-  registry()->OnMessageReceived(*message);
+  registry()->OnMessageReceived(*message, mock_render_process_id_);
   delete message;
 }
 
diff --git a/content/browser/service_worker/service_worker_disk_cache.cc b/content/browser/service_worker/service_worker_disk_cache.cc
index d31f00c..4b6237f 100644
--- a/content/browser/service_worker/service_worker_disk_cache.cc
+++ b/content/browser/service_worker/service_worker_disk_cache.cc
@@ -16,4 +16,10 @@
     : AppCacheResponseWriter(response_id, 0, disk_cache) {
 }
 
+ServiceWorkerResponseMetadataWriter::ServiceWorkerResponseMetadataWriter(
+    int64 response_id,
+    ServiceWorkerDiskCache* disk_cache)
+    : AppCacheResponseMetadataWriter(response_id, 0, disk_cache) {
+}
+
 }  // namespace content
diff --git a/content/browser/service_worker/service_worker_disk_cache.h b/content/browser/service_worker/service_worker_disk_cache.h
index 7fa51eed..7b9b4e4 100644
--- a/content/browser/service_worker/service_worker_disk_cache.h
+++ b/content/browser/service_worker/service_worker_disk_cache.h
@@ -40,6 +40,15 @@
       ServiceWorkerDiskCache* disk_cache);
 };
 
+class CONTENT_EXPORT ServiceWorkerResponseMetadataWriter
+    : public AppCacheResponseMetadataWriter {
+ protected:
+  // Should only be constructed by the storage class.
+  friend class ServiceWorkerStorage;
+  ServiceWorkerResponseMetadataWriter(int64 response_id,
+                                      ServiceWorkerDiskCache* disk_cache);
+};
+
 }  // namespace content
 
 #endif  // CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_DISK_CACHE_H_
diff --git a/content/browser/service_worker/service_worker_dispatcher_host.cc b/content/browser/service_worker/service_worker_dispatcher_host.cc
index af3378c..6ffbfc5 100644
--- a/content/browser/service_worker/service_worker_dispatcher_host.cc
+++ b/content/browser/service_worker/service_worker_dispatcher_host.cc
@@ -120,6 +120,8 @@
   }
 
   context_wrapper_ = context_wrapper;
+  if (!GetContext())
+    return;
   GetContext()->embedded_worker_registry()->AddChildProcessSender(
       render_process_id_, this, message_port_message_filter_);
 }
@@ -200,8 +202,8 @@
   IPC_END_MESSAGE_MAP()
 
   if (!handled && GetContext()) {
-    handled =
-        GetContext()->embedded_worker_registry()->OnMessageReceived(message);
+    handled = GetContext()->embedded_worker_registry()->OnMessageReceived(
+        message, render_process_id_);
     if (!handled)
       BadMessageReceived();
   }
diff --git a/content/browser/service_worker/service_worker_read_from_cache_job.cc b/content/browser/service_worker/service_worker_read_from_cache_job.cc
index 31498de..135c5898 100644
--- a/content/browser/service_worker/service_worker_read_from_cache_job.cc
+++ b/content/browser/service_worker/service_worker_read_from_cache_job.cc
@@ -75,11 +75,6 @@
 }
 
 net::LoadState ServiceWorkerReadFromCacheJob::GetLoadState() const {
-  // TODO(pkasting): Remove ScopedTracker below once crbug.com/455952 is
-  // fixed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "455952 ServiceWorkerReadFromCacheJob::GetLoadState"));
   if (reader_.get() && reader_->IsReadPending())
     return net::LOAD_STATE_READING_RESPONSE;
   return net::LOAD_STATE_IDLE;
diff --git a/content/browser/service_worker/service_worker_request_handler.cc b/content/browser/service_worker/service_worker_request_handler.cc
index c70b3fd..ac851ca 100644
--- a/content/browser/service_worker/service_worker_request_handler.cc
+++ b/content/browser/service_worker/service_worker_request_handler.cc
@@ -6,6 +6,7 @@
 
 #include <string>
 
+#include "base/profiler/scoped_tracker.h"
 #include "content/browser/service_worker/service_worker_context_core.h"
 #include "content/browser/service_worker/service_worker_context_wrapper.h"
 #include "content/browser/service_worker/service_worker_provider_host.h"
@@ -63,6 +64,10 @@
     RequestContextType request_context_type,
     RequestContextFrameType frame_type,
     scoped_refptr<ResourceRequestBody> body) {
+  // TODO(pkasting): Remove ScopedTracker below once crbug.com/456331 is fixed.
+  tracked_objects::ScopedTracker tracking_profile(
+      FROM_HERE_WITH_EXPLICIT_FUNCTION(
+          "456331 ServiceWorkerRequestHandler::InitializeHandler"));
   if (!request->url().SchemeIsHTTPOrHTTPS())
     return;
 
diff --git a/content/browser/service_worker/service_worker_script_cache_map.cc b/content/browser/service_worker/service_worker_script_cache_map.cc
index 6c74466..925fe6e 100644
--- a/content/browser/service_worker/service_worker_script_cache_map.cc
+++ b/content/browser/service_worker/service_worker_script_cache_map.cc
@@ -6,17 +6,19 @@
 
 #include "base/logging.h"
 #include "content/browser/service_worker/service_worker_context_core.h"
+#include "content/browser/service_worker/service_worker_disk_cache.h"
 #include "content/browser/service_worker/service_worker_storage.h"
 #include "content/browser/service_worker/service_worker_version.h"
 #include "content/common/service_worker/service_worker_types.h"
+#include "net/base/io_buffer.h"
+#include "net/base/net_errors.h"
 
 namespace content {
 
 ServiceWorkerScriptCacheMap::ServiceWorkerScriptCacheMap(
     ServiceWorkerVersion* owner,
     base::WeakPtr<ServiceWorkerContextCore> context)
-    : owner_(owner),
-      context_(context) {
+    : owner_(owner), context_(context), weak_factory_(this) {
 }
 
 ServiceWorkerScriptCacheMap::~ServiceWorkerScriptCacheMap() {
@@ -90,4 +92,40 @@
   }
 }
 
+void ServiceWorkerScriptCacheMap::WriteMetadata(
+    const GURL& url,
+    const std::vector<char>& data,
+    const net::CompletionCallback& callback) {
+  ResourceMap::iterator found = resource_map_.find(url);
+  if (found == resource_map_.end() ||
+      found->second.resource_id == kInvalidServiceWorkerResponseId) {
+    callback.Run(net::ERR_FILE_NOT_FOUND);
+    return;
+  }
+  scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(data.size()));
+  if (data.size())
+    memmove(buffer->data(), &data[0], data.size());
+  scoped_ptr<ServiceWorkerResponseMetadataWriter> writer;
+  writer = context_->storage()->CreateResponseMetadataWriter(
+      found->second.resource_id);
+  ServiceWorkerResponseMetadataWriter* raw_writer = writer.get();
+  raw_writer->WriteMetadata(
+      buffer.get(), data.size(),
+      base::Bind(&ServiceWorkerScriptCacheMap::OnMetadataWritten,
+                 weak_factory_.GetWeakPtr(), Passed(&writer), callback));
+}
+
+void ServiceWorkerScriptCacheMap::ClearMetadata(
+    const GURL& url,
+    const net::CompletionCallback& callback) {
+  WriteMetadata(url, std::vector<char>(), callback);
+}
+
+void ServiceWorkerScriptCacheMap::OnMetadataWritten(
+    scoped_ptr<ServiceWorkerResponseMetadataWriter> writer,
+    const net::CompletionCallback& callback,
+    int result) {
+  callback.Run(result);
+}
+
 }  // namespace content
diff --git a/content/browser/service_worker/service_worker_script_cache_map.h b/content/browser/service_worker/service_worker_script_cache_map.h
index 7650517c7..7d3366a 100644
--- a/content/browser/service_worker/service_worker_script_cache_map.h
+++ b/content/browser/service_worker/service_worker_script_cache_map.h
@@ -12,6 +12,7 @@
 #include "base/memory/weak_ptr.h"
 #include "content/browser/service_worker/service_worker_database.h"
 #include "content/common/content_export.h"
+#include "net/base/completion_callback.h"
 #include "net/url_request/url_request_status.h"
 
 class GURL;
@@ -20,6 +21,7 @@
 
 class ServiceWorkerContextCore;
 class ServiceWorkerVersion;
+class ServiceWorkerResponseMetadataWriter;
 
 // Class that maintains the mapping between urls and a resource id
 // for a particular version's implicit script resources.
@@ -46,6 +48,13 @@
   void SetResources(
      const std::vector<ServiceWorkerDatabase::ResourceRecord>& resources);
 
+  // Writes the metadata of the existing script.
+  void WriteMetadata(const GURL& url,
+                     const std::vector<char>& data,
+                     const net::CompletionCallback& callback);
+  // Clears the metadata of the existing script.
+  void ClearMetadata(const GURL& url, const net::CompletionCallback& callback);
+
   size_t size() const { return resource_map_.size(); }
 
   const net::URLRequestStatus& main_script_status() const {
@@ -66,12 +75,18 @@
       base::WeakPtr<ServiceWorkerContextCore> context);
   ~ServiceWorkerScriptCacheMap();
 
+  void OnMetadataWritten(scoped_ptr<ServiceWorkerResponseMetadataWriter> writer,
+                         const net::CompletionCallback& callback,
+                         int result);
+
   ServiceWorkerVersion* owner_;
   base::WeakPtr<ServiceWorkerContextCore> context_;
   ResourceMap resource_map_;
   net::URLRequestStatus main_script_status_;
   std::string main_script_status_message_;
 
+  base::WeakPtrFactory<ServiceWorkerScriptCacheMap> weak_factory_;
+
   DISALLOW_COPY_AND_ASSIGN(ServiceWorkerScriptCacheMap);
 };
 
diff --git a/content/browser/service_worker/service_worker_storage.cc b/content/browser/service_worker/service_worker_storage.cc
index 85b4e03..ad3d3d4 100644
--- a/content/browser/service_worker/service_worker_storage.cc
+++ b/content/browser/service_worker/service_worker_storage.cc
@@ -643,6 +643,12 @@
       new ServiceWorkerResponseWriter(response_id, disk_cache()));
 }
 
+scoped_ptr<ServiceWorkerResponseMetadataWriter>
+ServiceWorkerStorage::CreateResponseMetadataWriter(int64 response_id) {
+  return make_scoped_ptr(
+      new ServiceWorkerResponseMetadataWriter(response_id, disk_cache()));
+}
+
 void ServiceWorkerStorage::StoreUncommittedResponseId(int64 id) {
   DCHECK_NE(kInvalidServiceWorkerResponseId, id);
   DCHECK_EQ(INITIALIZED, state_);
diff --git a/content/browser/service_worker/service_worker_storage.h b/content/browser/service_worker/service_worker_storage.h
index 623bf755..b2c0e08c 100644
--- a/content/browser/service_worker/service_worker_storage.h
+++ b/content/browser/service_worker/service_worker_storage.h
@@ -39,6 +39,7 @@
 class ServiceWorkerDiskCache;
 class ServiceWorkerRegistration;
 class ServiceWorkerRegistrationInfo;
+class ServiceWorkerResponseMetadataWriter;
 class ServiceWorkerResponseReader;
 class ServiceWorkerResponseWriter;
 
@@ -144,6 +145,8 @@
       int64 response_id);
   scoped_ptr<ServiceWorkerResponseWriter> CreateResponseWriter(
       int64 response_id);
+  scoped_ptr<ServiceWorkerResponseMetadataWriter> CreateResponseMetadataWriter(
+      int64 response_id);
 
   // Adds |id| to the set of resources ids that are in the disk
   // cache but not yet stored with a registration.
diff --git a/content/browser/service_worker/service_worker_storage_unittest.cc b/content/browser/service_worker/service_worker_storage_unittest.cc
index e14a459..bbaab11c 100644
--- a/content/browser/service_worker/service_worker_storage_unittest.cc
+++ b/content/browser/service_worker/service_worker_storage_unittest.cc
@@ -204,6 +204,56 @@
   WriteResponse(storage, id, headers, buffer.get(), size);
 }
 
+int WriteResponseMetadata(ServiceWorkerStorage* storage,
+                          int64 id,
+                          const std::string& metadata) {
+  scoped_refptr<IOBuffer> body_buffer(new WrappedIOBuffer(metadata.data()));
+  scoped_ptr<ServiceWorkerResponseMetadataWriter> metadata_writer =
+      storage->CreateResponseMetadataWriter(id);
+  TestCompletionCallback cb;
+  metadata_writer->WriteMetadata(body_buffer.get(), metadata.length(),
+                                 cb.callback());
+  return cb.WaitForResult();
+}
+
+int WriteMetadata(ServiceWorkerVersion* version,
+                  const GURL& url,
+                  const std::string& metadata) {
+  const std::vector<char> data(metadata.begin(), metadata.end());
+  EXPECT_TRUE(version);
+  TestCompletionCallback cb;
+  version->script_cache_map()->WriteMetadata(url, data, cb.callback());
+  return cb.WaitForResult();
+}
+
+int ClearMetadata(ServiceWorkerVersion* version, const GURL& url) {
+  EXPECT_TRUE(version);
+  TestCompletionCallback cb;
+  version->script_cache_map()->ClearMetadata(url, cb.callback());
+  return cb.WaitForResult();
+}
+
+bool VerifyResponseMetadata(ServiceWorkerStorage* storage,
+                            int64 id,
+                            const std::string& expected_metadata) {
+  scoped_ptr<ServiceWorkerResponseReader> reader =
+      storage->CreateResponseReader(id);
+  scoped_refptr<HttpResponseInfoIOBuffer> info_buffer =
+      new HttpResponseInfoIOBuffer();
+  {
+    TestCompletionCallback cb;
+    reader->ReadInfo(info_buffer.get(), cb.callback());
+    int rv = cb.WaitForResult();
+    EXPECT_LT(0, rv);
+  }
+  const net::HttpResponseInfo* read_head = info_buffer->http_info.get();
+  if (!read_head->metadata.get())
+    return false;
+  EXPECT_EQ(0, memcmp(expected_metadata.data(), read_head->metadata->data(),
+                      expected_metadata.length()));
+  return true;
+}
+
 }  // namespace
 
 class ServiceWorkerStorageTest : public testing::Test {
@@ -909,6 +959,65 @@
   base::ScopedTempDir user_data_directory_;
 };
 
+TEST_F(ServiceWorkerResourceStorageTest,
+       WriteMetadataWithServiceWorkerResponseMetadataWriter) {
+  const char kMetadata1[] = "Test metadata";
+  const char kMetadata2[] = "small";
+  int64 new_resource_id_ = storage()->NewResourceId();
+  // Writing metadata to nonexistent resoirce ID must fail.
+  EXPECT_GE(0, WriteResponseMetadata(storage(), new_resource_id_, kMetadata1));
+
+  // Check metadata is written.
+  EXPECT_EQ(static_cast<int>(strlen(kMetadata1)),
+            WriteResponseMetadata(storage(), resource_id1_, kMetadata1));
+  EXPECT_TRUE(VerifyResponseMetadata(storage(), resource_id1_, kMetadata1));
+  EXPECT_TRUE(VerifyBasicResponse(storage(), resource_id1_, true));
+
+  // Check metadata is written and truncated.
+  EXPECT_EQ(static_cast<int>(strlen(kMetadata2)),
+            WriteResponseMetadata(storage(), resource_id1_, kMetadata2));
+  EXPECT_TRUE(VerifyResponseMetadata(storage(), resource_id1_, kMetadata2));
+  EXPECT_TRUE(VerifyBasicResponse(storage(), resource_id1_, true));
+
+  // Check metadata is deleted.
+  EXPECT_EQ(0, WriteResponseMetadata(storage(), resource_id1_, ""));
+  EXPECT_FALSE(VerifyResponseMetadata(storage(), resource_id1_, ""));
+  EXPECT_TRUE(VerifyBasicResponse(storage(), resource_id1_, true));
+}
+
+TEST_F(ServiceWorkerResourceStorageTest,
+       WriteMetadataWithServiceWorkerScriptCacheMap) {
+  const char kMetadata1[] = "Test metadata";
+  const char kMetadata2[] = "small";
+  ServiceWorkerVersion* version = registration_->waiting_version();
+  EXPECT_TRUE(version);
+
+  // Writing metadata to nonexistent URL must fail.
+  EXPECT_GE(0,
+            WriteMetadata(version, GURL("http://www.test.not/nonexistent.js"),
+                          kMetadata1));
+  // Clearing metadata of nonexistent URL must fail.
+  EXPECT_GE(0,
+            ClearMetadata(version, GURL("http://www.test.not/nonexistent.js")));
+
+  // Check metadata is written.
+  EXPECT_EQ(static_cast<int>(strlen(kMetadata1)),
+            WriteMetadata(version, script_, kMetadata1));
+  EXPECT_TRUE(VerifyResponseMetadata(storage(), resource_id1_, kMetadata1));
+  EXPECT_TRUE(VerifyBasicResponse(storage(), resource_id1_, true));
+
+  // Check metadata is written and truncated.
+  EXPECT_EQ(static_cast<int>(strlen(kMetadata2)),
+            WriteMetadata(version, script_, kMetadata2));
+  EXPECT_TRUE(VerifyResponseMetadata(storage(), resource_id1_, kMetadata2));
+  EXPECT_TRUE(VerifyBasicResponse(storage(), resource_id1_, true));
+
+  // Check metadata is deleted.
+  EXPECT_EQ(0, ClearMetadata(version, script_));
+  EXPECT_FALSE(VerifyResponseMetadata(storage(), resource_id1_, ""));
+  EXPECT_TRUE(VerifyBasicResponse(storage(), resource_id1_, true));
+}
+
 TEST_F(ServiceWorkerResourceStorageTest, DeleteRegistration_NoLiveVersion) {
   bool was_called = false;
   ServiceWorkerStatusCode result = SERVICE_WORKER_ERROR_FAILED;
diff --git a/content/browser/service_worker/service_worker_utils.cc b/content/browser/service_worker/service_worker_utils.cc
index d37bb22..59748ee 100644
--- a/content/browser/service_worker/service_worker_utils.cc
+++ b/content/browser/service_worker/service_worker_utils.cc
@@ -77,7 +77,9 @@
       error_message->append("set by Service-Worker-Allowed: ");
     error_message->append("'");
     error_message->append(max_scope_string);
-    error_message->append("').");
+    error_message->append(
+        "'). Adjust the scope, move the Service Worker script, or use the "
+        "Service-Worker-Allowed HTTP header to allow the scope.");
     return false;
   }
   return true;
diff --git a/content/browser/service_worker/service_worker_version.cc b/content/browser/service_worker/service_worker_version.cc
index c5828386..ede555288 100644
--- a/content/browser/service_worker/service_worker_version.cc
+++ b/content/browser/service_worker/service_worker_version.cc
@@ -9,6 +9,7 @@
 #include "base/stl_util.h"
 #include "base/strings/string16.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/time/time.h"
 #include "content/browser/message_port_message_filter.h"
 #include "content/browser/message_port_service.h"
 #include "content/browser/service_worker/embedded_worker_instance.h"
@@ -271,6 +272,18 @@
     render_process_host->ReceivedBadMessage();
 }
 
+void DidSetCachedMetadata(int64 callback_id, int result) {
+  TRACE_EVENT_ASYNC_END1("ServiceWorker",
+                         "ServiceWorkerVersion::OnSetCachedMetadata",
+                         callback_id, "result", result);
+}
+
+void DidClearCachedMetadata(int64 callback_id, int result) {
+  TRACE_EVENT_ASYNC_END1("ServiceWorker",
+                         "ServiceWorkerVersion::OnClearCachedMetadata",
+                         callback_id, "result", result);
+}
+
 }  // namespace
 
 ServiceWorkerVersion::ServiceWorkerVersion(
@@ -915,6 +928,10 @@
                         OnCrossOriginConnectEventFinished)
     IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_OpenWindow,
                         OnOpenWindow)
+    IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_SetCachedMetadata,
+                        OnSetCachedMetadata)
+    IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_ClearCachedMetadata,
+                        OnClearCachedMetadata)
     IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_PostMessageToDocument,
                         OnPostMessageToDocument)
     IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_FocusClient,
@@ -1220,6 +1237,25 @@
       request_id, client));
 }
 
+void ServiceWorkerVersion::OnSetCachedMetadata(const GURL& url,
+                                               const std::vector<char>& data) {
+  int64 callback_id = base::TimeTicks::Now().ToInternalValue();
+  TRACE_EVENT_ASYNC_BEGIN1("ServiceWorker",
+                           "ServiceWorkerVersion::OnSetCachedMetadata",
+                           callback_id, "URL", url.spec());
+  script_cache_map_.WriteMetadata(
+      url, data, base::Bind(&DidSetCachedMetadata, callback_id));
+}
+
+void ServiceWorkerVersion::OnClearCachedMetadata(const GURL& url) {
+  int64 callback_id = base::TimeTicks::Now().ToInternalValue();
+  TRACE_EVENT_ASYNC_BEGIN1("ServiceWorker",
+                           "ServiceWorkerVersion::OnClearCachedMetadata",
+                           callback_id, "URL", url.spec());
+  script_cache_map_.ClearMetadata(
+      url, base::Bind(&DidClearCachedMetadata, callback_id));
+}
+
 void ServiceWorkerVersion::OnPostMessageToDocument(
     int client_id,
     const base::string16& message,
diff --git a/content/browser/service_worker/service_worker_version.h b/content/browser/service_worker/service_worker_version.h
index 6098061..f95b4ea 100644
--- a/content/browser/service_worker/service_worker_version.h
+++ b/content/browser/service_worker/service_worker_version.h
@@ -371,6 +371,9 @@
                             int client_id,
                             const ServiceWorkerClientInfo& client_info);
 
+  void OnSetCachedMetadata(const GURL& url, const std::vector<char>& data);
+  void OnClearCachedMetadata(const GURL& url);
+
   void OnPostMessageToDocument(int client_id,
                                const base::string16& message,
                                const std::vector<int>& sent_message_port_ids);
diff --git a/content/browser/service_worker/service_worker_version_unittest.cc b/content/browser/service_worker/service_worker_version_unittest.cc
index 596a419..7cf16e2d 100644
--- a/content/browser/service_worker/service_worker_version_unittest.cc
+++ b/content/browser/service_worker/service_worker_version_unittest.cc
@@ -281,6 +281,14 @@
 }
 
 TEST_F(ServiceWorkerVersionTest, ReceiveMessageFromWorker) {
+  // Start worker.
+  ServiceWorkerStatusCode status = SERVICE_WORKER_ERROR_FAILED;
+  version_->StartWorker(CreateReceiverOnCurrentThread(&status));
+  EXPECT_EQ(ServiceWorkerVersion::STARTING, version_->running_status());
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(SERVICE_WORKER_OK, status);
+  EXPECT_EQ(ServiceWorkerVersion::RUNNING, version_->running_status());
+
   MessageReceiverFromWorker receiver(version_->embedded_worker());
 
   // Simulate sending some dummy values from the worker.
diff --git a/content/browser/service_worker/service_worker_write_to_cache_job.cc b/content/browser/service_worker/service_worker_write_to_cache_job.cc
index 15d2675..f1252f0 100644
--- a/content/browser/service_worker/service_worker_write_to_cache_job.cc
+++ b/content/browser/service_worker/service_worker_write_to_cache_job.cc
@@ -102,11 +102,6 @@
 }
 
 net::LoadState ServiceWorkerWriteToCacheJob::GetLoadState() const {
-  // TODO(pkasting): Remove ScopedTracker below once crbug.com/455952 is
-  // fixed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "455952 ServiceWorkerWriteToCacheJob::GetLoadState"));
   if (writer_ && writer_->IsWritePending())
     return net::LOAD_STATE_WAITING_FOR_APPCACHE;
   if (net_request_)
diff --git a/content/browser/site_per_process_browsertest.cc b/content/browser/site_per_process_browsertest.cc
index 9c9c0f2..d738429 100644
--- a/content/browser/site_per_process_browsertest.cc
+++ b/content/browser/site_per_process_browsertest.cc
@@ -1318,4 +1318,39 @@
   EXPECT_EQ(url, observer.last_navigation_url());
 }
 
+// Ensure that the renderer does not crash when navigating a frame that has a
+// sibling RemoteFrame.  See https://crbug.com/426953.
+IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
+                       NavigateWithSiblingRemoteFrame) {
+  GURL main_url(
+      embedded_test_server()->GetURL("/frame_tree/page_with_two_frames.html"));
+  NavigateToURL(shell(), main_url);
+
+  // It is safe to obtain the root frame tree node here, as it doesn't change.
+  FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
+                            ->GetFrameTree()
+                            ->root();
+  TestNavigationObserver observer(shell()->web_contents());
+
+  // Make sure the first frame is out of process.
+  ASSERT_EQ(2U, root->child_count());
+  FrameTreeNode* node2 = root->child_at(0);
+  EXPECT_NE(root->current_frame_host()->GetSiteInstance(),
+            node2->current_frame_host()->GetSiteInstance());
+
+  // Make sure the second frame is in the parent's process.
+  FrameTreeNode* node3 = root->child_at(1);
+  EXPECT_EQ(root->current_frame_host()->GetSiteInstance(),
+            node3->current_frame_host()->GetSiteInstance());
+
+  // Navigate the second iframe (node3) to a URL in its own process.
+  GURL title_url = embedded_test_server()->GetURL("/title2.html");
+  NavigateFrameToURL(node3, title_url);
+  EXPECT_TRUE(observer.last_navigation_succeeded());
+  EXPECT_EQ(title_url, observer.last_navigation_url());
+  EXPECT_EQ(root->current_frame_host()->GetSiteInstance(),
+            node3->current_frame_host()->GetSiteInstance());
+  EXPECT_TRUE(node3->current_frame_host()->IsRenderFrameLive());
+}
+
 }  // namespace content
diff --git a/content/browser/ssl/ssl_manager.cc b/content/browser/ssl/ssl_manager.cc
index ccaf9cf..865ca60 100644
--- a/content/browser/ssl/ssl_manager.cc
+++ b/content/browser/ssl/ssl_manager.cc
@@ -85,8 +85,7 @@
 
   for (std::set<SSLManager*>::iterator i = managers->get().begin();
        i != managers->get().end(); ++i) {
-    (*i)->UpdateEntry(NavigationEntryImpl::FromNavigationEntry(
-                          (*i)->controller()->GetLastCommittedEntry()));
+    (*i)->UpdateEntry((*i)->controller()->GetLastCommittedEntry());
   }
 }
 
@@ -112,9 +111,7 @@
 }
 
 void SSLManager::DidCommitProvisionalLoad(const LoadCommittedDetails& details) {
-  NavigationEntryImpl* entry =
-      NavigationEntryImpl::FromNavigationEntry(
-          controller_->GetLastCommittedEntry());
+  NavigationEntryImpl* entry = controller_->GetLastCommittedEntry();
 
   if (details.is_main_frame) {
     if (entry) {
@@ -148,15 +145,11 @@
 }
 
 void SSLManager::DidDisplayInsecureContent() {
-  UpdateEntry(
-      NavigationEntryImpl::FromNavigationEntry(
-          controller_->GetLastCommittedEntry()));
+  UpdateEntry(controller_->GetLastCommittedEntry());
 }
 
 void SSLManager::DidRunInsecureContent(const std::string& security_origin) {
-  NavigationEntryImpl* navigation_entry =
-      NavigationEntryImpl::FromNavigationEntry(
-          controller_->GetLastCommittedEntry());
+  NavigationEntryImpl* navigation_entry = controller_->GetLastCommittedEntry();
   policy()->DidRunInsecureContent(navigation_entry, security_origin);
   UpdateEntry(navigation_entry);
 }
diff --git a/content/browser/web_contents/aura/overscroll_navigation_overlay.cc b/content/browser/web_contents/aura/overscroll_navigation_overlay.cc
index 89aa4975..a96b0d2 100644
--- a/content/browser/web_contents/aura/overscroll_navigation_overlay.cc
+++ b/content/browser/web_contents/aura/overscroll_navigation_overlay.cc
@@ -208,8 +208,7 @@
 
 ui::Layer* OverscrollNavigationOverlay::CreateSlideLayer(int offset) {
   const NavigationControllerImpl& controller = web_contents_->GetController();
-  const NavigationEntryImpl* entry = NavigationEntryImpl::FromNavigationEntry(
-      controller.GetEntryAtOffset(offset));
+  const NavigationEntryImpl* entry = controller.GetEntryAtOffset(offset);
 
   gfx::Image image;
   if (entry && entry->screenshot().get()) {
diff --git a/content/browser/web_contents/aura/window_slider_unittest.cc b/content/browser/web_contents/aura/window_slider_unittest.cc
index a1988d49..390a002 100644
--- a/content/browser/web_contents/aura/window_slider_unittest.cc
+++ b/content/browser/web_contents/aura/window_slider_unittest.cc
@@ -290,12 +290,10 @@
   WindowSliderDelegateTest slider_delegate;
 
   ui::Event* events[] = {
-    new ui::MouseEvent(ui::ET_MOUSE_MOVED,
-                       gfx::Point(55, 10),
-                       gfx::Point(55, 10),
-                       0, 0),
-    new ui::KeyEvent('a', ui::VKEY_A, ui::EF_NONE),
-    NULL
+      new ui::MouseEvent(ui::ET_MOUSE_MOVED, gfx::Point(55, 10),
+                         gfx::Point(55, 10), ui::EventTimeForNow(), 0, 0),
+      new ui::KeyEvent('a', ui::VKEY_A, ui::EF_NONE),
+      nullptr
   };
 
   new WindowSlider(&slider_delegate, root_window(), window.get());
@@ -338,10 +336,9 @@
   WindowSlider* slider =
       new WindowSlider(&slider_delegate, root_window(), window.get());
 
-  ui::MouseEvent interrupt_event(ui::ET_MOUSE_MOVED,
-                                 gfx::Point(55, 10),
-                                 gfx::Point(55, 10),
-                                 0, 0);
+  ui::MouseEvent interrupt_event(ui::ET_MOUSE_MOVED, gfx::Point(55, 10),
+                                 gfx::Point(55, 10), ui::EventTimeForNow(), 0,
+                                 0);
 
   ui::test::EventGenerator generator(root_window());
 
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index 3c46724..ed67922 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -41,7 +41,6 @@
 #include "content/browser/media/audio_stream_monitor.h"
 #include "content/browser/media/capture/web_contents_audio_muter.h"
 #include "content/browser/message_port_message_filter.h"
-#include "content/browser/message_port_service.h"
 #include "content/browser/plugin_content_origin_whitelist.h"
 #include "content/browser/power_save_blocker_impl.h"
 #include "content/browser/renderer_host/render_process_host_impl.h"
@@ -1959,8 +1958,9 @@
 
   // Navigate in the FrameTreeNode specified in the pending entry, if any.  This
   // is currently only used in --site-per-process and tests.
-  NavigationEntryImpl* pending_entry =
-      NavigationEntryImpl::FromNavigationEntry(controller_.GetPendingEntry());
+  // TODO(creis): Remove this method and NavigationEntryImpl::frame_tree_node_id
+  // by using FrameNavigationEntries instead.  See https://crbug.com/236848.
+  NavigationEntryImpl* pending_entry = controller_.GetPendingEntry();
   if (pending_entry->frame_tree_node_id() != -1) {
     FrameTreeNode* subframe =
         frame_tree_.FindByID(pending_entry->frame_tree_node_id());
@@ -1969,8 +1969,7 @@
       node = subframe;
   }
 
-  return node->navigator()->NavigateToPendingEntry(
-      node->current_frame_host(), reload_type);
+  return node->navigator()->NavigateToPendingEntry(node, reload_type);
 }
 
 void WebContentsImpl::RenderFrameForInterstitialPageCreated(
@@ -3984,15 +3983,6 @@
 
   ViewMsg_PostMessage_Params new_params(params);
 
-  if (!params.message_port_ids.empty()) {
-    MessagePortMessageFilter* message_port_message_filter =
-        static_cast<RenderProcessHostImpl*>(GetRenderProcessHost())
-            ->message_port_message_filter();
-    message_port_message_filter->UpdateMessagePortsWithNewRoutes(
-        params.message_port_ids,
-        &new_params.new_routing_ids);
-  }
-
   // If there is a source_routing_id, translate it to the routing ID for
   // the equivalent swapped out RVH in the target process.  If we need
   // to create a swapped out RVH for the source tab, we create its opener
@@ -4027,7 +4017,24 @@
   // In most cases, we receive this from a swapped out RenderViewHost.
   // It is possible to receive it from one that has just been swapped in,
   // in which case we might as well deliver the message anyway.
-  Send(new ViewMsg_PostMessageEvent(GetRoutingID(), new_params));
+  if (!params.message_port_ids.empty()) {
+    // Updating the message port information has to be done in the IO thread;
+    // MessagePortMessageFilter::RouteMessageEventWithMessagePorts will send
+    // ViewMsg_PostMessageEvent after it's done. Note that a trivial solution
+    // would've been to post a task on the IO thread to do the IO-thread-bound
+    // work, and make that post a task back to WebContentsImpl in the UI
+    // thread. But we cannot do that, since there's nothing to guarantee that
+    // WebContentsImpl stays alive during the round trip.
+    scoped_refptr<MessagePortMessageFilter> message_port_message_filter(
+        static_cast<RenderProcessHostImpl*>(GetRenderProcessHost())
+        ->message_port_message_filter());
+    BrowserThread::PostTask(
+        BrowserThread::IO, FROM_HERE,
+        base::Bind(&MessagePortMessageFilter::RouteMessageEventWithMessagePorts,
+                   message_port_message_filter, GetRoutingID(), new_params));
+  } else {
+    Send(new ViewMsg_PostMessageEvent(GetRoutingID(), new_params));
+  }
 }
 
 bool WebContentsImpl::AddMessageToConsole(int32 level,
diff --git a/content/browser/web_contents/web_contents_view_aura.cc b/content/browser/web_contents/web_contents_view_aura.cc
index 83f64cb0..dc4c535 100644
--- a/content/browser/web_contents/web_contents_view_aura.cc
+++ b/content/browser/web_contents/web_contents_view_aura.cc
@@ -126,11 +126,9 @@
     const NavigationControllerImpl& controller = web_contents->GetController();
     const NavigationEntryImpl* entry = NULL;
     if (ShouldNavigateForward(controller, overscroll_mode)) {
-      entry = NavigationEntryImpl::FromNavigationEntry(
-          controller.GetEntryAtOffset(1));
+      entry = controller.GetEntryAtOffset(1);
     } else if (ShouldNavigateBack(controller, overscroll_mode)) {
-      entry = NavigationEntryImpl::FromNavigationEntry(
-          controller.GetEntryAtOffset(-1));
+      entry = controller.GetEntryAtOffset(-1);
     }
 
     gfx::Image image;
diff --git a/content/browser/web_contents/web_contents_view_aura_browsertest.cc b/content/browser/web_contents/web_contents_view_aura_browsertest.cc
index 6e8480c..0b145e7 100644
--- a/content/browser/web_contents/web_contents_view_aura_browsertest.cc
+++ b/content/browser/web_contents/web_contents_view_aura_browsertest.cc
@@ -577,27 +577,22 @@
   EXPECT_EQ(2, GetCurrentIndex());
   screenshot_manager()->WaitUntilScreenshotIsReady();
 
-  NavigationEntryImpl* entry = NavigationEntryImpl::FromNavigationEntry(
-      web_contents->GetController().GetEntryAtIndex(2));
+  NavigationEntryImpl* entry = web_contents->GetController().GetEntryAtIndex(2);
   EXPECT_FALSE(entry->screenshot().get());
 
-  entry = NavigationEntryImpl::FromNavigationEntry(
-      web_contents->GetController().GetEntryAtIndex(1));
+  entry = web_contents->GetController().GetEntryAtIndex(1);
   EXPECT_FALSE(screenshot_manager()->ScreenshotSetForEntry(entry));
 
-  entry = NavigationEntryImpl::FromNavigationEntry(
-      web_contents->GetController().GetEntryAtIndex(0));
+  entry = web_contents->GetController().GetEntryAtIndex(0);
   EXPECT_FALSE(screenshot_manager()->ScreenshotSetForEntry(entry));
 
   ExecuteSyncJSFunction(main_frame, "navigate_next()");
   screenshot_manager()->WaitUntilScreenshotIsReady();
 
-  entry = NavigationEntryImpl::FromNavigationEntry(
-      web_contents->GetController().GetEntryAtIndex(2));
+  entry = web_contents->GetController().GetEntryAtIndex(2);
   EXPECT_FALSE(screenshot_manager()->ScreenshotSetForEntry(entry));
 
-  entry = NavigationEntryImpl::FromNavigationEntry(
-      web_contents->GetController().GetEntryAtIndex(3));
+  entry = web_contents->GetController().GetEntryAtIndex(3);
   EXPECT_FALSE(entry->screenshot().get());
   {
     // Now, swipe right to navigate backwards. This should navigate away from
@@ -616,8 +611,7 @@
     EXPECT_EQ(expected_title, actual_title);
     EXPECT_EQ(2, GetCurrentIndex());
     screenshot_manager()->WaitUntilScreenshotIsReady();
-    entry = NavigationEntryImpl::FromNavigationEntry(
-        web_contents->GetController().GetEntryAtIndex(3));
+    entry = web_contents->GetController().GetEntryAtIndex(3);
     EXPECT_FALSE(screenshot_manager()->ScreenshotSetForEntry(entry));
   }
 
@@ -627,8 +621,7 @@
   ExecuteSyncJSFunction(main_frame, "navigate_next()");
   EXPECT_EQ(4, GetCurrentIndex());
   screenshot_manager()->WaitUntilScreenshotIsReady();
-  entry = NavigationEntryImpl::FromNavigationEntry(
-      web_contents->GetController().GetEntryAtIndex(4));
+  entry = web_contents->GetController().GetEntryAtIndex(4);
   EXPECT_FALSE(entry->screenshot().get());
 
   {
@@ -640,8 +633,7 @@
     EXPECT_EQ(expected_title, actual_title);
     EXPECT_EQ(3, GetCurrentIndex());
     screenshot_manager()->WaitUntilScreenshotIsReady();
-    entry = NavigationEntryImpl::FromNavigationEntry(
-        web_contents->GetController().GetEntryAtIndex(4));
+    entry = web_contents->GetController().GetEntryAtIndex(4);
     EXPECT_FALSE(screenshot_manager()->ScreenshotSetForEntry(entry));
   }
 }
@@ -700,12 +692,11 @@
         << navigations[i].url.spec();
     EXPECT_EQ(old_host, screenshot_manager()->screenshot_taken_for());
 
-    NavigationEntryImpl* entry = NavigationEntryImpl::FromNavigationEntry(
-        web_contents->GetController().GetEntryAtOffset(-1));
+    NavigationEntryImpl* entry =
+        web_contents->GetController().GetEntryAtOffset(-1);
     EXPECT_TRUE(screenshot_manager()->ScreenshotSetForEntry(entry));
 
-    entry = NavigationEntryImpl::FromNavigationEntry(
-        web_contents->GetController().GetLastCommittedEntry());
+    entry = web_contents->GetController().GetLastCommittedEntry();
     EXPECT_FALSE(screenshot_manager()->ScreenshotSetForEntry(entry));
     EXPECT_FALSE(entry->screenshot().get());
     screenshot_manager()->Reset();
diff --git a/content/browser/webui/shared_resources_data_source.cc b/content/browser/webui/shared_resources_data_source.cc
index 04faef8..d5377a1 100644
--- a/content/browser/webui/shared_resources_data_source.cc
+++ b/content/browser/webui/shared_resources_data_source.cc
@@ -26,11 +26,12 @@
 
 // TODO(rkc): Once we have a separate source for apps, remove '*/apps/' aliases.
 const char* kPathAliases[][2] = {
-  {"../../../third_party/polymer/components-chromium/", "polymer/"},
-  {"../../resources/default_100_percent/common/", "images/apps/"},
-  {"../../resources/default_200_percent/common/", "images/2x/apps/"},
-  {"../../webui/resources/cr_elements/", "cr_elements/"}
-};
+    {"../../../third_party/polymer/components-chromium/", "polymer/"},
+    {"../../../third_party/web-animations-js/sources/",
+     "polymer/web-animations-js/"},
+    {"../../resources/default_100_percent/common/", "images/apps/"},
+    {"../../resources/default_200_percent/common/", "images/2x/apps/"},
+    {"../../webui/resources/cr_elements/", "cr_elements/"}};
 
 void AddResource(const std::string& path,
                  int resource_id,
diff --git a/content/browser/webui/url_data_manager_backend.cc b/content/browser/webui/url_data_manager_backend.cc
index 100f3cb6..92f7bc6 100644
--- a/content/browser/webui/url_data_manager_backend.cc
+++ b/content/browser/webui/url_data_manager_backend.cc
@@ -347,10 +347,6 @@
       NotifyReadComplete(bytes_read);
     }
   } else {
-    // TODO(pkasting): Remove ScopedTracker below once crbug.com/455423 is
-    // fixed.
-    tracked_objects::ScopedTracker tracking_profile(
-        FROM_HERE_WITH_EXPLICIT_FUNCTION("455423 URLRequestJob::NotifyDone"));
     // The request failed.
     NotifyDone(net::URLRequestStatus(net::URLRequestStatus::FAILED,
                                      net::ERR_FAILED));
@@ -380,15 +376,15 @@
 
 void URLRequestChromeJob::CompleteRead(net::IOBuffer* buf, int buf_size,
                                        int* bytes_read) {
-  // http://crbug.com/373841
-  char url_buf[128];
-  base::strlcpy(url_buf, request_->url().spec().c_str(), arraysize(url_buf));
-  base::debug::Alias(url_buf);
-
   int remaining = static_cast<int>(data_->size()) - data_offset_;
   if (buf_size > remaining)
     buf_size = remaining;
   if (buf_size > 0) {
+    // TODO(pkasting): Remove ScopedTracker below once crbug.com/455423 is
+    // fixed.
+    tracked_objects::ScopedTracker tracking_profile(
+        FROM_HERE_WITH_EXPLICIT_FUNCTION(
+            "455423 URLRequestChromeJob::CompleteRead memcpy"));
     memcpy(buf->data(), data_->front() + data_offset_, buf_size);
     data_offset_ += buf_size;
   }
@@ -725,10 +721,6 @@
 
 void URLDataManagerBackend::DataAvailable(RequestID request_id,
                                           base::RefCountedMemory* bytes) {
-  // TODO(pkasting): Remove ScopedTracker below once crbug.com/455423 is fixed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "455423 URLDataManagerBackend::DataAvailable"));
   // Forward this data on to the pending net::URLRequest, if it exists.
   PendingRequestMap::iterator i = pending_requests_.find(request_id);
   if (i != pending_requests_.end()) {
diff --git a/content/browser/zygote_host/zygote_host_impl_linux.cc b/content/browser/zygote_host/zygote_host_impl_linux.cc
index 39c530f..35062072 100644
--- a/content/browser/zygote_host/zygote_host_impl_linux.cc
+++ b/content/browser/zygote_host/zygote_host_impl_linux.cc
@@ -22,6 +22,7 @@
 #include "base/memory/scoped_ptr.h"
 #include "base/memory/scoped_vector.h"
 #include "base/metrics/histogram.h"
+#include "base/metrics/sparse_histogram.h"
 #include "base/path_service.h"
 #include "base/posix/eintr_wrapper.h"
 #include "base/posix/unix_domain_socket_linux.h"
@@ -182,7 +183,7 @@
 
   dummy_fd.reset();
 
-  if (using_suid_sandbox) {
+  if (using_suid_sandbox || using_namespace_sandbox) {
     // The SUID sandbox will execute the zygote in a new PID namespace, and
     // the main zygote process will then fork from there.  Watch now our
     // elaborate dance to find and validate the zygote's PID.
@@ -301,7 +302,9 @@
         sizeof(sandbox_status_)) {
       return -1;
     }
+
     have_read_sandbox_status_word_ = true;
+    UMA_HISTOGRAM_SPARSE_SLOWLY("Linux.SandboxStatus", sandbox_status_);
   }
 
   return HANDLE_EINTR(read(control_fd_, buf, buf_len));
diff --git a/content/child/BUILD.gn b/content/child/BUILD.gn
index 935db3f..1380fe1 100644
--- a/content/child/BUILD.gn
+++ b/content/child/BUILD.gn
@@ -97,7 +97,10 @@
     }
   }
 
-  configs += [ "//content:content_implementation" ]
+  configs += [
+    "//content:content_implementation",
+    "//build/config/compiler:no_size_t_to_int_warning",
+  ]
 
   if (is_ios) {
     # iOS only needs a small portion of content; exclude all the
diff --git a/content/child/assert_matching_enums.cc b/content/child/assert_matching_enums.cc
index 33a4fbd..0d087ed 100644
--- a/content/child/assert_matching_enums.cc
+++ b/content/child/assert_matching_enums.cc
@@ -49,15 +49,14 @@
 // TargetProperty
 STATIC_ASSERT_MATCHING_ENUM(
     blink::WebCompositorAnimation::TargetPropertyTransform,
-    cc::Animation::Transform);
+    cc::Animation::TRANSFORM);
 STATIC_ASSERT_MATCHING_ENUM(
     blink::WebCompositorAnimation::TargetPropertyOpacity,
-    cc::Animation::Opacity);
-STATIC_ASSERT_MATCHING_ENUM(
-    blink::WebCompositorAnimation::TargetPropertyFilter,
-    cc::Animation::Filter);
+    cc::Animation::OPACITY);
+STATIC_ASSERT_MATCHING_ENUM(blink::WebCompositorAnimation::TargetPropertyFilter,
+                            cc::Animation::FILTER);
 STATIC_ASSERT_MATCHING_ENUM(
     blink::WebCompositorAnimation::TargetPropertyScrollOffset,
-    cc::Animation::ScrollOffset);
+    cc::Animation::SCROLL_OFFSET);
 
 } // namespace content
diff --git a/content/child/child_discardable_shared_memory_manager.cc b/content/child/child_discardable_shared_memory_manager.cc
index 83d29d6..140e949 100644
--- a/content/child/child_discardable_shared_memory_manager.cc
+++ b/content/child/child_discardable_shared_memory_manager.cc
@@ -31,9 +31,6 @@
   void* Memory() const override {
     return reinterpret_cast<void*>(span_->start() * base::GetPageSize());
   }
-  bool IsMemoryResident() const override {
-    return manager_->IsSpanResident(span_.get());
-  }
 
  private:
   ChildDiscardableSharedMemoryManager* manager_;
@@ -74,9 +71,12 @@
     if (free_span->shared_memory()->Lock(
             free_span->start() * base::GetPageSize() -
                 reinterpret_cast<size_t>(free_span->shared_memory()->memory()),
-            free_span->length() * base::GetPageSize()) !=
-        base::DiscardableSharedMemory::SUCCESS) {
-      heap_.DeleteSpan(free_span.Pass());
+            free_span->length() * base::GetPageSize()) ==
+        base::DiscardableSharedMemory::FAILED) {
+      DCHECK(!free_span->shared_memory()->IsMemoryResident());
+      // We have to release free memory before |free_span| can be destroyed.
+      heap_.ReleaseFreeMemory();
+      DCHECK(!free_span->shared_memory());
       continue;
     }
 
@@ -84,6 +84,10 @@
         new DiscardableMemoryShmemChunkImpl(this, free_span.Pass()));
   }
 
+  // Release free memory and free up the address space. Failing to do this
+  // can result in the child process running out of address space.
+  heap_.ReleaseFreeMemory();
+
   size_t pages_to_allocate =
       std::max(kAllocationSize / base::GetPageSize(), pages);
   size_t allocation_size_in_bytes = pages_to_allocate * base::GetPageSize();
@@ -111,10 +115,19 @@
       new DiscardableMemoryShmemChunkImpl(this, new_span.Pass()));
 }
 
+void ChildDiscardableSharedMemoryManager::ReleaseFreeMemory() {
+  base::AutoLock lock(lock_);
+
+  heap_.ReleaseFreeMemory();
+}
+
 bool ChildDiscardableSharedMemoryManager::LockSpan(
     DiscardableSharedMemoryHeap::Span* span) {
   base::AutoLock lock(lock_);
 
+  if (!span->shared_memory())
+    return false;
+
   size_t offset = span->start() * base::GetPageSize() -
       reinterpret_cast<size_t>(span->shared_memory()->memory());
   size_t length = span->length() * base::GetPageSize();
@@ -137,6 +150,7 @@
     DiscardableSharedMemoryHeap::Span* span) {
   base::AutoLock lock(lock_);
 
+  DCHECK(span->shared_memory());
   size_t offset = span->start() * base::GetPageSize() -
       reinterpret_cast<size_t>(span->shared_memory()->memory());
   size_t length = span->length() * base::GetPageSize();
@@ -144,21 +158,18 @@
   return span->shared_memory()->Unlock(offset, length);
 }
 
-bool ChildDiscardableSharedMemoryManager::IsSpanResident(
-    DiscardableSharedMemoryHeap::Span* span) const {
-  base::AutoLock lock(lock_);
-  return span->shared_memory()->IsMemoryResident();
-}
-
 void ChildDiscardableSharedMemoryManager::ReleaseSpan(
     scoped_ptr<DiscardableSharedMemoryHeap::Span> span) {
   base::AutoLock lock(lock_);
 
-  // Limit free list to spans less or equal to kAllocationSize.
-  if (span->length() * base::GetPageSize() > kAllocationSize) {
-    heap_.DeleteSpan(span.Pass());
+  // Delete span instead of merging it into free list if memory is gone.
+  if (!span->shared_memory())
     return;
-  }
+
+  // Purge backing memory if span is greater than kAllocationSize. This will
+  // prevent it from being reused and associated resources will be released.
+  if (span->length() * base::GetPageSize() > kAllocationSize)
+    span->shared_memory()->Purge(base::Time::Now());
 
   heap_.MergeIntoFreeList(span.Pass());
 }
diff --git a/content/child/child_discardable_shared_memory_manager.h b/content/child/child_discardable_shared_memory_manager.h
index aefd264..a5b8081f 100644
--- a/content/child/child_discardable_shared_memory_manager.h
+++ b/content/child/child_discardable_shared_memory_manager.h
@@ -9,13 +9,14 @@
 #include "base/memory/ref_counted.h"
 #include "base/synchronization/lock.h"
 #include "content/child/thread_safe_sender.h"
+#include "content/common/content_export.h"
 #include "content/common/discardable_shared_memory_heap.h"
 
 namespace content {
 
 // Implementation of DiscardableMemoryShmemAllocator that allocates
 // discardable memory segments through the browser process.
-class ChildDiscardableSharedMemoryManager
+class CONTENT_EXPORT ChildDiscardableSharedMemoryManager
     : public base::DiscardableMemoryShmemAllocator {
  public:
   explicit ChildDiscardableSharedMemoryManager(ThreadSafeSender* sender);
@@ -25,9 +26,11 @@
   scoped_ptr<base::DiscardableMemoryShmemChunk> AllocateLockedDiscardableMemory(
       size_t size) override;
 
+  // Release memory and associated resources that have been purged.
+  void ReleaseFreeMemory();
+
   bool LockSpan(DiscardableSharedMemoryHeap::Span* span);
   void UnlockSpan(DiscardableSharedMemoryHeap::Span* span);
-  bool IsSpanResident(DiscardableSharedMemoryHeap::Span* span) const;
   void ReleaseSpan(scoped_ptr<DiscardableSharedMemoryHeap::Span> span);
 
  private:
diff --git a/content/child/child_discardable_shared_memory_manager_browsertest.cc b/content/child/child_discardable_shared_memory_manager_browsertest.cc
new file mode 100644
index 0000000..1ffa01e
--- /dev/null
+++ b/content/child/child_discardable_shared_memory_manager_browsertest.cc
@@ -0,0 +1,121 @@
+// 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/bind.h"
+#include "base/command_line.h"
+#include "content/child/child_discardable_shared_memory_manager.h"
+#include "content/child/child_thread_impl.h"
+#include "content/common/host_discardable_shared_memory_manager.h"
+#include "content/public/common/content_switches.h"
+#include "content/public/test/content_browser_test.h"
+#include "content/public/test/content_browser_test_utils.h"
+#include "content/shell/browser/shell.h"
+#include "url/gurl.h"
+
+namespace content {
+
+class ChildDiscardableSharedMemoryManagerBrowserTest
+    : public ContentBrowserTest {
+ public:
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    command_line->AppendSwitch(switches::kSingleProcess);
+    command_line->AppendSwitch(switches::kDisableGpu);
+  }
+
+  static void ReleaseFreeMemory() {
+    ChildThreadImpl::current()
+        ->discardable_shared_memory_manager()
+        ->ReleaseFreeMemory();
+  }
+
+  static void AllocateLockedMemory(
+      size_t size,
+      scoped_ptr<base::DiscardableMemoryShmemChunk>* memory) {
+    *memory = ChildThreadImpl::current()
+                  ->discardable_shared_memory_manager()
+                  ->AllocateLockedDiscardableMemory(size);
+  }
+
+  static void LockMemory(base::DiscardableMemoryShmemChunk* memory,
+                         bool* result) {
+    *result = memory->Lock();
+  }
+
+  static void UnlockMemory(base::DiscardableMemoryShmemChunk* memory) {
+    memory->Unlock();
+  }
+
+  static void FreeMemory(scoped_ptr<base::DiscardableMemoryShmemChunk> memory) {
+  }
+};
+
+#if defined(OS_WIN)
+#define MAYBE_LockMemory DISABLED_LockMemory
+#else
+#define MAYBE_LockMemory LockMemory
+#endif
+
+IN_PROC_BROWSER_TEST_F(ChildDiscardableSharedMemoryManagerBrowserTest,
+                       MAYBE_LockMemory) {
+  const size_t kSize = 1024 * 1024;  // 1MiB.
+
+  NavigateToURL(shell(), GURL(url::kAboutBlankURL));
+
+  scoped_ptr<base::DiscardableMemoryShmemChunk> memory;
+  PostTaskToInProcessRendererAndWait(base::Bind(
+      &ChildDiscardableSharedMemoryManagerBrowserTest::AllocateLockedMemory,
+      kSize, &memory));
+
+  ASSERT_TRUE(memory);
+  void* addr = memory->Memory();
+  ASSERT_NE(nullptr, addr);
+
+  PostTaskToInProcessRendererAndWait(
+      base::Bind(&ChildDiscardableSharedMemoryManagerBrowserTest::UnlockMemory,
+                 memory.get()));
+
+  // Purge all unlocked memory.
+  HostDiscardableSharedMemoryManager::current()->SetMemoryLimit(0);
+
+  bool result = true;
+  PostTaskToInProcessRendererAndWait(
+      base::Bind(&ChildDiscardableSharedMemoryManagerBrowserTest::LockMemory,
+                 memory.get(), &result));
+
+  // Should fail as memory should have been purged.
+  EXPECT_FALSE(result);
+
+  PostTaskToInProcessRendererAndWait(
+      base::Bind(&ChildDiscardableSharedMemoryManagerBrowserTest::FreeMemory,
+                 base::Passed(&memory)));
+}
+
+IN_PROC_BROWSER_TEST_F(ChildDiscardableSharedMemoryManagerBrowserTest,
+                       AddressSpace) {
+  const size_t kLargeSize = 4 * 1024 * 1024;   // 4MiB.
+  const size_t kNumberOfInstances = 1024 + 1;  // >4GiB total.
+
+  NavigateToURL(shell(), GURL(url::kAboutBlankURL));
+
+  scoped_ptr<base::DiscardableMemoryShmemChunk> instances[kNumberOfInstances];
+  for (auto& memory : instances) {
+    PostTaskToInProcessRendererAndWait(base::Bind(
+        &ChildDiscardableSharedMemoryManagerBrowserTest::AllocateLockedMemory,
+        kLargeSize, &memory));
+    ASSERT_TRUE(memory);
+    void* addr = memory->Memory();
+    ASSERT_NE(nullptr, addr);
+    PostTaskToInProcessRendererAndWait(base::Bind(
+        &ChildDiscardableSharedMemoryManagerBrowserTest::UnlockMemory,
+        memory.get()));
+  }
+
+  for (auto& memory : instances) {
+    PostTaskToInProcessRendererAndWait(
+        base::Bind(&ChildDiscardableSharedMemoryManagerBrowserTest::FreeMemory,
+                   base::Passed(&memory)));
+  }
+}
+
+}  // content
diff --git a/content/child/child_thread_impl.cc b/content/child/child_thread_impl.cc
index d5d7a3d..5df6631 100644
--- a/content/child/child_thread_impl.cc
+++ b/content/child/child_thread_impl.cc
@@ -17,6 +17,7 @@
 #include "base/logging.h"
 #include "base/message_loop/message_loop.h"
 #include "base/message_loop/timer_slack.h"
+#include "base/metrics/field_trial.h"
 #include "base/process/kill.h"
 #include "base/process/process_handle.h"
 #include "base/strings/string_number_conversions.h"
@@ -161,18 +162,19 @@
 
 #if defined(OS_ANDROID)
 ChildThreadImpl* g_child_thread = NULL;
+bool g_child_thread_initialized = false;
 
 // A lock protects g_child_thread.
-base::LazyInstance<base::Lock> g_lazy_child_thread_lock =
+base::LazyInstance<base::Lock>::Leaky g_lazy_child_thread_lock =
     LAZY_INSTANCE_INITIALIZER;
 
 // base::ConditionVariable has an explicit constructor that takes
 // a base::Lock pointer as parameter. The base::DefaultLazyInstanceTraits
 // doesn't handle the case. Thus, we need our own class here.
 struct CondVarLazyInstanceTraits {
-  static const bool kRegisterOnExit = true;
+  static const bool kRegisterOnExit = false;
 #ifndef NDEBUG
-  static const bool kAllowedToAccessOnNonjoinableThread = false;
+  static const bool kAllowedToAccessOnNonjoinableThread = true;
 #endif
 
   static base::ConditionVariable* New(void* instance) {
@@ -373,6 +375,7 @@
   {
     base::AutoLock lock(g_lazy_child_thread_lock.Get());
     g_child_thread = this;
+    g_child_thread_initialized = true;
   }
   // Signalling without locking is fine here because only
   // one thread can wait on the condition variable.
@@ -396,6 +399,13 @@
 }
 
 ChildThreadImpl::~ChildThreadImpl() {
+#if defined(OS_ANDROID)
+  {
+    base::AutoLock lock(g_lazy_child_thread_lock.Get());
+    g_child_thread = nullptr;
+  }
+#endif
+
 #ifdef IPC_MESSAGE_LOG_ENABLED
   IPC::Logging::GetInstance()->SetIPCSender(NULL);
 #endif
@@ -599,12 +609,18 @@
       "this method should NOT be called from child thread itself";
   {
     base::AutoLock lock(g_lazy_child_thread_lock.Get());
-    while (!g_child_thread)
+    while (!g_child_thread_initialized)
       g_lazy_child_thread_cv.Get().Wait();
+
+    // g_child_thread may already have been destructed while we didn't hold the
+    // lock.
+    if (!g_child_thread)
+      return;
+
+    DCHECK_NE(base::MessageLoop::current(), g_child_thread->message_loop());
+    g_child_thread->message_loop()->PostTask(
+        FROM_HERE, base::Bind(&QuitMainThreadMessageLoop));
   }
-  DCHECK_NE(base::MessageLoop::current(), g_child_thread->message_loop());
-  g_child_thread->message_loop()->PostTask(
-      FROM_HERE, base::Bind(&QuitMainThreadMessageLoop));
 }
 #endif
 
@@ -634,6 +650,23 @@
   if (background)
     timer_slack = base::TIMER_SLACK_MAXIMUM;
   base::MessageLoop::current()->SetTimerSlack(timer_slack);
+
+#ifdef OS_WIN
+  // Windows Vista+ has a fancy process backgrounding mode that can only be set
+  // from within the process. This used to be how chrome set its renderers into
+  // background mode on Windows but was removed due to http://crbug.com/398103.
+  // As we experiment with bringing back some other form of background mode for
+  // hidden renderers, add a bucket to allow us to trigger this undesired method
+  // of setting background state in order to confirm that the metrics which were
+  // added to prevent regressions on the aforementioned issue indeed catch such
+  // regressions and are thus a reliable way to confirm that our latest proposal
+  // doesn't cause such issues. TODO(gab): Remove this once the experiment is
+  // over (http://crbug.com/458594).
+  base::FieldTrial* trial =
+      base::FieldTrialList::Find("BackgroundRendererProcesses");
+  if (trial && trial->group_name() == "AllowBackgroundModeFromRenderer")
+    base::Process::Current().SetProcessBackgrounded(background);
+#endif  // OS_WIN
 }
 
 }  // namespace content
diff --git a/content/child/notifications/notification_image_loader.cc b/content/child/notifications/notification_image_loader.cc
index 009f04ce..0478188d 100644
--- a/content/child/notifications/notification_image_loader.cc
+++ b/content/child/notifications/notification_image_loader.cc
@@ -11,6 +11,7 @@
 #include "third_party/WebKit/public/platform/Platform.h"
 #include "third_party/WebKit/public/platform/WebURL.h"
 #include "third_party/WebKit/public/platform/WebURLLoader.h"
+#include "third_party/WebKit/public/platform/WebURLRequest.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 
 using blink::WebURL;
@@ -21,42 +22,34 @@
 namespace content {
 
 NotificationImageLoader::NotificationImageLoader(
-    const NotificationImageLoadedCallback& callback)
+    const ImageLoadCompletedCallback& callback,
+    const scoped_refptr<base::SingleThreadTaskRunner>& worker_task_runner)
     : callback_(callback),
+      worker_task_runner_(worker_task_runner),
+      notification_id_(0),
       completed_(false) {}
 
 NotificationImageLoader::~NotificationImageLoader() {
-  // The WebURLLoader instance must be destroyed on the same thread it was
-  // created on, being the main thread.
-  if (!main_thread_task_runner_->RunsTasksOnCurrentThread())
-    main_thread_task_runner_->DeleteSoon(FROM_HERE, url_loader_.release());
+  if (main_thread_task_runner_)
+    DCHECK(main_thread_task_runner_->BelongsToCurrentThread());
 }
 
-void NotificationImageLoader::StartOnMainThread(
-    const WebURL& image_url,
-    const scoped_refptr<base::SingleThreadTaskRunner>& worker_task_runner) {
+void NotificationImageLoader::StartOnMainThread(int notification_id,
+                                                const GURL& image_url) {
   DCHECK(ChildThreadImpl::current());
   DCHECK(!url_loader_);
-  DCHECK(worker_task_runner);
 
-  worker_task_runner_ = worker_task_runner;
   main_thread_task_runner_ = base::ThreadTaskRunnerHandle::Get();
+  notification_id_ = notification_id;
 
-  WebURLRequest request(image_url);
+  WebURL image_web_url(image_url);
+  WebURLRequest request(image_web_url);
   request.setRequestContext(WebURLRequest::RequestContextImage);
 
   url_loader_.reset(blink::Platform::current()->createURLLoader());
   url_loader_->loadAsynchronously(request, this);
 }
 
-SkBitmap NotificationImageLoader::GetDecodedImage() const {
-  if (buffer_.empty())
-    return SkBitmap();
-
-  ImageDecoder decoder;
-  return decoder.Decode(&buffer_[0], buffer_.size());
-}
-
 void NotificationImageLoader::didReceiveData(
     WebURLLoader* loader,
     const char* data,
@@ -86,11 +79,35 @@
 }
 
 void NotificationImageLoader::RunCallbackOnWorkerThread() {
-  scoped_refptr<NotificationImageLoader> loader = make_scoped_refptr(this);
-  if (worker_task_runner_->BelongsToCurrentThread())
-    callback_.Run(loader);
-  else
-    worker_task_runner_->PostTask(FROM_HERE, base::Bind(callback_, loader));
+  url_loader_.reset();
+
+  completed_ = true;
+  SkBitmap icon = GetDecodedImage();
+
+  if (worker_task_runner_->BelongsToCurrentThread()) {
+    callback_.Run(notification_id_, icon);
+  } else {
+    worker_task_runner_->PostTask(
+        FROM_HERE, base::Bind(callback_, notification_id_, icon));
+  }
+}
+
+SkBitmap NotificationImageLoader::GetDecodedImage() const {
+  DCHECK(completed_);
+  if (buffer_.empty())
+    return SkBitmap();
+
+  ImageDecoder decoder;
+  return decoder.Decode(&buffer_[0], buffer_.size());
+}
+
+void NotificationImageLoader::DeleteOnCorrectThread() const {
+  if (!ChildThreadImpl::current()) {
+    main_thread_task_runner_->DeleteSoon(FROM_HERE, this);
+    return;
+  }
+
+  delete this;
 }
 
 }  // namespace content
diff --git a/content/child/notifications/notification_image_loader.h b/content/child/notifications/notification_image_loader.h
index 14a36da..33a38f0 100644
--- a/content/child/notifications/notification_image_loader.h
+++ b/content/child/notifications/notification_image_loader.h
@@ -10,8 +10,10 @@
 #include "base/callback.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/scoped_ptr.h"
+#include "base/sequenced_task_runner_helpers.h"
 #include "third_party/WebKit/public/platform/WebURLLoaderClient.h"
 
+class GURL;
 class SkBitmap;
 
 namespace base {
@@ -26,33 +28,28 @@
 
 namespace content {
 
-class NotificationImageLoader;
-
-// Callback to be invoked when an image load has been completed.
-using NotificationImageLoadedCallback =
-    base::Callback<void(scoped_refptr<NotificationImageLoader>)>;
+struct NotificationImageLoaderDeleter;
 
 // Downloads the image associated with a notification and decodes the received
 // image. This must be completed before notifications are shown to the user.
-// Image downloaders must not be re-used for multiple notifications. The image
-// loader must be started from the main thread, but will invoke the callback on
-// the thread identified in the StartOnMainThread call.
+// Image downloaders must not be re-used for multiple notifications.
+//
+// All methods, except for the constructor, are expected to be used on the
+// renderer main thread.
 class NotificationImageLoader
     : public blink::WebURLLoaderClient,
-      public base::RefCountedThreadSafe<NotificationImageLoader> {
- public:
-  explicit NotificationImageLoader(
-      const NotificationImageLoadedCallback& callback);
+      public base::RefCountedThreadSafe<NotificationImageLoader,
+                                        NotificationImageLoaderDeleter> {
+  using ImageLoadCompletedCallback = base::Callback<void(int, const SkBitmap&)>;
 
-  // Asynchronously starts loading |image_url|.
-  // Must be called on the main thread. The callback should be executed by
-  // |worker_task_runner|.
-  void StartOnMainThread(
-      const blink::WebURL& image_url,
+ public:
+  NotificationImageLoader(
+      const ImageLoadCompletedCallback& callback,
       const scoped_refptr<base::SingleThreadTaskRunner>& worker_task_runner);
 
-  // Returns the SkBitmap resulting from decoding the loaded buffer.
-  SkBitmap GetDecodedImage() const;
+  // Asynchronously starts loading |image_url| using a Blink WebURLLoader. Must
+  // only be called on the main thread.
+  void StartOnMainThread(int notification_id, const GURL& image_url);
 
   // blink::WebURLLoaderClient implementation.
   virtual void didReceiveData(blink::WebURLLoader* loader,
@@ -66,7 +63,11 @@
                        const blink::WebURLError& error);
 
  private:
-  friend class base::RefCountedThreadSafe<NotificationImageLoader>;
+  friend class base::DeleteHelper<NotificationImageLoader>;
+  friend class base::RefCountedThreadSafe<NotificationImageLoader,
+                                          NotificationImageLoaderDeleter>;
+  friend struct NotificationImageLoaderDeleter;
+
   virtual ~NotificationImageLoader();
 
   // Invokes the callback on the thread this image loader was started for. When
@@ -74,18 +75,34 @@
   // For all other threads a task will be posted to the appropriate task runner.
   void RunCallbackOnWorkerThread();
 
-  NotificationImageLoadedCallback callback_;
+  // Returns a Skia bitmap, empty if buffer_ was empty or could not be decoded
+  // as an image, or a valid bitmap otherwise.
+  SkBitmap GetDecodedImage() const;
 
-  scoped_ptr<blink::WebURLLoader> url_loader_;
+  // Ensures that we delete the image loader on the main thread.
+  void DeleteOnCorrectThread() const;
+
+  ImageLoadCompletedCallback callback_;
+
   scoped_refptr<base::SingleThreadTaskRunner> worker_task_runner_;
   scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner_;
+
+  int notification_id_;
   bool completed_;
 
+  scoped_ptr<blink::WebURLLoader> url_loader_;
+
   std::vector<uint8_t> buffer_;
 
   DISALLOW_COPY_AND_ASSIGN(NotificationImageLoader);
 };
 
+struct NotificationImageLoaderDeleter {
+  static void Destruct(const NotificationImageLoader* context) {
+    context->DeleteOnCorrectThread();
+  }
+};
+
 }  // namespace content
 
 #endif  // CONTENT_CHILD_NOTIFICATIONS_NOTIFICATION_IMAGE_LOADER_H_
diff --git a/content/child/notifications/notification_manager.cc b/content/child/notifications/notification_manager.cc
index 9108a3a..cccece9 100644
--- a/content/child/notifications/notification_manager.cc
+++ b/content/child/notifications/notification_manager.cc
@@ -10,14 +10,12 @@
 #include "base/threading/thread_local.h"
 #include "content/child/notifications/notification_data_conversions.h"
 #include "content/child/notifications/notification_dispatcher.h"
-#include "content/child/notifications/notification_image_loader.h"
 #include "content/child/service_worker/web_service_worker_registration_impl.h"
 #include "content/child/thread_safe_sender.h"
 #include "content/child/worker_task_runner.h"
 #include "content/common/platform_notification_messages.h"
 #include "content/public/common/platform_notification_data.h"
 #include "third_party/WebKit/public/platform/WebSerializedOrigin.h"
-#include "third_party/WebKit/public/platform/modules/notifications/WebNotificationData.h"
 #include "third_party/WebKit/public/platform/modules/notifications/WebNotificationDelegate.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 
@@ -40,9 +38,8 @@
     base::SingleThreadTaskRunner* main_thread_task_runner,
     NotificationDispatcher* notification_dispatcher)
     : thread_safe_sender_(thread_safe_sender),
-      main_thread_task_runner_(main_thread_task_runner),
       notification_dispatcher_(notification_dispatcher),
-      weak_factory_(this) {
+      pending_notifications_(main_thread_task_runner) {
   g_notification_manager_tls.Pointer()->Set(this);
 }
 
@@ -73,15 +70,15 @@
     const blink::WebNotificationData& notification_data,
     blink::WebNotificationDelegate* delegate) {
   if (notification_data.icon.isEmpty()) {
-    DisplayNotification(origin, notification_data, delegate,
-                        nullptr /* image_loader */);
+    DisplayPageNotification(origin, notification_data, delegate, SkBitmap());
     return;
   }
 
-  pending_page_notifications_[delegate] = CreateImageLoader(
-      notification_data.icon,
-      base::Bind(&NotificationManager::DisplayNotification,
-                 weak_factory_.GetWeakPtr(),
+  pending_notifications_.FetchPageNotificationResources(
+      notification_data,
+      delegate,
+      base::Bind(&NotificationManager::DisplayPageNotification,
+                 base::Unretained(this),  // this owns |pending_notifications_|
                  origin,
                  notification_data,
                  delegate));
@@ -93,48 +90,46 @@
     blink::WebServiceWorkerRegistration* service_worker_registration,
     blink::WebNotificationShowCallbacks* callbacks) {
   DCHECK(service_worker_registration);
-
   int64 service_worker_registration_id =
       static_cast<WebServiceWorkerRegistrationImpl*>(
           service_worker_registration)->registration_id();
 
-  int request_id = persistent_notification_requests_.Add(callbacks);
+  scoped_ptr<blink::WebNotificationShowCallbacks> owned_callbacks(callbacks);
   if (notification_data.icon.isEmpty()) {
     DisplayPersistentNotification(origin,
                                   notification_data,
                                   service_worker_registration_id,
-                                  request_id,
-                                  nullptr /* image_loader */);
+                                  owned_callbacks.Pass(),
+                                  SkBitmap());
     return;
   }
 
-  pending_persistent_notifications_.insert(CreateImageLoader(
-      notification_data.icon,
+  pending_notifications_.FetchPersistentNotificationResources(
+      notification_data,
       base::Bind(&NotificationManager::DisplayPersistentNotification,
-                 weak_factory_.GetWeakPtr(),
+                 base::Unretained(this),  // this owns |pending_notifications_|
                  origin,
                  notification_data,
                  service_worker_registration_id,
-                 request_id)));
+                 base::Passed(&owned_callbacks)));
 }
 
 void NotificationManager::close(blink::WebNotificationDelegate* delegate) {
-  if (RemovePendingPageNotification(delegate))
+  if (pending_notifications_.CancelPageNotificationFetches(delegate))
     return;
 
-  auto iter = active_notifications_.begin();
-  for (; iter != active_notifications_.end(); ++iter) {
-    if (iter->second != delegate)
+  for (auto& iter : active_page_notifications_) {
+    if (iter.second != delegate)
       continue;
 
     thread_safe_sender_->Send(
-        new PlatformNotificationHostMsg_Close(iter->first));
-    active_notifications_.erase(iter);
+        new PlatformNotificationHostMsg_Close(iter.first));
+    active_page_notifications_.erase(iter.first);
     return;
   }
 
   // It should not be possible for Blink to call close() on a Notification which
-  // does not exist anymore in the manager.
+  // does not exist in either the pending or active notification lists.
   NOTREACHED();
 }
 
@@ -146,15 +141,14 @@
 
 void NotificationManager::notifyDelegateDestroyed(
     blink::WebNotificationDelegate* delegate) {
-  if (RemovePendingPageNotification(delegate))
+  if (pending_notifications_.CancelPageNotificationFetches(delegate))
     return;
 
-  auto iter = active_notifications_.begin();
-  for (; iter != active_notifications_.end(); ++iter) {
-    if (iter->second != delegate)
+  for (auto& iter : active_page_notifications_) {
+    if (iter.second != delegate)
       continue;
 
-    active_notifications_.erase(iter);
+    active_page_notifications_.erase(iter.first);
     return;
   }
 }
@@ -182,85 +176,53 @@
 }
 
 void NotificationManager::OnDidShow(int notification_id) {
-  const auto& iter = active_notifications_.find(notification_id);
-  if (iter == active_notifications_.end())
+  const auto& iter = active_page_notifications_.find(notification_id);
+  if (iter == active_page_notifications_.end())
     return;
 
   iter->second->dispatchShowEvent();
 }
 
 void NotificationManager::OnDidClose(int notification_id) {
-  const auto& iter = active_notifications_.find(notification_id);
-  if (iter == active_notifications_.end())
+  const auto& iter = active_page_notifications_.find(notification_id);
+  if (iter == active_page_notifications_.end())
     return;
 
   iter->second->dispatchCloseEvent();
-  active_notifications_.erase(iter);
+  active_page_notifications_.erase(iter);
 }
 
 void NotificationManager::OnDidClick(int notification_id) {
-  const auto& iter = active_notifications_.find(notification_id);
-  if (iter == active_notifications_.end())
+  const auto& iter = active_page_notifications_.find(notification_id);
+  if (iter == active_page_notifications_.end())
     return;
 
   iter->second->dispatchClickEvent();
 }
 
-scoped_refptr<NotificationImageLoader> NotificationManager::CreateImageLoader(
-    const blink::WebURL& image_url,
-    const NotificationImageLoadedCallback& callback) const {
-  scoped_refptr<NotificationImageLoader> pending_notification(
-      new NotificationImageLoader(callback));
-
-  main_thread_task_runner_->PostTask(
-      FROM_HERE, base::Bind(&NotificationImageLoader::StartOnMainThread,
-                            pending_notification, image_url,
-                            base::ThreadTaskRunnerHandle::Get()));
-
-  return pending_notification;
-}
-
-void NotificationManager::DisplayNotification(
+void NotificationManager::DisplayPageNotification(
     const blink::WebSerializedOrigin& origin,
     const blink::WebNotificationData& notification_data,
     blink::WebNotificationDelegate* delegate,
-    scoped_refptr<NotificationImageLoader> image_loader) {
+    const SkBitmap& icon) {
   int notification_id =
       notification_dispatcher_->GenerateNotificationId(CurrentWorkerId());
 
-  active_notifications_[notification_id] = delegate;
-
-  SkBitmap icon;
-  if (image_loader)
-    icon = image_loader->GetDecodedImage();
-
+  active_page_notifications_[notification_id] = delegate;
   thread_safe_sender_->Send(
       new PlatformNotificationHostMsg_Show(
           notification_id,
           GURL(origin.string()),
           icon,
           ToPlatformNotificationData(notification_data)));
-
-  // If this Notification contained an icon, it can be safely deleted now.
-  RemovePendingPageNotification(delegate);
 }
 
 void NotificationManager::DisplayPersistentNotification(
     const blink::WebSerializedOrigin& origin,
     const blink::WebNotificationData& notification_data,
     int64 service_worker_registration_id,
-    int request_id,
-    scoped_refptr<NotificationImageLoader> image_loader) {
-  blink::WebNotificationShowCallbacks* callbacks =
-      persistent_notification_requests_.Lookup(request_id);
-  DCHECK(callbacks);
-
-  SkBitmap icon;
-  if (image_loader) {
-    pending_persistent_notifications_.erase(image_loader);
-    icon = image_loader->GetDecodedImage();
-  }
-
+    scoped_ptr<blink::WebNotificationShowCallbacks> callbacks,
+    const SkBitmap& icon) {
   thread_safe_sender_->Send(
       new PlatformNotificationHostMsg_ShowPersistent(
           service_worker_registration_id,
@@ -271,18 +233,6 @@
   // There currently isn't a case in which the promise would be rejected per
   // our implementation, so always resolve it here.
   callbacks->onSuccess();
-
-  persistent_notification_requests_.Remove(request_id);
-}
-
-bool NotificationManager::RemovePendingPageNotification(
-    blink::WebNotificationDelegate* delegate) {
-  const auto& iter = pending_page_notifications_.find(delegate);
-  if (iter == pending_page_notifications_.end())
-    return false;
-
-  pending_page_notifications_.erase(iter);
-  return true;
 }
 
 }  // namespace content
diff --git a/content/child/notifications/notification_manager.h b/content/child/notifications/notification_manager.h
index 47d979d5..ee64f3c 100644
--- a/content/child/notifications/notification_manager.h
+++ b/content/child/notifications/notification_manager.h
@@ -8,21 +8,15 @@
 #include <map>
 #include <set>
 
-#include "base/id_map.h"
 #include "base/memory/ref_counted.h"
-#include "base/memory/weak_ptr.h"
 #include "base/single_thread_task_runner.h"
 #include "content/child/notifications/notification_dispatcher.h"
-#include "content/child/notifications/notification_image_loader.h"
+#include "content/child/notifications/pending_notifications_tracker.h"
 #include "content/child/worker_task_runner.h"
 #include "third_party/WebKit/public/platform/modules/notifications/WebNotificationManager.h"
 
 class SkBitmap;
 
-namespace blink {
-class WebURL;
-}
-
 namespace content {
 
 class ThreadSafeSender;
@@ -73,56 +67,36 @@
   void OnDidClose(int notification_id);
   void OnDidClick(int notification_id);
 
-  // Asynchronously starts loading |image_url| on the main thread and returns a
-  // reference to the notification image loaded responsible for this. |callback|
-  // will be invoked on the calling thread when the load is complete.
-  scoped_refptr<NotificationImageLoader> CreateImageLoader(
-      const blink::WebURL& image_url,
-      const NotificationImageLoadedCallback& callback) const;
+  // To be called when a page notification is ready to be displayed. Will
+  // inform the browser process about all available data. The |delegate|,
+  // owned by Blink, will be used to feed back events associated with the
+  // notification to the JavaScript object.
+  void DisplayPageNotification(
+      const blink::WebSerializedOrigin& origin,
+      const blink::WebNotificationData& notification_data,
+      blink::WebNotificationDelegate* delegate,
+      const SkBitmap& icon);
 
-  // Sends an IPC to the browser process to display the notification,
-  // accompanied by the downloaded icon.
-  void DisplayNotification(const blink::WebSerializedOrigin& origin,
-                           const blink::WebNotificationData& notification_data,
-                           blink::WebNotificationDelegate* delegate,
-                           scoped_refptr<NotificationImageLoader> image_loader);
-
-  // Sends an IPC to the browser process to display the persistent notification,
-  // accompanied by the downloaded icon.
+  // To be called when a persistent notification is ready to be displayed. Will
+  // inform the browser process about all available data. The |callbacks| will
+  // be used to inform the Promise pending in Blink that the notification has
+  // been send to the browser process to be displayed.
   void DisplayPersistentNotification(
       const blink::WebSerializedOrigin& origin,
       const blink::WebNotificationData& notification_data,
       int64 service_worker_registration_id,
-      int request_id,
-      scoped_refptr<NotificationImageLoader> image_loader);
-
-  // Removes the notification identified by |delegate| from the set of
-  // pending notifications, and returns whether it could be found.
-  bool RemovePendingPageNotification(blink::WebNotificationDelegate* delegate);
+      scoped_ptr<blink::WebNotificationShowCallbacks> callbacks,
+      const SkBitmap& icon);
 
   scoped_refptr<ThreadSafeSender> thread_safe_sender_;
-  scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner_;
   scoped_refptr<NotificationDispatcher> notification_dispatcher_;
 
-  // Tracking display requests for persistent notifications.
-  IDMap<blink::WebNotificationShowCallbacks, IDMapOwnPointer>
-      persistent_notification_requests_;
-
-  // A map tracking Page-bound notifications whose icon is still being
-  // downloaded. These downloads can be cancelled by the developer.
-  std::map<blink::WebNotificationDelegate*,
-           scoped_refptr<NotificationImageLoader>>
-      pending_page_notifications_;
-
-  // A set tracking Persistent notifications whose icon is still being
-  // downloaded. These downloads cannot be cancelled by the developer.
-  std::set<scoped_refptr<NotificationImageLoader>>
-      pending_persistent_notifications_;
+  // Tracker which stores all pending Notifications, both page and persistent
+  // ones, until all their associated resources have been fetched.
+  PendingNotificationsTracker pending_notifications_;
 
   // Map to store the delegate associated with a notification request Id.
-  std::map<int, blink::WebNotificationDelegate*> active_notifications_;
-
-  base::WeakPtrFactory<NotificationManager> weak_factory_;
+  std::map<int, blink::WebNotificationDelegate*> active_page_notifications_;
 
   DISALLOW_COPY_AND_ASSIGN(NotificationManager);
 };
diff --git a/content/child/notifications/pending_notifications_tracker.cc b/content/child/notifications/pending_notifications_tracker.cc
new file mode 100644
index 0000000..69b5c8e
--- /dev/null
+++ b/content/child/notifications/pending_notifications_tracker.cc
@@ -0,0 +1,116 @@
+// 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 "content/child/notifications/pending_notifications_tracker.h"
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/thread_task_runner_handle.h"
+#include "content/child/notifications/notification_image_loader.h"
+#include "content/child/notifications/notification_manager.h"
+#include "third_party/WebKit/public/platform/WebSerializedOrigin.h"
+#include "third_party/WebKit/public/platform/modules/notifications/WebNotificationData.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+
+namespace content {
+
+// Stores the information associated with a pending notification.
+struct PendingNotificationsTracker::PendingNotification {
+  PendingNotification(
+      const scoped_refptr<NotificationImageLoader>& image_loader,
+      const NotificationResourcesFetchedCallback& callback)
+      : image_loader(image_loader),
+        callback(callback) {}
+
+  scoped_refptr<NotificationImageLoader> image_loader;
+  NotificationResourcesFetchedCallback callback;
+};
+
+PendingNotificationsTracker::PendingNotificationsTracker(
+    scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner)
+    : main_thread_task_runner_(main_thread_task_runner),
+      weak_factory_(this) {}
+
+PendingNotificationsTracker::~PendingNotificationsTracker() {}
+
+void PendingNotificationsTracker::FetchPageNotificationResources(
+    const blink::WebNotificationData& notification_data,
+    blink::WebNotificationDelegate* delegate,
+    const NotificationResourcesFetchedCallback& callback) {
+  delegate_to_pending_id_map_[delegate] = FetchNotificationResources(
+      notification_data,
+      callback,
+      new NotificationImageLoader(
+          base::Bind(
+              &PendingNotificationsTracker::DidFetchPageNotification,
+              weak_factory_.GetWeakPtr(), delegate),
+          base::ThreadTaskRunnerHandle::Get()));
+}
+
+void PendingNotificationsTracker::FetchPersistentNotificationResources(
+    const blink::WebNotificationData& notification_data,
+    const NotificationResourcesFetchedCallback& callback) {
+  FetchNotificationResources(
+      notification_data,
+      callback,
+      new NotificationImageLoader(
+          base::Bind(
+              &PendingNotificationsTracker::DidFetchPersistentNotification,
+              weak_factory_.GetWeakPtr()),
+          base::ThreadTaskRunnerHandle::Get()));
+}
+
+bool PendingNotificationsTracker::CancelPageNotificationFetches(
+    blink::WebNotificationDelegate* delegate) {
+  auto iter = delegate_to_pending_id_map_.find(delegate);
+  if (iter == delegate_to_pending_id_map_.end())
+    return false;
+
+  pending_notifications_.Remove(iter->second);
+  delegate_to_pending_id_map_.erase(iter);
+
+  return true;
+}
+
+void PendingNotificationsTracker::DidFetchPageNotification(
+    blink::WebNotificationDelegate* delegate,
+    int notification_id,
+    const SkBitmap& icon) {
+  PendingNotification* pending_notification =
+      pending_notifications_.Lookup(notification_id);
+  DCHECK(pending_notification);
+
+  pending_notification->callback.Run(icon);
+
+  delegate_to_pending_id_map_.erase(delegate);
+  pending_notifications_.Remove(notification_id);
+}
+
+void PendingNotificationsTracker::DidFetchPersistentNotification(
+    int notification_id, const SkBitmap& icon) {
+  PendingNotification* pending_notification =
+      pending_notifications_.Lookup(notification_id);
+  DCHECK(pending_notification);
+
+  pending_notification->callback.Run(icon);
+
+  pending_notifications_.Remove(notification_id);
+}
+
+int PendingNotificationsTracker::FetchNotificationResources(
+    const blink::WebNotificationData& notification_data,
+    const NotificationResourcesFetchedCallback& callback,
+    const scoped_refptr<NotificationImageLoader>& image_loader) {
+  int notification_id = pending_notifications_.Add(
+      new PendingNotification(image_loader, callback));
+
+  main_thread_task_runner_->PostTask(
+      FROM_HERE, base::Bind(&NotificationImageLoader::StartOnMainThread,
+                            image_loader, notification_id,
+                            GURL(notification_data.icon.spec())));
+
+  return notification_id;
+}
+
+}  // namespace content
diff --git a/content/child/notifications/pending_notifications_tracker.h b/content/child/notifications/pending_notifications_tracker.h
new file mode 100644
index 0000000..2d9d3b5
--- /dev/null
+++ b/content/child/notifications/pending_notifications_tracker.h
@@ -0,0 +1,107 @@
+// 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 CONTENT_CHILD_NOTIFICATIONS_PENDING_NOTIFICATIONS_TRACKER_H_
+#define CONTENT_CHILD_NOTIFICATIONS_PENDING_NOTIFICATIONS_TRACKER_H_
+
+#include <map>
+
+#include "base/id_map.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "third_party/WebKit/public/platform/modules/notifications/WebNotificationManager.h"
+
+class SkBitmap;
+
+namespace base {
+class SingleThreadTaskRunner;
+}
+
+namespace blink {
+struct WebNotificationData;
+class WebNotificationDelegate;
+class WebSerializedOrigin;
+}
+
+namespace content {
+
+class NotificationImageLoader;
+class NotificationManager;
+
+// Type definition for the callback signature which is to be invoked when the
+// resources associated with a notification have been fetched.
+using NotificationResourcesFetchedCallback =
+    base::Callback<void(const SkBitmap&)>;
+
+// Tracks all aspects of all pending Web Notifications. Most notably, it's in
+// charge of ensuring that all resource fetches associated with the notification
+// are completed as expected. The data associated with the notifications is
+// stored in this class, to maintain thread integrity of their members.
+//
+// The pending notification tracker is owned by the NotificationManager, and
+// lives on the thread that manager has been associated with.
+class PendingNotificationsTracker {
+ public:
+  explicit PendingNotificationsTracker(
+      scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner);
+  ~PendingNotificationsTracker();
+
+  // Adds a page notification to the tracker. Resource fetches for the
+  // notification will be started on asynchronously the main thread.
+  void FetchPageNotificationResources(
+      const blink::WebNotificationData& notification_data,
+      blink::WebNotificationDelegate* delegate,
+      const NotificationResourcesFetchedCallback& callback);
+
+  // Adds a persistent notification to the tracker. Resource fetches for the
+  // notification will be started asynchronously on the main thread.
+  void FetchPersistentNotificationResources(
+      const blink::WebNotificationData& notification_data,
+      const NotificationResourcesFetchedCallback& callback);
+
+  // Cancels all pending and in-fligth fetches for the page notification
+  // identified by |delegate|. Returns if the notification was cancelled.
+  bool CancelPageNotificationFetches(blink::WebNotificationDelegate* delegate);
+
+ private:
+  // To be called on the worker thread when the pending page notification
+  // identified by |notification_id| has finished fetching the icon.
+  void DidFetchPageNotification(blink::WebNotificationDelegate* delegate,
+                                int notification_id,
+                                const SkBitmap& icon);
+
+  // To be called on the worker thread when the pending persistent notification
+  // identified by |notification_id| has finished fetching the icon.
+  void DidFetchPersistentNotification(int notification_id,
+                                      const SkBitmap& icon);
+
+  // Common code for starting to fetch resources associated with any kind of
+  // notification. Will return the id of the pending notification as allocated
+  // in the |pending_notifications_| map.
+  int FetchNotificationResources(
+      const blink::WebNotificationData& notification_data,
+      const NotificationResourcesFetchedCallback& callback,
+      const scoped_refptr<NotificationImageLoader>& image_loader);
+
+  scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner_;
+
+  struct PendingNotification;
+
+  // List of the notifications whose resources are still being fetched.
+  IDMap<PendingNotification, IDMapOwnPointer> pending_notifications_;
+
+  // In order to be able to cancel pending page notifications by delegate, store
+  // a mapping of the delegate to the pending notification id as well.
+  std::map<blink::WebNotificationDelegate*, int> delegate_to_pending_id_map_;
+
+  base::WeakPtrFactory<PendingNotificationsTracker> weak_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(PendingNotificationsTracker);
+};
+
+}  // namespace content
+
+#endif  // CONTENT_CHILD_NOTIFICATIONS_PENDING_NOTIFICATIONS_TRACKER_H_
diff --git a/content/child/npapi/plugin_url_fetcher.cc b/content/child/npapi/plugin_url_fetcher.cc
index 15bd602..a99f2e1 100644
--- a/content/child/npapi/plugin_url_fetcher.cc
+++ b/content/child/npapi/plugin_url_fetcher.cc
@@ -16,7 +16,6 @@
 #include "content/child/request_extra_data.h"
 #include "content/child/request_info.h"
 #include "content/child/resource_dispatcher.h"
-#include "content/child/resource_loader_bridge.h"
 #include "content/child/web_url_loader_impl.h"
 #include "content/common/resource_request_body.h"
 #include "content/common/service_worker/service_worker_types.h"
@@ -104,7 +103,8 @@
       resource_id_(resource_id),
       copy_stream_data_(copy_stream_data),
       data_offset_(0),
-      pending_failure_notification_(false) {
+      pending_failure_notification_(false),
+      request_id_(-1) {
   RequestInfo request_info;
   request_info.method = method;
   request_info.url = url;
@@ -146,25 +146,25 @@
       request_info.headers = std::string("Range: ") + range;
   }
 
-  bridge_.reset(ChildThreadImpl::current()->resource_dispatcher()->CreateBridge(
-      request_info));
-  if (!body.empty()) {
-    scoped_refptr<ResourceRequestBody> request_body =
-        new ResourceRequestBody;
+  scoped_refptr<ResourceRequestBody> request_body = new ResourceRequestBody;
+  if (!body.empty())
     request_body->AppendBytes(&body[0], body.size());
-    bridge_->SetRequestBody(request_body.get());
-  }
 
-  bridge_->Start(this);
+  request_id_ = ChildThreadImpl::current()->resource_dispatcher()->StartAsync(
+      request_info, request_body.get(), this);
 
   // TODO(jam): range requests
 }
 
 PluginURLFetcher::~PluginURLFetcher() {
+  if (request_id_ >= 0) {
+    ChildThreadImpl::current()->resource_dispatcher()->RemovePendingRequest(
+        request_id_);
+  }
 }
 
 void PluginURLFetcher::Cancel() {
-  bridge_->Cancel();
+  ChildThreadImpl::current()->resource_dispatcher()->Cancel(request_id_);
 
   // Due to races and nested event loops, PluginURLFetcher may still receive
   // events from the bridge before being destroyed. Do not forward additional
@@ -181,9 +181,10 @@
     return;
 
   if (allow) {
-    bridge_->SetDefersLoading(false);
+    ChildThreadImpl::current()->resource_dispatcher()->SetDefersLoading(
+        request_id_, false);
   } else {
-    bridge_->Cancel();
+    ChildThreadImpl::current()->resource_dispatcher()->Cancel(request_id_);
     plugin_stream_->DidFail(resource_id_);  // That will delete |this|.
   }
 }
@@ -226,7 +227,8 @@
     }
   } else {
     // Pause the request while we ask the plugin what to do about the redirect.
-    bridge_->SetDefersLoading(true);
+    ChildThreadImpl::current()->resource_dispatcher()->SetDefersLoading(
+        request_id_, true);
     plugin_stream_->WillSendRequest(url_, redirect_info.status_code);
   }
 
diff --git a/content/child/npapi/plugin_url_fetcher.h b/content/child/npapi/plugin_url_fetcher.h
index ace77ff..e16a4ac 100644
--- a/content/child/npapi/plugin_url_fetcher.h
+++ b/content/child/npapi/plugin_url_fetcher.h
@@ -15,7 +15,6 @@
 namespace content {
 class MultipartResponseDelegate;
 class PluginStreamUrl;
-class ResourceLoaderBridge;
 
 // Fetches URLS for a plugin using ResourceDispatcher.
 class PluginURLFetcher : public RequestPeer {
@@ -83,11 +82,10 @@
   bool copy_stream_data_;
   int64 data_offset_;
   bool pending_failure_notification_;
+  int request_id_;
 
   scoped_ptr<MultipartResponseDelegate> multipart_delegate_;
 
-  scoped_ptr<ResourceLoaderBridge> bridge_;
-
   DISALLOW_COPY_AND_ASSIGN(PluginURLFetcher);
 };
 
diff --git a/content/child/resource_dispatcher.cc b/content/child/resource_dispatcher.cc
index 1a1c88a4..cf56a77c 100644
--- a/content/child/resource_dispatcher.cc
+++ b/content/child/resource_dispatcher.cc
@@ -18,7 +18,6 @@
 #include "base/strings/string_util.h"
 #include "content/child/request_extra_data.h"
 #include "content/child/request_info.h"
-#include "content/child/resource_loader_bridge.h"
 #include "content/child/site_isolation_policy.h"
 #include "content/child/sync_load_response.h"
 #include "content/child/threaded_data_provider.h"
@@ -65,227 +64,6 @@
 
 }  // namespace
 
-// ResourceLoaderBridge implementation ----------------------------------------
-
-class IPCResourceLoaderBridge : public ResourceLoaderBridge {
- public:
-  IPCResourceLoaderBridge(ResourceDispatcher* dispatcher,
-                          const RequestInfo& request_info);
-  ~IPCResourceLoaderBridge() override;
-
-  // ResourceLoaderBridge
-  void SetRequestBody(ResourceRequestBody* request_body) override;
-  bool Start(RequestPeer* peer) override;
-  void Cancel() override;
-  void SetDefersLoading(bool value) override;
-  void DidChangePriority(net::RequestPriority new_priority,
-                         int intra_priority_value) override;
-  bool AttachThreadedDataReceiver(
-      blink::WebThreadedDataReceiver* threaded_data_receiver) override;
-  void SyncLoad(SyncLoadResponse* response) override;
-
- private:
-  // The resource dispatcher for this loader.  The bridge doesn't own it, but
-  // it's guaranteed to outlive the bridge.
-  ResourceDispatcher* dispatcher_;
-
-  // The request to send, created on initialization for modification and
-  // appending data.
-  ResourceHostMsg_Request request_;
-
-  // ID for the request, valid once Start()ed, -1 if not valid yet.
-  int request_id_;
-
-  // The routing id used when sending IPC messages.
-  int routing_id_;
-
-  // The security origin of the frame that initiates this request.
-  GURL frame_origin_;
-
-  bool is_synchronous_request_;
-};
-
-IPCResourceLoaderBridge::IPCResourceLoaderBridge(
-    ResourceDispatcher* dispatcher,
-    const RequestInfo& request_info)
-    : dispatcher_(dispatcher),
-      request_id_(-1),
-      routing_id_(request_info.routing_id),
-      is_synchronous_request_(false) {
-  DCHECK(dispatcher_) << "no resource dispatcher";
-  request_.method = request_info.method;
-  request_.url = request_info.url;
-  request_.first_party_for_cookies = request_info.first_party_for_cookies;
-  request_.referrer = request_info.referrer.url;
-  request_.referrer_policy = request_info.referrer.policy;
-  request_.headers = request_info.headers;
-  request_.load_flags = request_info.load_flags;
-  request_.origin_pid = request_info.requestor_pid;
-  request_.resource_type = request_info.request_type;
-  request_.priority = request_info.priority;
-  request_.request_context = request_info.request_context;
-  request_.appcache_host_id = request_info.appcache_host_id;
-  request_.download_to_file = request_info.download_to_file;
-  request_.has_user_gesture = request_info.has_user_gesture;
-  request_.skip_service_worker = request_info.skip_service_worker;
-  request_.should_reset_appcache = request_info.should_reset_appcache;
-  request_.fetch_request_mode = request_info.fetch_request_mode;
-  request_.fetch_credentials_mode = request_info.fetch_credentials_mode;
-  request_.fetch_request_context_type = request_info.fetch_request_context_type;
-  request_.fetch_frame_type = request_info.fetch_frame_type;
-  request_.enable_load_timing = request_info.enable_load_timing;
-  request_.enable_upload_progress = request_info.enable_upload_progress;
-  request_.do_not_prompt_for_login = request_info.do_not_prompt_for_login;
-
-  if ((request_info.referrer.policy == blink::WebReferrerPolicyDefault ||
-       request_info.referrer.policy ==
-           blink::WebReferrerPolicyNoReferrerWhenDowngrade) &&
-      request_info.referrer.url.SchemeIsSecure() &&
-      !request_info.url.SchemeIsSecure()) {
-    LOG(FATAL) << "Trying to send secure referrer for insecure request "
-               << "without an appropriate referrer policy.\n"
-               << "URL = " << request_info.url << "\n"
-               << "Referrer = " << request_info.referrer.url;
-  }
-
-  const RequestExtraData kEmptyData;
-  const RequestExtraData* extra_data;
-  if (request_info.extra_data)
-    extra_data = static_cast<RequestExtraData*>(request_info.extra_data);
-  else
-    extra_data = &kEmptyData;
-  request_.visiblity_state = extra_data->visibility_state();
-  request_.render_frame_id = extra_data->render_frame_id();
-  request_.is_main_frame = extra_data->is_main_frame();
-  request_.parent_is_main_frame = extra_data->parent_is_main_frame();
-  request_.parent_render_frame_id = extra_data->parent_render_frame_id();
-  request_.allow_download = extra_data->allow_download();
-  request_.transition_type = extra_data->transition_type();
-  request_.should_replace_current_entry =
-      extra_data->should_replace_current_entry();
-  request_.transferred_request_child_id =
-      extra_data->transferred_request_child_id();
-  request_.transferred_request_request_id =
-      extra_data->transferred_request_request_id();
-  request_.service_worker_provider_id =
-      extra_data->service_worker_provider_id();
-  frame_origin_ = extra_data->frame_origin();
-}
-
-IPCResourceLoaderBridge::~IPCResourceLoaderBridge() {
-  // we remove our hook for the resource dispatcher only when going away, since
-  // it doesn't keep track of whether we've force terminated the request
-  if (request_id_ >= 0) {
-    // this operation may fail, as the dispatcher will have preemptively
-    // removed us when the renderer sends the ReceivedAllData message.
-    dispatcher_->RemovePendingRequest(request_id_);
-  }
-}
-
-void IPCResourceLoaderBridge::SetRequestBody(
-    ResourceRequestBody* request_body) {
-  DCHECK(request_id_ == -1) << "request already started";
-  request_.request_body = request_body;
-}
-
-// Writes a footer on the message and sends it
-bool IPCResourceLoaderBridge::Start(RequestPeer* peer) {
-  if (request_id_ != -1) {
-    NOTREACHED() << "Starting a request twice";
-    return false;
-  }
-
-  // generate the request ID, and append it to the message
-  request_id_ = dispatcher_->AddPendingRequest(peer,
-                                               request_.resource_type,
-                                               request_.origin_pid,
-                                               frame_origin_,
-                                               request_.url,
-                                               request_.download_to_file);
-
-  return dispatcher_->message_sender()->Send(
-      new ResourceHostMsg_RequestResource(routing_id_, request_id_, request_));
-}
-
-void IPCResourceLoaderBridge::Cancel() {
-  if (request_id_ < 0) {
-    NOTREACHED() << "Trying to cancel an unstarted request";
-    return;
-  }
-
-  if (!is_synchronous_request_) {
-    // This also removes the the request from the dispatcher.
-    dispatcher_->CancelPendingRequest(request_id_);
-  }
-}
-
-void IPCResourceLoaderBridge::SetDefersLoading(bool value) {
-  if (request_id_ < 0) {
-    NOTREACHED() << "Trying to (un)defer an unstarted request";
-    return;
-  }
-
-  dispatcher_->SetDefersLoading(request_id_, value);
-}
-
-void IPCResourceLoaderBridge::DidChangePriority(
-    net::RequestPriority new_priority,
-    int intra_priority_value) {
-  if (request_id_ < 0) {
-    NOTREACHED() << "Trying to change priority of an unstarted request";
-    return;
-  }
-
-  dispatcher_->DidChangePriority(
-      request_id_, new_priority, intra_priority_value);
-}
-
-bool IPCResourceLoaderBridge::AttachThreadedDataReceiver(
-    blink::WebThreadedDataReceiver* threaded_data_receiver) {
-  if (request_id_ < 0) {
-    NOTREACHED() << "Trying to attach threaded receiver on unstarted request";
-    return false;
-  }
-
-  return dispatcher_->AttachThreadedDataReceiver(request_id_,
-                                                 threaded_data_receiver);
-}
-
-void IPCResourceLoaderBridge::SyncLoad(SyncLoadResponse* response) {
-  if (request_id_ != -1) {
-    NOTREACHED() << "Starting a request twice";
-    response->error_code = net::ERR_FAILED;
-    return;
-  }
-
-  request_id_ = MakeRequestID();
-  is_synchronous_request_ = true;
-
-  SyncLoadResult result;
-  IPC::SyncMessage* msg = new ResourceHostMsg_SyncLoad(routing_id_, request_id_,
-                                                       request_, &result);
-  // NOTE: This may pump events (see RenderThread::Send).
-  if (!dispatcher_->message_sender()->Send(msg)) {
-    response->error_code = net::ERR_FAILED;
-    return;
-  }
-
-  response->error_code = result.error_code;
-  response->url = result.final_url;
-  response->headers = result.headers;
-  response->mime_type = result.mime_type;
-  response->charset = result.charset;
-  response->request_time = result.request_time;
-  response->response_time = result.response_time;
-  response->encoded_data_length = result.encoded_data_length;
-  response->load_timing = result.load_timing;
-  response->devtools_info = result.devtools_info;
-  response->data.swap(result.data);
-  response->download_file_path = result.download_file_path;
-}
-
-// ResourceDispatcher ---------------------------------------------------------
-
 ResourceDispatcher::ResourceDispatcher(
     IPC::Sender* sender,
     scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner)
@@ -299,8 +77,6 @@
 ResourceDispatcher::~ResourceDispatcher() {
 }
 
-// ResourceDispatcher implementation ------------------------------------------
-
 bool ResourceDispatcher::OnMessageReceived(const IPC::Message& message) {
   if (!IsResourceDispatcherMessage(message)) {
     return false;
@@ -445,8 +221,7 @@
     CHECK_GE(request_info->buffer_size, data_offset + data_length);
 
     // Ensure that the SHM buffer remains valid for the duration of this scope.
-    // It is possible for CancelPendingRequest() to be called before we exit
-    // this scope.
+    // It is possible for Cancel() to be called before we exit this scope.
     linked_ptr<base::SharedMemory> retain_buffer(request_info->buffer);
 
     base::TimeTicks time_start = base::TimeTicks::Now();
@@ -539,7 +314,7 @@
       FollowPendingRedirect(request_id, *request_info);
     }
   } else {
-    CancelPendingRequest(request_id);
+    Cancel(request_id);
   }
 }
 
@@ -615,23 +390,6 @@
                            request_complete_data.encoded_data_length);
 }
 
-int ResourceDispatcher::AddPendingRequest(RequestPeer* callback,
-                                          ResourceType resource_type,
-                                          int origin_pid,
-                                          const GURL& frame_origin,
-                                          const GURL& request_url,
-                                          bool download_to_file) {
-  // Compute a unique request_id for this renderer process.
-  int id = MakeRequestID();
-  pending_requests_[id] = PendingRequestInfo(callback,
-                                             resource_type,
-                                             origin_pid,
-                                             frame_origin,
-                                             request_url,
-                                             download_to_file);
-  return id;
-}
-
 bool ResourceDispatcher::RemovePendingRequest(int request_id) {
   PendingRequestList::iterator it = pending_requests_.find(request_id);
   if (it == pending_requests_.end())
@@ -652,7 +410,7 @@
   return true;
 }
 
-void ResourceDispatcher::CancelPendingRequest(int request_id) {
+void ResourceDispatcher::Cancel(int request_id) {
   PendingRequestList::iterator it = pending_requests_.find(request_id);
   if (it == pending_requests_.end()) {
     DVLOG(1) << "unknown request";
@@ -788,9 +546,57 @@
   }
 }
 
-ResourceLoaderBridge* ResourceDispatcher::CreateBridge(
-    const RequestInfo& request_info) {
-  return new IPCResourceLoaderBridge(this, request_info);
+void ResourceDispatcher::StartSync(const RequestInfo& request_info,
+                                   ResourceRequestBody* request_body,
+                                   SyncLoadResponse* response) {
+  scoped_ptr<ResourceHostMsg_Request> request =
+      CreateRequest(request_info, request_body, NULL);
+
+  SyncLoadResult result;
+  IPC::SyncMessage* msg = new ResourceHostMsg_SyncLoad(
+      request_info.routing_id, MakeRequestID(), *request, &result);
+
+  // NOTE: This may pump events (see RenderThread::Send).
+  if (!message_sender_->Send(msg)) {
+    response->error_code = net::ERR_FAILED;
+    return;
+  }
+
+  response->error_code = result.error_code;
+  response->url = result.final_url;
+  response->headers = result.headers;
+  response->mime_type = result.mime_type;
+  response->charset = result.charset;
+  response->request_time = result.request_time;
+  response->response_time = result.response_time;
+  response->encoded_data_length = result.encoded_data_length;
+  response->load_timing = result.load_timing;
+  response->devtools_info = result.devtools_info;
+  response->data.swap(result.data);
+  response->download_file_path = result.download_file_path;
+}
+
+int ResourceDispatcher::StartAsync(const RequestInfo& request_info,
+                                   ResourceRequestBody* request_body,
+                                   RequestPeer* peer) {
+  GURL frame_origin;
+  scoped_ptr<ResourceHostMsg_Request> request =
+      CreateRequest(request_info, request_body, &frame_origin);
+
+  // Compute a unique request_id for this renderer process.
+  int request_id = MakeRequestID();
+  pending_requests_[request_id] =
+      PendingRequestInfo(peer,
+                         request->resource_type,
+                         request->origin_pid,
+                         frame_origin,
+                         request->url,
+                         request_info.download_to_file);
+
+  message_sender_->Send(new ResourceHostMsg_RequestResource(
+      request_info.routing_id, request_id, *request));
+
+  return request_id;
 }
 
 void ResourceDispatcher::ToResourceResponseInfo(
@@ -928,4 +734,71 @@
   }
 }
 
+scoped_ptr<ResourceHostMsg_Request> ResourceDispatcher::CreateRequest(
+    const RequestInfo& request_info,
+    ResourceRequestBody* request_body,
+    GURL* frame_origin) {
+  scoped_ptr<ResourceHostMsg_Request> request(new ResourceHostMsg_Request);
+  request->method = request_info.method;
+  request->url = request_info.url;
+  request->first_party_for_cookies = request_info.first_party_for_cookies;
+  request->referrer = request_info.referrer.url;
+  request->referrer_policy = request_info.referrer.policy;
+  request->headers = request_info.headers;
+  request->load_flags = request_info.load_flags;
+  request->origin_pid = request_info.requestor_pid;
+  request->resource_type = request_info.request_type;
+  request->priority = request_info.priority;
+  request->request_context = request_info.request_context;
+  request->appcache_host_id = request_info.appcache_host_id;
+  request->download_to_file = request_info.download_to_file;
+  request->has_user_gesture = request_info.has_user_gesture;
+  request->skip_service_worker = request_info.skip_service_worker;
+  request->should_reset_appcache = request_info.should_reset_appcache;
+  request->fetch_request_mode = request_info.fetch_request_mode;
+  request->fetch_credentials_mode = request_info.fetch_credentials_mode;
+  request->fetch_request_context_type = request_info.fetch_request_context_type;
+  request->fetch_frame_type = request_info.fetch_frame_type;
+  request->enable_load_timing = request_info.enable_load_timing;
+  request->enable_upload_progress = request_info.enable_upload_progress;
+  request->do_not_prompt_for_login = request_info.do_not_prompt_for_login;
+
+  if ((request_info.referrer.policy == blink::WebReferrerPolicyDefault ||
+       request_info.referrer.policy ==
+           blink::WebReferrerPolicyNoReferrerWhenDowngrade) &&
+      request_info.referrer.url.SchemeIsSecure() &&
+      !request_info.url.SchemeIsSecure()) {
+    LOG(FATAL) << "Trying to send secure referrer for insecure request "
+               << "without an appropriate referrer policy.\n"
+               << "URL = " << request_info.url << "\n"
+               << "Referrer = " << request_info.referrer.url;
+  }
+
+  const RequestExtraData kEmptyData;
+  const RequestExtraData* extra_data;
+  if (request_info.extra_data)
+    extra_data = static_cast<RequestExtraData*>(request_info.extra_data);
+  else
+    extra_data = &kEmptyData;
+  request->visiblity_state = extra_data->visibility_state();
+  request->render_frame_id = extra_data->render_frame_id();
+  request->is_main_frame = extra_data->is_main_frame();
+  request->parent_is_main_frame = extra_data->parent_is_main_frame();
+  request->parent_render_frame_id = extra_data->parent_render_frame_id();
+  request->allow_download = extra_data->allow_download();
+  request->transition_type = extra_data->transition_type();
+  request->should_replace_current_entry =
+      extra_data->should_replace_current_entry();
+  request->transferred_request_child_id =
+      extra_data->transferred_request_child_id();
+  request->transferred_request_request_id =
+      extra_data->transferred_request_request_id();
+  request->service_worker_provider_id =
+      extra_data->service_worker_provider_id();
+  request->request_body = request_body;
+  if (frame_origin)
+    *frame_origin = extra_data->frame_origin();
+  return request.Pass();
+}
+
 }  // namespace content
diff --git a/content/child/resource_dispatcher.h b/content/child/resource_dispatcher.h
index ce21c3b..769eae2 100644
--- a/content/child/resource_dispatcher.h
+++ b/content/child/resource_dispatcher.h
@@ -12,6 +12,7 @@
 
 #include "base/containers/hash_tables.h"
 #include "base/memory/linked_ptr.h"
+#include "base/memory/scoped_ptr.h"
 #include "base/memory/shared_memory.h"
 #include "base/memory/weak_ptr.h"
 #include "base/single_thread_task_runner.h"
@@ -23,6 +24,7 @@
 #include "net/base/request_priority.h"
 #include "url/gurl.h"
 
+struct ResourceHostMsg_Request;
 struct ResourceMsg_RequestCompleteData;
 
 namespace blink {
@@ -36,16 +38,17 @@
 namespace content {
 class RequestPeer;
 class ResourceDispatcherDelegate;
-class ResourceLoaderBridge;
+class ResourceRequestBody;
 class ThreadedDataProvider;
 struct ResourceResponseInfo;
 struct RequestInfo;
 struct ResourceResponseHead;
 struct SiteIsolationResponseMetaData;
+struct SyncLoadResponse;
 
-// This class serves as a communication interface between the
-// ResourceDispatcherHost in the browser process and the ResourceLoaderBridge in
-// the child process.  It can be used from any child process.
+// This class serves as a communication interface to the ResourceDispatcherHost
+// in the browser process. It can be used from any child process.
+// Virtual methods are for tests.
 class CONTENT_EXPORT ResourceDispatcher : public IPC::Listener {
  public:
   ResourceDispatcher(
@@ -56,19 +59,23 @@
   // IPC::Listener implementation.
   bool OnMessageReceived(const IPC::Message& message) override;
 
-  // Creates a ResourceLoaderBridge for this type of dispatcher, this is so
-  // this can be tested regardless of the ResourceLoaderBridge::Create
-  // implementation.  Virtual for tests.
-  virtual ResourceLoaderBridge* CreateBridge(const RequestInfo& request_info);
+  // Call this method to load the resource synchronously (i.e., in one shot).
+  // This is an alternative to the StartAsync method. Be warned that this method
+  // will block the calling thread until the resource is fully downloaded or an
+  // error occurs. It could block the calling thread for a long time, so only
+  // use this if you really need it!  There is also no way for the caller to
+  // interrupt this method. Errors are reported via the status field of the
+  // response parameter.
+  void StartSync(const RequestInfo& request_info,
+                 ResourceRequestBody* request_body,
+                 SyncLoadResponse* response);
 
-  // Adds a request from the |pending_requests_| list, returning the new
-  // requests' ID.
-  int AddPendingRequest(RequestPeer* callback,
-                        ResourceType resource_type,
-                        int origin_pid,
-                        const GURL& frame_origin,
-                        const GURL& request_url,
-                        bool download_to_file);
+  // Call this method to initiate the request. If this method succeeds, then
+  // the peer's methods will be called asynchronously to report various events.
+  // Returns the request id.
+  virtual int StartAsync(const RequestInfo& request_info,
+                         ResourceRequestBody* request_body,
+                         RequestPeer* peer);
 
   // Removes a request from the |pending_requests_| list, returning true if the
   // request was found and removed.
@@ -76,7 +83,7 @@
 
   // Cancels a request in the |pending_requests_| list.  The request will be
   // removed from the dispatcher as well.
-  void CancelPendingRequest(int request_id);
+  virtual void Cancel(int request_id);
 
   // Toggles the is_deferred attribute for the specified request.
   void SetDefersLoading(int request_id, bool value);
@@ -105,8 +112,6 @@
     message_sender_ = sender;
   }
 
-  IPC::Sender* message_sender() const { return message_sender_; }
-
   // This does not take ownership of the delegate. It is expected that the
   // delegate have a longer lifetime than the ResourceDispatcher.
   void set_delegate(ResourceDispatcherDelegate* delegate) {
@@ -227,6 +232,11 @@
   // for use on deferred message queues that are no longer needed.
   static void ReleaseResourcesInMessageQueue(MessageQueue* queue);
 
+  scoped_ptr<ResourceHostMsg_Request> CreateRequest(
+      const RequestInfo& request_info,
+      ResourceRequestBody* request_body,
+      GURL* frame_origin);
+
   IPC::Sender* message_sender_;
 
   // All pending requests issued to the host
diff --git a/content/child/resource_dispatcher_unittest.cc b/content/child/resource_dispatcher_unittest.cc
index 46ce695d..e0938d1a 100644
--- a/content/child/resource_dispatcher_unittest.cc
+++ b/content/child/resource_dispatcher_unittest.cc
@@ -15,7 +15,6 @@
 #include "content/child/request_extra_data.h"
 #include "content/child/request_info.h"
 #include "content/child/resource_dispatcher.h"
-#include "content/child/resource_loader_bridge.h"
 #include "content/common/appcache_interfaces.h"
 #include "content/common/resource_messages.h"
 #include "content/common/service_worker/service_worker_types.h"
@@ -41,7 +40,7 @@
 // to the reference data.
 class TestRequestPeer : public RequestPeer {
  public:
-  TestRequestPeer(ResourceLoaderBridge* bridge)
+  TestRequestPeer(ResourceDispatcher* dispatcher)
       :  follow_redirects_(true),
          defer_on_redirect_(false),
          seen_redirects_(0),
@@ -50,16 +49,19 @@
          total_encoded_data_length_(0),
          total_downloaded_data_length_(0),
          complete_(false),
-         bridge_(bridge) {
+         dispatcher_(dispatcher),
+         request_id_(0) {
   }
 
+  void set_request_id(int request_id) { request_id_ = request_id; }
+
   void OnUploadProgress(uint64 position, uint64 size) override {}
 
   bool OnReceivedRedirect(const net::RedirectInfo& redirect_info,
                           const ResourceResponseInfo& info) override {
     ++seen_redirects_;
     if (defer_on_redirect_)
-      bridge_->SetDefersLoading(true);
+      dispatcher_->SetDefersLoading(request_id_, true);
     return follow_redirects_;
   }
 
@@ -67,7 +69,7 @@
     EXPECT_FALSE(received_response_);
     received_response_ = true;
     if (cancel_on_receive_response_)
-      bridge_->Cancel();
+      dispatcher_->Cancel(request_id_);
   }
 
   void OnDownloadedData(int len, int encoded_data_length) override {
@@ -144,7 +146,8 @@
 
   bool complete_;
 
-  ResourceLoaderBridge* bridge_;
+  ResourceDispatcher* dispatcher_;
+  int request_id_;
 
   DISALLOW_COPY_AND_ASSIGN(TestRequestPeer);
 };
@@ -306,37 +309,28 @@
         ResourceMsg_RequestComplete(request_id, request_complete_data)));
   }
 
-  ResourceLoaderBridge* CreateBridge() {
-    return CreateBridgeInternal(false);
-  }
+  RequestInfo* CreateRequestInfo(bool download_to_file) {
+    RequestInfo* request_info = new RequestInfo();
+    request_info->method = "GET";
+    request_info->url = GURL(kTestPageUrl);
+    request_info->first_party_for_cookies = GURL(kTestPageUrl);
+    request_info->referrer = Referrer();
+    request_info->headers = std::string();
+    request_info->load_flags = 0;
+    request_info->requestor_pid = 0;
+    request_info->request_type = RESOURCE_TYPE_SUB_RESOURCE;
+    request_info->appcache_host_id = kAppCacheNoHostId;
+    request_info->should_reset_appcache = false;
+    request_info->routing_id = 0;
+    request_info->download_to_file = download_to_file;
+    RequestExtraData extra_data;
 
-  ResourceLoaderBridge* CreateBridgeForDownloadToFile() {
-    return CreateBridgeInternal(true);
+    return request_info;
   }
 
   ResourceDispatcher* dispatcher() { return &dispatcher_; }
 
  private:
-  ResourceLoaderBridge* CreateBridgeInternal(bool download_to_file) {
-    RequestInfo request_info;
-    request_info.method = "GET";
-    request_info.url = GURL(kTestPageUrl);
-    request_info.first_party_for_cookies = GURL(kTestPageUrl);
-    request_info.referrer = Referrer();
-    request_info.headers = std::string();
-    request_info.load_flags = 0;
-    request_info.requestor_pid = 0;
-    request_info.request_type = RESOURCE_TYPE_SUB_RESOURCE;
-    request_info.appcache_host_id = kAppCacheNoHostId;
-    request_info.should_reset_appcache = false;
-    request_info.routing_id = 0;
-    request_info.download_to_file = download_to_file;
-    RequestExtraData extra_data;
-    request_info.extra_data = &extra_data;
-
-    return dispatcher_.CreateBridge(request_info);
-  }
-
   // Map of request IDs to shared memory.
   std::map<int, base::SharedMemory*> shared_memory_map_;
 
@@ -352,10 +346,11 @@
   const size_t kFirstReceiveSize = 2;
   ASSERT_LT(kFirstReceiveSize, strlen(kTestPageContents));
 
-  scoped_ptr<ResourceLoaderBridge> bridge(CreateBridge());
-  TestRequestPeer peer(bridge.get());
+  scoped_ptr<RequestInfo> request_info(CreateRequestInfo(false));
+  TestRequestPeer peer(dispatcher());
+  int request_id = dispatcher()->StartAsync(*request_info.get(), NULL, &peer);
+  peer.set_request_id(request_id);
 
-  EXPECT_TRUE(bridge->Start(&peer));
   int id = ConsumeRequestResource();
   EXPECT_EQ(0u, queued_messages());
 
@@ -383,14 +378,18 @@
 TEST_F(ResourceDispatcherTest, MultipleRequests) {
   const char kTestPageContents2[] = "Not kTestPageContents";
 
-  scoped_ptr<ResourceLoaderBridge> bridge1(CreateBridge());
-  TestRequestPeer peer1(bridge1.get());
-  scoped_ptr<ResourceLoaderBridge> bridge2(CreateBridge());
-  TestRequestPeer peer2(bridge2.get());
+  scoped_ptr<RequestInfo> request_info1(CreateRequestInfo(false));
+  TestRequestPeer peer1(dispatcher());
+  int request_id1 = dispatcher()->StartAsync(
+      *request_info1.get(), NULL, &peer1);
+  peer1.set_request_id(request_id1);
+  scoped_ptr<RequestInfo> request_info2(CreateRequestInfo(false));
+  TestRequestPeer peer2(dispatcher());
+  int request_id2 = dispatcher()->StartAsync(
+      *request_info1.get(), NULL, &peer2);
+  peer2.set_request_id(request_id2);
 
-  EXPECT_TRUE(bridge1->Start(&peer1));
   int id1 = ConsumeRequestResource();
-  EXPECT_TRUE(bridge2->Start(&peer2));
   int id2 = ConsumeRequestResource();
   EXPECT_EQ(0u, queued_messages());
 
@@ -423,15 +422,16 @@
 
 // Tests that the cancel method prevents other messages from being received.
 TEST_F(ResourceDispatcherTest, Cancel) {
-  scoped_ptr<ResourceLoaderBridge> bridge(CreateBridge());
-  TestRequestPeer peer(bridge.get());
+  scoped_ptr<RequestInfo> request_info(CreateRequestInfo(false));
+  TestRequestPeer peer(dispatcher());
+  int request_id = dispatcher()->StartAsync(*request_info.get(), NULL, &peer);
+  peer.set_request_id(request_id);
 
-  EXPECT_TRUE(bridge->Start(&peer));
   int id = ConsumeRequestResource();
   EXPECT_EQ(0u, queued_messages());
 
   // Cancel the request.
-  bridge->Cancel();
+  dispatcher()->Cancel(request_id);
   ConsumeCancelRequest(id);
 
   // Any future messages related to the request should be ignored.
@@ -448,11 +448,12 @@
 
 // Tests that calling cancel during a callback works as expected.
 TEST_F(ResourceDispatcherTest, CancelDuringCallback) {
-  scoped_ptr<ResourceLoaderBridge> bridge(CreateBridge());
-  TestRequestPeer peer(bridge.get());
+  scoped_ptr<RequestInfo> request_info(CreateRequestInfo(false));
+  TestRequestPeer peer(dispatcher());
+  int request_id = dispatcher()->StartAsync(*request_info.get(), NULL, &peer);
+  peer.set_request_id(request_id);
   peer.set_cancel_on_receive_response(true);
 
-  EXPECT_TRUE(bridge->Start(&peer));
   int id = ConsumeRequestResource();
   EXPECT_EQ(0u, queued_messages());
 
@@ -473,10 +474,11 @@
 
 // Checks that redirects work as expected.
 TEST_F(ResourceDispatcherTest, Redirect) {
-  scoped_ptr<ResourceLoaderBridge> bridge(CreateBridge());
-  TestRequestPeer peer(bridge.get());
+  scoped_ptr<RequestInfo> request_info(CreateRequestInfo(false));
+  TestRequestPeer peer(dispatcher());
+  int request_id = dispatcher()->StartAsync(*request_info.get(), NULL, &peer);
+  peer.set_request_id(request_id);
 
-  EXPECT_TRUE(bridge->Start(&peer));
   int id = ConsumeRequestResource();
 
   NotifyReceivedRedirect(id);
@@ -504,11 +506,12 @@
 // Tests that that cancelling during a redirect method prevents other messages
 // from being received.
 TEST_F(ResourceDispatcherTest, CancelDuringRedirect) {
-  scoped_ptr<ResourceLoaderBridge> bridge(CreateBridge());
-  TestRequestPeer peer(bridge.get());
+  scoped_ptr<RequestInfo> request_info(CreateRequestInfo(false));
+  TestRequestPeer peer(dispatcher());
+  int request_id = dispatcher()->StartAsync(*request_info.get(), NULL, &peer);
+  peer.set_request_id(request_id);
   peer.set_follow_redirects(false);
 
-  EXPECT_TRUE(bridge->Start(&peer));
   int id = ConsumeRequestResource();
   EXPECT_EQ(0u, queued_messages());
 
@@ -534,14 +537,15 @@
 
 // Checks that deferring a request delays messages until it's resumed.
 TEST_F(ResourceDispatcherTest, Defer) {
-  scoped_ptr<ResourceLoaderBridge> bridge(CreateBridge());
-  TestRequestPeer peer(bridge.get());
+  scoped_ptr<RequestInfo> request_info(CreateRequestInfo(false));
+  TestRequestPeer peer(dispatcher());
+  int request_id = dispatcher()->StartAsync(*request_info.get(), NULL, &peer);
+  peer.set_request_id(request_id);
 
-  EXPECT_TRUE(bridge->Start(&peer));
   int id = ConsumeRequestResource();
   EXPECT_EQ(0u, queued_messages());
 
-  bridge->SetDefersLoading(true);
+  dispatcher()->SetDefersLoading(request_id, true);
   NotifyReceivedResponse(id);
   NotifySetDataBuffer(id, strlen(kTestPageContents));
   NotifyDataReceived(id, kTestPageContents);
@@ -555,7 +559,7 @@
   EXPECT_EQ(0, peer.seen_redirects());
 
   // Resuming the request should asynchronously unleash the deferred messages.
-  bridge->SetDefersLoading(false);
+  dispatcher()->SetDefersLoading(request_id, false);
   base::RunLoop().RunUntilIdle();
 
   ConsumeDataReceived_ACK(id);
@@ -568,11 +572,12 @@
 // Checks that deferring a request during a redirect delays messages until it's
 // resumed.
 TEST_F(ResourceDispatcherTest, DeferOnRedirect) {
-  scoped_ptr<ResourceLoaderBridge> bridge(CreateBridge());
-  TestRequestPeer peer(bridge.get());
+  scoped_ptr<RequestInfo> request_info(CreateRequestInfo(false));
+  TestRequestPeer peer(dispatcher());
+  int request_id = dispatcher()->StartAsync(*request_info.get(), NULL, &peer);
+  peer.set_request_id(request_id);
   peer.set_defer_on_redirect(true);
 
-  EXPECT_TRUE(bridge->Start(&peer));
   int id = ConsumeRequestResource();
   EXPECT_EQ(0u, queued_messages());
 
@@ -592,7 +597,7 @@
   EXPECT_EQ(1, peer.seen_redirects());
 
   // Resuming the request should asynchronously unleash the deferred messages.
-  bridge->SetDefersLoading(false);
+  dispatcher()->SetDefersLoading(request_id, false);
   base::RunLoop().RunUntilIdle();
 
   ConsumeFollowRedirect(id);
@@ -607,16 +612,17 @@
 
 // Checks that a deferred request that's cancelled doesn't receive any messages.
 TEST_F(ResourceDispatcherTest, CancelDeferredRequest) {
-  scoped_ptr<ResourceLoaderBridge> bridge(CreateBridge());
-  TestRequestPeer peer(bridge.get());
+  scoped_ptr<RequestInfo> request_info(CreateRequestInfo(false));
+  TestRequestPeer peer(dispatcher());
+  int request_id = dispatcher()->StartAsync(*request_info.get(), NULL, &peer);
+  peer.set_request_id(request_id);
 
-  EXPECT_TRUE(bridge->Start(&peer));
   int id = ConsumeRequestResource();
   EXPECT_EQ(0u, queued_messages());
 
-  bridge->SetDefersLoading(true);
+  dispatcher()->SetDefersLoading(request_id, true);
   NotifyReceivedRedirect(id);
-  bridge->Cancel();
+  dispatcher()->Cancel(request_id);
   ConsumeCancelRequest(id);
 
   NotifyRequestComplete(id, 0);
@@ -630,12 +636,13 @@
 }
 
 TEST_F(ResourceDispatcherTest, DownloadToFile) {
-  scoped_ptr<ResourceLoaderBridge> bridge(CreateBridgeForDownloadToFile());
-  TestRequestPeer peer(bridge.get());
+  scoped_ptr<RequestInfo> request_info(CreateRequestInfo(true));
+  TestRequestPeer peer(dispatcher());
+  int request_id = dispatcher()->StartAsync(*request_info.get(), NULL, &peer);
+  peer.set_request_id(request_id);
   const int kDownloadedIncrement = 100;
   const int kEncodedIncrement = 50;
 
-  EXPECT_TRUE(bridge->Start(&peer));
   int id = ConsumeRequestResource();
   EXPECT_EQ(0u, queued_messages());
 
@@ -660,7 +667,7 @@
   EXPECT_TRUE(peer.complete());
   EXPECT_EQ(0u, queued_messages());
 
-  bridge.reset();
+  dispatcher()->RemovePendingRequest(request_id);
   ConsumeReleaseDownloadedFile(id);
   EXPECT_EQ(0u, queued_messages());
   EXPECT_EQ(expected_total_downloaded_length,
@@ -670,10 +677,11 @@
 
 // Make sure that when a download to file is cancelled, the file is destroyed.
 TEST_F(ResourceDispatcherTest, CancelDownloadToFile) {
-  scoped_ptr<ResourceLoaderBridge> bridge(CreateBridgeForDownloadToFile());
-  TestRequestPeer peer(bridge.get());
+  scoped_ptr<RequestInfo> request_info(CreateRequestInfo(true));
+  TestRequestPeer peer(dispatcher());
+  int request_id = dispatcher()->StartAsync(*request_info.get(), NULL, &peer);
+  peer.set_request_id(request_id);
 
-  EXPECT_TRUE(bridge->Start(&peer));
   int id = ConsumeRequestResource();
   EXPECT_EQ(0u, queued_messages());
 
@@ -682,13 +690,9 @@
   EXPECT_TRUE(peer.received_response());
 
   // Cancelling the request deletes the file.
-  bridge->Cancel();
+  dispatcher()->Cancel(request_id);
   ConsumeCancelRequest(id);
   ConsumeReleaseDownloadedFile(id);
-
-  // Deleting the bridge shouldn't send another message to delete the file.
-  bridge.reset();
-  EXPECT_EQ(0u, queued_messages());
 }
 
 TEST_F(ResourceDispatcherTest, Cookies) {
@@ -708,8 +712,9 @@
   }
 
   void PerformTest(const ResourceResponseHead& response_head) {
-    scoped_ptr<ResourceLoaderBridge> bridge(CreateBridge());
-    bridge->Start(this);
+    scoped_ptr<RequestInfo> request_info(CreateRequestInfo(false));
+    TestRequestPeer peer(dispatcher());
+    dispatcher()->StartAsync(*request_info.get(), NULL, &peer);
 
     dispatcher()->OnMessageReceived(
         ResourceMsg_ReceivedResponse(0, response_head));
diff --git a/content/child/resource_loader_bridge.cc b/content/child/resource_loader_bridge.cc
deleted file mode 100644
index dd68ccbb..0000000
--- a/content/child/resource_loader_bridge.cc
+++ /dev/null
@@ -1,13 +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 "content/child/resource_loader_bridge.h"
-
-namespace content {
-
-ResourceLoaderBridge::ResourceLoaderBridge() {}
-
-ResourceLoaderBridge::~ResourceLoaderBridge() {}
-
-}  // namespace content
diff --git a/content/child/resource_loader_bridge.h b/content/child/resource_loader_bridge.h
deleted file mode 100644
index 759f832..0000000
--- a/content/child/resource_loader_bridge.h
+++ /dev/null
@@ -1,92 +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.
-//
-// The intent of this file is to provide a type-neutral abstraction between
-// Chrome and WebKit for resource loading. This pure-virtual interface is
-// implemented by the embedder.
-//
-// One of these objects will be created by WebKit for each request. WebKit
-// will own the pointer to the bridge, and will delete it when the request is
-// no longer needed.
-//
-// In turn, the bridge's owner on the WebKit end will implement the
-// RequestPeer interface, which we will use to communicate notifications
-// back.
-
-#ifndef CONTENT_CHILD_RESOURCE_LOADER_BRIDGE_H_
-#define CONTENT_CHILD_RESOURCE_LOADER_BRIDGE_H_
-
-#include "base/macros.h"
-#include "content/common/content_export.h"
-#include "net/base/request_priority.h"
-
-namespace blink {
-class WebThreadedDataReceiver;
-}
-
-namespace content {
-
-class RequestPeer;
-class ResourceRequestBody;
-struct SyncLoadResponse;
-
-// TODO(tfarina): Refactor code that uses this class. This shouldn't be needed
-// now that it lives in content/.
-class CONTENT_EXPORT ResourceLoaderBridge {
- public:
-  // use BlinkPlatformImpl::CreateResourceLoader() for construction, but
-  // anybody can delete at any time, INCLUDING during processing of callbacks.
-  virtual ~ResourceLoaderBridge();
-
-  // Call this method before calling Start() to set the request body.
-  // May only be used with HTTP(S) POST requests.
-  virtual void SetRequestBody(ResourceRequestBody* request_body) = 0;
-
-  // Call this method to initiate the request.  If this method succeeds, then
-  // the peer's methods will be called asynchronously to report various events.
-  virtual bool Start(RequestPeer* peer) = 0;
-
-  // Call this method to cancel a request that is in progress.  This method
-  // causes the request to immediately transition into the 'done' state. The
-  // OnCompletedRequest method will be called asynchronously; this assumes
-  // the peer is still valid.
-  virtual void Cancel() = 0;
-
-  // Call this method to suspend or resume a load that is in progress.  This
-  // method may only be called after a successful call to the Start method.
-  virtual void SetDefersLoading(bool value) = 0;
-
-  // Call this method when the priority of the requested resource changes after
-  // Start() has been called.  This method may only be called after a successful
-  // call to the Start method.
-  virtual void DidChangePriority(net::RequestPriority new_priority,
-                                 int intra_priority_value) = 0;
-
-  // Call this method to attach a data receiver which will receive resource data
-  // on its own thread.
-  virtual bool AttachThreadedDataReceiver(
-      blink::WebThreadedDataReceiver* threaded_data_receiver) = 0;
-
-  // Call this method to load the resource synchronously (i.e., in one shot).
-  // This is an alternative to the Start method.  Be warned that this method
-  // will block the calling thread until the resource is fully downloaded or an
-  // error occurs.  It could block the calling thread for a long time, so only
-  // use this if you really need it!  There is also no way for the caller to
-  // interrupt this method.  Errors are reported via the status field of the
-  // response parameter.
-  virtual void SyncLoad(SyncLoadResponse* response) = 0;
-
- protected:
-  // Construction must go through BlinkPlatformImpl::CreateResourceLoader().
-  // For HTTP(S) POST requests, the AppendDataToUpload and AppendFileToUpload
-  // methods may be called to construct the body of the request.
-  ResourceLoaderBridge();
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(ResourceLoaderBridge);
-};
-
-}  // namespace content
-
-#endif  // CONTENT_CHILD_RESOURCE_LOADER_BRIDGE_H_
diff --git a/content/child/runtime_features.cc b/content/child/runtime_features.cc
index 21bebcb0..b8c7d37 100644
--- a/content/child/runtime_features.cc
+++ b/content/child/runtime_features.cc
@@ -191,6 +191,9 @@
   if (command_line.HasSwitch(switches::kEnablePushMessagePayload))
     WebRuntimeFeatures::enablePushMessagingData(true);
 
+  if (command_line.HasSwitch(switches::kEnablePushMessagingHasPermission))
+    WebRuntimeFeatures::enablePushMessagingHasPermission(true);
+
   if (command_line.HasSwitch(switches::kDisableV8IdleTasks))
     WebRuntimeFeatures::enableV8IdleTasks(false);
   else
diff --git a/content/child/web_data_consumer_handle_impl.cc b/content/child/web_data_consumer_handle_impl.cc
index a310238..a26656ed 100644
--- a/content/child/web_data_consumer_handle_impl.cc
+++ b/content/child/web_data_consumer_handle_impl.cc
@@ -7,7 +7,6 @@
 #include <limits>
 #include "base/bind.h"
 #include "base/logging.h"
-#include "base/profiler/scoped_tracker.h"
 #include "third_party/mojo/src/mojo/public/c/system/types.h"
 
 namespace content {
@@ -110,11 +109,6 @@
 }
 
 void WebDataConsumerHandleImpl::OnHandleGotReadable(MojoResult) {
-  // TODO(pkasting): Remove ScopedTracker below once crbug.com/455434 is
-  // fixed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "455434 WebDataConsumerHandleImpl::OnHandleGotReadable"));
   DCHECK(client_);
   client_->didGetReadable();
 }
diff --git a/content/child/web_url_loader_impl.cc b/content/child/web_url_loader_impl.cc
index adca0daa..db7cac72 100644
--- a/content/child/web_url_loader_impl.cc
+++ b/content/child/web_url_loader_impl.cc
@@ -2,8 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// An implementation of WebURLLoader in terms of ResourceLoaderBridge.
-
 #include "content/child/web_url_loader_impl.h"
 
 #include <algorithm>
@@ -14,20 +12,20 @@
 #include "base/command_line.h"
 #include "base/files/file_path.h"
 #include "base/memory/scoped_ptr.h"
-#include "base/profiler/scoped_tracker.h"
 #include "base/single_thread_task_runner.h"
 #include "base/strings/string_util.h"
 #include "base/time/time.h"
+#include "content/child/child_thread_impl.h"
 #include "content/child/ftp_directory_listing_response_delegate.h"
 #include "content/child/multipart_response_delegate.h"
 #include "content/child/request_extra_data.h"
 #include "content/child/request_info.h"
 #include "content/child/resource_dispatcher.h"
-#include "content/child/resource_loader_bridge.h"
 #include "content/child/sync_load_response.h"
 #include "content/child/web_data_consumer_handle_impl.h"
 #include "content/child/web_url_request_util.h"
 #include "content/child/weburlresponse_extradata_impl.h"
+#include "content/common/resource_messages.h"
 #include "content/common/resource_request_body.h"
 #include "content/common/service_worker/service_worker_types.h"
 #include "content/public/child/request_peer.h"
@@ -331,7 +329,7 @@
 
  private:
   friend class base::RefCounted<Context>;
-  ~Context() override {}
+  ~Context() override;
 
   // We can optimize the handling of data URLs in most cases.
   bool CanHandleDataURLRequestLocally() const;
@@ -345,10 +343,8 @@
   ResourceDispatcher* resource_dispatcher_;
   scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
   WebReferrerPolicy referrer_policy_;
-  scoped_ptr<ResourceLoaderBridge> bridge_;
   scoped_ptr<FtpDirectoryListingResponseDelegate> ftp_listing_delegate_;
   scoped_ptr<MultipartResponseDelegate> multipart_delegate_;
-  scoped_ptr<ResourceLoaderBridge> completed_bridge_;
   scoped_ptr<StreamOverrideParameters> stream_override_;
   mojo::ScopedDataPipeProducerHandle body_stream_writer_;
   mojo::common::HandleWatcher body_stream_writer_watcher_;
@@ -358,6 +354,7 @@
   bool got_all_stream_body_data_;
   enum DeferState {NOT_DEFERRING, SHOULD_DEFER, DEFERRED_DATA};
   DeferState defers_loading_;
+  int request_id_;
 };
 
 WebURLLoaderImpl::Context::Context(
@@ -370,13 +367,15 @@
       task_runner_(task_runner),
       referrer_policy_(blink::WebReferrerPolicyDefault),
       got_all_stream_body_data_(false),
-      defers_loading_(NOT_DEFERRING)  {
+      defers_loading_(NOT_DEFERRING),
+      request_id_(-1) {
 }
 
 void WebURLLoaderImpl::Context::Cancel() {
-  if (bridge_) {
-    bridge_->Cancel();
-    bridge_.reset();
+  if (resource_dispatcher_ && // NULL in unittest.
+      request_id_ != -1) {
+    resource_dispatcher_->Cancel(request_id_);
+    request_id_ = -1;
   }
 
   // Ensure that we do not notify the multipart delegate anymore as it has
@@ -393,8 +392,8 @@
 }
 
 void WebURLLoaderImpl::Context::SetDefersLoading(bool value) {
-  if (bridge_)
-    bridge_->SetDefersLoading(value);
+  if (request_id_ != -1)
+    resource_dispatcher_->SetDefersLoading(request_id_, value);
   if (value && defers_loading_ == NOT_DEFERRING) {
     defers_loading_ = SHOULD_DEFER;
   } else if (!value && defers_loading_ != NOT_DEFERRING) {
@@ -408,23 +407,27 @@
 
 void WebURLLoaderImpl::Context::DidChangePriority(
     WebURLRequest::Priority new_priority, int intra_priority_value) {
-  if (bridge_)
-    bridge_->DidChangePriority(
-        ConvertWebKitPriorityToNetPriority(new_priority), intra_priority_value);
+  if (request_id_ != -1) {
+    resource_dispatcher_->DidChangePriority(
+        request_id_,
+        ConvertWebKitPriorityToNetPriority(new_priority),
+        intra_priority_value);
+  }
 }
 
 bool WebURLLoaderImpl::Context::AttachThreadedDataReceiver(
     blink::WebThreadedDataReceiver* threaded_data_receiver) {
-  if (bridge_)
-    return bridge_->AttachThreadedDataReceiver(threaded_data_receiver);
+  if (request_id_ != -1) {
+    resource_dispatcher_->AttachThreadedDataReceiver(
+        request_id_, threaded_data_receiver);
+  }
 
   return false;
 }
 
 void WebURLLoaderImpl::Context::Start(const WebURLRequest& request,
                                       SyncLoadResponse* sync_load_response) {
-  DCHECK(!bridge_.get());
-
+  DCHECK(request_id_ == -1);
   request_ = request;  // Save the request.
   if (request.extraData()) {
     RequestExtraData* extra_data =
@@ -509,19 +512,18 @@
   request_info.fetch_request_context_type = GetRequestContextType(request);
   request_info.fetch_frame_type = GetRequestContextFrameType(request);
   request_info.extra_data = request.extraData();
-  bridge_.reset(resource_dispatcher_->CreateBridge(request_info));
-  bridge_->SetRequestBody(GetRequestBodyForWebURLRequest(request).get());
+
+  scoped_refptr<ResourceRequestBody> request_body =
+      GetRequestBodyForWebURLRequest(request).get();
 
   if (sync_load_response) {
-    bridge_->SyncLoad(sync_load_response);
+    resource_dispatcher_->StartSync(
+        request_info, request_body.get(), sync_load_response);
     return;
   }
 
-  // TODO(mmenke):  This case probably never happens, anyways.  Probably should
-  // not handle this case at all.  If it's worth handling, this code currently
-  // results in the request just hanging, which should be fixed.
-  if (!bridge_->Start(this))
-    bridge_.reset();
+  request_id_ = resource_dispatcher_->StartAsync(
+      request_info, request_body.get(), this);
 }
 
 void WebURLLoaderImpl::Context::OnUploadProgress(uint64 position, uint64 size) {
@@ -739,11 +741,6 @@
     multipart_delegate_.reset(NULL);
   }
 
-  // Prevent any further IPC to the browser now that we're complete, but
-  // don't delete it to keep any downloaded temp files alive.
-  DCHECK(!completed_bridge_.get());
-  completed_bridge_.swap(bridge_);
-
   if (client_) {
     if (error_code != net::OK) {
       client_->didFail(loader_, CreateError(request_.url(),
@@ -768,6 +765,12 @@
   }
 }
 
+WebURLLoaderImpl::Context::~Context() {
+  if (request_id_ >= 0) {
+    resource_dispatcher_->RemovePendingRequest(request_id_);
+  }
+}
+
 bool WebURLLoaderImpl::Context::CanHandleDataURLRequestLocally() const {
   GURL url = request_.url();
   if (!url.SchemeIs(url::kDataScheme))
@@ -783,8 +786,7 @@
   // download.
   //
   // NOTE: We special case MIME types we can render both for performance
-  // reasons as well as to support unit tests, which do not have an underlying
-  // ResourceLoaderBridge implementation.
+  // reasons as well as to support unit tests.
 
 #if defined(OS_ANDROID)
   // For compatibility reasons on Android we need to expose top-level data://
@@ -897,11 +899,6 @@
 }
 
 void WebURLLoaderImpl::Context::OnHandleGotWritable(MojoResult result) {
-  // TODO(pkasting): Remove ScopedTracker below once crbug.com/455434 is
-  // fixed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "455434 WebURLLoaderImpl::Context::OnHandleGotWritable"));
   if (result != MOJO_RESULT_OK) {
     if (client_) {
       client_->didFail(loader_,
@@ -973,7 +970,7 @@
                                            const ResourceResponseInfo& info,
                                            WebURLResponse* response) {
   response->setURL(url);
-  response->setResponseTime(info.response_time.ToDoubleT());
+  response->setResponseTime(info.response_time.ToInternalValue());
   response->setMIMEType(WebString::fromUTF8(info.mime_type));
   response->setTextEncodingName(WebString::fromUTF8(info.charset));
   response->setExpectedContentLength(info.content_length);
diff --git a/content/child/web_url_loader_impl_unittest.cc b/content/child/web_url_loader_impl_unittest.cc
index ab4c645..f132169 100644
--- a/content/child/web_url_loader_impl_unittest.cc
+++ b/content/child/web_url_loader_impl_unittest.cc
@@ -15,7 +15,6 @@
 #include "content/child/request_extra_data.h"
 #include "content/child/request_info.h"
 #include "content/child/resource_dispatcher.h"
-#include "content/child/resource_loader_bridge.h"
 #include "content/public/child/request_peer.h"
 #include "content/public/common/content_switches.h"
 #include "content/public/common/resource_response_info.h"
@@ -60,44 +59,32 @@
     "Content-type: text/html\n\n"
     "ah!";
 
-class TestBridge : public ResourceLoaderBridge,
-                   public base::SupportsWeakPtr<TestBridge> {
+class TestResourceDispatcher : public ResourceDispatcher {
  public:
-  TestBridge(const RequestInfo& info) :
-    peer_(NULL),
-    canceled_(false),
-    url_(info.url) {
+  TestResourceDispatcher() :
+      ResourceDispatcher(nullptr, nullptr),
+      peer_(NULL),
+      canceled_(false) {
   }
 
-  ~TestBridge() override {}
+  ~TestResourceDispatcher() override {}
 
-  // ResourceLoaderBridge implementation:
-  void SetRequestBody(ResourceRequestBody* request_body) override {}
+  // TestDispatcher implementation:
 
-  bool Start(RequestPeer* peer) override {
+  int StartAsync(const RequestInfo& request_info,
+                 ResourceRequestBody* request_body,
+                 RequestPeer* peer) override {
     EXPECT_FALSE(peer_);
     peer_ = peer;
-    return true;
+    url_ = request_info.url;
+    return 1;
   }
 
-  void Cancel() override {
+  void Cancel(int request_id) override {
     EXPECT_FALSE(canceled_);
     canceled_ = true;
   }
 
-  void SetDefersLoading(bool value) override {}
-
-  void DidChangePriority(net::RequestPriority new_priority,
-                         int intra_priority_value) override {}
-
-  bool AttachThreadedDataReceiver(
-      blink::WebThreadedDataReceiver* threaded_data_receiver) override {
-    NOTREACHED();
-    return false;
-  }
-
-  void SyncLoad(SyncLoadResponse* response) override {}
-
   RequestPeer* peer() { return peer_; }
 
   bool canceled() { return canceled_; }
@@ -109,27 +96,6 @@
   bool canceled_;
   GURL url_;
 
-  DISALLOW_COPY_AND_ASSIGN(TestBridge);
-};
-
-class TestResourceDispatcher : public ResourceDispatcher {
- public:
-  TestResourceDispatcher() : ResourceDispatcher(nullptr, nullptr) {}
-  ~TestResourceDispatcher() override {}
-
-  // ResourceDispatcher implementation:
-  ResourceLoaderBridge* CreateBridge(const RequestInfo& request_info) override {
-    EXPECT_FALSE(bridge_.get());
-    TestBridge* bridge = new TestBridge(request_info);
-    bridge_ = bridge->AsWeakPtr();
-    return bridge;
-  }
-
-  TestBridge* bridge() { return bridge_.get(); }
-
- private:
-  base::WeakPtr<TestBridge> bridge_;
-
   DISALLOW_COPY_AND_ASSIGN(TestResourceDispatcher);
 };
 
@@ -295,7 +261,6 @@
     request.initialize();
     request.setURL(GURL(kTestURL));
     client()->loader()->loadAsynchronously(request, client());
-    ASSERT_TRUE(bridge());
     ASSERT_TRUE(peer());
   }
 
@@ -377,8 +342,8 @@
   }
 
   TestWebURLLoaderClient* client() { return &client_; }
-  TestBridge* bridge() { return dispatcher_.bridge(); }
-  RequestPeer* peer() { return bridge()->peer(); }
+  TestResourceDispatcher* dispatcher() { return &dispatcher_; }
+  RequestPeer* peer() { return dispatcher()->peer(); }
   base::MessageLoop* message_loop() { return &message_loop_; }
 
  private:
@@ -392,7 +357,7 @@
   DoReceiveResponse();
   DoReceiveData();
   DoCompleteRequest();
-  EXPECT_FALSE(bridge()->canceled());
+  EXPECT_FALSE(dispatcher()->canceled());
   EXPECT_EQ(kTestData, client()->received_data());
 }
 
@@ -402,7 +367,7 @@
   DoReceiveResponse();
   DoReceiveData();
   DoCompleteRequest();
-  EXPECT_FALSE(bridge()->canceled());
+  EXPECT_FALSE(dispatcher()->canceled());
   EXPECT_EQ(kTestData, client()->received_data());
 }
 
@@ -411,7 +376,7 @@
   DoReceiveResponse();
   DoReceiveData();
   DoFailRequest();
-  EXPECT_FALSE(bridge()->canceled());
+  EXPECT_FALSE(dispatcher()->canceled());
 }
 
 // The client may delete the WebURLLoader during any callback from the loader.
@@ -420,14 +385,12 @@
   client()->set_delete_on_receive_redirect();
   DoStartAsyncRequest();
   DoReceiveRedirect();
-  EXPECT_FALSE(bridge());
 }
 
 TEST_F(WebURLLoaderImplTest, DeleteOnReceiveResponse) {
   client()->set_delete_on_receive_response();
   DoStartAsyncRequest();
   DoReceiveResponse();
-  EXPECT_FALSE(bridge());
 }
 
 TEST_F(WebURLLoaderImplTest, DeleteOnReceiveData) {
@@ -435,7 +398,6 @@
   DoStartAsyncRequest();
   DoReceiveResponse();
   DoReceiveData();
-  EXPECT_FALSE(bridge());
 }
 
 TEST_F(WebURLLoaderImplTest, DeleteOnFinish) {
@@ -444,7 +406,6 @@
   DoReceiveResponse();
   DoReceiveData();
   DoCompleteRequest();
-  EXPECT_FALSE(bridge());
 }
 
 TEST_F(WebURLLoaderImplTest, DeleteOnFail) {
@@ -453,7 +414,6 @@
   DoReceiveResponse();
   DoReceiveData();
   DoFailRequest();
-  EXPECT_FALSE(bridge());
 }
 
 TEST_F(WebURLLoaderImplTest, DeleteBeforeResponseDataURL) {
@@ -464,7 +424,6 @@
   client()->DeleteLoader();
   message_loop()->RunUntilIdle();
   EXPECT_FALSE(client()->did_receive_response());
-  EXPECT_FALSE(bridge());
 }
 
 // Data URL tests.
@@ -491,7 +450,6 @@
   EXPECT_TRUE(client()->did_receive_response());
   EXPECT_EQ("", client()->received_data());
   EXPECT_FALSE(client()->did_finish());
-  EXPECT_FALSE(bridge());
 }
 
 TEST_F(WebURLLoaderImplTest, DataURLDeleteOnReceiveData) {
@@ -504,7 +462,6 @@
   EXPECT_TRUE(client()->did_receive_response());
   EXPECT_EQ("blah!", client()->received_data());
   EXPECT_FALSE(client()->did_finish());
-  EXPECT_FALSE(bridge());
 }
 
 TEST_F(WebURLLoaderImplTest, DataURLDeleteOnFinish) {
@@ -517,7 +474,6 @@
   EXPECT_TRUE(client()->did_receive_response());
   EXPECT_EQ("blah!", client()->received_data());
   EXPECT_TRUE(client()->did_finish());
-  EXPECT_FALSE(bridge());
 }
 
 TEST_F(WebURLLoaderImplTest, DataURLDefersLoading) {
@@ -566,7 +522,7 @@
   DoReceiveResponseFtp();
   DoReceiveDataFtp();
   DoCompleteRequest();
-  EXPECT_FALSE(bridge()->canceled());
+  EXPECT_FALSE(dispatcher()->canceled());
 }
 
 TEST_F(WebURLLoaderImplTest, FtpDeleteOnReceiveResponse) {
@@ -576,18 +532,14 @@
 
   // No data should have been received.
   EXPECT_EQ("", client()->received_data());
-  EXPECT_FALSE(bridge());
 }
 
 TEST_F(WebURLLoaderImplTest, FtpDeleteOnReceiveFirstData) {
   client()->set_delete_on_receive_data();
   DoStartAsyncRequest();
-  // Some data is sent in ReceiveResponse for FTP requests, so the bridge should
-  // be deleted here.
   DoReceiveResponseFtp();
 
   EXPECT_NE("", client()->received_data());
-  EXPECT_FALSE(bridge());
 }
 
 TEST_F(WebURLLoaderImplTest, FtpDeleteOnReceiveMoreData) {
@@ -601,8 +553,6 @@
   peer()->OnCompletedRequest(net::OK, false, false, "", base::TimeTicks(),
                               strlen(kTestData));
   EXPECT_FALSE(client()->did_finish());
-
-  EXPECT_FALSE(bridge());
 }
 
 TEST_F(WebURLLoaderImplTest, FtpDeleteOnFinish) {
@@ -611,7 +561,6 @@
   DoReceiveResponseFtp();
   DoReceiveDataFtp();
   DoCompleteRequest();
-  EXPECT_FALSE(bridge());
 }
 
 TEST_F(WebURLLoaderImplTest, FtpDeleteOnFail) {
@@ -620,7 +569,6 @@
   DoReceiveResponseFtp();
   DoReceiveDataFtp();
   DoFailRequest();
-  EXPECT_FALSE(bridge());
 }
 
 // Multipart integration tests.  These are focused more on safe deletion than
@@ -633,7 +581,7 @@
   DoReceiveDataMultipart();
   DoCompleteRequest();
   EXPECT_EQ(kTestData, client()->received_data());
-  EXPECT_FALSE(bridge()->canceled());
+  EXPECT_FALSE(dispatcher()->canceled());
 }
 
 TEST_F(WebURLLoaderImplTest, MultipartDeleteOnReceiveFirstResponse) {
@@ -642,7 +590,6 @@
   DoStartAsyncRequest();
   DoReceiveResponseMultipart();
   EXPECT_EQ("", client()->received_data());
-  EXPECT_FALSE(bridge());
 }
 
 TEST_F(WebURLLoaderImplTest, MultipartDeleteOnReceiveSecondResponse) {
@@ -652,7 +599,6 @@
   client()->set_delete_on_receive_response();
   DoReceiveDataMultipart();
   EXPECT_EQ("", client()->received_data());
-  EXPECT_FALSE(bridge());
 }
 
 TEST_F(WebURLLoaderImplTest, MultipartDeleteOnReceiveFirstData) {
@@ -662,7 +608,6 @@
   DoReceiveResponseMultipart();
   DoReceiveDataMultipart();
   EXPECT_EQ("bl", client()->received_data());
-  EXPECT_FALSE(bridge());
 }
 
 TEST_F(WebURLLoaderImplTest, MultipartDeleteOnReceiveMoreData) {
@@ -677,7 +622,6 @@
                               strlen(kTestData));
   EXPECT_FALSE(client()->did_finish());
   EXPECT_EQ(kTestData, client()->received_data());
-  EXPECT_FALSE(bridge());
 }
 
 TEST_F(WebURLLoaderImplTest, MultipartDeleteFinish) {
@@ -688,7 +632,6 @@
   DoReceiveDataMultipart();
   DoCompleteRequest();
   EXPECT_EQ(kTestData, client()->received_data());
-  EXPECT_FALSE(bridge());
 }
 
 TEST_F(WebURLLoaderImplTest, MultipartDeleteFail) {
@@ -698,7 +641,6 @@
   DoReceiveResponseMultipart();
   DoReceiveDataMultipart();
   DoFailRequest();
-  EXPECT_FALSE(bridge());
 }
 
 // PlzNavigate: checks that the stream override parameters provided on
@@ -725,9 +667,8 @@
   client()->loader()->loadAsynchronously(request, client());
 
   // The stream url should have been requestead instead of the request url.
-  ASSERT_TRUE(bridge());
   ASSERT_TRUE(peer());
-  EXPECT_EQ(kStreamURL, bridge()->url());
+  EXPECT_EQ(kStreamURL, dispatcher()->url());
 
   EXPECT_FALSE(client()->did_receive_response());
   peer()->OnReceivedResponse(content::ResourceResponseInfo());
@@ -739,7 +680,7 @@
 
   DoReceiveData();
   DoCompleteRequest();
-  EXPECT_FALSE(bridge()->canceled());
+  EXPECT_FALSE(dispatcher()->canceled());
   EXPECT_EQ(kTestData, client()->received_data());
 }
 
diff --git a/content/child/webcrypto/openssl/aes_cbc_openssl.cc b/content/child/webcrypto/openssl/aes_cbc_openssl.cc
index e2a95850..5645935 100644
--- a/content/child/webcrypto/openssl/aes_cbc_openssl.cc
+++ b/content/child/webcrypto/openssl/aes_cbc_openssl.cc
@@ -65,7 +65,7 @@
     return Status::ErrorDataTooLarge();
 
   // Note: PKCS padding is enabled by default
-  crypto::ScopedOpenSSL<EVP_CIPHER_CTX, EVP_CIPHER_CTX_free>::Type context(
+  crypto::ScopedOpenSSL<EVP_CIPHER_CTX, EVP_CIPHER_CTX_free> context(
       EVP_CIPHER_CTX_new());
 
   if (!context.get())
diff --git a/content/child/webcrypto/openssl/aes_ctr_openssl.cc b/content/child/webcrypto/openssl/aes_ctr_openssl.cc
index 65bcdf7..43a1837 100644
--- a/content/child/webcrypto/openssl/aes_ctr_openssl.cc
+++ b/content/child/webcrypto/openssl/aes_ctr_openssl.cc
@@ -50,7 +50,7 @@
   DCHECK_EQ(16u, counter.byte_length());
 
   crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE);
-  crypto::ScopedOpenSSL<EVP_CIPHER_CTX, EVP_CIPHER_CTX_free>::Type context(
+  crypto::ScopedOpenSSL<EVP_CIPHER_CTX, EVP_CIPHER_CTX_free> context(
       EVP_CIPHER_CTX_new());
 
   if (!context.get())
diff --git a/content/child/webcrypto/openssl/util_openssl.cc b/content/child/webcrypto/openssl/util_openssl.cc
index f60fb3c..b03c6320 100644
--- a/content/child/webcrypto/openssl/util_openssl.cc
+++ b/content/child/webcrypto/openssl/util_openssl.cc
@@ -101,8 +101,7 @@
     return Status::OperationError();
   }
 
-  crypto::ScopedOpenSSL<EVP_AEAD_CTX, EVP_AEAD_CTX_cleanup>::Type ctx_cleanup(
-      &ctx);
+  crypto::ScopedOpenSSL<EVP_AEAD_CTX, EVP_AEAD_CTX_cleanup> ctx_cleanup(&ctx);
 
   size_t len;
   int ok;
@@ -226,8 +225,8 @@
   crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE);
 
   const uint8_t* ptr = key_data.bytes();
-  crypto::ScopedOpenSSL<PKCS8_PRIV_KEY_INFO, PKCS8_PRIV_KEY_INFO_free>::Type
-      p8inf(d2i_PKCS8_PRIV_KEY_INFO(nullptr, &ptr, key_data.byte_length()));
+  crypto::ScopedOpenSSL<PKCS8_PRIV_KEY_INFO, PKCS8_PRIV_KEY_INFO_free> p8inf(
+      d2i_PKCS8_PRIV_KEY_INFO(nullptr, &ptr, key_data.byte_length()));
   if (!p8inf.get() || ptr != key_data.bytes() + key_data.byte_length())
     return Status::DataError();
 
diff --git a/content/common/BUILD.gn b/content/common/BUILD.gn
index 6bf0f64..b311783 100644
--- a/content/common/BUILD.gn
+++ b/content/common/BUILD.gn
@@ -7,7 +7,7 @@
 import("//content/common/common.gni")
 import("//third_party/mojo/src/mojo/public/tools/bindings/mojom.gni")
 
-if (is_chromeos && cpu_arch != "arm") {
+if (is_chromeos && current_cpu != "arm") {
   action("libva_generate_stubs") {
     extra_header = "gpu/media/va_stub_header.fragment"
 
@@ -130,7 +130,10 @@
                         ".",
                         "//content")
 
-  configs += [ "//content:content_implementation" ]
+  configs += [
+    "//content:content_implementation",
+    "//build/config/compiler:no_size_t_to_int_warning",
+  ]
 
   public_deps = [
     "//gpu/command_buffer/common",
@@ -279,7 +282,7 @@
     include_dirs += [ "//third_party/khronos" ]
     configs += [ "//build/config/linux:xcomposite" ]
 
-    if (cpu_arch != "arm" || !is_chromeos) {
+    if (current_cpu != "arm" || !is_chromeos) {
       sources += [
         "gpu/x_util.cc",
         "gpu/x_util.h",
@@ -352,13 +355,13 @@
         "GLESv2",
       ]
     }
-    if (cpu_arch == "arm") {
+    if (current_cpu == "arm") {
       sources += [
         "gpu/media/tegra_v4l2_video_device.cc",
         "gpu/media/tegra_v4l2_video_device.h",
       ]
     }
-    if (cpu_arch != "arm") {
+    if (current_cpu != "arm") {
       sources += [
                    "gpu/media/va_surface.h",
                    "gpu/media/vaapi_h264_decoder.cc",
@@ -453,6 +456,11 @@
       ]
     }
   }
+
+  if (is_win && current_cpu == "x64") {
+    # TODO(jschuh): Remove this after crbug.com/173851 gets fixed.
+    cflags = [ "/bigobj" ]
+  }
 }
 
 mojom("mojo_bindings") {
diff --git a/content/common/android/address_parser.cc b/content/common/android/address_parser.cc
index 30fa304ba..41bdeb7 100644
--- a/content/common/android/address_parser.cc
+++ b/content/common/android/address_parser.cc
@@ -182,8 +182,14 @@
           size_t zip_word = state_last_word + 1;
           if (zip_word == words.size()) {
             do {
-              if (!tokenizer.GetNext())
-                return false;
+              if (!tokenizer.GetNext()) {
+                // The address ends with a state name without a zip code. This
+                // is legal according to WebView#findAddress public
+                // documentation.
+                *start_pos = words[0].begin - begin;
+                *end_pos = words[state_last_word].end - begin;
+                return true;
+              }
             } while (tokenizer.token_is_delim());
             words.push_back(Word(tokenizer.token_begin(),
                             tokenizer.token_end()));
diff --git a/content/common/android/address_parser_unittest.cc b/content/common/android/address_parser_unittest.cc
index cefc537..dd7148db 100644
--- a/content/common/android/address_parser_unittest.cc
+++ b/content/common/android/address_parser_unittest.cc
@@ -592,3 +592,8 @@
       "1 Supercalifragilisticexpialidocious is too long, CA 90000"));
   EXPECT_FALSE(ContainsAddress(""));
 }
+
+TEST_F(AddressParserTest, FullAddressWithoutZipCode) {
+  EXPECT_TRUE(IsAddress("1600 Amphitheatre Parkway Mountain View, CA"));
+  EXPECT_TRUE(IsAddress("201 S. Division St. Suite 500 Ann Arbor, MI"));
+}
diff --git a/content/common/clipboard_messages.h b/content/common/clipboard_messages.h
index 5874de6a..8e417c0 100644
--- a/content/common/clipboard_messages.h
+++ b/content/common/clipboard_messages.h
@@ -83,9 +83,13 @@
 IPC_MESSAGE_CONTROL2(ClipboardHostMsg_WriteCustomData,
                      ui::ClipboardType /* type */,
                      CustomDataMap /* custom data */)
+// TODO(dcheng): The |url| parameter should really be a GURL, but <canvas>'s
+// copy as image tries to set very long data: URLs on the clipboard. Using
+// GURL causes the browser to kill the renderer for sending a bad IPC (GURLs
+// bigger than 2 megabytes are considered to be bad). https://crbug.com/459822
 IPC_MESSAGE_CONTROL3(ClipboardHostMsg_WriteBookmark,
                      ui::ClipboardType /* type */,
-                     GURL /* url */,
+                     std::string /* url */,
                      base::string16 /* title */)
 IPC_SYNC_MESSAGE_CONTROL3_0(ClipboardHostMsg_WriteImage,
                             ui::ClipboardType /* type */,
diff --git a/content/common/cursors/webcursor_ozone.cc b/content/common/cursors/webcursor_ozone.cc
index 39a356d..15df6f51 100644
--- a/content/common/cursors/webcursor_ozone.cc
+++ b/content/common/cursors/webcursor_ozone.cc
@@ -19,7 +19,7 @@
   ImageFromCustomData(&bitmap);
   gfx::Point hotspot = hotspot_;
   ui::ScaleAndRotateCursorBitmapAndHotpoint(
-      device_scale_factor_, rotation_, &bitmap, &hotspot);
+      device_scale_factor_ / custom_scale_, rotation_, &bitmap, &hotspot);
 
   return ui::CursorFactoryOzone::GetInstance()->CreateImageCursor(bitmap,
                                                                   hotspot);
diff --git a/content/common/discardable_shared_memory_heap.cc b/content/common/discardable_shared_memory_heap.cc
index 427c3595..c854a96b 100644
--- a/content/common/discardable_shared_memory_heap.cc
+++ b/content/common/discardable_shared_memory_heap.cc
@@ -20,7 +20,7 @@
 }  // namespace
 
 DiscardableSharedMemoryHeap::Span::Span(
-    linked_ptr<base::DiscardableSharedMemory> shared_memory,
+    base::DiscardableSharedMemory* shared_memory,
     size_t start,
     size_t length)
     : shared_memory_(shared_memory), start_(start), length_(length) {
@@ -36,15 +36,15 @@
 }
 
 DiscardableSharedMemoryHeap::~DiscardableSharedMemoryHeap() {
-  while (!free_spans_.empty())
-    RemoveFromFreeList(free_spans_.tail()->value());
+  for (auto shared_memory : shared_memory_segments_)
+    ReleaseMemory(shared_memory);
+
+  DCHECK(free_spans_.empty());
 }
 
 scoped_ptr<DiscardableSharedMemoryHeap::Span> DiscardableSharedMemoryHeap::Grow(
-    scoped_ptr<base::DiscardableSharedMemory> memory,
+    scoped_ptr<base::DiscardableSharedMemory> shared_memory,
     size_t size) {
-  linked_ptr<base::DiscardableSharedMemory> shared_memory(memory.release());
-
   // Memory must be aligned to block size.
   DCHECK_EQ(
       reinterpret_cast<size_t>(shared_memory->memory()) & (block_size_ - 1),
@@ -52,25 +52,33 @@
   DCHECK_EQ(size & (block_size_ - 1), 0u);
 
   scoped_ptr<Span> span(
-      new Span(shared_memory,
+      new Span(shared_memory.get(),
                reinterpret_cast<size_t>(shared_memory->memory()) / block_size_,
                size / block_size_));
   DCHECK(spans_.find(span->start_) == spans_.end());
   DCHECK(spans_.find(span->start_ + span->length_ - 1) == spans_.end());
   RegisterSpan(span.get());
+
+  // Start tracking if segment is resident by adding it to
+  // |shared_memory_segments_|.
+  shared_memory_segments_.push_back(shared_memory.release());
+
   return span.Pass();
 }
 
 void DiscardableSharedMemoryHeap::MergeIntoFreeList(scoped_ptr<Span> span) {
+  DCHECK(span->shared_memory_);
+
   // Merge with previous span if possible.
   SpanMap::iterator prev_it = spans_.find(span->start_ - 1);
   if (prev_it != spans_.end() && IsInFreeList(prev_it->second)) {
     scoped_ptr<Span> prev = RemoveFromFreeList(prev_it->second);
     DCHECK_EQ(prev->start_ + prev->length_, span->start_);
-    spans_.erase(span->start_);
+    UnregisterSpan(prev.get());
+    if (span->length_ > 1)
+      spans_.erase(span->start_);
     span->start_ -= prev->length_;
     span->length_ += prev->length_;
-    DeleteSpan(prev.Pass());
     spans_[span->start_] = span.get();
   }
 
@@ -79,10 +87,10 @@
   if (next_it != spans_.end() && IsInFreeList(next_it->second)) {
     scoped_ptr<Span> next = RemoveFromFreeList(next_it->second);
     DCHECK_EQ(next->start_, span->start_ + span->length_);
+    UnregisterSpan(next.get());
     if (span->length_ > 1)
       spans_.erase(span->start_ + span->length_ - 1);
     span->length_ += next->length_;
-    DeleteSpan(next.Pass());
     spans_[span->start_ + span->length_ - 1] = span.get();
   }
 
@@ -134,11 +142,25 @@
   return best ? Carve(best, blocks) : nullptr;
 }
 
-void DiscardableSharedMemoryHeap::DeleteSpan(scoped_ptr<Span> span) {
-  DCHECK(!IsInFreeList(span.get()));
-  spans_.erase(span->start_);
-  if (span->length_ > 1)
-    spans_.erase(span->start_ + span->length_ - 1);
+void DiscardableSharedMemoryHeap::ReleaseFreeMemory() {
+  size_t i = 0;
+
+  // Release memory for all non-resident segments.
+  while (i < shared_memory_segments_.size()) {
+    base::DiscardableSharedMemory* shared_memory = shared_memory_segments_[i];
+
+    // Skip segment if still resident.
+    if (shared_memory->IsMemoryResident()) {
+      ++i;
+      continue;
+    }
+
+    // Release the memory and unregistering all associated spans.
+    ReleaseMemory(shared_memory);
+
+    std::swap(shared_memory_segments_[i], shared_memory_segments_.back());
+    shared_memory_segments_.pop_back();
+  }
 }
 
 scoped_ptr<DiscardableSharedMemoryHeap::Span>
@@ -176,4 +198,35 @@
     spans_[span->start_ + span->length_ - 1] = span;
 }
 
+void DiscardableSharedMemoryHeap::UnregisterSpan(Span* span) {
+  DCHECK(spans_.find(span->start_) != spans_.end());
+  DCHECK_EQ(spans_[span->start_], span);
+  spans_.erase(span->start_);
+  if (span->length_ > 1) {
+    DCHECK(spans_.find(span->start_ + span->length_ - 1) != spans_.end());
+    DCHECK_EQ(spans_[span->start_ + span->length_ - 1], span);
+    spans_.erase(span->start_ + span->length_ - 1);
+  }
+}
+
+void DiscardableSharedMemoryHeap::ReleaseMemory(
+    base::DiscardableSharedMemory* shared_memory) {
+  size_t offset =
+      reinterpret_cast<size_t>(shared_memory->memory()) / block_size_;
+  size_t end = offset + shared_memory->mapped_size() / block_size_;
+  while (offset < end) {
+    DCHECK(spans_.find(offset) != spans_.end());
+    Span* span = spans_[offset];
+    DCHECK_EQ(span->shared_memory_, shared_memory);
+    span->shared_memory_ = nullptr;
+    UnregisterSpan(span);
+
+    offset += span->length_;
+
+    // If |span| is in the free list, remove it.
+    if (IsInFreeList(span))
+      RemoveFromFreeList(span);
+  }
+}
+
 }  // namespace content
diff --git a/content/common/discardable_shared_memory_heap.h b/content/common/discardable_shared_memory_heap.h
index 410325b..919d4c6 100644
--- a/content/common/discardable_shared_memory_heap.h
+++ b/content/common/discardable_shared_memory_heap.h
@@ -28,20 +28,18 @@
    public:
     ~Span();
 
-    base::DiscardableSharedMemory* shared_memory() {
-      return shared_memory_.get();
-    }
+    base::DiscardableSharedMemory* shared_memory() { return shared_memory_; }
     size_t start() const { return start_; }
     size_t length() const { return length_; }
 
    private:
     friend class DiscardableSharedMemoryHeap;
 
-    Span(linked_ptr<base::DiscardableSharedMemory> shared_memory,
+    Span(base::DiscardableSharedMemory* shared_memory,
          size_t start,
          size_t length);
 
-    linked_ptr<base::DiscardableSharedMemory> shared_memory_;
+    base::DiscardableSharedMemory* shared_memory_;
     size_t start_;
     size_t length_;
 
@@ -51,10 +49,10 @@
   explicit DiscardableSharedMemoryHeap(size_t block_size);
   ~DiscardableSharedMemoryHeap();
 
-  // Grow heap using |memory| and return a span for this new memory.
-  // |memory| must be aligned to the block size and |size| must be a
+  // Grow heap using |shared_memory| and return a span for this new memory.
+  // |shared_memory| must be aligned to the block size and |size| must be a
   // multiple of the block size.
-  scoped_ptr<Span> Grow(scoped_ptr<base::DiscardableSharedMemory> memory,
+  scoped_ptr<Span> Grow(scoped_ptr<base::DiscardableSharedMemory> shared_memory,
                         size_t size);
 
   // Merge |span| into the free list. This will coalesce |span| with
@@ -70,16 +68,21 @@
   // memory. If found, the span is removed from the free list and returned.
   scoped_ptr<Span> SearchFreeList(size_t blocks);
 
-  // Delete span and release memory if possible.
-  void DeleteSpan(scoped_ptr<Span> span);
+  // Release shared memory segments that have been purged.
+  void ReleaseFreeMemory();
 
  private:
   scoped_ptr<Span> RemoveFromFreeList(Span* span);
   scoped_ptr<Span> Carve(Span* span, size_t blocks);
   void RegisterSpan(Span* span);
+  void UnregisterSpan(Span* span);
+  void ReleaseMemory(base::DiscardableSharedMemory* shared_memory);
 
   size_t block_size_;
 
+  // Discardable shared memory instances.
+  ScopedVector<base::DiscardableSharedMemory> shared_memory_segments_;
+
   // Mapping from first/last block of span to Span instance.
   typedef base::hash_map<size_t, Span*> SpanMap;
   SpanMap spans_;
diff --git a/content/common/discardable_shared_memory_heap_unittest.cc b/content/common/discardable_shared_memory_heap_unittest.cc
index 2ab736cc..a13604f0 100644
--- a/content/common/discardable_shared_memory_heap_unittest.cc
+++ b/content/common/discardable_shared_memory_heap_unittest.cc
@@ -45,11 +45,11 @@
       heap.SearchFreeList(kBlocks);
   ASSERT_TRUE(span);
 
-  // Delete span and shared memory backing.
-  heap.DeleteSpan(span.Pass());
-
   // Free list should be empty again.
   EXPECT_FALSE(heap.SearchFreeList(1));
+
+  // Merge it into the free list again.
+  heap.MergeIntoFreeList(span.Pass());
 }
 
 TEST_F(DiscardableSharedMemoryHeapTest, SplitAndMerge) {
@@ -99,7 +99,35 @@
   // All memory has been returned to the free list.
   scoped_ptr<DiscardableSharedMemoryHeap::Span> large_span =
       heap.SearchFreeList(kBlocks);
-  EXPECT_TRUE(large_span);
+  ASSERT_TRUE(large_span);
+
+  // Merge it into the free list again.
+  heap.MergeIntoFreeList(large_span.Pass());
+}
+
+TEST_F(DiscardableSharedMemoryHeapTest, MergeSingleBlockSpan) {
+  size_t block_size = base::GetPageSize();
+  DiscardableSharedMemoryHeap heap(block_size);
+
+  const size_t kBlocks = 6;
+  size_t memory_size = block_size * kBlocks;
+
+  scoped_ptr<base::DiscardableSharedMemory> memory(
+      new base::DiscardableSharedMemory);
+  ASSERT_TRUE(memory->CreateAndMap(memory_size));
+  scoped_ptr<DiscardableSharedMemoryHeap::Span> new_span(
+      heap.Grow(memory.Pass(), memory_size));
+
+  // Split span into two.
+  scoped_ptr<DiscardableSharedMemoryHeap::Span> leftover =
+      heap.Split(new_span.get(), 5);
+  ASSERT_TRUE(leftover);
+
+  // Merge |new_span| into free list.
+  heap.MergeIntoFreeList(new_span.Pass());
+
+  // Merge |leftover| into free list.
+  heap.MergeIntoFreeList(leftover.Pass());
 }
 
 TEST_F(DiscardableSharedMemoryHeapTest, Grow) {
@@ -127,6 +155,32 @@
   // Memory should now be available.
   scoped_ptr<DiscardableSharedMemoryHeap::Span> span2 = heap.SearchFreeList(1);
   EXPECT_TRUE(span2);
+
+  // Merge spans into the free list again.
+  heap.MergeIntoFreeList(span1.Pass());
+  heap.MergeIntoFreeList(span2.Pass());
+}
+
+TEST_F(DiscardableSharedMemoryHeapTest, ReleaseFreeMemory) {
+  size_t block_size = base::GetPageSize();
+  DiscardableSharedMemoryHeap heap(block_size);
+
+  scoped_ptr<base::DiscardableSharedMemory> memory(
+      new base::DiscardableSharedMemory);
+  ASSERT_TRUE(memory->CreateAndMap(block_size));
+  scoped_ptr<DiscardableSharedMemoryHeap::Span> span =
+      heap.Grow(memory.Pass(), block_size);
+
+  // Unlock memory so it can be purged.
+  span->shared_memory()->Unlock(0, 0);
+
+  // Purge and release shared memory.
+  bool rv = span->shared_memory()->Purge(base::Time::Now());
+  EXPECT_TRUE(rv);
+  heap.ReleaseFreeMemory();
+
+  // Shared memory backing for |span| should be gone.
+  EXPECT_FALSE(span->shared_memory());
 }
 
 }  // namespace
diff --git a/content/common/dom_storage/dom_storage_map.cc b/content/common/dom_storage/dom_storage_map.cc
index de33a52..71368bd 100644
--- a/content/common/dom_storage/dom_storage_map.cc
+++ b/content/common/dom_storage/dom_storage_map.cc
@@ -14,17 +14,6 @@
   return (key.length() + value.length()) * sizeof(base::char16);
 }
 
-size_t CountBytes(const DOMStorageValuesMap& values) {
-  if (values.size() == 0)
-    return 0;
-
-  size_t count = 0;
-  DOMStorageValuesMap::const_iterator it = values.begin();
-  for (; it != values.end(); ++it)
-    count += size_of_item(it->first, it->second.string());
-  return count;
-}
-
 }  // namespace
 
 DOMStorageMap::DOMStorageMap(size_t quota)
@@ -119,4 +108,14 @@
   last_key_index_ = 0;
 }
 
+size_t DOMStorageMap::CountBytes(const DOMStorageValuesMap& values) {
+  if (values.empty())
+    return 0;
+
+  size_t count = 0;
+  for (const auto& pair : values)
+    count += size_of_item(pair.first, pair.second.string());
+  return count;
+}
+
 }  // namespace content
diff --git a/content/common/dom_storage/dom_storage_map.h b/content/common/dom_storage/dom_storage_map.h
index 140de07..7130d27 100644
--- a/content/common/dom_storage/dom_storage_map.h
+++ b/content/common/dom_storage/dom_storage_map.h
@@ -46,6 +46,8 @@
   size_t quota() const { return quota_; }
   void set_quota(size_t quota) { quota_ = quota; }
 
+  static size_t CountBytes(const DOMStorageValuesMap& values);
+
  private:
   friend class base::RefCountedThreadSafe<DOMStorageMap>;
   ~DOMStorageMap();
diff --git a/content/common/gpu/client/command_buffer_proxy_impl.cc b/content/common/gpu/client/command_buffer_proxy_impl.cc
index 7ffd2e6..75cffa2 100644
--- a/content/common/gpu/client/command_buffer_proxy_impl.cc
+++ b/content/common/gpu/client/command_buffer_proxy_impl.cc
@@ -56,6 +56,8 @@
                         OnSignalSyncPointAck);
     IPC_MESSAGE_HANDLER(GpuCommandBufferMsg_SwapBuffersCompleted,
                         OnSwapBuffersCompleted);
+    IPC_MESSAGE_HANDLER(GpuCommandBufferMsg_UpdateVSyncParameters,
+                        OnUpdateVSyncParameters);
     IPC_MESSAGE_UNHANDLED(handled = false)
   IPC_END_MESSAGE_MAP()
 
@@ -237,6 +239,12 @@
   swap_buffers_completion_callback_ = callback;
 }
 
+void CommandBufferProxyImpl::SetUpdateVSyncParametersCallback(
+    const UpdateVSyncParametersCallback& callback) {
+  CheckLock();
+  update_vsync_parameters_completion_callback_ = callback;
+}
+
 void CommandBufferProxyImpl::WaitForTokenInRange(int32 start, int32 end) {
   CheckLock();
   TRACE_EVENT2("gpu",
@@ -594,4 +602,10 @@
   }
 }
 
+void CommandBufferProxyImpl::OnUpdateVSyncParameters(base::TimeTicks timebase,
+                                                     base::TimeDelta interval) {
+  if (!update_vsync_parameters_completion_callback_.is_null())
+    update_vsync_parameters_completion_callback_.Run(timebase, interval);
+}
+
 }  // namespace content
diff --git a/content/common/gpu/client/command_buffer_proxy_impl.h b/content/common/gpu/client/command_buffer_proxy_impl.h
index 0727064..2da5bbc2 100644
--- a/content/common/gpu/client/command_buffer_proxy_impl.h
+++ b/content/common/gpu/client/command_buffer_proxy_impl.h
@@ -135,11 +135,16 @@
       const GpuConsoleMessageCallback& callback);
 
   void SetLatencyInfo(const std::vector<ui::LatencyInfo>& latency_info);
-  typedef base::Callback<void(const std::vector<ui::LatencyInfo>& latency_info)>
-      SwapBuffersCompletionCallback;
+  using SwapBuffersCompletionCallback =
+      base::Callback<void(const std::vector<ui::LatencyInfo>& latency_info)>;
   void SetSwapBuffersCompletionCallback(
       const SwapBuffersCompletionCallback& callback);
 
+  using UpdateVSyncParametersCallback =
+      base::Callback<void(base::TimeTicks timebase, base::TimeDelta interval)>;
+  void SetUpdateVSyncParametersCallback(
+      const UpdateVSyncParametersCallback& callback);
+
   // TODO(apatrick): this is a temporary optimization while skia is calling
   // ContentGLContext::MakeCurrent prior to every GL call. It saves returning 6
   // ints redundantly when only the error is needed for the
@@ -175,6 +180,8 @@
   void OnSetMemoryAllocation(const gpu::MemoryAllocation& allocation);
   void OnSignalSyncPointAck(uint32 id);
   void OnSwapBuffersCompleted(const std::vector<ui::LatencyInfo>& latency_info);
+  void OnUpdateVSyncParameters(base::TimeTicks timebase,
+                               base::TimeDelta interval);
 
   // Try to read an updated copy of the state from shared memory.
   void TryUpdateState();
@@ -219,6 +226,7 @@
   std::vector<ui::LatencyInfo> latency_info_;
 
   SwapBuffersCompletionCallback swap_buffers_completion_callback_;
+  UpdateVSyncParametersCallback update_vsync_parameters_completion_callback_;
 
   DISALLOW_COPY_AND_ASSIGN(CommandBufferProxyImpl);
 };
diff --git a/content/common/gpu/gpu_command_buffer_stub.cc b/content/common/gpu/gpu_command_buffer_stub.cc
index 6503c67..87f0d05 100644
--- a/content/common/gpu/gpu_command_buffer_stub.cc
+++ b/content/common/gpu/gpu_command_buffer_stub.cc
@@ -1078,9 +1078,15 @@
   return GetMemoryManager()->GetClientMemoryUsage(this);
 }
 
-void GpuCommandBufferStub::SwapBuffersCompleted(
+void GpuCommandBufferStub::SendSwapBuffersCompleted(
     const std::vector<ui::LatencyInfo>& latency_info) {
   Send(new GpuCommandBufferMsg_SwapBuffersCompleted(route_id_, latency_info));
 }
 
+void GpuCommandBufferStub::SendUpdateVSyncParameters(base::TimeTicks timebase,
+                                                     base::TimeDelta interval) {
+  Send(new GpuCommandBufferMsg_UpdateVSyncParameters(route_id_, timebase,
+                                                     interval));
+}
+
 }  // namespace content
diff --git a/content/common/gpu/gpu_command_buffer_stub.h b/content/common/gpu/gpu_command_buffer_stub.h
index 2fad94a4..c9e215c7 100644
--- a/content/common/gpu/gpu_command_buffer_stub.h
+++ b/content/common/gpu/gpu_command_buffer_stub.h
@@ -147,7 +147,10 @@
 
   uint64 GetMemoryUsage() const;
 
-  void SwapBuffersCompleted(const std::vector<ui::LatencyInfo>& latency_info);
+  void SendSwapBuffersCompleted(
+      const std::vector<ui::LatencyInfo>& latency_info);
+  void SendUpdateVSyncParameters(base::TimeTicks timebase,
+                                 base::TimeDelta interval);
 
  private:
   GpuMemoryManager* GetMemoryManager() const;
diff --git a/content/common/gpu/gpu_messages.h b/content/common/gpu/gpu_messages.h
index c281975..5026150b 100644
--- a/content/common/gpu/gpu_messages.h
+++ b/content/common/gpu/gpu_messages.h
@@ -393,12 +393,6 @@
 IPC_MESSAGE_CONTROL1(GpuHostMsg_AcceleratedSurfaceBuffersSwapped,
                      GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params)
 
-// Tells the browser about updated parameters for vsync alignment.
-IPC_MESSAGE_CONTROL3(GpuHostMsg_UpdateVSyncParameters,
-                     int32 /* surface_id */,
-                     base::TimeTicks /* timebase */,
-                     base::TimeDelta /* interval */)
-
 IPC_MESSAGE_CONTROL1(GpuHostMsg_DidCreateOffscreenContext,
                      GURL /* url */)
 
@@ -564,6 +558,11 @@
 IPC_MESSAGE_ROUTED1(GpuCommandBufferMsg_SwapBuffersCompleted,
                     std::vector<ui::LatencyInfo> /* latency_info */)
 
+// Tells the browser about updated parameters for vsync alignment.
+IPC_MESSAGE_ROUTED2(GpuCommandBufferMsg_UpdateVSyncParameters,
+                    base::TimeTicks /* timebase */,
+                    base::TimeDelta /* interval */)
+
 // Send to stub on surface visibility change.
 IPC_MESSAGE_ROUTED1(GpuCommandBufferMsg_SetSurfaceVisible, bool /* visible */)
 
diff --git a/content/common/gpu/image_transport_surface.cc b/content/common/gpu/image_transport_surface.cc
index 7e01bc13..02fbdfa4 100644
--- a/content/common/gpu/image_transport_surface.cc
+++ b/content/common/gpu/image_transport_surface.cc
@@ -108,18 +108,6 @@
   manager_->Send(new GpuHostMsg_AcceleratedSurfaceBuffersSwapped(params));
 }
 
-void ImageTransportHelper::SendUpdateVSyncParameters(
-      base::TimeTicks timebase, base::TimeDelta interval) {
-  manager_->Send(new GpuHostMsg_UpdateVSyncParameters(stub_->surface_id(),
-                                                      timebase,
-                                                      interval));
-}
-
-void ImageTransportHelper::SwapBuffersCompleted(
-    const std::vector<ui::LatencyInfo>& latency_info) {
-  stub_->SwapBuffersCompleted(latency_info);
-}
-
 void ImageTransportHelper::SetPreemptByFlag(
     scoped_refptr<gpu::PreemptionFlag> preemption_flag) {
   stub_->channel()->SetPreemptByFlag(preemption_flag);
@@ -233,7 +221,7 @@
         ui::INPUT_EVENT_LATENCY_TERMINATED_FRAME_SWAP_COMPONENT, 0, 0);
   }
 
-  helper_->SwapBuffersCompleted(latency_info_);
+  helper_->stub()->SendSwapBuffersCompleted(latency_info_);
   latency_info_.clear();
 }
 
@@ -271,8 +259,8 @@
   gfx::VSyncProvider* vsync_provider = GetVSyncProvider();
   if (vsync_provider) {
     vsync_provider->GetVSyncParameters(
-      base::Bind(&ImageTransportHelper::SendUpdateVSyncParameters,
-                 helper_->AsWeakPtr()));
+        base::Bind(&GpuCommandBufferStub::SendUpdateVSyncParameters,
+                   helper_->stub()->AsWeakPtr()));
   }
 }
 
diff --git a/content/common/gpu/image_transport_surface.h b/content/common/gpu/image_transport_surface.h
index f87d9d8..ac571dc 100644
--- a/content/common/gpu/image_transport_surface.h
+++ b/content/common/gpu/image_transport_surface.h
@@ -123,10 +123,6 @@
   // like size and surface id. The helper fills in the rest.
   void SendAcceleratedSurfaceBuffersSwapped(
       GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params params);
-  void SendUpdateVSyncParameters(
-      base::TimeTicks timebase, base::TimeDelta interval);
-
-  void SwapBuffersCompleted(const std::vector<ui::LatencyInfo>& latency_info);
 
   void SetPreemptByFlag(
       scoped_refptr<gpu::PreemptionFlag> preemption_flag);
diff --git a/content/common/gpu/media/dxva_video_decode_accelerator.h b/content/common/gpu/media/dxva_video_decode_accelerator.h
index c8996c1..6aa21c9 100644
--- a/content/common/gpu/media/dxva_video_decode_accelerator.h
+++ b/content/common/gpu/media/dxva_video_decode_accelerator.h
@@ -50,19 +50,19 @@
   // Does not take ownership of |client| which must outlive |*this|.
   explicit DXVAVideoDecodeAccelerator(
       const base::Callback<bool(void)>& make_context_current);
-  virtual ~DXVAVideoDecodeAccelerator();
+  ~DXVAVideoDecodeAccelerator() override;
 
   // media::VideoDecodeAccelerator implementation.
-  virtual bool Initialize(media::VideoCodecProfile profile,
-                          Client* client) override;
-  virtual void Decode(const media::BitstreamBuffer& bitstream_buffer) override;
-  virtual void AssignPictureBuffers(
+  bool Initialize(media::VideoCodecProfile profile,
+                  Client* client) override;
+  void Decode(const media::BitstreamBuffer& bitstream_buffer) override;
+  void AssignPictureBuffers(
       const std::vector<media::PictureBuffer>& buffers) override;
-  virtual void ReusePictureBuffer(int32 picture_buffer_id) override;
-  virtual void Flush() override;
-  virtual void Reset() override;
-  virtual void Destroy() override;
-  virtual bool CanDecodeOnIOThread() override;
+  void ReusePictureBuffer(int32 picture_buffer_id) override;
+  void Flush() override;
+  void Reset() override;
+  void Destroy() override;
+  bool CanDecodeOnIOThread() override;
   GLenum GetSurfaceInternalFormat() const override;
 
  private:
diff --git a/content/common/gpu/media/rendering_helper.cc b/content/common/gpu/media/rendering_helper.cc
index 45271f1..cd4e601 100644
--- a/content/common/gpu/media/rendering_helper.cc
+++ b/content/common/gpu/media/rendering_helper.cc
@@ -42,7 +42,6 @@
 #include "ui/display/chromeos/display_configurator.h"
 #endif  // defined(OS_CHROMEOS)
 #include "ui/ozone/public/ozone_platform.h"
-#include "ui/ozone/public/ui_thread_gpu.h"
 #include "ui/platform_window/platform_window.h"
 #include "ui/platform_window/platform_window_delegate.h"
 #endif  // defined(USE_OZONE)
@@ -98,7 +97,6 @@
 class RenderingHelper::StubOzoneDelegate : public ui::PlatformWindowDelegate {
  public:
   StubOzoneDelegate() : accelerated_widget_(gfx::kNullAcceleratedWidget) {
-    ui_thread_gpu_.Initialize();
     platform_window_ = ui::OzonePlatform::GetInstance()->CreatePlatformWindow(
         this, gfx::Rect());
   }
@@ -132,7 +130,6 @@
   ui::PlatformWindow* platform_window() const { return platform_window_.get(); }
 
  private:
-  ui::UiThreadGpu ui_thread_gpu_;
   scoped_ptr<ui::PlatformWindow> platform_window_;
   gfx::AcceleratedWidget accelerated_widget_;
 
diff --git a/content/common/gpu/media/v4l2_slice_video_decode_accelerator.h b/content/common/gpu/media/v4l2_slice_video_decode_accelerator.h
index 832e1e7..1a8644d 100644
--- a/content/common/gpu/media/v4l2_slice_video_decode_accelerator.h
+++ b/content/common/gpu/media/v4l2_slice_video_decode_accelerator.h
@@ -39,7 +39,7 @@
       const base::WeakPtr<Client>& io_client_,
       const base::Callback<bool(void)>& make_context_current,
       const scoped_refptr<base::MessageLoopProxy>& io_message_loop_proxy);
-  virtual ~V4L2SliceVideoDecodeAccelerator() override;
+  ~V4L2SliceVideoDecodeAccelerator() override;
 
   // media::VideoDecodeAccelerator implementation.
   bool Initialize(media::VideoCodecProfile profile,
diff --git a/content/common/gpu/media/v4l2_video_decode_accelerator.cc b/content/common/gpu/media/v4l2_video_decode_accelerator.cc
index 681292e..019446b 100644
--- a/content/common/gpu/media/v4l2_video_decode_accelerator.cc
+++ b/content/common/gpu/media/v4l2_video_decode_accelerator.cc
@@ -171,6 +171,7 @@
       decoder_decode_buffer_tasks_scheduled_(0),
       decoder_frames_at_client_(0),
       decoder_flushing_(false),
+      resolution_change_pending_(false),
       resolution_change_reset_pending_(false),
       decoder_partial_frame_pending_(false),
       input_streamon_(false),
@@ -893,9 +894,8 @@
     return;
   }
 
-  bool resolution_change_pending = false;
   if (event_pending)
-    resolution_change_pending = DequeueResolutionChangeEvent();
+    DequeueEvents();
   Dequeue();
   Enqueue();
 
@@ -937,8 +937,7 @@
            << decoder_frames_at_client_ << "]";
 
   ScheduleDecodeBufferTaskIfNeeded();
-  if (resolution_change_pending)
-    StartResolutionChange();
+  StartResolutionChangeIfNeeded();
 }
 
 void V4L2VideoDecodeAccelerator::Enqueue() {
@@ -992,25 +991,24 @@
   }
 }
 
-bool V4L2VideoDecodeAccelerator::DequeueResolutionChangeEvent() {
+void V4L2VideoDecodeAccelerator::DequeueEvents() {
   DCHECK_EQ(decoder_thread_.message_loop(), base::MessageLoop::current());
   DCHECK_NE(decoder_state_, kUninitialized);
-  DVLOG(3) << "DequeueResolutionChangeEvent()";
+  DVLOG(3) << "DequeueEvents()";
 
   struct v4l2_event ev;
   memset(&ev, 0, sizeof(ev));
 
   while (device_->Ioctl(VIDIOC_DQEVENT, &ev) == 0) {
     if (ev.type == V4L2_EVENT_RESOLUTION_CHANGE) {
-      DVLOG(3)
-          << "DequeueResolutionChangeEvent(): got resolution change event.";
-      return true;
+      DVLOG(3) << "DequeueEvents(): got resolution change event.";
+      DCHECK(!resolution_change_pending_);
+      resolution_change_pending_ = IsResolutionChangeNecessary();
     } else {
-      LOG(ERROR) << "DequeueResolutionChangeEvent(): got an event (" << ev.type
-                 << ") we haven't subscribed to.";
+      LOG(ERROR) << "DequeueEvents(): got an event (" << ev.type
+                  << ") we haven't subscribed to.";
     }
   }
-  return false;
 }
 
 void V4L2VideoDecodeAccelerator::Dequeue() {
@@ -1286,7 +1284,7 @@
   // transitioning to next chunk.
   // For now, do the streamoff-streamon cycle to satisfy Exynos and not freeze
   // when doing MSE. This should be harmless otherwise.
-  if (!(StopDevicePoll() && StopOutputStream() && StopInputStream()))
+  if (!StopDevicePoll(false))
     return;
 
   if (!StartDevicePoll())
@@ -1314,29 +1312,17 @@
 
   // If we are in the middle of switching resolutions, postpone reset until
   // it's done. We don't have to worry about timing of this wrt to decoding,
-  // because output pipe is already stopped if we are changing resolution.
+  // because input pipe is already stopped if we are changing resolution.
   // We will come back here after we are done with the resolution change.
   DCHECK(!resolution_change_reset_pending_);
-  if (decoder_state_ == kChangingResolution) {
+  if (resolution_change_pending_ || decoder_state_ == kChangingResolution) {
     resolution_change_reset_pending_ = true;
     return;
   }
 
-  // After the output stream is stopped, the codec should not post any
-  // resolution change events. So we dequeue the resolution change event
-  // afterwards. The event could be posted before or while stopping the output
-  // stream. The codec will expect the buffer of new size after the seek, so
-  // we need to handle the resolution change event first.
-  if (!(StopDevicePoll() && StopOutputStream()))
-    return;
-
-  if (DequeueResolutionChangeEvent()) {
-    resolution_change_reset_pending_ = true;
-    StartResolutionChange();
-    return;
-  }
-
-  if (!StopInputStream())
+  // We stop streaming and clear buffer tracking info (not preserving inputs).
+  // StopDevicePoll() unconditionally does _not_ destroy buffers, however.
+  if (!StopDevicePoll(false))
     return;
 
   decoder_current_bitstream_buffer_.reset();
@@ -1370,6 +1356,12 @@
   if (!StartDevicePoll())
     return;
 
+  // We might have received a resolution change event while we were waiting
+  // for the reset to finish. The codec will not post another event if the
+  // resolution after reset remains the same as the one to which were just
+  // about to switch, so preserve the event across reset so we can address
+  // it after resuming.
+
   // Reset format-specific bits.
   if (video_profile_ >= media::H264PROFILE_MIN &&
       video_profile_ <= media::H264PROFILE_MAX) {
@@ -1401,9 +1393,8 @@
 
   // DestroyTask() should run regardless of decoder_state_.
 
-  StopDevicePoll();
-  StopOutputStream();
-  StopInputStream();
+  // Stop streaming and the device_poll_thread_.
+  StopDevicePoll(false);
 
   decoder_current_bitstream_buffer_.reset();
   decoder_current_input_buffer_ = -1;
@@ -1436,12 +1427,8 @@
   return true;
 }
 
-bool V4L2VideoDecodeAccelerator::StopDevicePoll() {
+bool V4L2VideoDecodeAccelerator::StopDevicePoll(bool keep_input_state) {
   DVLOG(3) << "StopDevicePoll()";
-
-  if (!device_poll_thread_.IsRunning())
-    return true;
-
   if (decoder_thread_.IsRunning())
     DCHECK_EQ(decoder_thread_.message_loop(), base::MessageLoop::current());
 
@@ -1457,20 +1444,35 @@
     NOTIFY_ERROR(PLATFORM_FAILURE);
     return false;
   }
-  DVLOG(3) << "StopDevicePoll(): device poll stopped";
-  return true;
-}
 
-bool V4L2VideoDecodeAccelerator::StopOutputStream() {
-  DVLOG(3) << "StopOutputStream()";
-  if (!output_streamon_)
-    return true;
-
-  __u32 type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
-  IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_STREAMOFF, &type);
+  // Stop streaming.
+  if (!keep_input_state) {
+    if (input_streamon_) {
+      __u32 type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+      IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_STREAMOFF, &type);
+    }
+    input_streamon_ = false;
+  }
+  if (output_streamon_) {
+    __u32 type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+    IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_STREAMOFF, &type);
+  }
   output_streamon_ = false;
 
-  // Reset accounting info for output.
+  // Reset all our accounting info.
+  if (!keep_input_state) {
+    while (!input_ready_queue_.empty())
+      input_ready_queue_.pop();
+    free_input_buffers_.clear();
+    for (size_t i = 0; i < input_buffer_map_.size(); ++i) {
+      free_input_buffers_.push_back(i);
+      input_buffer_map_[i].at_device = false;
+      input_buffer_map_[i].bytes_used = 0;
+      input_buffer_map_[i].input_id = -1;
+    }
+    input_buffer_queued_count_ = 0;
+  }
+
   while (!free_output_buffers_.empty())
     free_output_buffers_.pop();
 
@@ -1489,44 +1491,28 @@
     }
   }
   output_buffer_queued_count_ = 0;
+
+  DVLOG(3) << "StopDevicePoll(): device poll stopped";
   return true;
 }
 
-bool V4L2VideoDecodeAccelerator::StopInputStream() {
-  DVLOG(3) << "StopInputStream()";
-  if (!input_streamon_)
-    return true;
-
-  __u32 type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
-  IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_STREAMOFF, &type);
-  input_streamon_ = false;
-
-  // Reset accounting info for input.
-  while (!input_ready_queue_.empty())
-    input_ready_queue_.pop();
-  free_input_buffers_.clear();
-  for (size_t i = 0; i < input_buffer_map_.size(); ++i) {
-    free_input_buffers_.push_back(i);
-    input_buffer_map_[i].at_device = false;
-    input_buffer_map_[i].bytes_used = 0;
-    input_buffer_map_[i].input_id = -1;
-  }
-  input_buffer_queued_count_ = 0;
-
-  return true;
-}
-
-void V4L2VideoDecodeAccelerator::StartResolutionChange() {
+void V4L2VideoDecodeAccelerator::StartResolutionChangeIfNeeded() {
   DCHECK_EQ(decoder_thread_.message_loop(), base::MessageLoop::current());
   DCHECK_NE(decoder_state_, kUninitialized);
   DCHECK_NE(decoder_state_, kResetting);
 
-  DVLOG(3) << "Initiate resolution change";
+  if (!resolution_change_pending_)
+    return;
 
-  if (!(StopDevicePoll() && StopOutputStream()))
+  DVLOG(3) << "No more work, initiate resolution change";
+
+  // Keep input queue.
+  if (!StopDevicePoll(true))
     return;
 
   decoder_state_ = kChangingResolution;
+  DCHECK(resolution_change_pending_);
+  resolution_change_pending_ = false;
 
   // Post a task to clean up buffers on child thread. This will also ensure
   // that we won't accept ReusePictureBuffer() anymore after that.
@@ -1665,7 +1651,10 @@
   DVLOG(3) << "CreateBuffersForFormat(): new resolution: "
            << frame_buffer_size_.ToString();
 
-  return CreateOutputBuffers();
+  if (!CreateOutputBuffers())
+    return false;
+
+  return true;
 }
 
 bool V4L2VideoDecodeAccelerator::CreateInputBuffers() {
@@ -1963,4 +1952,32 @@
   SendPictureReady();
 }
 
+bool V4L2VideoDecodeAccelerator::IsResolutionChangeNecessary() {
+  DVLOG(3) << "IsResolutionChangeNecessary() ";
+
+  struct v4l2_control ctrl;
+  memset(&ctrl, 0, sizeof(ctrl));
+  ctrl.id = V4L2_CID_MIN_BUFFERS_FOR_CAPTURE;
+  IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_G_CTRL, &ctrl);
+  if (ctrl.value != output_dpb_size_) {
+    DVLOG(3)
+        << "IsResolutionChangeNecessary(): Returning true since DPB mismatch ";
+    return true;
+  }
+  struct v4l2_format format;
+  bool again = false;
+  bool ret = GetFormatInfo(&format, &again);
+  if (!ret || again) {
+    DVLOG(3) << "IsResolutionChangeNecessary(): GetFormatInfo() failed";
+    return false;
+  }
+  gfx::Size new_size(base::checked_cast<int>(format.fmt.pix_mp.width),
+                     base::checked_cast<int>(format.fmt.pix_mp.height));
+  if (frame_buffer_size_ != new_size) {
+    DVLOG(3) << "IsResolutionChangeNecessary(): Resolution change detected";
+    return true;
+  }
+  return false;
+}
+
 }  // namespace content
diff --git a/content/common/gpu/media/v4l2_video_decode_accelerator.h b/content/common/gpu/media/v4l2_video_decode_accelerator.h
index ed71198..dbc0286 100644
--- a/content/common/gpu/media/v4l2_video_decode_accelerator.h
+++ b/content/common/gpu/media/v4l2_video_decode_accelerator.h
@@ -83,20 +83,20 @@
       const base::Callback<bool(void)>& make_context_current,
       const scoped_refptr<V4L2Device>& device,
       const scoped_refptr<base::MessageLoopProxy>& io_message_loop_proxy);
-  virtual ~V4L2VideoDecodeAccelerator();
+  ~V4L2VideoDecodeAccelerator() override;
 
   // media::VideoDecodeAccelerator implementation.
   // Note: Initialize() and Destroy() are synchronous.
-  virtual bool Initialize(media::VideoCodecProfile profile,
-                          Client* client) override;
-  virtual void Decode(const media::BitstreamBuffer& bitstream_buffer) override;
-  virtual void AssignPictureBuffers(
+  bool Initialize(media::VideoCodecProfile profile,
+                  Client* client) override;
+  void Decode(const media::BitstreamBuffer& bitstream_buffer) override;
+  void AssignPictureBuffers(
       const std::vector<media::PictureBuffer>& buffers) override;
-  virtual void ReusePictureBuffer(int32 picture_buffer_id) override;
-  virtual void Flush() override;
-  virtual void Reset() override;
-  virtual void Destroy() override;
-  virtual bool CanDecodeOnIOThread() override;
+  void ReusePictureBuffer(int32 picture_buffer_id) override;
+  void Flush() override;
+  void Reset() override;
+  void Destroy() override;
+  bool CanDecodeOnIOThread() override;
 
  private:
   // These are rather subjectively tuned.
@@ -201,10 +201,8 @@
   // Handle the various device queues.
   void Enqueue();
   void Dequeue();
-
-  // Return true if there is a resolution change event pending.
-  bool DequeueResolutionChangeEvent();
-
+  // Handle incoming events.
+  void DequeueEvents();
   // Enqueue a buffer on the corresponding queue.
   bool EnqueueInputRecord();
   bool EnqueueOutputRecord();
@@ -235,16 +233,13 @@
   // Device destruction task.
   void DestroyTask();
 
-  // Start |device_poll_thread_|.
+  // Attempt to start/stop device_poll_thread_.
   bool StartDevicePoll();
+  // If |keep_input_state| is true, don't reset input state; used during
+  // resolution change.
+  bool StopDevicePoll(bool keep_input_state);
 
-  // Stop |device_poll_thread_|.
-  bool StopDevicePoll();
-
-  bool StopInputStream();
-  bool StopOutputStream();
-
-  void StartResolutionChange();
+  void StartResolutionChangeIfNeeded();
   void FinishResolutionChange();
 
   // Try to get output format, detected after parsing the beginning
@@ -303,6 +298,12 @@
   // Callback that indicates a picture has been cleared.
   void PictureCleared();
 
+  // This method determines whether a resolution change event processing
+  // is indeed required by returning true iff:
+  // - width or height of the new format is different than previous format; or
+  // - V4L2_CID_MIN_BUFFERS_FOR_CAPTURE has changed.
+  bool IsResolutionChangeNecessary();
+
   // Our original calling message loop for the child thread.
   scoped_refptr<base::MessageLoopProxy> child_message_loop_proxy_;
 
@@ -355,6 +356,9 @@
   int decoder_frames_at_client_;
   // Are we flushing?
   bool decoder_flushing_;
+  // Got a notification from driver that it reached resolution change point
+  // in the stream.
+  bool resolution_change_pending_;
   // Got a reset request while we were performing resolution change.
   bool resolution_change_reset_pending_;
   // Input queue for decoder_thread_: BitstreamBuffers in.
diff --git a/content/common/gpu/media/v4l2_video_encode_accelerator.h b/content/common/gpu/media/v4l2_video_encode_accelerator.h
index 3be5a83..2e473444 100644
--- a/content/common/gpu/media/v4l2_video_encode_accelerator.h
+++ b/content/common/gpu/media/v4l2_video_encode_accelerator.h
@@ -43,23 +43,23 @@
     : public media::VideoEncodeAccelerator {
  public:
   explicit V4L2VideoEncodeAccelerator(const scoped_refptr<V4L2Device>& device);
-  virtual ~V4L2VideoEncodeAccelerator();
+  ~V4L2VideoEncodeAccelerator() override;
 
   // media::VideoEncodeAccelerator implementation.
-  virtual std::vector<media::VideoEncodeAccelerator::SupportedProfile>
+  std::vector<media::VideoEncodeAccelerator::SupportedProfile>
       GetSupportedProfiles() override;
-  virtual bool Initialize(media::VideoFrame::Format format,
-                          const gfx::Size& input_visible_size,
-                          media::VideoCodecProfile output_profile,
-                          uint32 initial_bitrate,
-                          Client* client) override;
-  virtual void Encode(const scoped_refptr<media::VideoFrame>& frame,
-                      bool force_keyframe) override;
-  virtual void UseOutputBitstreamBuffer(const media::BitstreamBuffer& buffer)
+  bool Initialize(media::VideoFrame::Format format,
+                  const gfx::Size& input_visible_size,
+                  media::VideoCodecProfile output_profile,
+                  uint32 initial_bitrate,
+                  Client* client) override;
+  void Encode(const scoped_refptr<media::VideoFrame>& frame,
+              bool force_keyframe) override;
+  void UseOutputBitstreamBuffer(const media::BitstreamBuffer& buffer)
       override;
-  virtual void RequestEncodingParametersChange(uint32 bitrate,
-                                               uint32 framerate) override;
-  virtual void Destroy() override;
+  void RequestEncodingParametersChange(uint32 bitrate,
+                                       uint32 framerate) override;
+  void Destroy() override;
 
  private:
   // Auto-destroy reference for BitstreamBuffer, for tracking buffers passed to
diff --git a/content/common/gpu/media/video_decode_accelerator_unittest.cc b/content/common/gpu/media/video_decode_accelerator_unittest.cc
index 8f6149d..5f0e3a6 100644
--- a/content/common/gpu/media/video_decode_accelerator_unittest.cc
+++ b/content/common/gpu/media/video_decode_accelerator_unittest.cc
@@ -46,6 +46,7 @@
 #include "base/synchronization/condition_variable.h"
 #include "base/synchronization/lock.h"
 #include "base/synchronization/waitable_event.h"
+#include "base/thread_task_runner_handle.h"
 #include "base/threading/thread.h"
 #include "content/common/gpu/media/fake_video_decode_accelerator.h"
 #include "content/common/gpu/media/rendering_helper.h"
@@ -73,6 +74,7 @@
 #endif  // OS_WIN
 
 #if defined(USE_OZONE)
+#include "ui/ozone/public/ozone_gpu_test_helper.h"
 #include "ui/ozone/public/ozone_platform.h"
 #endif  // defined(USE_OZONE)
 
@@ -227,6 +229,16 @@
     rendering_thread_.task_runner()->PostTask(
         FROM_HERE, base::Bind(&RenderingHelper::InitializeOneOff, &done));
     done.Wait();
+
+#if defined(USE_OZONE)
+    // Need to initialize after the rendering side since the rendering side
+    // initializes the "GPU" parts of Ozone.
+    //
+    // This also needs to be done in the test environment since this shouldn't
+    // be initialized multiple times for the same Ozone platform.
+    gpu_helper_.Initialize(base::ThreadTaskRunnerHandle::Get(),
+                           GetRenderingTaskRunner());
+#endif
   }
 
   void TearDown() override { rendering_thread_.Stop(); }
@@ -237,6 +249,9 @@
 
  private:
   base::Thread rendering_thread_;
+#if defined(USE_OZONE)
+  ui::OzoneGpuTestHelper gpu_helper_;
+#endif
 
   DISALLOW_COPY_AND_ASSIGN(VideoDecodeAcceleratorTestEnvironment);
 };
diff --git a/content/common/gpu/media/video_encode_accelerator_unittest.cc b/content/common/gpu/media/video_encode_accelerator_unittest.cc
index 18054a1..73e319af 100644
--- a/content/common/gpu/media/video_encode_accelerator_unittest.cc
+++ b/content/common/gpu/media/video_encode_accelerator_unittest.cc
@@ -494,9 +494,7 @@
     const FrameFoundCallback& frame_cb) {
   scoped_ptr<StreamValidator> validator;
 
-  if (g_fake_encoder) {
-    validator.reset(NULL);
-  } else if (IsH264(profile)) {
+  if (IsH264(profile)) {
     validator.reset(new H264Validator(frame_cb));
   } else if (IsVP8(profile)) {
     validator.reset(new VP8Validator(frame_cb));
@@ -633,8 +631,11 @@
   // Request a keyframe every keyframe_period_ frames.
   const unsigned int keyframe_period_;
 
-  // Frame number for which we requested a keyframe.
-  unsigned int keyframe_requested_at_;
+  // Number of keyframes requested by now.
+  unsigned int num_keyframes_requested_;
+
+  // Next keyframe expected before next_keyframe_at_ + kMaxKeyframeDelay.
+  unsigned int next_keyframe_at_;
 
   // True if we are asking encoder for a particular bitrate.
   bool force_bitrate_;
@@ -696,7 +697,7 @@
     : state_(CS_CREATED),
       test_stream_(test_stream),
       note_(note),
-      next_input_id_(1),
+      next_input_id_(0),
       next_output_buffer_id_(0),
       pos_in_input_stream_(0),
       num_required_input_buffers_(0),
@@ -707,7 +708,8 @@
       seen_keyframe_in_this_buffer_(false),
       save_to_file_(save_to_file),
       keyframe_period_(keyframe_period),
-      keyframe_requested_at_(kMaxFrameNum),
+      num_keyframes_requested_(0),
+      next_keyframe_at_(kMaxFrameNum),
       force_bitrate_(force_bitrate),
       current_requested_bitrate_(0),
       current_framerate_(0),
@@ -721,12 +723,13 @@
   if (keyframe_period_)
     CHECK_LT(kMaxKeyframeDelay, keyframe_period_);
 
-  validator_ = StreamValidator::Create(
-      test_stream_->requested_profile,
-      base::Bind(&VEAClient::HandleEncodedFrame, base::Unretained(this)));
-
-
-  CHECK(g_fake_encoder || validator_.get());
+  // Fake encoder produces an invalid stream, so skip validating it.
+  if (!g_fake_encoder) {
+    validator_ = StreamValidator::Create(
+        test_stream_->requested_profile,
+        base::Bind(&VEAClient::HandleEncodedFrame, base::Unretained(this)));
+    CHECK(validator_);
+  }
 
   if (save_to_file_) {
     CHECK(!test_stream_->out_filename.empty());
@@ -1050,8 +1053,8 @@
 
   bool force_keyframe = false;
   if (keyframe_period_ && next_input_id_ % keyframe_period_ == 0) {
-    keyframe_requested_at_ = next_input_id_;
     force_keyframe = true;
+    ++num_keyframes_requested_;
   }
 
   scoped_refptr<media::VideoFrame> video_frame =
@@ -1087,12 +1090,6 @@
   ++num_frames_since_last_check_;
 
   last_frame_ready_time_ = base::TimeTicks::Now();
-  if (keyframe) {
-    // Got keyframe, reset keyframe detection regardless of whether we
-    // got a frame in time or not.
-    keyframe_requested_at_ = kMaxFrameNum;
-    seen_keyframe_in_this_buffer_ = true;
-  }
 
   // Because the keyframe behavior requirements are loose, we give
   // the encoder more freedom here. It could either deliver a keyframe
@@ -1104,7 +1101,14 @@
   // earlier than we requested one (in time), and not later than
   // kMaxKeyframeDelay frames after the frame, for which we requested
   // it, comes back encoded.
-  EXPECT_LE(num_encoded_frames_, keyframe_requested_at_ + kMaxKeyframeDelay);
+  EXPECT_LE(num_encoded_frames_, next_keyframe_at_ + kMaxKeyframeDelay);
+
+  if (keyframe) {
+    if (num_keyframes_requested_ > 0)
+      --num_keyframes_requested_;
+    next_keyframe_at_ += keyframe_period_;
+    seen_keyframe_in_this_buffer_ = true;
+  }
 
   if (num_encoded_frames_ == num_frames_to_encode_ / 2) {
     VerifyStreamProperties();
@@ -1152,6 +1156,14 @@
                 current_requested_bitrate_,
                 kBitrateTolerance * current_requested_bitrate_);
   }
+
+  // All requested keyframes should've been provided. Allow the last requested
+  // frame to remain undelivered if we haven't reached the maximum frame number
+  // by which it should have arrived.
+  if (num_encoded_frames_ < next_keyframe_at_  + kMaxKeyframeDelay)
+    EXPECT_LE(num_keyframes_requested_, 1UL);
+  else
+    EXPECT_EQ(num_keyframes_requested_, 0UL);
 }
 
 void VEAClient::WriteIvfFileHeader() {
diff --git a/content/common/gpu/media/vt_video_decode_accelerator.cc b/content/common/gpu/media/vt_video_decode_accelerator.cc
index f1e63bb0..bd26b7a 100644
--- a/content/common/gpu/media/vt_video_decode_accelerator.cc
+++ b/content/common/gpu/media/vt_video_decode_accelerator.cc
@@ -47,24 +47,68 @@
 // reorder queue.)
 static const int kMaxReorderQueueSize = 16;
 
+// Logged to UMA, so never reuse values. Make sure to update
+// VTVDAInitializationFailureType in histograms.xml to match.
+enum VTVDAInitializationFailureType {
+  IFT_SUCCESSFULLY_INITIALIZED = 0,
+  IFT_FRAMEWORK_LOAD_ERROR = 1,
+  IFT_HARDWARE_SESSION_ERROR = 2,
+  IFT_SOFTWARE_SESSION_ERROR = 3,
+  // Must always be equal to largest entry logged.
+  IFT_MAX = IFT_SOFTWARE_SESSION_ERROR
+};
+
+// Logged to UMA, so never reuse values. Make sure to update
+// VTVDASessionFailureType in histograms.xml to match.
+enum VTVDASessionFailureType {
+  SFT_SUCCESSFULLY_INITIALIZED = 0,
+  SFT_PLATFORM_ERROR = 1,
+  SFT_INVALID_STREAM = 2,
+  SFT_UNSUPPORTED_STREAM = 3,
+  SFT_DECODE_ERROR = 4,
+  // Must always be equal to largest entry logged.
+  SFT_MAX = SFT_DECODE_ERROR
+};
+
+static void ReportInitializationFailure(
+    VTVDAInitializationFailureType failure_type) {
+  DCHECK_LT(failure_type, IFT_MAX);
+  UMA_HISTOGRAM_ENUMERATION("Media.VTVDA.InitializationFailureReason",
+                            failure_type,
+                            IFT_MAX + 1);
+}
+
+static void ReportSessionFailure(VTVDASessionFailureType failure_type) {
+  DCHECK_LT(failure_type, SFT_MAX);
+  UMA_HISTOGRAM_ENUMERATION("Media.VTVDA.SessionFailureReason",
+                            failure_type,
+                            SFT_MAX + 1);
+}
+
 // Build an |image_config| dictionary for VideoToolbox initialization.
 static base::ScopedCFTypeRef<CFMutableDictionaryRef>
 BuildImageConfig(CMVideoDimensions coded_dimensions) {
+  base::ScopedCFTypeRef<CFMutableDictionaryRef> image_config;
+
   // TODO(sandersd): Does it save some work or memory to use 4:2:0?
   int32_t pixel_format = kCVPixelFormatType_422YpCbCr8;
-
 #define CFINT(i) CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &i)
   base::ScopedCFTypeRef<CFNumberRef> cf_pixel_format(CFINT(pixel_format));
   base::ScopedCFTypeRef<CFNumberRef> cf_width(CFINT(coded_dimensions.width));
   base::ScopedCFTypeRef<CFNumberRef> cf_height(CFINT(coded_dimensions.height));
 #undef CFINT
+  if (!cf_pixel_format.get() || !cf_width.get() || !cf_height.get())
+    return image_config;
 
-  base::ScopedCFTypeRef<CFMutableDictionaryRef> image_config(
+  image_config.reset(
       CFDictionaryCreateMutable(
           kCFAllocatorDefault,
           4,  // capacity
           &kCFTypeDictionaryKeyCallBacks,
           &kCFTypeDictionaryValueCallBacks));
+  if (!image_config.get())
+    return image_config;
+
   CFDictionarySetValue(image_config, kCVPixelBufferPixelFormatTypeKey,
                        cf_pixel_format);
   CFDictionarySetValue(image_config, kCVPixelBufferWidthKey, cf_width);
@@ -106,6 +150,8 @@
           1,  // capacity
           &kCFTypeDictionaryKeyCallBacks,
           &kCFTypeDictionaryValueCallBacks));
+  if (!decoder_config.get())
+    return false;
 
   if (require_hardware) {
     CFDictionarySetValue(
@@ -117,6 +163,8 @@
 
   base::ScopedCFTypeRef<CFMutableDictionaryRef> image_config(
       BuildImageConfig(CMVideoFormatDescriptionGetDimensions(format)));
+  if (!image_config.get())
+    return false;
 
   VTDecompressionOutputCallbackRecord callback = {0};
 
@@ -129,6 +177,8 @@
       &callback,            // output_callback
       session.InitializeInto());
   if (status) {
+    ReportInitializationFailure(require_hardware ? IFT_HARDWARE_SESSION_ERROR
+                                                 : IFT_SOFTWARE_SESSION_ERROR);
     OSSTATUS_LOG(ERROR, status) << "Failed to create VTDecompressionSession";
     return false;
   }
@@ -156,8 +206,12 @@
     StubPathMap paths;
     paths[kModuleVt].push_back(FILE_PATH_LITERAL(
         "/System/Library/Frameworks/VideoToolbox.framework/VideoToolbox"));
-    if (!InitializeStubs(paths))
+    if (!InitializeStubs(paths)) {
+      ReportInitializationFailure(IFT_FRAMEWORK_LOAD_ERROR);
+      LOG(ERROR) << "Failed to initialize VideoToobox framework. "
+                 << "Hardware accelerated video decoding will be disabled.";
       return false;
+    }
   }
 
   // Create a hardware decoding session.
@@ -168,6 +222,8 @@
   const uint8_t pps_normal[] = {0x68, 0xe9, 0x7b, 0xcb};
   if (!CreateVideoToolboxSession(sps_normal, arraysize(sps_normal), pps_normal,
                                  arraysize(pps_normal), true)) {
+    LOG(ERROR) << "Failed to create hardware VideoToolbox session. "
+               << "Hardware accelerated video decoding will be disabled.";
     return false;
   }
 
@@ -179,15 +235,19 @@
   const uint8_t pps_small[] = {0x68, 0xe9, 0x79, 0x72, 0xc0};
   if (!CreateVideoToolboxSession(sps_small, arraysize(sps_small), pps_small,
                                  arraysize(pps_small), false)) {
+    LOG(ERROR) << "Failed to create software VideoToolbox session. "
+               << "Hardware accelerated video decoding will be disabled.";
     return false;
   }
 
+  ReportInitializationFailure(IFT_SUCCESSFULLY_INITIALIZED);
   return true;
 }
 
 bool InitializeVideoToolbox() {
-  // InitializeVideoToolbox() is called during GPU process sandbox warmup,
-  // and then only from the GPU process main thread.
+  // InitializeVideoToolbox() is called only from the GPU process main thread;
+  // once for sandbox warmup, and then once each time a VTVideoDecodeAccelerator
+  // is initialized.
   static bool attempted = false;
   static bool succeeded = false;
 
@@ -243,10 +303,10 @@
     const base::Callback<bool(void)>& make_context_current)
     : cgl_context_(cgl_context),
       make_context_current_(make_context_current),
-      client_(NULL),
+      client_(nullptr),
       state_(STATE_DECODING),
-      format_(NULL),
-      session_(NULL),
+      format_(nullptr),
+      session_(nullptr),
       last_sps_id_(-1),
       last_pps_id_(-1),
       gpu_task_runner_(base::ThreadTaskRunnerHandle::Get()),
@@ -282,6 +342,7 @@
   if (!decoder_thread_.Start())
     return false;
 
+  ReportSessionFailure(SFT_SUCCESSFULLY_INITIALIZED);
   return true;
 }
 
@@ -290,6 +351,7 @@
   if (session_) {
     OSStatus status = VTDecompressionSessionWaitForAsynchronousFrames(session_);
     if (status) {
+      ReportSessionFailure(SFT_PLATFORM_ERROR);
       NOTIFY_STATUS("VTDecompressionSessionWaitForAsynchronousFrames()",
                     status);
       return false;
@@ -328,6 +390,7 @@
       kNALUHeaderLength,          // nal_unit_header_length
       format_.InitializeInto());
   if (status) {
+    ReportSessionFailure(SFT_PLATFORM_ERROR);
     NOTIFY_STATUS("CMVideoFormatDescriptionCreateFromH264ParameterSets()",
                   status);
     return false;
@@ -351,6 +414,12 @@
           1,  // capacity
           &kCFTypeDictionaryKeyCallBacks,
           &kCFTypeDictionaryValueCallBacks));
+  if (!decoder_config.get()) {
+    ReportSessionFailure(SFT_PLATFORM_ERROR);
+    DLOG(ERROR) << "Failed to create CFMutableDictionary.";
+    NotifyError(PLATFORM_FAILURE);
+    return false;
+  }
 
   CFDictionarySetValue(
       decoder_config,
@@ -360,9 +429,18 @@
 
   base::ScopedCFTypeRef<CFMutableDictionaryRef> image_config(
       BuildImageConfig(coded_dimensions));
+  if (!image_config.get()) {
+    ReportSessionFailure(SFT_PLATFORM_ERROR);
+    DLOG(ERROR) << "Failed to create decoder image configuration.";
+    NotifyError(PLATFORM_FAILURE);
+    return false;
+  }
 
+  // Ensure that the old decoder emits all frames before the new decoder can
+  // emit any.
   if (!FinishDelayedFrames())
     return false;
+
   session_.reset();
   status = VTDecompressionSessionCreate(
       kCFAllocatorDefault,
@@ -372,23 +450,23 @@
       &callback_,           // output_callback
       session_.InitializeInto());
   if (status) {
+    ReportSessionFailure(SFT_UNSUPPORTED_STREAM);
     NOTIFY_STATUS("VTDecompressionSessionCreate()", status);
     return false;
   }
 
   // Report whether hardware decode is being used.
-  base::ScopedCFTypeRef<CFBooleanRef> using_hardware;
+  bool using_hardware = false;
+  base::ScopedCFTypeRef<CFBooleanRef> cf_using_hardware;
   if (VTSessionCopyProperty(
           session_,
           // kVTDecompressionPropertyKey_UsingHardwareAcceleratedVideoDecoder
           CFSTR("UsingHardwareAcceleratedVideoDecoder"),
           kCFAllocatorDefault,
-          using_hardware.InitializeInto()) == 0) {
-    UMA_HISTOGRAM_BOOLEAN("Media.VTVDA.HardwareAccelerated",
-                          CFBooleanGetValue(using_hardware));
-  } else {
-    UMA_HISTOGRAM_BOOLEAN("Media.VTVDA.HardwareAccelerated", false);
+          cf_using_hardware.InitializeInto()) == 0) {
+    using_hardware = CFBooleanGetValue(cf_using_hardware);
   }
+  UMA_HISTOGRAM_BOOLEAN("Media.VTVDA.HardwareAccelerated", using_hardware);
 
   return true;
 }
@@ -402,6 +480,7 @@
   base::SharedMemory memory(bitstream.handle(), true);
   size_t size = bitstream.size();
   if (!memory.Map(size)) {
+    ReportSessionFailure(SFT_PLATFORM_ERROR);
     DLOG(ERROR) << "Failed to map bitstream buffer";
     NotifyError(PLATFORM_FAILURE);
     return;
@@ -425,6 +504,7 @@
     if (result == media::H264Parser::kEOStream)
       break;
     if (result != media::H264Parser::kOk) {
+      ReportSessionFailure(SFT_INVALID_STREAM);
       DLOG(ERROR) << "Failed to find H.264 NALU";
       NotifyError(UNREADABLE_INPUT);
       return;
@@ -435,6 +515,7 @@
         last_spsext_.clear();
         config_changed = true;
         if (parser_.ParseSPS(&last_sps_id_) != media::H264Parser::kOk) {
+          ReportSessionFailure(SFT_INVALID_STREAM);
           DLOG(ERROR) << "Could not parse SPS";
           NotifyError(UNREADABLE_INPUT);
           return;
@@ -451,6 +532,7 @@
         last_pps_.assign(nalu.data, nalu.data + nalu.size);
         config_changed = true;
         if (parser_.ParsePPS(&last_pps_id_) != media::H264Parser::kOk) {
+          ReportSessionFailure(SFT_INVALID_STREAM);
           DLOG(ERROR) << "Could not parse PPS";
           NotifyError(UNREADABLE_INPUT);
           return;
@@ -471,6 +553,7 @@
           media::H264SliceHeader slice_hdr;
           result = parser_.ParseSliceHeader(nalu, &slice_hdr);
           if (result != media::H264Parser::kOk) {
+            ReportSessionFailure(SFT_INVALID_STREAM);
             DLOG(ERROR) << "Could not parse slice header";
             NotifyError(UNREADABLE_INPUT);
             return;
@@ -482,6 +565,7 @@
           const media::H264PPS* pps =
               parser_.GetPPS(slice_hdr.pic_parameter_set_id);
           if (!pps) {
+            ReportSessionFailure(SFT_INVALID_STREAM);
             DLOG(ERROR) << "Mising PPS referenced by slice";
             NotifyError(UNREADABLE_INPUT);
             return;
@@ -490,12 +574,15 @@
           DCHECK_EQ(pps->seq_parameter_set_id, last_sps_id_);
           const media::H264SPS* sps = parser_.GetSPS(pps->seq_parameter_set_id);
           if (!sps) {
+            ReportSessionFailure(SFT_INVALID_STREAM);
             DLOG(ERROR) << "Mising SPS referenced by PPS";
             NotifyError(UNREADABLE_INPUT);
             return;
           }
 
           if (!poc_.ComputePicOrderCnt(sps, slice_hdr, &frame->pic_order_cnt)) {
+            ReportSessionFailure(SFT_INVALID_STREAM);
+            DLOG(ERROR) << "Unable to compute POC";
             NotifyError(UNREADABLE_INPUT);
             return;
           }
@@ -520,6 +607,7 @@
   // select from them using the slice header.
   if (config_changed) {
     if (last_sps_.size() == 0 || last_pps_.size() == 0) {
+      ReportSessionFailure(SFT_INVALID_STREAM);
       DLOG(ERROR) << "Invalid configuration data";
       NotifyError(INVALID_ARGUMENT);
       return;
@@ -540,7 +628,8 @@
 
   // If the session is not configured by this point, fail.
   if (!session_) {
-    DLOG(ERROR) << "Image slice without configuration";
+    ReportSessionFailure(SFT_INVALID_STREAM);
+    DLOG(ERROR) << "Configuration data missing";
     NotifyError(INVALID_ARGUMENT);
     return;
   }
@@ -553,15 +642,16 @@
   base::ScopedCFTypeRef<CMBlockBufferRef> data;
   OSStatus status = CMBlockBufferCreateWithMemoryBlock(
       kCFAllocatorDefault,
-      NULL,                 // &memory_block
+      nullptr,              // &memory_block
       data_size,            // block_length
       kCFAllocatorDefault,  // block_allocator
-      NULL,                 // &custom_block_source
+      nullptr,              // &custom_block_source
       0,                    // offset_to_data
       data_size,            // data_length
       0,                    // flags
       data.InitializeInto());
   if (status) {
+    ReportSessionFailure(SFT_PLATFORM_ERROR);
     NOTIFY_STATUS("CMBlockBufferCreateWithMemoryBlock()", status);
     return;
   }
@@ -574,12 +664,14 @@
     status = CMBlockBufferReplaceDataBytes(
         &header, data, offset, kNALUHeaderLength);
     if (status) {
+      ReportSessionFailure(SFT_PLATFORM_ERROR);
       NOTIFY_STATUS("CMBlockBufferReplaceDataBytes()", status);
       return;
     }
     offset += kNALUHeaderLength;
     status = CMBlockBufferReplaceDataBytes(nalu.data, data, offset, nalu.size);
     if (status) {
+      ReportSessionFailure(SFT_PLATFORM_ERROR);
       NOTIFY_STATUS("CMBlockBufferReplaceDataBytes()", status);
       return;
     }
@@ -592,16 +684,17 @@
       kCFAllocatorDefault,
       data,                 // data_buffer
       true,                 // data_ready
-      NULL,                 // make_data_ready_callback
-      NULL,                 // make_data_ready_refcon
+      nullptr,              // make_data_ready_callback
+      nullptr,              // make_data_ready_refcon
       format_,              // format_description
       1,                    // num_samples
       0,                    // num_sample_timing_entries
-      NULL,                 // &sample_timing_array
+      nullptr,              // &sample_timing_array
       0,                    // num_sample_size_entries
-      NULL,                 // &sample_size_array
+      nullptr,              // &sample_size_array
       sample.InitializeInto());
   if (status) {
+    ReportSessionFailure(SFT_PLATFORM_ERROR);
     NOTIFY_STATUS("CMSampleBufferCreate()", status);
     return;
   }
@@ -618,8 +711,9 @@
       sample,                                 // sample_buffer
       decode_flags,                           // decode_flags
       reinterpret_cast<void*>(frame),         // source_frame_refcon
-      NULL);                                  // &info_flags_out
+      nullptr);                               // &info_flags_out
   if (status) {
+    ReportSessionFailure(SFT_DECODE_ERROR);
     NOTIFY_STATUS("VTDecompressionSessionDecodeFrame()", status);
     return;
   }
@@ -631,6 +725,7 @@
     OSStatus status,
     CVImageBufferRef image_buffer) {
   if (status) {
+    ReportSessionFailure(SFT_DECODE_ERROR);
     NOTIFY_STATUS("Decoding", status);
     return;
   }
@@ -644,6 +739,7 @@
   // smoothly handle NULL as a dropped frame, we choose to fail permanantly here
   // until the issue is better understood.
   if (!image_buffer || CFGetTypeID(image_buffer) != CVPixelBufferGetTypeID()) {
+    ReportSessionFailure(SFT_DECODE_ERROR);
     DLOG(ERROR) << "Decoded frame is not a CVPixelBuffer";
     NotifyError(PLATFORM_FAILURE);
     return;
@@ -798,6 +894,7 @@
       return false;
 
     case TASK_DESTROY:
+      ReportSessionFailure(SFT_PLATFORM_ERROR);
       NOTREACHED() << "Can't destroy while in STATE_DECODING.";
       NotifyError(ILLEGAL_STATE);
       return false;
@@ -875,6 +972,7 @@
   IOSurfaceRef surface = CVPixelBufferGetIOSurface(frame.image.get());
 
   if (!make_context_current_.Run()) {
+    ReportSessionFailure(SFT_PLATFORM_ERROR);
     DLOG(ERROR) << "Failed to make GL context current";
     NotifyError(PLATFORM_FAILURE);
     return false;
@@ -894,6 +992,7 @@
       surface,                      // io_surface
       0);                           // plane
   if (status != kCGLNoError) {
+    ReportSessionFailure(SFT_PLATFORM_ERROR);
     NOTIFY_STATUS("CGLTexImageIOSurface2D()", status);
     return false;
   }
diff --git a/content/common/host_discardable_shared_memory_manager.cc b/content/common/host_discardable_shared_memory_manager.cc
index 6f2dead..c428bc5 100644
--- a/content/common/host_discardable_shared_memory_manager.cc
+++ b/content/common/host_discardable_shared_memory_manager.cc
@@ -229,8 +229,8 @@
 
 void HostDiscardableSharedMemoryManager::BytesAllocatedChanged(
     size_t new_bytes_allocated) const {
-  TRACE_COUNTER_ID1(
-      "base", "TotalDiscardableMemoryUsage", this, new_bytes_allocated);
+  TRACE_COUNTER_ID1("renderer_host", "TotalDiscardableMemoryUsage", this,
+                    new_bytes_allocated);
 
   static const char kTotalDiscardableMemoryUsageKey[] = "total-dm-usage";
   base::debug::SetCrashKeyValue(kTotalDiscardableMemoryUsageKey,
diff --git a/content/common/indexed_db/indexed_db_key.cc b/content/common/indexed_db/indexed_db_key.cc
index 4c553ca..1a3d72ec 100644
--- a/content/common/indexed_db/indexed_db_key.cc
+++ b/content/common/indexed_db/indexed_db_key.cc
@@ -92,7 +92,9 @@
       size_estimate_(kOverheadSize +
                      (string.length() * sizeof(base::string16::value_type))) {}
 
-IndexedDBKey::~IndexedDBKey() {}
+IndexedDBKey::IndexedDBKey(const IndexedDBKey& other) = default;
+IndexedDBKey::~IndexedDBKey() = default;
+IndexedDBKey& IndexedDBKey::operator=(const IndexedDBKey& other) = default;
 
 bool IndexedDBKey::IsValid() const {
   if (type_ == WebIDBKeyTypeInvalid || type_ == WebIDBKeyTypeNull)
diff --git a/content/common/indexed_db/indexed_db_key.h b/content/common/indexed_db/indexed_db_key.h
index bb02770..d9dfa1e 100644
--- a/content/common/indexed_db/indexed_db_key.h
+++ b/content/common/indexed_db/indexed_db_key.h
@@ -31,7 +31,9 @@
   explicit IndexedDBKey(const base::string16& string);
   IndexedDBKey(double number,
                blink::WebIDBKeyType type);  // must be date or number
+  IndexedDBKey(const IndexedDBKey& other);
   ~IndexedDBKey();
+  IndexedDBKey& operator=(const IndexedDBKey& other);
 
   bool IsValid() const;
 
diff --git a/content/common/indexed_db/indexed_db_key_path.cc b/content/common/indexed_db/indexed_db_key_path.cc
index 3783d42..a5756b5 100644
--- a/content/common/indexed_db/indexed_db_key_path.cc
+++ b/content/common/indexed_db/indexed_db_key_path.cc
@@ -20,7 +20,10 @@
 IndexedDBKeyPath::IndexedDBKeyPath(const std::vector<base::string16>& array)
     : type_(WebIDBKeyPathTypeArray), array_(array) {}
 
-IndexedDBKeyPath::~IndexedDBKeyPath() {}
+IndexedDBKeyPath::IndexedDBKeyPath(const IndexedDBKeyPath& other) = default;
+IndexedDBKeyPath::~IndexedDBKeyPath() = default;
+IndexedDBKeyPath& IndexedDBKeyPath::operator=(const IndexedDBKeyPath& other) =
+    default;
 
 const std::vector<base::string16>& IndexedDBKeyPath::array() const {
   DCHECK(type_ == blink::WebIDBKeyPathTypeArray);
diff --git a/content/common/indexed_db/indexed_db_key_path.h b/content/common/indexed_db/indexed_db_key_path.h
index b9387a4..74297e0d 100644
--- a/content/common/indexed_db/indexed_db_key_path.h
+++ b/content/common/indexed_db/indexed_db_key_path.h
@@ -20,7 +20,9 @@
   IndexedDBKeyPath();  // Defaults to blink::WebIDBKeyPathTypeNull.
   explicit IndexedDBKeyPath(const base::string16&);
   explicit IndexedDBKeyPath(const std::vector<base::string16>&);
+  IndexedDBKeyPath(const IndexedDBKeyPath& other);
   ~IndexedDBKeyPath();
+  IndexedDBKeyPath& operator=(const IndexedDBKeyPath& other);
 
   bool IsNull() const { return type_ == blink::WebIDBKeyPathTypeNull; }
   bool operator==(const IndexedDBKeyPath& other) const;
diff --git a/content/common/indexed_db/indexed_db_key_range.cc b/content/common/indexed_db/indexed_db_key_range.cc
index 47e0cf1..d061363 100644
--- a/content/common/indexed_db/indexed_db_key_range.cc
+++ b/content/common/indexed_db/indexed_db_key_range.cc
@@ -27,7 +27,10 @@
 IndexedDBKeyRange::IndexedDBKeyRange(const IndexedDBKey& key)
     : lower_(key), upper_(key), lower_open_(false), upper_open_(false) {}
 
-IndexedDBKeyRange::~IndexedDBKeyRange() {}
+IndexedDBKeyRange::IndexedDBKeyRange(const IndexedDBKeyRange& other) = default;
+IndexedDBKeyRange::~IndexedDBKeyRange() = default;
+IndexedDBKeyRange& IndexedDBKeyRange::operator=(
+    const IndexedDBKeyRange& other) = default;
 
 bool IndexedDBKeyRange::IsOnlyKey() const {
   if (lower_open_ || upper_open_)
diff --git a/content/common/indexed_db/indexed_db_key_range.h b/content/common/indexed_db/indexed_db_key_range.h
index e8d690cb..7dce7c6d 100644
--- a/content/common/indexed_db/indexed_db_key_range.h
+++ b/content/common/indexed_db/indexed_db_key_range.h
@@ -19,7 +19,9 @@
                     const IndexedDBKey& upper,
                     bool lower_open,
                     bool upper_open);
+  IndexedDBKeyRange(const IndexedDBKeyRange& other);
   ~IndexedDBKeyRange();
+  IndexedDBKeyRange& operator=(const IndexedDBKeyRange& other);
 
   const IndexedDBKey& lower() const { return lower_; }
   const IndexedDBKey& upper() const { return upper_; }
diff --git a/content/common/media/midi_messages.h b/content/common/media/midi_messages.h
index 6f314b6..6147c8ae 100644
--- a/content/common/media/midi_messages.h
+++ b/content/common/media/midi_messages.h
@@ -17,11 +17,15 @@
 #define IPC_MESSAGE_EXPORT CONTENT_EXPORT
 #define IPC_MESSAGE_START MidiMsgStart
 
+IPC_ENUM_TRAITS_MAX_VALUE(media::MidiPortState,
+                          media::MIDI_PORT_STATE_LAST)
+
 IPC_STRUCT_TRAITS_BEGIN(media::MidiPortInfo)
   IPC_STRUCT_TRAITS_MEMBER(id)
   IPC_STRUCT_TRAITS_MEMBER(manufacturer)
   IPC_STRUCT_TRAITS_MEMBER(name)
   IPC_STRUCT_TRAITS_MEMBER(version)
+  IPC_STRUCT_TRAITS_MEMBER(state)
 IPC_STRUCT_TRAITS_END()
 
 IPC_ENUM_TRAITS_MAX_VALUE(media::MidiResult, media::MIDI_RESULT_LAST)
@@ -46,6 +50,14 @@
 IPC_MESSAGE_CONTROL1(MidiMsg_AddOutputPort,
                      media::MidiPortInfo /* output port */)
 
+IPC_MESSAGE_CONTROL2(MidiMsg_SetInputPortState,
+                     uint32 /* port */,
+                     media::MidiPortState /* state */)
+
+IPC_MESSAGE_CONTROL2(MidiMsg_SetOutputPortState,
+                     uint32 /* port */,
+                     media::MidiPortState /* state */)
+
 IPC_MESSAGE_CONTROL1(MidiMsg_SessionStarted,
                      media::MidiResult /* result */)
 
diff --git a/content/common/pepper_renderer_instance_data.cc b/content/common/pepper_renderer_instance_data.cc
index 6fda4bf..6b0446b 100644
--- a/content/common/pepper_renderer_instance_data.cc
+++ b/content/common/pepper_renderer_instance_data.cc
@@ -7,15 +7,13 @@
 namespace content {
 
 PepperRendererInstanceData::PepperRendererInstanceData()
-    : render_process_id(0),
-      render_frame_id(0) {
+    : render_process_id(0), render_frame_id(0) {
 }
 
-PepperRendererInstanceData::PepperRendererInstanceData(
-    int render_process,
-    int render_frame,
-    const GURL& document,
-    const GURL& plugin)
+PepperRendererInstanceData::PepperRendererInstanceData(int render_process,
+                                                       int render_frame,
+                                                       const GURL& document,
+                                                       const GURL& plugin)
     : render_process_id(render_process),
       render_frame_id(render_frame),
       document_url(document),
diff --git a/content/common/service_worker/service_worker_messages.h b/content/common/service_worker/service_worker_messages.h
index a9613b9..31b5281b 100644
--- a/content/common/service_worker/service_worker_messages.h
+++ b/content/common/service_worker/service_worker_messages.h
@@ -230,6 +230,16 @@
                     base::string16 /* message */,
                     std::vector<int> /* sent_message_port_ids */)
 
+// ServiceWorker -> Browser message to request that the ServiceWorkerStorage
+// cache |data| associated with |url|.
+IPC_MESSAGE_ROUTED2(ServiceWorkerHostMsg_SetCachedMetadata,
+                    GURL /* url */,
+                    std::vector<char> /* data */)
+
+// ServiceWorker -> Browser message to request that the ServiceWorkerStorage
+// clear the cache associated with |url|.
+IPC_MESSAGE_ROUTED1(ServiceWorkerHostMsg_ClearCachedMetadata, GURL /* url */)
+
 // Ask the browser to open a tab/window (renderer->browser).
 IPC_MESSAGE_ROUTED2(ServiceWorkerHostMsg_OpenWindow,
                     int /* request_id */,
diff --git a/content/common/view_messages.h b/content/common/view_messages.h
index 77c2d73..f314706 100644
--- a/content/common/view_messages.h
+++ b/content/common/view_messages.h
@@ -224,6 +224,7 @@
   IPC_STRUCT_TRAITS_MEMBER(use_custom_colors)
   IPC_STRUCT_TRAITS_MEMBER(enable_referrers)
   IPC_STRUCT_TRAITS_MEMBER(enable_do_not_track)
+  IPC_STRUCT_TRAITS_MEMBER(enable_webrtc_multiple_routes)
   IPC_STRUCT_TRAITS_MEMBER(default_zoom_level)
   IPC_STRUCT_TRAITS_MEMBER(user_agent_override)
   IPC_STRUCT_TRAITS_MEMBER(accept_languages)
@@ -1357,6 +1358,13 @@
                     int /* routing_id */,
                     GURL /* document_url */,
                     base::FilePath /* plugin_path */)
+
+// A renderer sends this to the browser process when it throttles or unthrottles
+// a plugin instance for the Plugin Power Saver feature.
+IPC_MESSAGE_CONTROL3(ViewHostMsg_PluginInstanceThrottleStateChange,
+                     int /* plugin_child_id */,
+                     int32 /* pp_instance */,
+                     bool /* is_throttled */)
 #endif  // defined(ENABLE_PLUGINS)
 
 // Send the tooltip text for the current mouse position to the browser.
@@ -1461,10 +1469,10 @@
                             std::string /* signed public key and challenge */)
 
 // Message sent from the renderer to the browser to request that the browser
-// cache |data| associated with |url|.
+// cache |data| associated with |url| and |expected_response_time|.
 IPC_MESSAGE_CONTROL3(ViewHostMsg_DidGenerateCacheableMetadata,
                      GURL /* url */,
-                     double /* expected_response_time */,
+                     base::Time /* expected_response_time */,
                      std::vector<char> /* data */)
 
 // Register a new handler for URL requests with the given scheme.
diff --git a/content/content_browser.gypi b/content/content_browser.gypi
index bc3a996..26eadef 100644
--- a/content/content_browser.gypi
+++ b/content/content_browser.gypi
@@ -647,6 +647,8 @@
       'browser/frame_host/debug_urls.h',
       'browser/frame_host/frame_accessibility.cc',
       'browser/frame_host/frame_accessibility.h',
+      'browser/frame_host/frame_navigation_entry.cc',
+      'browser/frame_host/frame_navigation_entry.h',
       'browser/frame_host/frame_tree.cc',
       'browser/frame_host/frame_tree.h',
       'browser/frame_host/frame_tree_node.cc',
@@ -822,6 +824,7 @@
       'browser/indexed_db/indexed_db_database.h',
       'browser/indexed_db/indexed_db_database_callbacks.cc',
       'browser/indexed_db/indexed_db_database_callbacks.h',
+      'browser/indexed_db/indexed_db_database_error.cc',
       'browser/indexed_db/indexed_db_database_error.h',
       'browser/indexed_db/indexed_db_dispatcher_host.cc',
       'browser/indexed_db/indexed_db_dispatcher_host.h',
@@ -1508,8 +1511,6 @@
     'compositor_browser_sources': [
       'browser/compositor/browser_compositor_output_surface.cc',
       'browser/compositor/browser_compositor_output_surface.h',
-      'browser/compositor/browser_compositor_output_surface_proxy.cc',
-      'browser/compositor/browser_compositor_output_surface_proxy.h',
       'browser/compositor/browser_compositor_view_mac.h',
       'browser/compositor/browser_compositor_view_mac.mm',
       'browser/compositor/buffer_queue.cc',
diff --git a/content/content_child.gypi b/content/content_child.gypi
index 0456378..4ea20ba 100644
--- a/content/content_child.gypi
+++ b/content/content_child.gypi
@@ -119,6 +119,8 @@
       'child/notifications/notification_image_loader.h',
       'child/notifications/notification_manager.cc',
       'child/notifications/notification_manager.h',
+      'child/notifications/pending_notifications_tracker.cc',
+      'child/notifications/pending_notifications_tracker.h',
       'child/npapi/np_channel_base.cc',
       'child/npapi/np_channel_base.h',
       'child/npapi/npobject_base.h',
@@ -182,8 +184,6 @@
       'child/request_info.h',
       'child/resource_dispatcher.cc',
       'child/resource_dispatcher.h',
-      'child/resource_loader_bridge.cc',
-      'child/resource_loader_bridge.h',
       'child/resource_scheduling_filter.cc',
       'child/resource_scheduling_filter.h',
       'child/runtime_features.cc',
diff --git a/content/content_renderer.gypi b/content/content_renderer.gypi
index c4b16724..0150728 100644
--- a/content/content_renderer.gypi
+++ b/content/content_renderer.gypi
@@ -472,6 +472,8 @@
       'renderer/pepper/pepper_broker.h',
       'renderer/pepper/pepper_browser_connection.cc',
       'renderer/pepper/pepper_browser_connection.h',
+      'renderer/pepper/pepper_camera_device_host.cc',
+      'renderer/pepper/pepper_camera_device_host.h',
       'renderer/pepper/pepper_compositor_host.cc',
       'renderer/pepper/pepper_compositor_host.h',
       'renderer/pepper/pepper_device_enumeration_host_helper.cc',
@@ -486,8 +488,6 @@
       'renderer/pepper/pepper_graphics_2d_host.h',
       'renderer/pepper/pepper_hung_plugin_filter.cc',
       'renderer/pepper/pepper_hung_plugin_filter.h',
-      'renderer/pepper/pepper_image_capture_host.cc',
-      'renderer/pepper/pepper_image_capture_host.h',
       'renderer/pepper/pepper_in_process_resource_creation.cc',
       'renderer/pepper/pepper_in_process_resource_creation.h',
       'renderer/pepper/pepper_in_process_router.cc',
@@ -498,8 +498,8 @@
       'renderer/pepper/pepper_platform_audio_input.h',
       'renderer/pepper/pepper_platform_audio_output.cc',
       'renderer/pepper/pepper_platform_audio_output.h',
-      'renderer/pepper/pepper_platform_image_capture.cc',
-      'renderer/pepper/pepper_platform_image_capture.h',
+      'renderer/pepper/pepper_platform_camera_device.cc',
+      'renderer/pepper/pepper_platform_camera_device.h',
       'renderer/pepper/pepper_platform_video_capture.cc',
       'renderer/pepper/pepper_platform_video_capture.h',
       'renderer/pepper/pepper_plugin_instance_impl.cc',
diff --git a/content/content_tests.gypi b/content/content_tests.gypi
index e17c7e22..92ec2e6 100644
--- a/content/content_tests.gypi
+++ b/content/content_tests.gypi
@@ -245,6 +245,7 @@
       'browser/webkit_browsertest.cc',
       'browser/webui/web_ui_mojo_browsertest.cc',
       'child/site_isolation_policy_browsertest.cc',
+      'child/child_discardable_shared_memory_manager_browsertest.cc',
       'renderer/accessibility/renderer_accessibility_browsertest.cc',
       'renderer/devtools/v8_sampling_profiler_browsertest.cc',
       'renderer/gin_browsertest.cc',
diff --git a/content/gpu/BUILD.gn b/content/gpu/BUILD.gn
index 1056468..d43bb82 100644
--- a/content/gpu/BUILD.gn
+++ b/content/gpu/BUILD.gn
@@ -43,7 +43,7 @@
     ]
   }
 
-  if (is_chromeos && cpu_arch != "arm") {
+  if (is_chromeos && current_cpu != "arm") {
     configs += [ "//third_party/libva:libva_config" ]
   }
 
diff --git a/content/gpu/gpu_main.cc b/content/gpu/gpu_main.cc
index c91aac0..f73bb0d1 100644
--- a/content/gpu/gpu_main.cc
+++ b/content/gpu/gpu_main.cc
@@ -15,6 +15,7 @@
 #include "base/rand_util.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/stringprintf.h"
+#include "base/third_party/dynamic_annotations/dynamic_annotations.h"
 #include "base/threading/platform_thread.h"
 #include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
diff --git a/content/gpu/gpu_watchdog_thread.cc b/content/gpu/gpu_watchdog_thread.cc
index 02b6bff..6a6f459 100644
--- a/content/gpu/gpu_watchdog_thread.cc
+++ b/content/gpu/gpu_watchdog_thread.cc
@@ -26,6 +26,9 @@
 const base::FilePath::CharType
     kTtyFilePath[] = FILE_PATH_LITERAL("/sys/class/tty/tty0/active");
 #endif
+#if defined(USE_X11)
+const unsigned char text[20] = "check";
+#endif
 }  // namespace
 
 GpuWatchdogThread::GpuWatchdogThread(int timeout)
@@ -39,6 +42,11 @@
 #endif
       task_observer_(this),
       suspended_(false),
+#if defined(USE_X11)
+      display_(NULL),
+      window_(0),
+      atom_(None),
+#endif
       weak_factory_(this) {
   DCHECK(timeout >= 0);
 
@@ -59,6 +67,9 @@
 #if defined(OS_CHROMEOS)
   tty_file_ = base::OpenFile(base::FilePath(kTtyFilePath), "r");
 #endif
+#if defined(USE_X11)
+  SetupXServer();
+#endif
   watched_message_loop_->AddTaskObserver(&task_observer_);
 }
 
@@ -123,6 +134,11 @@
     fclose(tty_file_);
 #endif
 
+#if defined(USE_X11)
+  XDestroyWindow(display_, window_);
+  XCloseDisplay(display_);
+#endif
+
   watched_message_loop_->RemoveTaskObserver(&task_observer_);
 }
 
@@ -188,9 +204,8 @@
   // not respond in time.
   message_loop()->PostDelayedTask(
       FROM_HERE,
-      base::Bind(
-          &GpuWatchdogThread::DeliberatelyTerminateToRecoverFromHang,
-          weak_factory_.GetWeakPtr()),
+      base::Bind(&GpuWatchdogThread::DeliberatelyTerminateToRecoverFromHang,
+                 weak_factory_.GetWeakPtr()),
       timeout);
 }
 
@@ -224,6 +239,52 @@
     return;
   }
 
+#if defined(USE_X11)
+  XWindowAttributes attributes;
+  XGetWindowAttributes(display_, window_, &attributes);
+
+  XSelectInput(display_, window_, PropertyChangeMask);
+  SetupXChangeProp();
+
+  XFlush(display_);
+
+  // We wait for the property change event with a timeout. If it arrives we know
+  // that X is responsive and is not the cause of the watchdog trigger, so we
+  // should
+  // terminate. If it times out, it may be due to X taking a long time, but
+  // terminating won't help, so ignore the watchdog trigger.
+  XEvent event_return;
+  base::TimeTicks deadline = base::TimeTicks::Now() + timeout_;
+  while (true) {
+    base::TimeDelta delta = deadline - base::TimeTicks::Now();
+    if (delta < base::TimeDelta()) {
+      return;
+    } else {
+      while (XCheckWindowEvent(display_, window_, PropertyChangeMask,
+                               &event_return)) {
+        if (MatchXEventAtom(&event_return))
+          break;
+      }
+      struct pollfd fds[1];
+      fds[0].fd = XConnectionNumber(display_);
+      fds[0].events = POLLIN;
+      int status = poll(fds, 1, delta.InMilliseconds());
+      if (status == -1) {
+        if (errno == EINTR) {
+          continue;
+        } else {
+          LOG(FATAL) << "Lost X connection, aborting.";
+          break;
+        }
+      } else if (status == 0) {
+        return;
+      } else {
+        continue;
+      }
+    }
+  }
+#endif
+
   // For minimal developer annoyance, don't keep terminating. You need to skip
   // the call to base::Process::Terminate below in a debugger for this to be
   // useful.
@@ -259,6 +320,28 @@
   terminated = true;
 }
 
+#if defined(USE_X11)
+void GpuWatchdogThread::SetupXServer() {
+  display_ = XOpenDisplay(NULL);
+  window_ = XCreateWindow(display_, DefaultRootWindow(display_), 0, 0, 1, 1, 0,
+                          CopyFromParent, InputOutput, CopyFromParent, 0, NULL);
+  atom_ = XInternAtom(display_, "CHECK", False);
+}
+
+void GpuWatchdogThread::SetupXChangeProp() {
+  XChangeProperty(display_, window_, atom_, XA_STRING, 8, PropModeReplace, text,
+                  (arraysize(text) - 1));
+}
+
+bool GpuWatchdogThread::MatchXEventAtom(XEvent* event) {
+  if (event->xproperty.window == window_ && event->type == PropertyNotify &&
+      event->xproperty.atom == atom_)
+    return true;
+
+  return false;
+}
+
+#endif
 void GpuWatchdogThread::AddPowerObserver() {
   message_loop()->PostTask(
       FROM_HERE,
diff --git a/content/gpu/gpu_watchdog_thread.h b/content/gpu/gpu_watchdog_thread.h
index c766ca34..9dd184ab 100644
--- a/content/gpu/gpu_watchdog_thread.h
+++ b/content/gpu/gpu_watchdog_thread.h
@@ -12,6 +12,18 @@
 #include "base/threading/thread.h"
 #include "base/time/time.h"
 #include "content/common/gpu/gpu_watchdog.h"
+#include "ui/gfx/native_widget_types.h"
+
+#if defined(USE_X11)
+extern "C" {
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+}
+#include <sys/poll.h>
+#include "ui/base/x/x11_util.h"
+#include "ui/gfx/x/x11_types.h"
+
+#endif
 
 namespace content {
 
@@ -62,6 +74,11 @@
   void OnAcknowledge();
   void OnCheck(bool after_suspend);
   void DeliberatelyTerminateToRecoverFromHang();
+#if defined(USE_X11)
+  void SetupXServer();
+  void SetupXChangeProp();
+  bool MatchXEventAtom(XEvent* event);
+#endif
 
   void OnAddPowerObserver();
 
@@ -93,6 +110,12 @@
   FILE* tty_file_;
 #endif
 
+#if defined(USE_X11)
+  XDisplay* display_;
+  gfx::AcceleratedWidget window_;
+  XAtom atom_;
+#endif
+
   base::WeakPtrFactory<GpuWatchdogThread> weak_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(GpuWatchdogThread);
diff --git a/content/ppapi_plugin/broker_process_dispatcher.cc b/content/ppapi_plugin/broker_process_dispatcher.cc
index 35d5b9d..97d4250 100644
--- a/content/ppapi_plugin/broker_process_dispatcher.cc
+++ b/content/ppapi_plugin/broker_process_dispatcher.cc
@@ -309,14 +309,16 @@
 
   if (flash_browser_operations_1_3_) {
     PP_Bool result = flash_browser_operations_1_3_->SetSitePermission(
-        data_str.c_str(), setting_type, sites.size(), site_array.get());
+        data_str.c_str(), setting_type,
+        static_cast<uint32_t>(sites.size()), site_array.get());
 
     return PP_ToBool(result);
   }
 
   if (flash_browser_operations_1_2_) {
     PP_Bool result = flash_browser_operations_1_2_->SetSitePermission(
-        data_str.c_str(), setting_type, sites.size(), site_array.get());
+        data_str.c_str(), setting_type,
+        static_cast<uint32_t>(sites.size()), site_array.get());
 
     return PP_ToBool(result);
   }
diff --git a/content/public/android/java/src/org/chromium/content/browser/BrowserStartupController.java b/content/public/android/java/src/org/chromium/content/browser/BrowserStartupController.java
index 77ffca3..784ab9e3 100644
--- a/content/public/android/java/src/org/chromium/content/browser/BrowserStartupController.java
+++ b/content/public/android/java/src/org/chromium/content/browser/BrowserStartupController.java
@@ -92,6 +92,9 @@
     // Whether the async startup of the browser process has started.
     private boolean mHasStartedInitializingBrowserProcess;
 
+    // Whether tasks that occur after resource extraction have been completed.
+    private boolean mPostResourceExtractionTasksCompleted;
+
     // Whether the async startup of the browser process is complete.
     private boolean mStartupDone;
 
@@ -103,7 +106,7 @@
     private int mLibraryProcessType;
 
     BrowserStartupController(Context context, int libraryProcessType) {
-        mContext = context;
+        mContext = context.getApplicationContext();
         mAsyncStartupCallbacks = new ArrayList<StartupCallback>();
         mLibraryProcessType = libraryProcessType;
     }
@@ -123,8 +126,7 @@
         if (sInstance == null) {
             assert LibraryProcessType.PROCESS_BROWSER == libraryProcessType
                     || LibraryProcessType.PROCESS_WEBVIEW == libraryProcessType;
-            sInstance = new BrowserStartupController(context.getApplicationContext(),
-                    libraryProcessType);
+            sInstance = new BrowserStartupController(context, libraryProcessType);
         }
         assert sInstance.mLibraryProcessType == libraryProcessType : "Wrong process type";
         return sInstance;
@@ -164,13 +166,17 @@
             // flag that indicates that we have kicked off starting the browser process.
             mHasStartedInitializingBrowserProcess = true;
 
-            prepareToStartBrowserProcess(false);
-
             setAsynchronousStartup(true);
-            if (contentStart() > 0) {
-                // Failed. The callbacks may not have run, so run them.
-                enqueueCallbackExecution(STARTUP_FAILURE, NOT_ALREADY_STARTED);
-            }
+            prepareToStartBrowserProcess(false, new Runnable() {
+                @Override
+                public void run() {
+                    ThreadUtils.assertOnUiThread();
+                    if (contentStart() > 0) {
+                        // Failed. The callbacks may not have run, so run them.
+                        enqueueCallbackExecution(STARTUP_FAILURE, NOT_ALREADY_STARTED);
+                    }
+                }
+            });
         }
     }
 
@@ -188,8 +194,8 @@
     public void startBrowserProcessesSync(boolean singleProcess) throws ProcessInitException {
         // If already started skip to checking the result
         if (!mStartupDone) {
-            if (!mHasStartedInitializingBrowserProcess) {
-                prepareToStartBrowserProcess(singleProcess);
+            if (!mHasStartedInitializingBrowserProcess || !mPostResourceExtractionTasksCompleted) {
+                prepareToStartBrowserProcess(singleProcess, null);
             }
 
             setAsynchronousStartup(false);
@@ -263,7 +269,9 @@
     }
 
     @VisibleForTesting
-    void prepareToStartBrowserProcess(boolean singleProcess) throws ProcessInitException {
+    void prepareToStartBrowserProcess(
+            final boolean singleProcess, final Runnable completionCallback)
+                    throws ProcessInitException {
         Log.i(TAG, "Initializing chromium process, singleProcess=" + singleProcess);
 
         // Normally Main.java will have kicked this off asynchronously for Chrome. But other
@@ -276,15 +284,31 @@
         // to load it here if we arrived via another flow, e.g. bookmark access & sync setup.
         LibraryLoader.get(mLibraryProcessType).ensureInitialized(mContext, true);
 
-        // TODO(yfriedman): Remove dependency on a command line flag for this.
-        DeviceUtils.addDeviceSpecificUserAgentSwitch(mContext);
+        Runnable postResourceExtraction = new Runnable() {
+            @Override
+            public void run() {
+                if (!mPostResourceExtractionTasksCompleted) {
+                    // TODO(yfriedman): Remove dependency on a command line flag for this.
+                    DeviceUtils.addDeviceSpecificUserAgentSwitch(mContext);
 
-        Context appContext = mContext.getApplicationContext();
-        // Now we really need to have the resources ready.
-        resourceExtractor.waitForCompletion();
+                    ContentMain.initApplicationContext(mContext);
+                    nativeSetCommandLineFlags(
+                            singleProcess, nativeIsPluginEnabled() ? getPlugins() : null);
+                    mPostResourceExtractionTasksCompleted = true;
+                }
 
-        ContentMain.initApplicationContext(appContext);
-        nativeSetCommandLineFlags(singleProcess, nativeIsPluginEnabled() ? getPlugins() : null);
+                if (completionCallback != null) completionCallback.run();
+            }
+        };
+
+        if (completionCallback == null) {
+            // If no continuation callback is specified, then force the resource extraction
+            // to complete.
+            resourceExtractor.waitForCompletion();
+            postResourceExtraction.run();
+        } else {
+            resourceExtractor.addCompletionCallback(postResourceExtraction);
+        }
     }
 
     /**
diff --git a/content/public/android/java/src/org/chromium/content/browser/ContentViewCore.java b/content/public/android/java/src/org/chromium/content/browser/ContentViewCore.java
index 1616b717..f051aa45 100644
--- a/content/public/android/java/src/org/chromium/content/browser/ContentViewCore.java
+++ b/content/public/android/java/src/org/chromium/content/browser/ContentViewCore.java
@@ -748,6 +748,16 @@
 
         mWebContentsObserver = new WebContentsObserver(mWebContents) {
             @Override
+            public void didStartLoading(String url) {
+                mAccessibilityInjector.onPageLoadStarted();
+            }
+
+            @Override
+            public void didStopLoading(String url) {
+                mAccessibilityInjector.onPageLoadStopped();
+            }
+
+            @Override
             public void didFailLoad(boolean isProvisionalLoad, boolean isMainFrame, int errorCode,
                     String description, String failingUrl) {
                 // Navigation that fails the provisional load will have the strong binding removed
diff --git a/content/public/android/java/src/org/chromium/content/browser/MediaResourceGetter.java b/content/public/android/java/src/org/chromium/content/browser/MediaResourceGetter.java
index 71bd333d..ede0c2e 100644
--- a/content/public/android/java/src/org/chromium/content/browser/MediaResourceGetter.java
+++ b/content/public/android/java/src/org/chromium/content/browser/MediaResourceGetter.java
@@ -210,7 +210,7 @@
             Log.d(TAG, "extracted valid metadata: " + result.toString());
             return result;
         } catch (RuntimeException e) {
-            Log.e(TAG, "Unable to extract medata", e);
+            Log.e(TAG, "Unable to extract metadata: " + e.getMessage());
             return EMPTY_METADATA;
         }
     }
@@ -221,7 +221,7 @@
         try {
             uri = URI.create(url);
         } catch (IllegalArgumentException  e) {
-            Log.e(TAG, "Cannot parse uri.", e);
+            Log.e(TAG, "Cannot parse uri: " + e.getMessage());
             return false;
         }
         String scheme = uri.getScheme();
@@ -239,7 +239,7 @@
                 configure(file.getAbsolutePath());
                 return true;
             } catch (RuntimeException e) {
-                Log.e(TAG, "Error configuring data source", e);
+                Log.e(TAG, "Error configuring data source: " + e.getMessage());
                 return false;
             }
         }
@@ -263,7 +263,7 @@
             configure(url, headersMap);
             return true;
         } catch (RuntimeException e) {
-            Log.e(TAG, "Error configuring data source", e);
+            Log.e(TAG, "Error configuring data source: " + e.getMessage());
             return false;
         }
     }
diff --git a/content/public/android/java/src/org/chromium/content/browser/accessibility/AccessibilityInjector.java b/content/public/android/java/src/org/chromium/content/browser/accessibility/AccessibilityInjector.java
index 25b722d..d07c4931 100644
--- a/content/public/android/java/src/org/chromium/content/browser/accessibility/AccessibilityInjector.java
+++ b/content/public/android/java/src/org/chromium/content/browser/accessibility/AccessibilityInjector.java
@@ -20,7 +20,6 @@
 import org.chromium.base.CommandLine;
 import org.chromium.content.browser.ContentViewCore;
 import org.chromium.content.browser.JavascriptInterface;
-import org.chromium.content.browser.WebContentsObserver;
 import org.chromium.content.common.ContentSwitches;
 import org.json.JSONException;
 import org.json.JSONObject;
@@ -34,7 +33,7 @@
 /**
  * Responsible for accessibility injection and management of a {@link ContentViewCore}.
  */
-public class AccessibilityInjector extends WebContentsObserver {
+public class AccessibilityInjector {
     // The ContentView this injector is responsible for managing.
     protected ContentViewCore mContentViewCore;
 
@@ -103,7 +102,6 @@
      * @param view The ContentViewCore that this AccessibilityInjector manages.
      */
     protected AccessibilityInjector(ContentViewCore view) {
-        super(view.getWebContents());
         mContentViewCore = view;
 
         mAccessibilityScreenReaderUrl = CommandLine.getInstance().getSwitchValue(
@@ -208,13 +206,15 @@
      * accessibility script as not being injected.  This way we can properly ignore incoming
      * accessibility gesture events.
      */
-    @Override
-    public void didStartLoading(String url) {
+    public void onPageLoadStarted() {
         mScriptInjected = false;
     }
 
-    @Override
-    public void didStopLoading(String url) {
+    /**
+     * Notifies this handler that a page load has stopped, which means we can now inject the
+     * accessibility script.
+     */
+    public void onPageLoadStopped() {
         injectAccessibilityScriptIntoPage();
     }
 
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/BrowserStartupControllerTest.java b/content/public/android/javatests/src/org/chromium/content/browser/BrowserStartupControllerTest.java
index 2146d68..927f76a 100644
--- a/content/public/android/javatests/src/org/chromium/content/browser/BrowserStartupControllerTest.java
+++ b/content/public/android/javatests/src/org/chromium/content/browser/BrowserStartupControllerTest.java
@@ -28,10 +28,13 @@
         private int mInitializedCounter = 0;
 
         @Override
-        void prepareToStartBrowserProcess(boolean singleProcess) throws ProcessInitException {
+        void prepareToStartBrowserProcess(boolean singleProcess, Runnable completionCallback)
+                throws ProcessInitException {
             if (!mLibraryLoadSucceeds) {
                 throw new ProcessInitException(
                         LoaderErrors.LOADER_ERROR_NATIVE_LIBRARY_LOAD_FAILED);
+            } else if (completionCallback != null) {
+                completionCallback.run();
             }
         }
 
diff --git a/content/public/app/content_jni_onload.h b/content/public/app/content_jni_onload.h
index de2de3ab..2f3ee66 100644
--- a/content/public/app/content_jni_onload.h
+++ b/content/public/app/content_jni_onload.h
@@ -7,24 +7,19 @@
 
 #include <jni.h>
 
+#include "base/android/base_jni_onload.h"
 #include "content/common/content_export.h"
 
-namespace base {
-namespace android {
-
-class JNIOnLoadDelegate;
-
-}  // namespace android
-}  // namespace base
-
 namespace content {
 namespace android {
 
-// Returns true if JNI registration and initialization succeeded. Refer to
-// JNIOnLoadDelegate for more information.
-CONTENT_EXPORT bool OnJNIOnLoad(
+// Returns true if JNI registration succeeded.
+CONTENT_EXPORT bool OnJNIOnLoadRegisterJNI(
     JavaVM* vm,
-    base::android::JNIOnLoadDelegate* delegate);
+    base::android::RegisterCallback callback);
+
+// Returns true if initialization succeeded.
+CONTENT_EXPORT bool OnJNIOnLoadInit(base::android::InitCallback callback);
 
 }  // namespace android
 }  // namespace content
diff --git a/content/public/browser/browser_url_handler.h b/content/public/browser/browser_url_handler.h
index 4fabec3c..964741f6 100644
--- a/content/public/browser/browser_url_handler.h
+++ b/content/public/browser/browser_url_handler.h
@@ -43,6 +43,11 @@
                                      BrowserContext* browser_context,
                                      bool* reverse_on_redirect) = 0;
 
+  // Set the specified handler as a preliminary fixup phase to be done before
+  // rewriting.  This allows minor cleanup for the URL without having it affect
+  // the virtual URL.
+  virtual void SetFixupHandler(URLHandler handler) = 0;
+
   // Add the specified handler pair to the list of URL handlers.
   //
   // Note that normally, the reverse handler is only used if the modified URL is
diff --git a/content/public/browser/indexed_db_info.cc b/content/public/browser/indexed_db_info.cc
index 5ccaffb4..01e26a2 100644
--- a/content/public/browser/indexed_db_info.cc
+++ b/content/public/browser/indexed_db_info.cc
@@ -15,4 +15,8 @@
       last_modified_(last_modified),
       connection_count_(connection_count) {}
 
+IndexedDBInfo::IndexedDBInfo(const IndexedDBInfo& other) = default;
+IndexedDBInfo::~IndexedDBInfo() = default;
+IndexedDBInfo& IndexedDBInfo::operator=(const IndexedDBInfo& other) = default;
+
 }  // namespace content
diff --git a/content/public/browser/indexed_db_info.h b/content/public/browser/indexed_db_info.h
index 726949c..8a2ee7c 100644
--- a/content/public/browser/indexed_db_info.h
+++ b/content/public/browser/indexed_db_info.h
@@ -18,6 +18,9 @@
                 int64 size,
                 const base::Time& last_modified,
                 size_t connection_count);
+  IndexedDBInfo(const IndexedDBInfo& other);
+  ~IndexedDBInfo();
+  IndexedDBInfo& operator=(const IndexedDBInfo& other);
 
   GURL origin_;
   int64 size_;
diff --git a/content/public/browser/push_messaging_service.cc b/content/public/browser/push_messaging_service.cc
index 154b02d2..f9d62d6 100644
--- a/content/public/browser/push_messaging_service.cc
+++ b/content/public/browser/push_messaging_service.cc
@@ -4,20 +4,21 @@
 
 #include "content/public/browser/push_messaging_service.h"
 
+#include "content/browser/push_messaging/push_messaging_message_filter.h"
 #include "content/browser/service_worker/service_worker_context_wrapper.h"
+#include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_thread.h"
+#include "content/public/browser/storage_partition.h"
+
+namespace content {
 
 namespace {
 
 const char kNotificationsShownServiceWorkerKey[] =
     "notifications_shown_by_last_few_pushes";
 
-}  // namespace
-
-namespace content {
-
-static void CallGetNotificationsShownCallbackFromIO(
-    const PushMessagingService::GetNotificationsShownCallback& callback,
+void CallStringCallbackFromIO(
+    const PushMessagingService::StringCallback& callback,
     const std::string& data,
     ServiceWorkerStatusCode service_worker_status) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
@@ -27,7 +28,7 @@
                           base::Bind(callback, data, success, not_found));
 }
 
-static void CallResultCallbackFromIO(
+void CallResultCallbackFromIO(
     const ServiceWorkerContext::ResultCallback& callback,
     ServiceWorkerStatusCode service_worker_status) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
@@ -36,17 +37,18 @@
                           base::Bind(callback, success));
 }
 
-static void GetNotificationsShownOnIO(
+void GetUserDataOnIO(
     scoped_refptr<ServiceWorkerContextWrapper> service_worker_context_wrapper,
     int64 service_worker_registration_id,
-    const PushMessagingService::GetNotificationsShownCallback& callback) {
+    const std::string& key,
+    const PushMessagingService::StringCallback& callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   service_worker_context_wrapper->context()->storage()->GetUserData(
-      service_worker_registration_id, kNotificationsShownServiceWorkerKey,
-      base::Bind(&CallGetNotificationsShownCallbackFromIO, callback));
+      service_worker_registration_id, key,
+      base::Bind(&CallStringCallbackFromIO, callback));
 }
 
-static void SetNotificationsShownOnIO(
+void SetNotificationsShownOnIO(
     scoped_refptr<ServiceWorkerContextWrapper> service_worker_context_wrapper,
     int64 service_worker_registration_id, const GURL& origin,
     const std::string& data,
@@ -58,18 +60,44 @@
       base::Bind(&CallResultCallbackFromIO, callback));
 }
 
+void OnClearPushRegistrationServiceWorkerKey(ServiceWorkerStatusCode status) {
+}
+
+void ClearPushRegistrationIDOnIO(
+    scoped_refptr<ServiceWorkerContextWrapper> service_worker_context,
+    int64 service_worker_registration_id) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+  service_worker_context->context()->storage()->ClearUserData(
+      service_worker_registration_id,
+      kPushRegistrationIdServiceWorkerKey,
+      base::Bind(&OnClearPushRegistrationServiceWorkerKey));
+}
+
+scoped_refptr<ServiceWorkerContextWrapper> GetServiceWorkerContext(
+    BrowserContext* browser_context, const GURL& origin) {
+  StoragePartition* partition =
+      BrowserContext::GetStoragePartitionForSite(browser_context, origin);
+  return make_scoped_refptr(
+      static_cast<ServiceWorkerContextWrapper*>(
+          partition->GetServiceWorkerContext()));
+}
+
+}  // anonymous namespace
+
 // static
 void PushMessagingService::GetNotificationsShownByLastFewPushes(
     ServiceWorkerContext* service_worker_context,
     int64 service_worker_registration_id,
-    const GetNotificationsShownCallback& callback) {
+    const StringCallback& callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   scoped_refptr<ServiceWorkerContextWrapper> wrapper =
       static_cast<ServiceWorkerContextWrapper*>(service_worker_context);
   BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
-                          base::Bind(&GetNotificationsShownOnIO,
+                          base::Bind(&GetUserDataOnIO,
                                      wrapper,
                                      service_worker_registration_id,
+                                     kNotificationsShownServiceWorkerKey,
                                      callback));
 }
 
@@ -92,4 +120,34 @@
                                      callback));
 }
 
+// static
+void PushMessagingService::GetSenderId(BrowserContext* browser_context,
+                                       const GURL& origin,
+                                       int64 service_worker_registration_id,
+                                       const StringCallback& callback) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  BrowserThread::PostTask(
+      BrowserThread::IO,
+      FROM_HERE,
+      base::Bind(&GetUserDataOnIO,
+                 GetServiceWorkerContext(browser_context, origin),
+                 service_worker_registration_id,
+                 kPushSenderIdServiceWorkerKey,
+                 callback));
+}
+
+// static
+void PushMessagingService::ClearPushRegistrationID(
+    BrowserContext* browser_context,
+    const GURL& origin,
+    int64 service_worker_registration_id) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  BrowserThread::PostTask(
+      BrowserThread::IO,
+      FROM_HERE,
+      base::Bind(&ClearPushRegistrationIDOnIO,
+                 GetServiceWorkerContext(browser_context, origin),
+                 service_worker_registration_id));
+}
+
 }  // namespace content
diff --git a/content/public/browser/push_messaging_service.h b/content/public/browser/push_messaging_service.h
index 4d06cdde..f382cf1 100644
--- a/content/public/browser/push_messaging_service.h
+++ b/content/public/browser/push_messaging_service.h
@@ -15,37 +15,23 @@
 
 namespace content {
 
+class BrowserContext;
 class ServiceWorkerContext;
 
 // A push service-agnostic interface that the Push API uses for talking to
 // push messaging services like GCM. Must only be used on the UI thread.
 class CONTENT_EXPORT PushMessagingService {
  public:
-  using GetNotificationsShownCallback =
-      base::Callback<void(const std::string& notifications_shown,
-                          bool success, bool not_found)>;
-
-  using ResultCallback = base::Callback<void(bool success)>;
-
   using RegisterCallback =
       base::Callback<void(const std::string& /* registration_id */,
                           PushRegistrationStatus /* status */)>;
   using UnregisterCallback = base::Callback<void(PushUnregistrationStatus)>;
 
-  // Provide a storage mechanism to read/write an opaque
-  // "notifications_shown_by_last_few_pushes" string associated with a Service
-  // Worker registration. Stored data is deleted when the associated
-  // registration is deleted.
-  static void GetNotificationsShownByLastFewPushes(
-      ServiceWorkerContext* service_worker_context,
-      int64 service_worker_registration_id,
-      const GetNotificationsShownCallback& callback);
-  static void SetNotificationsShownByLastFewPushes(
-      ServiceWorkerContext* service_worker_context,
-      int64 service_worker_registration_id,
-      const GURL& origin,
-      const std::string& notifications_shown,
-      const ResultCallback& callback);
+  using StringCallback = base::Callback<void(const std::string& data,
+                                             bool success,
+                                             bool not_found)>;
+
+  using ResultCallback = base::Callback<void(bool success)>;
 
   virtual ~PushMessagingService() {}
 
@@ -77,6 +63,7 @@
   // the push service.
   virtual void Unregister(const GURL& requesting_origin,
                           int64 service_worker_registration_id,
+                          const std::string& sender_id,
                           bool retry_on_failure,
                           const UnregisterCallback& callback) = 0;
 
@@ -86,6 +73,33 @@
   virtual blink::WebPushPermissionStatus GetPermissionStatus(
       const GURL& requesting_origin,
       const GURL& embedding_origin) = 0;
+
+ protected:
+  // Provide a storage mechanism to read/write an opaque
+  // "notifications_shown_by_last_few_pushes" string associated with a Service
+  // Worker registration. Stored data is deleted when the associated
+  // registration is deleted.
+  static void GetNotificationsShownByLastFewPushes(
+      ServiceWorkerContext* service_worker_context,
+      int64 service_worker_registration_id,
+      const StringCallback& callback);
+  static void SetNotificationsShownByLastFewPushes(
+      ServiceWorkerContext* service_worker_context,
+      int64 service_worker_registration_id,
+      const GURL& origin,
+      const std::string& notifications_shown,
+      const ResultCallback& callback);
+
+  static void GetSenderId(BrowserContext* browser_context,
+                          const GURL& origin,
+                          int64 service_worker_registration_id,
+                          const StringCallback& callback);
+
+  // Clear the push registration id stored in the service worker with the given
+  // |service_worker_registration_id| for the given |origin|.
+  static void ClearPushRegistrationID(BrowserContext* browser_context,
+                                      const GURL& origin,
+                                      int64 service_worker_registration_id);
 };
 
 }  // namespace content
diff --git a/content/public/common/common_param_traits_macros.h b/content/public/common/common_param_traits_macros.h
index 1f619c0..df89031 100644
--- a/content/public/common/common_param_traits_macros.h
+++ b/content/public/common/common_param_traits_macros.h
@@ -28,7 +28,10 @@
 #undef IPC_MESSAGE_EXPORT
 #define IPC_MESSAGE_EXPORT CONTENT_EXPORT
 
-IPC_ENUM_TRAITS(ui::PageTransition)  // Bitmask.
+IPC_ENUM_TRAITS_VALIDATE(ui::PageTransition,
+                         ((value &
+                           ui::PageTransition::PAGE_TRANSITION_CORE_MASK) <=
+                          ui::PageTransition::PAGE_TRANSITION_LAST_CORE))
 IPC_ENUM_TRAITS_MAX_VALUE(net::NetworkChangeNotifier::ConnectionType,
                           net::NetworkChangeNotifier::CONNECTION_LAST)
 IPC_ENUM_TRAITS_MAX_VALUE(content::ConsoleMessageLevel,
diff --git a/content/public/common/content_switches.cc b/content/public/common/content_switches.cc
index c11062ce..3b7d13b58 100644
--- a/content/public/common/content_switches.cc
+++ b/content/public/common/content_switches.cc
@@ -384,6 +384,10 @@
 // Enables payloads for received push messages when using the W3C Push API.
 const char kEnablePushMessagePayload[] = "enable-push-message-payload";
 
+// Enable hasPermission() method of the W3C Push API.
+const char kEnablePushMessagingHasPermission[] =
+        "enable-push-messaging-has-permission";
+
 // Set options to cache V8 data. (off, preparse data, or code)
 const char kV8CacheOptions[] = "v8-cache-options";
 
diff --git a/content/public/common/content_switches.h b/content/public/common/content_switches.h
index 2c87ae03..84a1c64c 100644
--- a/content/public/common/content_switches.h
+++ b/content/public/common/content_switches.h
@@ -118,6 +118,7 @@
 CONTENT_EXPORT extern const char kEnablePinch[];
 CONTENT_EXPORT extern const char kEnablePreciseMemoryInfo[];
 CONTENT_EXPORT extern const char kEnablePushMessagePayload[];
+CONTENT_EXPORT extern const char kEnablePushMessagingHasPermission[];
 CONTENT_EXPORT extern const char kEnableRegionBasedColumns[];
 CONTENT_EXPORT extern const char kEnableSandboxLogging[];
 CONTENT_EXPORT extern const char kEnableSeccompFilterSandbox[];
diff --git a/content/public/common/push_messaging_status.cc b/content/public/common/push_messaging_status.cc
index 754587a..7cf33b04 100644
--- a/content/public/common/push_messaging_status.cc
+++ b/content/public/common/push_messaging_status.cc
@@ -36,6 +36,45 @@
 
     case PUSH_REGISTRATION_STATUS_SUCCESS_FROM_CACHE:
       return "Registration successful - from cache";
+
+    case PUSH_REGISTRATION_STATUS_NETWORK_ERROR:
+      return "Registration failed - could not connect to push server";
+
+    case PUSH_REGISTRATION_STATUS_INCOGNITO_PERMISSION_DENIED:
+      // We split this out for UMA, but it must be indistinguishable to JS.
+      return PushRegistrationStatusToString(
+          PUSH_REGISTRATION_STATUS_PERMISSION_DENIED);
+  }
+  NOTREACHED();
+  return "";
+}
+
+const char* PushUnregistrationStatusToString(PushUnregistrationStatus status) {
+  switch (status) {
+    case PUSH_UNREGISTRATION_STATUS_SUCCESS_UNREGISTERED:
+      return "Unregistration successful - from push service";
+
+    case PUSH_UNREGISTRATION_STATUS_SUCCESS_WAS_NOT_REGISTERED:
+      return "Unregistration successful - was not registered";
+
+    case PUSH_UNREGISTRATION_STATUS_PENDING_WILL_RETRY_NETWORK_ERROR:
+      return "Unregistration pending - a network error occurred, but it will"
+             "be retried until it succeeds";
+
+    case PUSH_UNREGISTRATION_STATUS_NO_SERVICE_WORKER:
+      return "Unregistration failed - no Service Worker";
+
+    case PUSH_UNREGISTRATION_STATUS_SERVICE_NOT_AVAILABLE:
+      return "Unregistration failed - push service not available";
+
+    case PUSH_UNREGISTRATION_STATUS_SERVICE_ERROR:
+      return "Unregistration failed - push service error";
+
+    case PUSH_UNREGISTRATION_STATUS_STORAGE_ERROR:
+      return "Unregistration failed - storage error";
+
+    case PUSH_UNREGISTRATION_STATUS_NETWORK_ERROR:
+      return "Unregistration failed - could not connect to push server";
   }
   NOTREACHED();
   return "";
diff --git a/content/public/common/push_messaging_status.h b/content/public/common/push_messaging_status.h
index e91e944..4e434d7 100644
--- a/content/public/common/push_messaging_status.h
+++ b/content/public/common/push_messaging_status.h
@@ -7,7 +7,7 @@
 
 namespace content {
 
-// Push registration success / error codes for internal use & reporting in UMA.
+// Push registration success/error codes for internal use & reporting in UMA.
 enum PushRegistrationStatus {
   // New successful registration (there was not yet a registration cached in
   // Service Worker storage, so the browser successfully registered with the
@@ -42,79 +42,113 @@
   // A successful registration was already cached in Service Worker storage.
   PUSH_REGISTRATION_STATUS_SUCCESS_FROM_CACHE = 8,
 
+  // Registration failed due to a network error.
+  PUSH_REGISTRATION_STATUS_NETWORK_ERROR = 9,
+
+  // Registration failed because the push service is not available in incognito,
+  // but we tell JS that permission was denied to not reveal incognito.
+  PUSH_REGISTRATION_STATUS_INCOGNITO_PERMISSION_DENIED = 10,
+
   // NOTE: Do not renumber these as that would confuse interpretation of
   // previously logged data. When making changes, also update the enum list
   // in tools/metrics/histograms/histograms.xml to keep it in sync, and
   // update PUSH_REGISTRATION_STATUS_LAST below.
 
-  // Used for IPC message range checks.
-  PUSH_REGISTRATION_STATUS_LAST = PUSH_REGISTRATION_STATUS_SUCCESS_FROM_CACHE
+  PUSH_REGISTRATION_STATUS_LAST =
+      PUSH_REGISTRATION_STATUS_INCOGNITO_PERMISSION_DENIED
 };
 
-// Push unregistration success / error codes for internal use & reporting.
+// Push unregistration success/error codes for internal use & reporting in UMA.
 enum PushUnregistrationStatus {
   // The unregistration was successful.
-  PUSH_UNREGISTRATION_STATUS_SUCCESS_UNREGISTER,
+  PUSH_UNREGISTRATION_STATUS_SUCCESS_UNREGISTERED = 0,
 
-  // The registration did not happen because of a network error, but will be
+  // Unregistration was unnecessary, as the registration was not found.
+  PUSH_UNREGISTRATION_STATUS_SUCCESS_WAS_NOT_REGISTERED = 1,
+
+  // The unregistration did not happen because of a network error, but will be
   // retried until it succeeds.
-  PUSH_UNREGISTRATION_STATUS_SUCCESS_WILL_RETRY_NETWORK_ERROR,
+  PUSH_UNREGISTRATION_STATUS_PENDING_WILL_RETRY_NETWORK_ERROR = 2,
 
-  // The registration was not registered.
-  PUSH_UNREGISTRATION_STATUS_SUCCESS_WAS_NOT_REGISTERED,
+  // Unregistration failed because there is no Service Worker.
+  PUSH_UNREGISTRATION_STATUS_NO_SERVICE_WORKER = 3,
 
-  // The unregistration did not happen because of a network error.
-  PUSH_UNREGISTRATION_STATUS_NETWORK_ERROR,
+  // Unregistration failed because the push service is not available.
+  PUSH_UNREGISTRATION_STATUS_SERVICE_NOT_AVAILABLE = 4,
 
-  // The unregistration did not happen because of a miscellaneous error.
-  PUSH_UNREGISTRATION_STATUS_UNKNOWN_ERROR,
+  // Unregistration failed in the push service implemented by the embedder.
+  PUSH_UNREGISTRATION_STATUS_SERVICE_ERROR = 5,
+
+  // Unregistration succeeded, but we failed to clear Service Worker storage.
+  PUSH_UNREGISTRATION_STATUS_STORAGE_ERROR = 6,
+
+  // Unregistration failed due to a network error.
+  PUSH_UNREGISTRATION_STATUS_NETWORK_ERROR = 7,
+
+  // NOTE: Do not renumber these as that would confuse interpretation of
+  // previously logged data. When making changes, also update the enum list
+  // in tools/metrics/histograms/histograms.xml to keep it in sync, and
+  // update PUSH_UNREGISTRATION_STATUS_LAST below.
+
+  PUSH_UNREGISTRATION_STATUS_LAST = PUSH_UNREGISTRATION_STATUS_NETWORK_ERROR
 };
 
-// Push get registration success / error codes for internal use.
+// Push getregistration success/error codes for internal use & reporting in UMA.
 enum PushGetRegistrationStatus {
   // Getting the registration was successful.
-  PUSH_GETREGISTRATION_STATUS_SUCCESS,
+  PUSH_GETREGISTRATION_STATUS_SUCCESS = 0,
+
+  // Getting the registration failed because the push service is not available.
+  PUSH_GETREGISTRATION_STATUS_SERVICE_NOT_AVAILABLE = 1,
+
+  // Getting the registration failed because we failed to read from storage.
+  PUSH_GETREGISTRATION_STATUS_STORAGE_ERROR = 2,
 
   // Getting the registration failed because there is no push registration.
-  PUSH_GETREGISTRATION_STATUS_REGISTRATION_NOT_FOUND,
+  PUSH_GETREGISTRATION_STATUS_REGISTRATION_NOT_FOUND = 3,
 
-  // Getting the registration failed because of a service worker error.
-  PUSH_GETREGISTRATION_STATUS_SERVICE_WORKER_ERROR,
+  // Getting the registration failed because the push service isn't available in
+  // incognito, but we tell JS registration not found to not reveal incognito.
+  PUSH_GETREGISTRATION_STATUS_INCOGNITO_REGISTRATION_NOT_FOUND = 4,
 
-  // When making changes, update PUSH_GETREGISTRATION_STATUS_LAST below.
+  // NOTE: Do not renumber these as that would confuse interpretation of
+  // previously logged data. When making changes, also update the enum list
+  // in tools/metrics/histograms/histograms.xml to keep it in sync, and
+  // update PUSH_GETREGISTRATION_STATUS_LAST below.
 
-  // Used for IPC message range checks.
   PUSH_GETREGISTRATION_STATUS_LAST =
-      PUSH_GETREGISTRATION_STATUS_SERVICE_WORKER_ERROR
+      PUSH_GETREGISTRATION_STATUS_INCOGNITO_REGISTRATION_NOT_FOUND
 };
 
-// Push message delivery success / error codes for internal use.
+// Push message event success/error codes for internal use & reporting in UMA.
 enum PushDeliveryStatus {
   // The message was successfully delivered.
-  PUSH_DELIVERY_STATUS_SUCCESS,
+  PUSH_DELIVERY_STATUS_SUCCESS = 0,
 
   // The message could not be delivered because it was invalid.
-  PUSH_DELIVERY_STATUS_INVALID_MESSAGE,
+  PUSH_DELIVERY_STATUS_INVALID_MESSAGE = 1,
 
   // The message could not be delivered because the app id was unknown.
-  PUSH_DELIVERY_STATUS_UNKNOWN_APP_ID,
+  PUSH_DELIVERY_STATUS_UNKNOWN_APP_ID = 2,
 
   // The message could not be delivered because origin no longer has permission.
-  PUSH_DELIVERY_STATUS_PERMISSION_DENIED,
+  PUSH_DELIVERY_STATUS_PERMISSION_DENIED = 3,
 
   // The message could not be delivered because no service worker was found.
-  PUSH_DELIVERY_STATUS_NO_SERVICE_WORKER,
+  PUSH_DELIVERY_STATUS_NO_SERVICE_WORKER = 4,
 
   // The message could not be delivered because of a service worker error.
-  PUSH_DELIVERY_STATUS_SERVICE_WORKER_ERROR,
+  PUSH_DELIVERY_STATUS_SERVICE_WORKER_ERROR = 5,
 
   // The message was delivered, but the Service Worker passed a Promise to
   // event.waitUntil that got rejected.
-  PUSH_DELIVERY_STATUS_EVENT_WAITUNTIL_REJECTED,
+  PUSH_DELIVERY_STATUS_EVENT_WAITUNTIL_REJECTED = 6,
 
-  // When making changes, update PUSH_DELIVERY_STATUS_LAST below.
+  // NOTE: Do not renumber these as that would confuse interpretation of
+  // previously logged data. When making changes, also update the enum list
+  // in tools/metrics/histograms/histograms.xml to keep it in sync, and
+  // update PUSH_DELIVERY_STATUS_LAST below.
 
-  // Used for IPC message range checks.
   PUSH_DELIVERY_STATUS_LAST = PUSH_DELIVERY_STATUS_EVENT_WAITUNTIL_REJECTED
 };
 
@@ -142,13 +176,14 @@
   // in tools/metrics/histograms/histograms.xml to keep it in sync, and
   // update PUSH_USER_VISIBLE_STATUS_LAST below.
 
-  // Used for IPC message range checks.
   PUSH_USER_VISIBLE_STATUS_LAST =
       PUSH_USER_VISIBLE_STATUS_REQUIRED_BUT_NOT_SHOWN_GRACE_EXCEEDED
 };
 
 const char* PushRegistrationStatusToString(PushRegistrationStatus status);
 
+const char* PushUnregistrationStatusToString(PushUnregistrationStatus status);
+
 }  // namespace content
 
 #endif  // CONTENT_PUBLIC_COMMON_PUSH_MESSAGING_STATUS_H_
diff --git a/content/public/common/renderer_preferences.cc b/content/public/common/renderer_preferences.cc
index 0e5ee46f..86c96a6 100644
--- a/content/public/common/renderer_preferences.cc
+++ b/content/public/common/renderer_preferences.cc
@@ -31,6 +31,7 @@
       use_custom_colors(true),
       enable_referrers(true),
       enable_do_not_track(false),
+      enable_webrtc_multiple_routes(true),
       default_zoom_level(0),
       report_frame_name_changes(false),
       tap_multiple_targets_strategy(TAP_MULTIPLE_TARGETS_STRATEGY_POPUP),
diff --git a/content/public/common/renderer_preferences.h b/content/public/common/renderer_preferences.h
index 153df3e..4bee4d4 100644
--- a/content/public/common/renderer_preferences.h
+++ b/content/public/common/renderer_preferences.h
@@ -96,6 +96,9 @@
   // Set to true to indicate that the preference to set DNT to 1 is enabled.
   bool enable_do_not_track;
 
+  // Set to false to indicate that WebRTC should use the OS default routing.
+  bool enable_webrtc_multiple_routes;
+
   // Default page zoom level.
   double default_zoom_level;
 
diff --git a/content/public/common/resource_response.cc b/content/public/common/resource_response.cc
index 92abfba8..e362f32 100644
--- a/content/public/common/resource_response.cc
+++ b/content/public/common/resource_response.cc
@@ -31,7 +31,7 @@
   new_response->head.was_fetched_via_spdy = head.was_fetched_via_spdy;
   new_response->head.was_npn_negotiated = head.was_npn_negotiated;
   new_response->head.was_alternate_protocol_available =
-      new_response->head.was_alternate_protocol_available;
+      head.was_alternate_protocol_available;
   new_response->head.connection_info = head.connection_info;
   new_response->head.was_fetched_via_proxy = head.was_fetched_via_proxy;
   new_response->head.proxy_server = head.proxy_server;
diff --git a/content/public/renderer/content_renderer_client.cc b/content/public/renderer/content_renderer_client.cc
index 79b40ac..5a7f370 100644
--- a/content/public/renderer/content_renderer_client.cc
+++ b/content/public/renderer/content_renderer_client.cc
@@ -175,7 +175,9 @@
 }
 
 scoped_ptr<media::RendererFactory>
-ContentRendererClient::CreateMediaRendererFactory(RenderFrame* render_frame) {
+ContentRendererClient::CreateMediaRendererFactory(
+    RenderFrame* render_frame,
+    const scoped_refptr<media::MediaLog>& media_log) {
   return nullptr;
 }
 
diff --git a/content/public/renderer/content_renderer_client.h b/content/public/renderer/content_renderer_client.h
index a7c7271..0744f627 100644
--- a/content/public/renderer/content_renderer_client.h
+++ b/content/public/renderer/content_renderer_client.h
@@ -53,6 +53,7 @@
 }
 
 namespace media {
+class MediaLog;
 class RendererFactory;
 struct KeySystemInfo;
 }
@@ -260,7 +261,8 @@
 
   // Allows an embedder to provide a media::RendererFactory.
   virtual scoped_ptr<media::RendererFactory> CreateMediaRendererFactory(
-      RenderFrame* render_frame);
+      RenderFrame* render_frame,
+      const scoped_refptr<media::MediaLog>& media_log);
 
   // Gives the embedder a chance to register the key system(s) it supports by
   // populating |key_systems|.
diff --git a/content/renderer/BUILD.gn b/content/renderer/BUILD.gn
index 1d643054..73445e1 100644
--- a/content/renderer/BUILD.gn
+++ b/content/renderer/BUILD.gn
@@ -16,7 +16,10 @@
                         ".",
                         "//content")
 
-  configs += [ "//content:content_implementation" ]
+  configs += [
+    "//content:content_implementation",
+    "//build/config/compiler:no_size_t_to_int_warning",
+  ]
 
   deps = [
     # TODO(GYP) bug 376846 remove this. This should be inherited from //net but
diff --git a/content/renderer/gpu/render_widget_compositor.cc b/content/renderer/gpu/render_widget_compositor.cc
index b15f6b8b..9b6c288bd 100644
--- a/content/renderer/gpu/render_widget_compositor.cc
+++ b/content/renderer/gpu/render_widget_compositor.cc
@@ -360,10 +360,10 @@
   settings.max_partial_texture_updates = 0;
   if (synchronous_compositor_factory) {
     // Android WebView uses system scrollbars, so make ours invisible.
-    settings.scrollbar_animator = cc::LayerTreeSettings::NoAnimator;
+    settings.scrollbar_animator = cc::LayerTreeSettings::NO_ANIMATOR;
     settings.solid_color_scrollbar_color = SK_ColorTRANSPARENT;
   } else {
-    settings.scrollbar_animator = cc::LayerTreeSettings::LinearFade;
+    settings.scrollbar_animator = cc::LayerTreeSettings::LINEAR_FADE;
     settings.scrollbar_fade_delay_ms = 300;
     settings.scrollbar_fade_resize_delay_ms = 2000;
     settings.scrollbar_fade_duration_ms = 300;
@@ -400,13 +400,13 @@
 
 #elif !defined(OS_MACOSX)
   if (ui::IsOverlayScrollbarEnabled()) {
-    settings.scrollbar_animator = cc::LayerTreeSettings::Thinning;
+    settings.scrollbar_animator = cc::LayerTreeSettings::THINNING;
     settings.solid_color_scrollbar_color = SkColorSetARGB(128, 128, 128, 128);
   } else if (cmd->HasSwitch(cc::switches::kEnablePinchVirtualViewport)) {
     // use_pinch_zoom_scrollbars is only true on desktop when non-overlay
     // scrollbars are in use.
     settings.use_pinch_zoom_scrollbars = true;
-    settings.scrollbar_animator = cc::LayerTreeSettings::LinearFade;
+    settings.scrollbar_animator = cc::LayerTreeSettings::LINEAR_FADE;
     settings.solid_color_scrollbar_color = SkColorSetARGB(128, 128, 128, 128);
   }
   settings.scrollbar_fade_delay_ms = 500;
@@ -523,6 +523,10 @@
   return layer_tree_host_->source_frame_number();
 }
 
+void RenderWidgetCompositor::SetNeedsUpdateLayers() {
+  layer_tree_host_->SetNeedsUpdateLayers();
+}
+
 void RenderWidgetCompositor::SetNeedsCommit() {
   layer_tree_host_->SetNeedsCommit();
 }
@@ -783,6 +787,10 @@
   widget_->webwidget()->beginFrame(web_begin_frame_args);
 }
 
+void RenderWidgetCompositor::BeginMainFrameNotExpectedSoon() {
+  compositor_deps_->GetRendererScheduler()->BeginFrameNotExpectedSoon();
+}
+
 void RenderWidgetCompositor::Layout() {
   widget_->webwidget()->layout();
 
diff --git a/content/renderer/gpu/render_widget_compositor.h b/content/renderer/gpu/render_widget_compositor.h
index 25d9f53..d464941 100644
--- a/content/renderer/gpu/render_widget_compositor.h
+++ b/content/renderer/gpu/render_widget_compositor.h
@@ -63,9 +63,9 @@
   void SetNeedsForcedRedraw();
   // Calling CreateLatencyInfoSwapPromiseMonitor() to get a scoped
   // LatencyInfoSwapPromiseMonitor. During the life time of the
-  // LatencyInfoSwapPromiseMonitor, if SetNeedsCommit() or SetNeedsUpdateLayer()
-  // is called on LayerTreeHost, the original latency info will be turned
-  // into a LatencyInfoSwapPromise.
+  // LatencyInfoSwapPromiseMonitor, if SetNeedsCommit() or
+  // SetNeedsUpdateLayers() is called on LayerTreeHost, the original latency
+  // info will be turned into a LatencyInfoSwapPromise.
   scoped_ptr<cc::SwapPromiseMonitor> CreateLatencyInfoSwapPromiseMonitor(
       ui::LatencyInfo* latency);
   // Calling QueueSwapPromise() to directly queue a SwapPromise into
@@ -73,6 +73,7 @@
   void QueueSwapPromise(scoped_ptr<cc::SwapPromise> swap_promise);
   int GetLayerTreeId() const;
   int GetSourceFrameNumber() const;
+  void SetNeedsUpdateLayers();
   void SetNeedsCommit();
   void NotifyInputThrottledUntilCommit();
   const cc::Layer* GetRootLayer() const;
@@ -138,6 +139,7 @@
   void WillBeginMainFrame() override;
   void DidBeginMainFrame() override;
   void BeginMainFrame(const cc::BeginFrameArgs& args) override;
+  void BeginMainFrameNotExpectedSoon() override;
   void Layout() override;
   void ApplyViewportDeltas(const gfx::Vector2dF& inner_delta,
                            const gfx::Vector2dF& outer_delta,
diff --git a/content/renderer/history_entry.cc b/content/renderer/history_entry.cc
index d1425165..7686165 100644
--- a/content/renderer/history_entry.cc
+++ b/content/renderer/history_entry.cc
@@ -86,6 +86,11 @@
          child = child->nextSibling()) {
       RenderFrameImpl* child_render_frame =
           RenderFrameImpl::FromWebFrame(child);
+      // TODO(creis): A child frame may be a RenderFrameProxy.  We should still
+      // process its children, but that will be possible when we move this code
+      // to the browser process in https://crbug.com/236848.
+      if (!child_render_frame)
+        continue;
       HistoryNode* child_history_node =
           entry_->GetHistoryNodeForFrame(child_render_frame);
       if (!child_history_node)
diff --git a/content/renderer/input/input_handler_proxy.cc b/content/renderer/input/input_handler_proxy.cc
index 60305581..f9cc75b9 100644
--- a/content/renderer/input/input_handler_proxy.cc
+++ b/content/renderer/input/input_handler_proxy.cc
@@ -346,10 +346,10 @@
             gfx::Point(wheel_event.x, wheel_event.y),
             gfx::Vector2dF(-wheel_event.deltaX, -wheel_event.deltaY));
     switch (scroll_status) {
-      case cc::InputHandler::ScrollStarted:
+      case cc::InputHandler::SCROLL_STARTED:
         result = DID_HANDLE;
         break;
-      case cc::InputHandler::ScrollIgnored:
+      case cc::InputHandler::SCROLL_IGNORED:
         result = DROP_EVENT;
       default:
         result = DID_NOT_HANDLE;
@@ -357,9 +357,9 @@
     }
   } else {
     cc::InputHandler::ScrollStatus scroll_status = input_handler_->ScrollBegin(
-        gfx::Point(wheel_event.x, wheel_event.y), cc::InputHandler::Wheel);
+        gfx::Point(wheel_event.x, wheel_event.y), cc::InputHandler::WHEEL);
     switch (scroll_status) {
-      case cc::InputHandler::ScrollStarted: {
+      case cc::InputHandler::SCROLL_STARTED: {
         TRACE_EVENT_INSTANT2(
             "input", "InputHandlerProxy::handle_input wheel scroll",
             TRACE_EVENT_SCOPE_THREAD, "deltaX", -wheel_event.deltaX, "deltaY",
@@ -372,15 +372,15 @@
         result = scroll_result.did_scroll ? DID_HANDLE : DROP_EVENT;
         break;
       }
-      case cc::InputHandler::ScrollIgnored:
+      case cc::InputHandler::SCROLL_IGNORED:
         // TODO(jamesr): This should be DROP_EVENT, but in cases where we fail
         // to properly sync scrollability it's safer to send the event to the
         // main thread. Change back to DROP_EVENT once we have synchronization
         // bugs sorted out.
         result = DID_NOT_HANDLE;
         break;
-      case cc::InputHandler::ScrollUnknown:
-      case cc::InputHandler::ScrollOnMainThread:
+      case cc::InputHandler::SCROLL_UNKNOWN:
+      case cc::InputHandler::SCROLL_ON_MAIN_THREAD:
         result = DID_NOT_HANDLE;
         break;
       case cc::InputHandler::ScrollStatusCount:
@@ -414,22 +414,21 @@
   expect_scroll_update_end_ = true;
 #endif
   cc::InputHandler::ScrollStatus scroll_status = input_handler_->ScrollBegin(
-      gfx::Point(gesture_event.x, gesture_event.y),
-      cc::InputHandler::Gesture);
+      gfx::Point(gesture_event.x, gesture_event.y), cc::InputHandler::GESTURE);
   UMA_HISTOGRAM_ENUMERATION("Renderer4.CompositorScrollHitTestResult",
                             scroll_status,
                             cc::InputHandler::ScrollStatusCount);
   switch (scroll_status) {
-    case cc::InputHandler::ScrollStarted:
+    case cc::InputHandler::SCROLL_STARTED:
       TRACE_EVENT_INSTANT0("input",
                            "InputHandlerProxy::handle_input gesture scroll",
                            TRACE_EVENT_SCOPE_THREAD);
       gesture_scroll_on_impl_thread_ = true;
       return DID_HANDLE;
-    case cc::InputHandler::ScrollUnknown:
-    case cc::InputHandler::ScrollOnMainThread:
+    case cc::InputHandler::SCROLL_UNKNOWN:
+    case cc::InputHandler::SCROLL_ON_MAIN_THREAD:
       return DID_NOT_HANDLE;
-    case cc::InputHandler::ScrollIgnored:
+    case cc::InputHandler::SCROLL_IGNORED:
       return DROP_EVENT;
     case cc::InputHandler::ScrollStatusCount:
       NOTREACHED();
@@ -477,10 +476,10 @@
   if (gesture_event.sourceDevice == blink::WebGestureDeviceTouchpad) {
     scroll_status = input_handler_->ScrollBegin(
         gfx::Point(gesture_event.x, gesture_event.y),
-        cc::InputHandler::NonBubblingGesture);
+        cc::InputHandler::NON_BUBBLING_GESTURE);
   } else {
     if (!gesture_scroll_on_impl_thread_)
-      scroll_status = cc::InputHandler::ScrollOnMainThread;
+      scroll_status = cc::InputHandler::SCROLL_ON_MAIN_THREAD;
     else
       scroll_status = input_handler_->FlingScrollBegin();
   }
@@ -490,7 +489,7 @@
 #endif
 
   switch (scroll_status) {
-    case cc::InputHandler::ScrollStarted: {
+    case cc::InputHandler::SCROLL_STARTED: {
       if (gesture_event.sourceDevice == blink::WebGestureDeviceTouchpad)
         input_handler_->ScrollEnd();
 
@@ -524,8 +523,8 @@
       input_handler_->SetNeedsAnimate();
       return DID_HANDLE;
     }
-    case cc::InputHandler::ScrollUnknown:
-    case cc::InputHandler::ScrollOnMainThread: {
+    case cc::InputHandler::SCROLL_UNKNOWN:
+    case cc::InputHandler::SCROLL_ON_MAIN_THREAD: {
       TRACE_EVENT_INSTANT0("input",
                            "InputHandlerProxy::HandleGestureFling::"
                            "scroll_on_main_thread",
@@ -533,7 +532,7 @@
       fling_may_be_active_on_main_thread_ = true;
       return DID_NOT_HANDLE;
     }
-    case cc::InputHandler::ScrollIgnored: {
+    case cc::InputHandler::SCROLL_IGNORED: {
       TRACE_EVENT_INSTANT0(
           "input",
           "InputHandlerProxy::HandleGestureFling::ignored",
@@ -616,8 +615,8 @@
       if (!input_handler_->IsCurrentlyScrollingLayerAt(
               gfx::Point(gesture_event.x, gesture_event.y),
               fling_parameters_.sourceDevice == blink::WebGestureDeviceTouchpad
-                  ? cc::InputHandler::NonBubblingGesture
-                  : cc::InputHandler::Gesture)) {
+                  ? cc::InputHandler::NON_BUBBLING_GESTURE
+                  : cc::InputHandler::GESTURE)) {
         CancelCurrentFling();
         return false;
       }
diff --git a/content/renderer/input/input_handler_proxy_unittest.cc b/content/renderer/input/input_handler_proxy_unittest.cc
index bfbd5cc3..caa771f 100644
--- a/content/renderer/input/input_handler_proxy_unittest.cc
+++ b/content/renderer/input/input_handler_proxy_unittest.cc
@@ -241,7 +241,7 @@
     VERIFY_AND_RESET_MOCKS();
 
     EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
-        .WillOnce(testing::Return(cc::InputHandler::ScrollStarted));
+        .WillOnce(testing::Return(cc::InputHandler::SCROLL_STARTED));
     gesture_.type = WebInputEvent::GestureScrollBegin;
     gesture_.sourceDevice = source_device;
     EXPECT_EQ(expected_disposition_,
@@ -250,7 +250,7 @@
     VERIFY_AND_RESET_MOCKS();
 
     EXPECT_CALL(mock_input_handler_, FlingScrollBegin())
-        .WillOnce(testing::Return(cc::InputHandler::ScrollStarted));
+        .WillOnce(testing::Return(cc::InputHandler::SCROLL_STARTED));
     EXPECT_CALL(mock_input_handler_, SetNeedsAnimate());
 
     gesture_ =
@@ -306,7 +306,7 @@
   VERIFY_AND_RESET_MOCKS();
 
   EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
-      .WillOnce(testing::Return(cc::InputHandler::ScrollStarted));
+      .WillOnce(testing::Return(cc::InputHandler::SCROLL_STARTED));
 
   gesture_.type = WebInputEvent::GestureScrollBegin;
   EXPECT_EQ(expected_disposition_,input_handler_->HandleInputEvent(gesture_));
@@ -351,7 +351,7 @@
   VERIFY_AND_RESET_MOCKS();
 
   EXPECT_CALL(mock_input_handler_, ScrollBegin(::testing::_, ::testing::_))
-      .WillOnce(testing::Return(cc::InputHandler::ScrollOnMainThread));
+      .WillOnce(testing::Return(cc::InputHandler::SCROLL_ON_MAIN_THREAD));
 
   gesture_.type = WebInputEvent::GestureScrollBegin;
   EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
@@ -380,7 +380,7 @@
   VERIFY_AND_RESET_MOCKS();
 
   EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
-      .WillOnce(testing::Return(cc::InputHandler::ScrollIgnored));
+      .WillOnce(testing::Return(cc::InputHandler::SCROLL_IGNORED));
 
   gesture_.type = WebInputEvent::GestureScrollBegin;
   EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
@@ -465,7 +465,7 @@
   VERIFY_AND_RESET_MOCKS();
 
   EXPECT_CALL(mock_input_handler_, ScrollBegin(::testing::_, ::testing::_))
-      .WillOnce(testing::Return(cc::InputHandler::ScrollOnMainThread));
+      .WillOnce(testing::Return(cc::InputHandler::SCROLL_ON_MAIN_THREAD));
 
   gesture_.type = WebInputEvent::GestureScrollBegin;
   EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
@@ -540,7 +540,7 @@
   VERIFY_AND_RESET_MOCKS();
 
   EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
-      .WillOnce(testing::Return(cc::InputHandler::ScrollStarted));
+      .WillOnce(testing::Return(cc::InputHandler::SCROLL_STARTED));
   EXPECT_CALL(mock_input_handler_, ScrollEnd());
   EXPECT_CALL(mock_input_handler_, SetNeedsAnimate());
 
@@ -563,7 +563,7 @@
   VERIFY_AND_RESET_MOCKS();
 
   EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
-      .WillOnce(testing::Return(cc::InputHandler::ScrollOnMainThread));
+      .WillOnce(testing::Return(cc::InputHandler::SCROLL_ON_MAIN_THREAD));
 
   gesture_.type = WebInputEvent::GestureFlingStart;
   gesture_.sourceDevice = blink::WebGestureDeviceTouchpad;
@@ -587,7 +587,7 @@
   VERIFY_AND_RESET_MOCKS();
 
   EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
-      .WillOnce(testing::Return(cc::InputHandler::ScrollIgnored));
+      .WillOnce(testing::Return(cc::InputHandler::SCROLL_IGNORED));
 
   gesture_.type = WebInputEvent::GestureFlingStart;
   gesture_.sourceDevice = blink::WebGestureDeviceTouchpad;
@@ -624,7 +624,7 @@
                          modifiers);
   EXPECT_CALL(mock_input_handler_, SetNeedsAnimate());
   EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
-      .WillOnce(testing::Return(cc::InputHandler::ScrollStarted));
+      .WillOnce(testing::Return(cc::InputHandler::SCROLL_STARTED));
   EXPECT_CALL(mock_input_handler_, ScrollEnd());
   EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
 
@@ -645,7 +645,7 @@
   // The second call should start scrolling in the -X direction.
   EXPECT_CALL(mock_input_handler_, SetNeedsAnimate());
   EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
-      .WillOnce(testing::Return(cc::InputHandler::ScrollStarted));
+      .WillOnce(testing::Return(cc::InputHandler::SCROLL_STARTED));
   EXPECT_CALL(mock_input_handler_,
               ScrollBy(testing::_,
                        testing::Property(&gfx::Vector2dF::x, testing::Lt(0))))
@@ -662,7 +662,7 @@
   // rest of the fling can be
   // transferred to the main thread.
   EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
-      .WillOnce(testing::Return(cc::InputHandler::ScrollOnMainThread));
+      .WillOnce(testing::Return(cc::InputHandler::SCROLL_ON_MAIN_THREAD));
   EXPECT_CALL(mock_input_handler_, ScrollBy(testing::_, testing::_)).Times(0);
   EXPECT_CALL(mock_input_handler_, ScrollEnd()).Times(0);
   // Expected wheel fling animation parameters:
@@ -729,7 +729,7 @@
                          modifiers);
   EXPECT_CALL(mock_input_handler_, SetNeedsAnimate());
   EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
-      .WillOnce(testing::Return(cc::InputHandler::ScrollStarted));
+      .WillOnce(testing::Return(cc::InputHandler::SCROLL_STARTED));
   EXPECT_CALL(mock_input_handler_, ScrollEnd());
   EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
 
@@ -748,7 +748,7 @@
   // The second call should start scrolling in the -X direction.
   EXPECT_CALL(mock_input_handler_, SetNeedsAnimate());
   EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
-      .WillOnce(testing::Return(cc::InputHandler::ScrollStarted));
+      .WillOnce(testing::Return(cc::InputHandler::SCROLL_STARTED));
   EXPECT_CALL(mock_input_handler_,
               ScrollBy(testing::_,
                        testing::Property(&gfx::Vector2dF::x, testing::Lt(0))))
@@ -765,7 +765,7 @@
   // rest of the fling can be
   // transferred to the main thread.
   EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
-      .WillOnce(testing::Return(cc::InputHandler::ScrollOnMainThread));
+      .WillOnce(testing::Return(cc::InputHandler::SCROLL_ON_MAIN_THREAD));
   EXPECT_CALL(mock_input_handler_, ScrollBy(testing::_, testing::_)).Times(0);
   EXPECT_CALL(mock_input_handler_, ScrollEnd()).Times(0);
 
@@ -830,7 +830,7 @@
                          modifiers);
   EXPECT_CALL(mock_input_handler_, SetNeedsAnimate());
   EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
-      .WillOnce(testing::Return(cc::InputHandler::ScrollStarted));
+      .WillOnce(testing::Return(cc::InputHandler::SCROLL_STARTED));
   EXPECT_CALL(mock_input_handler_, ScrollEnd());
   expected_disposition_ = InputHandlerProxy::DID_HANDLE;
   EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
@@ -849,7 +849,7 @@
   // Tick the second fling once normally.
   EXPECT_CALL(mock_input_handler_, SetNeedsAnimate());
   EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
-      .WillOnce(testing::Return(cc::InputHandler::ScrollStarted));
+      .WillOnce(testing::Return(cc::InputHandler::SCROLL_STARTED));
   EXPECT_CALL(mock_input_handler_,
               ScrollBy(testing::_,
                        testing::Property(&gfx::Vector2dF::y, testing::Gt(0))))
@@ -862,7 +862,7 @@
 
   // Then abort the second fling.
   EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
-      .WillOnce(testing::Return(cc::InputHandler::ScrollOnMainThread));
+      .WillOnce(testing::Return(cc::InputHandler::SCROLL_ON_MAIN_THREAD));
   EXPECT_CALL(mock_input_handler_, ScrollBy(testing::_, testing::_)).Times(0);
   EXPECT_CALL(mock_input_handler_, ScrollEnd()).Times(0);
 
@@ -893,7 +893,7 @@
   VERIFY_AND_RESET_MOCKS();
 
   EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
-      .WillOnce(testing::Return(cc::InputHandler::ScrollStarted));
+      .WillOnce(testing::Return(cc::InputHandler::SCROLL_STARTED));
   gesture_.type = WebInputEvent::GestureScrollBegin;
   gesture_.sourceDevice = blink::WebGestureDeviceTouchscreen;
   EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
@@ -901,7 +901,7 @@
   VERIFY_AND_RESET_MOCKS();
 
   EXPECT_CALL(mock_input_handler_, FlingScrollBegin())
-      .WillOnce(testing::Return(cc::InputHandler::ScrollStarted));
+      .WillOnce(testing::Return(cc::InputHandler::SCROLL_STARTED));
   EXPECT_CALL(mock_input_handler_, SetNeedsAnimate());
 
   gesture_.type = WebInputEvent::GestureFlingStart;
@@ -925,7 +925,7 @@
   VERIFY_AND_RESET_MOCKS();
 
   EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
-      .WillOnce(testing::Return(cc::InputHandler::ScrollOnMainThread));
+      .WillOnce(testing::Return(cc::InputHandler::SCROLL_ON_MAIN_THREAD));
 
   gesture_.type = WebInputEvent::GestureScrollBegin;
   EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
@@ -952,7 +952,7 @@
   VERIFY_AND_RESET_MOCKS();
 
   EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
-      .WillOnce(testing::Return(cc::InputHandler::ScrollStarted));
+      .WillOnce(testing::Return(cc::InputHandler::SCROLL_STARTED));
 
   gesture_.type = WebInputEvent::GestureScrollBegin;
   gesture_.sourceDevice = blink::WebGestureDeviceTouchscreen;
@@ -962,7 +962,7 @@
   VERIFY_AND_RESET_MOCKS();
 
   EXPECT_CALL(mock_input_handler_, FlingScrollBegin())
-      .WillOnce(testing::Return(cc::InputHandler::ScrollIgnored));
+      .WillOnce(testing::Return(cc::InputHandler::SCROLL_IGNORED));
 
   gesture_.type = WebInputEvent::GestureFlingStart;
   gesture_.sourceDevice = blink::WebGestureDeviceTouchscreen;
@@ -983,7 +983,7 @@
   VERIFY_AND_RESET_MOCKS();
 
   EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
-      .WillOnce(testing::Return(cc::InputHandler::ScrollStarted));
+      .WillOnce(testing::Return(cc::InputHandler::SCROLL_STARTED));
 
   gesture_.type = WebInputEvent::GestureScrollBegin;
   gesture_.sourceDevice = blink::WebGestureDeviceTouchscreen;
@@ -1005,7 +1005,7 @@
                          modifiers);
   EXPECT_CALL(mock_input_handler_, SetNeedsAnimate());
   EXPECT_CALL(mock_input_handler_, FlingScrollBegin())
-      .WillOnce(testing::Return(cc::InputHandler::ScrollStarted));
+      .WillOnce(testing::Return(cc::InputHandler::SCROLL_STARTED));
   EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
 
   testing::Mock::VerifyAndClearExpectations(&mock_input_handler_);
@@ -1042,7 +1042,7 @@
   VERIFY_AND_RESET_MOCKS();
 
   EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
-      .WillOnce(testing::Return(cc::InputHandler::ScrollStarted));
+      .WillOnce(testing::Return(cc::InputHandler::SCROLL_STARTED));
 
   gesture_.type = WebInputEvent::GestureScrollBegin;
   gesture_.sourceDevice = blink::WebGestureDeviceTouchscreen;
@@ -1066,7 +1066,7 @@
                          modifiers);
   EXPECT_CALL(mock_input_handler_, SetNeedsAnimate());
   EXPECT_CALL(mock_input_handler_, FlingScrollBegin())
-      .WillOnce(testing::Return(cc::InputHandler::ScrollStarted));
+      .WillOnce(testing::Return(cc::InputHandler::SCROLL_STARTED));
   EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
 
   testing::Mock::VerifyAndClearExpectations(&mock_input_handler_);
@@ -1096,7 +1096,7 @@
   VERIFY_AND_RESET_MOCKS();
 
   EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
-      .WillOnce(testing::Return(cc::InputHandler::ScrollStarted));
+      .WillOnce(testing::Return(cc::InputHandler::SCROLL_STARTED));
 
   gesture_.type = WebInputEvent::GestureScrollBegin;
   gesture_.sourceDevice = blink::WebGestureDeviceTouchscreen;
@@ -1123,7 +1123,7 @@
   gesture_.modifiers = modifiers;
   EXPECT_CALL(mock_input_handler_, SetNeedsAnimate());
   EXPECT_CALL(mock_input_handler_, FlingScrollBegin())
-      .WillOnce(testing::Return(cc::InputHandler::ScrollStarted));
+      .WillOnce(testing::Return(cc::InputHandler::SCROLL_STARTED));
   EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
 
   testing::Mock::VerifyAndClearExpectations(&mock_input_handler_);
@@ -1160,7 +1160,7 @@
   VERIFY_AND_RESET_MOCKS();
 
   EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
-      .WillOnce(testing::Return(cc::InputHandler::ScrollStarted));
+      .WillOnce(testing::Return(cc::InputHandler::SCROLL_STARTED));
 
   gesture_.type = WebInputEvent::GestureScrollBegin;
   EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
@@ -1185,7 +1185,7 @@
                          modifiers);
   EXPECT_CALL(mock_input_handler_, SetNeedsAnimate());
   EXPECT_CALL(mock_input_handler_, FlingScrollBegin())
-      .WillOnce(testing::Return(cc::InputHandler::ScrollStarted));
+      .WillOnce(testing::Return(cc::InputHandler::SCROLL_STARTED));
   EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
 
   // |gesture_scroll_on_impl_thread_| should still be true after
@@ -1236,7 +1236,7 @@
   gesture_.data.flingStart.velocityX = fling_delta.x;
   gesture_.data.flingStart.velocityY = fling_delta.y;
   EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
-      .WillOnce(testing::Return(cc::InputHandler::ScrollStarted));
+      .WillOnce(testing::Return(cc::InputHandler::SCROLL_STARTED));
   EXPECT_CALL(mock_input_handler_, ScrollEnd());
   EXPECT_CALL(mock_input_handler_, SetNeedsAnimate());
   EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
@@ -1250,7 +1250,7 @@
 
   // The second animate starts scrolling in the positive X and Y directions.
   EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
-      .WillOnce(testing::Return(cc::InputHandler::ScrollStarted));
+      .WillOnce(testing::Return(cc::InputHandler::SCROLL_STARTED));
   EXPECT_CALL(mock_input_handler_,
               ScrollBy(testing::_,
                        testing::Property(&gfx::Vector2dF::y, testing::Lt(0))))
@@ -1269,7 +1269,7 @@
   overscroll.accumulated_root_overscroll = gfx::Vector2dF(0, 100);
   overscroll.unused_scroll_delta = gfx::Vector2dF(0, 10);
   EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
-      .WillOnce(testing::Return(cc::InputHandler::ScrollStarted));
+      .WillOnce(testing::Return(cc::InputHandler::SCROLL_STARTED));
   EXPECT_CALL(mock_input_handler_,
               ScrollBy(testing::_,
                        testing::Property(&gfx::Vector2dF::y, testing::Lt(0))))
@@ -1295,7 +1295,7 @@
   // The next call to animate will no longer scroll vertically.
   EXPECT_CALL(mock_input_handler_, SetNeedsAnimate());
   EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
-      .WillOnce(testing::Return(cc::InputHandler::ScrollStarted));
+      .WillOnce(testing::Return(cc::InputHandler::SCROLL_STARTED));
   EXPECT_CALL(mock_input_handler_,
               ScrollBy(testing::_,
                        testing::Property(&gfx::Vector2dF::y, testing::Eq(0))))
@@ -1312,7 +1312,7 @@
   VERIFY_AND_RESET_MOCKS();
 
   EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
-      .WillOnce(testing::Return(cc::InputHandler::ScrollStarted));
+      .WillOnce(testing::Return(cc::InputHandler::SCROLL_STARTED));
 
   gesture_.type = WebInputEvent::GestureScrollBegin;
   gesture_.sourceDevice = blink::WebGestureDeviceTouchscreen;
@@ -1336,7 +1336,7 @@
                          modifiers);
   EXPECT_CALL(mock_input_handler_, SetNeedsAnimate());
   EXPECT_CALL(mock_input_handler_, FlingScrollBegin())
-      .WillOnce(testing::Return(cc::InputHandler::ScrollStarted));
+      .WillOnce(testing::Return(cc::InputHandler::SCROLL_STARTED));
   EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
 
   testing::Mock::VerifyAndClearExpectations(&mock_input_handler_);
@@ -1393,7 +1393,7 @@
   VERIFY_AND_RESET_MOCKS();
 
   EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
-      .WillOnce(testing::Return(cc::InputHandler::ScrollStarted));
+      .WillOnce(testing::Return(cc::InputHandler::SCROLL_STARTED));
   gesture_.type = WebInputEvent::GestureScrollBegin;
   gesture_.sourceDevice = blink::WebGestureDeviceTouchscreen;
   EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
@@ -1406,7 +1406,7 @@
   gesture_.data.flingStart.velocityX = fling_delta.x;
   gesture_.data.flingStart.velocityY = fling_delta.y;
   EXPECT_CALL(mock_input_handler_, FlingScrollBegin())
-      .WillOnce(testing::Return(cc::InputHandler::ScrollStarted));
+      .WillOnce(testing::Return(cc::InputHandler::SCROLL_STARTED));
   EXPECT_CALL(mock_input_handler_, SetNeedsAnimate());
   EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
   testing::Mock::VerifyAndClearExpectations(&mock_input_handler_);
@@ -1553,7 +1553,7 @@
   VERIFY_AND_RESET_MOCKS();
 
   EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
-      .WillOnce(testing::Return(cc::InputHandler::ScrollStarted));
+      .WillOnce(testing::Return(cc::InputHandler::SCROLL_STARTED));
   gesture_.type = WebInputEvent::GestureScrollBegin;
   gesture_.sourceDevice = blink::WebGestureDeviceTouchscreen;
   EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
@@ -1574,7 +1574,7 @@
   gesture_.data.flingStart.velocityX = fling_delta.x;
   gesture_.data.flingStart.velocityY = fling_delta.y;
   EXPECT_CALL(mock_input_handler_, FlingScrollBegin())
-      .WillOnce(testing::Return(cc::InputHandler::ScrollStarted));
+      .WillOnce(testing::Return(cc::InputHandler::SCROLL_STARTED));
   EXPECT_CALL(mock_input_handler_, SetNeedsAnimate());
   EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
   EXPECT_TRUE(input_handler_->gesture_scroll_on_impl_thread_for_testing());
@@ -1605,7 +1605,7 @@
   VERIFY_AND_RESET_MOCKS();
 
   EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
-      .WillOnce(testing::Return(cc::InputHandler::ScrollStarted));
+      .WillOnce(testing::Return(cc::InputHandler::SCROLL_STARTED));
 
   gesture_.type = WebInputEvent::GestureScrollBegin;
   gesture_.sourceDevice = blink::WebGestureDeviceTouchscreen;
@@ -1629,7 +1629,7 @@
                          modifiers);
   EXPECT_CALL(mock_input_handler_, SetNeedsAnimate());
   EXPECT_CALL(mock_input_handler_, FlingScrollBegin())
-      .WillOnce(testing::Return(cc::InputHandler::ScrollStarted));
+      .WillOnce(testing::Return(cc::InputHandler::SCROLL_STARTED));
   EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
 
   testing::Mock::VerifyAndClearExpectations(&mock_input_handler_);
@@ -1818,7 +1818,7 @@
       .WillOnce(testing::Return(false));
   EXPECT_CALL(mock_input_handler_, ScrollEnd());
   EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
-      .WillOnce(testing::Return(cc::InputHandler::ScrollStarted));
+      .WillOnce(testing::Return(cc::InputHandler::SCROLL_STARTED));
 
   time += dt;
   gesture_.timeStampSeconds = InSecondsF(time);
@@ -1859,7 +1859,7 @@
   time += base::TimeDelta::FromMilliseconds(100);
   EXPECT_CALL(mock_input_handler_, ScrollEnd());
   EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
-      .WillOnce(testing::Return(cc::InputHandler::ScrollStarted));
+      .WillOnce(testing::Return(cc::InputHandler::SCROLL_STARTED));
   input_handler_->Animate(time);
 
   VERIFY_AND_RESET_MOCKS();
@@ -1941,7 +1941,7 @@
   gesture_.data.scrollUpdate.deltaX = -fling_delta.x;
   EXPECT_CALL(mock_input_handler_, ScrollEnd());
   EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
-      .WillOnce(testing::Return(cc::InputHandler::ScrollStarted));
+      .WillOnce(testing::Return(cc::InputHandler::SCROLL_STARTED));
   EXPECT_CALL(mock_input_handler_,
               ScrollBy(testing::_,
                        testing::Property(&gfx::Vector2dF::x,
@@ -2051,7 +2051,7 @@
       .WillOnce(testing::Return(scroll_result_did_not_scroll_));
   EXPECT_CALL(mock_input_handler_, ScrollEnd());
   EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
-      .WillOnce(testing::Return(cc::InputHandler::ScrollStarted));
+      .WillOnce(testing::Return(cc::InputHandler::SCROLL_STARTED));
   input_handler_->Animate(time);
 
   VERIFY_AND_RESET_MOCKS();
@@ -2113,7 +2113,7 @@
   gesture_.data.flingStart.velocityY = fling_delta.y;
   EXPECT_CALL(mock_input_handler_, SetNeedsAnimate());
   EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
-      .WillOnce(testing::Return(cc::InputHandler::ScrollStarted));
+      .WillOnce(testing::Return(cc::InputHandler::SCROLL_STARTED));
   EXPECT_CALL(mock_input_handler_, ScrollEnd());
   EXPECT_CALL(mock_client,
               DidReceiveInputEvent(Field(&WebInputEvent::type,
diff --git a/content/renderer/input/input_scroll_elasticity_controller.cc b/content/renderer/input/input_scroll_elasticity_controller.cc
index 0ab8cb0f..00d078ff 100644
--- a/content/renderer/input/input_scroll_elasticity_controller.cc
+++ b/content/renderer/input/input_scroll_elasticity_controller.cc
@@ -182,6 +182,12 @@
 void InputScrollElasticityController::Overscroll(
     const gfx::Vector2dF& input_delta,
     const gfx::Vector2dF& overscroll_delta) {
+  // The effect can be dynamically disabled by setting disallowing user
+  // scrolling. When disabled, disallow active or momentum overscrolling, but
+  // allow any current overscroll to animate back.
+  if (!helper_->IsUserScrollable())
+    return;
+
   gfx::Vector2dF adjusted_overscroll_delta =
       pending_overscroll_delta_ + overscroll_delta;
   pending_overscroll_delta_ = gfx::Vector2dF();
diff --git a/content/renderer/input/input_scroll_elasticity_controller_unittest.cc b/content/renderer/input/input_scroll_elasticity_controller_unittest.cc
index 3adaa055..fa2c7f19 100644
--- a/content/renderer/input/input_scroll_elasticity_controller_unittest.cc
+++ b/content/renderer/input/input_scroll_elasticity_controller_unittest.cc
@@ -23,11 +23,13 @@
 class MockScrollElasticityHelper : public cc::ScrollElasticityHelper {
  public:
   MockScrollElasticityHelper()
-      : set_stretch_amount_count_(0),
+      : is_user_scrollable_(true),
+        set_stretch_amount_count_(0),
         request_animate_count_(0) {}
   ~MockScrollElasticityHelper() override {}
 
   // cc::ScrollElasticityHelper implementation:
+  bool IsUserScrollable() const override { return is_user_scrollable_; }
   gfx::Vector2dF StretchAmount() const override { return stretch_amount_; }
   void SetStretchAmount(const gfx::Vector2dF& stretch_amount) override {
     set_stretch_amount_count_ += 1;
@@ -52,8 +54,12 @@
     scroll_offset_ = scroll_offset;
     max_scroll_offset_ = max_scroll_offset;
   }
+  void SetUserScrollable(bool is_user_scrollable) {
+    is_user_scrollable_ = is_user_scrollable;
+  }
 
  private:
+  bool is_user_scrollable_;
   gfx::Vector2dF stretch_amount_;
   int set_stretch_amount_count_;
   int request_animate_count_;
@@ -327,5 +333,55 @@
   EXPECT_EQ(helper_.ScrollOffset(), gfx::ScrollOffset(7, 8));
 }
 
+// Verify that stretching only happens when the area is user scrollable.
+TEST_F(ScrollElasticityControllerTest, UserScrollableRequiredForStretch) {
+  helper_.SetScrollOffsetAndMaxScrollOffset(gfx::ScrollOffset(0, 0),
+                                            gfx::ScrollOffset(10, 10));
+  gfx::Vector2dF delta(0, -15);
+
+  // Do an active scroll, and ensure that the stretch amount doesn't change,
+  // and also that the stretch amount isn't even ever changed.
+  helper_.SetUserScrollable(false);
+  SendMouseWheelEvent(PhaseBegan, PhaseNone);
+  SendMouseWheelEvent(PhaseChanged, PhaseNone, delta, delta);
+  SendMouseWheelEvent(PhaseChanged, PhaseNone, delta, delta);
+  SendMouseWheelEvent(PhaseEnded, PhaseNone);
+  EXPECT_EQ(helper_.StretchAmount(), gfx::Vector2dF(0, 0));
+  EXPECT_EQ(0, helper_.set_stretch_amount_count());
+  SendMouseWheelEvent(PhaseNone, PhaseBegan);
+  SendMouseWheelEvent(PhaseNone, PhaseChanged, delta, delta);
+  SendMouseWheelEvent(PhaseNone, PhaseChanged, delta, delta);
+  SendMouseWheelEvent(PhaseNone, PhaseEnded);
+  EXPECT_EQ(helper_.StretchAmount(), gfx::Vector2dF(0, 0));
+  EXPECT_EQ(0, helper_.set_stretch_amount_count());
+
+  // Re-enable user scrolling and ensure that stretching is re-enabled.
+  helper_.SetUserScrollable(true);
+  SendMouseWheelEvent(PhaseBegan, PhaseNone);
+  SendMouseWheelEvent(PhaseChanged, PhaseNone, delta, delta);
+  SendMouseWheelEvent(PhaseChanged, PhaseNone, delta, delta);
+  SendMouseWheelEvent(PhaseEnded, PhaseNone);
+  EXPECT_NE(helper_.StretchAmount(), gfx::Vector2dF(0, 0));
+  EXPECT_GT(helper_.set_stretch_amount_count(), 0);
+  SendMouseWheelEvent(PhaseNone, PhaseBegan);
+  SendMouseWheelEvent(PhaseNone, PhaseChanged, delta, delta);
+  SendMouseWheelEvent(PhaseNone, PhaseChanged, delta, delta);
+  SendMouseWheelEvent(PhaseNone, PhaseEnded);
+  EXPECT_NE(helper_.StretchAmount(), gfx::Vector2dF(0, 0));
+  EXPECT_GT(helper_.set_stretch_amount_count(), 0);
+
+  // Disable user scrolling and tick the timer until the stretch goes back
+  // to zero. Ensure that the return to zero doesn't happen immediately.
+  helper_.SetUserScrollable(false);
+  int ticks_to_zero = 0;
+  while (1) {
+    TickCurrentTimeAndAnimate();
+    if (helper_.StretchAmount().IsZero())
+      break;
+    ticks_to_zero += 1;
+  }
+  EXPECT_GT(ticks_to_zero, 3);
+}
+
 }  // namespace
 }  // namespace content
diff --git a/content/renderer/media/midi_message_filter.cc b/content/renderer/media/midi_message_filter.cc
index 03a9d000..258d04d6 100644
--- a/content/renderer/media/midi_message_filter.cc
+++ b/content/renderer/media/midi_message_filter.cc
@@ -116,6 +116,8 @@
     IPC_MESSAGE_HANDLER(MidiMsg_SessionStarted, OnSessionStarted)
     IPC_MESSAGE_HANDLER(MidiMsg_AddInputPort, OnAddInputPort)
     IPC_MESSAGE_HANDLER(MidiMsg_AddOutputPort, OnAddOutputPort)
+    IPC_MESSAGE_HANDLER(MidiMsg_SetInputPortState, OnSetInputPortState)
+    IPC_MESSAGE_HANDLER(MidiMsg_SetOutputPortState, OnSetOutputPortState)
     IPC_MESSAGE_HANDLER(MidiMsg_DataReceived, OnDataReceived)
     IPC_MESSAGE_HANDLER(MidiMsg_AcknowledgeSentData, OnAcknowledgeSentData)
     IPC_MESSAGE_UNHANDLED(handled = false)
@@ -163,6 +165,24 @@
       base::Bind(&MidiMessageFilter::HandleAddOutputPort, this, info));
 }
 
+void MidiMessageFilter::OnSetInputPortState(uint32 port,
+                                            media::MidiPortState state) {
+  DCHECK(io_message_loop_->BelongsToCurrentThread());
+  main_message_loop_->PostTask(
+      FROM_HERE,
+      base::Bind(&MidiMessageFilter::HandleSetInputPortState,
+                 this, port, state));
+}
+
+void MidiMessageFilter::OnSetOutputPortState(uint32 port,
+                                             media::MidiPortState state) {
+  DCHECK(io_message_loop_->BelongsToCurrentThread());
+  main_message_loop_->PostTask(
+      FROM_HERE,
+      base::Bind(&MidiMessageFilter::HandleSetOutputPortState,
+                 this, port, state));
+}
+
 void MidiMessageFilter::OnDataReceived(uint32 port,
                                        const std::vector<uint8>& data,
                                        double timestamp) {
@@ -265,4 +285,18 @@
     unacknowledged_bytes_sent_ -= bytes_sent;
 }
 
+void MidiMessageFilter::HandleSetInputPortState(uint32 port,
+                                                media::MidiPortState state) {
+  DCHECK(main_message_loop_->BelongsToCurrentThread());
+  inputs_[port].state = state;
+  // TODO(toyoshim): Notify to all clients.
+}
+
+void MidiMessageFilter::HandleSetOutputPortState(uint32 port,
+                                                 media::MidiPortState state) {
+  DCHECK(main_message_loop_->BelongsToCurrentThread());
+  outputs_[port].state = state;
+  // TODO(toyoshim): Notify to all clients.
+}
+
 }  // namespace content
diff --git a/content/renderer/media/midi_message_filter.h b/content/renderer/media/midi_message_filter.h
index f7e1a84..83e760ab 100644
--- a/content/renderer/media/midi_message_filter.h
+++ b/content/renderer/media/midi_message_filter.h
@@ -77,6 +77,12 @@
   void OnAddInputPort(media::MidiPortInfo info);
   void OnAddOutputPort(media::MidiPortInfo info);
 
+  // These functions are called to notify the recipient that a device that is
+  // notified via OnAddInputPort() or OnAddOutputPort() gets disconnected, or
+  // connected again.
+  void OnSetInputPortState(uint32 port, media::MidiPortState state);
+  void OnSetOutputPortState(uint32 port, media::MidiPortState state);
+
   // Called when the browser process has sent MIDI data containing one or
   // more messages.
   void OnDataReceived(uint32 port,
@@ -93,6 +99,8 @@
 
   void HandleAddInputPort(media::MidiPortInfo info);
   void HandleAddOutputPort(media::MidiPortInfo info);
+  void HandleSetInputPortState(uint32 port, media::MidiPortState state);
+  void HandleSetOutputPortState(uint32 port, media::MidiPortState state);
 
   void HandleDataReceived(uint32 port,
                           const std::vector<uint8>& data,
diff --git a/content/renderer/media/render_media_client_unittest.cc b/content/renderer/media/render_media_client_unittest.cc
index e796e46..2366156 100644
--- a/content/renderer/media/render_media_client_unittest.cc
+++ b/content/renderer/media/render_media_client_unittest.cc
@@ -24,10 +24,32 @@
   // ContentRendererClient implementation.
   void AddKeySystems(
       std::vector<media::KeySystemInfo>* key_systems_info) override {
-    media::KeySystemInfo key_system_info("test.keysystem");
+    // TODO(sandersd): Was this supposed to be added to the list?
+    media::KeySystemInfo key_system_info;
+    key_system_info.key_system = "test.keysystem";
+    key_system_info.persistent_license_support =
+        media::EME_SESSION_TYPE_NOT_SUPPORTED;
+    key_system_info.persistent_release_message_support =
+        media::EME_SESSION_TYPE_NOT_SUPPORTED;
+    key_system_info.persistent_state_support =
+        media::EME_FEATURE_NOT_SUPPORTED;
+    key_system_info.distinctive_identifier_support =
+        media::EME_FEATURE_NOT_SUPPORTED;
+    key_systems_info->push_back(key_system_info);
 #if defined(WIDEVINE_CDM_AVAILABLE) && defined(WIDEVINE_CDM_IS_COMPONENT)
-    if (is_extra_key_system_enabled_)
-      key_systems_info->push_back(media::KeySystemInfo(kWidevineKeySystem));
+    if (is_extra_key_system_enabled_) {
+      media::KeySystemInfo wv_key_system_info;
+      wv_key_system_info.key_system = kWidevineKeySystem;
+      wv_key_system_info.persistent_license_support =
+          media::EME_SESSION_TYPE_NOT_SUPPORTED;
+      wv_key_system_info.persistent_release_message_support =
+          media::EME_SESSION_TYPE_NOT_SUPPORTED;
+      wv_key_system_info.persistent_state_support =
+          media::EME_FEATURE_NOT_SUPPORTED;
+      wv_key_system_info.distinctive_identifier_support =
+          media::EME_FEATURE_NOT_SUPPORTED;
+      key_systems_info->push_back(wv_key_system_info);
+    }
 #endif
   }
 
diff --git a/content/renderer/media/user_media_client_impl.cc b/content/renderer/media/user_media_client_impl.cc
index 316d62ae..756f3a2 100644
--- a/content/renderer/media/user_media_client_impl.cc
+++ b/content/renderer/media/user_media_client_impl.cc
@@ -499,7 +499,7 @@
     return;
   }
 
-  GetUserMediaRequestFailed(&request_info->request, result);
+  GetUserMediaRequestFailed(&request_info->request, result, "");
   DeleteUserMediaRequestInfo(request_info);
 }
 
@@ -654,9 +654,7 @@
   if (result == content::MEDIA_DEVICE_OK)
     GetUserMediaRequestSucceeded(request->web_stream, &request->request);
   else
-    GetUserMediaRequestTrackStartedFailed(&request->request,
-                                          result,
-                                          result_name);
+    GetUserMediaRequestFailed(&request->request, result, result_name);
 
   DeleteUserMediaRequestInfo(request);
 }
@@ -724,59 +722,53 @@
 
 void UserMediaClientImpl::GetUserMediaRequestFailed(
     blink::WebUserMediaRequest* request_info,
-    MediaStreamRequestResult result) {
+    MediaStreamRequestResult result,
+    const blink::WebString& result_name) {
   LogUserMediaRequestResult(result);
   switch (result) {
     case MEDIA_DEVICE_OK:
+    case NUM_MEDIA_REQUEST_RESULTS:
       NOTREACHED();
-      break;
+      return;
     case MEDIA_DEVICE_PERMISSION_DENIED:
       request_info->requestDenied();
-      break;
+      return;
     case MEDIA_DEVICE_PERMISSION_DISMISSED:
       request_info->requestFailedUASpecific("PermissionDismissedError");
-      break;
+      return;
     case MEDIA_DEVICE_INVALID_STATE:
       request_info->requestFailedUASpecific("InvalidStateError");
-      break;
+      return;
     case MEDIA_DEVICE_NO_HARDWARE:
       request_info->requestFailedUASpecific("DevicesNotFoundError");
-      break;
+      return;
     case MEDIA_DEVICE_INVALID_SECURITY_ORIGIN:
       request_info->requestFailedUASpecific("InvalidSecurityOriginError");
-      break;
+      return;
     case MEDIA_DEVICE_TAB_CAPTURE_FAILURE:
       request_info->requestFailedUASpecific("TabCaptureError");
-      break;
+      return;
     case MEDIA_DEVICE_SCREEN_CAPTURE_FAILURE:
       request_info->requestFailedUASpecific("ScreenCaptureError");
-      break;
+      return;
     case MEDIA_DEVICE_CAPTURE_FAILURE:
       request_info->requestFailedUASpecific("DeviceCaptureError");
-      break;
-    default:
-      NOTREACHED();
-      request_info->requestFailed();
-      break;
-  }
-}
-
-void UserMediaClientImpl::GetUserMediaRequestTrackStartedFailed(
-    blink::WebUserMediaRequest* request_info,
-    MediaStreamRequestResult result,
-    const blink::WebString& result_name) {
-  switch (result) {
+      return;
     case MEDIA_DEVICE_CONSTRAINT_NOT_SATISFIED:
       request_info->requestFailedConstraint(result_name);
-      break;
+      return;
     case MEDIA_DEVICE_TRACK_START_FAILURE:
       request_info->requestFailedUASpecific("TrackStartError");
-      break;
-    default:
-      NOTREACHED();
-      request_info->requestFailed();
-      break;
+      return;
+    case MEDIA_DEVICE_NOT_SUPPORTED:
+      request_info->requestFailedUASpecific("MediaDeviceNotSupported");
+      return;
+    case MEDIA_DEVICE_FAILED_DUE_TO_SHUTDOWN:
+      request_info->requestFailedUASpecific("MediaDeviceFailedDueToShutdown");
+      return;
   }
+  NOTREACHED();
+  request_info->requestFailed();
 }
 
 void UserMediaClientImpl::EnumerateDevicesSucceded(
diff --git a/content/renderer/media/user_media_client_impl.h b/content/renderer/media/user_media_client_impl.h
index 9ec1e736..a9bd398 100644
--- a/content/renderer/media/user_media_client_impl.h
+++ b/content/renderer/media/user_media_client_impl.h
@@ -99,9 +99,6 @@
        blink::WebUserMediaRequest* request_info);
   virtual void GetUserMediaRequestFailed(
       blink::WebUserMediaRequest* request_info,
-      MediaStreamRequestResult result);
-  virtual void GetUserMediaRequestTrackStartedFailed(
-      blink::WebUserMediaRequest* request_info,
       MediaStreamRequestResult result,
       const blink::WebString& result_name);
   virtual void EnumerateDevicesSucceded(
diff --git a/content/renderer/media/user_media_client_impl_unittest.cc b/content/renderer/media/user_media_client_impl_unittest.cc
index 35c41a06..08d5c8a 100644
--- a/content/renderer/media/user_media_client_impl_unittest.cc
+++ b/content/renderer/media/user_media_client_impl_unittest.cc
@@ -84,15 +84,7 @@
 
   void GetUserMediaRequestFailed(
       blink::WebUserMediaRequest* request_info,
-      content::MediaStreamRequestResult result) override {
-    last_generated_stream_.reset();
-    state_ = REQUEST_FAILED;
-    result_ = result;
-  }
-
-  void GetUserMediaRequestTrackStartedFailed(
-      blink::WebUserMediaRequest* request_info,
-      MediaStreamRequestResult result,
+      content::MediaStreamRequestResult result,
       const blink::WebString& result_name) override {
     last_generated_stream_.reset();
     state_ = REQUEST_FAILED;
diff --git a/content/renderer/media/webrtc/peer_connection_dependency_factory.cc b/content/renderer/media/webrtc/peer_connection_dependency_factory.cc
index fb09fafb..d9f485b 100644
--- a/content/renderer/media/webrtc/peer_connection_dependency_factory.cc
+++ b/content/renderer/media/webrtc/peer_connection_dependency_factory.cc
@@ -11,6 +11,7 @@
 #include "base/synchronization/waitable_event.h"
 #include "content/common/media/media_stream_messages.h"
 #include "content/public/common/content_switches.h"
+#include "content/public/common/renderer_preferences.h"
 #include "content/renderer/media/media_stream.h"
 #include "content/renderer/media/media_stream_audio_processor.h"
 #include "content/renderer/media/media_stream_audio_processor_options.h"
@@ -33,6 +34,7 @@
 #include "content/renderer/p2p/ipc_socket_factory.h"
 #include "content/renderer/p2p/port_allocator.h"
 #include "content/renderer/render_thread_impl.h"
+#include "content/renderer/render_view_impl.h"
 #include "jingle/glue/thread_wrapper.h"
 #include "media/filters/gpu_video_accelerator_factories.h"
 #include "third_party/WebKit/public/platform/WebMediaConstraints.h"
@@ -114,14 +116,14 @@
 
 class P2PPortAllocatorFactory : public webrtc::PortAllocatorFactoryInterface {
  public:
-  P2PPortAllocatorFactory(
-      P2PSocketDispatcher* socket_dispatcher,
-      rtc::NetworkManager* network_manager,
-      rtc::PacketSocketFactory* socket_factory)
+  P2PPortAllocatorFactory(P2PSocketDispatcher* socket_dispatcher,
+                          rtc::NetworkManager* network_manager,
+                          rtc::PacketSocketFactory* socket_factory,
+                          bool enable_multiple_routes)
       : socket_dispatcher_(socket_dispatcher),
         network_manager_(network_manager),
-        socket_factory_(socket_factory) {
-  }
+        socket_factory_(socket_factory),
+        enable_multiple_routes_(enable_multiple_routes) {}
 
   cricket::PortAllocator* CreatePortAllocator(
       const std::vector<StunConfiguration>& stun_servers,
@@ -147,6 +149,7 @@
           turn_configurations[i].server.hostname(),
           turn_configurations[i].server.port()));
     }
+    config.enable_multiple_routes = enable_multiple_routes_;
 
     return new P2PPortAllocator(
         socket_dispatcher_.get(), network_manager_, socket_factory_, config);
@@ -161,6 +164,10 @@
   // PeerConnectionDependencyFactory.
   rtc::NetworkManager* network_manager_;
   rtc::PacketSocketFactory* socket_factory_;
+
+  // When false, only 'any' address (all 0s) will be bound for address
+  // discovery.
+  bool enable_multiple_routes_;
 };
 
 PeerConnectionDependencyFactory::PeerConnectionDependencyFactory(
@@ -394,11 +401,21 @@
   if (!GetPcFactory().get())
     return NULL;
 
+  // Copy the flag from Preference associated with this WebFrame.
+  bool enable_multiple_routes = true;
+  if (web_frame && web_frame->view()) {
+    RenderViewImpl* renderer_view_impl =
+        RenderViewImpl::FromWebView(web_frame->view());
+    if (renderer_view_impl) {
+      enable_multiple_routes = renderer_view_impl->renderer_preferences()
+                                    .enable_webrtc_multiple_routes;
+    }
+  }
+
   scoped_refptr<P2PPortAllocatorFactory> pa_factory =
-        new rtc::RefCountedObject<P2PPortAllocatorFactory>(
-            p2p_socket_dispatcher_.get(),
-            network_manager_,
-            socket_factory_.get());
+      new rtc::RefCountedObject<P2PPortAllocatorFactory>(
+          p2p_socket_dispatcher_.get(), network_manager_, socket_factory_.get(),
+          enable_multiple_routes);
 
   PeerConnectionIdentityService* identity_service =
       new PeerConnectionIdentityService(
diff --git a/content/renderer/p2p/port_allocator.cc b/content/renderer/p2p/port_allocator.cc
index 7beb3ecc..488df30 100644
--- a/content/renderer/p2p/port_allocator.cc
+++ b/content/renderer/p2p/port_allocator.cc
@@ -31,6 +31,8 @@
   uint32 flags = 0;
   if (config_.disable_tcp_transport)
     flags |= cricket::PORTALLOCATOR_DISABLE_TCP;
+  if (!config_.enable_multiple_routes)
+    flags |= cricket::PORTALLOCATOR_DISABLE_ADAPTER_ENUMERATION;
   set_flags(flags);
   set_allow_tcp_listen(false);
 }
diff --git a/content/renderer/p2p/port_allocator.h b/content/renderer/p2p/port_allocator.h
index 3cb1377..057f37bb 100644
--- a/content/renderer/p2p/port_allocator.h
+++ b/content/renderer/p2p/port_allocator.h
@@ -36,6 +36,9 @@
 
     // Disable TCP-based transport when set to true.
     bool disable_tcp_transport;
+
+    // Disable binding to individual NICs when set to false.
+    bool enable_multiple_routes;
   };
 
   P2PPortAllocator(P2PSocketDispatcher* socket_dispatcher,
diff --git a/content/renderer/pepper/content_renderer_pepper_host_factory.cc b/content/renderer/pepper/content_renderer_pepper_host_factory.cc
index 598c18a..143fb18c 100644
--- a/content/renderer/pepper/content_renderer_pepper_host_factory.cc
+++ b/content/renderer/pepper/content_renderer_pepper_host_factory.cc
@@ -10,12 +10,12 @@
 #include "content/public/common/content_client.h"
 #include "content/public/renderer/content_renderer_client.h"
 #include "content/renderer/pepper/pepper_audio_input_host.h"
+#include "content/renderer/pepper/pepper_camera_device_host.h"
 #include "content/renderer/pepper/pepper_compositor_host.h"
 #include "content/renderer/pepper/pepper_file_chooser_host.h"
 #include "content/renderer/pepper/pepper_file_ref_renderer_host.h"
 #include "content/renderer/pepper/pepper_file_system_host.h"
 #include "content/renderer/pepper/pepper_graphics_2d_host.h"
-#include "content/renderer/pepper/pepper_image_capture_host.h"
 #include "content/renderer/pepper/pepper_media_stream_video_track_host.h"
 #include "content/renderer/pepper/pepper_plugin_instance_impl.h"
 #include "content/renderer/pepper/pepper_url_loader_host.h"
@@ -204,9 +204,9 @@
   // Private interfaces.
   if (GetPermissions().HasPermission(ppapi::PERMISSION_PRIVATE)) {
     switch (message.type()) {
-      case PpapiHostMsg_ImageCapture_Create::ID: {
-        scoped_ptr<PepperImageCaptureHost> host(
-            new PepperImageCaptureHost(host_, instance, resource));
+      case PpapiHostMsg_CameraDevice_Create::ID: {
+        scoped_ptr<PepperCameraDeviceHost> host(
+            new PepperCameraDeviceHost(host_, instance, resource));
         return host->Init() ? host.Pass() : nullptr;
       }
     }
diff --git a/content/renderer/pepper/pepper_camera_device_host.cc b/content/renderer/pepper/pepper_camera_device_host.cc
new file mode 100644
index 0000000..cf199e2
--- /dev/null
+++ b/content/renderer/pepper/pepper_camera_device_host.cc
@@ -0,0 +1,126 @@
+// 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 "content/renderer/pepper/pepper_camera_device_host.h"
+
+#include "content/renderer/pepper/pepper_platform_camera_device.h"
+#include "content/renderer/pepper/renderer_ppapi_host_impl.h"
+#include "content/renderer/render_frame_impl.h"
+#include "ppapi/host/dispatch_host_message.h"
+#include "ppapi/proxy/ppapi_messages.h"
+
+namespace content {
+
+PepperCameraDeviceHost::PepperCameraDeviceHost(RendererPpapiHostImpl* host,
+                                               PP_Instance instance,
+                                               PP_Resource resource)
+    : ResourceHost(host->GetPpapiHost(), instance, resource),
+      renderer_ppapi_host_(host) {
+}
+
+PepperCameraDeviceHost::~PepperCameraDeviceHost() {
+  DetachPlatformCameraDevice();
+}
+
+bool PepperCameraDeviceHost::Init() {
+  return !!renderer_ppapi_host_->GetPluginInstance(pp_instance());
+}
+
+int32_t PepperCameraDeviceHost::OnResourceMessageReceived(
+    const IPC::Message& msg,
+    ppapi::host::HostMessageContext* context) {
+  int32_t result = PP_ERROR_FAILED;
+
+  PPAPI_BEGIN_MESSAGE_MAP(PepperCameraDeviceHost, msg)
+    PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_CameraDevice_Open, OnOpen)
+    PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(
+        PpapiHostMsg_CameraDevice_GetSupportedVideoCaptureFormats,
+        OnGetSupportedVideoCaptureFormats)
+    PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_CameraDevice_Close,
+                                        OnClose)
+  PPAPI_END_MESSAGE_MAP()
+  return result;
+}
+
+void PepperCameraDeviceHost::OnInitialized(bool succeeded) {
+  if (!open_reply_context_.is_valid())
+    return;
+
+  if (succeeded) {
+    open_reply_context_.params.set_result(PP_OK);
+  } else {
+    DetachPlatformCameraDevice();
+    open_reply_context_.params.set_result(PP_ERROR_FAILED);
+  }
+
+  host()->SendReply(open_reply_context_,
+                    PpapiPluginMsg_CameraDevice_OpenReply());
+  open_reply_context_ = ppapi::host::ReplyMessageContext();
+}
+
+void PepperCameraDeviceHost::OnVideoCaptureFormatsEnumerated(
+    const std::vector<PP_VideoCaptureFormat>& formats) {
+  if (!video_capture_formats_reply_context_.is_valid())
+    return;
+
+  if (formats.size() > 0)
+    video_capture_formats_reply_context_.params.set_result(PP_OK);
+  else
+    video_capture_formats_reply_context_.params.set_result(PP_ERROR_FAILED);
+  host()->SendReply(
+      video_capture_formats_reply_context_,
+      PpapiPluginMsg_CameraDevice_GetSupportedVideoCaptureFormatsReply(
+          formats));
+  video_capture_formats_reply_context_ = ppapi::host::ReplyMessageContext();
+}
+
+int32_t PepperCameraDeviceHost::OnOpen(ppapi::host::HostMessageContext* context,
+                                       const std::string& device_id) {
+  if (open_reply_context_.is_valid())
+    return PP_ERROR_INPROGRESS;
+
+  if (platform_camera_device_.get())
+    return PP_ERROR_FAILED;
+
+  GURL document_url = renderer_ppapi_host_->GetDocumentURL(pp_instance());
+  if (!document_url.is_valid())
+    return PP_ERROR_FAILED;
+
+  platform_camera_device_.reset(new PepperPlatformCameraDevice(
+      renderer_ppapi_host_->GetRenderFrameForInstance(pp_instance())
+          ->GetRoutingID(),
+      device_id, document_url, this));
+
+  open_reply_context_ = context->MakeReplyMessageContext();
+
+  return PP_OK_COMPLETIONPENDING;
+}
+
+int32_t PepperCameraDeviceHost::OnClose(
+    ppapi::host::HostMessageContext* context) {
+  DetachPlatformCameraDevice();
+  return PP_OK;
+}
+
+int32_t PepperCameraDeviceHost::OnGetSupportedVideoCaptureFormats(
+    ppapi::host::HostMessageContext* context) {
+  if (video_capture_formats_reply_context_.is_valid())
+    return PP_ERROR_INPROGRESS;
+  if (!platform_camera_device_)
+    return PP_ERROR_FAILED;
+
+  video_capture_formats_reply_context_ = context->MakeReplyMessageContext();
+  platform_camera_device_->GetSupportedVideoCaptureFormats();
+
+  return PP_OK_COMPLETIONPENDING;
+}
+
+void PepperCameraDeviceHost::DetachPlatformCameraDevice() {
+  if (platform_camera_device_) {
+    platform_camera_device_->DetachEventHandler();
+    platform_camera_device_.reset();
+  }
+}
+
+}  // namespace content
diff --git a/content/renderer/pepper/pepper_camera_device_host.h b/content/renderer/pepper/pepper_camera_device_host.h
new file mode 100644
index 0000000..3a13cac
--- /dev/null
+++ b/content/renderer/pepper/pepper_camera_device_host.h
@@ -0,0 +1,67 @@
+// 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 CONTENT_RENDERER_PEPPER_PEPPER_CAMERA_DEVICE_HOST_H_
+#define CONTENT_RENDERER_PEPPER_PEPPER_CAMERA_DEVICE_HOST_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "content/public/renderer/renderer_ppapi_host.h"
+#include "content/renderer/pepper/ppb_buffer_impl.h"
+#include "ppapi/c/pp_size.h"
+#include "ppapi/c/private/pp_video_capture_format.h"
+#include "ppapi/host/host_message_context.h"
+#include "ppapi/host/resource_host.h"
+
+namespace content {
+class PepperPlatformCameraDevice;
+class RendererPpapiHostImpl;
+
+class PepperCameraDeviceHost : public ppapi::host::ResourceHost {
+ public:
+  PepperCameraDeviceHost(RendererPpapiHostImpl* host,
+                         PP_Instance instance,
+                         PP_Resource resource);
+
+  ~PepperCameraDeviceHost() override;
+
+  bool Init();
+
+  int32_t OnResourceMessageReceived(
+      const IPC::Message& msg,
+      ppapi::host::HostMessageContext* context) override;
+
+  // These methods are called by PepperPlatformCameraDevice only.
+
+  // Called when camera device is initialized.
+  void OnInitialized(bool succeeded);
+
+  // Called when the video capture formats are enumerated.
+  void OnVideoCaptureFormatsEnumerated(
+      const std::vector<PP_VideoCaptureFormat>& formats);
+
+ private:
+  // Plugin -> host message handlers.
+  int32_t OnOpen(ppapi::host::HostMessageContext* context,
+                 const std::string& device_id);
+  int32_t OnClose(ppapi::host::HostMessageContext* context);
+  int32_t OnGetSupportedVideoCaptureFormats(
+      ppapi::host::HostMessageContext* context);
+
+  // Utility methods.
+  void DetachPlatformCameraDevice();
+
+  scoped_ptr<PepperPlatformCameraDevice> platform_camera_device_;
+
+  RendererPpapiHostImpl* renderer_ppapi_host_;
+
+  ppapi::host::ReplyMessageContext open_reply_context_;
+
+  ppapi::host::ReplyMessageContext video_capture_formats_reply_context_;
+
+  DISALLOW_COPY_AND_ASSIGN(PepperCameraDeviceHost);
+};
+
+}  // namespace content
+
+#endif  // CONTENT_RENDERER_PEPPER_PEPPER_CAMERA_DEVICE_HOST_H_
diff --git a/content/renderer/pepper/pepper_image_capture_host.cc b/content/renderer/pepper/pepper_image_capture_host.cc
deleted file mode 100644
index 0734891..0000000
--- a/content/renderer/pepper/pepper_image_capture_host.cc
+++ /dev/null
@@ -1,125 +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 "content/renderer/pepper/pepper_image_capture_host.h"
-
-#include "content/renderer/pepper/pepper_platform_image_capture.h"
-#include "content/renderer/pepper/renderer_ppapi_host_impl.h"
-#include "content/renderer/render_frame_impl.h"
-#include "ppapi/host/dispatch_host_message.h"
-#include "ppapi/proxy/ppapi_messages.h"
-
-namespace content {
-
-PepperImageCaptureHost::PepperImageCaptureHost(RendererPpapiHostImpl* host,
-                                               PP_Instance instance,
-                                               PP_Resource resource)
-    : ResourceHost(host->GetPpapiHost(), instance, resource),
-      renderer_ppapi_host_(host) {
-}
-
-PepperImageCaptureHost::~PepperImageCaptureHost() {
-  DetachPlatformImageCapture();
-}
-
-bool PepperImageCaptureHost::Init() {
-  return !!renderer_ppapi_host_->GetPluginInstance(pp_instance());
-}
-
-int32_t PepperImageCaptureHost::OnResourceMessageReceived(
-    const IPC::Message& msg,
-    ppapi::host::HostMessageContext* context) {
-  int32_t result = PP_ERROR_FAILED;
-
-  PPAPI_BEGIN_MESSAGE_MAP(PepperImageCaptureHost, msg)
-    PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_ImageCapture_Open, OnOpen)
-    PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(
-        PpapiHostMsg_ImageCapture_GetSupportedPreviewSizes,
-        OnGetSupportedPreviewSizes)
-    PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_ImageCapture_Close,
-                                        OnClose)
-  PPAPI_END_MESSAGE_MAP()
-  return result;
-}
-
-void PepperImageCaptureHost::OnInitialized(bool succeeded) {
-  if (!open_reply_context_.is_valid())
-    return;
-
-  if (succeeded) {
-    open_reply_context_.params.set_result(PP_OK);
-  } else {
-    DetachPlatformImageCapture();
-    open_reply_context_.params.set_result(PP_ERROR_FAILED);
-  }
-
-  host()->SendReply(open_reply_context_,
-                    PpapiPluginMsg_ImageCapture_OpenReply());
-  open_reply_context_ = ppapi::host::ReplyMessageContext();
-}
-
-void PepperImageCaptureHost::OnPreviewSizesEnumerated(
-    const std::vector<PP_Size>& sizes) {
-  if (!preview_sizes_reply_context_.is_valid())
-    return;
-
-  if (sizes.size() > 0)
-    preview_sizes_reply_context_.params.set_result(PP_OK);
-  else
-    preview_sizes_reply_context_.params.set_result(PP_ERROR_FAILED);
-  host()->SendReply(
-      preview_sizes_reply_context_,
-      PpapiPluginMsg_ImageCapture_GetSupportedPreviewSizesReply(sizes));
-  preview_sizes_reply_context_ = ppapi::host::ReplyMessageContext();
-}
-
-int32_t PepperImageCaptureHost::OnOpen(ppapi::host::HostMessageContext* context,
-                                       const std::string& device_id) {
-  if (open_reply_context_.is_valid())
-    return PP_ERROR_INPROGRESS;
-
-  if (platform_image_capture_.get())
-    return PP_ERROR_FAILED;
-
-  GURL document_url = renderer_ppapi_host_->GetDocumentURL(pp_instance());
-  if (!document_url.is_valid())
-    return PP_ERROR_FAILED;
-
-  platform_image_capture_.reset(new PepperPlatformImageCapture(
-      renderer_ppapi_host_->GetRenderFrameForInstance(pp_instance())
-          ->GetRoutingID(),
-      device_id, document_url, this));
-
-  open_reply_context_ = context->MakeReplyMessageContext();
-
-  return PP_OK_COMPLETIONPENDING;
-}
-
-int32_t PepperImageCaptureHost::OnClose(
-    ppapi::host::HostMessageContext* context) {
-  DetachPlatformImageCapture();
-  return PP_OK;
-}
-
-int32_t PepperImageCaptureHost::OnGetSupportedPreviewSizes(
-    ppapi::host::HostMessageContext* context) {
-  if (preview_sizes_reply_context_.is_valid())
-    return PP_ERROR_INPROGRESS;
-  if (!platform_image_capture_)
-    return PP_ERROR_FAILED;
-
-  preview_sizes_reply_context_ = context->MakeReplyMessageContext();
-  platform_image_capture_->GetPreviewSizes();
-
-  return PP_OK_COMPLETIONPENDING;
-}
-
-void PepperImageCaptureHost::DetachPlatformImageCapture() {
-  if (platform_image_capture_) {
-    platform_image_capture_->DetachEventHandler();
-    platform_image_capture_.reset();
-  }
-}
-
-}  // namespace content
diff --git a/content/renderer/pepper/pepper_image_capture_host.h b/content/renderer/pepper/pepper_image_capture_host.h
deleted file mode 100644
index 126e4620..0000000
--- a/content/renderer/pepper/pepper_image_capture_host.h
+++ /dev/null
@@ -1,64 +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 CONTENT_RENDERER_PEPPER_PEPPER_IMAGE_CAPTURE_HOST_H_
-#define CONTENT_RENDERER_PEPPER_PEPPER_IMAGE_CAPTURE_HOST_H_
-
-#include "base/memory/scoped_ptr.h"
-#include "content/public/renderer/renderer_ppapi_host.h"
-#include "content/renderer/pepper/ppb_buffer_impl.h"
-#include "ppapi/c/pp_size.h"
-#include "ppapi/host/host_message_context.h"
-#include "ppapi/host/resource_host.h"
-
-namespace content {
-class PepperPlatformImageCapture;
-class RendererPpapiHostImpl;
-
-class PepperImageCaptureHost : public ppapi::host::ResourceHost {
- public:
-  PepperImageCaptureHost(RendererPpapiHostImpl* host,
-                         PP_Instance instance,
-                         PP_Resource resource);
-
-  ~PepperImageCaptureHost() override;
-
-  bool Init();
-
-  int32_t OnResourceMessageReceived(
-      const IPC::Message& msg,
-      ppapi::host::HostMessageContext* context) override;
-
-  // These methods are called by PepperPlatformImageCapture only.
-
-  // Called when image capture is initialized.
-  void OnInitialized(bool succeeded);
-
-  // Called when the preview frame sizes are enumerated.
-  void OnPreviewSizesEnumerated(const std::vector<PP_Size>& sizes);
-
- private:
-  // Plugin -> host message handlers.
-  int32_t OnOpen(ppapi::host::HostMessageContext* context,
-                 const std::string& device_id);
-  int32_t OnClose(ppapi::host::HostMessageContext* context);
-  int32_t OnGetSupportedPreviewSizes(ppapi::host::HostMessageContext* context);
-
-  // Utility methods.
-  void DetachPlatformImageCapture();
-
-  scoped_ptr<PepperPlatformImageCapture> platform_image_capture_;
-
-  RendererPpapiHostImpl* renderer_ppapi_host_;
-
-  ppapi::host::ReplyMessageContext open_reply_context_;
-
-  ppapi::host::ReplyMessageContext preview_sizes_reply_context_;
-
-  DISALLOW_COPY_AND_ASSIGN(PepperImageCaptureHost);
-};
-
-}  // namespace content
-
-#endif  // CONTENT_RENDERER_PEPPER_PEPPER_IMAGE_CAPTURE_HOST_H_
diff --git a/content/renderer/pepper/pepper_platform_camera_device.cc b/content/renderer/pepper/pepper_platform_camera_device.cc
new file mode 100644
index 0000000..c3dfff2
--- /dev/null
+++ b/content/renderer/pepper/pepper_platform_camera_device.cc
@@ -0,0 +1,131 @@
+// 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 "content/renderer/pepper/pepper_platform_camera_device.h"
+
+#include "base/bind.h"
+#include "base/callback_helpers.h"
+#include "base/logging.h"
+#include "base/message_loop/message_loop_proxy.h"
+#include "content/renderer/media/video_capture_impl_manager.h"
+#include "content/renderer/pepper/gfx_conversion.h"
+#include "content/renderer/pepper/pepper_camera_device_host.h"
+#include "content/renderer/pepper/pepper_media_device_manager.h"
+#include "content/renderer/render_frame_impl.h"
+#include "content/renderer/render_thread_impl.h"
+#include "media/base/bind_to_current_loop.h"
+#include "url/gurl.h"
+
+namespace content {
+
+PepperPlatformCameraDevice::PepperPlatformCameraDevice(
+    int render_frame_id,
+    const std::string& device_id,
+    const GURL& document_url,
+    PepperCameraDeviceHost* handler)
+    : render_frame_id_(render_frame_id),
+      device_id_(device_id),
+      session_id_(0),
+      handler_(handler),
+      pending_open_device_(false),
+      pending_open_device_id_(-1),
+      weak_factory_(this) {
+  // We need to open the device and obtain the label and session ID before
+  // initializing.
+  PepperMediaDeviceManager* const device_manager = GetMediaDeviceManager();
+  if (device_manager) {
+    pending_open_device_id_ = device_manager->OpenDevice(
+        PP_DEVICETYPE_DEV_VIDEOCAPTURE, device_id, document_url,
+        base::Bind(&PepperPlatformCameraDevice::OnDeviceOpened,
+                   weak_factory_.GetWeakPtr()));
+    pending_open_device_ = true;
+  }
+}
+
+void PepperPlatformCameraDevice::GetSupportedVideoCaptureFormats() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  VideoCaptureImplManager* manager =
+      RenderThreadImpl::current()->video_capture_impl_manager();
+  manager->GetDeviceSupportedFormats(
+      session_id_,
+      media::BindToCurrentLoop(base::Bind(
+          &PepperPlatformCameraDevice::OnDeviceSupportedFormatsEnumerated,
+          weak_factory_.GetWeakPtr())));
+}
+
+void PepperPlatformCameraDevice::DetachEventHandler() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  handler_ = NULL;
+  if (!release_device_cb_.is_null()) {
+    base::ResetAndReturn(&release_device_cb_).Run();
+  }
+  if (!label_.empty()) {
+    PepperMediaDeviceManager* const device_manager = GetMediaDeviceManager();
+    if (device_manager)
+      device_manager->CloseDevice(label_);
+    label_.clear();
+  }
+  if (pending_open_device_) {
+    PepperMediaDeviceManager* const device_manager = GetMediaDeviceManager();
+    if (device_manager)
+      device_manager->CancelOpenDevice(pending_open_device_id_);
+    pending_open_device_ = false;
+    pending_open_device_id_ = -1;
+  }
+}
+
+PepperPlatformCameraDevice::~PepperPlatformCameraDevice() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK(release_device_cb_.is_null());
+  DCHECK(label_.empty());
+  DCHECK(!pending_open_device_);
+}
+
+void PepperPlatformCameraDevice::OnDeviceOpened(int request_id,
+                                                bool succeeded,
+                                                const std::string& label) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK(handler_);
+
+  pending_open_device_ = false;
+  pending_open_device_id_ = -1;
+
+  PepperMediaDeviceManager* const device_manager = GetMediaDeviceManager();
+  succeeded = succeeded && device_manager;
+  if (succeeded) {
+    label_ = label;
+    session_id_ =
+        device_manager->GetSessionID(PP_DEVICETYPE_DEV_VIDEOCAPTURE, label);
+    VideoCaptureImplManager* manager =
+        RenderThreadImpl::current()->video_capture_impl_manager();
+    release_device_cb_ = manager->UseDevice(session_id_);
+  }
+
+  handler_->OnInitialized(succeeded);
+}
+
+void PepperPlatformCameraDevice::OnDeviceSupportedFormatsEnumerated(
+    const media::VideoCaptureFormats& formats) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK(handler_);
+
+  std::vector<PP_VideoCaptureFormat> output_formats;
+  for (const auto& format : formats) {
+    PP_VideoCaptureFormat output_format;
+    output_format.frame_size = PP_FromGfxSize(format.frame_size);
+    output_format.frame_rate = format.frame_rate;
+    output_formats.push_back(output_format);
+  }
+  handler_->OnVideoCaptureFormatsEnumerated(output_formats);
+}
+
+PepperMediaDeviceManager* PepperPlatformCameraDevice::GetMediaDeviceManager() {
+  RenderFrameImpl* const render_frame =
+      RenderFrameImpl::FromRoutingID(render_frame_id_);
+  return render_frame
+             ? PepperMediaDeviceManager::GetForRenderFrame(render_frame).get()
+             : NULL;
+}
+
+}  // namespace content
diff --git a/content/renderer/pepper/pepper_platform_camera_device.h b/content/renderer/pepper/pepper_platform_camera_device.h
new file mode 100644
index 0000000..bc7a535
--- /dev/null
+++ b/content/renderer/pepper/pepper_platform_camera_device.h
@@ -0,0 +1,73 @@
+// 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 CONTENT_RENDERER_PEPPER_PEPPER_PLATFORM_CAMERA_DEVICE_H_
+#define CONTENT_RENDERER_PEPPER_PEPPER_PLATFORM_CAMERA_DEVICE_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/callback.h"
+#include "base/compiler_specific.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/threading/thread_checker.h"
+#include "content/common/media/video_capture.h"
+#include "media/video/capture/video_capture_types.h"
+
+class GURL;
+
+namespace content {
+class PepperMediaDeviceManager;
+class PepperCameraDeviceHost;
+
+// This object must only be used on the thread it's constructed on.
+class PepperPlatformCameraDevice {
+ public:
+  PepperPlatformCameraDevice(int render_frame_id,
+                             const std::string& device_id,
+                             const GURL& document_url,
+                             PepperCameraDeviceHost* handler);
+  ~PepperPlatformCameraDevice();
+
+  // Detaches the event handler and stops sending notifications to it.
+  void DetachEventHandler();
+
+  void GetSupportedVideoCaptureFormats();
+
+ private:
+  void OnDeviceOpened(int request_id, bool succeeded, const std::string& label);
+
+  // Called by VideoCaptureImplManager.
+  void OnDeviceSupportedFormatsEnumerated(
+      const media::VideoCaptureFormats& formats);
+
+  // Can return NULL if the RenderFrame referenced by |render_frame_id_| has
+  // gone away.
+  PepperMediaDeviceManager* GetMediaDeviceManager();
+
+  const int render_frame_id_;
+  const std::string device_id_;
+
+  std::string label_;
+  int session_id_;
+  base::Closure release_device_cb_;
+
+  PepperCameraDeviceHost* handler_;
+
+  // Whether we have a pending request to open a device. We have to make sure
+  // there isn't any pending request before this object goes away.
+  bool pending_open_device_;
+  int pending_open_device_id_;
+
+  base::ThreadChecker thread_checker_;
+
+  base::WeakPtrFactory<PepperPlatformCameraDevice> weak_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(PepperPlatformCameraDevice);
+};
+
+}  // namespace content
+
+#endif  // CONTENT_RENDERER_PEPPER_PEPPER_PLATFORM_CAMERA_DEVICE_H_
diff --git a/content/renderer/pepper/pepper_platform_image_capture.cc b/content/renderer/pepper/pepper_platform_image_capture.cc
deleted file mode 100644
index 65f8f9d..0000000
--- a/content/renderer/pepper/pepper_platform_image_capture.cc
+++ /dev/null
@@ -1,127 +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 "content/renderer/pepper/pepper_platform_image_capture.h"
-
-#include "base/bind.h"
-#include "base/callback_helpers.h"
-#include "base/logging.h"
-#include "base/message_loop/message_loop_proxy.h"
-#include "content/renderer/media/video_capture_impl_manager.h"
-#include "content/renderer/pepper/gfx_conversion.h"
-#include "content/renderer/pepper/pepper_image_capture_host.h"
-#include "content/renderer/pepper/pepper_media_device_manager.h"
-#include "content/renderer/render_frame_impl.h"
-#include "content/renderer/render_thread_impl.h"
-#include "media/base/bind_to_current_loop.h"
-#include "url/gurl.h"
-
-namespace content {
-
-PepperPlatformImageCapture::PepperPlatformImageCapture(
-    int render_frame_id,
-    const std::string& device_id,
-    const GURL& document_url,
-    PepperImageCaptureHost* handler)
-    : render_frame_id_(render_frame_id),
-      device_id_(device_id),
-      session_id_(0),
-      handler_(handler),
-      pending_open_device_(false),
-      pending_open_device_id_(-1),
-      weak_factory_(this) {
-  // We need to open the device and obtain the label and session ID before
-  // initializing.
-  PepperMediaDeviceManager* const device_manager = GetMediaDeviceManager();
-  if (device_manager) {
-    pending_open_device_id_ = device_manager->OpenDevice(
-        PP_DEVICETYPE_DEV_VIDEOCAPTURE, device_id, document_url,
-        base::Bind(&PepperPlatformImageCapture::OnDeviceOpened,
-                   weak_factory_.GetWeakPtr()));
-    pending_open_device_ = true;
-  }
-}
-
-void PepperPlatformImageCapture::GetPreviewSizes() {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  VideoCaptureImplManager* manager =
-      RenderThreadImpl::current()->video_capture_impl_manager();
-  manager->GetDeviceSupportedFormats(
-      session_id_,
-      media::BindToCurrentLoop(base::Bind(
-          &PepperPlatformImageCapture::OnDeviceSupportedFormatsEnumerated,
-          weak_factory_.GetWeakPtr())));
-}
-
-void PepperPlatformImageCapture::DetachEventHandler() {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  handler_ = NULL;
-  if (!release_device_cb_.is_null()) {
-    base::ResetAndReturn(&release_device_cb_).Run();
-  }
-  if (!label_.empty()) {
-    PepperMediaDeviceManager* const device_manager = GetMediaDeviceManager();
-    if (device_manager)
-      device_manager->CloseDevice(label_);
-    label_.clear();
-  }
-  if (pending_open_device_) {
-    PepperMediaDeviceManager* const device_manager = GetMediaDeviceManager();
-    if (device_manager)
-      device_manager->CancelOpenDevice(pending_open_device_id_);
-    pending_open_device_ = false;
-    pending_open_device_id_ = -1;
-  }
-}
-
-PepperPlatformImageCapture::~PepperPlatformImageCapture() {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  DCHECK(release_device_cb_.is_null());
-  DCHECK(label_.empty());
-  DCHECK(!pending_open_device_);
-}
-
-void PepperPlatformImageCapture::OnDeviceOpened(int request_id,
-                                                bool succeeded,
-                                                const std::string& label) {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  DCHECK(handler_);
-
-  pending_open_device_ = false;
-  pending_open_device_id_ = -1;
-
-  PepperMediaDeviceManager* const device_manager = GetMediaDeviceManager();
-  succeeded = succeeded && device_manager;
-  if (succeeded) {
-    label_ = label;
-    session_id_ =
-        device_manager->GetSessionID(PP_DEVICETYPE_DEV_VIDEOCAPTURE, label);
-    VideoCaptureImplManager* manager =
-        RenderThreadImpl::current()->video_capture_impl_manager();
-    release_device_cb_ = manager->UseDevice(session_id_);
-  }
-
-  handler_->OnInitialized(succeeded);
-}
-
-void PepperPlatformImageCapture::OnDeviceSupportedFormatsEnumerated(
-    const media::VideoCaptureFormats& formats) {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  DCHECK(handler_);
-
-  std::vector<PP_Size> sizes;
-  for (const auto& format : formats)
-    sizes.push_back(PP_FromGfxSize(format.frame_size));
-  handler_->OnPreviewSizesEnumerated(sizes);
-}
-
-PepperMediaDeviceManager* PepperPlatformImageCapture::GetMediaDeviceManager() {
-  RenderFrameImpl* const render_frame =
-      RenderFrameImpl::FromRoutingID(render_frame_id_);
-  return render_frame
-             ? PepperMediaDeviceManager::GetForRenderFrame(render_frame).get()
-             : NULL;
-}
-
-}  // namespace content
diff --git a/content/renderer/pepper/pepper_platform_image_capture.h b/content/renderer/pepper/pepper_platform_image_capture.h
deleted file mode 100644
index b65acee..0000000
--- a/content/renderer/pepper/pepper_platform_image_capture.h
+++ /dev/null
@@ -1,73 +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 CONTENT_RENDERER_PEPPER_PEPPER_PLATFORM_VIDEO_CAPTURE_H_
-#define CONTENT_RENDERER_PEPPER_PEPPER_PLATFORM_VIDEO_CAPTURE_H_
-
-#include <string>
-
-#include "base/basictypes.h"
-#include "base/callback.h"
-#include "base/compiler_specific.h"
-#include "base/memory/scoped_ptr.h"
-#include "base/memory/weak_ptr.h"
-#include "base/threading/thread_checker.h"
-#include "content/common/media/video_capture.h"
-#include "media/video/capture/video_capture_types.h"
-
-class GURL;
-
-namespace content {
-class PepperMediaDeviceManager;
-class PepperImageCaptureHost;
-
-// This object must only be used on the thread it's constructed on.
-class PepperPlatformImageCapture {
- public:
-  PepperPlatformImageCapture(int render_frame_id,
-                             const std::string& device_id,
-                             const GURL& document_url,
-                             PepperImageCaptureHost* handler);
-  ~PepperPlatformImageCapture();
-
-  // Detaches the event handler and stops sending notifications to it.
-  void DetachEventHandler();
-
-  void GetPreviewSizes();
-
- private:
-  void OnDeviceOpened(int request_id, bool succeeded, const std::string& label);
-
-  // Called by VideoCaptureImplManager.
-  void OnDeviceSupportedFormatsEnumerated(
-      const media::VideoCaptureFormats& formats);
-
-  // Can return NULL if the RenderFrame referenced by |render_frame_id_| has
-  // gone away.
-  PepperMediaDeviceManager* GetMediaDeviceManager();
-
-  const int render_frame_id_;
-  const std::string device_id_;
-
-  std::string label_;
-  int session_id_;
-  base::Closure release_device_cb_;
-
-  PepperImageCaptureHost* handler_;
-
-  // Whether we have a pending request to open a device. We have to make sure
-  // there isn't any pending request before this object goes away.
-  bool pending_open_device_;
-  int pending_open_device_id_;
-
-  base::ThreadChecker thread_checker_;
-
-  base::WeakPtrFactory<PepperPlatformImageCapture> weak_factory_;
-
-  DISALLOW_COPY_AND_ASSIGN(PepperPlatformImageCapture);
-};
-
-}  // namespace content
-
-#endif  // CONTENT_RENDERER_PEPPER_PEPPER_PLATFORM_VIDEO_CAPTURE_H_
diff --git a/content/renderer/pepper/pepper_plugin_instance_impl.cc b/content/renderer/pepper/pepper_plugin_instance_impl.cc
index 4d4dec4e..b7c6468 100644
--- a/content/renderer/pepper/pepper_plugin_instance_impl.cc
+++ b/content/renderer/pepper/pepper_plugin_instance_impl.cc
@@ -22,6 +22,7 @@
 #include "content/common/content_constants_internal.h"
 #include "content/common/frame_messages.h"
 #include "content/common/input/web_input_event_traits.h"
+#include "content/common/view_messages.h"
 #include "content/public/common/content_constants.h"
 #include "content/public/common/page_zoom.h"
 #include "content/public/renderer/content_renderer_client.h"
@@ -2048,6 +2049,10 @@
 
 void PepperPluginInstanceImpl::OnThrottleStateChange() {
   SendDidChangeView();
+
+  bool is_throttled = throttler_->IsThrottled();
+  render_frame()->Send(new ViewHostMsg_PluginInstanceThrottleStateChange(
+      module_->GetPluginChildId(), pp_instance_, is_throttled));
 }
 
 void PepperPluginInstanceImpl::OnHiddenForPlaceholder(bool hidden) {
diff --git a/content/renderer/pepper/pepper_websocket_host.cc b/content/renderer/pepper/pepper_websocket_host.cc
index ba90a91..b5f1e959 100644
--- a/content/renderer/pepper/pepper_websocket_host.cc
+++ b/content/renderer/pepper/pepper_websocket_host.cc
@@ -213,7 +213,7 @@
     return PP_ERROR_BADARGUMENT;
   if (gurl.has_ref())
     return PP_ERROR_BADARGUMENT;
-  if (!net::IsPortAllowedByDefault(gurl.IntPort()))
+  if (!net::IsPortAllowedByDefault(gurl.EffectiveIntPort()))
     return PP_ERROR_BADARGUMENT;
   WebURL web_url(gurl);
 
diff --git a/content/renderer/pepper/plugin_module.cc b/content/renderer/pepper/plugin_module.cc
index 1164c43..1685fc0 100644
--- a/content/renderer/pepper/plugin_module.cc
+++ b/content/renderer/pepper/plugin_module.cc
@@ -99,6 +99,7 @@
 #include "ppapi/c/ppp.h"
 #include "ppapi/c/ppp_instance.h"
 #include "ppapi/c/private/ppb_camera_capabilities_private.h"
+#include "ppapi/c/private/ppb_camera_device_private.h"
 #include "ppapi/c/private/ppb_ext_crx_file_system_private.h"
 #include "ppapi/c/private/ppb_file_io_private.h"
 #include "ppapi/c/private/ppb_file_ref_private.h"
@@ -114,7 +115,6 @@
 #include "ppapi/c/private/ppb_flash_message_loop.h"
 #include "ppapi/c/private/ppb_flash_print.h"
 #include "ppapi/c/private/ppb_host_resolver_private.h"
-#include "ppapi/c/private/ppb_image_capture_private.h"
 #include "ppapi/c/private/ppb_input_event_private.h"
 #include "ppapi/c/private/ppb_instance_private.h"
 #include "ppapi/c/private/ppb_isolated_file_system_private.h"
diff --git a/content/renderer/pepper/resource_creation_impl.cc b/content/renderer/pepper/resource_creation_impl.cc
index 7935785..94c71375 100644
--- a/content/renderer/pepper/resource_creation_impl.cc
+++ b/content/renderer/pepper/resource_creation_impl.cc
@@ -82,6 +82,11 @@
   return PPB_Buffer_Impl::Create(instance, size);
 }
 
+PP_Resource ResourceCreationImpl::CreateCameraDevicePrivate(
+    PP_Instance instance) {
+  return 0;  // Not supported in-process.
+}
+
 PP_Resource ResourceCreationImpl::CreateFlashDRM(PP_Instance instance) {
   return 0;  // Not supported in-process.
 }
@@ -128,11 +133,6 @@
   return 0;  // Not supported in-process.
 }
 
-PP_Resource ResourceCreationImpl::CreateImageCapturePrivate(
-    PP_Instance instance) {
-  return 0;  // Not supported in-process.
-}
-
 PP_Resource ResourceCreationImpl::CreateImageData(PP_Instance instance,
                                                   PP_ImageDataFormat format,
                                                   const PP_Size* size,
diff --git a/content/renderer/pepper/resource_creation_impl.h b/content/renderer/pepper/resource_creation_impl.h
index fc8c6aac..ea39dff 100644
--- a/content/renderer/pepper/resource_creation_impl.h
+++ b/content/renderer/pepper/resource_creation_impl.h
@@ -39,6 +39,7 @@
   PP_Resource CreateCompositor(PP_Instance instance) override;
   PP_Resource CreateBroker(PP_Instance instance) override;
   PP_Resource CreateBuffer(PP_Instance instance, uint32_t size) override;
+  PP_Resource CreateCameraDevicePrivate(PP_Instance instance) override;
   PP_Resource CreateFlashDRM(PP_Instance instance) override;
   PP_Resource CreateFlashFontFile(
       PP_Instance instance,
@@ -58,7 +59,6 @@
       base::SharedMemoryHandle* shared_state) override;
   PP_Resource CreateHostResolver(PP_Instance instance) override;
   PP_Resource CreateHostResolverPrivate(PP_Instance instance) override;
-  PP_Resource CreateImageCapturePrivate(PP_Instance instance) override;
   PP_Resource CreateImageData(PP_Instance instance,
                               PP_ImageDataFormat format,
                               const PP_Size* size,
diff --git a/content/renderer/pepper/video_decoder_shim.cc b/content/renderer/pepper/video_decoder_shim.cc
index c3e7e78c..41fbc2ef 100644
--- a/content/renderer/pepper/video_decoder_shim.cc
+++ b/content/renderer/pepper/video_decoder_shim.cc
@@ -483,7 +483,7 @@
       for (TextureIdMap::const_iterator it = texture_id_map_.begin();
            it != texture_id_map_.end();
            ++it) {
-        textures_to_dismiss_.insert(it->second);
+        textures_to_dismiss_.insert(it->first);
       }
       for (TextureIdSet::const_iterator it = available_textures_.begin();
            it != available_textures_.end();
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index 17df925..ff57dc6 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -227,11 +227,12 @@
 typedef std::map<blink::WebFrame*, RenderFrameImpl*> FrameMap;
 base::LazyInstance<FrameMap> g_frame_map = LAZY_INSTANCE_INITIALIZER;
 
-int64 ExtractPostId(const WebHistoryItem& item) {
-  if (item.isNull())
+int64 ExtractPostId(HistoryEntry* entry) {
+  if (!entry)
     return -1;
 
-  if (item.httpBody().isNull())
+  const WebHistoryItem& item = entry->root();
+  if (item.isNull() || item.httpBody().isNull())
     return -1;
 
   return item.httpBody().identifier();
@@ -1927,7 +1928,8 @@
           new MediaRendererServiceProvider(GetServiceRegistry()))));
 #else
   scoped_ptr<media::RendererFactory> media_renderer_factory =
-      GetContentClient()->renderer()->CreateMediaRendererFactory(this);
+      GetContentClient()->renderer()->CreateMediaRendererFactory(this,
+                                                                 media_log);
 
   if (!media_renderer_factory.get()) {
     media_renderer_factory.reset(new media::DefaultRendererFactory(
@@ -3774,7 +3776,7 @@
     base::string16 method = request.httpMethod();
     if (EqualsASCII(method, "POST")) {
       params.is_post = true;
-      params.post_id = ExtractPostId(entry->root());
+      params.post_id = ExtractPostId(entry);
     }
 
     // Send the user agent override back.
@@ -3808,11 +3810,10 @@
     // generated a new session history entry. When they do generate a session
     // history entry, it means the user initiated the navigation and we should
     // mark it as such.
-    bool is_history_navigation = commit_type == blink::WebBackForwardCommit;
-    if (is_history_navigation || ds->replacesCurrentHistoryItem())
-      params.transition = ui::PAGE_TRANSITION_AUTO_SUBFRAME;
-    else
+    if (commit_type == blink::WebStandardCommit)
       params.transition = ui::PAGE_TRANSITION_MANUAL_SUBFRAME;
+    else
+      params.transition = ui::PAGE_TRANSITION_AUTO_SUBFRAME;
 
     DCHECK(!navigation_state->history_list_was_cleared());
     params.history_list_was_cleared = false;
diff --git a/content/renderer/render_thread_impl.cc b/content/renderer/render_thread_impl.cc
index 138ed16..35bd5e3 100644
--- a/content/renderer/render_thread_impl.cc
+++ b/content/renderer/render_thread_impl.cc
@@ -665,6 +665,8 @@
     DCHECK_GT(num_raster_threads, 0);
 
     // Force maximum 1 thread for threaded GPU rasterization.
+    // TODO(vmiura): crbug.com/459760 Support existence of multiple raster
+    // threads in GPU raster mode.
     if (is_threaded_gpu_rasterization_enabled_)
       num_raster_threads = 1;
     cc::TileTaskWorkerPool::SetNumWorkerThreads(num_raster_threads);
@@ -1169,7 +1171,7 @@
   }
 
   base::allocator::ReleaseFreeMemory();
-  base::DiscardableMemory::ReleaseFreeMemory();
+  discardable_shared_memory_manager()->ReleaseFreeMemory();
 
   // Continue the idle timer if the webkit shared timer is not suspended or
   // something is left to do.
diff --git a/content/renderer/render_view_impl.cc b/content/renderer/render_view_impl.cc
index 465329f..68e4e76 100644
--- a/content/renderer/render_view_impl.cc
+++ b/content/renderer/render_view_impl.cc
@@ -1134,6 +1134,11 @@
 #if defined(OS_WIN)
   settings->setShowContextMenuOnMouseUp(true);
 #endif
+
+#if defined(OS_MACOSX)
+  settings->setDoubleTapToZoomEnabled(true);
+  web_view->setMaximumLegibleScale(prefs.default_maximum_page_scale_factor);
+#endif
 }
 
 /*static*/
diff --git a/content/renderer/render_widget.cc b/content/renderer/render_widget.cc
index f9a12e1..c5155b4 100644
--- a/content/renderer/render_widget.cc
+++ b/content/renderer/render_widget.cc
@@ -1440,7 +1440,13 @@
 
   if (swap_promise) {
     compositor_->QueueSwapPromise(swap_promise.Pass());
-    compositor_->SetNeedsCommit();
+    // Request a commit. This might either A) request a commit ahead of time
+    // or B) request a commit which is not needed because there are not
+    // pending updates. If B) then the commit will be skipped and the swap
+    // promises will be broken (see EarlyOut_NoUpdates). To achieve that we
+    // call SetNeedsUpdateLayers instead of SetNeedsCommit so that
+    // can_cancel_commit is not unset.
+    compositor_->SetNeedsUpdateLayers();
   }
 }
 
diff --git a/content/renderer/renderer_blink_platform_impl.cc b/content/renderer/renderer_blink_platform_impl.cc
index bec58281a..3ac2bd0a 100644
--- a/content/renderer/renderer_blink_platform_impl.cc
+++ b/content/renderer/renderer_blink_platform_impl.cc
@@ -340,15 +340,15 @@
 }
 
 void RendererBlinkPlatformImpl::cacheMetadata(const blink::WebURL& url,
-                                              double response_time,
+                                              int64 response_time,
                                               const char* data,
                                               size_t size) {
   // Let the browser know we generated cacheable metadata for this resource. The
   // browser may cache it and return it on subsequent responses to speed
   // the processing of this resource.
   std::vector<char> copy(data, data + size);
-  RenderThread::Get()->Send(
-      new ViewHostMsg_DidGenerateCacheableMetadata(url, response_time, copy));
+  RenderThread::Get()->Send(new ViewHostMsg_DidGenerateCacheableMetadata(
+      url, base::Time::FromInternalValue(response_time), copy));
 }
 
 WebString RendererBlinkPlatformImpl::defaultLocale() {
diff --git a/content/renderer/renderer_blink_platform_impl.h b/content/renderer/renderer_blink_platform_impl.h
index 06e599d..97ab4eede 100644
--- a/content/renderer/renderer_blink_platform_impl.h
+++ b/content/renderer/renderer_blink_platform_impl.h
@@ -76,8 +76,7 @@
   virtual void createMessageChannel(blink::WebMessagePortChannel** channel1,
                                     blink::WebMessagePortChannel** channel2);
   virtual blink::WebPrescientNetworking* prescientNetworking();
-  virtual void cacheMetadata(
-      const blink::WebURL&, double, const char*, size_t);
+  virtual void cacheMetadata(const blink::WebURL&, int64, const char*, size_t);
   virtual blink::WebString defaultLocale();
   virtual void suddenTerminationChanged(bool enabled);
   virtual blink::WebStorageNamespace* createLocalStorageNamespace();
diff --git a/content/renderer/renderer_clipboard_delegate.cc b/content/renderer/renderer_clipboard_delegate.cc
index 0ed03f6..e1285d2 100644
--- a/content/renderer/renderer_clipboard_delegate.cc
+++ b/content/renderer/renderer_clipboard_delegate.cc
@@ -118,7 +118,7 @@
                                               const GURL& url,
                                               const base::string16& title) {
   RenderThreadImpl::current()->Send(
-      new ClipboardHostMsg_WriteBookmark(clipboard_type, url, title));
+      new ClipboardHostMsg_WriteBookmark(clipboard_type, url.spec(), title));
 }
 
 bool RendererClipboardDelegate::WriteImage(ui::ClipboardType clipboard_type,
diff --git a/content/renderer/renderer_main.cc b/content/renderer/renderer_main.cc
index 5ad7947..a76df805 100644
--- a/content/renderer/renderer_main.cc
+++ b/content/renderer/renderer_main.cc
@@ -5,6 +5,7 @@
 #include "base/base_switches.h"
 #include "base/command_line.h"
 #include "base/debug/debugger.h"
+#include "base/debug/leak_annotations.h"
 #include "base/debug/stack_trace.h"
 #include "base/i18n/rtl.h"
 #include "base/memory/ref_counted.h"
@@ -219,6 +220,11 @@
       base::MessageLoop::current()->Run();
       TRACE_EVENT_END_ETW("RendererMain.START_MSG_LOOP", 0, 0);
     }
+#if defined(LEAK_SANITIZER)
+    // Run leak detection before RenderProcessImpl goes out of scope. This helps
+    // ignore shutdown-only leaks.
+    __lsan_do_leak_check();
+#endif
   }
   platform.PlatformUninitialize();
   TRACE_EVENT_END_ETW("RendererMain", 0, "");
diff --git a/content/renderer/scheduler/null_renderer_scheduler.cc b/content/renderer/scheduler/null_renderer_scheduler.cc
index 4ac7bd4..00b5c47 100644
--- a/content/renderer/scheduler/null_renderer_scheduler.cc
+++ b/content/renderer/scheduler/null_renderer_scheduler.cc
@@ -68,6 +68,9 @@
 void NullRendererScheduler::WillBeginFrame(const cc::BeginFrameArgs& args) {
 }
 
+void NullRendererScheduler::BeginFrameNotExpectedSoon() {
+}
+
 void NullRendererScheduler::DidCommitFrameToCompositor() {
 }
 
diff --git a/content/renderer/scheduler/null_renderer_scheduler.h b/content/renderer/scheduler/null_renderer_scheduler.h
index 714bda4..eb2faecf 100644
--- a/content/renderer/scheduler/null_renderer_scheduler.h
+++ b/content/renderer/scheduler/null_renderer_scheduler.h
@@ -20,6 +20,7 @@
   scoped_refptr<SingleThreadIdleTaskRunner> IdleTaskRunner() override;
 
   void WillBeginFrame(const cc::BeginFrameArgs& args) override;
+  void BeginFrameNotExpectedSoon() override;
   void DidCommitFrameToCompositor() override;
   void DidReceiveInputEventOnCompositorThread(
       const blink::WebInputEvent& web_input_event) override;
diff --git a/content/renderer/scheduler/renderer_scheduler.h b/content/renderer/scheduler/renderer_scheduler.h
index 6b4ecfa..0a57339 100644
--- a/content/renderer/scheduler/renderer_scheduler.h
+++ b/content/renderer/scheduler/renderer_scheduler.h
@@ -36,6 +36,10 @@
   // to resource dispatch, foreground HTML parsing, etc...
   virtual scoped_refptr<base::SingleThreadTaskRunner> LoadingTaskRunner() = 0;
 
+  // Called to notify about the start of an extended period where no frames
+  // need to be drawn. Must be called from the main thread.
+  virtual void BeginFrameNotExpectedSoon() = 0;
+
   // Called to notify about the start of a new frame.  Must be called from the
   // main thread.
   virtual void WillBeginFrame(const cc::BeginFrameArgs& args) = 0;
diff --git a/content/renderer/scheduler/renderer_scheduler_impl.cc b/content/renderer/scheduler/renderer_scheduler_impl.cc
index 2e2da80..7a91a2d7 100644
--- a/content/renderer/scheduler/renderer_scheduler_impl.cc
+++ b/content/renderer/scheduler/renderer_scheduler_impl.cc
@@ -121,6 +121,13 @@
   }
 }
 
+void RendererSchedulerImpl::BeginFrameNotExpectedSoon() {
+  TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
+               "RendererSchedulerImpl::BeginFrameNotExpectedSoon");
+  DCHECK(main_thread_checker_.CalledOnValidThread());
+  // TODO(rmcilroy): Implement long idle times.
+}
+
 void RendererSchedulerImpl::DidReceiveInputEventOnCompositorThread(
     const blink::WebInputEvent& web_input_event) {
   TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
@@ -183,14 +190,25 @@
     return false;
 
   MaybeUpdatePolicy();
-  // We only yield if we are in the touchstart/compositor priority and there
-  // is input/compositor work outstanding. Note: even though the control queue
-  // is higher priority we don't yield for it since these tasks are not
-  // user-provided work and they are only intended to run before the next task,
-  // not interrupt the tasks.
-  return (SchedulerPolicy() == COMPOSITOR_PRIORITY_POLICY ||
-          SchedulerPolicy() == TOUCHSTART_PRIORITY_POLICY) &&
-         !task_queue_manager_->IsQueueEmpty(COMPOSITOR_TASK_QUEUE);
+  // We only yield if we are in the compositor priority and there is compositor
+  // work outstanding, or if we are in the touchstart response priority.
+  // Note: even though the control queue is higher priority we don't yield for
+  // it since these tasks are not user-provided work and they are only intended
+  // to run before the next task, not interrupt the tasks.
+  switch (SchedulerPolicy()) {
+    case NORMAL_PRIORITY_POLICY:
+      return false;
+
+    case COMPOSITOR_PRIORITY_POLICY:
+      return !task_queue_manager_->IsQueueEmpty(COMPOSITOR_TASK_QUEUE);
+
+    case TOUCHSTART_PRIORITY_POLICY:
+      return true;
+
+    default:
+      NOTREACHED();
+      return false;
+  }
 }
 
 void RendererSchedulerImpl::CurrentIdleTaskDeadlineCallback(
diff --git a/content/renderer/scheduler/renderer_scheduler_impl.h b/content/renderer/scheduler/renderer_scheduler_impl.h
index 15958dc..c17c05c 100644
--- a/content/renderer/scheduler/renderer_scheduler_impl.h
+++ b/content/renderer/scheduler/renderer_scheduler_impl.h
@@ -36,6 +36,7 @@
   scoped_refptr<SingleThreadIdleTaskRunner> IdleTaskRunner() override;
   scoped_refptr<base::SingleThreadTaskRunner> LoadingTaskRunner() override;
   void WillBeginFrame(const cc::BeginFrameArgs& args) override;
+  void BeginFrameNotExpectedSoon() override;
   void DidCommitFrameToCompositor() override;
   void DidReceiveInputEventOnCompositorThread(
       const blink::WebInputEvent& web_input_event) override;
diff --git a/content/renderer/scheduler/renderer_scheduler_impl_unittest.cc b/content/renderer/scheduler/renderer_scheduler_impl_unittest.cc
index 7fd53e8d..bf0e1cf 100644
--- a/content/renderer/scheduler/renderer_scheduler_impl_unittest.cc
+++ b/content/renderer/scheduler/renderer_scheduler_impl_unittest.cc
@@ -798,6 +798,14 @@
   // We should be able to switch to compositor priority mid-task.
   EXPECT_FALSE(should_yield_before);
   EXPECT_TRUE(should_yield_after);
+
+  // Receiving a touchstart should immediately trigger yielding, even if
+  // there's no immediately pending work in the compositor queue.
+  EXPECT_FALSE(scheduler_->ShouldYieldForHighPriorityWork());
+  scheduler_->DidReceiveInputEventOnCompositorThread(
+      FakeInputEvent(blink::WebInputEvent::TouchStart));
+  EXPECT_TRUE(scheduler_->ShouldYieldForHighPriorityWork());
+  RunUntilIdle();
 }
 
 }  // namespace content
diff --git a/content/renderer/service_worker/embedded_worker_context_client.cc b/content/renderer/service_worker/embedded_worker_context_client.cc
index 0045756..2173cede 100644
--- a/content/renderer/service_worker/embedded_worker_context_client.cc
+++ b/content/renderer/service_worker/embedded_worker_context_client.cc
@@ -162,6 +162,19 @@
   script_context_->OpenWindow(url, callbacks);
 }
 
+void EmbeddedWorkerContextClient::setCachedMetadata(const blink::WebURL& url,
+                                                    const char* data,
+                                                    size_t size) {
+  DCHECK(script_context_);
+  script_context_->SetCachedMetadata(url, data, size);
+}
+
+void EmbeddedWorkerContextClient::clearCachedMetadata(
+    const blink::WebURL& url) {
+  DCHECK(script_context_);
+  script_context_->ClearCachedMetadata(url);
+}
+
 void EmbeddedWorkerContextClient::workerReadyForInspection() {
   Send(new EmbeddedWorkerHostMsg_WorkerReadyForInspection(embedded_worker_id_));
 }
diff --git a/content/renderer/service_worker/embedded_worker_context_client.h b/content/renderer/service_worker/embedded_worker_context_client.h
index 1221f85a..1f7ac8d4 100644
--- a/content/renderer/service_worker/embedded_worker_context_client.h
+++ b/content/renderer/service_worker/embedded_worker_context_client.h
@@ -66,6 +66,10 @@
   virtual void getClients(blink::WebServiceWorkerClientsCallbacks*);
   virtual void openWindow(const blink::WebURL&,
                           blink::WebServiceWorkerClientCallbacks*);
+  virtual void setCachedMetadata(const blink::WebURL&,
+                                 const char* data,
+                                 size_t size);
+  virtual void clearCachedMetadata(const blink::WebURL&);
   virtual void workerReadyForInspection();
 
   // Called on the main thread.
diff --git a/content/renderer/service_worker/service_worker_script_context.cc b/content/renderer/service_worker/service_worker_script_context.cc
index ef72a1ac..372c7b9 100644
--- a/content/renderer/service_worker/service_worker_script_context.cc
+++ b/content/renderer/service_worker/service_worker_script_context.cc
@@ -233,6 +233,17 @@
   Send(new ServiceWorkerHostMsg_OpenWindow(GetRoutingID(), request_id, url));
 }
 
+void ServiceWorkerScriptContext::SetCachedMetadata(const GURL& url,
+                                                   const char* data,
+                                                   size_t size) {
+  std::vector<char> copy(data, data + size);
+  Send(new ServiceWorkerHostMsg_SetCachedMetadata(GetRoutingID(), url, copy));
+}
+
+void ServiceWorkerScriptContext::ClearCachedMetadata(const GURL& url) {
+  Send(new ServiceWorkerHostMsg_ClearCachedMetadata(GetRoutingID(), url));
+}
+
 void ServiceWorkerScriptContext::PostMessageToDocument(
     int client_id,
     const base::string16& message,
diff --git a/content/renderer/service_worker/service_worker_script_context.h b/content/renderer/service_worker/service_worker_script_context.h
index d33e911..10a026e2 100644
--- a/content/renderer/service_worker/service_worker_script_context.h
+++ b/content/renderer/service_worker/service_worker_script_context.h
@@ -76,6 +76,8 @@
       blink::WebServiceWorkerClientsCallbacks* callbacks);
   void OpenWindow(const GURL& url,
                   blink::WebServiceWorkerClientCallbacks* callbacks);
+  void SetCachedMetadata(const GURL& url, const char* data, size_t size);
+  void ClearCachedMetadata(const GURL& url);
   void PostMessageToDocument(
       int client_id,
       const base::string16& message,
diff --git a/content/shell/BUILD.gn b/content/shell/BUILD.gn
index 338c565..b0f6e1b 100644
--- a/content/shell/BUILD.gn
+++ b/content/shell/BUILD.gn
@@ -225,6 +225,9 @@
     "renderer/test_runner/web_test_runner.h",
   ]
 
+  # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+  configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
+
   defines = [ "CONTENT_SHELL_VERSION=\"$content_shell_version\"" ]
 
   public_deps = [
@@ -280,9 +283,6 @@
 
   if (is_win) {
     #'LinkIncremental': '<(msvs_large_module_debug_link_mode)',  TODO(GYP)
-
-    # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
-    cflags = [ "/wd4267" ]
     sources += [
       "common/v8_breakpad_support_win.cc",
       "common/v8_breakpad_support_win.h",
diff --git a/content/shell/android/BUILD.gn b/content/shell/android/BUILD.gn
index 210c0f1..9f1a7fa 100644
--- a/content/shell/android/BUILD.gn
+++ b/content/shell/android/BUILD.gn
@@ -168,7 +168,7 @@
   android_manifest = "javatests/AndroidManifest.xml"
 }
 
-if (cpu_arch != "x64" && cpu_arch != "arm64") {
+if (current_cpu != "x64" && current_cpu != "arm64") {
   chromium_linker_test_manifest =
       "$target_gen_dir/linker_test_apk/AndroidManifest.xml"
 
diff --git a/content/shell/android/browsertests_apk/content_browser_tests_jni_onload.cc b/content/shell/android/browsertests_apk/content_browser_tests_jni_onload.cc
index 3f62c34..fc0a37b0 100644
--- a/content/shell/android/browsertests_apk/content_browser_tests_jni_onload.cc
+++ b/content/shell/android/browsertests_apk/content_browser_tests_jni_onload.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 #include "base/android/jni_android.h"
-#include "base/android/jni_onload_delegate.h"
+#include "base/bind.h"
 #include "content/public/app/content_jni_onload.h"
 #include "content/public/app/content_main.h"
 #include "content/public/test/nested_message_pump_android.h"
@@ -13,20 +13,13 @@
 
 namespace {
 
-class ContentBrowserTestsJNIOnLoadDelegate :
-      public base::android::JNIOnLoadDelegate {
- public:
-  bool RegisterJNI(JNIEnv* env) override;
-  bool Init() override;
-};
-
-bool ContentBrowserTestsJNIOnLoadDelegate::RegisterJNI(JNIEnv* env) {
+bool RegisterJNI(JNIEnv* env) {
   return content::android::RegisterShellJni(env) &&
       content::NestedMessagePumpAndroid::RegisterJni(env) &&
       content::RegisterContentBrowserTestsAndroid(env);
 }
 
-bool ContentBrowserTestsJNIOnLoadDelegate::Init() {
+bool Init() {
   content::SetContentMainDelegate(new content::ShellMainDelegate());
   return true;
 }
@@ -36,8 +29,9 @@
 
 // This is called by the VM when the shared library is first loaded.
 JNI_EXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
-  ContentBrowserTestsJNIOnLoadDelegate delegate;
-  if (!content::android::OnJNIOnLoad(vm, &delegate))
+  if (!content::android::OnJNIOnLoadRegisterJNI(
+          vm, base::Bind(&RegisterJNI)) ||
+      !content::android::OnJNIOnLoadInit(base::Bind(&Init)))
     return -1;
 
   return JNI_VERSION_1_4;
diff --git a/content/shell/android/linker_test_apk/chromium_linker_test_android.cc b/content/shell/android/linker_test_apk/chromium_linker_test_android.cc
index 6e8ef56..b8aa1fa 100644
--- a/content/shell/android/linker_test_apk/chromium_linker_test_android.cc
+++ b/content/shell/android/linker_test_apk/chromium_linker_test_android.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 #include "base/android/jni_android.h"
-#include "base/android/jni_onload_delegate.h"
+#include "base/bind.h"
 #include "content/public/app/content_jni_onload.h"
 #include "content/public/app/content_main.h"
 #include "content/public/browser/android/compositor.h"
@@ -13,34 +13,31 @@
 
 namespace {
 
-class ChromiumLinkerTestJNIOnLoadDelegate :
-    public base::android::JNIOnLoadDelegate {
- public:
-  bool RegisterJNI(JNIEnv* env) override {
-    // To be called only from the UI thread.  If loading the library is done on
-    // a separate thread, this should be moved elsewhere.
-    if (!content::android::RegisterShellJni(env))
-      return false;
+bool RegisterJNI(JNIEnv* env) {
+  // To be called only from the UI thread.  If loading the library is done on
+  // a separate thread, this should be moved elsewhere.
+  if (!content::android::RegisterShellJni(env))
+    return false;
 
-    if (!content::RegisterLinkerTestsJni(env))
-      return false;
+  if (!content::RegisterLinkerTestsJni(env))
+    return false;
 
-    return true;
-  }
+  return true;
+}
 
-  bool Init() override {
-    content::Compositor::Initialize();
-    content::SetContentMainDelegate(new content::ShellMainDelegate());
-    return true;
-  }
-};
+bool Init() {
+  content::Compositor::Initialize();
+  content::SetContentMainDelegate(new content::ShellMainDelegate());
+  return true;
+}
 
 }  // namespace
 
 // This is called by the VM when the shared library is first loaded.
 JNI_EXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
-  ChromiumLinkerTestJNIOnLoadDelegate delegate;
-  if (!content::android::OnJNIOnLoad(vm, &delegate))
+  if (!content::android::OnJNIOnLoadRegisterJNI(
+          vm, base::Bind(&RegisterJNI)) ||
+      !content::android::OnJNIOnLoadInit(base::Bind(&Init)))
     return -1;
 
   return JNI_VERSION_1_4;
diff --git a/content/shell/android/linker_test_apk/src/org/chromium/chromium_linker_test_apk/ChromiumLinkerTestApplication.java b/content/shell/android/linker_test_apk/src/org/chromium/chromium_linker_test_apk/ChromiumLinkerTestApplication.java
index cf72619..9a29d4f0 100644
--- a/content/shell/android/linker_test_apk/src/org/chromium/chromium_linker_test_apk/ChromiumLinkerTestApplication.java
+++ b/content/shell/android/linker_test_apk/src/org/chromium/chromium_linker_test_apk/ChromiumLinkerTestApplication.java
@@ -4,15 +4,14 @@
 
 package org.chromium.chromium_linker_test_apk;
 
-import android.app.Application;
-
+import org.chromium.base.BaseChromiumApplication;
 import org.chromium.base.PathUtils;
 import org.chromium.base.ResourceExtractor;
 
 /**
  * Application for testing the Chromium Linker
  */
-public class ChromiumLinkerTestApplication extends Application {
+public class ChromiumLinkerTestApplication extends BaseChromiumApplication {
 
     /**
      * icudtl.dat provides ICU (i18n library) with all the data for its
diff --git a/content/shell/android/shell_library_loader.cc b/content/shell/android/shell_library_loader.cc
index 6d80fc3a..5bcd539a 100644
--- a/content/shell/android/shell_library_loader.cc
+++ b/content/shell/android/shell_library_loader.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 #include "base/android/jni_android.h"
-#include "base/android/jni_onload_delegate.h"
+#include "base/bind.h"
 #include "content/public/app/content_jni_onload.h"
 #include "content/public/app/content_main.h"
 #include "content/public/browser/android/compositor.h"
@@ -12,17 +12,11 @@
 
 namespace {
 
-class ShellJNIOnLoadDelegate : public base::android::JNIOnLoadDelegate {
- public:
-  bool RegisterJNI(JNIEnv* env) override;
-  bool Init() override;
-};
-
-bool ShellJNIOnLoadDelegate::RegisterJNI(JNIEnv* env) {
+bool RegisterJNI(JNIEnv* env) {
   return content::android::RegisterShellJni(env);
 }
 
-bool ShellJNIOnLoadDelegate::Init() {
+bool Init() {
   content::Compositor::Initialize();
   content::SetContentMainDelegate(new content::ShellMainDelegate());
   return true;
@@ -33,8 +27,9 @@
 
 // This is called by the VM when the shared library is first loaded.
 JNI_EXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
-  ShellJNIOnLoadDelegate delegate;
-  if (!content::android::OnJNIOnLoad(vm, &delegate))
+  if (!content::android::OnJNIOnLoadRegisterJNI(
+          vm, base::Bind(&RegisterJNI)) ||
+      !content::android::OnJNIOnLoadInit(base::Bind(&Init)))
     return -1;
 
   return JNI_VERSION_1_4;
diff --git a/content/shell/browser/layout_test/layout_test_push_messaging_service.cc b/content/shell/browser/layout_test/layout_test_push_messaging_service.cc
index 4082559..e283e0d 100644
--- a/content/shell/browser/layout_test/layout_test_push_messaging_service.cc
+++ b/content/shell/browser/layout_test/layout_test_push_messaging_service.cc
@@ -67,9 +67,10 @@
 void LayoutTestPushMessagingService::Unregister(
     const GURL& requesting_origin,
     int64 service_worker_registration_id,
+    const std::string& sender_id,
     bool retry_on_failure,
     const UnregisterCallback& callback) {
-  callback.Run(PUSH_UNREGISTRATION_STATUS_SUCCESS_UNREGISTER);
+  callback.Run(PUSH_UNREGISTRATION_STATUS_SUCCESS_UNREGISTERED);
 }
 
 }  // namespace content
diff --git a/content/shell/browser/layout_test/layout_test_push_messaging_service.h b/content/shell/browser/layout_test/layout_test_push_messaging_service.h
index ad44b267..2393bdb 100644
--- a/content/shell/browser/layout_test/layout_test_push_messaging_service.h
+++ b/content/shell/browser/layout_test/layout_test_push_messaging_service.h
@@ -42,6 +42,7 @@
       const GURL& embedding_origin) override;
   void Unregister(const GURL& requesting_origin,
                   int64 service_worker_registration_id,
+                  const std::string& sender_id,
                   bool retry_on_failure,
                   const UnregisterCallback& callback) override;
 
diff --git a/content/shell/renderer/layout_test/webkit_test_runner.cc b/content/shell/renderer/layout_test/webkit_test_runner.cc
index cf051cf..779c639 100644
--- a/content/shell/renderer/layout_test/webkit_test_runner.cc
+++ b/content/shell/renderer/layout_test/webkit_test_runner.cc
@@ -51,6 +51,7 @@
 #include "third_party/WebKit/public/platform/WebSize.h"
 #include "third_party/WebKit/public/platform/WebString.h"
 #include "third_party/WebKit/public/platform/WebThread.h"
+#include "third_party/WebKit/public/platform/WebTraceLocation.h"
 #include "third_party/WebKit/public/platform/WebURL.h"
 #include "third_party/WebKit/public/platform/WebURLError.h"
 #include "third_party/WebKit/public/platform/WebURLRequest.h"
@@ -90,6 +91,7 @@
 using blink::WebURLRequest;
 using blink::WebScreenOrientationType;
 using blink::WebTestingSupport;
+using blink::WebTraceLocation;
 using blink::WebThread;
 using blink::WebVector;
 using blink::WebView;
@@ -263,11 +265,13 @@
 
 void WebKitTestRunner::PostTask(WebTask* task) {
   Platform::current()->currentThread()->postTask(
+      WebTraceLocation(__FUNCTION__, __FILE__),
       new InvokeTaskHelper(make_scoped_ptr(task)));
 }
 
 void WebKitTestRunner::PostDelayedTask(WebTask* task, long long ms) {
   Platform::current()->currentThread()->postDelayedTask(
+      WebTraceLocation(__FUNCTION__, __FILE__),
       new InvokeTaskHelper(make_scoped_ptr(task)), ms);
 }
 
diff --git a/content/shell/renderer/test_runner/event_sender.cc b/content/shell/renderer/test_runner/event_sender.cc
index 4352db34..1960be99 100644
--- a/content/shell/renderer/test_runner/event_sender.cc
+++ b/content/shell/renderer/test_runner/event_sender.cc
@@ -1661,16 +1661,9 @@
   WebGestureEvent event;
   event.type = WebInputEvent::GestureFlingStart;
 
-  // TODO(tdresser): Once we've migrated all calls to GestureFlingStart
-  // to pass the device string, throw an error if args is empty. See
-  // crbug.com/456136 for details.
-  std::string device_string = kSourceDeviceStringTouchpad;
-  if (!args->PeekNext().IsEmpty() && args->PeekNext()->IsString()) {
-    if (!args->GetNext(&device_string)) {
-      args->ThrowError();
-      return;
-    }
-  }
+  std::string device_string;
+  if (!args->PeekNext().IsEmpty() && args->PeekNext()->IsString())
+    args->GetNext(&device_string);
 
   if (device_string == kSourceDeviceStringTouchpad) {
     event.sourceDevice = blink::WebGestureDeviceTouchpad;
diff --git a/content/shell/renderer/test_runner/test_plugin.cc b/content/shell/renderer/test_runner/test_plugin.cc
index 809ec71..0a80bdb 100644
--- a/content/shell/renderer/test_runner/test_plugin.cc
+++ b/content/shell/renderer/test_runner/test_plugin.cc
@@ -18,6 +18,8 @@
 #include "third_party/WebKit/public/platform/Platform.h"
 #include "third_party/WebKit/public/platform/WebCompositorSupport.h"
 #include "third_party/WebKit/public/platform/WebGraphicsContext3D.h"
+#include "third_party/WebKit/public/platform/WebThread.h"
+#include "third_party/WebKit/public/platform/WebTraceLocation.h"
 #include "third_party/WebKit/public/web/WebFrame.h"
 #include "third_party/WebKit/public/web/WebInputEvent.h"
 #include "third_party/WebKit/public/web/WebKit.h"
@@ -122,10 +124,15 @@
   return blink::WebPluginContainer::TouchEventRequestTypeNone;
 }
 
-void DeferredDelete(void* context) {
-  TestPlugin* plugin = static_cast<TestPlugin*>(context);
-  delete plugin;
-}
+class DeferredDeleteTask : public blink::WebThread::Task {
+ public:
+  DeferredDeleteTask(scoped_ptr<TestPlugin> plugin) : plugin_(plugin.Pass()) {}
+
+  void run() override {}
+
+ private:
+  scoped_ptr<TestPlugin> plugin_;
+};
 
 }  // namespace
 
@@ -239,7 +246,9 @@
   container_ = 0;
   frame_ = 0;
 
-  blink::Platform::current()->callOnMainThread(DeferredDelete, this);
+  blink::Platform::current()->mainThread()->postTask(
+      blink::WebTraceLocation(__FUNCTION__, __FILE__),
+      new DeferredDeleteTask(make_scoped_ptr(this)));
 }
 
 NPObject* TestPlugin::scriptableObject() {
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index a7ed9f83..e9f0372 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -257,6 +257,8 @@
 
     defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
 
+    configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
+
     deps = [
       ":browsertest_support",
       ":web_ui_test_mojo_bindings",
diff --git a/content/test/accessibility_browser_test_utils.cc b/content/test/accessibility_browser_test_utils.cc
index a2f3815..94245ecb 100644
--- a/content/test/accessibility_browser_test_utils.cc
+++ b/content/test/accessibility_browser_test_utils.cc
@@ -92,7 +92,7 @@
   // Skip any accessibility notifications related to "about:blank",
   // to avoid a possible race condition between the test beginning
   // listening for accessibility events and "about:blank" loading.
-  const ui::AXNodeData& root = GetAXTree().GetRoot()->data();
+  const ui::AXNodeData& root = GetAXTree().root()->data();
   for (size_t i = 0; i < root.string_attributes.size(); ++i) {
     if (root.string_attributes[i].first != ui::AX_ATTR_DOC_URL)
       continue;
diff --git a/content/test/data/indexeddb/disk_full_on_commit.html b/content/test/data/indexeddb/disk_full_on_commit.html
new file mode 100644
index 0000000..c42c850
--- /dev/null
+++ b/content/test/data/indexeddb/disk_full_on_commit.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<html>
+<!--
+  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.
+-->
+<head>
+<title>IDB test that a commit failing due to disk full results in a QuotaError</title>
+<script type="text/javascript" src="common.js"></script>
+<script>
+
+function test() {
+  indexedDBTest(prepareDatabase, startNewTransaction);
+}
+
+function prepareDatabase() {
+  db = event.target.result;
+  objectStore = db.createObjectStore('store');
+}
+
+function startNewTransaction() {
+  tx = db.transaction('store', 'readwrite');
+  tx.objectStore('store').put('value', 'key');
+  tx.oncomplete = unexpectedCompleteCallback;
+  tx.onabort = function() {
+    shouldBeEqualToString("tx.error.name", "QuotaExceededError");
+    done();
+  };
+}
+
+</script>
+</head>
+<body onLoad="test()">
+<div id="status">Starting...</div>
+</body>
+</html>
diff --git a/content/test/data/navigation_controller/page_with_iframe.html b/content/test/data/navigation_controller/page_with_iframe.html
new file mode 100644
index 0000000..cdd13b6
--- /dev/null
+++ b/content/test/data/navigation_controller/page_with_iframe.html
@@ -0,0 +1,7 @@
+<html>
+<head></head>
+<body>
+  <p>This page has an iframe. Yay for iframes!
+  <p><iframe src="about:blank"></iframe>
+</body>
+</html>
diff --git a/content/test/data/navigation_controller/simple_page_2.html b/content/test/data/navigation_controller/simple_page_2.html
new file mode 100644
index 0000000..e9eb828
--- /dev/null
+++ b/content/test/data/navigation_controller/simple_page_2.html
@@ -0,0 +1,7 @@
+<html>
+<head>
+</head>
+<body>
+<p>Simple page 2.
+</body>
+</html>
diff --git a/content/test/fake_renderer_scheduler.cc b/content/test/fake_renderer_scheduler.cc
index b38154f..86bf4a2 100644
--- a/content/test/fake_renderer_scheduler.cc
+++ b/content/test/fake_renderer_scheduler.cc
@@ -35,6 +35,9 @@
 void FakeRendererScheduler::WillBeginFrame(const cc::BeginFrameArgs& args) {
 }
 
+void FakeRendererScheduler::BeginFrameNotExpectedSoon() {
+}
+
 void FakeRendererScheduler::DidCommitFrameToCompositor() {
 }
 
diff --git a/content/test/fake_renderer_scheduler.h b/content/test/fake_renderer_scheduler.h
index 8f25a8f..fd2d3e00 100644
--- a/content/test/fake_renderer_scheduler.h
+++ b/content/test/fake_renderer_scheduler.h
@@ -20,6 +20,7 @@
   scoped_refptr<base::SingleThreadTaskRunner> LoadingTaskRunner() override;
   scoped_refptr<SingleThreadIdleTaskRunner> IdleTaskRunner() override;
   void WillBeginFrame(const cc::BeginFrameArgs& args) override;
+  void BeginFrameNotExpectedSoon() override;
   void DidCommitFrameToCompositor() override;
   void DidReceiveInputEventOnCompositorThread(
       const blink::WebInputEvent& web_input_event) override;
diff --git a/content/test/gpu/gpu_tests/screenshot_sync.py b/content/test/gpu/gpu_tests/screenshot_sync.py
index e38edb8..1adfeed 100644
--- a/content/test/gpu/gpu_tests/screenshot_sync.py
+++ b/content/test/gpu/gpu_tests/screenshot_sync.py
@@ -16,7 +16,6 @@
 data_path = os.path.join(
     util.GetChromiumSrcDir(), 'content', 'test', 'data', 'gpu')
 
-@benchmark.Disabled('mac')
 class _ScreenshotSyncValidator(page_test.PageTest):
   def CustomizeBrowserOptions(self, options):
     options.AppendExtraBrowserArgs('--force-gpu-rasterization')
@@ -43,7 +42,6 @@
     for n in range(0, repetitions):
       CheckScreenshot()
 
-@benchmark.Disabled('mac')
 class ScreenshotSyncPage(page.Page):
   def __init__(self, page_set, base_dir):
     super(ScreenshotSyncPage, self).__init__(
@@ -57,7 +55,7 @@
     super(ScreenshotSyncPage, self).RunNavigateSteps(action_runner)
 
 
-@benchmark.Disabled('mac')
+@benchmark.Disabled('linux', 'mac', 'win')
 class ScreenshotSyncProcess(benchmark.Benchmark):
   """Tests that screenhots are properly synchronized with the frame one which
   they were requested"""
diff --git a/content/test/gpu/gpu_tests/webgl_conformance_expectations.py b/content/test/gpu/gpu_tests/webgl_conformance_expectations.py
index a9bbad1..750ff57 100644
--- a/content/test/gpu/gpu_tests/webgl_conformance_expectations.py
+++ b/content/test/gpu/gpu_tests/webgl_conformance_expectations.py
@@ -49,6 +49,10 @@
     self.Fail('conformance/glsl/misc/shader-with-array-of-structs-uniform.html',
         ['win7', 'intel', 'nvidia'], bug=373972)
 
+    # Win8 / NVIDIA failures
+    self.Fail('conformance/textures/tex-image-and-sub-image-2d-with-array-buffer-view.html',
+        ['win', 'nvidia'], bug=459265)
+
     # Win / AMD failures
     self.Fail('conformance/textures/texparameter-test.html',
         ['win', 'amd', 'd3d9'], bug=839) # angle bug ID
diff --git a/content/test/test_render_frame_host.cc b/content/test/test_render_frame_host.cc
index d629a9e..9249d26 100644
--- a/content/test/test_render_frame_host.cc
+++ b/content/test/test_render_frame_host.cc
@@ -187,9 +187,10 @@
   OnDidCommitProvisionalLoad(msg);
 }
 
-void TestRenderFrameHost::SendBeginNavigationWithURL(const GURL& url) {
-  BeginNavigationParams begin_params(
-      "GET", std::string(), net::LOAD_NORMAL, false);
+void TestRenderFrameHost::SendBeginNavigationWithURL(const GURL& url,
+                                                     bool has_user_gesture) {
+  BeginNavigationParams begin_params("GET", std::string(), net::LOAD_NORMAL,
+                                     has_user_gesture);
   CommonNavigationParams common_params;
   common_params.url = url;
   common_params.referrer = Referrer(GURL(), blink::WebReferrerPolicyDefault);
@@ -215,9 +216,9 @@
       static_cast<NavigatorImpl*>(frame_tree_node_->navigator())
           ->GetNavigationRequestForNodeForTesting(frame_tree_node_);
 
-  // We are simulating a renderer-initiated navigation.
+  // We are simulating a renderer-initiated user-initiated navigation.
   if (!request) {
-    SendBeginNavigationWithURL(url);
+    SendBeginNavigationWithURL(url, true);
     request = static_cast<NavigatorImpl*>(frame_tree_node_->navigator())
         ->GetNavigationRequestForNodeForTesting(frame_tree_node_);
   }
diff --git a/content/test/test_render_frame_host.h b/content/test/test_render_frame_host.h
index 78536c2..fcf4a74 100644
--- a/content/test/test_render_frame_host.h
+++ b/content/test/test_render_frame_host.h
@@ -85,7 +85,7 @@
       int response_code,
       const base::FilePath* file_path_for_history_item,
       const std::vector<GURL>& redirects);
-  void SendBeginNavigationWithURL(const GURL& url);
+  void SendBeginNavigationWithURL(const GURL& url, bool has_user_gesture);
 
   void DidDisownOpener();
 
diff --git a/content/test/web_layer_tree_view_impl_for_testing.h b/content/test/web_layer_tree_view_impl_for_testing.h
index 3b21238b..6ff82f9 100644
--- a/content/test/web_layer_tree_view_impl_for_testing.h
+++ b/content/test/web_layer_tree_view_impl_for_testing.h
@@ -68,6 +68,7 @@
   void WillBeginMainFrame() override {}
   void DidBeginMainFrame() override {}
   void BeginMainFrame(const cc::BeginFrameArgs& args) override {}
+  void BeginMainFrameNotExpectedSoon() override {}
   void Layout() override;
   void ApplyViewportDeltas(const gfx::Vector2dF& inner_delta,
                            const gfx::Vector2dF& outer_delta,
diff --git a/content/zygote/zygote_linux.cc b/content/zygote/zygote_linux.cc
index 42dd5d8..5944f87 100644
--- a/content/zygote/zygote_linux.cc
+++ b/content/zygote/zygote_linux.cc
@@ -106,7 +106,7 @@
   action.sa_handler = &SIGCHLDHandler;
   CHECK(sigaction(SIGCHLD, &action, NULL) == 0);
 
-  if (UsingSUIDSandbox()) {
+  if (UsingSUIDSandbox() || UsingNSSandbox()) {
     // Let the ZygoteHost know we are ready to go.
     // The receiving code is in content/browser/zygote_host_linux.cc.
     bool r = UnixDomainSocket::SendMsg(kZygoteSocketPairFd,
@@ -147,6 +147,10 @@
   return sandbox_flags_ & kSandboxLinuxSUID;
 }
 
+bool Zygote::UsingNSSandbox() const {
+  return sandbox_flags_ & kSandboxLinuxUserNS;
+}
+
 bool Zygote::HandleRequestFromBrowser(int fd) {
   ScopedVector<base::ScopedFD> fds;
   char buf[kZygoteMaxMessageLength];
diff --git a/content/zygote/zygote_linux.h b/content/zygote/zygote_linux.h
index 41773fa..c8c44f07 100644
--- a/content/zygote/zygote_linux.h
+++ b/content/zygote/zygote_linux.h
@@ -53,6 +53,8 @@
 
   // Returns true if the SUID sandbox is active.
   bool UsingSUIDSandbox() const;
+  // Returns true if the NS sandbox is active.
+  bool UsingNSSandbox() const;
 
   // ---------------------------------------------------------------------------
   // Requests from the browser...
diff --git a/content/zygote/zygote_main_linux.cc b/content/zygote/zygote_main_linux.cc
index f43c299..efbfdb0 100644
--- a/content/zygote/zygote_main_linux.cc
+++ b/content/zygote/zygote_main_linux.cc
@@ -533,10 +533,14 @@
       linux_sandbox->setuid_sandbox_client()->IsSuidSandboxChild();
   const bool using_namespace_sandbox =
       sandbox::NamespaceSandbox::InNewUserNamespace();
+  const bool using_layer1_sandbox =
+      using_setuid_sandbox || using_namespace_sandbox;
 
   if (using_setuid_sandbox) {
     linux_sandbox->setuid_sandbox_client()->CloseDummyFile();
+  }
 
+  if (using_layer1_sandbox) {
     // Let the ZygoteHost know we're booting up.
     CHECK(UnixDomainSocket::SendMsg(kZygoteSocketPairFd,
                                     kZygoteBootMessage,
@@ -546,8 +550,6 @@
 
   VLOG(1) << "ZygoteMain: initializing " << fork_delegates.size()
           << " fork delegates";
-  const bool using_layer1_sandbox =
-      using_setuid_sandbox || using_namespace_sandbox;
   for (ZygoteForkDelegate* fork_delegate : fork_delegates) {
     fork_delegate->Init(GetSandboxFD(), using_layer1_sandbox);
   }
diff --git a/courgette/BUILD.gn b/courgette/BUILD.gn
index d277eb9e..af81012b 100644
--- a/courgette/BUILD.gn
+++ b/courgette/BUILD.gn
@@ -58,7 +58,7 @@
 }
 
 executable("courgette") {
-  if (is_win && cpu_arch == "x64") {
+  if (is_win && current_cpu == "x64") {
     # The build infrastructure needs courgette to be named courgette64.
     output_name = "courgette64"
   }
@@ -107,10 +107,8 @@
     "third_party/paged_array_unittest.cc",
   ]
 
-  if (is_win) {
-    # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
-    cflags = [ "/wd4267" ]
-  }
+  # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+  configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
 
   deps = [
     ":courgette_lib",
diff --git a/courgette/testdata/chrome64-1-2.v1.bsdiff b/courgette/testdata/chrome64-1-2.v1.bsdiff
new file mode 100644
index 0000000..6c0e83e
--- /dev/null
+++ b/courgette/testdata/chrome64-1-2.v1.bsdiff
Binary files differ
diff --git a/courgette/testdata/chrome64-1-2.v1.patch b/courgette/testdata/chrome64-1-2.v1.patch
deleted file mode 100644
index f8c25ec0..0000000
--- a/courgette/testdata/chrome64-1-2.v1.patch
+++ /dev/null
Binary files differ
diff --git a/courgette/testdata/setup1-setup2.v1.bsdiff b/courgette/testdata/setup1-setup2.v1.bsdiff
new file mode 100644
index 0000000..8b94461b
--- /dev/null
+++ b/courgette/testdata/setup1-setup2.v1.bsdiff
Binary files differ
diff --git a/courgette/testdata/setup1-setup2.v1.patch b/courgette/testdata/setup1-setup2.v1.patch
deleted file mode 100644
index dbdfa2b..0000000
--- a/courgette/testdata/setup1-setup2.v1.patch
+++ /dev/null
Binary files differ
diff --git a/courgette/versioning_unittest.cc b/courgette/versioning_unittest.cc
index 5313b8b..91154d82 100644
--- a/courgette/versioning_unittest.cc
+++ b/courgette/versioning_unittest.cc
@@ -9,17 +9,19 @@
 #include "base/basictypes.h"
 #include "courgette/courgette.h"
 #include "courgette/streams.h"
+#include "courgette/third_party/bsdiff.h"
 
 class VersioningTest : public BaseTest {
  public:
-  void TestApplyingOldPatch(const char* src_file,
-                            const char* patch_file,
-                            const char* expected_file) const;
+  void TestApplyingOldBsDiffPatch(const char* src_file,
+                                  const char* patch_file,
+                                  const char* expected_file) const;
 };
 
-void VersioningTest::TestApplyingOldPatch(const char* src_file,
-                                          const char* patch_file,
-                                          const char* expected_file) const {
+void VersioningTest::TestApplyingOldBsDiffPatch(
+    const char* src_file,
+    const char* patch_file,
+    const char* expected_file) const {
   std::string old_buffer = FileContents(src_file);
   std::string new_buffer = FileContents(patch_file);
   std::string expected_buffer = FileContents(expected_file);
@@ -31,12 +33,10 @@
 
   courgette::SinkStream generated_stream;
 
-  courgette::Status status =
-      courgette::ApplyEnsemblePatch(&old_stream,
-                                    &patch_stream,
-                                    &generated_stream);
+  courgette::BSDiffStatus status = courgette::ApplyBinaryPatch(
+      &old_stream, &patch_stream, &generated_stream);
 
-  EXPECT_EQ(status, courgette::C_OK);
+  EXPECT_EQ(status, courgette::OK);
 
   size_t expected_length = expected_buffer.size();
   size_t generated_length = generated_stream.Length();
@@ -47,11 +47,11 @@
                       expected_length));
 }
 
-
-TEST_F(VersioningTest, All) {
-  TestApplyingOldPatch("setup1.exe", "setup1-setup2.v1.patch", "setup2.exe");
-  TestApplyingOldPatch("chrome64_1.exe", "chrome64-1-2.v1.patch",
-                       "chrome64_2.exe");
+TEST_F(VersioningTest, BsDiff) {
+  TestApplyingOldBsDiffPatch("setup1.exe", "setup1-setup2.v1.bsdiff",
+                             "setup2.exe");
+  TestApplyingOldBsDiffPatch("chrome64_1.exe", "chrome64-1-2.v1.bsdiff",
+                             "chrome64_2.exe");
 
   // We also need a way to test that newly generated patches are appropriately
   // applicable by older clients... not sure of the best way to do that.
diff --git a/crypto/BUILD.gn b/crypto/BUILD.gn
index f6c45aa..d717197 100644
--- a/crypto/BUILD.gn
+++ b/crypto/BUILD.gn
@@ -93,6 +93,9 @@
     "third_party/nss/sha512.cc",
   ]
 
+  # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+  configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
+
   deps = [
     ":platform",
     "//base",
@@ -126,11 +129,6 @@
     deps += [ "//third_party/android_tools:cpu_features" ]
   }
 
-  if (is_win) {
-    # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
-    cflags = [ "/wd4267" ]
-  }
-
   if (use_openssl) {
     # Remove NSS files when using OpenSSL
     sources -= [
diff --git a/crypto/ec_private_key_openssl.cc b/crypto/ec_private_key_openssl.cc
index 2d44759d..b6b004cf 100644
--- a/crypto/ec_private_key_openssl.cc
+++ b/crypto/ec_private_key_openssl.cc
@@ -25,9 +25,9 @@
 //       style guide, hence the unusual parameter placement / types.
 typedef int (*ExportBioFunction)(BIO* bio, const void* key);
 
-typedef ScopedOpenSSL<PKCS8_PRIV_KEY_INFO, PKCS8_PRIV_KEY_INFO_free>::Type
-    ScopedPKCS8_PRIV_KEY_INFO;
-typedef ScopedOpenSSL<X509_SIG, X509_SIG_free>::Type ScopedX509_SIG;
+using ScopedPKCS8_PRIV_KEY_INFO =
+    ScopedOpenSSL<PKCS8_PRIV_KEY_INFO, PKCS8_PRIV_KEY_INFO_free>;
+using ScopedX509_SIG = ScopedOpenSSL<X509_SIG, X509_SIG_free>;
 
 // Helper to export |key| into |output| via the specified ExportBioFunction.
 bool ExportKeyWithBio(const void* key,
diff --git a/crypto/p224.cc b/crypto/p224.cc
index e8b56b83..11946a9 100644
--- a/crypto/p224.cc
+++ b/crypto/p224.cc
@@ -475,8 +475,8 @@
   uint32 x_equal = IsZero(h);
 
   // I = (2*H)²
-  for (int j = 0; j < 8; j++) {
-    i[j] = h[j] << 1;
+  for (int k = 0; k < 8; k++) {
+    i[k] = h[k] << 1;
   }
   Reduce(&i);
   Square(&i, i);
@@ -495,8 +495,8 @@
     return;
   }
 
-  for (int i = 0; i < 8; i++) {
-    r[i] <<= 1;
+  for (int k = 0; k < 8; k++) {
+    r[k] <<= 1;
   }
   Reduce(&r);
 
@@ -513,8 +513,8 @@
   Mul(&out->z, out->z, h);
 
   // X3 = r²-J-2*V
-  for (int i = 0; i < 8; i++) {
-    z1z1[i] = v[i] << 1;
+  for (int k = 0; k < 8; k++) {
+    z1z1[k] = v[k] << 1;
   }
   Add(&z1z1, j, z1z1);
   Reduce(&z1z1);
@@ -523,8 +523,8 @@
   Reduce(&out->x);
 
   // Y3 = r*(V-X3)-2*S1*J
-  for (int i = 0; i < 8; i++) {
-    s1[i] <<= 1;
+  for (int k = 0; k < 8; k++) {
+    s1[k] <<= 1;
   }
   Mul(&s1, s1, j);
   Subtract(&z1z1, v, out->x);
@@ -691,7 +691,7 @@
 }
 
 std::string Point::ToString() const {
-  FieldElement zinv, zinv_sq, x, y;
+  FieldElement zinv, zinv_sq, xx, yy;
 
   // If this is the point at infinity we return a string of all zeros.
   if (IsZero(this->z)) {
@@ -701,16 +701,16 @@
 
   Invert(&zinv, this->z);
   Square(&zinv_sq, zinv);
-  Mul(&x, this->x, zinv_sq);
+  Mul(&xx, x, zinv_sq);
   Mul(&zinv_sq, zinv_sq, zinv);
-  Mul(&y, this->y, zinv_sq);
+  Mul(&yy, y, zinv_sq);
 
-  Contract(&x);
-  Contract(&y);
+  Contract(&xx);
+  Contract(&yy);
 
   uint32 outwords[14];
-  Put224Bits(outwords, x);
-  Put224Bits(outwords + 7, y);
+  Put224Bits(outwords, xx);
+  Put224Bits(outwords + 7, yy);
   return std::string(reinterpret_cast<const char*>(outwords), sizeof(outwords));
 }
 
diff --git a/crypto/rsa_private_key_openssl.cc b/crypto/rsa_private_key_openssl.cc
index 799d0f0..9f8352d 100644
--- a/crypto/rsa_private_key_openssl.cc
+++ b/crypto/rsa_private_key_openssl.cc
@@ -19,13 +19,13 @@
 
 namespace {
 
-typedef ScopedOpenSSL<PKCS8_PRIV_KEY_INFO, PKCS8_PRIV_KEY_INFO_free>::Type
-    ScopedPKCS8_PRIV_KEY_INFO;
+using ScopedPKCS8_PRIV_KEY_INFO =
+    ScopedOpenSSL<PKCS8_PRIV_KEY_INFO, PKCS8_PRIV_KEY_INFO_free>;
 
 // Function pointer definition, for injecting the required key export function
 // into ExportKey, below. The supplied function should export EVP_PKEY into
 // the supplied BIO, returning 1 on success or 0 on failure.
-typedef int (ExportFunction)(BIO*, EVP_PKEY*);
+using ExportFunction = int (*)(BIO*, EVP_PKEY*);
 
 // Helper to export |key| into |output| via the specified ExportFunction.
 bool ExportKey(EVP_PKEY* key,
diff --git a/crypto/scoped_openssl_types.h b/crypto/scoped_openssl_types.h
index cc056e4..b392a072 100644
--- a/crypto/scoped_openssl_types.h
+++ b/crypto/scoped_openssl_types.h
@@ -22,15 +22,13 @@
 // base::internal::RunnableAdapter<>, but that's far too heavy weight.
 template <typename Type, void (*Destroyer)(Type*)>
 struct OpenSSLDestroyer {
-  typedef void AllowSelfReset;
+  using AllowSelfReset = void;
   void operator()(Type* ptr) const { Destroyer(ptr); }
 };
 
 template <typename PointerType, void (*Destroyer)(PointerType*)>
-struct ScopedOpenSSL {
-  typedef scoped_ptr<PointerType, OpenSSLDestroyer<PointerType, Destroyer> >
-      Type;
-};
+using ScopedOpenSSL =
+    scoped_ptr<PointerType, OpenSSLDestroyer<PointerType, Destroyer>>;
 
 struct OpenSSLFree {
   void operator()(uint8_t* ptr) const { OPENSSL_free(ptr); }
@@ -40,19 +38,21 @@
 // short-hand and prevalence. Note that OpenSSL types related to X.509 are
 // intentionally not included, as crypto/ does not generally deal with
 // certificates or PKI.
-typedef ScopedOpenSSL<BIGNUM, BN_free>::Type ScopedBIGNUM;
-typedef ScopedOpenSSL<EC_KEY, EC_KEY_free>::Type ScopedEC_KEY;
-typedef ScopedOpenSSL<BIO, BIO_free_all>::Type ScopedBIO;
-typedef ScopedOpenSSL<DSA, DSA_free>::Type ScopedDSA;
-typedef ScopedOpenSSL<ECDSA_SIG, ECDSA_SIG_free>::Type ScopedECDSA_SIG;
-typedef ScopedOpenSSL<EC_KEY, EC_KEY_free>::Type ScopedEC_KEY;
-typedef ScopedOpenSSL<EVP_MD_CTX, EVP_MD_CTX_destroy>::Type ScopedEVP_MD_CTX;
-typedef ScopedOpenSSL<EVP_PKEY, EVP_PKEY_free>::Type ScopedEVP_PKEY;
-typedef ScopedOpenSSL<EVP_PKEY_CTX, EVP_PKEY_CTX_free>::Type ScopedEVP_PKEY_CTX;
-typedef ScopedOpenSSL<RSA, RSA_free>::Type ScopedRSA;
+using ScopedBIGNUM = ScopedOpenSSL<BIGNUM, BN_free>;
+using ScopedEC_Key = ScopedOpenSSL<EC_KEY, EC_KEY_free>;
+using ScopedBIO = ScopedOpenSSL<BIO, BIO_free_all>;
+using ScopedDSA = ScopedOpenSSL<DSA, DSA_free>;
+using ScopedECDSA_SIG = ScopedOpenSSL<ECDSA_SIG, ECDSA_SIG_free>;
+using ScopedEC_GROUP = ScopedOpenSSL<EC_GROUP, EC_GROUP_free>;
+using ScopedEC_KEY = ScopedOpenSSL<EC_KEY, EC_KEY_free>;
+using ScopedEC_POINT = ScopedOpenSSL<EC_POINT, EC_POINT_free>;
+using ScopedEVP_MD_CTX = ScopedOpenSSL<EVP_MD_CTX, EVP_MD_CTX_destroy>;
+using ScopedEVP_PKEY = ScopedOpenSSL<EVP_PKEY, EVP_PKEY_free>;
+using ScopedEVP_PKEY_CTX = ScopedOpenSSL<EVP_PKEY_CTX, EVP_PKEY_CTX_free>;
+using ScopedRSA = ScopedOpenSSL<RSA, RSA_free>;
 
 // The bytes must have been allocated with OPENSSL_malloc.
-typedef scoped_ptr<uint8_t, OpenSSLFree> ScopedOpenSSLBytes;
+using ScopedOpenSSLBytes = scoped_ptr<uint8_t, OpenSSLFree>;
 
 }  // namespace crypto
 
diff --git a/crypto/signature_verifier_openssl.cc b/crypto/signature_verifier_openssl.cc
index 93ce9ba..a33d665 100644
--- a/crypto/signature_verifier_openssl.cc
+++ b/crypto/signature_verifier_openssl.cc
@@ -50,7 +50,7 @@
                                    const uint8* public_key_info,
                                    int public_key_info_len) {
   OpenSSLErrStackTracer err_tracer(FROM_HERE);
-  ScopedOpenSSL<X509_ALGOR, X509_ALGOR_free>::Type algorithm(
+  ScopedOpenSSL<X509_ALGOR, X509_ALGOR_free> algorithm(
       d2i_X509_ALGOR(NULL, &signature_algorithm, signature_algorithm_len));
   if (!algorithm.get())
     return false;
diff --git a/dbus/BUILD.gn b/dbus/BUILD.gn
index 4abcdb8..a133e72 100644
--- a/dbus/BUILD.gn
+++ b/dbus/BUILD.gn
@@ -39,9 +39,11 @@
   defines = [ "DBUS_IMPLEMENTATION" ]
 
   deps = [
-    "//base",
     "//third_party/protobuf:protobuf_lite",
   ]
+  public_deps = [
+    "//base",
+  ]
 
   public_configs = [ "//build/config/linux:dbus" ]
 }
diff --git a/device/battery/android/java/src/org/chromium/device/battery/BatteryStatusManager.java b/device/battery/android/java/src/org/chromium/device/battery/BatteryStatusManager.java
index 0601be1..b94c7f0 100644
--- a/device/battery/android/java/src/org/chromium/device/battery/BatteryStatusManager.java
+++ b/device/battery/android/java/src/org/chromium/device/battery/BatteryStatusManager.java
@@ -42,19 +42,26 @@
     // This is to workaround a Galaxy Nexus bug, see the comment in the constructor.
     private final boolean mIgnoreBatteryPresentState;
 
+    // Only used in L (API level 21 and higher).
+    private BatteryManager mLollipopBatteryManager;
+
     private boolean mEnabled = false;
 
     private BatteryStatusManager(
-            Context context, BatteryStatusCallback callback, boolean ignoreBatteryPresentState) {
+            Context context, BatteryStatusCallback callback, boolean ignoreBatteryPresentState,
+            BatteryManager batteryManager) {
         mAppContext = context.getApplicationContext();
         mCallback = callback;
         mIgnoreBatteryPresentState = ignoreBatteryPresentState;
+        mLollipopBatteryManager = batteryManager;
     }
 
     BatteryStatusManager(Context context, BatteryStatusCallback callback) {
         // BatteryManager.EXTRA_PRESENT appears to be unreliable on Galaxy Nexus,
         // Android 4.2.1, it always reports false. See http://crbug.com/384348.
-        this(context, callback, Build.MODEL.equals("Galaxy Nexus"));
+        this(context, callback, Build.MODEL.equals("Galaxy Nexus"),
+             Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP ? new BatteryManager()
+                                                                   : null);
     }
 
     /**
@@ -62,8 +69,10 @@
      * testing.
      */
     static BatteryStatusManager createBatteryStatusManagerForTesting(
-            Context context, BatteryStatusCallback callback) {
-        return new BatteryStatusManager(context, callback, false);
+            Context context,
+            BatteryStatusCallback callback,
+            BatteryManager batteryManager) {
+        return new BatteryStatusManager(context, callback, false, batteryManager);
     }
 
     /**
@@ -114,20 +123,44 @@
             level = 1.0;
         }
 
-        // Currently Android does not provide charging/discharging time, as a work-around
-        // we could compute it manually based on level delta.
+        // Currently Android (below L) does not provide charging/discharging time, as a work-around
+        // we could compute it manually based on the evolution of level delta.
         // TODO(timvolodine): add proper projection for chargingTime, dischargingTime
         // (see crbug.com/401553).
         boolean charging = pluggedStatus != 0;
         int status = intent.getIntExtra(BatteryManager.EXTRA_STATUS, -1);
         boolean batteryFull = status == BatteryManager.BATTERY_STATUS_FULL;
-        double chargingTime = (charging & batteryFull) ? 0 : Double.POSITIVE_INFINITY;
-        double dischargingTime = Double.POSITIVE_INFINITY;
+        double chargingTimeSeconds = (charging && batteryFull) ? 0 : Double.POSITIVE_INFINITY;
+        double dischargingTimeSeconds = Double.POSITIVE_INFINITY;
+
+        if (mLollipopBatteryManager != null) {
+            // On Lollipop we can provide a better estimate for chargingTime and dischargingTime.
+            double remainingCapacityRatio = mLollipopBatteryManager.getIntProperty(
+                    BatteryManager.BATTERY_PROPERTY_CAPACITY) / 100.0;
+            double batteryCapacityMicroAh = mLollipopBatteryManager.getIntProperty(
+                    BatteryManager.BATTERY_PROPERTY_CHARGE_COUNTER);
+            double averageCurrentMicroA = mLollipopBatteryManager.getIntProperty(
+                    BatteryManager.BATTERY_PROPERTY_CURRENT_AVERAGE);
+
+            if (charging) {
+                if (chargingTimeSeconds == Double.POSITIVE_INFINITY && averageCurrentMicroA > 0) {
+                    double chargeFromEmptyHours = batteryCapacityMicroAh / averageCurrentMicroA;
+                    chargingTimeSeconds =
+                            Math.ceil((1 - remainingCapacityRatio) * chargeFromEmptyHours * 3600.0);
+                }
+            } else {
+                if (averageCurrentMicroA < 0) {
+                    double dischargeFromFullHours = batteryCapacityMicroAh / -averageCurrentMicroA;
+                    dischargingTimeSeconds =
+                            Math.floor(remainingCapacityRatio * dischargeFromFullHours * 3600.0);
+                }
+            }
+        }
 
         BatteryStatus batteryStatus = new BatteryStatus();
         batteryStatus.charging = charging;
-        batteryStatus.chargingTime = chargingTime;
-        batteryStatus.dischargingTime = dischargingTime;
+        batteryStatus.chargingTime = chargingTimeSeconds;
+        batteryStatus.dischargingTime = dischargingTimeSeconds;
         batteryStatus.level = level;
         mCallback.onBatteryStatusChanged(batteryStatus);
     }
diff --git a/device/battery/android/javatests/src/org/chromium/device/battery/BatteryStatusManagerTest.java b/device/battery/android/javatests/src/org/chromium/device/battery/BatteryStatusManagerTest.java
index addcdde..536e31b 100644
--- a/device/battery/android/javatests/src/org/chromium/device/battery/BatteryStatusManagerTest.java
+++ b/device/battery/android/javatests/src/org/chromium/device/battery/BatteryStatusManagerTest.java
@@ -6,6 +6,7 @@
 
 import android.content.Intent;
 import android.os.BatteryManager;
+import android.os.Build;
 import android.test.AndroidTestCase;
 import android.test.suitebuilder.annotation.SmallTest;
 
@@ -42,11 +43,50 @@
         assertEquals(level, mLevel);
     }
 
+    private static class BatteryManagerForTesting extends BatteryManager {
+        private int mChargeCounter;
+        private int mCapacity;
+        private int mAverageCurrent;
+
+        @Override
+        public int getIntProperty(int id) {
+            switch(id) {
+                case BatteryManager.BATTERY_PROPERTY_CHARGE_COUNTER :
+                    return mChargeCounter;
+                case BatteryManager.BATTERY_PROPERTY_CAPACITY:
+                    return mCapacity;
+                case BatteryManager.BATTERY_PROPERTY_CURRENT_AVERAGE:
+                    return mAverageCurrent;
+            }
+            fail();
+            return 0;
+        }
+
+        public void setIntProperty(int id, int value) {
+            switch(id) {
+                case BatteryManager.BATTERY_PROPERTY_CHARGE_COUNTER :
+                    mChargeCounter = value;
+                    return;
+                case BatteryManager.BATTERY_PROPERTY_CAPACITY:
+                    mCapacity = value;
+                    return;
+                case BatteryManager.BATTERY_PROPERTY_CURRENT_AVERAGE:
+                    mAverageCurrent = value;
+                    return;
+            }
+            fail();
+        }
+    }
+
     @Override
     protected void setUp() throws Exception {
         super.setUp();
-        mManager =
-                BatteryStatusManager.createBatteryStatusManagerForTesting(getContext(), mCallback);
+        initializeManager(null);
+    }
+
+    public void initializeManager(BatteryManagerForTesting managerForTesting) {
+        mManager = BatteryStatusManager.createBatteryStatusManagerForTesting(
+                getContext(), mCallback, managerForTesting);
     }
 
     @SmallTest
@@ -125,4 +165,72 @@
         assertTrue(mManager.start());
         mManager.stop();
     }
+
+    @SmallTest
+    public void testLollipopChargingTimeEstimate() {
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP)
+            return;
+
+        BatteryManagerForTesting testManager = new BatteryManagerForTesting();
+        initializeManager(testManager);
+
+        Intent intent = new Intent(Intent.ACTION_BATTERY_CHANGED);
+        intent.putExtra(BatteryManager.EXTRA_PRESENT, true);
+        intent.putExtra(BatteryManager.EXTRA_PLUGGED, BatteryManager.BATTERY_PLUGGED_USB);
+        intent.putExtra(BatteryManager.EXTRA_LEVEL, 50);
+        intent.putExtra(BatteryManager.EXTRA_SCALE, 100);
+
+        testManager.setIntProperty(BatteryManager.BATTERY_PROPERTY_CHARGE_COUNTER, 1000);
+        testManager.setIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY, 50);
+        testManager.setIntProperty(BatteryManager.BATTERY_PROPERTY_CURRENT_AVERAGE, 100);
+
+        mManager.onReceive(intent);
+        verifyValues(true, 0.5 * 10 * 3600, Double.POSITIVE_INFINITY, 0.5);
+    }
+
+    @SmallTest
+    public void testLollipopDischargingTimeEstimate() {
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP)
+            return;
+
+        BatteryManagerForTesting testManager = new BatteryManagerForTesting();
+        initializeManager(testManager);
+
+        Intent intent = new Intent(Intent.ACTION_BATTERY_CHANGED);
+        intent.putExtra(BatteryManager.EXTRA_PRESENT, true);
+        intent.putExtra(BatteryManager.EXTRA_PLUGGED, 0);
+        intent.putExtra(BatteryManager.EXTRA_LEVEL, 60);
+        intent.putExtra(BatteryManager.EXTRA_SCALE, 100);
+        intent.putExtra(BatteryManager.EXTRA_STATUS, BatteryManager.BATTERY_STATUS_NOT_CHARGING);
+
+        testManager.setIntProperty(BatteryManager.BATTERY_PROPERTY_CHARGE_COUNTER, 1000);
+        testManager.setIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY, 60);
+        testManager.setIntProperty(BatteryManager.BATTERY_PROPERTY_CURRENT_AVERAGE, -100);
+
+        mManager.onReceive(intent);
+        verifyValues(false, Double.POSITIVE_INFINITY, 0.6 * 10 * 3600, 0.6);
+    }
+
+    @SmallTest
+    public void testLollipopDischargingTimeEstimateRounding() {
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP)
+            return;
+
+        BatteryManagerForTesting testManager = new BatteryManagerForTesting();
+        initializeManager(testManager);
+
+        Intent intent = new Intent(Intent.ACTION_BATTERY_CHANGED);
+        intent.putExtra(BatteryManager.EXTRA_PRESENT, true);
+        intent.putExtra(BatteryManager.EXTRA_PLUGGED, 0);
+        intent.putExtra(BatteryManager.EXTRA_LEVEL, 90);
+        intent.putExtra(BatteryManager.EXTRA_SCALE, 100);
+        intent.putExtra(BatteryManager.EXTRA_STATUS, BatteryManager.BATTERY_STATUS_NOT_CHARGING);
+
+        testManager.setIntProperty(BatteryManager.BATTERY_PROPERTY_CHARGE_COUNTER, 1999);
+        testManager.setIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY, 90);
+        testManager.setIntProperty(BatteryManager.BATTERY_PROPERTY_CURRENT_AVERAGE, -1000);
+
+        mManager.onReceive(intent);
+        verifyValues(false, Double.POSITIVE_INFINITY, Math.floor(0.9 * 1.999 * 3600), 0.9);
+    }
 }
diff --git a/device/battery/battery_status_manager_win.cc b/device/battery/battery_status_manager_win.cc
index adda9d0..6ad13da8 100644
--- a/device/battery/battery_status_manager_win.cc
+++ b/device/battery/battery_status_manager_win.cc
@@ -83,10 +83,14 @@
   }
 
   void Stop() {
-    if (power_handle_)
+    if (power_handle_) {
       UnregisterNotification(power_handle_);
-    if (battery_change_handle_)
+      power_handle_ = NULL;
+    }
+    if (battery_change_handle_) {
       UnregisterNotification(battery_change_handle_);
+      battery_change_handle_ = NULL;
+    }
     window_.reset();
   }
 
diff --git a/device/bluetooth/BUILD.gn b/device/bluetooth/BUILD.gn
index 0e703914..f66af9a 100644
--- a/device/bluetooth/BUILD.gn
+++ b/device/bluetooth/BUILD.gn
@@ -214,6 +214,8 @@
 
   deps = [
     ":bluetooth",
+    "//base",
+    "//net",
     "//testing/gmock",
   ]
 }
diff --git a/device/bluetooth/bluetooth_adapter_chromeos.cc b/device/bluetooth/bluetooth_adapter_chromeos.cc
index e6c6283..77030026 100644
--- a/device/bluetooth/bluetooth_adapter_chromeos.cc
+++ b/device/bluetooth/bluetooth_adapter_chromeos.cc
@@ -316,15 +316,14 @@
   scoped_refptr<BluetoothAudioSinkChromeOS> audio_sink(
       new BluetoothAudioSinkChromeOS(this));
   audio_sink->Register(
-      options,
-      base::Bind(&BluetoothAdapterChromeOS::OnRegisterAudioSink,
-                 weak_ptr_factory_.GetWeakPtr(), callback, audio_sink),
+      options, base::Bind(&BluetoothAdapterChromeOS::OnRegisterAudioSink,
+                          weak_ptr_factory_.GetWeakPtr(), callback,
+                          error_callback, audio_sink),
       error_callback);
 }
 
 void BluetoothAdapterChromeOS::RemovePairingDelegateInternal(
     BluetoothDevice::PairingDelegate* pairing_delegate) {
-  DCHECK(IsPresent());
   // Before removing a pairing delegate make sure that there aren't any devices
   // currently using it; if there are, clear the pairing context which will
   // make any responses no-ops.
@@ -481,9 +480,10 @@
 }
 
 void BluetoothAdapterChromeOS::Released() {
-  DCHECK(IsPresent());
-  DCHECK(agent_.get());
   VLOG(1) << "Release";
+  if (!IsPresent())
+    return;
+  DCHECK(agent_.get());
 
   // Called after we unregister the pairing agent, e.g. when changing I/O
   // capabilities. Nothing much to be done right now.
@@ -624,7 +624,6 @@
 }
 
 void BluetoothAdapterChromeOS::OnRegisterAgent() {
-  DCHECK(IsPresent());
   VLOG(1) << "Pairing agent registered, requesting to be made default";
 
   DBusThreadManager::Get()->GetBluetoothAgentManagerClient()->
@@ -639,7 +638,6 @@
 void BluetoothAdapterChromeOS::OnRegisterAgentError(
     const std::string& error_name,
     const std::string& error_message) {
-  DCHECK(IsPresent());
   // Our agent being already registered isn't an error.
   if (error_name == bluetooth_agent_manager::kErrorAlreadyExists)
     return;
@@ -649,21 +647,25 @@
 }
 
 void BluetoothAdapterChromeOS::OnRequestDefaultAgent() {
-  DCHECK(IsPresent());
   VLOG(1) << "Pairing agent now default";
 }
 
 void BluetoothAdapterChromeOS::OnRequestDefaultAgentError(
     const std::string& error_name,
     const std::string& error_message) {
-  DCHECK(IsPresent());
   LOG(WARNING) << ": Failed to make pairing agent default: "
                << error_name << ": " << error_message;
 }
 
 void BluetoothAdapterChromeOS::OnRegisterAudioSink(
     const device::BluetoothAdapter::AcquiredCallback& callback,
+    const device::BluetoothAudioSink::ErrorCallback& error_callback,
     scoped_refptr<BluetoothAudioSink> audio_sink) {
+  if (!IsPresent()) {
+    VLOG(1) << "Failed to register audio sink, adapter not present";
+    error_callback.Run(BluetoothAudioSink::ERROR_INVALID_ADAPTER);
+    return;
+  }
   DCHECK(audio_sink.get());
   callback.Run(audio_sink);
 }
@@ -1019,7 +1021,11 @@
     const base::Closure& callback,
     const ErrorCallback& error_callback,
     bool success) {
-  DCHECK(IsPresent());
+  if (!IsPresent()) {
+    error_callback.Run();
+    return;
+  }
+
   // Set the discoverable_timeout property to zero so the adapter remains
   // discoverable forever.
   DBusThreadManager::Get()->GetBluetoothAdapterClient()->
@@ -1035,8 +1041,7 @@
     const base::Closure& callback,
     const ErrorCallback& error_callback,
     bool success) {
-  DCHECK(IsPresent());
-  if (success)
+  if (IsPresent() && success)
     callback.Run();
   else
     error_callback.Run();
@@ -1074,22 +1079,22 @@
 
   // This is the first request to start device discovery.
   discovery_request_pending_ = true;
-  DBusThreadManager::Get()->GetBluetoothAdapterClient()->
-      StartDiscovery(
-          object_path_,
-          base::Bind(&BluetoothAdapterChromeOS::OnStartDiscovery,
-                     weak_ptr_factory_.GetWeakPtr(),
-                     callback),
-          base::Bind(&BluetoothAdapterChromeOS::OnStartDiscoveryError,
-                     weak_ptr_factory_.GetWeakPtr(),
-                     callback,
-                     error_callback));
+  DBusThreadManager::Get()->GetBluetoothAdapterClient()->StartDiscovery(
+      object_path_,
+      base::Bind(&BluetoothAdapterChromeOS::OnStartDiscovery,
+                 weak_ptr_factory_.GetWeakPtr(), callback, error_callback),
+      base::Bind(&BluetoothAdapterChromeOS::OnStartDiscoveryError,
+                 weak_ptr_factory_.GetWeakPtr(), callback, error_callback));
 }
 
 void BluetoothAdapterChromeOS::RemoveDiscoverySession(
     const base::Closure& callback,
     const ErrorCallback& error_callback) {
-  DCHECK(IsPresent());
+  if (!IsPresent()) {
+    error_callback.Run();
+    return;
+  }
+
   VLOG(1) << __func__;
   // There are active sessions other than the one currently being removed.
   if (num_discovery_sessions_ > 1) {
@@ -1133,15 +1138,19 @@
                      error_callback));
 }
 
-void BluetoothAdapterChromeOS::OnStartDiscovery(const base::Closure& callback) {
-  DCHECK(IsPresent());
+void BluetoothAdapterChromeOS::OnStartDiscovery(
+    const base::Closure& callback,
+    const ErrorCallback& error_callback) {
   // Report success on the original request and increment the count.
   VLOG(1) << __func__;
   DCHECK(discovery_request_pending_);
   DCHECK_EQ(num_discovery_sessions_, 0);
   discovery_request_pending_ = false;
   num_discovery_sessions_++;
-  callback.Run();
+  if (IsPresent())
+    callback.Run();
+  else
+    error_callback.Run();
 
   // Try to add a new discovery session for each queued request.
   ProcessQueuedDiscoveryRequests();
@@ -1152,7 +1161,6 @@
     const ErrorCallback& error_callback,
     const std::string& error_name,
     const std::string& error_message) {
-  DCHECK(IsPresent());
   LOG(WARNING) << object_path_.value() << ": Failed to start discovery: "
                << error_name << ": " << error_message;
 
@@ -1164,7 +1172,8 @@
   // Discovery request may fail if discovery was previously initiated by Chrome,
   // but the session were invalidated due to the discovery state unexpectedly
   // changing to false and then back to true. In this case, report success.
-  if (error_name == bluetooth_device::kErrorInProgress && IsDiscovering()) {
+  if (IsPresent() && error_name == bluetooth_device::kErrorInProgress &&
+      IsDiscovering()) {
     VLOG(1) << "Discovery previously initiated. Reporting success.";
     num_discovery_sessions_++;
     callback.Run();
@@ -1177,7 +1186,6 @@
 }
 
 void BluetoothAdapterChromeOS::OnStopDiscovery(const base::Closure& callback) {
-  DCHECK(IsPresent());
   // Report success on the original request and decrement the count.
   VLOG(1) << __func__;
   DCHECK(discovery_request_pending_);
@@ -1194,7 +1202,6 @@
     const ErrorCallback& error_callback,
     const std::string& error_name,
     const std::string& error_message) {
-  DCHECK(IsPresent());
   LOG(WARNING) << object_path_.value() << ": Failed to stop discovery: "
                << error_name << ": " << error_message;
 
diff --git a/device/bluetooth/bluetooth_adapter_chromeos.h b/device/bluetooth/bluetooth_adapter_chromeos.h
index 12a88f9b..61e4a3a 100644
--- a/device/bluetooth/bluetooth_adapter_chromeos.h
+++ b/device/bluetooth/bluetooth_adapter_chromeos.h
@@ -41,6 +41,13 @@
 
 // The BluetoothAdapterChromeOS class implements BluetoothAdapter for the
 // Chrome OS platform.
+//
+// Methods tolerate a shutdown scenario where BluetoothAdapterChromeOS::Shutdown
+// causes IsPresent to return false just before the dbus system is shutdown but
+// while references to the BluetoothAdapterChromeOS object still exists.
+//
+// When adding methods to this class verify shutdown behavior in
+// BluetoothChromeOSTest, Shutdown.
 class DEVICE_BLUETOOTH_EXPORT BluetoothAdapterChromeOS
     : public device::BluetoothAdapter,
       public chromeos::BluetoothAdapterClient::Observer,
@@ -149,6 +156,11 @@
  private:
   friend class base::DeleteHelper<BluetoothAdapterChromeOS>;
   friend class BluetoothChromeOSTest;
+  friend class BluetoothChromeOSTest_Shutdown_Test;
+  friend class BluetoothChromeOSTest_Shutdown_OnStartDiscovery_Test;
+  friend class BluetoothChromeOSTest_Shutdown_OnStartDiscoveryError_Test;
+  friend class BluetoothChromeOSTest_Shutdown_OnStopDiscovery_Test;
+  friend class BluetoothChromeOSTest_Shutdown_OnStopDiscoveryError_Test;
 
   // typedef for callback parameters that are passed to AddDiscoverySession
   // and RemoveDiscoverySession. This is used to queue incoming requests while
@@ -212,6 +224,7 @@
   // sink.
   void OnRegisterAudioSink(
       const device::BluetoothAdapter::AcquiredCallback& callback,
+      const device::BluetoothAudioSink::ErrorCallback& error_callback,
       scoped_refptr<device::BluetoothAudioSink> audio_sink);
 
   // Internal method to obtain a BluetoothPairingChromeOS object for the device
@@ -255,7 +268,8 @@
                               const ErrorCallback& error_callback) override;
 
   // Called by dbus:: on completion of the D-Bus method call to start discovery.
-  void OnStartDiscovery(const base::Closure& callback);
+  void OnStartDiscovery(const base::Closure& callback,
+                        const ErrorCallback& error_callback);
   void OnStartDiscoveryError(const base::Closure& callback,
                              const ErrorCallback& error_callback,
                              const std::string& error_name,
diff --git a/device/bluetooth/bluetooth_chromeos_unittest.cc b/device/bluetooth/bluetooth_chromeos_unittest.cc
index 6ce931b..7da63b7 100644
--- a/device/bluetooth/bluetooth_chromeos_unittest.cc
+++ b/device/bluetooth/bluetooth_chromeos_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "base/memory/scoped_vector.h"
 #include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/fake_bluetooth_adapter_client.h"
@@ -24,6 +25,7 @@
 
 using device::BluetoothAdapter;
 using device::BluetoothAdapterFactory;
+using device::BluetoothAudioSink;
 using device::BluetoothDevice;
 using device::BluetoothDiscoverySession;
 using device::BluetoothUUID;
@@ -146,6 +148,25 @@
   *out = conn_info;
 };
 
+class FakeBluetoothProfileServiceProviderDelegate
+    : public chromeos::BluetoothProfileServiceProvider::Delegate {
+ public:
+  FakeBluetoothProfileServiceProviderDelegate() {}
+
+  // BluetoothProfileServiceProvider::Delegate:
+  void Released() override {}
+
+  void NewConnection(const dbus::ObjectPath&,
+                     scoped_ptr<dbus::FileDescriptor>,
+                     const BluetoothProfileServiceProvider::Delegate::Options&,
+                     const ConfirmationCallback&) override {}
+
+  void RequestDisconnection(const dbus::ObjectPath&,
+                            const ConfirmationCallback&) override {}
+
+  void Cancel() override {}
+};
+
 }  // namespace
 
 class TestPairingDelegate : public BluetoothDevice::PairingDelegate {
@@ -272,11 +293,7 @@
       if (!session->IsActive())
         continue;
       callback_count_ = 0;
-      session->Stop(
-          base::Bind(&BluetoothChromeOSTest::Callback,
-                     base::Unretained(this)),
-          base::Bind(&BluetoothChromeOSTest::ErrorCallback,
-                     base::Unretained(this)));
+      session->Stop(GetCallback(), GetErrorCallback());
       message_loop_.Run();
       ASSERT_EQ(1, callback_count_);
     }
@@ -291,6 +308,10 @@
     QuitMessageLoop();
   }
 
+  base::Closure GetCallback() {
+    return base::Bind(&BluetoothChromeOSTest::Callback, base::Unretained(this));
+  }
+
   void DiscoverySessionCallback(
       scoped_ptr<BluetoothDiscoverySession> discovery_session) {
     ++callback_count_;
@@ -298,11 +319,26 @@
     QuitMessageLoop();
   }
 
+  void AudioSinkAcquiredCallback(scoped_refptr<BluetoothAudioSink>) {
+    ++callback_count_;
+    QuitMessageLoop();
+  }
+
+  void ProfileRegisteredCallback(BluetoothAdapterProfileChromeOS*) {
+    ++callback_count_;
+    QuitMessageLoop();
+  }
+
   void ErrorCallback() {
     ++error_callback_count_;
     QuitMessageLoop();
   }
 
+  base::Closure GetErrorCallback() {
+    return base::Bind(&BluetoothChromeOSTest::ErrorCallback,
+                      base::Unretained(this));
+  }
+
   void DBusErrorCallback(const std::string& error_name,
                          const std::string& error_message) {
     ++error_callback_count_;
@@ -315,6 +351,16 @@
     last_connect_error_ = error;
   }
 
+  void AudioSinkErrorCallback(BluetoothAudioSink::ErrorCode) {
+    ++error_callback_count_;
+    QuitMessageLoop();
+  }
+
+  void ErrorCompletionCallback(const std::string& error_message) {
+    ++error_callback_count_;
+    QuitMessageLoop();
+  }
+
   // Call to fill the adapter_ member with a BluetoothAdapter instance.
   void GetAdapter() {
     adapter_ = new BluetoothAdapterChromeOS();
@@ -334,17 +380,11 @@
 
     TestObserver observer(adapter_);
 
-    adapter_->SetPowered(
-        true,
-        base::Bind(&BluetoothChromeOSTest::Callback,
-                   base::Unretained(this)),
-        base::Bind(&BluetoothChromeOSTest::ErrorCallback,
-                   base::Unretained(this)));
+    adapter_->SetPowered(true, GetCallback(), GetErrorCallback());
     adapter_->StartDiscoverySession(
         base::Bind(&BluetoothChromeOSTest::DiscoverySessionCallback,
                    base::Unretained(this)),
-        base::Bind(&BluetoothChromeOSTest::ErrorCallback,
-                   base::Unretained(this)));
+        GetErrorCallback());
     base::MessageLoop::current()->Run();
     ASSERT_EQ(2, callback_count_);
     ASSERT_EQ(0, error_callback_count_);
@@ -359,11 +399,7 @@
            observer.last_device_address_ != address)
       base::MessageLoop::current()->Run();
 
-    discovery_sessions_[0]->Stop(
-        base::Bind(&BluetoothChromeOSTest::Callback,
-                   base::Unretained(this)),
-        base::Bind(&BluetoothChromeOSTest::ErrorCallback,
-                   base::Unretained(this)));
+    discovery_sessions_[0]->Stop(GetCallback(), GetErrorCallback());
     base::MessageLoop::current()->Run();
     ASSERT_EQ(1, callback_count_);
     ASSERT_EQ(0, error_callback_count_);
@@ -530,12 +566,7 @@
   // with true, and IsPowered() to return true.
   TestObserver observer(adapter_);
 
-  adapter_->SetPowered(
-      true,
-      base::Bind(&BluetoothChromeOSTest::Callback,
-                 base::Unretained(this)),
-      base::Bind(&BluetoothChromeOSTest::ErrorCallback,
-                 base::Unretained(this)));
+  adapter_->SetPowered(true, GetCallback(), GetErrorCallback());
   EXPECT_EQ(1, callback_count_);
   EXPECT_EQ(0, error_callback_count_);
 
@@ -547,12 +578,7 @@
 
 TEST_F(BluetoothChromeOSTest, BecomeNotPowered) {
   GetAdapter();
-  adapter_->SetPowered(
-      true,
-      base::Bind(&BluetoothChromeOSTest::Callback,
-                 base::Unretained(this)),
-      base::Bind(&BluetoothChromeOSTest::ErrorCallback,
-                 base::Unretained(this)));
+  adapter_->SetPowered(true, GetCallback(), GetErrorCallback());
   EXPECT_EQ(1, callback_count_);
   EXPECT_EQ(0, error_callback_count_);
   callback_count_ = 0;
@@ -563,12 +589,7 @@
   // with false, and IsPowered() to return false.
   TestObserver observer(adapter_);
 
-  adapter_->SetPowered(
-      false,
-      base::Bind(&BluetoothChromeOSTest::Callback,
-                 base::Unretained(this)),
-      base::Bind(&BluetoothChromeOSTest::ErrorCallback,
-                 base::Unretained(this)));
+  adapter_->SetPowered(false, GetCallback(), GetErrorCallback());
   EXPECT_EQ(1, callback_count_);
   EXPECT_EQ(0, error_callback_count_);
 
@@ -594,12 +615,7 @@
   EXPECT_FALSE(adapter_->IsPresent());
   EXPECT_FALSE(adapter_->IsPowered());
 
-  adapter_->SetPowered(
-      true,
-      base::Bind(&BluetoothChromeOSTest::Callback,
-                 base::Unretained(this)),
-      base::Bind(&BluetoothChromeOSTest::ErrorCallback,
-                 base::Unretained(this)));
+  adapter_->SetPowered(true, GetCallback(), GetErrorCallback());
   EXPECT_EQ(0, callback_count_);
   EXPECT_EQ(1, error_callback_count_);
 
@@ -614,12 +630,7 @@
 
   static const std::string new_name(".__.");
 
-  adapter_->SetName(
-      new_name,
-      base::Bind(&BluetoothChromeOSTest::Callback,
-                 base::Unretained(this)),
-      base::Bind(&BluetoothChromeOSTest::ErrorCallback,
-                 base::Unretained(this)));
+  adapter_->SetName(new_name, GetCallback(), GetErrorCallback());
   EXPECT_EQ(1, callback_count_);
   EXPECT_EQ(0, error_callback_count_);
 
@@ -642,12 +653,7 @@
   EXPECT_FALSE(adapter_->IsPresent());
   EXPECT_FALSE(adapter_->IsPowered());
 
-  adapter_->SetName(
-      "^o^",
-      base::Bind(&BluetoothChromeOSTest::Callback,
-                 base::Unretained(this)),
-      base::Bind(&BluetoothChromeOSTest::ErrorCallback,
-                 base::Unretained(this)));
+  adapter_->SetName("^o^", GetCallback(), GetErrorCallback());
   EXPECT_EQ(0, callback_count_);
   EXPECT_EQ(1, error_callback_count_);
 
@@ -662,12 +668,7 @@
   // with true, and IsDiscoverable() to return true.
   TestObserver observer(adapter_);
 
-  adapter_->SetDiscoverable(
-      true,
-      base::Bind(&BluetoothChromeOSTest::Callback,
-                 base::Unretained(this)),
-      base::Bind(&BluetoothChromeOSTest::ErrorCallback,
-                 base::Unretained(this)));
+  adapter_->SetDiscoverable(true, GetCallback(), GetErrorCallback());
   EXPECT_EQ(1, callback_count_);
   EXPECT_EQ(0, error_callback_count_);
 
@@ -678,12 +679,7 @@
 
 TEST_F(BluetoothChromeOSTest, BecomeNotDiscoverable) {
   GetAdapter();
-  adapter_->SetDiscoverable(
-      true,
-      base::Bind(&BluetoothChromeOSTest::Callback,
-                 base::Unretained(this)),
-      base::Bind(&BluetoothChromeOSTest::ErrorCallback,
-                 base::Unretained(this)));
+  adapter_->SetDiscoverable(true, GetCallback(), GetErrorCallback());
   EXPECT_EQ(1, callback_count_);
   EXPECT_EQ(0, error_callback_count_);
   callback_count_ = 0;
@@ -694,12 +690,7 @@
   // with false, and IsDiscoverable() to return false.
   TestObserver observer(adapter_);
 
-  adapter_->SetDiscoverable(
-      false,
-      base::Bind(&BluetoothChromeOSTest::Callback,
-                 base::Unretained(this)),
-      base::Bind(&BluetoothChromeOSTest::ErrorCallback,
-                 base::Unretained(this)));
+  adapter_->SetDiscoverable(false, GetCallback(), GetErrorCallback());
   EXPECT_EQ(1, callback_count_);
   EXPECT_EQ(0, error_callback_count_);
 
@@ -725,12 +716,7 @@
   EXPECT_FALSE(adapter_->IsPresent());
   EXPECT_FALSE(adapter_->IsDiscoverable());
 
-  adapter_->SetDiscoverable(
-      true,
-      base::Bind(&BluetoothChromeOSTest::Callback,
-                 base::Unretained(this)),
-      base::Bind(&BluetoothChromeOSTest::ErrorCallback,
-                 base::Unretained(this)));
+  adapter_->SetDiscoverable(true, GetCallback(), GetErrorCallback());
   EXPECT_EQ(0, callback_count_);
   EXPECT_EQ(1, error_callback_count_);
 
@@ -742,17 +728,11 @@
 TEST_F(BluetoothChromeOSTest, StopDiscovery) {
   GetAdapter();
 
-  adapter_->SetPowered(
-      true,
-      base::Bind(&BluetoothChromeOSTest::Callback,
-                 base::Unretained(this)),
-      base::Bind(&BluetoothChromeOSTest::ErrorCallback,
-                 base::Unretained(this)));
+  adapter_->SetPowered(true, GetCallback(), GetErrorCallback());
   adapter_->StartDiscoverySession(
       base::Bind(&BluetoothChromeOSTest::DiscoverySessionCallback,
                  base::Unretained(this)),
-      base::Bind(&BluetoothChromeOSTest::ErrorCallback,
-                 base::Unretained(this)));
+      GetErrorCallback());
   message_loop_.Run();
   EXPECT_EQ(2, callback_count_);
   EXPECT_EQ(0, error_callback_count_);
@@ -768,11 +748,7 @@
   // discovering,
   TestObserver observer(adapter_);
 
-  discovery_sessions_[0]->Stop(
-      base::Bind(&BluetoothChromeOSTest::Callback,
-                 base::Unretained(this)),
-      base::Bind(&BluetoothChromeOSTest::ErrorCallback,
-                 base::Unretained(this)));
+  discovery_sessions_[0]->Stop(GetCallback(), GetErrorCallback());
   message_loop_.Run();
   EXPECT_EQ(1, callback_count_);
   EXPECT_EQ(0, error_callback_count_);
@@ -790,17 +766,11 @@
 
   TestObserver observer(adapter_);
 
-  adapter_->SetPowered(
-      true,
-      base::Bind(&BluetoothChromeOSTest::Callback,
-                 base::Unretained(this)),
-      base::Bind(&BluetoothChromeOSTest::ErrorCallback,
-                 base::Unretained(this)));
+  adapter_->SetPowered(true, GetCallback(), GetErrorCallback());
   adapter_->StartDiscoverySession(
       base::Bind(&BluetoothChromeOSTest::DiscoverySessionCallback,
                  base::Unretained(this)),
-      base::Bind(&BluetoothChromeOSTest::ErrorCallback,
-                 base::Unretained(this)));
+      GetErrorCallback());
   message_loop_.Run();
   EXPECT_EQ(2, callback_count_);
   EXPECT_EQ(0, error_callback_count_);
@@ -833,17 +803,11 @@
 
 TEST_F(BluetoothChromeOSTest, PoweredAndDiscovering) {
   GetAdapter();
-  adapter_->SetPowered(
-      true,
-      base::Bind(&BluetoothChromeOSTest::Callback,
-                 base::Unretained(this)),
-      base::Bind(&BluetoothChromeOSTest::ErrorCallback,
-                 base::Unretained(this)));
+  adapter_->SetPowered(true, GetCallback(), GetErrorCallback());
   adapter_->StartDiscoverySession(
       base::Bind(&BluetoothChromeOSTest::DiscoverySessionCallback,
                  base::Unretained(this)),
-      base::Bind(&BluetoothChromeOSTest::ErrorCallback,
-                 base::Unretained(this)));
+      GetErrorCallback());
   message_loop_.Run();
   EXPECT_EQ(2, callback_count_);
   EXPECT_EQ(0, error_callback_count_);
@@ -907,12 +871,7 @@
 // correctly for discovery requests done via the BluetoothAdapter.
 TEST_F(BluetoothChromeOSTest, MultipleDiscoverySessions) {
   GetAdapter();
-  adapter_->SetPowered(
-      true,
-      base::Bind(&BluetoothChromeOSTest::Callback,
-                 base::Unretained(this)),
-      base::Bind(&BluetoothChromeOSTest::ErrorCallback,
-                 base::Unretained(this)));
+  adapter_->SetPowered(true, GetCallback(), GetErrorCallback());
   EXPECT_EQ(1, callback_count_);
   EXPECT_EQ(0, error_callback_count_);
   EXPECT_TRUE(adapter_->IsPowered());
@@ -927,10 +886,9 @@
   // Request device discovery 3 times.
   for (int i = 0; i < 3; i++) {
     adapter_->StartDiscoverySession(
-      base::Bind(&BluetoothChromeOSTest::DiscoverySessionCallback,
-                 base::Unretained(this)),
-      base::Bind(&BluetoothChromeOSTest::ErrorCallback,
-                 base::Unretained(this)));
+        base::Bind(&BluetoothChromeOSTest::DiscoverySessionCallback,
+                   base::Unretained(this)),
+        GetErrorCallback());
   }
   // Run only once, as there should have been one D-Bus call.
   message_loop_.Run();
@@ -947,11 +905,7 @@
 
   // Request to stop discovery twice.
   for (int i = 0; i < 2; i++) {
-    discovery_sessions_[i]->Stop(
-      base::Bind(&BluetoothChromeOSTest::Callback,
-                 base::Unretained(this)),
-      base::Bind(&BluetoothChromeOSTest::ErrorCallback,
-                 base::Unretained(this)));
+    discovery_sessions_[i]->Stop(GetCallback(), GetErrorCallback());
   }
 
   // The observer should have received no additional discovering changed events,
@@ -970,10 +924,9 @@
   // Request device discovery 3 times.
   for (int i = 0; i < 3; i++) {
     adapter_->StartDiscoverySession(
-      base::Bind(&BluetoothChromeOSTest::DiscoverySessionCallback,
-                 base::Unretained(this)),
-      base::Bind(&BluetoothChromeOSTest::ErrorCallback,
-                 base::Unretained(this)));
+        base::Bind(&BluetoothChromeOSTest::DiscoverySessionCallback,
+                   base::Unretained(this)),
+        GetErrorCallback());
   }
 
   // The observer should have received no additional discovering changed events,
@@ -988,11 +941,7 @@
 
   // Request to stop discovery 4 times.
   for (int i = 2; i < 6; i++) {
-    discovery_sessions_[i]->Stop(
-      base::Bind(&BluetoothChromeOSTest::Callback,
-                 base::Unretained(this)),
-      base::Bind(&BluetoothChromeOSTest::ErrorCallback,
-                 base::Unretained(this)));
+    discovery_sessions_[i]->Stop(GetCallback(), GetErrorCallback());
   }
   // Run only once, as there should have been one D-Bus call.
   message_loop_.Run();
@@ -1011,11 +960,7 @@
     EXPECT_FALSE(discovery_sessions_[i]->IsActive());
 
   // Request to stop discovery on of the inactive sessions.
-  discovery_sessions_[0]->Stop(
-    base::Bind(&BluetoothChromeOSTest::Callback,
-               base::Unretained(this)),
-    base::Bind(&BluetoothChromeOSTest::ErrorCallback,
-               base::Unretained(this)));
+  discovery_sessions_[0]->Stop(GetCallback(), GetErrorCallback());
 
   // The call should have failed.
   EXPECT_EQ(2, observer.discovering_changed_count_);
@@ -1031,12 +976,7 @@
 TEST_F(BluetoothChromeOSTest,
        UnexpectedChangesDuringMultipleDiscoverySessions) {
   GetAdapter();
-  adapter_->SetPowered(
-      true,
-      base::Bind(&BluetoothChromeOSTest::Callback,
-                 base::Unretained(this)),
-      base::Bind(&BluetoothChromeOSTest::ErrorCallback,
-                 base::Unretained(this)));
+  adapter_->SetPowered(true, GetCallback(), GetErrorCallback());
   EXPECT_EQ(1, callback_count_);
   EXPECT_EQ(0, error_callback_count_);
   EXPECT_TRUE(adapter_->IsPowered());
@@ -1051,10 +991,9 @@
   // Request device discovery 3 times.
   for (int i = 0; i < 3; i++) {
     adapter_->StartDiscoverySession(
-      base::Bind(&BluetoothChromeOSTest::DiscoverySessionCallback,
-                 base::Unretained(this)),
-      base::Bind(&BluetoothChromeOSTest::ErrorCallback,
-                 base::Unretained(this)));
+        base::Bind(&BluetoothChromeOSTest::DiscoverySessionCallback,
+                   base::Unretained(this)),
+        GetErrorCallback());
   }
   // Run only once, as there should have been one D-Bus call.
   message_loop_.Run();
@@ -1086,9 +1025,7 @@
   // FakeBluetoothAdapterClient's count should be only 1 and a single call to
   // FakeBluetoothAdapterClient::StopDiscovery should work.
   fake_bluetooth_adapter_client_->StopDiscovery(
-      dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath),
-      base::Bind(&BluetoothChromeOSTest::Callback,
-                 base::Unretained(this)),
+      dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath), GetCallback(),
       base::Bind(&BluetoothChromeOSTest::DBusErrorCallback,
                  base::Unretained(this)));
   message_loop_.Run();
@@ -1106,10 +1043,9 @@
   // It should be possible to successfully start discovery.
   for (int i = 0; i < 2; i++) {
     adapter_->StartDiscoverySession(
-      base::Bind(&BluetoothChromeOSTest::DiscoverySessionCallback,
-                 base::Unretained(this)),
-      base::Bind(&BluetoothChromeOSTest::ErrorCallback,
-                 base::Unretained(this)));
+        base::Bind(&BluetoothChromeOSTest::DiscoverySessionCallback,
+                   base::Unretained(this)),
+        GetErrorCallback());
   }
   // Run only once, as there should have been one D-Bus call.
   message_loop_.Run();
@@ -1154,10 +1090,9 @@
   // application other than us. Starting and stopping discovery will succeed
   // but it won't cause the discovery state to change.
   adapter_->StartDiscoverySession(
-    base::Bind(&BluetoothChromeOSTest::DiscoverySessionCallback,
-               base::Unretained(this)),
-    base::Bind(&BluetoothChromeOSTest::ErrorCallback,
-               base::Unretained(this)));
+      base::Bind(&BluetoothChromeOSTest::DiscoverySessionCallback,
+                 base::Unretained(this)),
+      GetErrorCallback());
   message_loop_.Run();  // Run the loop, as there should have been a D-Bus call.
   EXPECT_EQ(5, observer.discovering_changed_count_);
   EXPECT_EQ(7, callback_count_);
@@ -1167,11 +1102,7 @@
   ASSERT_EQ((size_t)1, discovery_sessions_.size());
   EXPECT_TRUE(discovery_sessions_[0]->IsActive());
 
-  discovery_sessions_[0]->Stop(
-    base::Bind(&BluetoothChromeOSTest::Callback,
-               base::Unretained(this)),
-    base::Bind(&BluetoothChromeOSTest::ErrorCallback,
-               base::Unretained(this)));
+  discovery_sessions_[0]->Stop(GetCallback(), GetErrorCallback());
   message_loop_.Run();  // Run the loop, as there should have been a D-Bus call.
   EXPECT_EQ(5, observer.discovering_changed_count_);
   EXPECT_EQ(8, callback_count_);
@@ -1183,10 +1114,9 @@
 
   // Start discovery again.
   adapter_->StartDiscoverySession(
-    base::Bind(&BluetoothChromeOSTest::DiscoverySessionCallback,
-               base::Unretained(this)),
-    base::Bind(&BluetoothChromeOSTest::ErrorCallback,
-               base::Unretained(this)));
+      base::Bind(&BluetoothChromeOSTest::DiscoverySessionCallback,
+                 base::Unretained(this)),
+      GetErrorCallback());
   message_loop_.Run();  // Run the loop, as there should have been a D-Bus call.
   EXPECT_EQ(5, observer.discovering_changed_count_);
   EXPECT_EQ(9, callback_count_);
@@ -1200,9 +1130,7 @@
   // the discovery state won't change since our BluetoothAdapter also just
   // requested it via D-Bus.
   fake_bluetooth_adapter_client_->StopDiscovery(
-      dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath),
-      base::Bind(&BluetoothChromeOSTest::Callback,
-                 base::Unretained(this)),
+      dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath), GetCallback(),
       base::Bind(&BluetoothChromeOSTest::DBusErrorCallback,
                  base::Unretained(this)));
   message_loop_.Run();
@@ -1214,11 +1142,7 @@
 
   // Now end the discovery session. This should change the adapter's discovery
   // state.
-  discovery_sessions_[0]->Stop(
-    base::Bind(&BluetoothChromeOSTest::Callback,
-               base::Unretained(this)),
-    base::Bind(&BluetoothChromeOSTest::ErrorCallback,
-               base::Unretained(this)));
+  discovery_sessions_[0]->Stop(GetCallback(), GetErrorCallback());
   message_loop_.Run();
   EXPECT_EQ(6, observer.discovering_changed_count_);
   EXPECT_EQ(11, callback_count_);
@@ -1230,12 +1154,7 @@
 
 TEST_F(BluetoothChromeOSTest, InvalidatedDiscoverySessions) {
   GetAdapter();
-  adapter_->SetPowered(
-      true,
-      base::Bind(&BluetoothChromeOSTest::Callback,
-                 base::Unretained(this)),
-      base::Bind(&BluetoothChromeOSTest::ErrorCallback,
-                 base::Unretained(this)));
+  adapter_->SetPowered(true, GetCallback(), GetErrorCallback());
   EXPECT_EQ(1, callback_count_);
   EXPECT_EQ(0, error_callback_count_);
   EXPECT_TRUE(adapter_->IsPowered());
@@ -1250,10 +1169,9 @@
   // Request device discovery 3 times.
   for (int i = 0; i < 3; i++) {
     adapter_->StartDiscoverySession(
-      base::Bind(&BluetoothChromeOSTest::DiscoverySessionCallback,
-                 base::Unretained(this)),
-      base::Bind(&BluetoothChromeOSTest::ErrorCallback,
-                 base::Unretained(this)));
+        base::Bind(&BluetoothChromeOSTest::DiscoverySessionCallback,
+                   base::Unretained(this)),
+        GetErrorCallback());
   }
   // Run only once, as there should have been one D-Bus call.
   message_loop_.Run();
@@ -1290,9 +1208,7 @@
   // memory errors as the sessions that we explicitly deleted should get
   // cleaned up.
   fake_bluetooth_adapter_client_->StopDiscovery(
-      dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath),
-      base::Bind(&BluetoothChromeOSTest::Callback,
-                 base::Unretained(this)),
+      dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath), GetCallback(),
       base::Bind(&BluetoothChromeOSTest::DBusErrorCallback,
                  base::Unretained(this)));
   message_loop_.Run();
@@ -1307,12 +1223,7 @@
 TEST_F(BluetoothChromeOSTest, QueuedDiscoveryRequests) {
   GetAdapter();
 
-  adapter_->SetPowered(
-      true,
-      base::Bind(&BluetoothChromeOSTest::Callback,
-                 base::Unretained(this)),
-      base::Bind(&BluetoothChromeOSTest::ErrorCallback,
-                 base::Unretained(this)));
+  adapter_->SetPowered(true, GetCallback(), GetErrorCallback());
   EXPECT_EQ(1, callback_count_);
   EXPECT_EQ(0, error_callback_count_);
   EXPECT_TRUE(adapter_->IsPowered());
@@ -1326,10 +1237,9 @@
 
   // Request to start discovery. The call should be pending.
   adapter_->StartDiscoverySession(
-    base::Bind(&BluetoothChromeOSTest::DiscoverySessionCallback,
-               base::Unretained(this)),
-    base::Bind(&BluetoothChromeOSTest::ErrorCallback,
-               base::Unretained(this)));
+      base::Bind(&BluetoothChromeOSTest::DiscoverySessionCallback,
+                 base::Unretained(this)),
+      GetErrorCallback());
   EXPECT_EQ(0, callback_count_);
 
   fake_bluetooth_device_client_->EndDiscoverySimulation(
@@ -1346,10 +1256,9 @@
   // be no change in state.
   for (int i = 0; i < 2; i++) {
     adapter_->StartDiscoverySession(
-      base::Bind(&BluetoothChromeOSTest::DiscoverySessionCallback,
-                 base::Unretained(this)),
-      base::Bind(&BluetoothChromeOSTest::ErrorCallback,
-                 base::Unretained(this)));
+        base::Bind(&BluetoothChromeOSTest::DiscoverySessionCallback,
+                   base::Unretained(this)),
+        GetErrorCallback());
   }
   EXPECT_EQ(0, callback_count_);
   EXPECT_EQ(0, error_callback_count_);
@@ -1371,11 +1280,7 @@
   // Verify the reference count by removing sessions 3 times. The last request
   // should remain pending.
   for (int i = 0; i < 3; i++) {
-    discovery_sessions_[i]->Stop(
-      base::Bind(&BluetoothChromeOSTest::Callback,
-                 base::Unretained(this)),
-      base::Bind(&BluetoothChromeOSTest::ErrorCallback,
-                 base::Unretained(this)));
+    discovery_sessions_[i]->Stop(GetCallback(), GetErrorCallback());
   }
   EXPECT_EQ(5, callback_count_);
   EXPECT_EQ(0, error_callback_count_);
@@ -1387,11 +1292,7 @@
   EXPECT_TRUE(discovery_sessions_[2]->IsActive());
 
   // Request to stop the session whose call is pending should fail.
-  discovery_sessions_[2]->Stop(
-    base::Bind(&BluetoothChromeOSTest::Callback,
-               base::Unretained(this)),
-    base::Bind(&BluetoothChromeOSTest::ErrorCallback,
-               base::Unretained(this)));
+  discovery_sessions_[2]->Stop(GetCallback(), GetErrorCallback());
   EXPECT_EQ(5, callback_count_);
   EXPECT_EQ(1, error_callback_count_);
   EXPECT_EQ(2, observer.discovering_changed_count_);
@@ -1401,10 +1302,9 @@
 
   // Request to start should get queued.
   adapter_->StartDiscoverySession(
-    base::Bind(&BluetoothChromeOSTest::DiscoverySessionCallback,
-               base::Unretained(this)),
-    base::Bind(&BluetoothChromeOSTest::ErrorCallback,
-               base::Unretained(this)));
+      base::Bind(&BluetoothChromeOSTest::DiscoverySessionCallback,
+                 base::Unretained(this)),
+      GetErrorCallback());
   EXPECT_EQ(5, callback_count_);
   EXPECT_EQ(1, error_callback_count_);
   EXPECT_EQ(2, observer.discovering_changed_count_);
@@ -1437,12 +1337,7 @@
 TEST_F(BluetoothChromeOSTest, StartDiscoverySession) {
   GetAdapter();
 
-  adapter_->SetPowered(
-      true,
-      base::Bind(&BluetoothChromeOSTest::Callback,
-                 base::Unretained(this)),
-      base::Bind(&BluetoothChromeOSTest::ErrorCallback,
-                 base::Unretained(this)));
+  adapter_->SetPowered(true, GetCallback(), GetErrorCallback());
   EXPECT_EQ(1, callback_count_);
   EXPECT_EQ(0, error_callback_count_);
   EXPECT_TRUE(adapter_->IsPowered());
@@ -1459,8 +1354,7 @@
   adapter_->StartDiscoverySession(
       base::Bind(&BluetoothChromeOSTest::DiscoverySessionCallback,
                  base::Unretained(this)),
-      base::Bind(&BluetoothChromeOSTest::ErrorCallback,
-                 base::Unretained(this)));
+      GetErrorCallback());
   message_loop_.Run();
   EXPECT_EQ(1, observer.discovering_changed_count_);
   EXPECT_EQ(1, callback_count_);
@@ -1476,8 +1370,7 @@
   adapter_->StartDiscoverySession(
       base::Bind(&BluetoothChromeOSTest::DiscoverySessionCallback,
                  base::Unretained(this)),
-      base::Bind(&BluetoothChromeOSTest::ErrorCallback,
-                 base::Unretained(this)));
+      GetErrorCallback());
   message_loop_.Run();
   EXPECT_EQ(1, observer.discovering_changed_count_);
   EXPECT_EQ(2, callback_count_);
@@ -1491,8 +1384,7 @@
   adapter_->StartDiscoverySession(
       base::Bind(&BluetoothChromeOSTest::DiscoverySessionCallback,
                  base::Unretained(this)),
-      base::Bind(&BluetoothChromeOSTest::ErrorCallback,
-                 base::Unretained(this)));
+      GetErrorCallback());
   message_loop_.Run();
   EXPECT_EQ(1, observer.discovering_changed_count_);
   EXPECT_EQ(3, callback_count_);
@@ -1505,11 +1397,7 @@
 
   // Stop the previous discovery session. The session should end but discovery
   // should continue.
-  discovery_sessions_[0]->Stop(
-      base::Bind(&BluetoothChromeOSTest::Callback,
-                 base::Unretained(this)),
-      base::Bind(&BluetoothChromeOSTest::ErrorCallback,
-                 base::Unretained(this)));
+  discovery_sessions_[0]->Stop(GetCallback(), GetErrorCallback());
   message_loop_.Run();
   EXPECT_EQ(1, observer.discovering_changed_count_);
   EXPECT_EQ(4, callback_count_);
@@ -1675,9 +1563,7 @@
   // with the device we remove.
   TestObserver observer(adapter_);
 
-  devices[0]->Forget(
-      base::Bind(&BluetoothChromeOSTest::ErrorCallback,
-                 base::Unretained(this)));
+  devices[0]->Forget(GetErrorCallback());
   EXPECT_EQ(0, error_callback_count_);
 
   EXPECT_EQ(1, observer.device_removed_count_);
@@ -1698,12 +1584,9 @@
   ASSERT_FALSE(device->IsPaired());
 
   // Connect the device so it becomes trusted and remembered.
-  device->Connect(
-      NULL,
-      base::Bind(&BluetoothChromeOSTest::Callback,
-                 base::Unretained(this)),
-      base::Bind(&BluetoothChromeOSTest::ConnectErrorCallback,
-                 base::Unretained(this)));
+  device->Connect(NULL, GetCallback(),
+                  base::Bind(&BluetoothChromeOSTest::ConnectErrorCallback,
+                             base::Unretained(this)));
 
   ASSERT_EQ(1, callback_count_);
   ASSERT_EQ(0, error_callback_count_);
@@ -1722,9 +1605,7 @@
   // with the device we remove.
   TestObserver observer(adapter_);
 
-  device->Forget(
-      base::Bind(&BluetoothChromeOSTest::ErrorCallback,
-                 base::Unretained(this)));
+  device->Forget(GetErrorCallback());
   EXPECT_EQ(0, error_callback_count_);
 
   EXPECT_EQ(1, observer.device_removed_count_);
@@ -1749,12 +1630,9 @@
 
   // Connect without a pairing delegate; since the device is already Paired
   // this should succeed and the device should become connected.
-  device->Connect(
-      NULL,
-      base::Bind(&BluetoothChromeOSTest::Callback,
-                 base::Unretained(this)),
-      base::Bind(&BluetoothChromeOSTest::ConnectErrorCallback,
-                 base::Unretained(this)));
+  device->Connect(NULL, GetCallback(),
+                  base::Bind(&BluetoothChromeOSTest::ConnectErrorCallback,
+                             base::Unretained(this)));
 
   EXPECT_EQ(1, callback_count_);
   EXPECT_EQ(0, error_callback_count_);
@@ -1781,12 +1659,9 @@
 
   // Connect without a pairing delegate; since the device does not require
   // pairing, this should succeed and the device should become connected.
-  device->Connect(
-      NULL,
-      base::Bind(&BluetoothChromeOSTest::Callback,
-                 base::Unretained(this)),
-      base::Bind(&BluetoothChromeOSTest::ConnectErrorCallback,
-                 base::Unretained(this)));
+  device->Connect(NULL, GetCallback(),
+                  base::Bind(&BluetoothChromeOSTest::ConnectErrorCallback,
+                             base::Unretained(this)));
 
   EXPECT_EQ(1, callback_count_);
   EXPECT_EQ(0, error_callback_count_);
@@ -1820,12 +1695,9 @@
   ASSERT_TRUE(device != NULL);
   ASSERT_TRUE(device->IsPaired());
 
-  device->Connect(
-      NULL,
-      base::Bind(&BluetoothChromeOSTest::Callback,
-                 base::Unretained(this)),
-      base::Bind(&BluetoothChromeOSTest::ConnectErrorCallback,
-                 base::Unretained(this)));
+  device->Connect(NULL, GetCallback(),
+                  base::Bind(&BluetoothChromeOSTest::ConnectErrorCallback,
+                             base::Unretained(this)));
 
   ASSERT_EQ(1, callback_count_);
   ASSERT_EQ(0, error_callback_count_);
@@ -1837,12 +1709,9 @@
   // anything to initiate the connection.
   TestObserver observer(adapter_);
 
-  device->Connect(
-      NULL,
-      base::Bind(&BluetoothChromeOSTest::Callback,
-                 base::Unretained(this)),
-      base::Bind(&BluetoothChromeOSTest::ConnectErrorCallback,
-                 base::Unretained(this)));
+  device->Connect(NULL, GetCallback(),
+                  base::Bind(&BluetoothChromeOSTest::ConnectErrorCallback,
+                             base::Unretained(this)));
 
   EXPECT_EQ(1, callback_count_);
   EXPECT_EQ(0, error_callback_count_);
@@ -1868,12 +1737,9 @@
 
   // Connect without a pairing delegate; since the device requires pairing,
   // this should fail with an error.
-  device->Connect(
-      NULL,
-      base::Bind(&BluetoothChromeOSTest::Callback,
-                 base::Unretained(this)),
-      base::Bind(&BluetoothChromeOSTest::ConnectErrorCallback,
-                 base::Unretained(this)));
+  device->Connect(NULL, GetCallback(),
+                  base::Bind(&BluetoothChromeOSTest::ConnectErrorCallback,
+                             base::Unretained(this)));
 
   EXPECT_EQ(0, callback_count_);
   EXPECT_EQ(1, error_callback_count_);
@@ -1893,12 +1759,9 @@
   ASSERT_TRUE(device != NULL);
   ASSERT_TRUE(device->IsPaired());
 
-  device->Connect(
-      NULL,
-      base::Bind(&BluetoothChromeOSTest::Callback,
-                 base::Unretained(this)),
-      base::Bind(&BluetoothChromeOSTest::ConnectErrorCallback,
-                 base::Unretained(this)));
+  device->Connect(NULL, GetCallback(),
+                  base::Bind(&BluetoothChromeOSTest::ConnectErrorCallback,
+                             base::Unretained(this)));
 
   ASSERT_EQ(1, callback_count_);
   ASSERT_EQ(0, error_callback_count_);
@@ -1911,11 +1774,7 @@
   // device get dropped.
   TestObserver observer(adapter_);
 
-  device->Disconnect(
-      base::Bind(&BluetoothChromeOSTest::Callback,
-                 base::Unretained(this)),
-      base::Bind(&BluetoothChromeOSTest::ErrorCallback,
-                 base::Unretained(this)));
+  device->Disconnect(GetCallback(), GetErrorCallback());
 
   EXPECT_EQ(1, callback_count_);
   EXPECT_EQ(0, error_callback_count_);
@@ -1939,11 +1798,7 @@
   // device get dropped.
   TestObserver observer(adapter_);
 
-  device->Disconnect(
-      base::Bind(&BluetoothChromeOSTest::Callback,
-                 base::Unretained(this)),
-      base::Bind(&BluetoothChromeOSTest::ErrorCallback,
-                 base::Unretained(this)));
+  device->Disconnect(GetCallback(), GetErrorCallback());
 
   EXPECT_EQ(0, callback_count_);
   EXPECT_EQ(1, error_callback_count_);
@@ -1969,12 +1824,9 @@
   TestObserver observer(adapter_);
 
   TestPairingDelegate pairing_delegate;
-  device->Connect(
-      &pairing_delegate,
-      base::Bind(&BluetoothChromeOSTest::Callback,
-                 base::Unretained(this)),
-      base::Bind(&BluetoothChromeOSTest::ConnectErrorCallback,
-                 base::Unretained(this)));
+  device->Connect(&pairing_delegate, GetCallback(),
+                  base::Bind(&BluetoothChromeOSTest::ConnectErrorCallback,
+                             base::Unretained(this)));
 
   EXPECT_EQ(0, pairing_delegate.call_count_);
   EXPECT_TRUE(device->IsConnecting());
@@ -2023,12 +1875,9 @@
   TestObserver observer(adapter_);
 
   TestPairingDelegate pairing_delegate;
-  device->Connect(
-      &pairing_delegate,
-      base::Bind(&BluetoothChromeOSTest::Callback,
-                 base::Unretained(this)),
-      base::Bind(&BluetoothChromeOSTest::ConnectErrorCallback,
-                 base::Unretained(this)));
+  device->Connect(&pairing_delegate, GetCallback(),
+                  base::Bind(&BluetoothChromeOSTest::ConnectErrorCallback,
+                             base::Unretained(this)));
 
   EXPECT_EQ(1, pairing_delegate.call_count_);
   EXPECT_EQ(1, pairing_delegate.display_pincode_count_);
@@ -2080,12 +1929,9 @@
   TestObserver observer(adapter_);
 
   TestPairingDelegate pairing_delegate;
-  device->Connect(
-      &pairing_delegate,
-      base::Bind(&BluetoothChromeOSTest::Callback,
-                 base::Unretained(this)),
-      base::Bind(&BluetoothChromeOSTest::ConnectErrorCallback,
-                 base::Unretained(this)));
+  device->Connect(&pairing_delegate, GetCallback(),
+                  base::Bind(&BluetoothChromeOSTest::ConnectErrorCallback,
+                             base::Unretained(this)));
 
   // One call for DisplayPasskey() and one for KeysEntered().
   EXPECT_EQ(2, pairing_delegate.call_count_);
@@ -2157,12 +2003,9 @@
   TestObserver observer(adapter_);
 
   TestPairingDelegate pairing_delegate;
-  device->Connect(
-      &pairing_delegate,
-      base::Bind(&BluetoothChromeOSTest::Callback,
-                 base::Unretained(this)),
-      base::Bind(&BluetoothChromeOSTest::ConnectErrorCallback,
-                 base::Unretained(this)));
+  device->Connect(&pairing_delegate, GetCallback(),
+                  base::Bind(&BluetoothChromeOSTest::ConnectErrorCallback,
+                             base::Unretained(this)));
 
   EXPECT_EQ(1, pairing_delegate.call_count_);
   EXPECT_EQ(1, pairing_delegate.request_pincode_count_);
@@ -2214,12 +2057,9 @@
   TestObserver observer(adapter_);
 
   TestPairingDelegate pairing_delegate;
-  device->Connect(
-      &pairing_delegate,
-      base::Bind(&BluetoothChromeOSTest::Callback,
-                 base::Unretained(this)),
-      base::Bind(&BluetoothChromeOSTest::ConnectErrorCallback,
-                 base::Unretained(this)));
+  device->Connect(&pairing_delegate, GetCallback(),
+                  base::Bind(&BluetoothChromeOSTest::ConnectErrorCallback,
+                             base::Unretained(this)));
 
   EXPECT_EQ(1, pairing_delegate.call_count_);
   EXPECT_EQ(1, pairing_delegate.confirm_passkey_count_);
@@ -2269,12 +2109,9 @@
   TestObserver observer(adapter_);
 
   TestPairingDelegate pairing_delegate;
-  device->Connect(
-      &pairing_delegate,
-      base::Bind(&BluetoothChromeOSTest::Callback,
-                 base::Unretained(this)),
-      base::Bind(&BluetoothChromeOSTest::ConnectErrorCallback,
-                 base::Unretained(this)));
+  device->Connect(&pairing_delegate, GetCallback(),
+                  base::Bind(&BluetoothChromeOSTest::ConnectErrorCallback,
+                             base::Unretained(this)));
 
   EXPECT_EQ(1, pairing_delegate.call_count_);
   EXPECT_EQ(1, pairing_delegate.request_passkey_count_);
@@ -2323,12 +2160,9 @@
   TestObserver observer(adapter_);
 
   TestPairingDelegate pairing_delegate;
-  device->Connect(
-      &pairing_delegate,
-      base::Bind(&BluetoothChromeOSTest::Callback,
-                 base::Unretained(this)),
-      base::Bind(&BluetoothChromeOSTest::ConnectErrorCallback,
-                 base::Unretained(this)));
+  device->Connect(&pairing_delegate, GetCallback(),
+                  base::Bind(&BluetoothChromeOSTest::ConnectErrorCallback,
+                             base::Unretained(this)));
 
   EXPECT_EQ(0, pairing_delegate.call_count_);
 
@@ -2371,12 +2205,9 @@
   TestObserver observer(adapter_);
 
   TestPairingDelegate pairing_delegate;
-  device->Connect(
-      &pairing_delegate,
-      base::Bind(&BluetoothChromeOSTest::Callback,
-                 base::Unretained(this)),
-      base::Bind(&BluetoothChromeOSTest::ConnectErrorCallback,
-                 base::Unretained(this)));
+  device->Connect(&pairing_delegate, GetCallback(),
+                  base::Bind(&BluetoothChromeOSTest::ConnectErrorCallback,
+                             base::Unretained(this)));
 
   EXPECT_EQ(0, pairing_delegate.call_count_);
   EXPECT_TRUE(device->IsConnecting());
@@ -2409,12 +2240,9 @@
   TestObserver observer(adapter_);
 
   TestPairingDelegate pairing_delegate;
-  device->Connect(
-      &pairing_delegate,
-      base::Bind(&BluetoothChromeOSTest::Callback,
-                 base::Unretained(this)),
-      base::Bind(&BluetoothChromeOSTest::ConnectErrorCallback,
-                 base::Unretained(this)));
+  device->Connect(&pairing_delegate, GetCallback(),
+                  base::Bind(&BluetoothChromeOSTest::ConnectErrorCallback,
+                             base::Unretained(this)));
 
   EXPECT_EQ(0, pairing_delegate.call_count_);
   EXPECT_TRUE(device->IsConnecting());
@@ -2448,12 +2276,9 @@
   TestObserver observer(adapter_);
 
   TestPairingDelegate pairing_delegate;
-  device->Connect(
-      &pairing_delegate,
-      base::Bind(&BluetoothChromeOSTest::Callback,
-                 base::Unretained(this)),
-      base::Bind(&BluetoothChromeOSTest::ConnectErrorCallback,
-                 base::Unretained(this)));
+  device->Connect(&pairing_delegate, GetCallback(),
+                  base::Bind(&BluetoothChromeOSTest::ConnectErrorCallback,
+                             base::Unretained(this)));
 
   EXPECT_EQ(0, pairing_delegate.call_count_);
   EXPECT_TRUE(device->IsConnecting());
@@ -2498,12 +2323,9 @@
   TestObserver observer(adapter_);
 
   TestPairingDelegate pairing_delegate;
-  device->Connect(
-      &pairing_delegate,
-      base::Bind(&BluetoothChromeOSTest::Callback,
-                 base::Unretained(this)),
-      base::Bind(&BluetoothChromeOSTest::ConnectErrorCallback,
-                 base::Unretained(this)));
+  device->Connect(&pairing_delegate, GetCallback(),
+                  base::Bind(&BluetoothChromeOSTest::ConnectErrorCallback,
+                             base::Unretained(this)));
 
   EXPECT_EQ(1, pairing_delegate.call_count_);
   EXPECT_EQ(1, pairing_delegate.request_pincode_count_);
@@ -2539,12 +2361,9 @@
   TestObserver observer(adapter_);
 
   TestPairingDelegate pairing_delegate;
-  device->Connect(
-      &pairing_delegate,
-      base::Bind(&BluetoothChromeOSTest::Callback,
-                 base::Unretained(this)),
-      base::Bind(&BluetoothChromeOSTest::ConnectErrorCallback,
-                 base::Unretained(this)));
+  device->Connect(&pairing_delegate, GetCallback(),
+                  base::Bind(&BluetoothChromeOSTest::ConnectErrorCallback,
+                             base::Unretained(this)));
 
   EXPECT_EQ(1, pairing_delegate.call_count_);
   EXPECT_EQ(1, pairing_delegate.request_pincode_count_);
@@ -2580,12 +2399,9 @@
   TestObserver observer(adapter_);
 
   TestPairingDelegate pairing_delegate;
-  device->Connect(
-      &pairing_delegate,
-      base::Bind(&BluetoothChromeOSTest::Callback,
-                 base::Unretained(this)),
-      base::Bind(&BluetoothChromeOSTest::ConnectErrorCallback,
-                 base::Unretained(this)));
+  device->Connect(&pairing_delegate, GetCallback(),
+                  base::Bind(&BluetoothChromeOSTest::ConnectErrorCallback,
+                             base::Unretained(this)));
 
   EXPECT_EQ(1, pairing_delegate.call_count_);
   EXPECT_EQ(1, pairing_delegate.request_passkey_count_);
@@ -2621,12 +2437,9 @@
   TestObserver observer(adapter_);
 
   TestPairingDelegate pairing_delegate;
-  device->Connect(
-      &pairing_delegate,
-      base::Bind(&BluetoothChromeOSTest::Callback,
-                 base::Unretained(this)),
-      base::Bind(&BluetoothChromeOSTest::ConnectErrorCallback,
-                 base::Unretained(this)));
+  device->Connect(&pairing_delegate, GetCallback(),
+                  base::Bind(&BluetoothChromeOSTest::ConnectErrorCallback,
+                             base::Unretained(this)));
 
   EXPECT_EQ(1, pairing_delegate.call_count_);
   EXPECT_EQ(1, pairing_delegate.request_passkey_count_);
@@ -2662,12 +2475,9 @@
   TestObserver observer(adapter_);
 
   TestPairingDelegate pairing_delegate;
-  device->Connect(
-      &pairing_delegate,
-      base::Bind(&BluetoothChromeOSTest::Callback,
-                 base::Unretained(this)),
-      base::Bind(&BluetoothChromeOSTest::ConnectErrorCallback,
-                 base::Unretained(this)));
+  device->Connect(&pairing_delegate, GetCallback(),
+                  base::Bind(&BluetoothChromeOSTest::ConnectErrorCallback,
+                             base::Unretained(this)));
 
   EXPECT_EQ(1, pairing_delegate.call_count_);
   EXPECT_EQ(1, pairing_delegate.confirm_passkey_count_);
@@ -2703,12 +2513,9 @@
   TestObserver observer(adapter_);
 
   TestPairingDelegate pairing_delegate;
-  device->Connect(
-      &pairing_delegate,
-      base::Bind(&BluetoothChromeOSTest::Callback,
-                 base::Unretained(this)),
-      base::Bind(&BluetoothChromeOSTest::ConnectErrorCallback,
-                 base::Unretained(this)));
+  device->Connect(&pairing_delegate, GetCallback(),
+                  base::Bind(&BluetoothChromeOSTest::ConnectErrorCallback,
+                             base::Unretained(this)));
 
   EXPECT_EQ(1, pairing_delegate.call_count_);
   EXPECT_EQ(1, pairing_delegate.confirm_passkey_count_);
@@ -2744,12 +2551,9 @@
   TestObserver observer(adapter_);
 
   TestPairingDelegate pairing_delegate;
-  device->Connect(
-      &pairing_delegate,
-      base::Bind(&BluetoothChromeOSTest::Callback,
-                 base::Unretained(this)),
-      base::Bind(&BluetoothChromeOSTest::ConnectErrorCallback,
-                 base::Unretained(this)));
+  device->Connect(&pairing_delegate, GetCallback(),
+                  base::Bind(&BluetoothChromeOSTest::ConnectErrorCallback,
+                             base::Unretained(this)));
 
   EXPECT_EQ(0, pairing_delegate.call_count_);
   EXPECT_TRUE(device->IsConnecting());
@@ -2791,12 +2595,9 @@
   TestObserver observer(adapter_);
 
   fake_bluetooth_device_client_->SimulatePairing(
-      dbus::ObjectPath(FakeBluetoothDeviceClient::kRequestPinCodePath),
-      true,
-      base::Bind(&BluetoothChromeOSTest::Callback,
-                 base::Unretained(this)),
-      base::Bind(&BluetoothChromeOSTest::DBusErrorCallback,
-                 base::Unretained(this)));
+      dbus::ObjectPath(FakeBluetoothDeviceClient::kRequestPinCodePath), true,
+      GetCallback(), base::Bind(&BluetoothChromeOSTest::DBusErrorCallback,
+                                base::Unretained(this)));
 
   EXPECT_EQ(1, pairing_delegate.call_count_);
   EXPECT_EQ(1, pairing_delegate.request_pincode_count_);
@@ -2848,12 +2649,9 @@
   TestObserver observer(adapter_);
 
   fake_bluetooth_device_client_->SimulatePairing(
-      dbus::ObjectPath(FakeBluetoothDeviceClient::kConfirmPasskeyPath),
-      true,
-      base::Bind(&BluetoothChromeOSTest::Callback,
-                 base::Unretained(this)),
-      base::Bind(&BluetoothChromeOSTest::DBusErrorCallback,
-                 base::Unretained(this)));
+      dbus::ObjectPath(FakeBluetoothDeviceClient::kConfirmPasskeyPath), true,
+      GetCallback(), base::Bind(&BluetoothChromeOSTest::DBusErrorCallback,
+                                base::Unretained(this)));
 
   EXPECT_EQ(1, pairing_delegate.call_count_);
   EXPECT_EQ(1, pairing_delegate.confirm_passkey_count_);
@@ -2906,12 +2704,9 @@
   TestObserver observer(adapter_);
 
   fake_bluetooth_device_client_->SimulatePairing(
-      dbus::ObjectPath(FakeBluetoothDeviceClient::kRequestPasskeyPath),
-      true,
-      base::Bind(&BluetoothChromeOSTest::Callback,
-                 base::Unretained(this)),
-      base::Bind(&BluetoothChromeOSTest::DBusErrorCallback,
-                 base::Unretained(this)));
+      dbus::ObjectPath(FakeBluetoothDeviceClient::kRequestPasskeyPath), true,
+      GetCallback(), base::Bind(&BluetoothChromeOSTest::DBusErrorCallback,
+                                base::Unretained(this)));
 
   EXPECT_EQ(1, pairing_delegate.call_count_);
   EXPECT_EQ(1, pairing_delegate.request_passkey_count_);
@@ -2964,12 +2759,9 @@
   TestObserver observer(adapter_);
 
   fake_bluetooth_device_client_->SimulatePairing(
-      dbus::ObjectPath(FakeBluetoothDeviceClient::kJustWorksPath),
-      true,
-      base::Bind(&BluetoothChromeOSTest::Callback,
-                 base::Unretained(this)),
-      base::Bind(&BluetoothChromeOSTest::DBusErrorCallback,
-                 base::Unretained(this)));
+      dbus::ObjectPath(FakeBluetoothDeviceClient::kJustWorksPath), true,
+      GetCallback(), base::Bind(&BluetoothChromeOSTest::DBusErrorCallback,
+                                base::Unretained(this)));
 
   EXPECT_EQ(1, pairing_delegate.call_count_);
   EXPECT_EQ(1, pairing_delegate.authorize_pairing_count_);
@@ -3017,12 +2809,9 @@
   TestObserver observer(adapter_);
 
   fake_bluetooth_device_client_->SimulatePairing(
-      dbus::ObjectPath(FakeBluetoothDeviceClient::kRequestPinCodePath),
-      true,
-      base::Bind(&BluetoothChromeOSTest::Callback,
-                 base::Unretained(this)),
-      base::Bind(&BluetoothChromeOSTest::DBusErrorCallback,
-                 base::Unretained(this)));
+      dbus::ObjectPath(FakeBluetoothDeviceClient::kRequestPinCodePath), true,
+      GetCallback(), base::Bind(&BluetoothChromeOSTest::DBusErrorCallback,
+                                base::Unretained(this)));
 
   message_loop_.Run();
 
@@ -3059,12 +2848,9 @@
   TestObserver observer(adapter_);
 
   fake_bluetooth_device_client_->SimulatePairing(
-      dbus::ObjectPath(FakeBluetoothDeviceClient::kConfirmPasskeyPath),
-      true,
-      base::Bind(&BluetoothChromeOSTest::Callback,
-                 base::Unretained(this)),
-      base::Bind(&BluetoothChromeOSTest::DBusErrorCallback,
-                 base::Unretained(this)));
+      dbus::ObjectPath(FakeBluetoothDeviceClient::kConfirmPasskeyPath), true,
+      GetCallback(), base::Bind(&BluetoothChromeOSTest::DBusErrorCallback,
+                                base::Unretained(this)));
 
   message_loop_.Run();
 
@@ -3101,12 +2887,9 @@
   TestObserver observer(adapter_);
 
   fake_bluetooth_device_client_->SimulatePairing(
-      dbus::ObjectPath(FakeBluetoothDeviceClient::kRequestPasskeyPath),
-      true,
-      base::Bind(&BluetoothChromeOSTest::Callback,
-                 base::Unretained(this)),
-      base::Bind(&BluetoothChromeOSTest::DBusErrorCallback,
-                 base::Unretained(this)));
+      dbus::ObjectPath(FakeBluetoothDeviceClient::kRequestPasskeyPath), true,
+      GetCallback(), base::Bind(&BluetoothChromeOSTest::DBusErrorCallback,
+                                base::Unretained(this)));
 
   message_loop_.Run();
 
@@ -3143,12 +2926,9 @@
   TestObserver observer(adapter_);
 
   fake_bluetooth_device_client_->SimulatePairing(
-      dbus::ObjectPath(FakeBluetoothDeviceClient::kJustWorksPath),
-      true,
-      base::Bind(&BluetoothChromeOSTest::Callback,
-                 base::Unretained(this)),
-      base::Bind(&BluetoothChromeOSTest::DBusErrorCallback,
-                 base::Unretained(this)));
+      dbus::ObjectPath(FakeBluetoothDeviceClient::kJustWorksPath), true,
+      GetCallback(), base::Bind(&BluetoothChromeOSTest::DBusErrorCallback,
+                                base::Unretained(this)));
 
   message_loop_.Run();
 
@@ -3189,12 +2969,9 @@
   TestObserver observer(adapter_);
 
   fake_bluetooth_device_client_->SimulatePairing(
-      dbus::ObjectPath(FakeBluetoothDeviceClient::kRequestPasskeyPath),
-      true,
-      base::Bind(&BluetoothChromeOSTest::Callback,
-                 base::Unretained(this)),
-      base::Bind(&BluetoothChromeOSTest::DBusErrorCallback,
-                 base::Unretained(this)));
+      dbus::ObjectPath(FakeBluetoothDeviceClient::kRequestPasskeyPath), true,
+      GetCallback(), base::Bind(&BluetoothChromeOSTest::DBusErrorCallback,
+                                base::Unretained(this)));
 
   EXPECT_EQ(1, pairing_delegate.call_count_);
   EXPECT_EQ(1, pairing_delegate.request_passkey_count_);
@@ -3296,12 +3073,9 @@
   BluetoothDevice* device =
       adapter_->GetDevice(FakeBluetoothDeviceClient::kPairedDeviceAddress);
 
-  device->Connect(
-      NULL,
-      base::Bind(&BluetoothChromeOSTest::Callback,
-                 base::Unretained(this)),
-      base::Bind(&BluetoothChromeOSTest::ConnectErrorCallback,
-                 base::Unretained(this)));
+  device->Connect(NULL, GetCallback(),
+                  base::Bind(&BluetoothChromeOSTest::ConnectErrorCallback,
+                             base::Unretained(this)));
   EXPECT_TRUE(device->IsConnected());
 
   // Calling GetConnectionInfo for a connected device should return valid
@@ -3316,26 +3090,23 @@
 
 // Verifies Shutdown shuts down the adapter as expected.
 TEST_F(BluetoothChromeOSTest, Shutdown) {
-  // Set up and adapter, power, discoverable, start discovery.
+  // Set up adapter. Set powered & discoverable, start discovery.
   GetAdapter();
-  adapter_->SetPowered(true, base::Bind(&BluetoothChromeOSTest::Callback,
-                                        base::Unretained(this)),
-                       base::Bind(&BluetoothChromeOSTest::ErrorCallback,
-                                  base::Unretained(this)));
-  adapter_->SetDiscoverable(true, base::Bind(&BluetoothChromeOSTest::Callback,
-                                             base::Unretained(this)),
-                            base::Bind(&BluetoothChromeOSTest::ErrorCallback,
-                                       base::Unretained(this)));
+  adapter_->SetPowered(true, GetCallback(), GetErrorCallback());
+  adapter_->SetDiscoverable(true, GetCallback(), GetErrorCallback());
   adapter_->StartDiscoverySession(
       base::Bind(&BluetoothChromeOSTest::DiscoverySessionCallback,
                  base::Unretained(this)),
-      base::Bind(&BluetoothChromeOSTest::ErrorCallback,
-                 base::Unretained(this)));
+      GetErrorCallback());
   base::MessageLoop::current()->Run();
   ASSERT_EQ(3, callback_count_);
   ASSERT_EQ(0, error_callback_count_);
   callback_count_ = 0;
 
+  TestPairingDelegate pairing_delegate;
+  adapter_->AddPairingDelegate(
+      &pairing_delegate, BluetoothAdapter::PAIRING_DELEGATE_PRIORITY_HIGH);
+
   // Validate running adapter state.
   EXPECT_NE("", adapter_->GetAddress());
   EXPECT_NE("", adapter_->GetName());
@@ -3351,37 +3122,285 @@
                                       adapter_.get())->object_path());
 
   // Shutdown
-  static_cast<BluetoothAdapterChromeOS*>(adapter_.get())->Shutdown();
+  adapter_->Shutdown();
 
-  // Validate post shutdown state.
+  // Validate post shutdown state by calling all BluetoothAdapterChromeOS
+  // members, in declaration order:
+
+  adapter_->Shutdown();
+  // DeleteOnCorrectThread omitted as we don't want to delete in this test.
+  {
+    TestObserver observer(adapter_);  // Calls AddObserver
+  }  // TestObserver::~TestObserver calls RemoveObserver.
   EXPECT_EQ("", adapter_->GetAddress());
   EXPECT_EQ("", adapter_->GetName());
+
+  adapter_->SetName("", GetCallback(), GetErrorCallback());
+  EXPECT_EQ(0, callback_count_);
+  EXPECT_EQ(1, error_callback_count_--) << "SetName error";
+
   EXPECT_TRUE(adapter_->IsInitialized());
   EXPECT_FALSE(adapter_->IsPresent());
   EXPECT_FALSE(adapter_->IsPowered());
-  EXPECT_FALSE(adapter_->IsDiscoverable());
-  EXPECT_FALSE(adapter_->IsDiscovering());
-  EXPECT_EQ(0U, adapter_->GetDevices().size());
-  EXPECT_EQ(nullptr, adapter_->GetDevice(
-                         FakeBluetoothDeviceClient::kPairedDeviceAddress));
-  EXPECT_EQ(dbus::ObjectPath(""), static_cast<BluetoothAdapterChromeOS*>(
-                                      adapter_.get())->object_path());
 
-  adapter_->SetPowered(true, base::Bind(&BluetoothChromeOSTest::Callback,
-                                        base::Unretained(this)),
-                       base::Bind(&BluetoothChromeOSTest::ErrorCallback,
-                                  base::Unretained(this)));
-  adapter_->SetDiscoverable(true, base::Bind(&BluetoothChromeOSTest::Callback,
-                                             base::Unretained(this)),
-                            base::Bind(&BluetoothChromeOSTest::ErrorCallback,
-                                       base::Unretained(this)));
+  adapter_->SetPowered(true, GetCallback(), GetErrorCallback());
+  EXPECT_EQ(0, callback_count_);
+  EXPECT_EQ(1, error_callback_count_--) << "SetPowered error";
+
+  EXPECT_FALSE(adapter_->IsDiscoverable());
+
+  adapter_->SetDiscoverable(true, GetCallback(), GetErrorCallback());
+  EXPECT_EQ(0, callback_count_);
+  EXPECT_EQ(1, error_callback_count_--) << "SetDiscoverable error";
+
+  EXPECT_FALSE(adapter_->IsDiscovering());
+  // CreateRfcommService will DCHECK after Shutdown().
+  // CreateL2capService will DCHECK after Shutdown().
+
+  BluetoothAudioSink::Options audio_sink_options;
+  adapter_->RegisterAudioSink(
+      audio_sink_options,
+      base::Bind(&BluetoothChromeOSTest::AudioSinkAcquiredCallback,
+                 base::Unretained(this)),
+      base::Bind(&BluetoothChromeOSTest::AudioSinkErrorCallback,
+                 base::Unretained(this)));
+  EXPECT_EQ(0, callback_count_);
+  EXPECT_EQ(1, error_callback_count_--) << "RegisterAudioSink error";
+
+  BluetoothAdapterChromeOS* adapter_chrome_os =
+      static_cast<BluetoothAdapterChromeOS*>(adapter_.get());
+  EXPECT_EQ(NULL, adapter_chrome_os->GetDeviceWithPath(dbus::ObjectPath("")));
+
+  // Notify methods presume objects exist that are owned by the adapter and
+  // destroyed in Shutdown(). Mocks are not attempted here that won't exist,
+  // as verified below by EXPECT_EQ(0U, adapter_->GetDevices().size());
+  // NotifyDeviceChanged
+  // NotifyGattServiceAdded
+  // NotifyGattServiceRemoved
+  // NotifyGattServiceChanged
+  // NotifyGattDiscoveryComplete
+  // NotifyGattCharacteristicAdded
+  // NotifyGattCharacteristicRemoved
+  // NotifyGattDescriptorAdded
+  // NotifyGattDescriptorRemoved
+  // NotifyGattCharacteristicValueChanged
+  // NotifyGattDescriptorValueChanged
+
+  EXPECT_EQ(dbus::ObjectPath(""), adapter_chrome_os->object_path());
+
+  FakeBluetoothProfileServiceProviderDelegate profile_delegate;
+  adapter_chrome_os->UseProfile(
+      BluetoothUUID(), dbus::ObjectPath(""),
+      BluetoothProfileManagerClient::Options(), &profile_delegate,
+      base::Bind(&BluetoothChromeOSTest::ProfileRegisteredCallback,
+                 base::Unretained(this)),
+      base::Bind(&BluetoothChromeOSTest::ErrorCompletionCallback,
+                 base::Unretained(this)));
+  base::MessageLoop::current()->Run();
+  EXPECT_EQ(1, callback_count_--) << "UseProfile error";
+  EXPECT_EQ(0, error_callback_count_) << "UseProfile error";
+
+  adapter_chrome_os->ReleaseProfile(BluetoothUUID());
+
+  // Protected and private methods:
+
+  adapter_chrome_os->RemovePairingDelegateInternal(&pairing_delegate);
+
+  // BluetoothAdapterClient::Observer methods omitted, dbus will be shutdown.
+  // BluetoothDeviceClient::Observer methods omitted, dbus will be shutdown.
+  // BluetoothInputClient::Observer methods omitted, dbus will be shutdown.
+  // BluetoothAgentServiceProvider::Delegate omitted, dbus will be shutdown,
+  //   with the exception of Released.
+  adapter_chrome_os->Released();
+
+  adapter_chrome_os->OnRegisterAgent();
+  adapter_chrome_os->OnRegisterAgentError("", "");
+  adapter_chrome_os->OnRequestDefaultAgent();
+  adapter_chrome_os->OnRequestDefaultAgentError("", "");
+
+  adapter_chrome_os->OnRegisterAudioSink(
+      base::Bind(&BluetoothChromeOSTest::AudioSinkAcquiredCallback,
+                 base::Unretained(this)),
+      base::Bind(&BluetoothChromeOSTest::AudioSinkErrorCallback,
+                 base::Unretained(this)),
+      scoped_refptr<device::BluetoothAudioSink>());
+  EXPECT_EQ(0, callback_count_);
+  EXPECT_EQ(1, error_callback_count_--) << "RegisterAudioSink error";
+
+  // GetPairing will DCHECK after Shutdown().
+  // SetAdapter will DCHECK after Shutdown().
+  // SetDefaultAdapterName will DCHECK after Shutdown().
+  // RemoveAdapter will DCHECK after Shutdown().
+  adapter_chrome_os->PoweredChanged(false);
+  adapter_chrome_os->DiscoverableChanged(false);
+  adapter_chrome_os->DiscoveringChanged(false);
+  adapter_chrome_os->PresentChanged(false);
+
+  adapter_chrome_os->OnSetDiscoverable(GetCallback(), GetErrorCallback(), true);
+  EXPECT_EQ(0, callback_count_) << "OnSetDiscoverable error";
+  EXPECT_EQ(1, error_callback_count_--) << "OnSetDiscoverable error";
+
+  adapter_chrome_os->OnPropertyChangeCompleted(GetCallback(),
+                                               GetErrorCallback(), true);
+  EXPECT_EQ(0, callback_count_) << "OnPropertyChangeCompleted error";
+  EXPECT_EQ(1, error_callback_count_--) << "OnPropertyChangeCompleted error";
+
+  adapter_chrome_os->AddDiscoverySession(GetCallback(), GetErrorCallback());
+  EXPECT_EQ(0, callback_count_) << "AddDiscoverySession error";
+  EXPECT_EQ(1, error_callback_count_--) << "AddDiscoverySession error";
+
+  adapter_chrome_os->RemoveDiscoverySession(GetCallback(), GetErrorCallback());
+  EXPECT_EQ(0, callback_count_) << "RemoveDiscoverySession error";
+  EXPECT_EQ(1, error_callback_count_--) << "RemoveDiscoverySession error";
+
+  // OnStartDiscovery tested in Shutdown_OnStartDiscovery
+  // OnStartDiscoveryError tested in Shutdown_OnStartDiscoveryError
+  // OnStopDiscovery tested in Shutdown_OnStopDiscovery
+  // OnStopDiscoveryError tested in Shutdown_OnStopDiscoveryError
+
+  // OnRegisterProfile SetProfileDelegate, OnRegisterProfileError, require
+  // UseProfile to be set first, do so again here just before calling them.
+  adapter_chrome_os->UseProfile(
+      BluetoothUUID(), dbus::ObjectPath(""),
+      BluetoothProfileManagerClient::Options(), &profile_delegate,
+      base::Bind(&BluetoothChromeOSTest::ProfileRegisteredCallback,
+                 base::Unretained(this)),
+      base::Bind(&BluetoothChromeOSTest::ErrorCompletionCallback,
+                 base::Unretained(this)));
+
+  adapter_chrome_os->OnRegisterProfile(
+      BluetoothUUID(), dbus::ObjectPath(""), &profile_delegate,
+      base::Bind(&BluetoothChromeOSTest::ProfileRegisteredCallback,
+                 base::Unretained(this)),
+      base::Bind(&BluetoothChromeOSTest::ErrorCompletionCallback,
+                 base::Unretained(this)));
+  EXPECT_EQ(1, callback_count_--) << "OnRegisterProfile error";
+  EXPECT_EQ(1, error_callback_count_--) << "OnRegisterProfile error";
+
+  adapter_chrome_os->SetProfileDelegate(
+      BluetoothUUID(), dbus::ObjectPath(""), &profile_delegate,
+      base::Bind(&BluetoothChromeOSTest::ProfileRegisteredCallback,
+                 base::Unretained(this)),
+      base::Bind(&BluetoothChromeOSTest::ErrorCompletionCallback,
+                 base::Unretained(this)));
+  EXPECT_EQ(0, callback_count_) << "SetProfileDelegate error";
+  EXPECT_EQ(1, error_callback_count_--) << "SetProfileDelegate error";
+
+  adapter_chrome_os->OnRegisterProfileError(
+      BluetoothUUID(),
+      base::Bind(&BluetoothChromeOSTest::ErrorCompletionCallback,
+                 base::Unretained(this)),
+      "", "");
+  EXPECT_EQ(0, callback_count_) << "OnRegisterProfileError error";
+  EXPECT_EQ(1, error_callback_count_--) << "OnRegisterProfileError error";
+
+  adapter_chrome_os->ProcessQueuedDiscoveryRequests();
+
+  // From BluetoothAdapater:
+
   adapter_->StartDiscoverySession(
       base::Bind(&BluetoothChromeOSTest::DiscoverySessionCallback,
                  base::Unretained(this)),
-      base::Bind(&BluetoothChromeOSTest::ErrorCallback,
-                 base::Unretained(this)));
-  ASSERT_EQ(0, callback_count_);
-  ASSERT_EQ(3, error_callback_count_);
+      GetErrorCallback());
+  EXPECT_EQ(0, callback_count_) << "StartDiscoverySession error";
+  EXPECT_EQ(1, error_callback_count_--) << "StartDiscoverySession error";
+
+  EXPECT_EQ(0U, adapter_->GetDevices().size());
+  EXPECT_EQ(nullptr, adapter_->GetDevice(
+                         FakeBluetoothDeviceClient::kPairedDeviceAddress));
+  TestPairingDelegate pairing_delegate2;
+  adapter_->AddPairingDelegate(
+      &pairing_delegate2, BluetoothAdapter::PAIRING_DELEGATE_PRIORITY_HIGH);
+  adapter_->RemovePairingDelegate(&pairing_delegate2);
+}
+
+// Verifies post-Shutdown of discovery sessions and OnStartDiscovery.
+TEST_F(BluetoothChromeOSTest, Shutdown_OnStartDiscovery) {
+  const int kNumberOfDiscoverySessions = 10;
+  GetAdapter();
+  BluetoothAdapterChromeOS* adapter_chrome_os =
+      static_cast<BluetoothAdapterChromeOS*>(adapter_.get());
+
+  for (int i = 0; i < kNumberOfDiscoverySessions; i++) {
+    adapter_chrome_os->AddDiscoverySession(GetCallback(), GetErrorCallback());
+  }
+  adapter_->Shutdown();
+  adapter_chrome_os->OnStartDiscovery(GetCallback(), GetErrorCallback());
+
+  EXPECT_EQ(0, callback_count_);
+  EXPECT_EQ(kNumberOfDiscoverySessions, error_callback_count_);
+}
+
+// Verifies post-Shutdown of discovery sessions and OnStartDiscoveryError.
+TEST_F(BluetoothChromeOSTest, Shutdown_OnStartDiscoveryError) {
+  const int kNumberOfDiscoverySessions = 10;
+  GetAdapter();
+  BluetoothAdapterChromeOS* adapter_chrome_os =
+      static_cast<BluetoothAdapterChromeOS*>(adapter_.get());
+
+  for (int i = 0; i < kNumberOfDiscoverySessions; i++) {
+    adapter_chrome_os->AddDiscoverySession(GetCallback(), GetErrorCallback());
+  }
+  adapter_->Shutdown();
+  adapter_chrome_os->OnStartDiscoveryError(GetCallback(), GetErrorCallback(),
+                                           "", "");
+
+  EXPECT_EQ(0, callback_count_);
+  EXPECT_EQ(kNumberOfDiscoverySessions, error_callback_count_);
+}
+
+// Verifies post-Shutdown of discovery sessions and OnStartDiscovery.
+TEST_F(BluetoothChromeOSTest, Shutdown_OnStopDiscovery) {
+  const int kNumberOfDiscoverySessions = 10;
+  GetAdapter();
+  BluetoothAdapterChromeOS* adapter_chrome_os =
+      static_cast<BluetoothAdapterChromeOS*>(adapter_.get());
+
+  // In order to queue up discovery sessions before an OnStopDiscovery call
+  // RemoveDiscoverySession must be called, so Add, Start, and Remove:
+  adapter_chrome_os->AddDiscoverySession(GetCallback(), GetErrorCallback());
+  adapter_chrome_os->OnStartDiscovery(GetCallback(), GetErrorCallback());
+  adapter_chrome_os->RemoveDiscoverySession(GetCallback(), GetErrorCallback());
+  callback_count_ = 0;
+  error_callback_count_ = 0;
+  // Can now queue discovery sessions while waiting for OnStopDiscovery.
+  for (int i = 0; i < kNumberOfDiscoverySessions; i++) {
+    adapter_chrome_os->AddDiscoverySession(GetCallback(), GetErrorCallback());
+  }
+  adapter_->Shutdown();
+  adapter_chrome_os->OnStopDiscovery(GetCallback());
+
+  // 1 successful stopped discovery from RemoveDiscoverySession, and
+  // kNumberOfDiscoverySessions errors from AddDiscoverySession/OnStopDiscovery.
+  EXPECT_EQ(1, callback_count_);
+  EXPECT_EQ(kNumberOfDiscoverySessions, error_callback_count_);
+}
+
+// Verifies post-Shutdown of discovery sessions and OnStopDiscoveryError.
+TEST_F(BluetoothChromeOSTest, Shutdown_OnStopDiscoveryError) {
+  const int kNumberOfDiscoverySessions = 10;
+  GetAdapter();
+  BluetoothAdapterChromeOS* adapter_chrome_os =
+      static_cast<BluetoothAdapterChromeOS*>(adapter_.get());
+
+  // In order to queue up discovery sessions before an OnStopDiscoveryError call
+  // RemoveDiscoverySession must be called, so Add, Start, and Remove:
+  adapter_chrome_os->AddDiscoverySession(GetCallback(), GetErrorCallback());
+  adapter_chrome_os->OnStartDiscovery(GetCallback(), GetErrorCallback());
+  adapter_chrome_os->RemoveDiscoverySession(GetCallback(), GetErrorCallback());
+  callback_count_ = 0;
+  error_callback_count_ = 0;
+  // Can now queue discovery sessions while waiting for OnStopDiscoveryError.
+  for (int i = 0; i < kNumberOfDiscoverySessions; i++) {
+    adapter_chrome_os->AddDiscoverySession(GetCallback(), GetErrorCallback());
+  }
+  adapter_->Shutdown();
+  adapter_chrome_os->OnStopDiscoveryError(GetErrorCallback(), "", "");
+
+  // 1 error reported to RemoveDiscoverySession because of OnStopDiscoveryError,
+  // and kNumberOfDiscoverySessions errors queued with AddDiscoverySession.
+  EXPECT_EQ(0, callback_count_);
+  EXPECT_EQ(1 + kNumberOfDiscoverySessions, error_callback_count_);
 }
 
 }  // namespace chromeos
diff --git a/device/core/BUILD.gn b/device/core/BUILD.gn
index eefca3b..b77bda8 100644
--- a/device/core/BUILD.gn
+++ b/device/core/BUILD.gn
@@ -9,4 +9,6 @@
     "device_monitor_win.cc",
     "device_monitor_win.h",
   ]
+
+  deps = [ "//base" ]
 }
diff --git a/device/hid/BUILD.gn b/device/hid/BUILD.gn
index 6dc7b17..64e99c4 100644
--- a/device/hid/BUILD.gn
+++ b/device/hid/BUILD.gn
@@ -49,4 +49,7 @@
   if (is_linux) {
     deps += [ "//device/udev_linux" ]
   }
+  if (is_chromeos) {
+    deps += [ "//chromeos" ]
+  }
 }
diff --git a/device/hid/hid_connection.cc b/device/hid/hid_connection.cc
index 6ce2d7fd..c43667a 100644
--- a/device/hid/hid_connection.cc
+++ b/device/hid/hid_connection.cc
@@ -99,6 +99,12 @@
     callback.Run(false);
     return;
   }
+  if (size > device_info_->max_output_report_size() + 1) {
+    VLOG(1) << "Output report buffer too long (" << size << " > "
+            << (device_info_->max_output_report_size() + 1) << ").";
+    callback.Run(false);
+    return;
+  }
   DCHECK_GE(size, 1u);
   uint8_t report_id = buffer->data()[0];
   if (device_info_->has_report_id() != (report_id != 0)) {
diff --git a/device/hid/hid_connection_win.cc b/device/hid/hid_connection_win.cc
index b453c2f..3b1a9c3 100644
--- a/device/hid/hid_connection_win.cc
+++ b/device/hid/hid_connection_win.cc
@@ -137,8 +137,19 @@
 void HidConnectionWin::PlatformWrite(scoped_refptr<net::IOBuffer> buffer,
                                      size_t size,
                                      const WriteCallback& callback) {
-  // The Windows API always wants either a report ID (if supported) or
-  // zero at the front of every output report.
+  size_t expected_size = device_info()->max_output_report_size() + 1;
+  DCHECK(size <= expected_size);
+  // The Windows API always wants either a report ID (if supported) or zero at
+  // the front of every output report and requires that the buffer size be equal
+  // to the maximum output report size supported by this collection.
+  if (size < expected_size) {
+    scoped_refptr<net::IOBuffer> tmp_buffer = new net::IOBuffer(
+        base::checked_cast<int>(expected_size));
+    memcpy(tmp_buffer->data(), buffer->data(), size);
+    memset(tmp_buffer->data() + size, 0, expected_size - size);
+    buffer = tmp_buffer;
+    size = expected_size;
+  }
   scoped_refptr<PendingHidTransfer> transfer(new PendingHidTransfer(
       buffer, base::Bind(&HidConnectionWin::OnWriteComplete, this, callback)));
   transfers_.insert(transfer);
diff --git a/device/hid/hid_service.cc b/device/hid/hid_service.cc
index c0b176e..8f502dfc 100644
--- a/device/hid/hid_service.cc
+++ b/device/hid/hid_service.cc
@@ -41,7 +41,7 @@
 #elif defined(OS_MACOSX)
     g_service = new HidServiceMac(file_task_runner);
 #elif defined(OS_WIN)
-    g_service = new HidServiceWin();
+    g_service = new HidServiceWin(file_task_runner);
 #endif
     if (g_service != nullptr) {
       base::AtExitManager::RegisterTask(base::Bind(
diff --git a/device/hid/hid_service_win.cc b/device/hid/hid_service_win.cc
index 234d37bc..3ab8286 100644
--- a/device/hid/hid_service_win.cc
+++ b/device/hid/hid_service_win.cc
@@ -28,7 +28,18 @@
 
 namespace device {
 
-HidServiceWin::HidServiceWin() : device_observer_(this) {
+namespace {
+
+void Noop() {
+  // This function does nothing.
+}
+}
+
+HidServiceWin::HidServiceWin(
+    scoped_refptr<base::SingleThreadTaskRunner> file_task_runner)
+    : device_observer_(this),
+      file_task_runner_(file_task_runner),
+      weak_factory_(this) {
   task_runner_ = base::ThreadTaskRunnerHandle::Get();
   DCHECK(task_runner_.get());
   DeviceMonitorWin* device_monitor =
@@ -36,7 +47,9 @@
   if (device_monitor) {
     device_observer_.Add(device_monitor);
   }
-  DoInitialEnumeration();
+  file_task_runner_->PostTask(
+      FROM_HERE, base::Bind(&HidServiceWin::EnumerateOnFileThread,
+                            weak_factory_.GetWeakPtr(), task_runner_));
 }
 
 void HidServiceWin::Connect(const HidDeviceId& device_id,
@@ -64,7 +77,10 @@
 HidServiceWin::~HidServiceWin() {
 }
 
-void HidServiceWin::DoInitialEnumeration() {
+// static
+void HidServiceWin::EnumerateOnFileThread(
+    base::WeakPtr<HidServiceWin> service,
+    scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
   HDEVINFO device_info_set =
       SetupDiGetClassDevs(&GUID_DEVINTERFACE_HID, NULL, NULL,
                           DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
@@ -103,11 +119,13 @@
       std::string device_path(
           base::SysWideToUTF8(device_interface_detail_data->DevicePath));
       DCHECK(base::IsStringASCII(device_path));
-      OnDeviceAdded(base::StringToLowerASCII(device_path));
+      AddDeviceOnFileThread(service, task_runner,
+                            base::StringToLowerASCII(device_path));
     }
   }
 
-  FirstEnumerationComplete();
+  task_runner->PostTask(
+      FROM_HERE, base::Bind(&HidServiceWin::FirstEnumerationComplete, service));
 }
 
 // static
@@ -155,7 +173,11 @@
   }
 }
 
-void HidServiceWin::OnDeviceAdded(const std::string& device_path) {
+// static
+void HidServiceWin::AddDeviceOnFileThread(
+    base::WeakPtr<HidServiceWin> service,
+    scoped_refptr<base::SingleThreadTaskRunner> task_runner,
+    const std::string& device_path) {
   base::win::ScopedHandle device_handle(OpenDevice(device_path));
   if (!device_handle.IsValid()) {
     return;
@@ -220,25 +242,53 @@
                            capabilities.NumberFeatureValueCaps,
                            &collection_info);
 
+  // 1023 characters plus NULL terminator is more than enough for a USB string
+  // descriptor which is limited to 126 characters.
+  wchar_t buffer[1024];
+  std::string product_name;
+  if (HidD_GetProductString(device_handle.Get(), &buffer[0], sizeof(buffer))) {
+    // NULL termination guaranteed by the API.
+    product_name = base::SysWideToUTF8(buffer);
+  }
+  std::string serial_number;
+  if (HidD_GetSerialNumberString(device_handle.Get(), &buffer[0],
+                                 sizeof(buffer))) {
+    // NULL termination guaranteed by the API.
+    serial_number = base::SysWideToUTF8(buffer);
+  }
+
   // This populates the HidDeviceInfo instance without a raw report descriptor.
   // The descriptor is unavailable on Windows because HID devices are exposed to
   // user-space as individual top-level collections.
   scoped_refptr<HidDeviceInfo> device_info(new HidDeviceInfo(
-      device_path, attrib.VendorID, attrib.ProductID,
-      "",              // TODO(reillyg): Get product name from Windows.
-      "",              // TODO(reillyg): Get serial number from Windows.
+      device_path, attrib.VendorID, attrib.ProductID, product_name,
+      serial_number,
       kHIDBusTypeUSB,  // TODO(reillyg): Detect Bluetooth. crbug.com/443335
       collection_info, max_input_report_size, max_output_report_size,
       max_feature_report_size));
 
   HidD_FreePreparsedData(preparsed_data);
-  AddDevice(device_info);
+  task_runner->PostTask(
+      FROM_HERE, base::Bind(&HidServiceWin::AddDevice, service, device_info));
+}
+
+void HidServiceWin::OnDeviceAdded(const std::string& device_path) {
+  file_task_runner_->PostTask(
+      FROM_HERE,
+      base::Bind(&HidServiceWin::AddDeviceOnFileThread,
+                 weak_factory_.GetWeakPtr(), task_runner_, device_path));
 }
 
 void HidServiceWin::OnDeviceRemoved(const std::string& device_path) {
-  RemoveDevice(device_path);
+  // Execute a no-op closure on the file task runner to synchronize with any
+  // devices that are still being enumerated.
+  file_task_runner_->PostTaskAndReply(
+      FROM_HERE, base::Bind(&Noop),
+      base::Bind(&HidServiceWin::RemoveDevice, weak_factory_.GetWeakPtr(),
+                 device_path));
 }
 
+// static
 base::win::ScopedHandle HidServiceWin::OpenDevice(
     const std::string& device_path) {
   base::win::ScopedHandle file(
diff --git a/device/hid/hid_service_win.h b/device/hid/hid_service_win.h
index 902d431..1704ac7 100644
--- a/device/hid/hid_service_win.h
+++ b/device/hid/hid_service_win.h
@@ -14,6 +14,7 @@
 }
 
 #include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
 #include "base/scoped_observer.h"
 #include "base/win/scoped_handle.h"
 #include "device/core/device_monitor_win.h"
@@ -30,7 +31,7 @@
 
 class HidServiceWin : public HidService, public DeviceMonitorWin::Observer {
  public:
-  HidServiceWin();
+  HidServiceWin(scoped_refptr<base::SingleThreadTaskRunner> file_task_runner);
 
   virtual void Connect(const HidDeviceId& device_id,
                        const ConnectCallback& callback) override;
@@ -38,7 +39,9 @@
  private:
   virtual ~HidServiceWin();
 
-  void DoInitialEnumeration();
+  static void EnumerateOnFileThread(
+      base::WeakPtr<HidServiceWin> service,
+      scoped_refptr<base::SingleThreadTaskRunner> task_runner);
   static void CollectInfoFromButtonCaps(PHIDP_PREPARSED_DATA preparsed_data,
                                         HIDP_REPORT_TYPE report_type,
                                         USHORT button_caps_length,
@@ -47,16 +50,22 @@
                                        HIDP_REPORT_TYPE report_type,
                                        USHORT value_caps_length,
                                        HidCollectionInfo* collection_info);
+  static void AddDeviceOnFileThread(
+      base::WeakPtr<HidServiceWin> service,
+      scoped_refptr<base::SingleThreadTaskRunner> task_runner,
+      const std::string& device_path);
 
   // DeviceMonitorWin::Observer implementation:
   void OnDeviceAdded(const std::string& device_path) override;
   void OnDeviceRemoved(const std::string& device_path) override;
 
   // Tries to open the device read-write and falls back to read-only.
-  base::win::ScopedHandle OpenDevice(const std::string& device_path);
+  static base::win::ScopedHandle OpenDevice(const std::string& device_path);
 
   scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+  scoped_refptr<base::SingleThreadTaskRunner> file_task_runner_;
   ScopedObserver<DeviceMonitorWin, DeviceMonitorWin::Observer> device_observer_;
+  base::WeakPtrFactory<HidServiceWin> weak_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(HidServiceWin);
 };
diff --git a/device/media_transfer_protocol/BUILD.gn b/device/media_transfer_protocol/BUILD.gn
index cd37089..a36c606 100644
--- a/device/media_transfer_protocol/BUILD.gn
+++ b/device/media_transfer_protocol/BUILD.gn
@@ -29,10 +29,16 @@
     "media_transfer_protocol_manager.h",
   ]
 
-  configs += [ "//build/config/linux:dbus" ]
-
   public_deps = [
     ":mtp_file_entry_proto",
     ":mtp_storage_info_proto",
+    "//base",
   ]
+  deps = [
+    "//dbus",
+  ]
+
+  if (is_chromeos) {
+    deps += [ "//chromeos" ]
+  }
 }
diff --git a/device/serial/BUILD.gn b/device/serial/BUILD.gn
index 1382604..099b7ade 100644
--- a/device/serial/BUILD.gn
+++ b/device/serial/BUILD.gn
@@ -43,12 +43,18 @@
 
   public_deps = [
     ":serial_mojo",
+    "//base",
   ]
-  deps = []
+  deps = [
+    "//third_party/mojo/src/mojo/public/cpp/system",
+  ]
 
   if (is_linux) {
     deps += [ "//device/udev_linux" ]
   }
+  if (is_chromeos) {
+    deps += [ "//chromeos" ]
+  }
 }
 
 # GYP version: device/serial/serial.gyp:device_serial_test_util
diff --git a/device/usb/BUILD.gn b/device/usb/BUILD.gn
index d4be7cf..9dcd29a 100644
--- a/device/usb/BUILD.gn
+++ b/device/usb/BUILD.gn
@@ -36,6 +36,7 @@
     ":usb_device_ids",
     "//base",
     "//base/third_party/dynamic_annotations",
+    "//device/core",
     "//net",
     "//third_party/libusb",
   ]
@@ -43,6 +44,9 @@
   if (is_linux) {
     deps += [ "//device/udev_linux" ]
   }
+  if (is_chromeos) {
+    deps += [ "//chromeos" ]
+  }
 }
 
 action("usb_device_ids") {
diff --git a/device/vibration/BUILD.gn b/device/vibration/BUILD.gn
index d2de6ab..fe5a19d 100644
--- a/device/vibration/BUILD.gn
+++ b/device/vibration/BUILD.gn
@@ -33,6 +33,7 @@
       "//device/vibration/android",
       "//device/vibration/android:vibration_jni_headers",
     ]
+    allow_circular_includes_from = [ "//device/vibration/android" ]
   }
 }
 
diff --git a/device/vibration/android/BUILD.gn b/device/vibration/android/BUILD.gn
index 5d9aa22a..ec6ab6c 100644
--- a/device/vibration/android/BUILD.gn
+++ b/device/vibration/android/BUILD.gn
@@ -16,6 +16,8 @@
   defines = [ "DEVICE_VIBRATION_IMPLEMENTATION" ]
 
   deps = [
+    "//base",
+
     # vibration_jni_registrar.cc includes a header from device/vibration
     # that includes a mojo-generated header file.  Make sure the header
     # is generated before vibration_jni_registrar.cc is compiled.
diff --git a/extensions/BUILD.gn b/extensions/BUILD.gn
index b1e69d27..5280a429 100644
--- a/extensions/BUILD.gn
+++ b/extensions/BUILD.gn
@@ -70,6 +70,8 @@
   sources = rebase_path(extensions_gypi_values.extensions_test_support_sources,
                         ".",
                         "//extensions")
+  # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+  configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
 
   deps = [
     ":extensions_resources",
@@ -90,10 +92,6 @@
     "//content/public/browser",
     "//extensions/common/api/cast_channel:cast_channel_proto",
   ]
-
-  if (is_win) {
-    cflags = [ "/wd4267" ]  # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
-  }
 }
 
 # GYP version: //extensions/extensions.gyp:extensions_shell_and_test_pak
@@ -214,6 +212,13 @@
     "//third_party/mojo/src/mojo/public/cpp/bindings",
   ]
 
+  if (is_chromeos) {
+    sources += rebase_path(
+            extensions_tests_gypi_values.extensions_browsertests_sources_chromeos,
+            ".",
+            "//extensions")
+  }
+
   if (is_win) {
     deps += [ "//base/allocator" ]
   }
diff --git a/extensions/browser/BUILD.gn b/extensions/browser/BUILD.gn
index a2b49b8..4659457 100644
--- a/extensions/browser/BUILD.gn
+++ b/extensions/browser/BUILD.gn
@@ -30,6 +30,9 @@
     "//third_party/re2",
   ]
 
+  # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+  configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
+
   if (enable_extensions) {
     # Includes all API implementations and the ExtensionsApiClient
     # interface. Moving an API from src/chrome to src/extensions implies
@@ -97,8 +100,4 @@
       }
     }
   }
-
-  if (is_win) {
-    cflags = [ "/wd4267" ]  # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
-  }
 }
diff --git a/extensions/browser/api/hid/hid_apitest.cc b/extensions/browser/api/hid/hid_apitest.cc
index c29c42195..63012a106 100644
--- a/extensions/browser/api/hid/hid_apitest.cc
+++ b/extensions/browser/api/hid/hid_apitest.cc
@@ -58,7 +58,7 @@
   void PlatformWrite(scoped_refptr<net::IOBuffer> buffer,
                      size_t size,
                      const WriteCallback& callback) override {
-    const char kExpected[] = "This is a HID output report.";
+    const char kExpected[] = "o-report";  // 8 bytes
     bool result = false;
     if (size == sizeof(kExpected)) {
       uint8_t report_id = buffer->data()[0];
diff --git a/extensions/browser/api/networking_config/networking_config_service.cc b/extensions/browser/api/networking_config/networking_config_service.cc
index 54a8ecd..f2d655b 100644
--- a/extensions/browser/api/networking_config/networking_config_service.cc
+++ b/extensions/browser/api/networking_config/networking_config_service.cc
@@ -3,10 +3,16 @@
 // found in the LICENSE file.
 
 #include <algorithm>
+#include <vector>
 
 #include "base/lazy_instance.h"
+#include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
+#include "chromeos/network/network_handler.h"
+#include "chromeos/network/network_state.h"
+#include "chromeos/network/network_state_handler.h"
 #include "extensions/browser/api/networking_config/networking_config_service.h"
+#include "extensions/common/api/networking_config.h"
 
 namespace extensions {
 
@@ -38,9 +44,12 @@
 }
 
 NetworkingConfigService::NetworkingConfigService(
+    content::BrowserContext* browser_context,
     scoped_ptr<EventDelegate> event_delegate,
     ExtensionRegistry* extension_registry)
-    : registry_observer_(this), event_delegate_(event_delegate.Pass()) {
+    : browser_context_(browser_context),
+      registry_observer_(this),
+      event_delegate_(event_delegate.Pass()) {
   registry_observer_.Add(extension_registry);
 }
 
@@ -109,4 +118,34 @@
   authentication_result_ = authentication_result;
 }
 
+bool NetworkingConfigService::DispatchPortalDetectedEvent(
+    std::string extension_id,
+    std::string guid) {
+  EventRouter* eventRouter = EventRouter::Get(browser_context_);
+  const chromeos::NetworkState* network = chromeos::NetworkHandler::Get()
+                                              ->network_state_handler()
+                                              ->GetNetworkStateFromGuid(guid);
+  if (!network)
+    return false;
+
+  // Populate the NetworkInfo object.
+  extensions::core_api::networking_config::NetworkInfo network_info;
+  network_info.type =
+      extensions::core_api::networking_config::NETWORK_TYPE_WIFI;
+  const std::vector<uint8_t>& raw_ssid = network->raw_ssid();
+  std::string hex_ssid =
+      base::HexEncode(vector_as_array(&raw_ssid), raw_ssid.size());
+  network_info.hex_ssid = make_scoped_ptr(new std::string(hex_ssid));
+  network_info.ssid = make_scoped_ptr(new std::string(network->name()));
+  network_info.guid = make_scoped_ptr(new std::string(network->guid()));
+  scoped_ptr<base::ListValue> results =
+      extensions::core_api::networking_config::OnCaptivePortalDetected::Create(
+          network_info);
+  scoped_ptr<Event> event(new Event(extensions::core_api::networking_config::
+                                        OnCaptivePortalDetected::kEventName,
+                                    results.Pass()));
+  eventRouter->DispatchEventToExtension(extension_id, event.Pass());
+  return true;
+}
+
 }  // namespace extensions
diff --git a/extensions/browser/api/networking_config/networking_config_service.h b/extensions/browser/api/networking_config/networking_config_service.h
index 236b992..07696c51 100644
--- a/extensions/browser/api/networking_config/networking_config_service.h
+++ b/extensions/browser/api/networking_config/networking_config_service.h
@@ -11,6 +11,7 @@
 #include "base/memory/scoped_ptr.h"
 #include "base/scoped_observer.h"
 #include "components/keyed_service/core/keyed_service.h"
+#include "content/public/browser/browser_context.h"
 #include "extensions/browser/event_router.h"
 #include "extensions/browser/extension_registry.h"
 #include "extensions/browser/extension_registry_observer.h"
@@ -49,7 +50,8 @@
   };
 
   // Note: |extension_registry| must outlive this class.
-  NetworkingConfigService(scoped_ptr<EventDelegate> event_delegate,
+  NetworkingConfigService(content::BrowserContext* browser_context,
+                          scoped_ptr<EventDelegate> event_delegate,
                           ExtensionRegistry* extension_registry);
   ~NetworkingConfigService() override;
 
@@ -90,7 +92,11 @@
   void SetAuthenticationResult(
       const AuthenticationResult& authentication_result);
 
+  bool DispatchPortalDetectedEvent(std::string extension_id, std::string guid);
+
  private:
+  content::BrowserContext* browser_context_;
+
   AuthenticationResult authentication_result_;
 
   ScopedObserver<ExtensionRegistry, ExtensionRegistryObserver>
diff --git a/extensions/browser/api/networking_config/networking_config_service_chromeos_unittest.cc b/extensions/browser/api/networking_config/networking_config_service_chromeos_unittest.cc
index 9990d74b..c7b62347 100644
--- a/extensions/browser/api/networking_config/networking_config_service_chromeos_unittest.cc
+++ b/extensions/browser/api/networking_config/networking_config_service_chromeos_unittest.cc
@@ -47,7 +47,8 @@
     scoped_ptr<MockEventDelegate> mock_event_delegate =
         scoped_ptr<MockEventDelegate>(new MockEventDelegate());
     service_ = scoped_ptr<NetworkingConfigService>(new NetworkingConfigService(
-        mock_event_delegate.Pass(), extension_registry_.get()));
+        browser_context(), mock_event_delegate.Pass(),
+        extension_registry_.get()));
     DCHECK(service_);
   }
 
diff --git a/extensions/browser/api/networking_config/networking_config_service_factory.cc b/extensions/browser/api/networking_config/networking_config_service_factory.cc
index 62bc8f17..0c1c82b 100644
--- a/extensions/browser/api/networking_config/networking_config_service_factory.cc
+++ b/extensions/browser/api/networking_config/networking_config_service_factory.cc
@@ -71,7 +71,7 @@
 KeyedService* NetworkingConfigServiceFactory::BuildServiceInstanceFor(
     content::BrowserContext* context) const {
   return new NetworkingConfigService(
-      make_scoped_ptr(new DefaultEventDelegate(context)),
+      context, make_scoped_ptr(new DefaultEventDelegate(context)),
       ExtensionRegistry::Get(context));
 }
 
diff --git a/extensions/browser/api/runtime/runtime_api.cc b/extensions/browser/api/runtime/runtime_api.cc
index 51e8ec9b..6f0716a 100644
--- a/extensions/browser/api/runtime/runtime_api.cc
+++ b/extensions/browser/api/runtime/runtime_api.cc
@@ -46,6 +46,7 @@
 
 const char kNoBackgroundPageError[] = "You do not have a background page.";
 const char kPageLoadError[] = "Background page failed to load.";
+const char kFailedToCreateOptionsPage[] = "Could not create an options page.";
 const char kInstallId[] = "id";
 const char kInstallReason[] = "reason";
 const char kInstallReasonChromeUpdate[] = "chrome_update";
@@ -268,6 +269,10 @@
   return delegate_->RestartDevice(error_message);
 }
 
+bool RuntimeAPI::OpenOptionsPage(const Extension* extension) {
+  return delegate_->OpenOptionsPage(extension);
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 
 // static
@@ -425,6 +430,13 @@
   }
 }
 
+ExtensionFunction::ResponseAction RuntimeOpenOptionsPageFunction::Run() {
+  RuntimeAPI* api = RuntimeAPI::GetFactoryInstance()->Get(browser_context());
+  return RespondNow(api->OpenOptionsPage(extension())
+                        ? NoArguments()
+                        : Error(kFailedToCreateOptionsPage));
+}
+
 ExtensionFunction::ResponseAction RuntimeSetUninstallURLFunction::Run() {
   std::string url_string;
   EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &url_string));
diff --git a/extensions/browser/api/runtime/runtime_api.h b/extensions/browser/api/runtime/runtime_api.h
index e3305219..901bab4a 100644
--- a/extensions/browser/api/runtime/runtime_api.h
+++ b/extensions/browser/api/runtime/runtime_api.h
@@ -64,6 +64,7 @@
   void OpenURL(const GURL& uninstall_url);
   bool GetPlatformInfo(core_api::runtime::PlatformInfo* info);
   bool RestartDevice(std::string* error_message);
+  bool OpenOptionsPage(const Extension* extension);
 
  private:
   friend class BrowserContextKeyedAPIFactory<RuntimeAPI>;
@@ -162,6 +163,15 @@
   void OnPageLoaded(ExtensionHost*);
 };
 
+class RuntimeOpenOptionsPageFunction : public UIThreadExtensionFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION("runtime.openOptionsPage", RUNTIME_OPENOPTIONSPAGE)
+
+ protected:
+  ~RuntimeOpenOptionsPageFunction() override {}
+  ResponseAction Run() override;
+};
+
 class RuntimeSetUninstallURLFunction : public UIThreadExtensionFunction {
  public:
   DECLARE_EXTENSION_FUNCTION("runtime.setUninstallURL", RUNTIME_SETUNINSTALLURL)
diff --git a/extensions/browser/api/runtime/runtime_api_delegate.cc b/extensions/browser/api/runtime/runtime_api_delegate.cc
index 3ead8b62..b012c74 100644
--- a/extensions/browser/api/runtime/runtime_api_delegate.cc
+++ b/extensions/browser/api/runtime/runtime_api_delegate.cc
@@ -13,4 +13,8 @@
     : success(success), response(response), version(version) {
 }
 
+bool RuntimeAPIDelegate::OpenOptionsPage(const Extension* extension) {
+  return false;
+}
+
 }  // namespace extensions
diff --git a/extensions/browser/api/runtime/runtime_api_delegate.h b/extensions/browser/api/runtime/runtime_api_delegate.h
index 98b4963..a704c2f 100644
--- a/extensions/browser/api/runtime/runtime_api_delegate.h
+++ b/extensions/browser/api/runtime/runtime_api_delegate.h
@@ -70,6 +70,11 @@
   // Request a restart of the host device. Returns false iff the device
   // will not be restarted.
   virtual bool RestartDevice(std::string* error_message) = 0;
+
+  // Open |extension|'s options page, if it has one. Returns true if an
+  // options page was opened, false otherwise. See the docs of the
+  // chrome.runtime.openOptionsPage function for the gritty details.
+  virtual bool OpenOptionsPage(const Extension* extension);
 };
 
 }  // namespace extensions
diff --git a/extensions/browser/api/runtime/runtime_apitest.cc b/extensions/browser/api/runtime/runtime_apitest.cc
index 7f01ef3..49614d08 100644
--- a/extensions/browser/api/runtime/runtime_apitest.cc
+++ b/extensions/browser/api/runtime/runtime_apitest.cc
@@ -43,6 +43,14 @@
 
 namespace extensions {
 
+IN_PROC_BROWSER_TEST_F(ExtensionApiTest, ChromeRuntimeOpenOptionsPage) {
+  ASSERT_TRUE(RunExtensionTest("runtime/open_options_page"));
+}
+
+IN_PROC_BROWSER_TEST_F(ExtensionApiTest, ChromeRuntimeOpenOptionsPageError) {
+  ASSERT_TRUE(RunExtensionTest("runtime/open_options_page_error"));
+}
+
 IN_PROC_BROWSER_TEST_F(ExtensionApiTest, ChromeRuntimeGetPlatformInfo) {
   scoped_ptr<base::Value> result(
       extension_function_test_utils::RunFunctionAndReturnSingleResult(
diff --git a/extensions/browser/api/vpn_provider/vpn_provider_api.cc b/extensions/browser/api/vpn_provider/vpn_provider_api.cc
index a7a0d82..194631c 100644
--- a/extensions/browser/api/vpn_provider/vpn_provider_api.cc
+++ b/extensions/browser/api/vpn_provider/vpn_provider_api.cc
@@ -24,15 +24,85 @@
 
 const char kCIDRSeperator[] = "/";
 
+bool CheckIPCIDRSanity(const std::string& value, bool cidr, bool ipv6) {
+  int dots = ipv6 ? 0 : 3;
+  int sep = cidr ? 1 : 0;
+  int colon = ipv6 ? 7 : 0;
+  bool hex_allowed = ipv6;
+  int counter = 0;
+
+  for (const auto& elem : value) {
+    if (IsAsciiDigit(elem)) {
+      counter++;
+      continue;
+    }
+    if (elem == '.') {
+      if (!dots)
+        return false;
+      dots--;
+    } else if (elem == kCIDRSeperator[0]) {
+      if (!sep || dots || colon == 7 || !counter)
+        return false;
+      // Separator observed, no more dots and colons, only digits are allowed
+      // after observing separator. So setting hex_allowed to false.
+      sep--;
+      counter = 0;
+      colon = 0;
+      hex_allowed = false;
+    } else if (elem == ':') {
+      if (!colon)
+        return false;
+      colon--;
+    } else if (!hex_allowed || !IsHexDigit(elem)) {
+      return false;
+    } else {
+      counter++;
+    }
+  }
+  return !sep && !dots && (colon < 7) && counter;
+}
+
+bool CheckIPCIDRSanityList(const std::vector<std::string>& list,
+                           bool cidr,
+                           bool ipv6) {
+  for (const auto& address : list) {
+    if (!CheckIPCIDRSanity(address, cidr, ipv6)) {
+      return false;
+    }
+  }
+  return true;
+}
+
 void ConvertParameters(const api_vpn::Parameters& parameters,
                        base::DictionaryValue* parameter_value,
                        std::string* error) {
-  std::vector<std::string> cidr_parts;
-  if (Tokenize(parameters.address, kCIDRSeperator, &cidr_parts) != 2) {
-    *error = "Invalid CIDR address.";
+  if (!CheckIPCIDRSanity(parameters.address, true /* CIDR */,
+                         false /*IPV4 */)) {
+    *error = "Address CIDR sanity check failed.";
     return;
   }
 
+  if (!CheckIPCIDRSanityList(parameters.exclusion_list, true /* CIDR */,
+                             false /*IPV4 */)) {
+    *error = "Exclusion list CIDR sanity check failed.";
+    return;
+  }
+
+  if (!CheckIPCIDRSanityList(parameters.inclusion_list, true /* CIDR */,
+                             false /*IPV4 */)) {
+    *error = "Inclusion list CIDR sanity check failed.";
+    return;
+  }
+
+  if (!CheckIPCIDRSanityList(parameters.dns_servers, false /* Not CIDR */,
+                             false /*IPV4 */)) {
+    *error = "DNS server IP sanity check failed.";
+    return;
+  }
+
+  std::vector<std::string> cidr_parts;
+  CHECK(Tokenize(parameters.address, kCIDRSeperator, &cidr_parts) == 2);
+
   parameter_value->SetStringWithoutPathExpansion(
       shill::kAddressParameterThirdPartyVpn, cidr_parts[0]);
 
@@ -40,8 +110,12 @@
       shill::kSubnetPrefixParameterThirdPartyVpn, cidr_parts[1]);
 
   parameter_value->SetStringWithoutPathExpansion(
-      shill::kBypassTunnelForIpParameterThirdPartyVpn,
-      JoinString(parameters.bypass_tunnel_for_ip, shill::kIPDelimiter));
+      shill::kExclusionListParameterThirdPartyVpn,
+      JoinString(parameters.exclusion_list, shill::kIPDelimiter));
+
+  parameter_value->SetStringWithoutPathExpansion(
+      shill::kInclusionListParameterThirdPartyVpn,
+      JoinString(parameters.inclusion_list, shill::kIPDelimiter));
 
   if (parameters.mtu) {
     parameter_value->SetStringWithoutPathExpansion(
diff --git a/extensions/browser/api/vpn_provider/vpn_provider_apitest.cc b/extensions/browser/api/vpn_provider/vpn_provider_apitest.cc
new file mode 100644
index 0000000..ab8259d
--- /dev/null
+++ b/extensions/browser/api/vpn_provider/vpn_provider_apitest.cc
@@ -0,0 +1,94 @@
+// 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/strings/stringprintf.h"
+#include "extensions/browser/api/vpn_provider/vpn_provider_api.h"
+#include "extensions/browser/api_test_utils.h"
+#include "extensions/common/extension.h"
+#include "extensions/common/test_util.h"
+#include "extensions/shell/test/shell_test.h"
+
+using extensions::api_test_utils::RunFunctionAndReturnSingleResult;
+using extensions::api_test_utils::RunFunctionAndReturnError;
+
+namespace extensions {
+
+const char* kErrorMessages[] = {"Address CIDR sanity check failed.",
+                                "DNS server IP sanity check failed.",
+                                "Unauthorized access."};
+
+struct SetParameterTestParams {
+  int err_index;
+  const char* address;
+  const char* dns_server;
+};
+
+const SetParameterTestParams set_parameter_tests[] = {
+    {0, "1+++", ""},              // + not allowed
+    {0, "1", ""},                 // 3 dots and separator missing
+    {0, "1..", ""},               // A dot and separator missing
+    {0, "1...", ""},              // Separator missing
+    {0, "1.../", ""},             // No digit after separator in address
+    {1, "1.../0", ""},            // Address passes sanity check, DNS incorrect
+    {1, "1.../0", "1.../"},       // DNS is not CIDR
+    {2, "1.../0", "1..."},        // Okay
+    {0, ".../", "..."},           // Address has no digits
+    {0, "0.../", "..."},          // Address has no digits post separator
+    {1, "0.../0", "..."},         // Address passes sanity check, DNS incorrect
+    {2, "0.../0", "...0"},        // Okay
+    {0, "1...:::/1279abe", ""},   // : not allowed for ipv4
+    {0, "1.../1279abcde", ""},    // Hex not allowed after separator
+    {0, "1...abcde/1279", ""},    // Hex not allowed in ipv4
+    {1, "1.../1279", ""},         // Address passes sanity check, DNS incorrect
+    {2, "1.../1279", "1..."},     // Okay
+    {0, "1--++", ""},             // + and - not supported
+    {0, "1.1.1.1", ""},           // Missing separator
+    {0, "1.1.1.1/", ""},          // No digits after separator in address
+    {1, "1.1.1.1/1", ""},         // Address passes sanity check, DNS incorrect
+    {2, "1.1.1.1/1", "1.1.1.1"},  // Okay
+    {0, "1.1.1./e", "1.1.1."},    // Hex not okay in ipv4
+    {2, "1.1.1./0", "1.1.1."},    // Okay
+    {1, "1.../1279", "..."},      // No digits in DNS
+    {1, "1.../1279", "e..."},     // Hex not allowed in ipv4
+    {2, "1.../1279", "4..."},     // Okay
+};
+
+class VpnProviderApiTest
+    : public AppShellTest,
+      public testing::WithParamInterface<const SetParameterTestParams*> {};
+
+IN_PROC_BROWSER_TEST_P(VpnProviderApiTest, SetParametersFunction) {
+  scoped_refptr<extensions::VpnProviderSetParametersFunction>
+      set_parameter_function(
+          new extensions::VpnProviderSetParametersFunction());
+  scoped_refptr<Extension> empty_extension = test_util::CreateEmptyExtension();
+
+  set_parameter_function->set_extension(empty_extension.get());
+  set_parameter_function->set_has_callback(true);
+
+  const std::string args =
+      "["
+      "  {"
+      "    \"address\": \"%s\","
+      "    \"exclusionList\": [],"
+      "    \"inclusionList\": [],"
+      "    \"dnsServers\": [\"%s\"]"
+      "  }"
+      "]";
+
+  EXPECT_EQ(kErrorMessages[GetParam()->err_index],
+            RunFunctionAndReturnError(
+                set_parameter_function.get(),
+                base::StringPrintf(args.c_str(), GetParam()->address,
+                                   GetParam()->dns_server),
+                browser_context()));
+}
+
+INSTANTIATE_TEST_CASE_P(
+    SetParameterTestParams,
+    VpnProviderApiTest,
+    testing::Range(&set_parameter_tests[0],
+                   &set_parameter_tests[arraysize(set_parameter_tests)]));
+
+}  //  namespace extensions
diff --git a/extensions/browser/api/vpn_provider/vpn_service.cc b/extensions/browser/api/vpn_provider/vpn_service.cc
index 2ebc3ff..7a51aac7 100644
--- a/extensions/browser/api/vpn_provider/vpn_service.cc
+++ b/extensions/browser/api/vpn_provider/vpn_service.cc
@@ -9,6 +9,7 @@
 
 #include "base/bind.h"
 #include "base/bind_helpers.h"
+#include "base/guid.h"
 #include "base/location.h"
 #include "base/logging.h"
 #include "base/stl_util.h"
@@ -263,7 +264,7 @@
       continue;
     }
 
-    network_configuration_handler_->GetProperties(
+    network_configuration_handler_->GetShillProperties(
         iter->path(), base::Bind(&VpnService::OnGetPropertiesSuccess,
                                  weak_factory_.GetWeakPtr()),
         base::Bind(&VpnService::OnGetPropertiesFailure,
@@ -316,7 +317,12 @@
   properties.SetStringWithoutPathExpansion(shill::kConfigurationNameProperty,
                                            configuration_name);
 
-  network_configuration_handler_->CreateConfiguration(
+  // Note: This will not create an entry in |policy_util|. TODO(pneubeck):
+  // Determine the correct thing to do here, crbug.com/459278.
+  std::string guid = base::GenerateGUID();
+  properties.SetStringWithoutPathExpansion(shill::kGuidProperty, guid);
+
+  network_configuration_handler_->CreateShillConfiguration(
       properties, NetworkConfigurationObserver::SOURCE_EXTENSION_INSTALL,
       base::Bind(&VpnService::OnCreateConfigurationSuccess,
                  weak_factory_.GetWeakPtr(), success, configuration),
diff --git a/extensions/browser/deferred_start_render_host.h b/extensions/browser/deferred_start_render_host.h
new file mode 100644
index 0000000..f6810c6
--- /dev/null
+++ b/extensions/browser/deferred_start_render_host.h
@@ -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.
+
+#ifndef EXTENSIONS_BROWSER_DEFERRED_START_RENDER_HOST_H_
+#define EXTENSIONS_BROWSER_DEFERRED_START_RENDER_HOST_H_
+
+namespace extensions {
+
+// A browser component that tracks a renderer. It allows for its renderer
+// startup to be deferred, to throttle resource usage upon profile startup.
+// To be used with ExtensionHostQueue.
+//
+// Note that if BackgroundContents and ExtensionHost are unified
+// (crbug.com/77790), this interface will be no longer needed.
+class DeferredStartRenderHost {
+ public:
+  virtual ~DeferredStartRenderHost() {}
+
+  // DO NOT CALL THIS unless you're implementing an ExtensionHostQueue.
+  // Called by the ExtensionHostQueue to create the RenderView.
+  virtual void CreateRenderViewNow() = 0;
+};
+
+}  // namespace extensions
+
+#endif  // EXTENSIONS_BROWSER_DEFERRED_START_RENDER_HOST_H_
diff --git a/extensions/browser/extension_function_histogram_value.h b/extensions/browser/extension_function_histogram_value.h
index f804b4d4d..c9a7c07d 100644
--- a/extensions/browser/extension_function_histogram_value.h
+++ b/extensions/browser/extension_function_histogram_value.h
@@ -1021,6 +1021,13 @@
   FILEMANAGERPRIVATE_ISUMAENABLED,
   WEBVIEWINTERNAL_SETALLOWSCALING,
   PLATFORMKEYSINTERNAL_GETPUBLICKEY,
+  RUNTIME_OPENOPTIONSPAGE,
+  AUDIOMODEM_TRANSMIT,
+  AUDIOMODEM_STOPTRANSMIT,
+  AUDIOMODEM_RECEIVE,
+  AUDIOMODEM_STOPRECEIVE,
+  WEBRTCLOGGINGPRIVATE_STORE,
+  WEBRTCLOGGINGPRIVATE_UPLOADSTORED,
   // Last entry: Add new entries above and ensure to update
   // tools/metrics/histograms/histograms.xml.
   ENUM_BOUNDARY
diff --git a/extensions/browser/extension_host.cc b/extensions/browser/extension_host.cc
index a69cffb..3d18bf9 100644
--- a/extensions/browser/extension_host.cc
+++ b/extensions/browser/extension_host.cc
@@ -4,13 +4,7 @@
 
 #include "extensions/browser/extension_host.h"
 
-#include <list>
-
-#include "base/bind.h"
 #include "base/logging.h"
-#include "base/memory/singleton.h"
-#include "base/memory/weak_ptr.h"
-#include "base/message_loop/message_loop.h"
 #include "base/metrics/field_trial.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/strings/string_util.h"
@@ -30,6 +24,7 @@
 #include "extensions/browser/extension_error.h"
 #include "extensions/browser/extension_host_delegate.h"
 #include "extensions/browser/extension_host_observer.h"
+#include "extensions/browser/extension_host_queue.h"
 #include "extensions/browser/extension_system.h"
 #include "extensions/browser/extensions_browser_client.h"
 #include "extensions/browser/notification_types.h"
@@ -53,66 +48,6 @@
 
 namespace extensions {
 
-// Helper class that rate-limits the creation of renderer processes for
-// ExtensionHosts, to avoid blocking the UI.
-class ExtensionHost::ProcessCreationQueue {
- public:
-  static ProcessCreationQueue* GetInstance() {
-    return Singleton<ProcessCreationQueue>::get();
-  }
-
-  // Add a host to the queue for RenderView creation.
-  void CreateSoon(ExtensionHost* host) {
-    queue_.push_back(host);
-    PostTask();
-  }
-
-  // Remove a host from the queue (in case it's being deleted).
-  void Remove(ExtensionHost* host) {
-    Queue::iterator it = std::find(queue_.begin(), queue_.end(), host);
-    if (it != queue_.end())
-      queue_.erase(it);
-  }
-
- private:
-  friend class Singleton<ProcessCreationQueue>;
-  friend struct DefaultSingletonTraits<ProcessCreationQueue>;
-  ProcessCreationQueue()
-      : pending_create_(false),
-        ptr_factory_(this) {}
-
-  // Queue up a delayed task to process the next ExtensionHost in the queue.
-  void PostTask() {
-    if (!pending_create_) {
-      base::MessageLoop::current()->PostTask(FROM_HERE,
-          base::Bind(&ProcessCreationQueue::ProcessOneHost,
-                     ptr_factory_.GetWeakPtr()));
-      pending_create_ = true;
-    }
-  }
-
-  // Create the RenderView for the next host in the queue.
-  void ProcessOneHost() {
-    pending_create_ = false;
-    if (queue_.empty())
-      return;  // can happen on shutdown
-
-    queue_.front()->CreateRenderViewNow();
-    queue_.pop_front();
-
-    if (!queue_.empty())
-      PostTask();
-  }
-
-  typedef std::list<ExtensionHost*> Queue;
-  Queue queue_;
-  bool pending_create_;
-  base::WeakPtrFactory<ProcessCreationQueue> ptr_factory_;
-};
-
-////////////////
-// ExtensionHost
-
 ExtensionHost::ExtensionHost(const Extension* extension,
                              SiteInstance* site_instance,
                              const GURL& url,
@@ -162,7 +97,7 @@
       content::Details<ExtensionHost>(this));
   FOR_EACH_OBSERVER(ExtensionHostObserver, observer_list_,
                     OnExtensionHostDestroyed(this));
-  ProcessCreationQueue::GetInstance()->Remove(this);
+  delegate_->GetExtensionHostQueue()->Remove(this);
   // Immediately stop observing |host_contents_| because its destruction events
   // (like DidStopLoading, it turns out) can call back into ExtensionHost
   // re-entrantly, when anything declared after |host_contents_| has already
@@ -190,17 +125,10 @@
     // to defer.
     CreateRenderViewNow();
   } else {
-    ProcessCreationQueue::GetInstance()->CreateSoon(this);
+    delegate_->GetExtensionHostQueue()->Add(this);
   }
 }
 
-void ExtensionHost::Close() {
-  content::NotificationService::current()->Notify(
-      extensions::NOTIFICATION_EXTENSION_HOST_VIEW_SHOULD_CLOSE,
-      content::Source<BrowserContext>(browser_context_),
-      content::Details<ExtensionHost>(this));
-}
-
 void ExtensionHost::CreateRenderViewNow() {
   LoadInitialURL();
   if (IsBackgroundPage()) {
@@ -220,6 +148,13 @@
   }
 }
 
+void ExtensionHost::Close() {
+  content::NotificationService::current()->Notify(
+      extensions::NOTIFICATION_EXTENSION_HOST_VIEW_SHOULD_CLOSE,
+      content::Source<BrowserContext>(browser_context_),
+      content::Details<ExtensionHost>(this));
+}
+
 void ExtensionHost::AddObserver(ExtensionHostObserver* observer) {
   observer_list_.AddObserver(observer);
 }
diff --git a/extensions/browser/extension_host.h b/extensions/browser/extension_host.h
index d7502593..76daa00c 100644
--- a/extensions/browser/extension_host.h
+++ b/extensions/browser/extension_host.h
@@ -17,6 +17,7 @@
 #include "content/public/browser/notification_registrar.h"
 #include "content/public/browser/web_contents_delegate.h"
 #include "content/public/browser/web_contents_observer.h"
+#include "extensions/browser/deferred_start_render_host.h"
 #include "extensions/browser/extension_function_dispatcher.h"
 #include "extensions/common/stack_frame.h"
 #include "extensions/common/view_type.h"
@@ -34,6 +35,7 @@
 class Extension;
 class ExtensionHostDelegate;
 class ExtensionHostObserver;
+class ExtensionHostQueue;
 class WindowController;
 
 // This class is the browser component of an extension component's RenderView.
@@ -43,13 +45,12 @@
 //
 // If you are adding code that only affects visible extension views (and not
 // invisible background pages) you should add it to ExtensionViewHost.
-class ExtensionHost : public content::WebContentsDelegate,
+class ExtensionHost : public DeferredStartRenderHost,
+                      public content::WebContentsDelegate,
                       public content::WebContentsObserver,
                       public ExtensionFunctionDispatcher::Delegate,
                       public content::NotificationObserver {
  public:
-  class ProcessCreationQueue;
-
   ExtensionHost(const Extension* extension,
                 content::SiteInstance* site_instance,
                 const GURL& url, ViewType host_type);
@@ -97,7 +98,7 @@
   // finished.
   void OnNetworkRequestDone(uint64 request_id);
 
-  // content::WebContentsObserver
+  // content::WebContentsObserver:
   bool OnMessageReceived(const IPC::Message& message) override;
   void RenderViewCreated(content::RenderViewHost* render_view_host) override;
   void RenderViewDeleted(content::RenderViewHost* render_view_host) override;
@@ -106,7 +107,7 @@
   void DocumentAvailableInMainFrame() override;
   void DidStopLoading(content::RenderViewHost* render_view_host) override;
 
-  // content::WebContentsDelegate
+  // content::WebContentsDelegate:
   content::JavaScriptDialogManager* GetJavaScriptDialogManager(
       content::WebContents* source) override;
   void AddNewContents(content::WebContents* source,
@@ -125,7 +126,7 @@
                                   content::MediaStreamType type) override;
   bool IsNeverVisible(content::WebContents* web_contents) override;
 
-  // content::NotificationObserver
+  // content::NotificationObserver:
   void Observe(int type,
                const content::NotificationSource& source,
                const content::NotificationDetails& details) override;
@@ -144,10 +145,8 @@
   virtual bool IsBackgroundPage() const;
 
  private:
-  friend class ProcessCreationQueue;
-
-  // Actually create the RenderView for this host. See CreateRenderViewSoon.
-  void CreateRenderViewNow();
+  // DeferredStartRenderHost:
+  void CreateRenderViewNow() override;
 
   // Message handlers.
   void OnRequest(const ExtensionHostMsg_Request_Params& params);
diff --git a/extensions/browser/extension_host_delegate.h b/extensions/browser/extension_host_delegate.h
index 71c0990..86726ff0 100644
--- a/extensions/browser/extension_host_delegate.h
+++ b/extensions/browser/extension_host_delegate.h
@@ -22,6 +22,7 @@
 namespace extensions {
 class Extension;
 class ExtensionHost;
+class ExtensionHostQueue;
 
 // A delegate to support functionality that cannot exist in the extensions
 // module. This is not an inner class of ExtensionHost to allow it to be forward
@@ -64,6 +65,10 @@
                                           const GURL& security_origin,
                                           content::MediaStreamType type,
                                           const Extension* extension) = 0;
+
+  // Returns the ExtensionHostQueue implementation to use for creating
+  // ExtensionHost renderers.
+  virtual ExtensionHostQueue* GetExtensionHostQueue() const = 0;
 };
 
 }  // namespace extensions
diff --git a/extensions/browser/extension_host_queue.h b/extensions/browser/extension_host_queue.h
new file mode 100644
index 0000000..420800b
--- /dev/null
+++ b/extensions/browser/extension_host_queue.h
@@ -0,0 +1,28 @@
+// 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 EXTENSIONS_BROWSER_EXTENSION_HOST_QUEUE_H_
+#define EXTENSIONS_BROWSER_EXTENSION_HOST_QUEUE_H_
+
+namespace extensions {
+
+class DeferredStartRenderHost;
+
+// An interface for a queue of ExtensionHosts waiting for initialization.
+// This is used to implement different throttling strategies.
+class ExtensionHostQueue {
+ public:
+  virtual ~ExtensionHostQueue() {}
+
+  // Adds a host to the queue for RenderView creation.
+  virtual void Add(DeferredStartRenderHost* host) = 0;
+
+  // Removes a host from the queue (for example, it may be deleted before
+  // having a chance to start).
+  virtual void Remove(DeferredStartRenderHost* host) = 0;
+};
+
+}  // namespace extensions
+
+#endif  // EXTENSIONS_BROWSER_EXTENSION_HOST_QUEUE_H_
diff --git a/extensions/browser/guest_view/extension_options/extension_options_guest.cc b/extensions/browser/guest_view/extension_options/extension_options_guest.cc
index 303ee902..f2db303 100644
--- a/extensions/browser/guest_view/extension_options/extension_options_guest.cc
+++ b/extensions/browser/guest_view/extension_options/extension_options_guest.cc
@@ -162,8 +162,9 @@
 
 void ExtensionOptionsGuest::OnPreferredSizeChanged(const gfx::Size& pref_size) {
   extension_options_internal::PreferredSizeChangedOptions options;
-  options.width = pref_size.width();
-  options.height = pref_size.height();
+  // Convert the size from physical pixels to logical pixels.
+  options.width = PhysicalPixelsToLogicalPixels(pref_size.width());
+  options.height = PhysicalPixelsToLogicalPixels(pref_size.height());
   DispatchEventToView(new GuestViewBase::Event(
       extension_options_internal::OnPreferredSizeChanged::kEventName,
       options.ToValue()));
diff --git a/extensions/browser/guest_view/extension_view/extension_view_constants.cc b/extensions/browser/guest_view/extension_view/extension_view_constants.cc
index c2e182f..ba40c3e 100644
--- a/extensions/browser/guest_view/extension_view/extension_view_constants.cc
+++ b/extensions/browser/guest_view/extension_view/extension_view_constants.cc
@@ -10,6 +10,10 @@
 extern const char kAPINamespace[] = "extensionViewInternal";
 
 // Attributes.
+const char kAttributeExtension[] = "extension";
 const char kAttributeSrc[] = "src";
 
+// Events.
+const char kEventLoadCommit[] = "extensionViewInternal.onLoadCommit";
+
 }  // namespace extensionview
diff --git a/extensions/browser/guest_view/extension_view/extension_view_constants.h b/extensions/browser/guest_view/extension_view/extension_view_constants.h
index 9258375..e2e0780 100644
--- a/extensions/browser/guest_view/extension_view/extension_view_constants.h
+++ b/extensions/browser/guest_view/extension_view/extension_view_constants.h
@@ -11,8 +11,12 @@
 extern const char kAPINamespace[];
 
 // Attributes.
+extern const char kAttributeExtension[];
 extern const char kAttributeSrc[];
 
+// Events.
+extern const char kEventLoadCommit[];
+
 }  // namespace extensionview
 
 #endif  // EXTENSIONS_BROWSER_GUEST_VIEW_EXTENSION_VIEW_EXTENSION_OPTIONS_CONSTANTS_H_
diff --git a/extensions/browser/guest_view/extension_view/extension_view_guest.cc b/extensions/browser/guest_view/extension_view/extension_view_guest.cc
index 952ec1331..e9c309a 100644
--- a/extensions/browser/guest_view/extension_view/extension_view_guest.cc
+++ b/extensions/browser/guest_view/extension_view/extension_view_guest.cc
@@ -5,6 +5,8 @@
 #include "extensions/browser/guest_view/extension_view/extension_view_guest.h"
 
 #include "base/metrics/user_metrics.h"
+#include "base/strings/string_util.h"
+#include "components/crx_file/id_util.h"
 #include "content/public/browser/render_process_host.h"
 #include "content/public/common/result_codes.h"
 #include "extensions/browser/api/extensions_api_client.h"
@@ -40,11 +42,18 @@
 
 void ExtensionViewGuest::NavigateGuest(const std::string& src,
                                        bool force_navigation) {
-  if (src.empty())
-    return;
+  GURL url = extension_url_.Resolve(src);
 
-  GURL url(src);
-  if (!url.is_valid() && !force_navigation && (url == view_page_))
+  // If the URL is not valid, about:blank, or the same origin as the extension,
+  // then navigate to about:blank.
+  bool url_not_allowed = (url != GURL(url::kAboutBlankURL)) &&
+      (url.GetOrigin() != extension_url_.GetOrigin());
+  if (!url.is_valid() || url_not_allowed) {
+    NavigateGuest(url::kAboutBlankURL, true /* force_navigation */);
+    return;
+  }
+
+  if (!force_navigation && (view_page_ == url))
     return;
 
   web_contents()->GetRenderProcessHost()->FilterURL(false, &url);
@@ -63,20 +72,27 @@
 void ExtensionViewGuest::CreateWebContents(
     const base::DictionaryValue& create_params,
     const WebContentsCreatedCallback& callback) {
-  std::string str;
-  if (!create_params.GetString(extensionview::kAttributeSrc, &str)) {
+  // Gets the extension ID.
+  std::string extension_id;
+  create_params.GetString(extensionview::kAttributeExtension, &extension_id);
+
+  if (!crx_file::id_util::IdIsValid(extension_id)) {
     callback.Run(nullptr);
     return;
   }
 
-  GURL source(str);
-  if (!source.is_valid()) {
+  // Gets the extension URL.
+  extension_url_ =
+      extensions::Extension::GetBaseURLFromExtensionId(extension_id);
+
+  if (!extension_url_.is_valid()) {
     callback.Run(nullptr);
     return;
   }
 
   content::SiteInstance* view_site_instance =
-      content::SiteInstance::CreateForURL(browser_context(), source);
+      content::SiteInstance::CreateForURL(browser_context(),
+                                          extension_url_);
 
   WebContents::CreateParams params(browser_context(), view_site_instance);
   params.guest_delegate = this;
@@ -107,6 +123,38 @@
 }
 
 // content::WebContentsObserver implementation.
+void ExtensionViewGuest::DidCommitProvisionalLoadForFrame(
+    content::RenderFrameHost* render_frame_host,
+    const GURL& url,
+    ui::PageTransition transition_type) {
+  if (render_frame_host->GetParent())
+    return;
+
+  view_page_ = url;
+
+  // Gets chrome-extension://extensionid/ prefix.
+  std::string prefix = url.GetWithEmptyPath().spec();
+  std::string relative_url = url.spec();
+
+  // Removes the prefix.
+  ReplaceFirstSubstringAfterOffset(&relative_url, 0, prefix, "");
+
+  scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue());
+  args->SetString(guestview::kUrl, relative_url);
+  DispatchEventToView(
+      new GuestViewBase::Event(extensionview::kEventLoadCommit, args.Pass()));
+}
+
+void ExtensionViewGuest::DidNavigateMainFrame(
+    const content::LoadCommittedDetails& details,
+    const content::FrameNavigateParams& params) {
+  if (attached() && (params.url.GetOrigin() != view_page_.GetOrigin())) {
+    base::RecordAction(base::UserMetricsAction("BadMessageTerminate_EVG"));
+    web_contents()->GetRenderProcessHost()->Shutdown(
+        content::RESULT_CODE_KILLED_BAD_MESSAGE, false /* wait */);
+  }
+}
+
 bool ExtensionViewGuest::OnMessageReceived(const IPC::Message& message) {
   bool handled = true;
   IPC_BEGIN_MESSAGE_MAP(ExtensionViewGuest, message)
diff --git a/extensions/browser/guest_view/extension_view/extension_view_guest.h b/extensions/browser/guest_view/extension_view/extension_view_guest.h
index 350c20c9..055ee22 100644
--- a/extensions/browser/guest_view/extension_view/extension_view_guest.h
+++ b/extensions/browser/guest_view/extension_view/extension_view_guest.h
@@ -38,6 +38,13 @@
   int GetTaskPrefix() const override;
 
   // content::WebContentsObserver implementation.
+  void DidCommitProvisionalLoadForFrame(
+      content::RenderFrameHost* render_frame_host,
+      const GURL& url,
+      ui::PageTransition transition_type) override;
+  void DidNavigateMainFrame(
+      const content::LoadCommittedDetails& details,
+      const content::FrameNavigateParams& params) override;
   bool OnMessageReceived(const IPC::Message& message) override;
 
  private:
@@ -53,6 +60,7 @@
   scoped_ptr<extensions::ExtensionViewGuestDelegate>
       extension_view_guest_delegate_;
   GURL view_page_;
+  GURL extension_url_;
 
   DISALLOW_COPY_AND_ASSIGN(ExtensionViewGuest);
 };
diff --git a/extensions/browser/guest_view/guest_view_base.cc b/extensions/browser/guest_view/guest_view_base.cc
index 14754dc..58bf893 100644
--- a/extensions/browser/guest_view/guest_view_base.cc
+++ b/extensions/browser/guest_view/guest_view_base.cc
@@ -510,6 +510,18 @@
   WillAttachToEmbedder();
 }
 
+int GuestViewBase::LogicalPixelsToPhysicalPixels(double logical_pixels) {
+  DCHECK(logical_pixels >= 0);
+  double zoom_factor = GetEmbedderZoomFactor();
+  return lround(logical_pixels * zoom_factor);
+}
+
+double GuestViewBase::PhysicalPixelsToLogicalPixels(int physical_pixels) {
+  DCHECK(physical_pixels >= 0);
+  double zoom_factor = GetEmbedderZoomFactor();
+  return physical_pixels / zoom_factor;
+}
+
 void GuestViewBase::DidStopLoading(content::RenderViewHost* render_view_host) {
   if (IsPreferredSizeModeEnabled()) {
     render_view_host->EnablePreferredSizeMode();
@@ -717,6 +729,20 @@
   callback.Run(guest_web_contents);
 }
 
+double GuestViewBase::GetEmbedderZoomFactor() {
+  if (!embedder_web_contents())
+    return 1.0;
+
+  auto zoom_controller =
+      ui_zoom::ZoomController::FromWebContents(embedder_web_contents());
+  if (!zoom_controller)
+    return 1.0;
+
+  double zoom_factor =
+      content::ZoomLevelToZoomFactor(zoom_controller->GetZoomLevel());
+  return zoom_factor;
+}
+
 void GuestViewBase::SetUpSizing(const base::DictionaryValue& params) {
   // Read the autosize parameters passed in from the embedder.
   bool auto_size_enabled = false;
@@ -734,10 +760,13 @@
 
   // Set the normal size to the element size so that the guestview will fit the
   // element initially if autosize is disabled.
-  int normal_height = 0;
-  int normal_width = 0;
-  params.GetInteger(guestview::kElementHeight, &normal_height);
-  params.GetInteger(guestview::kElementWidth, &normal_width);
+  double element_height = 0.0;
+  double element_width = 0.0;
+  params.GetDouble(guestview::kElementHeight, &element_height);
+  params.GetDouble(guestview::kElementWidth, &element_width);
+  // Convert the element size from logical pixels to physical pixels.
+  int normal_height = LogicalPixelsToPhysicalPixels(element_height);
+  int normal_width = LogicalPixelsToPhysicalPixels(element_width);
 
   SetSizeParams set_size_params;
   set_size_params.enable_auto_size.reset(new bool(auto_size_enabled));
diff --git a/extensions/browser/guest_view/guest_view_base.h b/extensions/browser/guest_view/guest_view_base.h
index ef3d855..d3fb07f 100644
--- a/extensions/browser/guest_view/guest_view_base.h
+++ b/extensions/browser/guest_view/guest_view_base.h
@@ -293,6 +293,20 @@
 
   ~GuestViewBase() override;
 
+  // Convert sizes in pixels from logical to physical numbers of pixels.
+  // Note that a size can consist of a fractional number of logical pixels
+  // (hence |logical_pixels| is represented as a double), but will always
+  // consist of an integral number of physical pixels (hence the return value
+  // is represented as an int).
+  int LogicalPixelsToPhysicalPixels(double logical_pixels);
+
+  // Convert sizes in pixels from physical to logical numbers of pixels.
+  // Note that a size can consist of a fractional number of logical pixels
+  // (hence the return value is represented as a double), but will always
+  // consist of an integral number of physical pixels (hence |physical_pixels|
+  // is represented as an int).
+  double PhysicalPixelsToLogicalPixels(int physical_pixels);
+
   // WebContentsObserver implementation.
   void DidStopLoading(content::RenderViewHost* render_view_host) final;
   void RenderViewReady() final;
@@ -340,6 +354,9 @@
   void DispatchOnResizeEvent(const gfx::Size& old_size,
                              const gfx::Size& new_size);
 
+  // Get the zoom factor for the embedder's web contents.
+  double GetEmbedderZoomFactor();
+
   void SetUpSizing(const base::DictionaryValue& params);
 
   void StartTrackingEmbedderZoomLevel();
diff --git a/extensions/browser/guest_view/test_guest_view_manager.h b/extensions/browser/guest_view/test_guest_view_manager.h
index 283eabb..b826f30 100644
--- a/extensions/browser/guest_view/test_guest_view_manager.h
+++ b/extensions/browser/guest_view/test_guest_view_manager.h
@@ -21,6 +21,8 @@
   void WaitForAllGuestsDeleted();
   content::WebContents* WaitForSingleGuestCreated();
 
+  content::WebContents* GetLastGuestCreated();
+
  private:
   // GuestViewManager override:
   void AddGuest(int guest_instance_id,
@@ -28,7 +30,6 @@
   void RemoveGuest(int guest_instance_id) override;
 
   int GetNumGuests() const;
-  content::WebContents* GetLastGuestCreated();
   void WaitForGuestCreated();
 
   std::vector<linked_ptr<content::WebContentsDestroyedWatcher>>
diff --git a/extensions/browser/guest_view/web_view/web_view_guest.cc b/extensions/browser/guest_view/web_view/web_view_guest.cc
index fce1b6d0..eacef42e6 100644
--- a/extensions/browser/guest_view/web_view/web_view_guest.cc
+++ b/extensions/browser/guest_view/web_view/web_view_guest.cc
@@ -875,35 +875,9 @@
 
   GURL url = ResolveURL(src);
 
-  // Do not allow navigating a guest to schemes other than known safe schemes.
-  // This will block the embedder trying to load unwanted schemes, e.g.
-  // chrome://settings.
-  bool scheme_is_blocked =
-      (!content::ChildProcessSecurityPolicy::GetInstance()->IsWebSafeScheme(
-           url.scheme()) &&
-       !url.SchemeIs(url::kAboutScheme)) ||
-      url.SchemeIs(url::kJavaScriptScheme);
-  if (scheme_is_blocked || !url.is_valid()) {
-    LoadAbort(true /* is_top_level */, url,
-              net::ErrorToShortString(net::ERR_ABORTED));
-    NavigateGuest(url::kAboutBlankURL, true /* force_navigation */);
-    return;
-  }
-  if (!force_navigation && (src_ == url))
-    return;
-
-  GURL validated_url(url);
-  web_contents()->GetRenderProcessHost()->FilterURL(false, &validated_url);
-  // As guests do not swap processes on navigation, only navigations to
-  // normal web URLs are supported.  No protocol handlers are installed for
-  // other schemes (e.g., WebUI or extensions), and no permissions or bindings
-  // can be granted to the guest process.
-  LoadURLWithParams(validated_url,
-                    content::Referrer(),
+  LoadURLWithParams(url, content::Referrer(),
                     ui::PAGE_TRANSITION_AUTO_TOPLEVEL,
-                    web_contents());
-
-  src_ = validated_url;
+                    force_navigation);
 }
 
 bool WebViewGuest::HandleKeyboardShortcuts(
@@ -1118,6 +1092,23 @@
 content::WebContents* WebViewGuest::OpenURLFromTab(
     content::WebContents* source,
     const content::OpenURLParams& params) {
+  // There are two use cases to consider from a security perspective:
+  // 1.) Renderer-initiated navigation to chrome:// must always be blocked even
+  //     if the <webview> is in WebUI. This is handled by
+  //     WebViewGuest::LoadURLWithParams. WebViewGuest::NavigateGuest will also
+  //     call LoadURLWithParams. CreateNewGuestWebViewWindow creates a new
+  //     WebViewGuest which will call NavigateGuest in DidInitialize.
+  // 2.) The Language Settings context menu item should always work, both in
+  //     Chrome Apps and WebUI. This is a browser initiated request and so
+  //     we pass it along to the embedder's WebContentsDelegate to get the
+  //     browser to perform the action for the <webview>.
+  if (!params.is_renderer_initiated) {
+    if (!owner_web_contents()->GetDelegate())
+      return nullptr;
+    return owner_web_contents()->GetDelegate()->OpenURLFromTab(
+        owner_web_contents(), params);
+  }
+
   // If the guest wishes to navigate away prior to attachment then we save the
   // navigation to perform upon attachment. Navigation initializes a lot of
   // state that assumes an embedder exists, such as RenderWidgetHostViewGuest.
@@ -1134,12 +1125,25 @@
     it->second = new_window_info;
     return nullptr;
   }
+
+  // This code path is taken if RenderFrameImpl::DecidePolicyForNavigation
+  // decides that a fork should happen. At the time of writing this comment,
+  // the only way a well behaving guest could hit this code path is if it
+  // navigates to a URL that's associated with the default search engine.
+  // This list of URLs is generated by chrome::GetSearchURLs. Validity checks
+  // are performed inside LoadURLWithParams such that if the guest attempts
+  // to navigate to a URL that it is not allowed to navigate to, a 'loadabort'
+  // event will fire in the embedder, and the guest will be navigated to
+  // about:blank.
   if (params.disposition == CURRENT_TAB) {
-    // This can happen for cross-site redirects.
-    LoadURLWithParams(params.url, params.referrer, params.transition, source);
-    return source;
+    LoadURLWithParams(params.url, params.referrer, params.transition,
+                      true /* force_navigation */);
+    return web_contents();
   }
 
+  // This code path is taken if Ctrl+Click, middle click or any of the
+  // keyboard/mouse combinations are used to open a link in a new tab/window.
+  // This code path is also taken on client-side redirects from about:blank.
   CreateNewGuestWebViewWindow(params);
   return nullptr;
 }
@@ -1161,8 +1165,32 @@
 void WebViewGuest::LoadURLWithParams(const GURL& url,
                                      const content::Referrer& referrer,
                                      ui::PageTransition transition_type,
-                                     content::WebContents* web_contents) {
-  content::NavigationController::LoadURLParams load_url_params(url);
+                                     bool force_navigation) {
+  // Do not allow navigating a guest to schemes other than known safe schemes.
+  // This will block the embedder trying to load unwanted schemes, e.g.
+  // chrome://.
+  bool scheme_is_blocked =
+      (!content::ChildProcessSecurityPolicy::GetInstance()->IsWebSafeScheme(
+           url.scheme()) &&
+       !url.SchemeIs(url::kAboutScheme)) ||
+      url.SchemeIs(url::kJavaScriptScheme);
+  if (scheme_is_blocked || !url.is_valid()) {
+    LoadAbort(true /* is_top_level */, url,
+              net::ErrorToShortString(net::ERR_ABORTED));
+    NavigateGuest(url::kAboutBlankURL, true /* force_navigation */);
+    return;
+  }
+
+  if (!force_navigation && (src_ == url))
+    return;
+
+  GURL validated_url(url);
+  web_contents()->GetRenderProcessHost()->FilterURL(false, &validated_url);
+  // As guests do not swap processes on navigation, only navigations to
+  // normal web URLs are supported.  No protocol handlers are installed for
+  // other schemes (e.g., WebUI or extensions), and no permissions or bindings
+  // can be granted to the guest process.
+  content::NavigationController::LoadURLParams load_url_params(validated_url);
   load_url_params.referrer = referrer;
   load_url_params.transition_type = transition_type;
   load_url_params.extra_headers = std::string();
@@ -1170,7 +1198,9 @@
     load_url_params.override_user_agent =
         content::NavigationController::UA_OVERRIDE_TRUE;
   }
-  web_contents->GetController().LoadURLWithParams(load_url_params);
+  web_contents()->GetController().LoadURLWithParams(load_url_params);
+
+  src_ = validated_url;
 }
 
 void WebViewGuest::RequestNewWindowPermission(
diff --git a/extensions/browser/guest_view/web_view/web_view_guest.h b/extensions/browser/guest_view/web_view/web_view_guest.h
index 537f3011..b05e5d65 100644
--- a/extensions/browser/guest_view/web_view/web_view_guest.h
+++ b/extensions/browser/guest_view/web_view/web_view_guest.h
@@ -285,10 +285,12 @@
   static void RemoveWebViewStateFromIOThread(
       content::WebContents* web_contents);
 
+  // Loads the |url| provided. |force_navigation| indicates whether to reload
+  // the content if the provided |url| matches the current page of the guest.
   void LoadURLWithParams(const GURL& url,
                          const content::Referrer& referrer,
                          ui::PageTransition transition_type,
-                         content::WebContents* web_contents);
+                         bool force_navigation);
 
   void RequestNewWindowPermission(
       WindowOpenDisposition disposition,
diff --git a/extensions/browser/serial_extension_host_queue.cc b/extensions/browser/serial_extension_host_queue.cc
new file mode 100644
index 0000000..7986523c
--- /dev/null
+++ b/extensions/browser/serial_extension_host_queue.cc
@@ -0,0 +1,53 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "extensions/browser/serial_extension_host_queue.h"
+
+#include "base/bind.h"
+#include "base/message_loop/message_loop.h"
+#include "extensions/browser/deferred_start_render_host.h"
+
+namespace extensions {
+
+SerialExtensionHostQueue::SerialExtensionHostQueue()
+    : pending_create_(false), ptr_factory_(this) {
+}
+
+SerialExtensionHostQueue::~SerialExtensionHostQueue() {
+}
+
+void SerialExtensionHostQueue::Add(DeferredStartRenderHost* host) {
+  queue_.push_back(host);
+  PostTask();
+}
+
+void SerialExtensionHostQueue::Remove(DeferredStartRenderHost* host) {
+  std::list<DeferredStartRenderHost*>::iterator it =
+      std::find(queue_.begin(), queue_.end(), host);
+  if (it != queue_.end())
+    queue_.erase(it);
+}
+
+void SerialExtensionHostQueue::PostTask() {
+  if (!pending_create_) {
+    base::MessageLoop::current()->PostTask(
+        FROM_HERE, base::Bind(&SerialExtensionHostQueue::ProcessOneHost,
+                              ptr_factory_.GetWeakPtr()));
+    pending_create_ = true;
+  }
+}
+
+void SerialExtensionHostQueue::ProcessOneHost() {
+  pending_create_ = false;
+  if (queue_.empty())
+    return;  // can happen on shutdown
+
+  queue_.front()->CreateRenderViewNow();
+  queue_.pop_front();
+
+  if (!queue_.empty())
+    PostTask();
+}
+
+}  // namespace extensions
diff --git a/extensions/browser/serial_extension_host_queue.h b/extensions/browser/serial_extension_host_queue.h
new file mode 100644
index 0000000..40d11ed
--- /dev/null
+++ b/extensions/browser/serial_extension_host_queue.h
@@ -0,0 +1,52 @@
+// 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 EXTENSIONS_BROWSER_SERIAL_EXTENSION_HOST_QUEUE_H_
+#define EXTENSIONS_BROWSER_SERIAL_EXTENSION_HOST_QUEUE_H_
+
+#include <list>
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "extensions/browser/extension_host_queue.h"
+
+namespace extensions {
+
+class DeferredStartRenderHost;
+
+// An ExtensionHostQueue which initializes DeferredStartRenderHosts in the order
+// they're Add()ed, with simple rate limiting logic that re-posts each task to
+// the UI thread, to avoid clogging it for a long period of time.
+class SerialExtensionHostQueue : public ExtensionHostQueue {
+ public:
+  SerialExtensionHostQueue();
+  ~SerialExtensionHostQueue() override;
+
+ private:
+  // ExtensionHostQueue:
+  void Add(DeferredStartRenderHost* host) override;
+  void Remove(DeferredStartRenderHost* host) override;
+
+  // Queues up a delayed task to process the next DeferredStartRenderHost in
+  // the queue.
+  void PostTask();
+
+  // Creates the RenderView for the next host in the queue.
+  void ProcessOneHost();
+
+  // True if this queue is currently in the process of starting an
+  // DeferredStartRenderHost.
+  bool pending_create_;
+
+  // The list of DeferredStartRenderHosts waiting to be started.
+  std::list<DeferredStartRenderHost*> queue_;
+
+  base::WeakPtrFactory<SerialExtensionHostQueue> ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(SerialExtensionHostQueue);
+};
+
+}  // namespace extensions
+
+#endif  // EXTENSIONS_BROWSER_SERIAL_EXTENSION_HOST_QUEUE_H_
diff --git a/extensions/common/BUILD.gn b/extensions/common/BUILD.gn
index faa1cb8..3cd8117 100644
--- a/extensions/common/BUILD.gn
+++ b/extensions/common/BUILD.gn
@@ -13,10 +13,8 @@
       rebase_path(extensions_gypi_values.extensions_common_constants_sources,
                   ".",
                   "//extensions")
-
-  if (is_win) {
-    cflags = [ "/wd4267" ]  # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
-  }
+  # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+  configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
 }
 
 if (enable_extensions) {
@@ -31,6 +29,8 @@
     sources = rebase_path(extensions_gypi_values.extensions_common_sources,
                           ".",
                           "//extensions")
+    # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+    configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
 
     deps = [
       ":common_constants",
@@ -81,9 +81,5 @@
                       "//extensions")
       sources += nacl_sources
     }
-
-    if (is_win) {
-      cflags = [ "/wd4267" ]  # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
-    }
   }
 }  # enable_extensions
diff --git a/extensions/common/api/extension_options_internal.idl b/extensions/common/api/extension_options_internal.idl
index b2841e9..be5346b 100644
--- a/extensions/common/api/extension_options_internal.idl
+++ b/extensions/common/api/extension_options_internal.idl
@@ -12,8 +12,8 @@
   };
 
   dictionary PreferredSizeChangedOptions {
-    long width;
-    long height;
+    double width;
+    double height;
   };
 
   interface Events {
diff --git a/extensions/common/api/printer_provider.idl b/extensions/common/api/printer_provider.idl
index a3b1b23..940e018 100644
--- a/extensions/common/api/printer_provider.idl
+++ b/extensions/common/api/printer_provider.idl
@@ -2,11 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// This API exposes events used by print manager to query printers controlled
-// by extensions, to query capabilities of them and to submit print jobs to
-// these printers.
+// <p>The <code>chrome.printerProvider</code> API exposes events used by print
+// manager to query printers controlled by extensions, to query their
+// capabilities and to submit print jobs to these printers.
+// <p/>
+// <p>Available only on dev channel since Chrome 42.</p>
 namespace printerProvider {
-  // Error codes used by providing extensions in response to requests.
+  // Error codes returned in response to $(ref:onPrintRequested) event.
   enum PrintError {
     // Operation completed successfully.
     OK,
@@ -20,59 +22,68 @@
     INVALID_TICKET,
 
     // Document is invalid. For example, data may be corrupted or the format is
-    // incompatible with the Extension.
+    // incompatible with the extension.
     INVALID_DATA
   };
 
   // Printer description for $(ref:onGetPrintersRequested) event.
   dictionary PrinterInfo {
-    // Unique ID of printer.
+    // Unique printer ID.
     DOMString id;
 
-    // Human readable display name of printer.
+    // Printer's human readable name.
     DOMString name;
 
-    // Human readable description of printer.
+    // Printer's human readable description.
     DOMString? description;
   };
 
-  // Parameters of $(ref:onPrintRequested).
+  // Printing request parameters. Passed to $(ref:onPrintRequested) event.
   dictionary PrintJob {
-    // ID of the printer to submit the job.
+    // ID of the printer which should handle the job.
     DOMString printerId;
 
-    // print ticket in CJT format described at
-    // https://developers.google.com/cloud-print/docs/cdd#cjt
+    // Print ticket in
+    // <a href="https://developers.google.com/cloud-print/docs/cdd#cjt">
+    // CJT format</a>.
     object ticket;
 
-    // Content type of the document. Supported formats are "application/pdf" and
-    // "image/pwg-raster".
+    // The document content type. Supported formats are
+    // <code>"application/pdf"</code> and <code>"image/pwg-raster"</code>.
     DOMString contentType;
 
-    // Buffer with document to printer. Format must match |contentType|.
+    // Buffer containing the document to print. Format must match |contentType|.
     ArrayBuffer document;
   };
 
   callback PrintersCallback = void(PrinterInfo[] printerInfo);
+
+  // |capabilities|: Device capabilities in
+  // <a href="https://developers.google.com/cloud-print/docs/cdd#cdd">CDD
+  // format</a>.
   callback CapabilitiesCallback = void(object capabilities);
+
   callback PrintCallback = void(PrintError result);
 
   interface Events {
-    // Event fired when print manager requests printers provided by extension.
-    // |resultCallback| : callback to return printer list. Every listener must
+    // Event fired when print manager requests printers provided by extensions.
+    // |resultCallback|: Callback to return printer list. Every listener must
     // call callback exactly once.
     static void onGetPrintersRequested(PrintersCallback resultCallback);
 
     // Event fired when print manager requests printer capabilities.
-    // |printerId| : unique ID of the printer.
-    // |resultCallback| : callback to return device capabilities in CDD format
-    // as described at https://developers.google.com/cloud-print/docs/cdd#cdd.
+    // |printerId|: Unique ID of the printer whose capabilities are requested.
+    // |resultCallback|: Callback to return device capabilities in
+    // <a href="https://developers.google.com/cloud-print/docs/cdd#cdd">CDD
+    // format</a>.
     // The receiving listener must call callback exectly once.
     static void onGetCapabilityRequested(DOMString printerId,
                                          CapabilitiesCallback resultCallback);
 
     // Event fired when print manager requests printing.
-    // |printJob| : parameters of printing request.
+    // |printJob|: The printing request parameters.
+    // |resultCallback|: Callback that should be called when the printing
+    // request is completed.
     static void onPrintRequested(PrintJob printJob,
                                  PrintCallback resultCallback);
   };
diff --git a/extensions/common/api/runtime.json b/extensions/common/api/runtime.json
index 7266ff5..de6e596 100644
--- a/extensions/common/api/runtime.json
+++ b/extensions/common/api/runtime.json
@@ -109,6 +109,17 @@
         ]
       },
       {
+        "name": "openOptionsPage",
+        "type": "function",
+        "description": "<p>Open your Extension's options page, if possible.</p><p>The precise behavior may depend on your manifest's <code><a href=\"optionsV2\">options_ui</a></code> or <code><a href=\"options\">options_page</a></code> key, or what Chrome happens to support at the time. For example, the page may be opened in a new tab, within chrome://extensions, within an App, or it may just focus an open options page. It will never cause the caller page to reload.</p><p>If your Extension does not declare an options page, or Chrome failed to create one for some other reason, the callback will set $(ref:lastError).</p>",
+        "parameters": [{
+          "type": "function",
+          "name": "callback",
+          "parameters": [],
+          "optional": true
+        }]
+      },
+      {
         "name": "getManifest",
         "description": "Returns details about the app or extension from the manifest. The object returned is a serialization of the full <a href=\"manifest.html\">manifest file</a>.",
         "type": "function",
diff --git a/extensions/common/api/vpn_provider.idl b/extensions/common/api/vpn_provider.idl
index 867484e..c48e0b01 100644
--- a/extensions/common/api/vpn_provider.idl
+++ b/extensions/common/api/vpn_provider.idl
@@ -15,10 +15,27 @@
     DOMString? broadcastAddress;
     // MTU setting for the VPN interface. (default: 1500 bytes)
     DOMString? mtu;
-    // Bypass network traffic to the below IPs from the tunnel. Typically used
-    // to bypass traffic to and from the VPN server. Currently only one IP is
-    // supported.
-    DOMString[] bypassTunnelForIp;
+    // Exclude network traffic to the list of IP blocks in CIDR notation from
+    // the tunnel. This can be used to bypass traffic to and from the VPN
+    // server.
+    // When many rules match a destination, the rule with the longest matching
+    // prefix wins.
+    // Entries that correspond to the same CIDR block are treated as duplicates.
+    // Such duplicates in the collated (exclusionList + inclusionList) list are
+    // eliminated and the exact duplicate entry that will be eliminated is
+    // undefined.
+    DOMString[] exclusionList;
+    // Include network traffic to the list of IP blocks in CIDR notation to the
+    // tunnel. This parameter can be used to set up a split tunnel. By default
+    // no traffic is directed to the tunnel. Adding the entry "0.0.0.0/0" to
+    // this list gets all the user traffic redirected to the tunnel.
+    // When many rules match a destination, the rule with the longest matching
+    // prefix wins.
+    // Entries that correspond to the same CIDR block are treated as duplicates.
+    // Such duplicates in the collated (exclusionList + inclusionList) list are
+    // eliminated and the exact duplicate entry that will be eliminated is
+    // undefined.
+    DOMString[] inclusionList;
     // A list of search domains. (default: no search domain)
     DOMString[]? domainSearch;
     // A list of IPs for the DNS servers.
diff --git a/extensions/common/cast/cast_cert_validator_openssl.cc b/extensions/common/cast/cast_cert_validator_openssl.cc
index 8c2e4c0..326aa49 100644
--- a/extensions/common/cast/cast_cert_validator_openssl.cc
+++ b/extensions/common/cast/cast_cert_validator_openssl.cc
@@ -18,14 +18,13 @@
 #include "extensions/browser/api/cast_channel/cast_auth_ica.h"
 #include "net/cert/x509_certificate.h"
 #include "net/cert/x509_util_openssl.h"
+#include "net/ssl/scoped_openssl_types.h"
 
 namespace extensions {
 namespace core_api {
 namespace cast_crypto {
 namespace {
 
-typedef crypto::ScopedOpenSSL<X509, X509_free>::Type ScopedX509;
-
 class CertVerificationContextOpenSSL : public CertVerificationContext {
  public:
   // Takes ownership of the passed-in x509 object
@@ -80,7 +79,7 @@
   }
 
  private:
-  ScopedX509 x509_;
+  net::ScopedX509 x509_;
 };
 
 }  // namespace
@@ -127,7 +126,7 @@
   const uint8_t* device_cert_der_ptr =
       reinterpret_cast<const uint8_t*>(device_cert.data());
   const uint8_t* device_cert_der_end = device_cert_der_ptr + device_cert.size();
-  ScopedX509 device_cert_x509(
+  net::ScopedX509 device_cert_x509(
       d2i_X509(NULL, &device_cert_der_ptr, device_cert.size()));
   if (!device_cert_x509 || device_cert_der_ptr != device_cert_der_end) {
     return VerificationResult("Failed to parse device certificate.",
diff --git a/extensions/common/permissions/api_permission.h b/extensions/common/permissions/api_permission.h
index c7acd83..23a8c822 100644
--- a/extensions/common/permissions/api_permission.h
+++ b/extensions/common/permissions/api_permission.h
@@ -8,6 +8,7 @@
 #include <map>
 #include <set>
 #include <string>
+#include <vector>
 
 #include "base/callback.h"
 #include "base/memory/scoped_ptr.h"
@@ -54,6 +55,7 @@
     kAppView,
     kAudio,
     kAudioCapture,
+    kAudioModem,
     kAutomation,
     kAutoTestPrivate,
     kBackground,
@@ -129,7 +131,6 @@
     kIdltest,
     kIdle,
     kImeWindowEnabled,
-    kInfobars,
     kInlineInstallPrivate,
     kInput,
     kInputMethodPrivate,
diff --git a/extensions/extensions.gyp b/extensions/extensions.gyp
index d9877372..0f28828 100644
--- a/extensions/extensions.gyp
+++ b/extensions/extensions.gyp
@@ -123,6 +123,7 @@
       'dependencies': [
         '../base/base.gyp:base',
         '../base/base.gyp:base_prefs',
+        '../components/components.gyp:device_event_log_component',
         '../components/components.gyp:keyed_service_content',
         '../components/components.gyp:keyed_service_core',
         '../components/components.gyp:onc_component',
diff --git a/extensions/extensions.gypi b/extensions/extensions.gypi
index f027d40..4a4515d 100644
--- a/extensions/extensions.gypi
+++ b/extensions/extensions.gypi
@@ -540,6 +540,7 @@
       'browser/content_verify_job.h',
       'browser/crx_file_info.cc',
       'browser/crx_file_info.h',
+      'browser/deferred_start_render_host.h',
       'browser/error_map.cc',
       'browser/error_map.h',
       'browser/event_listener_map.cc',
@@ -560,6 +561,7 @@
       'browser/extension_host.h',
       'browser/extension_host_delegate.h',
       'browser/extension_host_observer.h',
+      'browser/extension_host_queue.h',
       'browser/extension_icon_image.cc',
       'browser/extension_icon_image.h',
       'browser/extension_message_filter.cc',
@@ -706,6 +708,8 @@
       'browser/script_execution_observer.h',
       'browser/script_executor.cc',
       'browser/script_executor.h',
+      'browser/serial_extension_host_queue.cc',
+      'browser/serial_extension_host_queue.h',
       'browser/state_store.cc',
       'browser/state_store.h',
       'browser/suggest_permission_util.cc',
diff --git a/extensions/extensions_tests.gyp b/extensions/extensions_tests.gyp
index e68f274..c542277 100644
--- a/extensions/extensions_tests.gyp
+++ b/extensions/extensions_tests.gyp
@@ -96,6 +96,11 @@
             'shell/app_shell.gyp:app_shell',  # Needed for App Shell.app's Helper.
           ],
         }],
+        ['chromeos==1', {
+          'sources': [
+            '<@(extensions_browsertests_sources_chromeos)',
+          ],
+        }],
         # This is only here to keep gyp happy. This target never builds on
         # mobile platforms.
         ['OS != "ios" and OS != "android"', {
diff --git a/extensions/extensions_tests.gypi b/extensions/extensions_tests.gypi
index 479f0b6..7e54319 100644
--- a/extensions/extensions_tests.gypi
+++ b/extensions/extensions_tests.gypi
@@ -38,6 +38,9 @@
       'shell/test/shell_test_launcher_delegate.h',
       'shell/test/shell_tests_main.cc',
     ],
+    'extensions_browsertests_sources_chromeos': [
+      'browser/api/vpn_provider/vpn_provider_apitest.cc',
+    ],
     'extensions_unittests_sources': [
       'browser/api/alarms/alarms_api_unittest.cc',
       'browser/api/api_resource_manager_unittest.cc',
diff --git a/extensions/renderer/BUILD.gn b/extensions/renderer/BUILD.gn
index a35e723..1bfceef 100644
--- a/extensions/renderer/BUILD.gn
+++ b/extensions/renderer/BUILD.gn
@@ -13,6 +13,9 @@
                         ".",
                         "//extensions")
 
+  # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+  configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
+
   deps = [
     "//chrome:resources",
     "//content:resources",
@@ -22,8 +25,4 @@
     "//third_party/WebKit/public:blink",
     "//third_party/mojo/src/mojo/edk/js",
   ]
-
-  if (is_win) {
-    cflags = [ "/wd4267" ]  # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
-  }
 }
diff --git a/extensions/renderer/dispatcher.cc b/extensions/renderer/dispatcher.cc
index b40e77e..4fd36a0 100644
--- a/extensions/renderer/dispatcher.cc
+++ b/extensions/renderer/dispatcher.cc
@@ -341,6 +341,7 @@
 
   if (context->GetAvailability("extensionViewInternal").is_available()) {
     module_system->Require("extensionView");
+    module_system->Require("extensionViewApiMethods");
     module_system->Require("extensionViewAttributes");
   }
 
@@ -593,10 +594,14 @@
   resources.push_back(std::make_pair("guestViewContainer",
                                      IDR_GUEST_VIEW_CONTAINER_JS));
   resources.push_back(std::make_pair("extensionView", IDR_EXTENSION_VIEW_JS));
+  resources.push_back(std::make_pair("extensionViewApiMethods",
+                                     IDR_EXTENSION_VIEW_API_METHODS_JS));
   resources.push_back(std::make_pair("extensionViewAttributes",
                                      IDR_EXTENSION_VIEW_ATTRIBUTES_JS));
   resources.push_back(std::make_pair("extensionViewConstants",
                                      IDR_EXTENSION_VIEW_CONSTANTS_JS));
+  resources.push_back(std::make_pair("extensionViewEvents",
+      IDR_EXTENSION_VIEW_EVENTS_JS));
   resources.push_back(std::make_pair(
       "extensionViewInternal", IDR_EXTENSION_VIEW_INTERNAL_CUSTOM_BINDINGS_JS));
   resources.push_back(std::make_pair("webView", IDR_WEB_VIEW_JS));
diff --git a/extensions/renderer/resources/extensions_renderer_resources.grd b/extensions/renderer/resources/extensions_renderer_resources.grd
index 9b0cd1f..439079e 100644
--- a/extensions/renderer/resources/extensions_renderer_resources.grd
+++ b/extensions/renderer/resources/extensions_renderer_resources.grd
@@ -44,8 +44,10 @@
       <include name="IDR_GUEST_VIEW_JS" file="guest_view/guest_view.js" type="BINDATA" />
       <include name="IDR_GUEST_VIEW_CONTAINER_JS" file="guest_view/guest_view_container.js" type="BINDATA" />
       <include name="IDR_EXTENSION_VIEW_JS" file="guest_view/extension_view.js" type="BINDATA" />
+      <include name="IDR_EXTENSION_VIEW_API_METHODS_JS" file="guest_view/extension_view_api_methods.js" type="BINDATA" />
       <include name="IDR_EXTENSION_VIEW_ATTRIBUTES_JS" file="guest_view/extension_view_attributes.js" type="BINDATA" />
       <include name="IDR_EXTENSION_VIEW_CONSTANTS_JS" file="guest_view/extension_view_constants.js" type="BINDATA" />
+      <include name="IDR_EXTENSION_VIEW_EVENTS_JS" file="guest_view/extension_view_events.js" type="BINDATA" />
       <include name="IDR_EXTENSION_VIEW_INTERNAL_CUSTOM_BINDINGS_JS" file="guest_view/extension_view_internal.js" type="BINDATA" />
       <include name="IDR_WEB_VIEW_ACTION_REQUESTS_JS" file="guest_view/web_view_action_requests.js" type="BINDATA" />
       <include name="IDR_WEB_VIEW_API_METHODS_JS" file="guest_view/web_view_api_methods.js" type="BINDATA" />
diff --git a/extensions/renderer/resources/guest_view/extension_view.js b/extensions/renderer/resources/guest_view/extension_view.js
index 8ca6dfe..931a5c4 100644
--- a/extensions/renderer/resources/guest_view/extension_view.js
+++ b/extensions/renderer/resources/guest_view/extension_view.js
@@ -7,12 +7,15 @@
 var GuestViewContainer = require('guestViewContainer').GuestViewContainer;
 var ExtensionViewConstants =
     require('extensionViewConstants').ExtensionViewConstants;
+var ExtensionViewEvents = require('extensionViewEvents').ExtensionViewEvents;
 var ExtensionViewInternal =
     require('extensionViewInternal').ExtensionViewInternal;
 
 function ExtensionViewImpl(extensionviewElement) {
   GuestViewContainer.call(this, extensionviewElement, 'extensionview');
   this.setupExtensionViewAttributes();
+
+  new ExtensionViewEvents(this, this.viewInstanceId);
 }
 
 ExtensionViewImpl.prototype.__proto__ = GuestViewContainer.prototype;
@@ -20,9 +23,7 @@
 ExtensionViewImpl.VIEW_TYPE = 'ExtensionView';
 
 ExtensionViewImpl.setupElement = function(proto) {
-  var apiMethods = [
-    'navigate'
-  ];
+  var apiMethods = ExtensionViewImpl.getApiMethods();
 
   GuestViewContainer.forwardApiMethods(proto, apiMethods);
 };
@@ -33,10 +34,6 @@
   }.bind(this));
 };
 
-ExtensionViewImpl.prototype.onElementAttached = function() {
-  this.attributes[ExtensionViewConstants.ATTRIBUTE_SRC].parse();
-};
-
 ExtensionViewImpl.prototype.buildContainerParams = function() {
   var params = {};
   for (var i in this.attributes) {
@@ -51,13 +48,23 @@
   if (!this.attributes[attributeName])
     return;
 
-  // Let the changed attribute handle its own mutation;
+  // Let the changed attribute handle its own mutation.
   this.attributes[attributeName].maybeHandleMutation(oldValue, newValue);
 };
 
 ExtensionViewImpl.prototype.onElementDetached = function() {
   this.guest.destroy();
-  this.attributes[ExtensionViewConstants.ATTRIBUTE_SRC].reset();
+
+  // Reset all attributes.
+  for (var i in this.attributes) {
+    this.attributes[i].reset();
+  }
+};
+
+// Updates src upon loadcommit.
+ExtensionViewImpl.prototype.onLoadCommit = function(url) {
+  this.attributes[ExtensionViewConstants.ATTRIBUTE_SRC].
+      setValueIgnoreMutation(url);
 };
 
 GuestViewContainer.registerElement(ExtensionViewImpl);
diff --git a/extensions/renderer/resources/guest_view/extension_view_api_methods.js b/extensions/renderer/resources/guest_view/extension_view_api_methods.js
new file mode 100644
index 0000000..6f7030e
--- /dev/null
+++ b/extensions/renderer/resources/guest_view/extension_view_api_methods.js
@@ -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.
+
+// This module implements the public-facing API functions for the
+// <extensionview> tag.
+
+var ExtensionViewInternal =
+    require('extensionViewInternal').ExtensionViewInternal;
+var ExtensionViewImpl = require('extensionView').ExtensionViewImpl;
+var ExtensionViewConstants =
+    require('extensionViewConstants').ExtensionViewConstants;
+
+// An array of <extensionview>'s public-facing API methods.
+var EXTENSION_VIEW_API_METHODS = [
+  // Sets the extension ID and src for the guest. Must be called every time
+  // the extension ID changes. This is the only way to set the extension ID.
+  'connect'
+];
+
+// -----------------------------------------------------------------------------
+// Custom API method implementations.
+
+ExtensionViewImpl.prototype.connect = function(extension, src) {
+  this.guest.destroy();
+
+  // Sets the extension id and src.
+  this.attributes[ExtensionViewConstants.ATTRIBUTE_EXTENSION]
+      .setValueIgnoreMutation(extension);
+  this.attributes[ExtensionViewConstants.ATTRIBUTE_SRC]
+      .setValueIgnoreMutation(src);
+
+  this.createGuest();
+};
+
+// -----------------------------------------------------------------------------
+
+ExtensionViewImpl.getApiMethods = function() {
+  return EXTENSION_VIEW_API_METHODS;
+};
diff --git a/extensions/renderer/resources/guest_view/extension_view_attributes.js b/extensions/renderer/resources/guest_view/extension_view_attributes.js
index e71d2ea..b83a97e1 100644
--- a/extensions/renderer/resources/guest_view/extension_view_attributes.js
+++ b/extensions/renderer/resources/guest_view/extension_view_attributes.js
@@ -57,9 +57,8 @@
 // Called when the attribute's value changes.
 ExtensionViewAttribute.prototype.maybeHandleMutation =
     function(oldValue, newValue) {
-  if (this.ignoreMutation) {
+  if (this.ignoreMutation)
     return;
-  }
 
   this.handleMutation(oldValue, newValue);
 }
@@ -68,21 +67,30 @@
 ExtensionViewAttribute.prototype.handleMutation =
     function(oldValue, newValue) {};
 
+ExtensionViewAttribute.prototype.reset = function() {
+  this.setValueIgnoreMutation();
+}
+
+// Attribute that handles extension binded to the extensionview.
+function ExtensionAttribute(extensionViewImpl) {
+  ExtensionViewAttribute.call(this, ExtensionViewConstants.ATTRIBUTE_EXTENSION,
+                              extensionViewImpl);
+}
+
+ExtensionAttribute.prototype.__proto__ = ExtensionViewAttribute.prototype;
+
+ExtensionAttribute.prototype.handleMutation = function(oldValue, newValue) {
+  this.setValueIgnoreMutation(oldValue);
+}
+
 // Attribute that handles the location and navigation of the extensionview.
 function SrcAttribute(extensionViewImpl) {
   ExtensionViewAttribute.call(this, ExtensionViewConstants.ATTRIBUTE_SRC,
                               extensionViewImpl);
-  this.setupMutationObserver();
-  this.beforeFirstNavigation = true;
 }
 
 SrcAttribute.prototype.__proto__ = ExtensionViewAttribute.prototype;
 
-SrcAttribute.prototype.setValueIgnoreMutation = function(value) {
-  this.observer.takeRecords();
-  ExtensionViewAttribute.prototype.setValueIgnoreMutation.call(this, value);
-}
-
 SrcAttribute.prototype.handleMutation = function(oldValue, newValue) {
   if (!newValue && oldValue) {
     this.setValueIgnoreMutation(oldValue);
@@ -91,51 +99,24 @@
   this.parse();
 };
 
-SrcAttribute.prototype.setupMutationObserver =
-    function() {
-  this.observer = new MutationObserver(function(mutations) {
-    $Array.forEach(mutations, function(mutation) {
-      var oldValue = mutation.oldValue;
-      var newValue = this.getValue();
-      if (oldValue != newValue) {
-        return;
-      }
-      this.handleMutation(oldValue, newValue);
-    }.bind(this));
-  }.bind(this));
-  var params = {
-    attributes: true,
-    attributeOldValue: true,
-    attributeFilter: [this.name]
-  };
-  this.observer.observe(this.extensionViewImpl.element, params);
-};
-
 SrcAttribute.prototype.parse = function() {
   if (!this.extensionViewImpl.elementAttached || !this.getValue())
     return;
 
-  if (!this.extensionViewImpl.guest.getId()) {
-    if (this.beforeFirstNavigation) {
-      this.beforeFirstNavigation = false;
-      this.extensionViewImpl.createGuest();
-    }
+  if (!this.extensionViewImpl.guest.getId())
     return;
-  }
 
   ExtensionViewInternal.navigate(this.extensionViewImpl.guest.getId(),
                                  this.getValue());
 };
 
-SrcAttribute.prototype.reset = function() {
-  this.beforeFirstNavigation = true;
-};
-
 // -----------------------------------------------------------------------------
 
 // Sets up all of the extensionview attributes.
 ExtensionViewImpl.prototype.setupExtensionViewAttributes = function() {
   this.attributes = {};
+  this.attributes[ExtensionViewConstants.ATTRIBUTE_EXTENSION] =
+      new ExtensionAttribute(this);
   this.attributes[ExtensionViewConstants.ATTRIBUTE_SRC] =
       new SrcAttribute(this);
 };
diff --git a/extensions/renderer/resources/guest_view/extension_view_constants.js b/extensions/renderer/resources/guest_view/extension_view_constants.js
index 573725a..cedf68b 100644
--- a/extensions/renderer/resources/guest_view/extension_view_constants.js
+++ b/extensions/renderer/resources/guest_view/extension_view_constants.js
@@ -7,6 +7,7 @@
 // Container for the extensionview constants.
 var ExtensionViewConstants = {
 
+  ATTRIBUTE_EXTENSION: 'extension',
   ATTRIBUTE_SRC: 'src',
 };
 
diff --git a/extensions/renderer/resources/guest_view/extension_view_events.js b/extensions/renderer/resources/guest_view/extension_view_events.js
new file mode 100644
index 0000000..a30c5fd7
--- /dev/null
+++ b/extensions/renderer/resources/guest_view/extension_view_events.js
@@ -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.
+
+// Event management for ExtensionView.
+
+var EventBindings = require('event_bindings');
+
+var CreateEvent = function(name) {
+  var eventOpts = {supportsListeners: true, supportsFilters: true};
+  return new EventBindings.Event(name, undefined, eventOpts);
+};
+
+var EXTENSION_VIEW_EVENTS = {
+  'loadcommit': {
+    handler: function(event) {
+      ExtensionViewEvents.prototype.handleLoadCommitEvent.call(this, event);
+    },
+    evt: CreateEvent('extensionViewInternal.onLoadCommit'),
+  }
+};
+
+// Constructor.
+function ExtensionViewEvents(extensionViewImpl, viewInstanceId) {
+  this.extensionViewImpl = extensionViewImpl;
+  this.viewInstanceId = viewInstanceId;
+
+  // Set up the events.
+  var events = this.getEvents();
+  for (var eventName in events) {
+    this.setupEvent(eventName, events[eventName]);
+  }
+}
+
+ExtensionViewEvents.prototype.getEvents = function() {
+  return EXTENSION_VIEW_EVENTS;
+};
+
+ExtensionViewEvents.prototype.setupEvent = function(name, info) {
+  info.evt.addListener(function(e) {
+    if (info.handler)
+      info.handler.call(this, e);
+  }.bind(this), {instanceId: this.viewInstanceId});
+};
+
+ExtensionViewEvents.prototype.handleLoadCommitEvent = function(event) {
+  this.extensionViewImpl.onLoadCommit(event.url);
+};
+
+exports.ExtensionViewEvents = ExtensionViewEvents;
diff --git a/extensions/renderer/resources/guest_view/guest_view_container.js b/extensions/renderer/resources/guest_view/guest_view_container.js
index ad555e7c..0107a7e 100644
--- a/extensions/renderer/resources/guest_view/guest_view_container.js
+++ b/extensions/renderer/resources/guest_view/guest_view_container.js
@@ -115,8 +115,9 @@
 GuestViewContainer.prototype.buildParams = function() {
   var params = this.buildContainerParams();
   params['instanceId'] = this.viewInstanceId;
-  params['elementWidth'] = parseInt(this.element.offsetWidth);
-  params['elementHeight'] = parseInt(this.element.offsetHeight);
+  var elementRect = this.element.getBoundingClientRect();
+  params['elementWidth'] = parseInt(elementRect.width);
+  params['elementHeight'] = parseInt(elementRect.height);
   return params;
 };
 
diff --git a/extensions/renderer/resources/guest_view/web_view.js b/extensions/renderer/resources/guest_view/web_view.js
index 32f99f1a..e16555d 100644
--- a/extensions/renderer/resources/guest_view/web_view.js
+++ b/extensions/renderer/resources/guest_view/web_view.js
@@ -241,7 +241,7 @@
   }
 
   var webviewSrc = this.attributes[WebViewConstants.ATTRIBUTE_SRC].getValue();
-  if (this.baseUrlForDataUrl != '') {
+  if (this.baseUrlForDataUrl) {
     webviewSrc = this.baseUrlForDataUrl;
   }
 
diff --git a/extensions/shell/browser/api/vpn_provider/vpn_service_factory.cc b/extensions/shell/browser/api/vpn_provider/vpn_service_factory.cc
index 0a6a150..62145473 100644
--- a/extensions/shell/browser/api/vpn_provider/vpn_service_factory.cc
+++ b/extensions/shell/browser/api/vpn_provider/vpn_service_factory.cc
@@ -5,8 +5,12 @@
 #include "extensions/browser/api/vpn_provider/vpn_service_factory.h"
 
 #include "base/memory/singleton.h"
+#include "chromeos/dbus/dbus_thread_manager.h"
+#include "chromeos/network/network_handler.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
 #include "extensions/browser/api/vpn_provider/vpn_service.h"
+#include "extensions/browser/event_router.h"
+#include "extensions/browser/extension_registry.h"
 
 namespace chromeos {
 
@@ -43,7 +47,13 @@
 
 KeyedService* VpnServiceFactory::BuildServiceInstanceFor(
     content::BrowserContext* context) const {
-  return nullptr;
+  return new VpnService(
+      context, "testuser", extensions::ExtensionRegistry::Get(context),
+      extensions::EventRouter::Get(context),
+      DBusThreadManager::Get()->GetShillThirdPartyVpnDriverClient(),
+      NetworkHandler::Get()->network_configuration_handler(),
+      NetworkHandler::Get()->network_profile_handler(),
+      NetworkHandler::Get()->network_state_handler());
 }
 
 }  // namespace chromeos
diff --git a/extensions/shell/browser/shell_extension_host_delegate.cc b/extensions/shell/browser/shell_extension_host_delegate.cc
index d06363e..51c7334 100644
--- a/extensions/shell/browser/shell_extension_host_delegate.cc
+++ b/extensions/shell/browser/shell_extension_host_delegate.cc
@@ -4,7 +4,9 @@
 
 #include "extensions/shell/browser/shell_extension_host_delegate.h"
 
+#include "base/lazy_instance.h"
 #include "base/logging.h"
+#include "extensions/browser/serial_extension_host_queue.h"
 #include "extensions/shell/browser/media_capture_util.h"
 #include "extensions/shell/browser/shell_extension_web_contents_observer.h"
 
@@ -61,4 +63,11 @@
   return true;
 }
 
+static base::LazyInstance<SerialExtensionHostQueue> g_queue =
+    LAZY_INSTANCE_INITIALIZER;
+
+ExtensionHostQueue* ShellExtensionHostDelegate::GetExtensionHostQueue() const {
+  return g_queue.Pointer();
+}
+
 }  // namespace extensions
diff --git a/extensions/shell/browser/shell_extension_host_delegate.h b/extensions/shell/browser/shell_extension_host_delegate.h
index ba2a364..d91b407 100644
--- a/extensions/shell/browser/shell_extension_host_delegate.h
+++ b/extensions/shell/browser/shell_extension_host_delegate.h
@@ -33,6 +33,7 @@
                                   const GURL& security_origin,
                                   content::MediaStreamType type,
                                   const Extension* extension) override;
+  ExtensionHostQueue* GetExtensionHostQueue() const override;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(ShellExtensionHostDelegate);
diff --git a/extensions/test/data/api_test/hid/api/background.js b/extensions/test/data/api_test/hid/api/background.js
index eef7e2dd..9d8dbb7a 100644
--- a/extensions/test/data/api_test/hid/api/background.js
+++ b/extensions/test/data/api_test/hid/api/background.js
@@ -234,9 +234,20 @@
   });
 }
 
+function testSendOversizeReport() {
+  openDeviceWithReportId(function (connection) {
+    var buffer = stringToArrayBuffer("oversize report");
+    chrome.hid.send(connection, 1, buffer, function () {
+      chrome.test.assertLastError("Transfer failed.");
+      chrome.hid.disconnect(connection);
+      chrome.test.succeed("Caught oversize report.");
+    });
+  });
+}
+
 function testSendWithReportId() {
   openDeviceWithReportId(function (connection) {
-    var buffer = stringToArrayBuffer("This is a HID output report.");
+    var buffer = stringToArrayBuffer("o-report");
     chrome.hid.send(connection, 1, buffer, function () {
       chrome.test.assertNoLastError();
       chrome.hid.disconnect(connection);
@@ -247,7 +258,7 @@
 
 function testSendWithoutReportId() {
   openDeviceWithoutReportId(function (connection) {
-    var buffer = stringToArrayBuffer("This is a HID output report.");
+    var buffer = stringToArrayBuffer("o-report");
     chrome.hid.send(connection, 0, buffer, function () {
       chrome.test.assertNoLastError();
       chrome.hid.disconnect(connection);
@@ -258,7 +269,7 @@
 
 function testSendWithInvalidReportId() {
   openDeviceWithReportId(function (connection) {
-    var buffer = stringToArrayBuffer("This is a HID output report.");
+    var buffer = stringToArrayBuffer("o-report");
     chrome.hid.send(connection, 0, buffer, function () {
       chrome.test.assertLastError("Transfer failed.");
       chrome.hid.disconnect(connection);
@@ -269,7 +280,7 @@
 
 function testSendWithUnexpectedReportId() {
   openDeviceWithoutReportId(function (connection) {
-    var buffer = stringToArrayBuffer("This is a HID output report.");
+    var buffer = stringToArrayBuffer("o-report");
     chrome.hid.send(connection, 1, buffer, function () {
       chrome.test.assertLastError("Transfer failed.");
       chrome.hid.disconnect(connection);
@@ -396,6 +407,7 @@
   testReceiveWithReportId,
   testReceiveWithoutReportId,
   testSendWithInvalidConnectionId,
+  testSendOversizeReport,
   testSendWithReportId,
   testSendWithoutReportId,
   testSendWithInvalidReportId,
diff --git a/extensions/utility/BUILD.gn b/extensions/utility/BUILD.gn
index 1662c08..52f9d83 100644
--- a/extensions/utility/BUILD.gn
+++ b/extensions/utility/BUILD.gn
@@ -13,13 +13,12 @@
                         ".",
                         "//extensions")
 
+  # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+  configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
+
   deps = [
     "//content/public/utility",
     "//extensions/common",
     "//skia",
   ]
-
-  if (is_win) {
-    cflags = [ "/wd4267" ]  # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
-  }
 }
diff --git a/google_apis/BUILD.gn b/google_apis/BUILD.gn
index 9d10b9e..1cdf686f 100644
--- a/google_apis/BUILD.gn
+++ b/google_apis/BUILD.gn
@@ -122,11 +122,10 @@
     "google_api_keys.h",
   ]
 
-  if (is_win) {
-    cflags = [ "/wd4267" ]  # size_t -> int
-  }
-
-  configs += [ ":key_defines" ]
+  configs += [
+    ":key_defines",
+    "//build/config/compiler:no_size_t_to_int_warning",
+  ]
 
   deps = [
     "//base",
diff --git a/google_apis/drive/drive_api_parser.h b/google_apis/drive/drive_api_parser.h
index 450aa36..482d749 100644
--- a/google_apis/drive/drive_api_parser.h
+++ b/google_apis/drive/drive_api_parser.h
@@ -419,7 +419,6 @@
   int rotation_;
 };
 
-
 // FileResource represents a file or folder metadata in Drive.
 // https://developers.google.com/drive/v2/reference/files
 class FileResource {
diff --git a/google_apis/drive/drive_api_requests.cc b/google_apis/drive/drive_api_requests.cc
index 3691a9e0..8e32915 100644
--- a/google_apis/drive/drive_api_requests.cc
+++ b/google_apis/drive/drive_api_requests.cc
@@ -49,6 +49,12 @@
 
 namespace drive {
 
+Property::Property() : visibility_(VISIBILITY_PRIVATE) {
+}
+
+Property::~Property() {
+}
+
 //============================ DriveApiPartialFieldRequest ====================
 
 DriveApiPartialFieldRequest::DriveApiPartialFieldRequest(
@@ -226,6 +232,27 @@
     root.Set("parents", parents_value);
   }
 
+  if (!properties_.empty()) {
+    base::ListValue* properties_value = new base::ListValue;
+    for (const auto& property : properties_) {
+      base::DictionaryValue* const property_value = new base::DictionaryValue;
+      std::string visibility_as_string;
+      switch (property.visibility()) {
+        case Property::VISIBILITY_PRIVATE:
+          visibility_as_string = "PRIVATE";
+          break;
+        case Property::VISIBILITY_PUBLIC:
+          visibility_as_string = "PUBLIC";
+          break;
+      }
+      property_value->SetString("visibility", visibility_as_string);
+      property_value->SetString("key", property.key());
+      property_value->SetString("value", property.value());
+      properties_value->Append(property_value);
+    }
+    root.Set("properties", properties_value);
+  }
+
   base::JSONWriter::Write(&root, upload_content);
   DVLOG(1) << "FilesPatch data: " << *upload_content_type << ", ["
            << *upload_content << "]";
diff --git a/google_apis/drive/drive_api_requests.h b/google_apis/drive/drive_api_requests.h
index 4fa6eaf..33f74a8 100644
--- a/google_apis/drive/drive_api_requests.h
+++ b/google_apis/drive/drive_api_requests.h
@@ -6,6 +6,7 @@
 #define GOOGLE_APIS_DRIVE_DRIVE_API_REQUESTS_H_
 
 #include <string>
+#include <vector>
 
 #include "base/callback_forward.h"
 #include "base/location.h"
@@ -32,6 +33,38 @@
 
 namespace drive {
 
+// Represents a property for a file or a directory.
+// https://developers.google.com/drive/v2/reference/properties
+class Property {
+ public:
+  Property();
+  ~Property();
+
+  // Visibility of the property. Either limited to the same client, or to any.
+  enum Visibility { VISIBILITY_PRIVATE, VISIBILITY_PUBLIC };
+
+  // Whether the property is public or private.
+  Visibility visibility() const { return visibility_; }
+
+  // Name of the property.
+  const std::string& key() const { return key_; }
+
+  // Value of the property.
+  const std::string& value() const { return value_; }
+
+  void set_visibility(Visibility visibility) { visibility_ = visibility; }
+  void set_key(const std::string& key) { key_ = key; }
+  void set_value(const std::string& value) { value_ = value; }
+
+ private:
+  Visibility visibility_;
+  std::string key_;
+  std::string value_;
+};
+
+// List of properties for a single file or a directory.
+typedef std::vector<Property> Properties;
+
 //============================ DriveApiPartialFieldRequest ====================
 
 // This is base class of the Drive API related requests. All Drive API requests
@@ -306,6 +339,11 @@
   const std::vector<std::string>& parents() const { return parents_; }
   void add_parent(const std::string& parent) { parents_.push_back(parent); }
 
+  const Properties& properties() const { return properties_; }
+  void set_properties(const Properties& properties) {
+    properties_ = properties;
+  }
+
  protected:
   // Overridden from URLFetchRequestBase.
   net::URLFetcher::RequestType GetRequestType() const override;
@@ -327,6 +365,7 @@
   base::Time modified_date_;
   base::Time last_viewed_by_me_date_;
   std::vector<std::string> parents_;
+  Properties properties_;
 
   DISALLOW_COPY_AND_ASSIGN(FilesPatchRequest);
 };
diff --git a/google_apis/drive/drive_api_requests_unittest.cc b/google_apis/drive/drive_api_requests_unittest.cc
index c4cab5b1..deb2845 100644
--- a/google_apis/drive/drive_api_requests_unittest.cc
+++ b/google_apis/drive/drive_api_requests_unittest.cc
@@ -511,6 +511,20 @@
         base::Time::FromUTCExploded(kLastViewedByMeDate));
     request->add_parent("parent_resource_id");
 
+    drive::Property private_property;
+    private_property.set_key("key1");
+    private_property.set_value("value1");
+
+    drive::Property public_property;
+    public_property.set_visibility(drive::Property::VISIBILITY_PUBLIC);
+    public_property.set_key("key2");
+    public_property.set_value("value2");
+
+    drive::Properties properties;
+    properties.push_back(private_property);
+    properties.push_back(public_property);
+    request->set_properties(properties);
+
     request_sender_->StartRequestWithRetry(request);
     run_loop.Run();
   }
@@ -523,11 +537,15 @@
 
   EXPECT_EQ("application/json", http_request_.headers["Content-Type"]);
   EXPECT_TRUE(http_request_.has_content);
-  EXPECT_EQ("{\"lastViewedByMeDate\":\"2013-07-19T15:59:13.123Z\","
-            "\"modifiedDate\":\"2012-07-19T15:59:13.123Z\","
-            "\"parents\":[{\"id\":\"parent_resource_id\"}],"
-            "\"title\":\"new title\"}",
-            http_request_.content);
+  EXPECT_EQ(
+      "{\"lastViewedByMeDate\":\"2013-07-19T15:59:13.123Z\","
+      "\"modifiedDate\":\"2012-07-19T15:59:13.123Z\","
+      "\"parents\":[{\"id\":\"parent_resource_id\"}],"
+      "\"properties\":["
+      "{\"key\":\"key1\",\"value\":\"value1\",\"visibility\":\"PRIVATE\"},"
+      "{\"key\":\"key2\",\"value\":\"value2\",\"visibility\":\"PUBLIC\"}],"
+      "\"title\":\"new title\"}",
+      http_request_.content);
   EXPECT_TRUE(file_resource);
 }
 
diff --git a/google_apis/gcm/engine/connection_factory_impl.cc b/google_apis/gcm/engine/connection_factory_impl.cc
index a8ec37fe..218f833 100644
--- a/google_apis/gcm/engine/connection_factory_impl.cc
+++ b/google_apis/gcm/engine/connection_factory_impl.cc
@@ -7,7 +7,6 @@
 #include "base/message_loop/message_loop.h"
 #include "base/metrics/histogram.h"
 #include "base/metrics/sparse_histogram.h"
-#include "base/profiler/scoped_tracker.h"
 #include "google_apis/gcm/engine/connection_handler_impl.h"
 #include "google_apis/gcm/monitoring/gcm_stats_recorder.h"
 #include "google_apis/gcm/protocol/mcs.pb.h"
@@ -343,10 +342,6 @@
 }
 
 void ConnectionFactoryImpl::OnConnectDone(int result) {
-  // TODO(pkasting): Remove ScopedTracker below once crbug.com/455884 is fixed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "455884 ConnectionFactoryImpl::OnConnectDone"));
   if (result != net::OK) {
     // If the connection fails, try another proxy.
     result = ReconsiderProxyAfterError(result);
diff --git a/gpu/BUILD.gn b/gpu/BUILD.gn
index f7bb98bd..86888bd 100644
--- a/gpu/BUILD.gn
+++ b/gpu/BUILD.gn
@@ -99,6 +99,8 @@
 
     defines = [ "GL_GLEXT_PROTOTYPES" ]
 
+    configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
+
     deps = [
       ":gpu",
       ":test_support",
@@ -228,6 +230,8 @@
       "config/gpu_util_unittest.cc",
     ]
 
+    configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
+
     deps = [
       ":gpu",
       ":test_support",
diff --git a/gpu/GLES2/gl2chromium_autogen.h b/gpu/GLES2/gl2chromium_autogen.h
index a36a026..93f3c47b 100644
--- a/gpu/GLES2/gl2chromium_autogen.h
+++ b/gpu/GLES2/gl2chromium_autogen.h
@@ -40,6 +40,7 @@
 #define glClearColor GLES2_GET_FUN(ClearColor)
 #define glClearDepthf GLES2_GET_FUN(ClearDepthf)
 #define glClearStencil GLES2_GET_FUN(ClearStencil)
+#define glClientWaitSync GLES2_GET_FUN(ClientWaitSync)
 #define glColorMask GLES2_GET_FUN(ColorMask)
 #define glCompileShader GLES2_GET_FUN(CompileShader)
 #define glCompressedTexImage2D GLES2_GET_FUN(CompressedTexImage2D)
@@ -222,6 +223,7 @@
 #define glVertexAttribIPointer GLES2_GET_FUN(VertexAttribIPointer)
 #define glVertexAttribPointer GLES2_GET_FUN(VertexAttribPointer)
 #define glViewport GLES2_GET_FUN(Viewport)
+#define glWaitSync GLES2_GET_FUN(WaitSync)
 #define glBlitFramebufferCHROMIUM GLES2_GET_FUN(BlitFramebufferCHROMIUM)
 #define glRenderbufferStorageMultisampleCHROMIUM \
   GLES2_GET_FUN(RenderbufferStorageMultisampleCHROMIUM)
diff --git a/gpu/PRESUBMIT.py b/gpu/PRESUBMIT.py
deleted file mode 100644
index 53fc153..0000000
--- a/gpu/PRESUBMIT.py
+++ /dev/null
@@ -1,16 +0,0 @@
-# Copyright (c) 2014 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-"""Top-level presubmit script for gpu/.
-
-See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts
-for more details about the presubmit API built into depot_tools.
-"""
-
-def GetPreferredTryMasters(project, change):
-  return {
-    'tryserver.chromium.gpu': {
-      'win_gpu': set(['defaulttests']),
-    }
-  }
diff --git a/gpu/blink/BUILD.gn b/gpu/blink/BUILD.gn
index 464061c..7d08ba11 100644
--- a/gpu/blink/BUILD.gn
+++ b/gpu/blink/BUILD.gn
@@ -14,6 +14,9 @@
     "webgraphicscontext3d_in_process_command_buffer_impl.h",
   ]
 
+  # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+  configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
+
   defines = [ "GPU_BLINK_IMPLEMENTATION" ]
 
   deps = [
@@ -33,9 +36,4 @@
     "//ui/gfx",
     "//ui/gfx/geometry",
   ]
-
-  if (is_win) {
-    # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
-    cflags = [ "/wd4267" ]
-  }
 }
diff --git a/gpu/command_buffer/build_gles2_cmd_buffer.py b/gpu/command_buffer/build_gles2_cmd_buffer.py
index ca39150..141d259 100755
--- a/gpu/command_buffer/build_gles2_cmd_buffer.py
+++ b/gpu/command_buffer/build_gles2_cmd_buffer.py
@@ -1429,6 +1429,17 @@
       '1',
     ],
   },
+  'SyncFlushFlags': {
+    'type': 'GLbitfield',
+    'is_complete': True,
+    'valid': [
+      'GL_SYNC_FLUSH_COMMANDS_BIT',
+      '0',
+    ],
+    'invalid': [
+      '0xFFFFFFFF',
+    ],
+  },
 }
 
 # This table specifies the different pepper interfaces that are supported for
@@ -1642,6 +1653,14 @@
       '0': '0.5f'
     },
   },
+  'ClientWaitSync': {
+    'type': 'Custom',
+    'data_transfer_methods': ['shm'],
+    'cmd_args': 'GLuint sync, GLbitfieldSyncFlushFlags flags, '
+                'GLuint timeout_0, GLuint timeout_1, GLenum* result',
+    'unsafe': True,
+    'result': ['GLenum'],
+  },
   'ColorMask': {
     'type': 'StateSet',
     'state': 'ColorMask',
@@ -2909,6 +2928,14 @@
                 'GLsizei stride, GLuint offset',
     'client_test': False,
   },
+  'WaitSync': {
+    'type': 'Custom',
+    'cmd_args': 'GLuint sync, GLbitfieldSyncFlushFlags flags, '
+                'GLuint timeout_0, GLuint timeout_1',
+    'impl_func': False,
+    'client_test': False,
+    'unsafe': True,
+  },
   'Scissor': {
     'type': 'StateSet',
     'state': 'Scissor',
@@ -8971,11 +8998,12 @@
     # Forward declaration of a few enums used in constant argument
     # to avoid including GL header files.
     enum_defines = {
-        'GL_SYNC_GPU_COMMANDS_COMPLETE': 0x9117,
+        'GL_SYNC_GPU_COMMANDS_COMPLETE': '0x9117',
+        'GL_SYNC_FLUSH_COMMANDS_BIT': '0x00000001',
       }
     file.Write('\n')
     for enum in enum_defines:
-      file.Write("#define %s 0x%x\n" % (enum, enum_defines[enum]))
+      file.Write("#define %s %s\n" % (enum, enum_defines[enum]))
     file.Write('\n')
     for func in self.functions:
       if True:
diff --git a/gpu/command_buffer/client/BUILD.gn b/gpu/command_buffer/client/BUILD.gn
index 2d28d1af..a5532cb 100644
--- a/gpu/command_buffer/client/BUILD.gn
+++ b/gpu/command_buffer/client/BUILD.gn
@@ -19,10 +19,8 @@
 
   defines = [ "GPU_IMPLEMENTATION" ]
 
-  if (is_win) {
-    # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
-    cflags = [ "/wd4267" ]  # size_t to int truncation.
-  }
+  # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+  configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
 
   all_dependent_configs = [ "//third_party/khronos:khronos_headers" ]
 
@@ -56,10 +54,8 @@
 
   defines = [ "GPU_IMPLEMENTATION" ]
 
-  if (is_win) {
-    # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
-    cflags = [ "/wd4267" ]  # size_t to int truncation.
-  }
+  # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+  configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
 
   deps = [
     ":client",
@@ -129,14 +125,12 @@
 component("gles2_implementation") {
   sources = gles2_implementation_source_files
 
+  # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+  configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
+
   defines = [ "GLES2_IMPL_IMPLEMENTATION" ]
   all_dependent_configs = [ "//third_party/khronos:khronos_headers" ]
 
-  if (is_win) {
-    # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
-    cflags = [ "/wd4267" ]  # size_t to int truncation.
-  }
-
   deps = [
     ":gles2_cmd_helper",
     ":gles2_interface",
@@ -171,10 +165,8 @@
   sources = gles2_c_lib_source_files
   defines = [ "GLES2_C_LIB_IMPLEMENTATION" ]
 
-  if (is_win) {
-    # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
-    cflags = [ "/wd4267" ]  # size_t to int truncation.
-  }
+  # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+  configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
 
   deps = [
     ":client",
diff --git a/gpu/command_buffer/client/gles2_c_lib_autogen.h b/gpu/command_buffer/client/gles2_c_lib_autogen.h
index 23d3d6ea..803e66f 100644
--- a/gpu/command_buffer/client/gles2_c_lib_autogen.h
+++ b/gpu/command_buffer/client/gles2_c_lib_autogen.h
@@ -119,6 +119,9 @@
 void GLES2ClearStencil(GLint s) {
   gles2::GetGLContext()->ClearStencil(s);
 }
+GLenum GLES2ClientWaitSync(GLsync sync, GLbitfield flags, GLuint64 timeout) {
+  return gles2::GetGLContext()->ClientWaitSync(sync, flags, timeout);
+}
 void GLES2ColorMask(GLboolean red,
                     GLboolean green,
                     GLboolean blue,
@@ -929,6 +932,9 @@
 void GLES2Viewport(GLint x, GLint y, GLsizei width, GLsizei height) {
   gles2::GetGLContext()->Viewport(x, y, width, height);
 }
+void GLES2WaitSync(GLsync sync, GLbitfield flags, GLuint64 timeout) {
+  gles2::GetGLContext()->WaitSync(sync, flags, timeout);
+}
 void GLES2BlitFramebufferCHROMIUM(GLint srcX0,
                                   GLint srcY0,
                                   GLint srcX1,
@@ -1421,6 +1427,10 @@
      reinterpret_cast<GLES2FunctionPointer>(glClearStencil),
     },
     {
+     "glClientWaitSync",
+     reinterpret_cast<GLES2FunctionPointer>(glClientWaitSync),
+    },
+    {
      "glColorMask",
      reinterpret_cast<GLES2FunctionPointer>(glColorMask),
     },
@@ -2146,6 +2156,10 @@
      reinterpret_cast<GLES2FunctionPointer>(glViewport),
     },
     {
+     "glWaitSync",
+     reinterpret_cast<GLES2FunctionPointer>(glWaitSync),
+    },
+    {
      "glBlitFramebufferCHROMIUM",
      reinterpret_cast<GLES2FunctionPointer>(glBlitFramebufferCHROMIUM),
     },
diff --git a/gpu/command_buffer/client/gles2_cmd_helper_autogen.h b/gpu/command_buffer/client/gles2_cmd_helper_autogen.h
index bda8bb3b..c58ce2c8 100644
--- a/gpu/command_buffer/client/gles2_cmd_helper_autogen.h
+++ b/gpu/command_buffer/client/gles2_cmd_helper_autogen.h
@@ -240,6 +240,19 @@
   }
 }
 
+void ClientWaitSync(GLuint sync,
+                    GLbitfield flags,
+                    GLuint timeout_0,
+                    GLuint timeout_1,
+                    uint32_t result_shm_id,
+                    uint32_t result_shm_offset) {
+  gles2::cmds::ClientWaitSync* c = GetCmdSpace<gles2::cmds::ClientWaitSync>();
+  if (c) {
+    c->Init(sync, flags, timeout_0, timeout_1, result_shm_id,
+            result_shm_offset);
+  }
+}
+
 void ColorMask(GLboolean red,
                GLboolean green,
                GLboolean blue,
@@ -1982,6 +1995,16 @@
   }
 }
 
+void WaitSync(GLuint sync,
+              GLbitfield flags,
+              GLuint timeout_0,
+              GLuint timeout_1) {
+  gles2::cmds::WaitSync* c = GetCmdSpace<gles2::cmds::WaitSync>();
+  if (c) {
+    c->Init(sync, flags, timeout_0, timeout_1);
+  }
+}
+
 void BlitFramebufferCHROMIUM(GLint srcX0,
                              GLint srcY0,
                              GLint srcX1,
diff --git a/gpu/command_buffer/client/gles2_implementation.cc b/gpu/command_buffer/client/gles2_implementation.cc
index e853766..2dd95ec 100644
--- a/gpu/command_buffer/client/gles2_implementation.cc
+++ b/gpu/command_buffer/client/gles2_implementation.cc
@@ -4930,6 +4930,39 @@
   CheckGLError();
 }
 
+GLenum GLES2Implementation::ClientWaitSync(
+    GLsync sync, GLbitfield flags, GLuint64 timeout) {
+  GPU_CLIENT_SINGLE_THREAD_CHECK();
+  GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glClientWaitSync(" << sync
+                 << ", " << flags << ", " << timeout << ")");
+  typedef cmds::ClientWaitSync::Result Result;
+  Result* result = GetResultAs<Result*>();
+  if (!result) {
+    SetGLError(GL_OUT_OF_MEMORY, "ClientWaitSync", "");
+    return GL_WAIT_FAILED;
+  }
+  *result = GL_WAIT_FAILED;
+  uint32_t v32_0 = 0, v32_1 = 0;
+  GLES2Util::MapUint64ToTwoUint32(timeout, &v32_0, &v32_1);
+  helper_->ClientWaitSync(
+      ToGLuint(sync), flags, v32_0, v32_1,
+      GetResultShmId(), GetResultShmOffset());
+  WaitForCmd();
+  GPU_CLIENT_LOG("returned " << *result);
+  CheckGLError();
+  return *result;
+}
+
+void GLES2Implementation::WaitSync(
+    GLsync sync, GLbitfield flags, GLuint64 timeout) {
+  GPU_CLIENT_SINGLE_THREAD_CHECK();
+  GPU_CLIENT_LOG("[" << GetLogPrefix() << "] glWaitSync(" << sync << ", "
+                 << flags << ", " << timeout << ")");
+  uint32_t v32_0 = 0, v32_1 = 0;
+  GLES2Util::MapUint64ToTwoUint32(timeout, &v32_0, &v32_1);
+  helper_->WaitSync(ToGLuint(sync), flags, v32_0, v32_1);
+  CheckGLError();
+}
 
 // Include the auto-generated part of this file. We split this because it means
 // we can easily edit the non-auto generated parts right here in this file
diff --git a/gpu/command_buffer/client/gles2_implementation_autogen.h b/gpu/command_buffer/client/gles2_implementation_autogen.h
index 9d2d8eb..5c994e36 100644
--- a/gpu/command_buffer/client/gles2_implementation_autogen.h
+++ b/gpu/command_buffer/client/gles2_implementation_autogen.h
@@ -97,6 +97,8 @@
 
 void ClearStencil(GLint s) override;
 
+GLenum ClientWaitSync(GLsync sync, GLbitfield flags, GLuint64 timeout) override;
+
 void ColorMask(GLboolean red,
                GLboolean green,
                GLboolean blue,
@@ -702,6 +704,8 @@
 
 void Viewport(GLint x, GLint y, GLsizei width, GLsizei height) override;
 
+void WaitSync(GLsync sync, GLbitfield flags, GLuint64 timeout) override;
+
 void BlitFramebufferCHROMIUM(GLint srcX0,
                              GLint srcY0,
                              GLint srcX1,
diff --git a/gpu/command_buffer/client/gles2_implementation_unittest.cc b/gpu/command_buffer/client/gles2_implementation_unittest.cc
index 52a2cef..2acbc635 100644
--- a/gpu/command_buffer/client/gles2_implementation_unittest.cc
+++ b/gpu/command_buffer/client/gles2_implementation_unittest.cc
@@ -3583,7 +3583,7 @@
   expected.cmd.Init(kCap, result1.id, result1.offset);
 
   EXPECT_CALL(*command_buffer(), OnFlush())
-      .WillOnce(SetMemory(result1.ptr, uint32_t(kCap)))
+      .WillOnce(SetMemory(result1.ptr, uint32_t(GL_TRUE)))
       .RetiresOnSaturation();
 
   GLboolean result = gl_->IsEnabled(kCap);
@@ -3591,6 +3591,47 @@
   EXPECT_TRUE(result);
 }
 
+TEST_F(GLES2ImplementationTest, ClientWaitSync) {
+  const GLuint client_sync_id = 36;
+  struct Cmds {
+    cmds::ClientWaitSync cmd;
+  };
+
+  Cmds expected;
+  ExpectedMemoryInfo result1 =
+      GetExpectedResultMemory(sizeof(cmds::ClientWaitSync::Result));
+  const GLuint64 kTimeout = 0xABCDEF0123456789;
+  uint32_t v32_0 = 0, v32_1 = 0;
+  GLES2Util::MapUint64ToTwoUint32(kTimeout, &v32_0, &v32_1);
+  expected.cmd.Init(client_sync_id, GL_SYNC_FLUSH_COMMANDS_BIT,
+                    v32_0, v32_1, result1.id, result1.offset);
+
+  EXPECT_CALL(*command_buffer(), OnFlush())
+      .WillOnce(SetMemory(result1.ptr, uint32_t(GL_CONDITION_SATISFIED)))
+      .RetiresOnSaturation();
+
+  GLenum result = gl_->ClientWaitSync(
+      reinterpret_cast<GLsync>(client_sync_id), GL_SYNC_FLUSH_COMMANDS_BIT,
+      kTimeout);
+  EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected)));
+  EXPECT_EQ(static_cast<GLenum>(GL_CONDITION_SATISFIED), result);
+}
+
+TEST_F(GLES2ImplementationTest, WaitSync) {
+  const GLuint kClientSyncId = 36;
+  struct Cmds {
+    cmds::WaitSync cmd;
+  };
+  Cmds expected;
+  const GLuint64 kTimeout = GL_TIMEOUT_IGNORED;
+  uint32_t v32_0 = 0, v32_1 = 0;
+  GLES2Util::MapUint64ToTwoUint32(kTimeout, &v32_0, &v32_1);
+  expected.cmd.Init(kClientSyncId, 0, v32_0, v32_1);
+
+  gl_->WaitSync(reinterpret_cast<GLsync>(kClientSyncId), 0, kTimeout);
+  EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected)));
+}
+
 TEST_F(GLES2ImplementationManualInitTest, LoseContextOnOOM) {
   ContextInitOptions init_options;
   init_options.lose_context_when_out_of_memory = true;
diff --git a/gpu/command_buffer/client/gles2_implementation_unittest_autogen.h b/gpu/command_buffer/client/gles2_implementation_unittest_autogen.h
index 8b02ad9..8149a96 100644
--- a/gpu/command_buffer/client/gles2_implementation_unittest_autogen.h
+++ b/gpu/command_buffer/client/gles2_implementation_unittest_autogen.h
@@ -287,6 +287,7 @@
   gl_->ClearStencil(1);
   EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected)));
 }
+// TODO(zmo): Implement unit test for ClientWaitSync
 
 TEST_F(GLES2ImplementationTest, ColorMask) {
   struct Cmds {
diff --git a/gpu/command_buffer/client/gles2_interface_autogen.h b/gpu/command_buffer/client/gles2_interface_autogen.h
index 990e701..623c413 100644
--- a/gpu/command_buffer/client/gles2_interface_autogen.h
+++ b/gpu/command_buffer/client/gles2_interface_autogen.h
@@ -70,6 +70,9 @@
                         GLclampf alpha) = 0;
 virtual void ClearDepthf(GLclampf depth) = 0;
 virtual void ClearStencil(GLint s) = 0;
+virtual GLenum ClientWaitSync(GLsync sync,
+                              GLbitfield flags,
+                              GLuint64 timeout) = 0;
 virtual void ColorMask(GLboolean red,
                        GLboolean green,
                        GLboolean blue,
@@ -514,6 +517,7 @@
                                  GLsizei stride,
                                  const void* ptr) = 0;
 virtual void Viewport(GLint x, GLint y, GLsizei width, GLsizei height) = 0;
+virtual void WaitSync(GLsync sync, GLbitfield flags, GLuint64 timeout) = 0;
 virtual void BlitFramebufferCHROMIUM(GLint srcX0,
                                      GLint srcY0,
                                      GLint srcX1,
diff --git a/gpu/command_buffer/client/gles2_interface_stub_autogen.h b/gpu/command_buffer/client/gles2_interface_stub_autogen.h
index f020e142..31d96366 100644
--- a/gpu/command_buffer/client/gles2_interface_stub_autogen.h
+++ b/gpu/command_buffer/client/gles2_interface_stub_autogen.h
@@ -69,6 +69,7 @@
                 GLclampf alpha) override;
 void ClearDepthf(GLclampf depth) override;
 void ClearStencil(GLint s) override;
+GLenum ClientWaitSync(GLsync sync, GLbitfield flags, GLuint64 timeout) override;
 void ColorMask(GLboolean red,
                GLboolean green,
                GLboolean blue,
@@ -501,6 +502,7 @@
                          GLsizei stride,
                          const void* ptr) override;
 void Viewport(GLint x, GLint y, GLsizei width, GLsizei height) override;
+void WaitSync(GLsync sync, GLbitfield flags, GLuint64 timeout) override;
 void BlitFramebufferCHROMIUM(GLint srcX0,
                              GLint srcY0,
                              GLint srcX1,
diff --git a/gpu/command_buffer/client/gles2_interface_stub_impl_autogen.h b/gpu/command_buffer/client/gles2_interface_stub_impl_autogen.h
index 15f2912..d7aa072 100644
--- a/gpu/command_buffer/client/gles2_interface_stub_impl_autogen.h
+++ b/gpu/command_buffer/client/gles2_interface_stub_impl_autogen.h
@@ -105,6 +105,11 @@
 }
 void GLES2InterfaceStub::ClearStencil(GLint /* s */) {
 }
+GLenum GLES2InterfaceStub::ClientWaitSync(GLsync /* sync */,
+                                          GLbitfield /* flags */,
+                                          GLuint64 /* timeout */) {
+  return 0;
+}
 void GLES2InterfaceStub::ColorMask(GLboolean /* red */,
                                    GLboolean /* green */,
                                    GLboolean /* blue */,
@@ -864,6 +869,10 @@
                                   GLsizei /* width */,
                                   GLsizei /* height */) {
 }
+void GLES2InterfaceStub::WaitSync(GLsync /* sync */,
+                                  GLbitfield /* flags */,
+                                  GLuint64 /* timeout */) {
+}
 void GLES2InterfaceStub::BlitFramebufferCHROMIUM(GLint /* srcX0 */,
                                                  GLint /* srcY0 */,
                                                  GLint /* srcX1 */,
diff --git a/gpu/command_buffer/client/gles2_trace_implementation_autogen.h b/gpu/command_buffer/client/gles2_trace_implementation_autogen.h
index 66deca4..33626f7 100644
--- a/gpu/command_buffer/client/gles2_trace_implementation_autogen.h
+++ b/gpu/command_buffer/client/gles2_trace_implementation_autogen.h
@@ -69,6 +69,7 @@
                 GLclampf alpha) override;
 void ClearDepthf(GLclampf depth) override;
 void ClearStencil(GLint s) override;
+GLenum ClientWaitSync(GLsync sync, GLbitfield flags, GLuint64 timeout) override;
 void ColorMask(GLboolean red,
                GLboolean green,
                GLboolean blue,
@@ -501,6 +502,7 @@
                          GLsizei stride,
                          const void* ptr) override;
 void Viewport(GLint x, GLint y, GLsizei width, GLsizei height) override;
+void WaitSync(GLsync sync, GLbitfield flags, GLuint64 timeout) override;
 void BlitFramebufferCHROMIUM(GLint srcX0,
                              GLint srcY0,
                              GLint srcX1,
diff --git a/gpu/command_buffer/client/gles2_trace_implementation_impl_autogen.h b/gpu/command_buffer/client/gles2_trace_implementation_impl_autogen.h
index 79fd529..8e6faa7 100644
--- a/gpu/command_buffer/client/gles2_trace_implementation_impl_autogen.h
+++ b/gpu/command_buffer/client/gles2_trace_implementation_impl_autogen.h
@@ -183,6 +183,13 @@
   gl_->ClearStencil(s);
 }
 
+GLenum GLES2TraceImplementation::ClientWaitSync(GLsync sync,
+                                                GLbitfield flags,
+                                                GLuint64 timeout) {
+  TRACE_EVENT_BINARY_EFFICIENT0("gpu", "GLES2Trace::ClientWaitSync");
+  return gl_->ClientWaitSync(sync, flags, timeout);
+}
+
 void GLES2TraceImplementation::ColorMask(GLboolean red,
                                          GLboolean green,
                                          GLboolean blue,
@@ -1467,6 +1474,13 @@
   gl_->Viewport(x, y, width, height);
 }
 
+void GLES2TraceImplementation::WaitSync(GLsync sync,
+                                        GLbitfield flags,
+                                        GLuint64 timeout) {
+  TRACE_EVENT_BINARY_EFFICIENT0("gpu", "GLES2Trace::WaitSync");
+  gl_->WaitSync(sync, flags, timeout);
+}
+
 void GLES2TraceImplementation::BlitFramebufferCHROMIUM(GLint srcX0,
                                                        GLint srcY0,
                                                        GLint srcX1,
diff --git a/gpu/command_buffer/client/program_info_manager.cc b/gpu/command_buffer/client/program_info_manager.cc
index 779d914a..89e8913 100644
--- a/gpu/command_buffer/client/program_info_manager.cc
+++ b/gpu/command_buffer/client/program_info_manager.cc
@@ -332,11 +332,10 @@
   if (!link_status_) {
     return;
   }
-  attrib_infos_.clear();
-  uniform_infos_.clear();
-  frag_data_locations_.clear();
-  max_attrib_name_length_ = 0;
-  max_uniform_name_length_ = 0;
+  DCHECK_EQ(0u, attrib_infos_.size());
+  DCHECK_EQ(0u, uniform_infos_.size());
+  DCHECK_EQ(0, max_attrib_name_length_);
+  DCHECK_EQ(0, max_uniform_name_length_);
   const ProgramInput* inputs = LocalGetAs<const ProgramInput*>(
       result, sizeof(*header),
       sizeof(ProgramInput) * (header->num_attribs + header->num_uniforms));
@@ -382,8 +381,8 @@
     // This should only happen on a lost context.
     return;
   }
-  uniform_blocks_.clear();
-  active_uniform_block_max_name_length_ = 0;
+  DCHECK_EQ(0u, uniform_blocks_.size());
+  DCHECK_EQ(0u, active_uniform_block_max_name_length_);
 
   // |result| comes from GPU process. We consider it trusted data. Therefore,
   // no need to check for overflows as the GPU side did the checks already.
@@ -453,7 +452,7 @@
     // This should only happen on a lost context.
     return;
   }
-  uniforms_es3_.clear();
+  DCHECK_EQ(0u, uniforms_es3_.size());
 
   // |result| comes from GPU process. We consider it trusted data. Therefore,
   // no need to check for overflows as the GPU side did the checks already.
@@ -495,8 +494,8 @@
     // This should only happen on a lost context.
     return;
   }
-  transform_feedback_varyings_.clear();
-  transform_feedback_varying_max_length_ = 0;
+  DCHECK_EQ(0u, transform_feedback_varyings_.size());
+  DCHECK_EQ(0u, transform_feedback_varying_max_length_);
 
   // |result| comes from GPU process. We consider it trusted data. Therefore,
   // no need to check for overflows as the GPU side did the checks already.
diff --git a/gpu/command_buffer/cmd_buffer_functions.txt b/gpu/command_buffer/cmd_buffer_functions.txt
index 3e8d902..570eff1 100644
--- a/gpu/command_buffer/cmd_buffer_functions.txt
+++ b/gpu/command_buffer/cmd_buffer_functions.txt
@@ -31,6 +31,7 @@
 GL_APICALL void         GL_APIENTRY glClearColor (GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha);
 GL_APICALL void         GL_APIENTRY glClearDepthf (GLclampf depth);
 GL_APICALL void         GL_APIENTRY glClearStencil (GLint s);
+GL_APICALL GLenum       GL_APIENTRY glClientWaitSync (GLsync sync, GLbitfieldSyncFlushFlags flags, GLuint64 timeout);
 GL_APICALL void         GL_APIENTRY glColorMask (GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha);
 GL_APICALL void         GL_APIENTRY glCompileShader (GLidShader shader);
 GL_APICALL void         GL_APIENTRY glCompressedTexImage2D (GLenumTextureTarget target, GLint level, GLenumCompressedTextureFormat internalformat, GLsizei width, GLsizei height, GLintTextureBorder border, GLsizei imageSize, const void* data);
@@ -212,6 +213,7 @@
 GL_APICALL void         GL_APIENTRY glVertexAttribIPointer (GLuint indx, GLintVertexAttribSize size, GLenumVertexAttribType type, GLsizei stride, const void* ptr);
 GL_APICALL void         GL_APIENTRY glVertexAttribPointer (GLuint indx, GLintVertexAttribSize size, GLenumVertexAttribType type, GLboolean normalized, GLsizei stride, const void* ptr);
 GL_APICALL void         GL_APIENTRY glViewport (GLint x, GLint y, GLsizei width, GLsizei height);
+GL_APICALL void         GL_APIENTRY glWaitSync (GLsync sync, GLbitfieldSyncFlushFlags flags, GLuint64 timeout);
 GL_APICALL void         GL_APIENTRY glBlitFramebufferCHROMIUM (GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenumBlitFilter filter);
 GL_APICALL void         GL_APIENTRY glRenderbufferStorageMultisampleCHROMIUM (GLenumRenderBufferTarget target, GLsizei samples, GLenumRenderBufferFormat internalformat, GLsizei width, GLsizei height);
 GL_APICALL void         GL_APIENTRY glRenderbufferStorageMultisampleEXT (GLenumRenderBufferTarget target, GLsizei samples, GLenumRenderBufferFormat internalformat, GLsizei width, GLsizei height);
diff --git a/gpu/command_buffer/common/gles2_cmd_format.h b/gpu/command_buffer/common/gles2_cmd_format.h
index 944edfd..d0faf14e 100644
--- a/gpu/command_buffer/common/gles2_cmd_format.h
+++ b/gpu/command_buffer/common/gles2_cmd_format.h
@@ -43,6 +43,7 @@
 typedef khronos_intptr_t GLintptr;
 typedef khronos_ssize_t  GLsizeiptr;
 typedef struct __GLsync *GLsync;
+typedef uint64_t GLuint64;
 
 namespace gpu {
 namespace gles2 {
diff --git a/gpu/command_buffer/common/gles2_cmd_format_autogen.h b/gpu/command_buffer/common/gles2_cmd_format_autogen.h
index cb88581..76ca3dd 100644
--- a/gpu/command_buffer/common/gles2_cmd_format_autogen.h
+++ b/gpu/command_buffer/common/gles2_cmd_format_autogen.h
@@ -11,6 +11,7 @@
 #ifndef GPU_COMMAND_BUFFER_COMMON_GLES2_CMD_FORMAT_AUTOGEN_H_
 #define GPU_COMMAND_BUFFER_COMMON_GLES2_CMD_FORMAT_AUTOGEN_H_
 
+#define GL_SYNC_FLUSH_COMMANDS_BIT 0x00000001
 #define GL_SYNC_GPU_COMMANDS_COMPLETE 0x9117
 
 struct ActiveTexture {
@@ -1169,6 +1170,73 @@
 static_assert(offsetof(ClearStencil, s) == 4,
               "offset of ClearStencil s should be 4");
 
+struct ClientWaitSync {
+  typedef ClientWaitSync ValueType;
+  static const CommandId kCmdId = kClientWaitSync;
+  static const cmd::ArgFlags kArgFlags = cmd::kFixed;
+  static const uint8 cmd_flags = CMD_FLAG_SET_TRACE_LEVEL(3);
+
+  typedef GLenum Result;
+
+  static uint32_t ComputeSize() {
+    return static_cast<uint32_t>(sizeof(ValueType));  // NOLINT
+  }
+
+  void SetHeader() { header.SetCmd<ValueType>(); }
+
+  void Init(GLuint _sync,
+            GLbitfield _flags,
+            GLuint _timeout_0,
+            GLuint _timeout_1,
+            uint32_t _result_shm_id,
+            uint32_t _result_shm_offset) {
+    SetHeader();
+    sync = _sync;
+    flags = _flags;
+    timeout_0 = _timeout_0;
+    timeout_1 = _timeout_1;
+    result_shm_id = _result_shm_id;
+    result_shm_offset = _result_shm_offset;
+  }
+
+  void* Set(void* cmd,
+            GLuint _sync,
+            GLbitfield _flags,
+            GLuint _timeout_0,
+            GLuint _timeout_1,
+            uint32_t _result_shm_id,
+            uint32_t _result_shm_offset) {
+    static_cast<ValueType*>(cmd)->Init(_sync, _flags, _timeout_0, _timeout_1,
+                                       _result_shm_id, _result_shm_offset);
+    return NextCmdAddress<ValueType>(cmd);
+  }
+
+  gpu::CommandHeader header;
+  uint32_t sync;
+  uint32_t flags;
+  uint32_t timeout_0;
+  uint32_t timeout_1;
+  uint32_t result_shm_id;
+  uint32_t result_shm_offset;
+};
+
+static_assert(sizeof(ClientWaitSync) == 28,
+              "size of ClientWaitSync should be 28");
+static_assert(offsetof(ClientWaitSync, header) == 0,
+              "offset of ClientWaitSync header should be 0");
+static_assert(offsetof(ClientWaitSync, sync) == 4,
+              "offset of ClientWaitSync sync should be 4");
+static_assert(offsetof(ClientWaitSync, flags) == 8,
+              "offset of ClientWaitSync flags should be 8");
+static_assert(offsetof(ClientWaitSync, timeout_0) == 12,
+              "offset of ClientWaitSync timeout_0 should be 12");
+static_assert(offsetof(ClientWaitSync, timeout_1) == 16,
+              "offset of ClientWaitSync timeout_1 should be 16");
+static_assert(offsetof(ClientWaitSync, result_shm_id) == 20,
+              "offset of ClientWaitSync result_shm_id should be 20");
+static_assert(offsetof(ClientWaitSync, result_shm_offset) == 24,
+              "offset of ClientWaitSync result_shm_offset should be 24");
+
 struct ColorMask {
   typedef ColorMask ValueType;
   static const CommandId kCmdId = kColorMask;
@@ -9786,6 +9854,57 @@
 static_assert(offsetof(Viewport, height) == 16,
               "offset of Viewport height should be 16");
 
+struct WaitSync {
+  typedef WaitSync ValueType;
+  static const CommandId kCmdId = kWaitSync;
+  static const cmd::ArgFlags kArgFlags = cmd::kFixed;
+  static const uint8 cmd_flags = CMD_FLAG_SET_TRACE_LEVEL(3);
+
+  static uint32_t ComputeSize() {
+    return static_cast<uint32_t>(sizeof(ValueType));  // NOLINT
+  }
+
+  void SetHeader() { header.SetCmd<ValueType>(); }
+
+  void Init(GLuint _sync,
+            GLbitfield _flags,
+            GLuint _timeout_0,
+            GLuint _timeout_1) {
+    SetHeader();
+    sync = _sync;
+    flags = _flags;
+    timeout_0 = _timeout_0;
+    timeout_1 = _timeout_1;
+  }
+
+  void* Set(void* cmd,
+            GLuint _sync,
+            GLbitfield _flags,
+            GLuint _timeout_0,
+            GLuint _timeout_1) {
+    static_cast<ValueType*>(cmd)->Init(_sync, _flags, _timeout_0, _timeout_1);
+    return NextCmdAddress<ValueType>(cmd);
+  }
+
+  gpu::CommandHeader header;
+  uint32_t sync;
+  uint32_t flags;
+  uint32_t timeout_0;
+  uint32_t timeout_1;
+};
+
+static_assert(sizeof(WaitSync) == 20, "size of WaitSync should be 20");
+static_assert(offsetof(WaitSync, header) == 0,
+              "offset of WaitSync header should be 0");
+static_assert(offsetof(WaitSync, sync) == 4,
+              "offset of WaitSync sync should be 4");
+static_assert(offsetof(WaitSync, flags) == 8,
+              "offset of WaitSync flags should be 8");
+static_assert(offsetof(WaitSync, timeout_0) == 12,
+              "offset of WaitSync timeout_0 should be 12");
+static_assert(offsetof(WaitSync, timeout_1) == 16,
+              "offset of WaitSync timeout_1 should be 16");
+
 struct BlitFramebufferCHROMIUM {
   typedef BlitFramebufferCHROMIUM ValueType;
   static const CommandId kCmdId = kBlitFramebufferCHROMIUM;
diff --git a/gpu/command_buffer/common/gles2_cmd_format_test_autogen.h b/gpu/command_buffer/common/gles2_cmd_format_test_autogen.h
index a89683a32..9422405 100644
--- a/gpu/command_buffer/common/gles2_cmd_format_test_autogen.h
+++ b/gpu/command_buffer/common/gles2_cmd_format_test_autogen.h
@@ -394,6 +394,24 @@
   CheckBytesWrittenMatchesExpectedSize(next_cmd, sizeof(cmd));
 }
 
+TEST_F(GLES2FormatTest, ClientWaitSync) {
+  cmds::ClientWaitSync& cmd = *GetBufferAs<cmds::ClientWaitSync>();
+  void* next_cmd =
+      cmd.Set(&cmd, static_cast<GLuint>(11), static_cast<GLbitfield>(12),
+              static_cast<GLuint>(13), static_cast<GLuint>(14),
+              static_cast<uint32_t>(15), static_cast<uint32_t>(16));
+  EXPECT_EQ(static_cast<uint32_t>(cmds::ClientWaitSync::kCmdId),
+            cmd.header.command);
+  EXPECT_EQ(sizeof(cmd), cmd.header.size * 4u);
+  EXPECT_EQ(static_cast<GLuint>(11), cmd.sync);
+  EXPECT_EQ(static_cast<GLbitfield>(12), cmd.flags);
+  EXPECT_EQ(static_cast<GLuint>(13), cmd.timeout_0);
+  EXPECT_EQ(static_cast<GLuint>(14), cmd.timeout_1);
+  EXPECT_EQ(static_cast<uint32_t>(15), cmd.result_shm_id);
+  EXPECT_EQ(static_cast<uint32_t>(16), cmd.result_shm_offset);
+  CheckBytesWrittenMatchesExpectedSize(next_cmd, sizeof(cmd));
+}
+
 TEST_F(GLES2FormatTest, ColorMask) {
   cmds::ColorMask& cmd = *GetBufferAs<cmds::ColorMask>();
   void* next_cmd =
@@ -3423,6 +3441,20 @@
   CheckBytesWrittenMatchesExpectedSize(next_cmd, sizeof(cmd));
 }
 
+TEST_F(GLES2FormatTest, WaitSync) {
+  cmds::WaitSync& cmd = *GetBufferAs<cmds::WaitSync>();
+  void* next_cmd =
+      cmd.Set(&cmd, static_cast<GLuint>(11), static_cast<GLbitfield>(12),
+              static_cast<GLuint>(13), static_cast<GLuint>(14));
+  EXPECT_EQ(static_cast<uint32_t>(cmds::WaitSync::kCmdId), cmd.header.command);
+  EXPECT_EQ(sizeof(cmd), cmd.header.size * 4u);
+  EXPECT_EQ(static_cast<GLuint>(11), cmd.sync);
+  EXPECT_EQ(static_cast<GLbitfield>(12), cmd.flags);
+  EXPECT_EQ(static_cast<GLuint>(13), cmd.timeout_0);
+  EXPECT_EQ(static_cast<GLuint>(14), cmd.timeout_1);
+  CheckBytesWrittenMatchesExpectedSize(next_cmd, sizeof(cmd));
+}
+
 TEST_F(GLES2FormatTest, BlitFramebufferCHROMIUM) {
   cmds::BlitFramebufferCHROMIUM& cmd =
       *GetBufferAs<cmds::BlitFramebufferCHROMIUM>();
diff --git a/gpu/command_buffer/common/gles2_cmd_ids_autogen.h b/gpu/command_buffer/common/gles2_cmd_ids_autogen.h
index db67f4fb..d652511d15 100644
--- a/gpu/command_buffer/common/gles2_cmd_ids_autogen.h
+++ b/gpu/command_buffer/common/gles2_cmd_ids_autogen.h
@@ -39,253 +39,255 @@
   OP(ClearColor)                               /* 280 */ \
   OP(ClearDepthf)                              /* 281 */ \
   OP(ClearStencil)                             /* 282 */ \
-  OP(ColorMask)                                /* 283 */ \
-  OP(CompileShader)                            /* 284 */ \
-  OP(CompressedTexImage2DBucket)               /* 285 */ \
-  OP(CompressedTexImage2D)                     /* 286 */ \
-  OP(CompressedTexSubImage2DBucket)            /* 287 */ \
-  OP(CompressedTexSubImage2D)                  /* 288 */ \
-  OP(CopyBufferSubData)                        /* 289 */ \
-  OP(CopyTexImage2D)                           /* 290 */ \
-  OP(CopyTexSubImage2D)                        /* 291 */ \
-  OP(CopyTexSubImage3D)                        /* 292 */ \
-  OP(CreateProgram)                            /* 293 */ \
-  OP(CreateShader)                             /* 294 */ \
-  OP(CullFace)                                 /* 295 */ \
-  OP(DeleteBuffersImmediate)                   /* 296 */ \
-  OP(DeleteFramebuffersImmediate)              /* 297 */ \
-  OP(DeleteProgram)                            /* 298 */ \
-  OP(DeleteRenderbuffersImmediate)             /* 299 */ \
-  OP(DeleteSamplersImmediate)                  /* 300 */ \
-  OP(DeleteSync)                               /* 301 */ \
-  OP(DeleteShader)                             /* 302 */ \
-  OP(DeleteTexturesImmediate)                  /* 303 */ \
-  OP(DeleteTransformFeedbacksImmediate)        /* 304 */ \
-  OP(DepthFunc)                                /* 305 */ \
-  OP(DepthMask)                                /* 306 */ \
-  OP(DepthRangef)                              /* 307 */ \
-  OP(DetachShader)                             /* 308 */ \
-  OP(Disable)                                  /* 309 */ \
-  OP(DisableVertexAttribArray)                 /* 310 */ \
-  OP(DrawArrays)                               /* 311 */ \
-  OP(DrawElements)                             /* 312 */ \
-  OP(Enable)                                   /* 313 */ \
-  OP(EnableVertexAttribArray)                  /* 314 */ \
-  OP(FenceSync)                                /* 315 */ \
-  OP(Finish)                                   /* 316 */ \
-  OP(Flush)                                    /* 317 */ \
-  OP(FramebufferRenderbuffer)                  /* 318 */ \
-  OP(FramebufferTexture2D)                     /* 319 */ \
-  OP(FramebufferTextureLayer)                  /* 320 */ \
-  OP(FrontFace)                                /* 321 */ \
-  OP(GenBuffersImmediate)                      /* 322 */ \
-  OP(GenerateMipmap)                           /* 323 */ \
-  OP(GenFramebuffersImmediate)                 /* 324 */ \
-  OP(GenRenderbuffersImmediate)                /* 325 */ \
-  OP(GenSamplersImmediate)                     /* 326 */ \
-  OP(GenTexturesImmediate)                     /* 327 */ \
-  OP(GenTransformFeedbacksImmediate)           /* 328 */ \
-  OP(GetActiveAttrib)                          /* 329 */ \
-  OP(GetActiveUniform)                         /* 330 */ \
-  OP(GetActiveUniformBlockiv)                  /* 331 */ \
-  OP(GetActiveUniformBlockName)                /* 332 */ \
-  OP(GetActiveUniformsiv)                      /* 333 */ \
-  OP(GetAttachedShaders)                       /* 334 */ \
-  OP(GetAttribLocation)                        /* 335 */ \
-  OP(GetBooleanv)                              /* 336 */ \
-  OP(GetBufferParameteriv)                     /* 337 */ \
-  OP(GetError)                                 /* 338 */ \
-  OP(GetFloatv)                                /* 339 */ \
-  OP(GetFragDataLocation)                      /* 340 */ \
-  OP(GetFramebufferAttachmentParameteriv)      /* 341 */ \
-  OP(GetIntegerv)                              /* 342 */ \
-  OP(GetInternalformativ)                      /* 343 */ \
-  OP(GetProgramiv)                             /* 344 */ \
-  OP(GetProgramInfoLog)                        /* 345 */ \
-  OP(GetRenderbufferParameteriv)               /* 346 */ \
-  OP(GetSamplerParameterfv)                    /* 347 */ \
-  OP(GetSamplerParameteriv)                    /* 348 */ \
-  OP(GetShaderiv)                              /* 349 */ \
-  OP(GetShaderInfoLog)                         /* 350 */ \
-  OP(GetShaderPrecisionFormat)                 /* 351 */ \
-  OP(GetShaderSource)                          /* 352 */ \
-  OP(GetString)                                /* 353 */ \
-  OP(GetTexParameterfv)                        /* 354 */ \
-  OP(GetTexParameteriv)                        /* 355 */ \
-  OP(GetTransformFeedbackVarying)              /* 356 */ \
-  OP(GetUniformBlockIndex)                     /* 357 */ \
-  OP(GetUniformfv)                             /* 358 */ \
-  OP(GetUniformiv)                             /* 359 */ \
-  OP(GetUniformIndices)                        /* 360 */ \
-  OP(GetUniformLocation)                       /* 361 */ \
-  OP(GetVertexAttribfv)                        /* 362 */ \
-  OP(GetVertexAttribiv)                        /* 363 */ \
-  OP(GetVertexAttribPointerv)                  /* 364 */ \
-  OP(Hint)                                     /* 365 */ \
-  OP(InvalidateFramebufferImmediate)           /* 366 */ \
-  OP(InvalidateSubFramebufferImmediate)        /* 367 */ \
-  OP(IsBuffer)                                 /* 368 */ \
-  OP(IsEnabled)                                /* 369 */ \
-  OP(IsFramebuffer)                            /* 370 */ \
-  OP(IsProgram)                                /* 371 */ \
-  OP(IsRenderbuffer)                           /* 372 */ \
-  OP(IsSampler)                                /* 373 */ \
-  OP(IsShader)                                 /* 374 */ \
-  OP(IsSync)                                   /* 375 */ \
-  OP(IsTexture)                                /* 376 */ \
-  OP(IsTransformFeedback)                      /* 377 */ \
-  OP(LineWidth)                                /* 378 */ \
-  OP(LinkProgram)                              /* 379 */ \
-  OP(PauseTransformFeedback)                   /* 380 */ \
-  OP(PixelStorei)                              /* 381 */ \
-  OP(PolygonOffset)                            /* 382 */ \
-  OP(ReadBuffer)                               /* 383 */ \
-  OP(ReadPixels)                               /* 384 */ \
-  OP(ReleaseShaderCompiler)                    /* 385 */ \
-  OP(RenderbufferStorage)                      /* 386 */ \
-  OP(ResumeTransformFeedback)                  /* 387 */ \
-  OP(SampleCoverage)                           /* 388 */ \
-  OP(SamplerParameterf)                        /* 389 */ \
-  OP(SamplerParameterfvImmediate)              /* 390 */ \
-  OP(SamplerParameteri)                        /* 391 */ \
-  OP(SamplerParameterivImmediate)              /* 392 */ \
-  OP(Scissor)                                  /* 393 */ \
-  OP(ShaderBinary)                             /* 394 */ \
-  OP(ShaderSourceBucket)                       /* 395 */ \
-  OP(StencilFunc)                              /* 396 */ \
-  OP(StencilFuncSeparate)                      /* 397 */ \
-  OP(StencilMask)                              /* 398 */ \
-  OP(StencilMaskSeparate)                      /* 399 */ \
-  OP(StencilOp)                                /* 400 */ \
-  OP(StencilOpSeparate)                        /* 401 */ \
-  OP(TexImage2D)                               /* 402 */ \
-  OP(TexImage3D)                               /* 403 */ \
-  OP(TexParameterf)                            /* 404 */ \
-  OP(TexParameterfvImmediate)                  /* 405 */ \
-  OP(TexParameteri)                            /* 406 */ \
-  OP(TexParameterivImmediate)                  /* 407 */ \
-  OP(TexStorage3D)                             /* 408 */ \
-  OP(TexSubImage2D)                            /* 409 */ \
-  OP(TexSubImage3D)                            /* 410 */ \
-  OP(TransformFeedbackVaryingsBucket)          /* 411 */ \
-  OP(Uniform1f)                                /* 412 */ \
-  OP(Uniform1fvImmediate)                      /* 413 */ \
-  OP(Uniform1i)                                /* 414 */ \
-  OP(Uniform1ivImmediate)                      /* 415 */ \
-  OP(Uniform1ui)                               /* 416 */ \
-  OP(Uniform1uivImmediate)                     /* 417 */ \
-  OP(Uniform2f)                                /* 418 */ \
-  OP(Uniform2fvImmediate)                      /* 419 */ \
-  OP(Uniform2i)                                /* 420 */ \
-  OP(Uniform2ivImmediate)                      /* 421 */ \
-  OP(Uniform2ui)                               /* 422 */ \
-  OP(Uniform2uivImmediate)                     /* 423 */ \
-  OP(Uniform3f)                                /* 424 */ \
-  OP(Uniform3fvImmediate)                      /* 425 */ \
-  OP(Uniform3i)                                /* 426 */ \
-  OP(Uniform3ivImmediate)                      /* 427 */ \
-  OP(Uniform3ui)                               /* 428 */ \
-  OP(Uniform3uivImmediate)                     /* 429 */ \
-  OP(Uniform4f)                                /* 430 */ \
-  OP(Uniform4fvImmediate)                      /* 431 */ \
-  OP(Uniform4i)                                /* 432 */ \
-  OP(Uniform4ivImmediate)                      /* 433 */ \
-  OP(Uniform4ui)                               /* 434 */ \
-  OP(Uniform4uivImmediate)                     /* 435 */ \
-  OP(UniformBlockBinding)                      /* 436 */ \
-  OP(UniformMatrix2fvImmediate)                /* 437 */ \
-  OP(UniformMatrix2x3fvImmediate)              /* 438 */ \
-  OP(UniformMatrix2x4fvImmediate)              /* 439 */ \
-  OP(UniformMatrix3fvImmediate)                /* 440 */ \
-  OP(UniformMatrix3x2fvImmediate)              /* 441 */ \
-  OP(UniformMatrix3x4fvImmediate)              /* 442 */ \
-  OP(UniformMatrix4fvImmediate)                /* 443 */ \
-  OP(UniformMatrix4x2fvImmediate)              /* 444 */ \
-  OP(UniformMatrix4x3fvImmediate)              /* 445 */ \
-  OP(UseProgram)                               /* 446 */ \
-  OP(ValidateProgram)                          /* 447 */ \
-  OP(VertexAttrib1f)                           /* 448 */ \
-  OP(VertexAttrib1fvImmediate)                 /* 449 */ \
-  OP(VertexAttrib2f)                           /* 450 */ \
-  OP(VertexAttrib2fvImmediate)                 /* 451 */ \
-  OP(VertexAttrib3f)                           /* 452 */ \
-  OP(VertexAttrib3fvImmediate)                 /* 453 */ \
-  OP(VertexAttrib4f)                           /* 454 */ \
-  OP(VertexAttrib4fvImmediate)                 /* 455 */ \
-  OP(VertexAttribI4i)                          /* 456 */ \
-  OP(VertexAttribI4ivImmediate)                /* 457 */ \
-  OP(VertexAttribI4ui)                         /* 458 */ \
-  OP(VertexAttribI4uivImmediate)               /* 459 */ \
-  OP(VertexAttribIPointer)                     /* 460 */ \
-  OP(VertexAttribPointer)                      /* 461 */ \
-  OP(Viewport)                                 /* 462 */ \
-  OP(BlitFramebufferCHROMIUM)                  /* 463 */ \
-  OP(RenderbufferStorageMultisampleCHROMIUM)   /* 464 */ \
-  OP(RenderbufferStorageMultisampleEXT)        /* 465 */ \
-  OP(FramebufferTexture2DMultisampleEXT)       /* 466 */ \
-  OP(TexStorage2DEXT)                          /* 467 */ \
-  OP(GenQueriesEXTImmediate)                   /* 468 */ \
-  OP(DeleteQueriesEXTImmediate)                /* 469 */ \
-  OP(BeginQueryEXT)                            /* 470 */ \
-  OP(BeginTransformFeedback)                   /* 471 */ \
-  OP(EndQueryEXT)                              /* 472 */ \
-  OP(EndTransformFeedback)                     /* 473 */ \
-  OP(InsertEventMarkerEXT)                     /* 474 */ \
-  OP(PushGroupMarkerEXT)                       /* 475 */ \
-  OP(PopGroupMarkerEXT)                        /* 476 */ \
-  OP(GenVertexArraysOESImmediate)              /* 477 */ \
-  OP(DeleteVertexArraysOESImmediate)           /* 478 */ \
-  OP(IsVertexArrayOES)                         /* 479 */ \
-  OP(BindVertexArrayOES)                       /* 480 */ \
-  OP(SwapBuffers)                              /* 481 */ \
-  OP(GetMaxValueInBufferCHROMIUM)              /* 482 */ \
-  OP(EnableFeatureCHROMIUM)                    /* 483 */ \
-  OP(ResizeCHROMIUM)                           /* 484 */ \
-  OP(GetRequestableExtensionsCHROMIUM)         /* 485 */ \
-  OP(RequestExtensionCHROMIUM)                 /* 486 */ \
-  OP(GetProgramInfoCHROMIUM)                   /* 487 */ \
-  OP(GetUniformBlocksCHROMIUM)                 /* 488 */ \
-  OP(GetTransformFeedbackVaryingsCHROMIUM)     /* 489 */ \
-  OP(GetUniformsES3CHROMIUM)                   /* 490 */ \
-  OP(GetTranslatedShaderSourceANGLE)           /* 491 */ \
-  OP(PostSubBufferCHROMIUM)                    /* 492 */ \
-  OP(TexImageIOSurface2DCHROMIUM)              /* 493 */ \
-  OP(CopyTextureCHROMIUM)                      /* 494 */ \
-  OP(DrawArraysInstancedANGLE)                 /* 495 */ \
-  OP(DrawElementsInstancedANGLE)               /* 496 */ \
-  OP(VertexAttribDivisorANGLE)                 /* 497 */ \
-  OP(GenMailboxCHROMIUM)                       /* 498 */ \
-  OP(ProduceTextureCHROMIUMImmediate)          /* 499 */ \
-  OP(ProduceTextureDirectCHROMIUMImmediate)    /* 500 */ \
-  OP(ConsumeTextureCHROMIUMImmediate)          /* 501 */ \
-  OP(CreateAndConsumeTextureCHROMIUMImmediate) /* 502 */ \
-  OP(BindUniformLocationCHROMIUMBucket)        /* 503 */ \
-  OP(GenValuebuffersCHROMIUMImmediate)         /* 504 */ \
-  OP(DeleteValuebuffersCHROMIUMImmediate)      /* 505 */ \
-  OP(IsValuebufferCHROMIUM)                    /* 506 */ \
-  OP(BindValuebufferCHROMIUM)                  /* 507 */ \
-  OP(SubscribeValueCHROMIUM)                   /* 508 */ \
-  OP(PopulateSubscribedValuesCHROMIUM)         /* 509 */ \
-  OP(UniformValuebufferCHROMIUM)               /* 510 */ \
-  OP(BindTexImage2DCHROMIUM)                   /* 511 */ \
-  OP(ReleaseTexImage2DCHROMIUM)                /* 512 */ \
-  OP(TraceBeginCHROMIUM)                       /* 513 */ \
-  OP(TraceEndCHROMIUM)                         /* 514 */ \
-  OP(AsyncTexSubImage2DCHROMIUM)               /* 515 */ \
-  OP(AsyncTexImage2DCHROMIUM)                  /* 516 */ \
-  OP(WaitAsyncTexImage2DCHROMIUM)              /* 517 */ \
-  OP(WaitAllAsyncTexImage2DCHROMIUM)           /* 518 */ \
-  OP(DiscardFramebufferEXTImmediate)           /* 519 */ \
-  OP(LoseContextCHROMIUM)                      /* 520 */ \
-  OP(InsertSyncPointCHROMIUM)                  /* 521 */ \
-  OP(WaitSyncPointCHROMIUM)                    /* 522 */ \
-  OP(DrawBuffersEXTImmediate)                  /* 523 */ \
-  OP(DiscardBackbufferCHROMIUM)                /* 524 */ \
-  OP(ScheduleOverlayPlaneCHROMIUM)             /* 525 */ \
-  OP(SwapInterval)                             /* 526 */ \
-  OP(MatrixLoadfCHROMIUMImmediate)             /* 527 */ \
-  OP(MatrixLoadIdentityCHROMIUM)               /* 528 */ \
-  OP(BlendBarrierKHR)                          /* 529 */
+  OP(ClientWaitSync)                           /* 283 */ \
+  OP(ColorMask)                                /* 284 */ \
+  OP(CompileShader)                            /* 285 */ \
+  OP(CompressedTexImage2DBucket)               /* 286 */ \
+  OP(CompressedTexImage2D)                     /* 287 */ \
+  OP(CompressedTexSubImage2DBucket)            /* 288 */ \
+  OP(CompressedTexSubImage2D)                  /* 289 */ \
+  OP(CopyBufferSubData)                        /* 290 */ \
+  OP(CopyTexImage2D)                           /* 291 */ \
+  OP(CopyTexSubImage2D)                        /* 292 */ \
+  OP(CopyTexSubImage3D)                        /* 293 */ \
+  OP(CreateProgram)                            /* 294 */ \
+  OP(CreateShader)                             /* 295 */ \
+  OP(CullFace)                                 /* 296 */ \
+  OP(DeleteBuffersImmediate)                   /* 297 */ \
+  OP(DeleteFramebuffersImmediate)              /* 298 */ \
+  OP(DeleteProgram)                            /* 299 */ \
+  OP(DeleteRenderbuffersImmediate)             /* 300 */ \
+  OP(DeleteSamplersImmediate)                  /* 301 */ \
+  OP(DeleteSync)                               /* 302 */ \
+  OP(DeleteShader)                             /* 303 */ \
+  OP(DeleteTexturesImmediate)                  /* 304 */ \
+  OP(DeleteTransformFeedbacksImmediate)        /* 305 */ \
+  OP(DepthFunc)                                /* 306 */ \
+  OP(DepthMask)                                /* 307 */ \
+  OP(DepthRangef)                              /* 308 */ \
+  OP(DetachShader)                             /* 309 */ \
+  OP(Disable)                                  /* 310 */ \
+  OP(DisableVertexAttribArray)                 /* 311 */ \
+  OP(DrawArrays)                               /* 312 */ \
+  OP(DrawElements)                             /* 313 */ \
+  OP(Enable)                                   /* 314 */ \
+  OP(EnableVertexAttribArray)                  /* 315 */ \
+  OP(FenceSync)                                /* 316 */ \
+  OP(Finish)                                   /* 317 */ \
+  OP(Flush)                                    /* 318 */ \
+  OP(FramebufferRenderbuffer)                  /* 319 */ \
+  OP(FramebufferTexture2D)                     /* 320 */ \
+  OP(FramebufferTextureLayer)                  /* 321 */ \
+  OP(FrontFace)                                /* 322 */ \
+  OP(GenBuffersImmediate)                      /* 323 */ \
+  OP(GenerateMipmap)                           /* 324 */ \
+  OP(GenFramebuffersImmediate)                 /* 325 */ \
+  OP(GenRenderbuffersImmediate)                /* 326 */ \
+  OP(GenSamplersImmediate)                     /* 327 */ \
+  OP(GenTexturesImmediate)                     /* 328 */ \
+  OP(GenTransformFeedbacksImmediate)           /* 329 */ \
+  OP(GetActiveAttrib)                          /* 330 */ \
+  OP(GetActiveUniform)                         /* 331 */ \
+  OP(GetActiveUniformBlockiv)                  /* 332 */ \
+  OP(GetActiveUniformBlockName)                /* 333 */ \
+  OP(GetActiveUniformsiv)                      /* 334 */ \
+  OP(GetAttachedShaders)                       /* 335 */ \
+  OP(GetAttribLocation)                        /* 336 */ \
+  OP(GetBooleanv)                              /* 337 */ \
+  OP(GetBufferParameteriv)                     /* 338 */ \
+  OP(GetError)                                 /* 339 */ \
+  OP(GetFloatv)                                /* 340 */ \
+  OP(GetFragDataLocation)                      /* 341 */ \
+  OP(GetFramebufferAttachmentParameteriv)      /* 342 */ \
+  OP(GetIntegerv)                              /* 343 */ \
+  OP(GetInternalformativ)                      /* 344 */ \
+  OP(GetProgramiv)                             /* 345 */ \
+  OP(GetProgramInfoLog)                        /* 346 */ \
+  OP(GetRenderbufferParameteriv)               /* 347 */ \
+  OP(GetSamplerParameterfv)                    /* 348 */ \
+  OP(GetSamplerParameteriv)                    /* 349 */ \
+  OP(GetShaderiv)                              /* 350 */ \
+  OP(GetShaderInfoLog)                         /* 351 */ \
+  OP(GetShaderPrecisionFormat)                 /* 352 */ \
+  OP(GetShaderSource)                          /* 353 */ \
+  OP(GetString)                                /* 354 */ \
+  OP(GetTexParameterfv)                        /* 355 */ \
+  OP(GetTexParameteriv)                        /* 356 */ \
+  OP(GetTransformFeedbackVarying)              /* 357 */ \
+  OP(GetUniformBlockIndex)                     /* 358 */ \
+  OP(GetUniformfv)                             /* 359 */ \
+  OP(GetUniformiv)                             /* 360 */ \
+  OP(GetUniformIndices)                        /* 361 */ \
+  OP(GetUniformLocation)                       /* 362 */ \
+  OP(GetVertexAttribfv)                        /* 363 */ \
+  OP(GetVertexAttribiv)                        /* 364 */ \
+  OP(GetVertexAttribPointerv)                  /* 365 */ \
+  OP(Hint)                                     /* 366 */ \
+  OP(InvalidateFramebufferImmediate)           /* 367 */ \
+  OP(InvalidateSubFramebufferImmediate)        /* 368 */ \
+  OP(IsBuffer)                                 /* 369 */ \
+  OP(IsEnabled)                                /* 370 */ \
+  OP(IsFramebuffer)                            /* 371 */ \
+  OP(IsProgram)                                /* 372 */ \
+  OP(IsRenderbuffer)                           /* 373 */ \
+  OP(IsSampler)                                /* 374 */ \
+  OP(IsShader)                                 /* 375 */ \
+  OP(IsSync)                                   /* 376 */ \
+  OP(IsTexture)                                /* 377 */ \
+  OP(IsTransformFeedback)                      /* 378 */ \
+  OP(LineWidth)                                /* 379 */ \
+  OP(LinkProgram)                              /* 380 */ \
+  OP(PauseTransformFeedback)                   /* 381 */ \
+  OP(PixelStorei)                              /* 382 */ \
+  OP(PolygonOffset)                            /* 383 */ \
+  OP(ReadBuffer)                               /* 384 */ \
+  OP(ReadPixels)                               /* 385 */ \
+  OP(ReleaseShaderCompiler)                    /* 386 */ \
+  OP(RenderbufferStorage)                      /* 387 */ \
+  OP(ResumeTransformFeedback)                  /* 388 */ \
+  OP(SampleCoverage)                           /* 389 */ \
+  OP(SamplerParameterf)                        /* 390 */ \
+  OP(SamplerParameterfvImmediate)              /* 391 */ \
+  OP(SamplerParameteri)                        /* 392 */ \
+  OP(SamplerParameterivImmediate)              /* 393 */ \
+  OP(Scissor)                                  /* 394 */ \
+  OP(ShaderBinary)                             /* 395 */ \
+  OP(ShaderSourceBucket)                       /* 396 */ \
+  OP(StencilFunc)                              /* 397 */ \
+  OP(StencilFuncSeparate)                      /* 398 */ \
+  OP(StencilMask)                              /* 399 */ \
+  OP(StencilMaskSeparate)                      /* 400 */ \
+  OP(StencilOp)                                /* 401 */ \
+  OP(StencilOpSeparate)                        /* 402 */ \
+  OP(TexImage2D)                               /* 403 */ \
+  OP(TexImage3D)                               /* 404 */ \
+  OP(TexParameterf)                            /* 405 */ \
+  OP(TexParameterfvImmediate)                  /* 406 */ \
+  OP(TexParameteri)                            /* 407 */ \
+  OP(TexParameterivImmediate)                  /* 408 */ \
+  OP(TexStorage3D)                             /* 409 */ \
+  OP(TexSubImage2D)                            /* 410 */ \
+  OP(TexSubImage3D)                            /* 411 */ \
+  OP(TransformFeedbackVaryingsBucket)          /* 412 */ \
+  OP(Uniform1f)                                /* 413 */ \
+  OP(Uniform1fvImmediate)                      /* 414 */ \
+  OP(Uniform1i)                                /* 415 */ \
+  OP(Uniform1ivImmediate)                      /* 416 */ \
+  OP(Uniform1ui)                               /* 417 */ \
+  OP(Uniform1uivImmediate)                     /* 418 */ \
+  OP(Uniform2f)                                /* 419 */ \
+  OP(Uniform2fvImmediate)                      /* 420 */ \
+  OP(Uniform2i)                                /* 421 */ \
+  OP(Uniform2ivImmediate)                      /* 422 */ \
+  OP(Uniform2ui)                               /* 423 */ \
+  OP(Uniform2uivImmediate)                     /* 424 */ \
+  OP(Uniform3f)                                /* 425 */ \
+  OP(Uniform3fvImmediate)                      /* 426 */ \
+  OP(Uniform3i)                                /* 427 */ \
+  OP(Uniform3ivImmediate)                      /* 428 */ \
+  OP(Uniform3ui)                               /* 429 */ \
+  OP(Uniform3uivImmediate)                     /* 430 */ \
+  OP(Uniform4f)                                /* 431 */ \
+  OP(Uniform4fvImmediate)                      /* 432 */ \
+  OP(Uniform4i)                                /* 433 */ \
+  OP(Uniform4ivImmediate)                      /* 434 */ \
+  OP(Uniform4ui)                               /* 435 */ \
+  OP(Uniform4uivImmediate)                     /* 436 */ \
+  OP(UniformBlockBinding)                      /* 437 */ \
+  OP(UniformMatrix2fvImmediate)                /* 438 */ \
+  OP(UniformMatrix2x3fvImmediate)              /* 439 */ \
+  OP(UniformMatrix2x4fvImmediate)              /* 440 */ \
+  OP(UniformMatrix3fvImmediate)                /* 441 */ \
+  OP(UniformMatrix3x2fvImmediate)              /* 442 */ \
+  OP(UniformMatrix3x4fvImmediate)              /* 443 */ \
+  OP(UniformMatrix4fvImmediate)                /* 444 */ \
+  OP(UniformMatrix4x2fvImmediate)              /* 445 */ \
+  OP(UniformMatrix4x3fvImmediate)              /* 446 */ \
+  OP(UseProgram)                               /* 447 */ \
+  OP(ValidateProgram)                          /* 448 */ \
+  OP(VertexAttrib1f)                           /* 449 */ \
+  OP(VertexAttrib1fvImmediate)                 /* 450 */ \
+  OP(VertexAttrib2f)                           /* 451 */ \
+  OP(VertexAttrib2fvImmediate)                 /* 452 */ \
+  OP(VertexAttrib3f)                           /* 453 */ \
+  OP(VertexAttrib3fvImmediate)                 /* 454 */ \
+  OP(VertexAttrib4f)                           /* 455 */ \
+  OP(VertexAttrib4fvImmediate)                 /* 456 */ \
+  OP(VertexAttribI4i)                          /* 457 */ \
+  OP(VertexAttribI4ivImmediate)                /* 458 */ \
+  OP(VertexAttribI4ui)                         /* 459 */ \
+  OP(VertexAttribI4uivImmediate)               /* 460 */ \
+  OP(VertexAttribIPointer)                     /* 461 */ \
+  OP(VertexAttribPointer)                      /* 462 */ \
+  OP(Viewport)                                 /* 463 */ \
+  OP(WaitSync)                                 /* 464 */ \
+  OP(BlitFramebufferCHROMIUM)                  /* 465 */ \
+  OP(RenderbufferStorageMultisampleCHROMIUM)   /* 466 */ \
+  OP(RenderbufferStorageMultisampleEXT)        /* 467 */ \
+  OP(FramebufferTexture2DMultisampleEXT)       /* 468 */ \
+  OP(TexStorage2DEXT)                          /* 469 */ \
+  OP(GenQueriesEXTImmediate)                   /* 470 */ \
+  OP(DeleteQueriesEXTImmediate)                /* 471 */ \
+  OP(BeginQueryEXT)                            /* 472 */ \
+  OP(BeginTransformFeedback)                   /* 473 */ \
+  OP(EndQueryEXT)                              /* 474 */ \
+  OP(EndTransformFeedback)                     /* 475 */ \
+  OP(InsertEventMarkerEXT)                     /* 476 */ \
+  OP(PushGroupMarkerEXT)                       /* 477 */ \
+  OP(PopGroupMarkerEXT)                        /* 478 */ \
+  OP(GenVertexArraysOESImmediate)              /* 479 */ \
+  OP(DeleteVertexArraysOESImmediate)           /* 480 */ \
+  OP(IsVertexArrayOES)                         /* 481 */ \
+  OP(BindVertexArrayOES)                       /* 482 */ \
+  OP(SwapBuffers)                              /* 483 */ \
+  OP(GetMaxValueInBufferCHROMIUM)              /* 484 */ \
+  OP(EnableFeatureCHROMIUM)                    /* 485 */ \
+  OP(ResizeCHROMIUM)                           /* 486 */ \
+  OP(GetRequestableExtensionsCHROMIUM)         /* 487 */ \
+  OP(RequestExtensionCHROMIUM)                 /* 488 */ \
+  OP(GetProgramInfoCHROMIUM)                   /* 489 */ \
+  OP(GetUniformBlocksCHROMIUM)                 /* 490 */ \
+  OP(GetTransformFeedbackVaryingsCHROMIUM)     /* 491 */ \
+  OP(GetUniformsES3CHROMIUM)                   /* 492 */ \
+  OP(GetTranslatedShaderSourceANGLE)           /* 493 */ \
+  OP(PostSubBufferCHROMIUM)                    /* 494 */ \
+  OP(TexImageIOSurface2DCHROMIUM)              /* 495 */ \
+  OP(CopyTextureCHROMIUM)                      /* 496 */ \
+  OP(DrawArraysInstancedANGLE)                 /* 497 */ \
+  OP(DrawElementsInstancedANGLE)               /* 498 */ \
+  OP(VertexAttribDivisorANGLE)                 /* 499 */ \
+  OP(GenMailboxCHROMIUM)                       /* 500 */ \
+  OP(ProduceTextureCHROMIUMImmediate)          /* 501 */ \
+  OP(ProduceTextureDirectCHROMIUMImmediate)    /* 502 */ \
+  OP(ConsumeTextureCHROMIUMImmediate)          /* 503 */ \
+  OP(CreateAndConsumeTextureCHROMIUMImmediate) /* 504 */ \
+  OP(BindUniformLocationCHROMIUMBucket)        /* 505 */ \
+  OP(GenValuebuffersCHROMIUMImmediate)         /* 506 */ \
+  OP(DeleteValuebuffersCHROMIUMImmediate)      /* 507 */ \
+  OP(IsValuebufferCHROMIUM)                    /* 508 */ \
+  OP(BindValuebufferCHROMIUM)                  /* 509 */ \
+  OP(SubscribeValueCHROMIUM)                   /* 510 */ \
+  OP(PopulateSubscribedValuesCHROMIUM)         /* 511 */ \
+  OP(UniformValuebufferCHROMIUM)               /* 512 */ \
+  OP(BindTexImage2DCHROMIUM)                   /* 513 */ \
+  OP(ReleaseTexImage2DCHROMIUM)                /* 514 */ \
+  OP(TraceBeginCHROMIUM)                       /* 515 */ \
+  OP(TraceEndCHROMIUM)                         /* 516 */ \
+  OP(AsyncTexSubImage2DCHROMIUM)               /* 517 */ \
+  OP(AsyncTexImage2DCHROMIUM)                  /* 518 */ \
+  OP(WaitAsyncTexImage2DCHROMIUM)              /* 519 */ \
+  OP(WaitAllAsyncTexImage2DCHROMIUM)           /* 520 */ \
+  OP(DiscardFramebufferEXTImmediate)           /* 521 */ \
+  OP(LoseContextCHROMIUM)                      /* 522 */ \
+  OP(InsertSyncPointCHROMIUM)                  /* 523 */ \
+  OP(WaitSyncPointCHROMIUM)                    /* 524 */ \
+  OP(DrawBuffersEXTImmediate)                  /* 525 */ \
+  OP(DiscardBackbufferCHROMIUM)                /* 526 */ \
+  OP(ScheduleOverlayPlaneCHROMIUM)             /* 527 */ \
+  OP(SwapInterval)                             /* 528 */ \
+  OP(MatrixLoadfCHROMIUMImmediate)             /* 529 */ \
+  OP(MatrixLoadIdentityCHROMIUM)               /* 530 */ \
+  OP(BlendBarrierKHR)                          /* 531 */
 
 enum CommandId {
   kStartPoint = cmd::kLastCommonId,  // All GLES2 commands start after this.
diff --git a/gpu/command_buffer/common/gles2_cmd_utils.cc b/gpu/command_buffer/common/gles2_cmd_utils.cc
index 638245b7..c16a962f 100644
--- a/gpu/command_buffer/common/gles2_cmd_utils.cc
+++ b/gpu/command_buffer/common/gles2_cmd_utils.cc
@@ -846,6 +846,20 @@
   }
 }
 
+// static
+void GLES2Util::MapUint64ToTwoUint32(
+    uint64_t v64, uint32_t* v32_0, uint32_t* v32_1) {
+  DCHECK(v32_0 && v32_1);
+  *v32_0 = static_cast<uint32_t>(v64 & 0xFFFFFFFF);
+  *v32_1 = static_cast<uint32_t>((v64 & 0xFFFFFFFF00000000) >> 32);
+}
+
+// static
+uint64_t GLES2Util::MapTwoUint32ToUint64(uint32_t v32_0, uint32_t v32_1) {
+  uint64_t v64 = v32_1;
+  return (v64 << 32) | v32_0;
+}
+
 namespace {
 
 // WebGraphicsContext3DCommandBufferImpl configuration attributes. Those in
diff --git a/gpu/command_buffer/common/gles2_cmd_utils.h b/gpu/command_buffer/common/gles2_cmd_utils.h
index 127c2be..3528cfdb 100644
--- a/gpu/command_buffer/common/gles2_cmd_utils.h
+++ b/gpu/command_buffer/common/gles2_cmd_utils.h
@@ -187,6 +187,10 @@
   static size_t CalcClearBufferivDataCount(int buffer);
   static size_t CalcClearBufferfvDataCount(int buffer);
 
+  static void MapUint64ToTwoUint32(
+      uint64_t v64, uint32_t* v32_0, uint32_t* v32_1);
+  static uint64_t MapTwoUint32ToUint64(uint32_t v32_0, uint32_t v32_1);
+
   #include "../common/gles2_cmd_utils_autogen.h"
 
  private:
diff --git a/gpu/command_buffer/service/framebuffer_manager.cc b/gpu/command_buffer/service/framebuffer_manager.cc
index d766abb..3e9e0b2 100644
--- a/gpu/command_buffer/service/framebuffer_manager.cc
+++ b/gpu/command_buffer/service/framebuffer_manager.cc
@@ -631,6 +631,14 @@
   }
 }
 
+void Framebuffer::DoUnbindGLAttachmentsForWorkaround(GLenum target) {
+  // Replace all attachments with the default Renderbuffer.
+  for (AttachmentMap::const_iterator it = attachments_.begin();
+       it != attachments_.end(); ++it) {
+    glFramebufferRenderbufferEXT(target, it->first, GL_RENDERBUFFER, 0);
+  }
+}
+
 void Framebuffer::AttachRenderbuffer(
     GLenum attachment, Renderbuffer* renderbuffer) {
   const Attachment* a = GetAttachment(attachment);
diff --git a/gpu/command_buffer/service/framebuffer_manager.h b/gpu/command_buffer/service/framebuffer_manager.h
index 78c11ad2..75f7e2a7 100644
--- a/gpu/command_buffer/service/framebuffer_manager.h
+++ b/gpu/command_buffer/service/framebuffer_manager.h
@@ -74,6 +74,11 @@
     GLenum attachment,
     bool cleared);
 
+  // Unbinds all attachments from this framebuffer for workaround
+  // 'unbind_attachments_on_bound_render_fbo_delete'.  The Framebuffer must be
+  // bound when calling this.
+  void DoUnbindGLAttachmentsForWorkaround(GLenum target);
+
   // Attaches a renderbuffer to a particlar attachment.
   // Pass null to detach.
   void AttachRenderbuffer(
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.cc b/gpu/command_buffer/service/gles2_cmd_decoder.cc
index 52a9c43..6b254e09 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder.cc
@@ -3099,11 +3099,16 @@
         GetFramebuffer(client_ids[ii]);
     if (framebuffer && !framebuffer->IsDeleted()) {
       if (framebuffer == framebuffer_state_.bound_draw_framebuffer.get()) {
-        framebuffer_state_.bound_draw_framebuffer = NULL;
-        framebuffer_state_.clear_state_dirty = true;
         GLenum target = supports_separate_framebuffer_binds ?
             GL_DRAW_FRAMEBUFFER_EXT : GL_FRAMEBUFFER;
+
+        // Unbind attachments on FBO before deletion.
+        if (workarounds().unbind_attachments_on_bound_render_fbo_delete)
+          framebuffer->DoUnbindGLAttachmentsForWorkaround(target);
+
         glBindFramebufferEXT(target, GetBackbufferServiceId());
+        framebuffer_state_.bound_draw_framebuffer = NULL;
+        framebuffer_state_.clear_state_dirty = true;
       }
       if (framebuffer == framebuffer_state_.bound_read_framebuffer.get()) {
         framebuffer_state_.bound_read_framebuffer = NULL;
@@ -12055,6 +12060,51 @@
   return error::kNoError;
 }
 
+error::Error GLES2DecoderImpl::HandleClientWaitSync(
+    uint32_t immediate_data_size, const void* cmd_data) {
+  if (!unsafe_es3_apis_enabled())
+    return error::kUnknownCommand;
+  const gles2::cmds::ClientWaitSync& c =
+      *static_cast<const gles2::cmds::ClientWaitSync*>(cmd_data);
+  GLuint sync = static_cast<GLuint>(c.sync);
+  GLbitfield flags = static_cast<GLbitfield>(c.flags);
+  GLuint64 timeout = GLES2Util::MapTwoUint32ToUint64(c.timeout_0, c.timeout_1);
+  typedef cmds::ClientWaitSync::Result Result;
+  Result* result_dst = GetSharedMemoryAs<Result*>(
+      c.result_shm_id, c.result_shm_offset, sizeof(*result_dst));
+  if (!result_dst) {
+    return error::kOutOfBounds;
+  }
+  if (*result_dst != GL_WAIT_FAILED) {
+    return error::kInvalidArguments;
+  }
+  GLsync service_sync = 0;
+  if (!group_->GetSyncServiceId(sync, &service_sync)) {
+    LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, "ClientWaitSync", "invalid sync");
+    return error::kNoError;
+  }
+  *result_dst = glClientWaitSync(service_sync, flags, timeout);
+  return error::kNoError;
+}
+
+error::Error GLES2DecoderImpl::HandleWaitSync(
+    uint32_t immediate_data_size, const void* cmd_data) {
+  if (!unsafe_es3_apis_enabled())
+    return error::kUnknownCommand;
+  const gles2::cmds::WaitSync& c =
+      *static_cast<const gles2::cmds::WaitSync*>(cmd_data);
+  GLuint sync = static_cast<GLuint>(c.sync);
+  GLbitfield flags = static_cast<GLbitfield>(c.flags);
+  GLuint64 timeout = GLES2Util::MapTwoUint32ToUint64(c.timeout_0, c.timeout_1);
+  GLsync service_sync = 0;
+  if (!group_->GetSyncServiceId(sync, &service_sync)) {
+    LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, "WaitSync", "invalid sync");
+    return error::kNoError;
+  }
+  glWaitSync(service_sync, flags, timeout);
+  return error::kNoError;
+}
+
 void GLES2DecoderImpl::OnTextureRefDetachedFromFramebuffer(
     TextureRef* texture_ref) {
   Texture* texture = texture_ref->texture();
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc b/gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc
index 905a390..e07a8eef 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc
@@ -265,6 +265,115 @@
   EXPECT_FALSE(DoIsTexture(client_texture_id_));
 }
 
+TEST_P(GLES2DecoderTest, ClientWaitSyncValid) {
+  typedef cmds::ClientWaitSync::Result Result;
+  Result* result = static_cast<Result*>(shared_memory_address_);
+  cmds::ClientWaitSync cmd;
+  uint32_t v32_0 = 0, v32_1 = 0;
+  GLES2Util::MapUint64ToTwoUint32(0, &v32_0, &v32_1);
+  cmd.Init(client_sync_id_, GL_SYNC_FLUSH_COMMANDS_BIT, v32_0, v32_1,
+           shared_memory_id_, shared_memory_offset_);
+  EXPECT_CALL(*gl_,
+              ClientWaitSync(reinterpret_cast<GLsync>(kServiceSyncId),
+                             GL_SYNC_FLUSH_COMMANDS_BIT, 0))
+      .WillOnce(Return(GL_CONDITION_SATISFIED))
+      .RetiresOnSaturation();
+  *result = GL_WAIT_FAILED;
+  decoder_->set_unsafe_es3_apis_enabled(true);
+  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
+  EXPECT_EQ(static_cast<GLenum>(GL_CONDITION_SATISFIED), *result);
+  EXPECT_EQ(GL_NO_ERROR, GetGLError());
+  decoder_->set_unsafe_es3_apis_enabled(false);
+  EXPECT_EQ(error::kUnknownCommand, ExecuteCmd(cmd));
+}
+
+TEST_P(GLES2DecoderTest, ClientWaitSyncNonZeroTimeoutValid) {
+  typedef cmds::ClientWaitSync::Result Result;
+  Result* result = static_cast<Result*>(shared_memory_address_);
+  cmds::ClientWaitSync cmd;
+  const GLuint64 kTimeout = 0xABCDEF0123456789;
+  uint32_t v32_0 = 0, v32_1 = 0;
+  GLES2Util::MapUint64ToTwoUint32(kTimeout, &v32_0, &v32_1);
+  cmd.Init(client_sync_id_, GL_SYNC_FLUSH_COMMANDS_BIT, v32_0, v32_1,
+           shared_memory_id_, shared_memory_offset_);
+  EXPECT_CALL(*gl_,
+              ClientWaitSync(reinterpret_cast<GLsync>(kServiceSyncId),
+                             GL_SYNC_FLUSH_COMMANDS_BIT, kTimeout))
+      .WillOnce(Return(GL_CONDITION_SATISFIED))
+      .RetiresOnSaturation();
+  *result = GL_WAIT_FAILED;
+  decoder_->set_unsafe_es3_apis_enabled(true);
+  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
+  EXPECT_EQ(static_cast<GLenum>(GL_CONDITION_SATISFIED), *result);
+  EXPECT_EQ(GL_NO_ERROR, GetGLError());
+  decoder_->set_unsafe_es3_apis_enabled(false);
+  EXPECT_EQ(error::kUnknownCommand, ExecuteCmd(cmd));
+}
+
+TEST_P(GLES2DecoderTest, ClientWaitSyncInvalidSyncFails) {
+  typedef cmds::ClientWaitSync::Result Result;
+  Result* result = static_cast<Result*>(shared_memory_address_);
+  cmds::ClientWaitSync cmd;
+  uint32_t v32_0 = 0, v32_1 = 0;
+  GLES2Util::MapUint64ToTwoUint32(0, &v32_0, &v32_1);
+  decoder_->set_unsafe_es3_apis_enabled(true);
+  cmd.Init(kInvalidClientId, GL_SYNC_FLUSH_COMMANDS_BIT, v32_0, v32_1,
+           shared_memory_id_, shared_memory_offset_);
+  *result = GL_WAIT_FAILED;
+  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
+  EXPECT_EQ(static_cast<GLenum>(GL_WAIT_FAILED), *result);
+  EXPECT_EQ(GL_INVALID_VALUE, GetGLError());
+}
+
+TEST_P(GLES2DecoderTest, ClientWaitSyncResultNotInitFails) {
+  typedef cmds::ClientWaitSync::Result Result;
+  Result* result = static_cast<Result*>(shared_memory_address_);
+  cmds::ClientWaitSync cmd;
+  uint32_t v32_0 = 0, v32_1 = 0;
+  GLES2Util::MapUint64ToTwoUint32(0, &v32_0, &v32_1);
+  decoder_->set_unsafe_es3_apis_enabled(true);
+  cmd.Init(client_sync_id_, GL_SYNC_FLUSH_COMMANDS_BIT, v32_0, v32_1,
+           shared_memory_id_, shared_memory_offset_);
+  *result = 1;  // Any value other than GL_WAIT_FAILED
+  EXPECT_NE(error::kNoError, ExecuteCmd(cmd));
+}
+
+TEST_P(GLES2DecoderTest, ClientWaitSyncBadSharedMemoryFails) {
+  typedef cmds::ClientWaitSync::Result Result;
+  Result* result = static_cast<Result*>(shared_memory_address_);
+  cmds::ClientWaitSync cmd;
+  uint32_t v32_0 = 0, v32_1 = 0;
+  GLES2Util::MapUint64ToTwoUint32(0, &v32_0, &v32_1);
+  decoder_->set_unsafe_es3_apis_enabled(true);
+  *result = GL_WAIT_FAILED;
+  cmd.Init(client_sync_id_, GL_SYNC_FLUSH_COMMANDS_BIT, v32_0, v32_1,
+           kInvalidSharedMemoryId, shared_memory_offset_);
+  EXPECT_NE(error::kNoError, ExecuteCmd(cmd));
+
+  *result = GL_WAIT_FAILED;
+  cmd.Init(client_sync_id_, GL_SYNC_FLUSH_COMMANDS_BIT, v32_0, v32_1,
+           shared_memory_id_, kInvalidSharedMemoryOffset);
+  EXPECT_NE(error::kNoError, ExecuteCmd(cmd));
+}
+
+TEST_P(GLES2DecoderTest, WaitSyncValidArgs) {
+  const GLuint64 kTimeout = GL_TIMEOUT_IGNORED;
+  EXPECT_CALL(*gl_, WaitSync(reinterpret_cast<GLsync>(kServiceSyncId),
+                             0, kTimeout))
+      .Times(1)
+      .RetiresOnSaturation();
+
+  uint32_t v32_0 = 0, v32_1 = 0;
+  GLES2Util::MapUint64ToTwoUint32(kTimeout, &v32_0, &v32_1);
+  cmds::WaitSync cmd;
+  cmd.Init(client_sync_id_, 0, v32_0, v32_1);
+  decoder_->set_unsafe_es3_apis_enabled(true);
+  EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
+  EXPECT_EQ(GL_NO_ERROR, GetGLError());
+  decoder_->set_unsafe_es3_apis_enabled(false);
+  EXPECT_EQ(error::kUnknownCommand, ExecuteCmd(cmd));
+}
+
 TEST_P(GLES2DecoderManualInitTest, BindGeneratesResourceFalse) {
   InitState init;
   InitDecoder(init);
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_1_autogen.h b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_1_autogen.h
index a255fe8..f03d3a5 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_1_autogen.h
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_1_autogen.h
@@ -486,6 +486,7 @@
   EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
   EXPECT_EQ(GL_NO_ERROR, GetGLError());
 }
+// TODO(gman): ClientWaitSync
 
 TEST_P(GLES2DecoderTest1, ColorMaskValidArgs) {
   SpecializedSetup<cmds::ColorMask, 0>(true);
@@ -1810,6 +1811,4 @@
 // TODO(gman): GetShaderPrecisionFormat
 
 // TODO(gman): GetShaderSource
-// TODO(gman): GetString
-
 #endif  // GPU_COMMAND_BUFFER_SERVICE_GLES2_CMD_DECODER_UNITTEST_1_AUTOGEN_H_
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_2_autogen.h b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_2_autogen.h
index 2e60219..447914cb 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_2_autogen.h
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_2_autogen.h
@@ -12,6 +12,8 @@
 #ifndef GPU_COMMAND_BUFFER_SERVICE_GLES2_CMD_DECODER_UNITTEST_2_AUTOGEN_H_
 #define GPU_COMMAND_BUFFER_SERVICE_GLES2_CMD_DECODER_UNITTEST_2_AUTOGEN_H_
 
+// TODO(gman): GetString
+
 TEST_P(GLES2DecoderTest2, GetTexParameterfvValidArgs) {
   EXPECT_CALL(*gl_, GetError())
       .WillOnce(Return(GL_NO_ERROR))
@@ -1581,18 +1583,4 @@
   EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
   EXPECT_EQ(GL_NO_ERROR, GetGLError());
 }
-
-TEST_P(GLES2DecoderTest2, VertexAttrib2fvImmediateValidArgs) {
-  cmds::VertexAttrib2fvImmediate& cmd =
-      *GetImmediateAs<cmds::VertexAttrib2fvImmediate>();
-  SpecializedSetup<cmds::VertexAttrib2fvImmediate, 0>(true);
-  GLfloat temp[2] = {
-      0,
-  };
-  cmd.Init(1, &temp[0]);
-  EXPECT_CALL(*gl_, VertexAttrib2fv(1, reinterpret_cast<GLfloat*>(
-                                           ImmediateDataAddress(&cmd))));
-  EXPECT_EQ(error::kNoError, ExecuteImmediateCmd(cmd, sizeof(temp)));
-  EXPECT_EQ(GL_NO_ERROR, GetGLError());
-}
 #endif  // GPU_COMMAND_BUFFER_SERVICE_GLES2_CMD_DECODER_UNITTEST_2_AUTOGEN_H_
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_3_autogen.h b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_3_autogen.h
index 7a58038..1add3697 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_3_autogen.h
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_3_autogen.h
@@ -12,6 +12,20 @@
 #ifndef GPU_COMMAND_BUFFER_SERVICE_GLES2_CMD_DECODER_UNITTEST_3_AUTOGEN_H_
 #define GPU_COMMAND_BUFFER_SERVICE_GLES2_CMD_DECODER_UNITTEST_3_AUTOGEN_H_
 
+TEST_P(GLES2DecoderTest3, VertexAttrib2fvImmediateValidArgs) {
+  cmds::VertexAttrib2fvImmediate& cmd =
+      *GetImmediateAs<cmds::VertexAttrib2fvImmediate>();
+  SpecializedSetup<cmds::VertexAttrib2fvImmediate, 0>(true);
+  GLfloat temp[2] = {
+      0,
+  };
+  cmd.Init(1, &temp[0]);
+  EXPECT_CALL(*gl_, VertexAttrib2fv(1, reinterpret_cast<GLfloat*>(
+                                           ImmediateDataAddress(&cmd))));
+  EXPECT_EQ(error::kNoError, ExecuteImmediateCmd(cmd, sizeof(temp)));
+  EXPECT_EQ(GL_NO_ERROR, GetGLError());
+}
+
 TEST_P(GLES2DecoderTest3, VertexAttrib3fValidArgs) {
   EXPECT_CALL(*gl_, VertexAttrib3f(1, 2, 3, 4));
   SpecializedSetup<cmds::VertexAttrib3f, 0>(true);
@@ -145,6 +159,8 @@
   EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
   EXPECT_EQ(GL_INVALID_VALUE, GetGLError());
 }
+// TODO(gman): WaitSync
+
 // TODO(gman): TexStorage2DEXT
 // TODO(gman): GenQueriesEXTImmediate
 // TODO(gman): DeleteQueriesEXTImmediate
diff --git a/gpu/command_buffer/service/gles2_cmd_validation_autogen.h b/gpu/command_buffer/service/gles2_cmd_validation_autogen.h
index 27c61ad..abf1e90 100644
--- a/gpu/command_buffer/service/gles2_cmd_validation_autogen.h
+++ b/gpu/command_buffer/service/gles2_cmd_validation_autogen.h
@@ -64,6 +64,7 @@
 ValueValidator<GLenum> stencil_op;
 ValueValidator<GLenum> string_type;
 ValueValidator<GLenum> subscription_target;
+ValueValidator<GLbitfield> sync_flush_flags;
 ValueValidator<GLenum> texture_3_d_target;
 ValueValidator<GLenum> texture_bind_target;
 ValueValidator<GLenum> texture_format;
diff --git a/gpu/command_buffer/service/gles2_cmd_validation_implementation_autogen.h b/gpu/command_buffer/service/gles2_cmd_validation_implementation_autogen.h
index 97c3cdd..68df541 100644
--- a/gpu/command_buffer/service/gles2_cmd_validation_implementation_autogen.h
+++ b/gpu/command_buffer/service/gles2_cmd_validation_implementation_autogen.h
@@ -457,6 +457,11 @@
     GL_MOUSE_POSITION_CHROMIUM,
 };
 
+static const GLbitfield valid_sync_flush_flags_table[] = {
+    GL_SYNC_FLUSH_COMMANDS_BIT,
+    0,
+};
+
 static const GLenum valid_texture_3_d_target_table[] = {
     GL_TEXTURE_3D,
     GL_TEXTURE_2D_ARRAY,
@@ -688,6 +693,8 @@
       string_type(valid_string_type_table, arraysize(valid_string_type_table)),
       subscription_target(valid_subscription_target_table,
                           arraysize(valid_subscription_target_table)),
+      sync_flush_flags(valid_sync_flush_flags_table,
+                       arraysize(valid_sync_flush_flags_table)),
       texture_3_d_target(valid_texture_3_d_target_table,
                          arraysize(valid_texture_3_d_target_table)),
       texture_bind_target(valid_texture_bind_target_table,
diff --git a/gpu/command_buffer/service/gpu_timing.cc b/gpu/command_buffer/service/gpu_timing.cc
index 8716448..6ab3e831 100644
--- a/gpu/command_buffer/service/gpu_timing.cc
+++ b/gpu/command_buffer/service/gpu_timing.cc
@@ -5,6 +5,7 @@
 #include "gpu/command_buffer/service/gpu_timing.h"
 
 #include "base/time/time.h"
+#include "ui/gl/gl_bindings.h"
 #include "ui/gl/gl_context.h"
 #include "ui/gl/gl_version_info.h"
 
@@ -23,11 +24,11 @@
 void GPUTimer::Start() {
   // GL_TIMESTAMP and GL_TIMESTAMP_EXT both have the same value.
   glQueryCounter(queries_[0], GL_TIMESTAMP);
-  offset_ = gpu_timing_->CalculateTimerOffset();
 }
 
 void GPUTimer::End() {
   end_requested_ = true;
+  offset_ = gpu_timing_->CalculateTimerOffset();
   glQueryCounter(queries_[1], GL_TIMESTAMP);
 }
 
diff --git a/gpu/command_buffer/service/gpu_timing.h b/gpu/command_buffer/service/gpu_timing.h
index 1d9ecf6..726ac4e 100644
--- a/gpu/command_buffer/service/gpu_timing.h
+++ b/gpu/command_buffer/service/gpu_timing.h
@@ -8,10 +8,12 @@
 #include "base/callback.h"
 #include "base/memory/scoped_ptr.h"
 #include "gpu/gpu_export.h"
-#include "ui/gl/gl_bindings.h"
+
+namespace gfx {
+class GLContext;
+}
 
 namespace gpu {
-
 class GPUTiming;
 
 // Class to compute the amount of time it takes to fully
@@ -30,7 +32,7 @@
   int64 GetDeltaElapsed();
 
  private:
-  GLuint queries_[2];
+  unsigned int queries_[2];
   int64 offset_ = 0;
   bool end_requested_ = false;
   GPUTiming* gpu_timing_;
diff --git a/gpu/command_buffer/service/gpu_tracer.cc b/gpu/command_buffer/service/gpu_tracer.cc
index 6bedb7f..cc484dc 100644
--- a/gpu/command_buffer/service/gpu_tracer.cc
+++ b/gpu/command_buffer/service/gpu_tracer.cc
@@ -12,6 +12,7 @@
 #include "base/trace_event/trace_event.h"
 #include "gpu/command_buffer/common/gles2_cmd_utils.h"
 #include "gpu/command_buffer/service/context_group.h"
+#include "ui/gl/gl_bindings.h"
 #include "ui/gl/gl_version_info.h"
 
 namespace gpu {
diff --git a/gpu/command_buffer/service/gpu_tracer.h b/gpu/command_buffer/service/gpu_tracer.h
index c8ac9d7..7aba6127 100644
--- a/gpu/command_buffer/service/gpu_tracer.h
+++ b/gpu/command_buffer/service/gpu_tracer.h
@@ -17,7 +17,6 @@
 #include "gpu/command_buffer/service/gles2_cmd_decoder.h"
 #include "gpu/command_buffer/service/gpu_timing.h"
 #include "gpu/gpu_export.h"
-#include "ui/gl/gl_bindings.h"
 
 namespace gpu {
 namespace gles2 {
diff --git a/gpu/command_buffer/service/gpu_tracer_unittest.cc b/gpu/command_buffer/service/gpu_tracer_unittest.cc
index 9005421..ba61ba3 100644
--- a/gpu/command_buffer/service/gpu_tracer_unittest.cc
+++ b/gpu/command_buffer/service/gpu_tracer_unittest.cc
@@ -356,6 +356,7 @@
 
     // Shouldn't be available before End() call
     gl_fake_queries_.SetCurrentGLTime(end_timestamp);
+    g_fakeCPUTime = expect_end_time;
     EXPECT_FALSE(trace->IsAvailable());
 
     trace->End(true);
@@ -474,7 +475,7 @@
       gl_fake_queries_.SetCurrentGLTime(
           end_timestamp +
           (i * base::Time::kNanosecondsPerMicrosecond));
-      g_fakeCPUTime = expect_start_time + i;
+      g_fakeCPUTime = expect_end_time + i;
 
       // Each trace name should be different to differentiate.
       const char num_char = static_cast<char>('0' + i);
diff --git a/gpu/command_buffer/service/mailbox_manager_sync.cc b/gpu/command_buffer/service/mailbox_manager_sync.cc
index 4f24bd7..4cdc80d 100644
--- a/gpu/command_buffer/service/mailbox_manager_sync.cc
+++ b/gpu/command_buffer/service/mailbox_manager_sync.cc
@@ -22,18 +22,6 @@
 
 namespace {
 
-bool SkipTextureWorkarounds(const Texture* texture) {
-  // TODO(sievers): crbug.com/352274
-  // Should probably only fail if it already *has* mipmaps, while allowing
-  // incomplete textures here.
-  bool needs_mips =
-      texture->min_filter() != GL_NEAREST && texture->min_filter() != GL_LINEAR;
-  if (texture->target() != GL_TEXTURE_2D || needs_mips || !texture->IsDefined())
-    return true;
-
-  return false;
-}
-
 base::LazyInstance<base::Lock> g_lock = LAZY_INSTANCE_INITIALIZER;
 
 typedef std::map<uint32, linked_ptr<gfx::GLFence>> SyncPointToFenceMap;
@@ -196,6 +184,15 @@
   DCHECK_EQ(0U, texture_to_group_.size());
 }
 
+// static
+bool MailboxManagerSync::SkipTextureWorkarounds(const Texture* texture) {
+  // Cannot support mips due to support mismatch between
+  // EGL_KHR_gl_texture_2D_image and glEGLImageTargetTexture2DOES for
+  // texture levels.
+  bool has_mips = texture->NeedsMips() && texture->texture_complete();
+  return texture->target() != GL_TEXTURE_2D || has_mips;
+}
+
 bool MailboxManagerSync::UsesSync() {
   return true;
 }
@@ -294,6 +291,7 @@
   if (definition.Matches(texture))
     return;
 
+  DCHECK_IMPLIES(gl_image, image_buffer.get());
   if (gl_image && !image_buffer->IsClient(gl_image)) {
     LOG(ERROR) << "MailboxSync: Incompatible attachment";
     return;
diff --git a/gpu/command_buffer/service/mailbox_manager_sync.h b/gpu/command_buffer/service/mailbox_manager_sync.h
index 4727f3b..4b3abf9 100644
--- a/gpu/command_buffer/service/mailbox_manager_sync.h
+++ b/gpu/command_buffer/service/mailbox_manager_sync.h
@@ -40,6 +40,8 @@
  private:
   friend class base::RefCounted<MailboxManager>;
 
+  static bool SkipTextureWorkarounds(const Texture* texture);
+
   ~MailboxManagerSync() override;
 
   class TextureGroup : public base::RefCounted<TextureGroup> {
diff --git a/gpu/command_buffer/service/mailbox_manager_unittest.cc b/gpu/command_buffer/service/mailbox_manager_unittest.cc
index 388e1da..a22078c 100644
--- a/gpu/command_buffer/service/mailbox_manager_unittest.cc
+++ b/gpu/command_buffer/service/mailbox_manager_unittest.cc
@@ -595,6 +595,66 @@
   EXPECT_EQ(NULL, manager2_->ConsumeTexture(name));
 }
 
+TEST_F(MailboxManagerSyncTest, SyncIncompleteTexture) {
+  const GLuint kNewTextureId = 1234;
+
+  // Create but not define texture.
+  Texture* texture = CreateTexture();
+  SetTarget(texture, GL_TEXTURE_2D, 1);
+  EXPECT_FALSE(texture->IsDefined());
+
+  Mailbox name = Mailbox::Generate();
+  manager_->ProduceTexture(name, texture);
+  EXPECT_EQ(texture, manager_->ConsumeTexture(name));
+
+  // Synchronize
+  manager_->PushTextureUpdates(0);
+  manager2_->PullTextureUpdates(0);
+
+  // Should sync to new texture which is not defined.
+  EXPECT_CALL(*gl_, GenTextures(1, _))
+      .WillOnce(SetArgPointee<1>(kNewTextureId));
+  SetupUpdateTexParamExpectations(kNewTextureId, texture->min_filter(),
+                                  texture->mag_filter(), texture->wrap_s(),
+                                  texture->wrap_t());
+  Texture* new_texture = manager2_->ConsumeTexture(name);
+  ASSERT_TRUE(new_texture);
+  EXPECT_NE(texture, new_texture);
+  EXPECT_EQ(kNewTextureId, new_texture->service_id());
+  EXPECT_FALSE(new_texture->IsDefined());
+
+  // Change cleared to false.
+  SetLevelInfo(texture,
+               GL_TEXTURE_2D,
+               0,
+               GL_RGBA,
+               1,
+               1,
+               1,
+               0,
+               GL_RGBA,
+               GL_UNSIGNED_BYTE,
+               true);
+  SetParameter(texture, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+  SetParameter(texture, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+  EXPECT_TRUE(texture->IsDefined());
+
+  // Synchronize
+  manager_->PushTextureUpdates(0);
+  SetupUpdateTexParamExpectations(
+      kNewTextureId, GL_LINEAR, GL_LINEAR, GL_REPEAT, GL_REPEAT);
+  manager2_->PullTextureUpdates(0);
+
+  // Cleared state should be synced.
+  EXPECT_TRUE(new_texture->IsDefined());
+
+  DestroyTexture(texture);
+  DestroyTexture(new_texture);
+
+  EXPECT_EQ(NULL, manager_->ConsumeTexture(name));
+  EXPECT_EQ(NULL, manager2_->ConsumeTexture(name));
+}
+
 // Putting the same texture into multiple mailboxes should result in sharing
 // only a single texture also within a synchronized manager instance.
 TEST_F(MailboxManagerSyncTest, SharedThroughMultipleMailboxes) {
@@ -671,8 +731,6 @@
   DestroyTexture(new_texture);
 }
 
-// TODO: Produce incomplete texture
-
 // TODO: Texture::level_infos_[][].size()
 
 // TODO: unsupported targets and formats
diff --git a/gpu/command_buffer/service/shader_manager.cc b/gpu/command_buffer/service/shader_manager.cc
index 055cd70..d88a6288 100644
--- a/gpu/command_buffer/service/shader_manager.cc
+++ b/gpu/command_buffer/service/shader_manager.cc
@@ -81,15 +81,18 @@
     glGetShaderiv(service_id_,
                   GL_TRANSLATED_SHADER_SOURCE_LENGTH_ANGLE,
                   &max_len);
+    source_for_driver = "\0";
     translated_source_.resize(max_len);
-    GLint len = 0;
-    glGetTranslatedShaderSourceANGLE(
-        service_id_, translated_source_.size(),
-        &len, &translated_source_.at(0));
-    DCHECK(max_len == 0 || len < max_len);
-    DCHECK(len == 0 || translated_source_[len] == '\0');
-    translated_source_.resize(len);
-    source_for_driver = translated_source_.c_str();
+    if (max_len) {
+      GLint len = 0;
+      glGetTranslatedShaderSourceANGLE(
+          service_id_, translated_source_.size(),
+          &len, &translated_source_.at(0));
+      DCHECK(max_len == 0 || len < max_len);
+      DCHECK(len == 0 || translated_source_[len] == '\0');
+      translated_source_.resize(len);
+      source_for_driver = translated_source_.c_str();
+    }
   }
 
   GLint status = GL_FALSE;
@@ -97,18 +100,21 @@
   if (status == GL_TRUE) {
     valid_ = true;
   } else {
+    valid_ = false;
+
     // We cannot reach here if we are using the shader translator.
     // All invalid shaders must be rejected by the translator.
     // All translated shaders must compile.
     GLint max_len = 0;
     glGetShaderiv(service_id_, GL_INFO_LOG_LENGTH, &max_len);
     log_info_.resize(max_len);
-    GLint len = 0;
-    glGetShaderInfoLog(service_id_, log_info_.size(), &len, &log_info_.at(0));
-    DCHECK(max_len == 0 || len < max_len);
-    DCHECK(len == 0 || log_info_[len] == '\0');
-    valid_ = false;
-    log_info_.resize(len);
+    if (max_len) {
+      GLint len = 0;
+      glGetShaderInfoLog(service_id_, log_info_.size(), &len, &log_info_.at(0));
+      DCHECK(max_len == 0 || len < max_len);
+      DCHECK(len == 0 || log_info_[len] == '\0');
+      log_info_.resize(len);
+    }
     LOG_IF(ERROR, translator)
         << "Shader translator allowed/produced an invalid shader "
         << "unless the driver is buggy:"
diff --git a/gpu/command_buffer/service/texture_definition.cc b/gpu/command_buffer/service/texture_definition.cc
index 7af662c4..f2091b55 100644
--- a/gpu/command_buffer/service/texture_definition.cc
+++ b/gpu/command_buffer/service/texture_definition.cc
@@ -159,8 +159,11 @@
   EGLImageKHR egl_image = eglCreateImageKHR(
       egl_display, egl_context, egl_target, egl_buffer, egl_attrib_list);
 
-  if (egl_image == EGL_NO_IMAGE_KHR)
+  if (egl_image == EGL_NO_IMAGE_KHR) {
+    LOG(ERROR) << "eglCreateImageKHR for cross-thread sharing failed: 0x"
+               << std::hex << eglGetError();
     return NULL;
+  }
 
   return new NativeImageBufferEGL(egl_display, egl_image);
 }
@@ -257,6 +260,18 @@
   }
 }
 
+TextureDefinition::LevelInfo::LevelInfo()
+    : target(0),
+      internal_format(0),
+      width(0),
+      height(0),
+      depth(0),
+      border(0),
+      format(0),
+      type(0),
+      cleared(false) {
+}
+
 TextureDefinition::LevelInfo::LevelInfo(GLenum target,
                                         GLenum internal_format,
                                         GLsizei width,
@@ -295,53 +310,39 @@
     const scoped_refptr<NativeImageBuffer>& image_buffer)
     : version_(version),
       target_(texture->target()),
-      image_buffer_(image_buffer.get()
-                        ? image_buffer
-                        : NativeImageBuffer::Create(texture->service_id())),
+      image_buffer_(image_buffer),
       min_filter_(texture->min_filter()),
       mag_filter_(texture->mag_filter()),
       wrap_s_(texture->wrap_s()),
       wrap_t_(texture->wrap_t()),
       usage_(texture->usage()),
-      immutable_(texture->IsImmutable()) {
-  // TODO
-  DCHECK(!texture->face_infos_.empty());
-  DCHECK(!texture->face_infos_[0].level_infos.empty());
-  DCHECK(!texture->NeedsMips());
-  DCHECK(texture->face_infos_[0].level_infos[0].width);
-  DCHECK(texture->face_infos_[0].level_infos[0].height);
+      immutable_(texture->IsImmutable()),
+      defined_(texture->IsDefined()) {
+  DCHECK_IMPLIES(image_buffer_.get(), defined_);
+  if (!image_buffer_.get() && defined_) {
+    image_buffer_ = NativeImageBuffer::Create(texture->service_id());
+    DCHECK(image_buffer_.get());
+  }
 
   const Texture::FaceInfo& first_face = texture->face_infos_[0];
-  scoped_refptr<gfx::GLImage> gl_image(
-      new GLImageSync(image_buffer_,
-                      gfx::Size(first_face.level_infos[0].width,
-                                first_face.level_infos[0].height)));
-  texture->SetLevelImage(NULL, target_, 0, gl_image.get());
+  if (image_buffer_.get()) {
+    scoped_refptr<gfx::GLImage> gl_image(
+        new GLImageSync(image_buffer_,
+                        gfx::Size(first_face.level_infos[0].width,
+                                  first_face.level_infos[0].height)));
+    texture->SetLevelImage(NULL, target_, 0, gl_image.get());
+  }
 
-  // TODO: all levels
-  level_infos_.clear();
   const Texture::LevelInfo& level = first_face.level_infos[0];
-  LevelInfo info(level.target,
-                 level.internal_format,
-                 level.width,
-                 level.height,
-                 level.depth,
-                 level.border,
-                 level.format,
-                 level.type,
-                 level.cleared);
-  std::vector<LevelInfo> infos;
-  infos.push_back(info);
-  level_infos_.push_back(infos);
+  level_info_ = LevelInfo(level.target, level.internal_format, level.width,
+                          level.height, level.depth, level.border, level.format,
+                          level.type, level.cleared);
 }
 
 TextureDefinition::~TextureDefinition() {
 }
 
 Texture* TextureDefinition::CreateTexture() const {
-  if (!image_buffer_.get())
-    return NULL;
-
   GLuint texture_id;
   glGenTextures(1, &texture_id);
 
@@ -367,28 +368,16 @@
   // though.
   glFlush();
 
-  texture->face_infos_.resize(1);
-  for (size_t i = 0; i < level_infos_.size(); i++) {
-    const LevelInfo& base_info = level_infos_[i][0];
-    const size_t levels_needed = TextureManager::ComputeMipMapCount(
-        base_info.target, base_info.width, base_info.height, base_info.depth);
-    DCHECK(level_infos_.size() <= levels_needed);
-    texture->face_infos_[0].level_infos.resize(levels_needed);
-    for (size_t n = 0; n < level_infos_.size(); n++) {
-      const LevelInfo& info = level_infos_[i][n];
-      texture->SetLevelInfo(NULL,
-                            info.target,
-                            i,
-                            info.internal_format,
-                            info.width,
-                            info.height,
-                            info.depth,
-                            info.border,
-                            info.format,
-                            info.type,
-                            info.cleared);
-    }
+  if (defined_) {
+    texture->face_infos_.resize(1);
+    texture->face_infos_[0].level_infos.resize(1);
+    texture->SetLevelInfo(NULL, level_info_.target, 0,
+                          level_info_.internal_format, level_info_.width,
+                          level_info_.height, level_info_.depth,
+                          level_info_.border, level_info_.format,
+                          level_info_.type, level_info_.cleared);
   }
+
   if (image_buffer_.get()) {
     texture->SetLevelImage(
         NULL,
@@ -396,7 +385,7 @@
         0,
         new GLImageSync(
             image_buffer_,
-            gfx::Size(level_infos_[0][0].width, level_infos_[0][0].height)));
+            gfx::Size(level_info_.width, level_info_.height)));
   }
 
   texture->target_ = target_;
@@ -418,6 +407,10 @@
     return false;
   }
 
+  // Texture became defined.
+  if (!image_buffer_.get() && texture->IsDefined())
+    return false;
+
   // All structural changes should have orphaned the texture.
   if (image_buffer_.get() && !texture->GetLevelImage(texture->target(), 0))
     return false;
@@ -426,14 +419,7 @@
 }
 
 bool TextureDefinition::SafeToRenderFrom() const {
-  for (const std::vector<LevelInfo>& face_info : level_infos_) {
-    for (const LevelInfo& level_info : face_info) {
-      if (!level_info.cleared) {
-        return false;
-      }
-    }
-  }
-  return true;
+  return level_info_.cleared;
 }
 
 }  // namespace gles2
diff --git a/gpu/command_buffer/service/texture_definition.h b/gpu/command_buffer/service/texture_definition.h
index 95f0fa2e0..dcab0b8 100644
--- a/gpu/command_buffer/service/texture_definition.h
+++ b/gpu/command_buffer/service/texture_definition.h
@@ -61,6 +61,7 @@
   bool SafeToRenderFrom() const;
 
   struct LevelInfo {
+    LevelInfo();
     LevelInfo(GLenum target,
               GLenum internal_format,
               GLsizei width,
@@ -83,8 +84,6 @@
     bool cleared;
   };
 
-  typedef std::vector<std::vector<LevelInfo> > LevelInfos;
-
   unsigned int version_;
   GLenum target_;
   scoped_refptr<NativeImageBuffer> image_buffer_;
@@ -94,7 +93,10 @@
   GLenum wrap_t_;
   GLenum usage_;
   bool immutable_;
-  LevelInfos level_infos_;
+  bool defined_;
+
+  // Only support textures with one face and one level.
+  LevelInfo level_info_;
 };
 
 }  // namespage gles2
diff --git a/gpu/config/BUILD.gn b/gpu/config/BUILD.gn
index 1d86e69..4d068ed 100644
--- a/gpu/config/BUILD.gn
+++ b/gpu/config/BUILD.gn
@@ -45,6 +45,9 @@
     "software_rendering_list_json.cc",
   ]
 
+  # TODO(jschuh): size_t to int.
+  configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
+
   defines = [ "GPU_IMPLEMENTATION" ]
 
   deps = [
@@ -69,9 +72,6 @@
         "//third_party/amd/amd_videocard_info_win.cc",
       ]
     }
-
-    # TODO(jschuh): size_t to int.
-    cflags = [ "/wd4267" ]
   }
   if (use_libpci) {
     defines += [ "USE_LIBPCI=1" ]
diff --git a/gpu/config/gpu_driver_bug_list_json.cc b/gpu/config/gpu_driver_bug_list_json.cc
index 53361eb..dfebf2b6 100644
--- a/gpu/config/gpu_driver_bug_list_json.cc
+++ b/gpu/config/gpu_driver_bug_list_json.cc
@@ -19,7 +19,7 @@
 {
   "name": "gpu driver bug list",
   // Please update the version number whenever you change this file.
-  "version": "7.16",
+  "version": "7.17",
   "entries": [
     {
       "id": 1,
@@ -1159,6 +1159,23 @@
       "features": [
         "disable_post_sub_buffers_for_onscreen_surfaces"
       ]
+    },
+    {
+      "id": 102,
+      "description": "Adreno 420 driver loses FBO attachment contents on bound FBO deletion",
+      "cr_bugs": [457027],
+      "os": {
+        "type": "android",
+        "version": {
+          "op": ">",
+          "value": "5.0.2"
+        }
+      },
+      "gl_vendor": "Qualcomm.*",
+      "gl_renderer": ".*420",
+      "features": [
+        "unbind_attachments_on_bound_render_fbo_delete"
+      ]
     }
   ]
 }
diff --git a/gpu/config/gpu_driver_bug_workaround_type.h b/gpu/config/gpu_driver_bug_workaround_type.h
index f03b7a2..9476bf2 100644
--- a/gpu/config/gpu_driver_bug_workaround_type.h
+++ b/gpu/config/gpu_driver_bug_workaround_type.h
@@ -100,6 +100,8 @@
          swizzle_rgba_for_async_readpixels)                  \
   GPU_OP(TEXSUBIMAGE2D_FASTER_THAN_TEXIMAGE2D,               \
          texsubimage2d_faster_than_teximage2d)               \
+  GPU_OP(UNBIND_ATTACHMENTS_ON_BOUND_RENDER_FBO_DELETE,      \
+         unbind_attachments_on_bound_render_fbo_delete)      \
   GPU_OP(UNBIND_FBO_ON_CONTEXT_SWITCH,                       \
          unbind_fbo_on_context_switch)                       \
   GPU_OP(UNFOLD_SHORT_CIRCUIT_AS_TERNARY_OPERATION,          \
diff --git a/gpu/perftests/measurements.cc b/gpu/perftests/measurements.cc
index 270e459..f94d1cb 100644
--- a/gpu/perftests/measurements.cc
+++ b/gpu/perftests/measurements.cc
@@ -4,6 +4,8 @@
 
 #include "gpu/perftests/measurements.h"
 
+#include "base/logging.h"
+#include "gpu/command_buffer/service/gpu_timing.h"
 #include "testing/perf/perf_test.h"
 
 namespace gpu {
diff --git a/gpu/perftests/measurements.h b/gpu/perftests/measurements.h
index 15be422..1f72b00 100644
--- a/gpu/perftests/measurements.h
+++ b/gpu/perftests/measurements.h
@@ -9,10 +9,10 @@
 
 #include "base/memory/scoped_ptr.h"
 #include "base/time/time.h"
-#include "gpu/command_buffer/service/gpu_timing.h"
-#include "ui/gl/gl_bindings.h"
 
 namespace gpu {
+class GPUTiming;
+class GPUTimer;
 struct Measurement {
   Measurement();
   Measurement(const Measurement& m);
diff --git a/gpu/perftests/texture_upload_perftest.cc b/gpu/perftests/texture_upload_perftest.cc
index f691c9e..2e9ef6c 100644
--- a/gpu/perftests/texture_upload_perftest.cc
+++ b/gpu/perftests/texture_upload_perftest.cc
@@ -6,8 +6,10 @@
 #include <vector>
 
 #include "base/containers/small_map.h"
+#include "base/logging.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/scoped_ptr.h"
+#include "gpu/command_buffer/service/gpu_timing.h"
 #include "gpu/perftests/measurements.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/gfx/geometry/size.h"
@@ -31,7 +33,7 @@
   varying vec2 v_texCoord;
   void main() {
     gl_Position = vec4(a_position.x, a_position.y, 0.0, 1.0);
-    v_texCoord = vec2((a_position.x + 1) * 0.5, (a_position.y + 1) * 0.5);
+    v_texCoord = vec2((a_position.x + 1.0) * 0.5, (a_position.y + 1.0) * 0.5);
   }
 );
 const char kFragmentShader[] =
@@ -96,12 +98,30 @@
   void SetUp() override {
     // Initialize an offscreen surface and a gl context.
     gfx::GLSurface::InitializeOneOff();
-    surface_ = gfx::GLSurface::CreateOffscreenGLSurface(size_);
+    surface_ = gfx::GLSurface::CreateOffscreenGLSurface(gfx::Size(4, 4));
     gl_context_ = gfx::GLContext::CreateGLContext(NULL,  // share_group
                                                   surface_.get(),
                                                   gfx::PreferIntegratedGpu);
-
     ui::ScopedMakeCurrent smc(gl_context_.get(), surface_.get());
+    glGenTextures(1, &color_texture_);
+    glBindTexture(GL_TEXTURE_2D, color_texture_);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size_.width(), size_.height(), 0,
+                 GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
+
+    glGenFramebuffersEXT(1, &framebuffer_object_);
+    glBindFramebufferEXT(GL_FRAMEBUFFER, framebuffer_object_);
+
+    glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+                              GL_TEXTURE_2D, color_texture_, 0);
+    DCHECK_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_COMPLETE),
+              glCheckFramebufferStatusEXT(GL_FRAMEBUFFER));
+
+    glViewport(0, 0, size_.width(), size_.height());
+
     if (gpu_timing_.Initialize(gl_context_.get())) {
       LOG(INFO) << "Gpu timing initialized with timer type: "
                 << gpu_timing_.GetTimerTypeName();
@@ -110,7 +130,6 @@
     } else {
       LOG(WARNING) << "Can't initialize gpu timing";
     }
-
     // Prepare a simple program and a vertex buffer that will be
     // used to draw a quad on the offscreen surface.
     vertex_shader_ = LoadShader(GL_VERTEX_SHADER, kVertexShader);
@@ -142,18 +161,14 @@
 
   void TearDown() override {
     ui::ScopedMakeCurrent smc(gl_context_.get(), surface_.get());
-    if (program_object_ != 0) {
-      glDeleteProgram(program_object_);
-    }
-    if (vertex_shader_ != 0) {
-      glDeleteShader(vertex_shader_);
-    }
-    if (fragment_shader_ != 0) {
-      glDeleteShader(fragment_shader_);
-    }
-    if (vertex_buffer_ != 0) {
-      glDeleteShader(vertex_buffer_);
-    }
+    glDeleteProgram(program_object_);
+    glDeleteShader(vertex_shader_);
+    glDeleteShader(fragment_shader_);
+    glDeleteShader(vertex_buffer_);
+
+    glBindFramebufferEXT(GL_FRAMEBUFFER, 0);
+    glDeleteFramebuffersEXT(1, &framebuffer_object_);
+    glDeleteTextures(1, &color_texture_);
 
     gl_context_ = nullptr;
     surface_ = nullptr;
@@ -167,6 +182,8 @@
                                          const GLenum format,
                                          const GLenum type) {
     ui::ScopedMakeCurrent smc(gl_context_.get(), surface_.get());
+    DCHECK_NE(0u, framebuffer_object_);
+    glBindFramebufferEXT(GL_FRAMEBUFFER, framebuffer_object_);
 
     MeasurementTimers total_timers(&gpu_timing_);
     GLuint texture_id = 0;
@@ -178,8 +195,8 @@
 
     glTexImage2D(GL_TEXTURE_2D, 0, format, size_.width(), size_.height(), 0,
                  format, type, &pixels[0]);
-    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
-    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
     CheckNoGlError();
@@ -222,11 +239,13 @@
     return measurements;
   }
 
-  const gfx::Size size_;  // for the offscreen surface and the texture
+  const gfx::Size size_;  // for the fbo and the texture
   scoped_refptr<gfx::GLContext> gl_context_;
   scoped_refptr<gfx::GLSurface> surface_;
   GPUTiming gpu_timing_;
 
+  GLuint color_texture_ = 0;
+  GLuint framebuffer_object_ = 0;
   GLuint vertex_shader_ = 0;
   GLuint fragment_shader_ = 0;
   GLuint program_object_ = 0;
diff --git "a/ios/build/bots/chromium.fyi/Chromium_iOS_Device_\050ninja\051.json" "b/ios/build/bots/chromium.fyi/Chromium_iOS_Device_\050ninja\051.json"
index 8f63de1..0cc799e9 100644
--- "a/ios/build/bots/chromium.fyi/Chromium_iOS_Device_\050ninja\051.json"
+++ "b/ios/build/bots/chromium.fyi/Chromium_iOS_Device_\050ninja\051.json"
@@ -3,13 +3,13 @@
     "smut"
   ],
   "comments": [
-    "Builder for 64-bit devices using ninja."
+    "Builder for fat binaries using ninja."
   ],
   "xcode version": "5.1.1",
   "GYP_DEFINES": {
     "chromium_ios_signing": "0",
     "clang_xcode": "0",
-    "target_subarch": "arm64"
+    "target_subarch": "both"
   },
   "compiler": "ninja",
   "configuration": "Release",
diff --git a/ios/build/bots/chromium.mac/iOS_Device.json b/ios/build/bots/chromium.mac/iOS_Device.json
new file mode 100644
index 0000000..e30db2c
--- /dev/null
+++ b/ios/build/bots/chromium.mac/iOS_Device.json
@@ -0,0 +1,18 @@
+{
+  "owners": [
+    "smut"
+  ],
+  "comments": [
+    "Builder for 32-bit devices."
+  ],
+  "xcode version": "5.1.1",
+  "GYP_DEFINES": {
+    "chromium_ios_signing": "0",
+    "target_subarch": "arm32"
+  },
+  "compiler": "xcodebuild",
+  "configuration": "Release",
+  "sdk": "iphoneos7.1",
+  "tests": [
+  ]
+}
diff --git "a/ios/build/bots/chromium.mac/iOS_Device_\050ninja\051.json" "b/ios/build/bots/chromium.mac/iOS_Device_\050ninja\051.json"
new file mode 100644
index 0000000..0cc799e9
--- /dev/null
+++ "b/ios/build/bots/chromium.mac/iOS_Device_\050ninja\051.json"
@@ -0,0 +1,19 @@
+{
+  "owners": [
+    "smut"
+  ],
+  "comments": [
+    "Builder for fat binaries using ninja."
+  ],
+  "xcode version": "5.1.1",
+  "GYP_DEFINES": {
+    "chromium_ios_signing": "0",
+    "clang_xcode": "0",
+    "target_subarch": "both"
+  },
+  "compiler": "ninja",
+  "configuration": "Release",
+  "sdk": "iphoneos7.1",
+  "tests": [
+  ]
+}
diff --git "a/ios/build/bots/chromium.mac/iOS_Simulator_\050dbg\051.json" "b/ios/build/bots/chromium.mac/iOS_Simulator_\050dbg\051.json"
new file mode 100644
index 0000000..8c87e1d
--- /dev/null
+++ "b/ios/build/bots/chromium.mac/iOS_Simulator_\050dbg\051.json"
@@ -0,0 +1,78 @@
+{
+  "owners": [
+    "smut"
+  ],
+  "comments": [
+    "Tests for 32-bit iOS 7.1 iPhone simulator."
+  ],
+  "xcode version": "5.1.1",
+  "GYP_DEFINES": {
+    "chromium_ios_signing": "0",
+    "target_subarch": "arm32"
+  },
+  "compiler": "xcodebuild",
+  "configuration": "Debug",
+  "sdk": "iphonesimulator7.1",
+  "tests": [
+    {
+      "app": "ios_chrome_unittests",
+      "device type": "iPhone Retina (4-inch)",
+      "os": "7.1"
+    },
+    {
+      "app": "base_unittests",
+      "device type": "iPhone Retina (4-inch)",
+      "os": "7.1"
+    },
+    {
+      "app": "components_unittests",
+      "device type": "iPhone Retina (4-inch)",
+      "os": "7.1"
+    },
+    {
+      "app": "crypto_unittests",
+      "device type": "iPhone Retina (4-inch)",
+      "os": "7.1"
+    },
+    {
+      "app": "gfx_unittests",
+      "device type": "iPhone Retina (4-inch)",
+      "os": "7.1"
+    },
+    {
+      "app": "url_unittests",
+      "device type": "iPhone Retina (4-inch)",
+      "os": "7.1"
+    },
+    {
+      "app": "content_unittests",
+      "device type": "iPhone Retina (4-inch)",
+      "os": "7.1"
+    },
+    {
+      "app": "net_unittests",
+      "device type": "iPhone Retina (4-inch)",
+      "os": "7.1"
+    },
+    {
+      "app": "ui_base_unittests",
+      "device type": "iPhone Retina (4-inch)",
+      "os": "7.1"
+    },
+    {
+      "app": "ui_ios_unittests",
+      "device type": "iPhone Retina (4-inch)",
+      "os": "7.1"
+    },
+    {
+      "app": "sync_unit_tests",
+      "device type": "iPhone Retina (4-inch)",
+      "os": "7.1"
+    },
+    {
+      "app": "sql_unittests",
+      "device type": "iPhone Retina (4-inch)",
+      "os": "7.1"
+    }
+  ]
+}
diff --git a/ios/build/bots/tryserver.chromium.mac/ios_dbg_simulator.json b/ios/build/bots/tryserver.chromium.mac/ios_dbg_simulator.json
new file mode 100644
index 0000000..8c87e1d
--- /dev/null
+++ b/ios/build/bots/tryserver.chromium.mac/ios_dbg_simulator.json
@@ -0,0 +1,78 @@
+{
+  "owners": [
+    "smut"
+  ],
+  "comments": [
+    "Tests for 32-bit iOS 7.1 iPhone simulator."
+  ],
+  "xcode version": "5.1.1",
+  "GYP_DEFINES": {
+    "chromium_ios_signing": "0",
+    "target_subarch": "arm32"
+  },
+  "compiler": "xcodebuild",
+  "configuration": "Debug",
+  "sdk": "iphonesimulator7.1",
+  "tests": [
+    {
+      "app": "ios_chrome_unittests",
+      "device type": "iPhone Retina (4-inch)",
+      "os": "7.1"
+    },
+    {
+      "app": "base_unittests",
+      "device type": "iPhone Retina (4-inch)",
+      "os": "7.1"
+    },
+    {
+      "app": "components_unittests",
+      "device type": "iPhone Retina (4-inch)",
+      "os": "7.1"
+    },
+    {
+      "app": "crypto_unittests",
+      "device type": "iPhone Retina (4-inch)",
+      "os": "7.1"
+    },
+    {
+      "app": "gfx_unittests",
+      "device type": "iPhone Retina (4-inch)",
+      "os": "7.1"
+    },
+    {
+      "app": "url_unittests",
+      "device type": "iPhone Retina (4-inch)",
+      "os": "7.1"
+    },
+    {
+      "app": "content_unittests",
+      "device type": "iPhone Retina (4-inch)",
+      "os": "7.1"
+    },
+    {
+      "app": "net_unittests",
+      "device type": "iPhone Retina (4-inch)",
+      "os": "7.1"
+    },
+    {
+      "app": "ui_base_unittests",
+      "device type": "iPhone Retina (4-inch)",
+      "os": "7.1"
+    },
+    {
+      "app": "ui_ios_unittests",
+      "device type": "iPhone Retina (4-inch)",
+      "os": "7.1"
+    },
+    {
+      "app": "sync_unit_tests",
+      "device type": "iPhone Retina (4-inch)",
+      "os": "7.1"
+    },
+    {
+      "app": "sql_unittests",
+      "device type": "iPhone Retina (4-inch)",
+      "os": "7.1"
+    }
+  ]
+}
diff --git a/ios/build/bots/tryserver.chromium.mac/ios_dbg_simulator_ng.json b/ios/build/bots/tryserver.chromium.mac/ios_dbg_simulator_ng.json
new file mode 100644
index 0000000..8c87e1d
--- /dev/null
+++ b/ios/build/bots/tryserver.chromium.mac/ios_dbg_simulator_ng.json
@@ -0,0 +1,78 @@
+{
+  "owners": [
+    "smut"
+  ],
+  "comments": [
+    "Tests for 32-bit iOS 7.1 iPhone simulator."
+  ],
+  "xcode version": "5.1.1",
+  "GYP_DEFINES": {
+    "chromium_ios_signing": "0",
+    "target_subarch": "arm32"
+  },
+  "compiler": "xcodebuild",
+  "configuration": "Debug",
+  "sdk": "iphonesimulator7.1",
+  "tests": [
+    {
+      "app": "ios_chrome_unittests",
+      "device type": "iPhone Retina (4-inch)",
+      "os": "7.1"
+    },
+    {
+      "app": "base_unittests",
+      "device type": "iPhone Retina (4-inch)",
+      "os": "7.1"
+    },
+    {
+      "app": "components_unittests",
+      "device type": "iPhone Retina (4-inch)",
+      "os": "7.1"
+    },
+    {
+      "app": "crypto_unittests",
+      "device type": "iPhone Retina (4-inch)",
+      "os": "7.1"
+    },
+    {
+      "app": "gfx_unittests",
+      "device type": "iPhone Retina (4-inch)",
+      "os": "7.1"
+    },
+    {
+      "app": "url_unittests",
+      "device type": "iPhone Retina (4-inch)",
+      "os": "7.1"
+    },
+    {
+      "app": "content_unittests",
+      "device type": "iPhone Retina (4-inch)",
+      "os": "7.1"
+    },
+    {
+      "app": "net_unittests",
+      "device type": "iPhone Retina (4-inch)",
+      "os": "7.1"
+    },
+    {
+      "app": "ui_base_unittests",
+      "device type": "iPhone Retina (4-inch)",
+      "os": "7.1"
+    },
+    {
+      "app": "ui_ios_unittests",
+      "device type": "iPhone Retina (4-inch)",
+      "os": "7.1"
+    },
+    {
+      "app": "sync_unit_tests",
+      "device type": "iPhone Retina (4-inch)",
+      "os": "7.1"
+    },
+    {
+      "app": "sql_unittests",
+      "device type": "iPhone Retina (4-inch)",
+      "os": "7.1"
+    }
+  ]
+}
diff --git a/ios/build/bots/tryserver.chromium.mac/ios_dbg_simulator_ninja.json b/ios/build/bots/tryserver.chromium.mac/ios_dbg_simulator_ninja.json
new file mode 100644
index 0000000..d61cff0
--- /dev/null
+++ b/ios/build/bots/tryserver.chromium.mac/ios_dbg_simulator_ninja.json
@@ -0,0 +1,78 @@
+{
+  "owners": [
+    "smut"
+  ],
+  "comments": [
+    "Tests for 32-bit iOS 7.1 iPhone simulator."
+  ],
+  "xcode version": "5.1.1",
+  "GYP_DEFINES": {
+    "chromium_ios_signing": "0",
+    "target_subarch": "arm32"
+  },
+  "compiler": "ninja",
+  "configuration": "Debug",
+  "sdk": "iphonesimulator7.1",
+  "tests": [
+    {
+      "app": "ios_chrome_unittests",
+      "device type": "iPhone Retina (4-inch)",
+      "os": "7.1"
+    },
+    {
+      "app": "base_unittests",
+      "device type": "iPhone Retina (4-inch)",
+      "os": "7.1"
+    },
+    {
+      "app": "components_unittests",
+      "device type": "iPhone Retina (4-inch)",
+      "os": "7.1"
+    },
+    {
+      "app": "crypto_unittests",
+      "device type": "iPhone Retina (4-inch)",
+      "os": "7.1"
+    },
+    {
+      "app": "gfx_unittests",
+      "device type": "iPhone Retina (4-inch)",
+      "os": "7.1"
+    },
+    {
+      "app": "url_unittests",
+      "device type": "iPhone Retina (4-inch)",
+      "os": "7.1"
+    },
+    {
+      "app": "content_unittests",
+      "device type": "iPhone Retina (4-inch)",
+      "os": "7.1"
+    },
+    {
+      "app": "net_unittests",
+      "device type": "iPhone Retina (4-inch)",
+      "os": "7.1"
+    },
+    {
+      "app": "ui_base_unittests",
+      "device type": "iPhone Retina (4-inch)",
+      "os": "7.1"
+    },
+    {
+      "app": "ui_ios_unittests",
+      "device type": "iPhone Retina (4-inch)",
+      "os": "7.1"
+    },
+    {
+      "app": "sync_unit_tests",
+      "device type": "iPhone Retina (4-inch)",
+      "os": "7.1"
+    },
+    {
+      "app": "sql_unittests",
+      "device type": "iPhone Retina (4-inch)",
+      "os": "7.1"
+    }
+  ]
+}
diff --git a/ios/build/bots/tryserver.chromium.mac/ios_dbg_simulator_ninja_ng.json b/ios/build/bots/tryserver.chromium.mac/ios_dbg_simulator_ninja_ng.json
new file mode 100644
index 0000000..d61cff0
--- /dev/null
+++ b/ios/build/bots/tryserver.chromium.mac/ios_dbg_simulator_ninja_ng.json
@@ -0,0 +1,78 @@
+{
+  "owners": [
+    "smut"
+  ],
+  "comments": [
+    "Tests for 32-bit iOS 7.1 iPhone simulator."
+  ],
+  "xcode version": "5.1.1",
+  "GYP_DEFINES": {
+    "chromium_ios_signing": "0",
+    "target_subarch": "arm32"
+  },
+  "compiler": "ninja",
+  "configuration": "Debug",
+  "sdk": "iphonesimulator7.1",
+  "tests": [
+    {
+      "app": "ios_chrome_unittests",
+      "device type": "iPhone Retina (4-inch)",
+      "os": "7.1"
+    },
+    {
+      "app": "base_unittests",
+      "device type": "iPhone Retina (4-inch)",
+      "os": "7.1"
+    },
+    {
+      "app": "components_unittests",
+      "device type": "iPhone Retina (4-inch)",
+      "os": "7.1"
+    },
+    {
+      "app": "crypto_unittests",
+      "device type": "iPhone Retina (4-inch)",
+      "os": "7.1"
+    },
+    {
+      "app": "gfx_unittests",
+      "device type": "iPhone Retina (4-inch)",
+      "os": "7.1"
+    },
+    {
+      "app": "url_unittests",
+      "device type": "iPhone Retina (4-inch)",
+      "os": "7.1"
+    },
+    {
+      "app": "content_unittests",
+      "device type": "iPhone Retina (4-inch)",
+      "os": "7.1"
+    },
+    {
+      "app": "net_unittests",
+      "device type": "iPhone Retina (4-inch)",
+      "os": "7.1"
+    },
+    {
+      "app": "ui_base_unittests",
+      "device type": "iPhone Retina (4-inch)",
+      "os": "7.1"
+    },
+    {
+      "app": "ui_ios_unittests",
+      "device type": "iPhone Retina (4-inch)",
+      "os": "7.1"
+    },
+    {
+      "app": "sync_unit_tests",
+      "device type": "iPhone Retina (4-inch)",
+      "os": "7.1"
+    },
+    {
+      "app": "sql_unittests",
+      "device type": "iPhone Retina (4-inch)",
+      "os": "7.1"
+    }
+  ]
+}
diff --git a/ios/build/bots/tryserver.chromium.mac/ios_rel_device.json b/ios/build/bots/tryserver.chromium.mac/ios_rel_device.json
new file mode 100644
index 0000000..e30db2c
--- /dev/null
+++ b/ios/build/bots/tryserver.chromium.mac/ios_rel_device.json
@@ -0,0 +1,18 @@
+{
+  "owners": [
+    "smut"
+  ],
+  "comments": [
+    "Builder for 32-bit devices."
+  ],
+  "xcode version": "5.1.1",
+  "GYP_DEFINES": {
+    "chromium_ios_signing": "0",
+    "target_subarch": "arm32"
+  },
+  "compiler": "xcodebuild",
+  "configuration": "Release",
+  "sdk": "iphoneos7.1",
+  "tests": [
+  ]
+}
diff --git a/ios/build/bots/tryserver.chromium.mac/ios_rel_device_ng.json b/ios/build/bots/tryserver.chromium.mac/ios_rel_device_ng.json
new file mode 100644
index 0000000..e30db2c
--- /dev/null
+++ b/ios/build/bots/tryserver.chromium.mac/ios_rel_device_ng.json
@@ -0,0 +1,18 @@
+{
+  "owners": [
+    "smut"
+  ],
+  "comments": [
+    "Builder for 32-bit devices."
+  ],
+  "xcode version": "5.1.1",
+  "GYP_DEFINES": {
+    "chromium_ios_signing": "0",
+    "target_subarch": "arm32"
+  },
+  "compiler": "xcodebuild",
+  "configuration": "Release",
+  "sdk": "iphoneos7.1",
+  "tests": [
+  ]
+}
diff --git a/ios/build/bots/tryserver.chromium.mac/ios_rel_device_ninja.json b/ios/build/bots/tryserver.chromium.mac/ios_rel_device_ninja.json
new file mode 100644
index 0000000..0cc799e9
--- /dev/null
+++ b/ios/build/bots/tryserver.chromium.mac/ios_rel_device_ninja.json
@@ -0,0 +1,19 @@
+{
+  "owners": [
+    "smut"
+  ],
+  "comments": [
+    "Builder for fat binaries using ninja."
+  ],
+  "xcode version": "5.1.1",
+  "GYP_DEFINES": {
+    "chromium_ios_signing": "0",
+    "clang_xcode": "0",
+    "target_subarch": "both"
+  },
+  "compiler": "ninja",
+  "configuration": "Release",
+  "sdk": "iphoneos7.1",
+  "tests": [
+  ]
+}
diff --git a/ios/build/bots/tryserver.chromium.mac/ios_rel_device_ninja_ng.json b/ios/build/bots/tryserver.chromium.mac/ios_rel_device_ninja_ng.json
new file mode 100644
index 0000000..0cc799e9
--- /dev/null
+++ b/ios/build/bots/tryserver.chromium.mac/ios_rel_device_ninja_ng.json
@@ -0,0 +1,19 @@
+{
+  "owners": [
+    "smut"
+  ],
+  "comments": [
+    "Builder for fat binaries using ninja."
+  ],
+  "xcode version": "5.1.1",
+  "GYP_DEFINES": {
+    "chromium_ios_signing": "0",
+    "clang_xcode": "0",
+    "target_subarch": "both"
+  },
+  "compiler": "ninja",
+  "configuration": "Release",
+  "sdk": "iphoneos7.1",
+  "tests": [
+  ]
+}
diff --git a/ios/chrome/browser/net/mock_image_fetcher.h b/ios/chrome/browser/net/mock_image_fetcher.h
new file mode 100644
index 0000000..2b12af9
--- /dev/null
+++ b/ios/chrome/browser/net/mock_image_fetcher.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 IOS_CHROME_BROWSER_NET_MOCK_IMAGE_FETCHER_H_
+#define IOS_CHROME_BROWSER_NET_MOCK_IMAGE_FETCHER_H_
+
+#import "ios/chrome/browser/net/image_fetcher.h"
+
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace image_fetcher {
+
+// Mock the ImageFetcher utility class, which can be used to asynchronously
+// retrieve an image from an URL.
+class MockImageFetcher : public ImageFetcher {
+ public:
+  explicit MockImageFetcher(const scoped_refptr<base::TaskRunner>& task_runner);
+  ~MockImageFetcher() override;
+
+  MOCK_METHOD4(StartDownload,
+               void(const GURL& url,
+                    ImageFetchedCallback callback,
+                    const std::string& referrer,
+                    net::URLRequest::ReferrerPolicy referrer_policy));
+  MOCK_METHOD2(StartDownload,
+               void(const GURL& url, ImageFetchedCallback callback));
+  MOCK_METHOD1(SetRequestContextGetter,
+               void(const scoped_refptr<net::URLRequestContextGetter>&
+                        request_context_getter));
+};
+
+}  // namespace image_fetcher
+
+#endif  // IOS_CHROME_BROWSER_NET_MOCK_IMAGE_FETCHER_H_
diff --git a/ios/chrome/browser/net/mock_image_fetcher.mm b/ios/chrome/browser/net/mock_image_fetcher.mm
new file mode 100644
index 0000000..84fb327d
--- /dev/null
+++ b/ios/chrome/browser/net/mock_image_fetcher.mm
@@ -0,0 +1,17 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/net/mock_image_fetcher.h"
+
+namespace image_fetcher {
+
+MockImageFetcher::MockImageFetcher(
+    const scoped_refptr<base::TaskRunner>& task_runner)
+    : ImageFetcher(task_runner) {
+}
+
+MockImageFetcher::~MockImageFetcher() {
+}
+
+}  // namespace image_fetcher
diff --git a/ios/chrome/browser/snapshots/snapshot_cache.mm b/ios/chrome/browser/snapshots/snapshot_cache.mm
index 5b8c9a10..420a8e1ce 100644
--- a/ios/chrome/browser/snapshots/snapshot_cache.mm
+++ b/ios/chrome/browser/snapshots/snapshot_cache.mm
@@ -20,7 +20,6 @@
 #include "base/threading/thread_restrictions.h"
 #include "ios/chrome/browser/ui/ui_util.h"
 #import "ios/chrome/browser/ui/uikit_ui_util.h"
-#include "ios/public/consumer/base/util.h"
 #include "ios/web/public/web_thread.h"
 
 @interface SnapshotCache ()
diff --git a/ios/chrome/browser/ui/uikit_ui_util_unittest.mm b/ios/chrome/browser/ui/uikit_ui_util_unittest.mm
index c640071..3c76e156d 100644
--- a/ios/chrome/browser/ui/uikit_ui_util_unittest.mm
+++ b/ios/chrome/browser/ui/uikit_ui_util_unittest.mm
@@ -5,7 +5,6 @@
 #import "ios/chrome/browser/ui/uikit_ui_util.h"
 
 #include "base/basictypes.h"
-#include "base/ios/ios_util.h"
 #include "base/mac/scoped_nsobject.h"
 #include "base/strings/sys_string_conversions.h"
 #import "ios/chrome/browser/ui/ui_util.h"
diff --git a/ios/chrome/ios_chrome_tests.gyp b/ios/chrome/ios_chrome_tests.gyp
index 57185be..8916b97b9 100644
--- a/ios/chrome/ios_chrome_tests.gyp
+++ b/ios/chrome/ios_chrome_tests.gyp
@@ -36,11 +36,14 @@
       'type': 'static_library',
       'dependencies': [
         '../../base/base.gyp:base',
+        '../../testing/gmock.gyp:gmock',
         '../../testing/gtest.gyp:gtest',
         '../provider/ios_provider_chrome.gyp:ios_provider_chrome_browser',
         'ios_chrome.gyp:ios_chrome_browser',
       ],
       'sources': [
+        'browser/net/mock_image_fetcher.h',
+        'browser/net/mock_image_fetcher.mm',
         'test/ios_chrome_unit_test_suite.cc',
         'test/ios_chrome_unit_test_suite.h',
         'test/run_all_unittests.cc',
diff --git a/ios/consumer/OWNERS b/ios/consumer/OWNERS
deleted file mode 100644
index d065c3e..0000000
--- a/ios/consumer/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-sdefresne@chromium.org
-stuartmorgan@chromium.org
diff --git a/ios/consumer/README.txt b/ios/consumer/README.txt
deleted file mode 100644
index b6907eac..0000000
--- a/ios/consumer/README.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This directory exists to allow iOS code that is not yet upstreamed to call
-Chromium code without being vulnerable to breakage during a merge.
-Specifically, not-yet-upstreamed code is allowed to use the interfaces
-provided in public/. Any change to one of these interfaces should get a full
-review from an OWNER, as such a change will require corresponding changes to
-code not yet upstreamed. Any change to code not under public/ can be TBR'd to
-an OWNER.
diff --git a/ios/consumer/base/util.mm b/ios/consumer/base/util.mm
deleted file mode 100644
index 6657c6ba..0000000
--- a/ios/consumer/base/util.mm
+++ /dev/null
@@ -1,14 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/ios/ios_util.h"
-#include "ios/public/consumer/base/util.h"
-
-namespace ios {
-
-bool IsRunningOnOrLater(int major, int minor, int bug_fix) {
-  return base::ios::IsRunningOnOrLater(major, minor, bug_fix);
-}
-
-}  // namespace ios
diff --git a/ios/ios.gyp b/ios/ios.gyp
index 408f197..c519585 100644
--- a/ios/ios.gyp
+++ b/ios/ios.gyp
@@ -11,9 +11,9 @@
       'type': 'none',
       'dependencies': [
         'chrome/ios_chrome_tests.gyp:*',
-        'ios_base.gyp:*',
         'ios_tests_unit.gyp:*',
         'provider/ios_provider_chrome.gyp:*',
+        'provider/ios_provider_web.gyp:*',
         'web/ios_web.gyp:*',
       ],
     },
diff --git a/ios/ios_base.gyp b/ios/ios_base.gyp
deleted file mode 100644
index 79bf7b2..0000000
--- a/ios/ios_base.gyp
+++ /dev/null
@@ -1,23 +0,0 @@
-# Copyright 2013 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-{
-  'variables': {
-    'chromium_code': 1,
-   },
-  'targets': [
-    {
-      'target_name': 'ios_consumer_base',
-      'type': 'static_library',
-      'dependencies': [
-        '../base/base.gyp:base',
-      ],
-      'include_dirs': [
-        '..',
-      ],
-      'sources': [
-        'consumer/base/util.mm',
-      ],
-    },
-  ],
-}
diff --git a/ios/ios_tests_unit.gyp b/ios/ios_tests_unit.gyp
index 3d33685e..817dd47 100644
--- a/ios/ios_tests_unit.gyp
+++ b/ios/ios_tests_unit.gyp
@@ -15,7 +15,6 @@
         '../base/base.gyp:test_support_base',
         '../testing/gmock.gyp:gmock',
         '../testing/gtest.gyp:gtest',
-        'ios_base.gyp:ios_consumer_base',
         'web/ios_web.gyp:ios_web',
         'web/ios_web.gyp:test_support_ios_web',
       ],
diff --git a/ios/provider/ios_provider_web.gyp b/ios/provider/ios_provider_web.gyp
new file mode 100644
index 0000000..fe44f013
--- /dev/null
+++ b/ios/provider/ios_provider_web.gyp
@@ -0,0 +1,25 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+{
+  'variables': {
+    'chromium_code': 1,
+   },
+  'targets': [
+    {
+      'target_name': 'ios_provider_web',
+      'type': 'static_library',
+      'include_dirs': [
+        '../..',
+      ],
+      'dependencies': [
+        '../../base/base.gyp:base',
+        '../web/ios_web.gyp:ios_web',
+      ],
+      'sources': [
+        '../public/provider/web/web_controller_provider.h',
+        '../public/provider/web/web_controller_provider.mm',
+      ],
+    },
+  ],
+}
diff --git a/ios/public/consumer/OWNERS b/ios/public/consumer/OWNERS
deleted file mode 100644
index d065c3e..0000000
--- a/ios/public/consumer/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-sdefresne@chromium.org
-stuartmorgan@chromium.org
diff --git a/ios/public/consumer/base/util.h b/ios/public/consumer/base/util.h
deleted file mode 100644
index a132de5a..0000000
--- a/ios/public/consumer/base/util.h
+++ /dev/null
@@ -1,15 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_PUBLIC_CONSUMER_BASE_UTIL_H_
-#define IOS_PUBLIC_CONSUMER_BASE_UTIL_H_
-
-namespace ios {
-
-// Returns whether the operating system is at the given version or later.
-bool IsRunningOnOrLater(int major, int minor, int bug_fix);
-
-}  // namespace ios
-
-#endif  // IOS_PUBLIC_CONSUMER_BASE_UTIL_H_
diff --git a/ios/public/provider/web/OWNERS b/ios/public/provider/web/OWNERS
new file mode 100644
index 0000000..375b7119
--- /dev/null
+++ b/ios/public/provider/web/OWNERS
@@ -0,0 +1,2 @@
+eugenebut@chromium.org
+stuartmorgan@chromium.org
diff --git a/ios/public/provider/web/web_controller_provider.h b/ios/public/provider/web/web_controller_provider.h
new file mode 100644
index 0000000..4d9f81ba
--- /dev/null
+++ b/ios/public/provider/web/web_controller_provider.h
@@ -0,0 +1,67 @@
+// 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 IOS_PUBLIC_PROVIDER_WEB_WEB_CONTROLLER_PROVIDER_H_
+#define IOS_PUBLIC_PROVIDER_WEB_WEB_CONTROLLER_PROVIDER_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "ios/web/public/block_types.h"
+#include "ios/web/public/web_state/web_state_observer.h"
+#include "url/gurl.h"
+
+namespace web {
+class BrowserState;
+class WebState;
+}
+
+namespace ios {
+
+class WebControllerProviderFactory;
+
+// Setter and getter for the provider factory. The provider factory should be
+// set early, before any component using WebControllerProviders is called.
+void SetWebControllerProviderFactory(
+    WebControllerProviderFactory* provider_factory);
+WebControllerProviderFactory* GetWebControllerProviderFactory();
+
+// Interface that provides URL-loading and JavaScript injection with optional
+// dialog suppression.
+class WebControllerProvider {
+ public:
+  // Constructor for a WebControllerProvider backed by a CRWWebController
+  // initialized with |browser_state|.
+  explicit WebControllerProvider(web::BrowserState* browser_state);
+  virtual ~WebControllerProvider();
+
+  // Determines whether JavaScript dialogs are allowed.
+  virtual bool SuppressesDialogs() const;
+  virtual void SetSuppressesDialogs(bool should_suppress_dialogs) {}
+
+  // Triggers a load of |url|.
+  virtual void LoadURL(const GURL& url) {}
+
+  // Returns the WebState associated with this web controller.
+  virtual web::WebState* GetWebState() const;
+
+  // Injects |script| into the previously loaded page, if any, and calls
+  // |completion| with the result.  Calls |completion| with nil parameters
+  // when there is no previously loaded page.
+  virtual void InjectScript(const std::string& script,
+                            web::JavaScriptCompletion completion);
+};
+
+class WebControllerProviderFactory {
+ public:
+  WebControllerProviderFactory();
+  virtual ~WebControllerProviderFactory();
+
+  // Vends WebControllerProviders created using |browser_state|, passing
+  // ownership to callers.
+  virtual scoped_ptr<WebControllerProvider> CreateWebControllerProvider(
+      web::BrowserState* browser_state);
+};
+
+}  // namespace ios
+
+#endif  // IOS_PUBLIC_PROVIDER_WEB_WEB_CONTROLLER_PROVIDER_H_
diff --git a/ios/public/provider/web/web_controller_provider.mm b/ios/public/provider/web/web_controller_provider.mm
new file mode 100644
index 0000000..82aa875
--- /dev/null
+++ b/ios/public/provider/web/web_controller_provider.mm
@@ -0,0 +1,53 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ios/public/provider/web/web_controller_provider.h"
+
+namespace ios {
+
+static WebControllerProviderFactory* g_web_controller_provider_factory;
+
+void SetWebControllerProviderFactory(
+    WebControllerProviderFactory* provider_factory) {
+  g_web_controller_provider_factory = provider_factory;
+}
+
+WebControllerProviderFactory* GetWebControllerProviderFactory() {
+  return g_web_controller_provider_factory;
+}
+
+WebControllerProvider::WebControllerProvider(web::BrowserState* browser_state) {
+}
+
+WebControllerProvider::~WebControllerProvider() {
+}
+
+bool WebControllerProvider::SuppressesDialogs() const {
+  return false;
+}
+
+web::WebState* WebControllerProvider::GetWebState() const {
+  return nullptr;
+}
+
+void WebControllerProvider::InjectScript(const std::string& script,
+                                         web::JavaScriptCompletion completion) {
+  if (completion)
+    completion(nil, nil);
+}
+
+WebControllerProviderFactory::WebControllerProviderFactory() {
+}
+
+WebControllerProviderFactory::~WebControllerProviderFactory() {
+}
+
+scoped_ptr<WebControllerProvider>
+WebControllerProviderFactory::CreateWebControllerProvider(
+    web::BrowserState* browser_state) {
+  return scoped_ptr<WebControllerProvider>(
+      new WebControllerProvider(browser_state));
+}
+
+}  // namespace ios
diff --git a/ios/web/ios_web.gyp b/ios/web/ios_web.gyp
index 6abaacf..7e67484 100644
--- a/ios/web/ios_web.gyp
+++ b/ios/web/ios_web.gyp
@@ -25,6 +25,7 @@
         'load_committed_details.cc',
         'navigation/navigation_item_impl.h',
         'navigation/navigation_item_impl.mm',
+        'public/block_types.h',
         'public/browser_state.h',
         'public/favicon_status.cc',
         'public/favicon_status.h',
diff --git a/ios/web/public/block_types.h b/ios/web/public/block_types.h
new file mode 100644
index 0000000..8074dc0
--- /dev/null
+++ b/ios/web/public/block_types.h
@@ -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.
+
+#ifndef IOS_WEB_PUBLIC_BLOCK_TYPES_H_
+#define IOS_WEB_PUBLIC_BLOCK_TYPES_H_
+
+#import <Foundation/Foundation.h>
+
+namespace web {
+
+// The type of the completion handler block that is called to inform about
+// JavaScript evaluation completion.
+typedef void (^JavaScriptCompletion)(NSString*, NSError*);
+
+}  // namespace
+
+#endif  // IOS_WEB_PUBLIC_BLOCK_TYPES_H_
diff --git a/jingle/BUILD.gn b/jingle/BUILD.gn
index 1940dfc..d99f384d 100644
--- a/jingle/BUILD.gn
+++ b/jingle/BUILD.gn
@@ -3,6 +3,7 @@
 # found in the LICENSE file.
 
 import("//build/config/features.gni")
+import("//testing/test.gni")
 
 if (enable_webrtc || !is_android) {
   jingle_includes = exec_script("//build/gypi_to_gn.py",
@@ -21,6 +22,8 @@
       "//base/third_party/dynamic_annotations",
       "//net",
     ]
+
+    configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
   }
 
   # A library for sending and receiving peer-issued notifications.
@@ -108,9 +111,7 @@
   }
 
   # GYP version: jingle/jingle.gyp:jingle_unittests
-  # TODO(GYP): Convert to executable when its dependencies are linkable.
-  source_set("jingle_unittests") {
-    testonly = true
+  test("jingle_unittests") {
     sources = [
       "glue/channel_socket_adapter_unittest.cc",
       "glue/chrome_async_socket_unittest.cc",
@@ -149,6 +150,8 @@
       ]
     }
 
+    configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
+
     public_deps = [
       "//third_party/libjingle",
     ]
diff --git a/jingle/glue/proxy_resolving_client_socket.cc b/jingle/glue/proxy_resolving_client_socket.cc
index 916f4178..0b35559d 100644
--- a/jingle/glue/proxy_resolving_client_socket.cc
+++ b/jingle/glue/proxy_resolving_client_socket.cc
@@ -9,7 +9,6 @@
 #include "base/bind_helpers.h"
 #include "base/compiler_specific.h"
 #include "base/logging.h"
-#include "base/profiler/scoped_tracker.h"
 #include "net/base/io_buffer.h"
 #include "net/base/load_flags.h"
 #include "net/base/net_errors.h"
@@ -215,10 +214,6 @@
 }
 
 void ProxyResolvingClientSocket::ProcessConnectDone(int status) {
-  // TODO(pkasting): Remove ScopedTracker below once crbug.com/455884 is fixed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "455884 ProxyResolvingClientSocket::ProcessConnectDone"));
   if (status != net::OK) {
     // If the connection fails, try another proxy.
     status = ReconsiderProxyAfterError(status);
diff --git a/media/BUILD.gn b/media/BUILD.gn
index d9e2399..40c7850 100644
--- a/media/BUILD.gn
+++ b/media/BUILD.gn
@@ -20,7 +20,7 @@
   if (!media_use_ffmpeg) {
     defines += [ "MEDIA_DISABLE_FFMPEG" ]
   }
-  if (cpu_arch == "arm" && arm_use_neon) {
+  if (current_cpu == "arm" && arm_use_neon) {
     defines += [ "USE_NEON" ]
   }
   if (use_pulseaudio) {
@@ -82,6 +82,8 @@
   sources = [
     "cdm/aes_decryptor.cc",
     "cdm/aes_decryptor.h",
+    "cdm/cenc_utils.cc",
+    "cdm/cenc_utils.h",
     "cdm/default_cdm_factory.cc",
     "cdm/default_cdm_factory.h",
     "cdm/json_web_key.cc",
@@ -152,6 +154,32 @@
     "filters/webvtt_util.h",
     "filters/wsola_internals.cc",
     "filters/wsola_internals.h",
+    "formats/common/offset_byte_queue.cc",
+    "formats/common/offset_byte_queue.h",
+    "formats/webm/webm_audio_client.cc",
+    "formats/webm/webm_audio_client.h",
+    "formats/webm/webm_cluster_parser.cc",
+    "formats/webm/webm_cluster_parser.h",
+    "formats/webm/webm_constants.cc",
+    "formats/webm/webm_constants.h",
+    "formats/webm/webm_content_encodings.cc",
+    "formats/webm/webm_content_encodings.h",
+    "formats/webm/webm_content_encodings_client.cc",
+    "formats/webm/webm_content_encodings_client.h",
+    "formats/webm/webm_crypto_helpers.cc",
+    "formats/webm/webm_crypto_helpers.h",
+    "formats/webm/webm_info_parser.cc",
+    "formats/webm/webm_info_parser.h",
+    "formats/webm/webm_parser.cc",
+    "formats/webm/webm_parser.h",
+    "formats/webm/webm_stream_parser.cc",
+    "formats/webm/webm_stream_parser.h",
+    "formats/webm/webm_tracks_parser.cc",
+    "formats/webm/webm_tracks_parser.h",
+    "formats/webm/webm_video_client.cc",
+    "formats/webm/webm_video_client.h",
+    "formats/webm/webm_webvtt_parser.cc",
+    "formats/webm/webm_webvtt_parser.h",
     "midi/midi_manager.cc",
     "midi/midi_manager.h",
     "midi/midi_manager_mac.cc",
@@ -176,10 +204,10 @@
     "midi/usb_midi_output_stream.h",
     "video/capture/file_video_capture_device.cc",
     "video/capture/file_video_capture_device.h",
-    "video/capture/linux/video_capture_device_linux.cc",
-    "video/capture/linux/video_capture_device_linux.h",
     "video/capture/linux/video_capture_device_chromeos.cc",
     "video/capture/linux/video_capture_device_chromeos.h",
+    "video/capture/linux/video_capture_device_linux.cc",
+    "video/capture/linux/video_capture_device_linux.h",
     "video/capture/mac/platform_video_capturing_mac.h",
     "video/capture/mac/video_capture_device_avfoundation_mac.h",
     "video/capture/mac/video_capture_device_avfoundation_mac.mm",
@@ -224,35 +252,15 @@
     "video/video_decode_accelerator.h",
     "video/video_encode_accelerator.cc",
     "video/video_encode_accelerator.h",
-    "formats/common/offset_byte_queue.cc",
-    "formats/common/offset_byte_queue.h",
-    "formats/webm/webm_audio_client.cc",
-    "formats/webm/webm_audio_client.h",
-    "formats/webm/webm_cluster_parser.cc",
-    "formats/webm/webm_cluster_parser.h",
-    "formats/webm/webm_constants.cc",
-    "formats/webm/webm_constants.h",
-    "formats/webm/webm_content_encodings.cc",
-    "formats/webm/webm_content_encodings.h",
-    "formats/webm/webm_content_encodings_client.cc",
-    "formats/webm/webm_content_encodings_client.h",
-    "formats/webm/webm_crypto_helpers.cc",
-    "formats/webm/webm_crypto_helpers.h",
-    "formats/webm/webm_info_parser.cc",
-    "formats/webm/webm_info_parser.h",
-    "formats/webm/webm_parser.cc",
-    "formats/webm/webm_parser.h",
-    "formats/webm/webm_stream_parser.cc",
-    "formats/webm/webm_stream_parser.h",
-    "formats/webm/webm_tracks_parser.cc",
-    "formats/webm/webm_tracks_parser.h",
-    "formats/webm/webm_video_client.cc",
-    "formats/webm/webm_video_client.h",
-    "formats/webm/webm_webvtt_parser.cc",
-    "formats/webm/webm_webvtt_parser.h",
   ]
 
-  configs += [ ":media_config" ]
+  configs += [
+    ":media_config",
+
+    # TODO(wolenetz): Fix size_t to int trunctaion in win64.
+    # See http://crbug.com/171009
+    "//build/config/compiler:no_size_t_to_int_warning",
+  ]
   all_dependent_configs = [ ":media_dependent_config" ]
 
   cflags = []
@@ -292,7 +300,7 @@
     }
   }
 
-  if (cpu_arch == "arm" && arm_use_neon) {
+  if (current_cpu == "arm" && arm_use_neon) {
     defines += [ "USE_NEON" ]
   }
 
@@ -337,7 +345,7 @@
     }
   }
 
-  if (cpu_arch != "arm" && is_chromeos) {
+  if (current_cpu != "arm" && is_chromeos) {
     sources += [
       "filters/h264_bitstream_buffer.cc",
       "filters/h264_bitstream_buffer.h",
@@ -394,8 +402,6 @@
       "mfreadwrite.lib",
       "mfuuid.lib",
     ]
-    cflags += [ "/wd4267" ]  # TODO(wolenetz): Fix size_t to int trunctaion in win64. See
-                             # http://crbug.com/171009
   }
 
   if (proprietary_codecs) {
@@ -450,10 +456,10 @@
       "formats/mpeg/adts_constants.h",
       "formats/mpeg/adts_stream_parser.cc",
       "formats/mpeg/adts_stream_parser.h",
-      "formats/mpeg/mpeg_audio_stream_parser_base.cc",
-      "formats/mpeg/mpeg_audio_stream_parser_base.h",
       "formats/mpeg/mpeg1_audio_stream_parser.cc",
       "formats/mpeg/mpeg1_audio_stream_parser.h",
+      "formats/mpeg/mpeg_audio_stream_parser_base.cc",
+      "formats/mpeg/mpeg_audio_stream_parser_base.h",
     ]
   }
 
@@ -525,6 +531,7 @@
 test("media_unittests") {
   sources = [
     "cdm/aes_decryptor_unittest.cc",
+    "cdm/cenc_utils_unittest.cc",
     "cdm/json_web_key_unittest.cc",
     "filters/audio_clock_unittest.cc",
     "filters/audio_decoder_selector_unittest.cc",
@@ -555,16 +562,6 @@
     "filters/video_renderer_impl_unittest.cc",
     "filters/vp8_bool_decoder_unittest.cc",
     "filters/vp8_parser_unittest.cc",
-    "midi/midi_manager_unittest.cc",
-    "midi/midi_manager_usb_unittest.cc",
-    "midi/midi_message_queue_unittest.cc",
-    "midi/midi_message_util_unittest.cc",
-    "midi/usb_midi_descriptor_parser_unittest.cc",
-    "midi/usb_midi_input_stream_unittest.cc",
-    "midi/usb_midi_output_stream_unittest.cc",
-    "video/capture/fake_video_capture_device_unittest.cc",
-    "video/capture/video_capture_device_unittest.cc",
-    "video/h264_poc_unittest.cc",
     "formats/common/offset_byte_queue_unittest.cc",
     "formats/webm/cluster_builder.cc",
     "formats/webm/cluster_builder.h",
@@ -577,8 +574,22 @@
     "formats/webm/webm_parser_unittest.cc",
     "formats/webm/webm_tracks_parser_unittest.cc",
     "formats/webm/webm_webvtt_parser_unittest.cc",
+    "midi/midi_manager_unittest.cc",
+    "midi/midi_manager_usb_unittest.cc",
+    "midi/midi_message_queue_unittest.cc",
+    "midi/midi_message_util_unittest.cc",
+    "midi/usb_midi_descriptor_parser_unittest.cc",
+    "midi/usb_midi_input_stream_unittest.cc",
+    "midi/usb_midi_output_stream_unittest.cc",
+    "video/capture/fake_video_capture_device_unittest.cc",
+    "video/capture/video_capture_device_unittest.cc",
+    "video/h264_poc_unittest.cc",
   ]
 
+  # TODO(wolenetz): Fix size_t to int trunctaion in win64.
+  # See http://crbug.com/171009
+  configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
+
   deps = [
     ":media",
     ":test_support",
@@ -618,7 +629,7 @@
     ]
   }
 
-  if (cpu_arch != "arm" && is_chromeos) {
+  if (current_cpu != "arm" && is_chromeos) {
     sources += [ "filters/h264_bitstream_buffer_unittest.cc" ]
   }
 
@@ -649,18 +660,15 @@
     ]
   }
 
-  if (is_win && cpu_arch == "x64") {
-    cflags = [ "/wd4267" ]  # TODO(wolenetz): Fix size_t to int trunctaion in win64. See
-                            # http://crbug.com/171009
-  }
-
   if (is_mac || is_ios) {
     deps += [ "//media/base/mac" ]
   }
 
   if (is_mac) {
-    sources +=
-        [ "video/capture/mac/video_capture_device_factory_mac_unittest.mm" ]
+    sources += [
+      "midi/midi_manager_mac_unittest.cc",
+      "video/capture/mac/video_capture_device_factory_mac_unittest.mm",
+    ]
   }
 
   #      include_dirs += [
diff --git a/media/DEPS b/media/DEPS
index 57eecbb..cad0352 100644
--- a/media/DEPS
+++ b/media/DEPS
@@ -12,5 +12,6 @@
   "+ui/gfx",
   "+ui/gl",
   "+ui/ozone",
+  "+third_party/widevine/cdm/widevine_cdm_common.h",
   "-media/blink",
 ]
diff --git a/media/audio/BUILD.gn b/media/audio/BUILD.gn
index 9820b46..fc0f991 100644
--- a/media/audio/BUILD.gn
+++ b/media/audio/BUILD.gn
@@ -88,8 +88,8 @@
     "fake_audio_consumer.h",
     "fake_audio_input_stream.cc",
     "fake_audio_input_stream.h",
-    "fake_audio_log_factory.h",
     "fake_audio_log_factory.cc",
+    "fake_audio_log_factory.h",
     "fake_audio_manager.cc",
     "fake_audio_manager.h",
     "fake_audio_output_stream.cc",
@@ -243,6 +243,8 @@
       sources += get_target_outputs(":pulse_generate_stubs")
     }
   }
+
+  configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
 }
 
 source_set("test_support") {
@@ -284,7 +286,11 @@
     "//testing/gmock",
     "//testing/gtest",
   ]
-  configs += [ "//media:media_config" ]
+
+  configs += [
+    "//build/config/compiler:no_size_t_to_int_warning",
+    "//media:media_config",
+  ]
 
   if (is_android) {
     sources += [ "android/audio_android_unittest.cc" ]
diff --git a/media/base/BUILD.gn b/media/base/BUILD.gn
index 2091a5d2..b93ed70 100644
--- a/media/base/BUILD.gn
+++ b/media/base/BUILD.gn
@@ -15,11 +15,11 @@
     "audio_block_fifo.h",
     "audio_buffer.cc",
     "audio_buffer.h",
+    "audio_buffer_converter.cc",
+    "audio_buffer_converter.h",
     "audio_buffer_queue.cc",
     "audio_buffer_queue.h",
     "audio_capturer_source.h",
-    "audio_buffer_converter.cc",
-    "audio_buffer_converter.h",
     "audio_converter.cc",
     "audio_converter.h",
     "audio_decoder.cc",
@@ -101,9 +101,9 @@
     "key_system_info.h",
     "key_systems.cc",
     "key_systems.h",
+    "key_systems.h",
     "key_systems_support_uma.cc",
     "key_systems_support_uma.h",
-    "key_systems.h",
     "media.cc",
     "media.h",
     "media_client.cc",
@@ -242,7 +242,7 @@
     defines += [ "DISABLE_USER_INPUT_MONITOR" ]
   }
 
-  if (cpu_arch == "x86" || cpu_arch == "x64") {
+  if (current_cpu == "x86" || current_cpu == "x64") {
     sources += [ "simd/convert_yuv_to_rgb_x86.cc" ]
     deps += [
       ":media_yasm",
@@ -250,6 +250,8 @@
     ]
   }
 
+  configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
+
   if (is_linux || is_win) {
     sources += [
       "keyboard_event_counter.cc",
@@ -268,7 +270,10 @@
     "video_frame.cc",
     "video_frame.h",
   ]
-  configs += [ "//media:media_config" ]
+  configs += [
+    "//build/config/compiler:no_size_t_to_int_warning",
+    "//media:media_config",
+  ]
   all_dependent_configs = [ ":base_for_cast_ios_dependent_config" ]
 }
 
@@ -304,8 +309,8 @@
   sources = [
     "audio_block_fifo_unittest.cc",
     "audio_buffer_converter_unittest.cc",
-    "audio_buffer_unittest.cc",
     "audio_buffer_queue_unittest.cc",
+    "audio_buffer_unittest.cc",
     "audio_bus_unittest.cc",
     "audio_converter_unittest.cc",
     "audio_discard_helper_unittest.cc",
@@ -334,8 +339,8 @@
     "pipeline_unittest.cc",
     "ranges_unittest.cc",
     "run_all_unittests.cc",
-    "serial_runner_unittest.cc",
     "seekable_buffer_unittest.cc",
+    "serial_runner_unittest.cc",
     "sinc_resampler_unittest.cc",
     "stream_parser_unittest.cc",
     "text_ranges_unittest.cc",
@@ -343,13 +348,16 @@
     "user_input_monitor_unittest.cc",
     "vector_math_testing.h",
     "vector_math_unittest.cc",
-    "video_frame_unittest.cc",
     "video_frame_pool_unittest.cc",
+    "video_frame_unittest.cc",
     "video_util_unittest.cc",
     "wall_clock_time_source_unittest.cc",
     "yuv_convert_unittest.cc",
   ]
-  configs += [ "//media:media_config" ]
+  configs += [
+    "//build/config/compiler:no_size_t_to_int_warning",
+    "//media:media_config",
+  ]
   deps = [
     ":base",
     ":test_support",
@@ -371,7 +379,7 @@
     deps += [ "//ui/gl" ]
   }
 
-  if (cpu_arch == "x86" || cpu_arch == "x64") {
+  if (current_cpu == "x86" || current_cpu == "x64") {
     sources += [ "simd/convert_rgb_to_yuv_unittest.cc" ]
   }
 }
@@ -403,7 +411,7 @@
   }
 }
 
-if (cpu_arch == "x86" || cpu_arch == "x64") {
+if (current_cpu == "x86" || current_cpu == "x64") {
   source_set("media_sse2") {
     sources = [
       "simd/convert_rgb_to_yuv_sse2.cc",
@@ -450,9 +458,9 @@
       "simd/scale_yuv_to_rgb_mmx.inc",
     ]
 
-    if (cpu_arch == "x86") {
+    if (current_cpu == "x86") {
       yasm_flags += [ "-DARCH_X86_32" ]
-    } else if (cpu_arch == "x64") {
+    } else if (current_cpu == "x64") {
       yasm_flags += [ "-DARCH_X86_64" ]
       sources += [
         "simd/linear_scale_yuv_to_rgb_mmx_x64.asm",
@@ -468,7 +476,7 @@
     } else {
       if (is_posix) {
         yasm_flags += [ "-DELF" ]
-        if (cpu_arch == "x64") {
+        if (current_cpu == "x64") {
           # TODO(ajwong): Why isn't this true in mac?
           yasm_flags += [ "-DPIC" ]
         }
diff --git a/media/base/android/browser_cdm_factory_android.cc b/media/base/android/browser_cdm_factory_android.cc
index 5c7f3656..e116863 100644
--- a/media/base/android/browser_cdm_factory_android.cc
+++ b/media/base/android/browser_cdm_factory_android.cc
@@ -9,6 +9,7 @@
 #include "base/memory/scoped_ptr.h"
 #include "media/base/android/media_drm_bridge.h"
 #include "media/base/media_switches.h"
+#include "third_party/widevine/cdm/widevine_cdm_common.h"
 
 namespace media {
 
@@ -33,15 +34,26 @@
   }
 
   // TODO(xhwang/ddorwin): Pass the security level from key system.
-  MediaDrmBridge::SecurityLevel security_level =
-      MediaDrmBridge::SECURITY_LEVEL_3;
-  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
-          switches::kMediaDrmEnableNonCompositing)) {
-    security_level = MediaDrmBridge::SECURITY_LEVEL_1;
-  }
-  if (!cdm->SetSecurityLevel(security_level)) {
-    DVLOG(1) << "failed to set security level " << security_level;
-    return scoped_ptr<BrowserCdm>();
+  // http://crbug.com/459400
+  if (key_system == kWidevineKeySystem) {
+    MediaDrmBridge::SecurityLevel security_level =
+        MediaDrmBridge::SECURITY_LEVEL_3;
+    if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+            switches::kMediaDrmEnableNonCompositing)) {
+      security_level = MediaDrmBridge::SECURITY_LEVEL_1;
+    }
+    if (!cdm->SetSecurityLevel(security_level)) {
+      DVLOG(1) << "failed to set security level " << security_level;
+      return scoped_ptr<BrowserCdm>();
+    }
+  } else {
+    // Assume other key systems do not support full compositing.
+    if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
+            switches::kMediaDrmEnableNonCompositing)) {
+      NOTREACHED() << key_system << " may require "
+                   << switches::kMediaDrmEnableNonCompositing;
+      return scoped_ptr<BrowserCdm>();
+    }
   }
 
   return cdm.Pass();
diff --git a/media/base/android/media_drm_bridge.cc b/media/base/android/media_drm_bridge.cc
index a89522d..ecac7048 100644
--- a/media/base/android/media_drm_bridge.cc
+++ b/media/base/android/media_drm_bridge.cc
@@ -298,6 +298,7 @@
   return true;
 }
 
+// TODO(ddorwin): This is specific to Widevine. http://crbug.com/459400
 // static
 bool MediaDrmBridge::IsSecureDecoderRequired(SecurityLevel security_level) {
   DCHECK(IsAvailable());
@@ -305,20 +306,6 @@
 }
 
 // static
-bool MediaDrmBridge::IsSecurityLevelSupported(const std::string& key_system,
-                                              SecurityLevel security_level) {
-  if (!IsAvailable())
-    return false;
-
-  scoped_ptr<MediaDrmBridge> media_drm_bridge =
-      MediaDrmBridge::CreateWithoutSessionSupport(key_system);
-  if (!media_drm_bridge)
-    return false;
-
-  return media_drm_bridge->SetSecurityLevel(security_level);
-}
-
-// static
 std::vector<std::string> MediaDrmBridge::GetPlatformKeySystemNames() {
   return g_key_system_uuid_manager.Get().GetPlatformKeySystemNames();
 }
@@ -404,6 +391,13 @@
 }
 
 bool MediaDrmBridge::SetSecurityLevel(SecurityLevel security_level) {
+  if (security_level != SECURITY_LEVEL_NONE &&
+      !std::equal(scheme_uuid_.begin(), scheme_uuid_.end(), kWidevineUuid)) {
+    NOTREACHED() << "Widevine security level " << security_level
+                 << "used with another key system";
+    return false;
+  }
+
   JNIEnv* env = AttachCurrentThread();
 
   std::string security_level_str = GetSecurityLevelString(security_level);
@@ -636,7 +630,12 @@
 }
 
 bool MediaDrmBridge::IsProtectedSurfaceRequired() {
-  return IsSecureDecoderRequired(GetSecurityLevel());
+  // For Widevine, this depends on the security level.
+  if (std::equal(scheme_uuid_.begin(), scheme_uuid_.end(), kWidevineUuid))
+    return IsSecureDecoderRequired(GetSecurityLevel());
+
+  // For other key systems, assume true.
+  return true;
 }
 
 void MediaDrmBridge::ResetDeviceCredentials(
diff --git a/media/base/android/media_drm_bridge.h b/media/base/android/media_drm_bridge.h
index e774470..06716df 100644
--- a/media/base/android/media_drm_bridge.h
+++ b/media/base/android/media_drm_bridge.h
@@ -27,6 +27,7 @@
 // This class provides DRM services for android EME implementation.
 class MEDIA_EXPORT MediaDrmBridge : public BrowserCdm {
  public:
+  // TODO(ddorwin): These are specific to Widevine. http://crbug.com/459400
   enum SecurityLevel {
     SECURITY_LEVEL_NONE = 0,
     SECURITY_LEVEL_1 = 1,
@@ -42,9 +43,6 @@
   // to check IsAvailable() explicitly before calling them.
   static bool IsAvailable();
 
-  static bool IsSecurityLevelSupported(const std::string& key_system,
-                                       SecurityLevel security_level);
-
   // Checks whether |key_system| is supported.
   static bool IsKeySystemSupported(const std::string& key_system);
 
@@ -58,8 +56,6 @@
       const std::string& key_system,
       const std::string& container_mime_type);
 
-  static bool IsSecureDecoderRequired(SecurityLevel security_level);
-
   static bool RegisterMediaDrmBridge(JNIEnv* env);
 
   // Returns a MediaDrmBridge instance if |key_system| is supported, or a NULL
@@ -191,6 +187,8 @@
                  const SessionErrorCB& session_error_cb,
                  const SessionKeysChangeCB& session_keys_change_cb);
 
+  static bool IsSecureDecoderRequired(SecurityLevel security_level);
+
   // Get the security level of the media.
   SecurityLevel GetSecurityLevel();
 
diff --git a/media/base/android/media_drm_bridge_unittest.cc b/media/base/android/media_drm_bridge_unittest.cc
index 1a4c014..1855c2c 100644
--- a/media/base/android/media_drm_bridge_unittest.cc
+++ b/media/base/android/media_drm_bridge_unittest.cc
@@ -34,10 +34,6 @@
 
 // Helper functions to avoid typing "MediaDrmBridge::" in tests.
 
-static bool IsKeySystemSupported(const std::string& key_system) {
-  return MediaDrmBridge::IsKeySystemSupported(key_system);
-}
-
 static bool IsKeySystemSupportedWithType(
     const std::string& key_system,
     const std::string& container_mime_type) {
@@ -45,29 +41,9 @@
                                                       container_mime_type);
 }
 
-static bool IsSecurityLevelSupported(
-    const std::string& key_system,
-    MediaDrmBridge::SecurityLevel security_level) {
-  return MediaDrmBridge::IsSecurityLevelSupported(key_system, security_level);
-}
-
-TEST(MediaDrmBridgeTest, IsSecurityLevelSupported_Widevine) {
-  EXPECT_FALSE(IsSecurityLevelSupported(kWidevineKeySystem, kLNone));
-  // We test "L3" fully. But for "L1" we don't check the result as it depends on
-  // whether the test device supports "L1".
-  EXPECT_TRUE_IF_AVAILABLE(IsSecurityLevelSupported(kWidevineKeySystem, kL3));
-  IsSecurityLevelSupported(kWidevineKeySystem, kL1);
-}
-
-// Invalid keysytem is NOT supported regardless whether MediaDrm is available.
-TEST(MediaDrmBridgeTest, IsSecurityLevelSupported_InvalidKeySystem) {
-  EXPECT_FALSE(IsSecurityLevelSupported(kInvalidKeySystem, kLNone));
-  EXPECT_FALSE(IsSecurityLevelSupported(kInvalidKeySystem, kL1));
-  EXPECT_FALSE(IsSecurityLevelSupported(kInvalidKeySystem, kL3));
-}
-
 TEST(MediaDrmBridgeTest, IsKeySystemSupported_Widevine) {
-  EXPECT_TRUE_IF_AVAILABLE(IsKeySystemSupported(kWidevineKeySystem));
+  EXPECT_TRUE_IF_AVAILABLE(
+      MediaDrmBridge::IsKeySystemSupported(kWidevineKeySystem));
 
   // TODO(xhwang): Enable when b/13564917 is fixed.
   // EXPECT_TRUE_IF_AVAILABLE(
@@ -88,9 +64,9 @@
   EXPECT_FALSE(IsKeySystemSupportedWithType(kWidevineKeySystem, "audio/mp3"));
 }
 
-// Invalid keysytem is NOT supported regardless whether MediaDrm is available.
+// Invalid key system is NOT supported regardless whether MediaDrm is available.
 TEST(MediaDrmBridgeTest, IsKeySystemSupported_InvalidKeySystem) {
-  EXPECT_FALSE(IsKeySystemSupported(kInvalidKeySystem));
+  EXPECT_FALSE(MediaDrmBridge::IsKeySystemSupported(kInvalidKeySystem));
   EXPECT_FALSE(IsKeySystemSupportedWithType(kInvalidKeySystem, kAudioMp4));
   EXPECT_FALSE(IsKeySystemSupportedWithType(kInvalidKeySystem, kVideoMp4));
   EXPECT_FALSE(IsKeySystemSupportedWithType(kInvalidKeySystem, kAudioWebM));
@@ -100,4 +76,28 @@
   EXPECT_FALSE(IsKeySystemSupportedWithType(kInvalidKeySystem, "audio/mp3"));
 }
 
+TEST(MediaDrmBridgeTest, CreateWithoutSessionSupport_Widevine) {
+  EXPECT_TRUE_IF_AVAILABLE(
+      MediaDrmBridge::CreateWithoutSessionSupport(kWidevineKeySystem));
+}
+
+// Invalid key system is NOT supported regardless whether MediaDrm is available.
+TEST(MediaDrmBridgeTest, CreateWithoutSessionSupport_InvalidKeySystem) {
+  EXPECT_FALSE(MediaDrmBridge::CreateWithoutSessionSupport(kInvalidKeySystem));
+}
+
+TEST(MediaDrmBridgeTest, SetSecurityLevel_Widevine) {
+  scoped_ptr<MediaDrmBridge> media_drm_bridge =
+      MediaDrmBridge::CreateWithoutSessionSupport(kWidevineKeySystem);
+  EXPECT_TRUE_IF_AVAILABLE(media_drm_bridge);
+  if (!media_drm_bridge)
+    return;
+
+  EXPECT_FALSE(media_drm_bridge->SetSecurityLevel(kLNone));
+  // We test "L3" fully. But for "L1" we don't check the result as it depends on
+  // whether the test device supports "L1".
+  EXPECT_TRUE(media_drm_bridge->SetSecurityLevel(kL3));
+  media_drm_bridge->SetSecurityLevel(kL1);
+}
+
 }  // namespace media
diff --git a/media/base/eme_constants.h b/media/base/eme_constants.h
index e78af3b..4462c5c 100644
--- a/media/base/eme_constants.h
+++ b/media/base/eme_constants.h
@@ -49,6 +49,37 @@
 typedef uint32_t SupportedInitDataTypes;
 typedef uint32_t SupportedCodecs;
 
+enum EmeSessionTypeSupport {
+  // Invalid default value.
+  EME_SESSION_TYPE_INVALID,
+  // The session type is not supported.
+  EME_SESSION_TYPE_NOT_SUPPORTED,
+  // The session type is supported if the encrypted media permission is granted.
+  EME_SESSION_TYPE_SUPPORTED_WITH_PERMISSION,
+  // The session type is always supported.
+  EME_SESSION_TYPE_SUPPORTED,
+};
+
+enum EmeFeatureSupport {
+  // Invalid default value.
+  EME_FEATURE_INVALID,
+  // Access to the feature is not supported at all.
+  EME_FEATURE_NOT_SUPPORTED,
+  // Access to the feature may be requested if the encrypted media permission is
+  // granted.
+  EME_FEATURE_REQUESTABLE_WITH_PERMISSION,
+  // Access to the feature may be requested.
+  EME_FEATURE_REQUESTABLE,
+  // Access to the feature cannot be blocked.
+  EME_FEATURE_ALWAYS_ENABLED,
+};
+
+enum EmeFeatureRequirement {
+  EME_FEATURE_NOT_ALLOWED,
+  EME_FEATURE_OPTIONAL,
+  EME_FEATURE_REQUIRED,
+};
+
 }  // namespace media
 
 #endif  // MEDIA_BASE_EME_CONSTANTS_H_
diff --git a/media/base/key_system_info.cc b/media/base/key_system_info.cc
index f436912..b656f0f0 100644
--- a/media/base/key_system_info.cc
+++ b/media/base/key_system_info.cc
@@ -6,10 +6,13 @@
 
 namespace media {
 
-KeySystemInfo::KeySystemInfo(const std::string& key_system)
-    : key_system(key_system),
-      supported_init_data_types(EME_INIT_DATA_TYPE_NONE),
+KeySystemInfo::KeySystemInfo()
+    : supported_init_data_types(EME_INIT_DATA_TYPE_NONE),
       supported_codecs(EME_CODEC_NONE),
+      persistent_license_support(EME_SESSION_TYPE_INVALID),
+      persistent_release_message_support(EME_SESSION_TYPE_INVALID),
+      persistent_state_support(EME_FEATURE_INVALID),
+      distinctive_identifier_support(EME_FEATURE_INVALID),
       use_aes_decryptor(false) {
 }
 
diff --git a/media/base/key_system_info.h b/media/base/key_system_info.h
index e259c00d..e66d8be8 100644
--- a/media/base/key_system_info.h
+++ b/media/base/key_system_info.h
@@ -30,7 +30,7 @@
 // Contains information about an EME key system as well as how to instantiate
 // the corresponding CDM.
 struct MEDIA_EXPORT KeySystemInfo {
-  explicit KeySystemInfo(const std::string& key_system);
+  KeySystemInfo();
   ~KeySystemInfo();
 
   std::string key_system;
@@ -41,6 +41,12 @@
   // Specifies codecs supported by |key_system|.
   SupportedCodecs supported_codecs;
 
+  // Specifies session types and features supported by |key_system|.
+  EmeSessionTypeSupport persistent_license_support;
+  EmeSessionTypeSupport persistent_release_message_support;
+  EmeFeatureSupport persistent_state_support;
+  EmeFeatureSupport distinctive_identifier_support;
+
   // A hierarchical parent for |key_system|. This value can be used to check
   // supported types but cannot be used to instantiate a MediaKeys object.
   // Only one parent key system is currently supported per concrete key system.
diff --git a/media/base/key_systems.cc b/media/base/key_systems.cc
index b1c7e05a..db12a13 100644
--- a/media/base/key_systems.cc
+++ b/media/base/key_systems.cc
@@ -16,6 +16,8 @@
 #include "media/base/key_system_info.h"
 #include "media/base/key_systems_support_uma.h"
 #include "media/base/media_client.h"
+#include "media/cdm/key_system_names.h"
+#include "third_party/widevine/cdm/widevine_cdm_common.h"
 
 namespace media {
 
@@ -74,7 +76,8 @@
 };
 
 static void AddClearKey(std::vector<KeySystemInfo>* concrete_key_systems) {
-  KeySystemInfo info(kClearKeyKeySystem);
+  KeySystemInfo info;
+  info.key_system = kClearKeyKeySystem;
 
   // On Android, Vorbis, VP8, AAC and AVC1 are supported in MediaCodec:
   // http://developer.android.com/guide/appendix/media-formats.html
@@ -98,11 +101,66 @@
   info.supported_codecs |= EME_CODEC_MP4_ALL;
 #endif  // defined(USE_PROPRIETARY_CODECS)
 
+  info.persistent_license_support = EME_SESSION_TYPE_NOT_SUPPORTED;
+  info.persistent_release_message_support = EME_SESSION_TYPE_NOT_SUPPORTED;
+  info.persistent_state_support = EME_FEATURE_NOT_SUPPORTED;
+  info.distinctive_identifier_support = EME_FEATURE_NOT_SUPPORTED;
+
   info.use_aes_decryptor = true;
 
   concrete_key_systems->push_back(info);
 }
 
+// Returns whether the |key_system| is known to Chromium and is thus likely to
+// be implemented in an interoperable way.
+// True is always returned for a |key_system| that begins with "x-".
+//
+// As with other web platform features, advertising support for a key system
+// implies that it adheres to a defined and interoperable specification.
+//
+// To ensure interoperability, implementations of a specific |key_system| string
+// must conform to a specification for that identifier that defines
+// key system-specific behaviors not fully defined by the EME specification.
+// That specification should be provided by the owner of the domain that is the
+// reverse of the |key_system| string.
+// This involves more than calling a library, SDK, or platform API. KeySystems
+// must be populated appropriately, and there will likely be glue code to adapt
+// to the API of the library, SDK, or platform API.
+//
+// Chromium mainline contains this data and glue code for specific key systems,
+// which should help ensure interoperability with other implementations using
+// these key systems.
+//
+// If you need to add support for other key systems, ensure that you have
+// obtained the specification for how to integrate it with EME, implemented the
+// appropriate glue/adapter code, and added all the appropriate data to
+// KeySystems. Only then should you change this function.
+static bool IsPotentiallySupportedKeySystem(const std::string& key_system) {
+  // Known and supported key systems.
+  if (key_system == kWidevineKeySystem)
+    return true;
+  if (key_system == kClearKey)
+    return true;
+
+  // External Clear Key is known and supports suffixes for testing.
+  if (IsExternalClearKey(key_system))
+    return true;
+
+  // Chromecast defines behaviors for Cast clients within its reverse domain.
+  const char kChromecastRoot[] = "com.chromecast";
+  if (IsParentKeySystemOf(kChromecastRoot, key_system))
+    return true;
+
+  // Implementations that do not have a specification or appropriate glue code
+  // can use the "x-" prefix to avoid conflicting with and advertising support
+  // for real key system names. Use is discouraged.
+  const char kExcludedPrefix[] = "x-";
+  if (key_system.find(kExcludedPrefix, 0, arraysize(kExcludedPrefix) - 1) == 0)
+    return true;
+
+  return false;
+}
+
 class KeySystems {
  public:
   static KeySystems& GetInstance();
@@ -131,6 +189,24 @@
   std::string GetPepperType(const std::string& concrete_key_system);
 #endif
 
+  bool IsPersistentLicenseSessionSupported(
+      const std::string& key_system,
+      bool is_permission_granted);
+
+  bool IsPersistentReleaseMessageSessionSupported(
+      const std::string& key_system,
+      bool is_permission_granted);
+
+  bool IsPersistentStateRequirementSupported(
+      const std::string& key_system,
+      EmeFeatureRequirement requirement,
+      bool is_permission_granted);
+
+  bool IsDistinctiveIdentifierRequirementSupported(
+      const std::string& key_system,
+      EmeFeatureRequirement requirement,
+      bool is_permission_granted);
+
   void AddContainerMask(const std::string& container, uint32 mask);
   void AddCodecMask(const std::string& codec, uint32 mask);
 
@@ -142,32 +218,9 @@
   void AddConcreteSupportedKeySystems(
       const std::vector<KeySystemInfo>& concrete_key_systems);
 
-  void AddConcreteSupportedKeySystem(
-      const std::string& key_system,
-      bool use_aes_decryptor,
-#if defined(ENABLE_PEPPER_CDMS)
-      const std::string& pepper_type,
-#endif
-      SupportedInitDataTypes supported_init_data_types,
-      SupportedCodecs supported_codecs,
-      const std::string& parent_key_system);
-
   friend struct base::DefaultLazyInstanceTraits<KeySystems>;
 
-  struct KeySystemProperties {
-    KeySystemProperties()
-        : use_aes_decryptor(false), supported_codecs(EME_CODEC_NONE) {}
-
-    bool use_aes_decryptor;
-#if defined(ENABLE_PEPPER_CDMS)
-    std::string pepper_type;
-#endif
-    SupportedInitDataTypes supported_init_data_types;
-    SupportedCodecs supported_codecs;
-  };
-
-  typedef base::hash_map<std::string, KeySystemProperties>
-      KeySystemPropertiesMap;
+  typedef base::hash_map<std::string, KeySystemInfo> KeySystemInfoMap;
   typedef base::hash_map<std::string, std::string> ParentKeySystemMap;
   typedef base::hash_map<std::string, SupportedCodecs> ContainerCodecsMap;
   typedef base::hash_map<std::string, EmeCodec> CodecsMap;
@@ -202,7 +255,7 @@
       SupportedCodecs key_system_supported_codecs) const;
 
   // Map from key system string to capabilities.
-  KeySystemPropertiesMap concrete_key_system_map_;
+  KeySystemInfoMap concrete_key_system_map_;
 
   // Map from parent key system to the concrete key system that should be used
   // to represent its capabilities.
@@ -337,52 +390,55 @@
   DCHECK(concrete_key_system_map_.empty());
   DCHECK(parent_key_system_map_.empty());
 
-  for (size_t i = 0; i < concrete_key_systems.size(); ++i) {
-    const KeySystemInfo& key_system_info = concrete_key_systems[i];
-    AddConcreteSupportedKeySystem(key_system_info.key_system,
-                                  key_system_info.use_aes_decryptor,
-#if defined(ENABLE_PEPPER_CDMS)
-                                  key_system_info.pepper_type,
+  for (const KeySystemInfo& info : concrete_key_systems) {
+    DCHECK(!info.key_system.empty());
+    DCHECK_NE(info.persistent_license_support, EME_SESSION_TYPE_INVALID);
+    DCHECK_NE(info.persistent_release_message_support,
+              EME_SESSION_TYPE_INVALID);
+    // TODO(sandersd): Add REQUESTABLE and REQUESTABLE_WITH_PERMISSION for
+    // persistent_state_support once we can block access per-CDM-instance
+    // (http://crbug.com/457482).
+    DCHECK(info.persistent_state_support == EME_FEATURE_NOT_SUPPORTED ||
+           info.persistent_state_support == EME_FEATURE_ALWAYS_ENABLED);
+// TODO(sandersd): Allow REQUESTABLE_WITH_PERMISSION for all key systems on
+// all platforms once we have proper enforcement (http://crbug.com/457482).
+// On Chrome OS, an ID will not be used without permission, but we cannot
+// currently prevent the CDM from requesting the permission again when no
+// there was no initial prompt. Thus, we block "not-allowed" below.
+#if defined(OS_CHROMEOS)
+    DCHECK(info.distinctive_identifier_support == EME_FEATURE_NOT_SUPPORTED ||
+           (info.distinctive_identifier_support ==
+                EME_FEATURE_REQUESTABLE_WITH_PERMISSION &&
+            info.key_system == kWidevineKeySystem) ||
+           info.distinctive_identifier_support == EME_FEATURE_ALWAYS_ENABLED);
+#else
+    DCHECK(info.distinctive_identifier_support == EME_FEATURE_NOT_SUPPORTED ||
+           info.distinctive_identifier_support == EME_FEATURE_ALWAYS_ENABLED);
 #endif
-                                  key_system_info.supported_init_data_types,
-                                  key_system_info.supported_codecs,
-                                  key_system_info.parent_key_system);
-  }
-}
-
-void KeySystems::AddConcreteSupportedKeySystem(
-    const std::string& concrete_key_system,
-    bool use_aes_decryptor,
+    if (info.persistent_state_support == EME_FEATURE_NOT_SUPPORTED) {
+      DCHECK_EQ(info.persistent_license_support,
+                EME_SESSION_TYPE_NOT_SUPPORTED);
+      DCHECK_EQ(info.persistent_release_message_support,
+                EME_SESSION_TYPE_NOT_SUPPORTED);
+    }
+    DCHECK(!IsSupportedKeySystem(info.key_system))
+        << "Key system '" << info.key_system << "' already registered";
+    DCHECK(!parent_key_system_map_.count(info.key_system))
+        <<  "'" << info.key_system << "' is already registered as a parent";
 #if defined(ENABLE_PEPPER_CDMS)
-    const std::string& pepper_type,
-#endif
-    SupportedInitDataTypes supported_init_data_types,
-    SupportedCodecs supported_codecs,
-    const std::string& parent_key_system) {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  DCHECK(!IsConcreteSupportedKeySystem(concrete_key_system))
-      << "Key system '" << concrete_key_system << "' already registered";
-  DCHECK(!parent_key_system_map_.count(concrete_key_system))
-      <<  "'" << concrete_key_system << " is already registered as a parent";
-
-  KeySystemProperties properties;
-  properties.use_aes_decryptor = use_aes_decryptor;
-#if defined(ENABLE_PEPPER_CDMS)
-  DCHECK_EQ(use_aes_decryptor, pepper_type.empty());
-  properties.pepper_type = pepper_type;
+    DCHECK_EQ(info.use_aes_decryptor, info.pepper_type.empty());
 #endif
 
-  properties.supported_init_data_types = supported_init_data_types;
-  properties.supported_codecs = supported_codecs;
+    concrete_key_system_map_[info.key_system] = info;
 
-  concrete_key_system_map_[concrete_key_system] = properties;
-
-  if (!parent_key_system.empty()) {
-    DCHECK(!IsConcreteSupportedKeySystem(parent_key_system))
-        << "Parent '" << parent_key_system << "' already registered concrete";
-    DCHECK(!parent_key_system_map_.count(parent_key_system))
-        << "Parent '" << parent_key_system << "' already registered";
-    parent_key_system_map_[parent_key_system] = concrete_key_system;
+    if (!info.parent_key_system.empty()) {
+      DCHECK(!IsConcreteSupportedKeySystem(info.parent_key_system))
+          << "Parent '" << info.parent_key_system << "' "
+          << "already registered concrete";
+      DCHECK(!parent_key_system_map_.count(info.parent_key_system))
+          << "Parent '" << info.parent_key_system << "' already registered";
+      parent_key_system_map_[info.parent_key_system] = info.key_system;
+    }
   }
 }
 
@@ -456,15 +512,15 @@
   DCHECK(thread_checker_.CalledOnValidThread());
 
   // Locate |key_system|. Only concrete key systems are supported in unprefixed.
-  KeySystemPropertiesMap::const_iterator key_system_iter =
+  KeySystemInfoMap::const_iterator key_system_iter =
       concrete_key_system_map_.find(key_system);
   if (key_system_iter == concrete_key_system_map_.end())
     return false;
 
   // Check |init_data_type| and |key_system| x |init_data_type|.
-  const KeySystemProperties& properties = key_system_iter->second;
+  const KeySystemInfo& info = key_system_iter->second;
   EmeInitDataType eme_init_data_type = GetInitDataTypeForName(init_data_type);
-  return (properties.supported_init_data_types & eme_init_data_type) != 0;
+  return (info.supported_init_data_types & eme_init_data_type) != 0;
 }
 
 // TODO(sandersd): Reorganize to be more similar to
@@ -490,7 +546,7 @@
     key_systems_support_uma_.ReportKeySystemQuery(key_system, has_type);
 
   // Check key system support.
-  KeySystemPropertiesMap::const_iterator key_system_iter =
+  KeySystemInfoMap::const_iterator key_system_iter =
       concrete_key_system_map_.find(concrete_key_system);
   if (key_system_iter == concrete_key_system_map_.end())
     return false;
@@ -535,7 +591,7 @@
 bool KeySystems::UseAesDecryptor(const std::string& concrete_key_system) {
   DCHECK(thread_checker_.CalledOnValidThread());
 
-  KeySystemPropertiesMap::const_iterator key_system_iter =
+  KeySystemInfoMap::const_iterator key_system_iter =
       concrete_key_system_map_.find(concrete_key_system);
   if (key_system_iter == concrete_key_system_map_.end()) {
       DLOG(FATAL) << concrete_key_system << " is not a known concrete system";
@@ -549,7 +605,7 @@
 std::string KeySystems::GetPepperType(const std::string& concrete_key_system) {
   DCHECK(thread_checker_.CalledOnValidThread());
 
-  KeySystemPropertiesMap::const_iterator key_system_iter =
+  KeySystemInfoMap::const_iterator key_system_iter =
       concrete_key_system_map_.find(concrete_key_system);
   if (key_system_iter == concrete_key_system_map_.end()) {
       DLOG(FATAL) << concrete_key_system << " is not a known concrete system";
@@ -562,6 +618,134 @@
 }
 #endif
 
+bool KeySystems::IsPersistentLicenseSessionSupported(
+    const std::string& key_system,
+    bool is_permission_granted) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  KeySystemInfoMap::const_iterator key_system_iter =
+      concrete_key_system_map_.find(key_system);
+  if (key_system_iter == concrete_key_system_map_.end()) {
+    NOTREACHED();
+    return false;
+  }
+
+  switch (key_system_iter->second.persistent_license_support) {
+    case EME_SESSION_TYPE_INVALID:
+      NOTREACHED();
+      return false;
+    case EME_SESSION_TYPE_NOT_SUPPORTED:
+      return false;
+    case EME_SESSION_TYPE_SUPPORTED_WITH_PERMISSION:
+      return is_permission_granted;
+    case EME_SESSION_TYPE_SUPPORTED:
+      return true;
+  }
+
+  NOTREACHED();
+  return false;
+}
+
+bool KeySystems::IsPersistentReleaseMessageSessionSupported(
+    const std::string& key_system,
+    bool is_permission_granted) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  KeySystemInfoMap::const_iterator key_system_iter =
+      concrete_key_system_map_.find(key_system);
+  if (key_system_iter == concrete_key_system_map_.end()) {
+    NOTREACHED();
+    return false;
+  }
+
+  switch (key_system_iter->second.persistent_release_message_support) {
+    case EME_SESSION_TYPE_INVALID:
+      NOTREACHED();
+      return false;
+    case EME_SESSION_TYPE_NOT_SUPPORTED:
+      return false;
+    case EME_SESSION_TYPE_SUPPORTED_WITH_PERMISSION:
+      return is_permission_granted;
+    case EME_SESSION_TYPE_SUPPORTED:
+      return true;
+  }
+
+  NOTREACHED();
+  return false;
+}
+
+bool KeySystems::IsPersistentStateRequirementSupported(
+    const std::string& key_system,
+    EmeFeatureRequirement requirement,
+    bool is_permission_granted) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  KeySystemInfoMap::const_iterator key_system_iter =
+      concrete_key_system_map_.find(key_system);
+  if (key_system_iter == concrete_key_system_map_.end()) {
+    NOTREACHED();
+    return false;
+  }
+
+  switch (key_system_iter->second.persistent_state_support) {
+    case EME_FEATURE_INVALID:
+      NOTREACHED();
+      return false;
+    case EME_FEATURE_NOT_SUPPORTED:
+      return requirement != EME_FEATURE_REQUIRED;
+    case EME_FEATURE_REQUESTABLE_WITH_PERMISSION:
+      return (requirement != EME_FEATURE_REQUIRED) || is_permission_granted;
+    case EME_FEATURE_REQUESTABLE:
+      return true;
+    case EME_FEATURE_ALWAYS_ENABLED:
+      // Persistent state does not require user permission, but the session
+      // types that use it might.
+      return requirement != EME_FEATURE_NOT_ALLOWED;
+  }
+
+  NOTREACHED();
+  return false;
+}
+
+bool KeySystems::IsDistinctiveIdentifierRequirementSupported(
+    const std::string& key_system,
+    EmeFeatureRequirement requirement,
+    bool is_permission_granted) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  KeySystemInfoMap::const_iterator key_system_iter =
+      concrete_key_system_map_.find(key_system);
+  if (key_system_iter == concrete_key_system_map_.end()) {
+    NOTREACHED();
+    return false;
+  }
+
+  switch (key_system_iter->second.distinctive_identifier_support) {
+    case EME_FEATURE_INVALID:
+      NOTREACHED();
+      return false;
+    case EME_FEATURE_NOT_SUPPORTED:
+      return requirement != EME_FEATURE_REQUIRED;
+    case EME_FEATURE_REQUESTABLE_WITH_PERMISSION:
+      // TODO(sandersd): Remove this hack once crbug.com/457482 and
+      // crbug.com/460616 are addressed.
+      // We cannot currently enforce "not-allowed", so don't allow it.
+      // Note: Removing this check will expose crbug.com/460616.
+      if (requirement == EME_FEATURE_NOT_ALLOWED)
+        return false;
+      return (requirement != EME_FEATURE_REQUIRED) || is_permission_granted;
+    case EME_FEATURE_REQUESTABLE:
+      NOTREACHED();
+      return true;
+    case EME_FEATURE_ALWAYS_ENABLED:
+      // Distinctive identifiers always require user permission.
+      return (requirement != EME_FEATURE_NOT_ALLOWED) && is_permission_granted;
+  }
+
+  NOTREACHED();
+  return false;
+}
+
 void KeySystems::AddContainerMask(const std::string& container, uint32 mask) {
   DCHECK(thread_checker_.CalledOnValidThread());
   DCHECK(!container_to_codec_mask_map_.count(container));
@@ -612,7 +796,19 @@
 }
 
 bool IsSupportedKeySystem(const std::string& key_system) {
-  return KeySystems::GetInstance().IsSupportedKeySystem(key_system);
+  if (!KeySystems::GetInstance().IsSupportedKeySystem(key_system))
+    return false;
+
+  // TODO(ddorwin): Move this to where we add key systems when prefixed EME is
+  // removed (crbug.com/249976).
+  if (!IsPotentiallySupportedKeySystem(key_system)) {
+    // If you encounter this path, see the comments for the above function.
+    NOTREACHED() << "Unrecognized key system " << key_system
+                 << ". See code comments.";
+    return false;
+  }
+
+  return true;
 }
 
 bool IsSupportedKeySystemWithInitDataType(
@@ -652,14 +848,43 @@
 }
 #endif
 
+bool IsPersistentLicenseSessionSupported(
+    const std::string& key_system,
+    bool is_permission_granted) {
+  return KeySystems::GetInstance().IsPersistentLicenseSessionSupported(
+      key_system, is_permission_granted);
+}
+
+bool IsPersistentReleaseMessageSessionSupported(
+    const std::string& key_system,
+    bool is_permission_granted) {
+  return KeySystems::GetInstance().IsPersistentReleaseMessageSessionSupported(
+      key_system, is_permission_granted);
+}
+
+bool IsPersistentStateRequirementSupported(
+    const std::string& key_system,
+    EmeFeatureRequirement requirement,
+    bool is_permission_granted) {
+  return KeySystems::GetInstance().IsPersistentStateRequirementSupported(
+      key_system, requirement, is_permission_granted);
+}
+
+bool IsDistinctiveIdentifierRequirementSupported(
+    const std::string& key_system,
+    EmeFeatureRequirement requirement,
+    bool is_permission_granted) {
+  return KeySystems::GetInstance().IsDistinctiveIdentifierRequirementSupported(
+      key_system, requirement, is_permission_granted);
+}
+
 // These two functions are for testing purpose only. The declaration in the
 // header file is guarded by "#if defined(UNIT_TEST)" so that they can be used
 // by tests but not non-test code. However, this .cc file is compiled as part of
 // "media" where "UNIT_TEST" is not defined. So we need to specify
 // "MEDIA_EXPORT" here again so that they are visible to tests.
 
-MEDIA_EXPORT void AddContainerMask(const std::string& container,
-                                     uint32 mask) {
+MEDIA_EXPORT void AddContainerMask(const std::string& container, uint32 mask) {
   KeySystems::GetInstance().AddContainerMask(container, mask);
 }
 
diff --git a/media/base/key_systems.h b/media/base/key_systems.h
index 73eca6f4..93317a1 100644
--- a/media/base/key_systems.h
+++ b/media/base/key_systems.h
@@ -9,6 +9,7 @@
 #include <vector>
 
 #include "base/memory/scoped_ptr.h"
+#include "media/base/eme_constants.h"
 #include "media/base/media_export.h"
 
 namespace media {
@@ -84,6 +85,28 @@
     const std::string& concrete_key_system);
 #endif
 
+// Returns whether |key_system| supports persistent-license sessions.
+MEDIA_EXPORT bool IsPersistentLicenseSessionSupported(
+    const std::string& key_system,
+    bool is_permission_granted);
+
+// Returns whether |key_system| supports persistent-release-message sessions.
+MEDIA_EXPORT bool IsPersistentReleaseMessageSessionSupported(
+    const std::string& key_system,
+    bool is_permission_granted);
+
+// Returns whether |key_system| supports persistent state as requested.
+MEDIA_EXPORT bool IsPersistentStateRequirementSupported(
+    const std::string& key_system,
+    EmeFeatureRequirement requirement,
+    bool is_permission_granted);
+
+// Returns whether |key_system| supports distinctive identifiers as requested.
+MEDIA_EXPORT bool IsDistinctiveIdentifierRequirementSupported(
+    const std::string& key_system,
+    EmeFeatureRequirement requirement,
+    bool is_permission_granted);
+
 #if defined(UNIT_TEST)
 // Helper functions to add container/codec types for testing purposes.
 MEDIA_EXPORT void AddContainerMask(const std::string& container, uint32 mask);
diff --git a/media/base/key_systems_unittest.cc b/media/base/key_systems_unittest.cc
index bbd5989..7f8d27c 100644
--- a/media/base/key_systems_unittest.cc
+++ b/media/base/key_systems_unittest.cc
@@ -35,11 +35,11 @@
 // These are the (fake) key systems that are registered for these tests.
 // kUsesAes uses the AesDecryptor like Clear Key.
 // kExternal uses an external CDM, such as Pepper-based or Android platform CDM.
-const char kUsesAes[] = "org.example.clear";
-const char kUsesAesParent[] = "org.example";  // Not registered.
+const char kUsesAes[] = "x-org.example.clear";
+const char kUsesAesParent[] = "x-org.example";  // Not registered.
 const char kUseAesNameForUMA[] = "UseAes";
-const char kExternal[] = "com.example.test";
-const char kExternalParent[] = "com.example";
+const char kExternal[] = "x-com.example.test";
+const char kExternalParent[] = "x-com.example";
 const char kExternalNameForUMA[] = "External";
 
 const char kClearKey[] = "org.w3.clearkey";
@@ -91,14 +91,14 @@
 class TestMediaClient : public MediaClient {
  public:
   TestMediaClient();
-  ~TestMediaClient() final;
+  ~TestMediaClient() override;
 
   // MediaClient implementation.
   void AddKeySystemsInfoForUMA(
       std::vector<KeySystemInfoForUMA>* key_systems_info_for_uma) final;
   bool IsKeySystemsUpdateNeeded() final;
   void AddSupportedKeySystems(
-      std::vector<KeySystemInfo>* key_systems_info) final;
+      std::vector<KeySystemInfo>* key_systems_info) override;
 
   // Helper function to test the case where IsKeySystemsUpdateNeeded() is true
   // after AddSupportedKeySystems() is called.
@@ -109,8 +109,8 @@
   void DisableExternalKeySystemSupport();
 
  protected:
-  void AddUsesAesKeySystem(
-      std::vector<KeySystemInfo>* key_systems_info);
+  void AddUsesAesKeySystem(const std::string& name,
+                           std::vector<KeySystemInfo>* key_systems_info);
   void AddExternalKeySystem(
       std::vector<KeySystemInfo>* key_systems_info);
 
@@ -142,7 +142,7 @@
     std::vector<KeySystemInfo>* key_systems) {
   DCHECK(is_update_needed_);
 
-  AddUsesAesKeySystem(key_systems);
+  AddUsesAesKeySystem(kUsesAes, key_systems);
 
   if (supports_external_key_system_)
     AddExternalKeySystem(key_systems);
@@ -159,21 +159,32 @@
 }
 
 void TestMediaClient::AddUsesAesKeySystem(
+    const std::string& name,
     std::vector<KeySystemInfo>* key_systems) {
-  KeySystemInfo aes(kUsesAes);
-  aes.supported_codecs = EME_CODEC_WEBM_ALL;
-  aes.supported_codecs |= TEST_CODEC_FOO_ALL;
-  aes.supported_init_data_types = EME_INIT_DATA_TYPE_WEBM;
-  aes.use_aes_decryptor = true;
-  key_systems->push_back(aes);
+  KeySystemInfo system;
+  system.key_system = name;
+  system.supported_codecs = EME_CODEC_WEBM_ALL;
+  system.supported_codecs |= TEST_CODEC_FOO_ALL;
+  system.supported_init_data_types = EME_INIT_DATA_TYPE_WEBM;
+  system.persistent_license_support = EME_SESSION_TYPE_NOT_SUPPORTED;
+  system.persistent_release_message_support = EME_SESSION_TYPE_NOT_SUPPORTED;
+  system.persistent_state_support = EME_FEATURE_NOT_SUPPORTED;
+  system.distinctive_identifier_support = EME_FEATURE_NOT_SUPPORTED;
+  system.use_aes_decryptor = true;
+  key_systems->push_back(system);
 }
 
 void TestMediaClient::AddExternalKeySystem(
     std::vector<KeySystemInfo>* key_systems) {
-  KeySystemInfo ext(kExternal);
+  KeySystemInfo ext;
+  ext.key_system = kExternal;
   ext.supported_codecs = EME_CODEC_WEBM_ALL;
   ext.supported_codecs |= TEST_CODEC_FOO_ALL;
   ext.supported_init_data_types = EME_INIT_DATA_TYPE_WEBM;
+  ext.persistent_license_support = EME_SESSION_TYPE_SUPPORTED;
+  ext.persistent_release_message_support = EME_SESSION_TYPE_NOT_SUPPORTED;
+  ext.persistent_state_support = EME_FEATURE_ALWAYS_ENABLED;
+  ext.distinctive_identifier_support = EME_FEATURE_ALWAYS_ENABLED;
   ext.parent_key_system = kExternalParent;
 #if defined(ENABLE_PEPPER_CDMS)
   ext.pepper_type = "application/x-ppapi-external-cdm";
@@ -181,6 +192,36 @@
   key_systems->push_back(ext);
 }
 
+class PotentiallySupportedNamesTestMediaClient : public TestMediaClient {
+  void AddSupportedKeySystems(
+      std::vector<KeySystemInfo>* key_systems_info) final;
+};
+
+void PotentiallySupportedNamesTestMediaClient::AddSupportedKeySystems(
+    std::vector<KeySystemInfo>* key_systems) {
+  // org.w3.clearkey is automatically registered.
+  AddUsesAesKeySystem("com.widevine.alpha", key_systems);
+  AddUsesAesKeySystem("org.chromium.externalclearkey", key_systems);
+  AddUsesAesKeySystem("org.chromium.externalclearkey.something", key_systems);
+  AddUsesAesKeySystem("com.chromecast.something", key_systems);
+  AddUsesAesKeySystem("x-something", key_systems);
+}
+
+class KeySystemsPotentiallySupportedNamesTest : public testing::Test {
+ protected:
+  KeySystemsPotentiallySupportedNamesTest() {
+    SetMediaClient(&test_media_client_);
+  }
+
+  ~KeySystemsPotentiallySupportedNamesTest() override {
+    // Clear the use of |test_media_client_|, which was set in SetUp().
+    SetMediaClient(nullptr);
+  }
+
+ private:
+  PotentiallySupportedNamesTestMediaClient test_media_client_;
+};
+
 // TODO(sandersd): Refactor. http://crbug.com/417444
 class KeySystemsTest : public testing::Test {
  protected:
@@ -315,7 +356,7 @@
 
 // The key system is not registered and therefore is unrecognized.
 TEST_F(KeySystemsTest, Basic_UnrecognizedKeySystem) {
-  static const char* const kUnrecognized = "org.example.unrecognized";
+  static const char* const kUnrecognized = "x-org.example.unrecognized";
 
   EXPECT_FALSE(IsSupportedKeySystem(kUnrecognized));
   EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType(
@@ -326,13 +367,14 @@
   bool can_use = false;
   EXPECT_DEBUG_DEATH_PORTABLE(
       can_use = CanUseAesDecryptor(kUnrecognized),
-      "org.example.unrecognized is not a known concrete system");
+      "x-org.example.unrecognized is not a known concrete system");
   EXPECT_FALSE(can_use);
 
 #if defined(ENABLE_PEPPER_CDMS)
   std::string type;
-  EXPECT_DEBUG_DEATH(type = GetPepperType(kUnrecognized),
-                     "org.example.unrecognized is not a known concrete system");
+  EXPECT_DEBUG_DEATH(
+      type = GetPepperType(kUnrecognized),
+      "x-org.example.unrecognized is not a known concrete system");
   EXPECT_TRUE(type.empty());
 #endif
 }
@@ -349,7 +391,7 @@
 #if defined(ENABLE_PEPPER_CDMS)
   std::string type;
   EXPECT_DEBUG_DEATH(type = GetPepperType(kUsesAes),
-                     "org.example.clear is not Pepper-based");
+                     "x-org.example.clear is not Pepper-based");
   EXPECT_TRUE(type.empty());
 #endif
 }
@@ -411,21 +453,21 @@
   EXPECT_EQ("Unknown", GetKeySystemNameForUMA(kUsesAesParent));
   bool result = false;
   EXPECT_DEBUG_DEATH_PORTABLE(result = CanUseAesDecryptor(kUsesAesParent),
-                              "org.example is not a known concrete system");
+                              "x-org.example is not a known concrete system");
   EXPECT_FALSE(result);
 #if defined(ENABLE_PEPPER_CDMS)
   std::string type;
   EXPECT_DEBUG_DEATH(type = GetPepperType(kUsesAesParent),
-                     "org.example is not a known concrete system");
+                     "x-org.example is not a known concrete system");
   EXPECT_TRUE(type.empty());
 #endif
 }
 
 TEST_F(KeySystemsTest, IsSupportedKeySystem_InvalidVariants) {
   // Case sensitive.
-  EXPECT_FALSE(IsSupportedKeySystem("org.example.ClEaR"));
-  EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType(
-      kVideoWebM, no_codecs(), "org.example.ClEaR"));
+  EXPECT_FALSE(IsSupportedKeySystem("x-org.example.ClEaR"));
+  EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType(kVideoWebM, no_codecs(),
+                                                     "x-org.example.ClEaR"));
 
   // TLDs are not allowed.
   EXPECT_FALSE(IsSupportedKeySystem("org."));
@@ -436,24 +478,27 @@
       kVideoWebM, no_codecs(), "com"));
 
   // Extra period.
-  EXPECT_FALSE(IsSupportedKeySystem("org.example."));
-  EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType(
-      kVideoWebM, no_codecs(), "org.example."));
+  EXPECT_FALSE(IsSupportedKeySystem("x-org.example.clear."));
+  EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType(kVideoWebM, no_codecs(),
+                                                     "x-org.example.clear."));
+  EXPECT_FALSE(IsSupportedKeySystem("x-org.example."));
+  EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType(kVideoWebM, no_codecs(),
+                                                     "x-org.example."));
 
   // Incomplete.
-  EXPECT_FALSE(IsSupportedKeySystem("org.example.clea"));
-  EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType(
-      kVideoWebM, no_codecs(), "org.example.clea"));
+  EXPECT_FALSE(IsSupportedKeySystem("x-org.example.clea"));
+  EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType(kVideoWebM, no_codecs(),
+                                                     "x-org.example.clea"));
 
   // Extra character.
-  EXPECT_FALSE(IsSupportedKeySystem("org.example.clearz"));
-  EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType(
-      kVideoWebM, no_codecs(), "org.example.clearz"));
+  EXPECT_FALSE(IsSupportedKeySystem("x-org.example.clearz"));
+  EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType(kVideoWebM, no_codecs(),
+                                                     "x-org.example.clearz"));
 
   // There are no child key systems for UsesAes.
-  EXPECT_FALSE(IsSupportedKeySystem("org.example.clear.foo"));
+  EXPECT_FALSE(IsSupportedKeySystem("x-org.example.clear.foo"));
   EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType(
-      kVideoWebM, no_codecs(), "org.example.clear.foo"));
+      kVideoWebM, no_codecs(), "x-org.example.clear.foo"));
 }
 
 TEST_F(KeySystemsTest, IsSupportedKeySystemWithMediaMimeType_NoType) {
@@ -462,10 +507,10 @@
   EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType(
       std::string(), no_codecs(), kUsesAesParent));
 
+  EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType(std::string(), no_codecs(),
+                                                     "x-org.example.foo"));
   EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType(
-      std::string(), no_codecs(), "org.example.foo"));
-  EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType(
-      std::string(), no_codecs(), "org.example.clear.foo"));
+      std::string(), no_codecs(), "x-org.example.clear.foo"));
 }
 
 // Tests the second registered container type.
@@ -546,12 +591,12 @@
   EXPECT_EQ("Unknown", GetKeySystemNameForUMA(kExternalParent));
   bool result = false;
   EXPECT_DEBUG_DEATH_PORTABLE(result = CanUseAesDecryptor(kExternalParent),
-                              "com.example is not a known concrete system");
+                              "x-com.example is not a known concrete system");
   EXPECT_FALSE(result);
 #if defined(ENABLE_PEPPER_CDMS)
   std::string type;
   EXPECT_DEBUG_DEATH(type = GetPepperType(kExternalParent),
-                     "com.example is not a known concrete system");
+                     "x-com.example is not a known concrete system");
   EXPECT_TRUE(type.empty());
 #endif
 }
@@ -778,4 +823,43 @@
       kVideoWebM, no_codecs(), kExternal));
 }
 
+TEST_F(KeySystemsPotentiallySupportedNamesTest, PotentiallySupportedNames) {
+  EXPECT_FALSE(IsSupportedKeySystem("org.w3"));
+  EXPECT_FALSE(IsSupportedKeySystem("org.w3."));
+  EXPECT_FALSE(IsSupportedKeySystem("org.w3.clearke"));
+  EXPECT_TRUE(IsSupportedKeySystem("org.w3.clearkey"));
+  EXPECT_FALSE(IsSupportedKeySystem("org.w3.clearkeys"));
+
+  EXPECT_FALSE(IsSupportedKeySystem("com.widevine"));
+  EXPECT_FALSE(IsSupportedKeySystem("com.widevine."));
+  EXPECT_FALSE(IsSupportedKeySystem("com.widevine.alph"));
+  EXPECT_TRUE(IsSupportedKeySystem("com.widevine.alpha"));
+  EXPECT_FALSE(IsSupportedKeySystem("com.widevine.beta"));
+  EXPECT_FALSE(IsSupportedKeySystem("com.widevine.alphabeta"));
+  EXPECT_FALSE(IsSupportedKeySystem("com.widevine.alpha.beta"));
+
+  EXPECT_FALSE(IsSupportedKeySystem("org.chromium"));
+  EXPECT_FALSE(IsSupportedKeySystem("org.chromium."));
+  EXPECT_FALSE(IsSupportedKeySystem("org.chromium.externalclearke"));
+  EXPECT_TRUE(IsSupportedKeySystem("org.chromium.externalclearkey"));
+  EXPECT_FALSE(IsSupportedKeySystem("org.chromium.externalclearkeys"));
+  EXPECT_FALSE(IsSupportedKeySystem("org.chromium.externalclearkey."));
+  EXPECT_TRUE(IsSupportedKeySystem("org.chromium.externalclearkey.something"));
+  EXPECT_FALSE(
+      IsSupportedKeySystem("org.chromium.externalclearkey.something.else"));
+  EXPECT_FALSE(IsSupportedKeySystem("org.chromium.externalclearkey.other"));
+  EXPECT_FALSE(IsSupportedKeySystem("org.chromium.other"));
+
+  EXPECT_FALSE(IsSupportedKeySystem("com.chromecast"));
+  EXPECT_FALSE(IsSupportedKeySystem("com.chromecast."));
+  EXPECT_TRUE(IsSupportedKeySystem("com.chromecast.something"));
+  EXPECT_FALSE(IsSupportedKeySystem("com.chromecast.something.else"));
+  EXPECT_FALSE(IsSupportedKeySystem("com.chromecast.other"));
+
+  EXPECT_FALSE(IsSupportedKeySystem("x-"));
+  EXPECT_TRUE(IsSupportedKeySystem("x-something"));
+  EXPECT_FALSE(IsSupportedKeySystem("x-something.else"));
+  EXPECT_FALSE(IsSupportedKeySystem("x-other"));
+}
+
 }  // namespace media
diff --git a/media/base/mac/BUILD.gn b/media/base/mac/BUILD.gn
index c82d1d602..2bb7294 100644
--- a/media/base/mac/BUILD.gn
+++ b/media/base/mac/BUILD.gn
@@ -10,8 +10,8 @@
     "coremedia_glue.h",
     "coremedia_glue.mm",
     "corevideo_glue.h",
-    "video_frame_mac.h",
     "video_frame_mac.cc",
+    "video_frame_mac.h",
     "videotoolbox_glue.h",
     "videotoolbox_glue.mm",
   ]
diff --git a/media/base/stream_parser.h b/media/base/stream_parser.h
index efa3af8..dd5a31b 100644
--- a/media/base/stream_parser.h
+++ b/media/base/stream_parser.h
@@ -67,10 +67,8 @@
   };
 
   // Indicates completion of parser initialization.
-  //   success - True if initialization was successful.
-  //   params - Stream parameters, in case of successful initialization.
-  typedef base::Callback<void(bool success,
-                              const InitParameters& params)> InitCB;
+  //   params - Stream parameters.
+  typedef base::Callback<void(const InitParameters& params)> InitCB;
 
   // Indicates when new stream configurations have been parsed.
   // First parameter - The new audio configuration. If the config is not valid
diff --git a/media/base/video_frame.cc b/media/base/video_frame.cc
index 7f4f7839..c837159 100644
--- a/media/base/video_frame.cc
+++ b/media/base/video_frame.cc
@@ -309,6 +309,41 @@
   }
 }
 
+// static
+scoped_refptr<VideoFrame> VideoFrame::WrapExternalYuvData(
+    Format format,
+    const gfx::Size& coded_size,
+    const gfx::Rect& visible_rect,
+    const gfx::Size& natural_size,
+    int32 y_stride,
+    int32 u_stride,
+    int32 v_stride,
+    uint8* y_data,
+    uint8* u_data,
+    uint8* v_data,
+    base::TimeDelta timestamp,
+    const base::Closure& no_longer_needed_cb) {
+  const gfx::Size new_coded_size = AdjustCodedSize(format, coded_size);
+  CHECK(IsValidConfig(format, new_coded_size, visible_rect, natural_size));
+
+  scoped_refptr<VideoFrame> frame(
+      new VideoFrame(format,
+                     new_coded_size,
+                     visible_rect,
+                     natural_size,
+                     scoped_ptr<gpu::MailboxHolder>(),
+                     timestamp,
+                     false));
+  frame->strides_[kYPlane] = y_stride;
+  frame->strides_[kUPlane] = u_stride;
+  frame->strides_[kVPlane] = v_stride;
+  frame->data_[kYPlane] = y_data;
+  frame->data_[kUPlane] = u_data;
+  frame->data_[kVPlane] = v_data;
+  frame->no_longer_needed_cb_ = no_longer_needed_cb;
+  return frame;
+}
+
 #if defined(OS_POSIX)
 // static
 scoped_refptr<VideoFrame> VideoFrame::WrapExternalDmabufs(
@@ -404,41 +439,6 @@
 #endif
 
 // static
-scoped_refptr<VideoFrame> VideoFrame::WrapExternalYuvData(
-    Format format,
-    const gfx::Size& coded_size,
-    const gfx::Rect& visible_rect,
-    const gfx::Size& natural_size,
-    int32 y_stride,
-    int32 u_stride,
-    int32 v_stride,
-    uint8* y_data,
-    uint8* u_data,
-    uint8* v_data,
-    base::TimeDelta timestamp,
-    const base::Closure& no_longer_needed_cb) {
-  const gfx::Size new_coded_size = AdjustCodedSize(format, coded_size);
-  CHECK(IsValidConfig(format, new_coded_size, visible_rect, natural_size));
-
-  scoped_refptr<VideoFrame> frame(
-      new VideoFrame(format,
-                     new_coded_size,
-                     visible_rect,
-                     natural_size,
-                     scoped_ptr<gpu::MailboxHolder>(),
-                     timestamp,
-                     false));
-  frame->strides_[kYPlane] = y_stride;
-  frame->strides_[kUPlane] = u_stride;
-  frame->strides_[kVPlane] = v_stride;
-  frame->data_[kYPlane] = y_data;
-  frame->data_[kUPlane] = u_data;
-  frame->data_[kVPlane] = v_data;
-  frame->no_longer_needed_cb_ = no_longer_needed_cb;
-  return frame;
-}
-
-// static
 scoped_refptr<VideoFrame> VideoFrame::WrapVideoFrame(
       const scoped_refptr<VideoFrame>& frame,
       const gfx::Rect& visible_rect,
diff --git a/media/base/video_frame.h b/media/base/video_frame.h
index b7f47b5..366a356 100644
--- a/media/base/video_frame.h
+++ b/media/base/video_frame.h
@@ -129,6 +129,23 @@
       base::TimeDelta timestamp,
       const base::Closure& no_longer_needed_cb);
 
+  // Wraps external YUV data of the given parameters with a VideoFrame.
+  // The returned VideoFrame does not own the data passed in. When the frame
+  // is destroyed |no_longer_needed_cb.Run()| will be called.
+  static scoped_refptr<VideoFrame> WrapExternalYuvData(
+      Format format,
+      const gfx::Size& coded_size,
+      const gfx::Rect& visible_rect,
+      const gfx::Size& natural_size,
+      int32 y_stride,
+      int32 u_stride,
+      int32 v_stride,
+      uint8* y_data,
+      uint8* u_data,
+      uint8* v_data,
+      base::TimeDelta timestamp,
+      const base::Closure& no_longer_needed_cb);
+
 #if defined(OS_POSIX)
   // Wraps provided dmabufs
   // (https://www.kernel.org/doc/Documentation/dma-buf-sharing.txt) with a
@@ -165,25 +182,6 @@
       base::TimeDelta timestamp);
 #endif
 
-  // Wraps external YUV data of the given parameters with a VideoFrame.
-  // The returned VideoFrame does not own the data passed in. When the frame
-  // is destroyed |no_longer_needed_cb.Run()| will be called.
-  // TODO(sheu): merge this into WrapExternalSharedMemory().
-  // http://crbug.com/270217
-  static scoped_refptr<VideoFrame> WrapExternalYuvData(
-      Format format,
-      const gfx::Size& coded_size,
-      const gfx::Rect& visible_rect,
-      const gfx::Size& natural_size,
-      int32 y_stride,
-      int32 u_stride,
-      int32 v_stride,
-      uint8* y_data,
-      uint8* u_data,
-      uint8* v_data,
-      base::TimeDelta timestamp,
-      const base::Closure& no_longer_needed_cb);
-
   // Wraps |frame| and calls |no_longer_needed_cb| when the wrapper VideoFrame
   // gets destroyed. |visible_rect| must be a sub rect within
   // frame->visible_rect().
diff --git a/media/blink/BUILD.gn b/media/blink/BUILD.gn
index 48dd8dcc..bb52f5e 100644
--- a/media/blink/BUILD.gn
+++ b/media/blink/BUILD.gn
@@ -104,6 +104,8 @@
       "//url",
     ]
 
+    configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
+
     sources = [
       "buffered_data_source_host_impl_unittest.cc",
       "buffered_data_source_unittest.cc",
diff --git a/media/blink/media_blink.gyp b/media/blink/media_blink.gyp
index 6c6ccc7..fa6042f 100644
--- a/media/blink/media_blink.gyp
+++ b/media/blink/media_blink.gyp
@@ -57,8 +57,8 @@
         'webcontentdecryptionmoduleaccess_impl.h',
         'webcontentdecryptionmodulesession_impl.cc',
         'webcontentdecryptionmodulesession_impl.h',
-        "webencryptedmediaclient_impl.cc",
-        "webencryptedmediaclient_impl.h",
+        'webencryptedmediaclient_impl.cc',
+        'webencryptedmediaclient_impl.h',
         'webinbandtexttrack_impl.cc',
         'webinbandtexttrack_impl.h',
         'webmediaplayer_delegate.h',
diff --git a/media/blink/webencryptedmediaclient_impl.cc b/media/blink/webencryptedmediaclient_impl.cc
index 53b6583..2dd051e 100644
--- a/media/blink/webencryptedmediaclient_impl.cc
+++ b/media/blink/webencryptedmediaclient_impl.cc
@@ -4,6 +4,7 @@
 
 #include "webencryptedmediaclient_impl.h"
 
+#include "base/bind.h"
 #include "base/logging.h"
 #include "base/metrics/histogram.h"
 #include "base/strings/string_util.h"
@@ -24,123 +25,326 @@
 const char kKeySystemSupportUMAPrefix[] =
     "Media.EME.RequestMediaKeySystemAccess.";
 
-static bool IsSupportedContentType(
-    const std::string& key_system,
-    const std::string& mime_type,
-    const std::string& codecs) {
-  // Per RFC 6838, "Both top-level type and subtype names are case-insensitive."
-  // TODO(sandersd): Check that |container| matches the capability:
-  //   - audioCapabilitys: audio/mp4 or audio/webm.
-  //   - videoCapabilitys: video/mp4 or video/webm.
-  // http://crbug.com/457384.
+// TODO(jrummell): Convert to an enum. http://crbug.com/418239
+const char kTemporarySessionType[] = "temporary";
+const char kPersistentLicenseSessionType[] = "persistent-license";
+const char kPersistentReleaseMessageSessionType[] =
+    "persistent-release-message";
+
+enum ConfigurationSupport {
+  CONFIGURATION_NOT_SUPPORTED,
+  CONFIGURATION_REQUIRES_PERMISSION,
+  CONFIGURATION_SUPPORTED,
+};
+
+static bool IsSupportedContentType(const std::string& key_system,
+                                   const std::string& mime_type,
+                                   const std::string& codecs) {
+  // TODO(sandersd): Move contentType parsing from Blink to here so that invalid
+  // parameters can be rejected. http://crbug.com/417561
+  // TODO(sandersd): Pass in the media type (audio or video) and check that the
+  // container type matches. http://crbug.com/457384
   std::string container = base::StringToLowerASCII(mime_type);
 
-  // Check that |codecs| are supported as specified (e.g. "mp4a.40.2").
+  // Check that |codecs| are supported by the CDM. This check does not handle
+  // extended codecs, so extended codec information is stripped.
+  // TODO(sandersd): Reject codecs that do not match the media type.
+  // http://crbug.com/457386
   std::vector<std::string> codec_vector;
-  net::ParseCodecString(codecs, &codec_vector, false);
-  if (!net::AreSupportedMediaCodecs(codec_vector))
-    return false;
-
-  // IsSupportedKeySystemWithMediaMimeType() only works with base codecs
-  // (e.g. "mp4a"), so reparse |codecs| to get the base only.
-  codec_vector.clear();
   net::ParseCodecString(codecs, &codec_vector, true);
-  return IsSupportedKeySystemWithMediaMimeType(container, codec_vector,
-                                               key_system);
+  if (!IsSupportedKeySystemWithMediaMimeType(container, codec_vector,
+                                             key_system)) {
+    return false;
+  }
+
+  // Check that |codecs| are supported by Chrome. This is done primarily to
+  // validate extended codecs, but it also ensures that the CDM cannot support
+  // codecs that Chrome does not (which would be bad because it would require
+  // considering the accumulated configuration, and could affect whether secure
+  // decode is required).
+  // TODO(sandersd): Reject ambiguous codecs. http://crbug.com/374751
+  codec_vector.clear();
+  net::ParseCodecString(codecs, &codec_vector, false);
+  return net::AreSupportedMediaCodecs(codec_vector);
 }
 
-static bool GetSupportedConfiguration(
+static bool GetSupportedCapabilities(
+    const std::string& key_system,
+    const blink::WebVector<blink::WebMediaKeySystemMediaCapability>&
+        capabilities,
+    std::vector<blink::WebMediaKeySystemMediaCapability>*
+        media_type_capabilities) {
+  // From
+  // https://w3c.github.io/encrypted-media/#get-supported-capabilities-for-media-type
+  // 1. Let accumulated capabilities be partial configuration.
+  //    (Skipped as there are no configuration-based codec restrictions.)
+  // 2. Let media type capabilities be empty.
+  DCHECK_EQ(media_type_capabilities->size(), 0ul);
+  // 3. For each value in capabilities:
+  for (size_t i = 0; i < capabilities.size(); i++) {
+    // 3.1. Let contentType be the value's contentType member.
+    // 3.2. Let robustness be the value's robustness member.
+    const blink::WebMediaKeySystemMediaCapability& capability = capabilities[i];
+    // 3.3. If contentType is the empty string, return null.
+    if (capability.mimeType.isEmpty())
+      return false;
+    // 3.4-3.11. (Implemented by IsSupportedContentType().)
+    if (!base::IsStringASCII(capability.mimeType) ||
+        !base::IsStringASCII(capability.codecs) ||
+        !IsSupportedContentType(key_system,
+                                base::UTF16ToASCII(capability.mimeType),
+                                base::UTF16ToASCII(capability.codecs))) {
+      continue;
+    }
+    // 3.12. If robustness is not the empty string, run the following steps:
+    //       (Robustness is not supported.)
+    // TODO(sandersd): Implement robustness. http://crbug.com/442586
+    if (!capability.robustness.isEmpty()) {
+      LOG(WARNING) << "Configuration rejected because rubustness strings are "
+                   << "not yet supported.";
+      continue;
+    }
+    // 3.13. If the user agent and implementation do not support playback of
+    //       encrypted media data as specified by configuration, including all
+    //       media types, in combination with accumulated capabilities,
+    //       continue to the next iteration.
+    //       (Skipped as there are no configuration-based codec restrictions.)
+    // 3.14. Add configuration to media type capabilities.
+    media_type_capabilities->push_back(capability);
+    // 3.15. Add configuration to accumulated capabilities.
+    //       (Skipped as there are no configuration-based codec restrictions.)
+  }
+  // 4. If media type capabilities is empty, return null.
+  // 5. Return media type capabilities.
+  return !media_type_capabilities->empty();
+}
+
+static EmeFeatureRequirement ConvertRequirement(
+    blink::WebMediaKeySystemConfiguration::Requirement requirement) {
+  switch (requirement) {
+    case blink::WebMediaKeySystemConfiguration::Requirement::Required:
+      return EME_FEATURE_REQUIRED;
+    case blink::WebMediaKeySystemConfiguration::Requirement::Optional:
+      return EME_FEATURE_OPTIONAL;
+    case blink::WebMediaKeySystemConfiguration::Requirement::NotAllowed:
+      return EME_FEATURE_NOT_ALLOWED;
+  }
+
+  NOTREACHED();
+  return EME_FEATURE_NOT_ALLOWED;
+}
+
+static ConfigurationSupport GetSupportedConfiguration(
     const std::string& key_system,
     const blink::WebMediaKeySystemConfiguration& candidate,
-    const blink::WebSecurityOrigin& security_origin,
-    blink::WebMediaKeySystemConfiguration* accumulated_configuration) {
-  if (!candidate.initDataTypes.isEmpty()) {
-    std::vector<blink::WebString> init_data_types;
+    blink::WebMediaKeySystemConfiguration* accumulated_configuration,
+    bool was_permission_requested,
+    bool is_permission_granted) {
+  DCHECK(was_permission_requested || !is_permission_granted);
 
+  // It is possible to obtain user permission unless permission was already
+  // requested and denied.
+  bool is_permission_possible =
+      !was_permission_requested || is_permission_granted;
+
+  // From https://w3c.github.io/encrypted-media/#get-supported-configuration
+  // 1. Let accumulated configuration be empty. (Done by caller.)
+  // 2. If candidate configuration's initDataTypes attribute is not empty, run
+  //    the following steps:
+  if (!candidate.initDataTypes.isEmpty()) {
+    // 2.1. Let supported types be empty.
+    std::vector<blink::WebString> supported_types;
+
+    // 2.2. For each value in candidate configuration's initDataTypes attribute:
     for (size_t i = 0; i < candidate.initDataTypes.size(); i++) {
+      // 2.2.1. Let initDataType be the value.
       const blink::WebString& init_data_type = candidate.initDataTypes[i];
+      // 2.2.2. If initDataType is the empty string, return null.
       if (init_data_type.isEmpty())
-        return false;
+        return CONFIGURATION_NOT_SUPPORTED;
+      // 2.2.3. If the implementation supports generating requests based on
+      //        initDataType, add initDataType to supported types. String
+      //        comparison is case-sensitive.
       if (base::IsStringASCII(init_data_type) &&
           IsSupportedKeySystemWithInitDataType(
               key_system, base::UTF16ToASCII(init_data_type))) {
-        init_data_types.push_back(init_data_type);
+        supported_types.push_back(init_data_type);
       }
     }
 
-    if (init_data_types.empty())
-      return false;
+    // 2.3. If supported types is empty, return null.
+    if (supported_types.empty())
+      return CONFIGURATION_NOT_SUPPORTED;
 
-    accumulated_configuration->initDataTypes = init_data_types;
+    // 2.4. Add supported types to accumulated configuration.
+    accumulated_configuration->initDataTypes = supported_types;
   }
 
-  // TODO(sandersd): Implement distinctiveIdentifier and persistentState checks.
-  if (candidate.distinctiveIdentifier !=
-      blink::WebMediaKeySystemConfiguration::Requirement::Optional ||
-      candidate.persistentState !=
-      blink::WebMediaKeySystemConfiguration::Requirement::Optional) {
-    return false;
+  // 3. Follow the steps for the value of candidate configuration's
+  //    distinctiveIdentifier attribute from the following list:
+  //     - "required": If the implementation does not support a persistent
+  //       Distinctive Identifier in combination with accumulated configuration,
+  //       return null.
+  //     - "optional": Continue.
+  //     - "not-allowed": If the implementation requires a Distinctive
+  //       Identifier in combination with accumulated configuration, return
+  //       null.
+  EmeFeatureRequirement di_requirement =
+      ConvertRequirement(candidate.distinctiveIdentifier);
+  if (!IsDistinctiveIdentifierRequirementSupported(key_system, di_requirement,
+                                                   is_permission_possible)) {
+    return CONFIGURATION_NOT_SUPPORTED;
   }
 
-  if (!candidate.audioCapabilities.isEmpty()) {
-    std::vector<blink::WebMediaKeySystemMediaCapability> audio_capabilities;
+  // 4. Add the value of the candidate configuration's distinctiveIdentifier
+  //    attribute to accumulated configuration.
+  accumulated_configuration->distinctiveIdentifier =
+      candidate.distinctiveIdentifier;
 
-    for (size_t i = 0; i < candidate.audioCapabilities.size(); i++) {
-      const blink::WebMediaKeySystemMediaCapability& capabilities =
-          candidate.audioCapabilities[i];
-      if (capabilities.mimeType.isEmpty())
-        return false;
-      if (!base::IsStringASCII(capabilities.mimeType) ||
-          !base::IsStringASCII(capabilities.codecs) ||
-          !IsSupportedContentType(
-              key_system, base::UTF16ToASCII(capabilities.mimeType),
-              base::UTF16ToASCII(capabilities.codecs))) {
-        continue;
-      }
-      // TODO(sandersd): Support robustness.
-      if (!capabilities.robustness.isEmpty())
-        continue;
-      audio_capabilities.push_back(capabilities);
-    }
-
-    if (audio_capabilities.empty())
-      return false;
-
-    accumulated_configuration->audioCapabilities = audio_capabilities;
+  // 5. Follow the steps for the value of candidate configuration's
+  //    persistentState attribute from the following list:
+  //     - "required": If the implementation does not support persisting state
+  //       in combination with accumulated configuration, return null.
+  //     - "optional": Continue.
+  //     - "not-allowed": If the implementation requires persisting state in
+  //       combination with accumulated configuration, return null.
+  EmeFeatureRequirement ps_requirement =
+      ConvertRequirement(candidate.persistentState);
+  if (!IsPersistentStateRequirementSupported(key_system, ps_requirement,
+                                             is_permission_possible)) {
+    return CONFIGURATION_NOT_SUPPORTED;
   }
 
+  // 6. Add the value of the candidate configuration's persistentState
+  //    attribute to accumulated configuration.
+  accumulated_configuration->persistentState = candidate.persistentState;
+
+  // 7. If candidate configuration's videoCapabilities attribute is not empty,
+  //    run the following steps:
   if (!candidate.videoCapabilities.isEmpty()) {
+    // 7.1. Let video capabilities be the result of executing the Get Supported
+    //      Capabilities for Media Type algorithm on Video, candidate
+    //      configuration's videoCapabilities attribute, and accumulated
+    //      configuration.
+    // 7.2. If video capabilities is null, return null.
     std::vector<blink::WebMediaKeySystemMediaCapability> video_capabilities;
-
-    for (size_t i = 0; i < candidate.videoCapabilities.size(); i++) {
-      const blink::WebMediaKeySystemMediaCapability& capabilities =
-          candidate.videoCapabilities[i];
-      if (capabilities.mimeType.isEmpty())
-        return false;
-      if (!base::IsStringASCII(capabilities.mimeType) ||
-          !base::IsStringASCII(capabilities.codecs) ||
-          !IsSupportedContentType(
-              key_system, base::UTF16ToASCII(capabilities.mimeType),
-              base::UTF16ToASCII(capabilities.codecs))) {
-        continue;
-      }
-      // TODO(sandersd): Support robustness.
-      if (!capabilities.robustness.isEmpty())
-        continue;
-      video_capabilities.push_back(capabilities);
+    if (!GetSupportedCapabilities(key_system, candidate.videoCapabilities,
+                                  &video_capabilities)) {
+      return CONFIGURATION_NOT_SUPPORTED;
     }
 
-    if (video_capabilities.empty())
-      return false;
-
+    // 7.3. Add video capabilities to accumulated configuration.
     accumulated_configuration->videoCapabilities = video_capabilities;
   }
 
-  // TODO(sandersd): Prompt for distinctive identifiers and/or persistent state
-  // if required. Make sure that future checks are silent.
-  // http://crbug.com/446263.
+  // 8. If candidate configuration's audioCapabilities attribute is not empty,
+  //    run the following steps:
+  if (!candidate.audioCapabilities.isEmpty()) {
+    // 8.1. Let audio capabilities be the result of executing the Get Supported
+    //      Capabilities for Media Type algorithm on Audio, candidate
+    //      configuration's audioCapabilities attribute, and accumulated
+    //      configuration.
+    // 8.2. If audio capabilities is null, return null.
+    std::vector<blink::WebMediaKeySystemMediaCapability> audio_capabilities;
+    if (!GetSupportedCapabilities(key_system, candidate.audioCapabilities,
+                                  &audio_capabilities)) {
+      return CONFIGURATION_NOT_SUPPORTED;
+    }
 
-  return true;
+    // 8.3. Add audio capabilities to accumulated configuration.
+    accumulated_configuration->audioCapabilities = audio_capabilities;
+  }
+
+  // 9. If accumulated configuration's distinctiveIdentifier value is
+  //    "optional", follow the steps for the first matching condition from the
+  //    following list:
+  //      - If the implementation requires a Distinctive Identifier for any of
+  //        the combinations in accumulated configuration, change accumulated
+  //        configuration's distinctiveIdentifier value to "required".
+  //      - Otherwise, change accumulated configuration's distinctiveIdentifier
+  //        value to "not-allowed".
+  //    (Without robustness support, capabilities do not affect this.)
+  // TODO(sandersd): Implement robustness. http://crbug.com/442586
+  if (accumulated_configuration->distinctiveIdentifier ==
+      blink::WebMediaKeySystemConfiguration::Requirement::Optional) {
+    if (IsDistinctiveIdentifierRequirementSupported(
+            key_system, EME_FEATURE_NOT_ALLOWED, is_permission_possible)) {
+      accumulated_configuration->distinctiveIdentifier =
+          blink::WebMediaKeySystemConfiguration::Requirement::NotAllowed;
+    } else {
+      accumulated_configuration->distinctiveIdentifier =
+          blink::WebMediaKeySystemConfiguration::Requirement::Required;
+    }
+  }
+
+  // 10. If accumulated configuration's persistentState value is "optional",
+  //     follow the steps for the first matching condition from the following
+  //     list:
+  //       - If the implementation requires persisting state for any of the
+  //         combinations in accumulated configuration, change accumulated
+  //         configuration's persistentState value to "required".
+  //       - Otherwise, change accumulated configuration's persistentState value
+  //         to "not-allowed".
+  if (accumulated_configuration->persistentState ==
+      blink::WebMediaKeySystemConfiguration::Requirement::Optional) {
+    if (IsPersistentStateRequirementSupported(
+            key_system, EME_FEATURE_NOT_ALLOWED, is_permission_possible)) {
+      accumulated_configuration->persistentState =
+          blink::WebMediaKeySystemConfiguration::Requirement::NotAllowed;
+    } else {
+      accumulated_configuration->persistentState =
+          blink::WebMediaKeySystemConfiguration::Requirement::Required;
+    }
+  }
+
+  // 11. If implementation in the configuration specified by the combination of
+  //     the values in accumulated configuration is not supported or not allowed
+  //     in the origin, return null.
+  di_requirement =
+      ConvertRequirement(accumulated_configuration->distinctiveIdentifier);
+  if (!IsDistinctiveIdentifierRequirementSupported(key_system, di_requirement,
+                                                   is_permission_granted)) {
+    if (was_permission_requested) {
+      // The optional permission was requested and denied.
+      // TODO(sandersd): Avoid the need for this logic - crbug.com/460616.
+      DCHECK(candidate.distinctiveIdentifier ==
+             blink::WebMediaKeySystemConfiguration::Requirement::Optional);
+      DCHECK(di_requirement == EME_FEATURE_REQUIRED);
+      DCHECK(!is_permission_granted);
+      accumulated_configuration->distinctiveIdentifier =
+          blink::WebMediaKeySystemConfiguration::Requirement::NotAllowed;
+    } else {
+      return CONFIGURATION_REQUIRES_PERMISSION;
+    }
+  }
+
+  ps_requirement =
+      ConvertRequirement(accumulated_configuration->persistentState);
+  if (!IsPersistentStateRequirementSupported(key_system, ps_requirement,
+                                             is_permission_granted)) {
+    DCHECK(!was_permission_requested);  // Should have failed at step 5.
+    return CONFIGURATION_REQUIRES_PERMISSION;
+  }
+
+  // 12. Return accumulated configuration.
+  //     (As an extra step, we record the available session types so that
+  //     createSession() can be synchronous.)
+  std::vector<blink::WebString> session_types;
+  session_types.push_back(kTemporarySessionType);
+  if (accumulated_configuration->persistentState ==
+      blink::WebMediaKeySystemConfiguration::Requirement::Required) {
+    if (IsPersistentLicenseSessionSupported(key_system,
+                                            is_permission_granted)) {
+      session_types.push_back(kPersistentLicenseSessionType);
+    }
+    if (IsPersistentReleaseMessageSessionSupported(key_system,
+                                                   is_permission_granted)) {
+      session_types.push_back(kPersistentReleaseMessageSessionType);
+    }
+  }
+  accumulated_configuration->sessionTypes = session_types;
+
+  return CONFIGURATION_SUPPORTED;
 }
 
 // Report usage of key system to UMA. There are 2 different counts logged:
@@ -196,9 +400,8 @@
 WebEncryptedMediaClientImpl::WebEncryptedMediaClientImpl(
     scoped_ptr<CdmFactory> cdm_factory,
     MediaPermission* media_permission)
-    : cdm_factory_(cdm_factory.Pass()), weak_factory_(this) {
-  // TODO(sandersd): Use |media_permission| to check for media permissions in
-  // this class.
+    : cdm_factory_(cdm_factory.Pass()), media_permission_(media_permission),
+      weak_factory_(this) {
   DCHECK(media_permission);
 }
 
@@ -207,62 +410,85 @@
 
 void WebEncryptedMediaClientImpl::requestMediaKeySystemAccess(
     blink::WebEncryptedMediaRequest request) {
-  // TODO(jrummell): This should be asynchronous.
+  // TODO(jrummell): This should be asynchronous, ideally not on the main
+  // thread.
 
   // Continued from requestMediaKeySystemAccess(), step 7, from
   // https://w3c.github.io/encrypted-media/#requestmediakeysystemaccess
   //
-  // 7.1 If keySystem is not one of the Key Systems supported by the user
-  //     agent, reject promise with with a new DOMException whose name is
-  //     NotSupportedError. String comparison is case-sensitive.
+  // 7.1. If keySystem is not one of the Key Systems supported by the user
+  //      agent, reject promise with with a new DOMException whose name is
+  //      NotSupportedError. String comparison is case-sensitive.
   if (!base::IsStringASCII(request.keySystem())) {
     request.requestNotSupported("Only ASCII keySystems are supported");
     return;
   }
 
+  // Report this request to the UMA.
   std::string key_system = base::UTF16ToASCII(request.keySystem());
-
-  // Report this request to the appropriate Reporter.
-  Reporter* reporter = GetReporter(key_system);
-  reporter->ReportRequested();
+  GetReporter(key_system)->ReportRequested();
 
   if (!IsSupportedKeySystem(key_system)) {
     request.requestNotSupported("Unsupported keySystem");
     return;
   }
 
-  // 7.2 Let implementation be the implementation of keySystem.
-  // 7.3 For each value in supportedConfigurations, run the GetSupported
-  //     Configuration algorithm and if successful, resolve promise with access
-  //     and abort these steps.
+  // 7.2-7.4. Implemented by SelectSupportedConfiguration().
+  SelectSupportedConfiguration(request, false, false);
+}
+
+void WebEncryptedMediaClientImpl::SelectSupportedConfiguration(
+    blink::WebEncryptedMediaRequest request,
+    bool was_permission_requested,
+    bool is_permission_granted) {
+  // Continued from requestMediaKeySystemAccess(), step 7.1, from
+  // https://w3c.github.io/encrypted-media/#requestmediakeysystemaccess
+  //
+  // 7.2. Let implementation be the implementation of keySystem.
+  std::string key_system = base::UTF16ToASCII(request.keySystem());
+
+  // 7.3. For each value in supportedConfigurations:
   const blink::WebVector<blink::WebMediaKeySystemConfiguration>&
       configurations = request.supportedConfigurations();
-
-  // TODO(sandersd): Remove once Blink requires the configurations parameter for
-  // requestMediaKeySystemAccess().
-  if (configurations.isEmpty()) {
-    reporter->ReportSupported();
-    request.requestSucceeded(WebContentDecryptionModuleAccessImpl::Create(
-        request.keySystem(), blink::WebMediaKeySystemConfiguration(),
-        request.securityOrigin(), weak_factory_.GetWeakPtr()));
-    return;
-  }
-
   for (size_t i = 0; i < configurations.size(); i++) {
-    const blink::WebMediaKeySystemConfiguration& candidate = configurations[i];
+    // 7.3.1. Let candidate configuration be the value.
+    const blink::WebMediaKeySystemConfiguration& candidate_configuration =
+        configurations[i];
+    // 7.3.2. Let supported configuration be the result of executing the Get
+    //        Supported Configuration algorithm on implementation, candidate
+    //        configuration, and origin.
+    // 7.3.3. If supported configuration is not null, [initialize and return a
+    //        new MediaKeySystemAccess object.]
     blink::WebMediaKeySystemConfiguration accumulated_configuration;
-    if (GetSupportedConfiguration(key_system, candidate,
-                                  request.securityOrigin(),
-                                  &accumulated_configuration)) {
-      reporter->ReportSupported();
-      request.requestSucceeded(WebContentDecryptionModuleAccessImpl::Create(
-          request.keySystem(), accumulated_configuration,
-          request.securityOrigin(), weak_factory_.GetWeakPtr()));
-      return;
+    ConfigurationSupport supported = GetSupportedConfiguration(
+        key_system, candidate_configuration, &accumulated_configuration,
+        was_permission_requested, is_permission_granted);
+    switch (supported) {
+      case CONFIGURATION_NOT_SUPPORTED:
+        continue;
+      case CONFIGURATION_REQUIRES_PERMISSION:
+        DCHECK(!was_permission_requested);
+        media_permission_->RequestPermission(
+            MediaPermission::PROTECTED_MEDIA_IDENTIFIER,
+            GURL(request.securityOrigin().toString()),
+            // Try again with |was_permission_requested| true and
+            // |is_permission_granted| the value of the permission.
+            base::Bind(
+                &WebEncryptedMediaClientImpl::SelectSupportedConfiguration,
+                weak_factory_.GetWeakPtr(), request, true));
+        return;
+      case CONFIGURATION_SUPPORTED:
+        // Report that this request succeeded to the UMA.
+        GetReporter(key_system)->ReportSupported();
+        request.requestSucceeded(WebContentDecryptionModuleAccessImpl::Create(
+            request.keySystem(), accumulated_configuration,
+            request.securityOrigin(), weak_factory_.GetWeakPtr()));
+        return;
     }
   }
 
-  // 7.4 Reject promise with a new DOMException whose name is NotSupportedError.
+  // 7.4. Reject promise with a new DOMException whose name is
+  //      NotSupportedError.
   request.requestNotSupported(
       "None of the requested configurations were supported.");
 }
diff --git a/media/blink/webencryptedmediaclient_impl.h b/media/blink/webencryptedmediaclient_impl.h
index 7b606dc4..d531952 100644
--- a/media/blink/webencryptedmediaclient_impl.h
+++ b/media/blink/webencryptedmediaclient_impl.h
@@ -38,6 +38,12 @@
                  blink::WebContentDecryptionModuleResult result);
 
  private:
+  // Pick a supported configuration if possible, and complete the request. This
+  // method may asynchronously invoke itself after prompting for permissions.
+  void SelectSupportedConfiguration(blink::WebEncryptedMediaRequest request,
+                                    bool was_permission_requested,
+                                    bool is_permission_granted);
+
   // Report usage of key system to UMA. There are 2 different counts logged:
   // 1. The key system is requested.
   // 2. The requested key system and options are supported.
@@ -53,6 +59,7 @@
   Reporters reporters_;
 
   scoped_ptr<CdmFactory> cdm_factory_;
+  MediaPermission* media_permission_;
 
   base::WeakPtrFactory<WebEncryptedMediaClientImpl> weak_factory_;
 };
diff --git a/media/cast/BUILD.gn b/media/cast/BUILD.gn
index 99c3332b..ec91639 100644
--- a/media/cast/BUILD.gn
+++ b/media/cast/BUILD.gn
@@ -69,34 +69,34 @@
     "net/pacing/paced_sender.cc",
     "net/pacing/paced_sender.h",
     "net/rtcp/receiver_rtcp_event_subscriber.cc",
-    "net/rtcp/rtcp_defines.cc",
-    "net/rtcp/rtcp_defines.h",
-    "net/rtcp/rtcp.h",
     "net/rtcp/rtcp.cc",
+    "net/rtcp/rtcp.h",
     "net/rtcp/rtcp_builder.cc",
     "net/rtcp/rtcp_builder.h",
+    "net/rtcp/rtcp_defines.cc",
+    "net/rtcp/rtcp_defines.h",
     "net/rtcp/rtcp_utility.cc",
     "net/rtcp/rtcp_utility.h",
-    "net/rtp/packet_storage.cc",
-    "net/rtp/packet_storage.h",
-    "net/rtp/rtp_packetizer.cc",
-    "net/rtp/rtp_packetizer.h",
-    "net/rtp/rtp_sender.cc",
-    "net/rtp/rtp_sender.h",
-    "net/udp_transport.cc",
-    "net/udp_transport.h",
     "net/rtp/cast_message_builder.cc",
     "net/rtp/cast_message_builder.h",
     "net/rtp/frame_buffer.cc",
     "net/rtp/frame_buffer.h",
     "net/rtp/framer.cc",
     "net/rtp/framer.h",
+    "net/rtp/packet_storage.cc",
+    "net/rtp/packet_storage.h",
     "net/rtp/receiver_stats.cc",
     "net/rtp/receiver_stats.h",
+    "net/rtp/rtp_packetizer.cc",
+    "net/rtp/rtp_packetizer.h",
     "net/rtp/rtp_parser.cc",
     "net/rtp/rtp_parser.h",
     "net/rtp/rtp_receiver_defines.cc",
     "net/rtp/rtp_receiver_defines.h",
+    "net/rtp/rtp_sender.cc",
+    "net/rtp/rtp_sender.h",
+    "net/udp_transport.cc",
+    "net/udp_transport.h",
   ]
 
   deps = [
@@ -109,28 +109,28 @@
     "cast_sender.h",
     "cast_sender_impl.cc",
     "cast_sender_impl.h",
-    "sender/audio_encoder.h",
     "sender/audio_encoder.cc",
-    "sender/audio_sender.h",
+    "sender/audio_encoder.h",
     "sender/audio_sender.cc",
-    "sender/congestion_control.h",
+    "sender/audio_sender.h",
     "sender/congestion_control.cc",
-    "sender/external_video_encoder.h",
+    "sender/congestion_control.h",
     "sender/external_video_encoder.cc",
-    "sender/fake_software_video_encoder.h",
+    "sender/external_video_encoder.h",
     "sender/fake_software_video_encoder.cc",
+    "sender/fake_software_video_encoder.h",
     "sender/frame_sender.cc",
     "sender/frame_sender.h",
     "sender/size_adaptable_video_encoder_base.cc",
     "sender/size_adaptable_video_encoder_base.h",
     "sender/software_video_encoder.h",
-    "sender/video_encoder.h",
     "sender/video_encoder.cc",
-    "sender/video_encoder_impl.h",
+    "sender/video_encoder.h",
     "sender/video_encoder_impl.cc",
+    "sender/video_encoder_impl.h",
     "sender/video_frame_factory.h",
-    "sender/video_sender.h",
     "sender/video_sender.cc",
+    "sender/video_sender.h",
     "sender/vp8_encoder.cc",
     "sender/vp8_encoder.h",
   ]
@@ -154,10 +154,10 @@
       "//third_party/opus",
     ]
     sources -= [
-      "sender/external_video_encoder.h",
       "sender/external_video_encoder.cc",
-      "sender/video_encoder_impl.h",
+      "sender/external_video_encoder.h",
       "sender/video_encoder_impl.cc",
+      "sender/video_encoder_impl.h",
       "sender/vp8_encoder.cc",
       "sender/vp8_encoder.h",
     ]
@@ -214,12 +214,12 @@
     #    "test/fake_media_source.h",
     "test/fake_single_thread_task_runner.cc",
     "test/fake_single_thread_task_runner.h",
+    "test/loopback_transport.cc",
+    "test/loopback_transport.h",
     "test/skewed_single_thread_task_runner.cc",
     "test/skewed_single_thread_task_runner.h",
     "test/skewed_tick_clock.cc",
     "test/skewed_tick_clock.h",
-    "test/loopback_transport.cc",
-    "test/loopback_transport.h",
     "test/utility/audio_utility.cc",
     "test/utility/audio_utility.h",
     "test/utility/barcode.cc",
@@ -234,10 +234,10 @@
     "test/utility/net_utility.h",
     "test/utility/standalone_cast_environment.cc",
     "test/utility/standalone_cast_environment.h",
-    "test/utility/video_utility.cc",
-    "test/utility/video_utility.h",
     "test/utility/udp_proxy.cc",
     "test/utility/udp_proxy.h",
+    "test/utility/video_utility.cc",
+    "test/utility/video_utility.h",
   ]
 
   deps = [
@@ -258,10 +258,10 @@
 test("cast_unittests") {
   sources = [
     "logging/encoding_event_subscriber_unittest.cc",
-    "logging/serialize_deserialize_test.cc",
     "logging/logging_impl_unittest.cc",
     "logging/logging_raw_unittest.cc",
     "logging/receiver_time_offset_estimator_impl_unittest.cc",
+    "logging/serialize_deserialize_test.cc",
     "logging/simple_event_subscriber_unittest.cc",
     "logging/stats_event_subscriber_unittest.cc",
     "net/cast_transport_sender_impl_unittest.cc",
@@ -270,10 +270,10 @@
     "net/pacing/mock_paced_packet_sender.cc",
     "net/pacing/mock_paced_packet_sender.h",
     "net/pacing/paced_sender_unittest.cc",
+    "net/rtcp/receiver_rtcp_event_subscriber_unittest.cc",
     "net/rtcp/rtcp_builder_unittest.cc",
     "net/rtcp/rtcp_unittest.cc",
     "net/rtcp/rtcp_utility_unittest.cc",
-    "net/rtcp/receiver_rtcp_event_subscriber_unittest.cc",
 
     # TODO(miu): The following two are test utility modules.  Rename/move the files.
     "net/rtcp/test_rtcp_packet_builder.cc",
@@ -288,8 +288,8 @@
     "net/rtp/rtp_header_parser.cc",
     "net/rtp/rtp_header_parser.h",
     "net/rtp/rtp_packet_builder.cc",
-    "net/rtp/rtp_parser_unittest.cc",
     "net/rtp/rtp_packetizer_unittest.cc",
+    "net/rtp/rtp_parser_unittest.cc",
     "net/rtp/rtp_receiver_defines.h",
     "net/udp_transport_unittest.cc",
     "receiver/audio_decoder_unittest.cc",
diff --git a/media/cast/cast.gyp b/media/cast/cast.gyp
index 82ba1f4..1d6594f 100644
--- a/media/cast/cast.gyp
+++ b/media/cast/cast.gyp
@@ -99,14 +99,6 @@
       ],
       'sources': [
         'cast_receiver.h',
-        'receiver/audio_decoder.cc',
-        'receiver/audio_decoder.h',
-        'receiver/cast_receiver_impl.cc',
-        'receiver/cast_receiver_impl.h',
-        'receiver/frame_receiver.cc',
-        'receiver/frame_receiver.h',
-        'receiver/video_decoder.cc',
-        'receiver/video_decoder.h',
         'net/rtp/cast_message_builder.cc',
         'net/rtp/cast_message_builder.h',
         'net/rtp/frame_buffer.cc',
@@ -117,6 +109,14 @@
         'net/rtp/receiver_stats.h',
         'net/rtp/rtp_receiver_defines.cc',
         'net/rtp/rtp_receiver_defines.h',
+        'receiver/audio_decoder.cc',
+        'receiver/audio_decoder.h',
+        'receiver/cast_receiver_impl.cc',
+        'receiver/cast_receiver_impl.h',
+        'receiver/frame_receiver.cc',
+        'receiver/frame_receiver.h',
+        'receiver/video_decoder.cc',
+        'receiver/video_decoder.h',
       ], # source
       'conditions': [
         # use a restricted subset of media and no software codecs on iOS
@@ -150,28 +150,28 @@
         'cast_sender.h',
         'cast_sender_impl.cc',
         'cast_sender_impl.h',
-        'sender/audio_encoder.h',
         'sender/audio_encoder.cc',
-        'sender/audio_sender.h',
+        'sender/audio_encoder.h',
         'sender/audio_sender.cc',
-        'sender/congestion_control.h',
+        'sender/audio_sender.h',
         'sender/congestion_control.cc',
-        'sender/external_video_encoder.h',
+        'sender/congestion_control.h',
         'sender/external_video_encoder.cc',
-        'sender/fake_software_video_encoder.h',
+        'sender/external_video_encoder.h',
         'sender/fake_software_video_encoder.cc',
+        'sender/fake_software_video_encoder.h',
         'sender/frame_sender.cc',
         'sender/frame_sender.h',
         'sender/size_adaptable_video_encoder_base.cc',
         'sender/size_adaptable_video_encoder_base.h',
         'sender/software_video_encoder.h',
-        'sender/video_encoder.h',
         'sender/video_encoder.cc',
-        'sender/video_encoder_impl.h',
+        'sender/video_encoder.h',
         'sender/video_encoder_impl.cc',
+        'sender/video_encoder_impl.h',
         'sender/video_frame_factory.h',
-        'sender/video_sender.h',
         'sender/video_sender.cc',
+        'sender/video_sender.h',
         'sender/vp8_encoder.cc',
         'sender/vp8_encoder.h',
       ], # source
@@ -185,10 +185,10 @@
             '<(DEPTH)/third_party/libvpx/libvpx.gyp:libvpx',
           ],
           'sources!': [
-            'sender/external_video_encoder.h',
             'sender/external_video_encoder.cc',
-            'sender/video_encoder_impl.h',
+            'sender/external_video_encoder.h',
             'sender/video_encoder_impl.cc',
+            'sender/video_encoder_impl.h',
             'sender/vp8_encoder.cc',
             'sender/vp8_encoder.h',
           ],
@@ -224,12 +224,12 @@
         'net/pacing/paced_sender.cc',
         'net/pacing/paced_sender.h',
         'net/rtcp/receiver_rtcp_event_subscriber.cc',
+        'net/rtcp/rtcp.cc',
+        'net/rtcp/rtcp.h',
         'net/rtcp/rtcp_builder.cc',
         'net/rtcp/rtcp_builder.h',
         'net/rtcp/rtcp_defines.cc',
         'net/rtcp/rtcp_defines.h',
-        'net/rtcp/rtcp.h',
-        'net/rtcp/rtcp.cc',
         'net/rtcp/rtcp_utility.cc',
         'net/rtcp/rtcp_utility.h',
         'net/rtp/packet_storage.cc',
diff --git a/media/cast/cast_testing.gypi b/media/cast/cast_testing.gypi
index 22a2182..29d6901 100644
--- a/media/cast/cast_testing.gypi
+++ b/media/cast/cast_testing.gypi
@@ -21,6 +21,8 @@
         '<(DEPTH)/ui/gfx/gfx.gyp:gfx_geometry',
       ],
       'sources': [
+	'test/loopback_transport.cc',
+	'test/loopback_transport.h',
         'test/fake_media_source.cc',
         'test/fake_media_source.h',
         'test/fake_single_thread_task_runner.cc',
@@ -29,8 +31,6 @@
         'test/skewed_single_thread_task_runner.h',
         'test/skewed_tick_clock.cc',
         'test/skewed_tick_clock.h',
-	'test/loopback_transport.cc',
-	'test/loopback_transport.h',
         'test/utility/audio_utility.cc',
         'test/utility/audio_utility.h',
         'test/utility/barcode.cc',
@@ -45,10 +45,10 @@
         'test/utility/net_utility.h',
         'test/utility/standalone_cast_environment.cc',
         'test/utility/standalone_cast_environment.h',
-        'test/utility/video_utility.cc',
-        'test/utility/video_utility.h',
         'test/utility/udp_proxy.cc',
         'test/utility/udp_proxy.h',
+        'test/utility/video_utility.cc',
+        'test/utility/video_utility.h',
       ], # source
     },
     {
@@ -75,10 +75,10 @@
       'sources': [
         '<(DEPTH)/media/base/run_all_unittests.cc',
         'logging/encoding_event_subscriber_unittest.cc',
-        'logging/serialize_deserialize_test.cc',
         'logging/logging_impl_unittest.cc',
         'logging/logging_raw_unittest.cc',
         'logging/receiver_time_offset_estimator_impl_unittest.cc',
+        'logging/serialize_deserialize_test.cc',
         'logging/simple_event_subscriber_unittest.cc',
         'logging/stats_event_subscriber_unittest.cc',
         'net/cast_transport_sender_impl_unittest.cc',
@@ -88,10 +88,10 @@
         'net/pacing/mock_paced_packet_sender.cc',
         'net/pacing/mock_paced_packet_sender.h',
         'net/pacing/paced_sender_unittest.cc',
+        'net/rtcp/receiver_rtcp_event_subscriber_unittest.cc',
         'net/rtcp/rtcp_builder_unittest.cc',
         'net/rtcp/rtcp_unittest.cc',
         'net/rtcp/rtcp_utility_unittest.cc',
-        'net/rtcp/receiver_rtcp_event_subscriber_unittest.cc',
 # TODO(miu): The following two are test utility modules.  Rename/move the files.
         'net/rtcp/test_rtcp_packet_builder.cc',
         'net/rtcp/test_rtcp_packet_builder.h',
@@ -105,8 +105,8 @@
         'net/rtp/rtp_header_parser.cc',
         'net/rtp/rtp_header_parser.h',
         'net/rtp/rtp_packet_builder.cc',
-        'net/rtp/rtp_parser_unittest.cc',
         'net/rtp/rtp_packetizer_unittest.cc',
+        'net/rtp/rtp_parser_unittest.cc',
         'net/rtp/rtp_receiver_defines.h',
         'net/udp_transport_unittest.cc',
         'receiver/audio_decoder_unittest.cc',
diff --git a/media/cdm/aes_decryptor.cc b/media/cdm/aes_decryptor.cc
index 1c9171c..1bf574d 100644
--- a/media/cdm/aes_decryptor.cc
+++ b/media/cdm/aes_decryptor.cc
@@ -19,6 +19,7 @@
 #include "media/base/decrypt_config.h"
 #include "media/base/video_decoder_config.h"
 #include "media/base/video_frame.h"
+#include "media/cdm/cenc_utils.h"
 #include "media/cdm/json_web_key.h"
 
 namespace media {
@@ -254,13 +255,31 @@
   std::string session_id(base::UintToString(next_session_id_++));
   valid_sessions_.insert(session_id);
 
-  // For now, the AesDecryptor does not care about |init_data_type| or
-  // |session_type|; just resolve the promise and then fire a message event
-  // using the |init_data| as the key ID in the license request.
-  // TODO(jrummell): Validate |init_data_type| and |session_type|.
+  // For now, the AesDecryptor does not care about |session_type|.
+  // TODO(jrummell): Validate |session_type|.
+
   std::vector<uint8> message;
-  if (init_data && init_data_length)
-    CreateLicenseRequest(init_data, init_data_length, session_type, &message);
+  // TODO(jrummell): Since unprefixed will never send NULL, remove this check
+  // when prefixed EME is removed (http://crbug.com/249976).
+  if (init_data && init_data_length) {
+    std::vector<std::vector<uint8>> keys;
+    if (init_data_type == "webm") {
+      // |init_data| is simply the key needed.
+      keys.push_back(
+          std::vector<uint8>(init_data, init_data + init_data_length));
+    } else if (init_data_type == "cenc") {
+      // |init_data| is a set of 0 or more concatenated 'pssh' boxes.
+      if (!GetKeyIdsForCommonSystemId(init_data, init_data_length, &keys)) {
+        promise->reject(NOT_SUPPORTED_ERROR, 0, "No supported PSSH box found.");
+        return;
+      }
+    } else {
+      // TODO(jrummell): Support init_data_type == "keyids".
+      promise->reject(NOT_SUPPORTED_ERROR, 0, "init_data_type not supported.");
+      return;
+    }
+    CreateLicenseRequest(keys, session_type, &message);
+  }
 
   promise->resolve(session_id);
 
diff --git a/media/cdm/aes_decryptor_unittest.cc b/media/cdm/aes_decryptor_unittest.cc
index 3825ae1e..ce06fe2 100644
--- a/media/cdm/aes_decryptor_unittest.cc
+++ b/media/cdm/aes_decryptor_unittest.cc
@@ -285,7 +285,7 @@
     EXPECT_CALL(*this, OnSessionMessage(IsNotEmpty(), _, IsJSONDictionary(),
                                         GURL::EmptyGURL()));
     decryptor_.CreateSessionAndGenerateRequest(
-        MediaKeys::TEMPORARY_SESSION, std::string(), &key_id[0], key_id.size(),
+        MediaKeys::TEMPORARY_SESSION, "webm", &key_id[0], key_id.size(),
         CreateSessionPromise(RESOLVED));
     // This expects the promise to be called synchronously, which is the case
     // for AesDecryptor.
@@ -430,7 +430,7 @@
   EXPECT_CALL(*this,
               OnSessionMessage(IsNotEmpty(), _, IsEmpty(), GURL::EmptyGURL()));
   decryptor_.CreateSessionAndGenerateRequest(MediaKeys::TEMPORARY_SESSION,
-                                             std::string(), NULL, 0,
+                                             "webm", NULL, 0,
                                              CreateSessionPromise(RESOLVED));
 }
 
@@ -438,19 +438,19 @@
   EXPECT_CALL(*this,
               OnSessionMessage(IsNotEmpty(), _, IsEmpty(), GURL::EmptyGURL()));
   decryptor_.CreateSessionAndGenerateRequest(MediaKeys::TEMPORARY_SESSION,
-                                             std::string(), NULL, 0,
+                                             "webm", NULL, 0,
                                              CreateSessionPromise(RESOLVED));
 
   EXPECT_CALL(*this,
               OnSessionMessage(IsNotEmpty(), _, IsEmpty(), GURL::EmptyGURL()));
   decryptor_.CreateSessionAndGenerateRequest(MediaKeys::TEMPORARY_SESSION,
-                                             std::string(), NULL, 0,
+                                             "webm", NULL, 0,
                                              CreateSessionPromise(RESOLVED));
 
   EXPECT_CALL(*this,
               OnSessionMessage(IsNotEmpty(), _, IsEmpty(), GURL::EmptyGURL()));
   decryptor_.CreateSessionAndGenerateRequest(MediaKeys::TEMPORARY_SESSION,
-                                             std::string(), NULL, 0,
+                                             "webm", NULL, 0,
                                              CreateSessionPromise(RESOLVED));
 }
 
@@ -747,7 +747,7 @@
       "      \"kty\": \"oct\","
       "      \"alg\": \"A128KW\","
       "      \"kid\": \"JCUmJygpKissLS4vMA\","
-      "      \"k\":\"MTIzNDU2Nzg5Ojs8PT4/QA\""
+      "      \"k\":\"MTIzNDU2Nzg5Ojs8PT4_QA\""
       "    }"
       "  ]"
       "}";
diff --git a/media/cdm/cenc_utils.cc b/media/cdm/cenc_utils.cc
new file mode 100644
index 0000000..2eeafd7
--- /dev/null
+++ b/media/cdm/cenc_utils.cc
@@ -0,0 +1,154 @@
+// 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 "media/cdm/cenc_utils.h"
+
+#include "media/base/bit_reader.h"
+
+namespace media {
+
+// The initialization data for encrypted media files using the ISO Common
+// Encryption ('cenc') protection scheme may contain one or more protection
+// system specific header ('pssh') boxes.
+// ref: https://w3c.github.io/encrypted-media/cenc-format.html
+//
+// The format of a 'pssh' box is as follows:
+//   unsigned int(32) size;
+//   unsigned int(32) type = "pssh";
+//   if (size==1) {
+//     unsigned int(64) largesize;
+//   } else if (size==0) {
+//     -- box extends to end of file
+//   }
+//   unsigned int(8) version;
+//   bit(24) flags;
+//   unsigned int(8)[16] SystemID;
+//   if (version > 0)
+//   {
+//     unsigned int(32) KID_count;
+//     {
+//       unsigned int(8)[16] KID;
+//     } [KID_count]
+//   }
+//   unsigned int(32) DataSize;
+//   unsigned int(8)[DataSize] Data;
+
+// Minimum size of a 'pssh' box includes  all the required fields (size, type,
+// version, flags, SystemID, DataSize).
+const int kMinimumBoxSizeInBytes = 32;
+
+// SystemID for the Common System.
+// https://w3c.github.io/encrypted-media/cenc-format.html#common-system
+const uint8 kCommonSystemId[] = { 0x10, 0x77, 0xef, 0xec,
+                                  0xc0, 0xb2, 0x4d, 0x02,
+                                  0xac, 0xe3, 0x3c, 0x1e,
+                                  0x52, 0xe2, 0xfb, 0x4b };
+
+#define RCHECK(x)   \
+  do {              \
+    if (!(x))       \
+      return false; \
+  } while (0)
+
+// Helper function to read up to 32 bits from a bit stream.
+static uint32 ReadBits(BitReader* reader, int num_bits) {
+  DCHECK_GE(reader->bits_available(), num_bits);
+  DCHECK((num_bits > 0) && (num_bits <= 32));
+  uint32 value;
+  reader->ReadBits(num_bits, &value);
+  return value;
+}
+
+// Checks whether the next 16 bytes matches the Common SystemID.
+// Assumes |reader| has enough data.
+static bool IsCommonSystemID(BitReader* reader) {
+  for (uint32 i = 0; i < arraysize(kCommonSystemId); ++i) {
+    if (ReadBits(reader, 8) != kCommonSystemId[i])
+      return false;
+  }
+  return true;
+}
+
+bool GetKeyIdsForCommonSystemId(const uint8* input,
+                                int input_length,
+                                std::vector<std::vector<uint8>>* key_ids) {
+  int offset = 0;
+  std::vector<std::vector<uint8>> result;
+
+  while (offset < input_length) {
+    BitReader reader(input + offset, input_length - offset);
+
+    // Enough data for a miniumum size 'pssh' box?
+    RCHECK(reader.bits_available() >= kMinimumBoxSizeInBytes * 8);
+
+    uint32 size = ReadBits(&reader, 32);
+
+    // Must be a 'pssh' box or else fail.
+    RCHECK(ReadBits(&reader, 8) == 'p');
+    RCHECK(ReadBits(&reader, 8) == 's');
+    RCHECK(ReadBits(&reader, 8) == 's');
+    RCHECK(ReadBits(&reader, 8) == 'h');
+
+    if (size == 1) {
+      // If largesize > 2**32 it is too big.
+      RCHECK(ReadBits(&reader, 32) == 0);
+      size = ReadBits(&reader, 32);
+    } else if (size == 0) {
+      size = input_length - offset;
+    }
+
+    // Check that the buffer contains at least size bytes.
+    RCHECK(static_cast<uint32>(input_length - offset) >= size);
+
+    // Update offset to point at the next 'pssh' box (may not be one).
+    offset += size;
+
+    // Check the version, as KIDs only available if version > 0.
+    uint8 version = ReadBits(&reader, 8);
+    if (version == 0)
+      continue;
+
+    // flags must be 0. If not, assume incorrect 'pssh' box and move to the
+    // next one.
+    if (ReadBits(&reader, 24) != 0)
+      continue;
+
+    // Validate SystemID
+    RCHECK(static_cast<uint32>(reader.bits_available()) >=
+           arraysize(kCommonSystemId) * 8);
+    if (!IsCommonSystemID(&reader))
+      continue;  // Not Common System, so try the next pssh box.
+
+    // Since version > 0, next field is the KID_count.
+    RCHECK(static_cast<uint32>(reader.bits_available()) >= sizeof(uint32) * 8);
+    uint32 count = ReadBits(&reader, 32);
+
+    if (count == 0)
+      continue;
+
+    // Make sure there is enough data for all the KIDs specified, and then
+    // extract them.
+    RCHECK(static_cast<uint32>(reader.bits_available()) > count * 16 * 8);
+    while (count > 0) {
+      std::vector<uint8> key;
+      key.reserve(16);
+      for (int i = 0; i < 16; ++i) {
+        key.push_back(ReadBits(&reader, 8));
+      }
+      result.push_back(key);
+      --count;
+    }
+
+    // Don't bother checking DataSize and Data.
+  }
+
+  key_ids->swap(result);
+
+  // TODO(jrummell): This should return true only if there was at least one
+  // key ID present. However, numerous test files don't contain the 'pssh' box
+  // for Common Format, so no keys are found. http://crbug.com/460308
+  return true;
+}
+
+}  // namespace media
diff --git a/media/cdm/cenc_utils.h b/media/cdm/cenc_utils.h
new file mode 100644
index 0000000..c659742
--- /dev/null
+++ b/media/cdm/cenc_utils.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 MEDIA_CDM_CENC_UTILS_H_
+#define MEDIA_CDM_CENC_UTILS_H_
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "media/base/media_export.h"
+
+namespace media {
+
+// Gets the Key Ids from a 'pssh' box for the Common SystemID among one or
+// more concatenated 'pssh' boxes. If |input| looks valid, then true is
+// returned and |key_ids| is updated to contain the values found. Otherwise
+// return false.
+// TODO(jrummell): This returns true if no Common SystemID 'pssh' boxes are
+// found, or are included but don't contain any key IDs. This should be
+// fixed once the test files are updated to include correct 'pssh' boxes.
+// http://crbug.com/460308
+MEDIA_EXPORT bool GetKeyIdsForCommonSystemId(
+    const uint8* input,
+    int input_length,
+    std::vector<std::vector<uint8>>* key_ids);
+
+}  // namespace media
+
+#endif  // MEDIA_CDM_CENC_UTILS_H_
diff --git a/media/cdm/cenc_utils_unittest.cc b/media/cdm/cenc_utils_unittest.cc
new file mode 100644
index 0000000..ae75817
--- /dev/null
+++ b/media/cdm/cenc_utils_unittest.cc
@@ -0,0 +1,353 @@
+// 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 "media/cdm/cenc_utils.h"
+
+#include "base/logging.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace media {
+
+const uint8 kKey1Data[] = {
+    0x7E, 0x57, 0x1D, 0x03, 0x7E, 0x57, 0x1D, 0x03,
+    0x7E, 0x57, 0x1D, 0x03, 0x7E, 0x57, 0x1D, 0x03
+};
+const uint8 kKey2Data[] = {
+    0x7E, 0x57, 0x1D, 0x04, 0x7E, 0x57, 0x1D, 0x04,
+    0x7E, 0x57, 0x1D, 0x04, 0x7E, 0x57, 0x1D, 0x04,
+};
+const uint8 kKey3Data[] = {
+    0x7E, 0x57, 0x1D, 0x04, 0x7E, 0x57, 0x1D, 0x05,
+    0x7E, 0x57, 0x1D, 0x04, 0x7E, 0x57, 0x1D, 0x05,
+};
+const uint8 kKey4Data[] = {
+    0x7E, 0x57, 0x1D, 0x04, 0x7E, 0x57, 0x1D, 0x06,
+    0x7E, 0x57, 0x1D, 0x04, 0x7E, 0x57, 0x1D, 0x06,
+};
+
+class CencUtilsTest : public testing::Test {
+ public:
+  CencUtilsTest()
+      : key1_(kKey1Data, kKey1Data + arraysize(kKey1Data)),
+        key2_(kKey2Data, kKey2Data + arraysize(kKey2Data)),
+        key3_(kKey3Data, kKey3Data + arraysize(kKey3Data)),
+        key4_(kKey4Data, kKey4Data + arraysize(kKey4Data)) {}
+
+ protected:
+  // Initialize the start of the 'pssh' box (up to key_count)
+  void InitializePSSHBox(std::vector<uint8>* box, uint8 size, uint8 version) {
+    DCHECK(box->size() == 0);
+
+    box->reserve(size);
+    // Add size.
+    box->push_back(0);
+    box->push_back(0);
+    box->push_back(0);
+    box->push_back(size);
+    // Add 'pssh'.
+    box->push_back('p');
+    box->push_back('s');
+    box->push_back('s');
+    box->push_back('h');
+    // Add version.
+    box->push_back(version);
+    // Add flags.
+    box->push_back(0);
+    box->push_back(0);
+    box->push_back(0);
+    // Add Clear Key SystemID.
+    box->push_back(0x10);
+    box->push_back(0x77);
+    box->push_back(0xEF);
+    box->push_back(0xEC);
+    box->push_back(0xC0);
+    box->push_back(0xB2);
+    box->push_back(0x4D);
+    box->push_back(0x02);
+    box->push_back(0xAC);
+    box->push_back(0xE3);
+    box->push_back(0x3C);
+    box->push_back(0x1E);
+    box->push_back(0x52);
+    box->push_back(0xE2);
+    box->push_back(0xFB);
+    box->push_back(0x4B);
+  }
+
+  std::vector<uint8> MakePSSHBox(uint8 version) {
+    std::vector<uint8> box;
+    uint8 size = (version == 0) ? 32 : 36;
+    InitializePSSHBox(&box, size, version);
+    if (version > 0) {
+      // Add key_count (= 0).
+      box.push_back(0);
+      box.push_back(0);
+      box.push_back(0);
+      box.push_back(0);
+    }
+    // Add data_size (= 0).
+    box.push_back(0);
+    box.push_back(0);
+    box.push_back(0);
+    box.push_back(0);
+    return box;
+  }
+
+  std::vector<uint8> MakePSSHBox(uint8 version,
+                                 const std::vector<uint8>& key1) {
+    DCHECK(version > 0);
+    DCHECK(key1.size() == 16);
+
+    std::vector<uint8> box;
+    uint8 size = 52;
+    InitializePSSHBox(&box, size, version);
+
+    // Add key_count (= 1).
+    box.push_back(0);
+    box.push_back(0);
+    box.push_back(0);
+    box.push_back(1);
+
+    // Add key1.
+    for (int i = 0; i < 16; ++i)
+      box.push_back(key1[i]);
+
+    // Add data_size (= 0).
+    box.push_back(0);
+    box.push_back(0);
+    box.push_back(0);
+    box.push_back(0);
+    return box;
+  }
+
+  std::vector<uint8> MakePSSHBox(uint8 version,
+                                 const std::vector<uint8>& key1,
+                                 const std::vector<uint8>& key2) {
+    DCHECK(version > 0);
+    DCHECK(key1.size() == 16);
+    DCHECK(key2.size() == 16);
+
+    std::vector<uint8> box;
+    uint8 size = 68;
+    InitializePSSHBox(&box, size, version);
+
+    // Add key_count (= 2).
+    box.push_back(0);
+    box.push_back(0);
+    box.push_back(0);
+    box.push_back(2);
+
+    // Add key1.
+    for (int i = 0; i < 16; ++i)
+      box.push_back(key1[i]);
+
+    // Add key2.
+    for (int i = 0; i < 16; ++i)
+      box.push_back(key2[i]);
+
+    // Add data_size (= 0).
+    box.push_back(0);
+    box.push_back(0);
+    box.push_back(0);
+    box.push_back(0);
+    return box;
+  }
+
+  const std::vector<uint8>& Key1() { return key1_; }
+  const std::vector<uint8>& Key2() { return key2_; }
+  const std::vector<uint8>& Key3() { return key3_; }
+  const std::vector<uint8>& Key4() { return key4_; }
+
+ private:
+  std::vector<uint8> key1_;
+  std::vector<uint8> key2_;
+  std::vector<uint8> key3_;
+  std::vector<uint8> key4_;
+};
+
+TEST_F(CencUtilsTest, EmptyPSSH) {
+  std::vector<std::vector<uint8>> key_ids;
+  EXPECT_TRUE(GetKeyIdsForCommonSystemId(nullptr, 0, &key_ids));
+  EXPECT_EQ(0u, key_ids.size());
+}
+
+TEST_F(CencUtilsTest, PSSHVersion0) {
+  std::vector<uint8> box = MakePSSHBox(0);
+  std::vector<std::vector<uint8>> key_ids;
+  EXPECT_TRUE(GetKeyIdsForCommonSystemId(&box[0], box.size(), &key_ids));
+  EXPECT_EQ(0u, key_ids.size());
+}
+
+TEST_F(CencUtilsTest, PSSHVersion1WithNoKeys) {
+  std::vector<uint8> box = MakePSSHBox(1);
+  std::vector<std::vector<uint8>> key_ids;
+  EXPECT_TRUE(GetKeyIdsForCommonSystemId(&box[0], box.size(), &key_ids));
+  EXPECT_EQ(0u, key_ids.size());
+}
+
+TEST_F(CencUtilsTest, PSSHVersion1WithOneKey) {
+  std::vector<uint8> box = MakePSSHBox(1, Key1());
+  std::vector<std::vector<uint8>> key_ids;
+  EXPECT_TRUE(GetKeyIdsForCommonSystemId(&box[0], box.size(), &key_ids));
+  EXPECT_EQ(1u, key_ids.size());
+  EXPECT_EQ(key_ids[0], Key1());
+}
+
+TEST_F(CencUtilsTest, PSSHVersion1WithTwoKeys) {
+  std::vector<uint8> box = MakePSSHBox(1, Key1(), Key2());
+  std::vector<std::vector<uint8>> key_ids;
+  EXPECT_TRUE(GetKeyIdsForCommonSystemId(&box[0], box.size(), &key_ids));
+  EXPECT_EQ(2u, key_ids.size());
+  EXPECT_EQ(key_ids[0], Key1());
+  EXPECT_EQ(key_ids[1], Key2());
+}
+
+TEST_F(CencUtilsTest, PSSHVersion0Plus1) {
+  std::vector<uint8> box0 = MakePSSHBox(0);
+  std::vector<uint8> box1 = MakePSSHBox(1, Key1());
+
+  // Concatentate box1 into box0.
+  for (const auto& value : box1)
+    box0.push_back(value);
+
+  std::vector<std::vector<uint8>> key_ids;
+  EXPECT_TRUE(GetKeyIdsForCommonSystemId(&box0[0], box0.size(), &key_ids));
+  EXPECT_EQ(1u, key_ids.size());
+  EXPECT_EQ(key_ids[0], Key1());
+}
+
+TEST_F(CencUtilsTest, PSSHVersion1Plus0) {
+  std::vector<uint8> box0 = MakePSSHBox(0);
+  std::vector<uint8> box1 = MakePSSHBox(1, Key1());
+
+  // Concatentate box0 into box1.
+  for (const auto& value : box0)
+    box1.push_back(value);
+
+  std::vector<std::vector<uint8>> key_ids;
+  EXPECT_TRUE(GetKeyIdsForCommonSystemId(&box1[0], box1.size(), &key_ids));
+  EXPECT_EQ(1u, key_ids.size());
+  EXPECT_EQ(key_ids[0], Key1());
+}
+
+TEST_F(CencUtilsTest, MultiplePSSHVersion1) {
+  std::vector<uint8> box = MakePSSHBox(1, Key1(), Key2());
+  std::vector<uint8> box1 = MakePSSHBox(1, Key3());
+  std::vector<uint8> box2 = MakePSSHBox(1, Key4());
+
+  // Concatentate box1 into box.
+  for (const auto& value : box1)
+    box.push_back(value);
+  // Concatentate box2 into box.
+  for (const auto& value : box2)
+    box.push_back(value);
+
+  std::vector<std::vector<uint8>> key_ids;
+  EXPECT_TRUE(GetKeyIdsForCommonSystemId(&box[0], box.size(), &key_ids));
+  EXPECT_EQ(4u, key_ids.size());
+  EXPECT_EQ(key_ids[0], Key1());
+  EXPECT_EQ(key_ids[1], Key2());
+  EXPECT_EQ(key_ids[2], Key3());
+  EXPECT_EQ(key_ids[3], Key4());
+}
+
+TEST_F(CencUtilsTest, InvalidPSSH) {
+  std::vector<uint8> box = MakePSSHBox(1, Key1(), Key2());
+  std::vector<std::vector<uint8>> key_ids;
+  for (uint32 i = 1; i < box.size(); ++i) {
+    // Modify size of data passed to be less than real size.
+    EXPECT_FALSE(GetKeyIdsForCommonSystemId(&box[0], i, &key_ids));
+    // Modify starting point.
+    EXPECT_FALSE(GetKeyIdsForCommonSystemId(&box[i], box.size() - i, &key_ids));
+  }
+}
+
+TEST_F(CencUtilsTest, InvalidSystemID) {
+  std::vector<uint8> box = MakePSSHBox(1, Key1(), Key2());
+
+  // Modify the System ID.
+  ++box[20];
+
+  std::vector<std::vector<uint8>> key_ids;
+  EXPECT_TRUE(GetKeyIdsForCommonSystemId(&box[0], box.size(), &key_ids));
+  EXPECT_EQ(0u, key_ids.size());
+}
+
+TEST_F(CencUtilsTest, InvalidFlags) {
+  std::vector<uint8> box = MakePSSHBox(1, Key1(), Key2());
+
+  // Modify flags.
+  box[10] = 3;
+
+  std::vector<std::vector<uint8>> key_ids;
+  // TODO(jrummell): This should fail as the 'pssh' box is skipped.
+  EXPECT_TRUE(GetKeyIdsForCommonSystemId(&box[0], box.size(), &key_ids));
+  EXPECT_EQ(0u, key_ids.size());
+}
+
+TEST_F(CencUtilsTest, LongSize) {
+  const uint8 data[] = {
+      0x00, 0x00, 0x00, 0x01,                          // size = 1
+      0x70, 0x73, 0x73, 0x68,                          // 'pssh'
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4c,  // longsize
+      0x01,                                            // version
+      0x00, 0x00, 0x00,                                // flags
+      0x10, 0x77, 0xEF, 0xEC, 0xC0, 0xB2, 0x4D, 0x02,  // SystemID
+      0xAC, 0xE3, 0x3C, 0x1E, 0x52, 0xE2, 0xFB, 0x4B,
+      0x00, 0x00, 0x00, 0x02,                          // key count
+      0x7E, 0x57, 0x1D, 0x03, 0x7E, 0x57, 0x1D, 0x03,  // key1
+      0x7E, 0x57, 0x1D, 0x03, 0x7E, 0x57, 0x1D, 0x03,
+      0x7E, 0x57, 0x1D, 0x04, 0x7E, 0x57, 0x1D, 0x04,  // key2
+      0x7E, 0x57, 0x1D, 0x04, 0x7E, 0x57, 0x1D, 0x04,
+      0x00, 0x00, 0x00, 0x00  // datasize
+  };
+
+  std::vector<std::vector<uint8>> key_ids;
+  EXPECT_TRUE(GetKeyIdsForCommonSystemId(data, arraysize(data), &key_ids));
+  EXPECT_EQ(2u, key_ids.size());
+}
+
+TEST_F(CencUtilsTest, NoSize) {
+  const uint8 data[] = {
+      0x00, 0x00, 0x00, 0x00,                          // size = 0
+      0x70, 0x73, 0x73, 0x68,                          // 'pssh'
+      0x01,                                            // version
+      0x00, 0x00, 0x00,                                // flags
+      0x10, 0x77, 0xEF, 0xEC, 0xC0, 0xB2, 0x4D, 0x02,  // SystemID
+      0xAC, 0xE3, 0x3C, 0x1E, 0x52, 0xE2, 0xFB, 0x4B,
+      0x00, 0x00, 0x00, 0x02,                          // key count
+      0x7E, 0x57, 0x1D, 0x03, 0x7E, 0x57, 0x1D, 0x03,  // key1
+      0x7E, 0x57, 0x1D, 0x03, 0x7E, 0x57, 0x1D, 0x03,
+      0x7E, 0x57, 0x1D, 0x04, 0x7E, 0x57, 0x1D, 0x04,  // key2
+      0x7E, 0x57, 0x1D, 0x04, 0x7E, 0x57, 0x1D, 0x04,
+      0x00, 0x00, 0x00, 0x00  // datasize
+  };
+
+  std::vector<std::vector<uint8>> key_ids;
+  EXPECT_TRUE(GetKeyIdsForCommonSystemId(data, arraysize(data), &key_ids));
+  EXPECT_EQ(2u, key_ids.size());
+}
+
+TEST_F(CencUtilsTest, HugeSize) {
+  const uint8 data[] = {
+      0x00, 0x00, 0x00, 0x01,                          // size = 1
+      0x70, 0x73, 0x73, 0x68,                          // 'pssh'
+      0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,  // longsize = big
+      0x01,                                            // version
+      0x00, 0x00, 0x00,                                // flags
+      0x10, 0x77, 0xEF, 0xEC, 0xC0, 0xB2, 0x4D, 0x02,  // SystemID
+      0xAC, 0xE3, 0x3C, 0x1E, 0x52, 0xE2, 0xFB, 0x4B,
+      0x00, 0x00, 0x00, 0x02,                          // key count
+      0x7E, 0x57, 0x1D, 0x03, 0x7E, 0x57, 0x1D, 0x03,  // key1
+      0x7E, 0x57, 0x1D, 0x03, 0x7E, 0x57, 0x1D, 0x03,
+      0x7E, 0x57, 0x1D, 0x04, 0x7E, 0x57, 0x1D, 0x04,  // key2
+      0x7E, 0x57, 0x1D, 0x04, 0x7E, 0x57, 0x1D, 0x04,
+      0x00, 0x00, 0x00, 0x00  // datasize
+  };
+
+  std::vector<std::vector<uint8>> key_ids;
+  EXPECT_FALSE(GetKeyIdsForCommonSystemId(data, arraysize(data), &key_ids));
+}
+
+}  // namespace media
diff --git a/media/cdm/json_web_key.cc b/media/cdm/json_web_key.cc
index 446d389ac8..cd50a8c 100644
--- a/media/cdm/json_web_key.cc
+++ b/media/cdm/json_web_key.cc
@@ -24,13 +24,18 @@
 const char kKeyIdTag[] = "kid";
 const char kKeyIdsTag[] = "kids";
 const char kBase64Padding = '=';
+const char kBase64Plus[] = "+";
+const char kBase64UrlPlusReplacement[] = "-";
+const char kBase64Slash[] = "/";
+const char kBase64UrlSlashReplacement[] = "_";
+const char kBase64UrlInvalid[] = "+/=";
 const char kTypeTag[] = "type";
 const char kTemporarySession[] = "temporary";
 const char kPersistentLicenseSession[] = "persistent-license";
 const char kPersistentReleaseMessageSession[] = "persistent-release-message";
 
-// Encodes |input| into a base64 string without padding.
-static std::string EncodeBase64(const uint8* input, int input_length) {
+// Encodes |input| into a base64url string without padding.
+static std::string EncodeBase64Url(const uint8* input, int input_length) {
   std::string encoded_text;
   base::Base64Encode(
       std::string(reinterpret_cast<const char*>(input), input_length),
@@ -41,14 +46,21 @@
   if (found != std::string::npos)
     encoded_text.erase(found + 1);
 
+  // base64url encoding means the characters '-' and '_' must be used
+  // instead of '+' and '/', respectively.
+  base::ReplaceChars(encoded_text, kBase64Plus, kBase64UrlPlusReplacement,
+                     &encoded_text);
+  base::ReplaceChars(encoded_text, kBase64Slash, kBase64UrlSlashReplacement,
+                     &encoded_text);
+
   return encoded_text;
 }
 
-// Decodes an unpadded base64 string. Returns empty string on error.
-static std::string DecodeBase64(const std::string& encoded_text) {
-  // EME spec doesn't allow padding characters.
-  if (encoded_text.find_first_of(kBase64Padding) != std::string::npos) {
-    DVLOG(1) << "Padding characters not allowed: " << encoded_text;
+// Decodes a base64url string. Returns empty string on error.
+static std::string DecodeBase64Url(const std::string& encoded_text) {
+  // EME spec doesn't allow '+', '/', or padding characters.
+  if (encoded_text.find_first_of(kBase64UrlInvalid) != std::string::npos) {
+    DVLOG(1) << "Invalid base64url format: " << encoded_text;
     return std::string();
   }
 
@@ -59,6 +71,14 @@
   if (num_last_grouping_chars > 0)
     modified_text.append(4 - num_last_grouping_chars, kBase64Padding);
 
+  // base64url encoding means the characters '-' and '_' must be used
+  // instead of '+' and '/', respectively, so replace them before calling
+  // base::Base64Decode().
+  base::ReplaceChars(modified_text, kBase64UrlPlusReplacement, kBase64Plus,
+                     &modified_text);
+  base::ReplaceChars(modified_text, kBase64UrlSlashReplacement, kBase64Slash,
+                     &modified_text);
+
   std::string decoded_text;
   if (!base::Base64Decode(modified_text, &decoded_text)) {
     DVLOG(1) << "Base64 decoding failed on: " << modified_text;
@@ -71,8 +91,8 @@
 std::string GenerateJWKSet(const uint8* key, int key_length,
                            const uint8* key_id, int key_id_length) {
   // Both |key| and |key_id| need to be base64 encoded strings in the JWK.
-  std::string key_base64 = EncodeBase64(key, key_length);
-  std::string key_id_base64 = EncodeBase64(key_id, key_id_length);
+  std::string key_base64 = EncodeBase64Url(key, key_length);
+  std::string key_id_base64 = EncodeBase64Url(key_id, key_id_length);
 
   // Create the JWK, and wrap it into a JWK Set.
   scoped_ptr<base::DictionaryValue> jwk(new base::DictionaryValue());
@@ -121,13 +141,13 @@
   }
 
   // Key ID and key are base64-encoded strings, so decode them.
-  std::string raw_key_id = DecodeBase64(encoded_key_id);
+  std::string raw_key_id = DecodeBase64Url(encoded_key_id);
   if (raw_key_id.empty()) {
     DVLOG(1) << "Invalid '" << kKeyIdTag << "' value: " << encoded_key_id;
     return false;
   }
 
-  std::string raw_key = DecodeBase64(encoded_key);
+  std::string raw_key = DecodeBase64Url(encoded_key);
   if (raw_key.empty()) {
     DVLOG(1) << "Invalid '" << kKeyTag << "' value: " << encoded_key;
     return false;
@@ -206,14 +226,14 @@
   return true;
 }
 
-void CreateLicenseRequest(const uint8* key_id,
-                          int key_id_length,
+void CreateLicenseRequest(const KeyIdList& key_ids,
                           MediaKeys::SessionType session_type,
                           std::vector<uint8>* license) {
   // Create the license request.
   scoped_ptr<base::DictionaryValue> request(new base::DictionaryValue());
   scoped_ptr<base::ListValue> list(new base::ListValue());
-  list->AppendString(EncodeBase64(key_id, key_id_length));
+  for (const auto& key_id : key_ids)
+    list->AppendString(EncodeBase64Url(&key_id[0], key_id.size()));
   request->Set(kKeyIdsTag, list.release());
 
   switch (session_type) {
@@ -275,7 +295,7 @@
     return false;
   }
 
-  std::string decoded_string = DecodeBase64(encoded_key);
+  std::string decoded_string = DecodeBase64Url(encoded_key);
   if (decoded_string.empty()) {
     DVLOG(1) << "Invalid '" << kKeyIdsTag << "' value: " << encoded_key;
     return false;
diff --git a/media/cdm/json_web_key.h b/media/cdm/json_web_key.h
index af028f2f..2691b43e 100644
--- a/media/cdm/json_web_key.h
+++ b/media/cdm/json_web_key.h
@@ -48,6 +48,9 @@
 // Ref: http://tools.ietf.org/html/draft-ietf-jose-json-web-key and:
 // http://tools.ietf.org/html/draft-jones-jose-json-private-and-symmetric-key
 
+// Vector of key IDs.
+typedef std::vector<std::vector<uint8>> KeyIdList;
+
 // Vector of [key_id, key_value] pairs. Values are raw binary data, stored in
 // strings for convenience.
 typedef std::pair<std::string, std::string> KeyIdAndKeyPair;
@@ -64,12 +67,9 @@
                                         KeyIdAndKeyPairs* keys,
                                         MediaKeys::SessionType* session_type);
 
-// Create a license request message for the |key_id| and |session_type|
-// specified. Currently ClearKey generates a message for each key individually,
-// so no need to take a list of |key_id|'s. |license| is updated to contain the
-// resulting JSON string.
-MEDIA_EXPORT void CreateLicenseRequest(const uint8* key_id,
-                                       int key_id_length,
+// Creates a license request message for the |key_ids| and |session_type|
+// specified. |license| is updated to contain the resulting JSON string.
+MEDIA_EXPORT void CreateLicenseRequest(const KeyIdList& key_ids,
                                        MediaKeys::SessionType session_type,
                                        std::vector<uint8>* license);
 
diff --git a/media/cdm/json_web_key_unittest.cc b/media/cdm/json_web_key_unittest.cc
index 57a57c29..07d28c0 100644
--- a/media/cdm/json_web_key_unittest.cc
+++ b/media/cdm/json_web_key_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "media/cdm/json_web_key.h"
 
+#include "base/base64.h"
 #include "base/logging.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -44,7 +45,9 @@
                               MediaKeys::SessionType session_type,
                               const std::string& expected_result) {
     std::vector<uint8> result;
-    CreateLicenseRequest(key_id, key_id_length, session_type, &result);
+    KeyIdList key_ids;
+    key_ids.push_back(std::vector<uint8>(key_id, key_id + key_id_length));
+    CreateLicenseRequest(key_ids, session_type, &result);
     std::string s(result.begin(), result.end());
     EXPECT_EQ(expected_result, s);
   }
@@ -125,7 +128,7 @@
       "      \"kty\": \"oct\","
       "      \"alg\": \"A128KW\","
       "      \"kid\": \"JCUmJygpKissLS4vMA\","
-      "      \"k\":\"MTIzNDU2Nzg5Ojs8PT4/QA\""
+      "      \"k\":\"MTIzNDU2Nzg5Ojs8PT4_QA\""
       "    }"
       "  ]"
       "}";
@@ -152,7 +155,7 @@
       "      \"kty\": \"oct\","
       "      \"alg\": \"A128KW\","
       "      \"kid\": \"JCUmJygpKissLS4vMA\","
-      "      \"k\":\"MTIzNDU2Nzg5Ojs8PT4/QA\""
+      "      \"k\":\"MTIzNDU2Nzg5Ojs8PT4_QA\""
       "    }"
       "  ]"
       "}";
@@ -471,5 +474,50 @@
   ExtractKeyFromLicenseAndExpect("{\"kids\":[\"!@#$%^&*()\"]}", false, NULL, 0);
 }
 
+TEST_F(JSONWebKeyTest, Base64UrlEncoding) {
+  const uint8 data1[] = { 0xfb, 0xfd, 0xfb, 0xfd, 0xfb, 0xfd, 0xfb };
+
+  // Verify that |data1| contains invalid base64url characters '+' and '/'
+  // and is padded with = when converted to base64.
+  std::string encoded_text;
+  base::Base64Encode(
+      std::string(reinterpret_cast<const char*>(&data1[0]), arraysize(data1)),
+      &encoded_text);
+  EXPECT_EQ(encoded_text, "+/37/fv9+w==");
+  EXPECT_NE(encoded_text.find('+'), std::string::npos);
+  EXPECT_NE(encoded_text.find('/'), std::string::npos);
+  EXPECT_NE(encoded_text.find('='), std::string::npos);
+
+  // base64url characters '-' and '_' not in base64 encoding.
+  EXPECT_EQ(encoded_text.find('-'), std::string::npos);
+  EXPECT_EQ(encoded_text.find('_'), std::string::npos);
+
+  CreateLicenseAndExpect(data1, arraysize(data1), MediaKeys::TEMPORARY_SESSION,
+                         "{\"kids\":[\"-_37_fv9-w\"],\"type\":\"temporary\"}");
+
+  ExtractKeyFromLicenseAndExpect(
+      "{\"kids\":[\"-_37_fv9-w\"],\"type\":\"temporary\"}", true, data1,
+      arraysize(data1));
+}
+
+TEST_F(JSONWebKeyTest, MultipleKeys) {
+  const uint8 data1[] = { 0x01, 0x02 };
+  const uint8 data2[] = { 0x01, 0x02, 0x03, 0x04 };
+  const uint8 data3[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+                          0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10 };
+
+  std::vector<uint8> result;
+  KeyIdList key_ids;
+  key_ids.push_back(std::vector<uint8>(data1, data1 + arraysize(data1)));
+  key_ids.push_back(std::vector<uint8>(data2, data2 + arraysize(data2)));
+  key_ids.push_back(std::vector<uint8>(data3, data3 + arraysize(data3)));
+  CreateLicenseRequest(key_ids, MediaKeys::TEMPORARY_SESSION, &result);
+  std::string s(result.begin(), result.end());
+  EXPECT_EQ(
+      "{\"kids\":[\"AQI\",\"AQIDBA\",\"AQIDBAUGBwgJCgsMDQ4PEA\"],\"type\":"
+      "\"temporary\"}",
+      s);
+}
+
 }  // namespace media
 
diff --git a/media/cdm/key_system_names.cc b/media/cdm/key_system_names.cc
index 32b7475..f0abaf05 100644
--- a/media/cdm/key_system_names.cc
+++ b/media/cdm/key_system_names.cc
@@ -11,8 +11,8 @@
 const char kClearKey[] = "org.w3.clearkey";
 const char kExternalClearKey[] = "org.chromium.externalclearkey";
 
-static bool IsParentKeySystemOf(const std::string& parent_key_system,
-                                const std::string& key_system) {
+bool IsParentKeySystemOf(const std::string& parent_key_system,
+                         const std::string& key_system) {
   std::string prefix = parent_key_system + '.';
   return key_system.substr(0, prefix.size()) == prefix;
 }
diff --git a/media/cdm/key_system_names.h b/media/cdm/key_system_names.h
index c1818490..94c84da 100644
--- a/media/cdm/key_system_names.h
+++ b/media/cdm/key_system_names.h
@@ -24,6 +24,10 @@
   return key_system == kClearKey;
 }
 
+// Returns true if |key_system| is (reverse) sub-domain of |parent_key_system|.
+MEDIA_EXPORT bool IsParentKeySystemOf(const std::string& parent_key_system,
+                                      const std::string& key_system);
+
 // Returns true if |key_system| is External Clear Key, false otherwise.
 MEDIA_EXPORT bool IsExternalClearKey(const std::string& key_system);
 
diff --git a/media/cdm/ppapi/BUILD.gn b/media/cdm/ppapi/BUILD.gn
index 69369e2..9a85bd3 100644
--- a/media/cdm/ppapi/BUILD.gn
+++ b/media/cdm/ppapi/BUILD.gn
@@ -21,6 +21,9 @@
     "external_clear_key/clear_key_cdm_common.h",
   ]
 
+  # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+  configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
+
   defines = [ "CDM_IMPLEMENTATION" ]
 
   deps = [
@@ -41,11 +44,6 @@
     deps += [ "//third_party/ffmpeg" ]
   }
 
-  if (is_win) {
-    # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
-    cflags = [ "/wd4267" ]
-  }
-
   # TODO(GYP) on Mac: 'DYLIB_INSTALL_NAME_BASE': '@loader_path',
 }
 
diff --git a/media/filters/chunk_demuxer.cc b/media/filters/chunk_demuxer.cc
index 21579f16..40e7ed29 100644
--- a/media/filters/chunk_demuxer.cc
+++ b/media/filters/chunk_demuxer.cc
@@ -195,8 +195,7 @@
                     const StreamParser::BufferQueue& video_buffers,
                     const StreamParser::TextBufferQueueMap& text_map);
 
-  void OnSourceInitDone(bool success,
-                        const StreamParser::InitParameters& params);
+  void OnSourceInitDone(const StreamParser::InitParameters& params);
 
   CreateDemuxerStreamCB create_demuxer_stream_cb_;
   NewTextTrackCB new_text_track_cb_;
@@ -788,10 +787,9 @@
   return true;
 }
 
-void SourceState::OnSourceInitDone(bool success,
-                                   const StreamParser::InitParameters& params) {
+void SourceState::OnSourceInitDone(const StreamParser::InitParameters& params) {
   auto_update_timestamp_offset_ = params.auto_update_timestamp_offset;
-  base::ResetAndReturn(&init_cb_).Run(success, params);
+  base::ResetAndReturn(&init_cb_).Run(params);
 }
 
 ChunkDemuxerStream::ChunkDemuxerStream(Type type,
@@ -1644,13 +1642,11 @@
 }
 
 void ChunkDemuxer::OnSourceInitDone(
-    bool success,
     const StreamParser::InitParameters& params) {
-  DVLOG(1) << "OnSourceInitDone(" << success << ", "
-           << params.duration.InSecondsF() << ")";
+  DVLOG(1) << "OnSourceInitDone(" << params.duration.InSecondsF() << ")";
   lock_.AssertAcquired();
   DCHECK_EQ(state_, INITIALIZING);
-  if (!success || (!audio_ && !video_)) {
+  if (!audio_ && !video_) {
     ReportError_Locked(DEMUXER_ERROR_COULD_NOT_OPEN);
     return;
   }
diff --git a/media/filters/chunk_demuxer.h b/media/filters/chunk_demuxer.h
index 6e8bdf9..c924683 100644
--- a/media/filters/chunk_demuxer.h
+++ b/media/filters/chunk_demuxer.h
@@ -309,8 +309,7 @@
   bool CanEndOfStream_Locked() const;
 
   // SourceState callbacks.
-  void OnSourceInitDone(bool success,
-                        const StreamParser::InitParameters& params);
+  void OnSourceInitDone(const StreamParser::InitParameters& params);
 
   // Creates a DemuxerStream for the specified |type|.
   // Returns a new ChunkDemuxerStream instance if a stream of this type
diff --git a/media/filters/skcanvas_video_renderer.cc b/media/filters/skcanvas_video_renderer.cc
index b505b60..4229bc3 100644
--- a/media/filters/skcanvas_video_renderer.cc
+++ b/media/filters/skcanvas_video_renderer.cc
@@ -186,19 +186,17 @@
     return true;
   }
 
-  bool onGetPixels(const SkImageInfo& info,
-                   void* pixels,
-                   size_t row_bytes,
-                   SkPMColor ctable[],
-                   int* ctable_count) override {
+  Result onGetPixels(const SkImageInfo& info,
+                     void* pixels,
+                     size_t row_bytes,
+                     SkPMColor ctable[],
+                     int* ctable_count) override {
     if (!frame_.get())
-      return false;
-    if (!pixels)
-      return false;
+      return kInvalidInput;
     // If skia couldn't do the YUV conversion on GPU, we will on CPU.
     SkCanvasVideoRenderer::ConvertVideoFrameToRGBPixels(
         frame_, pixels, row_bytes);
-    return true;
+    return kSuccess;
   }
 
   bool onGetYUV8Planes(SkISize sizes[3],
diff --git a/media/formats/common/stream_parser_test_base.cc b/media/formats/common/stream_parser_test_base.cc
index 41892d1..3a8b5f21 100644
--- a/media/formats/common/stream_parser_test_base.cc
+++ b/media/formats/common/stream_parser_test_base.cc
@@ -73,11 +73,9 @@
 }
 
 void StreamParserTestBase::OnInitDone(
-    bool success,
     const StreamParser::InitParameters& params) {
   EXPECT_TRUE(params.auto_update_timestamp_offset);
-  DVLOG(1) << __FUNCTION__ << "(" << success << ", "
-           << params.duration.InMilliseconds() << ", "
+  DVLOG(1) << __FUNCTION__ << "(" << params.duration.InMilliseconds() << ", "
            << params.auto_update_timestamp_offset << ")";
 }
 
diff --git a/media/formats/common/stream_parser_test_base.h b/media/formats/common/stream_parser_test_base.h
index eb31562..1e3cd73 100644
--- a/media/formats/common/stream_parser_test_base.h
+++ b/media/formats/common/stream_parser_test_base.h
@@ -50,7 +50,7 @@
 
  private:
   bool AppendDataInPieces(const uint8* data, size_t length, size_t piece_size);
-  void OnInitDone(bool success, const StreamParser::InitParameters& params);
+  void OnInitDone(const StreamParser::InitParameters& params);
   bool OnNewConfig(const AudioDecoderConfig& audio_config,
                    const VideoDecoderConfig& video_config,
                    const StreamParser::TextTrackConfigMap& text_config);
diff --git a/media/formats/mp2t/mp2t_stream_parser.cc b/media/formats/mp2t/mp2t_stream_parser.cc
index 5b5bca4..562fbbe5 100644
--- a/media/formats/mp2t/mp2t_stream_parser.cc
+++ b/media/formats/mp2t/mp2t_stream_parser.cc
@@ -512,8 +512,7 @@
 
   // For Mpeg2 TS, the duration is not known.
   DVLOG(1) << "Mpeg2TS stream parser initialization done";
-  base::ResetAndReturn(&init_cb_)
-      .Run(true, InitParameters(kInfiniteDuration()));
+  base::ResetAndReturn(&init_cb_).Run(InitParameters(kInfiniteDuration()));
   is_initialized_ = true;
 
   return true;
diff --git a/media/formats/mp2t/mp2t_stream_parser_unittest.cc b/media/formats/mp2t/mp2t_stream_parser_unittest.cc
index bca3831..866c25e 100644
--- a/media/formats/mp2t/mp2t_stream_parser_unittest.cc
+++ b/media/formats/mp2t/mp2t_stream_parser_unittest.cc
@@ -101,10 +101,8 @@
     return true;
   }
 
-  void OnInit(bool init_ok,
-              const StreamParser::InitParameters& params) {
-    DVLOG(1) << "OnInit: ok=" << init_ok
-             << ", dur=" << params.duration.InMilliseconds()
+  void OnInit(const StreamParser::InitParameters& params) {
+    DVLOG(1) << "OnInit: dur=" << params.duration.InMilliseconds()
              << ", autoTimestampOffset=" << params.auto_update_timestamp_offset;
   }
 
diff --git a/media/formats/mp4/mp4_stream_parser.cc b/media/formats/mp4/mp4_stream_parser.cc
index d4f63cb..e6ad09d9 100644
--- a/media/formats/mp4/mp4_stream_parser.cc
+++ b/media/formats/mp4/mp4_stream_parser.cc
@@ -315,7 +315,7 @@
   }
 
   if (!init_cb_.is_null())
-    base::ResetAndReturn(&init_cb_).Run(true, params);
+    base::ResetAndReturn(&init_cb_).Run(params);
 
   if (!moov_->pssh.empty())
     OnEncryptedMediaInitData(moov_->pssh);
diff --git a/media/formats/mp4/mp4_stream_parser_unittest.cc b/media/formats/mp4/mp4_stream_parser_unittest.cc
index 5bcb7ad..c82d67b 100644
--- a/media/formats/mp4/mp4_stream_parser_unittest.cc
+++ b/media/formats/mp4/mp4_stream_parser_unittest.cc
@@ -60,9 +60,8 @@
     return true;
   }
 
-  void InitF(bool init_ok, const StreamParser::InitParameters& params) {
-    DVLOG(1) << "InitF: ok=" << init_ok
-             << ", dur=" << params.duration.InMilliseconds()
+  void InitF(const StreamParser::InitParameters& params) {
+    DVLOG(1) << "InitF: dur=" << params.duration.InMilliseconds()
              << ", autoTimestampOffset=" << params.auto_update_timestamp_offset;
   }
 
diff --git a/media/formats/mpeg/mpeg_audio_stream_parser_base.cc b/media/formats/mpeg/mpeg_audio_stream_parser_base.cc
index a643194..2a5895f 100644
--- a/media/formats/mpeg/mpeg_audio_stream_parser_base.cc
+++ b/media/formats/mpeg/mpeg_audio_stream_parser_base.cc
@@ -221,16 +221,14 @@
     timestamp_helper_->SetBaseTimestamp(base_timestamp);
 
     VideoDecoderConfig video_config;
-    bool success = config_cb_.Run(config_, video_config, TextTrackConfigMap());
+    if (!config_cb_.Run(config_, video_config, TextTrackConfigMap()))
+      return -1;
 
     if (!init_cb_.is_null()) {
       InitParameters params(kInfiniteDuration());
       params.auto_update_timestamp_offset = true;
-      base::ResetAndReturn(&init_cb_).Run(success, params);
+      base::ResetAndReturn(&init_cb_).Run(params);
     }
-
-    if (!success)
-      return -1;
   }
 
   if (metadata_frame)
diff --git a/media/formats/webm/webm_stream_parser.cc b/media/formats/webm/webm_stream_parser.cc
index f77dd031..b81aac4 100644
--- a/media/formats/webm/webm_stream_parser.cc
+++ b/media/formats/webm/webm_stream_parser.cc
@@ -244,7 +244,7 @@
       log_cb_));
 
   if (!init_cb_.is_null())
-    base::ResetAndReturn(&init_cb_).Run(true, params);
+    base::ResetAndReturn(&init_cb_).Run(params);
 
   return bytes_parsed;
 }
diff --git a/media/media.gyp b/media/media.gyp
index 0a3f4a4..3e65a43 100644
--- a/media/media.gyp
+++ b/media/media.gyp
@@ -144,8 +144,8 @@
         'audio/fake_audio_consumer.h',
         'audio/fake_audio_input_stream.cc',
         'audio/fake_audio_input_stream.h',
-        'audio/fake_audio_log_factory.h',
         'audio/fake_audio_log_factory.cc',
+        'audio/fake_audio_log_factory.h',
         'audio/fake_audio_manager.cc',
         'audio/fake_audio_manager.h',
         'audio/fake_audio_output_stream.cc',
@@ -211,10 +211,10 @@
         'base/audio_block_fifo.h',
         'base/audio_buffer.cc',
         'base/audio_buffer.h',
-        'base/audio_buffer_queue.cc',
-        'base/audio_buffer_queue.h',
         'base/audio_buffer_converter.cc',
         'base/audio_buffer_converter.h',
+        'base/audio_buffer_queue.cc',
+        'base/audio_buffer_queue.h',
         'base/audio_capturer_source.h',
         'base/audio_converter.cc',
         'base/audio_converter.h',
@@ -298,10 +298,10 @@
         'base/eme_constants.h',
         'base/key_system_info.cc',
         'base/key_system_info.h',
-        'base/key_systems_support_uma.cc',
-        'base/key_systems_support_uma.h',
         'base/key_systems.cc',
         'base/key_systems.h',
+        'base/key_systems_support_uma.cc',
+        'base/key_systems_support_uma.h',
         'base/keyboard_event_counter.cc',
         'base/keyboard_event_counter.h',
         'base/mac/avfoundation_glue.h',
@@ -309,10 +309,10 @@
         'base/mac/coremedia_glue.h',
         'base/mac/coremedia_glue.mm',
         'base/mac/corevideo_glue.h',
+        'base/mac/video_frame_mac.cc',
+        'base/mac/video_frame_mac.h',
         'base/mac/videotoolbox_glue.h',
         'base/mac/videotoolbox_glue.mm',
-        'base/mac/video_frame_mac.h',
-        'base/mac/video_frame_mac.cc',
         'base/media.cc',
         'base/media.h',
         'base/media_client.cc',
@@ -324,9 +324,9 @@
         'base/media_log.cc',
         'base/media_log.h',
         'base/media_log_event.h',
-        'base/media_posix.cc',
         'base/media_permission.cc',
         'base/media_permission.h',
+        'base/media_posix.cc',
         'base/media_switches.cc',
         'base/media_switches.h',
         'base/media_win.cc',
@@ -392,12 +392,14 @@
         'base/video_rotation.h',
         'base/video_util.cc',
         'base/video_util.h',
-        'base/yuv_convert.cc',
-        'base/yuv_convert.h',
         'base/wall_clock_time_source.cc',
         'base/wall_clock_time_source.h',
+        'base/yuv_convert.cc',
+        'base/yuv_convert.h',
         'cdm/aes_decryptor.cc',
         'cdm/aes_decryptor.h',
+        'cdm/cenc_utils.cc',
+        'cdm/cenc_utils.h',
         'cdm/default_cdm_factory.cc',
         'cdm/default_cdm_factory.h',
         'cdm/json_web_key.cc',
@@ -436,10 +438,10 @@
         'filters/decrypting_demuxer_stream.h',
         'filters/decrypting_video_decoder.cc',
         'filters/decrypting_video_decoder.h',
-        'filters/default_renderer_factory.cc',
-        'filters/default_renderer_factory.h',
         'filters/default_media_permission.cc',
         'filters/default_media_permission.h',
+        'filters/default_renderer_factory.cc',
+        'filters/default_renderer_factory.h',
         'filters/ffmpeg_audio_decoder.cc',
         'filters/ffmpeg_audio_decoder.h',
         'filters/ffmpeg_bitstream_converter.h',
@@ -493,6 +495,31 @@
         'filters/webvtt_util.h',
         'filters/wsola_internals.cc',
         'filters/wsola_internals.h',
+        'formats/common/offset_byte_queue.cc',
+        'formats/common/offset_byte_queue.h',
+        'formats/webm/webm_audio_client.cc',
+        'formats/webm/webm_audio_client.h',
+        'formats/webm/webm_cluster_parser.cc',
+        'formats/webm/webm_cluster_parser.h',
+        'formats/webm/webm_constants.cc',
+        'formats/webm/webm_constants.h',
+        'formats/webm/webm_content_encodings.cc',
+        'formats/webm/webm_content_encodings.h',
+        'formats/webm/webm_content_encodings_client.cc',
+        'formats/webm/webm_content_encodings_client.h',
+        'formats/webm/webm_crypto_helpers.cc',
+        'formats/webm/webm_crypto_helpers.h',
+        'formats/webm/webm_info_parser.cc',
+        'formats/webm/webm_info_parser.h',
+        'formats/webm/webm_parser.cc',
+        'formats/webm/webm_parser.h',
+        'formats/webm/webm_stream_parser.cc',
+        'formats/webm/webm_stream_parser.h',
+        'formats/webm/webm_tracks_parser.cc',
+        'formats/webm/webm_tracks_parser.h',
+        'formats/webm/webm_video_client.cc',
+        'formats/webm/webm_video_client.h',
+        'formats/webm/webm_webvtt_parser.cc',
         'midi/midi_manager.cc',
         'midi/midi_manager.h',
         'midi/midi_manager_alsa.cc',
@@ -530,18 +557,18 @@
         'video/capture/android/video_capture_device_factory_android.h',
         'video/capture/fake_video_capture_device.cc',
         'video/capture/fake_video_capture_device.h',
-        'video/capture/fake_video_capture_device_factory.h',
         'video/capture/fake_video_capture_device_factory.cc',
+        'video/capture/fake_video_capture_device_factory.h',
         'video/capture/file_video_capture_device.cc',
         'video/capture/file_video_capture_device.h',
-        'video/capture/file_video_capture_device_factory.h',
         'video/capture/file_video_capture_device_factory.cc',
+        'video/capture/file_video_capture_device_factory.h',
+        'video/capture/linux/video_capture_device_chromeos.cc',
+        'video/capture/linux/video_capture_device_chromeos.h',
         'video/capture/linux/video_capture_device_factory_linux.cc',
         'video/capture/linux/video_capture_device_factory_linux.h',
         'video/capture/linux/video_capture_device_linux.cc',
         'video/capture/linux/video_capture_device_linux.h',
-        'video/capture/linux/video_capture_device_chromeos.cc',
-        'video/capture/linux/video_capture_device_chromeos.h',
         'video/capture/mac/platform_video_capturing_mac.h',
         'video/capture/mac/video_capture_device_avfoundation_mac.h',
         'video/capture/mac/video_capture_device_avfoundation_mac.mm',
@@ -588,31 +615,6 @@
         'video/video_decode_accelerator.h',
         'video/video_encode_accelerator.cc',
         'video/video_encode_accelerator.h',
-        'formats/common/offset_byte_queue.cc',
-        'formats/common/offset_byte_queue.h',
-        'formats/webm/webm_audio_client.cc',
-        'formats/webm/webm_audio_client.h',
-        'formats/webm/webm_cluster_parser.cc',
-        'formats/webm/webm_cluster_parser.h',
-        'formats/webm/webm_constants.cc',
-        'formats/webm/webm_constants.h',
-        'formats/webm/webm_content_encodings.cc',
-        'formats/webm/webm_content_encodings.h',
-        'formats/webm/webm_content_encodings_client.cc',
-        'formats/webm/webm_content_encodings_client.h',
-        'formats/webm/webm_crypto_helpers.cc',
-        'formats/webm/webm_crypto_helpers.h',
-        'formats/webm/webm_info_parser.cc',
-        'formats/webm/webm_info_parser.h',
-        'formats/webm/webm_parser.cc',
-        'formats/webm/webm_parser.h',
-        'formats/webm/webm_stream_parser.cc',
-        'formats/webm/webm_stream_parser.h',
-        'formats/webm/webm_tracks_parser.cc',
-        'formats/webm/webm_tracks_parser.h',
-        'formats/webm/webm_video_client.cc',
-        'formats/webm/webm_video_client.h',
-        'formats/webm/webm_webvtt_parser.cc',
         'formats/webm/webm_webvtt_parser.h'
       ],
       'direct_dependent_settings': {
@@ -1050,10 +1052,10 @@
             'formats/mpeg/adts_constants.h',
             'formats/mpeg/adts_stream_parser.cc',
             'formats/mpeg/adts_stream_parser.h',
-            'formats/mpeg/mpeg_audio_stream_parser_base.cc',
-            'formats/mpeg/mpeg_audio_stream_parser_base.h',
             'formats/mpeg/mpeg1_audio_stream_parser.cc',
             'formats/mpeg/mpeg1_audio_stream_parser.h',
+            'formats/mpeg/mpeg_audio_stream_parser_base.cc',
+            'formats/mpeg/mpeg_audio_stream_parser_base.h',
           ],
         }],
         ['target_arch=="ia32" or target_arch=="x64"', {
@@ -1073,8 +1075,8 @@
         }],
         ['use_low_memory_buffer==1', {
           'sources': [
-            'filters/source_buffer_platform_lowmem.cc',
             'filters/source_buffer_platform.h',
+            'filters/source_buffer_platform_lowmem.cc',
           ]
         }, {  # 'use_low_memory_buffer==0'
           'sources': [
@@ -1121,8 +1123,8 @@
         '../url/url.gyp:url_lib',
       ],
       'sources': [
-        'audio/android/audio_android_unittest.cc',
         'audio/alsa/alsa_output_unittest.cc',
+        'audio/android/audio_android_unittest.cc',
         'audio/audio_input_controller_unittest.cc',
         'audio/audio_input_unittest.cc',
         'audio/audio_input_volume_unittest.cc',
@@ -1155,8 +1157,8 @@
         'base/android/media_source_player_unittest.cc',
         'base/audio_block_fifo_unittest.cc',
         'base/audio_buffer_converter_unittest.cc',
-        'base/audio_buffer_unittest.cc',
         'base/audio_buffer_queue_unittest.cc',
+        'base/audio_buffer_unittest.cc',
         'base/audio_bus_unittest.cc',
         'base/audio_converter_unittest.cc',
         'base/audio_discard_helper_unittest.cc',
@@ -1189,8 +1191,8 @@
         'base/pipeline_unittest.cc',
         'base/ranges_unittest.cc',
         'base/run_all_unittests.cc',
-        'base/serial_runner_unittest.cc',
         'base/seekable_buffer_unittest.cc',
+        'base/serial_runner_unittest.cc',
         'base/sinc_resampler_unittest.cc',
         'base/stream_parser_unittest.cc',
         'base/text_ranges_unittest.cc',
@@ -1199,17 +1201,18 @@
         'base/user_input_monitor_unittest.cc',
         'base/vector_math_testing.h',
         'base/vector_math_unittest.cc',
-        'base/video_frame_unittest.cc',
         'base/video_frame_pool_unittest.cc',
+        'base/video_frame_unittest.cc',
         'base/video_util_unittest.cc',
         'base/wall_clock_time_source_unittest.cc',
         'base/yuv_convert_unittest.cc',
         'cdm/aes_decryptor_unittest.cc',
+        'cdm/cenc_utils_unittest.cc',
         'cdm/json_web_key_unittest.cc',
         'ffmpeg/ffmpeg_common_unittest.cc',
         'filters/audio_clock_unittest.cc',
-        'filters/audio_decoder_unittest.cc',
         'filters/audio_decoder_selector_unittest.cc',
+        'filters/audio_decoder_unittest.cc',
         'filters/audio_file_reader_unittest.cc',
         'filters/audio_renderer_algorithm_unittest.cc',
         'filters/audio_renderer_impl_unittest.cc',
@@ -1243,16 +1246,6 @@
         'filters/video_renderer_impl_unittest.cc',
         'filters/vp8_bool_decoder_unittest.cc',
         'filters/vp8_parser_unittest.cc',
-        'midi/midi_manager_unittest.cc',
-        'midi/midi_manager_usb_unittest.cc',
-        'midi/midi_message_queue_unittest.cc',
-        'midi/midi_message_util_unittest.cc',
-        'midi/usb_midi_descriptor_parser_unittest.cc',
-        'midi/usb_midi_input_stream_unittest.cc',
-        'midi/usb_midi_output_stream_unittest.cc',
-        'video/capture/fake_video_capture_device_unittest.cc',
-        'video/capture/video_capture_device_unittest.cc',
-        'video/h264_poc_unittest.cc',
         'formats/common/offset_byte_queue_unittest.cc',
         'formats/webm/cluster_builder.cc',
         'formats/webm/cluster_builder.h',
@@ -1265,8 +1258,18 @@
         'formats/webm/webm_parser_unittest.cc',
         'formats/webm/webm_tracks_parser_unittest.cc',
         'formats/webm/webm_webvtt_parser_unittest.cc',
+        'midi/midi_manager_unittest.cc',
+        'midi/midi_manager_usb_unittest.cc',
+        'midi/midi_message_queue_unittest.cc',
+        'midi/midi_message_util_unittest.cc',
+        'midi/usb_midi_descriptor_parser_unittest.cc',
+        'midi/usb_midi_input_stream_unittest.cc',
+        'midi/usb_midi_output_stream_unittest.cc',
         'test/pipeline_integration_test.cc',
         'test/pipeline_integration_test_base.cc',
+        'video/capture/fake_video_capture_device_unittest.cc',
+        'video/capture/video_capture_device_unittest.cc',
+        'video/h264_poc_unittest.cc',
       ],
       'include_dirs': [
         # Needed by media_drm_bridge.cc.
@@ -1391,6 +1394,7 @@
         }],
         ['OS=="mac"', {
           'sources': [
+            'midi/midi_manager_mac_unittest.cc',
             'video/capture/mac/video_capture_device_factory_mac_unittest.mm',
           ]
         }],
@@ -1878,10 +1882,10 @@
             'base/mac/coremedia_glue.h',
             'base/mac/coremedia_glue.mm',
             'base/mac/corevideo_glue.h',
+            'base/mac/video_frame_mac.cc',
+            'base/mac/video_frame_mac.h',
             'base/mac/videotoolbox_glue.h',
             'base/mac/videotoolbox_glue.mm',
-            'base/mac/video_frame_mac.h',
-            'base/mac/video_frame_mac.cc',
             'base/video_frame.cc',
             'base/video_frame.h',
           ],
diff --git a/media/midi/midi_manager.cc b/media/midi/midi_manager.cc
index 9e1f1e2..f7de36f 100644
--- a/media/midi/midi_manager.cc
+++ b/media/midi/midi_manager.cc
@@ -130,6 +130,22 @@
     client->AddOutputPort(info);
 }
 
+void MidiManager::SetInputPortState(uint32 port_index, MidiPortState state) {
+  base::AutoLock auto_lock(lock_);
+  DCHECK_LT(port_index, input_ports_.size());
+  input_ports_[port_index].state = state;
+  for (auto client : clients_)
+    client->SetInputPortState(port_index, state);
+}
+
+void MidiManager::SetOutputPortState(uint32 port_index, MidiPortState state) {
+  base::AutoLock auto_lock(lock_);
+  DCHECK_LT(port_index, output_ports_.size());
+  output_ports_[port_index].state = state;
+  for (auto client : clients_)
+    client->SetOutputPortState(port_index, state);
+}
+
 void MidiManager::ReceiveMidiData(
     uint32 port_index,
     const uint8* data,
diff --git a/media/midi/midi_manager.h b/media/midi/midi_manager.h
index d7e7e47a..a7c5262d 100644
--- a/media/midi/midi_manager.h
+++ b/media/midi/midi_manager.h
@@ -35,10 +35,10 @@
   virtual void AddInputPort(const MidiPortInfo& info) = 0;
   virtual void AddOutputPort(const MidiPortInfo& info) = 0;
 
-  // TODO(toyoshim): DisableInputPort(const MidiPortInfo& info) and
-  // DisableOutputPort(const MidiPortInfo& info) should be added.
-  // On DisableInputPort(), internal states, e.g. received_messages_queues in
-  // MidiHost, should be reset.
+  // SetInputPortState() and SetOutputPortState() are called to notify a known
+  // device gets disconnected, or connected again.
+  virtual void SetInputPortState(uint32 port_index, MidiPortState state) = 0;
+  virtual void SetOutputPortState(uint32 port_index, MidiPortState state) = 0;
 
   // CompleteStartSession() is called when platform dependent preparation is
   // finished.
@@ -122,6 +122,8 @@
 
   void AddInputPort(const MidiPortInfo& info);
   void AddOutputPort(const MidiPortInfo& info);
+  void SetInputPortState(uint32 port_index, MidiPortState state);
+  void SetOutputPortState(uint32 port_index, MidiPortState state);
 
   // Dispatches to all clients.
   // TODO(toyoshim): Fix the mac implementation to use
diff --git a/media/midi/midi_manager_alsa.cc b/media/midi/midi_manager_alsa.cc
index e4cadfb..9043f1f8c 100644
--- a/media/midi/midi_manager_alsa.cc
+++ b/media/midi/midi_manager_alsa.cc
@@ -252,7 +252,8 @@
             VLOG(1) << "snd_seq_subscribe_port fails: " << snd_strerror(err);
           } else {
             source_map_[AddrToInt(sender)] = current_input++;
-            AddInputPort(MidiPortInfo(id, manufacturer, name, version));
+            AddInputPort(MidiPortInfo(
+                id, manufacturer, name, version, MIDI_PORT_OPENED));
           }
         }
         if ((caps & kRequiredOutputPortCaps) == kRequiredOutputPortCaps) {
@@ -286,7 +287,8 @@
             snd_midi_event_new(kSendBufferSize, &encoder);
             encoders_.push_back(encoder);
             out_ports_.push_back(out_port);
-            AddOutputPort(MidiPortInfo(id, manufacturer, name, version));
+            AddOutputPort(MidiPortInfo(
+                id, manufacturer, name, version, MIDI_PORT_OPENED));
           }
         }
       }
diff --git a/media/midi/midi_manager_mac.cc b/media/midi/midi_manager_mac.cc
index e9d56fb..8a9e0607 100644
--- a/media/midi/midi_manager_mac.cc
+++ b/media/midi/midi_manager_mac.cc
@@ -4,6 +4,7 @@
 
 #include "media/midi/midi_manager_mac.h"
 
+#include <algorithm>
 #include <string>
 
 #include "base/bind.h"
@@ -25,8 +26,7 @@
 
 namespace {
 
-MidiPortInfo GetPortInfoFromEndpoint(
-    MIDIEndpointRef endpoint) {
+MidiPortInfo GetPortInfoFromEndpoint(MIDIEndpointRef endpoint) {
   SInt32 id_number = 0;
   MIDIObjectGetIntegerProperty(endpoint, kMIDIPropertyUniqueID, &id_number);
   string id = IntToString(id_number);
@@ -65,7 +65,8 @@
                   << result;
   }
 
-  return MidiPortInfo(id, manufacturer, name, version);
+  const MidiPortState state = MIDI_PORT_OPENED;
+  return MidiPortInfo(id, manufacturer, name, version, state);
 }
 
 double MIDITimeStampToSeconds(MIDITimeStamp timestamp) {
@@ -140,7 +141,8 @@
   // TODO(toyoshim): Set MIDINotifyProc to receive CoreMIDI event notifications.
   midi_client_ = 0;
   OSStatus result =
-      MIDIClientCreate(CFSTR("Chrome"), NULL, NULL, &midi_client_);
+      MIDIClientCreate(CFSTR("Chrome"), ReceiveMidiNotifyDispatch, this,
+                       &midi_client_);
 
   if (result != noErr)
     return CompleteInitialization(MIDI_INITIALIZATION_ERROR);
@@ -200,6 +202,61 @@
 }
 
 // static
+void MidiManagerMac::ReceiveMidiNotifyDispatch(const MIDINotification* message,
+                                               void* refcon) {
+  MidiManagerMac* manager = static_cast<MidiManagerMac*>(refcon);
+  manager->ReceiveMidiNotify(message);
+}
+
+void MidiManagerMac::ReceiveMidiNotify(const MIDINotification* message) {
+  DCHECK(client_thread_.message_loop_proxy()->BelongsToCurrentThread());
+
+  if (kMIDIMsgObjectAdded == message->messageID) {
+    const MIDIObjectAddRemoveNotification* notification =
+        reinterpret_cast<const MIDIObjectAddRemoveNotification*>(message);
+    MIDIEndpointRef endpoint =
+        static_cast<MIDIEndpointRef>(notification->child);
+    if (notification->childType == kMIDIObjectType_Source) {
+      SourceMap::iterator it = source_map_.find(endpoint);
+      if (it == source_map_.end()) {
+        uint32 index = source_map_.size();
+        source_map_[endpoint] = index;
+        MidiPortInfo info = GetPortInfoFromEndpoint(endpoint);
+        AddInputPort(info);
+      } else {
+        uint32 index = it->second;
+        SetInputPortState(index, MIDI_PORT_OPENED);
+      }
+    } else if (notification->childType == kMIDIObjectType_Destination) {
+      auto i = std::find(destinations_.begin(), destinations_.end(), endpoint);
+      if (i != destinations_.end()) {
+        SetOutputPortState(i - destinations_.begin(), MIDI_PORT_OPENED);
+      } else {
+        destinations_.push_back(endpoint);
+        MidiPortInfo info = GetPortInfoFromEndpoint(endpoint);
+        AddOutputPort(info);
+      }
+    }
+  } else if (kMIDIMsgObjectRemoved == message->messageID) {
+    const MIDIObjectAddRemoveNotification* notification =
+        reinterpret_cast<const MIDIObjectAddRemoveNotification*>(message);
+    MIDIEndpointRef endpoint =
+        static_cast<MIDIEndpointRef>(notification->child);
+    if (notification->childType == kMIDIObjectType_Source) {
+      SourceMap::iterator it = source_map_.find(endpoint);
+      if (it != source_map_.end()) {
+        uint32 index = it->second;
+        SetInputPortState(index, MIDI_PORT_DISCONNECTED);
+      }
+    } else if (notification->childType == kMIDIObjectType_Destination) {
+      auto i = std::find(destinations_.begin(), destinations_.end(), endpoint);
+      if (i != destinations_.end())
+        SetOutputPortState(i - destinations_.begin(), MIDI_PORT_DISCONNECTED);
+    }
+  }
+}
+
+// static
 void MidiManagerMac::ReadMidiDispatch(const MIDIPacketList* packet_list,
                                       void* read_proc_refcon,
                                       void* src_conn_refcon) {
diff --git a/media/midi/midi_manager_mac.h b/media/midi/midi_manager_mac.h
index f7179ec..e8e44748 100644
--- a/media/midi/midi_manager_mac.h
+++ b/media/midi/midi_manager_mac.h
@@ -42,13 +42,18 @@
   // StartInitialization().
   void InitializeCoreMIDI();
 
+  // CoreMIDI callback for MIDI notification.
+  // Receives MIDI related event notifications from CoreMIDI.
+  static void ReceiveMidiNotifyDispatch(const MIDINotification* message,
+                                        void* refcon);
+  void ReceiveMidiNotify(const MIDINotification* message);
+
   // CoreMIDI callback for MIDI data.
   // Each callback can contain multiple packets, each of which can contain
   // multiple MIDI messages.
-  static void ReadMidiDispatch(
-      const MIDIPacketList *pktlist,
-      void *read_proc_refcon,
-      void *src_conn_refcon);
+  static void ReadMidiDispatch(const MIDIPacketList* packet_list,
+                               void* read_proc_refcon,
+                               void* src_conn_refcon);
   virtual void ReadMidi(MIDIEndpointRef source, const MIDIPacketList *pktlist);
 
   // An internal callback that runs on MidiSendThread.
@@ -67,13 +72,13 @@
   MIDIPacketList* packet_list_;
   MIDIPacket* midi_packet_;
 
-  typedef std::map<MIDIEndpointRef, uint32> SourceMap;
-
   // Keeps track of the index (0-based) for each of our sources.
+  typedef std::map<MIDIEndpointRef, uint32> SourceMap;
   SourceMap source_map_;
 
   // Keeps track of all destinations.
-  std::vector<MIDIEndpointRef> destinations_;
+  typedef std::vector<MIDIEndpointRef> DestinationVector;
+  DestinationVector destinations_;
 
   // |client_thread_| is used to handle platform dependent operations.
   base::Thread client_thread_;
diff --git a/media/midi/midi_manager_mac_unittest.cc b/media/midi/midi_manager_mac_unittest.cc
new file mode 100644
index 0000000..e608d7c
--- /dev/null
+++ b/media/midi/midi_manager_mac_unittest.cc
@@ -0,0 +1,126 @@
+// 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 "media/midi/midi_manager_mac.h"
+
+#include <CoreMIDI/MIDIServices.h>
+
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace media {
+
+namespace {
+
+void Noop(const MIDIPacketList*, void*, void*) {}
+
+class FakeMidiManagerClient : public MidiManagerClient {
+ public:
+  FakeMidiManagerClient()
+      : result_(MIDI_NOT_SUPPORTED),
+        wait_for_result_(true),
+        wait_for_port_(true) {}
+
+  // MidiManagerClient implementation.
+  void AddInputPort(const MidiPortInfo& info) override {}
+  void AddOutputPort(const MidiPortInfo& info) override {
+    CHECK(!wait_for_result_);
+    info_ = info;
+    wait_for_port_ = false;
+  }
+  void SetInputPortState(uint32 port_index, MidiPortState state) override {}
+  void SetOutputPortState(uint32 port_index, MidiPortState state) override {}
+
+  void CompleteStartSession(MidiResult result) override {
+    EXPECT_TRUE(wait_for_result_);
+    result_ = result;
+    wait_for_result_ = false;
+  }
+
+  void ReceiveMidiData(uint32 port_index, const uint8* data, size_t size,
+                       double timestamp) override {}
+  void AccumulateMidiBytesSent(size_t size) override {}
+
+  MidiResult WaitForResult() {
+    while (wait_for_result_) {
+      base::RunLoop run_loop;
+      run_loop.RunUntilIdle();
+    }
+    return result_;
+  }
+  MidiPortInfo WaitForPort() {
+    while (wait_for_port_) {
+      base::RunLoop run_loop;
+      run_loop.RunUntilIdle();
+    }
+    return info_;
+  }
+
+ private:
+  MidiResult result_;
+  bool wait_for_result_;
+  MidiPortInfo info_;
+  bool wait_for_port_;
+
+  DISALLOW_COPY_AND_ASSIGN(FakeMidiManagerClient);
+};
+
+class MidiManagerMacTest : public ::testing::Test {
+ public:
+  MidiManagerMacTest()
+      : manager_(new MidiManagerMac),
+        message_loop_(new base::MessageLoop) {}
+
+ protected:
+  void StartSession(MidiManagerClient* client) {
+    manager_->StartSession(client);
+  }
+  void EndSession(MidiManagerClient* client) {
+    manager_->EndSession(client);
+  }
+
+ private:
+  scoped_ptr<MidiManager> manager_;
+  scoped_ptr<base::MessageLoop> message_loop_;
+
+  DISALLOW_COPY_AND_ASSIGN(MidiManagerMacTest);
+};
+
+
+TEST_F(MidiManagerMacTest, MidiNotification) {
+  scoped_ptr<FakeMidiManagerClient> client(new FakeMidiManagerClient);
+  StartSession(client.get());
+
+  MidiResult result = client->WaitForResult();
+  EXPECT_EQ(MIDI_OK, result);
+
+  // Create MIDIClient, and MIDIEndpoint as a MIDIDestination. This should
+  // notify MIDIManagerMac as a MIDIObjectAddRemoveNotification.
+  MIDIClientRef midi_client = 0;
+  OSStatus status = MIDIClientCreate(
+      CFSTR("MidiManagerMacTest"), nullptr, nullptr, &midi_client);
+  EXPECT_EQ(noErr, status);
+
+  MIDIEndpointRef ep = 0;
+  status = MIDIDestinationCreate(
+      midi_client, CFSTR("DestinationTest"), Noop, nullptr, &ep);
+  EXPECT_EQ(noErr, status);
+
+  // Wait until the created device is notified to MidiManagerMac.
+  MidiPortInfo info = client->WaitForPort();
+  EXPECT_EQ("DestinationTest", info.name);
+
+  EndSession(client.get());
+  if (ep)
+    MIDIEndpointDispose(ep);
+  if (midi_client)
+    MIDIClientDispose(midi_client);
+}
+
+}  // namespace
+
+}  // namespace media
diff --git a/media/midi/midi_manager_unittest.cc b/media/midi/midi_manager_unittest.cc
index 40d9152..680f9db5 100644
--- a/media/midi/midi_manager_unittest.cc
+++ b/media/midi/midi_manager_unittest.cc
@@ -62,6 +62,8 @@
   // MidiManagerClient implementation.
   void AddInputPort(const MidiPortInfo& info) override {}
   void AddOutputPort(const MidiPortInfo& info) override {}
+  void SetInputPortState(uint32 port_index, MidiPortState state) override {}
+  void SetOutputPortState(uint32 port_index, MidiPortState state) override {}
 
   void CompleteStartSession(MidiResult result) override {
     EXPECT_TRUE(wait_for_result_);
diff --git a/media/midi/midi_manager_usb.cc b/media/midi/midi_manager_usb.cc
index 63c241b9..67953e4 100644
--- a/media/midi/midi_manager_usb.cc
+++ b/media/midi/midi_manager_usb.cc
@@ -106,6 +106,7 @@
         // setting is sufficiently unique although there is no user-friendly
         // meaning.
         MidiPortInfo port;
+        port.state = MIDI_PORT_OPENED;
         port.id = base::StringPrintf("port-%ld-%ld",
                                      static_cast<long>(i),
                                      static_cast<long>(j));
@@ -115,6 +116,7 @@
         input_jacks.push_back(jacks[j]);
         // TODO(yhirano): Set appropriate properties.
         MidiPortInfo port;
+        port.state = MIDI_PORT_OPENED;
         port.id = base::StringPrintf("port-%ld-%ld",
                                      static_cast<long>(i),
                                      static_cast<long>(j));
diff --git a/media/midi/midi_manager_usb_unittest.cc b/media/midi/midi_manager_usb_unittest.cc
index 8552403..d59f8576 100644
--- a/media/midi/midi_manager_usb_unittest.cc
+++ b/media/midi/midi_manager_usb_unittest.cc
@@ -86,6 +86,10 @@
     output_ports_.push_back(info);
   }
 
+  void SetInputPortState(uint32 port_index, MidiPortState state) override {}
+
+  void SetOutputPortState(uint32 port_index, MidiPortState state) override {}
+
   void CompleteStartSession(MidiResult result) override {
     complete_start_session_ = true;
     result_ = result;
diff --git a/media/midi/midi_manager_win.cc b/media/midi/midi_manager_win.cc
index bc4b15d0..26657d6 100644
--- a/media/midi/midi_manager_win.cc
+++ b/media/midi/midi_manager_win.cc
@@ -515,7 +515,8 @@
         base::IntToString(static_cast<int>(device_id)),
         "",
         base::WideToUTF8(caps.szPname),
-        base::IntToString(static_cast<int>(caps.vDriverVersion)));
+        base::IntToString(static_cast<int>(caps.vDriverVersion)),
+        MIDI_PORT_OPENED);
     AddInputPort(info);
     in_device->set_port_index(inport_index++);
     in_devices_.push_back(in_device.release());
@@ -538,7 +539,8 @@
         base::IntToString(static_cast<int>(device_id)),
         "",
         base::WideToUTF8(caps.szPname),
-        base::IntToString(static_cast<int>(caps.vDriverVersion)));
+        base::IntToString(static_cast<int>(caps.vDriverVersion)),
+        MIDI_PORT_OPENED);
     AddOutputPort(info);
     out_devices_.push_back(out_port.release());
   }
diff --git a/media/midi/midi_port_info.cc b/media/midi/midi_port_info.cc
index 02b4aa9..020394e 100644
--- a/media/midi/midi_port_info.cc
+++ b/media/midi/midi_port_info.cc
@@ -11,11 +11,13 @@
 MidiPortInfo::MidiPortInfo(const std::string& in_id,
                            const std::string& in_manufacturer,
                            const std::string& in_name,
-                           const std::string& in_version)
+                           const std::string& in_version,
+                           MidiPortState in_state)
     : id(in_id),
       manufacturer(in_manufacturer),
       name(in_name),
-      version(in_version) {}
+      version(in_version),
+      state(in_state) {}
 
 MidiPortInfo::~MidiPortInfo() {}
 
@@ -23,6 +25,7 @@
     : id(info.id),
       manufacturer(info.manufacturer),
       name(info.name),
-      version(info.version) {}
+      version(info.version),
+      state(info.state) {}
 
 }  // namespace media
diff --git a/media/midi/midi_port_info.h b/media/midi/midi_port_info.h
index 1fe3bca..32b94b4 100644
--- a/media/midi/midi_port_info.h
+++ b/media/midi/midi_port_info.h
@@ -13,12 +13,20 @@
 
 namespace media {
 
+enum MidiPortState {
+  MIDI_PORT_DISCONNECTED,
+  MIDI_PORT_CONNECTED,
+  MIDI_PORT_OPENED,
+  MIDI_PORT_STATE_LAST = MIDI_PORT_OPENED,
+};
+
 struct MEDIA_EXPORT MidiPortInfo {
   MidiPortInfo();
   MidiPortInfo(const std::string& in_id,
                const std::string& in_manufacturer,
                const std::string& in_name,
-               const std::string& in_version);
+               const std::string& in_version,
+               MidiPortState in_state);
 
   MidiPortInfo(const MidiPortInfo& info);
   ~MidiPortInfo();
@@ -27,6 +35,7 @@
   std::string manufacturer;
   std::string name;
   std::string version;
+  MidiPortState state;
 };
 
 typedef std::vector<MidiPortInfo> MidiPortInfoList;
diff --git a/media/mojo/services/BUILD.gn b/media/mojo/services/BUILD.gn
index c66c649..30e49574 100644
--- a/media/mojo/services/BUILD.gn
+++ b/media/mojo/services/BUILD.gn
@@ -75,6 +75,8 @@
     "mojo_cdm_service.h",
     "mojo_type_trait.h",
   ]
+
+  configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
 }
 
 # media::Renderer implementation using mojo::MediaRenderer.
diff --git a/media/test/BUILD.gn b/media/test/BUILD.gn
index 1337e9e..60849c1e 100644
--- a/media/test/BUILD.gn
+++ b/media/test/BUILD.gn
@@ -33,6 +33,8 @@
       "pipeline_integration_test.cc",
     ]
 
+    configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
+
     deps = [
       ":pipeline_integration_test_base",
       "//base",
@@ -83,6 +85,8 @@
 
       defines = [ "MOJO_RENDERER" ]
 
+      configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
+
       deps = [
         ":pipeline_integration_test_base",
         "//base",
diff --git a/media/test/data/eme_player_js/utils.js b/media/test/data/eme_player_js/utils.js
index 9d7bb1b4..b492510 100644
--- a/media/test/data/eme_player_js/utils.js
+++ b/media/test/data/eme_player_js/utils.js
@@ -45,21 +45,21 @@
 Utils.createJWKData = function(keyId, key) {
   // JWK routines copied from third_party/WebKit/LayoutTests/media/
   //   encrypted-media/encrypted-media-utils.js
-  //
-  // Encodes data (Uint8Array) into base64 string without trailing '='.
-  // TODO(jrummell): Update once the EME spec is updated to say base64url
-  // encoding.
-  function base64Encode(data) {
+
+  // Encodes data (Uint8Array) into base64url string. There is no '=' padding,
+  // and the characters '-' and '_' must be used instead of '+' and '/',
+  // respectively.
+  function base64urlEncode(data) {
     var result = btoa(String.fromCharCode.apply(null, data));
-    return result.replace(/=+$/g, '');
+    return result.replace(/=+$/g, '').replace(/\+/g, "-").replace(/\//g, "_");
   }
 
   // Creates a JWK from raw key ID and key.
   function createJWK(keyId, key) {
     var jwk = '{"kty":"oct","alg":"A128KW","kid":"';
-    jwk += base64Encode(keyId);
+    jwk += base64urlEncode(keyId);
     jwk += '","k":"';
-    jwk += base64Encode(key);
+    jwk += base64urlEncode(key);
     jwk += '"}';
     return jwk;
   }
@@ -80,11 +80,9 @@
 };
 
 Utils.extractFirstLicenseKey = function(message) {
-  // Decodes data (Uint8Array) from base64 string.
-  // TODO(jrummell): Update once the EME spec is updated to say base64url
-  // encoding.
-  function base64Decode(data) {
-    return atob(data);
+  // Decodes data (Uint8Array) from base64url string.
+  function base64urlDecode(data) {
+    return atob(data.replace(/\-/g, "+").replace(/\_/g, "/"));
   }
 
   function convertToString(data) {
@@ -94,7 +92,7 @@
   try {
     var json = JSON.parse(convertToString(message));
     // Decode the first element of 'kids', return it as an Uint8Array.
-    return Utils.convertToUint8Array(base64Decode(json.kids[0]));
+    return Utils.convertToUint8Array(base64urlDecode(json.kids[0]));
   } catch (error) {
     // Not valid JSON, so return message untouched as Uint8Array.
     return Utils.convertToUint8Array(message);
diff --git a/media/test/pipeline_integration_test.cc b/media/test/pipeline_integration_test.cc
index d326a6c..9d263c1 100644
--- a/media/test/pipeline_integration_test.cc
+++ b/media/test/pipeline_integration_test.cc
@@ -56,7 +56,6 @@
 
 const char kSourceId[] = "SourceId";
 const char kCencInitDataType[] = "cenc";
-const uint8 kInitData[] = { 0x69, 0x6e, 0x69, 0x74 };
 
 const char kWebM[] = "video/webm; codecs=\"vp8,vorbis\"";
 const char kWebMVP9[] = "video/webm; codecs=\"vp9\"";
@@ -256,7 +255,7 @@
                 media::MediaKeys::Exception exception_code,
                 uint32 system_code,
                 const std::string& error_message) {
-    EXPECT_EQ(expected, REJECTED);
+    EXPECT_EQ(expected, REJECTED) << error_message;
   }
 
   scoped_ptr<SimpleCdmPromise> CreatePromise(PromiseResult expected) {
@@ -304,15 +303,26 @@
                                 const std::vector<uint8>& init_data,
                                 AesDecryptor* decryptor) override {
     if (current_session_id_.empty()) {
-      decryptor->CreateSessionAndGenerateRequest(
-          MediaKeys::TEMPORARY_SESSION, init_data_type, kInitData,
-          arraysize(kInitData), CreateSessionPromise(RESOLVED));
+      if (init_data_type == kCencInitDataType) {
+        // Since the 'cenc' files are not created with proper 'pssh' boxes,
+        // simply pretend that this is a webm file and pass the expected
+        // key ID as the init_data.
+        // http://crbug.com/460308
+        decryptor->CreateSessionAndGenerateRequest(
+            MediaKeys::TEMPORARY_SESSION, "webm", kKeyId, arraysize(kKeyId),
+            CreateSessionPromise(RESOLVED));
+      } else {
+        decryptor->CreateSessionAndGenerateRequest(
+            MediaKeys::TEMPORARY_SESSION, init_data_type,
+            vector_as_array(&init_data), init_data.size(),
+            CreateSessionPromise(RESOLVED));
+      }
       EXPECT_FALSE(current_session_id_.empty());
     }
 
-    // Clear Key really needs the key ID in |init_data|. For WebM, they are the
-    // same, but this is not the case for ISO CENC. Therefore, provide the
-    // correct key ID.
+    // Clear Key really needs the key ID from |init_data|. For WebM, they are
+    // the same, but this is not the case for ISO CENC (key ID embedded in a
+    // 'pssh' box). Therefore, provide the correct key ID.
     const uint8* key_id = init_data.empty() ? NULL : &init_data[0];
     size_t key_id_length = init_data.size();
     if (init_data_type == kCencInitDataType) {
@@ -350,15 +360,25 @@
     prev_init_data_ = init_data;
     ++num_distint_need_key_calls_;
 
-    decryptor->CreateSessionAndGenerateRequest(
-        MediaKeys::TEMPORARY_SESSION, init_data_type,
-        vector_as_array(&init_data), init_data.size(),
-        CreateSessionPromise(RESOLVED));
-
     std::vector<uint8> key_id;
     std::vector<uint8> key;
     EXPECT_TRUE(GetKeyAndKeyId(init_data, &key, &key_id));
 
+    if (init_data_type == kCencInitDataType) {
+      // Since the 'cenc' files are not created with proper 'pssh' boxes,
+      // simply pretend that this is a webm file and pass the expected
+      // key ID as the init_data.
+      // http://crbug.com/460308
+      decryptor->CreateSessionAndGenerateRequest(
+          MediaKeys::TEMPORARY_SESSION, "webm", vector_as_array(&key_id),
+          key_id.size(), CreateSessionPromise(RESOLVED));
+    } else {
+      decryptor->CreateSessionAndGenerateRequest(
+          MediaKeys::TEMPORARY_SESSION, init_data_type,
+          vector_as_array(&init_data), init_data.size(),
+          CreateSessionPromise(RESOLVED));
+    }
+
     // Convert key into a JSON structure and then add it.
     std::string jwk = GenerateJWKSet(vector_as_array(&key),
                                      key.size(),
diff --git a/media/video/capture/fake_video_capture_device_unittest.cc b/media/video/capture/fake_video_capture_device_unittest.cc
index e680b14..21ed8e54 100644
--- a/media/video/capture/fake_video_capture_device_unittest.cc
+++ b/media/video/capture/fake_video_capture_device_unittest.cc
@@ -26,30 +26,24 @@
   MOCK_METHOD2(ReserveOutputBuffer,
                scoped_refptr<Buffer>(VideoFrame::Format format,
                                      const gfx::Size& dimensions));
-  MOCK_METHOD0(OnErr, void());
+  MOCK_METHOD4(OnIncomingCapturedVideoFrame,
+               void(const scoped_refptr<Buffer>& buffer,
+                    const VideoCaptureFormat& buffer_format,
+                    const scoped_refptr<media::VideoFrame>& frame,
+                    const base::TimeTicks& timestamp));
+  MOCK_METHOD1(OnError, void(const std::string& reason));
 
   explicit MockClient(base::Callback<void(const VideoCaptureFormat&)> frame_cb)
       : main_thread_(base::MessageLoopProxy::current()), frame_cb_(frame_cb) {}
 
-  void OnError(const std::string& error_message) override {
-    OnErr();
-  }
-
   void OnIncomingCapturedData(const uint8* data,
                               int length,
                               const VideoCaptureFormat& format,
                               int rotation,
-                              base::TimeTicks timestamp) override {
+                              const base::TimeTicks& timestamp) override {
     main_thread_->PostTask(FROM_HERE, base::Bind(frame_cb_, format));
   }
 
-  void OnIncomingCapturedVideoFrame(const scoped_refptr<Buffer>& buffer,
-                                    const VideoCaptureFormat& buffer_format,
-                                    const scoped_refptr<VideoFrame>& frame,
-                                    base::TimeTicks timestamp) override {
-    NOTREACHED();
-  }
-
  private:
   scoped_refptr<base::SingleThreadTaskRunner> main_thread_;
   base::Callback<void(const VideoCaptureFormat&)> frame_cb_;
@@ -86,6 +80,11 @@
     device_enumeration_listener_ = new DeviceEnumerationListener();
   }
 
+  void SetUp() override {
+    EXPECT_CALL(*client_, ReserveOutputBuffer(_,_)).Times(0);
+    EXPECT_CALL(*client_, OnIncomingCapturedVideoFrame(_,_,_,_)).Times(0);
+  }
+
   void OnFrameCaptured(const VideoCaptureFormat& format) {
     last_format_ = format;
     run_loop_->QuitClosure().Run();
@@ -128,7 +127,7 @@
       video_capture_device_factory_->Create(names->front()));
   ASSERT_TRUE(device);
 
-  EXPECT_CALL(*client_, OnErr()).Times(0);
+  EXPECT_CALL(*client_, OnError(_)).Times(0);
 
   VideoCaptureParams capture_params;
   capture_params.requested_format.frame_size.SetSize(640, 480);
@@ -194,7 +193,7 @@
   static_cast<FakeVideoCaptureDevice*>(device.get())->
       PopulateVariableFormatsRoster(formats);
 
-  EXPECT_CALL(*client_, OnErr()).Times(0);
+  EXPECT_CALL(*client_, OnError(_)).Times(0);
   int action_count = 200;
 
   device->AllocateAndStart(capture_params, client_.Pass());
diff --git a/media/video/capture/file_video_capture_device.cc b/media/video/capture/file_video_capture_device.cc
index 24673aa..46f89b5d 100644
--- a/media/video/capture/file_video_capture_device.cc
+++ b/media/video/capture/file_video_capture_device.cc
@@ -10,6 +10,7 @@
 #include "base/memory/scoped_ptr.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_piece.h"
+#include "media/video/capture/video_capture_types.h"
 
 namespace media {
 static const int kY4MHeaderMaxSize = 200;
@@ -169,10 +170,10 @@
   capture_thread_.Stop();
 }
 
-int FileVideoCaptureDevice::CalculateFrameSize() {
+int FileVideoCaptureDevice::CalculateFrameSize() const {
   DCHECK_EQ(capture_format_.pixel_format, PIXEL_FORMAT_I420);
   DCHECK_EQ(capture_thread_.message_loop(), base::MessageLoop::current());
-  return capture_format_.frame_size.GetArea() * 12 / 8;
+  return capture_format_.ImageAllocationSize();
 }
 
 void FileVideoCaptureDevice::OnAllocateAndStart(
diff --git a/media/video/capture/file_video_capture_device.h b/media/video/capture/file_video_capture_device.h
index 7b6cd1d..bd3b2985 100644
--- a/media/video/capture/file_video_capture_device.h
+++ b/media/video/capture/file_video_capture_device.h
@@ -43,7 +43,7 @@
  private:
   // Returns size in bytes of an I420 frame, not including possible paddings,
   // defined by |capture_format_|.
-  int CalculateFrameSize();
+  int CalculateFrameSize() const;
 
   // Called on the |capture_thread_|.
   void OnAllocateAndStart(const VideoCaptureParams& params,
diff --git a/media/video/capture/mac/video_capture_device_avfoundation_mac.mm b/media/video/capture/mac/video_capture_device_avfoundation_mac.mm
index f3c7ca7..6d71ef8 100644
--- a/media/video/capture/mac/video_capture_device_avfoundation_mac.mm
+++ b/media/video/capture/mac/video_capture_device_avfoundation_mac.mm
@@ -6,6 +6,8 @@
 
 #import <CoreVideo/CoreVideo.h>
 
+#include <cstring>  // For memchr.
+
 #include "base/logging.h"
 #include "base/mac/foundation_util.h"
 #include "media/video/capture/mac/video_capture_device_mac.h"
@@ -30,6 +32,21 @@
   }
 }
 
+// TODO(magjed): Remove this when Chromium has the latest libyuv version.
+// Returns frame size by finding the End Of Image marker, or 0 if not found.
+size_t JpegFrameSize(const char* sample, size_t sampleSize) {
+  // Jump to next marker (0xff), check for End Of Image (0xd9), repeat.
+  const char* end = sample + sampleSize - 1;
+  for (const char* it = sample;
+       (it = static_cast<const char*>(memchr(it, 0xff, end - it)));
+       ++it) {
+    if (it[1] == static_cast<char>(0xd9))
+      return 2 + (it - sample);
+  }
+  DLOG(WARNING) << "JPEG End Of Image (EOI) marker not found.";
+  return 0;
+}
+
 @implementation VideoCaptureDeviceAVFoundation
 
 #pragma mark Class methods
@@ -306,6 +323,16 @@
       // Expect the MJPEG data to be available as a contiguous reference, i.e.
       // not covered by multiple memory blocks.
       CHECK_EQ(lengthAtOffset, frameSize);
+
+      // TODO(magjed): Remove this when Chromium has the latest libyuv version.
+      // If |frameSize| is suspiciously high (>= 8 bpp), calculate the actual
+      // size by finding the end of image marker. The purpose is to speed up the
+      // jpeg decoding in the browser.
+      if (static_cast<int>(frameSize) >= dimensions.width * dimensions.height)
+        frameSize = JpegFrameSize(baseAddress, frameSize);
+
+      if (frameSize == 0)
+        return;
     }
   } else {
     videoFrame = CoreMediaGlue::CMSampleBufferGetImageBuffer(sampleBuffer);
diff --git a/media/video/capture/video_capture_device.h b/media/video/capture/video_capture_device.h
index 455dc83..84a211f 100644
--- a/media/video/capture/video_capture_device.h
+++ b/media/video/capture/video_capture_device.h
@@ -166,29 +166,38 @@
   // Manages a list of Name entries.
   typedef std::list<Name> Names;
 
-  class MEDIA_EXPORT Client {
+   // Interface defining the methods that clients of VideoCapture must have. It
+   // is actually two-in-one: clients may implement OnIncomingCapturedData() or
+   // ReserveOutputBuffer() + OnIncomingCapturedVideoFrame(), or all of them.
+   // All clients must implement OnError().
+   class MEDIA_EXPORT Client {
    public:
     // Memory buffer returned by Client::ReserveOutputBuffer().
     class Buffer : public base::RefCountedThreadSafe<Buffer> {
      public:
-      int id() const { return id_; }
-      void* data() const { return data_; }
-      size_t size() const { return size_; }
+      virtual int id() const = 0;
+      virtual void* data() const = 0;
+      virtual size_t size() const = 0;
 
      protected:
       friend class base::RefCountedThreadSafe<Buffer>;
-
-      Buffer(int id, void* data, size_t size)
-          : id_(id), data_(data), size_(size) {}
       virtual ~Buffer() {}
-
-      const int id_;
-      void* const data_;
-      const size_t size_;
     };
 
     virtual ~Client() {}
 
+    // Captured a new video frame, data for which is pointed to by |data|.
+    //
+    // The format of the frame is described by |frame_format|, and is assumed to
+    // be tightly packed. This method will try to reserve an output buffer and
+    // copy from |data| into the output buffer. If no output buffer is
+    // available, the frame will be silently dropped.
+    virtual void OnIncomingCapturedData(const uint8* data,
+                                        int length,
+                                        const VideoCaptureFormat& frame_format,
+                                        int rotation,  // Clockwise.
+                                        const base::TimeTicks& timestamp) = 0;
+
     // Reserve an output buffer into which contents can be captured directly.
     // The returned Buffer will always be allocated with a memory size suitable
     // for holding a packed video frame with pixels of |format| format, of
@@ -203,18 +212,6 @@
         media::VideoFrame::Format format,
         const gfx::Size& dimensions) = 0;
 
-    // Captured a new video frame, data for which is pointed to by |data|.
-    //
-    // The format of the frame is described by |frame_format|, and is assumed to
-    // be tightly packed. This method will try to reserve an output buffer and
-    // copy from |data| into the output buffer. If no output buffer is
-    // available, the frame will be silently dropped.
-    virtual void OnIncomingCapturedData(const uint8* data,
-                                        int length,
-                                        const VideoCaptureFormat& frame_format,
-                                        int rotation,  // Clockwise.
-                                        base::TimeTicks timestamp) = 0;
-
     // Captured a new video frame, held in |frame|.
     //
     // As the frame is backed by a reservation returned by
@@ -224,7 +221,7 @@
         const scoped_refptr<Buffer>& buffer,
         const VideoCaptureFormat& buffer_format,
         const scoped_refptr<media::VideoFrame>& frame,
-        base::TimeTicks timestamp) = 0;
+        const base::TimeTicks& timestamp) = 0;
 
     // An error has occurred that cannot be handled and VideoCaptureDevice must
     // be StopAndDeAllocate()-ed. |reason| is a text description of the error.
diff --git a/media/video/capture/video_capture_device_unittest.cc b/media/video/capture/video_capture_device_unittest.cc
index f365213d..8b07e14 100644
--- a/media/video/capture/video_capture_device_unittest.cc
+++ b/media/video/capture/video_capture_device_unittest.cc
@@ -67,33 +67,26 @@
   MOCK_METHOD2(ReserveOutputBuffer,
                scoped_refptr<Buffer>(VideoFrame::Format format,
                                      const gfx::Size& dimensions));
-  MOCK_METHOD0(OnErr, void());
+  MOCK_METHOD4(OnIncomingCapturedVideoFrame,
+               void(const scoped_refptr<Buffer>& buffer,
+                    const VideoCaptureFormat& buffer_format,
+                    const scoped_refptr<VideoFrame>& frame,
+                    const base::TimeTicks& timestamp));
+  MOCK_METHOD1(OnError, void(const std::string& reason));
 
   explicit MockClient(base::Callback<void(const VideoCaptureFormat&)> frame_cb)
       : main_thread_(base::MessageLoopProxy::current()), frame_cb_(frame_cb) {}
 
-  void OnError(const std::string& error_message) override {
-    OnErr();
-  }
-
   void OnIncomingCapturedData(const uint8* data,
                               int length,
                               const VideoCaptureFormat& format,
                               int rotation,
-                              base::TimeTicks timestamp) override {
+                              const base::TimeTicks& timestamp) override {
     ASSERT_GT(length, 0);
     ASSERT_TRUE(data != NULL);
     main_thread_->PostTask(FROM_HERE, base::Bind(frame_cb_, format));
   }
 
-  void OnIncomingCapturedVideoFrame(
-      const scoped_refptr<Buffer>& buffer,
-      const VideoCaptureFormat& buffer_format,
-      const scoped_refptr<VideoFrame>& frame,
-      base::TimeTicks timestamp) override {
-    NOTREACHED();
-  }
-
  private:
   scoped_refptr<base::SingleThreadTaskRunner> main_thread_;
   base::Callback<void(const VideoCaptureFormat&)> frame_cb_;
@@ -130,12 +123,14 @@
     device_enumeration_listener_ = new DeviceEnumerationListener();
   }
 
-#if defined(OS_ANDROID)
   void SetUp() override {
+#if defined(OS_ANDROID)
     VideoCaptureDeviceAndroid::RegisterVideoCaptureDevice(
         base::android::AttachCurrentThread());
-  }
 #endif
+    EXPECT_CALL(*client_, ReserveOutputBuffer(_,_)).Times(0);
+    EXPECT_CALL(*client_, OnIncomingCapturedVideoFrame(_,_,_,_)).Times(0);
+  }
 
   void ResetWithNewClient() {
     client_.reset(new MockClient(base::Bind(
@@ -233,7 +228,7 @@
   } else {
     // The presence of the actual device is only checked on AllocateAndStart()
     // and not on creation for QTKit API in Mac OS X platform.
-    EXPECT_CALL(*client_, OnErr()).Times(1);
+    EXPECT_CALL(*client_, OnError(_)).Times(1);
 
     VideoCaptureParams capture_params;
     capture_params.requested_format.frame_size.SetSize(640, 480);
@@ -257,7 +252,7 @@
   ASSERT_TRUE(device);
   DVLOG(1) << names_->front().id();
 
-  EXPECT_CALL(*client_, OnErr()).Times(0);
+  EXPECT_CALL(*client_, OnError(_)).Times(0);
 
   VideoCaptureParams capture_params;
   capture_params.requested_format.frame_size.SetSize(640, 480);
@@ -268,6 +263,8 @@
   WaitForCapturedFrame();
   EXPECT_EQ(last_format().frame_size.width(), 640);
   EXPECT_EQ(last_format().frame_size.height(), 480);
+  EXPECT_EQ(static_cast<size_t>(640 * 480 * 3 / 2),
+            last_format().ImageAllocationSize());
   device->StopAndDeAllocate();
 }
 
@@ -282,7 +279,7 @@
       video_capture_device_factory_->Create(names_->front()));
   ASSERT_TRUE(device);
 
-  EXPECT_CALL(*client_, OnErr()).Times(0);
+  EXPECT_CALL(*client_, OnError(_)).Times(0);
 
   VideoCaptureParams capture_params;
   capture_params.requested_format.frame_size.SetSize(1280, 720);
@@ -291,6 +288,10 @@
   device->AllocateAndStart(capture_params, client_.Pass());
   // Get captured video frames.
   WaitForCapturedFrame();
+  EXPECT_EQ(last_format().frame_size.width(), 1280);
+  EXPECT_EQ(last_format().frame_size.height(), 720);
+  EXPECT_EQ(static_cast<size_t>(1280 * 720 * 3 / 2),
+            last_format().ImageAllocationSize());
   device->StopAndDeAllocate();
 }
 
@@ -304,7 +305,7 @@
       video_capture_device_factory_->Create(names_->front()));
   ASSERT_TRUE(device);
 
-  EXPECT_CALL(*client_, OnErr()).Times(0);
+  EXPECT_CALL(*client_, OnError(_)).Times(0);
 
   VideoCaptureParams capture_params;
   capture_params.requested_format.frame_size.SetSize(637, 472);
@@ -315,6 +316,8 @@
   device->StopAndDeAllocate();
   EXPECT_EQ(last_format().frame_size.width(), 640);
   EXPECT_EQ(last_format().frame_size.height(), 480);
+  EXPECT_EQ(static_cast<size_t>(640 * 480 * 3 / 2),
+            last_format().ImageAllocationSize());
 }
 
 // Cause hangs on Windows Debug. http://crbug.com/417824
@@ -378,7 +381,7 @@
       video_capture_device_factory_->Create(names_->front()));
   ASSERT_TRUE(device);
 
-  EXPECT_CALL(*client_, OnErr()).Times(0);
+  EXPECT_CALL(*client_, OnError(_)).Times(0);
 
   VideoCaptureParams capture_params;
   capture_params.requested_format.frame_size.SetSize(640, 480);
@@ -405,7 +408,7 @@
       video_capture_device_factory_->Create(*name));
   ASSERT_TRUE(device);
 
-  EXPECT_CALL(*client_, OnErr()).Times(0);
+  EXPECT_CALL(*client_, OnError(_)).Times(0);
 
   VideoCaptureParams capture_params;
   capture_params.requested_format.frame_size.SetSize(1280, 720);
@@ -417,6 +420,8 @@
   // Verify we get MJPEG from the device. Not all devices can capture 1280x720
   // @ 30 fps, so we don't care about the exact resolution we get.
   EXPECT_EQ(last_format().pixel_format, PIXEL_FORMAT_MJPEG);
+  EXPECT_GE(static_cast<size_t>(1280 * 720),
+            last_format().ImageAllocationSize());
   device->StopAndDeAllocate();
 }
 
diff --git a/media/video/capture/video_capture_types.cc b/media/video/capture/video_capture_types.cc
index 2cddeee..a20ea4f 100644
--- a/media/video/capture/video_capture_types.cc
+++ b/media/video/capture/video_capture_types.cc
@@ -31,6 +31,36 @@
          (pixel_format < PIXEL_FORMAT_MAX);
 }
 
+size_t VideoCaptureFormat::ImageAllocationSize() const {
+  size_t result_frame_size = frame_size.GetArea();
+  switch (pixel_format) {
+    case PIXEL_FORMAT_I420:
+    case PIXEL_FORMAT_YV12:
+    case PIXEL_FORMAT_NV12:
+    case PIXEL_FORMAT_NV21:
+      result_frame_size = result_frame_size * 3 / 2;
+      break;
+    case PIXEL_FORMAT_UYVY:
+    case PIXEL_FORMAT_YUY2:
+      result_frame_size *= 2;
+      break;
+    case PIXEL_FORMAT_RGB24:
+      result_frame_size *= 3;
+      break;
+    case PIXEL_FORMAT_ARGB:
+      result_frame_size *= 4;
+      break;
+    case PIXEL_FORMAT_MJPEG:
+    case PIXEL_FORMAT_TEXTURE:
+      result_frame_size = 0;
+      break;
+    default:  // Sizes for the rest of the formats are unknown.
+      NOTREACHED() << "Unknown pixel format provided.";
+      break;
+  }
+  return result_frame_size;
+}
+
 std::string VideoCaptureFormat::ToString() const {
   return base::StringPrintf("resolution: %s, fps: %.3f, pixel format: %s",
                             frame_size.ToString().c_str(),
diff --git a/media/video/capture/video_capture_types.h b/media/video/capture/video_capture_types.h
index f8e8cab..3869876 100644
--- a/media/video/capture/video_capture_types.h
+++ b/media/video/capture/video_capture_types.h
@@ -67,6 +67,10 @@
   std::string ToString() const;
   static std::string PixelFormatToString(VideoPixelFormat format);
 
+  // Returns the required buffer size to hold an image of a given
+  // VideoCaptureFormat with no padding and tightly packed.
+  size_t ImageAllocationSize() const;
+
   // Checks that all values are in the expected range. All limits are specified
   // in media::Limits.
   bool IsValid() const;
diff --git a/media/video/capture/win/sink_input_pin_win.cc b/media/video/capture/win/sink_input_pin_win.cc
index 795dc75..08e76a72 100644
--- a/media/video/capture/win/sink_input_pin_win.cc
+++ b/media/video/capture/win/sink_input_pin_win.cc
@@ -68,8 +68,7 @@
       pvi->bmiHeader.biBitCount = 12;  // bit per pixel
       pvi->bmiHeader.biWidth = requested_info_header_.biWidth;
       pvi->bmiHeader.biHeight = requested_info_header_.biHeight;
-      pvi->bmiHeader.biSizeImage =
-        GetArea(requested_info_header_) * 3 / 2;
+      pvi->bmiHeader.biSizeImage = GetArea(requested_info_header_) * 3 / 2;
       media_type->subtype = kMediaSubTypeI420;
       break;
     }
@@ -151,6 +150,12 @@
 HRESULT SinkInputPin::Receive(IMediaSample* sample) {
   const int length = sample->GetActualDataLength();
   uint8* buffer = NULL;
+
+  if (length <= 0) {
+    DLOG(WARNING) << "Media sample length is 0 or less.";
+    return S_FALSE;
+  }
+
   if (FAILED(sample->GetPointer(&buffer)))
     return S_FALSE;
 
diff --git a/mojo/converters/surfaces/surfaces_type_converters.cc b/mojo/converters/surfaces/surfaces_type_converters.cc
index ee2013a..1054744 100644
--- a/mojo/converters/surfaces/surfaces_type_converters.cc
+++ b/mojo/converters/surfaces/surfaces_type_converters.cc
@@ -43,10 +43,8 @@
     cc::YUVVideoDrawQuad::REC_601 ==
         static_cast<cc::YUVVideoDrawQuad::ColorSpace>(YUV_COLOR_SPACE_REC_601),
     rec_601_enum_matches);
-COMPILE_ASSERT(cc::YUVVideoDrawQuad::REC_601_JPEG ==
-                   static_cast<cc::YUVVideoDrawQuad::ColorSpace>(
-                       YUV_COLOR_SPACE_REC_601_JPEG),
-               rec_601_jpeg_enum_matches);
+// TODO(jamesr): Add REC_709 and JPEG to the YUVColorSpace enum upstream in
+// mojo.
 
 namespace {
 
diff --git a/mojo/mojo_public_gles2_for_loadable_module.gypi b/mojo/mojo_public_gles2_for_loadable_module.gypi
index 31a350e..cd5dbbf 100644
--- a/mojo/mojo_public_gles2_for_loadable_module.gypi
+++ b/mojo/mojo_public_gles2_for_loadable_module.gypi
@@ -31,17 +31,6 @@
           'GLES2_USE_MOJO',
         ],
       },
-      'all_dependent_settings': {
-        'conditions': [
-          # We need to be able to call the MojoSetGLES2Thunks() function in
-          # gles2_thunks.cc
-          ['OS=="android"', {
-            'ldflags!': [
-              '-Wl,--exclude-libs=ALL',
-            ],
-          }],
-        ],
-      },
       'sources': [
         '<(DEPTH)/third_party/mojo/src/mojo/public/c/gles2/gles2.h',
         '<(DEPTH)/third_party/mojo/src/mojo/public/c/gles2/gles2_export.h',
diff --git a/mojo/nacl/BUILD.gn b/mojo/nacl/BUILD.gn
index 0b626bc..222d45f6 100644
--- a/mojo/nacl/BUILD.gn
+++ b/mojo/nacl/BUILD.gn
@@ -109,7 +109,7 @@
 
 group("mojo_nacl") {
   deps = [
-    ":irt_mojo(//native_client/build/toolchain/nacl:irt_${cpu_arch})",
+    ":irt_mojo(//native_client/build/toolchain/nacl:irt_${current_cpu})",
   ]
 }
 
@@ -117,6 +117,6 @@
   testonly = true
   deps = [
     ":monacl_shell",
-    ":monacl_test(//native_client/build/toolchain/nacl:clang_newlib_${cpu_arch})",
+    ":monacl_test(//native_client/build/toolchain/nacl:clang_newlib_${current_cpu})",
   ]
 }
diff --git a/mojo/services/html_viewer/weblayertreeview_impl.cc b/mojo/services/html_viewer/weblayertreeview_impl.cc
index a1d4da8..e806d10 100644
--- a/mojo/services/html_viewer/weblayertreeview_impl.cc
+++ b/mojo/services/html_viewer/weblayertreeview_impl.cc
@@ -65,6 +65,9 @@
 void WebLayerTreeViewImpl::DidBeginMainFrame() {
 }
 
+void WebLayerTreeViewImpl::BeginMainFrameNotExpectedSoon() {
+}
+
 void WebLayerTreeViewImpl::BeginMainFrame(const cc::BeginFrameArgs& args) {
   VLOG(2) << "WebLayerTreeViewImpl::BeginMainFrame";
   double frame_time_sec = (args.frame_time - base::TimeTicks()).InSecondsF();
diff --git a/mojo/services/html_viewer/weblayertreeview_impl.h b/mojo/services/html_viewer/weblayertreeview_impl.h
index deb2f50a..9a6b07f 100644
--- a/mojo/services/html_viewer/weblayertreeview_impl.h
+++ b/mojo/services/html_viewer/weblayertreeview_impl.h
@@ -51,6 +51,7 @@
   void WillBeginMainFrame() override;
   void DidBeginMainFrame() override;
   void BeginMainFrame(const cc::BeginFrameArgs& args) override;
+  void BeginMainFrameNotExpectedSoon() override;
   void Layout() override;
   void ApplyViewportDeltas(const gfx::Vector2dF& inner_delta,
                            const gfx::Vector2dF& outer_delta,
diff --git a/native_client_sdk/doc_generated/reference/pnacl-c-cpp-language-support.html b/native_client_sdk/doc_generated/reference/pnacl-c-cpp-language-support.html
index d1e3585..3b76173 100644
--- a/native_client_sdk/doc_generated/reference/pnacl-c-cpp-language-support.html
+++ b/native_client_sdk/doc_generated/reference/pnacl-c-cpp-language-support.html
@@ -156,12 +156,18 @@
 restrictions beyond C&#8217;s.</p>
 <h2 id="c-exception-handling"><span id="exception-handling"></span>C++ Exception Handling</h2>
 <p>PNaCl currently supports C++ exception handling through <code>setjmp()</code> and
-<code>longjmp()</code>, which can be enabled with the <code>--pnacl-exceptions=sjlj</code>
-linker flag. Exceptions are disabled by default so that faster and
-smaller code is generated, and <code>throw</code> statements are replaced with
-calls to <code>abort()</code>. The usual <code>-fno-exceptions</code> flag is also
-supported. PNaCl will support full zero-cost exception handling in the
-future.</p>
+<code>longjmp()</code>, which can be enabled with the <code>--pnacl-exceptions=sjlj</code> linker
+flag (set with <code>LDFLAGS</code> when using Make). Exceptions are disabled by default
+so that faster and smaller code is generated, and <code>throw</code> statements are
+replaced with calls to <code>abort()</code>. The usual <code>-fno-exceptions</code> flag is also
+supported, though the default is <code>-fexceptions</code>. PNaCl will support full
+zero-cost exception handling in the future.</p>
+<aside>
+When using <a class="reference external" href="https://code.google.com/p/naclports">naclports</a> or other prebuilt static libraries, you don&#8217;t
+need to recompile because the exception handling support is
+implemented at link time (when all the static libraries are put
+together with your application).
+</aside>
 <p>NaCl supports full zero-cost C++ exception handling.</p>
 <h2 id="inline-assembly">Inline Assembly</h2>
 <p>Inline assembly isn&#8217;t supported by PNaCl because it isn&#8217;t portable. The
diff --git a/native_client_sdk/doc_generated/sdk/release-notes.html b/native_client_sdk/doc_generated/sdk/release-notes.html
index 55e2f13..7a81d94 100644
--- a/native_client_sdk/doc_generated/sdk/release-notes.html
+++ b/native_client_sdk/doc_generated/sdk/release-notes.html
@@ -5,6 +5,14 @@
 <p>The dates in the following release notes denote when Chrome and the NaCl SDK
 reached canary status. The stable release is typically 6 weeks later.</p>
 <h2 id="chrome-pepper-42-20-february-2015">Chrome/Pepper 42 (20 February 2015)</h2>
+<h3 id="sdk">SDK</h3>
+<ul class="small-gap">
+<li>The SDK now contains experimental versions of <code>i686-nacl-clang</code>,
+<code>x86_64-nacl-clang</code>, and <code>arm-nacl-clang</code> as well as the <code>clang++</code>
+equivalents. These toolchains are based on the same LLVM version as PNaCl, but
+can be used to generate NaCl <code>.nexe</code> files instead of translating a
+<code>.pexe</code> locally or using the GCC toolchain.</li>
+</ul>
 <h3 id="nacl">NaCl</h3>
 <ul class="small-gap">
 <li>The x86 NaCl validators accept instructions from the FMA3 extensions, as well
@@ -16,7 +24,10 @@
 used to upgrade all accesses to <cite>seq_cst</cite>. It still upgrades <cite>consume</cite> to
 <cite>acquire</cite> (no compiler currently implements <cite>consume</cite>), and <cite>relaxed</cite> to
 <cite>seq_cst</cite> (to conservatively avoid platform differences due to out-of-thin-air
-problems).</li>
+problems). This is currently disabled by default in the SDK so that the
+in-browser translator installed on users&#8217; machines has time to gain this
+support. Developers can turn it on by passing the
+<code>-pnacl-memory-order-seq-cst-only=false</code> flag to <code>opt</code>.</li>
 <li>PNaCl handles nested struct type expansion, which allows it to better support
 non-C languages such as Rust.</li>
 <li>PNaCl breaks up many integer operations over 64-bits into individual 64-bit
@@ -65,12 +76,12 @@
 <li>Hardware Decode API in development preview.</li>
 <li>Sync API in development preview.</li>
 </ul>
-<h3 id="sdk">SDK</h3>
+<h3 id="id6">SDK</h3>
 <ul class="small-gap">
 <li>Demo of a <a class="reference internal" href="/native-client/io2014.html#io2014"><em>full development environment in the browser</em></a>.</li>
 </ul>
 <h2 id="chrome-pepper-36-09-may-2014">Chrome/Pepper 36 (09 May 2014)</h2>
-<h3 id="id6">PNaCl</h3>
+<h3 id="id7">PNaCl</h3>
 <ul class="small-gap">
 <li>Support <a class="reference external" href="http://clang.llvm.org/docs/LanguageExtensions.html#vectors-and-extended-vectors">LLVM vectors</a>
 and <a class="reference external" href="http://gcc.gnu.org/onlinedocs/gcc/Vector-Extensions.html">GCC vectors</a> for SIMD
@@ -79,7 +90,7 @@
 Chrome. More SIMD instructions will be added in later releases.</li>
 </ul>
 <h2 id="chrome-pepper-35-31-mar-2014">Chrome/Pepper 35 (31 Mar 2014)</h2>
-<h3 id="id7">PNaCl</h3>
+<h3 id="id8">PNaCl</h3>
 <ul class="small-gap">
 <li>Upgraded LLVM to version 3.4.</li>
 <li>Translation now uses dynamic load balancing, making translation time faster.</li>
@@ -87,7 +98,7 @@
 Chrome, simplifying debugging with PNaCl. See <a class="reference internal" href="/native-client/devguide/devcycle/debugging.html#debugging-pnacl-pexes"><em>Debugging PNaCl pexes</em></a></li>
 </ul>
 <h2 id="chrome-pepper-34-20-feb-2014">Chrome/Pepper 34 (20 Feb 2014)</h2>
-<h3 id="id8">Pepper</h3>
+<h3 id="id9">Pepper</h3>
 <ul class="small-gap">
 <li>Filesystems can now be passed from JavaScript to NaCl. The resulting
 <code>pp::Var</code> will contain a <code>pp::Resource</code> that can be given to the
@@ -97,7 +108,7 @@
 <a class="reference external" href="/native-client/pepper_dev/cpp/classpp_1_1_media_stream_video_track">pp::MediaStreamVideoTrack</a> for
 more details.</li>
 </ul>
-<h3 id="id9">PNaCl</h3>
+<h3 id="id10">PNaCl</h3>
 <ul class="small-gap">
 <li>Parallel translation: at least 1.7x faster, even with older pexes.</li>
 <li>Intelligent abbreviations in the bitcode: 20% reduction in binary size using
@@ -111,7 +122,7 @@
 handling (see <a class="reference external" href="https://groups.google.com/forum/#!topic/native-client-discuss/0spfg6O04FM">announcement</a>
 for details).</li>
 </ul>
-<h3 id="id10">SDK</h3>
+<h3 id="id11">SDK</h3>
 <ul class="small-gap">
 <li>The <code>nacl_io</code> library now includes a FUSE mount.</li>
 <li>In the SDK examples, <code>common.js</code> now loads the Release version of the
@@ -225,7 +236,7 @@
 <p>The Pepper 26 bundle includes a new HTTP filesystem type in the nacl_mounts
 library (which has been renamed nacl_io), changes to the example Makefiles, a
 simple new 3D example, and a threaded file IO example.</p>
-<h3 id="id11">Build tools and toolchains</h3>
+<h3 id="id12">Build tools and toolchains</h3>
 <ul class="small-gap">
 <li><p class="first">Makefiles have been changed significantly:</p>
 <ul class="small-gap">
@@ -245,14 +256,14 @@
 the same set of header files as host builds. Previously host and NaCl builds
 used different headers, which could cause build problems.</li>
 </ul>
-<h3 id="id12">Libraries</h3>
+<h3 id="id13">Libraries</h3>
 <ul class="small-gap">
 <li>The nacl_mounts library has been renamed <strong>nacl_io</strong>, and has been expanded
 with a new type of mount, httpfs, which can be used to read URLs via HTTP.
 For details see <code>include/nacl_io/nacl_io.h</code>, as well as the
 <code>hello_nacl_io</code> example.</li>
 </ul>
-<h3 id="id13">Examples</h3>
+<h3 id="id14">Examples</h3>
 <ul class="small-gap">
 <li>A new example, <strong>hello_world_instance3d</strong>, has been added to demonstrate a
 simplified 3D app.</li>
@@ -274,7 +285,7 @@
 operations, and ppapi_main, which lets you implement a Native Client module
 using a simple ppapi_main function), and two new examples that demonstrate how
 to use the nacl_mounts and ppapi_main libraries.</p>
-<h3 id="id14">Build tools and toolchains</h3>
+<h3 id="id15">Build tools and toolchains</h3>
 <ul class="small-gap">
 <li><p class="first">The SDK includes a new toolchain to build Native Client executables (.nexe
 files) for <strong>ARM devices</strong>.</p>
@@ -311,7 +322,7 @@
 cannot make asynchronous PPAPI calls on a background thread without creating
 and using a message loop.</li>
 </ul>
-<h3 id="id15">Libraries</h3>
+<h3 id="id16">Libraries</h3>
 <p>The SDK includes two new libraries:</p>
 <ul class="small-gap">
 <li><p class="first">The <strong>nacl_mounts</strong> library provides a virtual file system that your module
@@ -345,7 +356,7 @@
 <p>Header files for the new libraries are in the <code>include/</code> directory, source
 files are in the <code>src/</code> directory, and compiled libraries are in the <code>lib/</code>
 directory.</p>
-<h3 id="id16">Examples</h3>
+<h3 id="id17">Examples</h3>
 <ul class="small-gap">
 <li><p class="first">The SDK includes two new examples:</p>
 <ul class="small-gap">
@@ -386,7 +397,7 @@
 for &#8220;Portable Native Client&#8221;), a new library (pthreads-win32) for the Windows
 SDK, and an expanded list of attributes for Pepper 3D contexts that lets
 applications specify a GPU preference for low power or performance.</p>
-<h3 id="id17">Build tools and toolchains</h3>
+<h3 id="id18">Build tools and toolchains</h3>
 <ul class="small-gap">
 <li>The SDK includes a new, experimental toolchain called <a class="reference external" href="http://nativeclient.googlecode.com/svn/data/site/pnacl.pdf">PNaCl</a> (pronounced
 &#8220;pinnacle&#8221;). The PNaCl toolchain produces architecture-independent executable
@@ -402,7 +413,7 @@
 names of your .nexe files and <code>create_nmf.py</code> will still be able to
 generate the appropriate Native Client manifest file for your application.</li>
 </ul>
-<h3 id="id19">Examples</h3>
+<h3 id="id20">Examples</h3>
 <ul class="small-gap">
 <li>The SDK examples now build with four toolchains: the glibc and newlib
 toolchains, the experimental PNaCl toolchain, and the hosted toolchain on
@@ -413,7 +424,7 @@
 drawing function is now set up as the Flush() callback, which allows 2D
 drawing to occur as quickly as possible.</li>
 </ul>
-<h3 id="id20">PPAPI</h3>
+<h3 id="id21">PPAPI</h3>
 <ul class="small-gap">
 <li>When creating a 3D rendering context, the <a class="reference external" href="/native-client/pepper_stable/c/group___enums#ga7df48e1c55f6401beea2a1b9c07967e8">attribute list</a>
 for the context can specify whether to prefer low power or performance for
@@ -485,7 +496,7 @@
 </ul>
 </li>
 </ul>
-<h3 id="id21">Examples</h3>
+<h3 id="id22">Examples</h3>
 <ul class="small-gap">
 <li>On Linux and Windows systems, most of the examples now build with three
 toolchains: the Native Client glibc and newlib toolchains, and the native
@@ -501,7 +512,7 @@
 a list of changes between version 1 and version 2 of the manifest file
 format, and a support schedule for applications that use version 1.</li>
 </ul>
-<h3 id="id22">PPAPI</h3>
+<h3 id="id23">PPAPI</h3>
 <ul class="small-gap">
 <li><a class="reference external" href="/native-client/pepper_stable/c/group___enums#ga21b811ac0484a214a8751aa3e1c959d9">PP_InputEvent_Modifier</a>
 has two new enum values (_ISLEFT and _ISRIGHT).</li>
@@ -512,14 +523,14 @@
 <p>The Pepper 22 bundle includes a <strong>command-line debugger</strong>, resources to enable
 <strong>hosted development on Windows</strong>, and changes to the example Makefiles (each
 example now builds both a debug and a release version).</p>
-<h3 id="id23">Tools</h3>
+<h3 id="id24">Tools</h3>
 <ul class="small-gap">
 <li>The SDK now includes a <strong>command-line debugger</strong> that you can use to debug
 Native Client modules. See <a class="reference internal" href="/native-client/devguide/devcycle/debugging.html#devcycle-debugging"><em>Debugging with nacl-gdb</em></a> for instructions on how to use this debugger. For now,
 nacl-gdb only works on 64-bit Windows, 64-bit Linux, and 32-bit Linux
 systems. Support for Mac and 32-bit Windows systems will be added soon.</li>
 </ul>
-<h3 id="id24">Windows SDK</h3>
+<h3 id="id25">Windows SDK</h3>
 <ul class="small-gap">
 <li><p class="first">Developers using the Windows SDK can now <strong>build a module as a Pepper
 plugin</strong> (sometimes called a &#8220;trusted&#8221; or &#8220;in-process&#8221; plugin) using the
@@ -567,7 +578,7 @@
 In the future, the SDK will include resources for hosted development on Mac
 and Linux as well as Windows.
 </aside>
-<h3 id="id25">Examples</h3>
+<h3 id="id26">Examples</h3>
 <ul class="small-gap">
 <li>Each example in the SDK now builds both a debug and a release version. As
 before, most examples also build newlib and glibc versions, which means that
@@ -581,7 +592,7 @@
 of the module, and implements handleMessage() to respond to messages sent
 from the NaCl module to the JavaScript side of the application</li>
 </ul>
-<h3 id="id26">PPAPI</h3>
+<h3 id="id27">PPAPI</h3>
 <ul class="small-gap">
 <li>The <code>CompletionCallbackFactory</code> class template now takes a thread traits
 class as its second parameter. For details see the <a class="reference external" href="/native-client/pepper_stable/cpp/classpp_1_1_completion_callback_factory#details">CompletionCallbackFactory
diff --git a/native_client_sdk/src/build_tools/build_sdk.py b/native_client_sdk/src/build_tools/build_sdk.py
index 1711242..ffa456f 100755
--- a/native_client_sdk/src/build_tools/build_sdk.py
+++ b/native_client_sdk/src/build_tools/build_sdk.py
@@ -170,7 +170,7 @@
   if 'bionic' in toolchains:
     build_platform = '%s_x86' % getos.GetPlatform()
     args.extend(['--append', os.path.join(build_platform, 'nacl_arm_bionic')])
-  args.append('sync')
+  args.extend(['sync', '--extract'])
   buildbot_common.Run(args, cwd=NACL_DIR)
 
 
@@ -343,6 +343,8 @@
   return os.path.join(os.path.relpath(OUT_DIR, SRC_DIR), path)
 
 
+# TODO(ncbray): stop building and copying libraries into the SDK that are
+# already provided by the toolchain.
 TOOLCHAIN_LIBS = {
   'bionic' : [
     'libminidump_generator.a',
@@ -352,8 +354,6 @@
     'libppapi.a',
   ],
   'newlib' : [
-    'crti.o',
-    'crtn.o',
     'libminidump_generator.a',
     'libnacl.a',
     'libnacl_dyncode.a',
@@ -448,7 +448,7 @@
 
   for tc in set(toolchains) & set(['newlib', 'glibc', 'pnacl']):
     if tc == 'pnacl':
-      xarches = (None, 'ia32', 'x64')
+      xarches = (None, 'ia32', 'x64', 'arm')
     elif tc == 'glibc':
       xarches = ('ia32', 'x64')
     else:
@@ -464,10 +464,6 @@
         bionic_dir = GetOutputToolchainLib(pepperdir, 'bionic', xarch)
         InstallFiles(src_dir, bionic_dir, TOOLCHAIN_LIBS['bionic'])
 
-      if tc != 'pnacl':
-        src_dir = GetGypToolchainLib(tc, xarch)
-        InstallFiles(src_dir, dst_dir, ['crt1.o'])
-
 
 def GypNinjaBuild_NaCl(rel_out_dir):
   # TODO(binji): gyp_nacl doesn't build properly on Windows anymore; it only
@@ -487,14 +483,17 @@
   out_dir_arm = MakeNinjaRelPath(rel_out_dir + '-arm')
   out_dir_clang_32 = MakeNinjaRelPath(rel_out_dir + '-clang-ia32')
   out_dir_clang_64 = MakeNinjaRelPath(rel_out_dir + '-clang-x64')
+  out_dir_clang_arm = MakeNinjaRelPath(rel_out_dir + '-clang-arm')
 
   GypNinjaBuild('ia32', gyp_py, nacl_core_sdk_gyp, 'nacl_core_sdk', out_dir_32)
   GypNinjaBuild('x64', gyp_py, nacl_core_sdk_gyp, 'nacl_core_sdk', out_dir_64)
+  GypNinjaBuild('arm', gyp_py, nacl_core_sdk_gyp, 'nacl_core_sdk', out_dir_arm)
   GypNinjaBuild('ia32', gyp_py, nacl_core_sdk_gyp, 'nacl_core_sdk',
       out_dir_clang_32, gyp_defines=['use_nacl_clang=1'])
   GypNinjaBuild('x64', gyp_py, nacl_core_sdk_gyp, 'nacl_core_sdk',
       out_dir_clang_64, gyp_defines=['use_nacl_clang=1'])
-  GypNinjaBuild('arm', gyp_py, nacl_core_sdk_gyp, 'nacl_core_sdk', out_dir_arm)
+  GypNinjaBuild('arm', gyp_py, nacl_core_sdk_gyp, 'nacl_core_sdk',
+      out_dir_clang_arm, gyp_defines=['use_nacl_clang=1'])
   GypNinjaBuild('x64', gyp_py, all_gyp, 'ncval_new', out_dir_64)
 
 
@@ -600,6 +599,8 @@
                           ['use_nacl_clang=1'])
       GypNinjaBuild_PPAPI('x64', GYPBUILD_DIR + '-clang-x64',
                           ['use_nacl_clang=1'])
+      GypNinjaBuild_PPAPI('arm', GYPBUILD_DIR + '-clang-arm',
+                          ['use_nacl_clang=1'])
 
       # NOTE: For ia32, gyp builds both x86-32 and x86-64 by default.
       for arch in ('ia32', 'arm'):
@@ -662,6 +663,8 @@
                        'pnacl')
     InstallNaClHeaders(GetToolchainNaClInclude('pnacl', pnacldir, 'x86'),
                        'pnacl')
+    InstallNaClHeaders(GetToolchainNaClInclude('pnacl', pnacldir, 'arm'),
+                       'pnacl')
 
 
 def MakeDirectoryOrClobber(pepperdir, dirname, clobber):
diff --git a/native_client_sdk/src/build_tools/sdk_files.list b/native_client_sdk/src/build_tools/sdk_files.list
index ea65895..161ac2bc 100644
--- a/native_client_sdk/src/build_tools/sdk_files.list
+++ b/native_client_sdk/src/build_tools/sdk_files.list
@@ -65,6 +65,7 @@
 include/gtest/internal/src/*
 include/json/*
 include/KHR/*
+include/mac/sys/mount.h
 include/nacl_io/*
 include/nacl_io/pepper/*
 include/newlib/*
@@ -72,7 +73,6 @@
 include/newlib/netinet/*
 include/newlib/netinet6/*
 include/newlib/sys/*
-include/mac/sys/mount.h
 include/pnacl/*
 include/pnacl/arpa/*
 include/pnacl/netinet/*
@@ -133,6 +133,22 @@
 [win]lib/${PLATFORM}_x86_32_host/Release/ppapi_gles2.lib
 [win]lib/${PLATFORM}_x86_32_host/Release/pthread.lib
 [win]lib/${PLATFORM}_x86_32_host/Release/sdk_util.lib
+lib/clang-newlib_arm/Debug/libgmock.a
+lib/clang-newlib_arm/Debug/libgtest.a
+lib/clang-newlib_arm/Debug/libnacl_io.a
+lib/clang-newlib_arm/Debug/libppapi_cpp.a
+lib/clang-newlib_arm/Debug/libppapi_cpp_private.a
+lib/clang-newlib_arm/Debug/libppapi_gles2.a
+lib/clang-newlib_arm/Debug/libppapi_simple.a
+lib/clang-newlib_arm/Debug/libsdk_util.a
+lib/clang-newlib_arm/Release/libgmock.a
+lib/clang-newlib_arm/Release/libgtest.a
+lib/clang-newlib_arm/Release/libnacl_io.a
+lib/clang-newlib_arm/Release/libppapi_cpp.a
+lib/clang-newlib_arm/Release/libppapi_cpp_private.a
+lib/clang-newlib_arm/Release/libppapi_gles2.a
+lib/clang-newlib_arm/Release/libppapi_simple.a
+lib/clang-newlib_arm/Release/libsdk_util.a
 lib/clang-newlib_x86_32/Debug/libgmock.a
 lib/clang-newlib_x86_32/Debug/libgtest.a
 lib/clang-newlib_x86_32/Debug/libnacl_io.a
diff --git a/native_client_sdk/src/doc/reference/pnacl-c-cpp-language-support.rst b/native_client_sdk/src/doc/reference/pnacl-c-cpp-language-support.rst
index 2324774..1a87dff6 100644
--- a/native_client_sdk/src/doc/reference/pnacl-c-cpp-language-support.rst
+++ b/native_client_sdk/src/doc/reference/pnacl-c-cpp-language-support.rst
@@ -176,12 +176,19 @@
 ======================
 
 PNaCl currently supports C++ exception handling through ``setjmp()`` and
-``longjmp()``, which can be enabled with the ``--pnacl-exceptions=sjlj``
-linker flag. Exceptions are disabled by default so that faster and
-smaller code is generated, and ``throw`` statements are replaced with
-calls to ``abort()``. The usual ``-fno-exceptions`` flag is also
-supported. PNaCl will support full zero-cost exception handling in the
-future.
+``longjmp()``, which can be enabled with the ``--pnacl-exceptions=sjlj`` linker
+flag (set with ``LDFLAGS`` when using Make). Exceptions are disabled by default
+so that faster and smaller code is generated, and ``throw`` statements are
+replaced with calls to ``abort()``. The usual ``-fno-exceptions`` flag is also
+supported, though the default is ``-fexceptions``. PNaCl will support full
+zero-cost exception handling in the future.
+
+.. note:: When using naclports_ or other prebuilt static libraries, you don't
+          need to recompile because the exception handling support is
+          implemented at link time (when all the static libraries are put
+          together with your application).
+
+.. _naclports: https://code.google.com/p/naclports
 
 NaCl supports full zero-cost C++ exception handling.
 
diff --git a/native_client_sdk/src/doc/sdk/release-notes.rst b/native_client_sdk/src/doc/sdk/release-notes.rst
index e6cfc3c..f3ccf1537 100644
--- a/native_client_sdk/src/doc/sdk/release-notes.rst
+++ b/native_client_sdk/src/doc/sdk/release-notes.rst
@@ -7,16 +7,35 @@
 The dates in the following release notes denote when Chrome and the NaCl SDK
 reached canary status. The stable release is typically 6 weeks later.
 
+
+.. Chrome/Pepper 43 (03 April 2015)
+.. ===================================
+..
+.. PNaCl
+.. -----
+..
+.. * The C11/C++11 `acquire`, `release`, and `acq_rel` memory orders are now
+   generated by default. The in-browser Chrome 42 translator supports them, the
+   SDK can therefore generate them.
+
 Chrome/Pepper 42 (20 February 2015)
 ===================================
 
+SDK
+---
+
+* The SDK now contains experimental versions of ``i686-nacl-clang``,
+  ``x86_64-nacl-clang``, and ``arm-nacl-clang`` as well as the ``clang++``
+  equivalents. These toolchains are based on the same LLVM version as PNaCl, but
+  can be used to generate NaCl ``.nexe`` files instead of translating a
+  ``.pexe`` locally or using the GCC toolchain.
+
 NaCl
 ----
 
 * The x86 NaCl validators accept instructions from the FMA3 extensions, as well
   as AVX2 instructions (except `VGATHER`).
 
-
 PNaCl
 -----
 
@@ -24,7 +43,10 @@
   used to upgrade all accesses to `seq_cst`. It still upgrades `consume` to
   `acquire` (no compiler currently implements `consume`), and `relaxed` to
   `seq_cst` (to conservatively avoid platform differences due to out-of-thin-air
-  problems).
+  problems). This is currently disabled by default in the SDK so that the
+  in-browser translator installed on users' machines has time to gain this
+  support. Developers can turn it on by passing the
+  ``-pnacl-memory-order-seq-cst-only=false`` flag to ``opt``.
 * PNaCl handles nested struct type expansion, which allows it to better support
   non-C languages such as Rust.
 * PNaCl breaks up many integer operations over 64-bits into individual 64-bit
diff --git a/native_client_sdk/src/tools/nacl_config.py b/native_client_sdk/src/tools/nacl_config.py
index 67542b7f..146563be 100755
--- a/native_client_sdk/src/tools/nacl_config.py
+++ b/native_client_sdk/src/tools/nacl_config.py
@@ -119,7 +119,7 @@
       ExpectArch(arch, VALID_ARCHES)
 
     if arch == 'arm':
-      Expect(toolchain in ['newlib', 'bionic'],
+      Expect(toolchain in ['newlib', 'bionic', 'clang-newlib'],
              'The arm arch only supports newlib.')
 
 
diff --git a/native_client_sdk/src/tools/nacl_gcc.mk b/native_client_sdk/src/tools/nacl_gcc.mk
index 37fb448..edd95a19 100644
--- a/native_client_sdk/src/tools/nacl_gcc.mk
+++ b/native_client_sdk/src/tools/nacl_gcc.mk
@@ -26,7 +26,7 @@
 X86_64_NM := $(shell $(NACL_CONFIG) -t $(TOOLCHAIN) -a x86_64 --tool=nm)
 endif
 
-ifneq (,$(findstring $(TOOLCHAIN),newlib bionic))
+ifneq (,$(findstring $(TOOLCHAIN),newlib bionic clang-newlib))
 ARM_SUPPORT=1
 endif
 
diff --git a/net/BUILD.gn b/net/BUILD.gn
index 4935b67..76a5cf8 100644
--- a/net/BUILD.gn
+++ b/net/BUILD.gn
@@ -38,7 +38,7 @@
 # pretty confident that mmap-ing the index would not hurt any existing x86
 # android devices, but we cannot be so sure about the variety of ARM devices.
 # So enable it for x86 only for now.
-posix_avoid_mmap = is_android && cpu_arch != "x86"
+posix_avoid_mmap = is_android && current_cpu != "x86"
 
 # WebSockets and socket stream code are used everywhere except iOS.
 enable_websockets = !is_ios
@@ -61,14 +61,6 @@
   }
 }
 
-# Disables Windows warning about size to int truncations.
-# TODO(jschuh): crbug.com/167187 fix this and delete this config.
-config("net_win_size_truncation") {
-  if (is_win) {
-    cflags = [ "/wd4267" ]
-  }
-}
-
 component("net") {
   sources =
       gypi_values.net_nacl_common_sources + gypi_values.net_non_nacl_sources
@@ -83,13 +75,16 @@
     "DLOPEN_KERBEROS",
     "NET_IMPLEMENTATION",
   ]
-  configs += [ ":net_win_size_truncation" ]
+
+  # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+  configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
   public_configs = [ ":net_config" ]
   include_dirs = []
 
   public_deps = [
     "//crypto",
     "//crypto:platform",
+    "//url",
   ]
   deps = [
     ":net_resources",
@@ -548,6 +543,7 @@
   configs += [ "//build/config/compiler:wexit_time_destructors" ]
   deps = [
     ":net",
+    "//base",
     "//sql:sql",
   ]
 }
@@ -568,8 +564,8 @@
     "server/web_socket_encoder.h",
   ]
   configs += [
+    "//build/config/compiler:no_size_t_to_int_warning",
     "//build/config/compiler:wexit_time_destructors",
-    ":net_win_size_truncation",
   ]
   deps = [
     ":net",
@@ -593,7 +589,8 @@
     "tools/dump_cache/url_utilities.h",
   ]
 
-  configs += [ ":net_win_size_truncation" ]
+  # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+  configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
 
   deps = [
     "//base",
@@ -691,7 +688,8 @@
     "url_request/url_request_test_util.h",
   ]
 
-  configs += [ ":net_win_size_truncation" ]
+  # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+  configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
 
   public_deps = [
     "//base",
@@ -774,7 +772,7 @@
 
     defines = [ "NET_IMPLEMENTATION" ]
     configs += [
-      ":net_win_size_truncation",
+      "//build/config/compiler:no_size_t_to_int_warning",
       "//build/config/compiler:wexit_time_destructors",
     ]
 
@@ -800,6 +798,7 @@
     public_deps = [
       ":mojo_type_converters",
       ":net",
+      "//base",
       "//net/interfaces",
       "//third_party/mojo/src/mojo/public/cpp/bindings",
     ]
@@ -825,7 +824,9 @@
     sources = [
       "tools/crash_cache/crash_cache.cc",
     ]
-    configs += [ ":net_win_size_truncation" ]
+
+    # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+    configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
     deps = [
       ":net",
       ":test_support",
@@ -838,7 +839,9 @@
     sources = [
       "tools/crl_set_dump/crl_set_dump.cc",
     ]
-    configs += [ ":net_win_size_truncation" ]
+
+    # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+    configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
     deps = [
       ":net",
       "//base",
@@ -850,7 +853,9 @@
     sources = [
       "tools/dns_fuzz_stub/dns_fuzz_stub.cc",
     ]
-    configs += [ ":net_win_size_truncation" ]
+
+    # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+    configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
     deps = [
       ":net",
       "//base",
@@ -874,7 +879,9 @@
     sources = [
       "tools/get_server_time/get_server_time.cc",
     ]
-    configs += [ ":net_win_size_truncation" ]
+
+    # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+    configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
     deps = [
       ":net",
       "//base",
@@ -925,7 +932,9 @@
     sources = [
       "disk_cache/blockfile/stress_cache.cc",
     ]
-    configs += [ ":net_win_size_truncation" ]
+
+    # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+    configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
     deps = [
       ":net",
       ":test_support",
@@ -937,7 +946,9 @@
     sources = [
       "tools/tld_cleanup/tld_cleanup.cc",
     ]
-    configs += [ ":net_win_size_truncation" ]
+
+    # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+    configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
     deps = [
       "//base",
       "//base:i18n",
@@ -1013,6 +1024,7 @@
       "tools/flip_server/spdy_interface_test.cc",
     ]
     deps = [
+      ":balsa",
       ":flip_in_mem_edsm_server_base",
       ":net",
       ":test_support",
@@ -1028,6 +1040,7 @@
       "tools/flip_server/flip_in_mem_edsm_server.cc",
     ]
     deps = [
+      ":balsa",
       ":flip_in_mem_edsm_server_base",
       ":net",
       "//base",
@@ -1084,6 +1097,8 @@
       "tools/quic/quic_client_bin.cc",
     ]
     deps = [
+      ":balsa",
+      ":epoll_server",
       ":quic_base",
       ":net",
       "//base",
@@ -1161,7 +1176,8 @@
   test("net_unittests") {
     sources = gypi_values.net_test_sources
 
-    configs += [ ":net_win_size_truncation" ]
+    # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+    configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
     defines = []
 
     deps = [
@@ -1178,7 +1194,9 @@
       "//crypto",
       "//crypto:platform",
       "//crypto:test_support",
+      "//gin",
       "//net/base/registry_controlled_domains",
+      "//sql",
       "//testing/gmock",
       "//testing/gtest",
       "//third_party/zlib",
diff --git a/net/android/keystore_openssl.cc b/net/android/keystore_openssl.cc
index 886b9ee..ff794dbb 100644
--- a/net/android/keystore_openssl.cc
+++ b/net/android/keystore_openssl.cc
@@ -23,6 +23,7 @@
 #include "crypto/openssl_util.h"
 #include "net/android/keystore.h"
 #include "net/android/legacy_openssl.h"
+#include "net/ssl/scoped_openssl_types.h"
 #include "net/ssl/ssl_client_cert_type.h"
 
 // IMPORTANT NOTE: The following code will currently only work when used
@@ -67,9 +68,6 @@
 
 namespace {
 
-using ScopedPKCS8_PRIV_KEY_INFO =
-    crypto::ScopedOpenSSL<PKCS8_PRIV_KEY_INFO, PKCS8_PRIV_KEY_INFO_free>::Type;
-
 extern const RSA_METHOD android_rsa_method;
 extern const ECDSA_METHOD android_ecdsa_method;
 
diff --git a/net/android/keystore_unittest.cc b/net/android/keystore_unittest.cc
index 15fec4bf..ae03b3e 100644
--- a/net/android/keystore_unittest.cc
+++ b/net/android/keystore_unittest.cc
@@ -25,11 +25,11 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
 #include "crypto/openssl_util.h"
-#include "crypto/scoped_openssl_types.h"
 #include "jni/AndroidKeyStoreTestUtil_jni.h"
 #include "net/android/keystore.h"
 #include "net/android/keystore_openssl.h"
 #include "net/base/test_data_directory.h"
+#include "net/ssl/scoped_openssl_types.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 // Technical note:
@@ -57,10 +57,6 @@
 
 namespace {
 
-typedef crypto::ScopedOpenSSL<PKCS8_PRIV_KEY_INFO,
-                              PKCS8_PRIV_KEY_INFO_free>::Type
-    ScopedPKCS8_PRIV_KEY_INFO;
-
 typedef base::android::ScopedJavaLocalRef<jobject> ScopedJava;
 
 JNIEnv* InitEnv() {
diff --git a/net/base/dir_header.html b/net/base/dir_header.html
index 7cf9c80..cb22787c 100644
--- a/net/base/dir_header.html
+++ b/net/base/dir_header.html
@@ -4,6 +4,7 @@
 
 <head>
 <meta charset="utf-8">
+<meta name="google" value="notranslate">
 
 <script>
 function addRow(name, url, isdir, size, date_modified) {
diff --git a/net/base/keygen_handler_openssl.cc b/net/base/keygen_handler_openssl.cc
index aeb64a8..38bb81a 100644
--- a/net/base/keygen_handler_openssl.cc
+++ b/net/base/keygen_handler_openssl.cc
@@ -23,7 +23,7 @@
   if (stores_key_)
     OpenSSLPrivateKeyStore::StoreKeyPair(url_, pkey);
 
-  crypto::ScopedOpenSSL<NETSCAPE_SPKI, NETSCAPE_SPKI_free>::Type spki(
+  crypto::ScopedOpenSSL<NETSCAPE_SPKI, NETSCAPE_SPKI_free> spki(
       NETSCAPE_SPKI_new());
   ASN1_STRING_set(spki.get()->spkac->challenge,
                   challenge_.data(), challenge_.size());
diff --git a/net/base/net_util_win.cc b/net/base/net_util_win.cc
index 6658358..5cd4a42d 100644
--- a/net/base/net_util_win.cc
+++ b/net/base/net_util_win.cc
@@ -12,6 +12,7 @@
 #include "base/files/file_path.h"
 #include "base/lazy_instance.h"
 #include "base/memory/scoped_ptr.h"
+#include "base/profiler/scoped_tracker.h"
 #include "base/strings/string_piece.h"
 #include "base/strings/string_util.h"
 #include "base/strings/sys_string_conversions.h"
@@ -200,6 +201,10 @@
 }
 
 WifiPHYLayerProtocol GetWifiPHYLayerProtocol() {
+  // TODO(rtenneti): Remove ScopedTracker below once crbug.com/422516 is fixed.
+  tracked_objects::ScopedTracker tracking_profile1(
+      FROM_HERE_WITH_EXPLICIT_FUNCTION(
+          "422516 net_util_win::GetWifiPHYLayerProtocol1"));
   const internal::WlanApi& wlanapi = internal::WlanApi::GetInstance();
   if (!wlanapi.initialized)
     return WIFI_PHY_LAYER_PROTOCOL_NONE;
@@ -207,10 +212,18 @@
   internal::WlanHandle client;
   DWORD cur_version = 0;
   const DWORD kMaxClientVersion = 2;
+  // TODO(rtenneti): Remove ScopedTracker below once crbug.com/422516 is fixed.
+  tracked_objects::ScopedTracker tracking_profile2(
+      FROM_HERE_WITH_EXPLICIT_FUNCTION(
+          "422516 net_util_win::GetWifiPHYLayerProtocol2"));
   DWORD result = wlanapi.OpenHandle(kMaxClientVersion, &cur_version, &client);
   if (result != ERROR_SUCCESS)
     return WIFI_PHY_LAYER_PROTOCOL_NONE;
 
+  // TODO(rtenneti): Remove ScopedTracker below once crbug.com/422516 is fixed.
+  tracked_objects::ScopedTracker tracking_profile3(
+      FROM_HERE_WITH_EXPLICIT_FUNCTION(
+          "422516 net_util_win::GetWifiPHYLayerProtocol3"));
   WLAN_INTERFACE_INFO_LIST* interface_list_ptr = NULL;
   result = wlanapi.enum_interfaces_func(client.Get(), NULL,
                                         &interface_list_ptr);
@@ -219,6 +232,10 @@
   scoped_ptr<WLAN_INTERFACE_INFO_LIST, internal::WlanApiDeleter> interface_list(
       interface_list_ptr);
 
+  // TODO(rtenneti): Remove ScopedTracker below once crbug.com/422516 is fixed.
+  tracked_objects::ScopedTracker tracking_profile4(
+      FROM_HERE_WITH_EXPLICIT_FUNCTION(
+          "422516 net_util_win::GetWifiPHYLayerProtocol4"));
   // Assume at most one connected wifi interface.
   WLAN_INTERFACE_INFO* info = NULL;
   for (unsigned i = 0; i < interface_list->dwNumberOfItems; ++i) {
@@ -235,6 +252,10 @@
   WLAN_CONNECTION_ATTRIBUTES* conn_info_ptr;
   DWORD conn_info_size = 0;
   WLAN_OPCODE_VALUE_TYPE op_code;
+  // TODO(rtenneti): Remove ScopedTracker below once crbug.com/422516 is fixed.
+  tracked_objects::ScopedTracker tracking_profile5(
+      FROM_HERE_WITH_EXPLICIT_FUNCTION(
+          "422516 net_util_win::GetWifiPHYLayerProtocol5"));
   result = wlanapi.query_interface_func(
       client.Get(), &info->InterfaceGuid, wlan_intf_opcode_current_connection,
       NULL, &conn_info_size, reinterpret_cast<VOID**>(&conn_info_ptr),
diff --git a/net/base/openssl_private_key_store_android.cc b/net/base/openssl_private_key_store_android.cc
index 56a63d2..a917d589 100644
--- a/net/base/openssl_private_key_store_android.cc
+++ b/net/base/openssl_private_key_store_android.cc
@@ -10,8 +10,8 @@
 #include "base/logging.h"
 #include "base/memory/singleton.h"
 #include "crypto/openssl_util.h"
-#include "crypto/scoped_openssl_types.h"
 #include "net/android/network_library.h"
+#include "net/ssl/scoped_openssl_types.h"
 
 namespace net {
 
@@ -30,9 +30,8 @@
   // in a format that is incompatible with what the platform expects.
   unsigned char* private_key = NULL;
   int private_len = 0;
-  crypto::ScopedOpenSSL<PKCS8_PRIV_KEY_INFO, PKCS8_PRIV_KEY_INFO_free>::Type
-      pkcs8(EVP_PKEY2PKCS8(pkey));
-  if (pkcs8.get() != NULL) {
+  ScopedPKCS8_PRIV_KEY_INFO pkcs8(EVP_PKEY2PKCS8(pkey));
+  if (!pkcs8) {
     private_len = i2d_PKCS8_PRIV_KEY_INFO(pkcs8.get(), &private_key);
   }
   bool ret = false;
diff --git a/net/base/registry_controlled_domains/effective_tld_names.dat b/net/base/registry_controlled_domains/effective_tld_names.dat
index d36987a1..d6d64f7 100644
--- a/net/base/registry_controlled_domains/effective_tld_names.dat
+++ b/net/base/registry_controlled_domains/effective_tld_names.dat
@@ -1133,7 +1133,7 @@
 tv.im
 
 // in : http://en.wikipedia.org/wiki/.in
-// see also: http://www.inregistry.in/policies/
+// see also: https://registry.in/Policies
 // Please note, that nic.in is not an offical eTLD, but used by most
 // government institutions.
 in
@@ -6783,8 +6783,7 @@
 *.zw
 
 
-// NEW TLDS SECTION FROM https://newgtlds.icann.org/newgtlds.csv >>> Updated on: 2014-12-12T06:02:07Z <<<
-
+// List of new gTLDs imported from https://newgtlds.icann.org/newgtlds.csv on 2015-01-27T00:02:07Z
 
 // abb : 2014-10-24 ABB Ltd
 abb
@@ -6807,6 +6806,9 @@
 // accountants : 2014-03-20 Knob Town, LLC
 accountants
 
+// aco : 2015-01-08 ACO Severin Ahlmann GmbH & Co. KG
+aco
+
 // active : 2014-05-01 The Active Network, Inc
 active
 
@@ -6828,12 +6830,21 @@
 // agency : 2013-11-14 Steel Falls, LLC
 agency
 
+// aig : 2014-12-18 American International Group, Inc.
+aig
+
 // airforce : 2014-03-06 United TLD Holdco Ltd.
 airforce
 
 // airtel : 2014-10-24 Bharti Airtel Limited
 airtel
 
+// alibaba : 2015-01-15 Alibaba Group Holding Limited
+alibaba
+
+// alipay : 2015-01-15 Alibaba Group Holding Limited
+alipay
+
 // allfinanz : 2014-07-03 Allfinanz Deutsche Vermögensberatung Aktiengesellschaft
 allfinanz
 
@@ -6843,9 +6854,15 @@
 // amsterdam : 2014-07-24 Gemeente Amsterdam
 amsterdam
 
+// analytics : 2014-12-18 Campus IP LLC
+analytics
+
 // android : 2014-08-07 Charleston Road Registry Inc.
 android
 
+// anquan : 2015-01-08 QIHOO 360 TECHNOLOGY CO. LTD.
+anquan
+
 // apartments : 2014-12-11 June Maple, LLC
 apartments
 
@@ -6867,25 +6884,37 @@
 // associates : 2014-03-06 Baxter Hill, LLC
 associates
 
-// attorney : 2014-03-20 undefined
+// attorney : 2014-03-20  
 attorney
 
-// auction : 2014-03-20 undefined
+// auction : 2014-03-20  
 auction
 
 // audio : 2014-03-20 Uniregistry, Corp.
 audio
 
+// author : 2014-12-18 Amazon EU S.à r.l.
+author
+
 // auto : 2014-11-13 Uniregistry, Corp.
 auto
 
 // autos : 2014-01-09 DERAutos, LLC
 autos
 
+// avianca : 2015-01-08 Aerovias del Continente Americano S.A. Avianca
+avianca
+
 // axa : 2013-12-19 AXA SA
 axa
 
-// band : 2014-06-12 undefined
+// azure : 2014-12-18 Microsoft Corporation
+azure
+
+// baidu : 2015-01-08 Baidu, Inc.
+baidu
+
+// band : 2014-06-12  
 band
 
 // bank : 2014-09-25 fTLD Registry Services LLC
@@ -6912,6 +6941,9 @@
 // bayern : 2014-01-23 Bayern Connect GmbH
 bayern
 
+// bbc : 2014-12-18 British Broadcasting Corporation
+bbc
+
 // bbva : 2014-10-02 BANCO BILBAO VIZCAYA ARGENTARIA, S.A.
 bbva
 
@@ -6921,6 +6953,9 @@
 // beer : 2014-01-09 Top Level Domain Holdings Limited
 beer
 
+// bentley : 2014-12-18 Bentley Motors Limited
+bentley
+
 // berlin : 2013-10-31 dotBERLIN GmbH & Co. KG
 berlin
 
@@ -6939,6 +6974,9 @@
 // bike : 2013-08-27 Grand Hollow, LLC
 bike
 
+// bing : 2014-12-18 Microsoft Corporation
+bing
+
 // bingo : 2014-12-04 Sand Cedar, LLC
 bingo
 
@@ -6981,9 +7019,24 @@
 // boo : 2014-01-30 Charleston Road Registry Inc.
 boo
 
+// boots : 2015-01-08 THE BOOTS COMPANY PLC
+boots
+
+// bot : 2014-12-18 Amazon EU S.à r.l.
+bot
+
 // boutique : 2013-11-14 Over Galley, LLC
 boutique
 
+// bradesco : 2014-12-18 Banco Bradesco S.A.
+bradesco
+
+// bridgestone : 2014-12-18 Bridgestone Corporation
+bridgestone
+
+// broadway : 2014-12-22 Celebrate Broadway, Inc.
+broadway
+
 // broker : 2014-12-11 IG Group Holdings PLC
 broker
 
@@ -7002,6 +7055,9 @@
 // business : 2013-11-07 Spring Cross, LLC
 business
 
+// buy : 2014-12-18 Amazon EU S.à r.l.
+buy
+
 // buzz : 2013-10-02 DOTSTRATEGY CO.
 buzz
 
@@ -7014,6 +7070,9 @@
 // cal : 2014-07-24 Charleston Road Registry Inc.
 cal
 
+// call : 2014-12-18 Amazon EU S.à r.l.
+call
+
 // camera : 2013-08-27 Atomic Maple, LLC
 camera
 
@@ -7032,6 +7091,9 @@
 // capital : 2014-03-06 Delta Mill, LLC
 capital
 
+// car : 2015-01-22 Charleston Road Registry Inc.
+car
+
 // caravan : 2013-12-12 Caravan International, Inc.
 caravan
 
@@ -7059,6 +7121,9 @@
 // cash : 2014-03-06 Delta Lake, LLC
 cash
 
+// casino : 2014-12-18 Binky Sky, LLC
+casino
+
 // catering : 2013-12-05 New Falls. LLC
 catering
 
@@ -7104,6 +7169,12 @@
 // church : 2014-02-06 Holly Fileds, LLC
 church
 
+// circle : 2014-12-18 Amazon EU S.à r.l.
+circle
+
+// cisco : 2014-12-22 Cisco Technology, Inc.
+cisco
+
 // citic : 2014-01-09 CITIC Group Corporation
 citic
 
@@ -7158,15 +7229,21 @@
 // computer : 2013-10-24 Pine Mill, LLC
 computer
 
+// comsec : 2015-01-08 VeriSign, Inc.
+comsec
+
 // condos : 2013-12-05 Pine House, LLC
 condos
 
 // construction : 2013-09-16 Fox Dynamite, LLC
 construction
 
-// consulting : 2013-12-05 undefined
+// consulting : 2013-12-05  
 consulting
 
+// contact : 2015-01-08 Top Level Spectrum, Inc.
+contact
+
 // contractors : 2013-09-10 Magic Woods, LLC
 contractors
 
@@ -7191,6 +7268,9 @@
 // creditcard : 2014-03-20 Binky Frostbite, LLC
 creditcard
 
+// creditunion : 2015-01-22 CUNA Performance Resources, LLC
+creditunion
+
 // cricket : 2014-10-09 dot Cricket Limited
 cricket
 
@@ -7212,6 +7292,9 @@
 // cymru : 2014-05-08 Nominet UK
 cymru
 
+// cyou : 2015-01-22 Beijing Gamease Age Digital Technology Co., Ltd.
+cyou
+
 // dabur : 2014-02-06 Dabur India Limited
 dabur
 
@@ -7236,10 +7319,13 @@
 // dclk : 2014-11-20 Charleston Road Registry Inc.
 dclk
 
+// dealer : 2014-12-22 Dealer Dot Com, Inc.
+dealer
+
 // deals : 2014-05-22 Sand Sunset, LLC
 deals
 
-// degree : 2014-03-06 undefined
+// degree : 2014-03-06  
 degree
 
 // delivery : 2014-09-11 Steel Station, LLC
@@ -7254,7 +7340,7 @@
 // dental : 2014-03-20 Tin Birch, LLC
 dental
 
-// dentist : 2014-03-20 undefined
+// dentist : 2014-03-20  
 dentist
 
 // desi : 2013-11-14 Desi Networks LLC
@@ -7305,6 +7391,9 @@
 // download : 2014-11-20 dot Support Limited
 download
 
+// dubai : 2015-01-01 Dubai Smart Government Department
+dubai
+
 // durban : 2014-03-24 ZA Central Registry NPC trading as ZA Central Registry
 durban
 
@@ -7317,6 +7406,9 @@
 // eat : 2014-01-23 Charleston Road Registry Inc.
 eat
 
+// edeka : 2014-12-18 EDEKA Verband kaufmännischer Genossenschaften e.V.
+edeka
+
 // education : 2013-11-07 Brice Way, LLC
 education
 
@@ -7374,6 +7466,9 @@
 // exposed : 2013-12-05 Victor Beach, LLC
 exposed
 
+// fage : 2014-12-18 Fage International S.A.
+fage
+
 // fail : 2014-03-06 Atomic Pipe, LLC
 fail
 
@@ -7383,7 +7478,7 @@
 // faith : 2014-11-20 dot Faith Limited
 faith
 
-// fan : 2014-03-06 undefined
+// fan : 2014-03-06  
 fan
 
 // fans : 2014-11-07 Asiamix Digital Limited
@@ -7395,9 +7490,18 @@
 // fashion : 2014-07-03 Top Level Domain Holdings Limited
 fashion
 
+// fast : 2014-12-18 Amazon EU S.à r.l.
+fast
+
 // feedback : 2013-12-19 Top Level Spectrum, Inc.
 feedback
 
+// ferrero : 2014-12-18 Ferrero Trading Lux S.A.
+ferrero
+
+// film : 2015-01-08 Motion Picture Domain Registry Pty Ltd
+film
+
 // final : 2014-10-16 Núcleo de Informação e Coordenação do Ponto BR - NIC.br
 final
 
@@ -7407,6 +7511,9 @@
 // financial : 2014-03-06 Just Cover, LLC
 financial
 
+// firestone : 2014-12-18 Bridgestone Corporation
+firestone
+
 // firmdale : 2014-03-27 Firmdale Holdings Limited
 firmdale
 
@@ -7440,13 +7547,16 @@
 // foo : 2014-01-23 Charleston Road Registry Inc.
 foo
 
+// football : 2014-12-18 Foggy Farms, LLC
+football
+
 // ford : 2014-11-13 Ford Motor Company
 ford
 
 // forex : 2014-12-11 IG Group Holdings PLC
 forex
 
-// forsale : 2014-05-22 undefined
+// forsale : 2014-05-22  
 forsale
 
 // foundation : 2013-12-05 John Dale, LLC
@@ -7464,7 +7574,7 @@
 // furniture : 2014-03-20 Lone Fields, LLC
 furniture
 
-// futbol : 2013-09-20 undefined
+// futbol : 2013-09-20  
 futbol
 
 // gal : 2013-11-07 Asociación puntoGAL
@@ -7524,9 +7634,18 @@
 // gmx : 2014-04-24 1&1 Mail & Media GmbH
 gmx
 
+// gold : 2015-01-22 June Edge, LLC
+gold
+
 // goldpoint : 2014-11-20 YODOBASHI CAMERA CO.,LTD.
 goldpoint
 
+// golf : 2014-12-18 Lone falls, LLC
+golf
+
+// goo : 2014-12-18 NTT Resonant Inc.
+goo
+
 // goog : 2014-11-20 Charleston Road Registry Inc.
 goog
 
@@ -7536,6 +7655,9 @@
 // gop : 2014-01-16 Republican State Leadership Committee, Inc.
 gop
 
+// got : 2014-12-18 Amazon EU S.à r.l.
+got
+
 // graphics : 2013-09-13 Over Madison, LLC
 graphics
 
@@ -7572,7 +7694,7 @@
 // hangout : 2014-11-13 Charleston Road Registry Inc.
 hangout
 
-// haus : 2013-12-05 undefined
+// haus : 2013-12-05  
 haus
 
 // healthcare : 2014-06-12 Silver Glen, LLC
@@ -7605,6 +7727,9 @@
 // homes : 2014-01-09 DERHomes, LLC
 homes
 
+// honda : 2014-12-18 Honda Motor Co., Ltd.
+honda
+
 // horse : 2013-11-21 Top Level Domain Holdings Limited
 horse
 
@@ -7614,6 +7739,9 @@
 // hosting : 2014-05-29 Uniregistry, Corp.
 hosting
 
+// hotmail : 2014-12-18 Microsoft Corporation
+hotmail
+
 // house : 2013-11-07 Sugar Park, LLC
 house
 
@@ -7629,6 +7757,9 @@
 // ice : 2014-10-30 IntercontinentalExchange, Inc.
 ice
 
+// icu : 2015-01-08 One.com A/S
+icu
+
 // ifm : 2014-01-30 ifm electronic gmbh
 ifm
 
@@ -7701,6 +7832,12 @@
 // joburg : 2014-03-24 ZA Central Registry NPC trading as ZA Central Registry
 joburg
 
+// jot : 2014-12-18 Amazon EU S.à r.l.
+jot
+
+// joy : 2014-12-18 Amazon EU S.à r.l.
+joy
+
 // jprs : 2014-09-18 Japan Registry Services Co., Ltd.
 jprs
 
@@ -7731,6 +7868,12 @@
 // koeln : 2014-01-09 NetCologne Gesellschaft für Telekommunikation mbH
 koeln
 
+// komatsu : 2015-01-08 Komatsu Ltd.
+komatsu
+
+// kpn : 2015-01-08 Koninklijke KPN N.V.
+kpn
+
 // krd : 2013-12-05 KRG Department of Information Technology
 krd
 
@@ -7755,7 +7898,10 @@
 // latrobe : 2014-06-16 La Trobe University
 latrobe
 
-// lawyer : 2014-03-20 undefined
+// law : 2015-01-22 Minds + Machines Group Limited
+law
+
+// lawyer : 2014-03-20  
 lawyer
 
 // lds : 2014-03-20 IRI Domain Management, LLC (\
@@ -7782,12 +7928,18 @@
 // life : 2014-02-06 Trixy Oaks, LLC
 life
 
+// lifeinsurance : 2015-01-15 American Council of Life Insurers
+lifeinsurance
+
 // lifestyle : 2014-12-11 Lifestyle Domain Holdings, Inc.
 lifestyle
 
 // lighting : 2013-08-27 John McCook, LLC
 lighting
 
+// like : 2014-12-18 Amazon EU S.à r.l.
+like
+
 // limited : 2014-03-06 Big Fest, LLC
 limited
 
@@ -7821,6 +7973,9 @@
 // lotto : 2014-04-10 Afilias Limited
 lotto
 
+// love : 2014-12-22 Merchant Law Group LLP
+love
+
 // ltd : 2014-09-25 Over Corner, LLC
 ltd
 
@@ -7845,6 +8000,9 @@
 // maison : 2013-12-05 Victor Frostbite, LLC
 maison
 
+// makeup : 2015-01-15 L'Oréal
+makeup
+
 // man : 2014-12-04 MAN SE
 man
 
@@ -7854,7 +8012,7 @@
 // mango : 2013-10-24 PUNTO FA S.L.
 mango
 
-// market : 2014-03-06 undefined
+// market : 2014-03-06  
 market
 
 // marketing : 2013-11-07 Fern Pass, LLC
@@ -7890,18 +8048,27 @@
 // miami : 2013-12-19 Top Level Domain Holdings Limited
 miami
 
+// microsoft : 2014-12-18 Microsoft Corporation
+microsoft
+
 // mini : 2014-01-09 Bayerische Motoren Werke Aktiengesellschaft
 mini
 
 // mma : 2014-11-07 MMA IARD
 mma
 
+// mobily : 2014-12-18 GreenTech Consultancy Company W.L.L.
+mobily
+
 // moda : 2013-11-07 United TLD Holdco Ltd.
 moda
 
 // moe : 2013-11-13 Interlink Co., Ltd.
 moe
 
+// moi : 2014-12-18 Amazon EU S.à r.l.
+moi
+
 // monash : 2013-09-30 Monash University
 monash
 
@@ -7914,7 +8081,7 @@
 // mormon : 2013-12-05 IRI Domain Management, LLC (\
 mormon
 
-// mortgage : 2014-03-20 undefined
+// mortgage : 2014-03-20  
 mortgage
 
 // moscow : 2013-12-19 Foundation for Assistance for Internet Technologies and Infrastructure Development (FAITID)
@@ -7944,6 +8111,9 @@
 // navy : 2014-03-06 United TLD Holdco Ltd.
 navy
 
+// nec : 2015-01-08 NEC Corporation
+nec
+
 // netbank : 2014-06-26 COMMONWEALTH BANK OF AUSTRALIA
 netbank
 
@@ -7956,6 +8126,9 @@
 // new : 2014-01-30 Charleston Road Registry Inc.
 new
 
+// news : 2014-12-18 Hidden Bloom, LLC
+news
+
 // nexus : 2014-07-24 Charleston Road Registry Inc.
 nexus
 
@@ -7974,6 +8147,9 @@
 // nissan : 2014-03-27 NISSAN MOTOR CO., LTD.
 nissan
 
+// nokia : 2015-01-08 Nokia Corporation
+nokia
+
 // norton : 2014-12-04 Symantec Corporation
 norton
 
@@ -7998,6 +8174,9 @@
 // okinawa : 2013-12-05 BusinessRalliart Inc.
 okinawa
 
+// omega : 2015-01-08 The Swatch Group Ltd
+omega
+
 // one : 2014-11-07 One.com A/S
 one
 
@@ -8007,6 +8186,9 @@
 // onl : 2013-09-16 I-Registry Ltd.
 onl
 
+// online : 2015-01-15 DotOnline Inc.
+online
+
 // ooo : 2014-01-09 INFIBEAM INCORPORATION LIMITED
 ooo
 
@@ -8076,6 +8258,12 @@
 // pictures : 2014-03-06 Foggy Sky, LLC
 pictures
 
+// pid : 2015-01-08 Top Level Spectrum, Inc.
+pid
+
+// pin : 2014-12-18 Amazon EU S.à r.l.
+pin
+
 // pink : 2013-10-01 Afilias Limited
 pink
 
@@ -8112,6 +8300,9 @@
 // prof : 2014-07-24 Charleston Road Registry Inc.
 prof
 
+// promo : 2014-12-18 Play.PROMO Oy
+promo
+
 // properties : 2013-12-05 Big Pass, LLC
 properties
 
@@ -8130,6 +8321,9 @@
 // racing : 2014-12-04 Premier Registry Limited
 racing
 
+// read : 2014-12-18 Amazon EU S.à r.l.
+read
+
 // realtor : 2014-05-29 Real Estate Domains LLC
 realtor
 
@@ -8181,7 +8375,7 @@
 // review : 2014-11-20 dot Review Limited
 review
 
-// reviews : 2013-09-13 undefined
+// reviews : 2013-09-13  
 reviews
 
 // rich : 2013-11-21 I-Registry Ltd.
@@ -8196,12 +8390,18 @@
 // rip : 2014-07-10 United TLD Holdco Ltd.
 rip
 
-// rocks : 2013-11-14 undefined
+// rocher : 2014-12-18 Ferrero Trading Lux S.A.
+rocher
+
+// rocks : 2013-11-14  
 rocks
 
 // rodeo : 2013-12-19 Top Level Domain Holdings Limited
 rodeo
 
+// room : 2014-12-18 Amazon EU S.à r.l.
+room
+
 // rsvp : 2014-05-08 Charleston Road Registry Inc.
 rsvp
 
@@ -8214,7 +8414,16 @@
 // saarland : 2013-12-12 dotSaarland GmbH
 saarland
 
-// sale : 2014-10-16 undefined
+// safe : 2014-12-18 Amazon EU S.à r.l.
+safe
+
+// safety : 2015-01-08 Safety Registry Services, LLC.
+safety
+
+// sakura : 2014-12-18 SAKURA Internet Inc.
+sakura
+
+// sale : 2014-10-16  
 sale
 
 // salon : 2014-12-11 Outer Orchard, LLC
@@ -8259,6 +8468,9 @@
 // scholarships : 2014-04-24 Scholarships.com, LLC
 scholarships
 
+// school : 2014-12-18 Little Galley, LLC
+school
+
 // schule : 2014-03-06 Outer Moon, LLC
 schule
 
@@ -8307,19 +8519,34 @@
 // shoes : 2013-10-02 Binky Galley, LLC
 shoes
 
+// shouji : 2015-01-08 QIHOO 360 TECHNOLOGY CO. LTD.
+shouji
+
 // shriram : 2014-01-23 Shriram Capital Ltd.
 shriram
 
 // singles : 2013-08-27 Fern Madison, LLC
 singles
 
+// site : 2015-01-15 DotSite Inc.
+site
+
+// skin : 2015-01-15 L'Oréal
+skin
+
 // sky : 2014-06-19 Sky IP International Ltd, a company incorporated in England and Wales, operating via its registered Swiss branch
 sky
 
+// skype : 2014-12-18 Microsoft Corporation
+skype
+
+// smile : 2014-12-18 Amazon EU S.à r.l.
+smile
+
 // social : 2013-11-07 United TLD Holdco Ltd.
 social
 
-// software : 2014-03-20 undefined
+// software : 2014-03-20  
 software
 
 // sohu : 2013-12-19 Sohu.com Limited
@@ -8331,6 +8558,9 @@
 // solutions : 2013-11-07 Silver Cover, LLC
 solutions
 
+// sony : 2015-01-08 Sony Corporation
+sony
+
 // soy : 2014-01-23 Charleston Road Registry Inc.
 soy
 
@@ -8346,6 +8576,9 @@
 // stada : 2014-11-13 STADA Arzneimittel AG
 stada
 
+// star : 2015-01-08 Star India Private Limited
+star
+
 // statoil : 2014-12-04 Statoil ASA
 statoil
 
@@ -8355,12 +8588,21 @@
 // stcgroup : 2014-10-09 Saudi Telecom Company
 stcgroup
 
+// stockholm : 2014-12-18 Stockholms kommun
+stockholm
+
+// storage : 2014-12-22 Self Storage Company LLC
+storage
+
 // study : 2014-12-11 OPEN UNIVERSITIES AUSTRALIA PTY LTD
 study
 
 // style : 2014-12-04 Binky Moon, LLC
 style
 
+// sucks : 2014-12-22 Vox Populi Registry Inc.
+sucks
+
 // supplies : 2013-12-19 Atomic Fields, LLC
 supplies
 
@@ -8379,6 +8621,9 @@
 // suzuki : 2014-02-20 SUZUKI MOTOR CORPORATION
 suzuki
 
+// swatch : 2015-01-08 The Swatch Group Ltd
+swatch
+
 // swiss : 2014-10-16 Swiss Confederation
 swiss
 
@@ -8397,6 +8642,9 @@
 // taipei : 2014-07-10 Taipei City Government
 taipei
 
+// taobao : 2015-01-15 Alibaba Group Holding Limited
+taobao
+
 // tatar : 2014-04-24 Limited Liability Company \
 tatar
 
@@ -8433,6 +8681,9 @@
 // tirol : 2014-04-24 punkt Tirol GmbH
 tirol
 
+// tmall : 2015-01-15 Alibaba Group Holding Limited
+tmall
+
 // today : 2013-09-20 Pearl Woods, LLC
 today
 
@@ -8445,9 +8696,15 @@
 // top : 2014-03-20 Jiangsu Bangning Science & Technology Co.,Ltd.
 top
 
+// toray : 2014-12-18 Toray Industries, Inc.
+toray
+
 // toshiba : 2014-04-10 TOSHIBA Corporation
 toshiba
 
+// tours : 2015-01-22 Sugar Station, LLC
+tours
+
 // town : 2014-03-06 Koko Moon, LLC
 town
 
@@ -8463,12 +8720,15 @@
 // training : 2013-11-07 Wild Willow, LLC
 training
 
-// trust : 2014-10-16 undefined
+// trust : 2014-10-16  
 trust
 
 // tui : 2014-07-03 TUI AG
 tui
 
+// tushu : 2014-12-18 Amazon EU S.à r.l.
+tushu
+
 // ubs : 2014-12-11 UBS AG
 ubs
 
@@ -8496,18 +8756,21 @@
 // versicherung : 2014-03-20 dotversicherung-registry GmbH
 versicherung
 
-// vet : 2014-03-06 undefined
+// vet : 2014-03-06  
 vet
 
 // viajes : 2013-10-17 Black Madison, LLC
 viajes
 
-// video : 2014-10-16 undefined
+// video : 2014-10-16  
 video
 
 // villas : 2013-12-05 New Sky, LLC
 villas
 
+// vip : 2015-01-22 Minds + Machines Group Limited
+vip
+
 // virgin : 2014-09-25 Virgin Enterprises Limited
 virgin
 
@@ -8550,9 +8813,18 @@
 // wang : 2013-10-24 Zodiac Leo Limited
 wang
 
+// wanggou : 2014-12-18 Amazon EU S.à r.l.
+wanggou
+
 // watch : 2013-11-14 Sand Shadow, LLC
 watch
 
+// watches : 2014-12-22 Richemont DNS Inc.
+watches
+
+// weather : 2015-01-08 The Weather Channel, LLC
+weather
+
 // webcam : 2014-01-23 dot Webcam Limited
 webcam
 
@@ -8580,6 +8852,9 @@
 // win : 2014-11-20 First Registry Limited
 win
 
+// windows : 2014-12-18 Microsoft Corporation
+windows
+
 // wme : 2014-02-13 William Morris Endeavor Entertainment, LLC
 wme
 
@@ -8598,12 +8873,21 @@
 // wtf : 2014-03-06 Hidden Way, LLC
 wtf
 
+// xbox : 2014-12-18 Microsoft Corporation
+xbox
+
 // xerox : 2014-10-24 Xerox DNHC LLC
 xerox
 
+// xihuan : 2015-01-08 QIHOO 360 TECHNOLOGY CO. LTD.
+xihuan
+
 // xin : 2014-12-11 Elegant Leader Limited
 xin
 
+// xn--11b4c3d : 2015-01-15 VeriSign Sarl
+कॉम
+
 // xn--1qqw23a : 2014-01-09 Guangzhou YU Wei Information Technology Co., Ltd.
 佛山
 
@@ -8616,6 +8900,12 @@
 // xn--3ds443g : 2013-09-08 TLD REGISTRY LIMITED
 在线
 
+// xn--3pxu8k : 2015-01-15 VeriSign Sarl
+点看
+
+// xn--42c2d9a : 2015-01-15 VeriSign Sarl
+คอม
+
 // xn--45q11c : 2013-11-21 Zodiac Scorpio Limited
 八卦
 
@@ -8628,6 +8918,9 @@
 // xn--55qx5d : 2013-11-14 Computer Network Information Center of Chinese Academy of Sciences (China Internet Network Information Center)
 公司
 
+// xn--5tzm5g : 2014-12-22 Global Website TLD Asia Limited
+网站
+
 // xn--6frz82g : 2013-09-23 Afilias Limited
 移动
 
@@ -8643,6 +8936,9 @@
 // xn--80aswg : 2013-07-14 CORE Association
 сайт
 
+// xn--9dbq2a : 2015-01-15 VeriSign Sarl
+קום
+
 // xn--9et52u : 2014-06-12 RISE VICTORY LIMITED
 时尚
 
@@ -8652,6 +8948,9 @@
 // xn--c1avg : 2013-11-14 Public Interest Registry
 орг
 
+// xn--c2br7g : 2015-01-15 VeriSign Sarl
+नेट
+
 // xn--cg4bki : 2013-09-27 SAMSUNG SDS CO., LTD
 삼성
 
@@ -8667,9 +8966,15 @@
 // xn--d1acj3b : 2013-11-20 The Foundation for Network Initiatives “The Smart Internet”
 дети
 
+// xn--eckvdtc9d : 2014-12-18 Amazon EU S.à r.l.
+ポイント
+
 // xn--efvy88h : 2014-08-22 Xinhua News Agency Guangdong Branch 新华通讯社广东分社
 新闻
 
+// xn--fhbei : 2015-01-15 VeriSign Sarl
+كوم
+
 // xn--fiq228c5hs : 2013-09-08 TLD REGISTRY LIMITED
 中文网
 
@@ -8694,9 +8999,18 @@
 // xn--io0a7i : 2013-11-14 Computer Network Information Center of Chinese Academy of Sciences (China Internet Network Information Center)
 网络
 
+// xn--j1aef : 2015-01-15 VeriSign Sarl
+ком
+
+// xn--jlq61u9w7b : 2015-01-08 Nokia Corporation
+诺基亚
+
 // xn--kcrx77d1x4a : 2014-11-07 Koninklijke Philips N.V.
 飞利浦
 
+// xn--kpu716f : 2014-12-22 Richemont DNS Inc.
+手表
+
 // xn--kput3i : 2014-02-13 Beijing RITT-Net Technology Development Co., Ltd
 手机
 
@@ -8706,9 +9020,15 @@
 // xn--mgbab2bd : 2013-10-31 CORE Association
 بازار
 
+// xn--mgbb9fbpob : 2014-12-18 GreenTech Consultancy Company W.L.L.
+موبايلي
+
 // xn--mgbt3dhd : 2014-09-04 Asia Green IT System Bilgisayar San. ve Tic. Ltd. Sti.
 همراه
 
+// xn--mk1bu44c : 2015-01-15 VeriSign Sarl
+닷컴
+
 // xn--mxtq1m : 2014-03-06 Net-Chinese Co., Ltd.
 政府
 
@@ -8730,6 +9050,12 @@
 // xn--p1acf : 2013-12-12 Rusnames Limited
 рус
 
+// xn--pbt977c : 2014-12-22 Richemont DNS Inc.
+珠宝
+
+// xn--pssy2u : 2015-01-15 VeriSign Sarl
+大拿
+
 // xn--q9jyb4c : 2013-09-17 Charleston Road Registry Inc.
 みんな
 
@@ -8739,9 +9065,15 @@
 // xn--rhqv96g : 2013-09-11 Stable Tone Limited
 世界
 
-// xn--ses554g : 2014-01-16 HU YI GLOBAL INFORMATION RESOURCES (HOLDING) COMPANY. HONGKONG LIMITED
+// xn--ses554g : 2014-01-16
 网址
 
+// xn--t60b56a : 2015-01-15 VeriSign Sarl
+닷넷
+
+// xn--tckwe : 2015-01-15 VeriSign Sarl
+コム
+
 // xn--unup4y : 2013-07-14 Spring Fields, LLC
 游戏
 
@@ -8769,6 +9101,9 @@
 // yachts : 2014-01-09 DERYachts, LLC
 yachts
 
+// yamaxun : 2014-12-18 Amazon EU S.à r.l.
+yamaxun
+
 // yandex : 2014-04-10 YANDEX, LLC
 yandex
 
@@ -8784,9 +9119,15 @@
 // youtube : 2014-05-01 Charleston Road Registry Inc.
 youtube
 
+// yun : 2015-01-08 QIHOO 360 TECHNOLOGY CO. LTD.
+yun
+
 // zara : 2014-11-07 Industria de Diseño Textil, S.A. (INDITEX, S.A.)
 zara
 
+// zero : 2014-12-18 Amazon EU S.à r.l.
+zero
+
 // zip : 2014-05-08 Charleston Road Registry Inc.
 zip
 
@@ -8796,15 +9137,17 @@
 // zuerich : 2014-11-07 Kanton Zürich (Canton of Zurich)
 zuerich
 
+
 // ===END ICANN DOMAINS===
 // ===BEGIN PRIVATE DOMAINS===
+// (Note: these are in alphabetical order by company name)
 
 // Amazon CloudFront : https://aws.amazon.com/cloudfront/
 // Submitted by Donavan Miller <donavanm@amazon.com> 2013-03-22
 cloudfront.net
 
 // Amazon Elastic Compute Cloud: https://aws.amazon.com/ec2/
-// Submitted by Osman Surkatty <osmans@amazon.com> 2014-05-20
+// Submitted by Osman Surkatty <osmans@amazon.com> 2014-12-16
 ap-northeast-1.compute.amazonaws.com
 ap-southeast-1.compute.amazonaws.com
 ap-southeast-2.compute.amazonaws.com
@@ -8813,6 +9156,7 @@
 compute.amazonaws.com
 compute-1.amazonaws.com
 eu-west-1.compute.amazonaws.com
+eu-central-1.compute.amazonaws.com
 sa-east-1.compute.amazonaws.com
 us-east-1.amazonaws.com
 us-gov-west-1.compute.amazonaws.com
@@ -8921,6 +9265,11 @@
 co.nl
 co.no
 
+// Commerce Guys, SAS
+// Submitted by Damien Tournoud <damien@commerceguys.com> 2015-01-22
+// CHROMIUM - Disabled as per https://code.google.com/p/chromium/issues/detail?id=459802
+// *.platform.sh
+
 // Cupcake : https://cupcake.io/
 // Submitted by Jonathan Rudenberg <jonathan@cupcake.io> 2013-10-08
 cupcake.is
@@ -9226,6 +9575,10 @@
 // Submitted by Jonathan Rudenberg <jonathan@flynn.io> 2014-07-12
 flynnhub.com
 
+// GDS : https://www.gov.uk/service-manual/operations/operating-servicegovuk-subdomains
+// Submitted by David Illsley <david.illsley@digital.cabinet-office.gov.uk> 2014-08-28
+service.gov.uk
+
 // GitHub, Inc.
 // Submitted by Ben Toews <btoews@github.com> 2014-02-06
 github.io
@@ -9236,7 +9589,7 @@
 ro.com
 
 // Google, Inc.
-// Submitted by Eduardo Vela <evn@google.com> 2012-10-24
+// Submitted by Eduardo Vela <evn@google.com> 2014-12-19
 appspot.com
 blogspot.ae
 blogspot.be
@@ -9284,6 +9637,7 @@
 codespot.com
 googleapis.com
 googlecode.com
+pagespeedmobilizer.com
 withgoogle.com
 
 // Heroku : https://www.heroku.com/
@@ -9336,17 +9690,19 @@
 wroc.pl
 zakopane.pl
 
+// priv.at : http://www.nic.priv.at/
+// Submitted by registry <lendl@nic.at> 2008-06-09
+priv.at
+
 // Red Hat, Inc. OpenShift : https://openshift.redhat.com/
 // Submitted by Tim Kramer <tkramer@rhcloud.com> 2012-10-24
 rhcloud.com
 
-// GDS : https://www.gov.uk/service-manual/operations/operating-servicegovuk-subdomains
-// Submitted by David Illsley <david.illsley@digital.cabinet-office.gov.uk> 2014-08-28
-service.gov.uk
-
-// priv.at : http://www.nic.priv.at/
-// Submitted by registry <lendl@nic.at> 2008-06-09
-priv.at
+// SinaAppEngine : http://sae.sina.com.cn/
+// Submitted by SinaAppEngine <saesupport@sinacloud.com> 2015-02-02
+sinaapp.com
+vipsinaapp.com
+1kapp.com
 
 // TASK geographical domains (www.task.gda.pl/uslugi/dns)
 gda.pl
diff --git a/net/base/registry_controlled_domains/effective_tld_names.gperf b/net/base/registry_controlled_domains/effective_tld_names.gperf
index 8dbfd0e3..f6e2f5f 100644
--- a/net/base/registry_controlled_domains/effective_tld_names.gperf
+++ b/net/base/registry_controlled_domains/effective_tld_names.gperf
@@ -13,6 +13,7 @@
 %%
 0.bg, 0
 1.bg, 0
+1kapp.com, 4
 2.bg, 0
 2000.hu, 0
 3.bg, 0
@@ -81,6 +82,7 @@
 accountant, 0
 accountants, 0
 achi.nagano.jp, 0
+aco, 0
 act.au, 0
 act.edu.au, 0
 active, 0
@@ -130,6 +132,7 @@
 aibetsu.hokkaido.jp, 0
 aichi.jp, 0
 aid.pl, 0
+aig, 0
 aikawa.kanagawa.jp, 0
 ainan.ehime.jp, 0
 aioi.hyogo.jp, 0
@@ -176,6 +179,8 @@
 alessandria.it, 0
 alesund.no, 0
 algard.no, 0
+alibaba, 0
+alipay, 0
 allfinanz, 0
 alsace, 0
 alstahaug.no, 0
@@ -208,6 +213,7 @@
 amusement.aero, 0
 an, 0
 an.it, 0
+analytics, 0
 anamizu.ishikawa.jp, 0
 anan.nagano.jp, 0
 anan.tokushima.jp, 0
@@ -226,6 +232,7 @@
 annaka.gunma.jp, 0
 annefrank.museum, 0
 anpachi.gifu.jp, 0
+anquan, 0
 anthro.museum, 0
 anthropology.museum, 0
 antiques.museum, 0
@@ -376,6 +383,7 @@
 austin.museum, 0
 australia.museum, 0
 austrheim.no, 0
+author, 0
 author.aero, 0
 auto, 0
 auto.pl, 0
@@ -385,6 +393,7 @@
 av.tr, 0
 avellino.it, 0
 averoy.no, 0
+avianca, 0
 aviation.museum, 0
 avocat.fr, 0
 avoues.fr, 0
@@ -400,6 +409,7 @@
 az, 0
 az.us, 0
 azumino.nagano.jp, 0
+azure, 0
 azure-mobile.net, 4
 azurewebsites.net, 4
 b.bg, 0
@@ -416,6 +426,7 @@
 bahccavuotna.no, 0
 bahn.museum, 0
 baidar.no, 0
+baidu, 0
 baikal.ru, 0
 bajddar.no, 0
 balat.no, 0
@@ -458,6 +469,7 @@
 bauhaus, 0
 bayern, 0
 bb, 0
+bbc, 0
 bbs.tr, 0
 bbva, 0
 bc.ca, 0
@@ -478,6 +490,7 @@
 bellevue.museum, 0
 belluno.it, 0
 benevento.it, 0
+bentley, 0
 beppu.oita.jp, 0
 berg.no, 0
 bergamo.it, 0
@@ -516,6 +529,7 @@
 bilbao.museum, 0
 bill.museum, 0
 bindal.no, 0
+bing, 0
 bingo, 0
 bio, 0
 bio.br, 0
@@ -625,7 +639,9 @@
 bond, 0
 bonn.museum, 0
 boo, 0
+boots, 0
 boston.museum, 0
+bot, 0
 botanical.museum, 0
 botanicalgarden.museum, 0
 botanicgarden.museum, 0
@@ -635,16 +651,19 @@
 br, 0
 br.com, 4
 br.it, 0
+bradesco, 0
 brand.se, 0
 brandywinevalley.museum, 0
 brasil.museum, 0
 bremanger.no, 0
 brescia.it, 0
+bridgestone, 0
 brindisi.it, 0
 bristol.museum, 0
 british.museum, 0
 britishcolumbia.museum, 0
 broadcast.museum, 0
+broadway, 0
 broke-it.net, 4
 broker, 0
 broker.aero, 0
@@ -677,6 +696,7 @@
 busan.kr, 0
 bushey.museum, 0
 business, 0
+buy, 0
 buyshouses.net, 4
 buzen.fukuoka.jp, 0
 buzz, 0
@@ -707,6 +727,7 @@
 cal.it, 0
 calabria.it, 0
 california.museum, 0
+call, 0
 caltanissetta.it, 0
 cam.it, 0
 cambridge.museum, 0
@@ -723,6 +744,7 @@
 capebreton.museum, 0
 capetown, 0
 capital, 0
+car, 0
 caravan, 0
 carbonia-iglesias.it, 0
 carboniaiglesias.it, 0
@@ -741,6 +763,7 @@
 casadelamoneda.museum, 0
 caserta.it, 0
 cash, 0
+casino, 0
 casino.hu, 0
 castle.museum, 0
 castres.museum, 0
@@ -901,7 +924,9 @@
 cim.br, 0
 cincinnati.museum, 0
 cinema.museum, 0
+circle, 0
 circus.museum, 0
+cisco, 0
 citic, 0
 city, 0
 city.hu, 0
@@ -1157,6 +1182,7 @@
 computer, 0
 computer.museum, 0
 computerhistory.museum, 0
+comsec, 0
 condos, 0
 conf.au, 0
 conf.lv, 0
@@ -1166,6 +1192,7 @@
 consultant.aero, 0
 consulting, 0
 consulting.aero, 0
+contact, 0
 contemporary.museum, 0
 contemporaryart.museum, 0
 contractors, 0
@@ -1202,6 +1229,7 @@
 creation.museum, 0
 credit, 0
 creditcard, 0
+creditunion, 0
 cremona.it, 0
 crew.aero, 0
 cri.nz, 0
@@ -1230,6 +1258,7 @@
 cyber.museum, 0
 cymru, 0
 cymru.museum, 0
+cyou, 0
 cz, 0
 cz.it, 0
 czeladz.pl, 0
@@ -1264,6 +1293,7 @@
 de, 0
 de.com, 4
 de.us, 0
+dealer, 0
 deals, 0
 deatnu.no, 0
 decorativearts.museum, 0
@@ -1344,6 +1374,7 @@
 drangedal.no, 0
 dreamhosters.com, 4
 drobak.no, 0
+dubai, 0
 dudinka.ru, 0
 durban, 0
 durham.museum, 0
@@ -1397,6 +1428,7 @@
 ed.cr, 0
 ed.jp, 0
 ed.pw, 0
+edeka, 0
 edogawa.tokyo.jp, 0
 edu, 0
 edu.ac, 0
@@ -1601,6 +1633,7 @@
 etne.no, 0
 etnedal.no, 0
 eu, 0
+eu-central-1.compute.amazonaws.com, 4
 eu-west-1.compute.amazonaws.com, 4
 eu.com, 4
 eu.int, 0
@@ -1622,6 +1655,7 @@
 express.aero, 0
 f.bg, 0
 f.se, 0
+fage, 0
 fail, 0
 fairwinds, 0
 faith, 0
@@ -1638,6 +1672,7 @@
 farmstead.museum, 0
 farsund.no, 0
 fashion, 0
+fast, 0
 fauske.no, 0
 fc.it, 0
 fe.it, 0
@@ -1647,6 +1682,7 @@
 feedback, 0
 fermo.it, 0
 ferrara.it, 0
+ferrero, 0
 fet.no, 0
 fetsund.no, 0
 fg.it, 0
@@ -1661,6 +1697,7 @@
 field.museum, 0
 figueres.museum, 0
 filatelia.museum, 0
+film, 0
 film.hu, 0
 film.museum, 0
 fin.ec, 0
@@ -1674,6 +1711,7 @@
 finnoy.no, 0
 firebaseapp.com, 4
 firenze.it, 0
+firestone, 0
 firm.co, 0
 firm.ht, 0
 firm.in, 0
@@ -1720,6 +1758,7 @@
 folkebibl.no, 0
 folldal.no, 0
 foo, 0
+football, 0
 for-better.biz, 4
 for-more.biz, 4
 for-our.info, 4
@@ -2022,9 +2061,12 @@
 gok.pk, 0
 gokase.miyazaki.jp, 0
 gol.no, 0
+gold, 0
 goldpoint, 0
+golf, 0
 gon.pk, 0
 gonohe.aomori.jp, 0
+goo, 0
 goog, 0
 google, 0
 googleapis.com, 4
@@ -2038,6 +2080,7 @@
 gose.nara.jp, 0
 gosen.niigata.jp, 0
 goshiki.hyogo.jp, 0
+got, 0
 gotdns.com, 4
 gotdns.org, 4
 gotemba.shizuoka.jp, 0
@@ -2472,6 +2515,7 @@
 homeunix.org, 4
 honai.ehime.jp, 0
 honbetsu.hokkaido.jp, 0
+honda, 0
 honefoss.no, 0
 hongo.hiroshima.jp, 0
 honjo.akita.jp, 0
@@ -2488,6 +2532,7 @@
 hotel.hu, 0
 hotel.lk, 0
 hotel.tz, 0
+hotmail, 0
 house, 0
 house.museum, 0
 how, 0
@@ -2531,6 +2576,7 @@
 ichinomiya.aichi.jp, 0
 ichinomiya.chiba.jp, 0
 ichinoseki.iwate.jp, 0
+icu, 0
 id, 0
 id.au, 0
 id.ir, 0
@@ -2883,9 +2929,11 @@
 jorpeland.no, 0
 joshkar-ola.ru, 0
 joso.ibaraki.jp, 0
+jot, 0
 journal.aero, 0
 journalism.museum, 0
 journalist.aero, 0
+joy, 0
 joyo.kyoto.jp, 0
 jp, 0
 jp.net, 4
@@ -3227,6 +3275,7 @@
 komae.tokyo.jp, 0
 komagane.nagano.jp, 0
 komaki.aichi.jp, 0
+komatsu, 0
 komatsu.ishikawa.jp, 0
 komatsushima.tokushima.jp, 0
 komforb.se, 0
@@ -3270,6 +3319,7 @@
 kozagawa.wakayama.jp, 0
 kozaki.chiba.jp, 0
 kp, 0
+kpn, 0
 kr, 0
 kr.com, 4
 kr.it, 0
@@ -3395,6 +3445,7 @@
 latrobe, 0
 lavagis.no, 0
 lavangen.no, 0
+law, 0
 law.pro, 0
 lawyer, 0
 laz.it, 0
@@ -3494,10 +3545,12 @@
 lier.no, 0
 lierne.no, 0
 life, 0
+lifeinsurance, 0
 lifestyle, 0
 lig.it, 0
 lighting, 0
 liguria.it, 0
+like, 0
 likes-pie.com, 4
 likescandy.com, 4
 lillehammer.no, 0
@@ -3541,6 +3594,7 @@
 lotte, 0
 lotto, 0
 louvre.museum, 0
+love, 0
 lowicz.pl, 0
 loyalist.museum, 0
 lr, 0
@@ -3598,6 +3652,7 @@
 maintenance.aero, 0
 maison, 0
 maizuru.kyoto.jp, 0
+makeup, 0
 makinohara.shizuoka.jp, 0
 makurazaki.kagoshima.jp, 0
 malatvuopmi.no, 0
@@ -3733,6 +3788,7 @@
 mibu.tochigi.jp, 0
 michigan.museum, 0
 microlight.aero, 0
+microsoft, 0
 midatlantic.museum, 0
 midori.chiba.jp, 0
 midori.gunma.jp, 0
@@ -3918,6 +3974,7 @@
 mobi.ng, 0
 mobi.tt, 0
 mobi.tz, 0
+mobily, 0
 mochizuki.nagano.jp, 0
 mod.gi, 0
 moda, 0
@@ -3927,6 +3984,7 @@
 modern.museum, 0
 modum.no, 0
 moe, 0
+moi, 0
 moka.tochigi.jp, 0
 mol.it, 0
 molde.no, 0
@@ -4176,6 +4234,7 @@
 ne.us, 0
 neat-url.com, 4
 nebraska.museum, 0
+nec, 0
 nedre-eiker.no, 0
 nemuro.hokkaido.jp, 0
 nerima.tokyo.jp, 0
@@ -4314,6 +4373,7 @@
 newjersey.museum, 0
 newmexico.museum, 0
 newport.museum, 0
+news, 0
 news.hu, 0
 newspaper.museum, 0
 newyork.museum, 0
@@ -4396,6 +4456,7 @@
 nogata.fukuoka.jp, 0
 nogi.tochigi.jp, 0
 noheji.aomori.jp, 0
+nokia, 0
 nom.ad, 0
 nom.ag, 0
 nom.br, 2
@@ -4575,6 +4636,7 @@
 omaha.museum, 0
 omasvuotna.no, 0
 ome.tokyo.jp, 0
+omega, 0
 omi.nagano.jp, 0
 omi.niigata.jp, 0
 omigawa.chiba.jp, 0
@@ -4593,6 +4655,7 @@
 onga.fukuoka.jp, 0
 onjuku.chiba.jp, 0
 onl, 0
+online, 0
 online.museum, 0
 onna.okinawa.jp, 0
 ono.fukui.jp, 0
@@ -4849,6 +4912,7 @@
 padova.it, 0
 padua.it, 0
 page, 0
+pagespeedmobilizer.com, 4
 palace.museum, 0
 palana.ru, 0
 paleo.museum, 0
@@ -4915,11 +4979,13 @@
 pics, 0
 pictet, 0
 pictures, 0
+pid, 0
 piedmont.it, 0
 piemonte.it, 0
 pila.pl, 0
 pilot.aero, 0
 pilots.museum, 0
+pin, 0
 pink, 0
 pippu.hokkaido.jp, 0
 pisa.it, 0
@@ -5023,6 +5089,7 @@
 prof, 0
 prof.pr, 0
 project.museum, 0
+promo, 0
 properties, 0
 property, 0
 pruszkow.pl, 0
@@ -5088,6 +5155,7 @@
 re, 0
 re.it, 0
 re.kr, 0
+read, 0
 readmyblog.org, 4
 realestate.pl, 0
 realtor, 0
@@ -5169,6 +5237,7 @@
 ro.com, 4
 ro.it, 0
 roan.no, 0
+rocher, 0
 rochester.museum, 0
 rockart.museum, 0
 rocks, 0
@@ -5181,6 +5250,7 @@
 rome.it, 0
 romsa.no, 0
 romskog.no, 0
+room, 0
 roros.no, 0
 rost.no, 0
 rotorcraft.aero, 0
@@ -5239,6 +5309,8 @@
 saarland, 0
 sabae.fukui.jp, 0
 sado.niigata.jp, 0
+safe, 0
+safety, 0
 safety.aero, 0
 saga.jp, 0
 saga.saga.jp, 0
@@ -5268,6 +5340,7 @@
 sakhalin.ru, 0
 saku.nagano.jp, 0
 sakuho.nagano.jp, 0
+sakura, 0
 sakura.chiba.jp, 0
 sakura.tochigi.jp, 0
 sakuragawa.ibaraki.jp, 0
@@ -5367,6 +5440,7 @@
 schoenbrunn.museum, 0
 schokoladen.museum, 0
 scholarships, 0
+school, 0
 school.museum, 0
 school.na, 0
 school.nz, 0
@@ -5555,6 +5629,7 @@
 shop.ht, 0
 shop.hu, 0
 shop.pl, 0
+shouji, 0
 show.aero, 0
 showa.fukushima.jp, 0
 showa.gunma.jp, 0
@@ -5574,9 +5649,11 @@
 silk.museum, 0
 simbirsk.ru, 0
 simple-url.com, 4
+sinaapp.com, 4
 singles, 0
 siracusa.it, 0
 sirdal.no, 0
+site, 0
 sj, 0
 sk, 0
 sk.ca, 0
@@ -5589,6 +5666,7 @@
 ski.no, 0
 skien.no, 0
 skierva.no, 0
+skin, 0
 skiptvet.no, 0
 skjak.no, 0
 skjervoy.no, 0
@@ -5598,6 +5676,7 @@
 skole.museum, 0
 sky, 0
 skydiving.aero, 0
+skype, 0
 sl, 0
 slask.pl, 0
 slattum.no, 0
@@ -5607,6 +5686,7 @@
 slupsk.pl, 0
 sm, 0
 sm.ua, 0
+smile, 0
 smola.no, 0
 smolensk.ru, 0
 sn, 0
@@ -5644,6 +5724,7 @@
 sondrio.it, 0
 songdalen.no, 0
 soni.nara.jp, 0
+sony, 0
 soo.kagoshima.jp, 0
 sopot.pl, 4
 sor-aurdal.no, 0
@@ -5686,6 +5767,7 @@
 stalbans.museum, 0
 stalowa-wola.pl, 0
 stange.no, 0
+star, 0
 starachowice.pl, 0
 stargard.pl, 0
 starnberg.museum, 0
@@ -5708,9 +5790,11 @@
 stjohn.museum, 0
 stjordal.no, 0
 stjordalshalsen.no, 0
+stockholm, 0
 stockholm.museum, 0
 stokke.no, 0
 stor-elvdal.no, 0
+storage, 0
 stord.no, 0
 stordal.no, 0
 store.bb, 0
@@ -5731,6 +5815,7 @@
 stv.ru, 0
 style, 0
 su, 0
+sucks, 0
 sue.fukuoka.jp, 0
 suedtirol.it, 0
 suginami.tokyo.jp, 0
@@ -5774,6 +5859,7 @@
 sveio.no, 0
 svelvik.no, 0
 svizzera.museum, 0
+swatch, 0
 sweden.museum, 0
 swidnica.pl, 0
 swiebodzin.pl, 0
@@ -5876,6 +5962,7 @@
 tananger.no, 0
 tank.museum, 0
 tanohata.iwate.jp, 0
+taobao, 0
 tara.saga.jp, 0
 tarama.okinawa.jp, 0
 taranto.it, 0
@@ -5963,6 +6050,7 @@
 tm.pl, 0
 tm.ro, 0
 tm.se, 0
+tmall, 0
 tmp.br, 0
 tn, 0
 tn.it, 0
@@ -6027,6 +6115,7 @@
 top, 0
 topology.museum, 0
 torahime.shiga.jp, 0
+toray, 0
 toride.ibaraki.jp, 0
 torino.it, 0
 torino.museum, 0
@@ -6043,6 +6132,7 @@
 touch.museum, 0
 tourism.pl, 0
 tourism.tn, 0
+tours, 0
 towada.aomori.jp, 0
 town, 0
 town.museum, 0
@@ -6169,6 +6259,7 @@
 turin.it, 0
 turystyka.pl, 0
 tuscany.it, 0
+tushu, 0
 tuva.ru, 0
 tv, 0
 tv.bb, 0
@@ -6394,6 +6485,8 @@
 vindafjord.no, 0
 vinnica.ua, 0
 vinnytsia.ua, 0
+vip, 0
+vipsinaapp.com, 4
 virgin, 0
 virginia.museum, 0
 virtual.museum, 0
@@ -6455,6 +6548,7 @@
 wallonie.museum, 0
 walter, 0
 wang, 0
+wanggou, 0
 wanouchi.gifu.jp, 0
 war.museum, 0
 warabi.saitama.jp, 0
@@ -6467,8 +6561,10 @@
 watch, 0
 watch-and-clock.museum, 0
 watchandclock.museum, 0
+watches, 0
 waw.pl, 0
 wazuka.kyoto.jp, 0
+weather, 0
 web.co, 0
 web.do, 0
 web.id, 0
@@ -6502,6 +6598,7 @@
 williamsburg.museum, 0
 win, 0
 windmill.museum, 0
+windows, 0
 withgoogle.com, 4
 wlocl.pl, 0
 wloclawek.pl, 0
@@ -6528,10 +6625,13 @@
 wy.us, 0
 x.bg, 0
 x.se, 0
+xbox, 0
 xerox, 0
+xihuan, 0
 xin, 0
 xj.cn, 0
 xn--0trq7p7nn.jp, 0
+xn--11b4c3d, 0
 xn--1ctwo.jp, 0
 xn--1lqs03n.jp, 0
 xn--1lqs71d.jp, 0
@@ -6542,6 +6642,8 @@
 xn--3bst00m, 0
 xn--3ds443g, 0
 xn--3e0b707e, 0
+xn--3pxu8k, 0
+xn--42c2d9a, 0
 xn--45brj9c, 0
 xn--45q11c, 0
 xn--4gbrim, 0
@@ -6556,6 +6658,7 @@
 xn--5js045d.jp, 0
 xn--5rtp49c.jp, 0
 xn--5rtq34k.jp, 0
+xn--5tzm5g, 0
 xn--6btw5a.jp, 0
 xn--6frz82g, 0
 xn--6orx2r.jp, 0
@@ -6571,6 +6674,7 @@
 xn--90a3ac, 0
 xn--90azh.xn--90a3ac, 0
 xn--9dbhblg6di.museum, 0
+xn--9dbq2a, 0
 xn--9et52u, 0
 xn--andy-ira.no, 0
 xn--aroport-bya.ci, 0
@@ -6598,6 +6702,7 @@
 xn--btsfjord-9za.no, 0
 xn--c1avg, 0
 xn--c1avg.xn--90a3ac, 0
+xn--c2br7g, 0
 xn--c3s14m.jp, 0
 xn--cg4bki, 0
 xn--ciqpn.hk, 0
@@ -6617,12 +6722,14 @@
 xn--dnna-gra.no, 0
 xn--drbak-wua.no, 0
 xn--dyry-ira.no, 0
+xn--eckvdtc9d, 0
 xn--efvn9s.jp, 0
 xn--efvy88h, 0
 xn--ehqz56n.jp, 0
 xn--elqq16h.jp, 0
 xn--eveni-0qa01ga.no, 0
 xn--f6qx53a.jp, 0
+xn--fhbei, 0
 xn--finny-yua.no, 0
 xn--fiq228c5hs, 0
 xn--fiq64b, 0
@@ -6668,8 +6775,10 @@
 xn--io0a7i, 0
 xn--io0a7i.cn, 0
 xn--io0a7i.hk, 0
+xn--j1aef, 0
 xn--j1amh, 0
 xn--j6w193g, 0
+xn--jlq61u9w7b, 0
 xn--jlster-bya.no, 0
 xn--jrpeland-54a.no, 0
 xn--k7yn95e.jp, 0
@@ -6685,6 +6794,7 @@
 xn--koluokta-7ya57h.no, 0
 xn--kprw13d, 0
 xn--kpry57d, 0
+xn--kpu716f, 0
 xn--kput3i, 0
 xn--krager-gya.no, 0
 xn--kranghke-b0a.no, 0
@@ -6726,6 +6836,7 @@
 xn--mgbaam7a8h, 0
 xn--mgbab2bd, 0
 xn--mgbayh7gpa, 0
+xn--mgbb9fbpob, 0
 xn--mgbbh1a71e, 0
 xn--mgbc0a9azcg, 0
 xn--mgberp4a5d4a87g, 0
@@ -6737,6 +6848,7 @@
 xn--mgbx4cd0ab, 0
 xn--mjndalen-64a.no, 0
 xn--mk0axi.hk, 0
+xn--mk1bu44c, 0
 xn--mkru45i.jp, 0
 xn--mlatvuopmi-s4a.no, 0
 xn--mli-tla.no, 0
@@ -6776,9 +6888,11 @@
 xn--osyro-wua.no, 0
 xn--p1acf, 0
 xn--p1ai, 0
+xn--pbt977c, 0
 xn--pgbs0dh, 0
 xn--porsgu-sta26f.no, 0
 xn--pssu33l.jp, 0
+xn--pssy2u, 0
 xn--q9jyb4c, 0
 xn--qcka1pmc, 0
 xn--qqqt11m.jp, 0
@@ -6835,6 +6949,8 @@
 xn--stjrdal-s1a.no, 0
 xn--stjrdalshalsen-sqb.no, 0
 xn--stre-toten-zcb.no, 0
+xn--t60b56a, 0
+xn--tckwe, 0
 xn--tjme-hra.no, 0
 xn--tn0ag.hk, 0
 xn--tnsberg-q1a.no, 0
@@ -6930,6 +7046,7 @@
 yamatokoriyama.nara.jp, 0
 yamatotakada.nara.jp, 0
 yamatsuri.fukushima.jp, 0
+yamaxun, 0
 yamazoe.nara.jp, 0
 yame.fukuoka.jp, 0
 yanagawa.fukuoka.jp, 0
@@ -6999,6 +7116,7 @@
 yugawara.kanagawa.jp, 0
 yuki.ibaraki.jp, 0
 yukuhashi.fukuoka.jp, 0
+yun, 0
 yura.wakayama.jp, 0
 yurihonjo.akita.jp, 0
 yusuhara.kochi.jp, 0
@@ -7027,6 +7145,7 @@
 zara, 0
 zarow.pl, 0
 zentsuji.kagawa.jp, 0
+zero, 0
 zgora.pl, 0
 zgorzelec.pl, 0
 zgrad.ru, 0
diff --git a/net/base/sdch_manager.cc b/net/base/sdch_manager.cc
index 27d86ee8..7828f907 100644
--- a/net/base/sdch_manager.cc
+++ b/net/base/sdch_manager.cc
@@ -259,6 +259,12 @@
     auto it = dictionaries_.begin();
     dictionaries_.erase(it->first);
   }
+#if defined(OS_CHROMEOS)
+  // For debugging http://crbug.com/454198; remove when resolved.
+
+  // Explicitly confirm that we can't notify any observers anymore.
+  CHECK(!observers_.might_have_observers());
+#endif
 }
 
 void SdchManager::ClearData() {
diff --git a/net/cert/cert_verify_proc_openssl.cc b/net/cert/cert_verify_proc_openssl.cc
index a74bbc3..890462e6 100644
--- a/net/cert/cert_verify_proc_openssl.cc
+++ b/net/cert/cert_verify_proc_openssl.cc
@@ -204,10 +204,10 @@
     verify_result->cert_status |= CERT_STATUS_COMMON_NAME_INVALID;
   }
 
-  crypto::ScopedOpenSSL<X509_STORE_CTX, X509_STORE_CTX_free>::Type ctx(
+  crypto::ScopedOpenSSL<X509_STORE_CTX, X509_STORE_CTX_free> ctx(
       X509_STORE_CTX_new());
 
-  crypto::ScopedOpenSSL<STACK_OF(X509), sk_X509_free_fn>::Type intermediates(
+  crypto::ScopedOpenSSL<STACK_OF(X509), sk_X509_free_fn> intermediates(
       sk_X509_new_null());
   if (!intermediates.get())
     return ERR_OUT_OF_MEMORY;
diff --git a/net/cert/ct_objects_extractor_openssl.cc b/net/cert/ct_objects_extractor_openssl.cc
index ccc94fe4..80fed58 100644
--- a/net/cert/ct_objects_extractor_openssl.cc
+++ b/net/cert/ct_objects_extractor_openssl.cc
@@ -17,6 +17,7 @@
 #include "crypto/sha2.h"
 #include "net/cert/asn1_util.h"
 #include "net/cert/signed_certificate_timestamp.h"
+#include "net/ssl/scoped_openssl_types.h"
 
 namespace net {
 
@@ -24,14 +25,12 @@
 
 namespace {
 
-typedef crypto::ScopedOpenSSL<X509, X509_free>::Type ScopedX509;
-
 void FreeX509_EXTENSIONS(X509_EXTENSIONS* ptr) {
   sk_X509_EXTENSION_pop_free(ptr, X509_EXTENSION_free);
 }
 
-typedef crypto::ScopedOpenSSL<X509_EXTENSIONS, FreeX509_EXTENSIONS>::Type
-    ScopedX509_EXTENSIONS;
+using ScopedX509_EXTENSIONS =
+    crypto::ScopedOpenSSL<X509_EXTENSIONS, FreeX509_EXTENSIONS>;
 
 // The wire form of the OID 1.3.6.1.4.1.11129.2.4.2. See Section 3.3 of
 // RFC6962.
diff --git a/net/cert/sha256_legacy_support_openssl_win.cc b/net/cert/sha256_legacy_support_openssl_win.cc
index 991f0519..2c5fc708 100644
--- a/net/cert/sha256_legacy_support_openssl_win.cc
+++ b/net/cert/sha256_legacy_support_openssl_win.cc
@@ -18,8 +18,7 @@
 
 namespace {
 
-typedef crypto::ScopedOpenSSL<X509_ALGOR, X509_ALGOR_free>::Type
-    ScopedX509_ALGOR;
+using ScopedX509_ALGOR = crypto::ScopedOpenSSL<X509_ALGOR, X509_ALGOR_free>;
 
 // Parses |subject_signature| and writes the components into |*out_tbs_data|,
 // |*out_algor|, and |*out_signature|. The BIT STRING in the signature must be
@@ -151,4 +150,4 @@
 
 }  // namespace sha256_interception
 
-}  // namespace net
\ No newline at end of file
+}  // namespace net
diff --git a/net/cert/x509_certificate_openssl.cc b/net/cert/x509_certificate_openssl.cc
index e92f3a19..5d043d1 100644
--- a/net/cert/x509_certificate_openssl.cc
+++ b/net/cert/x509_certificate_openssl.cc
@@ -34,8 +34,8 @@
 
 namespace {
 
-typedef crypto::ScopedOpenSSL<GENERAL_NAMES, GENERAL_NAMES_free>::Type
-    ScopedGENERAL_NAMES;
+using ScopedGENERAL_NAMES =
+    crypto::ScopedOpenSSL<GENERAL_NAMES, GENERAL_NAMES_free>;
 
 void CreateOSCertHandlesFromPKCS7Bytes(
     const char* data, int length,
@@ -161,7 +161,7 @@
     ResetCertStore();
   }
 
-  crypto::ScopedOpenSSL<X509_STORE, X509_STORE_free>::Type store_;
+  crypto::ScopedOpenSSL<X509_STORE, X509_STORE_free> store_;
 
   DISALLOW_COPY_AND_ASSIGN(X509InitSingleton);
 };
@@ -404,7 +404,7 @@
 
   // Convert to a temporary list of X509_NAME objects.
   // It will own the objects it points to.
-  crypto::ScopedOpenSSL<STACK_OF(X509_NAME), sk_X509_NAME_free_all>::Type
+  crypto::ScopedOpenSSL<STACK_OF(X509_NAME), sk_X509_NAME_free_all>
       issuer_names(sk_X509_NAME_new_null());
   if (!issuer_names.get())
     return false;
diff --git a/net/cert/x509_util_openssl.cc b/net/cert/x509_util_openssl.cc
index 4b9e1c68..6bbf12d 100644
--- a/net/cert/x509_util_openssl.cc
+++ b/net/cert/x509_util_openssl.cc
@@ -17,22 +17,21 @@
 #include "crypto/scoped_openssl_types.h"
 #include "net/cert/x509_cert_types.h"
 #include "net/cert/x509_util.h"
+#include "net/ssl/scoped_openssl_types.h"
 
 namespace net {
 
 namespace {
 
-typedef crypto::ScopedOpenSSL<ASN1_INTEGER, ASN1_INTEGER_free>::Type
-    ScopedASN1_INTEGER;
-typedef crypto::ScopedOpenSSL<ASN1_OCTET_STRING, ASN1_OCTET_STRING_free>::Type
-    ScopedASN1_OCTET_STRING;
-typedef crypto::ScopedOpenSSL<ASN1_STRING, ASN1_STRING_free>::Type
-    ScopedASN1_STRING;
-typedef crypto::ScopedOpenSSL<ASN1_TIME, ASN1_TIME_free>::Type ScopedASN1_TIME;
-typedef crypto::ScopedOpenSSL<X509, X509_free>::Type ScopedX509;
-typedef crypto::ScopedOpenSSL<X509_EXTENSION, X509_EXTENSION_free>::Type
-    ScopedX509_EXTENSION;
-typedef crypto::ScopedOpenSSL<X509_NAME, X509_NAME_free>::Type ScopedX509_NAME;
+using ScopedASN1_INTEGER =
+    crypto::ScopedOpenSSL<ASN1_INTEGER, ASN1_INTEGER_free>;
+using ScopedASN1_OCTET_STRING =
+    crypto::ScopedOpenSSL<ASN1_OCTET_STRING, ASN1_OCTET_STRING_free>;
+using ScopedASN1_STRING = crypto::ScopedOpenSSL<ASN1_STRING, ASN1_STRING_free>;
+using ScopedASN1_TIME = crypto::ScopedOpenSSL<ASN1_TIME, ASN1_TIME_free>;
+using ScopedX509_EXTENSION =
+    crypto::ScopedOpenSSL<X509_EXTENSION, X509_EXTENSION_free>;
+using ScopedX509_NAME = crypto::ScopedOpenSSL<X509_NAME, X509_NAME_free>;
 
 const EVP_MD* ToEVP(x509_util::DigestAlgorithm alg) {
   switch (alg) {
diff --git a/net/cert/x509_util_openssl_unittest.cc b/net/cert/x509_util_openssl_unittest.cc
index 81b709ae..070fba4 100644
--- a/net/cert/x509_util_openssl_unittest.cc
+++ b/net/cert/x509_util_openssl_unittest.cc
@@ -8,14 +8,13 @@
 #include "crypto/scoped_openssl_types.h"
 #include "net/cert/x509_util.h"
 #include "net/cert/x509_util_openssl.h"
+#include "net/ssl/scoped_openssl_types.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace net {
 
 namespace {
 
-typedef crypto::ScopedOpenSSL<X509, X509_free>::Type ScopedX509;
-
 // Verify that a given certificate was signed with the private key corresponding
 // to a given public key.
 // |der_cert| is the DER-encoded X.509 certificate.
@@ -47,7 +46,7 @@
                      const std::string& der_cert) {
   // Origin Bound Cert OID.
   static const char oid_string[] = "1.3.6.1.4.1.11129.2.1.6";
-  crypto::ScopedOpenSSL<ASN1_OBJECT, ASN1_OBJECT_free>::Type oid_obj(
+  crypto::ScopedOpenSSL<ASN1_OBJECT, ASN1_OBJECT_free> oid_obj(
       OBJ_txt2obj(oid_string, 0));
   ASSERT_TRUE(oid_obj.get());
 
diff --git a/net/cookies/canonical_cookie_unittest.cc b/net/cookies/canonical_cookie_unittest.cc
index 26e4e12..46d730b 100644
--- a/net/cookies/canonical_cookie_unittest.cc
+++ b/net/cookies/canonical_cookie_unittest.cc
@@ -13,33 +13,24 @@
 namespace net {
 
 TEST(CanonicalCookieTest, GetCookieSourceFromURL) {
-  EXPECT_EQ("http://example.com/",
-            CanonicalCookie::GetCookieSourceFromURL(
-                GURL("http://example.com")));
-  EXPECT_EQ("http://example.com/",
-            CanonicalCookie::GetCookieSourceFromURL(
-                GURL("http://example.com/")));
-  EXPECT_EQ("http://example.com/",
-            CanonicalCookie::GetCookieSourceFromURL(
-                GURL("http://example.com/test")));
-  EXPECT_EQ("file:///tmp/test.html",
-            CanonicalCookie::GetCookieSourceFromURL(
-                GURL("file:///tmp/test.html")));
-  EXPECT_EQ("http://example.com/",
-            CanonicalCookie::GetCookieSourceFromURL(
-                GURL("http://example.com:1234/")));
-  EXPECT_EQ("http://example.com/",
-            CanonicalCookie::GetCookieSourceFromURL(
-                GURL("https://example.com/")));
-  EXPECT_EQ("http://example.com/",
-            CanonicalCookie::GetCookieSourceFromURL(
-                GURL("http://user:pwd@example.com/")));
-  EXPECT_EQ("http://example.com/",
-            CanonicalCookie::GetCookieSourceFromURL(
-                GURL("http://example.com/test?foo")));
-  EXPECT_EQ("http://example.com/",
-            CanonicalCookie::GetCookieSourceFromURL(
-                GURL("http://example.com/test#foo")));
+  EXPECT_EQ("http://example.com/", CanonicalCookie::GetCookieSourceFromURL(
+                                       GURL("http://example.com")));
+  EXPECT_EQ("http://example.com/", CanonicalCookie::GetCookieSourceFromURL(
+                                       GURL("http://example.com/")));
+  EXPECT_EQ("http://example.com/", CanonicalCookie::GetCookieSourceFromURL(
+                                       GURL("http://example.com/test")));
+  EXPECT_EQ("file:///tmp/test.html", CanonicalCookie::GetCookieSourceFromURL(
+                                         GURL("file:///tmp/test.html")));
+  EXPECT_EQ("http://example.com/", CanonicalCookie::GetCookieSourceFromURL(
+                                       GURL("http://example.com:1234/")));
+  EXPECT_EQ("http://example.com/", CanonicalCookie::GetCookieSourceFromURL(
+                                       GURL("https://example.com/")));
+  EXPECT_EQ("http://example.com/", CanonicalCookie::GetCookieSourceFromURL(
+                                       GURL("http://user:pwd@example.com/")));
+  EXPECT_EQ("http://example.com/", CanonicalCookie::GetCookieSourceFromURL(
+                                       GURL("http://example.com/test?foo")));
+  EXPECT_EQ("http://example.com/", CanonicalCookie::GetCookieSourceFromURL(
+                                       GURL("http://example.com/test#foo")));
 }
 
 TEST(CanonicalCookieTest, Constructor) {
@@ -56,24 +47,15 @@
   EXPECT_EQ("/test", cookie.Path());
   EXPECT_FALSE(cookie.IsSecure());
 
-  CanonicalCookie cookie2(url,
-                          "A",
-                          "2",
-                          std::string(),
-                          std::string(),
-                          current_time,
-                          base::Time(),
-                          current_time,
-                          false,
-                          false,
-                          COOKIE_PRIORITY_DEFAULT);
+  CanonicalCookie cookie2(url, "A", "2", std::string(), std::string(),
+                          current_time, base::Time(), current_time, false,
+                          false, COOKIE_PRIORITY_DEFAULT);
   EXPECT_EQ(url.GetOrigin().spec(), cookie.Source());
   EXPECT_EQ("A", cookie2.Name());
   EXPECT_EQ("2", cookie2.Value());
   EXPECT_EQ("", cookie2.Domain());
   EXPECT_EQ("", cookie2.Path());
   EXPECT_FALSE(cookie2.IsSecure());
-
 }
 
 TEST(CanonicalCookieTest, Create) {
@@ -83,7 +65,7 @@
   CookieOptions options;
 
   scoped_ptr<CanonicalCookie> cookie(
-        CanonicalCookie::Create(url, "A=2", creation_time, options));
+      CanonicalCookie::Create(url, "A=2", creation_time, options));
   EXPECT_EQ(url.GetOrigin().spec(), cookie->Source());
   EXPECT_EQ("A", cookie->Name());
   EXPECT_EQ("2", cookie->Value());
@@ -113,16 +95,15 @@
   EXPECT_FALSE(cookie.get());
   CookieOptions httponly_options;
   httponly_options.set_include_httponly();
-  cookie.reset(
-      CanonicalCookie::Create(url, "A=2; HttpOnly", creation_time,
-                              httponly_options));
+  cookie.reset(CanonicalCookie::Create(url, "A=2; HttpOnly", creation_time,
+                                       httponly_options));
   EXPECT_TRUE(cookie->IsHttpOnly());
 
   // Test the creating cookies using specific parameter instead of a cookie
   // string.
-  cookie.reset(CanonicalCookie::Create(
-      url, "A", "2", "www.example.com", "/test", creation_time, base::Time(),
-      false, false, COOKIE_PRIORITY_DEFAULT));
+  cookie.reset(CanonicalCookie::Create(url, "A", "2", "www.example.com",
+                                       "/test", creation_time, base::Time(),
+                                       false, false, COOKIE_PRIORITY_DEFAULT));
   EXPECT_EQ(url.GetOrigin().spec(), cookie->Source());
   EXPECT_EQ("A", cookie->Name());
   EXPECT_EQ("2", cookie->Value());
@@ -130,9 +111,9 @@
   EXPECT_EQ("/test", cookie->Path());
   EXPECT_FALSE(cookie->IsSecure());
 
-  cookie.reset(CanonicalCookie::Create(
-      url, "A", "2", ".www.example.com", "/test", creation_time, base::Time(),
-      false, false, COOKIE_PRIORITY_DEFAULT));
+  cookie.reset(CanonicalCookie::Create(url, "A", "2", ".www.example.com",
+                                       "/test", creation_time, base::Time(),
+                                       false, false, COOKIE_PRIORITY_DEFAULT));
   EXPECT_EQ(url.GetOrigin().spec(), cookie->Source());
   EXPECT_EQ("A", cookie->Name());
   EXPECT_EQ("2", cookie->Value());
@@ -148,8 +129,8 @@
 
   std::string cookie_line =
       "ACSTM=20130308043820420042; path=/; domain=ipdl.inpit.go.jp; Expires=";
-  scoped_ptr<CanonicalCookie> cookie(CanonicalCookie::Create(
-      url, cookie_line, creation_time, options));
+  scoped_ptr<CanonicalCookie> cookie(
+      CanonicalCookie::Create(url, cookie_line, creation_time, options));
   EXPECT_TRUE(cookie.get());
   EXPECT_FALSE(cookie->IsPersistent());
   EXPECT_FALSE(cookie->IsExpired(creation_time));
@@ -157,8 +138,8 @@
 
   // With a stale server time
   options.set_server_time(creation_time - base::TimeDelta::FromHours(1));
-  cookie.reset(CanonicalCookie::Create(
-      url, cookie_line, creation_time, options));
+  cookie.reset(
+      CanonicalCookie::Create(url, cookie_line, creation_time, options));
   EXPECT_TRUE(cookie.get());
   EXPECT_FALSE(cookie->IsPersistent());
   EXPECT_FALSE(cookie->IsExpired(creation_time));
@@ -166,8 +147,8 @@
 
   // With a future server time
   options.set_server_time(creation_time + base::TimeDelta::FromHours(1));
-  cookie.reset(CanonicalCookie::Create(
-      url, cookie_line, creation_time, options));
+  cookie.reset(
+      CanonicalCookie::Create(url, cookie_line, creation_time, options));
   EXPECT_TRUE(cookie.get());
   EXPECT_FALSE(cookie->IsPersistent());
   EXPECT_FALSE(cookie->IsExpired(creation_time));
@@ -187,76 +168,66 @@
   bool httponly(false);
 
   // Test that a cookie is equivalent to itself.
-  scoped_ptr<CanonicalCookie> cookie(
-      new CanonicalCookie(url, cookie_name, cookie_value, cookie_domain,
-                          cookie_path, creation_time, expiration_time,
-                          last_access_time, secure, httponly,
-                          COOKIE_PRIORITY_MEDIUM));
+  scoped_ptr<CanonicalCookie> cookie(new CanonicalCookie(
+      url, cookie_name, cookie_value, cookie_domain, cookie_path, creation_time,
+      expiration_time, last_access_time, secure, httponly,
+      COOKIE_PRIORITY_MEDIUM));
   EXPECT_TRUE(cookie->IsEquivalent(*cookie));
 
   // Test that two identical cookies are equivalent.
-  scoped_ptr<CanonicalCookie> other_cookie(
-      new CanonicalCookie(url, cookie_name, cookie_value, cookie_domain,
-                          cookie_path, creation_time, expiration_time,
-                          last_access_time, secure, httponly,
-                          COOKIE_PRIORITY_MEDIUM));
+  scoped_ptr<CanonicalCookie> other_cookie(new CanonicalCookie(
+      url, cookie_name, cookie_value, cookie_domain, cookie_path, creation_time,
+      expiration_time, last_access_time, secure, httponly,
+      COOKIE_PRIORITY_MEDIUM));
   EXPECT_TRUE(cookie->IsEquivalent(*other_cookie));
 
   // Tests that use different variations of attribute values that
   // DON'T affect cookie equivalence.
-  other_cookie.reset(new CanonicalCookie(url, cookie_name, "2", cookie_domain,
-                                         cookie_path, creation_time,
-                                         expiration_time, last_access_time,
-                                         secure, httponly,
-                                         COOKIE_PRIORITY_HIGH));
+  other_cookie.reset(
+      new CanonicalCookie(url, cookie_name, "2", cookie_domain, cookie_path,
+                          creation_time, expiration_time, last_access_time,
+                          secure, httponly, COOKIE_PRIORITY_HIGH));
   EXPECT_TRUE(cookie->IsEquivalent(*other_cookie));
 
   base::Time other_creation_time =
       creation_time + base::TimeDelta::FromMinutes(2);
-  other_cookie.reset(new CanonicalCookie(url, cookie_name, "2", cookie_domain,
-                                         cookie_path, other_creation_time,
-                                         expiration_time, last_access_time,
-                                         secure, httponly,
-                                         COOKIE_PRIORITY_MEDIUM));
+  other_cookie.reset(new CanonicalCookie(
+      url, cookie_name, "2", cookie_domain, cookie_path, other_creation_time,
+      expiration_time, last_access_time, secure, httponly,
+      COOKIE_PRIORITY_MEDIUM));
   EXPECT_TRUE(cookie->IsEquivalent(*other_cookie));
 
-  other_cookie.reset(new CanonicalCookie(url, cookie_name, cookie_name,
-                                         cookie_domain, cookie_path,
-                                         creation_time, expiration_time,
-                                         last_access_time, true, httponly,
-                                         COOKIE_PRIORITY_LOW));
+  other_cookie.reset(new CanonicalCookie(
+      url, cookie_name, cookie_name, cookie_domain, cookie_path, creation_time,
+      expiration_time, last_access_time, true, httponly, COOKIE_PRIORITY_LOW));
   EXPECT_TRUE(cookie->IsEquivalent(*other_cookie));
 
   // Tests that use different variations of attribute values that
   // DO affect cookie equivalence.
-  other_cookie.reset(new CanonicalCookie(url, "B", cookie_value, cookie_domain,
-                                         cookie_path, creation_time,
-                                         expiration_time, last_access_time,
-                                         secure, httponly,
-                                         COOKIE_PRIORITY_MEDIUM));
+  other_cookie.reset(
+      new CanonicalCookie(url, "B", cookie_value, cookie_domain, cookie_path,
+                          creation_time, expiration_time, last_access_time,
+                          secure, httponly, COOKIE_PRIORITY_MEDIUM));
   EXPECT_FALSE(cookie->IsEquivalent(*other_cookie));
 
-  other_cookie.reset(new CanonicalCookie(url, cookie_name, cookie_value,
-                                         "www.example.com", cookie_path,
-                                         creation_time, expiration_time,
-                                         last_access_time, secure, httponly,
-                                         COOKIE_PRIORITY_MEDIUM));
+  other_cookie.reset(new CanonicalCookie(
+      url, cookie_name, cookie_value, "www.example.com", cookie_path,
+      creation_time, expiration_time, last_access_time, secure, httponly,
+      COOKIE_PRIORITY_MEDIUM));
   EXPECT_TRUE(cookie->IsDomainCookie());
   EXPECT_FALSE(other_cookie->IsDomainCookie());
   EXPECT_FALSE(cookie->IsEquivalent(*other_cookie));
 
-  other_cookie.reset(new CanonicalCookie(url, cookie_name, cookie_value,
-                                         ".example.com", cookie_path,
-                                         creation_time, expiration_time,
-                                         last_access_time, secure, httponly,
-                                         COOKIE_PRIORITY_MEDIUM));
+  other_cookie.reset(new CanonicalCookie(
+      url, cookie_name, cookie_value, ".example.com", cookie_path,
+      creation_time, expiration_time, last_access_time, secure, httponly,
+      COOKIE_PRIORITY_MEDIUM));
   EXPECT_FALSE(cookie->IsEquivalent(*other_cookie));
 
-  other_cookie.reset(new CanonicalCookie(url, cookie_name, cookie_value,
-                                         cookie_domain, "/test/0",
-                                         creation_time, expiration_time,
-                                         last_access_time, secure, httponly,
-                                         COOKIE_PRIORITY_MEDIUM));
+  other_cookie.reset(new CanonicalCookie(
+      url, cookie_name, cookie_value, cookie_domain, "/test/0", creation_time,
+      expiration_time, last_access_time, secure, httponly,
+      COOKIE_PRIORITY_MEDIUM));
   EXPECT_FALSE(cookie->IsEquivalent(*other_cookie));
 }
 
@@ -274,9 +245,8 @@
   EXPECT_FALSE(cookie->IsDomainMatch("www0.example.com"));
   EXPECT_FALSE(cookie->IsDomainMatch("example.com"));
 
-  cookie.reset(
-      CanonicalCookie::Create(url, "A=2; Domain=www.example.com", creation_time,
-                              options));
+  cookie.reset(CanonicalCookie::Create(url, "A=2; Domain=www.example.com",
+                                       creation_time, options));
   EXPECT_TRUE(cookie->IsDomainCookie());
   EXPECT_TRUE(cookie->IsDomainMatch("www.example.com"));
   EXPECT_TRUE(cookie->IsDomainMatch("www.example.com"));
@@ -284,9 +254,8 @@
   EXPECT_FALSE(cookie->IsDomainMatch("www0.example.com"));
   EXPECT_FALSE(cookie->IsDomainMatch("example.com"));
 
-  cookie.reset(
-      CanonicalCookie::Create(url, "A=2; Domain=.www.example.com",
-                              creation_time, options));
+  cookie.reset(CanonicalCookie::Create(url, "A=2; Domain=.www.example.com",
+                                       creation_time, options));
   EXPECT_TRUE(cookie->IsDomainMatch("www.example.com"));
   EXPECT_TRUE(cookie->IsDomainMatch("www.example.com"));
   EXPECT_TRUE(cookie->IsDomainMatch("foo.www.example.com"));
@@ -298,9 +267,8 @@
   base::Time creation_time = base::Time::Now();
   CookieOptions options;
 
-  scoped_ptr<CanonicalCookie> cookie(
-      CanonicalCookie::Create(GURL("http://www.example.com"),
-                              "A=2", creation_time, options));
+  scoped_ptr<CanonicalCookie> cookie(CanonicalCookie::Create(
+      GURL("http://www.example.com"), "A=2", creation_time, options));
   EXPECT_TRUE(cookie->IsOnPath("/"));
   EXPECT_TRUE(cookie->IsOnPath("/test"));
   EXPECT_TRUE(cookie->IsOnPath("/test/bar.html"));
@@ -329,8 +297,8 @@
       GURL("http://www.example.com/foo/bar"), options));
   EXPECT_TRUE(cookie->IncludeForRequestURL(
       GURL("https://www.example.com/foo/bar"), options));
-  EXPECT_FALSE(cookie->IncludeForRequestURL(GURL("https://sub.example.com"),
-                                            options));
+  EXPECT_FALSE(
+      cookie->IncludeForRequestURL(GURL("https://sub.example.com"), options));
   EXPECT_FALSE(cookie->IncludeForRequestURL(GURL("https://sub.www.example.com"),
                                             options));
 
diff --git a/net/cookies/cookie_monster.cc b/net/cookies/cookie_monster.cc
index 452ce68..bcf0fd0 100644
--- a/net/cookies/cookie_monster.cc
+++ b/net/cookies/cookie_monster.cc
@@ -97,18 +97,18 @@
 
 // See comments at declaration of these variables in cookie_monster.h
 // for details.
-const size_t CookieMonster::kDomainMaxCookies           = 180;
-const size_t CookieMonster::kDomainPurgeCookies         = 30;
-const size_t CookieMonster::kMaxCookies                 = 3300;
-const size_t CookieMonster::kPurgeCookies               = 300;
+const size_t CookieMonster::kDomainMaxCookies = 180;
+const size_t CookieMonster::kDomainPurgeCookies = 30;
+const size_t CookieMonster::kMaxCookies = 3300;
+const size_t CookieMonster::kPurgeCookies = 300;
 
-const size_t CookieMonster::kDomainCookiesQuotaLow    = 30;
+const size_t CookieMonster::kDomainCookiesQuotaLow = 30;
 const size_t CookieMonster::kDomainCookiesQuotaMedium = 50;
-const size_t CookieMonster::kDomainCookiesQuotaHigh   =
-    kDomainMaxCookies - kDomainPurgeCookies
-    - kDomainCookiesQuotaLow - kDomainCookiesQuotaMedium;
+const size_t CookieMonster::kDomainCookiesQuotaHigh =
+    kDomainMaxCookies - kDomainPurgeCookies - kDomainCookiesQuotaLow -
+    kDomainCookiesQuotaMedium;
 
-const int CookieMonster::kSafeFromGlobalPurgeDays       = 30;
+const int CookieMonster::kSafeFromGlobalPurgeDays = 30;
 
 namespace {
 
@@ -177,8 +177,7 @@
   CookieSignature(const std::string& name,
                   const std::string& domain,
                   const std::string& path)
-      : name(name), domain(domain), path(path) {
-  }
+      : name(name), domain(domain), path(path) {}
 
   // To be a key for a map this class needs to be assignable, copyable,
   // and have an operator<.  The default assignment operator
@@ -206,10 +205,9 @@
 // sorts the first |num_sort| + 1 elements by LastAccessDate().
 // The + 1 element exists so for any interval of length <= |num_sort| starting
 // from |cookies_its_begin|, a LastAccessDate() bound can be found.
-void SortLeastRecentlyAccessed(
-    CookieMonster::CookieItVector::iterator it_begin,
-    CookieMonster::CookieItVector::iterator it_end,
-    size_t num_sort) {
+void SortLeastRecentlyAccessed(CookieMonster::CookieItVector::iterator it_begin,
+                               CookieMonster::CookieItVector::iterator it_end,
+                               size_t num_sort) {
   DCHECK_LT(static_cast<int>(num_sort), it_end - it_begin);
   std::partial_sort(it_begin, it_begin + num_sort + 1, it_end, LRACookieSorter);
 }
@@ -218,7 +216,7 @@
 struct CookiePriorityEqualsTo
     : std::unary_function<const CookieMonster::CookieMap::iterator, bool> {
   explicit CookiePriorityEqualsTo(CookiePriority priority)
-    : priority_(priority) {}
+      : priority_(priority) {}
 
   bool operator()(const CookieMonster::CookieMap::iterator it) const {
     return it->second->Priority() == priority_;
@@ -238,8 +236,8 @@
   return std::partition(it_begin, it_end, CookiePriorityEqualsTo(priority));
 }
 
-bool LowerBoundAccessDateComparator(
-  const CookieMonster::CookieMap::iterator it, const Time& access_date) {
+bool LowerBoundAccessDateComparator(const CookieMonster::CookieMap::iterator it,
+                                    const Time& access_date) {
   return it->second->LastAccessDate() < access_date;
 }
 
@@ -263,33 +261,32 @@
   bool notify;
 } ChangeCausePair;
 ChangeCausePair ChangeCauseMapping[] = {
-  // DELETE_COOKIE_EXPLICIT
-  { CookieMonsterDelegate::CHANGE_COOKIE_EXPLICIT, true },
-  // DELETE_COOKIE_OVERWRITE
-  { CookieMonsterDelegate::CHANGE_COOKIE_OVERWRITE, true },
-  // DELETE_COOKIE_EXPIRED
-  { CookieMonsterDelegate::CHANGE_COOKIE_EXPIRED, true },
-  // DELETE_COOKIE_EVICTED
-  { CookieMonsterDelegate::CHANGE_COOKIE_EVICTED, true },
-  // DELETE_COOKIE_DUPLICATE_IN_BACKING_STORE
-  { CookieMonsterDelegate::CHANGE_COOKIE_EXPLICIT, false },
-  // DELETE_COOKIE_DONT_RECORD
-  { CookieMonsterDelegate::CHANGE_COOKIE_EXPLICIT, false },
-  // DELETE_COOKIE_EVICTED_DOMAIN
-  { CookieMonsterDelegate::CHANGE_COOKIE_EVICTED, true },
-  // DELETE_COOKIE_EVICTED_GLOBAL
-  { CookieMonsterDelegate::CHANGE_COOKIE_EVICTED, true },
-  // DELETE_COOKIE_EVICTED_DOMAIN_PRE_SAFE
-  { CookieMonsterDelegate::CHANGE_COOKIE_EVICTED, true },
-  // DELETE_COOKIE_EVICTED_DOMAIN_POST_SAFE
-  { CookieMonsterDelegate::CHANGE_COOKIE_EVICTED, true },
-  // DELETE_COOKIE_EXPIRED_OVERWRITE
-  { CookieMonsterDelegate::CHANGE_COOKIE_EXPIRED_OVERWRITE, true },
-  // DELETE_COOKIE_CONTROL_CHAR
-  { CookieMonsterDelegate::CHANGE_COOKIE_EVICTED, true},
-  // DELETE_COOKIE_LAST_ENTRY
-  { CookieMonsterDelegate::CHANGE_COOKIE_EXPLICIT, false }
-};
+    // DELETE_COOKIE_EXPLICIT
+    {CookieMonsterDelegate::CHANGE_COOKIE_EXPLICIT, true},
+    // DELETE_COOKIE_OVERWRITE
+    {CookieMonsterDelegate::CHANGE_COOKIE_OVERWRITE, true},
+    // DELETE_COOKIE_EXPIRED
+    {CookieMonsterDelegate::CHANGE_COOKIE_EXPIRED, true},
+    // DELETE_COOKIE_EVICTED
+    {CookieMonsterDelegate::CHANGE_COOKIE_EVICTED, true},
+    // DELETE_COOKIE_DUPLICATE_IN_BACKING_STORE
+    {CookieMonsterDelegate::CHANGE_COOKIE_EXPLICIT, false},
+    // DELETE_COOKIE_DONT_RECORD
+    {CookieMonsterDelegate::CHANGE_COOKIE_EXPLICIT, false},
+    // DELETE_COOKIE_EVICTED_DOMAIN
+    {CookieMonsterDelegate::CHANGE_COOKIE_EVICTED, true},
+    // DELETE_COOKIE_EVICTED_GLOBAL
+    {CookieMonsterDelegate::CHANGE_COOKIE_EVICTED, true},
+    // DELETE_COOKIE_EVICTED_DOMAIN_PRE_SAFE
+    {CookieMonsterDelegate::CHANGE_COOKIE_EVICTED, true},
+    // DELETE_COOKIE_EVICTED_DOMAIN_POST_SAFE
+    {CookieMonsterDelegate::CHANGE_COOKIE_EVICTED, true},
+    // DELETE_COOKIE_EXPIRED_OVERWRITE
+    {CookieMonsterDelegate::CHANGE_COOKIE_EXPIRED_OVERWRITE, true},
+    // DELETE_COOKIE_CONTROL_CHAR
+    {CookieMonsterDelegate::CHANGE_COOKIE_EVICTED, true},
+    // DELETE_COOKIE_LAST_ENTRY
+    {CookieMonsterDelegate::CHANGE_COOKIE_EXPLICIT, false}};
 
 std::string BuildCookieLine(const CanonicalCookieVector& cookies) {
   std::string cookie_line;
@@ -347,7 +344,6 @@
   SetDefaultCookieableSchemes();
 }
 
-
 // Task classes for queueing the coming request.
 
 class CookieMonster::CookieMonsterTask
@@ -367,9 +363,7 @@
   // instance until the callback completes.
   void InvokeCallback(base::Closure callback);
 
-  CookieMonster* cookie_monster() {
-    return cookie_monster_;
-  }
+  CookieMonster* cookie_monster() { return cookie_monster_; }
 
  private:
   friend class base::RefCountedThreadSafe<CookieMonsterTask>;
@@ -386,7 +380,8 @@
       thread_(base::MessageLoopProxy::current()) {
 }
 
-CookieMonster::CookieMonsterTask::~CookieMonsterTask() {}
+CookieMonster::CookieMonsterTask::~CookieMonsterTask() {
+}
 
 // Unfortunately, one cannot re-bind a Callback with parameters into a closure.
 // Therefore, the closure passed to InvokeCallback is a clumsy binding of
@@ -402,8 +397,8 @@
   if (thread_->BelongsToCurrentThread()) {
     callback.Run();
   } else {
-    thread_->PostTask(FROM_HERE, base::Bind(
-        &CookieMonsterTask::InvokeCallback, this, callback));
+    thread_->PostTask(FROM_HERE, base::Bind(&CookieMonsterTask::InvokeCallback,
+                                            this, callback));
   }
 }
 
@@ -431,8 +426,7 @@
         secure_(secure),
         http_only_(http_only),
         priority_(priority),
-        callback_(callback) {
-  }
+        callback_(callback) {}
 
   // CookieMonsterTask:
   void Run() override;
@@ -456,9 +450,13 @@
 };
 
 void CookieMonster::SetCookieWithDetailsTask::Run() {
-  bool success = this->cookie_monster()->
-      SetCookieWithDetails(url_, name_, value_, domain_, path_,
-                           expiration_time_, secure_, http_only_, priority_);
+  // TODO(pkasting): Remove ScopedTracker below once crbug.com/456373 is fixed.
+  tracked_objects::ScopedTracker tracking_profile(
+      FROM_HERE_WITH_EXPLICIT_FUNCTION(
+          "456373 CookieMonster::SetCookieWithDetailsTask::Run"));
+  bool success = this->cookie_monster()->SetCookieWithDetails(
+      url_, name_, value_, domain_, path_, expiration_time_, secure_,
+      http_only_, priority_);
   if (!callback_.is_null()) {
     this->InvokeCallback(base::Bind(&SetCookiesCallback::Run,
                                     base::Unretained(&callback_), success));
@@ -470,9 +468,7 @@
  public:
   GetAllCookiesTask(CookieMonster* cookie_monster,
                     const GetCookieListCallback& callback)
-      : CookieMonsterTask(cookie_monster),
-        callback_(callback) {
-  }
+      : CookieMonsterTask(cookie_monster), callback_(callback) {}
 
   // CookieMonsterTask
   void Run() override;
@@ -487,27 +483,29 @@
 };
 
 void CookieMonster::GetAllCookiesTask::Run() {
+  // TODO(pkasting): Remove ScopedTracker below once crbug.com/456373 is fixed.
+  tracked_objects::ScopedTracker tracking_profile(
+      FROM_HERE_WITH_EXPLICIT_FUNCTION(
+          "456373 CookieMonster::GetAllCookiesTask::Run"));
   if (!callback_.is_null()) {
     CookieList cookies = this->cookie_monster()->GetAllCookies();
     this->InvokeCallback(base::Bind(&GetCookieListCallback::Run,
                                     base::Unretained(&callback_), cookies));
-    }
+  }
 }
 
 // Task class for GetAllCookiesForURLWithOptions call.
 class CookieMonster::GetAllCookiesForURLWithOptionsTask
     : public CookieMonsterTask {
  public:
-  GetAllCookiesForURLWithOptionsTask(
-      CookieMonster* cookie_monster,
-      const GURL& url,
-      const CookieOptions& options,
-      const GetCookieListCallback& callback)
+  GetAllCookiesForURLWithOptionsTask(CookieMonster* cookie_monster,
+                                     const GURL& url,
+                                     const CookieOptions& options,
+                                     const GetCookieListCallback& callback)
       : CookieMonsterTask(cookie_monster),
         url_(url),
         options_(options),
-        callback_(callback) {
-  }
+        callback_(callback) {}
 
   // CookieMonsterTask:
   void Run() override;
@@ -524,19 +522,25 @@
 };
 
 void CookieMonster::GetAllCookiesForURLWithOptionsTask::Run() {
+  // TODO(pkasting): Remove ScopedTracker below once crbug.com/456373 is fixed.
+  tracked_objects::ScopedTracker tracking_profile(
+      FROM_HERE_WITH_EXPLICIT_FUNCTION(
+          "456373 CookieMonster::GetAllCookiesForURLWithOptionsTask::Run"));
   if (!callback_.is_null()) {
-    CookieList cookies = this->cookie_monster()->
-        GetAllCookiesForURLWithOptions(url_, options_);
+    CookieList cookies =
+        this->cookie_monster()->GetAllCookiesForURLWithOptions(url_, options_);
     this->InvokeCallback(base::Bind(&GetCookieListCallback::Run,
                                     base::Unretained(&callback_), cookies));
   }
 }
 
-template <typename Result> struct CallbackType {
+template <typename Result>
+struct CallbackType {
   typedef base::Callback<void(Result)> Type;
 };
 
-template <> struct CallbackType<void> {
+template <>
+struct CallbackType<void> {
   typedef base::Closure Type;
 };
 
@@ -546,9 +550,7 @@
  public:
   DeleteTask(CookieMonster* cookie_monster,
              const typename CallbackType<Result>::Type& callback)
-      : CookieMonsterTask(cookie_monster),
-        callback_(callback) {
-  }
+      : CookieMonsterTask(cookie_monster), callback_(callback) {}
 
   // CookieMonsterTask:
   virtual void Run() override;
@@ -588,9 +590,12 @@
 
 template <typename Result>
 void CookieMonster::DeleteTask<Result>::Run() {
-  this->cookie_monster()->FlushStore(
-      base::Bind(&DeleteTask<Result>::FlushDone, this,
-                 RunDeleteTaskAndBindCallback()));
+  // TODO(pkasting): Remove ScopedTracker below once crbug.com/456373 is fixed.
+  tracked_objects::ScopedTracker tracking_profile(
+      FROM_HERE_WITH_EXPLICIT_FUNCTION(
+          "456373 CookieMonster::DeleteTask::Run"));
+  this->cookie_monster()->FlushStore(base::Bind(
+      &DeleteTask<Result>::FlushDone, this, RunDeleteTaskAndBindCallback()));
 }
 
 template <typename Result>
@@ -604,10 +609,8 @@
 // Task class for DeleteAll call.
 class CookieMonster::DeleteAllTask : public DeleteTask<int> {
  public:
-  DeleteAllTask(CookieMonster* cookie_monster,
-                const DeleteCallback& callback)
-      : DeleteTask<int>(cookie_monster, callback) {
-  }
+  DeleteAllTask(CookieMonster* cookie_monster, const DeleteCallback& callback)
+      : DeleteTask<int>(cookie_monster, callback) {}
 
   // DeleteTask:
   int RunDeleteTask() override;
@@ -632,8 +635,7 @@
                               const DeleteCallback& callback)
       : DeleteTask<int>(cookie_monster, callback),
         delete_begin_(delete_begin),
-        delete_end_(delete_end) {
-  }
+        delete_end_(delete_end) {}
 
   // DeleteTask:
   int RunDeleteTask() override;
@@ -649,8 +651,8 @@
 };
 
 int CookieMonster::DeleteAllCreatedBetweenTask::RunDeleteTask() {
-  return this->cookie_monster()->
-      DeleteAllCreatedBetween(delete_begin_, delete_end_);
+  return this->cookie_monster()->DeleteAllCreatedBetween(delete_begin_,
+                                                         delete_end_);
 }
 
 // Task class for DeleteAllForHost call.
@@ -659,9 +661,7 @@
   DeleteAllForHostTask(CookieMonster* cookie_monster,
                        const GURL& url,
                        const DeleteCallback& callback)
-      : DeleteTask<int>(cookie_monster, callback),
-        url_(url) {
-  }
+      : DeleteTask<int>(cookie_monster, callback), url_(url) {}
 
   // DeleteTask:
   int RunDeleteTask() override;
@@ -683,17 +683,15 @@
 class CookieMonster::DeleteAllCreatedBetweenForHostTask
     : public DeleteTask<int> {
  public:
-  DeleteAllCreatedBetweenForHostTask(
-      CookieMonster* cookie_monster,
-      Time delete_begin,
-      Time delete_end,
-      const GURL& url,
-      const DeleteCallback& callback)
+  DeleteAllCreatedBetweenForHostTask(CookieMonster* cookie_monster,
+                                     Time delete_begin,
+                                     Time delete_end,
+                                     const GURL& url,
+                                     const DeleteCallback& callback)
       : DeleteTask<int>(cookie_monster, callback),
         delete_begin_(delete_begin),
         delete_end_(delete_end),
-        url_(url) {
-  }
+        url_(url) {}
 
   // DeleteTask:
   int RunDeleteTask() override;
@@ -720,9 +718,7 @@
   DeleteCanonicalCookieTask(CookieMonster* cookie_monster,
                             const CanonicalCookie& cookie,
                             const DeleteCookieCallback& callback)
-      : DeleteTask<bool>(cookie_monster, callback),
-        cookie_(cookie) {
-  }
+      : DeleteTask<bool>(cookie_monster, callback), cookie_(cookie) {}
 
   // DeleteTask:
   bool RunDeleteTask() override;
@@ -752,8 +748,7 @@
         url_(url),
         cookie_line_(cookie_line),
         options_(options),
-        callback_(callback) {
-  }
+        callback_(callback) {}
 
   // CookieMonsterTask:
   void Run() override;
@@ -771,8 +766,12 @@
 };
 
 void CookieMonster::SetCookieWithOptionsTask::Run() {
-  bool result = this->cookie_monster()->
-      SetCookieWithOptions(url_, cookie_line_, options_);
+  // TODO(pkasting): Remove ScopedTracker below once crbug.com/456373 is fixed.
+  tracked_objects::ScopedTracker tracking_profile(
+      FROM_HERE_WITH_EXPLICIT_FUNCTION(
+          "456373 CookieMonster::SetCookieWithOptionsTask::Run"));
+  bool result = this->cookie_monster()->SetCookieWithOptions(url_, cookie_line_,
+                                                             options_);
   if (!callback_.is_null()) {
     this->InvokeCallback(base::Bind(&SetCookiesCallback::Run,
                                     base::Unretained(&callback_), result));
@@ -789,8 +788,7 @@
       : CookieMonsterTask(cookie_monster),
         url_(url),
         options_(options),
-        callback_(callback) {
-  }
+        callback_(callback) {}
 
   // CookieMonsterTask:
   void Run() override;
@@ -807,8 +805,12 @@
 };
 
 void CookieMonster::GetCookiesWithOptionsTask::Run() {
-  std::string cookie = this->cookie_monster()->
-      GetCookiesWithOptions(url_, options_);
+  // TODO(pkasting): Remove ScopedTracker below once crbug.com/456373 is fixed.
+  tracked_objects::ScopedTracker tracking_profile(
+      FROM_HERE_WITH_EXPLICIT_FUNCTION(
+          "456373 CookieMonster::GetCookiesWithOptionsTask::Run"));
+  std::string cookie =
+      this->cookie_monster()->GetCookiesWithOptions(url_, options_);
   if (!callback_.is_null()) {
     this->InvokeCallback(base::Bind(&GetCookiesCallback::Run,
                                     base::Unretained(&callback_), cookie));
@@ -824,8 +826,7 @@
                    const base::Closure& callback)
       : DeleteTask<void>(cookie_monster, callback),
         url_(url),
-        cookie_name_(cookie_name) {
-  }
+        cookie_name_(cookie_name) {}
 
   // DeleteTask:
   void RunDeleteTask() override;
@@ -849,8 +850,7 @@
  public:
   DeleteSessionCookiesTask(CookieMonster* cookie_monster,
                            const DeleteCallback& callback)
-      : DeleteTask<int>(cookie_monster, callback) {
-  }
+      : DeleteTask<int>(cookie_monster, callback) {}
 
   // DeleteTask:
   int RunDeleteTask() override;
@@ -869,14 +869,12 @@
 // Task class for HasCookiesForETLDP1Task call.
 class CookieMonster::HasCookiesForETLDP1Task : public CookieMonsterTask {
  public:
-  HasCookiesForETLDP1Task(
-      CookieMonster* cookie_monster,
-      const std::string& etldp1,
-      const HasCookiesForETLDP1Callback& callback)
+  HasCookiesForETLDP1Task(CookieMonster* cookie_monster,
+                          const std::string& etldp1,
+                          const HasCookiesForETLDP1Callback& callback)
       : CookieMonsterTask(cookie_monster),
         etldp1_(etldp1),
-        callback_(callback) {
-  }
+        callback_(callback) {}
 
   // CookieMonsterTask:
   void Run() override;
@@ -892,11 +890,14 @@
 };
 
 void CookieMonster::HasCookiesForETLDP1Task::Run() {
+  // TODO(pkasting): Remove ScopedTracker below once crbug.com/456373 is fixed.
+  tracked_objects::ScopedTracker tracking_profile(
+      FROM_HERE_WITH_EXPLICIT_FUNCTION(
+          "456373 CookieMonster::HasCookiesForETLDP1Task::Run"));
   bool result = this->cookie_monster()->HasCookiesForETLDP1(etldp1_);
   if (!callback_.is_null()) {
-    this->InvokeCallback(
-        base::Bind(&HasCookiesForETLDP1Callback::Run,
-                   base::Unretained(&callback_), result));
+    this->InvokeCallback(base::Bind(&HasCookiesForETLDP1Callback::Run,
+                                    base::Unretained(&callback_), result));
   }
 }
 
@@ -913,21 +914,18 @@
     bool http_only,
     CookiePriority priority,
     const SetCookiesCallback& callback) {
-  scoped_refptr<SetCookieWithDetailsTask> task =
-      new SetCookieWithDetailsTask(this, url, name, value, domain, path,
-                                   expiration_time, secure, http_only, priority,
-                                   callback);
+  scoped_refptr<SetCookieWithDetailsTask> task = new SetCookieWithDetailsTask(
+      this, url, name, value, domain, path, expiration_time, secure, http_only,
+      priority, callback);
   DoCookieTaskForURL(task, url);
 }
 
 void CookieMonster::GetAllCookiesAsync(const GetCookieListCallback& callback) {
-  scoped_refptr<GetAllCookiesTask> task =
-      new GetAllCookiesTask(this, callback);
+  scoped_refptr<GetAllCookiesTask> task = new GetAllCookiesTask(this, callback);
 
   DoCookieTask(task);
 }
 
-
 void CookieMonster::GetAllCookiesForURLWithOptionsAsync(
     const GURL& url,
     const CookieOptions& options,
@@ -939,7 +937,8 @@
 }
 
 void CookieMonster::GetAllCookiesForURLAsync(
-    const GURL& url, const GetCookieListCallback& callback) {
+    const GURL& url,
+    const GetCookieListCallback& callback) {
   CookieOptions options;
   options.set_include_httponly();
   scoped_refptr<GetAllCookiesForURLWithOptionsTask> task =
@@ -958,18 +957,17 @@
 }
 
 void CookieMonster::DeleteAllAsync(const DeleteCallback& callback) {
-  scoped_refptr<DeleteAllTask> task =
-      new DeleteAllTask(this, callback);
+  scoped_refptr<DeleteAllTask> task = new DeleteAllTask(this, callback);
 
   DoCookieTask(task);
 }
 
 void CookieMonster::DeleteAllCreatedBetweenAsync(
-    const Time& delete_begin, const Time& delete_end,
+    const Time& delete_begin,
+    const Time& delete_end,
     const DeleteCallback& callback) {
   scoped_refptr<DeleteAllCreatedBetweenTask> task =
-      new DeleteAllCreatedBetweenTask(this, delete_begin, delete_end,
-                                      callback);
+      new DeleteAllCreatedBetweenTask(this, delete_begin, delete_end, callback);
 
   DoCookieTask(task);
 }
@@ -980,14 +978,14 @@
     const GURL& url,
     const DeleteCallback& callback) {
   scoped_refptr<DeleteAllCreatedBetweenForHostTask> task =
-      new DeleteAllCreatedBetweenForHostTask(
-          this, delete_begin, delete_end, url, callback);
+      new DeleteAllCreatedBetweenForHostTask(this, delete_begin, delete_end,
+                                             url, callback);
 
   DoCookieTaskForURL(task, url);
 }
 
-void CookieMonster::DeleteAllForHostAsync(
-    const GURL& url, const DeleteCallback& callback) {
+void CookieMonster::DeleteAllForHostAsync(const GURL& url,
+                                          const DeleteCallback& callback) {
   scoped_refptr<DeleteAllForHostTask> task =
       new DeleteAllForHostTask(this, url, callback);
 
@@ -1065,16 +1063,19 @@
     // then run the task, otherwise load from DB.
     if (!loaded_) {
       // Checks if the domain key has been loaded.
-      std::string key(cookie_util::GetEffectiveDomain(url.scheme(),
-                                                       url.host()));
+      std::string key(
+          cookie_util::GetEffectiveDomain(url.scheme(), url.host()));
       if (keys_loaded_.find(key) == keys_loaded_.end()) {
-        std::map<std::string, std::deque<scoped_refptr<CookieMonsterTask> > >
-          ::iterator it = tasks_pending_for_key_.find(key);
+        std::map<std::string,
+                 std::deque<scoped_refptr<CookieMonsterTask>>>::iterator it =
+            tasks_pending_for_key_.find(key);
         if (it == tasks_pending_for_key_.end()) {
-          store_->LoadCookiesForKey(key,
-            base::Bind(&CookieMonster::OnKeyLoaded, this, key));
-          it = tasks_pending_for_key_.insert(std::make_pair(key,
-            std::deque<scoped_refptr<CookieMonsterTask> >())).first;
+          store_->LoadCookiesForKey(
+              key, base::Bind(&CookieMonster::OnKeyLoaded, this, key));
+          it = tasks_pending_for_key_
+                   .insert(std::make_pair(
+                       key, std::deque<scoped_refptr<CookieMonsterTask>>()))
+                   .first;
         }
         it->second.push_back(task_item);
         return;
@@ -1103,8 +1104,8 @@
 
   scoped_ptr<CanonicalCookie> cc;
   cc.reset(CanonicalCookie::Create(url, name, value, domain, path,
-                                   creation_time, expiration_time,
-                                   secure, http_only, priority));
+                                   creation_time, expiration_time, secure,
+                                   http_only, priority));
 
   if (!cc.get())
     return false;
@@ -1117,8 +1118,8 @@
 bool CookieMonster::ImportCookies(const CookieList& list) {
   base::AutoLock autolock(lock_);
   InitIfNecessary();
-  for (net::CookieList::const_iterator iter = list.begin();
-           iter != list.end(); ++iter) {
+  for (net::CookieList::const_iterator iter = list.begin(); iter != list.end();
+       ++iter) {
     scoped_ptr<CanonicalCookie> cookie(new CanonicalCookie(*iter));
     net::CookieOptions options;
     options.set_include_httponly();
@@ -1139,9 +1140,8 @@
   //
   // Note that this does not prune cookies to be below our limits (if we've
   // exceeded them) the way that calling GarbageCollect() would.
-  GarbageCollectExpired(Time::Now(),
-                        CookieMapItPair(cookies_.begin(), cookies_.end()),
-                        NULL);
+  GarbageCollectExpired(
+      Time::Now(), CookieMapItPair(cookies_.begin(), cookies_.end()), NULL);
 
   // Copy the CanonicalCookie pointers from the map so that we can use the same
   // sorter as elsewhere, then copy the result out.
@@ -1193,8 +1193,9 @@
     CookieMap::iterator curit = it;
     ++it;
     InternalDeleteCookie(curit, sync_to_store,
-                         sync_to_store ? DELETE_COOKIE_EXPLICIT :
-                             DELETE_COOKIE_DONT_RECORD /* Destruction. */);
+                         sync_to_store
+                             ? DELETE_COOKIE_EXPLICIT
+                             : DELETE_COOKIE_DONT_RECORD /* Destruction. */);
     ++num_deleted;
   }
 
@@ -1213,8 +1214,7 @@
 
     if (cc->CreationDate() >= delete_begin &&
         (delete_end.is_null() || cc->CreationDate() < delete_end)) {
-      InternalDeleteCookie(curit,
-                           true,  /*sync_to_store*/
+      InternalDeleteCookie(curit, true, /*sync_to_store*/
                            DELETE_COOKIE_EXPLICIT);
       ++num_deleted;
     }
@@ -1262,7 +1262,6 @@
   return DeleteAllCreatedBetweenForHost(Time(), Time::Max(), url);
 }
 
-
 bool CookieMonster::DeleteCanonicalCookie(const CanonicalCookie& cookie) {
   base::AutoLock autolock(lock_);
 
@@ -1285,15 +1284,15 @@
   DCHECK(!initialized_);
 
   cookieable_schemes_.clear();
-  cookieable_schemes_.insert(cookieable_schemes_.end(),
-                             schemes, schemes + num_schemes);
+  cookieable_schemes_.insert(cookieable_schemes_.end(), schemes,
+                             schemes + num_schemes);
 }
 
 void CookieMonster::SetEnableFileScheme(bool accept) {
   // This assumes "file" is always at the end of the array. See the comment
   // above kDefaultCookieableSchemes.
-  int num_schemes = accept ? kDefaultCookieableSchemesCount :
-      kDefaultCookieableSchemesCount - 1;
+  int num_schemes = accept ? kDefaultCookieableSchemesCount
+                           : kDefaultCookieableSchemesCount - 1;
   SetCookieableSchemes(kDefaultCookieableSchemes, num_schemes);
 }
 
@@ -1381,8 +1380,7 @@
     ++it;
 
     if (!cc->IsPersistent()) {
-      InternalDeleteCookie(curit,
-                           true,  /*sync_to_store*/
+      InternalDeleteCookie(curit, true, /*sync_to_store*/
                            DELETE_COOKIE_EXPIRED);
       ++num_deleted;
     }
@@ -1444,9 +1442,6 @@
 }
 
 void CookieMonster::ReportLoaded() {
-  // TODO(pkasting): Remove ScopedTracker below once crbug.com/457528 is fixed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION("457528 CookieMonster::ReportLoaded"));
   if (delegate_.get())
     delegate_->OnLoaded();
 }
@@ -1467,19 +1462,16 @@
   // This function does its own separate locking.
   StoreLoadedCookies(cookies);
 
-  std::deque<scoped_refptr<CookieMonsterTask> > tasks_pending_for_key;
+  std::deque<scoped_refptr<CookieMonsterTask>> tasks_pending_for_key;
 
   // We need to do this repeatedly until no more tasks were added to the queue
   // during the period where we release the lock.
   while (true) {
-    // TODO(pkasting): Remove ScopedTracker below once crbug.com/456373 is
-    // fixed.
-    tracked_objects::ScopedTracker tracking_profile1(
-        FROM_HERE_WITH_EXPLICIT_FUNCTION("456373 CookieMonster::OnKeyLoaded1"));
     {
       base::AutoLock autolock(lock_);
-      std::map<std::string, std::deque<scoped_refptr<CookieMonsterTask> > >
-        ::iterator it = tasks_pending_for_key_.find(key);
+      std::map<std::string,
+               std::deque<scoped_refptr<CookieMonsterTask>>>::iterator it =
+          tasks_pending_for_key_.find(key);
       if (it == tasks_pending_for_key_.end()) {
         keys_loaded_.insert(key);
         return;
@@ -1492,10 +1484,6 @@
       it->second.swap(tasks_pending_for_key);
     }
 
-    // TODO(pkasting): Remove ScopedTracker below once crbug.com/456373 is
-    // fixed.
-    tracked_objects::ScopedTracker tracking_profile2(
-        FROM_HERE_WITH_EXPLICIT_FUNCTION("456373 CookieMonster::OnKeyLoaded2"));
     while (!tasks_pending_for_key.empty()) {
       scoped_refptr<CookieMonsterTask> task = tasks_pending_for_key.front();
       task->Run();
@@ -1531,15 +1519,15 @@
 
       if (ContainsControlCharacter((*it)->Name()) ||
           ContainsControlCharacter((*it)->Value())) {
-          cookies_with_control_chars.push_back(inserted);
+        cookies_with_control_chars.push_back(inserted);
       }
     } else {
-      LOG(ERROR) << base::StringPrintf("Found cookies with duplicate creation "
-                                       "times in backing store: "
-                                       "{name='%s', domain='%s', path='%s'}",
-                                       (*it)->Name().c_str(),
-                                       (*it)->Domain().c_str(),
-                                       (*it)->Path().c_str());
+      LOG(ERROR) << base::StringPrintf(
+          "Found cookies with duplicate creation "
+          "times in backing store: "
+          "{name='%s', domain='%s', path='%s'}",
+          (*it)->Name().c_str(), (*it)->Domain().c_str(),
+          (*it)->Path().c_str());
       // We've been given ownership of the cookie and are throwing it
       // away; reclaim the space.
       delete (*it);
@@ -1567,9 +1555,6 @@
 }
 
 void CookieMonster::InvokeQueue() {
-  // TODO(pkasting): Remove ScopedTracker below once crbug.com/457528 is fixed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION("457528 CookieMonster::InvokeQueue"));
   while (true) {
     scoped_refptr<CookieMonsterTask> request_task;
     {
@@ -1588,6 +1573,10 @@
 }
 
 void CookieMonster::EnsureCookiesMapIsValid() {
+  // TODO(pkasting): Remove ScopedTracker below once crbug.com/456373 is fixed.
+  tracked_objects::ScopedTracker tracking_profile(
+      FROM_HERE_WITH_EXPLICIT_FUNCTION(
+          "456373 CookieMonster::EnsureCookiesMapIsValid"));
   lock_.AssertAcquired();
 
   int num_duplicates_trimmed = 0;
@@ -1610,10 +1599,9 @@
   histogram_cookie_deletion_cause_->Add(num_duplicates_trimmed);
 }
 
-int CookieMonster::TrimDuplicateCookiesForKey(
-    const std::string& key,
-    CookieMap::iterator begin,
-    CookieMap::iterator end) {
+int CookieMonster::TrimDuplicateCookiesForKey(const std::string& key,
+                                              CookieMap::iterator begin,
+                                              CookieMap::iterator end) {
   lock_.AssertAcquired();
 
   // Set of cookies ordered by creation time.
@@ -1632,8 +1620,7 @@
     DCHECK_EQ(key, it->first);
     CanonicalCookie* cookie = it->second;
 
-    CookieSignature signature(cookie->Name(), cookie->Domain(),
-                              cookie->Path());
+    CookieSignature signature(cookie->Name(), cookie->Domain(), cookie->Path());
     CookieSet& set = equivalent_cookies[signature];
 
     // We found a duplicate!
@@ -1643,8 +1630,8 @@
     // We save the iterator into |cookies_| rather than the actual cookie
     // pointer, since we may need to delete it later.
     bool insert_success = set.insert(it).second;
-    DCHECK(insert_success) <<
-        "Duplicate creation times found in duplicate cookie name scan.";
+    DCHECK(insert_success)
+        << "Duplicate creation times found in duplicate cookie name scan.";
   }
 
   // If there were no duplicates, we are done!
@@ -1657,8 +1644,7 @@
   // Otherwise, delete all the duplicate cookies, both from our in-memory store
   // and from the backing store.
   for (EquivalenceMap::iterator it = equivalent_cookies.begin();
-       it != equivalent_cookies.end();
-       ++it) {
+       it != equivalent_cookies.end(); ++it) {
     const CookieSignature& signature = it->first;
     CookieSet& dupes = it->second;
 
@@ -1673,17 +1659,13 @@
     LOG(ERROR) << base::StringPrintf(
         "Found %d duplicate cookies for host='%s', "
         "with {name='%s', domain='%s', path='%s'}",
-        static_cast<int>(dupes.size()),
-        key.c_str(),
-        signature.name.c_str(),
-        signature.domain.c_str(),
-        signature.path.c_str());
+        static_cast<int>(dupes.size()), key.c_str(), signature.name.c_str(),
+        signature.domain.c_str(), signature.path.c_str());
 
     // Remove all the cookies identified by |dupes|. It is valid to delete our
     // list of iterators one at a time, since |cookies_| is a multimap (they
     // don't invalidate existing iterators following deletion).
-    for (CookieSet::iterator dupes_it = dupes.begin();
-         dupes_it != dupes.end();
+    for (CookieSet::iterator dupes_it = dupes.begin(); dupes_it != dupes.end();
          ++dupes_it) {
       InternalDeleteCookie(*dupes_it, true,
                            DELETE_COOKIE_DUPLICATE_IN_BACKING_STORE);
@@ -1695,8 +1677,11 @@
 }
 
 // Note: file must be the last scheme.
-const char* const CookieMonster::kDefaultCookieableSchemes[] =
-    { "http", "https", "ws", "wss", "file" };
+const char* const CookieMonster::kDefaultCookieableSchemes[] = {"http",
+                                                                "https",
+                                                                "ws",
+                                                                "wss",
+                                                                "file"};
 const int CookieMonster::kDefaultCookieableSchemesCount =
     arraysize(kDefaultCookieableSchemes);
 
@@ -1722,8 +1707,8 @@
 
   // Can just dispatch to FindCookiesForKey
   const std::string key(GetKey(url.host()));
-  FindCookiesForKey(key, url, options, current_time,
-                    update_access_time, cookies);
+  FindCookiesForKey(key, url, options, current_time, update_access_time,
+                    cookies);
 }
 
 void CookieMonster::FindCookiesForKey(const std::string& key,
@@ -1735,7 +1720,7 @@
   lock_.AssertAcquired();
 
   for (CookieMapItPair its = cookies_.equal_range(key);
-       its.first != its.second; ) {
+       its.first != its.second;) {
     CookieMap::iterator curit = its.first;
     CanonicalCookie* cc = curit->second;
     ++its.first;
@@ -1770,7 +1755,7 @@
   bool found_equivalent_cookie = false;
   bool skipped_httponly = false;
   for (CookieMapItPair its = cookies_.equal_range(key);
-       its.first != its.second; ) {
+       its.first != its.second;) {
     CookieMap::iterator curit = its.first;
     CanonicalCookie* cc = curit->second;
     ++its.first;
@@ -1778,13 +1763,14 @@
     if (ecc.IsEquivalent(*cc)) {
       // We should never have more than one equivalent cookie, since they should
       // overwrite each other.
-      CHECK(!found_equivalent_cookie) <<
-          "Duplicate equivalent cookies found, cookie store is corrupted.";
+      CHECK(!found_equivalent_cookie)
+          << "Duplicate equivalent cookies found, cookie store is corrupted.";
       if (skip_httponly && cc->IsHttpOnly()) {
         skipped_httponly = true;
       } else {
-        InternalDeleteCookie(curit, true, already_expired ?
-            DELETE_COOKIE_EXPIRED_OVERWRITE : DELETE_COOKIE_OVERWRITE);
+        InternalDeleteCookie(curit, true, already_expired
+                                              ? DELETE_COOKIE_EXPIRED_OVERWRITE
+                                              : DELETE_COOKIE_OVERWRITE);
       }
       found_equivalent_cookie = true;
     }
@@ -1796,6 +1782,10 @@
     const std::string& key,
     CanonicalCookie* cc,
     bool sync_to_store) {
+  // TODO(pkasting): Remove ScopedTracker below once crbug.com/456373 is fixed.
+  tracked_objects::ScopedTracker tracking_profile(
+      FROM_HERE_WITH_EXPLICIT_FUNCTION(
+          "456373 CookieMonster::InternalInsertCookie"));
   lock_.AssertAcquired();
 
   if ((cc->IsPersistent() || persist_session_cookies_) && store_.get() &&
@@ -1804,8 +1794,8 @@
   CookieMap::iterator inserted =
       cookies_.insert(CookieMap::value_type(key, cc));
   if (delegate_.get()) {
-    delegate_->OnCookieChanged(
-        *cc, false, CookieMonsterDelegate::CHANGE_COOKIE_EXPLICIT);
+    delegate_->OnCookieChanged(*cc, false,
+                               CookieMonsterDelegate::CHANGE_COOKIE_EXPLICIT);
   }
   RunCallbacks(*cc, false);
 
@@ -1849,8 +1839,8 @@
     return false;
   }
 
-  VLOG(kVlogSetCookies) << "SetCookie() key: " << key << " cc: "
-                        << (*cc)->DebugString();
+  VLOG(kVlogSetCookies) << "SetCookie() key: " << key
+                        << " cc: " << (*cc)->DebugString();
 
   // Realize that we might be setting an expired cookie, and the only point
   // was to delete the cookie which we've already done.
@@ -1904,6 +1894,10 @@
 void CookieMonster::InternalDeleteCookie(CookieMap::iterator it,
                                          bool sync_to_store,
                                          DeletionCause deletion_cause) {
+  // TODO(pkasting): Remove ScopedTracker below once crbug.com/456373 is fixed.
+  tracked_objects::ScopedTracker tracking_profile(
+      FROM_HERE_WITH_EXPLICIT_FUNCTION(
+          "456373 CookieMonster::InternalDeleteCookie"));
   lock_.AssertAcquired();
 
   // Ideally, this would be asserted up where we define ChangeCauseMapping,
@@ -1935,21 +1929,19 @@
 
 // Domain expiry behavior is unchanged by key/expiry scheme (the
 // meaning of the key is different, but that's not visible to this routine).
-int CookieMonster::GarbageCollect(const Time& current,
-                                  const std::string& key) {
+int CookieMonster::GarbageCollect(const Time& current, const std::string& key) {
   lock_.AssertAcquired();
 
   int num_deleted = 0;
-  Time safe_date(
-      Time::Now() - TimeDelta::FromDays(kSafeFromGlobalPurgeDays));
+  Time safe_date(Time::Now() - TimeDelta::FromDays(kSafeFromGlobalPurgeDays));
 
   // Collect garbage for this key, minding cookie priorities.
   if (cookies_.count(key) > kDomainMaxCookies) {
     VLOG(kVlogGarbageCollection) << "GarbageCollect() key: " << key;
 
     CookieItVector cookie_its;
-    num_deleted += GarbageCollectExpired(
-        current, cookies_.equal_range(key), &cookie_its);
+    num_deleted +=
+        GarbageCollectExpired(current, cookies_.equal_range(key), &cookie_its);
     if (cookie_its.size() > kDomainMaxCookies) {
       VLOG(kVlogGarbageCollection) << "Deep Garbage Collect domain.";
       size_t purge_goal =
@@ -1962,15 +1954,13 @@
       // Schematic: [MLLHMHHLMM] => [LLL|MMMM|HHH], with 4 boundaries.
       it_bdd[0] = cookie_its.begin();
       it_bdd[3] = cookie_its.end();
-      it_bdd[1] = PartitionCookieByPriority(it_bdd[0], it_bdd[3],
-                                            COOKIE_PRIORITY_LOW);
+      it_bdd[1] =
+          PartitionCookieByPriority(it_bdd[0], it_bdd[3], COOKIE_PRIORITY_LOW);
       it_bdd[2] = PartitionCookieByPriority(it_bdd[1], it_bdd[3],
                                             COOKIE_PRIORITY_MEDIUM);
-      size_t quota[3] = {
-        kDomainCookiesQuotaLow,
-        kDomainCookiesQuotaMedium,
-        kDomainCookiesQuotaHigh
-      };
+      size_t quota[3] = {kDomainCookiesQuotaLow,
+                         kDomainCookiesQuotaMedium,
+                         kDomainCookiesQuotaHigh};
 
       // Purge domain cookies in 3 rounds.
       // Round 1: consider low-priority cookies only: evict least-recently
@@ -2000,15 +1990,11 @@
             LowerBoundAccessDate(it_purge_begin, it_purge_end, safe_date);
         // Delete cookies accessed before |safe_date|.
         num_deleted += GarbageCollectDeleteRange(
-            current,
-            DELETE_COOKIE_EVICTED_DOMAIN_PRE_SAFE,
-            it_purge_begin,
+            current, DELETE_COOKIE_EVICTED_DOMAIN_PRE_SAFE, it_purge_begin,
             it_purge_middle);
         // Delete cookies accessed on or after |safe_date|.
         num_deleted += GarbageCollectDeleteRange(
-            current,
-            DELETE_COOKIE_EVICTED_DOMAIN_POST_SAFE,
-            it_purge_middle,
+            current, DELETE_COOKIE_EVICTED_DOMAIN_POST_SAFE, it_purge_middle,
             it_purge_end);
         it_purge_begin = it_purge_end;
       }
@@ -2018,8 +2004,7 @@
 
   // Collect garbage for everything. With firefox style we want to preserve
   // cookies accessed in kSafeFromGlobalPurgeDays, otherwise evict.
-  if (cookies_.size() > kMaxCookies &&
-      earliest_access_time_ < safe_date) {
+  if (cookies_.size() > kMaxCookies && earliest_access_time_ < safe_date) {
     VLOG(kVlogGarbageCollection) << "GarbageCollect() everything";
     CookieItVector cookie_its;
     num_deleted += GarbageCollectExpired(
@@ -2035,16 +2020,12 @@
       SortLeastRecentlyAccessed(cookie_its.begin(), cookie_its.end(),
                                 purge_goal);
       // Find boundary to cookies older than safe_date.
-      CookieItVector::iterator global_purge_it =
-          LowerBoundAccessDate(cookie_its.begin(),
-                               cookie_its.begin() + purge_goal,
-                               safe_date);
+      CookieItVector::iterator global_purge_it = LowerBoundAccessDate(
+          cookie_its.begin(), cookie_its.begin() + purge_goal, safe_date);
       // Only delete the old cookies.
-      num_deleted += GarbageCollectDeleteRange(
-          current,
-          DELETE_COOKIE_EVICTED_GLOBAL,
-          cookie_its.begin(),
-          global_purge_it);
+      num_deleted +=
+          GarbageCollectDeleteRange(current, DELETE_COOKIE_EVICTED_GLOBAL,
+                                    cookie_its.begin(), global_purge_it);
       // Set access day to the oldest cookie that wasn't deleted.
       earliest_access_time_ = (*global_purge_it)->second->LastAccessDate();
     }
@@ -2053,10 +2034,9 @@
   return num_deleted;
 }
 
-int CookieMonster::GarbageCollectExpired(
-    const Time& current,
-    const CookieMapItPair& itpair,
-    CookieItVector* cookie_its) {
+int CookieMonster::GarbageCollectExpired(const Time& current,
+                                         const CookieMapItPair& itpair,
+                                         CookieItVector* cookie_its) {
   if (keep_expired_cookies_)
     return 0;
 
@@ -2078,11 +2058,10 @@
   return num_deleted;
 }
 
-int CookieMonster::GarbageCollectDeleteRange(
-    const Time& current,
-    DeletionCause cause,
-    CookieItVector::iterator it_begin,
-    CookieItVector::iterator it_end) {
+int CookieMonster::GarbageCollectDeleteRange(const Time& current,
+                                             DeletionCause cause,
+                                             CookieItVector::iterator it_begin,
+                                             CookieItVector::iterator it_end) {
   for (CookieItVector::iterator it = it_begin; it != it_end; it++) {
     histogram_evicted_last_access_minutes_->Add(
         (current - (*it)->second->LastAccessDate()).InMinutes());
@@ -2146,8 +2125,8 @@
   }
 
   // The scheme didn't match any in our whitelist.
-  VLOG(kVlogPerCookieMonster) << "WARNING: Unsupported cookie scheme: "
-                              << url.scheme();
+  VLOG(kVlogPerCookieMonster)
+      << "WARNING: Unsupported cookie scheme: " << url.scheme();
   return false;
 }
 
@@ -2176,7 +2155,7 @@
   TimeTicks beginning_of_time(TimeTicks::Now());
 
   for (CookieMap::const_iterator it_key = cookies_.begin();
-       it_key != cookies_.end(); ) {
+       it_key != cookies_.end();) {
     const std::string& key(it_key->first);
 
     int key_count = 0;
@@ -2199,9 +2178,9 @@
     it_key = its_cookies.second;
   }
 
-  VLOG(kVlogPeriodic)
-      << "Time for recording cookie stats (us): "
-      << (TimeTicks::Now() - beginning_of_time).InMicroseconds();
+  VLOG(kVlogPeriodic) << "Time for recording cookie stats (us): "
+                      << (TimeTicks::Now() - beginning_of_time)
+                             .InMicroseconds();
 
   last_statistic_record_time_ = current_time;
 }
@@ -2232,55 +2211,49 @@
 void CookieMonster::InitializeHistograms() {
   // From UMA_HISTOGRAM_CUSTOM_COUNTS
   histogram_expiration_duration_minutes_ = base::Histogram::FactoryGet(
-      "Cookie.ExpirationDurationMinutes",
-      1, kMinutesInTenYears, 50,
+      "Cookie.ExpirationDurationMinutes", 1, kMinutesInTenYears, 50,
       base::Histogram::kUmaTargetedHistogramFlag);
   histogram_between_access_interval_minutes_ = base::Histogram::FactoryGet(
-      "Cookie.BetweenAccessIntervalMinutes",
-      1, kMinutesInTenYears, 50,
+      "Cookie.BetweenAccessIntervalMinutes", 1, kMinutesInTenYears, 50,
       base::Histogram::kUmaTargetedHistogramFlag);
   histogram_evicted_last_access_minutes_ = base::Histogram::FactoryGet(
-      "Cookie.EvictedLastAccessMinutes",
-      1, kMinutesInTenYears, 50,
+      "Cookie.EvictedLastAccessMinutes", 1, kMinutesInTenYears, 50,
       base::Histogram::kUmaTargetedHistogramFlag);
   histogram_count_ = base::Histogram::FactoryGet(
-      "Cookie.Count", 1, 4000, 50,
-      base::Histogram::kUmaTargetedHistogramFlag);
-  histogram_domain_count_ = base::Histogram::FactoryGet(
-      "Cookie.DomainCount", 1, 4000, 50,
-      base::Histogram::kUmaTargetedHistogramFlag);
-  histogram_etldp1_count_ = base::Histogram::FactoryGet(
-      "Cookie.Etldp1Count", 1, 4000, 50,
-      base::Histogram::kUmaTargetedHistogramFlag);
-  histogram_domain_per_etldp1_count_ = base::Histogram::FactoryGet(
-      "Cookie.DomainPerEtldp1Count", 1, 4000, 50,
-      base::Histogram::kUmaTargetedHistogramFlag);
+      "Cookie.Count", 1, 4000, 50, base::Histogram::kUmaTargetedHistogramFlag);
+  histogram_domain_count_ =
+      base::Histogram::FactoryGet("Cookie.DomainCount", 1, 4000, 50,
+                                  base::Histogram::kUmaTargetedHistogramFlag);
+  histogram_etldp1_count_ =
+      base::Histogram::FactoryGet("Cookie.Etldp1Count", 1, 4000, 50,
+                                  base::Histogram::kUmaTargetedHistogramFlag);
+  histogram_domain_per_etldp1_count_ =
+      base::Histogram::FactoryGet("Cookie.DomainPerEtldp1Count", 1, 4000, 50,
+                                  base::Histogram::kUmaTargetedHistogramFlag);
 
   // From UMA_HISTOGRAM_COUNTS_10000 & UMA_HISTOGRAM_CUSTOM_COUNTS
-  histogram_number_duplicate_db_cookies_ = base::Histogram::FactoryGet(
-      "Net.NumDuplicateCookiesInDb", 1, 10000, 50,
-      base::Histogram::kUmaTargetedHistogramFlag);
+  histogram_number_duplicate_db_cookies_ =
+      base::Histogram::FactoryGet("Net.NumDuplicateCookiesInDb", 1, 10000, 50,
+                                  base::Histogram::kUmaTargetedHistogramFlag);
 
   // From UMA_HISTOGRAM_ENUMERATION
   histogram_cookie_deletion_cause_ = base::LinearHistogram::FactoryGet(
-      "Cookie.DeletionCause", 1,
-      DELETE_COOKIE_LAST_ENTRY - 1, DELETE_COOKIE_LAST_ENTRY,
-      base::Histogram::kUmaTargetedHistogramFlag);
+      "Cookie.DeletionCause", 1, DELETE_COOKIE_LAST_ENTRY - 1,
+      DELETE_COOKIE_LAST_ENTRY, base::Histogram::kUmaTargetedHistogramFlag);
 
   // From UMA_HISTOGRAM_{CUSTOM_,}TIMES
   histogram_time_blocked_on_load_ = base::Histogram::FactoryTimeGet(
-      "Cookie.TimeBlockedOnLoad",
-      base::TimeDelta::FromMilliseconds(1), base::TimeDelta::FromMinutes(1),
-      50, base::Histogram::kUmaTargetedHistogramFlag);
+      "Cookie.TimeBlockedOnLoad", base::TimeDelta::FromMilliseconds(1),
+      base::TimeDelta::FromMinutes(1), 50,
+      base::Histogram::kUmaTargetedHistogramFlag);
 }
 
-
 // The system resolution is not high enough, so we can have multiple
 // set cookies that result in the same system time.  When this happens, we
 // increment by one Time unit.  Let's hope computers don't get too fast.
 Time CookieMonster::CurrentTime() {
-  return std::max(Time::Now(),
-      Time::FromInternalValue(last_time_seen_.ToInternalValue() + 1));
+  return std::max(Time::Now(), Time::FromInternalValue(
+                                   last_time_seen_.ToInternalValue() + 1));
 }
 
 bool CookieMonster::CopyCookiesForKeyToOtherCookieMonster(
@@ -2295,8 +2268,7 @@
       return false;
 
     for (CookieMapItPair its = cookies_.equal_range(key);
-         its.first != its.second;
-         ++its.first) {
+         its.first != its.second; ++its.first) {
       CookieMap::iterator curit = its.first;
       CanonicalCookie* cc = curit->second;
 
@@ -2317,8 +2289,7 @@
     // Store the copied cookies in |other|.
     for (ScopedVector<CanonicalCookie>::const_iterator it =
              duplicated_cookies.begin();
-         it != duplicated_cookies.end();
-         ++it) {
+         it != duplicated_cookies.end(); ++it) {
       other->InternalInsertCookie(key, *it, true);
     }
 
@@ -2335,10 +2306,9 @@
 }
 
 scoped_ptr<CookieStore::CookieChangedSubscription>
-CookieMonster::AddCallbackForCookie(
-    const GURL& gurl,
-    const std::string& name,
-    const CookieChangedCallback& callback) {
+CookieMonster::AddCallbackForCookie(const GURL& gurl,
+                                    const std::string& name,
+                                    const CookieChangedCallback& callback) {
   base::AutoLock autolock(lock_);
   std::pair<GURL, std::string> key(gurl, name);
   if (hook_map_.count(key) == 0)
diff --git a/net/cookies/cookie_monster.h b/net/cookies/cookie_monster.h
index 0f00490..75c63976 100644
--- a/net/cookies/cookie_monster.h
+++ b/net/cookies/cookie_monster.h
@@ -171,7 +171,6 @@
                                  CookiePriority priority,
                                  const SetCookiesCallback& callback);
 
-
   // Returns all the cookies, for use in management UI, etc. This does not mark
   // the cookies as having been accessed.
   // The returned cookies are ordered by longest path, then by earliest
@@ -195,8 +194,7 @@
   // regardless of path.  This includes all http_only and secure cookies,
   // but does not include any domain cookies that may apply to this host.
   // Returns the number of cookies deleted.
-  void DeleteAllForHostAsync(const GURL& url,
-                             const DeleteCallback& callback);
+  void DeleteAllForHostAsync(const GURL& url, const DeleteCallback& callback);
 
   // Deletes one specific cookie.
   void DeleteCanonicalCookieAsync(const CanonicalCookie& cookie,
@@ -325,7 +323,8 @@
  private:
   // For queueing the cookie monster calls.
   class CookieMonsterTask;
-  template <typename Result> class DeleteTask;
+  template <typename Result>
+  class DeleteTask;
   class DeleteAllCreatedBetweenTask;
   class DeleteAllCreatedBetweenForHostTask;
   class DeleteAllForHostTask;
@@ -495,9 +494,8 @@
   // (eTLD+1). Called when all cookies for the domain key(eTLD+1) have been
   // loaded from DB. See PersistentCookieStore::Load for details on the contents
   // of cookies.
-  void OnKeyLoaded(
-    const std::string& key,
-    const std::vector<CanonicalCookie*>& cookies);
+  void OnKeyLoaded(const std::string& key,
+                   const std::vector<CanonicalCookie*>& cookies);
 
   // Stores the loaded cookies.
   void StoreLoadedCookies(const std::vector<CanonicalCookie*>& cookies);
@@ -568,7 +566,8 @@
   // the correct CookieMonsterDelegate::ChangeCause for OnCookieChanged
   // notifications.  Guarantee: All iterators to cookies_ except to the
   // deleted entry remain vaild.
-  void InternalDeleteCookie(CookieMap::iterator it, bool sync_to_store,
+  void InternalDeleteCookie(CookieMap::iterator it,
+                            bool sync_to_store,
                             DeletionCause deletion_cause);
 
   // If the number of cookies for CookieMap key |key|, or globally, are
@@ -618,7 +617,7 @@
   // Runs the task if, or defers the task until, the cookies for the given URL
   // are loaded.
   void DoCookieTaskForURL(const scoped_refptr<CookieMonsterTask>& task_item,
-    const GURL& url);
+                          const GURL& url);
 
   // Run all cookie changed callbacks that are monitoring |cookie|.
   // |removed| is true if the cookie was deleted.
@@ -654,12 +653,12 @@
   // Map of domain keys to their associated task queues. These tasks are blocked
   // until all cookies for the associated domain key eTLD+1 are loaded from the
   // backend store.
-  std::map<std::string, std::deque<scoped_refptr<CookieMonsterTask> > >
+  std::map<std::string, std::deque<scoped_refptr<CookieMonsterTask>>>
       tasks_pending_for_key_;
 
   // Queues tasks that are blocked until all cookies are loaded from the backend
   // store.
-  std::queue<scoped_refptr<CookieMonsterTask> > tasks_pending_;
+  std::queue<scoped_refptr<CookieMonsterTask>> tasks_pending_;
 
   scoped_refptr<PersistentCookieStore> store_;
 
diff --git a/net/cookies/cookie_monster_perftest.cc b/net/cookies/cookie_monster_perftest.cc
index 40dc9463..95ccbad 100644
--- a/net/cookies/cookie_monster_perftest.cc
+++ b/net/cookies/cookie_monster_perftest.cc
@@ -52,21 +52,22 @@
     has_run_ = false;
   }
 
-  void Run() {
-    has_run_ = true;
-  }
+  void Run() { has_run_ = true; }
 
   bool has_run_;
 };
 
-class SetCookieCallback  : public BaseCallback {
+class SetCookieCallback : public BaseCallback {
  public:
-  void SetCookie(
-      CookieMonster* cm, const GURL& gurl, const std::string& cookie) {
-    cm->SetCookieWithOptionsAsync(gurl, cookie, options_, base::Bind(
-        &SetCookieCallback::Run, base::Unretained(this)));
+  void SetCookie(CookieMonster* cm,
+                 const GURL& gurl,
+                 const std::string& cookie) {
+    cm->SetCookieWithOptionsAsync(
+        gurl, cookie, options_,
+        base::Bind(&SetCookieCallback::Run, base::Unretained(this)));
     WaitForCallback();
   }
+
  private:
   void Run(bool success) {
     EXPECT_TRUE(success);
@@ -78,8 +79,9 @@
 class GetCookiesCallback : public BaseCallback {
  public:
   const std::string& GetCookies(CookieMonster* cm, const GURL& gurl) {
-    cm->GetCookiesWithOptionsAsync(gurl, options_, base::Bind(
-        &GetCookiesCallback::Run, base::Unretained(this)));
+    cm->GetCookiesWithOptionsAsync(
+        gurl, options_,
+        base::Bind(&GetCookiesCallback::Run, base::Unretained(this)));
     WaitForCallback();
     return cookies_;
   }
@@ -161,8 +163,8 @@
 
   // Add a cookie on a bunch of host
   base::PerfTimeLogger timer("Cookie_monster_add_many_hosts");
-  for (std::vector<GURL>::const_iterator it = gurls.begin();
-       it != gurls.end(); ++it) {
+  for (std::vector<GURL>::const_iterator it = gurls.begin(); it != gurls.end();
+       ++it) {
     setCookieCallback.SetCookie(cm.get(), *it, cookie);
   }
   timer.Done();
@@ -170,8 +172,8 @@
   GetCookiesCallback getCookiesCallback;
 
   base::PerfTimeLogger timer2("Cookie_monster_query_many_hosts");
-  for (std::vector<GURL>::const_iterator it = gurls.begin();
-       it != gurls.end(); ++it) {
+  for (std::vector<GURL>::const_iterator it = gurls.begin(); it != gurls.end();
+       ++it) {
     getCookiesCallback.GetCookies(cm.get(), *it);
   }
   timer2.Done();
@@ -214,13 +216,12 @@
     }
   }
 
-
   EXPECT_EQ(31u, domain_list.size());
   for (std::vector<std::string>::const_iterator it = domain_list.begin();
        it != domain_list.end(); it++) {
     GURL gurl("https://" + *it + "/");
-    const std::string cookie = base::StringPrintf(domain_cookie_format_tree,
-                                                  it->c_str());
+    const std::string cookie =
+        base::StringPrintf(domain_cookie_format_tree, it->c_str());
     setCookieCallback.SetCookie(cm.get(), gurl, cookie);
   }
   EXPECT_EQ(31u, cm->GetAllCookies().size());
@@ -261,8 +262,8 @@
     for (std::vector<std::string>::const_iterator it = domain_list.begin();
          it != domain_list.end(); it++) {
       GURL gurl("https://" + *it + "/");
-      const std::string cookie = base::StringPrintf(domain_cookie_format_line,
-                                                    i, it->c_str());
+      const std::string cookie =
+          base::StringPrintf(domain_cookie_format_line, i, it->c_str());
       setCookieCallback.SetCookie(cm.get(), gurl, cookie);
     }
   }
@@ -289,8 +290,8 @@
     std::string domain_name(base::StringPrintf(".Domain_%d.com", domain_num));
     std::string gurl("www" + domain_name);
     for (int cookie_num = 0; cookie_num < 50; cookie_num++) {
-      std::string cookie_line(base::StringPrintf("Cookie_%d=1; Path=/",
-                                                 cookie_num));
+      std::string cookie_line(
+          base::StringPrintf("Cookie_%d=1; Path=/", cookie_num));
       AddCookieToList(gurl, cookie_line,
                       base::Time::FromInternalValue(time_tick++),
                       &initial_cookies);
@@ -335,40 +336,43 @@
     size_t num_cookies;
     size_t num_old_cookies;
   } test_cases[] = {
-    {
-      // A whole lot of recent cookies; gc shouldn't happen.
-      "all_recent",
-      CookieMonster::kMaxCookies * 2,
-      0,
-    }, {
-      // Some old cookies, but still overflowing max.
-      "mostly_recent",
-      CookieMonster::kMaxCookies * 2,
-      CookieMonster::kMaxCookies / 2,
-    }, {
-      // Old cookies enough to bring us right down to our purge line.
-      "balanced",
-      CookieMonster::kMaxCookies * 2,
-      CookieMonster::kMaxCookies + CookieMonster::kPurgeCookies + 1,
-    }, {
-      "mostly_old",
-      // Old cookies enough to bring below our purge line (which we
-      // shouldn't do).
-      CookieMonster::kMaxCookies * 2,
-      CookieMonster::kMaxCookies * 3 / 4,
-    }, {
-      "less_than_gc_thresh",
-      // Few enough cookies that gc shouldn't happen at all.
-      CookieMonster::kMaxCookies - 5,
-      0,
-    },
+      {
+       // A whole lot of recent cookies; gc shouldn't happen.
+       "all_recent",
+       CookieMonster::kMaxCookies * 2,
+       0,
+      },
+      {
+       // Some old cookies, but still overflowing max.
+       "mostly_recent",
+       CookieMonster::kMaxCookies * 2,
+       CookieMonster::kMaxCookies / 2,
+      },
+      {
+       // Old cookies enough to bring us right down to our purge line.
+       "balanced",
+       CookieMonster::kMaxCookies * 2,
+       CookieMonster::kMaxCookies + CookieMonster::kPurgeCookies + 1,
+      },
+      {
+       "mostly_old",
+       // Old cookies enough to bring below our purge line (which we
+       // shouldn't do).
+       CookieMonster::kMaxCookies * 2,
+       CookieMonster::kMaxCookies * 3 / 4,
+      },
+      {
+       "less_than_gc_thresh",
+       // Few enough cookies that gc shouldn't happen at all.
+       CookieMonster::kMaxCookies - 5,
+       0,
+      },
   };
   for (int ci = 0; ci < static_cast<int>(arraysize(test_cases)); ++ci) {
     const TestCase& test_case(test_cases[ci]);
-    scoped_refptr<CookieMonster> cm(
-        CreateMonsterFromStoreForGC(
-            test_case.num_cookies, test_case.num_old_cookies,
-            CookieMonster::kSafeFromGlobalPurgeDays * 2));
+    scoped_refptr<CookieMonster> cm(CreateMonsterFromStoreForGC(
+        test_case.num_cookies, test_case.num_old_cookies,
+        CookieMonster::kSafeFromGlobalPurgeDays * 2));
 
     GURL gurl("http://google.com");
     std::string cookie_line("z=3");
diff --git a/net/cookies/cookie_monster_store_test.cc b/net/cookies/cookie_monster_store_test.cc
index 226242a..dc371ed 100644
--- a/net/cookies/cookie_monster_store_test.cc
+++ b/net/cookies/cookie_monster_store_test.cc
@@ -17,15 +17,14 @@
 namespace net {
 LoadedCallbackTask::LoadedCallbackTask(LoadedCallback loaded_callback,
                                        std::vector<CanonicalCookie*> cookies)
-    : loaded_callback_(loaded_callback),
-      cookies_(cookies) {
+    : loaded_callback_(loaded_callback), cookies_(cookies) {
 }
 
-LoadedCallbackTask::~LoadedCallbackTask() {}
+LoadedCallbackTask::~LoadedCallbackTask() {
+}
 
 MockPersistentCookieStore::MockPersistentCookieStore()
-    : load_return_value_(true),
-      loaded_(false) {
+    : load_return_value_(true), loaded_(false) {
 }
 
 void MockPersistentCookieStore::SetLoadExpectation(
@@ -62,19 +61,17 @@
 }
 
 void MockPersistentCookieStore::AddCookie(const CanonicalCookie& cookie) {
-  commands_.push_back(
-      CookieStoreCommand(CookieStoreCommand::ADD, cookie));
+  commands_.push_back(CookieStoreCommand(CookieStoreCommand::ADD, cookie));
 }
 
 void MockPersistentCookieStore::UpdateCookieAccessTime(
     const CanonicalCookie& cookie) {
-  commands_.push_back(CookieStoreCommand(
-      CookieStoreCommand::UPDATE_ACCESS_TIME, cookie));
+  commands_.push_back(
+      CookieStoreCommand(CookieStoreCommand::UPDATE_ACCESS_TIME, cookie));
 }
 
 void MockPersistentCookieStore::DeleteCookie(const CanonicalCookie& cookie) {
-  commands_.push_back(
-      CookieStoreCommand(CookieStoreCommand::REMOVE, cookie));
+  commands_.push_back(CookieStoreCommand(CookieStoreCommand::REMOVE, cookie));
 }
 
 void MockPersistentCookieStore::Flush(const base::Closure& callback) {
@@ -85,9 +82,11 @@
 void MockPersistentCookieStore::SetForceKeepSessionState() {
 }
 
-MockPersistentCookieStore::~MockPersistentCookieStore() {}
+MockPersistentCookieStore::~MockPersistentCookieStore() {
+}
 
-MockCookieMonsterDelegate::MockCookieMonsterDelegate() {}
+MockCookieMonsterDelegate::MockCookieMonsterDelegate() {
+}
 
 void MockCookieMonsterDelegate::OnCookieChanged(
     const CanonicalCookie& cookie,
@@ -97,14 +96,15 @@
   changes_.push_back(notification);
 }
 
-void MockCookieMonsterDelegate::OnLoaded() {}
+void MockCookieMonsterDelegate::OnLoaded() {
+}
 
-MockCookieMonsterDelegate::~MockCookieMonsterDelegate() {}
+MockCookieMonsterDelegate::~MockCookieMonsterDelegate() {
+}
 
 CanonicalCookie BuildCanonicalCookie(const std::string& key,
                                      const std::string& cookie_line,
                                      const base::Time& creation_time) {
-
   // Parse the cookie line.
   ParsedCookie pc(cookie_line);
   EXPECT_TRUE(pc.IsValid());
@@ -114,24 +114,22 @@
   // functions. Would be nice to export them, and re-use here.
   EXPECT_FALSE(pc.HasMaxAge());
   EXPECT_TRUE(pc.HasPath());
-  base::Time cookie_expires = pc.HasExpires() ?
-      cookie_util::ParseCookieTime(pc.Expires()) : base::Time();
+  base::Time cookie_expires = pc.HasExpires()
+                                  ? cookie_util::ParseCookieTime(pc.Expires())
+                                  : base::Time();
   std::string cookie_path = pc.Path();
 
-  return CanonicalCookie(
-      GURL(), pc.Name(), pc.Value(), key, cookie_path,
-      creation_time, cookie_expires, creation_time,
-      pc.IsSecure(), pc.IsHttpOnly(), pc.Priority());
+  return CanonicalCookie(GURL(), pc.Name(), pc.Value(), key, cookie_path,
+                         creation_time, cookie_expires, creation_time,
+                         pc.IsSecure(), pc.IsHttpOnly(), pc.Priority());
 }
 
-void AddCookieToList(
-    const std::string& key,
-    const std::string& cookie_line,
-    const base::Time& creation_time,
-    std::vector<CanonicalCookie*>* out_list) {
-  scoped_ptr<CanonicalCookie> cookie(
-      new CanonicalCookie(
-          BuildCanonicalCookie(key, cookie_line, creation_time)));
+void AddCookieToList(const std::string& key,
+                     const std::string& cookie_line,
+                     const base::Time& creation_time,
+                     std::vector<CanonicalCookie*>* out_list) {
+  scoped_ptr<CanonicalCookie> cookie(new CanonicalCookie(
+      BuildCanonicalCookie(key, cookie_line, creation_time)));
 
   out_list->push_back(cookie.release());
 }
@@ -155,7 +153,8 @@
   loaded_ = true;
 }
 
-void MockSimplePersistentCookieStore::LoadCookiesForKey(const std::string& key,
+void MockSimplePersistentCookieStore::LoadCookiesForKey(
+    const std::string& key,
     const LoadedCallback& loaded_callback) {
   if (!loaded_) {
     Load(loaded_callback);
@@ -197,10 +196,9 @@
 void MockSimplePersistentCookieStore::SetForceKeepSessionState() {
 }
 
-CookieMonster* CreateMonsterFromStoreForGC(
-    int num_cookies,
-    int num_old_cookies,
-    int days_old) {
+CookieMonster* CreateMonsterFromStoreForGC(int num_cookies,
+                                           int num_old_cookies,
+                                           int days_old) {
   base::Time current(base::Time::Now());
   base::Time past_creation(base::Time::Now() - base::TimeDelta::FromDays(1000));
   scoped_refptr<MockSimplePersistentCookieStore> store(
@@ -211,19 +209,19 @@
         past_creation + base::TimeDelta::FromMicroseconds(i);
     base::Time expiration_time = current + base::TimeDelta::FromDays(30);
     base::Time last_access_time =
-        (i < num_old_cookies) ? current - base::TimeDelta::FromDays(days_old) :
-                                current;
+        (i < num_old_cookies) ? current - base::TimeDelta::FromDays(days_old)
+                              : current;
 
-    CanonicalCookie cc(
-        GURL(), "a", "1", base::StringPrintf("h%05d.izzle", i), "/path",
-        creation_time, expiration_time, last_access_time, false, false,
-        COOKIE_PRIORITY_DEFAULT);
+    CanonicalCookie cc(GURL(), "a", "1", base::StringPrintf("h%05d.izzle", i),
+                       "/path", creation_time, expiration_time,
+                       last_access_time, false, false, COOKIE_PRIORITY_DEFAULT);
     store->AddCookie(cc);
   }
 
   return new CookieMonster(store.get(), NULL);
 }
 
-MockSimplePersistentCookieStore::~MockSimplePersistentCookieStore() {}
+MockSimplePersistentCookieStore::~MockSimplePersistentCookieStore() {
+}
 
 }  // namespace net
diff --git a/net/cookies/cookie_monster_store_test.h b/net/cookies/cookie_monster_store_test.h
index 4a3f9a96..4c4d9df2 100644
--- a/net/cookies/cookie_monster_store_test.h
+++ b/net/cookies/cookie_monster_store_test.h
@@ -34,9 +34,7 @@
   LoadedCallbackTask(LoadedCallback loaded_callback,
                      std::vector<CanonicalCookie*> cookies);
 
-  void Run() {
-    loaded_callback_.Run(cookies_);
-  }
+  void Run() { loaded_callback_.Run(cookies_); }
 
  private:
   friend class base::RefCountedThreadSafe<LoadedCallbackTask>;
@@ -57,8 +55,7 @@
   };
 
   CookieStoreCommand(Type type, const CanonicalCookie& cookie)
-      : type(type),
-        cookie(cookie) {}
+      : type(type), cookie(cookie) {}
 
   Type type;
   CanonicalCookie cookie;
@@ -67,20 +64,16 @@
 // Implementation of PersistentCookieStore that captures the
 // received commands and saves them to a list.
 // The result of calls to Load() can be configured using SetLoadExpectation().
-class MockPersistentCookieStore
-    : public CookieMonster::PersistentCookieStore {
+class MockPersistentCookieStore : public CookieMonster::PersistentCookieStore {
  public:
   typedef std::vector<CookieStoreCommand> CommandList;
 
   MockPersistentCookieStore();
 
-  void SetLoadExpectation(
-      bool return_value,
-      const std::vector<CanonicalCookie*>& result);
+  void SetLoadExpectation(bool return_value,
+                          const std::vector<CanonicalCookie*>& result);
 
-  const CommandList& commands() const {
-    return commands_;
-  }
+  const CommandList& commands() const { return commands_; }
 
   void Load(const LoadedCallback& loaded_callback) override;
 
@@ -116,8 +109,7 @@
 // Mock for CookieMonsterDelegate
 class MockCookieMonsterDelegate : public CookieMonsterDelegate {
  public:
-  typedef std::pair<CanonicalCookie, bool>
-      CookieNotification;
+  typedef std::pair<CanonicalCookie, bool> CookieNotification;
 
   MockCookieMonsterDelegate();
 
@@ -145,11 +137,10 @@
                                      const base::Time& creation_time);
 
 // Helper to build a list of CanonicalCookie*s.
-void AddCookieToList(
-    const std::string& key,
-    const std::string& cookie_line,
-    const base::Time& creation_time,
-    std::vector<CanonicalCookie*>* out_list);
+void AddCookieToList(const std::string& key,
+                     const std::string& cookie_line,
+                     const base::Time& creation_time,
+                     std::vector<CanonicalCookie*>* out_list);
 
 // Just act like a backing database.  Keep cookie information from
 // Add/Update/Delete and regurgitate it when Load is called.
@@ -194,10 +185,9 @@
 // Do two SetCookies().  Return whether each of the two SetCookies() took
 // longer than |gc_perf_micros| to complete, and how many cookie were
 // left in the store afterwards.
-CookieMonster* CreateMonsterFromStoreForGC(
-    int num_cookies,
-    int num_old_cookies,
-    int days_old);
+CookieMonster* CreateMonsterFromStoreForGC(int num_cookies,
+                                           int num_old_cookies,
+                                           int days_old);
 
 }  // namespace net
 
diff --git a/net/cookies/cookie_monster_unittest.cc b/net/cookies/cookie_monster_unittest.cc
index 224efec..51724a9 100644
--- a/net/cookies/cookie_monster_unittest.cc
+++ b/net/cookies/cookie_monster_unittest.cc
@@ -46,8 +46,9 @@
     : public CookieMonster::PersistentCookieStore {
  public:
   MOCK_METHOD1(Load, void(const LoadedCallback& loaded_callback));
-  MOCK_METHOD2(LoadCookiesForKey, void(const std::string& key,
-                                       const LoadedCallback& loaded_callback));
+  MOCK_METHOD2(LoadCookiesForKey,
+               void(const std::string& key,
+                    const LoadedCallback& loaded_callback));
   MOCK_METHOD1(AddCookie, void(const CanonicalCookie& cc));
   MOCK_METHOD1(UpdateCookieAccessTime, void(const CanonicalCookie& cc));
   MOCK_METHOD1(DeleteCookie, void(const CanonicalCookie& cc));
@@ -64,8 +65,7 @@
 const char kTopLevelDomainPlus1[] = "http://www.harvard.edu";
 const char kTopLevelDomainPlus2[] = "http://www.math.harvard.edu";
 const char kTopLevelDomainPlus2Secure[] = "https://www.math.harvard.edu";
-const char kTopLevelDomainPlus3[] =
-    "http://www.bourbaki.math.harvard.edu";
+const char kTopLevelDomainPlus3[] = "http://www.bourbaki.math.harvard.edu";
 const char kOtherDomain[] = "http://www.mit.edu";
 const char kUrlGoogleSpecific[] = "http://www.gmail.google.izzle";
 
@@ -91,12 +91,12 @@
     return new CookieMonster(NULL, NULL);
   }
 
-  static const bool is_cookie_monster              = true;
-  static const bool supports_http_only             = true;
-  static const bool supports_non_dotted_domains    = true;
-  static const bool supports_trailing_dots         = true;
-  static const bool filters_schemes                = true;
-  static const bool has_path_prefix_bug            = false;
+  static const bool is_cookie_monster = true;
+  static const bool supports_http_only = true;
+  static const bool supports_non_dotted_domains = true;
+  static const bool supports_trailing_dots = true;
+  static const bool filters_schemes = true;
+  static const bool has_path_prefix_bug = false;
   static const int creation_time_granularity_in_ms = 0;
 };
 
@@ -110,25 +110,21 @@
 
 class CookieMonsterTest : public CookieStoreTest<CookieMonsterTestTraits> {
  protected:
-
   CookieList GetAllCookies(CookieMonster* cm) {
     DCHECK(cm);
     GetCookieListCallback callback;
     cm->GetAllCookiesAsync(
-        base::Bind(&GetCookieListCallback::Run,
-                   base::Unretained(&callback)));
+        base::Bind(&GetCookieListCallback::Run, base::Unretained(&callback)));
     RunFor(kTimeout);
     EXPECT_TRUE(callback.did_run());
     return callback.cookies();
   }
 
-  CookieList GetAllCookiesForURL(CookieMonster* cm,
-                                 const GURL& url) {
+  CookieList GetAllCookiesForURL(CookieMonster* cm, const GURL& url) {
     DCHECK(cm);
     GetCookieListCallback callback;
-    cm->GetAllCookiesForURLAsync(
-        url, base::Bind(&GetCookieListCallback::Run,
-                        base::Unretained(&callback)));
+    cm->GetAllCookiesForURLAsync(url, base::Bind(&GetCookieListCallback::Run,
+                                                 base::Unretained(&callback)));
     RunFor(kTimeout);
     EXPECT_TRUE(callback.did_run());
     return callback.cookies();
@@ -140,8 +136,8 @@
     DCHECK(cm);
     GetCookieListCallback callback;
     cm->GetAllCookiesForURLWithOptionsAsync(
-        url, options, base::Bind(&GetCookieListCallback::Run,
-                                 base::Unretained(&callback)));
+        url, options,
+        base::Bind(&GetCookieListCallback::Run, base::Unretained(&callback)));
     RunFor(kTimeout);
     EXPECT_TRUE(callback.did_run());
     return callback.cookies();
@@ -161,37 +157,32 @@
     ResultSavingCookieCallback<bool> callback;
     cm->SetCookieWithDetailsAsync(
         url, name, value, domain, path, expiration_time, secure, http_only,
-        priority,
-        base::Bind(
-            &ResultSavingCookieCallback<bool>::Run,
-            base::Unretained(&callback)));
+        priority, base::Bind(&ResultSavingCookieCallback<bool>::Run,
+                             base::Unretained(&callback)));
     RunFor(kTimeout);
     EXPECT_TRUE(callback.did_run());
     return callback.result();
   }
 
-  int DeleteAll(CookieMonster*cm) {
+  int DeleteAll(CookieMonster* cm) {
     DCHECK(cm);
     ResultSavingCookieCallback<int> callback;
-    cm->DeleteAllAsync(
-        base::Bind(
-            &ResultSavingCookieCallback<int>::Run,
-            base::Unretained(&callback)));
+    cm->DeleteAllAsync(base::Bind(&ResultSavingCookieCallback<int>::Run,
+                                  base::Unretained(&callback)));
     RunFor(kTimeout);
     EXPECT_TRUE(callback.did_run());
     return callback.result();
   }
 
-  int DeleteAllCreatedBetween(CookieMonster*cm,
+  int DeleteAllCreatedBetween(CookieMonster* cm,
                               const base::Time& delete_begin,
                               const base::Time& delete_end) {
     DCHECK(cm);
     ResultSavingCookieCallback<int> callback;
     cm->DeleteAllCreatedBetweenAsync(
         delete_begin, delete_end,
-        base::Bind(
-            &ResultSavingCookieCallback<int>::Run,
-            base::Unretained(&callback)));
+        base::Bind(&ResultSavingCookieCallback<int>::Run,
+                   base::Unretained(&callback)));
     RunFor(kTimeout);
     EXPECT_TRUE(callback.did_run());
     return callback.result();
@@ -205,21 +196,19 @@
     ResultSavingCookieCallback<int> callback;
     cm->DeleteAllCreatedBetweenForHostAsync(
         delete_begin, delete_end, url,
-        base::Bind(
-            &ResultSavingCookieCallback<int>::Run,
-            base::Unretained(&callback)));
+        base::Bind(&ResultSavingCookieCallback<int>::Run,
+                   base::Unretained(&callback)));
     RunFor(kTimeout);
     EXPECT_TRUE(callback.did_run());
     return callback.result();
   }
 
-  int DeleteAllForHost(CookieMonster* cm,
-                       const GURL& url) {
+  int DeleteAllForHost(CookieMonster* cm, const GURL& url) {
     DCHECK(cm);
     ResultSavingCookieCallback<int> callback;
-    cm->DeleteAllForHostAsync(
-        url, base::Bind(&ResultSavingCookieCallback<int>::Run,
-                        base::Unretained(&callback)));
+    cm->DeleteAllForHostAsync(url,
+                              base::Bind(&ResultSavingCookieCallback<int>::Run,
+                                         base::Unretained(&callback)));
     RunFor(kTimeout);
     EXPECT_TRUE(callback.did_run());
     return callback.result();
@@ -229,9 +218,8 @@
     DCHECK(cm);
     ResultSavingCookieCallback<bool> callback;
     cm->DeleteCanonicalCookieAsync(
-        cookie,
-        base::Bind(&ResultSavingCookieCallback<bool>::Run,
-                   base::Unretained(&callback)));
+        cookie, base::Bind(&ResultSavingCookieCallback<bool>::Run,
+                           base::Unretained(&callback)));
     RunFor(kTimeout);
     EXPECT_TRUE(callback.did_run());
     return callback.result();
@@ -257,146 +245,64 @@
     //    * Two host path cookies (w.c.b.a/dir1, w.c.b.a/dir1/dir2)
 
     // Domain cookies
-    EXPECT_TRUE(this->SetCookieWithDetails(cm.get(),
-                                           url_top_level_domain_plus_1,
-                                           "dom_1",
-                                           "X",
-                                           ".harvard.edu",
-                                           "/",
-                                           base::Time(),
-                                           false,
-                                           false,
-                                           COOKIE_PRIORITY_DEFAULT));
-    EXPECT_TRUE(this->SetCookieWithDetails(cm.get(),
-                                           url_top_level_domain_plus_2,
-                                           "dom_2",
-                                           "X",
-                                           ".math.harvard.edu",
-                                           "/",
-                                           base::Time(),
-                                           false,
-                                           false,
-                                           COOKIE_PRIORITY_DEFAULT));
-    EXPECT_TRUE(this->SetCookieWithDetails(cm.get(),
-                                           url_top_level_domain_plus_3,
-                                           "dom_3",
-                                           "X",
-                                           ".bourbaki.math.harvard.edu",
-                                           "/",
-                                           base::Time(),
-                                           false,
-                                           false,
-                                           COOKIE_PRIORITY_DEFAULT));
+    EXPECT_TRUE(this->SetCookieWithDetails(
+        cm.get(), url_top_level_domain_plus_1, "dom_1", "X", ".harvard.edu",
+        "/", base::Time(), false, false, COOKIE_PRIORITY_DEFAULT));
+    EXPECT_TRUE(this->SetCookieWithDetails(
+        cm.get(), url_top_level_domain_plus_2, "dom_2", "X",
+        ".math.harvard.edu", "/", base::Time(), false, false,
+        COOKIE_PRIORITY_DEFAULT));
+    EXPECT_TRUE(this->SetCookieWithDetails(
+        cm.get(), url_top_level_domain_plus_3, "dom_3", "X",
+        ".bourbaki.math.harvard.edu", "/", base::Time(), false, false,
+        COOKIE_PRIORITY_DEFAULT));
 
     // Host cookies
-    EXPECT_TRUE(this->SetCookieWithDetails(cm.get(),
-                                           url_top_level_domain_plus_1,
-                                           "host_1",
-                                           "X",
-                                           std::string(),
-                                           "/",
-                                           base::Time(),
-                                           false,
-                                           false,
-                                           COOKIE_PRIORITY_DEFAULT));
-    EXPECT_TRUE(this->SetCookieWithDetails(cm.get(),
-                                           url_top_level_domain_plus_2,
-                                           "host_2",
-                                           "X",
-                                           std::string(),
-                                           "/",
-                                           base::Time(),
-                                           false,
-                                           false,
-                                           COOKIE_PRIORITY_DEFAULT));
-    EXPECT_TRUE(this->SetCookieWithDetails(cm.get(),
-                                           url_top_level_domain_plus_3,
-                                           "host_3",
-                                           "X",
-                                           std::string(),
-                                           "/",
-                                           base::Time(),
-                                           false,
-                                           false,
-                                           COOKIE_PRIORITY_DEFAULT));
+    EXPECT_TRUE(this->SetCookieWithDetails(
+        cm.get(), url_top_level_domain_plus_1, "host_1", "X", std::string(),
+        "/", base::Time(), false, false, COOKIE_PRIORITY_DEFAULT));
+    EXPECT_TRUE(this->SetCookieWithDetails(
+        cm.get(), url_top_level_domain_plus_2, "host_2", "X", std::string(),
+        "/", base::Time(), false, false, COOKIE_PRIORITY_DEFAULT));
+    EXPECT_TRUE(this->SetCookieWithDetails(
+        cm.get(), url_top_level_domain_plus_3, "host_3", "X", std::string(),
+        "/", base::Time(), false, false, COOKIE_PRIORITY_DEFAULT));
 
     // Http_only cookie
-    EXPECT_TRUE(this->SetCookieWithDetails(cm.get(),
-                                           url_top_level_domain_plus_2,
-                                           "httpo_check",
-                                           "X",
-                                           std::string(),
-                                           "/",
-                                           base::Time(),
-                                           false,
-                                           true,
-                                           COOKIE_PRIORITY_DEFAULT));
+    EXPECT_TRUE(this->SetCookieWithDetails(
+        cm.get(), url_top_level_domain_plus_2, "httpo_check", "X",
+        std::string(), "/", base::Time(), false, true,
+        COOKIE_PRIORITY_DEFAULT));
 
     // Secure cookies
-    EXPECT_TRUE(this->SetCookieWithDetails(cm.get(),
-                                           url_top_level_domain_plus_2_secure,
-                                           "sec_dom",
-                                           "X",
-                                           ".math.harvard.edu",
-                                           "/",
-                                           base::Time(),
-                                           true,
-                                           false,
-                                           COOKIE_PRIORITY_DEFAULT));
-    EXPECT_TRUE(this->SetCookieWithDetails(cm.get(),
-                                           url_top_level_domain_plus_2_secure,
-                                           "sec_host",
-                                           "X",
-                                           std::string(),
-                                           "/",
-                                           base::Time(),
-                                           true,
-                                           false,
-                                           COOKIE_PRIORITY_DEFAULT));
+    EXPECT_TRUE(this->SetCookieWithDetails(
+        cm.get(), url_top_level_domain_plus_2_secure, "sec_dom", "X",
+        ".math.harvard.edu", "/", base::Time(), true, false,
+        COOKIE_PRIORITY_DEFAULT));
+    EXPECT_TRUE(this->SetCookieWithDetails(
+        cm.get(), url_top_level_domain_plus_2_secure, "sec_host", "X",
+        std::string(), "/", base::Time(), true, false,
+        COOKIE_PRIORITY_DEFAULT));
 
     // Domain path cookies
-    EXPECT_TRUE(this->SetCookieWithDetails(cm.get(),
-                                           url_top_level_domain_plus_2,
-                                           "dom_path_1",
-                                           "X",
-                                           ".math.harvard.edu",
-                                           "/dir1",
-                                           base::Time(),
-                                           false,
-                                           false,
-                                           COOKIE_PRIORITY_DEFAULT));
-    EXPECT_TRUE(this->SetCookieWithDetails(cm.get(),
-                                           url_top_level_domain_plus_2,
-                                           "dom_path_2",
-                                           "X",
-                                           ".math.harvard.edu",
-                                           "/dir1/dir2",
-                                           base::Time(),
-                                           false,
-                                           false,
-                                           COOKIE_PRIORITY_DEFAULT));
+    EXPECT_TRUE(this->SetCookieWithDetails(
+        cm.get(), url_top_level_domain_plus_2, "dom_path_1", "X",
+        ".math.harvard.edu", "/dir1", base::Time(), false, false,
+        COOKIE_PRIORITY_DEFAULT));
+    EXPECT_TRUE(this->SetCookieWithDetails(
+        cm.get(), url_top_level_domain_plus_2, "dom_path_2", "X",
+        ".math.harvard.edu", "/dir1/dir2", base::Time(), false, false,
+        COOKIE_PRIORITY_DEFAULT));
 
     // Host path cookies
-    EXPECT_TRUE(this->SetCookieWithDetails(cm.get(),
-                                           url_top_level_domain_plus_2,
-                                           "host_path_1",
-                                           "X",
-                                           std::string(),
-                                           "/dir1",
-                                           base::Time(),
-                                           false,
-                                           false,
-                                           COOKIE_PRIORITY_DEFAULT));
-    EXPECT_TRUE(this->SetCookieWithDetails(cm.get(),
-                                           url_top_level_domain_plus_2,
-                                           "host_path_2",
-                                           "X",
-                                           std::string(),
-                                           "/dir1/dir2",
-                                           base::Time(),
-                                           false,
-                                           false,
-                                           COOKIE_PRIORITY_DEFAULT));
+    EXPECT_TRUE(this->SetCookieWithDetails(
+        cm.get(), url_top_level_domain_plus_2, "host_path_1", "X",
+        std::string(), "/dir1", base::Time(), false, false,
+        COOKIE_PRIORITY_DEFAULT));
+    EXPECT_TRUE(this->SetCookieWithDetails(
+        cm.get(), url_top_level_domain_plus_2, "host_path_2", "X",
+        std::string(), "/dir1/dir2", base::Time(), false, false,
+        COOKIE_PRIORITY_DEFAULT));
 
     EXPECT_EQ(13U, this->GetAllCookies(cm.get()).size());
   }
@@ -410,8 +316,7 @@
                            const std::string& domain,
                            const std::string& name) {
     CookieList cookies = this->GetAllCookies(cm);
-    for (CookieList::iterator it = cookies.begin();
-         it != cookies.end(); ++it)
+    for (CookieList::iterator it = cookies.begin(); it != cookies.end(); ++it)
       if (it->Domain() == domain && it->Name() == name)
         return this->DeleteCanonicalCookie(cm, *it);
     return false;
@@ -554,8 +459,7 @@
 
     // Validate each priority.
     size_t expected_count[3] = {
-      expected_low_count, expected_medium_count, expected_high_count
-    };
+        expected_low_count, expected_medium_count, expected_high_count};
     for (int i = 0; i < 3; ++i) {
       DCHECK_LE(surviving_id_list[i].size(), id_list[i].size());
       EXPECT_EQ(expected_count[i], surviving_id_list[i].size());
@@ -574,7 +478,7 @@
     // Hard-coding limits in the test, but use DCHECK_EQ to enforce constraint.
     DCHECK_EQ(180U, CookieMonster::kDomainMaxCookies);
     DCHECK_EQ(150U, CookieMonster::kDomainMaxCookies -
-              CookieMonster::kDomainPurgeCookies);
+                        CookieMonster::kDomainPurgeCookies);
     DCHECK_EQ(30U, CookieMonster::kDomainCookiesQuotaLow);
     DCHECK_EQ(50U, CookieMonster::kDomainCookiesQuotaMedium);
     DCHECK_EQ(70U, CookieMonster::kDomainCookiesQuotaHigh);
@@ -610,8 +514,8 @@
     // Round 1 => 10L; round 2 => 10M; round 3 => 11H.
     TestPriorityCookieCase(cm.get(), "21H 60M 40L 60H", 30U, 50U, 70U);
     // Round 1 => 10L; round 2 => 11M, 10L; round 3 => none.
-    TestPriorityCookieCase(
-        cm.get(), "11H 10M 20L 110M 20L 10H", 20U, 109U, 21U);
+    TestPriorityCookieCase(cm.get(), "11H 10M 20L 110M 20L 10H", 20U, 109U,
+                           21U);
     // Round 1 => none; round 2 => none; round 3 => 11L, 10M, 10H.
     TestPriorityCookieCase(cm.get(), "11L 10M 140H 10M 10L", 10U, 10U, 130U);
     // Round 1 => none; round 2 => 1M; round 3 => 10L, 10M, 10H.
@@ -633,7 +537,8 @@
 
 // TODO(erikwright): Replace the other callbacks and synchronous helper methods
 // in this test suite with these Mocks.
-template<typename T, typename C> class MockCookieCallback {
+template <typename T, typename C>
+class MockCookieCallback {
  public:
   C AsCallback() {
     return base::Bind(&T::Invoke, base::Unretained(static_cast<T*>(this)));
@@ -641,42 +546,41 @@
 };
 
 class MockGetCookiesCallback
-  : public MockCookieCallback<MockGetCookiesCallback,
-                              CookieStore::GetCookiesCallback> {
+    : public MockCookieCallback<MockGetCookiesCallback,
+                                CookieStore::GetCookiesCallback> {
  public:
   MOCK_METHOD1(Invoke, void(const std::string& cookies));
 };
 
 class MockSetCookiesCallback
-  : public MockCookieCallback<MockSetCookiesCallback,
-                              CookieStore::SetCookiesCallback> {
+    : public MockCookieCallback<MockSetCookiesCallback,
+                                CookieStore::SetCookiesCallback> {
  public:
   MOCK_METHOD1(Invoke, void(bool success));
 };
 
-class MockClosure
-  : public MockCookieCallback<MockClosure, base::Closure> {
+class MockClosure : public MockCookieCallback<MockClosure, base::Closure> {
  public:
   MOCK_METHOD0(Invoke, void(void));
 };
 
 class MockGetCookieListCallback
-  : public MockCookieCallback<MockGetCookieListCallback,
-                              CookieMonster::GetCookieListCallback> {
+    : public MockCookieCallback<MockGetCookieListCallback,
+                                CookieMonster::GetCookieListCallback> {
  public:
   MOCK_METHOD1(Invoke, void(const CookieList& cookies));
 };
 
 class MockDeleteCallback
-  : public MockCookieCallback<MockDeleteCallback,
-                              CookieMonster::DeleteCallback> {
+    : public MockCookieCallback<MockDeleteCallback,
+                                CookieMonster::DeleteCallback> {
  public:
   MOCK_METHOD1(Invoke, void(int num_deleted));
 };
 
 class MockDeleteCookieCallback
-  : public MockCookieCallback<MockDeleteCookieCallback,
-                              CookieMonster::DeleteCookieCallback> {
+    : public MockCookieCallback<MockDeleteCookieCallback,
+                                CookieMonster::DeleteCookieCallback> {
  public:
   MOCK_METHOD1(Invoke, void(bool success));
 };
@@ -704,23 +608,25 @@
   cookie_monster->DeleteCookieAsync(url, name, callback->AsCallback());
 }
 ACTION_P3(GetCookiesAction, cookie_monster, url, callback) {
-  cookie_monster->GetCookiesWithOptionsAsync(
-      url, CookieOptions(), callback->AsCallback());
+  cookie_monster->GetCookiesWithOptionsAsync(url, CookieOptions(),
+                                             callback->AsCallback());
 }
 ACTION_P4(SetCookieAction, cookie_monster, url, cookie_line, callback) {
-  cookie_monster->SetCookieWithOptionsAsync(
-      url, cookie_line, CookieOptions(), callback->AsCallback());
+  cookie_monster->SetCookieWithOptionsAsync(url, cookie_line, CookieOptions(),
+                                            callback->AsCallback());
 }
 ACTION_P4(DeleteAllCreatedBetweenAction,
-          cookie_monster, delete_begin, delete_end, callback) {
-  cookie_monster->DeleteAllCreatedBetweenAsync(
-      delete_begin, delete_end, callback->AsCallback());
+          cookie_monster,
+          delete_begin,
+          delete_end,
+          callback) {
+  cookie_monster->DeleteAllCreatedBetweenAsync(delete_begin, delete_end,
+                                               callback->AsCallback());
 }
 ACTION_P3(SetCookieWithDetailsAction, cookie_monster, cc, callback) {
   cookie_monster->SetCookieWithDetailsAsync(
       cc.url, cc.name, cc.value, cc.domain, cc.path, cc.expiration_time,
-      cc.secure, cc.http_only, cc.priority,
-      callback->AsCallback());
+      cc.secure, cc.http_only, cc.priority, callback->AsCallback());
 }
 
 ACTION_P2(GetAllCookiesAction, cookie_monster, callback) {
@@ -740,8 +646,8 @@
 }
 
 ACTION_P3(GetAllCookiesForUrlWithOptionsAction, cookie_monster, url, callback) {
-  cookie_monster->GetAllCookiesForURLWithOptionsAsync(
-      url, CookieOptions(), callback->AsCallback());
+  cookie_monster->GetAllCookiesForURLWithOptionsAsync(url, CookieOptions(),
+                                                      callback->AsCallback());
 }
 
 ACTION_P3(GetAllCookiesForUrlAction, cookie_monster, url, callback) {
@@ -834,8 +740,8 @@
     if (quit_queue)
       EXPECT_CALL(*persistent_store_.get(), LoadCookiesForKey(key, testing::_))
           .WillOnce(
-               testing::DoAll(PushCallbackAction(&loaded_for_key_callbacks_),
-                              QuitCurrentMessageLoop()));
+              testing::DoAll(PushCallbackAction(&loaded_for_key_callbacks_),
+                             QuitCurrentMessageLoop()));
     else
       EXPECT_CALL(*persistent_store_.get(), LoadCookiesForKey(key, testing::_))
           .WillOnce(PushCallbackAction(&loaded_for_key_callbacks_));
@@ -859,7 +765,7 @@
   // Stores the callback passed from the CookieMonster to the
   // PersistentCookieStore::LoadCookiesForKey
   std::queue<CookieMonster::PersistentCookieStore::LoadedCallback>
-    loaded_for_key_callbacks_;
+      loaded_for_key_callbacks_;
 
   // Stores the CookieMonster under test.
   scoped_refptr<CookieMonster> cookie_monster_;
@@ -874,15 +780,17 @@
 
   MockGetCookiesCallback get_cookies_callback;
 
-  BeginWithForDomainKey("google.izzle", GetCookiesAction(
-      &cookie_monster(), url_google_, &get_cookies_callback));
+  BeginWithForDomainKey(
+      "google.izzle",
+      GetCookiesAction(&cookie_monster(), url_google_, &get_cookies_callback));
 
   WaitForLoadCall();
 
-  EXPECT_CALL(get_cookies_callback, Invoke("X=1")).WillOnce(
-      GetCookiesAction(&cookie_monster(), url_google_, &get_cookies_callback));
-  EXPECT_CALL(get_cookies_callback, Invoke("X=1")).WillOnce(
-      QuitCurrentMessageLoop());
+  EXPECT_CALL(get_cookies_callback, Invoke("X=1"))
+      .WillOnce(GetCookiesAction(&cookie_monster(), url_google_,
+                                 &get_cookies_callback));
+  EXPECT_CALL(get_cookies_callback, Invoke("X=1"))
+      .WillOnce(QuitCurrentMessageLoop());
 
   CompleteLoadingAndWait();
 }
@@ -890,16 +798,17 @@
 TEST_F(DeferredCookieTaskTest, DeferredSetCookie) {
   MockSetCookiesCallback set_cookies_callback;
 
-  BeginWithForDomainKey("google.izzle", SetCookieAction(
-      &cookie_monster(), url_google_, "A=B", &set_cookies_callback));
+  BeginWithForDomainKey("google.izzle",
+                        SetCookieAction(&cookie_monster(), url_google_, "A=B",
+                                        &set_cookies_callback));
 
   WaitForLoadCall();
 
-  EXPECT_CALL(set_cookies_callback, Invoke(true)).WillOnce(
-      SetCookieAction(
-          &cookie_monster(), url_google_, "X=Y", &set_cookies_callback));
-  EXPECT_CALL(set_cookies_callback, Invoke(true)).WillOnce(
-      QuitCurrentMessageLoop());
+  EXPECT_CALL(set_cookies_callback, Invoke(true))
+      .WillOnce(SetCookieAction(&cookie_monster(), url_google_, "X=Y",
+                                &set_cookies_callback));
+  EXPECT_CALL(set_cookies_callback, Invoke(true))
+      .WillOnce(QuitCurrentMessageLoop());
 
   CompleteLoadingAndWait();
 }
@@ -907,16 +816,17 @@
 TEST_F(DeferredCookieTaskTest, DeferredDeleteCookie) {
   MockClosure delete_cookie_callback;
 
-  BeginWithForDomainKey("google.izzle", DeleteCookieAction(
-      &cookie_monster(), url_google_, "A", &delete_cookie_callback));
+  BeginWithForDomainKey("google.izzle",
+                        DeleteCookieAction(&cookie_monster(), url_google_, "A",
+                                           &delete_cookie_callback));
 
   WaitForLoadCall();
 
-  EXPECT_CALL(delete_cookie_callback, Invoke()).WillOnce(
-      DeleteCookieAction(
-          &cookie_monster(), url_google_, "X", &delete_cookie_callback));
-  EXPECT_CALL(delete_cookie_callback, Invoke()).WillOnce(
-      QuitCurrentMessageLoop());
+  EXPECT_CALL(delete_cookie_callback, Invoke())
+      .WillOnce(DeleteCookieAction(&cookie_monster(), url_google_, "X",
+                                   &delete_cookie_callback));
+  EXPECT_CALL(delete_cookie_callback, Invoke())
+      .WillOnce(QuitCurrentMessageLoop());
 
   CompleteLoadingAndWait();
 }
@@ -924,24 +834,35 @@
 TEST_F(DeferredCookieTaskTest, DeferredSetCookieWithDetails) {
   MockSetCookiesCallback set_cookies_callback;
 
-  CookiesInputInfo cookie_info = {
-    url_google_foo_, "A", "B", std::string(), "/foo",
-    base::Time(), false, false, COOKIE_PRIORITY_DEFAULT
-  };
-  BeginWithForDomainKey("google.izzle", SetCookieWithDetailsAction(
-      &cookie_monster(), cookie_info, &set_cookies_callback));
+  CookiesInputInfo cookie_info = {url_google_foo_,
+                                  "A",
+                                  "B",
+                                  std::string(),
+                                  "/foo",
+                                  base::Time(),
+                                  false,
+                                  false,
+                                  COOKIE_PRIORITY_DEFAULT};
+  BeginWithForDomainKey(
+      "google.izzle", SetCookieWithDetailsAction(&cookie_monster(), cookie_info,
+                                                 &set_cookies_callback));
 
   WaitForLoadCall();
 
-  CookiesInputInfo cookie_info_exp = {
-    url_google_foo_, "A", "B", std::string(), "/foo",
-    base::Time(), false, false, COOKIE_PRIORITY_DEFAULT
-  };
-  EXPECT_CALL(set_cookies_callback, Invoke(true)).WillOnce(
-      SetCookieWithDetailsAction(
-          &cookie_monster(), cookie_info_exp, &set_cookies_callback));
-  EXPECT_CALL(set_cookies_callback, Invoke(true)).WillOnce(
-      QuitCurrentMessageLoop());
+  CookiesInputInfo cookie_info_exp = {url_google_foo_,
+                                      "A",
+                                      "B",
+                                      std::string(),
+                                      "/foo",
+                                      base::Time(),
+                                      false,
+                                      false,
+                                      COOKIE_PRIORITY_DEFAULT};
+  EXPECT_CALL(set_cookies_callback, Invoke(true))
+      .WillOnce(SetCookieWithDetailsAction(&cookie_monster(), cookie_info_exp,
+                                           &set_cookies_callback));
+  EXPECT_CALL(set_cookies_callback, Invoke(true))
+      .WillOnce(QuitCurrentMessageLoop());
 
   CompleteLoadingAndWait();
 }
@@ -953,15 +874,15 @@
 
   MockGetCookieListCallback get_cookie_list_callback;
 
-  BeginWith(GetAllCookiesAction(
-      &cookie_monster(), &get_cookie_list_callback));
+  BeginWith(GetAllCookiesAction(&cookie_monster(), &get_cookie_list_callback));
 
   WaitForLoadCall();
 
-  EXPECT_CALL(get_cookie_list_callback, Invoke(testing::_)).WillOnce(
-      GetAllCookiesAction(&cookie_monster(), &get_cookie_list_callback));
-  EXPECT_CALL(get_cookie_list_callback, Invoke(testing::_)).WillOnce(
-      QuitCurrentMessageLoop());
+  EXPECT_CALL(get_cookie_list_callback, Invoke(testing::_))
+      .WillOnce(
+          GetAllCookiesAction(&cookie_monster(), &get_cookie_list_callback));
+  EXPECT_CALL(get_cookie_list_callback, Invoke(testing::_))
+      .WillOnce(QuitCurrentMessageLoop());
 
   CompleteLoadingAndWait();
 }
@@ -973,16 +894,17 @@
 
   MockGetCookieListCallback get_cookie_list_callback;
 
-  BeginWithForDomainKey("google.izzle", GetAllCookiesForUrlAction(
-      &cookie_monster(), url_google_, &get_cookie_list_callback));
+  BeginWithForDomainKey(
+      "google.izzle", GetAllCookiesForUrlAction(&cookie_monster(), url_google_,
+                                                &get_cookie_list_callback));
 
   WaitForLoadCall();
 
-  EXPECT_CALL(get_cookie_list_callback, Invoke(testing::_)).WillOnce(
-      GetAllCookiesForUrlAction(
-          &cookie_monster(), url_google_, &get_cookie_list_callback));
-  EXPECT_CALL(get_cookie_list_callback, Invoke(testing::_)).WillOnce(
-      QuitCurrentMessageLoop());
+  EXPECT_CALL(get_cookie_list_callback, Invoke(testing::_))
+      .WillOnce(GetAllCookiesForUrlAction(&cookie_monster(), url_google_,
+                                          &get_cookie_list_callback));
+  EXPECT_CALL(get_cookie_list_callback, Invoke(testing::_))
+      .WillOnce(QuitCurrentMessageLoop());
 
   CompleteLoadingAndWait();
 }
@@ -995,15 +917,16 @@
   MockGetCookieListCallback get_cookie_list_callback;
 
   BeginWithForDomainKey("google.izzle", GetAllCookiesForUrlWithOptionsAction(
-      &cookie_monster(), url_google_, &get_cookie_list_callback));
+                                            &cookie_monster(), url_google_,
+                                            &get_cookie_list_callback));
 
   WaitForLoadCall();
 
-  EXPECT_CALL(get_cookie_list_callback, Invoke(testing::_)).WillOnce(
-      GetAllCookiesForUrlWithOptionsAction(
+  EXPECT_CALL(get_cookie_list_callback, Invoke(testing::_))
+      .WillOnce(GetAllCookiesForUrlWithOptionsAction(
           &cookie_monster(), url_google_, &get_cookie_list_callback));
-  EXPECT_CALL(get_cookie_list_callback, Invoke(testing::_)).WillOnce(
-      QuitCurrentMessageLoop());
+  EXPECT_CALL(get_cookie_list_callback, Invoke(testing::_))
+      .WillOnce(QuitCurrentMessageLoop());
 
   CompleteLoadingAndWait();
 }
@@ -1011,15 +934,14 @@
 TEST_F(DeferredCookieTaskTest, DeferredDeleteAllCookies) {
   MockDeleteCallback delete_callback;
 
-  BeginWith(DeleteAllAction(
-      &cookie_monster(), &delete_callback));
+  BeginWith(DeleteAllAction(&cookie_monster(), &delete_callback));
 
   WaitForLoadCall();
 
-  EXPECT_CALL(delete_callback, Invoke(false)).WillOnce(
-      DeleteAllAction(&cookie_monster(), &delete_callback));
-  EXPECT_CALL(delete_callback, Invoke(false)).WillOnce(
-      QuitCurrentMessageLoop());
+  EXPECT_CALL(delete_callback, Invoke(false))
+      .WillOnce(DeleteAllAction(&cookie_monster(), &delete_callback));
+  EXPECT_CALL(delete_callback, Invoke(false))
+      .WillOnce(QuitCurrentMessageLoop());
 
   CompleteLoadingAndWait();
 }
@@ -1027,17 +949,17 @@
 TEST_F(DeferredCookieTaskTest, DeferredDeleteAllCreatedBetweenCookies) {
   MockDeleteCallback delete_callback;
 
-  BeginWith(DeleteAllCreatedBetweenAction(
-      &cookie_monster(), base::Time(), base::Time::Now(), &delete_callback));
+  BeginWith(DeleteAllCreatedBetweenAction(&cookie_monster(), base::Time(),
+                                          base::Time::Now(), &delete_callback));
 
   WaitForLoadCall();
 
-  EXPECT_CALL(delete_callback, Invoke(false)).WillOnce(
-      DeleteAllCreatedBetweenAction(
-          &cookie_monster(), base::Time(), base::Time::Now(),
-          &delete_callback));
-  EXPECT_CALL(delete_callback, Invoke(false)).WillOnce(
-      QuitCurrentMessageLoop());
+  EXPECT_CALL(delete_callback, Invoke(false))
+      .WillOnce(DeleteAllCreatedBetweenAction(&cookie_monster(), base::Time(),
+                                              base::Time::Now(),
+                                              &delete_callback));
+  EXPECT_CALL(delete_callback, Invoke(false))
+      .WillOnce(QuitCurrentMessageLoop());
 
   CompleteLoadingAndWait();
 }
@@ -1045,37 +967,38 @@
 TEST_F(DeferredCookieTaskTest, DeferredDeleteAllForHostCookies) {
   MockDeleteCallback delete_callback;
 
-  BeginWithForDomainKey("google.izzle", DeleteAllForHostAction(
-      &cookie_monster(), url_google_, &delete_callback));
+  BeginWithForDomainKey(
+      "google.izzle",
+      DeleteAllForHostAction(&cookie_monster(), url_google_, &delete_callback));
 
   WaitForLoadCall();
 
-  EXPECT_CALL(delete_callback, Invoke(false)).WillOnce(
-      DeleteAllForHostAction(
-          &cookie_monster(), url_google_, &delete_callback));
-  EXPECT_CALL(delete_callback, Invoke(false)).WillOnce(
-      QuitCurrentMessageLoop());
+  EXPECT_CALL(delete_callback, Invoke(false))
+      .WillOnce(DeleteAllForHostAction(&cookie_monster(), url_google_,
+                                       &delete_callback));
+  EXPECT_CALL(delete_callback, Invoke(false))
+      .WillOnce(QuitCurrentMessageLoop());
 
   CompleteLoadingAndWait();
 }
 
 TEST_F(DeferredCookieTaskTest, DeferredDeleteCanonicalCookie) {
   std::vector<CanonicalCookie*> cookies;
-  CanonicalCookie cookie = BuildCanonicalCookie(
-      "www.google.com", "X=1; path=/", base::Time::Now());
+  CanonicalCookie cookie =
+      BuildCanonicalCookie("www.google.com", "X=1; path=/", base::Time::Now());
 
   MockDeleteCookieCallback delete_cookie_callback;
 
-  BeginWith(DeleteCanonicalCookieAction(
-      &cookie_monster(), cookie, &delete_cookie_callback));
+  BeginWith(DeleteCanonicalCookieAction(&cookie_monster(), cookie,
+                                        &delete_cookie_callback));
 
   WaitForLoadCall();
 
-  EXPECT_CALL(delete_cookie_callback, Invoke(false)).WillOnce(
-      DeleteCanonicalCookieAction(
-      &cookie_monster(), cookie, &delete_cookie_callback));
-  EXPECT_CALL(delete_cookie_callback, Invoke(false)).WillOnce(
-      QuitCurrentMessageLoop());
+  EXPECT_CALL(delete_cookie_callback, Invoke(false))
+      .WillOnce(DeleteCanonicalCookieAction(&cookie_monster(), cookie,
+                                            &delete_cookie_callback));
+  EXPECT_CALL(delete_cookie_callback, Invoke(false))
+      .WillOnce(QuitCurrentMessageLoop());
 
   CompleteLoadingAndWait();
 }
@@ -1083,15 +1006,15 @@
 TEST_F(DeferredCookieTaskTest, DeferredDeleteSessionCookies) {
   MockDeleteCallback delete_callback;
 
-  BeginWith(DeleteSessionCookiesAction(
-      &cookie_monster(), &delete_callback));
+  BeginWith(DeleteSessionCookiesAction(&cookie_monster(), &delete_callback));
 
   WaitForLoadCall();
 
-  EXPECT_CALL(delete_callback, Invoke(false)).WillOnce(
-      DeleteSessionCookiesAction(&cookie_monster(), &delete_callback));
-  EXPECT_CALL(delete_callback, Invoke(false)).WillOnce(
-      QuitCurrentMessageLoop());
+  EXPECT_CALL(delete_callback, Invoke(false))
+      .WillOnce(
+          DeleteSessionCookiesAction(&cookie_monster(), &delete_callback));
+  EXPECT_CALL(delete_callback, Invoke(false))
+      .WillOnce(QuitCurrentMessageLoop());
 
   CompleteLoadingAndWait();
 }
@@ -1108,29 +1031,28 @@
   MockSetCookiesCallback set_cookies_callback;
   MockGetCookiesCallback get_cookies_callback_deferred;
 
-  EXPECT_CALL(*this, Begin()).WillOnce(testing::DoAll(
-      GetCookiesAction(
-          &cookie_monster(), url_google_, &get_cookies_callback),
-      SetCookieAction(
-          &cookie_monster(), url_google_, "A=B", &set_cookies_callback)));
+  EXPECT_CALL(*this, Begin())
+      .WillOnce(testing::DoAll(GetCookiesAction(&cookie_monster(), url_google_,
+                                                &get_cookies_callback),
+                               SetCookieAction(&cookie_monster(), url_google_,
+                                               "A=B", &set_cookies_callback)));
   ExpectLoadCall();
   ExpectLoadForKeyCall("google.izzle", false);
   Begin();
 
   WaitForLoadCall();
-  EXPECT_CALL(get_cookies_callback, Invoke("X=1")).WillOnce(
-      GetCookiesAction(
-          &cookie_monster(), url_google_, &get_cookies_callback_deferred));
+  EXPECT_CALL(get_cookies_callback, Invoke("X=1"))
+      .WillOnce(GetCookiesAction(&cookie_monster(), url_google_,
+                                 &get_cookies_callback_deferred));
   EXPECT_CALL(set_cookies_callback, Invoke(true));
-  EXPECT_CALL(get_cookies_callback_deferred, Invoke("A=B; X=1")).WillOnce(
-      QuitCurrentMessageLoop());
+  EXPECT_CALL(get_cookies_callback_deferred, Invoke("A=B; X=1"))
+      .WillOnce(QuitCurrentMessageLoop());
 
   CompleteLoadingAndWait();
 }
 
 TEST_F(CookieMonsterTest, TestCookieDeleteAll) {
-  scoped_refptr<MockPersistentCookieStore> store(
-      new MockPersistentCookieStore);
+  scoped_refptr<MockPersistentCookieStore> store(new MockPersistentCookieStore);
   scoped_refptr<CookieMonster> cm(new CookieMonster(store.get(), NULL));
   CookieOptions options;
   options.set_include_httponly();
@@ -1148,8 +1070,7 @@
 
   // Create a persistent cookie.
   EXPECT_TRUE(SetCookie(
-      cm.get(),
-      url_google_,
+      cm.get(), url_google_,
       std::string(kValidCookieLine) + "; expires=Mon, 18-Apr-22 22:50:13 GMT"));
   ASSERT_EQ(1u, store->commands().size());
   EXPECT_EQ(CookieStoreCommand::ADD, store->commands()[0].type);
@@ -1166,9 +1087,8 @@
   Time now = Time::Now();
 
   // Nothing has been added so nothing should be deleted.
-  EXPECT_EQ(
-      0,
-      DeleteAllCreatedBetween(cm.get(), now - TimeDelta::FromDays(99), Time()));
+  EXPECT_EQ(0, DeleteAllCreatedBetween(cm.get(), now - TimeDelta::FromDays(99),
+                                       Time()));
 
   // Create 3 cookies with creation date of today, yesterday and the day before.
   EXPECT_TRUE(cm->SetCookieWithCreationTime(url_google_, "T-0=Now", now));
@@ -1182,10 +1102,8 @@
                                             now - TimeDelta::FromDays(7)));
 
   // Try to delete threedays and the daybefore.
-  EXPECT_EQ(2,
-            DeleteAllCreatedBetween(cm.get(),
-                                    now - TimeDelta::FromDays(3),
-                                    now - TimeDelta::FromDays(1)));
+  EXPECT_EQ(2, DeleteAllCreatedBetween(cm.get(), now - TimeDelta::FromDays(3),
+                                       now - TimeDelta::FromDays(1)));
 
   // Try to delete yesterday, also make sure that delete_end is not
   // inclusive.
@@ -1274,10 +1192,9 @@
 
   EXPECT_TRUE(
       SetCookieWithOptions(cm.get(), url_google_, "A=B; httponly", options));
-  EXPECT_TRUE(SetCookieWithOptions(
-      cm.get(), url_google_, "C=D; domain=.google.izzle", options));
-  EXPECT_TRUE(SetCookieWithOptions(cm.get(),
-                                   url_google_secure_,
+  EXPECT_TRUE(SetCookieWithOptions(cm.get(), url_google_,
+                                   "C=D; domain=.google.izzle", options));
+  EXPECT_TRUE(SetCookieWithOptions(cm.get(), url_google_secure_,
                                    "E=F; domain=.google.izzle; secure",
                                    options));
 
@@ -1337,10 +1254,10 @@
   scoped_refptr<CookieMonster> cm(new CookieMonster(NULL, NULL));
   CookieOptions options;
 
-  EXPECT_TRUE(SetCookieWithOptions(
-      cm.get(), url_google_foo_, "A=B; path=/foo;", options));
-  EXPECT_TRUE(SetCookieWithOptions(
-      cm.get(), url_google_bar_, "C=D; path=/bar;", options));
+  EXPECT_TRUE(SetCookieWithOptions(cm.get(), url_google_foo_, "A=B; path=/foo;",
+                                   options));
+  EXPECT_TRUE(SetCookieWithOptions(cm.get(), url_google_bar_, "C=D; path=/bar;",
+                                   options));
   EXPECT_TRUE(SetCookieWithOptions(cm.get(), url_google_, "E=F;", options));
 
   CookieList cookies = GetAllCookiesForURL(cm.get(), url_google_foo_);
@@ -1385,8 +1302,7 @@
   CookieList cookies = GetAllCookies(cm.get());
   size_t expected_size = 4;
   EXPECT_EQ(expected_size, cookies.size());
-  for (CookieList::iterator it = cookies.begin();
-       it != cookies.end(); ++it) {
+  for (CookieList::iterator it = cookies.begin(); it != cookies.end(); ++it) {
     EXPECT_NE("A1", it->Value());
     EXPECT_NE("A2", it->Value());
   }
@@ -1397,14 +1313,10 @@
   CookieOptions options;
 
   EXPECT_TRUE(SetCookieWithOptions(cm_1.get(), url_google_foo_,
-                                               "A1=B; path=/foo;",
-                                               options));
+                                   "A1=B; path=/foo;", options));
   EXPECT_TRUE(SetCookieWithOptions(cm_1.get(), url_google_bar_,
-                                               "A2=D; path=/bar;",
-                                               options));
-  EXPECT_TRUE(SetCookieWithOptions(cm_1.get(), url_google_,
-                                               "A3=F;",
-                                               options));
+                                   "A2=D; path=/bar;", options));
+  EXPECT_TRUE(SetCookieWithOptions(cm_1.get(), url_google_, "A3=F;", options));
 
   CookieList cookies_1 = GetAllCookies(cm_1.get());
   scoped_refptr<CookieMonster> cm_2(new CookieMonster(NULL, NULL));
@@ -1435,8 +1347,7 @@
 //
 // This is a regression test for: http://crbug.com/17855.
 TEST_F(CookieMonsterTest, DontImportDuplicateCookies) {
-  scoped_refptr<MockPersistentCookieStore> store(
-      new MockPersistentCookieStore);
+  scoped_refptr<MockPersistentCookieStore> store(new MockPersistentCookieStore);
 
   // We will fill some initial cookies into the PersistentCookieStore,
   // to simulate a database with 4 duplicates.  Note that we need to
@@ -1451,24 +1362,20 @@
 
   AddCookieToList("www.google.com",
                   "X=1; path=/; expires=Mon, 18-Apr-22 22:50:14 GMT",
-                  Time::Now() + TimeDelta::FromDays(3),
-                  &initial_cookies);
+                  Time::Now() + TimeDelta::FromDays(3), &initial_cookies);
 
   AddCookieToList("www.google.com",
                   "X=2; path=/; expires=Mon, 18-Apr-22 22:50:14 GMT",
-                  Time::Now() + TimeDelta::FromDays(1),
-                  &initial_cookies);
+                  Time::Now() + TimeDelta::FromDays(1), &initial_cookies);
 
   // ===> This one is the WINNER (biggest creation time).  <====
   AddCookieToList("www.google.com",
                   "X=3; path=/; expires=Mon, 18-Apr-22 22:50:14 GMT",
-                  Time::Now() + TimeDelta::FromDays(4),
-                  &initial_cookies);
+                  Time::Now() + TimeDelta::FromDays(4), &initial_cookies);
 
   AddCookieToList("www.google.com",
                   "X=4; path=/; expires=Mon, 18-Apr-22 22:50:14 GMT",
-                  Time::Now(),
-                  &initial_cookies);
+                  Time::Now(), &initial_cookies);
 
   // Insert 2 cookies with name "X" on path "/2", with varying creation
   // dates. We expect only the most recent one to be preserved the import.
@@ -1476,19 +1383,16 @@
   // ===> This one is the WINNER (biggest creation time).  <====
   AddCookieToList("www.google.com",
                   "X=a1; path=/2; expires=Mon, 18-Apr-22 22:50:14 GMT",
-                  Time::Now() + TimeDelta::FromDays(9),
-                  &initial_cookies);
+                  Time::Now() + TimeDelta::FromDays(9), &initial_cookies);
 
   AddCookieToList("www.google.com",
                   "X=a2; path=/2; expires=Mon, 18-Apr-22 22:50:14 GMT",
-                  Time::Now() + TimeDelta::FromDays(2),
-                  &initial_cookies);
+                  Time::Now() + TimeDelta::FromDays(2), &initial_cookies);
 
   // Insert 1 cookie with name "Y" on path "/".
   AddCookieToList("www.google.com",
                   "Y=a; path=/; expires=Mon, 18-Apr-22 22:50:14 GMT",
-                  Time::Now() + TimeDelta::FromDays(10),
-                  &initial_cookies);
+                  Time::Now() + TimeDelta::FromDays(10), &initial_cookies);
 
   // Inject our initial cookies into the mock PersistentCookieStore.
   store->SetLoadExpectation(true, initial_cookies);
@@ -1518,8 +1422,7 @@
 //
 // This is a regression test for: http://crbug.com/43188.
 TEST_F(CookieMonsterTest, DontImportDuplicateCreationTimes) {
-  scoped_refptr<MockPersistentCookieStore> store(
-      new MockPersistentCookieStore);
+  scoped_refptr<MockPersistentCookieStore> store(new MockPersistentCookieStore);
 
   Time now(Time::Now());
   Time earlier(now - TimeDelta::FromDays(1));
@@ -1555,8 +1458,7 @@
 }
 
 TEST_F(CookieMonsterTest, CookieMonsterDelegate) {
-  scoped_refptr<MockPersistentCookieStore> store(
-      new MockPersistentCookieStore);
+  scoped_refptr<MockPersistentCookieStore> store(new MockPersistentCookieStore);
   scoped_refptr<MockCookieMonsterDelegate> delegate(
       new MockCookieMonsterDelegate);
   scoped_refptr<CookieMonster> cm(
@@ -1595,8 +1497,7 @@
   EXPECT_EQ(0u, delegate->changes().size());
 
   // Insert a cookie "a" for path "/path1"
-  EXPECT_TRUE(SetCookie(cm.get(),
-                        url_google_,
+  EXPECT_TRUE(SetCookie(cm.get(), url_google_,
                         "a=val1; path=/path1; "
                         "expires=Mon, 18-Apr-22 22:50:13 GMT"));
   ASSERT_EQ(1u, store->commands().size());
@@ -1612,8 +1513,7 @@
   // overwrite the non-http-only version.
   CookieOptions allow_httponly;
   allow_httponly.set_include_httponly();
-  EXPECT_TRUE(SetCookieWithOptions(cm.get(),
-                                   url_google_,
+  EXPECT_TRUE(SetCookieWithOptions(cm.get(), url_google_,
                                    "a=val2; path=/path1; httponly; "
                                    "expires=Mon, 18-Apr-22 22:50:14 GMT",
                                    allow_httponly));
@@ -1635,88 +1535,32 @@
 TEST_F(CookieMonsterTest, SetCookieWithDetails) {
   scoped_refptr<CookieMonster> cm(new CookieMonster(NULL, NULL));
 
-  EXPECT_TRUE(SetCookieWithDetails(cm.get(),
-                                   url_google_foo_,
-                                   "A",
-                                   "B",
-                                   std::string(),
-                                   "/foo",
-                                   base::Time(),
-                                   false,
-                                   false,
-                                   COOKIE_PRIORITY_DEFAULT));
-  EXPECT_TRUE(SetCookieWithDetails(cm.get(),
-                                   url_google_bar_,
-                                   "C",
-                                   "D",
-                                   "google.izzle",
-                                   "/bar",
-                                   base::Time(),
-                                   false,
-                                   true,
-                                   COOKIE_PRIORITY_DEFAULT));
-  EXPECT_TRUE(SetCookieWithDetails(cm.get(),
-                                   url_google_,
-                                   "E",
-                                   "F",
-                                   std::string(),
-                                   std::string(),
-                                   base::Time(),
-                                   true,
-                                   false,
-                                   COOKIE_PRIORITY_DEFAULT));
+  EXPECT_TRUE(SetCookieWithDetails(cm.get(), url_google_foo_, "A", "B",
+                                   std::string(), "/foo", base::Time(), false,
+                                   false, COOKIE_PRIORITY_DEFAULT));
+  EXPECT_TRUE(SetCookieWithDetails(cm.get(), url_google_bar_, "C", "D",
+                                   "google.izzle", "/bar", base::Time(), false,
+                                   true, COOKIE_PRIORITY_DEFAULT));
+  EXPECT_TRUE(SetCookieWithDetails(cm.get(), url_google_, "E", "F",
+                                   std::string(), std::string(), base::Time(),
+                                   true, false, COOKIE_PRIORITY_DEFAULT));
 
   // Test that malformed attributes fail to set the cookie.
-  EXPECT_FALSE(SetCookieWithDetails(cm.get(),
-                                    url_google_foo_,
-                                    " A",
-                                    "B",
-                                    std::string(),
-                                    "/foo",
-                                    base::Time(),
-                                    false,
-                                    false,
-                                    COOKIE_PRIORITY_DEFAULT));
-  EXPECT_FALSE(SetCookieWithDetails(cm.get(),
-                                    url_google_foo_,
-                                    "A;",
-                                    "B",
-                                    std::string(),
-                                    "/foo",
-                                    base::Time(),
-                                    false,
-                                    false,
-                                    COOKIE_PRIORITY_DEFAULT));
-  EXPECT_FALSE(SetCookieWithDetails(cm.get(),
-                                    url_google_foo_,
-                                    "A=",
-                                    "B",
-                                    std::string(),
-                                    "/foo",
-                                    base::Time(),
-                                    false,
-                                    false,
-                                    COOKIE_PRIORITY_DEFAULT));
-  EXPECT_FALSE(SetCookieWithDetails(cm.get(),
-                                    url_google_foo_,
-                                    "A",
-                                    "B",
-                                    "google.ozzzzzzle",
-                                    "foo",
-                                    base::Time(),
-                                    false,
-                                    false,
-                                    COOKIE_PRIORITY_DEFAULT));
-  EXPECT_FALSE(SetCookieWithDetails(cm.get(),
-                                    url_google_foo_,
-                                    "A=",
-                                    "B",
-                                    std::string(),
-                                    "foo",
-                                    base::Time(),
-                                    false,
-                                    false,
-                                    COOKIE_PRIORITY_DEFAULT));
+  EXPECT_FALSE(SetCookieWithDetails(cm.get(), url_google_foo_, " A", "B",
+                                    std::string(), "/foo", base::Time(), false,
+                                    false, COOKIE_PRIORITY_DEFAULT));
+  EXPECT_FALSE(SetCookieWithDetails(cm.get(), url_google_foo_, "A;", "B",
+                                    std::string(), "/foo", base::Time(), false,
+                                    false, COOKIE_PRIORITY_DEFAULT));
+  EXPECT_FALSE(SetCookieWithDetails(cm.get(), url_google_foo_, "A=", "B",
+                                    std::string(), "/foo", base::Time(), false,
+                                    false, COOKIE_PRIORITY_DEFAULT));
+  EXPECT_FALSE(SetCookieWithDetails(cm.get(), url_google_foo_, "A", "B",
+                                    "google.ozzzzzzle", "foo", base::Time(),
+                                    false, false, COOKIE_PRIORITY_DEFAULT));
+  EXPECT_FALSE(SetCookieWithDetails(cm.get(), url_google_foo_, "A=", "B",
+                                    std::string(), "foo", base::Time(), false,
+                                    false, COOKIE_PRIORITY_DEFAULT));
 
   CookieList cookies = GetAllCookiesForURL(cm.get(), url_google_foo_);
   CookieList::iterator it = cookies.begin();
@@ -1777,11 +1621,11 @@
             GetCookies(cm.get(), GURL(kTopLevelDomainPlus2Secure)));
   EXPECT_EQ("dom_1=X; host_1=X",
             GetCookies(cm.get(), GURL(kTopLevelDomainPlus1)));
-  EXPECT_EQ("dom_path_2=X; host_path_2=X; dom_path_1=X; host_path_1=X; "
-            "dom_1=X; dom_2=X; host_2=X; sec_dom=X; sec_host=X",
-            GetCookies(cm.get(),
-                       GURL(kTopLevelDomainPlus2Secure +
-                            std::string("/dir1/dir2/xxx"))));
+  EXPECT_EQ(
+      "dom_path_2=X; host_path_2=X; dom_path_1=X; host_path_1=X; "
+      "dom_1=X; dom_2=X; host_2=X; sec_dom=X; sec_host=X",
+      GetCookies(cm.get(), GURL(kTopLevelDomainPlus2Secure +
+                                std::string("/dir1/dir2/xxx"))));
 
   EXPECT_EQ(5, DeleteAllForHost(cm.get(), GURL(kTopLevelDomainPlus2)));
   EXPECT_EQ(8U, GetAllCookies(cm.get()).size());
@@ -1793,9 +1637,8 @@
   EXPECT_EQ("dom_1=X; host_1=X",
             GetCookies(cm.get(), GURL(kTopLevelDomainPlus1)));
   EXPECT_EQ("dom_path_2=X; dom_path_1=X; dom_1=X; dom_2=X; sec_dom=X",
-            GetCookies(cm.get(),
-                       GURL(kTopLevelDomainPlus2Secure +
-                            std::string("/dir1/dir2/xxx"))));
+            GetCookies(cm.get(), GURL(kTopLevelDomainPlus2Secure +
+                                      std::string("/dir1/dir2/xxx"))));
 
   PopulateCmForDeleteAllForHost(cm);
   EXPECT_EQ(5, DeleteAllForHost(cm.get(), GURL(kTopLevelDomainPlus2Secure)));
@@ -1808,15 +1651,12 @@
   EXPECT_EQ("dom_1=X; host_1=X",
             GetCookies(cm.get(), GURL(kTopLevelDomainPlus1)));
   EXPECT_EQ("dom_path_2=X; dom_path_1=X; dom_1=X; dom_2=X; sec_dom=X",
-            GetCookies(cm.get(),
-                       GURL(kTopLevelDomainPlus2Secure +
-                            std::string("/dir1/dir2/xxx"))));
+            GetCookies(cm.get(), GURL(kTopLevelDomainPlus2Secure +
+                                      std::string("/dir1/dir2/xxx"))));
 
   PopulateCmForDeleteAllForHost(cm);
-  EXPECT_EQ(5,
-            DeleteAllForHost(
-                cm.get(),
-                GURL(kTopLevelDomainPlus2Secure + std::string("/dir1/xxx"))));
+  EXPECT_EQ(5, DeleteAllForHost(cm.get(), GURL(kTopLevelDomainPlus2Secure +
+                                               std::string("/dir1/xxx"))));
   EXPECT_EQ(8U, GetAllCookies(cm.get()).size());
 
   EXPECT_EQ("dom_1=X; dom_2=X; dom_3=X; host_3=X",
@@ -1826,9 +1666,8 @@
   EXPECT_EQ("dom_1=X; host_1=X",
             GetCookies(cm.get(), GURL(kTopLevelDomainPlus1)));
   EXPECT_EQ("dom_path_2=X; dom_path_1=X; dom_1=X; dom_2=X; sec_dom=X",
-            GetCookies(cm.get(),
-                       GURL(kTopLevelDomainPlus2Secure +
-                            std::string("/dir1/dir2/xxx"))));
+            GetCookies(cm.get(), GURL(kTopLevelDomainPlus2Secure +
+                                      std::string("/dir1/dir2/xxx"))));
 }
 
 TEST_F(CookieMonsterTest, UniqueCreationTime) {
@@ -1849,42 +1688,21 @@
   SetCookie(cm.get(), url_google_, "SetCookie2=A");
   SetCookie(cm.get(), url_google_, "SetCookie3=A");
 
-  SetCookieWithOptions(
-      cm.get(), url_google_, "setCookieWithOptions1=A", options);
-  SetCookieWithOptions(
-      cm.get(), url_google_, "setCookieWithOptions2=A", options);
-  SetCookieWithOptions(
-      cm.get(), url_google_, "setCookieWithOptions3=A", options);
+  SetCookieWithOptions(cm.get(), url_google_, "setCookieWithOptions1=A",
+                       options);
+  SetCookieWithOptions(cm.get(), url_google_, "setCookieWithOptions2=A",
+                       options);
+  SetCookieWithOptions(cm.get(), url_google_, "setCookieWithOptions3=A",
+                       options);
 
-  SetCookieWithDetails(cm.get(),
-                       url_google_,
-                       "setCookieWithDetails1",
-                       "A",
-                       ".google.com",
-                       "/",
-                       Time(),
-                       false,
-                       false,
+  SetCookieWithDetails(cm.get(), url_google_, "setCookieWithDetails1", "A",
+                       ".google.com", "/", Time(), false, false,
                        COOKIE_PRIORITY_DEFAULT);
-  SetCookieWithDetails(cm.get(),
-                       url_google_,
-                       "setCookieWithDetails2",
-                       "A",
-                       ".google.com",
-                       "/",
-                       Time(),
-                       false,
-                       false,
+  SetCookieWithDetails(cm.get(), url_google_, "setCookieWithDetails2", "A",
+                       ".google.com", "/", Time(), false, false,
                        COOKIE_PRIORITY_DEFAULT);
-  SetCookieWithDetails(cm.get(),
-                       url_google_,
-                       "setCookieWithDetails3",
-                       "A",
-                       ".google.com",
-                       "/",
-                       Time(),
-                       false,
-                       false,
+  SetCookieWithDetails(cm.get(), url_google_, "setCookieWithDetails3", "A",
+                       ".google.com", "/", Time(), false, false,
                        COOKIE_PRIORITY_DEFAULT);
 
   // Now we check
@@ -1894,8 +1712,8 @@
   for (CookieList::const_iterator it = cookie_list.begin();
        it != cookie_list.end(); it++) {
     const int64 creation_date = it->CreationDate().ToInternalValue();
-    TimeCookieMap::const_iterator
-        existing_cookie_it(check_map.find(creation_date));
+    TimeCookieMap::const_iterator existing_cookie_it(
+        check_map.find(creation_date));
     EXPECT_TRUE(existing_cookie_it == check_map.end())
         << "Cookie " << it->Name() << " has same creation date ("
         << it->CreationDate().ToInternalValue()
@@ -1903,8 +1721,8 @@
         << existing_cookie_it->second.Name();
 
     if (existing_cookie_it == check_map.end()) {
-      check_map.insert(TimeCookieMap::value_type(
-          it->CreationDate().ToInternalValue(), *it));
+      check_map.insert(
+          TimeCookieMap::value_type(it->CreationDate().ToInternalValue(), *it));
     }
   }
 }
@@ -1944,36 +1762,47 @@
   base::Time expires(base::Time::Now() + base::TimeDelta::FromSeconds(100));
 
   const CookiesInputInfo input_info[] = {
-    {GURL("http://a.b.google.com"), "a", "1", "", "/path/to/cookie", expires,
-     false, false, COOKIE_PRIORITY_DEFAULT},
-    {GURL("https://www.google.com"), "b", "2", ".google.com",
-     "/path/from/cookie", expires + TimeDelta::FromSeconds(10),
-     true, true, COOKIE_PRIORITY_DEFAULT},
-    {GURL("https://google.com"), "c", "3", "", "/another/path/to/cookie",
-     base::Time::Now() + base::TimeDelta::FromSeconds(100),
-     true, false, COOKIE_PRIORITY_DEFAULT}
-  };
+      {GURL("http://a.b.google.com"),
+       "a",
+       "1",
+       "",
+       "/path/to/cookie",
+       expires,
+       false,
+       false,
+       COOKIE_PRIORITY_DEFAULT},
+      {GURL("https://www.google.com"),
+       "b",
+       "2",
+       ".google.com",
+       "/path/from/cookie",
+       expires + TimeDelta::FromSeconds(10),
+       true,
+       true,
+       COOKIE_PRIORITY_DEFAULT},
+      {GURL("https://google.com"),
+       "c",
+       "3",
+       "",
+       "/another/path/to/cookie",
+       base::Time::Now() + base::TimeDelta::FromSeconds(100),
+       true,
+       false,
+       COOKIE_PRIORITY_DEFAULT}};
   const int INPUT_DELETE = 1;
 
   // Create new cookies and flush them to the store.
   {
     scoped_refptr<CookieMonster> cmout(new CookieMonster(store.get(), NULL));
     for (const CookiesInputInfo* p = input_info;
-         p < &input_info[arraysize(input_info)];
-         p++) {
-      EXPECT_TRUE(SetCookieWithDetails(cmout.get(),
-                                       p->url,
-                                       p->name,
-                                       p->value,
-                                       p->domain,
-                                       p->path,
-                                       p->expiration_time,
-                                       p->secure,
-                                       p->http_only,
-                                       p->priority));
+         p < &input_info[arraysize(input_info)]; p++) {
+      EXPECT_TRUE(SetCookieWithDetails(cmout.get(), p->url, p->name, p->value,
+                                       p->domain, p->path, p->expiration_time,
+                                       p->secure, p->http_only, p->priority));
     }
-    GURL del_url(input_info[INPUT_DELETE].url.Resolve(
-                     input_info[INPUT_DELETE].path).spec());
+    GURL del_url(input_info[INPUT_DELETE]
+                     .url.Resolve(input_info[INPUT_DELETE].path)
+                     .spec());
     DeleteCookie(cmout.get(), del_url, input_info[INPUT_DELETE].name);
   }
 
@@ -2011,19 +1840,17 @@
   scoped_refptr<CookieMonster> cm(new CookieMonster(NULL, NULL));
   EXPECT_TRUE(
       SetCookie(cm.get(), GURL("http://d.c.b.a.google.com/aa/x.html"), "c=1"));
-  EXPECT_TRUE(SetCookie(cm.get(),
-                        GURL("http://b.a.google.com/aa/bb/cc/x.html"),
+  EXPECT_TRUE(SetCookie(cm.get(), GURL("http://b.a.google.com/aa/bb/cc/x.html"),
                         "d=1; domain=b.a.google.com"));
-  EXPECT_TRUE(SetCookie(cm.get(),
-                        GURL("http://b.a.google.com/aa/bb/cc/x.html"),
+  EXPECT_TRUE(SetCookie(cm.get(), GURL("http://b.a.google.com/aa/bb/cc/x.html"),
                         "a=4; domain=b.a.google.com"));
   EXPECT_TRUE(SetCookie(cm.get(),
                         GURL("http://c.b.a.google.com/aa/bb/cc/x.html"),
                         "e=1; domain=c.b.a.google.com"));
-  EXPECT_TRUE(SetCookie(
-      cm.get(), GURL("http://d.c.b.a.google.com/aa/bb/x.html"), "b=1"));
-  EXPECT_TRUE(SetCookie(
-      cm.get(), GURL("http://news.bbc.co.uk/midpath/x.html"), "g=10"));
+  EXPECT_TRUE(SetCookie(cm.get(),
+                        GURL("http://d.c.b.a.google.com/aa/bb/x.html"), "b=1"));
+  EXPECT_TRUE(SetCookie(cm.get(), GURL("http://news.bbc.co.uk/midpath/x.html"),
+                        "g=10"));
   {
     unsigned int i = 0;
     CookieList cookies(GetAllCookiesForURL(
@@ -2085,46 +1912,41 @@
     // Indexed by ExpiryAndKeyScheme
     size_t expected_cookies_after_set;
   } test_cases[] = {
-    {
-      // A whole lot of recent cookies; gc shouldn't happen.
-      CookieMonster::kMaxCookies * 2,
-      0,
-      CookieMonster::kMaxCookies * 2,
-      CookieMonster::kMaxCookies * 2 + 1
-    }, {
-      // Some old cookies, but still overflowing max.
-      CookieMonster::kMaxCookies * 2,
-      CookieMonster::kMaxCookies / 2,
-      CookieMonster::kMaxCookies * 2,
-      CookieMonster::kMaxCookies * 2 - CookieMonster::kMaxCookies / 2 + 1
-    }, {
-      // Old cookies enough to bring us right down to our purge line.
-      CookieMonster::kMaxCookies * 2,
-      CookieMonster::kMaxCookies + CookieMonster::kPurgeCookies + 1,
-      CookieMonster::kMaxCookies * 2,
-      CookieMonster::kMaxCookies - CookieMonster::kPurgeCookies
-    }, {
-      // Old cookies enough to bring below our purge line (which we
-      // shouldn't do).
-      CookieMonster::kMaxCookies * 2,
-      CookieMonster::kMaxCookies * 3 / 2,
-      CookieMonster::kMaxCookies * 2,
-      CookieMonster::kMaxCookies - CookieMonster::kPurgeCookies
-    }
-  };
+      {// A whole lot of recent cookies; gc shouldn't happen.
+       CookieMonster::kMaxCookies * 2,
+       0,
+       CookieMonster::kMaxCookies * 2,
+       CookieMonster::kMaxCookies * 2 + 1},
+      {// Some old cookies, but still overflowing max.
+       CookieMonster::kMaxCookies * 2,
+       CookieMonster::kMaxCookies / 2,
+       CookieMonster::kMaxCookies * 2,
+       CookieMonster::kMaxCookies * 2 - CookieMonster::kMaxCookies / 2 + 1},
+      {// Old cookies enough to bring us right down to our purge line.
+       CookieMonster::kMaxCookies * 2,
+       CookieMonster::kMaxCookies + CookieMonster::kPurgeCookies + 1,
+       CookieMonster::kMaxCookies * 2,
+       CookieMonster::kMaxCookies - CookieMonster::kPurgeCookies},
+      {// Old cookies enough to bring below our purge line (which we
+       // shouldn't do).
+       CookieMonster::kMaxCookies * 2,
+       CookieMonster::kMaxCookies * 3 / 2,
+       CookieMonster::kMaxCookies * 2,
+       CookieMonster::kMaxCookies - CookieMonster::kPurgeCookies}};
 
   for (int ci = 0; ci < static_cast<int>(arraysize(test_cases)); ++ci) {
-    const TestCase *test_case = &test_cases[ci];
-    scoped_refptr<CookieMonster> cm(
-        CreateMonsterFromStoreForGC(
-            test_case->num_cookies, test_case->num_old_cookies,
-            CookieMonster::kSafeFromGlobalPurgeDays * 2));
+    const TestCase* test_case = &test_cases[ci];
+    scoped_refptr<CookieMonster> cm(CreateMonsterFromStoreForGC(
+        test_case->num_cookies, test_case->num_old_cookies,
+        CookieMonster::kSafeFromGlobalPurgeDays * 2));
     EXPECT_EQ(test_case->expected_initial_cookies,
-              GetAllCookies(cm.get()).size()) << "For test case " << ci;
+              GetAllCookies(cm.get()).size())
+        << "For test case " << ci;
     // Will trigger GC
     SetCookie(cm.get(), GURL("http://newdomain.com"), "b=2");
     EXPECT_EQ(test_case->expected_cookies_after_set,
-              GetAllCookies(cm.get()).size()) << "For test case " << ci;
+              GetAllCookies(cm.get()).size())
+        << "For test case " << ci;
   }
 }
 
@@ -2136,8 +1958,7 @@
 
   // Set a persistent cookie.
   ASSERT_TRUE(SetCookieWithOptions(
-      cm.get(),
-      url_google_,
+      cm.get(), url_google_,
       std::string(kValidCookieLine) + "; expires=Mon, 18-Apr-22 22:50:13 GMT",
       options));
 
@@ -2147,8 +1968,7 @@
 
   // Use a past expiry date to delete the cookie.
   ASSERT_TRUE(SetCookieWithOptions(
-      cm.get(),
-      url_google_,
+      cm.get(), url_google_,
       std::string(kValidCookieLine) + "; expires=Mon, 18-Apr-1977 22:50:13 GMT",
       options));
 
@@ -2190,9 +2010,7 @@
       callback.Run();
   }
 
-  int flush_count() {
-    return flush_count_;
-  }
+  int flush_count() { return flush_count_; }
 
  private:
   ~FlushablePersistentStore() override {}
@@ -2205,13 +2023,9 @@
  public:
   CallbackCounter() : callback_count_(0) {}
 
-  void Callback() {
-    ++callback_count_;
-  }
+  void Callback() { ++callback_count_; }
 
-  int callback_count() {
-    return callback_count_;
-  }
+  int callback_count() { return callback_count_; }
 
  private:
   friend class base::RefCountedThreadSafe<CallbackCounter>;
@@ -2279,24 +2093,16 @@
   // Should match call in InitializeHistograms, but doesn't really matter
   // since the histogram should have been initialized by the CM construction
   // above.
-  base::HistogramBase* expired_histogram =
-      base::Histogram::FactoryGet(
-          "Cookie.ExpirationDurationMinutes", 1, 10 * 365 * 24 * 60, 50,
-          base::Histogram::kUmaTargetedHistogramFlag);
+  base::HistogramBase* expired_histogram = base::Histogram::FactoryGet(
+      "Cookie.ExpirationDurationMinutes", 1, 10 * 365 * 24 * 60, 50,
+      base::Histogram::kUmaTargetedHistogramFlag);
 
   scoped_ptr<base::HistogramSamples> samples1(
       expired_histogram->SnapshotSamples());
-  ASSERT_TRUE(
-      SetCookieWithDetails(cm.get(),
-                           GURL("http://fake.a.url"),
-                           "a",
-                           "b",
-                           "a.url",
-                           "/",
-                           base::Time::Now() + base::TimeDelta::FromMinutes(59),
-                           false,
-                           false,
-                           COOKIE_PRIORITY_DEFAULT));
+  ASSERT_TRUE(SetCookieWithDetails(
+      cm.get(), GURL("http://fake.a.url"), "a", "b", "a.url", "/",
+      base::Time::Now() + base::TimeDelta::FromMinutes(59), false, false,
+      COOKIE_PRIORITY_DEFAULT));
 
   scoped_ptr<base::HistogramSamples> samples2(
       expired_histogram->SnapshotSamples());
@@ -2319,8 +2125,7 @@
   // Helper methods for calling the asynchronous CookieMonster methods
   // from a different thread.
 
-  void GetAllCookiesTask(CookieMonster* cm,
-                         GetCookieListCallback* callback) {
+  void GetAllCookiesTask(CookieMonster* cm, GetCookieListCallback* callback) {
     cm->GetAllCookiesAsync(
         base::Bind(&GetCookieListCallback::Run, base::Unretained(callback)));
   }
@@ -2328,9 +2133,8 @@
   void GetAllCookiesForURLTask(CookieMonster* cm,
                                const GURL& url,
                                GetCookieListCallback* callback) {
-    cm->GetAllCookiesForURLAsync(
-        url,
-        base::Bind(&GetCookieListCallback::Run, base::Unretained(callback)));
+    cm->GetAllCookiesForURLAsync(url, base::Bind(&GetCookieListCallback::Run,
+                                                 base::Unretained(callback)));
   }
 
   void GetAllCookiesForURLWithOptionsTask(CookieMonster* cm,
@@ -2342,7 +2146,8 @@
         base::Bind(&GetCookieListCallback::Run, base::Unretained(callback)));
   }
 
-  void SetCookieWithDetailsTask(CookieMonster* cm, const GURL& url,
+  void SetCookieWithDetailsTask(CookieMonster* cm,
+                                const GURL& url,
                                 ResultSavingCookieCallback<bool>* callback) {
     // Define the parameters here instead of in the calling fucntion.
     // The maximum number of parameters for Bind function is 6.
@@ -2356,10 +2161,8 @@
     CookiePriority priority = COOKIE_PRIORITY_DEFAULT;
     cm->SetCookieWithDetailsAsync(
         url, name, value, domain, path, expiration_time, secure, http_only,
-        priority,
-        base::Bind(
-            &ResultSavingCookieCallback<bool>::Run,
-            base::Unretained(callback)));
+        priority, base::Bind(&ResultSavingCookieCallback<bool>::Run,
+                             base::Unretained(callback)));
   }
 
   void DeleteAllCreatedBetweenTask(CookieMonster* cm,
@@ -2368,17 +2171,16 @@
                                    ResultSavingCookieCallback<int>* callback) {
     cm->DeleteAllCreatedBetweenAsync(
         delete_begin, delete_end,
-        base::Bind(
-            &ResultSavingCookieCallback<int>::Run, base::Unretained(callback)));
+        base::Bind(&ResultSavingCookieCallback<int>::Run,
+                   base::Unretained(callback)));
   }
 
   void DeleteAllForHostTask(CookieMonster* cm,
                             const GURL& url,
                             ResultSavingCookieCallback<int>* callback) {
-    cm->DeleteAllForHostAsync(
-        url,
-        base::Bind(
-            &ResultSavingCookieCallback<int>::Run, base::Unretained(callback)));
+    cm->DeleteAllForHostAsync(url,
+                              base::Bind(&ResultSavingCookieCallback<int>::Run,
+                                         base::Unretained(callback)));
   }
 
   void DeleteAllCreatedBetweenForHostTask(
@@ -2389,19 +2191,16 @@
       ResultSavingCookieCallback<int>* callback) {
     cm->DeleteAllCreatedBetweenForHostAsync(
         delete_begin, delete_end, url,
-        base::Bind(
-            &ResultSavingCookieCallback<int>::Run,
-            base::Unretained(callback)));
+        base::Bind(&ResultSavingCookieCallback<int>::Run,
+                   base::Unretained(callback)));
   }
 
   void DeleteCanonicalCookieTask(CookieMonster* cm,
                                  const CanonicalCookie& cookie,
                                  ResultSavingCookieCallback<bool>* callback) {
     cm->DeleteCanonicalCookieAsync(
-        cookie,
-        base::Bind(
-            &ResultSavingCookieCallback<bool>::Run,
-            base::Unretained(callback)));
+        cookie, base::Bind(&ResultSavingCookieCallback<bool>::Run,
+                           base::Unretained(callback)));
   }
 
  protected:
@@ -2429,8 +2228,7 @@
   GetCookieListCallback callback(&other_thread_);
   base::Closure task =
       base::Bind(&net::MultiThreadedCookieMonsterTest::GetAllCookiesTask,
-                 base::Unretained(this),
-                 cm, &callback);
+                 base::Unretained(this), cm, &callback);
   RunOnOtherThread(task);
   EXPECT_TRUE(callback.did_run());
   it = callback.cookies().begin();
@@ -2452,8 +2250,7 @@
   GetCookieListCallback callback(&other_thread_);
   base::Closure task =
       base::Bind(&net::MultiThreadedCookieMonsterTest::GetAllCookiesForURLTask,
-                 base::Unretained(this),
-                 cm, url_google_, &callback);
+                 base::Unretained(this), cm, url_google_, &callback);
   RunOnOtherThread(task);
   EXPECT_TRUE(callback.did_run());
   it = callback.cookies().begin();
@@ -2477,8 +2274,7 @@
   GetCookieListCallback callback(&other_thread_);
   base::Closure task = base::Bind(
       &net::MultiThreadedCookieMonsterTest::GetAllCookiesForURLWithOptionsTask,
-      base::Unretained(this),
-      cm, url_google_, options, &callback);
+      base::Unretained(this), cm, url_google_, options, &callback);
   RunOnOtherThread(task);
   EXPECT_TRUE(callback.did_run());
   it = callback.cookies().begin();
@@ -2490,21 +2286,13 @@
 
 TEST_F(MultiThreadedCookieMonsterTest, ThreadCheckSetCookieWithDetails) {
   scoped_refptr<CookieMonster> cm(new CookieMonster(NULL, NULL));
-  EXPECT_TRUE(SetCookieWithDetails(cm.get(),
-                                   url_google_foo_,
-                                   "A",
-                                   "B",
-                                   std::string(),
-                                   "/foo",
-                                   base::Time(),
-                                   false,
-                                   false,
-                                   COOKIE_PRIORITY_DEFAULT));
+  EXPECT_TRUE(SetCookieWithDetails(cm.get(), url_google_foo_, "A", "B",
+                                   std::string(), "/foo", base::Time(), false,
+                                   false, COOKIE_PRIORITY_DEFAULT));
   ResultSavingCookieCallback<bool> callback(&other_thread_);
-  base::Closure task = base::Bind(
-      &net::MultiThreadedCookieMonsterTest::SetCookieWithDetailsTask,
-      base::Unretained(this),
-      cm, url_google_foo_, &callback);
+  base::Closure task =
+      base::Bind(&net::MultiThreadedCookieMonsterTest::SetCookieWithDetailsTask,
+                 base::Unretained(this), cm, url_google_foo_, &callback);
   RunOnOtherThread(task);
   EXPECT_TRUE(callback.did_run());
   EXPECT_TRUE(callback.result());
@@ -2515,16 +2303,14 @@
   CookieOptions options;
   Time now = Time::Now();
   EXPECT_TRUE(SetCookieWithOptions(cm.get(), url_google_, "A=B", options));
-  EXPECT_EQ(
-      1,
-      DeleteAllCreatedBetween(cm.get(), now - TimeDelta::FromDays(99), Time()));
+  EXPECT_EQ(1, DeleteAllCreatedBetween(cm.get(), now - TimeDelta::FromDays(99),
+                                       Time()));
   EXPECT_TRUE(SetCookieWithOptions(cm.get(), url_google_, "A=B", options));
   ResultSavingCookieCallback<int> callback(&other_thread_);
   base::Closure task = base::Bind(
       &net::MultiThreadedCookieMonsterTest::DeleteAllCreatedBetweenTask,
-      base::Unretained(this),
-      cm, now - TimeDelta::FromDays(99),
-      Time(), &callback);
+      base::Unretained(this), cm, now - TimeDelta::FromDays(99), Time(),
+      &callback);
   RunOnOtherThread(task);
   EXPECT_TRUE(callback.did_run());
   EXPECT_EQ(1, callback.result());
@@ -2537,10 +2323,9 @@
   EXPECT_EQ(1, DeleteAllForHost(cm.get(), url_google_));
   EXPECT_TRUE(SetCookieWithOptions(cm.get(), url_google_, "A=B", options));
   ResultSavingCookieCallback<int> callback(&other_thread_);
-  base::Closure task = base::Bind(
-      &net::MultiThreadedCookieMonsterTest::DeleteAllForHostTask,
-      base::Unretained(this),
-      cm, url_google_, &callback);
+  base::Closure task =
+      base::Bind(&net::MultiThreadedCookieMonsterTest::DeleteAllForHostTask,
+                 base::Unretained(this), cm, url_google_, &callback);
   RunOnOtherThread(task);
   EXPECT_TRUE(callback.did_run());
   EXPECT_EQ(1, callback.result());
@@ -2573,8 +2358,7 @@
   // 1. First set of deletions.
   EXPECT_EQ(
       3,  // Deletes A=B, C=D, Y=Z
-      DeleteAllCreatedBetweenForHost(
-          cm.get(), ago3, Time::Max(), url_google_));
+      DeleteAllCreatedBetweenForHost(cm.get(), ago3, Time::Max(), url_google_));
 
   EXPECT_TRUE(SetCookieWithOptions(cm.get(), url_google_, "A=B", options));
   ResultSavingCookieCallback<int> callback(&other_thread_);
@@ -2582,9 +2366,7 @@
   // 2. Second set of deletions.
   base::Closure task = base::Bind(
       &net::MultiThreadedCookieMonsterTest::DeleteAllCreatedBetweenForHostTask,
-      base::Unretained(this),
-      cm, ago1, Time(), url_google_,
-      &callback);
+      base::Unretained(this), cm, ago1, Time(), url_google_, &callback);
   RunOnOtherThread(task);
   EXPECT_TRUE(callback.did_run());
   EXPECT_EQ(2, callback.result());  // Deletes A=B, G=H.
@@ -2604,8 +2386,7 @@
   it = cookies.begin();
   base::Closure task = base::Bind(
       &net::MultiThreadedCookieMonsterTest::DeleteCanonicalCookieTask,
-      base::Unretained(this),
-      cm, *it, &callback);
+      base::Unretained(this), cm, *it, &callback);
   RunOnOtherThread(task);
   EXPECT_TRUE(callback.did_run());
   EXPECT_TRUE(callback.result());
@@ -2675,17 +2456,15 @@
 TEST_F(CookieMonsterTest, InvalidExpiryTime) {
   std::string cookie_line =
       std::string(kValidCookieLine) + "; expires=Blarg arg arg";
-  scoped_ptr<CanonicalCookie> cookie(
-      CanonicalCookie::Create(url_google_, cookie_line, Time::Now(),
-                              CookieOptions()));
+  scoped_ptr<CanonicalCookie> cookie(CanonicalCookie::Create(
+      url_google_, cookie_line, Time::Now(), CookieOptions()));
   ASSERT_FALSE(cookie->IsPersistent());
 }
 
 // Test that CookieMonster writes session cookies into the underlying
 // CookieStore if the "persist session cookies" option is on.
 TEST_F(CookieMonsterTest, PersistSessionCookies) {
-  scoped_refptr<MockPersistentCookieStore> store(
-      new MockPersistentCookieStore);
+  scoped_refptr<MockPersistentCookieStore> store(new MockPersistentCookieStore);
   scoped_refptr<CookieMonster> cm(new CookieMonster(store.get(), NULL));
   cm->SetPersistSessionCookies(true);
 
@@ -2721,13 +2500,12 @@
 
 // Test the commands sent to the persistent cookie store.
 TEST_F(CookieMonsterTest, PersisentCookieStorageTest) {
-  scoped_refptr<MockPersistentCookieStore> store(
-      new MockPersistentCookieStore);
+  scoped_refptr<MockPersistentCookieStore> store(new MockPersistentCookieStore);
   scoped_refptr<CookieMonster> cm(new CookieMonster(store.get(), NULL));
 
   // Add a cookie.
-  EXPECT_TRUE(SetCookie(
-      cm.get(), url_google_, "A=B; expires=Mon, 18-Apr-22 22:50:13 GMT"));
+  EXPECT_TRUE(SetCookie(cm.get(), url_google_,
+                        "A=B; expires=Mon, 18-Apr-22 22:50:13 GMT"));
   this->MatchCookieLines("A=B", GetCookies(cm.get(), url_google_));
   ASSERT_EQ(1u, store->commands().size());
   EXPECT_EQ(CookieStoreCommand::ADD, store->commands()[0].type);
@@ -2738,14 +2516,14 @@
   EXPECT_EQ(CookieStoreCommand::REMOVE, store->commands()[1].type);
 
   // Add a cookie.
-  EXPECT_TRUE(SetCookie(
-      cm.get(), url_google_, "A=B; expires=Mon, 18-Apr-22 22:50:13 GMT"));
+  EXPECT_TRUE(SetCookie(cm.get(), url_google_,
+                        "A=B; expires=Mon, 18-Apr-22 22:50:13 GMT"));
   this->MatchCookieLines("A=B", GetCookies(cm.get(), url_google_));
   ASSERT_EQ(3u, store->commands().size());
   EXPECT_EQ(CookieStoreCommand::ADD, store->commands()[2].type);
   // Overwrite it.
-  EXPECT_TRUE(SetCookie(
-      cm.get(), url_google_, "A=Foo; expires=Mon, 18-Apr-22 22:50:14 GMT"));
+  EXPECT_TRUE(SetCookie(cm.get(), url_google_,
+                        "A=Foo; expires=Mon, 18-Apr-22 22:50:14 GMT"));
   this->MatchCookieLines("A=Foo", GetCookies(cm.get(), url_google_));
   ASSERT_EQ(5u, store->commands().size());
   EXPECT_EQ(CookieStoreCommand::REMOVE, store->commands()[3].type);
@@ -2769,27 +2547,22 @@
   const std::string domain("host");
   const std::string path("/path");
 
-  scoped_refptr<MockPersistentCookieStore> store(
-      new MockPersistentCookieStore);
+  scoped_refptr<MockPersistentCookieStore> store(new MockPersistentCookieStore);
 
   std::vector<CanonicalCookie*> initial_cookies;
 
-  AddCookieToList(domain,
-                  "foo=bar; path=" + path,
-                  now1,
-                  &initial_cookies);
+  AddCookieToList(domain, "foo=bar; path=" + path, now1, &initial_cookies);
 
   // We have to manually build this cookie because it contains a control
   // character, and our cookie line parser rejects control characters.
-  CanonicalCookie *cc = new CanonicalCookie(url, "baz", "\x05" "boo", domain,
-                                            path, now2, later, now2, false,
-                                            false, COOKIE_PRIORITY_DEFAULT);
+  CanonicalCookie* cc = new CanonicalCookie(
+      url, "baz",
+      "\x05"
+      "boo",
+      domain, path, now2, later, now2, false, false, COOKIE_PRIORITY_DEFAULT);
   initial_cookies.push_back(cc);
 
-  AddCookieToList(domain,
-                  "hello=world; path=" + path,
-                  now3,
-                  &initial_cookies);
+  AddCookieToList(domain, "hello=world; path=" + path, now3, &initial_cookies);
 
   // Inject our initial cookies into the mock PersistentCookieStore.
   store->SetLoadExpectation(true, initial_cookies);
@@ -2831,7 +2604,8 @@
 TEST_F(CookieMonsterNotificationTest, NoNotifyWithNoCookie) {
   std::vector<net::CanonicalCookie> cookies;
   scoped_ptr<CookieStore::CookieChangedSubscription> sub(
-      monster()->AddCallbackForCookie(test_url_, "abc",
+      monster()->AddCallbackForCookie(
+          test_url_, "abc",
           base::Bind(&RecordCookieChanges, &cookies, nullptr)));
   base::MessageLoop::current()->RunUntilIdle();
   EXPECT_EQ(0U, cookies.size());
@@ -2842,7 +2616,8 @@
   SetCookie(monster(), test_url_, "abc=def");
   base::MessageLoop::current()->RunUntilIdle();
   scoped_ptr<CookieStore::CookieChangedSubscription> sub(
-      monster()->AddCallbackForCookie(test_url_, "abc",
+      monster()->AddCallbackForCookie(
+          test_url_, "abc",
           base::Bind(&RecordCookieChanges, &cookies, nullptr)));
   base::MessageLoop::current()->RunUntilIdle();
   EXPECT_EQ(0U, cookies.size());
@@ -2852,7 +2627,8 @@
   std::vector<net::CanonicalCookie> cookies;
   std::vector<bool> removes;
   scoped_ptr<CookieStore::CookieChangedSubscription> sub(
-      monster()->AddCallbackForCookie(test_url_, "abc",
+      monster()->AddCallbackForCookie(
+          test_url_, "abc",
           base::Bind(&RecordCookieChanges, &cookies, &removes)));
   SetCookie(monster(), test_url_, "abc=def");
   base::MessageLoop::current()->RunUntilIdle();
@@ -2868,7 +2644,8 @@
   std::vector<net::CanonicalCookie> cookies;
   std::vector<bool> removes;
   scoped_ptr<CookieStore::CookieChangedSubscription> sub(
-      monster()->AddCallbackForCookie(test_url_, "abc",
+      monster()->AddCallbackForCookie(
+          test_url_, "abc",
           base::Bind(&RecordCookieChanges, &cookies, &removes)));
   SetCookie(monster(), test_url_, "abc=def");
   base::MessageLoop::current()->RunUntilIdle();
@@ -2889,7 +2666,8 @@
   std::vector<net::CanonicalCookie> cookies;
   std::vector<bool> removes;
   scoped_ptr<CookieStore::CookieChangedSubscription> sub(
-      monster()->AddCallbackForCookie(test_url_, "abc",
+      monster()->AddCallbackForCookie(
+          test_url_, "abc",
           base::Bind(&RecordCookieChanges, &cookies, &removes)));
   SetCookie(monster(), test_url_, "abc=def");
   base::MessageLoop::current()->RunUntilIdle();
@@ -2916,10 +2694,12 @@
   std::vector<net::CanonicalCookie> cookies0;
   std::vector<net::CanonicalCookie> cookies1;
   scoped_ptr<CookieStore::CookieChangedSubscription> sub0(
-      monster()->AddCallbackForCookie(test_url_, "abc",
+      monster()->AddCallbackForCookie(
+          test_url_, "abc",
           base::Bind(&RecordCookieChanges, &cookies0, nullptr)));
   scoped_ptr<CookieStore::CookieChangedSubscription> sub1(
-      monster()->AddCallbackForCookie(test_url_, "def",
+      monster()->AddCallbackForCookie(
+          test_url_, "def",
           base::Bind(&RecordCookieChanges, &cookies1, nullptr)));
   SetCookie(monster(), test_url_, "abc=def");
   base::MessageLoop::current()->RunUntilIdle();
@@ -2935,10 +2715,12 @@
   std::vector<net::CanonicalCookie> cookies0;
   std::vector<net::CanonicalCookie> cookies1;
   scoped_ptr<CookieStore::CookieChangedSubscription> sub0(
-      monster()->AddCallbackForCookie(test_url_, "abc",
+      monster()->AddCallbackForCookie(
+          test_url_, "abc",
           base::Bind(&RecordCookieChanges, &cookies0, nullptr)));
   scoped_ptr<CookieStore::CookieChangedSubscription> sub1(
-      monster()->AddCallbackForCookie(test_url_, "abc",
+      monster()->AddCallbackForCookie(
+          test_url_, "abc",
           base::Bind(&RecordCookieChanges, &cookies1, nullptr)));
   SetCookie(monster(), test_url_, "abc=def");
   base::MessageLoop::current()->RunUntilIdle();
diff --git a/net/cookies/cookie_options.h b/net/cookies/cookie_options.h
index ed5e2ef..975b8c8f 100644
--- a/net/cookies/cookie_options.h
+++ b/net/cookies/cookie_options.h
@@ -14,10 +14,7 @@
   // Default is to exclude httponly, which means:
   // - reading operations will not return httponly cookies.
   // - writing operations will not write httponly cookies.
-  CookieOptions()
-      : exclude_httponly_(true),
-        server_time_() {
-  }
+  CookieOptions() : exclude_httponly_(true), server_time_() {}
 
   void set_exclude_httponly() { exclude_httponly_ = true; }
   void set_include_httponly() { exclude_httponly_ = false; }
@@ -39,4 +36,3 @@
 }  // namespace net
 
 #endif  // NET_COOKIES_COOKIE_OPTIONS_H_
-
diff --git a/net/cookies/parsed_cookie.cc b/net/cookies/parsed_cookie.cc
index 2c72c3b8..8cbca5c 100644
--- a/net/cookies/parsed_cookie.cc
+++ b/net/cookies/parsed_cookie.cc
@@ -73,7 +73,8 @@
 inline bool SeekTo(std::string::const_iterator* it,
                    const std::string::const_iterator& end,
                    const char* chars) {
-  for (; *it != end && !CharIsA(**it, chars); ++(*it)) {}
+  for (; *it != end && !CharIsA(**it, chars); ++(*it)) {
+  }
   return *it == end;
 }
 // Seek the iterator to the first occurrence of a character not in |chars|.
@@ -81,13 +82,15 @@
 inline bool SeekPast(std::string::const_iterator* it,
                      const std::string::const_iterator& end,
                      const char* chars) {
-  for (; *it != end && CharIsA(**it, chars); ++(*it)) {}
+  for (; *it != end && CharIsA(**it, chars); ++(*it)) {
+  }
   return *it == end;
 }
 inline bool SeekBackPast(std::string::const_iterator* it,
                          const std::string::const_iterator& end,
                          const char* chars) {
-  for (; *it != end && CharIsA(**it, chars); --(*it)) {}
+  for (; *it != end && CharIsA(**it, chars); --(*it)) {
+  }
   return *it == end;
 }
 
@@ -120,15 +123,13 @@
 bool IsValidCookieValue(const std::string& value) {
   // Number of characters to skip in validation at beginning and end of string.
   size_t skip = 0;
-  if (value.size() >= 2 && *value.begin() == '"' && *(value.end()-1) == '"')
+  if (value.size() >= 2 && *value.begin() == '"' && *(value.end() - 1) == '"')
     skip = 1;
   for (std::string::const_iterator i = value.begin() + skip;
        i != value.end() - skip; ++i) {
     bool valid_octet =
-        (*i == 0x21 ||
-         (*i >= 0x23 && *i <= 0x2B) ||
-         (*i >= 0x2D && *i <= 0x3A) ||
-         (*i >= 0x3C && *i <= 0x5B) ||
+        (*i == 0x21 || (*i >= 0x23 && *i <= 0x2B) ||
+         (*i >= 0x2D && *i <= 0x3A) || (*i >= 0x3C && *i <= 0x5B) ||
          (*i >= 0x5D && *i <= 0x7E));
     if (!valid_octet)
       return false;
@@ -162,7 +163,6 @@
       secure_index_(0),
       httponly_index_(0),
       priority_index_(0) {
-
   if (cookie_line.size() > kMaxCookieSize) {
     VLOG(1) << "Not parsing cookie, too large: " << cookie_line.size();
     return;
@@ -181,8 +181,9 @@
 }
 
 CookiePriority ParsedCookie::Priority() const {
-  return (priority_index_ == 0) ? COOKIE_PRIORITY_DEFAULT :
-      StringToCookiePriority(pairs_[priority_index_].second);
+  return (priority_index_ == 0)
+             ? COOKIE_PRIORITY_DEFAULT
+             : StringToCookiePriority(pairs_[priority_index_].second);
 }
 
 bool ParsedCookie::SetName(const std::string& name) {
@@ -233,8 +234,7 @@
 
 std::string ParsedCookie::ToCookieLine() const {
   std::string out;
-  for (PairList::const_iterator it = pairs_.begin();
-       it != pairs_.end(); ++it) {
+  for (PairList::const_iterator it = pairs_.begin(); it != pairs_.end(); ++it) {
     if (!out.empty())
       out.append("; ");
     out.append(it->first);
@@ -249,8 +249,7 @@
 std::string::const_iterator ParsedCookie::FindFirstTerminator(
     const std::string& s) {
   std::string::const_iterator end = s.end();
-  size_t term_pos =
-      s.find_first_of(std::string(kTerminator, kTerminatorLen));
+  size_t term_pos = s.find_first_of(std::string(kTerminator, kTerminatorLen));
   if (term_pos != std::string::npos) {
     // We found a character we should treat as an end of string.
     end = s.begin() + term_pos;
@@ -281,7 +280,7 @@
   // token_end should point after the last interesting token character,
   // pointing at either whitespace, or at '=' (and equal to token_real_end).
   if (*it != *token_start) {  // We could have an empty token name.
-    --(*it);  // Go back before the token separator.
+    --(*it);                  // Go back before the token separator.
     // Skip over any whitespace to the first non-whitespace character.
     SeekBackPast(it, *token_start, kWhitespace);
     // Point after it.
@@ -449,9 +448,7 @@
   }
 }
 
-bool ParsedCookie::SetBool(size_t* index,
-                           const std::string& key,
-                           bool value) {
+bool ParsedCookie::SetBool(size_t* index, const std::string& key, bool value) {
   if (!value) {
     ClearAttributePair(*index);
     return true;
@@ -483,9 +480,13 @@
   if (index == 0)
     return;
 
-  size_t* indexes[] = { &path_index_, &domain_index_, &expires_index_,
-                        &maxage_index_, &secure_index_, &httponly_index_,
-                        &priority_index_ };
+  size_t* indexes[] = {&path_index_,
+                       &domain_index_,
+                       &expires_index_,
+                       &maxage_index_,
+                       &secure_index_,
+                       &httponly_index_,
+                       &priority_index_};
   for (size_t i = 0; i < arraysize(indexes); ++i) {
     if (*indexes[i] == index)
       *indexes[i] = 0;
diff --git a/net/cookies/parsed_cookie.h b/net/cookies/parsed_cookie.h
index 115ca1f..971cf37 100644
--- a/net/cookies/parsed_cookie.h
+++ b/net/cookies/parsed_cookie.h
@@ -113,9 +113,7 @@
   bool SetString(size_t* index,
                  const std::string& key,
                  const std::string& value);
-  bool SetBool(size_t* index,
-               const std::string& key,
-               bool value);
+  bool SetBool(size_t* index, const std::string& key, bool value);
 
   // Helper function for SetString and SetBool handling the case that the
   // key/value pair shall not be removed.
diff --git a/net/cookies/parsed_cookie_unittest.cc b/net/cookies/parsed_cookie_unittest.cc
index 30d41eb..9f231e83 100644
--- a/net/cookies/parsed_cookie_unittest.cc
+++ b/net/cookies/parsed_cookie_unittest.cc
@@ -39,22 +39,26 @@
   // Firefox 3, and Safari Windows 3.2.1.  We originally tried to match
   // Firefox closely, however we now match Internet Explorer and Safari.
   const char* const values[] = {
-    // Trailing whitespace after a quoted value.  The whitespace after
-    // the quote is stripped in all browsers.
-    "\"zzz \"  ",              "\"zzz \"",
-    // Handling a quoted value with a ';', like FOO="zz;pp"  ;
-    // IE and Safari: "zz;
-    // Firefox and Opera: "zz;pp"
-    "\"zz;pp\" ;",             "\"zz",
-    // Handling a value with multiple quoted parts, like FOO="zzz "   "ppp" ;
-    // IE and Safari: "zzz "   "ppp";
-    // Firefox: "zzz ";
-    // Opera: <rejects cookie>
-    "\"zzz \"   \"ppp\" ",     "\"zzz \"   \"ppp\"",
-    // A quote in a value that didn't start quoted.  like FOO=A"B ;
-    // IE, Safari, and Firefox: A"B;
-    // Opera: <rejects cookie>
-    "A\"B",                    "A\"B",
+      // Trailing whitespace after a quoted value.  The whitespace after
+      // the quote is stripped in all browsers.
+      "\"zzz \"  ",
+      "\"zzz \"",
+      // Handling a quoted value with a ';', like FOO="zz;pp"  ;
+      // IE and Safari: "zz;
+      // Firefox and Opera: "zz;pp"
+      "\"zz;pp\" ;",
+      "\"zz",
+      // Handling a value with multiple quoted parts, like FOO="zzz "   "ppp" ;
+      // IE and Safari: "zzz "   "ppp";
+      // Firefox: "zzz ";
+      // Opera: <rejects cookie>
+      "\"zzz \"   \"ppp\" ",
+      "\"zzz \"   \"ppp\"",
+      // A quote in a value that didn't start quoted.  like FOO=A"B ;
+      // IE, Safari, and Firefox: A"B;
+      // Opera: <rejects cookie>
+      "A\"B",
+      "A\"B",
   };
 
   for (size_t i = 0; i < arraysize(values); i += 2) {
@@ -169,9 +173,10 @@
 }
 
 TEST(ParsedCookieTest, QuotedTrailingWhitespace) {
-  ParsedCookie pc("ANCUUID=\"zohNumRKgI0oxyhSsV3Z7D\"  ; "
-                      "expires=Sun, 18-Apr-2027 21:06:29 GMT ; "
-                      "path=/  ;  ");
+  ParsedCookie pc(
+      "ANCUUID=\"zohNumRKgI0oxyhSsV3Z7D\"  ; "
+      "expires=Sun, 18-Apr-2027 21:06:29 GMT ; "
+      "path=/  ;  ");
   EXPECT_TRUE(pc.IsValid());
   EXPECT_EQ("ANCUUID", pc.Name());
   // Stripping whitespace after the quotes matches all other major browsers.
@@ -184,9 +189,10 @@
 }
 
 TEST(ParsedCookieTest, TrailingWhitespace) {
-  ParsedCookie pc("ANCUUID=zohNumRKgI0oxyhSsV3Z7D  ; "
-                      "expires=Sun, 18-Apr-2027 21:06:29 GMT ; "
-                      "path=/  ;  ");
+  ParsedCookie pc(
+      "ANCUUID=zohNumRKgI0oxyhSsV3Z7D  ; "
+      "expires=Sun, 18-Apr-2027 21:06:29 GMT ; "
+      "path=/  ;  ");
   EXPECT_TRUE(pc.IsValid());
   EXPECT_EQ("ANCUUID", pc.Name());
   EXPECT_EQ("zohNumRKgI0oxyhSsV3Z7D", pc.Value());
@@ -248,36 +254,31 @@
 }
 
 TEST(ParsedCookieTest, ParseTokensAndValues) {
-  EXPECT_EQ("hello",
-            ParsedCookie::ParseTokenString("hello\nworld"));
-  EXPECT_EQ("fs!!@",
-            ParsedCookie::ParseTokenString("fs!!@;helloworld"));
+  EXPECT_EQ("hello", ParsedCookie::ParseTokenString("hello\nworld"));
+  EXPECT_EQ("fs!!@", ParsedCookie::ParseTokenString("fs!!@;helloworld"));
   EXPECT_EQ("hello world\tgood",
             ParsedCookie::ParseTokenString("hello world\tgood\rbye"));
-  EXPECT_EQ("A",
-            ParsedCookie::ParseTokenString("A=B=C;D=E"));
-  EXPECT_EQ("hello",
-            ParsedCookie::ParseValueString("hello\nworld"));
-  EXPECT_EQ("fs!!@",
-            ParsedCookie::ParseValueString("fs!!@;helloworld"));
+  EXPECT_EQ("A", ParsedCookie::ParseTokenString("A=B=C;D=E"));
+  EXPECT_EQ("hello", ParsedCookie::ParseValueString("hello\nworld"));
+  EXPECT_EQ("fs!!@", ParsedCookie::ParseValueString("fs!!@;helloworld"));
   EXPECT_EQ("hello world\tgood",
             ParsedCookie::ParseValueString("hello world\tgood\rbye"));
-  EXPECT_EQ("A=B=C",
-            ParsedCookie::ParseValueString("A=B=C;D=E"));
+  EXPECT_EQ("A=B=C", ParsedCookie::ParseValueString("A=B=C;D=E"));
 }
 
 TEST(ParsedCookieTest, SerializeCookieLine) {
-  const char input[] = "ANCUUID=zohNumRKgI0oxyhSsV3Z7D  ; "
-                       "expires=Sun, 18-Apr-2027 21:06:29 GMT ; "
-                       "path=/  ;  priority=low  ;  ";
-  const char output[] = "ANCUUID=zohNumRKgI0oxyhSsV3Z7D; "
-                        "expires=Sun, 18-Apr-2027 21:06:29 GMT; "
-                        "path=/; priority=low";
+  const char input[] =
+      "ANCUUID=zohNumRKgI0oxyhSsV3Z7D  ; "
+      "expires=Sun, 18-Apr-2027 21:06:29 GMT ; "
+      "path=/  ;  priority=low  ;  ";
+  const char output[] =
+      "ANCUUID=zohNumRKgI0oxyhSsV3Z7D; "
+      "expires=Sun, 18-Apr-2027 21:06:29 GMT; "
+      "path=/; priority=low";
   ParsedCookie pc(input);
   EXPECT_EQ(output, pc.ToCookieLine());
 }
 
-
 TEST(ParsedCookieTest, SetNameAndValue) {
   ParsedCookie empty((std::string()));
   EXPECT_FALSE(empty.IsValid());
@@ -353,10 +354,11 @@
   EXPECT_TRUE(pc.SetIsHttpOnly(true));
   EXPECT_TRUE(pc.SetIsHttpOnly(true));
   EXPECT_TRUE(pc.SetPriority("HIGH"));
-  EXPECT_EQ("name=value; domain=domain.com; path=/; "
-            "expires=Sun, 18-Apr-2027 21:06:29 GMT; max-age=12345; secure; "
-            "httponly; priority=HIGH",
-            pc.ToCookieLine());
+  EXPECT_EQ(
+      "name=value; domain=domain.com; path=/; "
+      "expires=Sun, 18-Apr-2027 21:06:29 GMT; max-age=12345; secure; "
+      "httponly; priority=HIGH",
+      pc.ToCookieLine());
   EXPECT_TRUE(pc.HasDomain());
   EXPECT_TRUE(pc.HasPath());
   EXPECT_TRUE(pc.HasExpires());
@@ -372,17 +374,19 @@
   EXPECT_TRUE(pc.HasExpires());
   EXPECT_TRUE(pc.IsSecure());
   EXPECT_TRUE(pc.IsHttpOnly());
-  EXPECT_EQ("name=value; domain=domain.com; path=/foo; "
-            "expires=Sun, 18-Apr-2027 21:06:29 GMT; max-age=12345; secure; "
-            "httponly; priority=HIGH",
-            pc.ToCookieLine());
+  EXPECT_EQ(
+      "name=value; domain=domain.com; path=/foo; "
+      "expires=Sun, 18-Apr-2027 21:06:29 GMT; max-age=12345; secure; "
+      "httponly; priority=HIGH",
+      pc.ToCookieLine());
 
   // Set priority to medium.
   EXPECT_TRUE(pc.SetPriority("medium"));
-  EXPECT_EQ("name=value; domain=domain.com; path=/foo; "
-            "expires=Sun, 18-Apr-2027 21:06:29 GMT; max-age=12345; secure; "
-            "httponly; priority=medium",
-            pc.ToCookieLine());
+  EXPECT_EQ(
+      "name=value; domain=domain.com; path=/foo; "
+      "expires=Sun, 18-Apr-2027 21:06:29 GMT; max-age=12345; secure; "
+      "httponly; priority=medium",
+      pc.ToCookieLine());
 
   // Clear the rest and change the name and value.
   EXPECT_TRUE(pc.SetDomain(std::string()));
@@ -439,20 +443,53 @@
 
 TEST(ParsedCookieTest, InvalidNonAlphanumericChars) {
   ParsedCookie pc1("name=\x05");
-  ParsedCookie pc2("name=foo" "\x1c" "bar");
-  ParsedCookie pc3("name=foobar" "\x11");
-  ParsedCookie pc4("name=\x02" "foobar");
+  ParsedCookie pc2(
+      "name=foo"
+      "\x1c"
+      "bar");
+  ParsedCookie pc3(
+      "name=foobar"
+      "\x11");
+  ParsedCookie pc4(
+      "name=\x02"
+      "foobar");
 
   ParsedCookie pc5("\x05=value");
-  ParsedCookie pc6("foo" "\x05" "bar=value");
-  ParsedCookie pc7("foobar" "\x05" "=value");
-  ParsedCookie pc8("\x05" "foobar" "=value");
+  ParsedCookie pc6(
+      "foo"
+      "\x05"
+      "bar=value");
+  ParsedCookie pc7(
+      "foobar"
+      "\x05"
+      "=value");
+  ParsedCookie pc8(
+      "\x05"
+      "foobar"
+      "=value");
 
-  ParsedCookie pc9("foo" "\x05" "bar" "=foo" "\x05" "bar");
+  ParsedCookie pc9(
+      "foo"
+      "\x05"
+      "bar"
+      "=foo"
+      "\x05"
+      "bar");
 
-  ParsedCookie pc10("foo=bar;ba" "\x05" "z=boo");
-  ParsedCookie pc11("foo=bar;baz=bo" "\x05" "o");
-  ParsedCookie pc12("foo=bar;ba" "\05" "z=bo" "\x05" "o");
+  ParsedCookie pc10(
+      "foo=bar;ba"
+      "\x05"
+      "z=boo");
+  ParsedCookie pc11(
+      "foo=bar;baz=bo"
+      "\x05"
+      "o");
+  ParsedCookie pc12(
+      "foo=bar;ba"
+      "\05"
+      "z=bo"
+      "\x05"
+      "o");
 
   EXPECT_FALSE(pc1.IsValid());
   EXPECT_FALSE(pc2.IsValid());
@@ -505,5 +542,4 @@
   EXPECT_TRUE(pc8.IsValid());
   EXPECT_EQ(pc8_literal, pc8.ToCookieLine());
 }
-
 }
diff --git a/net/data/websocket/set-hsts_wsh.py b/net/data/websocket/set-hsts_wsh.py
new file mode 100644
index 0000000..c78a82a
--- /dev/null
+++ b/net/data/websocket/set-hsts_wsh.py
@@ -0,0 +1,19 @@
+# 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.
+#
+# Add a Strict-Transport-Security header to the response.
+
+
+import json
+
+
+def web_socket_do_extra_handshake(request):
+  request.extra_headers.append(
+      ('Strict-Transport-Security', 'max-age=3600'))
+  pass
+
+
+def web_socket_transfer_data(request):
+  # Wait for closing handshake
+  request.ws_stream.receive_message()
diff --git a/net/disk_cache/simple/simple_backend_impl.cc b/net/disk_cache/simple/simple_backend_impl.cc
index 6c43eeb..3808644 100644
--- a/net/disk_cache/simple/simple_backend_impl.cc
+++ b/net/disk_cache/simple/simple_backend_impl.cc
@@ -15,6 +15,7 @@
 #include "base/bind.h"
 #include "base/callback.h"
 #include "base/files/file_util.h"
+#include "base/lazy_instance.h"
 #include "base/location.h"
 #include "base/metrics/field_trial.h"
 #include "base/metrics/histogram.h"
@@ -57,16 +58,27 @@
 // Maximum fraction of the cache that one entry can consume.
 const int kMaxFileRatio = 8;
 
-// A global sequenced worker pool to use for launching all tasks.
-SequencedWorkerPool* g_sequenced_worker_pool = NULL;
+class LeakySequencedWorkerPool {
+ public:
+  LeakySequencedWorkerPool()
+      : sequenced_worker_pool_(
+            new SequencedWorkerPool(kMaxWorkerThreads, kThreadNamePrefix)) {}
 
-void MaybeCreateSequencedWorkerPool() {
-  if (!g_sequenced_worker_pool) {
-    g_sequenced_worker_pool =
-        new SequencedWorkerPool(kMaxWorkerThreads, kThreadNamePrefix);
-    g_sequenced_worker_pool->AddRef();  // Leak it.
+  void FlushForTesting() { sequenced_worker_pool_->FlushForTesting(); }
+
+  scoped_refptr<base::TaskRunner> GetTaskRunner() {
+    return sequenced_worker_pool_->GetTaskRunnerWithShutdownBehavior(
+        SequencedWorkerPool::CONTINUE_ON_SHUTDOWN);
   }
-}
+
+ private:
+  scoped_refptr<SequencedWorkerPool> sequenced_worker_pool_;
+
+  DISALLOW_COPY_AND_ASSIGN(LeakySequencedWorkerPool);
+};
+
+base::LazyInstance<LeakySequencedWorkerPool>::Leaky g_sequenced_worker_pool =
+    LAZY_INSTANCE_INITIALIZER;
 
 bool g_fd_limit_histogram_has_been_populated = false;
 
@@ -236,10 +248,7 @@
 }
 
 int SimpleBackendImpl::Init(const CompletionCallback& completion_callback) {
-  MaybeCreateSequencedWorkerPool();
-
-  worker_pool_ = g_sequenced_worker_pool->GetTaskRunnerWithShutdownBehavior(
-      SequencedWorkerPool::CONTINUE_ON_SHUTDOWN);
+  worker_pool_ = g_sequenced_worker_pool.Get().GetTaskRunner();
 
   index_.reset(new SimpleIndex(
       base::ThreadTaskRunnerHandle::Get(),
@@ -724,9 +733,9 @@
   callback.Run(result);
 }
 
+// static
 void SimpleBackendImpl::FlushWorkerPoolForTesting() {
-  if (g_sequenced_worker_pool)
-    g_sequenced_worker_pool->FlushForTesting();
+  g_sequenced_worker_pool.Get().FlushForTesting();
 }
 
 }  // namespace disk_cache
diff --git a/net/http/http_cache.cc b/net/http/http_cache.cc
index fb6f40c..5260410 100644
--- a/net/http/http_cache.cc
+++ b/net/http/http_cache.cc
@@ -212,7 +212,7 @@
 
   // Implements the bulk of HttpCache::WriteMetadata.
   void Write(const GURL& url,
-             double expected_response_time,
+             base::Time expected_response_time,
              IOBuffer* buf,
              int buf_len);
 
@@ -225,13 +225,13 @@
   bool verified_;
   scoped_refptr<IOBuffer> buf_;
   int buf_len_;
-  double expected_response_time_;
+  base::Time expected_response_time_;
   HttpRequestInfo request_info_;
   DISALLOW_COPY_AND_ASSIGN(MetadataWriter);
 };
 
 void HttpCache::MetadataWriter::Write(const GURL& url,
-                                      double expected_response_time,
+                                      base::Time expected_response_time,
                                       IOBuffer* buf,
                                       int buf_len) {
   DCHECK_GT(buf_len, 0);
@@ -261,7 +261,7 @@
 
   const HttpResponseInfo* response_info = transaction_->GetResponseInfo();
   DCHECK(response_info->was_cached);
-  if (response_info->response_time.ToDoubleT() != expected_response_time_)
+  if (response_info->response_time != expected_response_time_)
     return SelfDestroy();
 
   result = transaction_->WriteMetadata(
@@ -579,7 +579,7 @@
 
 void HttpCache::WriteMetadata(const GURL& url,
                               RequestPriority priority,
-                              double expected_response_time,
+                              base::Time expected_response_time,
                               IOBuffer* buf,
                               int buf_len) {
   if (!buf_len)
diff --git a/net/http/http_cache.h b/net/http/http_cache.h
index 4f75ac5..33b89f382 100644
--- a/net/http/http_cache.h
+++ b/net/http/http_cache.h
@@ -178,7 +178,7 @@
   // be performed asynchronously without any completion notification.
   void WriteMetadata(const GURL& url,
                      RequestPriority priority,
-                     double expected_response_time,
+                     base::Time expected_response_time,
                      IOBuffer* buf,
                      int buf_len);
 
diff --git a/net/http/http_cache_unittest.cc b/net/http/http_cache_unittest.cc
index 66349b6..189e952e 100644
--- a/net/http/http_cache_unittest.cc
+++ b/net/http/http_cache_unittest.cc
@@ -6444,7 +6444,7 @@
 
   // Trivial call.
   cache.http_cache()->WriteMetadata(GURL("foo"), net::DEFAULT_PRIORITY,
-                                    Time::Now().ToDoubleT(), NULL, 0);
+                                    Time::Now(), NULL, 0);
 
   // Write meta data to the same entry.
   scoped_refptr<net::IOBufferWithSize> buf(new net::IOBufferWithSize(50));
@@ -6452,7 +6452,7 @@
   base::strlcpy(buf->data(), "Hi there", buf->size());
   cache.http_cache()->WriteMetadata(
       GURL(kSimpleGET_Transaction.url), net::DEFAULT_PRIORITY,
-      response.response_time.ToDoubleT(), buf.get(), buf->size());
+      response.response_time, buf.get(), buf->size());
 
   // Release the buffer before the operation takes place.
   buf = NULL;
@@ -6487,9 +6487,9 @@
   base::strlcpy(buf->data(), "Hi there", buf->size());
   base::Time expected_time = response.response_time -
                              base::TimeDelta::FromMilliseconds(20);
-  cache.http_cache()->WriteMetadata(
-      GURL(kSimpleGET_Transaction.url), net::DEFAULT_PRIORITY,
-      expected_time.ToDoubleT(), buf.get(), buf->size());
+  cache.http_cache()->WriteMetadata(GURL(kSimpleGET_Transaction.url),
+                                    net::DEFAULT_PRIORITY, expected_time,
+                                    buf.get(), buf->size());
 
   // Makes sure we finish pending operations.
   base::MessageLoop::current()->RunUntilIdle();
@@ -6520,7 +6520,7 @@
   base::strlcpy(buf->data(), "Hi there", buf->size());
   cache.http_cache()->WriteMetadata(
       GURL(kTypicalGET_Transaction.url), net::DEFAULT_PRIORITY,
-      response.response_time.ToDoubleT(), buf.get(), buf->size());
+      response.response_time, buf.get(), buf->size());
 
   // Makes sure we finish pending operations.
   base::MessageLoop::current()->RunUntilIdle();
diff --git a/net/http/http_network_session.cc b/net/http/http_network_session.cc
index ff87956c..276b3c8 100644
--- a/net/http/http_network_session.cc
+++ b/net/http/http_network_session.cc
@@ -90,6 +90,7 @@
       use_alternate_protocols(false),
       alternate_protocol_probability_threshold(1),
       enable_quic(false),
+      enable_quic_for_proxies(false),
       enable_quic_port_selection(true),
       quic_always_require_handshake_confirmation(false),
       quic_disable_connection_pooling(false),
@@ -265,6 +266,7 @@
   base::DictionaryValue* dict = new base::DictionaryValue();
   dict->Set("sessions", quic_stream_factory_.QuicStreamFactoryInfoToValue());
   dict->SetBoolean("quic_enabled", params_.enable_quic);
+  dict->SetBoolean("quic_enabled_for_proxies", params_.enable_quic_for_proxies);
   dict->SetBoolean("enable_quic_port_selection",
                    params_.enable_quic_port_selection);
   base::ListValue* connection_options = new base::ListValue;
diff --git a/net/http/http_network_session.h b/net/http/http_network_session.h
index 3216c6cb..f290f55 100644
--- a/net/http/http_network_session.h
+++ b/net/http/http_network_session.h
@@ -113,6 +113,7 @@
     double alternate_protocol_probability_threshold;
 
     bool enable_quic;
+    bool enable_quic_for_proxies;
     bool enable_quic_port_selection;
     bool quic_always_require_handshake_confirmation;
     bool quic_disable_connection_pooling;
diff --git a/net/http/http_network_transaction.cc b/net/http/http_network_transaction.cc
index e16d7c6..5ce7572 100644
--- a/net/http/http_network_transaction.cc
+++ b/net/http/http_network_transaction.cc
@@ -569,8 +569,8 @@
   OnIOComplete(ERR_HTTPS_PROXY_TUNNEL_RESPONSE);
 }
 
-bool HttpNetworkTransaction::is_https_request() const {
-  return request_->url.SchemeIs("https");
+bool HttpNetworkTransaction::IsSecureRequest() const {
+  return request_->url.SchemeIsSecure();
 }
 
 bool HttpNetworkTransaction::UsingHttpProxyWithoutTunnel() const {
@@ -969,7 +969,7 @@
   } else if (result == ERR_SSL_CLIENT_AUTH_CERT_NEEDED) {
     // TODO(wtc): Need a test case for this code path!
     DCHECK(stream_.get());
-    DCHECK(is_https_request());
+    DCHECK(IsSecureRequest());
     response_.cert_request_info = new SSLCertRequestInfo;
     stream_->GetSSLCertRequestInfo(response_.cert_request_info.get());
     result = HandleCertificateRequest(result);
@@ -1050,7 +1050,7 @@
   if (rv != OK)
     return rv;
 
-  if (is_https_request())
+  if (IsSecureRequest())
     stream_->GetSSLInfo(&response_.ssl_info);
 
   headers_valid_ = true;
diff --git a/net/http/http_network_transaction.h b/net/http/http_network_transaction.h
index 2c4bd93..c098d75 100644
--- a/net/http/http_network_transaction.h
+++ b/net/http/http_network_transaction.h
@@ -142,7 +142,7 @@
     STATE_NONE
   };
 
-  bool is_https_request() const;
+  bool IsSecureRequest() const;
 
   // Returns true if the request is using an HTTP(S) proxy without being
   // tunneled via the CONNECT method.
diff --git a/net/http/http_network_transaction_unittest.cc b/net/http/http_network_transaction_unittest.cc
index c3eab76..6e1ad43 100644
--- a/net/http/http_network_transaction_unittest.cc
+++ b/net/http/http_network_transaction_unittest.cc
@@ -12554,7 +12554,7 @@
     return false;
   }
 
-  void GetSSLInfo(SSLInfo* ssl_info) override { NOTREACHED(); }
+  void GetSSLInfo(SSLInfo* ssl_info) override {}
 
   void GetSSLCertRequestInfo(SSLCertRequestInfo* cert_request_info) override {
     NOTREACHED();
diff --git a/net/http/http_proxy_client_socket_pool.cc b/net/http/http_proxy_client_socket_pool.cc
index b97c957c..9c8a5317 100644
--- a/net/http/http_proxy_client_socket_pool.cc
+++ b/net/http/http_proxy_client_socket_pool.cc
@@ -128,10 +128,6 @@
 }
 
 void HttpProxyConnectJob::OnIOComplete(int result) {
-  // TODO(pkasting): Remove ScopedTracker below once crbug.com/455884 is fixed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "455884 HttpProxyConnectJob::OnIOComplete"));
   int rv = DoLoop(result);
   if (rv != ERR_IO_PENDING) {
     NotifyProxyDelegateOfCompletion(rv);
diff --git a/net/http/http_stream_factory_impl_job.cc b/net/http/http_stream_factory_impl_job.cc
index 8729dfbb..510d64ef 100644
--- a/net/http/http_stream_factory_impl_job.cc
+++ b/net/http/http_stream_factory_impl_job.cc
@@ -437,14 +437,14 @@
 }
 
 void HttpStreamFactoryImpl::Job::OnIOComplete(int result) {
-  // TODO(pkasting): Remove ScopedTracker below once crbug.com/455884 is fixed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "455884 HttpStreamFactoryImpl::Job::OnIOComplete"));
   RunLoop(result);
 }
 
 int HttpStreamFactoryImpl::Job::RunLoop(int result) {
+  // TODO(pkasting): Remove ScopedTracker below once crbug.com/455884 is fixed.
+  tracked_objects::ScopedTracker tracking_profile(
+      FROM_HERE_WITH_EXPLICIT_FUNCTION(
+          "455884 HttpStreamFactoryImpl::Job::RunLoop"));
   result = DoLoop(result);
 
   if (result == ERR_IO_PENDING)
@@ -621,6 +621,10 @@
 }
 
 int HttpStreamFactoryImpl::Job::DoStart() {
+  // TODO(pkasting): Remove ScopedTracker below once crbug.com/455884 is fixed.
+  tracked_objects::ScopedTracker tracking_profile(
+      FROM_HERE_WITH_EXPLICIT_FUNCTION(
+          "455884 HttpStreamFactoryImpl::Job::DoStart"));
   origin_ = HostPortPair::FromURL(request_info_.url);
   origin_url_ = stream_factory_->ApplyHostMappingRules(
       request_info_.url, &origin_);
@@ -651,6 +655,10 @@
 }
 
 int HttpStreamFactoryImpl::Job::DoResolveProxy() {
+  // TODO(pkasting): Remove ScopedTracker below once crbug.com/455884 is fixed.
+  tracked_objects::ScopedTracker tracking_profile(
+      FROM_HERE_WITH_EXPLICIT_FUNCTION(
+          "455884 HttpStreamFactoryImpl::Job::DoResolveProxy"));
   DCHECK(!pac_request_);
   DCHECK(session_);
 
@@ -667,14 +675,23 @@
 }
 
 int HttpStreamFactoryImpl::Job::DoResolveProxyComplete(int result) {
+  // TODO(pkasting): Remove ScopedTracker below once crbug.com/455884 is fixed.
+  tracked_objects::ScopedTracker tracking_profile(
+      FROM_HERE_WITH_EXPLICIT_FUNCTION(
+          "455884 HttpStreamFactoryImpl::Job::DoResolveProxyComplete"));
   pac_request_ = NULL;
 
   if (result == OK) {
     // Remove unsupported proxies from the list.
-    proxy_info_.RemoveProxiesWithoutScheme(
-        ProxyServer::SCHEME_DIRECT | ProxyServer::SCHEME_QUIC |
-        ProxyServer::SCHEME_HTTP | ProxyServer::SCHEME_HTTPS |
-        ProxyServer::SCHEME_SOCKS4 | ProxyServer::SCHEME_SOCKS5);
+    int supported_proxies =
+        ProxyServer::SCHEME_DIRECT | ProxyServer::SCHEME_HTTP |
+        ProxyServer::SCHEME_HTTPS | ProxyServer::SCHEME_SOCKS4 |
+        ProxyServer::SCHEME_SOCKS5;
+
+    if (session_->params().enable_quic_for_proxies)
+      supported_proxies |= ProxyServer::SCHEME_QUIC;
+
+    proxy_info_.RemoveProxiesWithoutScheme(supported_proxies);
 
     if (proxy_info_.is_empty()) {
       // No proxies/direct to choose from. This happens when we don't support
@@ -735,6 +752,10 @@
 }
 
 int HttpStreamFactoryImpl::Job::DoInitConnection() {
+  // TODO(pkasting): Remove ScopedTracker below once crbug.com/455884 is fixed.
+  tracked_objects::ScopedTracker tracking_profile(
+      FROM_HERE_WITH_EXPLICIT_FUNCTION(
+          "455884 HttpStreamFactoryImpl::Job::DoInitConnection"));
   DCHECK(!blocking_job_);
   DCHECK(!connection_->is_initialized());
   DCHECK(proxy_info_.proxy_server().is_valid());
@@ -747,11 +768,14 @@
   if (ShouldForceQuic())
     using_quic_ = true;
 
-  if (proxy_info_.is_quic())
+  DCHECK(!using_quic_ || session_->params().enable_quic);
+
+  if (proxy_info_.is_quic()) {
     using_quic_ = true;
+    DCHECK(session_->params().enable_quic_for_proxies);
+  }
 
   if (using_quic_) {
-    DCHECK(session_->params().enable_quic);
     if (proxy_info_.is_quic() && !request_info_.url.SchemeIs("http")) {
       NOTREACHED();
       // TODO(rch): support QUIC proxies for HTTPS urls.
@@ -879,6 +903,10 @@
 }
 
 int HttpStreamFactoryImpl::Job::DoInitConnectionComplete(int result) {
+  // TODO(pkasting): Remove ScopedTracker below once crbug.com/455884 is fixed.
+  tracked_objects::ScopedTracker tracking_profile(
+      FROM_HERE_WITH_EXPLICIT_FUNCTION(
+          "455884 HttpStreamFactoryImpl::Job::DoInitConnectionComplete"));
   if (IsPreconnecting()) {
     if (using_quic_)
       return result;
@@ -903,6 +931,13 @@
     return OK;
   }
 
+  if (proxy_info_.is_quic() && using_quic_ &&
+      (result == ERR_QUIC_PROTOCOL_ERROR ||
+       result == ERR_QUIC_HANDSHAKE_FAILED)) {
+    using_quic_ = false;
+    return ReconsiderProxyAfterError(result);
+  }
+
   // TODO(willchan): Make this a bit more exact. Maybe there are recoverable
   // errors, such as ignoring certificate errors for Alternate-Protocol.
   if (result < 0 && waiting_job_) {
@@ -1058,6 +1093,10 @@
 }
 
 int HttpStreamFactoryImpl::Job::DoCreateStream() {
+  // TODO(pkasting): Remove ScopedTracker below once crbug.com/455884 is fixed.
+  tracked_objects::ScopedTracker tracking_profile(
+      FROM_HERE_WITH_EXPLICIT_FUNCTION(
+          "455884 HttpStreamFactoryImpl::Job::DoCreateStream"));
   DCHECK(connection_->socket() || existing_spdy_session_.get() || using_quic_);
 
   next_state_ = STATE_CREATE_STREAM_COMPLETE;
@@ -1151,6 +1190,10 @@
 }
 
 int HttpStreamFactoryImpl::Job::DoCreateStreamComplete(int result) {
+  // TODO(pkasting): Remove ScopedTracker below once crbug.com/455884 is fixed.
+  tracked_objects::ScopedTracker tracking_profile(
+      FROM_HERE_WITH_EXPLICIT_FUNCTION(
+          "455884 HttpStreamFactoryImpl::Job::DoCreateStreamComplete"));
   if (result < 0)
     return result;
 
@@ -1161,6 +1204,10 @@
 }
 
 int HttpStreamFactoryImpl::Job::DoRestartTunnelAuth() {
+  // TODO(pkasting): Remove ScopedTracker below once crbug.com/455884 is fixed.
+  tracked_objects::ScopedTracker tracking_profile(
+      FROM_HERE_WITH_EXPLICIT_FUNCTION(
+          "455884 HttpStreamFactoryImpl::Job::DoRestartTunnelAuth"));
   next_state_ = STATE_RESTART_TUNNEL_AUTH_COMPLETE;
   ProxyClientSocket* proxy_socket =
       static_cast<ProxyClientSocket*>(connection_->socket());
@@ -1168,6 +1215,10 @@
 }
 
 int HttpStreamFactoryImpl::Job::DoRestartTunnelAuthComplete(int result) {
+  // TODO(pkasting): Remove ScopedTracker below once crbug.com/455884 is fixed.
+  tracked_objects::ScopedTracker tracking_profile(
+      FROM_HERE_WITH_EXPLICIT_FUNCTION(
+          "455884 HttpStreamFactoryImpl::Job::DoRestartTunnelAuthComplete"));
   if (result == ERR_PROXY_AUTH_REQUESTED)
     return result;
 
@@ -1313,6 +1364,8 @@
     case ERR_PROXY_CERTIFICATE_INVALID:
     // This can happen when trying to talk SSL to a non-SSL server (Like a
     // captive portal).
+    case ERR_QUIC_PROTOCOL_ERROR:
+    case ERR_QUIC_HANDSHAKE_FAILED:
     case ERR_SSL_PROTOCOL_ERROR:
       break;
     case ERR_SOCKS_CONNECTION_HOST_UNREACHABLE:
diff --git a/net/http/http_stream_factory_impl_unittest.cc b/net/http/http_stream_factory_impl_unittest.cc
index 505acdf..e55aff5 100644
--- a/net/http/http_stream_factory_impl_unittest.cc
+++ b/net/http/http_stream_factory_impl_unittest.cc
@@ -616,6 +616,70 @@
   EXPECT_TRUE(iter != retry_info.end());
 }
 
+TEST_P(HttpStreamFactoryTest, UnreachableQuicProxyMarkedAsBad) {
+  for (int i = 1; i <= 2; i++) {
+    int mock_error =
+        i == 1 ? ERR_QUIC_PROTOCOL_ERROR : ERR_QUIC_HANDSHAKE_FAILED;
+
+    scoped_ptr<ProxyService> proxy_service;
+    proxy_service.reset(
+        ProxyService::CreateFixedFromPacResult("QUIC bad:99; DIRECT"));
+
+    HttpNetworkSession::Params params;
+    params.enable_quic = true;
+    params.enable_quic_for_proxies = true;
+    scoped_refptr<SSLConfigServiceDefaults> ssl_config_service(
+        new SSLConfigServiceDefaults);
+    HttpServerPropertiesImpl http_server_properties;
+    MockClientSocketFactory socket_factory;
+    params.client_socket_factory = &socket_factory;
+    MockHostResolver host_resolver;
+    params.host_resolver = &host_resolver;
+    TransportSecurityState transport_security_state;
+    params.transport_security_state = &transport_security_state;
+    params.proxy_service = proxy_service.get();
+    params.ssl_config_service = ssl_config_service.get();
+    params.http_server_properties = http_server_properties.GetWeakPtr();
+
+    scoped_refptr<HttpNetworkSession> session;
+    session = new HttpNetworkSession(params);
+    session->quic_stream_factory()->set_require_confirmation(false);
+
+    StaticSocketDataProvider socket_data1;
+    socket_data1.set_connect_data(MockConnect(ASYNC, mock_error));
+    socket_factory.AddSocketDataProvider(&socket_data1);
+
+    // Second connection attempt succeeds.
+    StaticSocketDataProvider socket_data2;
+    socket_data2.set_connect_data(MockConnect(ASYNC, OK));
+    socket_factory.AddSocketDataProvider(&socket_data2);
+
+    // Now request a stream. It should succeed using the second proxy in the
+    // list.
+    HttpRequestInfo request_info;
+    request_info.method = "GET";
+    request_info.url = GURL("http://www.google.com");
+
+    SSLConfig ssl_config;
+    StreamRequestWaiter waiter;
+    scoped_ptr<HttpStreamRequest> request(
+        session->http_stream_factory()->RequestStream(
+            request_info, DEFAULT_PRIORITY, ssl_config, ssl_config, &waiter,
+            BoundNetLog()));
+    waiter.WaitForStream();
+
+    // The proxy that failed should now be known to the proxy_service as bad.
+    const ProxyRetryInfoMap& retry_info =
+        session->proxy_service()->proxy_retry_info();
+    // proxy_headers_handler.proxy_info_used.proxy_retry_info();
+    EXPECT_EQ(1u, retry_info.size()) << i;
+    // EXPECT_TRUE(waiter.used_proxy_info().is_direct());
+
+    ProxyRetryInfoMap::const_iterator iter = retry_info.find("quic://bad:99");
+    EXPECT_TRUE(iter != retry_info.end()) << i;
+  }
+}
+
 TEST_P(HttpStreamFactoryTest, PrivacyModeDisablesChannelId) {
   SpdySessionDependencies session_deps(
       GetParam(), ProxyService::CreateDirect());
diff --git a/net/http/http_stream_parser_unittest.cc b/net/http/http_stream_parser_unittest.cc
index 01d4e12..a30cb00 100644
--- a/net/http/http_stream_parser_unittest.cc
+++ b/net/http/http_stream_parser_unittest.cc
@@ -40,6 +40,24 @@
 const size_t kMaxPayloadSize =
     kOutputSize - HttpStreamParser::kChunkHeaderFooterSize;
 
+// Helper method to create a connected ClientSocketHandle using |data|.
+// Modifies |data|.
+scoped_ptr<ClientSocketHandle> CreateConnectedSocketHandle(
+    DeterministicSocketData* data) {
+  data->set_connect_data(MockConnect(SYNCHRONOUS, OK));
+
+  scoped_ptr<DeterministicMockTCPClientSocket> transport(
+      new DeterministicMockTCPClientSocket(nullptr, data));
+  data->set_delegate(transport->AsWeakPtr());
+
+  TestCompletionCallback callback;
+  EXPECT_EQ(OK, transport->Connect(callback.callback()));
+
+  scoped_ptr<ClientSocketHandle> socket_handle(new ClientSocketHandle);
+  socket_handle->SetSocket(transport.Pass());
+  return socket_handle.Pass();
+}
+
 // The empty payload is how the last chunk is encoded.
 TEST(HttpStreamParser, EncodeChunk_EmptyPayload) {
   char output[kOutputSize];
@@ -184,11 +202,174 @@
 }
 
 // Test to ensure the HttpStreamParser state machine does not get confused
+// when sending a request with a chunked body with only one chunk that becomes
+// available asynchronously.
+TEST(HttpStreamParser, AsyncSingleChunkAndAsyncSocket) {
+  static const char kChunk[] = "Chunk";
+
+  MockWrite writes[] = {
+      MockWrite(ASYNC, 0,
+                "GET /one.html HTTP/1.1\r\n"
+                "Transfer-Encoding: chunked\r\n\r\n"),
+      MockWrite(ASYNC, 1, "5\r\nChunk\r\n"),
+      MockWrite(ASYNC, 2, "0\r\n\r\n"),
+  };
+
+  // The size of the response body, as reflected in the Content-Length of the
+  // MockRead below.
+  static const int kBodySize = 8;
+
+  MockRead reads[] = {
+      MockRead(ASYNC, 3, "HTTP/1.1 200 OK\r\n"),
+      MockRead(ASYNC, 4, "Content-Length: 8\r\n\r\n"),
+      MockRead(ASYNC, 5, "one.html"),
+      MockRead(SYNCHRONOUS, 0, 6),  // EOF
+  };
+
+  ChunkedUploadDataStream upload_stream(0);
+  ASSERT_EQ(OK, upload_stream.Init(TestCompletionCallback().callback()));
+
+  DeterministicSocketData data(reads, arraysize(reads), writes,
+                               arraysize(writes));
+  scoped_ptr<ClientSocketHandle> socket_handle =
+      CreateConnectedSocketHandle(&data);
+
+  HttpRequestInfo request_info;
+  request_info.method = "GET";
+  request_info.url = GURL("http://localhost");
+  request_info.upload_data_stream = &upload_stream;
+
+  scoped_refptr<GrowableIOBuffer> read_buffer(new GrowableIOBuffer);
+  HttpStreamParser parser(socket_handle.get(), &request_info, read_buffer.get(),
+                          BoundNetLog());
+
+  HttpRequestHeaders request_headers;
+  request_headers.SetHeader("Transfer-Encoding", "chunked");
+
+  HttpResponseInfo response_info;
+  TestCompletionCallback callback;
+  // This will attempt to Write() the initial request and headers, which will
+  // complete asynchronously.
+  ASSERT_EQ(ERR_IO_PENDING,
+            parser.SendRequest("GET /one.html HTTP/1.1\r\n", request_headers,
+                               &response_info, callback.callback()));
+
+  // Complete the initial request write.
+  data.RunFor(1);
+  ASSERT_FALSE(callback.have_result());
+
+  // Now append the only chunk.
+  upload_stream.AppendData(kChunk, arraysize(kChunk) - 1, true);
+  // Write the chunk.
+  data.RunFor(1);
+  ASSERT_FALSE(callback.have_result());
+
+  // Write the trailer.
+  data.RunFor(1);
+  ASSERT_TRUE(callback.have_result());
+  ASSERT_EQ(OK, callback.WaitForResult());
+
+  // Attempt to read the response status and the response headers.
+  ASSERT_EQ(ERR_IO_PENDING, parser.ReadResponseHeaders(callback.callback()));
+  data.RunFor(2);
+  ASSERT_TRUE(callback.have_result());
+  ASSERT_GT(callback.WaitForResult(), 0);
+
+  // Finally, attempt to read the response body.
+  scoped_refptr<IOBuffer> body_buffer(new IOBuffer(kBodySize));
+  ASSERT_EQ(ERR_IO_PENDING,
+            parser.ReadResponseBody(body_buffer.get(), kBodySize,
+                                    callback.callback()));
+  data.RunFor(1);
+  ASSERT_TRUE(callback.have_result());
+  ASSERT_EQ(kBodySize, callback.WaitForResult());
+}
+
+// Test to ensure the HttpStreamParser state machine does not get confused
+// when sending a request with a chunked body with only one chunk that is
+// available synchronously.
+TEST(HttpStreamParser, SyncSingleChunkAndAsyncSocket) {
+  static const char kChunk[] = "Chunk";
+
+  MockWrite writes[] = {
+      MockWrite(ASYNC, 0,
+                "GET /one.html HTTP/1.1\r\n"
+                "Transfer-Encoding: chunked\r\n\r\n"),
+      MockWrite(ASYNC, 1, "5\r\nChunk\r\n"),
+      MockWrite(ASYNC, 2, "0\r\n\r\n"),
+  };
+
+  // The size of the response body, as reflected in the Content-Length of the
+  // MockRead below.
+  static const int kBodySize = 8;
+
+  MockRead reads[] = {
+      MockRead(ASYNC, 3, "HTTP/1.1 200 OK\r\n"),
+      MockRead(ASYNC, 4, "Content-Length: 8\r\n\r\n"),
+      MockRead(ASYNC, 5, "one.html"),
+      MockRead(SYNCHRONOUS, 0, 6),  // EOF
+  };
+
+  ChunkedUploadDataStream upload_stream(0);
+  ASSERT_EQ(OK, upload_stream.Init(TestCompletionCallback().callback()));
+  // Append the only chunk.
+  upload_stream.AppendData(kChunk, arraysize(kChunk) - 1, true);
+
+  DeterministicSocketData data(reads, arraysize(reads), writes,
+                               arraysize(writes));
+  scoped_ptr<ClientSocketHandle> socket_handle =
+      CreateConnectedSocketHandle(&data);
+
+  HttpRequestInfo request_info;
+  request_info.method = "GET";
+  request_info.url = GURL("http://localhost");
+  request_info.upload_data_stream = &upload_stream;
+
+  scoped_refptr<GrowableIOBuffer> read_buffer(new GrowableIOBuffer);
+  HttpStreamParser parser(socket_handle.get(), &request_info, read_buffer.get(),
+                          BoundNetLog());
+
+  HttpRequestHeaders request_headers;
+  request_headers.SetHeader("Transfer-Encoding", "chunked");
+
+  HttpResponseInfo response_info;
+  TestCompletionCallback callback;
+  // This will attempt to Write() the initial request and headers, which will
+  // complete asynchronously.
+  ASSERT_EQ(ERR_IO_PENDING,
+            parser.SendRequest("GET /one.html HTTP/1.1\r\n", request_headers,
+                               &response_info, callback.callback()));
+
+  // Write the request and the only chunk.
+  data.RunFor(2);
+
+  // Write the trailer.
+  data.RunFor(1);
+  ASSERT_TRUE(callback.have_result());
+  ASSERT_EQ(OK, callback.WaitForResult());
+
+  // Attempt to read the response status and the response headers.
+  ASSERT_EQ(ERR_IO_PENDING, parser.ReadResponseHeaders(callback.callback()));
+  data.RunFor(2);
+  ASSERT_TRUE(callback.have_result());
+  ASSERT_GT(callback.WaitForResult(), 0);
+
+  // Finally, attempt to read the response body.
+  scoped_refptr<IOBuffer> body_buffer(new IOBuffer(kBodySize));
+  ASSERT_EQ(ERR_IO_PENDING,
+            parser.ReadResponseBody(body_buffer.get(), kBodySize,
+                                    callback.callback()));
+  data.RunFor(1);
+  ASSERT_TRUE(callback.have_result());
+  ASSERT_EQ(kBodySize, callback.WaitForResult());
+}
+
+// Test to ensure the HttpStreamParser state machine does not get confused
 // when sending a request with a chunked body, where chunks become available
 // asynchronously, over a socket where writes may also complete
 // asynchronously.
 // This is a regression test for http://crbug.com/132243
-TEST(HttpStreamParser, AsyncChunkAndAsyncSocket) {
+TEST(HttpStreamParser, AsyncChunkAndAsyncSocketWithMultipleChunks) {
   // The chunks that will be written in the request, as reflected in the
   // MockWrites below.
   static const char kChunk1[] = "Chunk 1";
@@ -196,15 +377,13 @@
   static const char kChunk3[] = "Test 3";
 
   MockWrite writes[] = {
-    MockWrite(ASYNC, 0,
-              "GET /one.html HTTP/1.1\r\n"
-              "Host: localhost\r\n"
-              "Transfer-Encoding: chunked\r\n"
-              "Connection: keep-alive\r\n\r\n"),
-    MockWrite(ASYNC, 1, "7\r\nChunk 1\r\n"),
-    MockWrite(ASYNC, 2, "8\r\nChunky 2\r\n"),
-    MockWrite(ASYNC, 3, "6\r\nTest 3\r\n"),
-    MockWrite(ASYNC, 4, "0\r\n\r\n"),
+      MockWrite(ASYNC, 0,
+                "GET /one.html HTTP/1.1\r\n"
+                "Transfer-Encoding: chunked\r\n\r\n"),
+      MockWrite(ASYNC, 1, "7\r\nChunk 1\r\n"),
+      MockWrite(ASYNC, 2, "8\r\nChunky 2\r\n"),
+      MockWrite(ASYNC, 3, "6\r\nTest 3\r\n"),
+      MockWrite(ASYNC, 4, "0\r\n\r\n"),
   };
 
   // The size of the response body, as reflected in the Content-Length of the
@@ -222,26 +401,14 @@
   upload_stream.AppendData(kChunk1, arraysize(kChunk1) - 1, false);
   ASSERT_EQ(OK, upload_stream.Init(TestCompletionCallback().callback()));
 
-  DeterministicSocketData data(reads, arraysize(reads),
-                               writes, arraysize(writes));
-  data.set_connect_data(MockConnect(SYNCHRONOUS, OK));
-
-  scoped_ptr<DeterministicMockTCPClientSocket> transport(
-      new DeterministicMockTCPClientSocket(NULL, &data));
-  data.set_delegate(transport->AsWeakPtr());
-
-  TestCompletionCallback callback;
-  int rv = transport->Connect(callback.callback());
-  rv = callback.GetResult(rv);
-  ASSERT_EQ(OK, rv);
-
-  scoped_ptr<ClientSocketHandle> socket_handle(new ClientSocketHandle);
-  socket_handle->SetSocket(transport.Pass());
+  DeterministicSocketData data(reads, arraysize(reads), writes,
+                               arraysize(writes));
+  scoped_ptr<ClientSocketHandle> socket_handle =
+      CreateConnectedSocketHandle(&data);
 
   HttpRequestInfo request_info;
   request_info.method = "GET";
   request_info.url = GURL("http://localhost");
-  request_info.load_flags = LOAD_NORMAL;
   request_info.upload_data_stream = &upload_stream;
 
   scoped_refptr<GrowableIOBuffer> read_buffer(new GrowableIOBuffer);
@@ -249,16 +416,15 @@
       socket_handle.get(), &request_info, read_buffer.get(), BoundNetLog());
 
   HttpRequestHeaders request_headers;
-  request_headers.SetHeader("Host", "localhost");
   request_headers.SetHeader("Transfer-Encoding", "chunked");
-  request_headers.SetHeader("Connection", "keep-alive");
 
   HttpResponseInfo response_info;
+  TestCompletionCallback callback;
   // This will attempt to Write() the initial request and headers, which will
   // complete asynchronously.
-  rv = parser.SendRequest("GET /one.html HTTP/1.1\r\n", request_headers,
-                          &response_info, callback.callback());
-  ASSERT_EQ(ERR_IO_PENDING, rv);
+  ASSERT_EQ(ERR_IO_PENDING,
+            parser.SendRequest("GET /one.html HTTP/1.1\r\n", request_headers,
+                               &response_info, callback.callback()));
 
   // Complete the initial request write. Additionally, this should enqueue the
   // first chunk.
@@ -294,32 +460,174 @@
   // Finalize writing the trailer.
   data.RunFor(1);
   ASSERT_TRUE(callback.have_result());
-
-  // Warning: This will hang if the callback doesn't already have a result,
-  // due to the deterministic socket provider. Do not remove the above
-  // ASSERT_TRUE, which will avoid this hang.
-  rv = callback.WaitForResult();
-  ASSERT_EQ(OK, rv);
+  ASSERT_EQ(OK, callback.WaitForResult());
 
   // Attempt to read the response status and the response headers.
-  rv = parser.ReadResponseHeaders(callback.callback());
-  ASSERT_EQ(ERR_IO_PENDING, rv);
+  ASSERT_EQ(ERR_IO_PENDING, parser.ReadResponseHeaders(callback.callback()));
   data.RunFor(2);
-
   ASSERT_TRUE(callback.have_result());
-  rv = callback.WaitForResult();
-  ASSERT_GT(rv, 0);
+  ASSERT_GT(callback.WaitForResult(), 0);
 
   // Finally, attempt to read the response body.
   scoped_refptr<IOBuffer> body_buffer(new IOBuffer(kBodySize));
-  rv = parser.ReadResponseBody(
-      body_buffer.get(), kBodySize, callback.callback());
-  ASSERT_EQ(ERR_IO_PENDING, rv);
+  ASSERT_EQ(ERR_IO_PENDING,
+            parser.ReadResponseBody(body_buffer.get(), kBodySize,
+                                    callback.callback()));
   data.RunFor(1);
-
   ASSERT_TRUE(callback.have_result());
-  rv = callback.WaitForResult();
-  ASSERT_EQ(kBodySize, rv);
+  ASSERT_EQ(kBodySize, callback.WaitForResult());
+}
+
+// Test to ensure the HttpStreamParser state machine does not get confused
+// when there's only one "chunk" with 0 bytes, and is received from the
+// UploadStream only after sending the request headers successfully.
+TEST(HttpStreamParser, AsyncEmptyChunkedUpload) {
+  MockWrite writes[] = {
+      MockWrite(ASYNC, 0,
+                "GET /one.html HTTP/1.1\r\n"
+                "Transfer-Encoding: chunked\r\n\r\n"),
+      MockWrite(ASYNC, 1, "0\r\n\r\n"),
+  };
+
+  // The size of the response body, as reflected in the Content-Length of the
+  // MockRead below.
+  const int kBodySize = 8;
+
+  MockRead reads[] = {
+      MockRead(ASYNC, 2, "HTTP/1.1 200 OK\r\n"),
+      MockRead(ASYNC, 3, "Content-Length: 8\r\n\r\n"),
+      MockRead(ASYNC, 4, "one.html"),
+      MockRead(SYNCHRONOUS, 0, 5),  // EOF
+  };
+
+  ChunkedUploadDataStream upload_stream(0);
+  ASSERT_EQ(OK, upload_stream.Init(TestCompletionCallback().callback()));
+
+  DeterministicSocketData data(reads, arraysize(reads), writes,
+                               arraysize(writes));
+  scoped_ptr<ClientSocketHandle> socket_handle =
+      CreateConnectedSocketHandle(&data);
+
+  HttpRequestInfo request_info;
+  request_info.method = "GET";
+  request_info.url = GURL("http://localhost");
+  request_info.upload_data_stream = &upload_stream;
+
+  scoped_refptr<GrowableIOBuffer> read_buffer(new GrowableIOBuffer);
+  HttpStreamParser parser(socket_handle.get(), &request_info, read_buffer.get(),
+                          BoundNetLog());
+
+  HttpRequestHeaders request_headers;
+  request_headers.SetHeader("Transfer-Encoding", "chunked");
+
+  HttpResponseInfo response_info;
+  TestCompletionCallback callback;
+  // This will attempt to Write() the initial request and headers, which will
+  // complete asynchronously.
+  ASSERT_EQ(ERR_IO_PENDING,
+            parser.SendRequest("GET /one.html HTTP/1.1\r\n", request_headers,
+                               &response_info, callback.callback()));
+
+  // Complete writing the request headers.
+  data.RunFor(1);
+  ASSERT_FALSE(callback.have_result());
+
+  // Now append the terminal 0-byte "chunk".
+  upload_stream.AppendData(nullptr, 0, true);
+  ASSERT_FALSE(callback.have_result());
+
+  // Finalize writing the trailer.
+  data.RunFor(1);
+  ASSERT_TRUE(callback.have_result());
+  ASSERT_EQ(OK, callback.WaitForResult());
+
+  // Attempt to read the response status and the response headers.
+  ASSERT_EQ(ERR_IO_PENDING, parser.ReadResponseHeaders(callback.callback()));
+  data.RunFor(2);
+  ASSERT_TRUE(callback.have_result());
+  ASSERT_GT(callback.WaitForResult(), 0);
+
+  // Finally, attempt to read the response body.
+  scoped_refptr<IOBuffer> body_buffer(new IOBuffer(kBodySize));
+  ASSERT_EQ(ERR_IO_PENDING,
+            parser.ReadResponseBody(body_buffer.get(), kBodySize,
+                                    callback.callback()));
+  data.RunFor(1);
+  ASSERT_TRUE(callback.have_result());
+  ASSERT_EQ(kBodySize, callback.WaitForResult());
+}
+
+// Test to ensure the HttpStreamParser state machine does not get confused
+// when there's only one "chunk" with 0 bytes, which was already appended before
+// the request was started.
+TEST(HttpStreamParser, SyncEmptyChunkedUpload) {
+  MockWrite writes[] = {
+      MockWrite(ASYNC, 0,
+                "GET /one.html HTTP/1.1\r\n"
+                "Transfer-Encoding: chunked\r\n\r\n"),
+      MockWrite(ASYNC, 1, "0\r\n\r\n"),
+  };
+
+  // The size of the response body, as reflected in the Content-Length of the
+  // MockRead below.
+  const int kBodySize = 8;
+
+  MockRead reads[] = {
+      MockRead(ASYNC, 2, "HTTP/1.1 200 OK\r\n"),
+      MockRead(ASYNC, 3, "Content-Length: 8\r\n\r\n"),
+      MockRead(ASYNC, 4, "one.html"),
+      MockRead(SYNCHRONOUS, 0, 5),  // EOF
+  };
+
+  ChunkedUploadDataStream upload_stream(0);
+  ASSERT_EQ(OK, upload_stream.Init(TestCompletionCallback().callback()));
+  // Append final empty chunk.
+  upload_stream.AppendData(nullptr, 0, true);
+
+  DeterministicSocketData data(reads, arraysize(reads), writes,
+                               arraysize(writes));
+  scoped_ptr<ClientSocketHandle> socket_handle =
+      CreateConnectedSocketHandle(&data);
+
+  HttpRequestInfo request_info;
+  request_info.method = "GET";
+  request_info.url = GURL("http://localhost");
+  request_info.upload_data_stream = &upload_stream;
+
+  scoped_refptr<GrowableIOBuffer> read_buffer(new GrowableIOBuffer);
+  HttpStreamParser parser(socket_handle.get(), &request_info, read_buffer.get(),
+                          BoundNetLog());
+
+  HttpRequestHeaders request_headers;
+  request_headers.SetHeader("Transfer-Encoding", "chunked");
+
+  HttpResponseInfo response_info;
+  TestCompletionCallback callback;
+  // This will attempt to Write() the initial request and headers, which will
+  // complete asynchronously.
+  ASSERT_EQ(ERR_IO_PENDING,
+            parser.SendRequest("GET /one.html HTTP/1.1\r\n", request_headers,
+                               &response_info, callback.callback()));
+
+  // Complete writing the request headers and body.
+  data.RunFor(2);
+  ASSERT_TRUE(callback.have_result());
+  ASSERT_EQ(OK, callback.WaitForResult());
+
+  // Attempt to read the response status and the response headers.
+  ASSERT_EQ(ERR_IO_PENDING, parser.ReadResponseHeaders(callback.callback()));
+  data.RunFor(2);
+  ASSERT_TRUE(callback.have_result());
+  ASSERT_GT(callback.WaitForResult(), 0);
+
+  // Finally, attempt to read the response body.
+  scoped_refptr<IOBuffer> body_buffer(new IOBuffer(kBodySize));
+  ASSERT_EQ(ERR_IO_PENDING,
+            parser.ReadResponseBody(body_buffer.get(), kBodySize,
+                                    callback.callback()));
+  data.RunFor(1);
+  ASSERT_TRUE(callback.have_result());
+  ASSERT_EQ(kBodySize, callback.WaitForResult());
 }
 
 TEST(HttpStreamParser, TruncatedHeaders) {
@@ -387,7 +695,6 @@
 
       TestCompletionCallback callback;
       int rv = transport->Connect(callback.callback());
-      rv = callback.GetResult(rv);
       ASSERT_EQ(OK, rv);
 
       scoped_ptr<ClientSocketHandle> socket_handle(new ClientSocketHandle);
@@ -456,7 +763,6 @@
 
   TestCompletionCallback callback;
   int rv = transport->Connect(callback.callback());
-  rv = callback.GetResult(rv);
   ASSERT_EQ(OK, rv);
 
   scoped_ptr<ClientSocketHandle> socket_handle(new ClientSocketHandle);
diff --git a/net/http/transport_security_state_static.h b/net/http/transport_security_state_static.h
index 418f09a..6923b525 100644
--- a/net/http/transport_security_state_static.h
+++ b/net/http/transport_security_state_static.h
@@ -265,6 +265,7 @@
   DOMAIN_FACEBOOK_COM,
   DOMAIN_SPIDEROAK_COM,
   DOMAIN_BLOGGER_COM,
+  DOMAIN_CHROME_COM,
   // Boundary value for UMA_HISTOGRAM_ENUMERATION.
   DOMAIN_NUM_EVENTS,
 };
@@ -677,1355 +678,1374 @@
 // value has the MSB set then it represents a literal leaf value. Otherwise
 // it's a pointer to the n'th element of the array.
 static const uint8 kHSTSHuffmanTree[] = {
-    0xf9, 0xe6, 0xf0, 0x00, 0xf2, 0x01, 0xae, 0xed, 0xe9, 0x03, 0x02, 0x04,
-    0xb0, 0xb2, 0xb1, 0xb5, 0xb3, 0x07, 0x06, 0x08, 0x09, 0xfa, 0x0a, 0xf6,
+    0xf9, 0xe6, 0xf0, 0x00, 0x01, 0xf2, 0xae, 0xed, 0xe9, 0x03, 0x02, 0x04,
+    0xf1, 0xb2, 0xb1, 0xb5, 0xb3, 0x07, 0x06, 0x08, 0x09, 0xfa, 0x0a, 0xf6,
     0xeb, 0x0b, 0x0c, 0xe3, 0xe1, 0x0d, 0x80, 0x0e, 0x05, 0x0f, 0xe2, 0xf7,
-    0x11, 0xf3, 0xef, 0x12, 0xff, 0x13, 0xee, 0xf4, 0xe5, 0x15, 0xe8, 0xf5,
-    0x17, 0xe7, 0xb7, 0xb6, 0xb9, 0xb8, 0x19, 0x1a, 0x1b, 0xf1, 0xb4, 0x1c,
+    0x11, 0xf3, 0xef, 0x12, 0xff, 0x13, 0xe8, 0xf5, 0xee, 0x15, 0xe5, 0x16,
+    0xf4, 0xe7, 0xb7, 0xb6, 0xb9, 0xb8, 0x19, 0x1a, 0x1b, 0xb0, 0xb4, 0x1c,
     0x1d, 0xea, 0xf8, 0x1e, 0xad, 0x1f, 0x20, 0xe4, 0x21, 0xec, 0x18, 0x22,
-    0x16, 0x23, 0x14, 0x24, 0x10, 0x25,
+    0x17, 0x23, 0x14, 0x24, 0x10, 0x25,
 };
 
 static const uint8 kPreloadedHSTSData[] = {
     0xfe, 0x36, 0x7f, 0xb5, 0x5d, 0x54, 0x44, 0xb3, 0xef, 0xf6, 0xab, 0xaa,
-    0x88, 0xb6, 0x6c, 0xe1, 0x29, 0x32, 0x52, 0x17, 0xcd, 0x28, 0x85, 0xa7,
-    0xf6, 0xf3, 0xe2, 0x39, 0x5d, 0x23, 0x1a, 0x79, 0xf9, 0xfa, 0x27, 0x78,
-    0xe2, 0xd0, 0xc7, 0xe3, 0x48, 0x53, 0xfc, 0x0d, 0xa2, 0xeb, 0x1c, 0x96,
-    0x91, 0xb1, 0x34, 0x5f, 0x28, 0x78, 0x0c, 0x41, 0x3f, 0x8d, 0x9f, 0xed,
-    0x57, 0x55, 0x15, 0x14, 0xff, 0xf6, 0x8b, 0x46, 0x1f, 0x32, 0xcf, 0x5b,
-    0x3d, 0x5a, 0x7d, 0xfe, 0xd5, 0x75, 0x51, 0x5e, 0xcf, 0x89, 0xd7, 0xbc,
+    0x88, 0xb6, 0x6c, 0xd9, 0x29, 0x32, 0x52, 0x07, 0xcd, 0x28, 0x05, 0xa7,
+    0xf7, 0x33, 0xe0, 0x39, 0x5d, 0x23, 0x1a, 0x79, 0xf9, 0xfa, 0x27, 0x78,
+    0xe2, 0xd0, 0xc7, 0xe3, 0x48, 0x53, 0xfc, 0x2d, 0xa2, 0xeb, 0x1c, 0x96,
+    0x91, 0xb1, 0x34, 0x5f, 0x28, 0x78, 0x04, 0x41, 0x3f, 0x8d, 0x9f, 0xed,
+    0x57, 0x55, 0x15, 0x14, 0xff, 0xf6, 0x8b, 0x46, 0x0f, 0x32, 0xcf, 0x5b,
+    0x3d, 0x5a, 0x7d, 0xfe, 0xd5, 0x75, 0x51, 0x5e, 0xcf, 0x89, 0xd7, 0xbb,
     0xd8, 0xb4, 0x8d, 0x87, 0xbf, 0xf3, 0x39, 0xff, 0x8d, 0x4f, 0x36, 0x7f,
     0xb5, 0x5d, 0x54, 0x48, 0xd3, 0xff, 0x8c, 0x7a, 0x79, 0xb3, 0xfd, 0xaa,
     0xea, 0xa2, 0x78, 0x9f, 0xfc, 0x63, 0xd3, 0xcd, 0x9f, 0xed, 0x57, 0x55,
     0x13, 0xfc, 0xff, 0xe3, 0x1e, 0x9e, 0x6c, 0xff, 0x6a, 0xba, 0xa8, 0xa1,
     0xa7, 0xff, 0x18, 0xf4, 0xf3, 0x67, 0xfb, 0x55, 0xd5, 0x45, 0x11, 0x3f,
     0xea, 0x79, 0xb3, 0xfd, 0xaa, 0xea, 0xa2, 0x91, 0x9f, 0xff, 0xb3, 0xdd,
-    0x30, 0xc6, 0xd8, 0x50, 0xaf, 0x76, 0x12, 0x52, 0x31, 0xd1, 0x49, 0xe4,
+    0x30, 0x46, 0xe0, 0xd0, 0x2f, 0x76, 0x02, 0x52, 0x31, 0xd1, 0x49, 0xe4,
     0x93, 0x3f, 0xf1, 0xa9, 0xe6, 0xcf, 0xf6, 0xab, 0xaa, 0x89, 0x42, 0x2c,
     0x5d, 0x99, 0x7c, 0x70, 0x7d, 0x3f, 0xf3, 0x9f, 0x7e, 0x16, 0x07, 0x27,
-    0xd2, 0x78, 0x95, 0x7c, 0xda, 0xad, 0xd5, 0x79, 0x87, 0xcb, 0xa9, 0x64,
-    0xff, 0xe3, 0x1e, 0x9e, 0x6c, 0xff, 0x6a, 0xba, 0xa8, 0x9c, 0xe7, 0xff,
-    0x18, 0xf4, 0xf3, 0x67, 0xfb, 0x55, 0xd5, 0x45, 0x13, 0x3f, 0xf7, 0xde,
-    0x6d, 0x15, 0xdc, 0x79, 0x09, 0x68, 0xfa, 0x3b, 0xee, 0xab, 0xca, 0xac,
-    0xfe, 0x36, 0x7f, 0xb5, 0x5d, 0x54, 0x43, 0x93, 0xfc, 0xdf, 0xce, 0xe9,
-    0xb8, 0x7a, 0xd3, 0xef, 0xf6, 0xab, 0xaa, 0x88, 0xbe, 0x7f, 0x5f, 0x3d,
-    0xd6, 0x0a, 0x96, 0x9a, 0xfc, 0x2d, 0x3d, 0xb7, 0xb0, 0xe9, 0x41, 0xcd,
-    0xcd, 0x0b, 0xc8, 0xd6, 0x23, 0xdb, 0x0e, 0x1c, 0x33, 0x06, 0xb9, 0xff,
-    0xc6, 0x3d, 0x3c, 0xd9, 0xfe, 0xd5, 0x75, 0x51, 0x32, 0x4f, 0xe3, 0x67,
-    0xfb, 0x55, 0xd5, 0x45, 0xbd, 0x3f, 0xf8, 0xc7, 0xa7, 0x9b, 0x3f, 0xda,
-    0xae, 0xaa, 0x29, 0x39, 0x9d, 0x07, 0x49, 0x69, 0xfb, 0x81, 0x0d, 0xfa,
-    0x12, 0xd3, 0x68, 0xce, 0x89, 0xe7, 0xd1, 0x04, 0x32, 0xae, 0x07, 0xc6,
-    0x69, 0x88, 0x77, 0x55, 0x76, 0x7c, 0x34, 0x29, 0x27, 0xf1, 0xb3, 0xfd,
+    0xd2, 0x78, 0x15, 0x5d, 0x05, 0x5b, 0xaa, 0xef, 0x0f, 0x97, 0x52, 0xc9,
+    0xff, 0xc6, 0x3d, 0x3c, 0xd9, 0xfe, 0xd5, 0x75, 0x51, 0x39, 0xcf, 0xfe,
+    0x31, 0xe9, 0xe6, 0xcf, 0xf6, 0xab, 0xaa, 0x8a, 0x26, 0x7f, 0xef, 0xbc,
+    0xda, 0x2b, 0xb8, 0xf2, 0x02, 0xd1, 0xf4, 0x77, 0xdd, 0x57, 0x75, 0x59,
+    0xfc, 0x6c, 0xff, 0x6a, 0xba, 0xa8, 0x87, 0x27, 0xf9, 0xbf, 0x9d, 0xd3,
+    0x6c, 0xf5, 0xa7, 0xdf, 0xed, 0x57, 0x55, 0x11, 0x7c, 0xfe, 0xbe, 0x7b,
+    0xac, 0x05, 0x2d, 0x35, 0xf6, 0x5a, 0x7b, 0x8f, 0x60, 0xd2, 0x83, 0x9b,
+    0x9a, 0x17, 0x91, 0xac, 0x47, 0xb6, 0x1c, 0x38, 0x66, 0x2d, 0x73, 0xff,
+    0x8c, 0x7a, 0x79, 0xb3, 0xfd, 0xaa, 0xea, 0xa2, 0x64, 0x9f, 0xc6, 0xcf,
+    0xf6, 0xab, 0xaa, 0x8b, 0x7a, 0x7f, 0xf1, 0x8f, 0x4f, 0x36, 0x7f, 0xb5,
+    0x5d, 0x54, 0x52, 0x73, 0x79, 0xb7, 0x49, 0x69, 0xfb, 0x60, 0x05, 0xfa,
+    0x02, 0xd3, 0x68, 0xce, 0x89, 0xe8, 0x51, 0x0c, 0x32, 0xae, 0x17, 0xc6,
+    0x69, 0x88, 0x77, 0x55, 0x76, 0x7c, 0x14, 0x29, 0x67, 0xf1, 0xb3, 0xfd,
     0xaa, 0xea, 0xa2, 0x1d, 0x9f, 0xfc, 0x63, 0xd3, 0xcd, 0x9f, 0xed, 0x57,
     0x55, 0x12, 0xc4, 0xfe, 0x36, 0x7f, 0xb5, 0x5d, 0x54, 0x46, 0x13, 0xef,
-    0xf6, 0xab, 0xaa, 0x88, 0xf6, 0x7a, 0xf5, 0xc3, 0xb5, 0xa7, 0x8d, 0x4f,
-    0x36, 0x1e, 0xbe, 0xcc, 0xe7, 0xf1, 0xb3, 0xfd, 0xaa, 0xea, 0xa2, 0xc3,
+    0xf6, 0xab, 0xaa, 0x88, 0xf6, 0x7a, 0xf5, 0xb3, 0xb5, 0xa7, 0x8d, 0x4f,
+    0x36, 0x1e, 0xbf, 0x0c, 0xe7, 0xf1, 0xb3, 0xfd, 0xaa, 0xea, 0xa2, 0xc3,
     0x9f, 0xc6, 0xcf, 0xf6, 0xab, 0xaa, 0x8b, 0x9e, 0x19, 0x3d, 0xcb, 0x0f,
-    0x9e, 0xaa, 0x50, 0x95, 0x39, 0xc6, 0xcf, 0xa7, 0xf1, 0xb3, 0xfd, 0xaa,
+    0x9e, 0xaa, 0x50, 0x95, 0x39, 0xc7, 0x0f, 0xa7, 0xf1, 0xb3, 0xfd, 0xaa,
     0xea, 0xa2, 0x1e, 0x9f, 0x7f, 0xb5, 0x5d, 0x54, 0x44, 0xd3, 0xff, 0xb3,
-    0x75, 0xad, 0x33, 0xb7, 0xe7, 0xd9, 0x69, 0xb6, 0x75, 0xa7, 0xff, 0x66,
-    0x86, 0xb2, 0xec, 0x5c, 0x6d, 0x8e, 0xb4, 0x70, 0x7c, 0x3a, 0x16, 0x9e,
-    0xdf, 0x34, 0x32, 0xa2, 0x17, 0x91, 0xb1, 0x33, 0x0d, 0x19, 0xde, 0x15,
-    0x5c, 0x91, 0xce, 0xfd, 0x75, 0x69, 0x69, 0x68, 0x1c, 0xd5, 0xf2, 0x37,
-    0x3d, 0xbd, 0x16, 0xd6, 0x9f, 0xff, 0xfb, 0x5c, 0xfa, 0x59, 0xf7, 0x03,
-    0x1d, 0xeb, 0x3d, 0x6b, 0xdd, 0x87, 0x5a, 0x7f, 0xf1, 0x38, 0xfc, 0xe1,
-    0xfe, 0x0f, 0x54, 0x0b, 0x4d, 0x4e, 0xab, 0x46, 0x23, 0xeb, 0xa4, 0x3a,
-    0x76, 0xa4, 0xb9, 0xdf, 0xae, 0xad, 0x0e, 0x89, 0xeb, 0x0e, 0x77, 0x3e,
-    0xd5, 0x6e, 0x9e, 0xb4, 0xfe, 0x7d, 0xda, 0xfe, 0x68, 0x74, 0x1d, 0x05,
-    0xa7, 0x8a, 0xe4, 0x25, 0xa7, 0x5b, 0x6d, 0xa9, 0x4f, 0xa8, 0x37, 0xac,
-    0x48, 0xc5, 0xfc, 0xfa, 0xb6, 0xe6, 0x6d, 0x68, 0xf5, 0x13, 0x42, 0x3f,
-    0x03, 0x49, 0xed, 0x55, 0x52, 0xd3, 0xff, 0x08, 0x9c, 0xf1, 0xa6, 0x2f,
-    0xf3, 0xe7, 0x5a, 0x7f, 0xc4, 0x3b, 0x03, 0x5d, 0x88, 0xeb, 0x4f, 0xd8,
-    0xef, 0x97, 0xe5, 0xd6, 0x9e, 0xcf, 0x72, 0xc5, 0xa3, 0x15, 0x15, 0xf0,
-    0x4c, 0x50, 0xdf, 0xf3, 0x98, 0x38, 0x3f, 0xa4, 0xb0, 0x3b, 0xd9, 0x7c,
-    0xfd, 0x96, 0xb6, 0x7a, 0x4b, 0x4e, 0x6e, 0x44, 0xb4, 0xff, 0xff, 0xe0,
-    0x0e, 0x18, 0x70, 0xcf, 0x74, 0x4e, 0x78, 0xbe, 0x70, 0xff, 0xe5, 0xab,
-    0x4f, 0x8b, 0x7c, 0xb5, 0x8b, 0x4f, 0xff, 0xec, 0xe1, 0xf7, 0x3e, 0x07,
-    0x87, 0x52, 0xd5, 0x85, 0x9f, 0x5a, 0x7f, 0xff, 0xd6, 0xf8, 0x0c, 0xee,
-    0x88, 0x33, 0x9e, 0x37, 0xe2, 0xad, 0xc1, 0xd6, 0x9f, 0xdc, 0x3e, 0xe3,
-    0xf2, 0xdc, 0x2d, 0x3f, 0xaa, 0xde, 0x5c, 0x6d, 0xd2, 0xd0, 0xc8, 0xdd,
-    0x27, 0x20, 0x37, 0x9e, 0x16, 0xca, 0xc5, 0xa7, 0xfc, 0xd5, 0x63, 0x1c,
-    0x1b, 0x81, 0x2d, 0x0c, 0xa9, 0xcb, 0x1f, 0x08, 0xa3, 0xf1, 0xa1, 0x51,
-    0x68, 0x11, 0x4d, 0xcb, 0xd6, 0x9f, 0x30, 0xaf, 0x66, 0xd6, 0x9d, 0x5b,
-    0xba, 0xd3, 0x73, 0x62, 0xd3, 0x83, 0x6e, 0x3c, 0xd9, 0xfc, 0x6e, 0x7e,
-    0xe7, 0xbe, 0x3d, 0xe2, 0xd5, 0xa1, 0x8f, 0x9c, 0x8c, 0xe7, 0xff, 0xdc,
-    0xb6, 0xfc, 0x07, 0x2e, 0xbe, 0x33, 0x7e, 0x59, 0xb5, 0xa7, 0xfe, 0xd3,
-    0x0f, 0xe3, 0x61, 0x5b, 0x6d, 0xad, 0x3f, 0xbc, 0x70, 0xf2, 0xd7, 0x27,
-    0x5a, 0x04, 0x7f, 0x80, 0x8b, 0x3f, 0xef, 0xb9, 0xe2, 0xec, 0x56, 0x56,
-    0xd6, 0x86, 0x3e, 0x2e, 0x48, 0x67, 0xff, 0xff, 0xb9, 0x0b, 0xfc, 0x19,
-    0xcf, 0x03, 0x6c, 0x9f, 0x9c, 0x6f, 0x1d, 0x86, 0x5d, 0x69, 0x32, 0xd3,
-    0xf5, 0x7c, 0xae, 0xdc, 0x2d, 0x3f, 0xea, 0xb7, 0xec, 0x1b, 0xd6, 0x3a,
-    0xad, 0x0f, 0x3e, 0xcf, 0x96, 0x4f, 0xf6, 0x8b, 0xee, 0x6a, 0xb3, 0xeb,
-    0x4f, 0xff, 0xff, 0xc2, 0xb8, 0x65, 0x71, 0xe3, 0xdd, 0x73, 0xe0, 0x33,
-    0x87, 0xe7, 0x36, 0x06, 0x57, 0x0b, 0x47, 0x51, 0x90, 0x47, 0x13, 0xaa,
-    0xba, 0xa8, 0xa6, 0x23, 0x0f, 0x27, 0xa4, 0x53, 0xfe, 0xc0, 0xe1, 0xf7,
-    0xdb, 0x7d, 0xc5, 0xa7, 0x61, 0x09, 0x69, 0x62, 0xd3, 0xb8, 0x0c, 0x11,
-    0xa9, 0x76, 0x35, 0x04, 0x89, 0xca, 0x68, 0x9c, 0xf6, 0xe1, 0x69, 0xfc,
-    0x7f, 0x74, 0xd6, 0x65, 0xab, 0x47, 0x07, 0xa3, 0x43, 0x93, 0xff, 0xf8,
-    0x33, 0xbb, 0xdb, 0x6e, 0xee, 0x06, 0x5b, 0xe1, 0x9d, 0x32, 0xd3, 0xfe,
-    0xeb, 0x1f, 0x59, 0x7c, 0x20, 0x5a, 0x19, 0x14, 0x7a, 0x68, 0x9f, 0x7d,
-    0x9d, 0xb7, 0xd6, 0x9f, 0xa8, 0x3c, 0x7a, 0xc0, 0xb4, 0x61, 0xfb, 0x91,
-    0x17, 0x25, 0x11, 0x62, 0xf9, 0x13, 0xc5, 0xfd, 0x86, 0x10, 0xe4, 0x1d,
-    0x8c, 0xfb, 0x82, 0x22, 0x84, 0x13, 0x8f, 0x7f, 0x8c, 0xcc, 0xf0, 0xbd,
-    0x07, 0x2d, 0xc6, 0x79, 0x3f, 0xe1, 0xff, 0x56, 0xf8, 0xaf, 0x6a, 0xc5,
-    0xa7, 0xb0, 0x07, 0xf3, 0xad, 0x3f, 0xff, 0xfe, 0xd1, 0x6f, 0x59, 0xf6,
-    0x77, 0xe0, 0xfc, 0xba, 0xf8, 0xce, 0x1f, 0x9c, 0x80, 0xae, 0xb4, 0xb6,
-    0x48, 0xb3, 0xb9, 0x2c, 0xff, 0xfb, 0x33, 0x45, 0xdd, 0x67, 0x8b, 0xf3,
-    0xa6, 0xf3, 0xad, 0x3f, 0xfd, 0xbd, 0x60, 0xbc, 0x1d, 0xb3, 0x7e, 0xe0,
-    0x2d, 0x3f, 0xdc, 0x78, 0x3b, 0x60, 0x6f, 0xc9, 0x69, 0x5f, 0x11, 0x1b,
-    0x4a, 0x12, 0x20, 0x4c, 0x3b, 0x70, 0xe7, 0x9f, 0x3d, 0xb8, 0x7d, 0xd6,
-    0x9f, 0xff, 0xff, 0xf6, 0x00, 0xfe, 0x0b, 0x5c, 0x99, 0xc2, 0xdb, 0x1c,
-    0x28, 0x7d, 0x11, 0xb3, 0x5c, 0x09, 0xbd, 0x4a, 0x7f, 0xff, 0xee, 0x95,
-    0xbe, 0xf3, 0xff, 0x1c, 0xb5, 0x97, 0xee, 0xb9, 0xd7, 0x15, 0x6a, 0xd3,
-    0x73, 0x67, 0x09, 0xa1, 0x91, 0x45, 0xe1, 0x3d, 0x0c, 0xab, 0xab, 0x51,
-    0x9b, 0x04, 0x6e, 0x13, 0xff, 0x8f, 0xdf, 0x1a, 0xa7, 0xe1, 0xe8, 0x04,
-    0xb4, 0xff, 0xf8, 0x83, 0x7a, 0xc7, 0x5d, 0xb9, 0x9e, 0x7a, 0x01, 0x2d,
-    0x3f, 0xf3, 0x05, 0x81, 0x8e, 0xef, 0xcb, 0x70, 0xb4, 0x5d, 0x14, 0x1c,
-    0xad, 0x4f, 0x66, 0xf3, 0xcd, 0xad, 0x3b, 0xcb, 0x34, 0xb4, 0xd9, 0xea,
-    0xd1, 0x62, 0x6d, 0x0f, 0x87, 0x55, 0xc9, 0x36, 0x4c, 0xec, 0x7a, 0x7c,
-    0x5b, 0xbe, 0x09, 0x69, 0xfb, 0xd6, 0x0d, 0x53, 0xd6, 0x9c, 0xde, 0xb2,
-    0xd3, 0xf3, 0xf3, 0x77, 0xa7, 0x3c, 0x1e, 0x39, 0xcb, 0x21, 0xe8, 0xb4,
-    0x71, 0xb6, 0x7e, 0x75, 0xd5, 0x79, 0xd8, 0x4b, 0x4f, 0xef, 0xb8, 0x19,
-    0xaa, 0xda, 0xd1, 0x87, 0xca, 0x46, 0x73, 0xf3, 0x0b, 0x36, 0x35, 0x8b,
-    0x43, 0xcf, 0x38, 0x08, 0x27, 0xfb, 0x7a, 0xc7, 0xd0, 0x67, 0x0b, 0x4f,
-    0xff, 0xf0, 0x50, 0xaf, 0x66, 0x10, 0x6c, 0x31, 0xdf, 0x8b, 0xda, 0x0b,
-    0x40, 0x22, 0x7f, 0x66, 0xf3, 0xfe, 0x2c, 0x73, 0xc5, 0xdb, 0x55, 0xe7,
-    0x5a, 0x7f, 0x57, 0x17, 0xd6, 0x57, 0xd6, 0x8f, 0x4f, 0xd7, 0x68, 0x73,
-    0xfd, 0x9f, 0xb1, 0x8b, 0xf8, 0x0b, 0x48, 0xeb, 0x78, 0x36, 0xd3, 0x83,
-    0x3d, 0x5a, 0x18, 0xde, 0xf0, 0x45, 0x3f, 0x71, 0x7c, 0xc0, 0x1d, 0x69,
-    0xe3, 0x8c, 0xc2, 0x5a, 0x30, 0xf3, 0xc8, 0xb6, 0x77, 0x3c, 0x09, 0x68,
-    0x64, 0xf6, 0xf2, 0x12, 0xe5, 0x09, 0x6f, 0xb8, 0x01, 0x04, 0xfe, 0xd9,
-    0x59, 0x9a, 0xe7, 0x6b, 0x49, 0xc5, 0xa7, 0xec, 0xe7, 0x35, 0xdb, 0x16,
-    0x9b, 0x38, 0x63, 0x7f, 0x71, 0x19, 0xfb, 0x54, 0xf3, 0xfc, 0x96, 0x9e,
-    0x17, 0xf3, 0x85, 0xa7, 0xc5, 0x7b, 0xb1, 0xd6, 0x82, 0x3c, 0x87, 0x64,
-    0x50, 0x3a, 0x66, 0xc4, 0xe6, 0x22, 0xce, 0x5d, 0x27, 0xff, 0xef, 0xbf,
-    0x58, 0xe6, 0x98, 0x07, 0x0c, 0xde, 0xb9, 0x5a, 0x7f, 0xff, 0x16, 0x70,
-    0xfb, 0xef, 0x4c, 0x76, 0x2f, 0x7c, 0x33, 0xa6, 0x5a, 0x7f, 0xff, 0xfd,
-    0xc8, 0x78, 0xb6, 0xb8, 0x7f, 0xe8, 0x5e, 0x2f, 0xcb, 0x71, 0xac, 0x20,
-    0xc1, 0xd6, 0x9f, 0xff, 0x73, 0xff, 0xe6, 0xef, 0xa2, 0xcf, 0x1a, 0x13,
-    0x2d, 0x18, 0x8e, 0x1d, 0x42, 0x2a, 0x7f, 0xf9, 0xee, 0xba, 0xc6, 0x07,
-    0x03, 0x37, 0x7a, 0x5a, 0x7f, 0xfd, 0xee, 0x1f, 0xc3, 0xae, 0xda, 0xee,
-    0x3e, 0xbf, 0x62, 0xd3, 0xff, 0xf6, 0xca, 0xcc, 0xf7, 0xc1, 0xe9, 0xff,
-    0x2b, 0x28, 0xeb, 0x4f, 0xeb, 0x28, 0xe0, 0xdf, 0x71, 0x69, 0xfe, 0xe3,
-    0x07, 0x11, 0xf3, 0xc7, 0xf1, 0x12, 0x24, 0xbd, 0x3f, 0xd8, 0x16, 0x78,
-    0x15, 0x53, 0x8b, 0x4f, 0xf7, 0xec, 0x6b, 0x2c, 0xe7, 0xf6, 0x2d, 0x3f,
-    0xdc, 0x87, 0x8b, 0xf3, 0xe5, 0xcf, 0xd6, 0x86, 0x3f, 0xfb, 0x9f, 0x4f,
-    0xfe, 0xb3, 0xc5, 0xf5, 0xcf, 0x8d, 0xde, 0xed, 0xf5, 0xa7, 0xff, 0xfd,
-    0x9c, 0x5c, 0xb2, 0xdf, 0x1a, 0xcd, 0xdf, 0x2c, 0xf1, 0x85, 0xea, 0xd1,
-    0x88, 0xc2, 0x25, 0x18, 0xb1, 0x72, 0xfb, 0xb1, 0xa4, 0xf0, 0x4e, 0x4a,
-    0x1f, 0x87, 0x71, 0xd4, 0xef, 0x0b, 0x9d, 0xc3, 0x8e, 0x7f, 0x6c, 0x33,
-    0x7a, 0xcb, 0x16, 0x9f, 0xf5, 0x7f, 0x59, 0xb6, 0x02, 0x12, 0xd3, 0xfd,
-    0x96, 0xf0, 0xfb, 0xf8, 0xd9, 0x2d, 0x3f, 0xff, 0xe6, 0xeb, 0x1f, 0xc3,
-    0x9a, 0x20, 0xf7, 0xc0, 0xdd, 0x0c, 0xd6, 0x2d, 0x0c, 0x98, 0xcf, 0x4c,
-    0x80, 0xed, 0xd4, 0xf2, 0x78, 0xb6, 0x4c, 0xb4, 0xff, 0xff, 0xe0, 0x21,
-    0xf5, 0x4e, 0x78, 0x0e, 0x5d, 0x7c, 0x67, 0x0f, 0xce, 0x40, 0x57, 0x5a,
-    0x7d, 0xa2, 0x60, 0xb1, 0x69, 0xb6, 0xc7, 0x45, 0x3b, 0xb8, 0x40, 0xc3,
-    0xd1, 0xf6, 0x50, 0xc6, 0x86, 0x64, 0x0f, 0x64, 0xe8, 0xd1, 0x47, 0x47,
-    0x51, 0x9d, 0x4f, 0x98, 0x36, 0x4e, 0xd6, 0x9e, 0xdd, 0xe8, 0x4b, 0x4f,
-    0xfd, 0xa2, 0xf0, 0x5f, 0xe6, 0xca, 0xff, 0x56, 0x9e, 0x2d, 0x55, 0x8b,
-    0x4f, 0x3b, 0x6d, 0xb2, 0xd3, 0xf6, 0xaa, 0xcf, 0x0f, 0xda, 0xd3, 0xea,
-    0xf8, 0x70, 0x4b, 0x41, 0x1e, 0xb6, 0xcc, 0x23, 0xd4, 0xd2, 0x70, 0x9c,
-    0x72, 0x1e, 0xa3, 0x91, 0x0e, 0xde, 0x27, 0x86, 0xe6, 0xae, 0xb4, 0xff,
-    0xfc, 0x5f, 0xf1, 0x85, 0x80, 0x3e, 0x8b, 0x0f, 0x9d, 0x5a, 0x7f, 0xff,
-    0x8a, 0xcf, 0xb1, 0xef, 0xcb, 0x78, 0x6f, 0xb7, 0xba, 0x63, 0xad, 0x18,
-    0x8c, 0x2f, 0xac, 0xcf, 0xff, 0xfe, 0xff, 0xca, 0xcf, 0x1a, 0xcd, 0x83,
-    0x39, 0xac, 0xb1, 0xb8, 0xdf, 0x3d, 0x5a, 0x7f, 0xff, 0xe6, 0x1f, 0x4d,
-    0xe0, 0x33, 0x91, 0xfc, 0x70, 0xfb, 0x90, 0x0c, 0xd6, 0x2d, 0x3e, 0xe2,
-    0xe5, 0x80, 0xb4, 0x62, 0x27, 0xf9, 0x79, 0x82, 0x4c, 0xff, 0x51, 0x8e,
-    0x4f, 0xfc, 0xfb, 0x90, 0xfa, 0x62, 0xb1, 0x87, 0x5a, 0x7f, 0x83, 0x97,
-    0x03, 0x35, 0x5b, 0x5a, 0x7f, 0xbd, 0xd3, 0x5b, 0xee, 0xa8, 0xeb, 0x4f,
-    0xff, 0xf5, 0x7f, 0x59, 0xb2, 0xc0, 0xb2, 0xed, 0x81, 0xee, 0x99, 0x69,
-    0xed, 0xf8, 0xb3, 0xeb, 0x47, 0x08, 0x88, 0xa6, 0x39, 0xff, 0xd6, 0xd0,
-    0x58, 0x7e, 0x4b, 0xd2, 0x02, 0x5a, 0x7e, 0x73, 0x87, 0xf2, 0xdb, 0x5a,
-    0x7f, 0xb5, 0x47, 0x0c, 0xd5, 0x6d, 0x69, 0xec, 0xf8, 0xad, 0x5a, 0x19,
-    0x11, 0x17, 0x30, 0xd9, 0xb4, 0xfd, 0x9b, 0xff, 0xf1, 0xeb, 0x4f, 0xc1,
-    0x9a, 0x0c, 0xea, 0xd3, 0xc1, 0x97, 0xf0, 0xe1, 0xeb, 0x6c, 0xb2, 0x3d,
-    0x5c, 0x7f, 0xec, 0x6c, 0x3c, 0x14, 0x92, 0x2f, 0xce, 0x35, 0x0c, 0x3b,
-    0x91, 0xee, 0x1a, 0x4e, 0xe1, 0x0b, 0x3f, 0xf0, 0x06, 0x78, 0xfe, 0x68,
-    0x31, 0xc5, 0xa1, 0x97, 0x90, 0x32, 0x75, 0x92, 0xa1, 0x33, 0x3e, 0x06,
-    0x3d, 0x7a, 0xb4, 0xfe, 0xc7, 0x35, 0x9b, 0x6f, 0x56, 0x9f, 0xff, 0xfe,
-    0xd6, 0x6e, 0xf4, 0xe7, 0x80, 0xce, 0x1f, 0x9c, 0xd8, 0x19, 0xee, 0x89,
-    0xc5, 0xa6, 0xe4, 0x75, 0xa3, 0xe8, 0x9a, 0xdc, 0x20, 0xa7, 0xec, 0xb0,
-    0x33, 0x8b, 0xad, 0x3f, 0xfb, 0xfc, 0x3e, 0xf8, 0x7e, 0x1b, 0x5a, 0x65,
-    0xa5, 0x75, 0xa3, 0x93, 0xdb, 0x19, 0x2e, 0x7f, 0x31, 0xc3, 0x35, 0x5b,
-    0x5a, 0x7b, 0xc7, 0x6c, 0xb5, 0x69, 0xff, 0xff, 0x6c, 0x18, 0x5c, 0x3e,
-    0xfc, 0xb6, 0xc3, 0x3d, 0x2c, 0xfb, 0x8b, 0x41, 0x2a, 0x8a, 0x70, 0x9a,
-    0xa1, 0x9a, 0x05, 0x17, 0x84, 0x47, 0x24, 0x8e, 0xcc, 0x06, 0x26, 0x9f,
-    0x3c, 0xbf, 0x9c, 0x2d, 0x3e, 0xcb, 0xd9, 0x96, 0xad, 0x1c, 0x1e, 0x7e,
-    0x89, 0xe7, 0xff, 0xc5, 0x66, 0x77, 0xdd, 0x16, 0x0f, 0xe0, 0x7e, 0x59,
-    0x69, 0xe7, 0x5d, 0xe6, 0x96, 0x98, 0x8e, 0xb4, 0xff, 0xff, 0xb8, 0x7d,
-    0x71, 0xe0, 0x39, 0x75, 0xf1, 0x9c, 0x3f, 0x39, 0x01, 0x5d, 0x68, 0x3a,
-    0x23, 0x1d, 0x45, 0x62, 0xc4, 0x6b, 0x64, 0x2c, 0x27, 0xff, 0xff, 0xcd,
-    0xc5, 0xf9, 0x6e, 0x3c, 0x6b, 0x3d, 0xf0, 0x19, 0xc8, 0xfe, 0x2e, 0x21,
-    0x16, 0xd6, 0x9f, 0x30, 0x71, 0x56, 0x2d, 0x3f, 0xff, 0xff, 0xfb, 0x0f,
-    0x9d, 0x6d, 0x31, 0xf7, 0x97, 0xbb, 0x7f, 0xec, 0x19, 0x66, 0x73, 0x7e,
-    0x5c, 0x06, 0x3a, 0xd3, 0xfc, 0x2c, 0xf4, 0x33, 0x55, 0xb5, 0xa7, 0xf5,
-    0x9c, 0xb6, 0xce, 0x5b, 0x5a, 0x7f, 0xcc, 0x3b, 0xaf, 0x38, 0x0e, 0x0c,
-    0x75, 0xa3, 0x0f, 0xe3, 0x83, 0x49, 0xff, 0x66, 0x0a, 0xfe, 0x3f, 0x7f,
-    0x6e, 0xb4, 0xfe, 0xfe, 0x77, 0x45, 0xf7, 0x16, 0x9b, 0x37, 0xe9, 0xfa,
-    0x62, 0x0c, 0xf5, 0x84, 0x1b, 0x5a, 0x19, 0x71, 0x9f, 0x08, 0xca, 0x33,
-    0x0f, 0x94, 0x1e, 0x11, 0xda, 0x27, 0xa8, 0x52, 0x04, 0x2b, 0xef, 0x09,
-    0x6e, 0x4b, 0x67, 0x7b, 0xe1, 0xc5, 0xa7, 0xff, 0xb8, 0x78, 0x37, 0x83,
-    0x83, 0x0f, 0xb2, 0xb1, 0x68, 0xc3, 0xf2, 0x22, 0x09, 0xfa, 0xc7, 0x1f,
-    0xe6, 0x7e, 0xc5, 0xa7, 0xc7, 0x2f, 0x73, 0xd5, 0xa4, 0xe2, 0xd3, 0x31,
-    0xd6, 0x96, 0xd6, 0x8f, 0x9a, 0x5a, 0x15, 0x8f, 0x4f, 0x5b, 0x46, 0xd3,
-    0xdb, 0xbd, 0x70, 0xb4, 0xce, 0x52, 0xd3, 0xfb, 0x45, 0xfe, 0x1e, 0x0c,
-    0xb4, 0x3a, 0x09, 0xa0, 0xb1, 0xb6, 0x3f, 0x11, 0x19, 0xc8, 0xb9, 0x16,
-    0x9f, 0xd7, 0x2e, 0x2d, 0xa0, 0x12, 0xd3, 0xff, 0xe6, 0xfb, 0x77, 0x0e,
-    0xdd, 0x79, 0x5f, 0x93, 0xad, 0x0e, 0x22, 0x1f, 0xc8, 0xce, 0x7e, 0xfe,
-    0x30, 0xec, 0x0b, 0x4e, 0x0a, 0x3a, 0xd3, 0x09, 0x96, 0x87, 0x9e, 0xd1,
-    0x15, 0x80, 0xd4, 0x3a, 0x4e, 0xdd, 0x67, 0xcc, 0x8d, 0xcd, 0xa5, 0x29,
-    0xd8, 0xf6, 0xf2, 0xcf, 0x65, 0x47, 0x64, 0xfb, 0xe0, 0xf0, 0xcc, 0xec,
-    0xbe, 0x0e, 0x23, 0xa4, 0x28, 0xc1, 0xdc, 0x86, 0xbf, 0xe5, 0x4d, 0x9e,
-    0x38, 0x7d, 0x52, 0x17, 0x6a, 0x39, 0xa0, 0x9e, 0x98, 0xbc, 0xac, 0x7d,
-    0xce, 0x9a, 0xbb, 0x8c, 0x5b, 0xca, 0x36, 0x11, 0xa1, 0xc4, 0xeb, 0x08,
-    0x19, 0xff, 0xfb, 0x9e, 0x9b, 0x8d, 0xfd, 0xb3, 0x8f, 0x33, 0xda, 0xa1,
-    0xd6, 0x83, 0x2a, 0x33, 0x28, 0xf3, 0xe7, 0xf1, 0xb3, 0xfd, 0xaa, 0xea,
-    0xa2, 0x9d, 0x9f, 0x7f, 0xb5, 0x5d, 0x54, 0x54, 0x73, 0xfe, 0xa7, 0x9b,
-    0x3f, 0xda, 0xae, 0xaa, 0x26, 0x88, 0xc3, 0xfc, 0x39, 0x9c, 0xfe, 0x1a,
-    0x9d, 0x9b, 0x65, 0x62, 0xd0, 0x63, 0xd7, 0x62, 0x19, 0xfc, 0x6c, 0xff,
-    0x6a, 0xba, 0xa8, 0xab, 0xe7, 0xdf, 0xed, 0x57, 0x55, 0x15, 0xb4, 0xff,
-    0x3c, 0xd9, 0xfe, 0xd5, 0x75, 0x51, 0x20, 0xc8, 0xd8, 0x7e, 0xf4, 0x67,
-    0x3f, 0xf1, 0xa9, 0xe6, 0xcf, 0xf6, 0xab, 0xaa, 0x89, 0x0e, 0x7f, 0xcf,
-    0xd3, 0x6e, 0xee, 0x9a, 0xee, 0x90, 0x2d, 0x2e, 0x16, 0x98, 0x81, 0x68,
-    0xf4, 0xd2, 0xb8, 0x23, 0x3e, 0xcc, 0x01, 0x5d, 0x69, 0xf7, 0xfb, 0x55,
-    0xd5, 0x45, 0x89, 0x3e, 0xcf, 0xb0, 0x0e, 0xb4, 0xf8, 0x6f, 0x2c, 0xfb,
-    0x2d, 0x3a, 0xec, 0x0b, 0x4b, 0x4c, 0x78, 0x84, 0x55, 0x3f, 0xf3, 0x65,
-    0xf0, 0x3d, 0x21, 0x30, 0x2d, 0x38, 0x1a, 0xc5, 0xa4, 0xf0, 0x3d, 0xbe,
-    0x50, 0x27, 0x79, 0x10, 0x96, 0x9f, 0x13, 0xbe, 0x6a, 0xeb, 0x4f, 0xfc,
-    0xef, 0x9c, 0xe3, 0xd7, 0x7a, 0xca, 0x71, 0x69, 0x99, 0xeb, 0x46, 0x1f,
-    0x07, 0xd2, 0xe7, 0xf6, 0x7d, 0xe2, 0xcc, 0x71, 0x69, 0xf5, 0x3c, 0x6d,
-    0x32, 0xd3, 0xf5, 0xef, 0xf2, 0xb7, 0xce, 0xb4, 0x32, 0x22, 0xc8, 0xca,
-    0x89, 0xe7, 0xff, 0x9e, 0xc7, 0x67, 0xfa, 0x0d, 0xaa, 0xcf, 0xad, 0x3f,
-    0xba, 0x18, 0xec, 0x32, 0xeb, 0x47, 0x07, 0xff, 0xa4, 0xd9, 0xfc, 0x26,
-    0xcd, 0x90, 0x5a, 0x94, 0xff, 0xda, 0xa2, 0xdb, 0xf0, 0x70, 0xc1, 0x2d,
-    0x3e, 0xcd, 0x71, 0x5e, 0x75, 0xa7, 0x85, 0x76, 0x05, 0xa7, 0xf7, 0xf5,
-    0x45, 0x72, 0x76, 0xb4, 0x31, 0xe9, 0xe1, 0x04, 0x12, 0x38, 0x01, 0x0a,
-    0xef, 0xd3, 0xf5, 0x59, 0xb6, 0xe1, 0xeb, 0x4f, 0xf8, 0x18, 0xbd, 0xd8,
-    0x57, 0x17, 0x5a, 0x1d, 0x25, 0xdc, 0x86, 0x6e, 0xf4, 0x8f, 0x09, 0x47,
-    0x33, 0xeb, 0x57, 0x10, 0x81, 0x22, 0x87, 0x07, 0xbf, 0x08, 0x9d, 0x42,
-    0xb2, 0xa1, 0x58, 0x22, 0x3b, 0xc6, 0x23, 0xb2, 0xe1, 0x8b, 0xa7, 0xfe,
-    0xd1, 0x36, 0xf5, 0x43, 0xe0, 0x09, 0x69, 0xff, 0xec, 0xe1, 0xfb, 0xe6,
-    0xc3, 0x6a, 0x8e, 0x57, 0x5a, 0x6d, 0x1b, 0x11, 0x28, 0x08, 0x50, 0x64,
-    0xef, 0x5a, 0x39, 0x09, 0xfc, 0x6c, 0xff, 0x6a, 0xba, 0xa8, 0xb3, 0x66,
-    0xcf, 0x56, 0x9f, 0xf5, 0x3c, 0xd9, 0xfe, 0xd5, 0x75, 0x51, 0x40, 0x4f,
-    0xe2, 0x3b, 0x79, 0xf5, 0x96, 0x2d, 0x23, 0x62, 0x25, 0x4e, 0x2d, 0xa4,
-    0x99, 0xb9, 0xd2, 0xd3, 0xf5, 0xd8, 0xe0, 0xce, 0x2d, 0x2d, 0x2d, 0x38,
-    0x60, 0x71, 0x69, 0xb3, 0x78, 0x6b, 0x44, 0x21, 0x02, 0x44, 0x20, 0x2c,
-    0xcd, 0xe2, 0xeb, 0x4f, 0xf6, 0xb3, 0xfb, 0x06, 0x3d, 0xd6, 0x9e, 0xed,
-    0x57, 0x55, 0x16, 0xfc, 0xfc, 0x36, 0xeb, 0xb5, 0xf5, 0xa3, 0xe7, 0xb4,
-    0x22, 0xd9, 0xec, 0x02, 0x71, 0x69, 0xf6, 0xdf, 0x71, 0xae, 0xb4, 0xfc,
-    0xec, 0x6d, 0x83, 0x09, 0x69, 0xf7, 0xad, 0xe7, 0xe4, 0xeb, 0x43, 0x1e,
-    0xe1, 0x17, 0xcf, 0x51, 0x06, 0xd6, 0x9f, 0xdc, 0xf1, 0x76, 0xee, 0x1d,
-    0x69, 0xed, 0x0f, 0xe9, 0xd6, 0x9b, 0x1e, 0xb4, 0x61, 0xb9, 0x02, 0x49,
-    0x19, 0xd2, 0x55, 0xf0, 0xd0, 0x9b, 0x79, 0x17, 0xa3, 0x19, 0x09, 0x12,
-    0x22, 0xf9, 0x06, 0xa1, 0x0a, 0x22, 0x00, 0x1f, 0xdb, 0x6c, 0xff, 0xa9,
-    0xe6, 0xcf, 0xf6, 0xab, 0xaa, 0x8a, 0x52, 0x7f, 0x9e, 0x6c, 0xff, 0x6a,
-    0xba, 0xa8, 0x93, 0xa4, 0x63, 0xa2, 0x36, 0x92, 0x61, 0x9f, 0x2c, 0x22,
-    0xc8, 0xdb, 0xbd, 0xb7, 0x40, 0x18, 0x55, 0xd8, 0xc4, 0x48, 0x87, 0xcf,
-    0x0b, 0x57, 0x09, 0x8f, 0x3f, 0xa9, 0xa2, 0x91, 0x43, 0x4e, 0xf2, 0xcb,
-    0x5d, 0xc6, 0x7d, 0x3e, 0xff, 0x6a, 0xba, 0xa8, 0x87, 0xe7, 0xfd, 0x4f,
-    0x36, 0x7f, 0xb5, 0x5d, 0x54, 0x4a, 0x72, 0x36, 0x1f, 0xe1, 0xcc, 0xe7,
-    0xf1, 0xb3, 0xfd, 0xaa, 0xea, 0xa2, 0x27, 0x9f, 0xc6, 0xcf, 0xf6, 0xab,
-    0xaa, 0x88, 0xc6, 0x7f, 0xf1, 0x8f, 0x4f, 0x36, 0x7f, 0xb5, 0x5d, 0x54,
-    0x4f, 0x30, 0xc8, 0xf8, 0xb0, 0x9d, 0xe7, 0xda, 0x3e, 0x9f, 0x7f, 0xb5,
-    0x5d, 0x54, 0x44, 0x13, 0xfe, 0xa7, 0x9b, 0x3f, 0xda, 0xae, 0xaa, 0x25,
-    0x49, 0x1b, 0x0f, 0xf0, 0xe6, 0x73, 0xf8, 0xd9, 0xfe, 0xd5, 0x75, 0x51,
-    0x14, 0x4f, 0xe3, 0x67, 0xfb, 0x55, 0xd5, 0x44, 0x65, 0x3f, 0xf8, 0xc7,
-    0xa7, 0x9b, 0x3f, 0xda, 0xae, 0xaa, 0x26, 0x59, 0xfc, 0x6c, 0xff, 0x6a,
-    0xba, 0xa8, 0xa9, 0x27, 0xf1, 0xb3, 0xfd, 0xaa, 0xea, 0xa2, 0xba, 0x9f,
-    0xc6, 0xcf, 0xf6, 0xab, 0xaa, 0x8b, 0x16, 0x7f, 0xe3, 0xd3, 0xcd, 0x9f,
-    0xed, 0x57, 0x55, 0x13, 0xd4, 0xff, 0x5a, 0x6a, 0x62, 0xf2, 0x27, 0x55,
-    0xa0, 0xc8, 0x8c, 0x64, 0xf9, 0xfb, 0xcd, 0x19, 0xb2, 0xe6, 0xeb, 0x4f,
-    0x7c, 0xfb, 0xe1, 0x69, 0xdd, 0xcd, 0x25, 0x38, 0xed, 0x75, 0xa7, 0xff,
-    0xed, 0x05, 0x0e, 0x5f, 0x67, 0xbf, 0x2e, 0x21, 0x32, 0xd3, 0xff, 0xf7,
-    0xc9, 0xd6, 0x8e, 0x66, 0xff, 0xc9, 0xd7, 0xbc, 0xd8, 0xb4, 0xf1, 0x68,
-    0x63, 0xad, 0x3f, 0xf8, 0x87, 0xf1, 0x81, 0x5c, 0x5c, 0x71, 0xe9, 0x68,
-    0xe9, 0xf7, 0x81, 0x14, 0xff, 0xea, 0xb8, 0x60, 0x8d, 0xa2, 0xed, 0x7d,
-    0x69, 0xfb, 0xdc, 0x1f, 0x79, 0xd5, 0xa7, 0xb4, 0xc7, 0xea, 0xd1, 0x47,
-    0x9f, 0xb2, 0xe9, 0xff, 0xc1, 0xe1, 0x98, 0x18, 0xfe, 0x3c, 0x6b, 0xc9,
-    0x69, 0xab, 0x85, 0xa7, 0xf5, 0x5e, 0xb5, 0xc8, 0x58, 0xb4, 0x01, 0xe4,
-    0xf2, 0x2d, 0x16, 0x2a, 0xd8, 0x21, 0xbf, 0xac, 0x9e, 0x1a, 0x3a, 0x21,
-    0x08, 0x4b, 0x6c, 0x85, 0xd6, 0x13, 0x33, 0x82, 0xb6, 0xb4, 0xe0, 0x2b,
-    0xad, 0x2b, 0x5d, 0x13, 0x68, 0xc3, 0x53, 0xdf, 0x77, 0x47, 0x5a, 0x30,
-    0xf3, 0xc8, 0xb2, 0x7f, 0xc5, 0xaa, 0xdb, 0x39, 0x72, 0x3a, 0xd3, 0xe0,
-    0x1c, 0xb2, 0xeb, 0x4f, 0xfd, 0x96, 0xd0, 0xb3, 0xba, 0xa1, 0x5d, 0x69,
-    0xff, 0x5e, 0xb8, 0x60, 0xaf, 0x70, 0x75, 0xa0, 0x91, 0x4e, 0x04, 0xdb,
-    0x43, 0x9c, 0x76, 0xd2, 0xd3, 0xc1, 0xef, 0x81, 0xd6, 0x9f, 0xb2, 0xf6,
-    0xd0, 0x58, 0xb4, 0x61, 0xf6, 0xd0, 0xd8, 0x12, 0xcf, 0xb9, 0x1e, 0xfe,
-    0xb2, 0xd1, 0x88, 0xf1, 0x28, 0x4d, 0xe8, 0xb2, 0x7f, 0xff, 0xf6, 0x98,
-    0xbf, 0xf6, 0x1b, 0x64, 0x1c, 0xbf, 0x2f, 0xef, 0x35, 0xf7, 0xad, 0x3e,
-    0xb3, 0xdf, 0x95, 0xab, 0x4f, 0xde, 0xd7, 0xc5, 0xe5, 0x8b, 0x4f, 0xf8,
-    0x09, 0xfb, 0xd6, 0x7c, 0xae, 0xb4, 0xff, 0xab, 0x04, 0xc1, 0x5e, 0xe0,
-    0xeb, 0x43, 0xcf, 0xe0, 0x47, 0x93, 0xfa, 0xb8, 0xbe, 0xf7, 0x9e, 0x75,
-    0xa7, 0xff, 0x36, 0x71, 0xbd, 0x36, 0xc1, 0x85, 0x75, 0xa7, 0x5f, 0x47,
-    0x5a, 0x36, 0x7c, 0x9c, 0xa4, 0x4f, 0xdc, 0xdf, 0x77, 0xcd, 0xad, 0x3a,
-    0xdb, 0x6d, 0x4a, 0x7f, 0xf6, 0xcb, 0x77, 0xd1, 0x79, 0x0d, 0xf6, 0x7a,
-    0x46, 0x2f, 0xe0, 0x91, 0x53, 0xca, 0x6c, 0x3d, 0x57, 0xbe, 0x3c, 0x91,
-    0x57, 0xe1, 0x50, 0x72, 0x20, 0x84, 0xde, 0xe1, 0x99, 0x3f, 0xdc, 0x3e,
-    0xf7, 0xcf, 0x6b, 0x6b, 0x4b, 0x16, 0x86, 0x3c, 0x91, 0xce, 0xa7, 0xff,
-    0xf8, 0x82, 0xb6, 0x5a, 0xa3, 0xf8, 0xcb, 0xb6, 0x7f, 0x01, 0x69, 0xed,
-    0xb7, 0x16, 0xad, 0x1a, 0x44, 0x2d, 0xac, 0x53, 0xbb, 0x6e, 0x96, 0x9f,
-    0xcd, 0x5f, 0xd5, 0xb5, 0xea, 0x53, 0x5b, 0x6a, 0x51, 0xe9, 0xe5, 0xda,
-    0x67, 0x3b, 0x65, 0xe7, 0x48, 0xc6, 0x92, 0x7b, 0x97, 0xf3, 0xb5, 0xa2,
-    0xe7, 0xa7, 0xe4, 0x5f, 0x35, 0xb7, 0x5a, 0x58, 0xb4, 0xb8, 0xc3, 0x4d,
-    0xb1, 0x79, 0xff, 0xaf, 0x7f, 0x41, 0xbd, 0xd3, 0x6e, 0xeb, 0x43, 0x1f,
-    0x67, 0x24, 0xf3, 0xfe, 0xd9, 0x7e, 0x84, 0xeb, 0xbc, 0xd2, 0xd3, 0xfc,
-    0x56, 0x86, 0x6c, 0xb9, 0x12, 0xd3, 0xfa, 0xbf, 0xbb, 0x81, 0x0e, 0xb4,
-    0x3d, 0x19, 0x84, 0x45, 0xe7, 0x40, 0xa3, 0x89, 0xd8, 0x5f, 0x5a, 0x7a,
-    0xbe, 0xcf, 0x5a, 0x2c, 0x37, 0x47, 0x1a, 0x9f, 0xf3, 0x71, 0xa6, 0x3f,
-    0x74, 0x4e, 0x2d, 0x18, 0x7c, 0x3f, 0x22, 0x9f, 0xb6, 0xdf, 0xf7, 0x01,
-    0x69, 0xfd, 0xee, 0x98, 0x2d, 0xae, 0x16, 0x9f, 0xf8, 0xbd, 0xd3, 0x75,
-    0x85, 0x40, 0x25, 0xa7, 0xff, 0xfc, 0x47, 0xa0, 0xe1, 0xe6, 0xb7, 0x09,
-    0xde, 0x06, 0x7b, 0x4f, 0x5c, 0x5e, 0xb0, 0xc9, 0x8d, 0xd1, 0x65, 0xcd,
-    0x39, 0x42, 0x9f, 0xfe, 0xcd, 0xdf, 0x1c, 0xd3, 0x3b, 0xcd, 0x69, 0x96,
-    0x9f, 0xff, 0xff, 0x16, 0x71, 0x72, 0xcb, 0x7c, 0x67, 0x0f, 0xb9, 0x6e,
-    0xe5, 0xc7, 0xbe, 0xf3, 0x62, 0xd3, 0xff, 0xe2, 0x0f, 0x7c, 0x0d, 0xb2,
-    0xbd, 0x10, 0x72, 0xf5, 0xa0, 0x13, 0x32, 0xda, 0x83, 0xb8, 0x45, 0xcf,
-    0xeb, 0x2b, 0x62, 0x18, 0x1e, 0xb4, 0xd5, 0xea, 0xd3, 0xf7, 0xea, 0xfa,
-    0xa7, 0xad, 0x2e, 0x16, 0x98, 0xad, 0x5a, 0x61, 0x84, 0xb4, 0x38, 0x6b,
-    0x44, 0x2d, 0x39, 0xf5, 0xea, 0xd3, 0x5b, 0x6a, 0xd0, 0xf4, 0x6b, 0x60,
-    0xb7, 0x4b, 0x08, 0xfa, 0xe4, 0x56, 0x8e, 0x4e, 0xd8, 0xc2, 0x48, 0xc7,
-    0xad, 0x3f, 0xff, 0xfb, 0x6d, 0xee, 0xb9, 0xa1, 0xcf, 0x5e, 0xe6, 0x00,
-    0xf7, 0xbe, 0x00, 0x96, 0x8e, 0x11, 0x4e, 0x45, 0xd3, 0xfd, 0x9b, 0xd3,
-    0x7f, 0x79, 0xd5, 0xa7, 0x37, 0xdc, 0x5a, 0x31, 0x55, 0xa1, 0x1b, 0xd4,
-    0xa0, 0x70, 0x23, 0xf2, 0x37, 0x9d, 0x7a, 0x71, 0x69, 0xec, 0xdd, 0x3a,
-    0xad, 0x1c, 0x1b, 0xf0, 0x1c, 0x9f, 0xfb, 0x3f, 0xfc, 0x73, 0x59, 0x7a,
-    0x05, 0xa7, 0xf0, 0x37, 0xf9, 0xee, 0x99, 0x69, 0xf1, 0x0f, 0xaa, 0x7a,
-    0xd3, 0xf1, 0x31, 0xec, 0xcb, 0x56, 0x9a, 0xdb, 0x56, 0x8e, 0x9f, 0x67,
-    0xc9, 0xed, 0x2d, 0x9f, 0xec, 0x72, 0x9c, 0xae, 0x1f, 0x74, 0x8c, 0x6c,
-    0x27, 0x7a, 0xc7, 0x5a, 0x7f, 0x60, 0xb1, 0xc6, 0xe2, 0xeb, 0x43, 0x1e,
-    0x7e, 0x0e, 0x4f, 0xfe, 0xc0, 0x16, 0x68, 0xb8, 0xbe, 0xb9, 0xba, 0xd3,
-    0xf0, 0x50, 0xbd, 0xc0, 0x5a, 0x7e, 0x39, 0x79, 0x67, 0xc7, 0x5a, 0x75,
-    0xb6, 0xda, 0x94, 0xff, 0xcd, 0xf0, 0xe6, 0x86, 0xc7, 0x18, 0xeb, 0x18,
-    0xbf, 0x8f, 0x3a, 0xa9, 0xe7, 0x21, 0x9c, 0x10, 0x99, 0xb9, 0x06, 0xd2,
-    0xb9, 0x2b, 0xb5, 0x3e, 0x7f, 0xfd, 0x5e, 0xe6, 0x00, 0xe3, 0x03, 0x05,
-    0x85, 0xf5, 0xa7, 0x57, 0xec, 0x5c, 0x82, 0x53, 0xcf, 0xce, 0x1e, 0xb9,
-    0x04, 0xa7, 0x5c, 0xbd, 0x5c, 0x82, 0x53, 0x5b, 0x6a, 0xe4, 0x12, 0x8e,
-    0xa2, 0x99, 0xc2, 0x9d, 0x97, 0xda, 0x53, 0x35, 0x6d, 0x32, 0x09, 0x0c,
-    0x6f, 0xe7, 0xec, 0xd6, 0x60, 0x0e, 0xb4, 0xea, 0x01, 0x1d, 0x39, 0x70,
-    0x8c, 0xe7, 0x66, 0x73, 0xde, 0x59, 0x5a, 0x5a, 0x59, 0xe7, 0x3e, 0x91,
-    0x23, 0x4f, 0xe7, 0x1f, 0xcd, 0x9b, 0x2b, 0x12, 0x9f, 0xc4, 0xfe, 0x6a,
-    0xfa, 0x25, 0xa4, 0xf4, 0xa7, 0x57, 0xec, 0x4a, 0x12, 0x86, 0x36, 0xdd,
-    0x10, 0x38, 0xe4, 0xf0, 0x30, 0xba, 0x91, 0x8d, 0x6c, 0x32, 0x31, 0x72,
-    0x12, 0xb3, 0xdc, 0xd7, 0xde, 0xb4, 0x8e, 0xb4, 0xd8, 0xff, 0x4d, 0x93,
-    0x84, 0x53, 0xeb, 0x71, 0xc2, 0x3a, 0xd3, 0xf7, 0x59, 0xc7, 0xb0, 0x92,
-    0x91, 0x2d, 0x3f, 0xd6, 0x72, 0xff, 0x75, 0xcd, 0x02, 0xd3, 0xf1, 0x31,
-    0xec, 0xcb, 0x56, 0x9f, 0x65, 0x87, 0xc3, 0xad, 0x3a, 0xe5, 0xf5, 0xa0,
-    0x0f, 0x0b, 0x64, 0xf3, 0xf0, 0x50, 0x85, 0xcd, 0xd6, 0x9f, 0x88, 0x71,
-    0x98, 0xe6, 0xf5, 0x30, 0x8c, 0x0f, 0xf9, 0xe5, 0xdb, 0xf6, 0x43, 0x18,
-    0x9e, 0x61, 0x14, 0x04, 0x66, 0xb3, 0xc1, 0x96, 0x72, 0xb4, 0x3d, 0x5e,
-    0x7e, 0x15, 0xea, 0x1f, 0x80, 0xb1, 0x78, 0xfe, 0xb6, 0x69, 0x3f, 0xfe,
-    0xb3, 0xff, 0xcf, 0x68, 0xfa, 0xad, 0x83, 0x38, 0xb4, 0xff, 0xff, 0xee,
-    0x7f, 0xcb, 0x81, 0x42, 0xbd, 0xf0, 0x7c, 0xe7, 0xf5, 0xeb, 0x69, 0x96,
-    0x9d, 0x6d, 0xb6, 0xa5, 0x33, 0xd9, 0x23, 0x17, 0xf0, 0xc9, 0x8a, 0x58,
-    0xad, 0xf8, 0x4d, 0x4f, 0xd6, 0x38, 0xff, 0x33, 0xf6, 0x2d, 0x3d, 0xaa,
-    0xe2, 0xd5, 0xa7, 0xd6, 0x7c, 0x98, 0xeb, 0x4f, 0xff, 0x30, 0xdb, 0x06,
-    0x15, 0x6c, 0x43, 0x03, 0xd6, 0x87, 0x41, 0x19, 0xe4, 0x6d, 0x44, 0x7b,
-    0x27, 0x9c, 0x37, 0xae, 0x2d, 0x39, 0xe4, 0x0b, 0x4f, 0xdb, 0x67, 0x03,
-    0x9b, 0xad, 0x16, 0x1f, 0x3b, 0xc7, 0xf6, 0x35, 0x35, 0x5d, 0x69, 0xb9,
-    0xf3, 0x6b, 0x40, 0x1b, 0x2e, 0x45, 0x66, 0xa3, 0x3a, 0x4e, 0x84, 0x39,
-    0xd3, 0x9a, 0xf9, 0xa8, 0x8d, 0xa5, 0x79, 0xd9, 0x18, 0x3b, 0xc8, 0x3d,
-    0x8f, 0x57, 0x25, 0x95, 0x0f, 0x0b, 0xbe, 0xc2, 0x9f, 0x82, 0x42, 0x86,
-    0xfb, 0x91, 0xc1, 0xfe, 0x1b, 0x47, 0x8c, 0xf3, 0x53, 0x84, 0x75, 0x09,
-    0x10, 0x21, 0xbc, 0xae, 0x0d, 0xcf, 0x22, 0xf3, 0x1b, 0xb7, 0x94, 0x60,
-    0xa3, 0x42, 0x9d, 0xd5, 0x8a, 0x7f, 0xf1, 0x8f, 0x4f, 0x36, 0x7f, 0xb5,
-    0x5d, 0x54, 0x51, 0x53, 0xf8, 0xd9, 0xfe, 0xd5, 0x75, 0x51, 0x74, 0x4f,
-    0xfc, 0x6a, 0x79, 0xb3, 0xfd, 0xaa, 0xea, 0xa2, 0x4b, 0x9f, 0xc6, 0xcf,
-    0xf6, 0xab, 0xaa, 0x8b, 0xca, 0x19, 0xd3, 0x31, 0x58, 0x4e, 0xf3, 0xec,
-    0x3e, 0xea, 0xab, 0x87, 0xc7, 0x3e, 0xd4, 0x32, 0x82, 0xd0, 0x98, 0x5e,
-    0x3d, 0x4d, 0xaa, 0xf2, 0x7c, 0xed, 0x3e, 0x7f, 0xf1, 0x8f, 0x4f, 0x36,
-    0x7f, 0xb5, 0x5d, 0x54, 0x4b, 0x33, 0x73, 0xa5, 0xa7, 0xbb, 0x55, 0xd5,
-    0x44, 0x67, 0x3f, 0x13, 0x1e, 0xcc, 0xb5, 0x69, 0x81, 0xeb, 0x48, 0xeb,
-    0x4f, 0x8b, 0xfa, 0xa3, 0x7c, 0xf4, 0x8e, 0x5a, 0x30, 0xac, 0xfd, 0x4f,
-    0x2f, 0xd7, 0xd6, 0x9f, 0xfa, 0xe5, 0xcd, 0xf0, 0x39, 0x7e, 0x5d, 0x69,
-    0xd7, 0x6e, 0x16, 0x8f, 0xa6, 0xac, 0x77, 0xf0, 0x4c, 0xb9, 0x5e, 0xd1,
-    0x27, 0xf9, 0xf7, 0xc6, 0xf0, 0xd7, 0x3a, 0xd3, 0xff, 0x6c, 0xbf, 0x42,
-    0xf1, 0xc3, 0xd8, 0xeb, 0x4f, 0x81, 0x9f, 0x7a, 0x5a, 0x08, 0xfa, 0xdd,
-    0xa3, 0x4e, 0xa1, 0x9e, 0xb4, 0xff, 0xf7, 0x3b, 0xbf, 0x0f, 0x60, 0x15,
-    0xfc, 0x72, 0x0b, 0x4e, 0x6d, 0xb2, 0xd2, 0x2f, 0x4f, 0xb9, 0xda, 0xa4,
-    0xda, 0x71, 0x69, 0xfd, 0xc5, 0xc3, 0x3f, 0xeb, 0x2d, 0x04, 0x79, 0x54,
-    0x2f, 0x3d, 0xbd, 0x73, 0xe4, 0xb4, 0xff, 0x6c, 0xad, 0xbb, 0x6a, 0xbc,
-    0xeb, 0x4b, 0x08, 0xf8, 0x8e, 0x4d, 0x3f, 0xed, 0x31, 0x68, 0x98, 0x1c,
-    0xe5, 0x69, 0xfd, 0x97, 0x6d, 0xdf, 0x44, 0xb4, 0x8c, 0xe9, 0x2e, 0x49,
-    0x64, 0x6d, 0xbc, 0x25, 0x39, 0x0a, 0x4d, 0x11, 0xd4, 0x22, 0x84, 0xe8,
-    0x10, 0x82, 0xb9, 0x37, 0x91, 0xec, 0xff, 0xe3, 0x1e, 0x9e, 0x6c, 0xff,
-    0x6a, 0xba, 0xa8, 0x9a, 0x67, 0xff, 0x18, 0xf4, 0xf3, 0x67, 0xfb, 0x55,
-    0xd5, 0x44, 0xe3, 0x3f, 0xf8, 0xc7, 0xa7, 0x9b, 0x3f, 0xda, 0xae, 0xaa,
-    0x28, 0x19, 0xf7, 0xfb, 0x55, 0xd5, 0x45, 0xc1, 0x33, 0x12, 0xd3, 0xf8,
-    0x1e, 0xc5, 0xee, 0x1d, 0x69, 0x1b, 0x0f, 0xe3, 0x46, 0x7c, 0x8a, 0x4f,
-    0xe2, 0x30, 0xcc, 0x2c, 0xb5, 0x69, 0xfe, 0x79, 0xb3, 0xfd, 0xaa, 0xea,
-    0xa2, 0x49, 0x91, 0xb4, 0x7e, 0xf4, 0x67, 0x3e, 0xa3, 0x7b, 0x68, 0x96,
-    0x8b, 0x19, 0x01, 0x4f, 0x9c, 0xe7, 0xec, 0x2c, 0x1c, 0x55, 0x12, 0xad,
-    0xe1, 0x9b, 0xb8, 0x56, 0x3b, 0x27, 0x9f, 0xf0, 0xf4, 0x6c, 0xff, 0x6a,
-    0xba, 0xa8, 0xb5, 0x27, 0xfd, 0x4f, 0x36, 0x7f, 0xb5, 0x5d, 0x54, 0x4a,
-    0xb0, 0xb4, 0x8c, 0x3a, 0x27, 0x4e, 0x94, 0xea, 0x93, 0x3f, 0x8d, 0x9f,
-    0xed, 0x57, 0x55, 0x11, 0x4c, 0xfe, 0x36, 0x7f, 0xb5, 0x5d, 0x54, 0x46,
-    0x93, 0x73, 0xa5, 0xa7, 0xbb, 0x55, 0xd5, 0x45, 0x3f, 0x36, 0xd9, 0x68,
-    0xf9, 0xe1, 0x6c, 0xb6, 0x7f, 0xdb, 0x71, 0xb7, 0xaa, 0x73, 0xc6, 0x2d,
-    0x3f, 0xf7, 0x3b, 0xff, 0x2e, 0x5f, 0xb4, 0xe5, 0xd6, 0x9f, 0xfb, 0x1c,
-    0xe4, 0x2c, 0xbf, 0x69, 0xcb, 0xad, 0x23, 0x3a, 0x49, 0xa6, 0x62, 0xc9,
-    0x11, 0x02, 0x0e, 0xd2, 0x67, 0xff, 0x18, 0xf4, 0xf3, 0x67, 0xfb, 0x55,
-    0xd5, 0x44, 0xd5, 0x3f, 0x8d, 0x9f, 0xed, 0x57, 0x55, 0x15, 0xdc, 0xf7,
-    0x6a, 0xba, 0xa8, 0xaf, 0xa7, 0x5b, 0x6d, 0xa9, 0x48, 0x12, 0x31, 0x7f,
-    0x1f, 0x3e, 0xa3, 0xa6, 0x4e, 0x7b, 0x70, 0xb4, 0xe7, 0x33, 0x4b, 0x4f,
-    0xfb, 0x35, 0x6d, 0x6e, 0xed, 0x96, 0x2d, 0x2d, 0xad, 0x3f, 0xc5, 0xfc,
-    0xb0, 0x9f, 0x9e, 0xad, 0x1f, 0x3c, 0x8a, 0x10, 0x91, 0xb1, 0x30, 0x17,
-    0x08, 0xa8, 0x70, 0x07, 0x2f, 0x08, 0x69, 0xff, 0xc6, 0x3d, 0x3c, 0xd9,
-    0xfe, 0xd5, 0x75, 0x51, 0x3d, 0xcf, 0xe3, 0x67, 0xfb, 0x55, 0xd5, 0x45,
-    0xc3, 0x3f, 0xf1, 0xa9, 0xe6, 0xcf, 0xf6, 0xab, 0xaa, 0x89, 0x3e, 0x19,
-    0x76, 0x26, 0xc3, 0xa7, 0x9f, 0x0f, 0x1c, 0x57, 0x54, 0xdc, 0x55, 0xfc,
-    0x75, 0x3a, 0x54, 0xba, 0xab, 0xb3, 0xe9, 0xfc, 0x6c, 0xff, 0x6a, 0xba,
-    0xa8, 0x8a, 0xa7, 0xdf, 0xed, 0x57, 0x55, 0x14, 0xcc, 0xfc, 0x5a, 0xca,
-    0xe0, 0x4b, 0x48, 0xd8, 0x7b, 0xfe, 0x46, 0x73, 0xff, 0x8c, 0x7a, 0x79,
-    0xb3, 0xfd, 0xaa, 0xea, 0xa2, 0x67, 0x9f, 0xfc, 0x63, 0xd3, 0xcd, 0x9f,
-    0xed, 0x57, 0x55, 0x14, 0x6c, 0x58, 0x9b, 0x8f, 0xb0, 0x9d, 0x1c, 0xab,
-    0x6a, 0xb3, 0xff, 0x1a, 0x9e, 0x6c, 0xff, 0x6a, 0xba, 0xa8, 0x8e, 0xa6,
-    0xe7, 0x4b, 0x4f, 0xc5, 0x81, 0xee, 0x58, 0xb4, 0xf7, 0x6a, 0xba, 0xa8,
-    0xa6, 0xa7, 0xd9, 0xc5, 0xb5, 0xa5, 0xa3, 0xe7, 0xaa, 0x05, 0xb3, 0xff,
-    0xb4, 0x5b, 0xbd, 0x71, 0x8e, 0x36, 0xae, 0xb4, 0xff, 0xcf, 0xd1, 0x05,
-    0x97, 0xe5, 0xbe, 0xe2, 0xd3, 0x30, 0xeb, 0x4f, 0xc4, 0xc7, 0xb3, 0x2d,
-    0x5a, 0x7f, 0xfb, 0x37, 0xac, 0x7f, 0xe8, 0x37, 0xaa, 0x7a, 0xd3, 0x30,
-    0x96, 0x9f, 0xd5, 0xc3, 0x74, 0xba, 0x6f, 0xa2, 0x3c, 0x0b, 0x46, 0x4d,
-    0x8d, 0x23, 0xf6, 0xf0, 0xa5, 0x91, 0x9d, 0x25, 0x4d, 0xde, 0x8b, 0xe4,
-    0x20, 0x7a, 0x43, 0xc2, 0x56, 0xe3, 0x43, 0x9f, 0x7f, 0xb5, 0x5d, 0x54,
-    0x55, 0x53, 0xfe, 0xa7, 0x9b, 0x3f, 0xda, 0xae, 0xaa, 0x26, 0xd9, 0x1b,
-    0x0f, 0xf0, 0xe6, 0x73, 0xf8, 0xd9, 0xfe, 0xd5, 0x75, 0x51, 0x5f, 0xcf,
-    0xe3, 0x67, 0xfb, 0x55, 0xd5, 0x45, 0x8d, 0x3f, 0xd9, 0xbd, 0xe7, 0xfd,
-    0xc0, 0x5a, 0x70, 0xef, 0xc5, 0xa7, 0x17, 0xcc, 0x07, 0xa7, 0x73, 0x79,
-    0xf7, 0xfb, 0x55, 0xd5, 0x45, 0xab, 0x3f, 0xea, 0x79, 0xb3, 0xfd, 0xaa,
-    0xea, 0xa2, 0x82, 0x91, 0xb0, 0xff, 0x0e, 0x67, 0x3e, 0xff, 0x6a, 0xba,
-    0xa8, 0xb8, 0xa7, 0xa9, 0xd4, 0xac, 0x5a, 0x79, 0xd5, 0xd4, 0xba, 0xb4,
-    0xfe, 0x63, 0xd3, 0xb6, 0xfb, 0xaa, 0xd2, 0x36, 0x22, 0xd3, 0x46, 0x74,
-    0x49, 0xb2, 0x79, 0xf7, 0xfb, 0x55, 0xd5, 0x45, 0xd3, 0x3e, 0x60, 0xaf,
-    0x59, 0x69, 0x1b, 0x0f, 0x60, 0x8c, 0xe7, 0xff, 0xb0, 0x36, 0x47, 0xd3,
-    0x10, 0x57, 0xc7, 0x5a, 0x7f, 0xfe, 0x39, 0xb1, 0xed, 0xf1, 0x78, 0xbe,
-    0x98, 0xf4, 0x0b, 0x4f, 0x65, 0xae, 0x81, 0xd6, 0x9f, 0xff, 0xb4, 0x1c,
-    0xd7, 0x9f, 0x1f, 0xac, 0x1f, 0x3d, 0x73, 0x95, 0xa6, 0xee, 0x2d, 0x0c,
-    0x7e, 0xe3, 0xb1, 0x4f, 0xf9, 0xf9, 0xff, 0xd3, 0xec, 0xcb, 0x56, 0x9c,
-    0x14, 0x6f, 0x9f, 0x0e, 0xc8, 0xa7, 0xfd, 0xf6, 0xe3, 0x55, 0x7d, 0xb5,
-    0xab, 0x4d, 0xce, 0x96, 0x9f, 0x06, 0x6a, 0xb6, 0xb4, 0xfc, 0xfa, 0xc7,
-    0x6d, 0xea, 0xd0, 0xe8, 0x9e, 0x9f, 0x9a, 0x49, 0x67, 0xe1, 0x53, 0x8f,
-    0x2d, 0xad, 0x3f, 0xff, 0xcd, 0xee, 0xb3, 0x8d, 0x85, 0x68, 0x55, 0x7b,
-    0xd7, 0xfa, 0xb4, 0xfe, 0xcc, 0xc1, 0xc7, 0xa7, 0xad, 0x03, 0xa2, 0x63,
-    0x6c, 0xd3, 0xff, 0xfd, 0xa6, 0x0e, 0x6f, 0xa2, 0xbe, 0xf5, 0x9e, 0x95,
-    0xf0, 0x16, 0x9e, 0xed, 0x57, 0x55, 0x12, 0x64, 0xfb, 0xbe, 0x6b, 0xf3,
-    0x1d, 0x07, 0x45, 0x69, 0xfd, 0x9c, 0x6f, 0x74, 0xfb, 0xad, 0x0c, 0x7e,
-    0x5f, 0x3f, 0x87, 0xa6, 0x4b, 0xf6, 0x53, 0xc2, 0x7e, 0x7e, 0xf4, 0x4e,
-    0x8d, 0xc5, 0xe4, 0xb4, 0xff, 0xed, 0x16, 0xf8, 0xe9, 0x7e, 0xee, 0x35,
-    0x2d, 0x3e, 0xcd, 0x91, 0xe9, 0x69, 0xfe, 0x6d, 0x6c, 0xac, 0x3b, 0x75,
-    0x69, 0xff, 0xf5, 0x77, 0xed, 0xa0, 0xcd, 0xe1, 0xad, 0xb6, 0xd4, 0xa1,
-    0xe8, 0xb3, 0xb9, 0x2e, 0xce, 0x27, 0xee, 0xf7, 0x9e, 0x83, 0x2d, 0x3f,
-    0xfb, 0x90, 0xad, 0xb7, 0xef, 0xa6, 0x0d, 0xad, 0x3a, 0xdb, 0x6d, 0x4a,
-    0x7e, 0xfd, 0x58, 0x72, 0xba, 0x46, 0x2f, 0xe7, 0xd8, 0x7f, 0xe7, 0x9d,
-    0x69, 0xf1, 0x6e, 0xcc, 0xb5, 0x69, 0xfe, 0x6e, 0xe8, 0x39, 0xdb, 0x5d,
-    0x69, 0xff, 0xf6, 0xf5, 0x8f, 0xf1, 0xdd, 0x16, 0xf8, 0xe9, 0x7d, 0x68,
-    0xfa, 0x2e, 0x0e, 0x50, 0x07, 0x13, 0xfd, 0x94, 0x2f, 0x19, 0xed, 0x3d,
-    0x69, 0xfe, 0xf0, 0x1e, 0x2f, 0x65, 0x56, 0x96, 0x9f, 0xfd, 0x56, 0x68,
-    0x98, 0x31, 0xc2, 0xf9, 0x2d, 0x18, 0x8e, 0xbd, 0x97, 0xf2, 0x73, 0x69,
-    0xe4, 0xf8, 0x33, 0x55, 0xb5, 0xa7, 0xfc, 0xf6, 0x0b, 0x06, 0xf8, 0x72,
-    0xf5, 0xa3, 0xce, 0x7c, 0xae, 0x12, 0xcf, 0x6e, 0xcc, 0xf5, 0x69, 0xfe,
-    0x21, 0xc6, 0xf8, 0x8e, 0x57, 0x5a, 0x1c, 0x3d, 0xdf, 0x22, 0x39, 0xd6,
-    0xdb, 0x6a, 0xd3, 0xff, 0xec, 0x3e, 0xc1, 0x85, 0x9e, 0xdf, 0x0e, 0xdd,
-    0x48, 0xc5, 0xfc, 0x62, 0x65, 0x65, 0x08, 0x4d, 0x22, 0xc8, 0x96, 0x9f,
-    0xb4, 0xde, 0x3a, 0x56, 0x2d, 0x3f, 0x7e, 0xbe, 0x0c, 0xf5, 0xa1, 0xd3,
-    0x9f, 0x4f, 0xa1, 0xff, 0x2f, 0x99, 0xe6, 0x74, 0x99, 0x2f, 0xfe, 0x63,
-    0x53, 0x17, 0xbe, 0x18, 0xb9, 0x1a, 0x48, 0xe6, 0xdd, 0x37, 0xe2, 0x1f,
-    0xa4, 0x60, 0xe1, 0x6d, 0x33, 0x81, 0xe5, 0xe3, 0xcd, 0xdc, 0x79, 0xc3,
-    0x42, 0x76, 0x7f, 0xf7, 0xf2, 0xdf, 0x75, 0x8e, 0x06, 0x7d, 0xeb, 0x4e,
-    0xfe, 0x79, 0xd6, 0x96, 0x7c, 0xfa, 0x8e, 0x97, 0x3f, 0xf6, 0x68, 0x1a,
-    0xc1, 0xb5, 0xaa, 0xf3, 0xad, 0x3f, 0x57, 0x4d, 0x6d, 0xb6, 0xad, 0x23,
-    0x33, 0x36, 0x97, 0x13, 0x07, 0x5b, 0xec, 0x3f, 0xfe, 0x65, 0x54, 0x9b,
-    0xe0, 0x8d, 0xc2, 0xe4, 0xee, 0xd2, 0x21, 0xed, 0xa9, 0x3f, 0xb2, 0xaf,
-    0x78, 0x87, 0x67, 0xc9, 0xce, 0x7d, 0xa8, 0x48, 0x8a, 0x15, 0xd7, 0x86,
-    0xde, 0xe1, 0x33, 0xcd, 0x37, 0x2e, 0x7f, 0x1b, 0x3f, 0xda, 0xae, 0xaa,
-    0x23, 0x59, 0xf7, 0xfb, 0x55, 0xd5, 0x45, 0x4b, 0x35, 0x75, 0x51, 0x0d,
-    0x48, 0xd8, 0x7a, 0xb4, 0x67, 0x3f, 0xf1, 0xa9, 0xe6, 0xcf, 0xf6, 0xab,
-    0xaa, 0x88, 0xfa, 0x7d, 0xfe, 0xd5, 0x75, 0x51, 0x63, 0xcf, 0xf0, 0x50,
-    0xfa, 0x26, 0x0b, 0x16, 0x91, 0xb0, 0xfa, 0x9c, 0x33, 0x9d, 0xe6, 0xb3,
-    0x8e, 0xb4, 0xfc, 0xe8, 0x86, 0x6a, 0xb6, 0xb4, 0xff, 0x05, 0x0a, 0xf5,
-    0xbd, 0xd2, 0xd3, 0xc2, 0x39, 0x7d, 0x69, 0xff, 0xfe, 0x6f, 0x86, 0x7b,
-    0x9a, 0xd1, 0x77, 0x45, 0xbb, 0xd7, 0x0b, 0x41, 0x22, 0x1b, 0x44, 0x33,
-    0xaa, 0xba, 0xa8, 0xb4, 0x27, 0xfd, 0x56, 0xba, 0xf6, 0xbf, 0x66, 0x5a,
-    0xb4, 0x74, 0xfa, 0xbe, 0x4f, 0x3f, 0xff, 0xfc, 0x5f, 0xd5, 0x01, 0x36,
-    0xcb, 0x4d, 0x7d, 0x65, 0xff, 0x9d, 0xcd, 0xad, 0x1f, 0x44, 0xd6, 0x88,
-    0xa7, 0xd4, 0xf0, 0xa1, 0x96, 0x9f, 0x9d, 0x8d, 0xb0, 0x61, 0x2d, 0x3e,
-    0x6d, 0xe6, 0x99, 0x69, 0xff, 0xec, 0xdd, 0xf1, 0xcd, 0x33, 0xbc, 0xd6,
-    0x99, 0x68, 0xb0, 0xfd, 0x40, 0x96, 0x19, 0x18, 0xd9, 0x0a, 0x49, 0xff,
-    0x09, 0xbb, 0xa2, 0x72, 0xaa, 0xc5, 0xa7, 0xf8, 0xb9, 0x07, 0xfd, 0xb3,
-    0xd5, 0xa7, 0xff, 0xfb, 0x58, 0xfe, 0xe8, 0xb4, 0xd9, 0xf7, 0xec, 0x18,
-    0xf7, 0x5a, 0x59, 0x74, 0x4e, 0x6c, 0xe2, 0x7e, 0xfb, 0x6e, 0xcc, 0xb5,
-    0x69, 0xf6, 0xf5, 0x40, 0x3a, 0xd3, 0xfc, 0x1c, 0xd9, 0xee, 0xb0, 0x19,
-    0x69, 0x66, 0x1f, 0x07, 0x49, 0xe0, 0x91, 0x68, 0x10, 0x8e, 0x9f, 0xd9,
-    0xa7, 0x0b, 0x44, 0x75, 0xa7, 0xf3, 0xf3, 0xf7, 0x06, 0xb1, 0x69, 0xd9,
-    0xaa, 0x5a, 0x7c, 0xe7, 0xbc, 0xf2, 0x0b, 0x47, 0xa7, 0x8b, 0x43, 0x53,
-    0xfd, 0x99, 0x6e, 0x77, 0xb9, 0x6a, 0xd1, 0x89, 0x81, 0xf0, 0x65, 0x4e,
-    0xf7, 0x22, 0x9e, 0xcd, 0x56, 0xd6, 0x9f, 0x88, 0x39, 0x7e, 0x5d, 0x69,
-    0xff, 0xfe, 0x20, 0xe5, 0xf9, 0x7f, 0x19, 0xb6, 0xe0, 0x55, 0xad, 0x12,
-    0xd0, 0x3a, 0x24, 0xf6, 0x59, 0x23, 0x3a, 0x4b, 0xeb, 0xfe, 0x61, 0x2b,
-    0x18, 0x7b, 0x0b, 0xcc, 0x8c, 0x98, 0xe4, 0x7a, 0x87, 0x60, 0x89, 0x82,
-    0x1b, 0x57, 0x86, 0xe6, 0xe3, 0x29, 0xf2, 0x3d, 0x1a, 0x16, 0x73, 0xf3,
-    0xa9, 0x7a, 0x0c, 0xe2, 0xd3, 0xd9, 0xaa, 0xda, 0xd2, 0x74, 0x70, 0xf4,
-    0x00, 0xc2, 0x7d, 0xfe, 0xd5, 0x75, 0x51, 0x6b, 0x4f, 0xfa, 0x9e, 0x6c,
-    0xff, 0x6a, 0xba, 0xa8, 0xa0, 0xe4, 0x6f, 0x31, 0x14, 0xf8, 0x56, 0x73,
-    0x39, 0xff, 0xc6, 0x3d, 0x3c, 0xd9, 0xfe, 0xd5, 0x75, 0x51, 0x45, 0xcf,
-    0xe3, 0x67, 0xfb, 0x55, 0xd5, 0x45, 0xd5, 0x0f, 0x65, 0xd4, 0xf6, 0x12,
-    0x64, 0x4c, 0x78, 0x5c, 0xea, 0x7e, 0x1c, 0x51, 0xf1, 0xdc, 0xeb, 0x6a,
-    0xb3, 0xef, 0xf6, 0xab, 0xaa, 0x88, 0x86, 0x76, 0xb3, 0xd5, 0xa7, 0xb1,
-    0xd9, 0x7d, 0x69, 0x1b, 0x0f, 0xc7, 0x83, 0x3f, 0x38, 0xe4, 0xfe, 0x36,
-    0x7f, 0xb5, 0x5d, 0x54, 0x46, 0xd3, 0xf8, 0xd9, 0xfe, 0xd5, 0x75, 0x51,
-    0x4d, 0xcf, 0xe3, 0x67, 0xfb, 0x55, 0xd5, 0x45, 0x41, 0x3f, 0x8d, 0x9f,
-    0xed, 0x57, 0x55, 0x15, 0x34, 0xfb, 0xfd, 0xaa, 0xea, 0xa2, 0xb0, 0x9f,
-    0x15, 0xbe, 0xf3, 0xf5, 0xa7, 0xf9, 0xe6, 0xcf, 0xf6, 0xab, 0xaa, 0x88,
-    0xfe, 0x76, 0x35, 0x8b, 0x48, 0xd8, 0x8b, 0xce, 0x99, 0xd1, 0x50, 0x22,
-    0x4f, 0xfe, 0x31, 0xe9, 0xe6, 0xcf, 0xf6, 0xab, 0xaa, 0x89, 0xbe, 0x7f,
-    0xe3, 0xd3, 0xcd, 0x9f, 0xed, 0x57, 0x55, 0x13, 0xf4, 0xf9, 0xd0, 0x74,
-    0x7c, 0xb3, 0x85, 0xa5, 0xa5, 0xa1, 0xd3, 0x1e, 0x2f, 0x26, 0xb3, 0xfd,
-    0xe6, 0xad, 0x56, 0xf7, 0x55, 0x6a, 0xd3, 0xef, 0x03, 0xfe, 0xba, 0xb4,
-    0xfb, 0xcc, 0x74, 0xe2, 0x1d, 0x96, 0x9f, 0xfe, 0x66, 0x66, 0x66, 0x66,
-    0x66, 0xe2, 0xeb, 0x4f, 0xbe, 0x56, 0x51, 0xd2, 0x9a, 0xdb, 0x52, 0x8c,
-    0x37, 0xf6, 0x93, 0xcb, 0x94, 0x8c, 0x68, 0x61, 0x91, 0x95, 0x78, 0x54,
-    0xcf, 0x7c, 0x09, 0x96, 0x9f, 0xff, 0xb0, 0xf7, 0xdf, 0xea, 0xcf, 0x74,
-    0xd7, 0xdf, 0x3a, 0x5a, 0x7d, 0x5d, 0xd1, 0x7d, 0x69, 0xff, 0xff, 0xc1,
-    0x43, 0x97, 0xd9, 0xfe, 0x34, 0x47, 0xf5, 0xbc, 0x79, 0x73, 0xa2, 0xfa,
-    0xd3, 0xd4, 0xf2, 0xe1, 0x68, 0xf5, 0x32, 0x4f, 0xaf, 0x68, 0x98, 0x1f,
-    0xe7, 0xff, 0xce, 0xad, 0xc3, 0xe8, 0x05, 0xe0, 0x45, 0xf2, 0x1d, 0x69,
-    0xeb, 0x0e, 0x4f, 0x5a, 0x7d, 0x70, 0xe6, 0x9e, 0xb4, 0xfe, 0xd3, 0x1c,
-    0xc2, 0x13, 0x2d, 0x2f, 0xb1, 0xfd, 0xe1, 0x11, 0x13, 0xcf, 0xf9, 0xb8,
-    0xf4, 0x66, 0xf3, 0xeb, 0x9b, 0xad, 0x3b, 0x31, 0xc5, 0xa5, 0xe7, 0x5a,
-    0x6b, 0xe2, 0xd0, 0x46, 0xa4, 0x42, 0x93, 0xff, 0x7a, 0xdb, 0xab, 0xe6,
-    0xb4, 0x47, 0x5a, 0x76, 0xc9, 0xc5, 0xa1, 0x8f, 0x7e, 0xe8, 0x70, 0xca,
-    0xf6, 0x70, 0x9c, 0xa3, 0x43, 0x70, 0xfb, 0xf0, 0xd5, 0x39, 0x90, 0x91,
-    0x81, 0x13, 0x6f, 0xf3, 0x9c, 0x18, 0xeb, 0x4f, 0xf6, 0x6e, 0xf9, 0xbd,
-    0x60, 0x96, 0x8a, 0x3d, 0x50, 0x1e, 0x9a, 0x81, 0x68, 0xf9, 0xb4, 0x39,
-    0x04, 0xeb, 0x6d, 0xb5, 0x69, 0xeb, 0x00, 0x99, 0x23, 0x17, 0xf3, 0x70,
-    0xf5, 0xa7, 0xf0, 0xed, 0xbb, 0xea, 0xb9, 0x5a, 0x7c, 0x56, 0x67, 0x1b,
-    0x5a, 0x62, 0xea, 0xd2, 0xe1, 0x91, 0x10, 0x38, 0xb9, 0xcd, 0x36, 0x51,
-    0x0c, 0x99, 0xde, 0x90, 0x39, 0x85, 0xbc, 0xff, 0xd6, 0x72, 0x2c, 0x0e,
-    0x5c, 0x6d, 0xd2, 0xd3, 0xff, 0x36, 0x86, 0xf7, 0x65, 0x8e, 0x31, 0xd6,
-    0x9e, 0x6d, 0xf2, 0x75, 0xa6, 0xbf, 0x55, 0x10, 0x74, 0x98, 0xe7, 0x8b,
-    0xb2, 0x29, 0xfa, 0xb4, 0xde, 0x7e, 0x5e, 0xb4, 0x7d, 0x32, 0x83, 0xa3,
-    0x6e, 0x11, 0x4e, 0xc9, 0xe6, 0xff, 0xab, 0x4d, 0x6d, 0xab, 0x43, 0xcd,
-    0x7d, 0xa2, 0xf3, 0xd7, 0x62, 0x76, 0x91, 0x8d, 0x14, 0xfa, 0xe3, 0x7a,
-    0x2b, 0xad, 0x04, 0x7b, 0x83, 0x19, 0x4e, 0xb6, 0xdb, 0x52, 0x84, 0x8c,
-    0x5f, 0xcf, 0x15, 0xa4, 0xe2, 0x51, 0xd3, 0x76, 0x43, 0x30, 0x49, 0xc7,
-    0x6a, 0x1e, 0x40, 0xf9, 0x38, 0x0a, 0xeb, 0x4f, 0x7b, 0x40, 0x25, 0xa7,
-    0xfe, 0x60, 0x10, 0x37, 0x0e, 0x30, 0x09, 0x68, 0x23, 0xfd, 0x10, 0xd5,
-    0xc8, 0x67, 0xcd, 0x9f, 0xd1, 0x2d, 0x3b, 0x0f, 0xe7, 0x5a, 0x7f, 0x65,
-    0xc3, 0x37, 0xa2, 0x5a, 0x3d, 0x3c, 0xf0, 0x1f, 0x87, 0x11, 0x2c, 0xed,
-    0xd2, 0x7e, 0xde, 0x38, 0x0c, 0x75, 0xc4, 0x05, 0x3e, 0xa0, 0xf7, 0x4c,
-    0xa8, 0x80, 0x8c, 0x6e, 0x67, 0xef, 0xb3, 0xeb, 0x8b, 0x56, 0x9f, 0xe6,
-    0xfd, 0x9e, 0x31, 0xc2, 0x3a, 0xd3, 0xc7, 0x6c, 0x3a, 0xd3, 0xe6, 0x11,
-    0x71, 0x75, 0xa7, 0xf7, 0xdc, 0xb9, 0x06, 0xc9, 0x69, 0xee, 0x05, 0xf6,
-    0x4a, 0x6b, 0x6d, 0x4a, 0x18, 0xdd, 0x5a, 0x45, 0x3f, 0x57, 0xde, 0xf0,
-    0x24, 0x8c, 0x68, 0x61, 0x95, 0x6f, 0x64, 0x31, 0x09, 0x7f, 0xe8, 0x7a,
-    0x2e, 0xa3, 0xb0, 0x20, 0xd9, 0x3f, 0x30, 0x8d, 0x9f, 0x73, 0xbe, 0x68,
-    0x65, 0x44, 0x0f, 0x33, 0x72, 0xb4, 0xfb, 0x90, 0x10, 0xcf, 0x5a, 0x7f,
-    0xfc, 0xec, 0x32, 0xfe, 0x06, 0xd3, 0x08, 0xf4, 0xff, 0x18, 0x6f, 0xee,
-    0x2d, 0x3f, 0xed, 0x73, 0x63, 0x78, 0x6d, 0xb7, 0xab, 0x4e, 0xaf, 0xd8,
-    0xb4, 0xd6, 0xda, 0xb4, 0xfe, 0xf9, 0x63, 0xb1, 0x9e, 0x6e, 0x9b, 0x4b,
-    0x47, 0x23, 0xd4, 0x61, 0xfd, 0xda, 0x7f, 0xe0, 0xc1, 0xf5, 0x4f, 0x1a,
-    0xb8, 0x65, 0xa1, 0x8f, 0xa3, 0x09, 0x27, 0xff, 0x61, 0xcf, 0x4f, 0xcf,
-    0xf6, 0xab, 0xaa, 0x88, 0x62, 0x09, 0x50, 0x86, 0xa3, 0x9c, 0x02, 0x09,
-    0xf7, 0xfb, 0x55, 0xd5, 0x44, 0x15, 0x3b, 0x4c, 0x75, 0xa3, 0x0f, 0x38,
-    0x8c, 0xe7, 0xb7, 0x76, 0x12, 0x53, 0xab, 0xf6, 0x25, 0x3d, 0xc8, 0x50,
-    0xe9, 0x4f, 0xe2, 0xb3, 0x37, 0x76, 0x12, 0x50, 0x94, 0xfd, 0x9d, 0x6d,
-    0x31, 0xd2, 0x9a, 0xdb, 0x52, 0x9f, 0x81, 0x82, 0xc2, 0xfa, 0x51, 0x89,
-    0x84, 0xf4, 0x84, 0xe3, 0x9a, 0x22, 0x11, 0x90, 0x05, 0x5a, 0x56, 0x30,
-    0xc4, 0xc5, 0xf4, 0x8c, 0x7e, 0x52, 0xa7, 0xa7, 0x83, 0xa8, 0xe6, 0xe7,
-    0xfc, 0xcf, 0xa0, 0xcb, 0xfd, 0xae, 0xb4, 0xff, 0x6f, 0x4c, 0xef, 0xd6,
-    0x13, 0x2d, 0x3f, 0xcd, 0xc3, 0xfc, 0x9b, 0xb8, 0x75, 0xa1, 0x8f, 0xd7,
-    0x47, 0x53, 0xff, 0xf3, 0x95, 0x4f, 0xad, 0x78, 0xcb, 0x39, 0xdf, 0x34,
-    0x32, 0xa2, 0xfb, 0x9d, 0xbc, 0xea, 0xd3, 0xdb, 0x0c, 0x7a, 0xd0, 0xc6,
-    0xec, 0x86, 0xe7, 0xfc, 0x04, 0xfd, 0xeb, 0x3e, 0x57, 0x5a, 0x7f, 0x6c,
-    0x43, 0x03, 0xf5, 0x8b, 0x88, 0x06, 0x75, 0x7d, 0xeb, 0x88, 0x06, 0x30,
-    0xfa, 0x39, 0x40, 0x9a, 0x9e, 0xb8, 0x80, 0x67, 0xa8, 0x38, 0x7a, 0xe2,
-    0x01, 0x9f, 0xc5, 0xdd, 0x60, 0x84, 0xcb, 0x88, 0x06, 0x71, 0x07, 0xab,
-    0x88, 0x06, 0x3d, 0x45, 0xb1, 0xc8, 0xb4, 0x5c, 0xec, 0xfa, 0x77, 0xdb,
-    0x6b, 0x88, 0x06, 0x17, 0x10, 0x0c, 0xcc, 0x75, 0xc4, 0x03, 0x1e, 0x9b,
-    0x92, 0x17, 0x9e, 0x23, 0xfa, 0xcb, 0x88, 0x06, 0x76, 0xeb, 0xab, 0x88,
-    0x06, 0x7f, 0xc1, 0x4f, 0x36, 0x8b, 0xb5, 0xf5, 0xc4, 0x03, 0x35, 0x7a,
-    0xb8, 0x80, 0x67, 0xf0, 0x55, 0xb7, 0xbb, 0x09, 0x71, 0x00, 0xcf, 0x88,
-    0x7a, 0x01, 0x2e, 0x20, 0x19, 0x8a, 0xeb, 0x88, 0x06, 0x3e, 0x7a, 0xbb,
-    0x33, 0x9f, 0x6c, 0x1b, 0x87, 0xaa, 0x20, 0x19, 0x84, 0xcb, 0x88, 0x04,
-    0xc6, 0xd2, 0x7c, 0x4c, 0x7a, 0xf5, 0x71, 0x00, 0xcf, 0x73, 0x41, 0xd5,
-    0xc4, 0x03, 0x39, 0xbf, 0xd5, 0xc4, 0x03, 0x3f, 0xec, 0xe2, 0xfe, 0xb6,
-    0x00, 0xae, 0xb8, 0x80, 0x67, 0xdc, 0xd3, 0xde, 0xcb, 0x88, 0x06, 0x30,
-    0xff, 0xf4, 0x97, 0x37, 0xc4, 0xb8, 0x80, 0x61, 0xea, 0xa2, 0xfd, 0x23,
-    0xc8, 0x4a, 0x12, 0xb6, 0x8c, 0x84, 0x67, 0x72, 0xed, 0xc2, 0x9f, 0x92,
-    0x19, 0xf6, 0x6e, 0xf5, 0xc2, 0xe2, 0x01, 0x9f, 0xde, 0xb7, 0x5c, 0xfb,
-    0x6d, 0x71, 0x00, 0xfa, 0x6d, 0x27, 0x7d, 0xad, 0x5c, 0x40, 0x31, 0xd3,
-    0xfb, 0xfa, 0x94, 0xf1, 0x03, 0xee, 0xb8, 0x80, 0x67, 0xea, 0x71, 0xf9,
-    0xc3, 0xd7, 0x10, 0x0c, 0x62, 0x22, 0x84, 0x45, 0xc9, 0x6c, 0xff, 0x69,
-    0xae, 0x6b, 0xf3, 0xf1, 0x2e, 0x20, 0x19, 0x12, 0xe2, 0x01, 0x9a, 0xac,
-    0xf4, 0xf8, 0xf4, 0x8f, 0x37, 0xc4, 0xb8, 0x80, 0x67, 0xd5, 0xbb, 0x97,
-    0x0b, 0x88, 0x06, 0x7e, 0x20, 0xe5, 0xf9, 0x75, 0xc4, 0x03, 0x0c, 0x89,
-    0x00, 0x23, 0xd9, 0xa4, 0x7a, 0xc8, 0x07, 0xc8, 0x6c, 0x38, 0x81, 0xf6,
-    0x1d, 0x10, 0xd1, 0x70, 0x8a, 0x2f, 0x2b, 0x53, 0x71, 0xe1, 0xf9, 0x42,
-    0x4c, 0x68, 0x5f, 0xce, 0xaa, 0xea, 0xa2, 0x01, 0x32, 0x2f, 0x27, 0x87,
-    0x74, 0xfe, 0x9d, 0x69, 0xff, 0x9e, 0x19, 0xdd, 0x10, 0x85, 0xcb, 0x8b,
-    0x4c, 0x26, 0x4a, 0x43, 0xa5, 0x3b, 0x4d, 0x62, 0xd3, 0x5b, 0x6a, 0x50,
-    0x47, 0xb5, 0xe7, 0x15, 0x70, 0x46, 0xd1, 0xc9, 0xc7, 0x2b, 0xa4, 0x63,
-    0xc1, 0x9f, 0xfd, 0xbb, 0xe0, 0xe5, 0xf1, 0x84, 0x26, 0xe1, 0x68, 0xb1,
-    0x96, 0x82, 0xf1, 0xfe, 0xd2, 0x59, 0xc8, 0xe6, 0x8a, 0x85, 0x0e, 0x01,
-    0x8b, 0x27, 0xf1, 0x6c, 0x67, 0x6e, 0xda, 0xeb, 0x4f, 0xdf, 0x71, 0xed,
-    0xfb, 0x16, 0x9f, 0xf3, 0xef, 0xe0, 0x30, 0xed, 0xf1, 0xd6, 0x9f, 0xfb,
-    0xfa, 0xac, 0x7b, 0x7c, 0x43, 0x1d, 0x69, 0xf8, 0x6b, 0x7e, 0x59, 0xa4,
-    0xa7, 0x9c, 0xd5, 0x38, 0xb4, 0xfb, 0x1c, 0x7b, 0x70, 0xb4, 0xf6, 0x03,
-    0x3d, 0x28, 0xf9, 0xf5, 0x01, 0x1b, 0xa9, 0x44, 0x32, 0x6f, 0x98, 0x5e,
-    0x48, 0x1a, 0x44, 0xa8, 0x4c, 0x4f, 0xdc, 0x5f, 0xff, 0x6d, 0xad, 0x3c,
-    0x41, 0xbb, 0xad, 0x3b, 0x9f, 0x37, 0x75, 0xa7, 0xfe, 0x11, 0xf3, 0xba,
-    0x2f, 0x75, 0xc8, 0xeb, 0x4f, 0xfd, 0x71, 0xc7, 0xaf, 0x0c, 0x70, 0x21,
-    0xd6, 0x9f, 0xec, 0xde, 0xc8, 0x7d, 0xe7, 0x56, 0x9f, 0xde, 0xb7, 0x17,
-    0xce, 0x36, 0xb4, 0xff, 0x36, 0xcb, 0xa5, 0xeb, 0x6d, 0x69, 0x5f, 0xd4,
-    0x52, 0x61, 0xc7, 0xcd, 0x63, 0x84, 0xc8, 0x77, 0x0e, 0xb9, 0xfc, 0xfc,
-    0xb8, 0xd7, 0xde, 0x2d, 0x0c, 0x9f, 0x41, 0x11, 0xd4, 0x6d, 0x1c, 0x95,
-    0x4f, 0xfc, 0xdc, 0x3c, 0x39, 0x17, 0x8e, 0x9c, 0x4b, 0x4f, 0xa9, 0xdf,
-    0xbe, 0xb2, 0xd3, 0x5e, 0xeb, 0x4e, 0xb6, 0xdb, 0x56, 0x98, 0x54, 0x91,
-    0x8b, 0xf8, 0xf9, 0xec, 0xdc, 0xd6, 0x7f, 0xd9, 0x70, 0xa1, 0xdc, 0x02,
-    0x71, 0x69, 0xd4, 0x2c, 0x4a, 0x1e, 0x8f, 0xd3, 0xc2, 0x0f, 0x44, 0x5e,
-    0x47, 0xd3, 0xfb, 0x61, 0x9c, 0x08, 0x67, 0xad, 0x35, 0x1d, 0x69, 0xfe,
-    0x0c, 0x77, 0x86, 0xb6, 0xdb, 0x52, 0x9c, 0x7a, 0xda, 0xd0, 0xe1, 0xf6,
-    0x68, 0x55, 0xd9, 0xe4, 0xfb, 0x1c, 0x7b, 0x3d, 0x69, 0xfa, 0xcf, 0x5b,
-    0x03, 0xd5, 0xa1, 0x93, 0x6c, 0x24, 0x5a, 0x85, 0x06, 0xcc, 0x86, 0x27,
-    0x9f, 0xbc, 0xd9, 0x7d, 0xbe, 0x0b, 0x4f, 0xfe, 0xcf, 0x68, 0xf9, 0xc3,
-    0xf0, 0x59, 0xea, 0xd3, 0x6a, 0x96, 0x99, 0xcb, 0xad, 0x3e, 0xc7, 0x64,
-    0x1e, 0xe1, 0xac, 0x18, 0x56, 0x3c, 0x22, 0xd4, 0xce, 0xd3, 0xde, 0xe6,
-    0xa9, 0x69, 0xff, 0x9b, 0x01, 0xfa, 0xc0, 0x06, 0xb5, 0x69, 0x17, 0xd1,
-    0x0c, 0x04, 0xbb, 0x21, 0x8b, 0x1b, 0xb4, 0x07, 0xca, 0x86, 0xf6, 0x52,
-    0x28, 0xe5, 0xfd, 0x85, 0x81, 0x10, 0xfe, 0x97, 0xd2, 0x77, 0x6d, 0x47,
-    0x67, 0x49, 0x82, 0x2e, 0x09, 0x41, 0x17, 0x42, 0xdc, 0xab, 0x1e, 0x53,
-    0x9d, 0xc6, 0xd9, 0x3f, 0xd9, 0xf0, 0x6d, 0xbf, 0x34, 0xb4, 0xfc, 0x02,
-    0xcf, 0x69, 0xeb, 0x4f, 0xa9, 0xfe, 0x3d, 0x25, 0xa7, 0xc3, 0x38, 0xf6,
-    0xf3, 0xa5, 0x0c, 0x8b, 0x8c, 0x36, 0xd1, 0x60, 0xc5, 0x33, 0xff, 0x0e,
-    0x1c, 0xd0, 0xd9, 0x72, 0x60, 0x5a, 0x77, 0x91, 0x7a, 0xb4, 0x7c, 0xf8,
-    0x8e, 0x87, 0x21, 0x95, 0x10, 0x2c, 0xb6, 0xaa, 0x05, 0x24, 0x5b, 0x37,
-    0x76, 0x8f, 0x4f, 0xf8, 0xbc, 0xb4, 0x43, 0x18, 0x42, 0x64, 0xa7, 0xfb,
-    0x5c, 0xd5, 0xf3, 0x00, 0x75, 0xa7, 0xfc, 0x0d, 0xc1, 0xeb, 0x79, 0xc6,
-    0xd6, 0x9f, 0xff, 0xda, 0x6f, 0x8b, 0x1d, 0x95, 0xef, 0x81, 0xda, 0x01,
-    0x2d, 0x3e, 0x6c, 0x0d, 0xf9, 0x2d, 0x23, 0xad, 0x3d, 0x7a, 0xe3, 0xc1,
-    0xcd, 0xc0, 0x14, 0x47, 0xa9, 0xa3, 0xfc, 0xe3, 0x47, 0xdb, 0x84, 0xbc,
-    0x32, 0x79, 0x85, 0x1c, 0x44, 0xff, 0xff, 0x7e, 0xcc, 0x27, 0x2a, 0x9f,
-    0x76, 0x3f, 0xba, 0x60, 0xea, 0xd3, 0xac, 0x07, 0xad, 0x0c, 0xab, 0xe8,
-    0xeb, 0xba, 0x8f, 0xce, 0x8a, 0x39, 0x66, 0x9f, 0xcf, 0x73, 0x02, 0xb5,
-    0x8b, 0x4f, 0xfe, 0xff, 0x43, 0x1d, 0x86, 0x37, 0xda, 0xeb, 0x4f, 0xea,
-    0x73, 0x58, 0x7c, 0xba, 0xd1, 0x87, 0xf4, 0xed, 0x22, 0x7e, 0x21, 0xf7,
-    0x98, 0x75, 0xa7, 0xd9, 0xe9, 0x7e, 0xeb, 0x4e, 0xde, 0xf8, 0x5a, 0x7f,
-    0xee, 0x3a, 0x5a, 0xf0, 0xeb, 0x9a, 0x23, 0xad, 0x2b, 0x88, 0xf9, 0xb6,
-    0x3b, 0x26, 0xb1, 0x16, 0xb6, 0xc2, 0x36, 0x09, 0x33, 0x3d, 0x11, 0xee,
-    0x1c, 0x33, 0xfe, 0x02, 0x1b, 0x8c, 0x77, 0xc3, 0x0c, 0xb4, 0xef, 0x6b,
-    0xeb, 0x4f, 0x85, 0x41, 0x8e, 0xab, 0x43, 0x1e, 0x27, 0x23, 0x93, 0x8f,
-    0xc3, 0x8b, 0x4f, 0xfc, 0x7b, 0x9d, 0xbd, 0x6f, 0xf2, 0xc2, 0x5a, 0x7f,
-    0xcd, 0x41, 0x9b, 0xd9, 0x5b, 0xb5, 0xa1, 0x91, 0x3e, 0x43, 0xdc, 0xa2,
-    0xc3, 0x2b, 0x89, 0x7c, 0x7b, 0xd8, 0x6b, 0x78, 0x43, 0x6e, 0x18, 0x33,
-    0xff, 0x9e, 0x5a, 0xc7, 0x09, 0xce, 0x5d, 0x79, 0xba, 0xd3, 0xf8, 0xb5,
-    0xed, 0x03, 0x69, 0x69, 0xf3, 0x58, 0xce, 0xac, 0xb4, 0xfd, 0xc8, 0xda,
-    0xce, 0x2e, 0xb4, 0xff, 0xff, 0xfb, 0xed, 0xb0, 0xae, 0x95, 0xdc, 0x2f,
-    0xf5, 0xaf, 0xb6, 0xe1, 0x86, 0xcb, 0xad, 0x3f, 0xff, 0xff, 0xeb, 0xdf,
-    0x37, 0xaa, 0x3d, 0xff, 0xf2, 0xe3, 0x75, 0x43, 0xe6, 0x39, 0xce, 0xda,
-    0xde, 0x19, 0x68, 0x64, 0xc7, 0xea, 0x10, 0x53, 0xc2, 0xae, 0x2e, 0xb4,
-    0xd6, 0xda, 0xb4, 0x5c, 0xdd, 0x5a, 0x45, 0x3e, 0x2d, 0x56, 0x69, 0x23,
-    0x1a, 0x28, 0xe1, 0x55, 0x8b, 0x8a, 0x1a, 0x31, 0xf3, 0x65, 0x21, 0x1a,
-    0x7d, 0xe1, 0x07, 0x3a, 0xdb, 0x6d, 0x4a, 0x79, 0xfb, 0xce, 0xa4, 0x62,
-    0xfe, 0x7d, 0x9e, 0x90, 0xae, 0xbb, 0xbf, 0xa5, 0x5f, 0x3e, 0x40, 0x30,
-    0x9b, 0xf7, 0x5a, 0x73, 0xb6, 0xea, 0xd0, 0xc6, 0xcc, 0x85, 0xa7, 0xe0,
-    0xe2, 0xee, 0x66, 0xd6, 0x9f, 0xfe, 0x0d, 0xb9, 0x9f, 0xca, 0x3b, 0x68,
-    0xbd, 0x5a, 0x7d, 0x67, 0x9c, 0xb3, 0x85, 0xa7, 0xc5, 0x76, 0xb0, 0xeb,
-    0xb3, 0xf6, 0x7c, 0xd5, 0xf1, 0x72, 0xbb, 0x3f, 0x66, 0xa7, 0xae, 0xcf,
-    0xd9, 0xee, 0x5f, 0x97, 0x5d, 0x9f, 0xb1, 0xe9, 0xe8, 0x81, 0x14, 0xf9,
-    0xb2, 0xf8, 0x0b, 0xb3, 0xf6, 0x17, 0x67, 0xec, 0xd5, 0xb5, 0xd9, 0xfa,
-    0x72, 0xde, 0x4f, 0x03, 0xf9, 0xe5, 0x22, 0x7b, 0x3c, 0x88, 0x4b, 0xb3,
-    0xf6, 0x17, 0x67, 0xec, 0xc2, 0x65, 0xd9, 0xfb, 0x3f, 0xd8, 0x2a, 0xfe,
-    0x3b, 0xcf, 0x57, 0x67, 0xec, 0xfd, 0x9a, 0x2b, 0xf3, 0x62, 0xec, 0xfd,
-    0x81, 0x22, 0x8c, 0x08, 0xae, 0x8d, 0x3d, 0xfb, 0x1b, 0x6b, 0xb3, 0xf6,
-    0x17, 0x67, 0xee, 0x1a, 0xf9, 0xad, 0xb5, 0x76, 0x7e, 0xc3, 0xd5, 0x87,
-    0xfa, 0x69, 0x90, 0x85, 0xe2, 0x13, 0x7f, 0x28, 0x39, 0x85, 0xe1, 0x75,
-    0xb5, 0xeb, 0x49, 0xa7, 0xb1, 0xec, 0x3a, 0x6c, 0xfd, 0x19, 0x12, 0x33,
-    0xfe, 0xce, 0xfb, 0x9f, 0xb6, 0xb4, 0xe2, 0xd3, 0x3e, 0xc4, 0xa2, 0xc4,
-    0x4c, 0x70, 0x82, 0x08, 0x11, 0xf5, 0xc9, 0x5a, 0x9c, 0x64, 0x9f, 0xff,
-    0x3f, 0xec, 0xee, 0x9c, 0x6f, 0x74, 0xc7, 0xa7, 0x16, 0x9f, 0xce, 0xb5,
-    0x72, 0xbb, 0x98, 0xb4, 0x70, 0x88, 0xdf, 0xac, 0x43, 0x2f, 0x33, 0xbd,
-    0x67, 0x07, 0xc8, 0xba, 0xa7, 0x38, 0xf9, 0x85, 0xd4, 0xff, 0x87, 0x6e,
-    0x01, 0xb5, 0xae, 0x7a, 0xb4, 0xfc, 0x14, 0x3e, 0xa9, 0xeb, 0x4e, 0xb6,
-    0xdb, 0x52, 0x9c, 0x33, 0x09, 0x23, 0x17, 0xf3, 0xfe, 0x0a, 0x78, 0xdf,
-    0xc7, 0x75, 0xf5, 0xa7, 0xf8, 0x28, 0x5e, 0x1e, 0xc2, 0xe1, 0x68, 0xf5,
-    0x32, 0xf3, 0xa0, 0x09, 0x2c, 0x0a, 0xf6, 0x7f, 0x3f, 0xe0, 0x02, 0xe7,
-    0xcd, 0xdf, 0xc9, 0xb6, 0xb4, 0xeb, 0x6d, 0xb5, 0x31, 0x08, 0x27, 0xdf,
-    0xed, 0x57, 0x53, 0x10, 0x80, 0xc6, 0xb6, 0x75, 0xb6, 0xda, 0x98, 0x83,
-    0xd0, 0x98, 0x83, 0xc6, 0x35, 0xb2, 0x3e, 0x22, 0x67, 0xae, 0x33, 0xff,
-    0x69, 0x9e, 0x5b, 0xcd, 0x75, 0x8e, 0xb4, 0x31, 0xf5, 0x3b, 0x28, 0x9f,
-    0x6d, 0xb6, 0xc7, 0x5a, 0x78, 0x09, 0x87, 0x5a, 0x70, 0xcc, 0x25, 0xa2,
-    0xc3, 0x78, 0x72, 0x09, 0xf7, 0x5b, 0x4c, 0x74, 0xa7, 0xc1, 0x40, 0x18,
-    0x94, 0xd9, 0xd4, 0xa6, 0xb6, 0xd4, 0xa3, 0x0f, 0xda, 0xe4, 0xdb, 0x23,
-    0xb4, 0x56, 0x7f, 0x00, 0xcc, 0x2d, 0x63, 0xaa, 0x46, 0x37, 0x90, 0xc9,
-    0xbd, 0x93, 0x15, 0x43, 0x52, 0x7f, 0xe6, 0x15, 0xf3, 0x3d, 0xa0, 0xa1,
-    0xd6, 0x9f, 0xff, 0x70, 0xfb, 0xda, 0xdf, 0xd3, 0x16, 0x98, 0x09, 0x68,
-    0xd2, 0x25, 0xa9, 0x0a, 0x7f, 0x5c, 0x63, 0xde, 0xda, 0xe1, 0x68, 0x65,
-    0xc8, 0x0c, 0x4b, 0x28, 0xdd, 0xbf, 0x1c, 0xf5, 0x43, 0x26, 0xe4, 0x53,
-    0xad, 0xb6, 0xd4, 0xa7, 0x85, 0x41, 0xea, 0x46, 0x2f, 0xe7, 0xe1, 0xb3,
-    0xf9, 0x8e, 0x2d, 0x0f, 0x3d, 0xdb, 0x98, 0x4f, 0xff, 0x07, 0x1e, 0x38,
-    0x13, 0x07, 0x6a, 0xc0, 0xa5, 0xa7, 0xff, 0xff, 0xfb, 0x77, 0x1a, 0xfe,
-    0x35, 0x45, 0xb2, 0xd7, 0x4b, 0xc3, 0xcb, 0x54, 0x1b, 0x7e, 0x7f, 0x16,
-    0x9f, 0xfe, 0xc6, 0x37, 0xa0, 0xdc, 0xf7, 0x07, 0x2f, 0xad, 0x3c, 0xeb,
-    0xa1, 0x3b, 0x5a, 0x1e, 0x7e, 0xd4, 0x9f, 0x3f, 0xf6, 0x7c, 0x47, 0x2b,
-    0x8d, 0x76, 0xc5, 0xa7, 0x57, 0x9e, 0xeb, 0x43, 0xcf, 0x8a, 0x90, 0xe7,
-    0x8b, 0xdc, 0x3a, 0xd0, 0xca, 0x91, 0x1e, 0x45, 0x8a, 0x35, 0x18, 0x60,
-    0x42, 0x0f, 0x92, 0x19, 0xfb, 0xcf, 0x72, 0x0e, 0x1e, 0xb4, 0xff, 0xab,
-    0xfe, 0x45, 0xac, 0xae, 0x04, 0xb4, 0xff, 0xf8, 0xb6, 0xc7, 0xce, 0x36,
-    0xc7, 0x1b, 0xf4, 0x25, 0xa7, 0xfa, 0xed, 0xfd, 0xb1, 0xbd, 0x25, 0xa1,
-    0xe8, 0x8d, 0xba, 0xac, 0xfe, 0x21, 0xf6, 0xda, 0x6b, 0xad, 0x3f, 0xe0,
-    0xa7, 0x35, 0x5f, 0xf9, 0x7a, 0xb4, 0x62, 0x72, 0x3d, 0x31, 0x28, 0x66,
-    0xfc, 0x8c, 0xe6, 0x53, 0xcf, 0x1b, 0x1c, 0x5a, 0x7d, 0xff, 0x73, 0x01,
-    0x69, 0xff, 0xbc, 0xe5, 0x9a, 0x67, 0x77, 0xd3, 0x70, 0xb4, 0x51, 0xf7,
-    0xdc, 0x9a, 0x7f, 0xb0, 0x3f, 0x97, 0xc2, 0xd2, 0xd3, 0xff, 0xb7, 0x7c,
-    0x73, 0x4c, 0xef, 0x35, 0xa6, 0x5a, 0x7e, 0xd7, 0xa4, 0xed, 0xbd, 0x5a,
-    0x30, 0xfe, 0xe9, 0x2e, 0x7b, 0x36, 0x57, 0x5a, 0x7e, 0x74, 0xce, 0x03,
-    0xa5, 0x67, 0x2b, 0x45, 0xcf, 0x6f, 0x64, 0x11, 0xea, 0x6d, 0xdd, 0x21,
-    0x08, 0x58, 0x6d, 0xee, 0x7f, 0xf8, 0xaf, 0x9e, 0xee, 0xe4, 0x3e, 0xa8,
-    0x09, 0x69, 0xff, 0xfc, 0x1b, 0xcd, 0x13, 0x6c, 0x84, 0xcf, 0x35, 0xb6,
-    0xda, 0x94, 0xf7, 0xb9, 0x83, 0xa5, 0x3d, 0x42, 0xad, 0xad, 0x39, 0xe1,
-    0xca, 0xa2, 0x19, 0x9d, 0x6d, 0xb6, 0xa5, 0x3b, 0x03, 0xa9, 0x18, 0xbf,
-    0x9f, 0xf6, 0x59, 0x9e, 0xd3, 0xc1, 0xac, 0x5a, 0x04, 0x7c, 0xe0, 0x53,
-    0x3f, 0x9e, 0x19, 0xbd, 0xf3, 0x62, 0xd3, 0xab, 0xce, 0xcb, 0x43, 0x27,
-    0x8b, 0x63, 0x0f, 0xa4, 0x5c, 0x10, 0x7e, 0x15, 0xf4, 0x43, 0xc9, 0xa4,
-    0xf1, 0x6c, 0x99, 0x69, 0xfd, 0xce, 0x7c, 0x42, 0x20, 0x5a, 0x7f, 0xf9,
-    0xe5, 0xee, 0x86, 0xbe, 0x7f, 0xb5, 0x5d, 0x54, 0x41, 0x93, 0xff, 0xbf,
-    0x4e, 0xf0, 0x3b, 0xa2, 0x10, 0xdb, 0x5a, 0x19, 0x15, 0x1c, 0xae, 0xc6,
-    0x91, 0xfb, 0xb8, 0x6d, 0x4d, 0xae, 0x56, 0x9e, 0xf9, 0x5b, 0xe7, 0x5a,
-    0x08, 0xde, 0xdc, 0x5e, 0x7f, 0x79, 0x65, 0xfc, 0xb3, 0xec, 0xb4, 0x3d,
-    0x3d, 0x82, 0x8c, 0x72, 0xec, 0xbb, 0x20, 0x9f, 0xfe, 0x60, 0xb3, 0xc3,
-    0x84, 0x19, 0xee, 0x00, 0xeb, 0x4e, 0x6f, 0xb8, 0xb4, 0x32, 0xfc, 0xc7,
-    0xa9, 0xd9, 0x28, 0x03, 0xa8, 0x65, 0x28, 0x4e, 0xa5, 0x22, 0x72, 0x87,
-    0xe4, 0xa3, 0x3e, 0xa3, 0xd6, 0x5d, 0x69, 0xff, 0xee, 0xb5, 0xee, 0xc7,
-    0xcd, 0x95, 0xf5, 0x4b, 0x4f, 0xe0, 0x06, 0x79, 0xd8, 0x96, 0x9f, 0xdb,
-    0x27, 0x65, 0xb7, 0xb2, 0x52, 0x3a, 0xd3, 0xf6, 0x00, 0xff, 0x63, 0x11,
-    0xe1, 0x8c, 0x67, 0x18, 0x98, 0x19, 0x26, 0xdd, 0xca, 0x7b, 0xfc, 0xf0,
-    0xf5, 0xa7, 0xf0, 0x3c, 0x30, 0xed, 0xd5, 0xa7, 0x1f, 0xdc, 0x5a, 0x18,
-    0xfc, 0x30, 0x92, 0x8c, 0x27, 0x6c, 0xac, 0x5a, 0x7d, 0x69, 0x7a, 0xea,
-    0x4b, 0x4f, 0xfb, 0x9d, 0xdc, 0x6f, 0x88, 0xe5, 0x75, 0xa7, 0xf5, 0xda,
-    0xcc, 0x02, 0x1d, 0x69, 0xff, 0x98, 0x2b, 0x4c, 0x23, 0x7e, 0xf7, 0x4a,
-    0x3d, 0x45, 0xae, 0x20, 0x9c, 0xca, 0x7f, 0x70, 0xfe, 0x2e, 0x5b, 0x65,
-    0xa7, 0x17, 0x17, 0x5a, 0x5a, 0xc3, 0xd0, 0x23, 0x49, 0xff, 0xf6, 0x7b,
-    0xe2, 0x8b, 0x2e, 0xd6, 0x60, 0x10, 0xeb, 0x4d, 0x96, 0xad, 0x09, 0x4f,
-    0xd7, 0x19, 0xbe, 0xc7, 0x4a, 0x12, 0x84, 0xa1, 0x28, 0x4a, 0x1e, 0x7b,
-    0xe4, 0x14, 0x22, 0xde, 0x42, 0xbc, 0x82, 0x9d, 0x42, 0xa6, 0xd6, 0x25,
-    0x3f, 0x55, 0x6d, 0xc6, 0x3a, 0x5e, 0x0b, 0x59, 0x79, 0xb4, 0xa1, 0x28,
-    0x4a, 0x1e, 0x5a, 0x10, 0x54, 0x25, 0x09, 0x42, 0x50, 0x94, 0x25, 0x09,
-    0x45, 0x86, 0xf3, 0xd0, 0xa2, 0x0a, 0x10, 0x55, 0xc2, 0x9d, 0x85, 0x42,
-    0x50, 0x94, 0x3c, 0xb4, 0xb8, 0x54, 0x25, 0x09, 0x42, 0x50, 0x94, 0x3c,
-    0xd4, 0x08, 0x2b, 0x61, 0x4e, 0xa1, 0x50, 0x94, 0x25, 0x09, 0x42, 0x51,
-    0x61, 0xa8, 0x1c, 0x2b, 0xe1, 0x5a, 0x0a, 0x90, 0xe9, 0x42, 0x50, 0x94,
-    0x25, 0x09, 0x47, 0xa6, 0xa3, 0x80, 0xa1, 0x05, 0x72, 0x15, 0x09, 0x42,
-    0x50, 0x94, 0xfb, 0x4c, 0x2b, 0xe2, 0x50, 0x94, 0x3c, 0xf3, 0xba, 0x15,
-    0xa0, 0xaa, 0x0a, 0x11, 0x34, 0xba, 0x94, 0x25, 0x09, 0x42, 0x50, 0x94,
-    0x3c, 0xd4, 0x70, 0x14, 0x41, 0x4e, 0xc2, 0xa1, 0x28, 0x4a, 0x12, 0x84,
-    0xa1, 0xe6, 0xa3, 0xd0, 0xad, 0x05, 0x00, 0x54, 0xb4, 0x94, 0x25, 0x09,
-    0x49, 0xe9, 0x42, 0x5c, 0x16, 0x10, 0x94, 0x25, 0x09, 0x42, 0x51, 0x61,
-    0xf3, 0x3c, 0x28, 0x71, 0xae, 0x8d, 0x38, 0x14, 0x20, 0xad, 0x85, 0x4b,
-    0x12, 0x84, 0xa1, 0x29, 0x3d, 0x28, 0x4b, 0x82, 0xc2, 0x12, 0x84, 0xa1,
-    0x8f, 0x49, 0xe1, 0x44, 0x35, 0xf1, 0xa3, 0x85, 0x42, 0x50, 0x94, 0x25,
-    0x09, 0x42, 0x50, 0xc6, 0xcb, 0x80, 0xaf, 0x85, 0x1c, 0x28, 0x02, 0xa1,
-    0x28, 0x4a, 0x12, 0x8f, 0x97, 0xd7, 0x0a, 0xd8, 0x54, 0x25, 0x09, 0x42,
-    0x50, 0x72, 0xf8, 0x02, 0xb6, 0x15, 0x23, 0xa5, 0x09, 0x42, 0x50, 0x22,
-    0xd3, 0x90, 0xa8, 0x4a, 0x12, 0x84, 0xa1, 0x28, 0x63, 0x50, 0xe0, 0x56,
-    0x82, 0xb9, 0x0a, 0x86, 0x5f, 0xaa, 0xb1, 0xc5, 0xe7, 0xbe, 0x95, 0x62,
-    0xdf, 0x5b, 0x38, 0x48, 0x26, 0x6f, 0x39, 0xeb, 0x87, 0x3f, 0x84, 0x61,
-    0xd2, 0x34, 0x71, 0x4c, 0xa2, 0x3b, 0x07, 0x6b, 0xb3, 0x6d, 0x87, 0x97,
-    0x97, 0x6c, 0xb6, 0xa5, 0xf9, 0x16, 0x0c, 0x48, 0xea, 0x93, 0x3e, 0x78,
-    0x36, 0x5d, 0x23, 0x26, 0xaf, 0x38, 0x98, 0xe9, 0x4f, 0x17, 0x6b, 0xeb,
-    0x4e, 0x2f, 0x71, 0x69, 0x1b, 0x84, 0x46, 0xfc, 0xe3, 0x43, 0x5b, 0x20,
-    0x93, 0x3b, 0x65, 0x23, 0x8d, 0x4a, 0x03, 0x9f, 0xda, 0x2b, 0x7d, 0xfb,
-    0x58, 0xb4, 0x70, 0x7d, 0xae, 0x1c, 0x4c, 0x26, 0x5a, 0x7e, 0xfd, 0x00,
-    0xcc, 0x25, 0xa6, 0xaf, 0xad, 0x22, 0x5b, 0xa5, 0xac, 0xba, 0xb4, 0x8e,
-    0xb4, 0xde, 0x46, 0x24, 0x4a, 0x7c, 0x57, 0x48, 0x34, 0x3c, 0x30, 0x84,
-    0xff, 0x69, 0x8f, 0x8e, 0x30, 0x58, 0xb4, 0x62, 0x24, 0x04, 0xb1, 0x3f,
-    0x1c, 0xbc, 0xa8, 0x04, 0xb4, 0xfe, 0xe0, 0x58, 0x19, 0xc5, 0xab, 0x4f,
-    0xff, 0x37, 0x8a, 0xff, 0xe8, 0x4c, 0x6b, 0x6d, 0xb5, 0x68, 0x64, 0x5a,
-    0xd1, 0x77, 0x26, 0x91, 0x63, 0x61, 0xd8, 0xf8, 0xca, 0x3d, 0x84, 0xff,
-    0x4b, 0x38, 0x1c, 0x28, 0x70, 0x39, 0x08, 0x03, 0x93, 0x55, 0x2c, 0x24,
-    0x23, 0x48, 0xdc, 0x35, 0xe7, 0xc7, 0x20, 0xeb, 0x2d, 0x3f, 0x98, 0x37,
-    0x41, 0xce, 0x2d, 0x3f, 0x3c, 0x09, 0xfa, 0xc5, 0xa7, 0xd6, 0x17, 0xd9,
-    0xeb, 0x47, 0xcf, 0x48, 0x0a, 0xe7, 0xfd, 0xa6, 0xf8, 0x83, 0x2d, 0xa1,
-    0x2d, 0x3b, 0x31, 0xc5, 0xa5, 0x9a, 0x3d, 0x81, 0x1e, 0xcf, 0xdc, 0xff,
-    0x93, 0xd0, 0x2d, 0x3c, 0x76, 0xcb, 0x16, 0x9f, 0x63, 0xb0, 0x6e, 0x16,
-    0x82, 0x3c, 0x9b, 0x90, 0xcf, 0x30, 0x6f, 0xab, 0x49, 0xb1, 0x35, 0x80,
-    0x79, 0xb9, 0x3e, 0xdd, 0xfc, 0x88, 0x67, 0xb9, 0xaf, 0xbd, 0x69, 0xe6,
-    0xe7, 0xcd, 0xdd, 0x29, 0xf3, 0xcd, 0x6d, 0xb6, 0xad, 0x00, 0x7a, 0x7c,
-    0x94, 0x4f, 0x56, 0xcf, 0xb5, 0xa3, 0xd4, 0x5c, 0x63, 0xa8, 0x88, 0xa1,
-    0x95, 0x9c, 0x1c, 0x94, 0x51, 0xed, 0x73, 0x18, 0x4c, 0xfb, 0x91, 0x60,
-    0x75, 0x69, 0xfd, 0xbc, 0x19, 0x9c, 0x2b, 0xad, 0x1f, 0x3d, 0xa7, 0x64,
-    0xf3, 0xfd, 0x60, 0x63, 0xf5, 0x82, 0xba, 0xd3, 0xde, 0xfa, 0xce, 0xd6,
-    0x9f, 0xfe, 0xd3, 0x1f, 0xad, 0xbc, 0xff, 0x6a, 0xba, 0xa8, 0xbe, 0x27,
-    0x57, 0xec, 0x54, 0x5f, 0xf0, 0xc8, 0x83, 0xea, 0xd4, 0xff, 0x9d, 0x4b,
-    0x03, 0xd1, 0xcb, 0x3e, 0xb4, 0xea, 0xe1, 0xd5, 0x69, 0xfa, 0xb7, 0x76,
-    0xcb, 0x16, 0x87, 0x67, 0x97, 0x68, 0xfc, 0xe2, 0xd1, 0x2d, 0x3f, 0xf3,
-    0xbd, 0x8e, 0xc0, 0xfc, 0xd1, 0x1d, 0x69, 0xfb, 0x2d, 0x1b, 0x55, 0xb5,
-    0xa7, 0xff, 0xdb, 0x19, 0xec, 0xec, 0x80, 0x5f, 0x26, 0x3e, 0x25, 0x3d,
-    0xa1, 0xb8, 0xda, 0xd1, 0x62, 0x29, 0xf0, 0xbb, 0xaa, 0xb3, 0x6f, 0x16,
-    0x9f, 0x7c, 0xac, 0xa3, 0xaa, 0x61, 0x39, 0xed, 0xf2, 0xd4, 0xa9, 0x84,
-    0xe6, 0x13, 0x2a, 0x81, 0x39, 0xfc, 0x14, 0x3e, 0xdb, 0xe2, 0x55, 0x02,
-    0x73, 0xfa, 0xf9, 0xa2, 0xbf, 0x36, 0x2a, 0x61, 0x39, 0xb3, 0xd5, 0x4c,
-    0x27, 0x35, 0xb6, 0xae, 0x61, 0x38, 0xc4, 0xd3, 0x38, 0x34, 0x22, 0xe3,
-    0x91, 0x5c, 0xfd, 0xda, 0x0d, 0xa4, 0x72, 0xda, 0x66, 0x13, 0x18, 0xf9,
-    0xe4, 0x5c, 0x27, 0xef, 0x6c, 0x79, 0x10, 0x4a, 0xd7, 0xa8, 0x6a, 0xf0,
-    0xec, 0xdc, 0xa2, 0x79, 0xc0, 0x23, 0xad, 0x3f, 0xcd, 0xce, 0xc0, 0x9d,
-    0x8c, 0x75, 0xa7, 0x06, 0x5f, 0x0f, 0x5b, 0xe3, 0x70, 0xcb, 0xbc, 0x1e,
-    0x9c, 0x76, 0x15, 0x1f, 0x22, 0xd4, 0x22, 0xf7, 0x2e, 0x0d, 0xdc, 0x27,
-    0xe7, 0xde, 0xe7, 0xc8, 0x75, 0xa7, 0xed, 0x30, 0xc1, 0xc0, 0x96, 0x9f,
-    0x9b, 0x78, 0xe1, 0x1d, 0x69, 0xf0, 0x60, 0xd8, 0xe2, 0xd3, 0xfd, 0xaa,
-    0x3e, 0x89, 0xb8, 0x7a, 0xd3, 0xf0, 0xc0, 0xff, 0x2a, 0xb1, 0x68, 0x23,
-    0xe9, 0x11, 0xc4, 0x58, 0x8d, 0xd0, 0x2c, 0xdc, 0x24, 0x27, 0xfd, 0x95,
-    0xa7, 0x08, 0x35, 0x9c, 0x2d, 0x0c, 0xc8, 0x28, 0xc9, 0xe0, 0x42, 0x7f,
-    0xf3, 0x94, 0x54, 0x61, 0x60, 0x6b, 0x3f, 0xf8, 0xfb, 0xb9, 0x07, 0x0f,
-    0xd9, 0x5c, 0x96, 0x9f, 0xff, 0xc1, 0x9c, 0x65, 0xe8, 0x34, 0x4d, 0xbd,
-    0x73, 0x47, 0x5a, 0x7e, 0x0e, 0x4e, 0x5a, 0x65, 0xa7, 0xff, 0xd4, 0x1b,
-    0xaa, 0xd0, 0x36, 0xef, 0x6e, 0x69, 0x69, 0xff, 0x59, 0x41, 0x7d, 0xe8,
-    0x9a, 0xeb, 0x4e, 0xfd, 0xee, 0xb8, 0x80, 0xe7, 0xff, 0x7d, 0xb4, 0xde,
-    0x33, 0xfd, 0xaa, 0xea, 0xa2, 0x03, 0x31, 0xa9, 0x8a, 0x46, 0x60, 0xcc,
-    0xd0, 0x24, 0xd0, 0x77, 0x18, 0xf4, 0x62, 0x72, 0xe5, 0x1b, 0x6c, 0xfb,
-    0x6c, 0x2f, 0x2c, 0x5a, 0x78, 0x36, 0x2e, 0x56, 0x9f, 0xff, 0x9f, 0x97,
-    0x2f, 0xfc, 0x8e, 0x36, 0xf5, 0x96, 0xed, 0x68, 0xc3, 0xfe, 0xa2, 0x29,
-    0xff, 0xfe, 0xa7, 0xe0, 0x38, 0xfc, 0xe1, 0xe3, 0x30, 0x8d, 0x6d, 0xb6,
-    0xa5, 0x0c, 0x99, 0x96, 0xa1, 0x4e, 0x04, 0x13, 0xf9, 0xb3, 0xfd, 0xaa,
-    0xea, 0xa2, 0x09, 0x9f, 0xe6, 0xde, 0x7f, 0xb5, 0x5d, 0x54, 0x5f, 0x33,
-    0xe2, 0x76, 0x19, 0x7f, 0x51, 0x01, 0xd3, 0xd9, 0xfd, 0xce, 0xbc, 0xb4,
-    0x5d, 0xd2, 0xd3, 0xfe, 0xb2, 0xb8, 0xf2, 0xcd, 0x85, 0x3b, 0x5a, 0x7f,
-    0xd9, 0xeb, 0x3d, 0xb5, 0x82, 0xa5, 0xa7, 0x5b, 0x6d, 0xa9, 0x4f, 0xb5,
-    0x9d, 0xaf, 0xa4, 0x62, 0xfe, 0x7f, 0xff, 0x39, 0xf6, 0x77, 0xe0, 0x7a,
-    0xf0, 0x33, 0x97, 0xf1, 0x9c, 0x6d, 0x68, 0xda, 0x28, 0xc6, 0x35, 0x8b,
-    0x13, 0x65, 0x3a, 0x18, 0x43, 0xfa, 0x7f, 0xeb, 0x0e, 0xdc, 0x5c, 0x32,
-    0xc1, 0x9e, 0xb4, 0xfd, 0xe7, 0xc7, 0xe9, 0xbe, 0xb4, 0xff, 0xee, 0x03,
-    0xc7, 0x74, 0x42, 0x13, 0x7d, 0xeb, 0x43, 0x1f, 0xe0, 0xc6, 0x13, 0xfe,
-    0xe6, 0xed, 0x81, 0xb7, 0x46, 0xde, 0xad, 0x3a, 0xdb, 0x6d, 0x4a, 0x7f,
-    0x0c, 0x26, 0x0c, 0xe3, 0x69, 0x18, 0xbf, 0x9f, 0xff, 0xbc, 0xdf, 0x8e,
-    0xe8, 0xbd, 0xf7, 0x2d, 0xf1, 0xbb, 0xe5, 0x8b, 0x4a, 0xb4, 0x8a, 0xbe,
-    0x50, 0xa1, 0xeb, 0x85, 0xfe, 0xa0, 0x94, 0x73, 0x9f, 0x36, 0x3c, 0x30,
-    0xc0, 0x8b, 0x71, 0x87, 0x4f, 0xfb, 0x45, 0x6f, 0xba, 0xcd, 0xf9, 0x62,
-    0xd3, 0xed, 0x66, 0xdc, 0x7a, 0xd3, 0xfe, 0xcd, 0xb7, 0xef, 0xa6, 0x0d,
-    0xae, 0x20, 0x89, 0xfc, 0xd9, 0xfe, 0xd5, 0x75, 0x51, 0x04, 0x18, 0xf2,
-    0x67, 0xea, 0xd7, 0xfe, 0x56, 0xad, 0x3f, 0x79, 0xfd, 0xb6, 0x81, 0xeb,
-    0x40, 0x1e, 0xff, 0x25, 0xb1, 0xa4, 0xd0, 0x04, 0xe2, 0x10, 0xad, 0x9f,
-    0xe6, 0x0d, 0x8d, 0x9a, 0xae, 0x16, 0x9f, 0xfe, 0xab, 0x77, 0x9a, 0x62,
-    0xd8, 0x66, 0x89, 0x68, 0x7a, 0x21, 0x00, 0xe2, 0x7f, 0xb4, 0xdf, 0xd8,
-    0x36, 0x70, 0xb4, 0xff, 0xff, 0x15, 0x8c, 0xfb, 0xff, 0xe5, 0xc3, 0x8f,
-    0xaf, 0x9f, 0x38, 0x5a, 0x7b, 0x7a, 0xc0, 0xf5, 0x14, 0x1a, 0x37, 0x9f,
-    0x86, 0x3d, 0xed, 0xae, 0x16, 0x86, 0x3e, 0xad, 0x9d, 0x4f, 0x13, 0x85,
-    0x6a, 0xd3, 0xff, 0xee, 0x6c, 0x06, 0xcd, 0x19, 0xb7, 0x9a, 0xe7, 0xeb,
-    0x4f, 0xb3, 0x77, 0x1a, 0xfe, 0x9f, 0xc0, 0xc4, 0x53, 0xf7, 0x74, 0x5e,
-    0x44, 0x3a, 0xd3, 0xf1, 0xdb, 0xcf, 0xac, 0xb1, 0x69, 0xff, 0xff, 0xe2,
-    0xdd, 0x1e, 0x80, 0x4f, 0x2b, 0xf2, 0x7d, 0x13, 0x6f, 0x5c, 0xd1, 0xd6,
-    0x8f, 0x51, 0xba, 0x46, 0x14, 0x63, 0x3f, 0x00, 0xb0, 0xed, 0xa5, 0xa7,
-    0x7c, 0x6b, 0xad, 0x3f, 0xea, 0x0e, 0xe7, 0xfb, 0x55, 0xd5, 0x44, 0x23,
-    0x0c, 0x7c, 0xbc, 0x8e, 0xcf, 0xfe, 0x6c, 0xe0, 0x6d, 0x90, 0x72, 0xfc,
-    0xba, 0xd3, 0xff, 0xd5, 0x56, 0x0d, 0x9a, 0xa3, 0x9a, 0xdb, 0x6d, 0x5a,
-    0x3d, 0x44, 0xeb, 0x89, 0x53, 0xfd, 0xeb, 0x5e, 0xed, 0x66, 0x5a, 0xb4,
-    0x61, 0xef, 0xd1, 0x2c, 0xeb, 0x6d, 0xb5, 0x29, 0xfe, 0xc1, 0x57, 0xf1,
-    0xde, 0x7a, 0x91, 0x8b, 0xf9, 0xad, 0xb5, 0x29, 0xd6, 0xdb, 0x6a, 0x53,
-    0xf5, 0x59, 0xee, 0x9a, 0xe9, 0x18, 0xbf, 0x80, 0x45, 0xe5, 0xa9, 0x1e,
-    0x46, 0xf3, 0xee, 0x97, 0xa3, 0x1d, 0x23, 0x1b, 0x39, 0xd6, 0xdb, 0x6a,
-    0x53, 0xae, 0xdf, 0x48, 0xc5, 0xfc, 0x84, 0x48, 0x82, 0xda, 0xc4, 0xfd,
-    0xe8, 0x33, 0xeb, 0xab, 0x4f, 0xe1, 0xbf, 0xe9, 0x3b, 0x6f, 0x56, 0x9f,
-    0xfb, 0x54, 0xf0, 0xa1, 0xdc, 0x02, 0x71, 0x69, 0xfb, 0x58, 0xef, 0x37,
-    0x75, 0xa3, 0xa7, 0xe5, 0xb4, 0x48, 0xfa, 0x3d, 0xf4, 0x58, 0x10, 0xa9,
-    0x86, 0x4d, 0x4f, 0x23, 0x1f, 0x9d, 0xc7, 0x9a, 0x1d, 0x32, 0xd3, 0xff,
-    0xb3, 0xcf, 0x40, 0x2c, 0xeb, 0x69, 0x8e, 0xb4, 0xfd, 0xd2, 0xbe, 0x89,
-    0xc4, 0xa7, 0xf0, 0x67, 0x0f, 0xb9, 0x0e, 0xb4, 0xf5, 0x57, 0xc7, 0x5a,
-    0x3c, 0x1e, 0xa5, 0x86, 0x73, 0xcd, 0x47, 0x76, 0x94, 0xeb, 0x6d, 0xb5,
-    0x29, 0xff, 0xe1, 0x98, 0x59, 0x81, 0xbd, 0x13, 0x05, 0x89, 0x18, 0xbf,
-    0x96, 0x75, 0x13, 0xa7, 0x41, 0x86, 0x4f, 0x81, 0xe5, 0x7c, 0x25, 0x14,
-    0x20, 0x75, 0x0c, 0x29, 0xff, 0xf1, 0xf3, 0xac, 0x15, 0xbb, 0xfb, 0xcd,
-    0x7d, 0xeb, 0x4f, 0x9b, 0x77, 0x77, 0x8b, 0x43, 0x1f, 0xee, 0xd5, 0x67,
-    0xff, 0x89, 0xde, 0x07, 0xa1, 0x56, 0xe0, 0x50, 0xeb, 0x4f, 0xff, 0xf7,
-    0xba, 0x2b, 0x71, 0xd4, 0x8f, 0x5b, 0xbf, 0x2f, 0xce, 0x36, 0xb4, 0x62,
-    0x2e, 0xe9, 0x3e, 0x19, 0xb6, 0xc7, 0xb1, 0x25, 0xf1, 0xfa, 0xfb, 0x1a,
-    0xd6, 0x4e, 0x71, 0xf5, 0xdc, 0xa3, 0x82, 0x72, 0x17, 0x3f, 0x8c, 0xb0,
-    0xf0, 0x99, 0xd4, 0x63, 0xf4, 0x5e, 0x28, 0x48, 0x04, 0x63, 0xd7, 0x96,
-    0x07, 0xb9, 0x41, 0x1c, 0xc3, 0x46, 0xd8, 0x6e, 0x4e, 0x76, 0xc0, 0xb4,
-    0xff, 0xf6, 0x01, 0xb6, 0x39, 0x71, 0x7d, 0x60, 0xa9, 0x69, 0x70, 0xf3,
-    0xeb, 0xf4, 0x72, 0x7e, 0xd3, 0x85, 0xa2, 0x3a, 0xd3, 0xfe, 0x06, 0xde,
-    0xa8, 0x55, 0xc5, 0xd6, 0x9c, 0xe7, 0x3f, 0x5a, 0x7f, 0xd4, 0x5f, 0xce,
-    0x0d, 0x6d, 0xb6, 0xad, 0x14, 0x7b, 0xf7, 0x1e, 0x9f, 0xfe, 0x06, 0x7f,
-    0x81, 0xb6, 0x41, 0xcb, 0xf2, 0xeb, 0x46, 0x26, 0x77, 0xd2, 0xdd, 0x42,
-    0x70, 0x44, 0x33, 0xf9, 0x81, 0xe1, 0x98, 0x25, 0xa7, 0xf3, 0xf3, 0xf7,
-    0x06, 0xb1, 0x69, 0xff, 0xec, 0xdb, 0x07, 0x73, 0x7a, 0x2f, 0x8b, 0xab,
-    0x4f, 0xe6, 0x3d, 0xef, 0x41, 0x62, 0xd3, 0xea, 0xbe, 0x06, 0xd6, 0x9d,
-    0xba, 0xf3, 0xad, 0x3f, 0xb7, 0xa2, 0x30, 0xa8, 0x96, 0x8f, 0x51, 0xe9,
-    0xa4, 0xd0, 0x32, 0xd9, 0x2f, 0x23, 0xf3, 0xfb, 0x91, 0x36, 0x8a, 0x9e,
-    0xb4, 0xff, 0x77, 0x36, 0xce, 0xc8, 0x04, 0xb4, 0xff, 0xfd, 0xcb, 0x6e,
-    0xf8, 0x1e, 0x3e, 0xc5, 0xe8, 0x53, 0xd6, 0x82, 0x44, 0x98, 0x1c, 0x4f,
-    0xff, 0xc1, 0x97, 0xe7, 0xc6, 0x6a, 0x83, 0x1c, 0x7b, 0x70, 0xb4, 0xff,
-    0xb2, 0xfc, 0xe7, 0xfb, 0x55, 0xd5, 0x44, 0x0d, 0x3d, 0xbd, 0x53, 0xfc,
-    0x22, 0x98, 0x17, 0x21, 0x93, 0x02, 0xc8, 0x63, 0xcf, 0xfa, 0xbf, 0x80,
-    0x2b, 0xee, 0xfc, 0x2d, 0x3f, 0xff, 0xff, 0x66, 0xf5, 0x40, 0x3f, 0x8b,
-    0xfe, 0xab, 0x6e, 0xf3, 0xdf, 0x18, 0x7a, 0x0e, 0x1e, 0xb8, 0x82, 0xe7,
-    0xfd, 0x54, 0x3f, 0x6b, 0xdf, 0x03, 0xda, 0xb8, 0x82, 0xe7, 0xfe, 0xd1,
-    0x68, 0x98, 0x37, 0xe0, 0x7b, 0x57, 0x10, 0x5c, 0xfe, 0x62, 0x0d, 0xf8,
-    0x1e, 0xd5, 0xc4, 0x17, 0x3f, 0x1f, 0x3d, 0xf0, 0x3d, 0xab, 0x88, 0x2e,
-    0x7f, 0xff, 0xa8, 0x00, 0x8f, 0xe2, 0xfd, 0xd1, 0x7c, 0x87, 0xb3, 0x2d,
-    0x5c, 0x41, 0x73, 0x71, 0xe3, 0xd4, 0xe6, 0x7a, 0xa1, 0xf5, 0x3a, 0x42,
-    0x03, 0xf8, 0x65, 0x59, 0x7a, 0x3e, 0x09, 0x46, 0x93, 0xf8, 0x98, 0x7b,
-    0xee, 0xfc, 0x2d, 0x3d, 0x5f, 0x15, 0x2d, 0x3f, 0xf6, 0x8b, 0x44, 0xc1,
-    0xbf, 0x03, 0xda, 0xb8, 0x82, 0xe7, 0xf9, 0xcb, 0xf4, 0xac, 0xf0, 0x3d,
-    0xab, 0x88, 0x2e, 0x7d, 0xbb, 0xb1, 0xfc, 0x75, 0x13, 0xe3, 0x29, 0xcf,
-    0xfe, 0xf1, 0xdd, 0x17, 0x5b, 0x77, 0xf0, 0x3d, 0xab, 0x88, 0x2e, 0x7f,
-    0xff, 0xe0, 0x02, 0x3f, 0x8e, 0x73, 0xc5, 0xfb, 0xa2, 0xf9, 0x0f, 0x66,
-    0x5a, 0xb8, 0x82, 0xe3, 0x13, 0x24, 0xe1, 0x0e, 0x97, 0x67, 0xfb, 0x45,
-    0xf2, 0x1e, 0xcc, 0xb5, 0x71, 0x05, 0xcf, 0xff, 0x55, 0x70, 0xfb, 0xe8,
-    0xbd, 0xf5, 0xbe, 0xc9, 0x4f, 0xfb, 0x1e, 0xfe, 0x6f, 0xf0, 0xb3, 0xc9,
-    0x71, 0x05, 0xc7, 0x51, 0xcf, 0xf4, 0x7b, 0xa8, 0x4f, 0xfb, 0xa5, 0xfd,
-    0xd0, 0xaf, 0xe2, 0xd5, 0xc4, 0x17, 0x3f, 0x68, 0xb5, 0xa2, 0x12, 0xa0,
-    0x0b, 0x9f, 0x60, 0xbc, 0x0f, 0x6a, 0xe2, 0x0b, 0x9b, 0x37, 0xd3, 0xf1,
-    0xd1, 0xcc, 0x70, 0x8e, 0xcb, 0xc2, 0xfe, 0x7e, 0x3e, 0x7b, 0xe0, 0x7b,
-    0x57, 0x10, 0x5c, 0xff, 0xbb, 0xa2, 0xf9, 0x0f, 0x66, 0x5a, 0xb8, 0x82,
-    0xe6, 0xcf, 0x14, 0x88, 0xcb, 0x9f, 0xcf, 0xee, 0x48, 0xf4, 0x1c, 0x3d,
-    0x71, 0x05, 0xcf, 0xfb, 0x09, 0xde, 0x06, 0x7b, 0x4f, 0x5c, 0x41, 0x67,
-    0x3c, 0x08, 0xf5, 0x78, 0x03, 0xe6, 0xe2, 0x34, 0x08, 0xf8, 0xef, 0x18,
-    0xbe, 0xe3, 0x1c, 0xe6, 0x16, 0xb6, 0xb7, 0xcf, 0x85, 0x71, 0x09, 0x95,
-    0x10, 0x59, 0x91, 0x1b, 0x3f, 0xec, 0x7e, 0xa9, 0xee, 0xae, 0xbc, 0xd8,
-    0xb4, 0xe3, 0xb3, 0xd6, 0x9f, 0x67, 0xba, 0xe6, 0xd5, 0xa5, 0xfa, 0x3c,
-    0x50, 0x1b, 0x9c, 0x5e, 0x58, 0xb4, 0xea, 0xf8, 0x96, 0x97, 0x0c, 0x6e,
-    0x36, 0x3b, 0x3f, 0x65, 0x95, 0xa6, 0xf3, 0xad, 0x04, 0x8b, 0x5a, 0x5e,
-    0x02, 0x79, 0xd5, 0xfb, 0x16, 0x98, 0x4c, 0xb4, 0xf1, 0x31, 0xf1, 0x68,
-    0x5a, 0x7e, 0xa1, 0xf6, 0xdf, 0x12, 0xd1, 0xe9, 0xb7, 0x00, 0xa9, 0xff,
-    0xfc, 0xc5, 0xe8, 0x37, 0x3d, 0xde, 0x77, 0xd6, 0xe8, 0xc7, 0x5a, 0x61,
-    0x32, 0xd3, 0x30, 0xeb, 0x4f, 0xf6, 0x6e, 0xec, 0x71, 0xb1, 0xc5, 0xa7,
-    0xf5, 0xf3, 0x45, 0x7e, 0x6c, 0x5a, 0x6b, 0x6d, 0x4a, 0x7f, 0x83, 0x1d,
-    0xd3, 0xee, 0x5e, 0xad, 0x1d, 0x4f, 0xcc, 0x87, 0x3e, 0x2a, 0x75, 0x6d,
-    0x10, 0x89, 0x8b, 0xcd, 0x8a, 0x80, 0xb5, 0xce, 0x6d, 0x34, 0x18, 0x62,
-    0x75, 0xb6, 0xda, 0x94, 0x8e, 0x91, 0x8b, 0xf9, 0xf5, 0x39, 0x55, 0xf4,
-    0x8c, 0x8d, 0xd1, 0xa1, 0x7d, 0x3f, 0xb8, 0xdb, 0x66, 0xb2, 0xc5, 0xa1,
-    0x9b, 0x38, 0xec, 0x8d, 0x94, 0x74, 0x2e, 0x0b, 0xca, 0x32, 0x6f, 0xa5,
-    0xea, 0x3c, 0x7a, 0xa4, 0xbf, 0x82, 0x35, 0xe3, 0x40, 0xe6, 0x57, 0xeb,
-    0xb4, 0xb9, 0xec, 0xeb, 0x6d, 0x69, 0xec, 0x3e, 0x6d, 0x69, 0xff, 0xb5,
-    0xcd, 0x9c, 0x9b, 0x45, 0xda, 0xfa, 0xd3, 0xd5, 0x60, 0xcf, 0x5a, 0x19,
-    0x15, 0x5c, 0x10, 0x6c, 0x81, 0xd5, 0x1e, 0x7e, 0x0e, 0x7d, 0x3d, 0x3d,
-    0x69, 0xff, 0xee, 0x1e, 0x0d, 0xe0, 0xe0, 0xc3, 0xec, 0xac, 0x5a, 0x7f,
-    0x5f, 0x6e, 0xf0, 0x33, 0xd5, 0xa1, 0x91, 0x68, 0x45, 0xd4, 0xa5, 0x3a,
-    0xbc, 0x0c, 0xb4, 0xff, 0xf0, 0x3f, 0xc6, 0x7a, 0x57, 0xe3, 0x7c, 0xd5,
-    0xd6, 0x9f, 0x78, 0xd3, 0x7d, 0xeb, 0x47, 0x84, 0x4d, 0x60, 0xf5, 0xaa,
-    0x73, 0xf5, 0x8e, 0x3f, 0xcc, 0xfd, 0x8b, 0x4c, 0xfb, 0x16, 0x9f, 0x6b,
-    0x09, 0xf7, 0x5a, 0x7f, 0xfb, 0x4c, 0x31, 0xb6, 0x14, 0x2b, 0xdd, 0x84,
-    0x94, 0xfe, 0x16, 0x7f, 0xb5, 0x5d, 0x5c, 0x40, 0x93, 0xb6, 0x56, 0x2d,
-    0x0f, 0x46, 0x7f, 0xa4, 0xdf, 0x4f, 0x39, 0xf4, 0xce, 0xaf, 0x5a, 0x7b,
-    0x76, 0x50, 0x2d, 0x3d, 0xc5, 0xeb, 0x6b, 0x45, 0x87, 0xb9, 0x83, 0x3a,
-    0x21, 0x9f, 0xc1, 0x56, 0xde, 0xec, 0x25, 0xa7, 0x5b, 0x6d, 0xab, 0xab,
-    0xea, 0x75, 0x07, 0xa9, 0xab, 0xe8, 0x63, 0x59, 0x1e, 0xa2, 0x5c, 0x4b,
-    0xd3, 0xff, 0xec, 0x02, 0x1c, 0xd7, 0xde, 0xb9, 0xa7, 0xbd, 0x96, 0x8b,
-    0x0f, 0xe4, 0x72, 0x49, 0xec, 0xf6, 0x9e, 0xb4, 0xfa, 0x80, 0x08, 0xeb,
-    0x43, 0xa0, 0xad, 0x79, 0xe6, 0xd9, 0x0e, 0x62, 0x84, 0xd5, 0x46, 0x7e,
-    0x22, 0x40, 0x21, 0x9f, 0x16, 0xdb, 0x77, 0x4a, 0x7b, 0xad, 0xbb, 0xa5,
-    0x35, 0xb6, 0xa5, 0x0f, 0x3d, 0xfc, 0x26, 0xb4, 0x86, 0x6c, 0xb5, 0x23,
-    0x1a, 0xf9, 0xff, 0xf6, 0x6d, 0xbb, 0x98, 0x1b, 0xd1, 0x30, 0x58, 0xb4,
-    0x08, 0xfd, 0xed, 0x26, 0x9f, 0xff, 0xcc, 0xec, 0x80, 0x5e, 0x37, 0x7c,
-    0xee, 0xa8, 0x37, 0x75, 0xa7, 0xcf, 0xd7, 0x2d, 0xf5, 0xa7, 0xfe, 0xa3,
-    0xfd, 0x9d, 0x93, 0xb0, 0xe6, 0xeb, 0x47, 0x07, 0xdf, 0xa2, 0x89, 0xff,
-    0x66, 0x39, 0xfc, 0xe4, 0xed, 0x75, 0xa1, 0xc3, 0xe1, 0xf9, 0x1c, 0xe7,
-    0xd7, 0xd6, 0x9f, 0x3f, 0x37, 0x9e, 0xa5, 0x22, 0x70, 0xf0, 0xb6, 0x37,
-    0x3d, 0xb1, 0xf3, 0x4b, 0x4c, 0x26, 0x5a, 0x61, 0x32, 0xd3, 0xee, 0x5f,
-    0x80, 0x62, 0x35, 0x42, 0x15, 0x86, 0x44, 0x48, 0x26, 0x4f, 0xff, 0x60,
-    0x57, 0x17, 0x1c, 0x7a, 0xf1, 0xd3, 0x89, 0x69, 0xff, 0xb7, 0xac, 0x38,
-    0xcc, 0x2c, 0xe3, 0x6b, 0x4f, 0xee, 0x3a, 0x5f, 0x19, 0xbd, 0x5a, 0x78,
-    0x47, 0xa7, 0xf8, 0x46, 0x6d, 0xd4, 0xf6, 0x8b, 0x3b, 0xcb, 0x38, 0x5a,
-    0x78, 0x5f, 0x2f, 0xad, 0x0c, 0x88, 0x9a, 0x49, 0xb4, 0x7e, 0x7a, 0xfc,
-    0xf1, 0xb5, 0xa7, 0x5b, 0x6d, 0xa9, 0x4f, 0xb0, 0x70, 0xe6, 0xe9, 0x18,
-    0xbf, 0x9f, 0x61, 0xad, 0xb6, 0xd5, 0xa1, 0x8f, 0x87, 0x66, 0xf3, 0xff,
-    0x60, 0x65, 0xfb, 0xa2, 0xeb, 0x02, 0xd3, 0xd6, 0xd0, 0x3d, 0x69, 0xfe,
-    0xdb, 0x70, 0x2a, 0xd6, 0x89, 0x69, 0x69, 0x68, 0xc3, 0xc8, 0xe9, 0xc4,
-    0xeb, 0x6d, 0xb5, 0x29, 0xf8, 0xa8, 0x7d, 0x16, 0xd2, 0x31, 0x7f, 0x3e,
-    0xc1, 0x66, 0x38, 0xb4, 0xaf, 0xe1, 0x14, 0x3f, 0x40, 0x03, 0xb9, 0xb9,
-    0x7f, 0xa9, 0xa0, 0xe4, 0x61, 0x52, 0x2d, 0x27, 0x70, 0x11, 0xc0, 0xcf,
-    0x57, 0xcb, 0x4b, 0x43, 0x2f, 0x6e, 0x3c, 0x8f, 0x23, 0x32, 0x1d, 0x97,
-    0xf0, 0xb1, 0xd4, 0x6e, 0xf4, 0x5f, 0x78, 0x52, 0x6e, 0x3d, 0x9f, 0x22,
-    0xe9, 0xf9, 0x8f, 0xff, 0xd6, 0xd6, 0x9f, 0xfe, 0x23, 0xf0, 0xfb, 0xf8,
-    0xc0, 0x0c, 0x21, 0x2d, 0x1a, 0x3f, 0xbd, 0x96, 0x48, 0xce, 0x94, 0x2a,
-    0xdd, 0xdd, 0x39, 0x4f, 0x9a, 0x50, 0x3c, 0xd6, 0x54, 0xd3, 0x9a, 0xd6,
-    0x4a, 0x4c, 0x7c, 0xae, 0xcf, 0x65, 0xc7, 0xe5, 0x77, 0x8c, 0x3c, 0x79,
-    0xfd, 0x97, 0x31, 0xc4, 0xbd, 0xa2, 0x96, 0x16, 0xe4, 0xfa, 0xaf, 0xe7,
-    0x84, 0xcf, 0x2c, 0x9f, 0x54, 0x98, 0xaa, 0xac, 0x5d, 0x85, 0x2f, 0xfc,
-    0x29, 0x19, 0xb7, 0xad, 0xcb, 0xf7, 0x5a, 0xa2, 0x73, 0x1b, 0xe3, 0xb8,
-    0x7a, 0x5b, 0x0c, 0x7f, 0x29, 0x72, 0x03, 0x52, 0x16, 0x1d, 0x63, 0x26,
-    0x83, 0x42, 0xb9, 0xa9, 0xf8, 0xac, 0x0b, 0x9f, 0xc6, 0xcf, 0xf6, 0xab,
-    0xaa, 0x8b, 0x8e, 0x7f, 0x1b, 0x3f, 0xda, 0xae, 0xaa, 0x2e, 0xb9, 0xff,
-    0x8d, 0x4f, 0x36, 0x7f, 0xb5, 0x5d, 0x54, 0x4a, 0x30, 0xd0, 0xb1, 0xef,
-    0x79, 0xcf, 0xa7, 0xc3, 0x9f, 0x74, 0xf8, 0xa1, 0xf5, 0xe7, 0x40, 0xac,
-    0x57, 0x5f, 0xdc, 0xe3, 0x67, 0xce, 0xa7, 0xd3, 0xff, 0x8c, 0x7a, 0x79,
-    0xb3, 0xfd, 0xaa, 0xea, 0xa2, 0x5a, 0x9f, 0x7f, 0xb5, 0x5d, 0x54, 0x46,
-    0xf3, 0xfe, 0xa7, 0x9b, 0x3f, 0xda, 0xae, 0xaa, 0x25, 0xf9, 0x1b, 0x0f,
-    0xf0, 0xe6, 0x73, 0xf8, 0xd9, 0xfe, 0xd5, 0x75, 0x51, 0x55, 0xce, 0x07,
-    0x5b, 0x56, 0x9f, 0xe1, 0xf0, 0x33, 0x8d, 0xd1, 0xd6, 0x9b, 0xd2, 0x5a,
-    0x7e, 0xcf, 0xf6, 0xab, 0xaa, 0x89, 0x02, 0x3c, 0x1e, 0x71, 0x85, 0xa7,
-    0xd7, 0xa0, 0xae, 0x16, 0x87, 0x9e, 0x57, 0x04, 0x93, 0xfd, 0x9f, 0xb0,
-    0xec, 0x0f, 0xba, 0xd3, 0xff, 0xb1, 0xcd, 0x51, 0xf7, 0xa6, 0xdf, 0x27,
-    0x5a, 0x3d, 0x4e, 0x54, 0x71, 0xfa, 0x86, 0x80, 0x11, 0x8c, 0x71, 0x3f,
-    0xf0, 0x65, 0x86, 0xf2, 0x60, 0x0c, 0xda, 0xd0, 0x64, 0x4a, 0xfd, 0x46,
-    0x7f, 0x1b, 0x3f, 0xda, 0xae, 0xaa, 0x2c, 0x89, 0xfc, 0x6c, 0xff, 0x6a,
-    0xba, 0xa8, 0xb5, 0xe7, 0xf1, 0xb3, 0xfd, 0xaa, 0xea, 0xa2, 0xe4, 0x9f,
-    0x7f, 0xb5, 0x5d, 0x54, 0x5d, 0x93, 0xfe, 0xa7, 0x9b, 0x3f, 0xda, 0xae,
-    0xaa, 0x28, 0xe9, 0x1b, 0x0f, 0xf0, 0xe6, 0x72, 0x7a, 0xd3, 0xef, 0xf6,
-    0xab, 0xaa, 0x8a, 0x56, 0x7f, 0xff, 0xfb, 0x4d, 0x63, 0x68, 0x9d, 0xd7,
-    0xba, 0x23, 0x77, 0x44, 0xe6, 0x9a, 0xcc, 0x5a, 0x67, 0x9b, 0x11, 0x66,
-    0xe1, 0x9c, 0xc6, 0x3f, 0x08, 0xf2, 0xa8, 0x61, 0xc5, 0x8b, 0xc0, 0xcf,
-    0x86, 0x8f, 0x04, 0xe5, 0x29, 0xa0, 0xe4, 0x62, 0x3e, 0xb9, 0xf6, 0xe1,
-    0x74, 0xee, 0x31, 0x99, 0xfe, 0x79, 0xb3, 0xfd, 0xaa, 0xea, 0xa2, 0x38,
-    0x9f, 0xe2, 0x36, 0x7f, 0xb5, 0x5d, 0x54, 0x56, 0xb2, 0x35, 0x22, 0x1f,
-    0x68, 0x93, 0xff, 0x8c, 0x7a, 0x79, 0xb3, 0xfd, 0xaa, 0xea, 0xa2, 0x5b,
-    0x9b, 0x38, 0x5a, 0x7f, 0x7b, 0x87, 0x3b, 0x15, 0xd6, 0x8e, 0x0f, 0x20,
-    0x85, 0xa7, 0x6b, 0xd7, 0xad, 0x39, 0xe7, 0xea, 0xd3, 0xff, 0xf6, 0xab,
-    0x81, 0x7b, 0xaa, 0x7e, 0x5c, 0x39, 0xa1, 0x96, 0x85, 0x44, 0x37, 0x3f,
-    0xea, 0x79, 0xb3, 0xfd, 0xaa, 0xea, 0xa2, 0x60, 0x9b, 0xb8, 0xb4, 0xdc,
-    0x89, 0x28, 0x23, 0x59, 0xf1, 0x59, 0xfc, 0x19, 0xbd, 0xb7, 0xee, 0xb4,
-    0x8c, 0xc9, 0xca, 0xd8, 0x45, 0xe8, 0xee, 0x0e, 0x75, 0x74, 0xe2, 0xfb,
-    0x75, 0x18, 0x82, 0x71, 0xf9, 0xfa, 0x53, 0xfe, 0xa7, 0x9b, 0x3f, 0xda,
-    0xae, 0xaa, 0x26, 0x39, 0x18, 0x8f, 0x98, 0xe3, 0xb3, 0xf3, 0x5f, 0xc6,
-    0xf3, 0x4b, 0x4e, 0x60, 0xb1, 0x69, 0xff, 0xff, 0xe7, 0xdf, 0x59, 0xcd,
-    0xb7, 0xd6, 0x77, 0x2f, 0xe3, 0x38, 0x7d, 0xc8, 0x09, 0x69, 0xee, 0xd5,
-    0x75, 0x51, 0x58, 0xcf, 0xfb, 0xcb, 0x36, 0x19, 0xcb, 0x06, 0xd6, 0x81,
-    0xd3, 0x35, 0xe9, 0x77, 0x03, 0x7f, 0x84, 0x06, 0xcb, 0x67, 0xff, 0x30,
-    0x6c, 0x7e, 0x58, 0x33, 0xad, 0xf5, 0xa7, 0xfb, 0xdd, 0x65, 0xf8, 0x7b,
-    0x69, 0x69, 0xfe, 0x6e, 0x1e, 0xe3, 0xeb, 0xf6, 0x2d, 0x18, 0x7e, 0xa4,
-    0x75, 0x3f, 0xec, 0xf7, 0xe5, 0xcd, 0x82, 0xbb, 0xd6, 0x9f, 0xff, 0xf8,
-    0x39, 0xb3, 0x3b, 0xe0, 0x39, 0x75, 0xf1, 0x9c, 0x3f, 0x39, 0x01, 0x5d,
-    0x69, 0xfc, 0xe8, 0x58, 0xe3, 0xfc, 0xcf, 0xd8, 0xb4, 0xff, 0xec, 0x73,
-    0x54, 0x7d, 0xe9, 0xb7, 0xc9, 0xd6, 0x9d, 0xa2, 0x36, 0x2a, 0x78, 0xea,
-    0x96, 0xa1, 0x7a, 0x04, 0x1b, 0x41, 0xf2, 0x7c, 0x19, 0x06, 0x61, 0x84,
-    0xb4, 0xdc, 0xb8, 0xb4, 0xfa, 0x82, 0xce, 0x6e, 0xb4, 0x08, 0xf5, 0xc0,
-    0x5e, 0xe3, 0x13, 0xae, 0x35, 0xd6, 0x9e, 0xe2, 0xc6, 0x1d, 0x68, 0xf4,
-    0xf0, 0x79, 0x1d, 0x9f, 0x1f, 0x01, 0x9e, 0xb4, 0xf3, 0x7e, 0x81, 0x69,
-    0xeb, 0xdd, 0x84, 0xb4, 0x31, 0xf2, 0xe8, 0x96, 0xd1, 0xf9, 0xf3, 0x3c,
-    0x5c, 0xbd, 0x69, 0xcc, 0x0f, 0x5a, 0x1c, 0x3c, 0x2d, 0x94, 0x4e, 0xd0,
-    0xc7, 0x5a, 0x7e, 0x63, 0xe0, 0x10, 0xeb, 0x4a, 0xeb, 0x41, 0x1b, 0xba,
-    0x2b, 0x98, 0x4c, 0x94, 0xd6, 0xda, 0x94, 0x11, 0xad, 0xb4, 0x56, 0x7f,
-    0x53, 0xc3, 0x37, 0xa2, 0x48, 0xc6, 0x86, 0x7b, 0xda, 0xe2, 0xeb, 0x4e,
-    0x60, 0x71, 0x69, 0x81, 0x96, 0x87, 0x0d, 0x7f, 0xc6, 0xe7, 0xfe, 0x1c,
-    0x83, 0xb7, 0x63, 0xd7, 0x1b, 0x5a, 0x70, 0xb1, 0xd5, 0x68, 0x1c, 0xf8,
-    0xbe, 0x89, 0x21, 0xd6, 0x9b, 0xe3, 0xad, 0x04, 0x6a, 0x06, 0x11, 0x9f,
-    0xfd, 0xb6, 0x79, 0x6f, 0x59, 0xc6, 0xf9, 0x65, 0xa5, 0x8b, 0x41, 0x1e,
-    0xcf, 0x92, 0x54, 0x32, 0x77, 0xf8, 0xa4, 0x50, 0x84, 0xd2, 0x5d, 0x3d,
-    0x42, 0xd2, 0x3a, 0xd2, 0xd6, 0xcb, 0xa7, 0x61, 0x53, 0xf0, 0x9b, 0x58,
-    0xee, 0x96, 0x9f, 0x8b, 0x79, 0xaa, 0x1d, 0x69, 0xd6, 0xdb, 0x6a, 0x53,
-    0xff, 0x67, 0x7d, 0x6b, 0x06, 0xcd, 0x57, 0x09, 0x18, 0xbf, 0x9e, 0x2f,
-    0xba, 0xfa, 0xb4, 0xbe, 0xb4, 0xf8, 0xe5, 0xb1, 0x52, 0xd1, 0x61, 0xed,
-    0xf9, 0xc9, 0x79, 0x10, 0x9f, 0xdd, 0x2b, 0x86, 0x3b, 0xa5, 0xa6, 0xcd,
-    0xad, 0x1c, 0x1e, 0x3e, 0x8c, 0xa7, 0xb6, 0x18, 0xf5, 0xa7, 0xff, 0x15,
-    0xf5, 0x9e, 0x86, 0x3b, 0x0c, 0xba, 0xd1, 0xf3, 0xe9, 0xd9, 0x04, 0x8c,
-    0xcb, 0xe6, 0x16, 0x37, 0xfb, 0x08, 0x9c, 0x71, 0x22, 0x3f, 0xac, 0x1d,
-    0xb7, 0x51, 0xff, 0xd3, 0x30, 0x8a, 0x3c, 0xd9, 0x68, 0x25, 0xde, 0x16,
-    0x1b, 0x7c, 0x1a, 0x11, 0x93, 0xef, 0xf6, 0xab, 0xaa, 0x8a, 0xde, 0x7f,
-    0xd4, 0xf3, 0x67, 0xfb, 0x55, 0xd5, 0x44, 0xe1, 0x23, 0x61, 0xfe, 0x1c,
-    0xce, 0x6e, 0x74, 0xb4, 0xfb, 0xfd, 0xaa, 0xea, 0xa2, 0xd1, 0x9f, 0x9a,
-    0x83, 0x9f, 0x71, 0x69, 0x88, 0x75, 0xa7, 0xff, 0xd6, 0x36, 0xdb, 0xdf,
-    0xb6, 0xaa, 0xe1, 0x82, 0x5a, 0x7f, 0xcd, 0xc5, 0x79, 0xf4, 0x15, 0xe7,
-    0xb1, 0x68, 0x64, 0x4d, 0x69, 0x52, 0x58, 0xe2, 0x34, 0xaf, 0x0b, 0x09,
-    0x19, 0xd2, 0x4d, 0xdb, 0x05, 0xce, 0x67, 0xa8, 0x7f, 0x4f, 0x76, 0xab,
-    0xaa, 0x8b, 0x6a, 0x7e, 0xd3, 0x1d, 0x8b, 0xd5, 0xa3, 0xe7, 0xb3, 0xb2,
-    0xd9, 0xfe, 0x6f, 0xf8, 0xbb, 0x6a, 0x89, 0x68, 0xc3, 0xda, 0xb4, 0x8a,
-    0x7e, 0x1e, 0xbf, 0xef, 0x20, 0xb4, 0xfa, 0xb8, 0x78, 0xd7, 0x5a, 0x46,
-    0x1c, 0xf6, 0x1d, 0x97, 0xc1, 0x91, 0x4a, 0x27, 0xa9, 0xfc, 0x6c, 0xff,
-    0x6a, 0xba, 0xa8, 0xb9, 0x67, 0xf1, 0xb3, 0xfd, 0xaa, 0xea, 0xa2, 0xed,
-    0x86, 0x6d, 0x17, 0x6c, 0x28, 0x7c, 0xa3, 0x2f, 0x61, 0xbe, 0x3c, 0xbd,
-    0x42, 0xa4, 0x02, 0xf9, 0xe3, 0x6a, 0xd4, 0x73, 0xa1, 0x1d, 0x2d, 0xc8,
-    0xf6, 0x7d, 0x3f, 0x8d, 0x9f, 0xed, 0x57, 0x55, 0x15, 0x3c, 0xc5, 0xb5,
-    0xa7, 0xbb, 0x55, 0xd5, 0x45, 0x73, 0x3f, 0x8d, 0x9f, 0xed, 0x57, 0x55,
-    0x16, 0x74, 0x7c, 0xfa, 0xf4, 0x5b, 0x3f, 0xfd, 0x9b, 0x6b, 0xf8, 0xe6,
-    0xfb, 0x67, 0x1c, 0x25, 0xa7, 0xfd, 0xa2, 0xff, 0xba, 0xc2, 0x01, 0xd6,
-    0x9e, 0xa0, 0x6f, 0x56, 0x9f, 0x7f, 0xb5, 0x5d, 0x54, 0x48, 0x53, 0xfc,
-    0x5c, 0x5f, 0x60, 0xc7, 0xba, 0xd3, 0x3c, 0xd8, 0x7d, 0x62, 0x33, 0x9e,
-    0x74, 0xb4, 0xc7, 0x5a, 0x7f, 0x66, 0xaf, 0x7c, 0xfd, 0x8b, 0x48, 0xde,
-    0xaa, 0x44, 0xc8, 0x40, 0x11, 0x13, 0x8a, 0x67, 0x3e, 0xa8, 0x45, 0xdc,
-    0xb7, 0x64, 0xd3, 0xff, 0x8c, 0x7a, 0x79, 0xb3, 0xfd, 0xaa, 0xea, 0xa2,
-    0x7c, 0x8e, 0xab, 0x59, 0xf3, 0xca, 0x82, 0xd4, 0x20, 0xa7, 0xdf, 0xed,
-    0x57, 0x55, 0x15, 0x94, 0xff, 0xa9, 0xe6, 0xcf, 0xf6, 0xab, 0xaa, 0x89,
-    0xba, 0x62, 0x36, 0x1f, 0xe1, 0xcc, 0xe7, 0xff, 0x18, 0xf4, 0xf3, 0x67,
-    0xfb, 0x55, 0xd5, 0x44, 0xad, 0x3f, 0xe2, 0xbf, 0x27, 0x02, 0x77, 0xcd,
-    0x2d, 0x3f, 0x81, 0xbf, 0x8e, 0xe8, 0xeb, 0x43, 0xcf, 0xc8, 0x90, 0x27,
-    0xdf, 0xed, 0x57, 0x55, 0x12, 0xe4, 0xff, 0x15, 0x3f, 0x60, 0xc7, 0xba,
-    0xd3, 0xce, 0xc8, 0x19, 0x69, 0xf1, 0x8f, 0x4f, 0x33, 0x22, 0xaf, 0x08,
-    0x74, 0x67, 0x46, 0xd3, 0xff, 0x1a, 0x9e, 0x6c, 0xff, 0x6a, 0xba, 0xa8,
-    0x8e, 0xe7, 0xfb, 0x7a, 0xc7, 0x9a, 0x82, 0xeb, 0x4f, 0xde, 0x60, 0x37,
-    0x70, 0x16, 0x9b, 0x9d, 0x2d, 0x3f, 0x3a, 0x21, 0x9a, 0xad, 0xad, 0x3f,
-    0xb2, 0xdb, 0xb0, 0x6e, 0xeb, 0x4f, 0xbf, 0xda, 0xae, 0xaa, 0x2a, 0x19,
-    0x9f, 0x8b, 0x4f, 0x6b, 0x79, 0x62, 0xd3, 0xec, 0xb2, 0x81, 0xda, 0xd3,
-    0xff, 0xff, 0xf9, 0x9f, 0xe3, 0x7a, 0x26, 0xbf, 0x8c, 0xe7, 0xac, 0xef,
-    0xc6, 0x7f, 0xf8, 0xef, 0x9a, 0xd2, 0xd1, 0x88, 0xe2, 0x02, 0x2b, 0x94,
-    0x4f, 0xff, 0xf7, 0x35, 0xe7, 0x2b, 0x35, 0x45, 0xb6, 0xd1, 0x6e, 0xf9,
-    0xea, 0xd2, 0x33, 0xa2, 0xa9, 0xa9, 0xd2, 0x2f, 0xf3, 0x05, 0xec, 0x30,
-    0xc3, 0x2e, 0x99, 0xee, 0x1e, 0xfe, 0x45, 0xd3, 0xef, 0xf6, 0xab, 0xaa,
-    0x8a, 0xa2, 0x7f, 0xd4, 0xf3, 0x67, 0xfb, 0x55, 0xd5, 0x44, 0xd7, 0x23,
-    0x61, 0xfe, 0x1c, 0xce, 0x7f, 0x1b, 0x3f, 0xda, 0xae, 0xaa, 0x2a, 0xc9,
-    0xff, 0x1b, 0x4d, 0xc6, 0xdb, 0x55, 0xe7, 0x5a, 0x7f, 0xe3, 0x53, 0xcd,
-    0x9f, 0xed, 0x57, 0x55, 0x12, 0x24, 0xfb, 0xfd, 0xaa, 0xea, 0xa2, 0xd2,
-    0x9f, 0xf5, 0x3c, 0xd9, 0xfe, 0xd5, 0x75, 0x51, 0x3e, 0xc8, 0xd8, 0x7f,
-    0x87, 0x33, 0x9f, 0xfc, 0x63, 0xd3, 0xcd, 0x9f, 0xed, 0x57, 0x55, 0x14,
-    0x24, 0xfb, 0x45, 0x63, 0x7d, 0x69, 0xf7, 0xfb, 0x55, 0xd5, 0x45, 0x1f,
-    0x3f, 0xe0, 0x67, 0x18, 0x19, 0xcd, 0x32, 0xd3, 0xff, 0xf6, 0x7b, 0xa6,
-    0x18, 0xdb, 0x0a, 0x15, 0xee, 0xc2, 0x4a, 0x7c, 0x63, 0xd3, 0xcc, 0xc8,
-    0xf1, 0xc2, 0x7d, 0x99, 0xf9, 0x1e, 0x43, 0x32, 0x3f, 0xec, 0x8d, 0xc9,
-    0xea, 0xf8, 0x9e, 0x3c, 0xaa, 0x1e, 0xc6, 0x19, 0xc1, 0x39, 0x1f, 0x38,
-    0x85, 0xa8, 0x65, 0x88, 0x9f, 0x71, 0xa3, 0x4f, 0xe3, 0x67, 0xfb, 0x55,
-    0xd5, 0x44, 0x45, 0x3f, 0x67, 0xfb, 0x55, 0xd5, 0x44, 0x57, 0x3f, 0xde,
-    0x73, 0x67, 0xfb, 0x55, 0xd5, 0x45, 0x71, 0x06, 0x44, 0x05, 0x1c, 0x4f,
-    0x66, 0xab, 0x6b, 0x4f, 0xec, 0x2f, 0x88, 0x5c, 0xdd, 0x69, 0x3a, 0x20,
-    0x7a, 0x77, 0x20, 0x9f, 0xff, 0xb7, 0x72, 0xd5, 0x13, 0xc8, 0x37, 0xce,
-    0xbf, 0x8b, 0x4f, 0xbf, 0xda, 0xae, 0xaa, 0x29, 0xe9, 0xef, 0x01, 0x4f,
-    0x5a, 0x7d, 0x59, 0xa2, 0x3a, 0xd2, 0xcb, 0x9e, 0x3b, 0xb2, 0x28, 0xf3,
-    0x13, 0x28, 0x62, 0xcc, 0x59, 0x07, 0xa9, 0xff, 0xd4, 0xf3, 0x7a, 0x15,
-    0x6d, 0xee, 0xc2, 0x5a, 0x0c, 0x88, 0xaf, 0x50, 0x27, 0x1b, 0xdc, 0x5a,
-    0x7f, 0xfd, 0xa6, 0xfe, 0x3c, 0xab, 0x79, 0xe9, 0x73, 0xd5, 0xa7, 0xcc,
-    0xf1, 0x72, 0xf5, 0xa7, 0xbb, 0x55, 0xd5, 0x45, 0x67, 0x0e, 0x1e, 0xaf,
-    0xca, 0x27, 0xeb, 0x0e, 0xc0, 0xfb, 0xad, 0x39, 0xac, 0x25, 0xa7, 0xdb,
-    0x10, 0xc0, 0xf5, 0xa7, 0x73, 0x57, 0x5a, 0x7d, 0x8e, 0x59, 0xc8, 0x96,
-    0x91, 0xbd, 0x4e, 0x0b, 0x21, 0x53, 0xf2, 0x3d, 0x16, 0xd0, 0xd8, 0x14,
-    0x0c, 0x39, 0x3f, 0xf1, 0xa9, 0xe6, 0xcf, 0xf6, 0xab, 0xaa, 0x89, 0x16,
-    0x7e, 0xcf, 0xf6, 0xab, 0xaa, 0x8b, 0x26, 0x7f, 0xf6, 0xee, 0x35, 0xcd,
-    0x96, 0x10, 0x3e, 0xeb, 0x41, 0x91, 0x07, 0x87, 0x13, 0xf8, 0xd9, 0xfe,
-    0xd5, 0x75, 0x51, 0x6c, 0x4e, 0xcb, 0xe2, 0xd3, 0xdd, 0xaa, 0xea, 0xa2,
-    0xdb, 0x9e, 0xbd, 0x95, 0xf5, 0xa3, 0xe7, 0x9f, 0xb2, 0xd9, 0x19, 0xe8,
-    0x8b, 0xc6, 0x99, 0xd6, 0x68, 0x96, 0x9f, 0xf7, 0x99, 0xe6, 0x3a, 0x7d,
-    0xeb, 0x01, 0x84, 0xb4, 0xfc, 0xc0, 0xff, 0x79, 0x3a, 0xd3, 0xef, 0xf6,
-    0xab, 0xaa, 0x8b, 0xc2, 0x7b, 0x7e, 0x59, 0xa5, 0xa7, 0xfe, 0x61, 0x8f,
-    0xc3, 0xf7, 0x81, 0xbb, 0xad, 0x3e, 0xc0, 0x13, 0xab, 0x2d, 0x3e, 0x61,
-    0xb9, 0xab, 0xad, 0x3b, 0x6c, 0x25, 0xa4, 0x6f, 0x31, 0x39, 0xef, 0x34,
-    0x8e, 0x58, 0x9b, 0x85, 0xdc, 0x19, 0xe8, 0x90, 0x48, 0xa0, 0x51, 0xb2,
-    0x89, 0xc5, 0x9c, 0x2d, 0x3e, 0xff, 0x6a, 0xba, 0xa8, 0xbd, 0x27, 0xfc,
-    0x15, 0xb7, 0xb0, 0xfb, 0x2b, 0x16, 0x9f, 0x61, 0xf5, 0x5e, 0xad, 0x23,
-    0x7a, 0x8b, 0x3c, 0x1b, 0x18, 0xcd, 0xd4, 0xfa, 0x19, 0x94, 0x4b, 0x64,
-    0x2f, 0xb2, 0x51, 0x3f, 0x49, 0x0a, 0x39, 0xef, 0xb7, 0x9e, 0x1a, 0x42,
-    0x22, 0x08, 0x64, 0xf3, 0x1e, 0x35, 0xb1, 0xa2, 0xcd, 0xce, 0x96, 0x9e,
-    0xcd, 0x56, 0xd6, 0x9f, 0xd8, 0x5f, 0x10, 0xb9, 0xba, 0xd2, 0x74, 0x40,
-    0xf4, 0xee, 0x41, 0x3e, 0x2d, 0xb7, 0x16, 0x2d, 0x3e, 0xff, 0x6a, 0xba,
-    0xa8, 0x88, 0xe7, 0xff, 0xb5, 0xcd, 0xef, 0x87, 0x0c, 0x77, 0xe1, 0x9d,
-    0x32, 0xd3, 0xfe, 0x7b, 0x59, 0xfc, 0xde, 0xcb, 0x6b, 0x4f, 0xf6, 0x7f,
-    0x2f, 0x94, 0x16, 0x2d, 0x3f, 0xff, 0xcd, 0xaa, 0x2d, 0xb6, 0x83, 0x37,
-    0xa2, 0xd6, 0x15, 0xab, 0x4d, 0x61, 0xd2, 0x9a, 0xdb, 0x52, 0x9f, 0xf1,
-    0xbb, 0xa2, 0x73, 0x4d, 0x61, 0x84, 0x6b, 0xed, 0x17, 0x9e, 0x76, 0xdb,
-    0x65, 0xa1, 0xe7, 0xf4, 0x4b, 0x13, 0xf7, 0x9c, 0x33, 0x55, 0xb5, 0xa7,
-    0xe6, 0x1f, 0x00, 0x87, 0x5a, 0x7e, 0xcd, 0xba, 0xef, 0x34, 0xb4, 0x62,
-    0x22, 0x80, 0xbe, 0xe5, 0x93, 0xff, 0xf7, 0x4d, 0xfa, 0x02, 0xd9, 0xbe,
-    0xdb, 0x0a, 0x12, 0xd0, 0xe9, 0x2b, 0xfa, 0xf3, 0x1a, 0x98, 0xb3, 0x0a,
-    0x3a, 0x67, 0xc2, 0xc9, 0x1f, 0x7c, 0xda, 0xa3, 0x0b, 0xdc, 0x2a, 0xdd,
-    0x97, 0x4f, 0xbf, 0xda, 0xae, 0xaa, 0x22, 0xe9, 0xf6, 0xc4, 0x30, 0x3d,
-    0x29, 0x1b, 0x0f, 0x6e, 0x8c, 0xe0, 0xc9, 0x97, 0xbe, 0x30, 0x99, 0xff,
-    0x8f, 0x4f, 0x36, 0x7f, 0xb5, 0x5d, 0x54, 0x4c, 0xd3, 0x5d, 0xeb, 0x4f,
-    0x9d, 0x3b, 0xa3, 0x76, 0xda, 0xd0, 0xe9, 0xcf, 0x23, 0xcd, 0x62, 0xd3,
-    0xb8, 0x6b, 0xad, 0x3b, 0xa4, 0x75, 0xa6, 0xf2, 0x74, 0x16, 0x9f, 0xfd,
-    0xa6, 0x3d, 0x6f, 0x38, 0x11, 0xc6, 0xd2, 0xd3, 0xf6, 0xf2, 0xcc, 0x13,
-    0x8b, 0x4f, 0xf7, 0xbe, 0x30, 0x2b, 0xe2, 0xf2, 0x5a, 0x1d, 0x04, 0x6a,
-    0x10, 0xf3, 0x89, 0x7a, 0x2e, 0x9f, 0x76, 0xb4, 0x4e, 0x2d, 0x3f, 0xfe,
-    0x17, 0x0f, 0xd7, 0x2d, 0xf3, 0x68, 0xbb, 0x5f, 0x5a, 0x7f, 0xe7, 0x52,
-    0xd9, 0x74, 0xbd, 0xde, 0x75, 0x69, 0xff, 0xfe, 0xe6, 0x83, 0xa1, 0x9e,
-    0xeb, 0x9f, 0x1a, 0x67, 0xfd, 0xac, 0x5a, 0x19, 0x30, 0x7c, 0x57, 0xd2,
-    0x34, 0xff, 0xfb, 0xd2, 0x0b, 0x32, 0xe5, 0x5a, 0x35, 0xb6, 0xda, 0x94,
-    0xfd, 0xc3, 0xee, 0x2e, 0x5c, 0x5a, 0x7b, 0xb5, 0x5d, 0x54, 0x59, 0xf3,
-    0xfe, 0x2d, 0x33, 0xcb, 0x6d, 0xbb, 0xad, 0x3f, 0xfc, 0x5f, 0xd5, 0x78,
-    0xc7, 0x61, 0x97, 0xc0, 0x5a, 0x75, 0xb6, 0xda, 0x94, 0xff, 0xa9, 0xf7,
-    0x0c, 0xef, 0xb9, 0xf4, 0x8c, 0x5f, 0xcf, 0xfc, 0xdc, 0x3c, 0x39, 0x17,
-    0x8e, 0x9c, 0x4b, 0x4f, 0xf3, 0x68, 0xbd, 0x0a, 0xa7, 0x16, 0x9f, 0xe2,
-    0x0f, 0x46, 0xf7, 0x9d, 0xdd, 0x68, 0xea, 0xa6, 0x77, 0x16, 0x3e, 0x62,
-    0x72, 0xdd, 0x1e, 0x09, 0xba, 0xe9, 0x9b, 0x48, 0x76, 0x6f, 0x3f, 0xdf,
-    0xab, 0x74, 0x4d, 0xf6, 0x5a, 0x7f, 0xff, 0xfd, 0x82, 0xbe, 0x60, 0x0e,
-    0x37, 0xc1, 0xb3, 0x46, 0xb8, 0x65, 0x87, 0xc3, 0xad, 0x3e, 0x77, 0x5a,
-    0x61, 0x96, 0x8c, 0x45, 0x51, 0x42, 0x06, 0x75, 0xda, 0xc5, 0xa7, 0x79,
-    0x66, 0x96, 0x87, 0x9f, 0x05, 0xc9, 0xb6, 0x39, 0x3b, 0x5c, 0xda, 0xb4,
-    0xe1, 0x73, 0xd5, 0xa7, 0x6f, 0x06, 0x5a, 0x3d, 0x3d, 0x7e, 0x07, 0x88,
-    0x76, 0x7e, 0xf9, 0x3b, 0xc0, 0xba, 0xd3, 0xed, 0x03, 0x67, 0xd4, 0x9f,
-    0xf1, 0x7d, 0xf9, 0xfb, 0x39, 0xaf, 0x55, 0x10, 0x69, 0x8d, 0x2c, 0xfa,
-    0xba, 0x47, 0x65, 0xa7, 0xe7, 0xe8, 0x2b, 0x8d, 0xad, 0x2c, 0xe9, 0xe9,
-    0x01, 0x2c, 0xff, 0xd8, 0x1c, 0x6f, 0x3e, 0x56, 0x51, 0xd6, 0x98, 0x81,
-    0x69, 0xfa, 0xad, 0xde, 0x6b, 0x6c, 0x7a, 0xe3, 0xa1, 0xc3, 0x27, 0x86,
-    0xf5, 0x2c, 0x85, 0x78, 0x3e, 0xcf, 0xdf, 0xcf, 0x6f, 0xa6, 0x5a, 0x7f,
-    0xff, 0x72, 0x14, 0x3f, 0x81, 0x82, 0xb7, 0x72, 0xe3, 0x45, 0x6a, 0xd3,
-    0xfe, 0x3e, 0x6e, 0xe3, 0x5e, 0x9f, 0x8b, 0x4f, 0xd7, 0xbb, 0x7f, 0x6c,
-    0xb1, 0x8d, 0xf4, 0xf6, 0xb0, 0x9d, 0xad, 0x3f, 0x5c, 0xbf, 0x9c, 0xfd,
-    0x68, 0x24, 0x45, 0xe8, 0xf3, 0x64, 0x53, 0x1d, 0xd1, 0x54, 0x5f, 0x93,
-    0xff, 0x3f, 0x7c, 0xbd, 0xe7, 0xde, 0xd8, 0xeb, 0x4f, 0xfc, 0x58, 0x19,
-    0x97, 0xf0, 0xfb, 0x9d, 0x68, 0xe1, 0x11, 0x37, 0x46, 0x9c, 0x3b, 0x7d,
-    0x69, 0xfb, 0xd6, 0x17, 0xf3, 0x85, 0xa0, 0xc7, 0x94, 0x71, 0xc9, 0x89,
-    0xc5, 0xa7, 0x6c, 0xac, 0x5a, 0x70, 0x37, 0x56, 0x8f, 0x07, 0x98, 0x71,
-    0x60, 0x1c, 0x86, 0x4f, 0xb3, 0xd2, 0xfc, 0x85, 0x89, 0x37, 0x53, 0x74,
-    0xf5, 0x6d, 0xb8, 0x5a, 0x7f, 0xff, 0xcd, 0xa2, 0x39, 0x9c, 0x7d, 0x7c,
-    0x6d, 0x17, 0x17, 0xe9, 0x58, 0xb4, 0x38, 0x89, 0x07, 0x52, 0x19, 0xfa,
-    0xbe, 0xda, 0x1b, 0xd5, 0xa7, 0xad, 0xac, 0xba, 0xd3, 0xfc, 0xf3, 0xd3,
-    0xf7, 0x76, 0x1d, 0x68, 0x63, 0xda, 0xa2, 0x19, 0xec, 0xb9, 0x02, 0xd1,
-    0x48, 0xc7, 0x08, 0x44, 0x72, 0x41, 0x3c, 0x37, 0x35, 0x75, 0xa7, 0x84,
-    0x2c, 0xe1, 0x69, 0xf9, 0xd7, 0x79, 0x41, 0xd5, 0xa7, 0xfa, 0xe2, 0x2e,
-    0x38, 0x7e, 0x6d, 0x69, 0xf9, 0xb8, 0xb9, 0xcb, 0xce, 0xb4, 0x7d, 0x17,
-    0x7a, 0x22, 0xd9, 0x73, 0xb3, 0xa8, 0x64, 0xc7, 0x72, 0x1b, 0xd3, 0x39,
-    0xca, 0xd2, 0x3a, 0xd1, 0xc1, 0xa8, 0xf3, 0x63, 0x13, 0xfa, 0xab, 0x87,
-    0x83, 0x7a, 0xb4, 0xff, 0xe0, 0x26, 0xb8, 0x53, 0xfc, 0x5f, 0x3e, 0xb4,
-    0xff, 0xff, 0xb4, 0x5c, 0x6f, 0x38, 0xdf, 0xfe, 0xdb, 0x10, 0xc0, 0xfd,
-    0x62, 0xd3, 0xde, 0xfb, 0x40, 0xb4, 0xff, 0xab, 0x5d, 0x2a, 0xf1, 0x7a,
-    0xd2, 0xd3, 0xfd, 0xa2, 0xa1, 0x67, 0xb4, 0xf5, 0xa1, 0x93, 0xa7, 0xf4,
-    0xa3, 0x0c, 0xba, 0x90, 0x4d, 0xda, 0x22, 0xd9, 0xf4, 0xff, 0xd8, 0x1c,
-    0x6f, 0x3e, 0x56, 0x51, 0xd6, 0x9f, 0x66, 0xde, 0x7d, 0x2d, 0x37, 0x8c,
-    0x5a, 0x7a, 0xe0, 0xc7, 0x5a, 0x18, 0xdb, 0xf0, 0x2f, 0x0c, 0x8c, 0x2b,
-    0xa1, 0xf2, 0xbd, 0x2d, 0x2d, 0x35, 0xde, 0xb4, 0x08, 0xd3, 0x72, 0x21,
-    0x3f, 0x58, 0xe3, 0xfc, 0xcf, 0xd8, 0xb4, 0xfe, 0x1f, 0x59, 0xbd, 0x16,
-    0xd6, 0x98, 0xbe, 0xb4, 0x3a, 0x07, 0xfc, 0x46, 0xda, 0x33, 0x9f, 0xfb,
-    0xd6, 0xf7, 0xed, 0xb2, 0xe9, 0x7a, 0xb4, 0xfd, 0xae, 0x6c, 0x73, 0x44,
-    0xb4, 0xf0, 0x06, 0x6d, 0x68, 0xf5, 0x12, 0x9a, 0x45, 0xd9, 0x84, 0x8c,
-    0xe8, 0x36, 0xa5, 0x4e, 0x91, 0x83, 0xa7, 0x1c, 0x68, 0x77, 0xd8, 0x85,
-    0xec, 0x65, 0xd9, 0x2a, 0x58, 0x78, 0x41, 0xf1, 0x1a, 0x09, 0x42, 0x19,
-    0xc8, 0xfc, 0xfe, 0x80, 0x72, 0xdd, 0x4b, 0x1a, 0xa8, 0x6e, 0x8a, 0x1d,
-    0xc1, 0x1a, 0x8d, 0xe3, 0xf6, 0xdc, 0x64, 0xb6, 0xab, 0x79, 0x42, 0x80,
-    0x68, 0x63, 0xc1, 0x9b, 0x75, 0xdd, 0x56, 0xb8, 0x33, 0xff, 0x8c, 0x7a,
-    0x79, 0xb3, 0xfd, 0xaa, 0xea, 0xa2, 0x6c, 0x9f, 0xc6, 0xcf, 0xf6, 0xab,
-    0xaa, 0x8a, 0xb6, 0x7f, 0x3d, 0xfc, 0xec, 0x19, 0xeb, 0x4f, 0x66, 0xab,
-    0x6b, 0x49, 0xd1, 0xc3, 0xd3, 0x03, 0x39, 0xf7, 0xfb, 0x55, 0xd5, 0x45,
-    0x69, 0x3f, 0xfd, 0xd6, 0xfe, 0x0a, 0x8f, 0x4f, 0x62, 0xea, 0xd3, 0xff,
-    0xcf, 0xbe, 0x03, 0x1e, 0xb3, 0x7b, 0x63, 0xad, 0x36, 0xf6, 0xc8, 0x98,
-    0xa4, 0xc9, 0xfc, 0xce, 0xc6, 0xd8, 0x30, 0x96, 0x9f, 0xac, 0xcd, 0x16,
-    0x1d, 0x69, 0xf3, 0x99, 0xe9, 0xfc, 0xeb, 0x4f, 0xc0, 0xd6, 0xf0, 0xfb,
-    0xad, 0x2c, 0x70, 0xf6, 0xdd, 0x4b, 0x67, 0xf3, 0xf3, 0x74, 0x02, 0xe5,
-    0x69, 0x1b, 0xcc, 0x54, 0x77, 0x85, 0x65, 0x0c, 0xbd, 0x16, 0xd1, 0xa0,
-    0x42, 0x1f, 0x65, 0x73, 0xf8, 0xd9, 0xfe, 0xd5, 0x75, 0x51, 0x60, 0x4f,
-    0xbf, 0xda, 0xae, 0xaa, 0x27, 0x59, 0xff, 0xfd, 0xa6, 0xb3, 0xdc, 0x39,
-    0xaf, 0x7c, 0xd1, 0x5f, 0x9b, 0x16, 0x9f, 0x18, 0xf4, 0xf3, 0x62, 0x25,
-    0xdc, 0x33, 0x9f, 0xcf, 0xb9, 0x87, 0x1d, 0x8e, 0xb4, 0xfb, 0xfd, 0xaa,
-    0xea, 0xa2, 0xd9, 0x9f, 0xf0, 0x9a, 0xe6, 0xd1, 0x76, 0xbe, 0xb4, 0x8d,
-    0x87, 0xe1, 0x46, 0x73, 0xdd, 0xaa, 0xea, 0xa2, 0xe6, 0x91, 0xd6, 0x8f,
-    0x9b, 0xdb, 0x4b, 0x66, 0x70, 0xeb, 0x48, 0xd8, 0x6e, 0x6d, 0x22, 0x9f,
-    0x7f, 0xb5, 0x5d, 0x54, 0x5d, 0xf3, 0xf9, 0xe6, 0xbf, 0xfe, 0x56, 0xad,
-    0x23, 0x61, 0xf3, 0xd1, 0x9c, 0xf1, 0x87, 0xf4, 0x4b, 0x43, 0x37, 0xcf,
-    0xf9, 0x5b, 0xc3, 0xf4, 0xcb, 0x85, 0x52, 0x95, 0x99, 0xf7, 0x73, 0xc3,
-    0x56, 0x90, 0x05, 0x0a, 0x5b, 0xc2, 0x73, 0x70, 0x8e, 0x18, 0x9a, 0x7d,
-    0x7b, 0x9e, 0x9e, 0xb4, 0xff, 0xdc, 0x77, 0x9a, 0xe3, 0x79, 0x80, 0x3a,
-    0xd3, 0x82, 0x8c, 0xc7, 0xdb, 0xb2, 0x79, 0xfc, 0x16, 0x63, 0xae, 0xd8,
-    0x75, 0xa7, 0xdf, 0xed, 0x57, 0x55, 0x12, 0xbc, 0xff, 0x82, 0xad, 0xf7,
-    0x4d, 0x66, 0x5a, 0xb4, 0xff, 0xfc, 0xfc, 0x21, 0x61, 0xcb, 0x77, 0x63,
-    0xd7, 0x1b, 0x5a, 0x7b, 0x9a, 0xfb, 0xd6, 0x9f, 0xff, 0xc5, 0xba, 0x3d,
-    0x00, 0xb5, 0x6d, 0x05, 0x9d, 0x2b, 0x16, 0x8f, 0x51, 0x05, 0x44, 0x51,
-    0xea, 0x67, 0xd8, 0x81, 0xa8, 0x6d, 0xcf, 0xfb, 0x74, 0xfb, 0xe9, 0xbe,
-    0x37, 0xab, 0x4f, 0xfd, 0xb2, 0x76, 0x5b, 0x71, 0xec, 0x1b, 0x5a, 0x75,
-    0x3c, 0xcc, 0xa9, 0x8b, 0x0d, 0xaf, 0x1b, 0x8e, 0xcd, 0x86, 0x40, 0x9f,
-    0xfd, 0x9a, 0x30, 0x67, 0xa4, 0x1b, 0xcd, 0x2d, 0x3b, 0x6d, 0xea, 0x53,
-    0xff, 0xb5, 0x45, 0xb6, 0x79, 0x7e, 0xce, 0x44, 0x94, 0xfe, 0xae, 0x9b,
-    0xdf, 0xf9, 0xb3, 0x31, 0xf3, 0x5c, 0x72, 0x46, 0xea, 0xbf, 0x33, 0xca,
-    0x93, 0xdb, 0x13, 0xb8, 0x56, 0xcf, 0xfe, 0x31, 0xe9, 0xe6, 0xcf, 0xf6,
-    0xab, 0xaa, 0x89, 0x86, 0x7f, 0xf0, 0x33, 0xb0, 0x6e, 0x0c, 0xe8, 0x3a,
-    0x7b, 0xbd, 0x69, 0xff, 0xdf, 0xa1, 0x18, 0x7d, 0xbe, 0xd6, 0xbb, 0xd6,
-    0x9b, 0x0d, 0xea, 0x28, 0x86, 0x56, 0x9f, 0xf9, 0xd1, 0xce, 0x1f, 0xcd,
-    0x6c, 0x19, 0xeb, 0x4f, 0xe6, 0xab, 0x36, 0x3d, 0x7d, 0x69, 0xf5, 0x7d,
-    0xf4, 0x75, 0xa0, 0x0f, 0x67, 0x93, 0x39, 0xf8, 0xed, 0x81, 0xbf, 0x25,
-    0xa5, 0x75, 0xa7, 0xcd, 0x81, 0xbf, 0x25, 0xa7, 0xed, 0x16, 0xef, 0x5c,
-    0x78, 0x3e, 0x67, 0x0b, 0x8e, 0x21, 0x3f, 0xff, 0xe0, 0xcb, 0x5d, 0x77,
-    0x9a, 0xf0, 0x19, 0x61, 0x60, 0x70, 0xfb, 0xad, 0x3f, 0xfc, 0xdb, 0x0a,
-    0x16, 0xb0, 0xbd, 0xe7, 0x77, 0x5a, 0x7e, 0x76, 0x36, 0xc1, 0x84, 0xb4,
-    0xff, 0xe6, 0xd8, 0x99, 0xf9, 0xc6, 0xec, 0xe6, 0xd5, 0xa1, 0x8f, 0xf0,
-    0x8b, 0xe7, 0xeb, 0x90, 0xe7, 0xad, 0xad, 0x3f, 0xff, 0xef, 0xe3, 0xb6,
-    0x0d, 0xf8, 0x18, 0x1b, 0x54, 0x5b, 0x6d, 0xdd, 0x68, 0xe1, 0x13, 0x5f,
-    0x2d, 0x9e, 0xf0, 0xed, 0xd6, 0xc5, 0xa3, 0x0f, 0x38, 0x72, 0x49, 0xff,
-    0xf6, 0x83, 0x37, 0xa2, 0xce, 0x37, 0x8e, 0xda, 0xeb, 0x4e, 0x61, 0x0c,
-    0xb4, 0x61, 0xf8, 0x82, 0xac, 0xf5, 0x0d, 0xee, 0x2d, 0x23, 0x79, 0x8b,
-    0x92, 0xfe, 0xc2, 0x8b, 0xb0, 0x88, 0x71, 0x57, 0xee, 0x1a, 0x86, 0x58,
-    0x46, 0x11, 0xb8, 0x49, 0xf2, 0x41, 0x3f, 0xfd, 0xf2, 0xe1, 0xf7, 0x2d,
-    0xe9, 0xbf, 0xb6, 0x5a, 0x7d, 0xfe, 0xd5, 0x75, 0x51, 0x57, 0x4f, 0xf3,
-    0xcd, 0x9f, 0xed, 0x57, 0x55, 0x11, 0xe4, 0x8d, 0xea, 0x31, 0xb1, 0x3e,
-    0x8c, 0xe7, 0xf1, 0xb3, 0xfd, 0xaa, 0xea, 0xa2, 0xc1, 0x9f, 0xc6, 0xcf,
-    0xf6, 0xab, 0xaa, 0x8b, 0x2a, 0x7f, 0x1b, 0x3f, 0xda, 0xae, 0xaa, 0x2d,
-    0x39, 0xfd, 0x97, 0xf1, 0xff, 0xf3, 0xa5, 0xa7, 0xbb, 0x55, 0xd5, 0x45,
-    0xb9, 0x3f, 0xf3, 0x67, 0xf0, 0x3c, 0x6b, 0x38, 0xb5, 0x68, 0xf9, 0xf9,
-    0x5c, 0xb6, 0x40, 0xb4, 0xff, 0xc5, 0xee, 0xb9, 0xab, 0xe6, 0x00, 0xeb,
-    0x46, 0x1e, 0x9e, 0x83, 0xe7, 0xf9, 0x83, 0x93, 0xb5, 0xf6, 0x4b, 0x4f,
-    0xea, 0xab, 0x3d, 0x6c, 0xfa, 0xd3, 0xfe, 0xa7, 0x9b, 0x3f, 0xda, 0xae,
-    0xaa, 0x28, 0x79, 0xfc, 0xdf, 0x13, 0x77, 0x0e, 0xb4, 0xff, 0xc7, 0xaf,
-    0x5b, 0xdd, 0x10, 0x11, 0xd6, 0x9f, 0xfb, 0x97, 0xea, 0xbf, 0xc6, 0xd8,
-    0xac, 0x5a, 0x7f, 0xdc, 0xfc, 0x5f, 0xa0, 0xde, 0x09, 0x69, 0xfe, 0xc0,
-    0x0a, 0xd8, 0xcd, 0xd5, 0xa7, 0xd5, 0xc5, 0xf6, 0x4b, 0x46, 0x8f, 0x76,
-    0xe6, 0xd3, 0xf0, 0x79, 0x99, 0xf6, 0xba, 0xd3, 0xff, 0xf6, 0x7b, 0xa6,
-    0x18, 0xdb, 0x0a, 0x15, 0xee, 0xc2, 0x4a, 0x7f, 0x6a, 0x84, 0xe0, 0x37,
-    0xab, 0x48, 0xcf, 0x5c, 0x25, 0xc8, 0x54, 0x75, 0xd4, 0x88, 0x5c, 0x37,
-    0x39, 0x96, 0x92, 0x68, 0xc4, 0x48, 0x77, 0x46, 0xdc, 0x26, 0xf9, 0x22,
-    0xf2, 0x30, 0x19, 0x6e, 0x7f, 0x1b, 0x3f, 0xda, 0xae, 0xaa, 0x2f, 0x38,
-    0x66, 0x6d, 0x33, 0xd4, 0xfd, 0x87, 0xbe, 0x4e, 0x63, 0xf1, 0x1b, 0x77,
-    0xcd, 0x4e, 0x7d, 0xa3, 0xe0, 0x9c, 0x63, 0x77, 0x0d, 0x59, 0xfc, 0x6c,
-    0xff, 0x6a, 0xba, 0xa8, 0xa5, 0xa7, 0xdf, 0xed, 0x57, 0x55, 0x16, 0x14,
-    0xff, 0xf9, 0xb0, 0x3d, 0x6d, 0x37, 0x1b, 0xc0, 0xa3, 0xad, 0x23, 0x62,
-    0x21, 0x4e, 0x67, 0x3f, 0x8d, 0x9f, 0xed, 0x57, 0x55, 0x16, 0x5c, 0xf1,
-    0x87, 0xf3, 0x5b, 0xa6, 0x5a, 0x71, 0x86, 0x3a, 0xd2, 0xbf, 0x4f, 0x3b,
-    0x93, 0x19, 0xf5, 0x7f, 0x07, 0xb1, 0x69, 0xfd, 0x67, 0x17, 0x6f, 0x76,
-    0x4b, 0x4f, 0x9b, 0x1d, 0xb5, 0xd6, 0x98, 0x1a, 0x8f, 0x72, 0xd3, 0x59,
-    0xea, 0x7b, 0xfd, 0x5a, 0x7e, 0xaf, 0xb6, 0xeb, 0x4b, 0x4c, 0xd7, 0x5a,
-    0x3d, 0x3e, 0x2d, 0x11, 0x3b, 0x2b, 0x9f, 0x8a, 0xae, 0x2b, 0xf2, 0xb4,
-    0xff, 0x7d, 0x9f, 0xba, 0xd5, 0x7a, 0xb4, 0x9e, 0xb4, 0x9e, 0xb4, 0x9e,
-    0xb4, 0x31, 0xb0, 0x1c, 0x40, 0x84, 0x27, 0xff, 0xdb, 0xb9, 0x77, 0x03,
-    0xec, 0x19, 0xee, 0x0e, 0xb4, 0xb8, 0x5a, 0x6b, 0xf2, 0xb4, 0x61, 0xfd,
-    0xdd, 0x4f, 0x62, 0x33, 0x13, 0x2d, 0x3f, 0x03, 0x1c, 0x1b, 0x16, 0x99,
-    0xec, 0xb4, 0x7a, 0x7a, 0x4e, 0x0a, 0x72, 0x53, 0x3f, 0xff, 0xfd, 0xa6,
-    0xdd, 0xca, 0xf7, 0x6d, 0xe8, 0x9b, 0xe2, 0x3d, 0x3d, 0xfb, 0x1d, 0x69,
-    0x98, 0xeb, 0x4f, 0xff, 0xcf, 0x60, 0xe5, 0xef, 0x6d, 0xeb, 0x0e, 0x33,
-    0x09, 0x69, 0xba, 0x66, 0x5c, 0x49, 0xb0, 0xa7, 0xd8, 0x42, 0x64, 0x23,
-    0xba, 0x65, 0xf2, 0xda, 0x68, 0x08, 0x54, 0x5e, 0x10, 0x1b, 0x32, 0xf2,
-    0x7d, 0x18, 0x56, 0x7f, 0xf1, 0x8f, 0x4f, 0x36, 0x7f, 0xb5, 0x5d, 0x54,
-    0x51, 0x93, 0xe7, 0x6d, 0xf6, 0x12, 0xd3, 0xe0, 0xa1, 0x30, 0xcb, 0x48,
-    0xcc, 0x79, 0xda, 0x28, 0x87, 0xaf, 0xaa, 0x7e, 0x18, 0x67, 0x28, 0xa9,
-    0xcd, 0x5b, 0xc3, 0x2b, 0x70, 0xa3, 0x9f, 0xf8, 0xd4, 0xf3, 0x67, 0xfb,
-    0x55, 0xd5, 0x44, 0x73, 0x3f, 0xf8, 0xc7, 0xa7, 0x9b, 0x3f, 0xda, 0xae,
-    0xaa, 0x27, 0x29, 0xfc, 0x6c, 0xff, 0x6a, 0xba, 0xa8, 0xb3, 0x27, 0xfb,
-    0xcd, 0x5e, 0xe1, 0xdb, 0x86, 0x3a, 0xd3, 0xfc, 0x2f, 0xb6, 0xf5, 0x5c,
-    0x3d, 0x69, 0xfd, 0x97, 0xff, 0xf3, 0x8b, 0xad, 0x23, 0x3a, 0x64, 0x56,
-    0xb2, 0x0b, 0xce, 0x67, 0xf1, 0xb3, 0xfd, 0xaa, 0xea, 0xa2, 0xdd, 0x9f,
-    0xfc, 0x63, 0xd3, 0xcd, 0x9f, 0xed, 0x57, 0x55, 0x14, 0x84, 0xff, 0xc6,
-    0xa7, 0x9b, 0x3f, 0xda, 0xae, 0xaa, 0x25, 0x28, 0xb1, 0x55, 0x63, 0x89,
-    0xe7, 0x55, 0x14, 0x3a, 0xc0, 0xef, 0x67, 0xce, 0xaa, 0xb3, 0xfe, 0xa7,
-    0x9b, 0x3f, 0xda, 0xae, 0xaa, 0x27, 0x69, 0xff, 0xf1, 0x7b, 0xc9, 0xd9,
-    0xd6, 0xbd, 0x06, 0x0c, 0xea, 0xd2, 0x31, 0xd1, 0x41, 0x49, 0x33, 0xff,
-    0x51, 0xf5, 0x9d, 0xad, 0x30, 0xcf, 0x5a, 0x7f, 0xe0, 0xcd, 0x55, 0x99,
-    0x70, 0xae, 0x16, 0x9b, 0xc8, 0xde, 0xa2, 0x16, 0xe8, 0x70, 0x74, 0x70,
-    0x79, 0x42, 0xb6, 0x7d, 0xfe, 0xd5, 0x75, 0x51, 0x16, 0x4f, 0xfa, 0x9e,
-    0x6c, 0xff, 0x6a, 0xba, 0xa8, 0x97, 0x67, 0xff, 0xec, 0xf7, 0x4c, 0x31,
-    0xb6, 0x14, 0x2b, 0xdd, 0x84, 0x94, 0x8d, 0x88, 0xd7, 0x39, 0x9f, 0x92,
-    0x4c, 0xff, 0xe3, 0x1e, 0x9e, 0x6c, 0xff, 0x6a, 0xba, 0xa8, 0x98, 0xa7,
-    0xdf, 0xed, 0x57, 0x55, 0x15, 0x4c, 0xb1, 0x68, 0xc3, 0xc2, 0x1c, 0xce,
-    0x7f, 0xff, 0xf7, 0xeb, 0x5c, 0x3f, 0x36, 0x6d, 0x55, 0xfe, 0x56, 0xef,
-    0x45, 0xf7, 0xad, 0x06, 0x44, 0xdd, 0x11, 0x4f, 0xfe, 0x31, 0xe9, 0xe6,
-    0xcf, 0xf6, 0xab, 0xaa, 0x89, 0xd2, 0x7f, 0x15, 0x7d, 0x9f, 0x4e, 0xd6,
-    0x9f, 0x38, 0xce, 0x35, 0xd6, 0x9f, 0xad, 0xc2, 0x77, 0x80, 0xb4, 0x11,
-    0xea, 0xdc, 0xa2, 0x71, 0x69, 0x96, 0x31, 0xa1, 0x9f, 0xfb, 0x9b, 0xed,
-    0xb8, 0x37, 0x2e, 0x11, 0xd6, 0x83, 0x9f, 0x96, 0x8a, 0xa7, 0xfd, 0x4f,
-    0x36, 0x7f, 0xb5, 0x5d, 0x54, 0x4e, 0xf3, 0xed, 0xf3, 0x47, 0x24, 0xa4,
-    0x6f, 0x53, 0xac, 0xc8, 0xc6, 0x8e, 0x45, 0x49, 0x33, 0xff, 0x8c, 0x7a,
-    0x79, 0xb3, 0xfd, 0xaa, 0xea, 0xa2, 0x85, 0x9f, 0xfc, 0x63, 0xd3, 0xcd,
-    0x9f, 0xed, 0x57, 0x55, 0x14, 0x94, 0xff, 0xfe, 0xcb, 0x98, 0x6c, 0x73,
-    0x54, 0x7d, 0xe9, 0xb7, 0xc9, 0xd6, 0x8b, 0x17, 0x38, 0xde, 0x71, 0xd8,
-    0xcc, 0xbe, 0x44, 0x79, 0x40, 0x22, 0x49, 0xe5, 0x57, 0xc9, 0x56, 0x7f,
-    0x1b, 0x3f, 0xda, 0xae, 0xaa, 0x22, 0x49, 0xff, 0xc6, 0x3d, 0x3c, 0xd9,
-    0xfe, 0xd5, 0x75, 0x51, 0x2f, 0x4f, 0x7f, 0x9d, 0xdd, 0x69, 0xf7, 0xc4,
-    0x72, 0xba, 0xd3, 0x84, 0xdb, 0x5a, 0x7f, 0xea, 0x72, 0xec, 0xee, 0xb4,
-    0xdc, 0x38, 0xb4, 0x7a, 0x8a, 0xc2, 0x23, 0x70, 0x9f, 0xe3, 0x73, 0xfc,
-    0x54, 0x1c, 0xed, 0xc6, 0x1d, 0x69, 0xf7, 0xfb, 0x55, 0xd5, 0x45, 0x2f,
-    0x3e, 0x0d, 0xd9, 0x96, 0xad, 0x3f, 0xc2, 0xbf, 0xdb, 0x87, 0x91, 0xd6,
-    0x9f, 0xea, 0xb3, 0xc5, 0x9c, 0xb8, 0xde, 0x75, 0xa6, 0xc7, 0xb1, 0xfd,
-    0xf2, 0x73, 0x3f, 0x7c, 0x4d, 0xdc, 0x3a, 0xd3, 0x3f, 0x16, 0x9c, 0xc2,
-    0xba, 0xd0, 0xc7, 0xb9, 0xe9, 0x65, 0xa2, 0xb3, 0xff, 0x11, 0xeb, 0xd1,
-    0x9c, 0x7b, 0x07, 0x0b, 0x4f, 0x5c, 0xad, 0xda, 0xd1, 0xa3, 0xe8, 0xda,
-    0x34, 0xff, 0x56, 0xa9, 0xc2, 0xfb, 0x58, 0xb4, 0xf9, 0xd4, 0x8e, 0xd7,
-    0x5a, 0x7d, 0x4e, 0xf3, 0xf7, 0x5a, 0x4d, 0xe9, 0xe8, 0x9c, 0xa6, 0x7c,
-    0x43, 0xdc, 0x5d, 0x5a, 0x46, 0x65, 0x6d, 0x3e, 0x9f, 0x61, 0xcf, 0x06,
-    0x6e, 0x42, 0x8f, 0x50, 0x89, 0xa8, 0x4c, 0x5c, 0x8b, 0x70, 0x89, 0xf2,
-    0x28, 0x9f, 0xc6, 0xcf, 0xf6, 0xab, 0xaa, 0x8a, 0x72, 0x7f, 0xc5, 0xfa,
-    0xbe, 0xb3, 0x03, 0xab, 0x4f, 0xfc, 0x39, 0x71, 0x70, 0xcf, 0x75, 0x56,
-    0xad, 0x3f, 0xec, 0xd3, 0x7f, 0x0d, 0x6d, 0xb6, 0xa5, 0x31, 0x5a, 0xb4,
-    0xd6, 0x1b, 0xd4, 0x6b, 0xec, 0xef, 0xc9, 0x19, 0xd5, 0x02, 0x7d, 0xfe,
-    0xd5, 0x75, 0x51, 0x5e, 0x4f, 0xff, 0xd9, 0xee, 0x98, 0x63, 0x6c, 0x28,
-    0x57, 0xbb, 0x09, 0x29, 0x1b, 0x11, 0x1d, 0xe4, 0x67, 0x3f, 0xf1, 0xa9,
-    0xe6, 0xcf, 0xf6, 0xab, 0xaa, 0x89, 0x1e, 0x71, 0x09, 0x96, 0x9d, 0xdc,
-    0x3a, 0x46, 0x2e, 0xa7, 0xdf, 0xed, 0x57, 0x55, 0x12, 0x44, 0xf1, 0xa9,
-    0xe6, 0x63, 0xdd, 0xc2, 0xa9, 0xff, 0x8d, 0x4f, 0x36, 0x7f, 0xb5, 0x5d,
-    0x54, 0x49, 0x53, 0xef, 0xf6, 0xab, 0xaa, 0x8b, 0xc6, 0x7d, 0x7b, 0xef,
-    0x3a, 0xb4, 0xff, 0x3c, 0xd9, 0xfe, 0xd5, 0x75, 0x51, 0x26, 0xc8, 0xd8,
-    0x89, 0xef, 0x99, 0xd1, 0x3c, 0x33, 0x26, 0x3e, 0xc3, 0xe7, 0xce, 0x35,
-    0x7b, 0x0a, 0xa2, 0x8c, 0x55, 0xc8, 0x6f, 0x9c, 0xaf, 0x50, 0xc6, 0xd9,
-    0xbf, 0x30, 0xed, 0x86, 0x8f, 0xb2, 0x0a, 0xc8, 0xed, 0x9f, 0x2c, 0x47,
-    0xd9, 0x48, 0x39, 0x79, 0x6c, 0x83, 0xca, 0x69, 0xed, 0xa7, 0x1b, 0xe2,
-    0x91, 0xb4, 0x53, 0xd6, 0xde, 0x79, 0x41, 0xee, 0x56, 0xad, 0xbf, 0xa6,
-    0x32, 0x9f, 0x16, 0x5d, 0x9a, 0x9e, 0xbe, 0xaa, 0xd5, 0xfc, 0x53, 0x85,
-    0x1e, 0x6e, 0x17, 0xc1, 0x49, 0x2b, 0xbd, 0x2b, 0xb7, 0x76, 0x91, 0xaf,
-    0x9a, 0xcb, 0x4d, 0xdd, 0x20, 0x12, 0xd9, 0x63, 0x9e, 0x51, 0xf7, 0x8d,
-    0x3b, 0xca, 0xeb, 0x4a, 0x15, 0x80,
+    0x95, 0xad, 0x33, 0xb7, 0xe7, 0xd9, 0x69, 0xb8, 0x75, 0xa7, 0xff, 0x66,
+    0x82, 0xb2, 0xec, 0x5b, 0x71, 0x8e, 0xb4, 0x6c, 0x7c, 0x3a, 0x16, 0x9e,
+    0xe6, 0xf4, 0x12, 0xa2, 0x17, 0x91, 0xb1, 0x33, 0x0d, 0x19, 0xde, 0x15,
+    0x5b, 0x91, 0xce, 0xfd, 0x75, 0x69, 0x69, 0x68, 0x0c, 0xd5, 0xee, 0x37,
+    0x3d, 0xcd, 0x17, 0x16, 0x9f, 0xff, 0xfb, 0x5b, 0xfa, 0x59, 0xf7, 0x07,
+    0x1d, 0xeb, 0x3d, 0x6b, 0xdd, 0x83, 0x5a, 0x7f, 0xf1, 0x38, 0xfc, 0xd9,
+    0xfe, 0x0f, 0x54, 0x2b, 0x4d, 0x4e, 0xab, 0x46, 0x23, 0xeb, 0xa4, 0x3a,
+    0x76, 0xa4, 0xb9, 0xdf, 0xae, 0xad, 0x0e, 0x89, 0xeb, 0x06, 0x77, 0x3e,
+    0xd5, 0x72, 0x9e, 0xb4, 0xfe, 0x7d, 0xda, 0xfe, 0x68, 0xf3, 0x7e, 0x6d,
+    0x69, 0xe2, 0xb9, 0x01, 0x69, 0xd6, 0xdb, 0x6a, 0x53, 0xea, 0x1e, 0x6b,
+    0x12, 0x31, 0x7f, 0x3e, 0xae, 0x39, 0x9c, 0x5a, 0x3d, 0x44, 0xd0, 0x0f,
+    0xc4, 0xd2, 0x7b, 0x55, 0x54, 0xb4, 0xff, 0xc0, 0x27, 0x3c, 0x69, 0x8b,
+    0xfb, 0xf9, 0xd6, 0x9f, 0xf1, 0x06, 0xc2, 0xd7, 0x62, 0x3a, 0xd3, 0xf6,
+    0x3b, 0xdd, 0xf9, 0x75, 0xa7, 0xb3, 0xdc, 0xb1, 0x68, 0xc5, 0x45, 0xbb,
+    0x13, 0x14, 0x38, 0x7c, 0xe6, 0x0e, 0x0f, 0xe9, 0x2c, 0x4e, 0xf8, 0x5f,
+    0x3f, 0x65, 0xad, 0x9e, 0x92, 0xd3, 0x9b, 0x70, 0x2d, 0x3f, 0xff, 0xf8,
+    0x47, 0x66, 0x0c, 0x73, 0xdd, 0x13, 0x9e, 0x2f, 0x9b, 0x3f, 0xf9, 0x6a,
+    0xd3, 0xe2, 0xe6, 0xed, 0x62, 0xd3, 0xff, 0xfb, 0x36, 0x7d, 0xcf, 0x83,
+    0xe1, 0xd4, 0xb5, 0x61, 0x67, 0xd6, 0x9f, 0xff, 0xf5, 0xbe, 0x07, 0x3b,
+    0xa2, 0x1c, 0xdf, 0x6e, 0x78, 0xab, 0x70, 0x35, 0xa7, 0xf6, 0xcf, 0xb8,
+    0x7b, 0xb6, 0xcb, 0x4f, 0xea, 0xb7, 0x77, 0x1b, 0x94, 0xb4, 0x32, 0x37,
+    0x49, 0xc8, 0x4d, 0xe7, 0x81, 0xc2, 0xb1, 0x69, 0xff, 0x35, 0x58, 0xc7,
+    0x16, 0xd8, 0x0b, 0x43, 0x2a, 0x72, 0xc7, 0xc2, 0x28, 0xfc, 0x68, 0x54,
+    0x5a, 0x24, 0x53, 0x6e, 0xf5, 0xa7, 0xcc, 0x0b, 0xd9, 0xc5, 0xa7, 0x57,
+    0x2e, 0xb4, 0xdb, 0xd8, 0xb4, 0xe1, 0xe3, 0x8f, 0x36, 0x7f, 0x1b, 0x9f,
+    0xb7, 0xef, 0x8f, 0x76, 0xb5, 0x68, 0x63, 0xe7, 0x23, 0x39, 0xff, 0xf6,
+    0xed, 0xcf, 0x03, 0xbb, 0xaf, 0x8c, 0xe7, 0x96, 0x71, 0x69, 0xff, 0xb4,
+    0xc1, 0xf8, 0xe0, 0xd7, 0x1b, 0x8b, 0x4f, 0xef, 0x1b, 0x3c, 0xb5, 0xb9,
+    0xd6, 0x80, 0x1f, 0xe0, 0xa2, 0xcf, 0xfb, 0xee, 0x78, 0xbb, 0x15, 0x95,
+    0xc5, 0xa1, 0x8f, 0x8b, 0x72, 0x19, 0xff, 0xff, 0xed, 0xc6, 0xff, 0x16,
+    0x73, 0xc0, 0x5c, 0x27, 0xe6, 0xdc, 0xc7, 0x63, 0x97, 0x5a, 0x4c, 0xb4,
+    0xfd, 0x5f, 0x2b, 0xb6, 0xcb, 0x4f, 0xfa, 0xad, 0xfb, 0x0f, 0x35, 0x8e,
+    0xab, 0x43, 0xcf, 0xb3, 0xe5, 0x93, 0xfd, 0xa2, 0xfb, 0x9a, 0xac, 0xfa,
+    0xd3, 0xff, 0xff, 0xf0, 0x2e, 0x39, 0x5b, 0x78, 0xf7, 0x5b, 0xf8, 0x1c,
+    0xd9, 0xf9, 0xbd, 0x83, 0x95, 0xb2, 0xd1, 0xd4, 0x64, 0x11, 0xc4, 0xea,
+    0xae, 0xaa, 0x29, 0x88, 0xc3, 0xc9, 0xe9, 0x14, 0xff, 0xb0, 0x76, 0x7d,
+    0xf8, 0xdf, 0x71, 0x69, 0xd8, 0x40, 0x5a, 0x58, 0xb4, 0xed, 0x87, 0x00,
+    0x6a, 0x5d, 0x8d, 0x41, 0x22, 0x72, 0x9a, 0x27, 0x3d, 0xb6, 0x5a, 0x7f,
+    0x1f, 0xdd, 0x35, 0x99, 0x6a, 0xd1, 0xb1, 0xe8, 0xd0, 0xe4, 0xff, 0xfe,
+    0x1c, 0xef, 0x38, 0xdc, 0xbb, 0x83, 0x96, 0xf8, 0x67, 0x4c, 0xb4, 0xff,
+    0xba, 0xc7, 0xd6, 0x5f, 0x08, 0x56, 0x86, 0x45, 0x1e, 0x9a, 0x27, 0xdf,
+    0x67, 0x6d, 0xf5, 0xa7, 0xea, 0x1f, 0x1e, 0xb0, 0xad, 0x18, 0x7e, 0xe4,
+    0x45, 0xb9, 0x44, 0x58, 0xbe, 0x44, 0xf1, 0x7f, 0x61, 0x84, 0x19, 0x07,
+    0x63, 0x3e, 0xd8, 0x88, 0xa1, 0x04, 0xe3, 0xdf, 0xe3, 0x33, 0x3c, 0x2f,
+    0x45, 0xcb, 0x91, 0x9e, 0x4f, 0xf8, 0x3f, 0xd5, 0xbe, 0x2b, 0xda, 0xb1,
+    0x69, 0xec, 0x10, 0xfc, 0xeb, 0x4f, 0xff, 0xff, 0xb4, 0x5c, 0xd6, 0x7d,
+    0x9d, 0xf8, 0x3e, 0xee, 0xbe, 0x33, 0x67, 0xe6, 0xe2, 0x0b, 0xad, 0x2e,
+    0x12, 0x2c, 0xee, 0x4b, 0x3f, 0xfe, 0xcc, 0xd1, 0x77, 0x59, 0xe2, 0xfb,
+    0xe9, 0xbc, 0xeb, 0x4f, 0xff, 0x73, 0x58, 0x0f, 0x07, 0x6c, 0xe7, 0xb8,
+    0x2b, 0x4f, 0xf6, 0xde, 0x0e, 0xd8, 0x3c, 0xf2, 0x5a, 0x57, 0xc4, 0x46,
+    0xd2, 0x84, 0x88, 0x53, 0x0e, 0xe4, 0x39, 0xe7, 0xcf, 0x6d, 0x9f, 0x75,
+    0xa7, 0xff, 0xff, 0xfd, 0x82, 0x1f, 0x82, 0xd6, 0xe6, 0x70, 0xb8, 0xc7,
+    0x1a, 0x0f, 0x44, 0x6c, 0xd6, 0xc0, 0x6f, 0x52, 0x9f, 0xff, 0xfb, 0xa5,
+    0x6f, 0xbb, 0xff, 0xc6, 0xed, 0x65, 0xfb, 0xad, 0xf5, 0xb5, 0x5a, 0xb4,
+    0xdb, 0xd9, 0xb2, 0x68, 0x64, 0x51, 0x78, 0x4f, 0x43, 0x2a, 0xea, 0xd4,
+    0x66, 0xc3, 0x1b, 0x84, 0xff, 0xe3, 0xf7, 0xc6, 0xa9, 0xf8, 0x7a, 0x10,
+    0x2d, 0x3f, 0xfe, 0x21, 0xe6, 0xb1, 0xd7, 0x8e, 0x67, 0x9e, 0x84, 0x0b,
+    0x4f, 0xfc, 0xc3, 0x60, 0xe3, 0xbb, 0xee, 0xdb, 0x2d, 0x17, 0x45, 0x06,
+    0xeb, 0x53, 0xd9, 0xcc, 0x74, 0x16, 0x9d, 0xe5, 0x9a, 0x5a, 0x6c, 0xf5,
+    0x68, 0xb1, 0x36, 0x77, 0xc3, 0xaa, 0xe4, 0x9c, 0x25, 0x76, 0x3d, 0x3e,
+    0x2e, 0x5f, 0x00, 0xb4, 0xfd, 0xeb, 0x0e, 0xa9, 0xeb, 0x4e, 0x6f, 0x59,
+    0x69, 0xf9, 0xf9, 0xcb, 0xd3, 0x9e, 0x0f, 0x1c, 0xe5, 0x90, 0xf4, 0x5a,
+    0x38, 0xdb, 0x3f, 0x3a, 0xea, 0xbc, 0xec, 0x05, 0xa7, 0xf7, 0xdc, 0x1c,
+    0xd5, 0x71, 0x68, 0xc3, 0xe5, 0x23, 0x39, 0xf9, 0x81, 0x9c, 0x0a, 0xc5,
+    0xa1, 0xe7, 0x9c, 0x24, 0x13, 0xfd, 0xcd, 0x63, 0xe8, 0x73, 0x65, 0xa7,
+    0xff, 0xf8, 0x68, 0x17, 0xb3, 0x08, 0x78, 0x38, 0xef, 0xc5, 0xed, 0x15,
+    0xa0, 0x51, 0x3f, 0xc3, 0x79, 0xff, 0x16, 0x39, 0xe2, 0xed, 0xaa, 0xf3,
+    0xad, 0x3f, 0xab, 0x6b, 0xeb, 0x2b, 0xeb, 0x47, 0xa7, 0xeb, 0xc4, 0x39,
+    0xfe, 0xcf, 0xd8, 0xc5, 0xfc, 0x15, 0xa4, 0x75, 0xbc, 0x1b, 0x69, 0xc3,
+    0x9e, 0xad, 0x0c, 0x6f, 0x76, 0x22, 0x9f, 0xb6, 0xbe, 0x60, 0x86, 0xb4,
+    0xf1, 0xc2, 0x60, 0x2d, 0x18, 0x79, 0xe4, 0x5b, 0x3b, 0x7d, 0x80, 0xb4,
+    0x32, 0x7b, 0x79, 0x09, 0x72, 0x84, 0xb7, 0xdc, 0x04, 0x82, 0x7f, 0x70,
+    0xac, 0xcd, 0x6f, 0xc5, 0xa4, 0xe2, 0xd3, 0xf6, 0x6f, 0x9a, 0xed, 0x8b,
+    0x4d, 0x9b, 0x31, 0xbf, 0xb8, 0x8c, 0xfd, 0xaa, 0x79, 0xfe, 0x4b, 0x4f,
+    0x03, 0xf9, 0xb2, 0xd3, 0xe2, 0xbd, 0xd8, 0xeb, 0x41, 0x1e, 0x43, 0xb2,
+    0x28, 0x0d, 0x33, 0x62, 0x73, 0x01, 0x66, 0xee, 0x93, 0xff, 0xf7, 0xdf,
+    0xac, 0x73, 0x4c, 0x21, 0x8e, 0x73, 0x5b, 0xad, 0x3f, 0xff, 0x8b, 0x36,
+    0x7d, 0xf9, 0xa6, 0x3b, 0x17, 0xbe, 0x19, 0xd3, 0x2d, 0x3f, 0xff, 0xfe,
+    0xdc, 0x7c, 0x5b, 0x5b, 0x3f, 0xf4, 0x0f, 0x17, 0xdd, 0xb6, 0xd6, 0x10,
+    0xe0, 0x6b, 0x4f, 0xff, 0xb7, 0xff, 0xf3, 0x97, 0xd1, 0x67, 0x8d, 0x01,
+    0x96, 0x8c, 0x47, 0x0e, 0xa1, 0x15, 0x3f, 0xfc, 0xf7, 0x5d, 0x63, 0x0b,
+    0x83, 0x9c, 0xbd, 0x2d, 0x3f, 0xfe, 0xf7, 0x0f, 0xe1, 0xd7, 0x8d, 0x77,
+    0x1f, 0x5f, 0xb1, 0x69, 0xff, 0xfb, 0x85, 0x66, 0x7b, 0xe0, 0xf4, 0xff,
+    0x95, 0x94, 0x75, 0xa7, 0xf5, 0x94, 0x71, 0x6f, 0xb8, 0xb4, 0xff, 0x6d,
+    0x81, 0x80, 0xf9, 0xe3, 0xf8, 0x89, 0x12, 0x5e, 0x9f, 0xec, 0x1b, 0x3c,
+    0x02, 0xa9, 0xc5, 0xa7, 0xfb, 0xf6, 0x35, 0x96, 0x6f, 0xfb, 0x16, 0x9f,
+    0xed, 0xc7, 0xc5, 0xf7, 0xf2, 0xdf, 0xeb, 0x43, 0x1f, 0xfd, 0xcf, 0xa7,
+    0xff, 0x59, 0xe2, 0xfa, 0xdf, 0xc7, 0x2f, 0x76, 0xfa, 0xd3, 0xff, 0xfe,
+    0xcd, 0xae, 0x59, 0x6f, 0x8d, 0x67, 0x2f, 0x96, 0x78, 0xc2, 0xf5, 0x68,
+    0xc4, 0x61, 0x12, 0x8c, 0x58, 0xb9, 0x7d, 0xd8, 0xd2, 0x76, 0x27, 0x25,
+    0x0f, 0xc3, 0xb8, 0xea, 0x77, 0x85, 0xcf, 0x21, 0xc7, 0x3f, 0xb8, 0x39,
+    0xcd, 0x65, 0x8b, 0x4f, 0xfa, 0xbf, 0xac, 0xe3, 0x09, 0x01, 0x69, 0xfe,
+    0xcb, 0x76, 0x7d, 0xfc, 0x70, 0x96, 0x9f, 0xff, 0xf3, 0x75, 0x8f, 0xe1,
+    0xcd, 0x10, 0xfb, 0xe0, 0x2e, 0x8e, 0x6b, 0x16, 0x86, 0x4c, 0x67, 0xa6,
+    0x42, 0x76, 0xea, 0x79, 0x3c, 0x5c, 0x26, 0x5a, 0x7f, 0xff, 0xf0, 0x90,
+    0x7a, 0xa7, 0x3c, 0x0e, 0xee, 0xbe, 0x33, 0x67, 0xe6, 0xe2, 0x0b, 0xad,
+    0x3e, 0xd1, 0x30, 0xd8, 0xb4, 0xdc, 0x63, 0xa2, 0x9d, 0xdc, 0x20, 0x61,
+    0xe8, 0xfb, 0x28, 0x63, 0x43, 0x32, 0x07, 0xb2, 0x74, 0x68, 0xa3, 0xa3,
+    0xa8, 0xce, 0xa7, 0xcc, 0x3c, 0x27, 0x6b, 0x4f, 0x72, 0xf4, 0x05, 0xa7,
+    0xfe, 0xd1, 0x78, 0x2f, 0xef, 0x65, 0x7f, 0xab, 0x4f, 0x16, 0xaa, 0xc5,
+    0xa7, 0x9d, 0xb7, 0x19, 0x69, 0xfb, 0x55, 0x67, 0x87, 0xf1, 0x69, 0xf5,
+    0x7c, 0x76, 0x25, 0xa0, 0x8f, 0x5b, 0x86, 0x11, 0xea, 0x69, 0x38, 0x4e,
+    0x19, 0x0f, 0x51, 0xc8, 0x87, 0x8f, 0x13, 0xc1, 0x6f, 0x57, 0x5a, 0x7f,
+    0xfe, 0x2f, 0xf8, 0xc2, 0xc1, 0x0f, 0x45, 0x87, 0xce, 0xad, 0x3f, 0xff,
+    0xc5, 0x67, 0xd8, 0xf7, 0xdd, 0xbc, 0x37, 0xdb, 0xdd, 0x31, 0xd6, 0x8c,
+    0x46, 0x17, 0xd6, 0x67, 0xff, 0xff, 0x7f, 0xe5, 0x67, 0x8d, 0x67, 0x05,
+    0x9c, 0xd6, 0x58, 0xdb, 0x73, 0x7e, 0xad, 0x3f, 0xff, 0xf3, 0x07, 0xa6,
+    0xf0, 0x39, 0xb8, 0x7e, 0x36, 0x7d, 0xc8, 0x42, 0x6b, 0x16, 0x9f, 0x6d,
+    0x72, 0xc1, 0x5a, 0x31, 0x13, 0xfb, 0xbc, 0xc1, 0x26, 0x7f, 0xa8, 0xc7,
+    0x27, 0xfe, 0x7d, 0xc8, 0x3d, 0x31, 0x58, 0xc1, 0xad, 0x3f, 0xc3, 0xbb,
+    0x83, 0x9a, 0xae, 0x2d, 0x3f, 0xde, 0xe9, 0xad, 0xf7, 0x54, 0x75, 0xa7,
+    0xff, 0xfa, 0xbf, 0xac, 0xe1, 0x60, 0xd9, 0x76, 0xc1, 0xf7, 0x4c, 0xb4,
+    0xf7, 0x3c, 0x59, 0xf5, 0xa3, 0x64, 0x44, 0x53, 0x1c, 0xff, 0xeb, 0x68,
+    0x6c, 0x3e, 0xe5, 0xe9, 0x09, 0x2d, 0x3f, 0x39, 0xb3, 0xf7, 0x6e, 0x2d,
+    0x3f, 0xda, 0xa3, 0x8e, 0x6a, 0xb8, 0xb4, 0xf6, 0x7c, 0x16, 0xad, 0x0c,
+    0x88, 0x8b, 0x98, 0x70, 0xda, 0x7e, 0xce, 0x7f, 0xf8, 0xf5, 0xa7, 0xe1,
+    0xcd, 0x0e, 0x75, 0x69, 0xe1, 0xcb, 0xf8, 0x70, 0xf5, 0xb8, 0x59, 0x1e,
+    0xae, 0x3f, 0xf6, 0x36, 0x1d, 0x8a, 0x49, 0x17, 0xe7, 0x1a, 0x86, 0x1d,
+    0xc8, 0xf9, 0x0d, 0x27, 0x70, 0x85, 0x9f, 0xf8, 0x47, 0x3c, 0x7f, 0x34,
+    0x38, 0xe2, 0xd0, 0xcb, 0xc8, 0x19, 0x3a, 0xc9, 0x50, 0x99, 0x9f, 0x0b,
+    0x1e, 0xbd, 0x5a, 0x7f, 0x63, 0x9a, 0xce, 0x37, 0xab, 0x4f, 0xff, 0xff,
+    0x6b, 0x39, 0x7a, 0x73, 0xc0, 0xe6, 0xcf, 0xcd, 0xec, 0x1c, 0xf7, 0x44,
+    0xe2, 0xd3, 0x6e, 0x1a, 0xd1, 0xf4, 0x4d, 0x72, 0x10, 0x53, 0xf6, 0x58,
+    0x39, 0xb5, 0xd6, 0x9f, 0xfd, 0xfd, 0x9f, 0x7c, 0x3e, 0xcd, 0xad, 0x32,
+    0xd2, 0xba, 0xd1, 0xb9, 0xed, 0x84, 0x97, 0x3f, 0x98, 0xe3, 0x9a, 0xae,
+    0x2d, 0x3d, 0xe3, 0xb6, 0x5a, 0xb4, 0xff, 0xff, 0xb8, 0x2c, 0x0d, 0x9f,
+    0x7d, 0xdb, 0x83, 0x9e, 0x96, 0x7d, 0xc5, 0xa0, 0x95, 0x45, 0x38, 0x4d,
+    0x50, 0xcd, 0x12, 0x8b, 0xc2, 0x23, 0x72, 0x47, 0x66, 0x01, 0x13, 0x4f,
+    0x9e, 0x5f, 0xcd, 0x96, 0x9f, 0x65, 0xec, 0xcb, 0x56, 0x8d, 0x8f, 0x3f,
+    0x44, 0xf3, 0xff, 0xe2, 0xb3, 0x3b, 0xee, 0x8b, 0x03, 0xf0, 0x1e, 0xec,
+    0xb4, 0xf3, 0xaf, 0x33, 0x4b, 0x4c, 0x47, 0x5a, 0x7f, 0xff, 0xdb, 0x3e,
+    0xb6, 0xf0, 0x3b, 0xba, 0xf8, 0xcd, 0x9f, 0x9b, 0x88, 0x2e, 0xb4, 0x1d,
+    0x11, 0x8e, 0xa2, 0xb1, 0x62, 0x35, 0xb2, 0x16, 0x13, 0xff, 0xff, 0xe6,
+    0xda, 0xfb, 0xb6, 0xde, 0x35, 0x9e, 0xf8, 0x1c, 0xdc, 0x3f, 0x17, 0x00,
+    0x0b, 0x8b, 0x4f, 0x98, 0x76, 0xab, 0x16, 0x9f, 0xff, 0xff, 0xfd, 0x87,
+    0xce, 0xb6, 0x98, 0xfc, 0xcb, 0xdd, 0xbf, 0xf6, 0x1c, 0xb3, 0x37, 0xbe,
+    0xee, 0x0b, 0x1d, 0x69, 0xfe, 0x06, 0x7a, 0x39, 0xaa, 0xe2, 0xd3, 0xfa,
+    0xcd, 0xdb, 0x87, 0x2e, 0x2d, 0x3f, 0xe6, 0x0d, 0xd7, 0x7c, 0x17, 0x02,
+    0x3a, 0xd1, 0x87, 0xf1, 0xb1, 0xa4, 0xff, 0xb3, 0x01, 0x7f, 0x1f, 0xbf,
+    0xb7, 0x5a, 0x7f, 0x7f, 0x3b, 0xa2, 0xfb, 0x8b, 0x4d, 0x9c, 0xf4, 0xfd,
+    0x31, 0x06, 0x7a, 0xc2, 0x1e, 0x2d, 0x0c, 0xb8, 0xcf, 0x84, 0x65, 0x19,
+    0x87, 0xca, 0x0f, 0x08, 0xed, 0x13, 0xd4, 0x29, 0x06, 0x15, 0xf7, 0x84,
+    0xb6, 0xe5, 0xb3, 0xbd, 0xf0, 0xe2, 0xd3, 0xff, 0xdb, 0x3c, 0x5b, 0xc1,
+    0xc5, 0x83, 0xe1, 0x58, 0xb4, 0x61, 0xf9, 0x11, 0x04, 0xfd, 0x63, 0x8f,
+    0xf3, 0x3f, 0x62, 0xd3, 0xe3, 0x97, 0xb9, 0xea, 0xd2, 0x71, 0x69, 0x98,
+    0xeb, 0x4b, 0x8b, 0x47, 0xcd, 0x2d, 0x0a, 0xc7, 0xa7, 0xad, 0xa3, 0x69,
+    0xee, 0x5e, 0xb6, 0x5a, 0x67, 0x29, 0x69, 0xfd, 0xa2, 0xfe, 0xcf, 0x16,
+    0x5a, 0x3c, 0xda, 0x68, 0x2c, 0x6d, 0x8f, 0xc4, 0x46, 0x72, 0x2d, 0xc5,
+    0xa7, 0xf5, 0xcb, 0x6b, 0x68, 0x40, 0xb4, 0xff, 0xf9, 0xbe, 0xdd, 0xc3,
+    0xb7, 0x5e, 0x57, 0xdc, 0xeb, 0x43, 0x88, 0x87, 0xf2, 0x33, 0x9f, 0xbf,
+    0x8c, 0x1b, 0x0a, 0xd3, 0x86, 0x8e, 0xb4, 0xc0, 0x65, 0xa1, 0xe7, 0xb4,
+    0x45, 0x62, 0x35, 0x0e, 0x93, 0xb7, 0x5b, 0xf3, 0x23, 0x73, 0x69, 0x4a,
+    0xb6, 0x3d, 0xbc, 0xb3, 0xd9, 0x51, 0xd9, 0x3e, 0xf8, 0x1c, 0x33, 0x3b,
+    0x2f, 0x83, 0x68, 0xe8, 0xca, 0x30, 0x77, 0x21, 0xaf, 0xf9, 0x53, 0x67,
+    0x8e, 0x1f, 0x54, 0x85, 0xda, 0x8e, 0x68, 0x67, 0xa6, 0x2f, 0x2b, 0x1f,
+    0x93, 0xa6, 0xae, 0xe3, 0x16, 0xf2, 0x8d, 0x84, 0x28, 0x71, 0xba, 0xc2,
+    0x06, 0x7f, 0xfe, 0xdf, 0xa6, 0xdb, 0x9f, 0x6c, 0xdb, 0xcc, 0xf6, 0xa8,
+    0x35, 0xa0, 0xca, 0x8c, 0xca, 0x3c, 0xf9, 0xfc, 0x6c, 0xff, 0x6a, 0xba,
+    0xa8, 0xa7, 0x67, 0xdf, 0xed, 0x57, 0x55, 0x15, 0x1c, 0xff, 0xa9, 0xe6,
+    0xcf, 0xf6, 0xab, 0xaa, 0x89, 0xa2, 0x30, 0xff, 0x0e, 0x67, 0x3f, 0x82,
+    0xa7, 0x66, 0xe1, 0x58, 0xb4, 0x18, 0xf5, 0xd8, 0x86, 0x7f, 0x1b, 0x3f,
+    0xda, 0xae, 0xaa, 0x2a, 0xf9, 0xf7, 0xfb, 0x55, 0xd5, 0x45, 0x6d, 0x3f,
+    0xcf, 0x36, 0x7f, 0xb5, 0x5d, 0x54, 0x48, 0x32, 0x36, 0x1f, 0xbd, 0x19,
+    0xcf, 0xfc, 0x6a, 0x79, 0xb3, 0xfd, 0xaa, 0xea, 0xa2, 0x43, 0x9f, 0xf3,
+    0xf4, 0xdc, 0xbb, 0xa6, 0xbb, 0xa4, 0x2b, 0x4b, 0x65, 0xa6, 0x21, 0x5a,
+    0x3d, 0x34, 0xae, 0x08, 0xcf, 0xb3, 0x04, 0x17, 0x5a, 0x7d, 0xfe, 0xd5,
+    0x75, 0x51, 0x62, 0x4f, 0xb3, 0xec, 0x21, 0xad, 0x3e, 0x0b, 0xcb, 0x3e,
+    0xcb, 0x4e, 0xbb, 0x0a, 0xd2, 0xd3, 0x1e, 0x21, 0x15, 0x4f, 0xfc, 0xd9,
+    0x7c, 0x1f, 0x48, 0x0c, 0x2b, 0x4e, 0x16, 0xb1, 0x69, 0x3c, 0x4f, 0x6f,
+    0x74, 0x09, 0xde, 0x44, 0x05, 0xa7, 0xc4, 0xef, 0x7a, 0xba, 0xd3, 0xff,
+    0x3b, 0xdf, 0x36, 0xf5, 0xde, 0xb2, 0x9c, 0x5a, 0x66, 0x7a, 0xd1, 0x87,
+    0xc1, 0xf4, 0xb9, 0xfd, 0x9f, 0x78, 0x33, 0x1c, 0x5a, 0x7d, 0x4f, 0x0b,
+    0x4c, 0xb4, 0xfd, 0x7b, 0xfc, 0xad, 0xf3, 0xad, 0x0c, 0x88, 0xb2, 0x32,
+    0xa2, 0x79, 0xff, 0xe7, 0xb1, 0xd9, 0xfe, 0x8b, 0x6a, 0xb3, 0xeb, 0x4f,
+    0xee, 0x8e, 0x3b, 0x1c, 0xba, 0xd1, 0xb1, 0xff, 0xe9, 0x36, 0x7f, 0x01,
+    0xb3, 0x84, 0x36, 0xa5, 0x3f, 0xf6, 0xa8, 0xb8, 0xfc, 0x0c, 0x70, 0x0b,
+    0x4f, 0xb3, 0x5b, 0x57, 0x9d, 0x69, 0xe0, 0x5d, 0x85, 0x69, 0xfd, 0xfd,
+    0x51, 0x5c, 0x9d, 0xad, 0x0c, 0x7a, 0x78, 0x41, 0x04, 0x8e, 0x01, 0x42,
+    0xbb, 0xf4, 0xfd, 0x56, 0x71, 0xb6, 0x7a, 0xd3, 0xfe, 0x16, 0x2f, 0x78,
+    0x35, 0xb5, 0xd6, 0x87, 0x49, 0x77, 0x21, 0x9b, 0xbd, 0x23, 0xc2, 0x50,
+    0xcc, 0xfa, 0xd5, 0xb4, 0x20, 0x48, 0xa1, 0xc1, 0xef, 0xc2, 0x27, 0x50,
+    0xac, 0xa8, 0x56, 0x00, 0x8e, 0xf1, 0x88, 0xf0, 0xb8, 0x22, 0xe9, 0xff,
+    0xb4, 0x4d, 0xcd, 0x50, 0x78, 0x20, 0x5a, 0x7f, 0xfb, 0x36, 0x7f, 0x37,
+    0xb0, 0xda, 0xa3, 0x95, 0xd6, 0x9d, 0xc1, 0x65, 0xa6, 0xd1, 0xb1, 0x15,
+    0xc2, 0x84, 0x12, 0x94, 0x19, 0x3e, 0x56, 0x8e, 0xaa, 0x7f, 0x1b, 0x3f,
+    0xda, 0xae, 0xaa, 0x2c, 0xd9, 0xb3, 0xd5, 0xa7, 0xfd, 0x4f, 0x36, 0x7f,
+    0xb5, 0x5d, 0x54, 0x50, 0x13, 0xf8, 0x8e, 0xde, 0x7d, 0x65, 0x8b, 0x48,
+    0xd8, 0x89, 0x53, 0x8b, 0x69, 0x26, 0x6d, 0xf4, 0xb4, 0xfd, 0x76, 0x38,
+    0xb3, 0x8b, 0x4b, 0x4b, 0x4e, 0x08, 0x5c, 0x5a, 0x6c, 0xe6, 0x1a, 0xd0,
+    0x08, 0x40, 0x11, 0x08, 0x2b, 0x33, 0x78, 0xba, 0xd3, 0xfd, 0xac, 0xff,
+    0x05, 0x8f, 0x75, 0xa7, 0xbb, 0x55, 0xd5, 0x45, 0xbf, 0x3f, 0x05, 0xca,
+    0xed, 0x7d, 0x68, 0xf9, 0xed, 0x00, 0xb6, 0x7b, 0x04, 0x9c, 0x5a, 0x7d,
+    0xc7, 0xdc, 0x2b, 0xad, 0x3f, 0x3b, 0x0b, 0x82, 0xc0, 0x5a, 0x7d, 0xeb,
+    0x79, 0xf7, 0x3a, 0xd0, 0xc7, 0xb8, 0x45, 0xf3, 0xd4, 0x43, 0xc5, 0xa7,
+    0xf6, 0xfb, 0x5d, 0xbb, 0x87, 0x5a, 0x7b, 0x41, 0xfa, 0x75, 0xa6, 0xc7,
+    0xad, 0x18, 0x6e, 0x44, 0x92, 0x46, 0x74, 0x95, 0x7c, 0x34, 0x26, 0xde,
+    0x45, 0xe8, 0xc6, 0x42, 0x44, 0x88, 0xbe, 0x41, 0xa8, 0x42, 0x80, 0x80,
+    0x47, 0xf8, 0xdb, 0x3f, 0xea, 0x79, 0xb3, 0xfd, 0xaa, 0xea, 0xa2, 0x94,
+    0x9f, 0xe7, 0x9b, 0x3f, 0xda, 0xae, 0xaa, 0x24, 0xe9, 0x18, 0xe8, 0x8d,
+    0xa4, 0x98, 0x67, 0xcb, 0x32, 0xb2, 0x36, 0xef, 0x6d, 0xd0, 0x16, 0x15,
+    0x76, 0x31, 0x12, 0x21, 0xf3, 0xc2, 0xd5, 0xc2, 0x63, 0xcf, 0xfe, 0x68,
+    0xa4, 0x10, 0xd3, 0xbc, 0xb2, 0xd7, 0x71, 0x9f, 0x4f, 0xbf, 0xda, 0xae,
+    0xaa, 0x21, 0xf9, 0xff, 0x53, 0xcd, 0x9f, 0xed, 0x57, 0x55, 0x12, 0x9c,
+    0x8d, 0x87, 0xf8, 0x73, 0x39, 0xfc, 0x6c, 0xff, 0x6a, 0xba, 0xa8, 0x89,
+    0xe7, 0xf1, 0xb3, 0xfd, 0xaa, 0xea, 0xa2, 0x31, 0x9f, 0xfc, 0x63, 0xd3,
+    0xcd, 0x9f, 0xed, 0x57, 0x55, 0x13, 0xcc, 0x32, 0x3e, 0x2c, 0x27, 0x79,
+    0xf6, 0x8f, 0xa7, 0xdf, 0xed, 0x57, 0x55, 0x11, 0x04, 0xff, 0xa9, 0xe6,
+    0xcf, 0xf6, 0xab, 0xaa, 0x89, 0x52, 0x46, 0xc3, 0xfc, 0x39, 0x9c, 0xfe,
+    0x36, 0x7f, 0xb5, 0x5d, 0x54, 0x45, 0x13, 0xf8, 0xd9, 0xfe, 0xd5, 0x75,
+    0x51, 0x19, 0x4f, 0xfe, 0x31, 0xe9, 0xe6, 0xcf, 0xf6, 0xab, 0xaa, 0x89,
+    0x96, 0x7f, 0x1b, 0x3f, 0xda, 0xae, 0xaa, 0x2a, 0x49, 0xfc, 0x6c, 0xff,
+    0x6a, 0xba, 0xa8, 0xae, 0xa7, 0xf1, 0xb3, 0xfd, 0xaa, 0xea, 0xa2, 0xc5,
+    0x9f, 0xf8, 0xf4, 0xf3, 0x67, 0xfb, 0x55, 0xd5, 0x44, 0xf5, 0x3f, 0xd6,
+    0x9a, 0x98, 0xbc, 0x89, 0xd5, 0x68, 0x32, 0x23, 0x19, 0x3e, 0x7e, 0xf3,
+    0x46, 0x70, 0xb7, 0xba, 0xd3, 0xdf, 0x3f, 0x36, 0x5a, 0x77, 0x73, 0x49,
+    0x4e, 0x3b, 0x5d, 0x69, 0xff, 0xfb, 0x43, 0x41, 0x97, 0xd9, 0xef, 0xcb,
+    0x80, 0x0c, 0xb4, 0xff, 0xfd, 0xf2, 0x75, 0xa3, 0x99, 0xbf, 0xf2, 0x75,
+    0xee, 0xf6, 0x2d, 0x3c, 0x5a, 0x08, 0xeb, 0x4f, 0xfe, 0x20, 0xfc, 0x60,
+    0xd6, 0xd7, 0x0c, 0x3a, 0x5a, 0x3a, 0x7d, 0xe2, 0x45, 0x3f, 0xfa, 0xae,
+    0x38, 0x03, 0x68, 0xbb, 0x5f, 0x5a, 0x7e, 0xf7, 0x03, 0xe6, 0x75, 0x69,
+    0xed, 0x31, 0xfa, 0xb4, 0x51, 0xe7, 0xf0, 0xba, 0x7f, 0xf0, 0xf8, 0x66,
+    0x16, 0x3f, 0x8f, 0x1a, 0xf2, 0x5a, 0x6a, 0xd9, 0x69, 0xfd, 0x57, 0xad,
+    0x6e, 0x36, 0x2d, 0x02, 0x79, 0x3b, 0x8b, 0x45, 0x8a, 0xb6, 0x08, 0x6f,
+    0xeb, 0x27, 0x86, 0x8e, 0x88, 0x46, 0x12, 0xdc, 0x21, 0x75, 0x84, 0xcc,
+    0xe1, 0xae, 0x2d, 0x38, 0x4a, 0xeb, 0x4a, 0xd7, 0x44, 0xda, 0x30, 0xd4,
+    0xf7, 0xdd, 0xd1, 0xd6, 0x8c, 0x3c, 0xf2, 0x2c, 0x9f, 0xf1, 0x6a, 0xb8,
+    0xce, 0x5c, 0x8e, 0xb4, 0xf8, 0x43, 0x2c, 0xba, 0xd3, 0xff, 0x65, 0xb4,
+    0x0c, 0xee, 0xa8, 0x17, 0x5a, 0x7f, 0xd7, 0xad, 0x98, 0x6b, 0xdc, 0x0d,
+    0x68, 0x24, 0x53, 0x89, 0x37, 0x10, 0xe7, 0x1d, 0xb4, 0xb4, 0xf0, 0xfb,
+    0xe0, 0x35, 0xa7, 0xec, 0xbd, 0xb4, 0x36, 0x2d, 0x18, 0x7d, 0xb4, 0x36,
+    0x24, 0xb3, 0xed, 0xc3, 0xbf, 0xac, 0xb4, 0x62, 0x3c, 0x4a, 0x13, 0x7a,
+    0x2c, 0x9f, 0xff, 0xfd, 0xa6, 0x2f, 0xfd, 0x82, 0xe1, 0x0e, 0xef, 0xcb,
+    0xfb, 0xbd, 0x7d, 0xeb, 0x4f, 0xac, 0xf7, 0xe5, 0x6a, 0xd3, 0xf7, 0xb5,
+    0xf0, 0x79, 0x62, 0xd3, 0xfe, 0x12, 0x7f, 0x35, 0x9f, 0x2b, 0xad, 0x3f,
+    0xea, 0xc0, 0x30, 0xd7, 0xb8, 0x1a, 0xd0, 0xf3, 0xf8, 0x01, 0xe4, 0xfe,
+    0xad, 0xaf, 0xce, 0x67, 0x9d, 0x69, 0xff, 0xcd, 0x9b, 0x73, 0x4d, 0xc1,
+    0x60, 0x5d, 0x69, 0xd7, 0xd1, 0xd6, 0x8e, 0x1f, 0x26, 0xe9, 0x13, 0xf6,
+    0xf7, 0xe5, 0xf3, 0x8b, 0x4e, 0xb6, 0xdb, 0x52, 0x9f, 0xfd, 0xc2, 0xe5,
+    0xf4, 0x5e, 0x41, 0x7d, 0x9e, 0x91, 0x8b, 0xf8, 0x24, 0x54, 0xee, 0x9b,
+    0x0f, 0x55, 0xef, 0x8f, 0x24, 0x55, 0xf8, 0x54, 0x1c, 0x88, 0x61, 0x37,
+    0xc8, 0x66, 0x4f, 0xf6, 0xcf, 0xbd, 0xf3, 0xda, 0xe2, 0xd2, 0xc5, 0xa1,
+    0x8f, 0x24, 0x33, 0xa9, 0xff, 0xfe, 0x21, 0xae, 0x16, 0xa8, 0xfe, 0x32,
+    0xed, 0x9f, 0xc1, 0x5a, 0x7b, 0x8d, 0xb5, 0xab, 0x46, 0x91, 0x0b, 0x6b,
+    0x14, 0xee, 0xdb, 0xa5, 0xa7, 0xf3, 0x57, 0xf5, 0x6d, 0x7a, 0x94, 0xd6,
+    0xda, 0x94, 0x7a, 0x79, 0x76, 0x99, 0xce, 0xe1, 0x79, 0xd2, 0x31, 0xa4,
+    0x9e, 0xdd, 0xfb, 0xf1, 0x68, 0xb9, 0xe9, 0xf9, 0x17, 0xcd, 0x6d, 0xd6,
+    0x96, 0x2d, 0x2d, 0xb0, 0xd3, 0x70, 0x5e, 0x7f, 0xeb, 0xdf, 0xd1, 0x6f,
+    0x74, 0xdc, 0xba, 0xd0, 0xc7, 0xd9, 0xb9, 0x3c, 0xff, 0xb8, 0x5f, 0xa0,
+    0x3a, 0xf3, 0x34, 0xb4, 0xff, 0x15, 0xa3, 0x9c, 0x2d, 0xc0, 0xb4, 0xfe,
+    0xaf, 0xf2, 0xe2, 0x41, 0xad, 0x0f, 0x46, 0x61, 0x11, 0x79, 0xd0, 0x28,
+    0xe2, 0x76, 0x17, 0xd6, 0x9e, 0xaf, 0xb3, 0xd6, 0x8b, 0x0d, 0xd1, 0xc6,
+    0xa7, 0xfc, 0xdb, 0x69, 0x8f, 0xdd, 0x13, 0x8b, 0x46, 0x1f, 0x0f, 0xc8,
+    0xa7, 0xee, 0x37, 0xfd, 0xc1, 0x5a, 0x7f, 0x7b, 0xa6, 0x1b, 0x6b, 0x65,
+    0xa7, 0xfe, 0x2f, 0x74, 0xdd, 0x60, 0x50, 0x81, 0x69, 0xff, 0xff, 0x11,
+    0xe8, 0x76, 0x79, 0xad, 0xc2, 0x77, 0x83, 0x9e, 0xd3, 0xd7, 0x17, 0xac,
+    0x32, 0x63, 0x74, 0x59, 0x73, 0x4d, 0xd0, 0xa7, 0xff, 0xb3, 0x97, 0xc7,
+    0x34, 0xce, 0xf3, 0x5a, 0x65, 0xa7, 0xff, 0xff, 0xc5, 0x9b, 0x5c, 0xb2,
+    0xdf, 0x19, 0xb3, 0xee, 0x5c, 0xb9, 0x6d, 0xef, 0xbb, 0xd8, 0xb4, 0xff,
+    0xf8, 0x87, 0xdf, 0x01, 0x70, 0xaf, 0x44, 0x3b, 0xbd, 0x68, 0x14, 0xcc,
+    0xb8, 0xa0, 0xee, 0x11, 0x73, 0xfa, 0xca, 0xe0, 0x02, 0x17, 0xad, 0x35,
+    0x7a, 0xb4, 0xfd, 0xfa, 0xbe, 0xa9, 0xeb, 0x4b, 0x65, 0xa6, 0x2b, 0x56,
+    0x98, 0x20, 0x2d, 0x0e, 0x1a, 0xd0, 0x0b, 0x4e, 0x7d, 0x7a, 0xb4, 0xd6,
+    0xda, 0xb4, 0x3d, 0x1a, 0xd8, 0x2d, 0xd2, 0xc2, 0x3e, 0xb9, 0x15, 0xa3,
+    0x93, 0xb8, 0x10, 0x12, 0x31, 0xeb, 0x4f, 0xff, 0xfe, 0xe3, 0x7b, 0xad,
+    0xe8, 0x33, 0xd7, 0xb9, 0x82, 0x1d, 0xef, 0x82, 0x05, 0xa3, 0x64, 0x53,
+    0x91, 0x74, 0xff, 0x67, 0x34, 0xdf, 0xe6, 0x75, 0x69, 0xcd, 0xf7, 0x16,
+    0x8c, 0x55, 0x68, 0x46, 0xf5, 0x28, 0x1c, 0x48, 0xfc, 0x8d, 0xe7, 0x5e,
+    0x9c, 0x5a, 0x7b, 0x39, 0x4e, 0xab, 0x46, 0xc6, 0xfc, 0x47, 0x27, 0xfe,
+    0xcf, 0xff, 0x1c, 0xd6, 0x5e, 0x85, 0x69, 0xfc, 0x2d, 0xfd, 0xfb, 0xa6,
+    0x5a, 0x7c, 0x41, 0xea, 0x9e, 0xb4, 0xfc, 0x4c, 0x7b, 0x32, 0xd5, 0xa6,
+    0xb6, 0xd5, 0xa3, 0xa7, 0xd9, 0xf2, 0x7b, 0x4b, 0x67, 0xfb, 0x1c, 0xa7,
+    0x2b, 0x67, 0xdd, 0x23, 0x1b, 0x09, 0xde, 0xb1, 0xd6, 0x9f, 0xd8, 0x0c,
+    0x71, 0xb6, 0xba, 0xd0, 0xc7, 0x9f, 0x83, 0x93, 0xff, 0xb0, 0x41, 0x9a,
+    0x2d, 0xaf, 0xad, 0xee, 0xb4, 0xfc, 0x34, 0x0f, 0x70, 0x56, 0x9f, 0x8e,
+    0x5e, 0x59, 0xf0, 0xd6, 0x9d, 0x6d, 0xb6, 0xa5, 0x3f, 0xf3, 0x7c, 0x77,
+    0xa0, 0xb1, 0xc6, 0x3a, 0xc6, 0x2f, 0xe3, 0xce, 0xaa, 0x79, 0xc8, 0x67,
+    0x0c, 0x26, 0x6e, 0x41, 0xc4, 0xad, 0xca, 0xed, 0x4f, 0x93, 0xa0, 0xb4,
+    0x89, 0x69, 0xfe, 0xe3, 0x7e, 0xfa, 0x61, 0xe2, 0xd2, 0x74, 0x16, 0x9f,
+    0xee, 0x37, 0xef, 0xa6, 0x1e, 0x2d, 0x35, 0xd9, 0x69, 0xc7, 0xf5, 0x96,
+    0x9f, 0xc3, 0xca, 0x72, 0xed, 0xc5, 0xa0, 0x8f, 0x3a, 0x87, 0x27, 0x8a,
+    0xb3, 0xab, 0x4f, 0xfb, 0x1d, 0x8e, 0x5d, 0xc1, 0x6f, 0x56, 0x9e, 0xef,
+    0xad, 0x62, 0xd3, 0x01, 0x96, 0x9d, 0x72, 0xfa, 0xd1, 0x87, 0xa6, 0x02,
+    0x3e, 0x0a, 0xcf, 0xe7, 0xe0, 0x97, 0xb8, 0x2b, 0x4e, 0xe5, 0xcc, 0xc9,
+    0xef, 0x60, 0xab, 0x86, 0xfa, 0x63, 0x01, 0x00, 0x8f, 0xde, 0x12, 0x5c,
+    0x2e, 0x9a, 0xec, 0xb4, 0xe3, 0xfa, 0xcb, 0x4f, 0xe1, 0xe5, 0x39, 0x76,
+    0xe2, 0xd0, 0x47, 0x9d, 0x43, 0x93, 0xc5, 0x59, 0xd5, 0xa7, 0xfd, 0x8e,
+    0xc7, 0x2e, 0xe0, 0xb7, 0xab, 0x4f, 0x77, 0xd6, 0xb1, 0x69, 0xf9, 0xc0,
+    0x17, 0x9e, 0x9c, 0x5a, 0x60, 0x32, 0xd3, 0xae, 0x5f, 0x5a, 0x31, 0x10,
+    0xbd, 0x23, 0x01, 0x97, 0x05, 0x67, 0xf3, 0xf0, 0x4b, 0xdc, 0x15, 0xa7,
+    0xe1, 0xaf, 0x70, 0x33, 0x32, 0xe4, 0x97, 0xa2, 0xb8, 0x1f, 0xd9, 0x42,
+    0xae, 0x42, 0x1f, 0x4c, 0x60, 0x20, 0x11, 0xfb, 0xc3, 0x13, 0x87, 0x93,
+    0xff, 0xea, 0xf7, 0x30, 0x43, 0x08, 0x58, 0x6c, 0x2f, 0xad, 0x3a, 0xbf,
+    0x62, 0xe4, 0x12, 0x9e, 0x7e, 0x6c, 0xf5, 0xc8, 0x25, 0x3a, 0xe5, 0xea,
+    0xe4, 0x12, 0x9a, 0xdb, 0x57, 0x20, 0x94, 0x75, 0x14, 0xce, 0x14, 0xf0,
+    0xbe, 0xd2, 0x99, 0xab, 0x89, 0x90, 0x48, 0x63, 0x7f, 0x3f, 0x66, 0xb3,
+    0x04, 0x35, 0xa7, 0x50, 0x81, 0x95, 0x2c, 0x1e, 0x18, 0x23, 0x19, 0xcf,
+    0x0c, 0xe7, 0xbc, 0xb2, 0xb4, 0xb4, 0xb3, 0xce, 0x7f, 0x60, 0x58, 0x9f,
+    0xce, 0x3f, 0x7b, 0x38, 0x56, 0x25, 0x3f, 0x89, 0xfb, 0xd5, 0xf4, 0x4b,
+    0x49, 0xe9, 0x4e, 0xaf, 0xd8, 0x94, 0x25, 0x0c, 0x6d, 0xba, 0x20, 0x71,
+    0xc9, 0xe1, 0x60, 0x75, 0x23, 0x1a, 0xd8, 0x64, 0x62, 0xe4, 0x25, 0x67,
+    0xb7, 0xaf, 0xbd, 0x69, 0x1d, 0x69, 0xb1, 0xfe, 0x9b, 0x27, 0x08, 0xa7,
+    0xd6, 0xe3, 0x84, 0x75, 0xa7, 0xee, 0xb3, 0x8f, 0x60, 0x25, 0x22, 0x5a,
+    0x7f, 0xac, 0xdd, 0xfe, 0xeb, 0x7a, 0x15, 0xa7, 0xe2, 0x63, 0xd9, 0x96,
+    0xad, 0x3e, 0xcb, 0x0f, 0x87, 0x5a, 0x75, 0xcb, 0xeb, 0x40, 0x9e, 0x17,
+    0x09, 0xe7, 0xe1, 0xa0, 0x03, 0x7b, 0xad, 0x3f, 0x10, 0x61, 0x31, 0xcd,
+    0xea, 0x61, 0x18, 0x1f, 0xf3, 0xcb, 0xb7, 0xf0, 0x86, 0x31, 0x3c, 0xc2,
+    0x28, 0x18, 0xcd, 0x67, 0x87, 0x2c, 0xdd, 0x68, 0x7a, 0xbc, 0xfc, 0x2b,
+    0xd4, 0x3f, 0x05, 0x62, 0xf1, 0xfd, 0x70, 0xd2, 0x7f, 0xfd, 0x67, 0xff,
+    0x9e, 0xd1, 0xf5, 0x5c, 0x16, 0x71, 0x69, 0xff, 0xff, 0xdb, 0xff, 0x77,
+    0x06, 0x81, 0x7b, 0xe0, 0x79, 0xbf, 0xeb, 0xd6, 0xd3, 0x2d, 0x3a, 0xdb,
+    0x6d, 0x4a, 0x67, 0xb2, 0x46, 0x2f, 0xe1, 0x93, 0x14, 0xb1, 0x5b, 0xf0,
+    0x9a, 0x9f, 0xac, 0x71, 0xfe, 0x67, 0xec, 0x5a, 0x7b, 0x55, 0xb5, 0xab,
+    0x4f, 0xac, 0xf9, 0x31, 0xd6, 0x9f, 0xfe, 0x60, 0xb8, 0x2c, 0x0a, 0xe0,
+    0x02, 0x17, 0xad, 0x1e, 0x6d, 0x19, 0xe4, 0x6d, 0x44, 0x7c, 0x27, 0x9c,
+    0x17, 0xae, 0x2d, 0x39, 0xe4, 0x2b, 0x4f, 0xdc, 0x67, 0x07, 0x7b, 0xad,
+    0x16, 0x1f, 0x3b, 0xc7, 0xf8, 0x35, 0x35, 0x5d, 0x69, 0xb7, 0x74, 0x16,
+    0x81, 0x36, 0x3b, 0x8a, 0xcd, 0x46, 0x74, 0x9d, 0x33, 0x9b, 0xa7, 0x35,
+    0xf3, 0x51, 0x1b, 0x4a, 0xf3, 0xb2, 0x30, 0x77, 0x90, 0x7b, 0x1e, 0xae,
+    0x4b, 0x2a, 0x0e, 0x17, 0x7d, 0x85, 0x3e, 0xc4, 0x85, 0x0d, 0xf7, 0x23,
+    0x83, 0xfc, 0x36, 0x8f, 0x19, 0xe6, 0xa7, 0x08, 0xea, 0x12, 0x22, 0x43,
+    0x79, 0x5c, 0x1c, 0xa5, 0xd5, 0x6f, 0x1b, 0xb7, 0x94, 0x60, 0xa1, 0x42,
+    0xa1, 0xd5, 0x86, 0x7f, 0xf1, 0x8f, 0x4f, 0x36, 0x7f, 0xb5, 0x5d, 0x54,
+    0x51, 0x53, 0xf8, 0xd9, 0xfe, 0xd5, 0x75, 0x51, 0x74, 0x4f, 0xfc, 0x6a,
+    0x79, 0xb3, 0xfd, 0xaa, 0xea, 0xa2, 0x4b, 0x9f, 0xc6, 0xcf, 0xf6, 0xab,
+    0xaa, 0x8b, 0xca, 0x19, 0xd5, 0xe4, 0x58, 0x4e, 0xf3, 0xec, 0x3e, 0xea,
+    0xab, 0x87, 0xc7, 0x3e, 0xd4, 0x32, 0x86, 0xd3, 0x4a, 0xde, 0x3d, 0x5e,
+    0x2a, 0xee, 0x7c, 0xed, 0x3e, 0x7f, 0xf1, 0x8f, 0x4f, 0x36, 0x7f, 0xb5,
+    0x5d, 0x54, 0x4b, 0x33, 0x6f, 0xa5, 0xa7, 0xbb, 0x55, 0xd5, 0x44, 0x67,
+    0x3f, 0x13, 0x1e, 0xcc, 0xb5, 0x69, 0x85, 0xeb, 0x48, 0xeb, 0x4f, 0x8b,
+    0xfa, 0xa3, 0x7c, 0xf4, 0x8e, 0x5a, 0x10, 0xac, 0xfd, 0x4f, 0x2f, 0xd7,
+    0xd6, 0x9f, 0xfa, 0xe5, 0xbd, 0xf0, 0x77, 0x7e, 0x5d, 0x69, 0xd7, 0x6d,
+    0x96, 0x8f, 0xa6, 0xac, 0x77, 0xf1, 0x4c, 0xb9, 0x5f, 0x11, 0x27, 0xf9,
+    0xf7, 0xc6, 0xf0, 0xd7, 0x3a, 0xd3, 0xff, 0x70, 0xbf, 0x40, 0xf1, 0xb3,
+    0xd8, 0xeb, 0x4f, 0x85, 0x9f, 0x7a, 0x5a, 0x08, 0xfa, 0xdd, 0xa3, 0x4e,
+    0xa0, 0x9e, 0xb4, 0xff, 0xf6, 0xfc, 0xbe, 0xcf, 0x61, 0x05, 0xfc, 0x6e,
+    0x2b, 0x4e, 0x6e, 0x32, 0xd2, 0x2f, 0x4f, 0xb9, 0xda, 0xa4, 0xda, 0x71,
+    0x69, 0xfd, 0xb5, 0xc7, 0x3f, 0xeb, 0x2d, 0x04, 0x79, 0x54, 0x2f, 0x3d,
+    0xcd, 0x6f, 0xe4, 0xb4, 0xff, 0x70, 0xad, 0xbb, 0x6a, 0xbc, 0xeb, 0x4b,
+    0x08, 0xf8, 0x8e, 0x4d, 0x3f, 0xed, 0x31, 0x68, 0x98, 0x5c, 0xdd, 0x69,
+    0xfd, 0x97, 0x6e, 0x5f, 0x44, 0xb4, 0x8c, 0xe9, 0x2e, 0x49, 0x64, 0x6d,
+    0xbb, 0x25, 0x39, 0x0a, 0x4d, 0x11, 0xd4, 0x22, 0x80, 0xe8, 0x30, 0x82,
+    0xb9, 0x37, 0x91, 0xec, 0xff, 0xe3, 0x1e, 0x9e, 0x6c, 0xff, 0x6a, 0xba,
+    0xa8, 0x9a, 0x67, 0xff, 0x18, 0xf4, 0xf3, 0x67, 0xfb, 0x55, 0xd5, 0x44,
+    0xe3, 0x3f, 0xf8, 0xc7, 0xa7, 0x9b, 0x3f, 0xda, 0xae, 0xaa, 0x28, 0x19,
+    0xf7, 0xfb, 0x55, 0xd5, 0x45, 0xc1, 0x33, 0x12, 0xd3, 0xf8, 0x5e, 0xc5,
+    0xee, 0x1d, 0x69, 0x1b, 0x0f, 0xe3, 0x46, 0x7b, 0x8a, 0x4f, 0xe2, 0x30,
+    0x4c, 0x0c, 0xb5, 0x69, 0xfe, 0x79, 0xb3, 0xfd, 0xaa, 0xea, 0xa2, 0x49,
+    0x91, 0xb4, 0x7e, 0xf4, 0x67, 0x3e, 0xa3, 0x7b, 0x68, 0x16, 0x8b, 0x19,
+    0x01, 0x4f, 0x9c, 0xe7, 0xec, 0x2c, 0x1c, 0x55, 0x02, 0xad, 0xe1, 0x9b,
+    0xc8, 0x56, 0x3b, 0x27, 0x9f, 0xf0, 0x74, 0x6c, 0xff, 0x6a, 0xba, 0xa8,
+    0xb5, 0x27, 0xfd, 0x4f, 0x36, 0x7f, 0xb5, 0x5d, 0x54, 0x4a, 0xb0, 0xb4,
+    0x8c, 0x1a, 0x27, 0x4e, 0x94, 0xea, 0x93, 0x3f, 0x8d, 0x9f, 0xed, 0x57,
+    0x55, 0x11, 0x4c, 0xfe, 0x36, 0x7f, 0xb5, 0x5d, 0x54, 0x46, 0x93, 0x6f,
+    0xa5, 0xa7, 0xbb, 0x55, 0xd5, 0x45, 0x3f, 0x37, 0x19, 0x68, 0xf9, 0xe1,
+    0x70, 0xb6, 0x7f, 0xdc, 0x71, 0xb9, 0xaa, 0x73, 0xc6, 0x2d, 0x3f, 0xf6,
+    0xfc, 0xfe, 0xee, 0x5f, 0xb4, 0xe5, 0xd6, 0x9f, 0xfb, 0x1c, 0xdc, 0x6c,
+    0xbf, 0x69, 0xcb, 0xad, 0x23, 0x3a, 0x49, 0xa6, 0x62, 0xc9, 0x11, 0x0a,
+    0x0f, 0x12, 0x67, 0xff, 0x18, 0xf4, 0xf3, 0x67, 0xfb, 0x55, 0xd5, 0x44,
+    0xd5, 0x3f, 0x8d, 0x9f, 0xed, 0x57, 0x55, 0x15, 0xdc, 0xf7, 0x6a, 0xba,
+    0xa8, 0xaf, 0xa7, 0x5b, 0x6d, 0xa9, 0x48, 0x52, 0x31, 0x7f, 0x1f, 0x3e,
+    0xa3, 0xa6, 0x4e, 0x7b, 0x6c, 0xb4, 0xe7, 0x33, 0x4b, 0x4f, 0xfb, 0x35,
+    0x6d, 0x72, 0xed, 0x96, 0x2d, 0x2e, 0x2d, 0x3f, 0xc5, 0xfc, 0xb0, 0x9f,
+    0x9e, 0xad, 0x1f, 0x3c, 0x8a, 0x10, 0x91, 0xb1, 0x30, 0x17, 0x08, 0xa8,
+    0x70, 0x47, 0x2f, 0x08, 0x69, 0xff, 0xc6, 0x3d, 0x3c, 0xd9, 0xfe, 0xd5,
+    0x75, 0x51, 0x3d, 0xcf, 0xe3, 0x67, 0xfb, 0x55, 0xd5, 0x45, 0xc3, 0x3f,
+    0xf1, 0xa9, 0xe6, 0xcf, 0xf6, 0xab, 0xaa, 0x89, 0x3e, 0x19, 0x76, 0x26,
+    0xc3, 0xa7, 0x9f, 0x07, 0x1c, 0x57, 0x54, 0xdc, 0x55, 0xfc, 0x75, 0x3a,
+    0x54, 0xba, 0xab, 0xb3, 0xe9, 0xfc, 0x6c, 0xff, 0x6a, 0xba, 0xa8, 0x8a,
+    0xa7, 0xdf, 0xed, 0x57, 0x55, 0x14, 0xcc, 0xfc, 0x5a, 0xca, 0xd8, 0x0b,
+    0x48, 0xd8, 0x7b, 0xfe, 0x46, 0x73, 0xff, 0x8c, 0x7a, 0x79, 0xb3, 0xfd,
+    0xaa, 0xea, 0xa2, 0x67, 0x9f, 0xfc, 0x63, 0xd3, 0xcd, 0x9f, 0xed, 0x57,
+    0x55, 0x14, 0x6c, 0x58, 0x9b, 0x8f, 0xb0, 0x9d, 0x0c, 0xab, 0x8a, 0xb3,
+    0xff, 0x1a, 0x9e, 0x6c, 0xff, 0x6a, 0xba, 0xa8, 0x8e, 0xa6, 0xdf, 0x4b,
+    0x4f, 0xc5, 0x83, 0xee, 0x58, 0xb4, 0xf7, 0x6a, 0xba, 0xa8, 0xa6, 0xa7,
+    0xd9, 0xb5, 0xb5, 0xa5, 0xa3, 0xe7, 0xaa, 0x25, 0xb3, 0xff, 0xb4, 0x5c,
+    0xbd, 0x6d, 0x8e, 0x36, 0xae, 0xb4, 0xff, 0xcf, 0xd1, 0x0d, 0x97, 0xdd,
+    0xbe, 0xe2, 0xd3, 0x30, 0x6b, 0x4f, 0xc4, 0xc7, 0xb3, 0x2d, 0x5a, 0x7f,
+    0xfb, 0x39, 0xac, 0x7f, 0xe8, 0x79, 0xaa, 0x7a, 0xd3, 0x30, 0x16, 0x9f,
+    0xd5, 0xb3, 0x74, 0xba, 0x6f, 0xa2, 0x3c, 0x4b, 0x42, 0x4d, 0x8d, 0x23,
+    0xf6, 0xf0, 0xa5, 0x91, 0x9d, 0x25, 0x4d, 0xde, 0x8b, 0xe4, 0x20, 0x7a,
+    0x43, 0xb2, 0x57, 0x23, 0x43, 0x9f, 0x7f, 0xb5, 0x5d, 0x54, 0x55, 0x53,
+    0xfe, 0xa7, 0x9b, 0x3f, 0xda, 0xae, 0xaa, 0x26, 0xd9, 0x1b, 0x0f, 0xf0,
+    0xe6, 0x73, 0xf8, 0xd9, 0xfe, 0xd5, 0x75, 0x51, 0x5f, 0xcf, 0xe3, 0x67,
+    0xfb, 0x55, 0xd5, 0x45, 0x8d, 0x3f, 0xd9, 0xce, 0x67, 0xfd, 0xc1, 0x5a,
+    0x70, 0x6f, 0xc5, 0xa7, 0x17, 0xcc, 0x27, 0xa7, 0x73, 0x79, 0xf7, 0xfb,
+    0x55, 0xd5, 0x45, 0xab, 0x3f, 0xea, 0x79, 0xb3, 0xfd, 0xaa, 0xea, 0xa2,
+    0x82, 0x91, 0xb0, 0xff, 0x0e, 0x67, 0x3e, 0xff, 0x6a, 0xba, 0xa8, 0xb8,
+    0xa7, 0xa9, 0xd4, 0xac, 0x5a, 0x79, 0xd5, 0xd4, 0xba, 0xb4, 0xfe, 0x63,
+    0xd3, 0xb6, 0xfb, 0xaa, 0xd2, 0x36, 0x22, 0xd3, 0x46, 0x74, 0x49, 0xc2,
+    0x79, 0xf7, 0xfb, 0x55, 0xd5, 0x45, 0xd3, 0x3e, 0x61, 0xaf, 0x59, 0x69,
+    0x1b, 0x0f, 0x60, 0x8c, 0xe7, 0xff, 0xb0, 0x78, 0x47, 0xd3, 0x10, 0xd7,
+    0xc3, 0x5a, 0x7f, 0xfe, 0x39, 0xb1, 0xed, 0xf0, 0x78, 0xbe, 0x98, 0xf4,
+    0x2b, 0x4f, 0x65, 0xbe, 0x6c, 0xeb, 0x4f, 0xff, 0xda, 0x1d, 0xeb, 0xcf,
+    0x8f, 0xd6, 0x07, 0x9e, 0xb9, 0xba, 0xd3, 0x77, 0x16, 0x86, 0x3f, 0x70,
+    0xd8, 0xa7, 0xfc, 0xfc, 0xff, 0xe9, 0xf6, 0x65, 0xab, 0x4e, 0x1a, 0x37,
+    0xcf, 0x87, 0x84, 0x53, 0xfe, 0xfb, 0x6d, 0xaa, 0xbf, 0x1a, 0xd5, 0xa6,
+    0xdf, 0x4b, 0x4f, 0x87, 0x35, 0x5c, 0x5a, 0x7e, 0x7d, 0x63, 0xb6, 0xf5,
+    0x68, 0x74, 0x4f, 0x4f, 0xcd, 0x24, 0xb3, 0xf0, 0x29, 0xc7, 0x97, 0x16,
+    0x9f, 0xff, 0xe6, 0xf7, 0x59, 0xb7, 0x06, 0xb4, 0x0a, 0xbd, 0xeb, 0xfd,
+    0x5a, 0x7f, 0x66, 0x60, 0x61, 0xd3, 0xd6, 0x80, 0xd1, 0x31, 0xc6, 0x69,
+    0xff, 0xfe, 0xd3, 0x0e, 0xf7, 0xd1, 0x5f, 0x9a, 0xcf, 0x4a, 0xf8, 0x2b,
+    0x4f, 0x76, 0xab, 0xaa, 0x89, 0x32, 0x7d, 0xdf, 0x35, 0xf9, 0x9e, 0x6d,
+    0xd1, 0x5a, 0x7f, 0x66, 0xdc, 0xe5, 0x3e, 0xeb, 0x43, 0x1f, 0x9f, 0xd0,
+    0x21, 0xe9, 0x93, 0x7d, 0x94, 0xf0, 0xa0, 0x9f, 0xbd, 0x03, 0xa3, 0x70,
+    0x79, 0x2d, 0x3f, 0xfb, 0x45, 0xcd, 0xba, 0x5f, 0xbb, 0x8d, 0x4b, 0x4f,
+    0xb3, 0x84, 0x7a, 0x5a, 0x7f, 0x9b, 0x5c, 0x2b, 0x0e, 0xdd, 0x5a, 0x7f,
+    0xfd, 0x5d, 0xfb, 0x68, 0x73, 0x98, 0x6b, 0x6d, 0xb5, 0x28, 0x7a, 0x2c,
+    0xee, 0x4b, 0xc3, 0x89, 0xfb, 0xbd, 0xdf, 0xa2, 0xcb, 0x4f, 0xfe, 0xdc,
+    0x6b, 0x8d, 0xfb, 0xe9, 0x87, 0x8b, 0x4e, 0xb6, 0xdb, 0x52, 0x9f, 0xbf,
+    0x56, 0x1c, 0xae, 0x91, 0x8b, 0xf9, 0xf6, 0x1f, 0xf9, 0xe7, 0x5a, 0x7c,
+    0x5c, 0xb3, 0x2d, 0x5a, 0x7f, 0x9b, 0xba, 0x1d, 0xf8, 0xd7, 0x5a, 0x7f,
+    0xfd, 0xcd, 0x63, 0xfc, 0x77, 0x45, 0xcd, 0xba, 0x5f, 0x5a, 0x3e, 0x8b,
+    0x83, 0x94, 0x09, 0xc4, 0xff, 0x65, 0x03, 0xc6, 0x7b, 0x4f, 0x5a, 0x7f,
+    0xbc, 0x0f, 0x8b, 0xd9, 0x55, 0xa5, 0xa7, 0xff, 0x55, 0x9a, 0x26, 0x1c,
+    0x70, 0xbe, 0x4b, 0x46, 0x23, 0xaf, 0x85, 0xfb, 0x9c, 0xda, 0x79, 0x3e,
+    0x1c, 0xd5, 0x71, 0x69, 0xff, 0x3d, 0x86, 0xc0, 0xbe, 0x3b, 0xbd, 0x68,
+    0xf3, 0x9f, 0x2b, 0x84, 0xb3, 0xdc, 0xb3, 0x3d, 0x5a, 0x7f, 0x88, 0x30,
+    0xbe, 0x03, 0x95, 0xd6, 0x87, 0x0f, 0x77, 0xc8, 0x8e, 0x75, 0xb6, 0xda,
+    0xb4, 0xff, 0xfb, 0x0f, 0xc1, 0x60, 0x67, 0xb7, 0xc3, 0xb7, 0x52, 0x31,
+    0x7f, 0x18, 0x99, 0x59, 0x42, 0x13, 0x48, 0xb2, 0x25, 0xa7, 0xed, 0x37,
+    0x8e, 0x95, 0x8b, 0x4f, 0xdf, 0xaf, 0x8b, 0x3d, 0x68, 0x74, 0xe7, 0xd3,
+    0xe8, 0x7f, 0xcb, 0xe6, 0x79, 0x9d, 0x26, 0x4c, 0x07, 0x98, 0xd4, 0xc5,
+    0xef, 0x86, 0x2e, 0x46, 0x94, 0x19, 0xb7, 0x4d, 0xf6, 0x87, 0xe9, 0x18,
+    0x38, 0x5b, 0x4c, 0xe2, 0x79, 0x78, 0xf3, 0x79, 0x1e, 0x70, 0x50, 0x9d,
+    0x9f, 0xfd, 0xfc, 0xb7, 0xdd, 0x63, 0x83, 0x9f, 0x7a, 0xd3, 0xbf, 0x9e,
+    0x75, 0xa5, 0x9f, 0x3e, 0xa3, 0xa5, 0xcf, 0xfd, 0x9a, 0x16, 0xb0, 0x2d,
+    0x6a, 0xbc, 0xeb, 0x4f, 0xd5, 0xd3, 0x5b, 0x6d, 0xab, 0x48, 0xcc, 0xcd,
+    0xa7, 0xc4, 0xc0, 0xd6, 0xfb, 0x18, 0x07, 0xcc, 0xaa, 0x93, 0x7e, 0x31,
+    0xb8, 0x5c, 0x9d, 0xda, 0x44, 0x3d, 0xb5, 0x2a, 0xf6, 0x55, 0xee, 0xd0,
+    0xec, 0xf9, 0x39, 0xcf, 0xb5, 0x09, 0x10, 0x42, 0xba, 0xf0, 0xdb, 0xe4,
+    0x26, 0x77, 0xa6, 0xe6, 0xcf, 0xe3, 0x67, 0xfb, 0x55, 0xd5, 0x44, 0x6b,
+    0x3e, 0xff, 0x6a, 0xba, 0xa8, 0xa9, 0x66, 0xae, 0xaa, 0x21, 0xa9, 0x1b,
+    0x0f, 0x56, 0x8c, 0xe7, 0xfe, 0x35, 0x3c, 0xd9, 0xfe, 0xd5, 0x75, 0x51,
+    0x1f, 0x4f, 0xbf, 0xda, 0xae, 0xaa, 0x2c, 0x79, 0xfe, 0x1a, 0x0f, 0x44,
+    0xc3, 0x62, 0xd2, 0x36, 0x1f, 0x53, 0x86, 0x73, 0xbc, 0xd6, 0x70, 0xd6,
+    0x9f, 0x9d, 0x11, 0xcd, 0x57, 0x16, 0x9f, 0xe1, 0xa0, 0x5e, 0xb9, 0xca,
+    0x5a, 0x78, 0x07, 0x2f, 0xad, 0x3f, 0xff, 0xcd, 0xf1, 0xcf, 0x73, 0x5a,
+    0x2e, 0xe8, 0xb9, 0x7a, 0xd9, 0x68, 0x24, 0x43, 0x68, 0x86, 0x75, 0x57,
+    0x55, 0x16, 0x84, 0xff, 0xaa, 0xd7, 0x5e, 0xd7, 0xec, 0xcb, 0x56, 0x8e,
+    0x9f, 0x57, 0xc9, 0xe7, 0xff, 0xff, 0x8b, 0xfa, 0xa1, 0x26, 0xe1, 0x69,
+    0xaf, 0xac, 0xbf, 0xf3, 0xb9, 0xc5, 0xa3, 0xe8, 0x9a, 0xd1, 0x14, 0xfa,
+    0x9e, 0x34, 0x12, 0xd3, 0xf3, 0xb0, 0xb8, 0x2c, 0x05, 0xa7, 0xcd, 0xcc,
+    0xd3, 0x2d, 0x3f, 0xfd, 0x9c, 0xbe, 0x39, 0xa6, 0x77, 0x9a, 0xd3, 0x2d,
+    0x16, 0x1f, 0xa8, 0x92, 0xc3, 0x23, 0x1b, 0x21, 0x49, 0x3f, 0xe0, 0x37,
+    0x74, 0x4e, 0x55, 0x58, 0xb4, 0xff, 0x16, 0xe2, 0xff, 0xb6, 0x7a, 0xb4,
+    0xff, 0xff, 0x6b, 0x1f, 0xdd, 0x16, 0x9b, 0x3e, 0xfe, 0x0b, 0x1e, 0xeb,
+    0x4b, 0x2e, 0x89, 0xce, 0x1c, 0x4f, 0xdf, 0x6e, 0x59, 0x96, 0xad, 0x3e,
+    0xe6, 0xa8, 0x43, 0x5a, 0x7f, 0x87, 0x7b, 0x3d, 0xd6, 0x0b, 0x2d, 0x2c,
+    0xc3, 0xe0, 0xe9, 0x3c, 0x12, 0x2d, 0x06, 0x11, 0xd3, 0xfb, 0x34, 0xe1,
+    0x68, 0x8e, 0xb4, 0xfe, 0x7e, 0x7e, 0xe2, 0xd6, 0x2d, 0x3b, 0x35, 0x4b,
+    0x4f, 0x9c, 0xf7, 0x7d, 0xc5, 0x68, 0xf4, 0xf1, 0x68, 0x6a, 0x7f, 0xb3,
+    0x2d, 0xce, 0xf7, 0x2d, 0x5a, 0x31, 0x30, 0x3d, 0x8c, 0xa9, 0xde, 0xe4,
+    0x53, 0xd9, 0xaa, 0xe2, 0xd3, 0xf1, 0x0e, 0xef, 0xcb, 0xad, 0x3f, 0xff,
+    0xc4, 0x3b, 0xbf, 0x2f, 0xe3, 0x38, 0xdb, 0x02, 0xb5, 0xa2, 0x5a, 0x03,
+    0x44, 0x9f, 0x0b, 0x24, 0x67, 0x49, 0x7d, 0x7f, 0xcc, 0x25, 0x63, 0x0f,
+    0x61, 0x79, 0x91, 0x93, 0x1c, 0x8f, 0x50, 0xec, 0x01, 0x30, 0xc3, 0x6a,
+    0xf0, 0xdc, 0xe4, 0x65, 0x3e, 0x47, 0xa1, 0x42, 0xce, 0x7e, 0x75, 0x2f,
+    0x45, 0x9c, 0x5a, 0x7b, 0x35, 0x5c, 0x5a, 0x4e, 0x8e, 0x1e, 0x80, 0x98,
+    0x4f, 0xbf, 0xda, 0xae, 0xaa, 0x2d, 0x69, 0xff, 0x53, 0xcd, 0x9f, 0xed,
+    0x57, 0x55, 0x14, 0x1c, 0x8d, 0xe6, 0x22, 0x9f, 0x0a, 0xce, 0x67, 0x3f,
+    0xf8, 0xc7, 0xa7, 0x9b, 0x3f, 0xda, 0xae, 0xaa, 0x28, 0xb9, 0xfc, 0x6c,
+    0xff, 0x6a, 0xba, 0xa8, 0xba, 0xa1, 0xec, 0xba, 0x9e, 0xc2, 0x4c, 0x89,
+    0x8f, 0x0b, 0x9d, 0x4f, 0xc3, 0x82, 0x3e, 0x3b, 0x9d, 0x71, 0x56, 0x7d,
+    0xfe, 0xd5, 0x75, 0x51, 0x10, 0xce, 0xd6, 0x7a, 0xb4, 0xf6, 0x3b, 0x2f,
+    0xad, 0x23, 0x61, 0xf8, 0xec, 0x67, 0xe7, 0x1c, 0x9f, 0xc6, 0xcf, 0xf6,
+    0xab, 0xaa, 0x88, 0xda, 0x7f, 0x1b, 0x3f, 0xda, 0xae, 0xaa, 0x29, 0xb9,
+    0xfc, 0x6c, 0xff, 0x6a, 0xba, 0xa8, 0xa8, 0x27, 0xf1, 0xb3, 0xfd, 0xaa,
+    0xea, 0xa2, 0xa6, 0x9f, 0x7f, 0xb5, 0x5d, 0x54, 0x56, 0x13, 0xe2, 0xb7,
+    0xdd, 0xfe, 0xb4, 0xff, 0x3c, 0xd9, 0xfe, 0xd5, 0x75, 0x51, 0x1f, 0xce,
+    0xc6, 0xb1, 0x69, 0x1b, 0x11, 0x79, 0xd3, 0x3a, 0x2a, 0x14, 0x49, 0xff,
+    0xc6, 0x3d, 0x3c, 0xd9, 0xfe, 0xd5, 0x75, 0x51, 0x37, 0xcf, 0xfc, 0x7a,
+    0x79, 0xb3, 0xfd, 0xaa, 0xea, 0xa2, 0x7e, 0x9f, 0x79, 0xb7, 0x47, 0xcb,
+    0x36, 0x5a, 0x5a, 0x5a, 0x1d, 0x31, 0xe3, 0x6e, 0x6d, 0x3f, 0xde, 0x6a,
+    0xd5, 0x73, 0x95, 0x56, 0xad, 0x3e, 0xf0, 0x1f, 0xeb, 0xab, 0x4f, 0xbc,
+    0xc7, 0x4e, 0x00, 0xd9, 0x69, 0xff, 0xe6, 0x66, 0x66, 0x66, 0x66, 0x6d,
+    0xae, 0xb4, 0xfb, 0xe5, 0x65, 0x1d, 0x29, 0xad, 0xb5, 0x28, 0xc3, 0x7f,
+    0x69, 0x3c, 0xb7, 0x48, 0xc6, 0x86, 0x19, 0x19, 0x57, 0x85, 0x4c, 0xf7,
+    0xc4, 0x99, 0x69, 0xff, 0xfb, 0x0f, 0x7e, 0x7e, 0xac, 0xf7, 0x4d, 0x7e,
+    0x6f, 0xa5, 0xa7, 0xd5, 0xdd, 0x17, 0xd6, 0x9f, 0xff, 0xfc, 0x34, 0x19,
+    0x7d, 0x9f, 0xe3, 0x44, 0x7f, 0x5b, 0xc7, 0x96, 0xfa, 0x2f, 0xad, 0x3d,
+    0x4f, 0x2d, 0x96, 0x8f, 0x53, 0x24, 0xfa, 0xf6, 0x89, 0x85, 0xfe, 0x7f,
+    0xfc, 0xea, 0xdb, 0x3e, 0x84, 0x1e, 0x00, 0x5f, 0x20, 0xd6, 0x9e, 0xb0,
+    0xe4, 0xf5, 0xa7, 0xd7, 0x1d, 0xe9, 0xeb, 0x4f, 0xed, 0x31, 0xcc, 0x00,
+    0x32, 0xd2, 0xfb, 0x1f, 0xde, 0x11, 0x11, 0x3c, 0xff, 0x9b, 0x6f, 0x42,
+    0x6f, 0x3e, 0xb7, 0xba, 0xd3, 0xb3, 0x1c, 0x5a, 0x5e, 0x75, 0xa6, 0xbe,
+    0x2d, 0x04, 0x6a, 0x40, 0x29, 0x3f, 0xf7, 0xad, 0xca, 0xbe, 0x6b, 0x44,
+    0x75, 0xa7, 0x70, 0x9c, 0x5a, 0x18, 0xf7, 0xee, 0x87, 0x0c, 0xaf, 0x67,
+    0x09, 0xca, 0x34, 0x37, 0x0f, 0xbf, 0x0d, 0x53, 0x99, 0x01, 0x18, 0x51,
+    0x38, 0xff, 0x39, 0xc0, 0x8e, 0xb4, 0xff, 0x67, 0x2f, 0x9c, 0xd6, 0x01,
+    0x68, 0xa3, 0xd5, 0x11, 0xe9, 0xa8, 0x56, 0x8f, 0x9b, 0x43, 0x90, 0x4e,
+    0xb6, 0xdb, 0x56, 0x9e, 0xb0, 0x49, 0x92, 0x31, 0x7f, 0x36, 0xcf, 0x5a,
+    0x7f, 0x06, 0xdc, 0xbe, 0xab, 0x75, 0xa7, 0xc5, 0x66, 0x6d, 0xc5, 0xa6,
+    0x2e, 0xad, 0x2d, 0x99, 0x11, 0x01, 0x8b, 0x9c, 0xd3, 0x85, 0x10, 0xc9,
+    0x9d, 0xe9, 0x03, 0x78, 0x5b, 0xcf, 0xfd, 0x66, 0xe0, 0xc1, 0xdd, 0xc6,
+    0xe5, 0x2d, 0x3f, 0xf3, 0x68, 0x2f, 0x78, 0x58, 0xe3, 0x1d, 0x69, 0xe6,
+    0xe6, 0xe7, 0x5a, 0x6b, 0xf5, 0x51, 0x07, 0x49, 0x8e, 0x78, 0xbc, 0x22,
+    0x9f, 0xab, 0x4d, 0xe7, 0xdd, 0xeb, 0x47, 0xd3, 0x28, 0x3a, 0x37, 0x21,
+    0x14, 0xec, 0x9e, 0x6f, 0xfa, 0xb4, 0xd6, 0xda, 0xb4, 0x3c, 0xd7, 0xda,
+    0x2f, 0x3d, 0x76, 0x27, 0x69, 0x18, 0xd1, 0x4f, 0xae, 0x17, 0xa0, 0xba,
+    0xd0, 0x47, 0xb8, 0x11, 0x94, 0xeb, 0x6d, 0xb5, 0x28, 0x48, 0xc5, 0xfc,
+    0xf1, 0x5a, 0x4e, 0x25, 0x1d, 0x37, 0x64, 0x33, 0x04, 0x9c, 0x76, 0xa1,
+    0xe4, 0x2f, 0x93, 0x84, 0xae, 0xb4, 0xf7, 0xb4, 0x20, 0x5a, 0x7f, 0xe6,
+    0x10, 0x0b, 0x6c, 0xe3, 0x08, 0x16, 0x82, 0x3f, 0xd0, 0x0d, 0x5c, 0x86,
+    0x7c, 0xd9, 0xfd, 0x12, 0xd3, 0xb0, 0xfe, 0x75, 0xa7, 0xf6, 0x5c, 0x73,
+    0x9a, 0x25, 0xa3, 0xd3, 0xcf, 0x11, 0xf8, 0x71, 0x12, 0xce, 0xdd, 0x27,
+    0xee, 0x63, 0x82, 0xc7, 0x5c, 0x40, 0x53, 0xea, 0x1f, 0x74, 0xca, 0x88,
+    0x08, 0xc6, 0xe6, 0x7e, 0xfb, 0x3e, 0xb6, 0xb5, 0x69, 0xfe, 0x6f, 0xd9,
+    0xe3, 0x1c, 0x23, 0xad, 0x3c, 0x76, 0xc3, 0xad, 0x3e, 0x60, 0x16, 0xd7,
+    0x5a, 0x7f, 0x7d, 0xcb, 0x90, 0xf0, 0x96, 0x9e, 0xd8, 0x1f, 0x64, 0xa6,
+    0xb6, 0xd4, 0xa1, 0x8d, 0xd5, 0xa4, 0x53, 0xf5, 0x7d, 0xef, 0x12, 0x48,
+    0xc6, 0x86, 0x19, 0x56, 0xf6, 0x43, 0x10, 0x97, 0xfe, 0x87, 0xa2, 0xea,
+    0x3b, 0x12, 0x0e, 0x13, 0xef, 0x08, 0xd9, 0xf6, 0xfc, 0xde, 0x82, 0x54,
+    0x40, 0xf3, 0x36, 0xeb, 0x4f, 0xb7, 0x10, 0x04, 0xf5, 0xa7, 0xff, 0xce,
+    0xc7, 0x2f, 0xe0, 0x2d, 0x30, 0x0f, 0x4f, 0xf1, 0x86, 0xfe, 0xe2, 0xd3,
+    0xfe, 0xd6, 0xf6, 0x37, 0x86, 0xe3, 0x7a, 0xb4, 0xea, 0xfd, 0x8b, 0x4d,
+    0x6d, 0xab, 0x4f, 0xef, 0x96, 0x3b, 0x09, 0xe6, 0xe9, 0xb4, 0xb4, 0x72,
+    0x3d, 0x46, 0x1f, 0xdd, 0xa7, 0xfe, 0x1c, 0x0f, 0x54, 0xf0, 0xab, 0x66,
+    0x5a, 0x18, 0xfa, 0x30, 0x92, 0x7f, 0xf6, 0x1c, 0xf4, 0xfc, 0xff, 0x6a,
+    0xba, 0xa8, 0x86, 0x20, 0x95, 0x08, 0x6a, 0x39, 0xc1, 0x20, 0x9f, 0x7f,
+    0xb5, 0x5d, 0x54, 0x41, 0x53, 0xb4, 0xc7, 0x5a, 0x30, 0xf3, 0x88, 0xce,
+    0x7b, 0x97, 0x60, 0x25, 0x3a, 0xbf, 0x62, 0x53, 0xdb, 0x8d, 0x06, 0x94,
+    0xfe, 0x2b, 0x33, 0x97, 0x60, 0x25, 0x09, 0x4f, 0xd9, 0xd6, 0xd3, 0x1d,
+    0x29, 0xad, 0xb5, 0x29, 0xf8, 0x58, 0x6c, 0x2f, 0xa5, 0x18, 0x98, 0x4f,
+    0x48, 0x4e, 0x39, 0xa2, 0x20, 0x19, 0x08, 0x55, 0xa5, 0x61, 0x0c, 0x4c,
+    0x5f, 0x48, 0xc7, 0xe5, 0x2a, 0x7a, 0x78, 0x3a, 0x8e, 0x6e, 0x7f, 0xcc,
+    0xfa, 0x1c, 0xbf, 0xda, 0xeb, 0x4f, 0xf7, 0x34, 0xce, 0xfd, 0x60, 0x32,
+    0xd3, 0xfc, 0xdb, 0x3f, 0xc9, 0xbb, 0x87, 0x5a, 0x18, 0xfd, 0x74, 0x75,
+    0x3f, 0xff, 0x39, 0x54, 0xfa, 0xd7, 0x8c, 0xb3, 0x7e, 0x6f, 0x41, 0x2a,
+    0x2f, 0xb9, 0xdc, 0xce, 0xad, 0x3d, 0xc1, 0xc7, 0xad, 0x0c, 0x6e, 0xc8,
+    0x6e, 0x7f, 0xc2, 0x4f, 0xe6, 0xb3, 0xe5, 0x75, 0xa7, 0xf7, 0x00, 0x10,
+    0xbf, 0x58, 0xb8, 0x80, 0x67, 0x57, 0xde, 0xb8, 0x80, 0x63, 0x0f, 0xa3,
+    0x74, 0x09, 0xa9, 0xeb, 0x88, 0x06, 0x7a, 0x87, 0x67, 0xae, 0x20, 0x19,
+    0xfc, 0x5d, 0xd6, 0x00, 0x0c, 0xb8, 0x80, 0x67, 0x10, 0xfa, 0xb8, 0x80,
+    0x63, 0xd4, 0x5b, 0x1c, 0x8b, 0x45, 0xce, 0xcf, 0xa7, 0x7d, 0xb8, 0xb8,
+    0x80, 0x61, 0x71, 0x00, 0xcc, 0xc7, 0x5c, 0x40, 0x31, 0xe9, 0xb9, 0x21,
+    0x79, 0xe2, 0x3f, 0xac, 0xb8, 0x80, 0x67, 0x72, 0xba, 0xb8, 0x80, 0x67,
+    0xfc, 0x34, 0xf3, 0x68, 0xbb, 0x5f, 0x5c, 0x40, 0x33, 0x57, 0xab, 0x88,
+    0x06, 0x7f, 0x0d, 0x5b, 0x7b, 0xb0, 0x17, 0x10, 0x0c, 0xf8, 0x83, 0xa1,
+    0x02, 0xe2, 0x01, 0x98, 0xae, 0xb8, 0x80, 0x63, 0xe7, 0xab, 0xc3, 0x39,
+    0xf7, 0x05, 0xb6, 0x7a, 0xa2, 0x01, 0x98, 0x0c, 0xb8, 0x80, 0x4c, 0x6d,
+    0x27, 0xc4, 0xc7, 0xaf, 0x57, 0x10, 0x0c, 0xf6, 0xf4, 0x3d, 0x5c, 0x40,
+    0x33, 0x9b, 0xfd, 0x5c, 0x40, 0x33, 0xfe, 0xcd, 0xaf, 0xeb, 0x60, 0x82,
+    0xeb, 0x88, 0x06, 0x7d, 0xbd, 0x3d, 0xec, 0xb8, 0x80, 0x63, 0x0f, 0xff,
+    0x49, 0x73, 0x7c, 0x0b, 0x88, 0x06, 0x1e, 0xaa, 0x2f, 0xd2, 0x3c, 0x84,
+    0xa1, 0x2b, 0x68, 0xc8, 0x06, 0x77, 0x2e, 0xe4, 0x29, 0xf7, 0x21, 0x9f,
+    0x67, 0x2f, 0x5b, 0x2e, 0x20, 0x19, 0xfd, 0xeb, 0x75, 0xcf, 0xb7, 0x17,
+    0x10, 0x0f, 0xa6, 0xd2, 0x77, 0xda, 0xd5, 0xc4, 0x03, 0x1d, 0x3f, 0xbf,
+    0xa9, 0x4f, 0x10, 0xbe, 0xeb, 0x88, 0x06, 0x7e, 0xa7, 0x1f, 0x9b, 0x3d,
+    0x71, 0x00, 0xc6, 0x22, 0x28, 0x04, 0x5b, 0x96, 0xcf, 0xf6, 0x9a, 0xe6,
+    0xbe, 0xff, 0x02, 0xe2, 0x01, 0x91, 0x2e, 0x20, 0x19, 0xaa, 0xcf, 0x4f,
+    0x8f, 0x48, 0xf3, 0x7c, 0x0b, 0x88, 0x06, 0x7d, 0x5c, 0xb9, 0x6c, 0xb8,
+    0x80, 0x67, 0xe2, 0x1d, 0xdf, 0x97, 0x5c, 0x40, 0x30, 0xc8, 0x90, 0x12,
+    0x3e, 0x1a, 0x47, 0xac, 0x80, 0x7c, 0x86, 0xc3, 0x88, 0x1f, 0x61, 0xd1,
+    0x0d, 0x17, 0x00, 0xa2, 0xf2, 0xb5, 0x39, 0x1e, 0x1f, 0x94, 0x24, 0xc2,
+    0x85, 0xfc, 0xea, 0xae, 0xaa, 0x20, 0x13, 0x22, 0xf2, 0x78, 0x37, 0x4f,
+    0xe9, 0xd6, 0x9f, 0xf9, 0xe3, 0x9d, 0xd1, 0x00, 0x1b, 0xb8, 0xb4, 0xc0,
+    0x64, 0xa4, 0x1a, 0x53, 0xb4, 0xd6, 0x2d, 0x35, 0xb6, 0xa5, 0x04, 0x7b,
+    0x5e, 0x71, 0x57, 0x04, 0x6d, 0x1c, 0x9c, 0x72, 0xba, 0x46, 0x3c, 0x19,
+    0xff, 0xdc, 0xbe, 0x06, 0x5f, 0x08, 0x00, 0x6d, 0x96, 0x8b, 0x19, 0x68,
+    0x2f, 0x1f, 0xed, 0x25, 0x9c, 0x8e, 0x68, 0xa8, 0x10, 0xe0, 0x08, 0xb2,
+    0x7e, 0xe0, 0x4e, 0xdd, 0xb5, 0xd6, 0x9c, 0x3b, 0x3d, 0x71, 0x80, 0x41,
+    0x1e, 0xdd, 0x19, 0x4f, 0xdf, 0x71, 0xed, 0xfb, 0x16, 0x9f, 0xf3, 0xef,
+    0xe0, 0x70, 0xed, 0xf0, 0xd6, 0x9f, 0xfb, 0xfa, 0xac, 0x7b, 0x7c, 0x01,
+    0x1d, 0x69, 0xf8, 0x2b, 0x7e, 0x59, 0xa4, 0xa7, 0x9c, 0xd5, 0x38, 0xb4,
+    0xfb, 0x1c, 0x7b, 0x6c, 0xb4, 0xf6, 0x0b, 0x3d, 0x28, 0xf9, 0xf5, 0x09,
+    0x1b, 0xa9, 0x44, 0x32, 0x6f, 0x98, 0x5e, 0x48, 0x1a, 0x44, 0xa8, 0x4c,
+    0x4f, 0xdb, 0x5f, 0xff, 0x6e, 0x2d, 0x3c, 0x43, 0xcb, 0xad, 0x3b, 0x77,
+    0x42, 0xeb, 0x4f, 0xfc, 0x03, 0xe7, 0x74, 0x5e, 0xeb, 0x70, 0xd6, 0x9f,
+    0xfa, 0xe1, 0x87, 0x5e, 0x18, 0xe2, 0x41, 0xad, 0x3f, 0xd9, 0xce, 0x10,
+    0x7c, 0xce, 0xad, 0x3f, 0xbd, 0x6d, 0xaf, 0x9b, 0x71, 0x69, 0xfe, 0x6e,
+    0x17, 0x4b, 0xd6, 0xe2, 0xd2, 0xbf, 0xa8, 0xa4, 0xc3, 0x8f, 0x9a, 0xc6,
+    0xc9, 0x90, 0xf2, 0x1d, 0x73, 0xf9, 0xf9, 0x70, 0xaf, 0xcc, 0x5a, 0x19,
+    0x3e, 0x72, 0x22, 0xa8, 0xda, 0x37, 0x2a, 0x9f, 0xf9, 0xb6, 0x78, 0xee,
+    0x0f, 0x1d, 0x38, 0x16, 0x9f, 0x53, 0xbf, 0x7d, 0x65, 0xa6, 0xbd, 0xd6,
+    0x9d, 0x6d, 0xb6, 0xad, 0x30, 0x29, 0x23, 0x17, 0xf1, 0xf3, 0xd9, 0xb9,
+    0xac, 0xff, 0xb2, 0xe3, 0x41, 0xb8, 0x24, 0xe2, 0xd3, 0xa8, 0x18, 0x94,
+    0x3d, 0x1f, 0xa7, 0x84, 0x1e, 0x88, 0xbc, 0x8f, 0xa7, 0xf7, 0x07, 0x36,
+    0x00, 0x4f, 0x5a, 0x6a, 0x3a, 0xd3, 0xfc, 0x38, 0xef, 0x0d, 0x6d, 0xb6,
+    0xa5, 0x38, 0xf5, 0xc5, 0xa1, 0xc3, 0xec, 0xd0, 0xab, 0xb3, 0xc9, 0xf6,
+    0x38, 0xf6, 0x7a, 0xd3, 0xf5, 0x9e, 0xb6, 0x0f, 0xab, 0x43, 0x26, 0xd8,
+    0x48, 0xb5, 0x0a, 0x0e, 0x19, 0x04, 0x4f, 0x3f, 0x3a, 0x05, 0xf6, 0xf8,
+    0xad, 0x3f, 0xfb, 0x3d, 0xa3, 0xe6, 0xcf, 0xc0, 0x67, 0xab, 0x4d, 0xaa,
+    0x5a, 0x67, 0x2e, 0xb4, 0xfb, 0x1d, 0x90, 0xfb, 0x86, 0xb0, 0x21, 0x58,
+    0xf0, 0x8b, 0x53, 0x3b, 0x4f, 0x7b, 0x9a, 0xa5, 0xa7, 0xfe, 0x6c, 0x17,
+    0xeb, 0x04, 0x5a, 0xd5, 0xa4, 0x5f, 0x44, 0x30, 0x92, 0xf0, 0x86, 0x2c,
+    0x6e, 0xe0, 0x9f, 0x2a, 0x1b, 0xd9, 0x48, 0xa1, 0x97, 0xf6, 0x16, 0x04,
+    0x43, 0xfa, 0x5f, 0x49, 0xe1, 0xa3, 0xa8, 0xe7, 0x29, 0x30, 0x05, 0xc3,
+    0x28, 0x1e, 0xe8, 0x5c, 0x95, 0x63, 0xba, 0x73, 0xb8, 0xda, 0xe7, 0xfb,
+    0x3e, 0x2d, 0xc7, 0xe6, 0x96, 0x9f, 0x84, 0x19, 0xed, 0x3d, 0x69, 0xf5,
+    0x3f, 0xc7, 0xa4, 0xb4, 0xf8, 0x27, 0x1e, 0xde, 0x74, 0xa1, 0x91, 0x71,
+    0x86, 0xda, 0x2c, 0x08, 0xa6, 0x7f, 0xe0, 0xc7, 0x7a, 0x0b, 0x2e, 0x4c,
+    0x2b, 0x4e, 0xf2, 0x2f, 0x56, 0x8f, 0x9f, 0x11, 0xd0, 0xe4, 0x12, 0xa2,
+    0x05, 0x97, 0x15, 0x40, 0xa4, 0x8b, 0x86, 0xee, 0xd1, 0xe9, 0xff, 0x17,
+    0x96, 0x88, 0x23, 0x00, 0x0c, 0x94, 0xff, 0x6b, 0x7a, 0xbe, 0x60, 0x86,
+    0xb4, 0xff, 0x85, 0xb6, 0x3d, 0x73, 0x36, 0xe2, 0xd3, 0xff, 0xfb, 0x4d,
+    0xf0, 0x63, 0xb2, 0xbd, 0xf0, 0x7b, 0x42, 0x05, 0xa7, 0xcd, 0x83, 0xcf,
+    0x25, 0xa4, 0x75, 0xa7, 0xaf, 0x5b, 0x78, 0x39, 0xb8, 0x12, 0x88, 0xf5,
+    0x34, 0x7f, 0x9c, 0x68, 0xfb, 0x90, 0x97, 0x86, 0x4f, 0x30, 0xa3, 0x88,
+    0x9f, 0xff, 0xef, 0xd9, 0x84, 0xe5, 0x53, 0xee, 0xc7, 0xf7, 0x4c, 0x3d,
+    0x5a, 0x75, 0x82, 0xf5, 0xa1, 0x95, 0x7d, 0x1d, 0x77, 0x51, 0xf9, 0xd1,
+    0x46, 0xec, 0xd3, 0xf9, 0xee, 0x60, 0xd6, 0xb1, 0x69, 0xff, 0xdf, 0xe8,
+    0xe3, 0xb1, 0xc6, 0xfb, 0x5d, 0x69, 0xfd, 0x4e, 0x6b, 0x0f, 0x97, 0x5a,
+    0x30, 0xfe, 0x9d, 0xa4, 0x4f, 0xc4, 0x1f, 0x33, 0x0e, 0xb4, 0xfb, 0x3d,
+    0x2f, 0xdd, 0x69, 0xdc, 0xe6, 0xcb, 0x4f, 0xfd, 0xb7, 0x4b, 0x5e, 0x1d,
+    0x73, 0x44, 0x75, 0xa5, 0x70, 0x1f, 0x37, 0x07, 0x64, 0xd6, 0x22, 0xd6,
+    0xd8, 0x46, 0xc1, 0x26, 0x67, 0xa2, 0x3e, 0x43, 0x86, 0x7f, 0xc2, 0x41,
+    0x6d, 0x8e, 0xf6, 0x60, 0x96, 0x9d, 0xed, 0x7d, 0x69, 0xf0, 0x28, 0x71,
+    0xd5, 0x68, 0x63, 0xc4, 0xdc, 0x72, 0x71, 0xf6, 0x71, 0x69, 0xff, 0x8f,
+    0x73, 0xb7, 0xad, 0xfd, 0xd8, 0x0b, 0x4f, 0xf9, 0xa8, 0x73, 0x9c, 0x2b,
+    0x78, 0xb4, 0x32, 0x27, 0xc8, 0x7b, 0x74, 0x58, 0x65, 0x71, 0x2f, 0x8f,
+    0x7b, 0x0d, 0x6f, 0x08, 0x6e, 0x43, 0x06, 0x7f, 0xf3, 0xcb, 0x58, 0xe1,
+    0x39, 0xbb, 0xae, 0xf7, 0x5a, 0x7f, 0x16, 0xbd, 0xa1, 0x6d, 0x2d, 0x3e,
+    0x6b, 0x19, 0xd5, 0x96, 0x9f, 0xb7, 0x0b, 0x59, 0xb5, 0xd6, 0x9f, 0xff,
+    0xff, 0x7d, 0xb8, 0x35, 0xd2, 0xbb, 0x85, 0xfe, 0xb5, 0xf8, 0xdb, 0x30,
+    0x59, 0x75, 0xa7, 0xff, 0xff, 0xfd, 0x7b, 0xe7, 0x35, 0x47, 0xbf, 0xfe,
+    0x5b, 0x72, 0xa8, 0x3c, 0xc7, 0x37, 0xe3, 0x5b, 0xb3, 0x2d, 0x0c, 0x98,
+    0xfd, 0x42, 0x0a, 0x78, 0x15, 0xb5, 0xd6, 0x9a, 0xdb, 0x56, 0x8b, 0x9b,
+    0xab, 0x48, 0xa7, 0xc5, 0xaa, 0xcd, 0x24, 0x63, 0x45, 0x1b, 0x2a, 0xb1,
+    0x71, 0x43, 0x46, 0x2e, 0x81, 0x48, 0xc6, 0x9f, 0x78, 0x41, 0xce, 0xb6,
+    0xdb, 0x52, 0x9e, 0x7f, 0x33, 0xa9, 0x18, 0xbf, 0x9f, 0x67, 0xa4, 0x0b,
+    0xae, 0xef, 0xe9, 0x57, 0xcf, 0x90, 0x4c, 0x26, 0xfd, 0xd6, 0x9c, 0xed,
+    0xba, 0xb4, 0x31, 0xb3, 0x21, 0x69, 0xf8, 0x76, 0xbb, 0x99, 0xc5, 0xa7,
+    0xff, 0x87, 0x8e, 0x67, 0xf2, 0x8e, 0xda, 0x2f, 0x56, 0x9f, 0x59, 0xe7,
+    0x2c, 0xd9, 0x69, 0xf1, 0x5d, 0xac, 0x3a, 0xec, 0xfd, 0x9f, 0x35, 0x7c,
+    0x1b, 0xae, 0xcf, 0xd9, 0xa9, 0xeb, 0xb3, 0xf6, 0x7b, 0x77, 0xe5, 0xd7,
+    0x67, 0xec, 0x7a, 0x7a, 0x22, 0x45, 0x3e, 0x6c, 0xbe, 0x0a, 0xec, 0xfd,
+    0x85, 0xd9, 0xfb, 0x35, 0x71, 0x76, 0x7e, 0x9c, 0xb7, 0x93, 0xc4, 0xfe,
+    0x77, 0x48, 0x9e, 0xcf, 0x22, 0x02, 0xec, 0xfd, 0x85, 0xd9, 0xfb, 0x30,
+    0x19, 0x76, 0x7e, 0xcf, 0xf6, 0x02, 0xbf, 0x8e, 0xf3, 0xd5, 0xd9, 0xfb,
+    0x3f, 0x66, 0x8a, 0xfb, 0xd8, 0xbb, 0x3f, 0x60, 0x08, 0xa3, 0x12, 0x2b,
+    0xa3, 0x4f, 0x7e, 0xc6, 0xe2, 0xec, 0xfd, 0x85, 0xd9, 0xfb, 0x86, 0xbe,
+    0x6b, 0x6d, 0x5d, 0x9f, 0xb0, 0xf5, 0x61, 0xfe, 0x9a, 0x64, 0x21, 0x76,
+    0x84, 0xdf, 0xca, 0x0e, 0x61, 0x78, 0x5d, 0x71, 0x7a, 0xd2, 0x69, 0xec,
+    0x7b, 0x06, 0x9b, 0x3f, 0x46, 0x44, 0x8c, 0xff, 0xb3, 0xbe, 0xe7, 0xed,
+    0xad, 0x38, 0xb4, 0xcf, 0xb1, 0x28, 0xb1, 0x13, 0x1b, 0x20, 0x8a, 0x04,
+    0x7d, 0x72, 0x56, 0xa7, 0x19, 0x27, 0xff, 0xcf, 0xfb, 0x3b, 0xa7, 0x1b,
+    0xdd, 0x31, 0xe9, 0xc5, 0xa7, 0xf3, 0xad, 0x5c, 0xae, 0xe6, 0x2d, 0x1b,
+    0x22, 0x37, 0xeb, 0x10, 0xcb, 0xcc, 0xef, 0x59, 0xc1, 0xf2, 0x2e, 0xa9,
+    0xce, 0x3d, 0xe1, 0x75, 0x3f, 0xff, 0xc5, 0xaa, 0xd3, 0x3c, 0xd8, 0x06,
+    0x7d, 0xfd, 0xd3, 0x7d, 0x69, 0xff, 0x06, 0xdb, 0x0b, 0x6b, 0x5b, 0xf5,
+    0x69, 0xf8, 0x68, 0x3d, 0x53, 0xd6, 0x9d, 0x6d, 0xb6, 0xa5, 0x38, 0x26,
+    0x02, 0x46, 0x2f, 0xe7, 0xfc, 0x34, 0xf0, 0xbf, 0x8e, 0xeb, 0xeb, 0x4f,
+    0xf0, 0xd0, 0x3c, 0x3d, 0x81, 0xb2, 0xd0, 0xf4, 0xe5, 0x3d, 0x65, 0x3a,
+    0x00, 0x12, 0xc4, 0xaf, 0x87, 0xf3, 0xfe, 0x11, 0x2d, 0xdd, 0x0b, 0xf9,
+    0x37, 0x16, 0x9d, 0x6d, 0xb6, 0xa6, 0x21, 0x04, 0xfb, 0xfd, 0xaa, 0xea,
+    0x62, 0x10, 0x18, 0xd6, 0xce, 0xb6, 0xdb, 0x53, 0x10, 0x7a, 0x13, 0x10,
+    0x78, 0xc6, 0xb6, 0x47, 0xc4, 0x4c, 0xf5, 0xc6, 0x7f, 0xed, 0x33, 0xcb,
+    0x99, 0xae, 0xb1, 0xd6, 0x86, 0x3e, 0xa7, 0x65, 0x13, 0xee, 0x37, 0x18,
+    0xeb, 0x4f, 0x09, 0x30, 0x6b, 0x4e, 0x09, 0x80, 0xb4, 0x58, 0x6f, 0x0e,
+    0x41, 0x3e, 0xeb, 0x69, 0x8e, 0x94, 0xf8, 0x68, 0x47, 0x12, 0x9b, 0x3a,
+    0x94, 0xd6, 0xda, 0x94, 0x61, 0xfb, 0x5c, 0x9b, 0x84, 0x76, 0x8a, 0xcf,
+    0xe1, 0x09, 0x81, 0xac, 0x75, 0x48, 0xc6, 0xf2, 0x19, 0x37, 0xb2, 0x62,
+    0xa8, 0x6a, 0x4f, 0xfc, 0xc0, 0xbe, 0x67, 0xb4, 0x34, 0x1a, 0xd3, 0xff,
+    0xed, 0x9f, 0x7b, 0x5b, 0xfa, 0x62, 0xd3, 0x09, 0x2d, 0x1a, 0x44, 0xb5,
+    0x21, 0x4f, 0xeb, 0x84, 0x7b, 0xdb, 0x5b, 0x2d, 0x0c, 0xb9, 0x19, 0x8b,
+    0x05, 0x1b, 0xaf, 0xe3, 0x9e, 0xa8, 0x64, 0xdc, 0x8a, 0x75, 0xb6, 0xda,
+    0x94, 0xf0, 0x28, 0x7d, 0x48, 0xc5, 0xfc, 0xfc, 0x16, 0x7f, 0x31, 0xc5,
+    0xa1, 0xe7, 0xbb, 0x73, 0x09, 0xff, 0xe1, 0xdb, 0xc6, 0xc0, 0x61, 0xed,
+    0x58, 0x34, 0xb4, 0xff, 0xff, 0xff, 0x72, 0xe1, 0x5f, 0xc6, 0xa8, 0xb8,
+    0x5a, 0xe9, 0x78, 0x79, 0x6a, 0x87, 0x8f, 0xcf, 0xe2, 0xd3, 0xff, 0xd8,
+    0xc6, 0xf4, 0x5b, 0x7e, 0xe0, 0x65, 0xf5, 0xa7, 0x9d, 0x74, 0x07, 0x6b,
+    0x43, 0xcf, 0xda, 0x93, 0xe7, 0xfe, 0xcf, 0x80, 0xe5, 0x70, 0xae, 0xd8,
+    0xb4, 0xea, 0xf3, 0xdd, 0x68, 0x79, 0xf1, 0x52, 0x1c, 0xf1, 0x7b, 0x87,
+    0x5a, 0x19, 0x52, 0x23, 0xc8, 0xb1, 0x46, 0xa3, 0x0c, 0x18, 0x41, 0xee,
+    0x43, 0x3f, 0x79, 0xee, 0x43, 0xb3, 0xd6, 0x9f, 0xf5, 0x7f, 0xc8, 0xb5,
+    0x95, 0xb0, 0x16, 0x9f, 0xff, 0x17, 0x18, 0xf9, 0xb7, 0x18, 0xe1, 0x7e,
+    0x80, 0xb4, 0xff, 0x5d, 0xbf, 0xc6, 0x37, 0xa4, 0xb4, 0x3d, 0x11, 0xb7,
+    0x55, 0x9f, 0xc4, 0x1f, 0x1b, 0x4d, 0x75, 0xa7, 0xfc, 0x34, 0xe6, 0xab,
+    0xff, 0x2f, 0x56, 0x8c, 0x4e, 0x47, 0xa6, 0x25, 0x0c, 0xdf, 0x91, 0x9c,
+    0xca, 0x79, 0xe1, 0x63, 0x8b, 0x4f, 0xbf, 0xee, 0x60, 0xad, 0x3f, 0xf7,
+    0x9c, 0xb3, 0x4c, 0xee, 0xfa, 0x6d, 0x96, 0x8a, 0x3e, 0xfb, 0x93, 0x4f,
+    0xf6, 0x0f, 0xf2, 0xf8, 0x5a, 0x5a, 0x7f, 0xf7, 0x2f, 0x8e, 0x69, 0x9d,
+    0xe6, 0xb4, 0xcb, 0x4f, 0xda, 0xf4, 0x9d, 0xb7, 0xab, 0x46, 0x1f, 0xdd,
+    0x25, 0xcf, 0x67, 0x0a, 0xeb, 0x4f, 0xce, 0x99, 0xc1, 0x74, 0xac, 0xdd,
+    0x68, 0xb9, 0xed, 0xf0, 0x82, 0x3d, 0x4d, 0xbb, 0xa4, 0x23, 0x0b, 0x0e,
+    0x3d, 0xcf, 0xff, 0x15, 0xf3, 0xde, 0x5c, 0x83, 0xd5, 0x09, 0x2d, 0x3f,
+    0xff, 0x87, 0x99, 0xa2, 0x6e, 0x10, 0x19, 0xe6, 0xb6, 0xdb, 0x52, 0x9e,
+    0xf7, 0x30, 0x34, 0xa7, 0xa8, 0x15, 0xc5, 0xa7, 0x3c, 0x77, 0x54, 0x43,
+    0x33, 0xad, 0xb6, 0xd4, 0xa7, 0x60, 0xf5, 0x23, 0x17, 0xf3, 0xfe, 0xcb,
+    0x33, 0xda, 0x78, 0xb5, 0x8b, 0x40, 0x0f, 0x9c, 0x4a, 0x67, 0xf3, 0xc7,
+    0x39, 0xcd, 0xec, 0x5a, 0x75, 0x79, 0xd9, 0x68, 0x64, 0xf1, 0x6c, 0x61,
+    0xf4, 0x8b, 0x62, 0x0f, 0xc2, 0xbe, 0x88, 0x77, 0x34, 0x9e, 0x2e, 0x13,
+    0x2d, 0x3f, 0xb7, 0xcf, 0x80, 0x04, 0x2b, 0x4f, 0xff, 0x3c, 0xbd, 0xd0,
+    0x57, 0xcf, 0xf6, 0xab, 0xaa, 0x88, 0x32, 0x7f, 0xf7, 0xe9, 0xde, 0x0f,
+    0x74, 0x40, 0x0b, 0x8b, 0x43, 0x22, 0xa3, 0x75, 0xd8, 0xd2, 0x3f, 0x79,
+    0x0d, 0xa9, 0xb5, 0xba, 0xd3, 0xdf, 0x2b, 0x7c, 0xeb, 0x41, 0x1b, 0xdb,
+    0x8b, 0xcf, 0xef, 0x2c, 0xbf, 0x96, 0x7d, 0x96, 0x87, 0xa7, 0xb0, 0x51,
+    0x8e, 0x5d, 0x97, 0x84, 0x13, 0xff, 0xcc, 0x36, 0x78, 0x70, 0x87, 0x3d,
+    0xc1, 0x0d, 0x69, 0xcd, 0xf7, 0x16, 0x86, 0x5f, 0x98, 0xf5, 0x3b, 0x25,
+    0x00, 0x75, 0x0c, 0xa5, 0x09, 0xd4, 0xa4, 0x4d, 0xd0, 0xfc, 0x94, 0x67,
+    0xd4, 0x7a, 0xcb, 0xad, 0x3f, 0xfd, 0xd6, 0xbd, 0xd8, 0xf9, 0xc2, 0xbe,
+    0xa9, 0x69, 0xfc, 0x22, 0xcf, 0x3b, 0x12, 0xd3, 0xfb, 0x84, 0xec, 0xb8,
+    0xf6, 0x4a, 0x47, 0x5a, 0x7e, 0xc1, 0x0f, 0xec, 0x62, 0x3c, 0x30, 0x8c,
+    0xe3, 0x13, 0x03, 0x24, 0xdb, 0xb9, 0x4f, 0x7f, 0x7d, 0x9e, 0xb4, 0xfe,
+    0x17, 0x8e, 0x1d, 0xba, 0xb4, 0xe3, 0xfb, 0x8b, 0x43, 0x1f, 0x86, 0x12,
+    0x51, 0x84, 0xee, 0x15, 0x8b, 0x4f, 0xad, 0x2f, 0x5d, 0x49, 0x69, 0xff,
+    0x6f, 0xcb, 0x85, 0xf0, 0x1c, 0xae, 0xb4, 0xfe, 0xbb, 0x59, 0x82, 0x41,
+    0xad, 0x3f, 0xf3, 0x0d, 0x69, 0x80, 0x6f, 0xde, 0xe9, 0x47, 0xa8, 0xb5,
+    0xc4, 0x13, 0x99, 0x4f, 0xed, 0x9f, 0xb5, 0xcb, 0x8c, 0xb4, 0xe2, 0xda,
+    0xeb, 0x4b, 0x58, 0x7a, 0x04, 0x69, 0x3f, 0xfe, 0xcf, 0x7c, 0x51, 0x65,
+    0xda, 0xcc, 0x12, 0x0d, 0x69, 0xb2, 0xd5, 0xa1, 0x29, 0xfa, 0xe1, 0x37,
+    0xd8, 0xe9, 0x42, 0x50, 0x94, 0x25, 0x09, 0x43, 0xcf, 0x7c, 0x82, 0x80,
+    0x5b, 0xb8, 0x57, 0x90, 0x53, 0xa8, 0x54, 0xda, 0xc4, 0xa7, 0xea, 0xae,
+    0x38, 0xc7, 0x4b, 0xc1, 0x6b, 0x27, 0x41, 0x28, 0x4a, 0x12, 0x87, 0x96,
+    0x84, 0x15, 0x09, 0x42, 0x50, 0x94, 0x25, 0x09, 0x42, 0x51, 0x61, 0xbc,
+    0xf4, 0x28, 0x82, 0x80, 0x15, 0x70, 0xa7, 0x61, 0x50, 0x94, 0x25, 0x0f,
+    0x2d, 0x2e, 0x15, 0x09, 0x42, 0x50, 0x94, 0x25, 0x0f, 0x35, 0x00, 0x0a,
+    0xe0, 0x53, 0xa8, 0x54, 0x25, 0x09, 0x42, 0x50, 0x94, 0x58, 0x6a, 0x03,
+    0x0a, 0xf8, 0x56, 0x82, 0xa4, 0x1a, 0x50, 0x94, 0x25, 0x09, 0x42, 0x51,
+    0xe9, 0xa8, 0xd8, 0x28, 0x01, 0x5b, 0x85, 0x42, 0x50, 0x94, 0x25, 0x3e,
+    0xd3, 0x02, 0xf8, 0x94, 0x25, 0x0f, 0x3c, 0xee, 0x85, 0x68, 0x2a, 0x82,
+    0x80, 0x4d, 0x2e, 0xa5, 0x09, 0x42, 0x50, 0x94, 0x25, 0x0f, 0x35, 0x1b,
+    0x05, 0x10, 0x53, 0xb0, 0xa8, 0x4a, 0x12, 0x84, 0xa1, 0x28, 0x79, 0xa8,
+    0xf4, 0x2b, 0x41, 0x42, 0x15, 0x2d, 0x25, 0x09, 0x42, 0x52, 0x7a, 0x50,
+    0x96, 0xc5, 0x84, 0x25, 0x09, 0x42, 0x50, 0x94, 0x58, 0x7c, 0xcf, 0x0a,
+    0x0c, 0x6b, 0xa3, 0x4e, 0x05, 0x00, 0x2b, 0x81, 0x52, 0xc4, 0xa1, 0x28,
+    0x4a, 0x4f, 0x4a, 0x12, 0xd8, 0xb0, 0x84, 0xa1, 0x28, 0x63, 0xd2, 0x78,
+    0x51, 0x0d, 0x7c, 0x68, 0xe1, 0x50, 0x94, 0x25, 0x09, 0x42, 0x50, 0x94,
+    0x31, 0xb2, 0xd8, 0x2b, 0xe1, 0x47, 0x0a, 0x10, 0xa8, 0x4a, 0x12, 0x84,
+    0xa3, 0xe5, 0xf5, 0xc2, 0xb8, 0x15, 0x09, 0x42, 0x50, 0x94, 0x1c, 0xbe,
+    0x10, 0xae, 0x05, 0x48, 0xe9, 0x42, 0x50, 0x94, 0x00, 0xb4, 0xdc, 0x2a,
+    0x12, 0x84, 0xa1, 0x28, 0x4a, 0x18, 0xd4, 0x38, 0x15, 0xa0, 0xad, 0xc2,
+    0xa1, 0x97, 0xea, 0x6c, 0x71, 0x79, 0xef, 0xa5, 0x38, 0xb7, 0xd6, 0xcd,
+    0x92, 0x09, 0x9b, 0xce, 0x7a, 0xe1, 0xcf, 0xe1, 0x18, 0x74, 0x8d, 0x1c,
+    0x53, 0x28, 0x0e, 0xc5, 0xda, 0xec, 0xdc, 0x61, 0xdd, 0xe5, 0xdb, 0x2d,
+    0xa9, 0x7e, 0x45, 0x81, 0x12, 0x3a, 0xa4, 0xcf, 0x9e, 0x2d, 0x97, 0x48,
+    0xc9, 0xab, 0xce, 0x26, 0x3a, 0x53, 0xc5, 0xda, 0xfa, 0xd3, 0x8b, 0xdc,
+    0x5a, 0x46, 0xd9, 0x11, 0xbf, 0x38, 0xd0, 0xd7, 0x08, 0x24, 0xce, 0xd9,
+    0x48, 0xc1, 0x52, 0x80, 0xa7, 0xf6, 0x8a, 0xdf, 0x7e, 0xd6, 0x2d, 0x1b,
+    0x1f, 0x6b, 0x87, 0x13, 0x01, 0x96, 0x9f, 0xbf, 0x42, 0x13, 0x01, 0x69,
+    0xab, 0xeb, 0x48, 0x96, 0xe9, 0x6b, 0x2e, 0xad, 0x23, 0xad, 0x37, 0x91,
+    0x89, 0x12, 0x9f, 0x15, 0xd2, 0x0d, 0x0f, 0x04, 0x21, 0x3f, 0xda, 0x63,
+    0xe3, 0x8c, 0x36, 0x2d, 0x18, 0x89, 0x00, 0x2c, 0x4f, 0xc7, 0x2f, 0x2a,
+    0x10, 0x2d, 0x3f, 0xb6, 0x06, 0x0e, 0x6d, 0x6a, 0xd3, 0xff, 0xcd, 0xe2,
+    0xbf, 0xfa, 0x03, 0x1a, 0xdb, 0x6d, 0x5a, 0x19, 0x16, 0xb4, 0x5d, 0xb9,
+    0xa4, 0x58, 0xd8, 0x75, 0xbe, 0x32, 0x8f, 0x61, 0x3f, 0xd2, 0xcd, 0x87,
+    0x0a, 0x1c, 0x0e, 0x42, 0x00, 0xe4, 0xd5, 0x4b, 0x08, 0x18, 0xd2, 0x39,
+    0x0d, 0x79, 0xf1, 0xc8, 0x7a, 0xcb, 0x4f, 0xe6, 0x1e, 0x50, 0xef, 0x8b,
+    0x4f, 0xcf, 0x12, 0x7e, 0xb1, 0x69, 0xf5, 0x85, 0xf6, 0x7a, 0xd1, 0xf3,
+    0xd2, 0x12, 0xb9, 0xff, 0x69, 0xbe, 0x01, 0xcb, 0x68, 0x0b, 0x4e, 0xcc,
+    0x71, 0x69, 0x66, 0x8f, 0x60, 0x07, 0xb3, 0xf6, 0xff, 0xdc, 0xf4, 0x2b,
+    0x4f, 0x1d, 0xb2, 0xc5, 0xa7, 0xd8, 0xec, 0x5b, 0x65, 0xa0, 0x8f, 0x26,
+    0xe4, 0x33, 0xcc, 0x3c, 0xea, 0xd2, 0x6c, 0x4d, 0x60, 0x5e, 0x6e, 0x4f,
+    0xc7, 0x7f, 0x22, 0x19, 0xed, 0xeb, 0xef, 0x5a, 0x79, 0xb7, 0x74, 0x2e,
+    0x94, 0xf9, 0xe6, 0xb6, 0xdb, 0x56, 0x81, 0x3d, 0x2d, 0xc9, 0xe7, 0xab,
+    0x87, 0xe2, 0xd1, 0xea, 0x2d, 0xf1, 0xd0, 0x04, 0x50, 0xca, 0xcd, 0xce,
+    0x4a, 0x08, 0xf6, 0xb7, 0x8c, 0x22, 0x7d, 0xb8, 0x30, 0x7a, 0xb4, 0xfe,
+    0xe6, 0x04, 0xce, 0x15, 0xd6, 0x8f, 0x9e, 0xd3, 0xb2, 0x79, 0xfe, 0xb0,
+    0x71, 0xfa, 0xc0, 0x5d, 0x69, 0xef, 0x7d, 0x67, 0x6b, 0x4f, 0xff, 0x69,
+    0x8f, 0xd6, 0xe6, 0x7f, 0xb5, 0x5d, 0x54, 0x5f, 0x13, 0xab, 0xf6, 0x2a,
+    0x2f, 0xf8, 0x64, 0x41, 0xf5, 0x6a, 0x7f, 0xce, 0xa5, 0x83, 0xe8, 0x65,
+    0x9f, 0x5a, 0x75, 0x6c, 0xea, 0xb4, 0xfd, 0x5c, 0xbb, 0x65, 0x8b, 0x43,
+    0xb3, 0xcb, 0xb4, 0x7e, 0x71, 0x68, 0x96, 0x9f, 0xf9, 0xdf, 0x03, 0x61,
+    0x7e, 0x68, 0x8e, 0xb4, 0xfd, 0x96, 0x85, 0xaa, 0xe2, 0xd3, 0xff, 0xee,
+    0x04, 0xf6, 0x76, 0x42, 0x0f, 0x93, 0x1f, 0x12, 0x9e, 0xd0, 0x5b, 0x71,
+    0x68, 0xb1, 0x14, 0xf8, 0x5d, 0xd5, 0x59, 0xb9, 0x8b, 0x4f, 0xbe, 0x56,
+    0x51, 0xd5, 0x30, 0x9c, 0xf7, 0x37, 0x6a, 0x54, 0xc2, 0x73, 0x01, 0x95,
+    0x40, 0x9c, 0xfe, 0x1a, 0x0f, 0x8d, 0xf0, 0x2a, 0x81, 0x39, 0xfd, 0x7c,
+    0xd1, 0x5f, 0x7b, 0x15, 0x30, 0x9c, 0xd9, 0xea, 0xa6, 0x13, 0x9a, 0xdb,
+    0x57, 0x30, 0x9c, 0x62, 0x69, 0x9b, 0x1a, 0x11, 0x71, 0xc8, 0xae, 0x7e,
+    0xed, 0x06, 0xd2, 0x39, 0x71, 0x33, 0x09, 0x8c, 0x7c, 0xf2, 0x2d, 0x93,
+    0xf7, 0xb6, 0x3c, 0x88, 0x25, 0x6b, 0xd4, 0x35, 0x78, 0x76, 0x72, 0x51,
+    0x3c, 0xe1, 0x01, 0xd6, 0x9f, 0xe6, 0xdf, 0x82, 0x4e, 0xc2, 0x3a, 0xd3,
+    0x87, 0x2f, 0x87, 0xad, 0xf1, 0xb8, 0x65, 0xde, 0x0f, 0x4e, 0x3b, 0x0a,
+    0x8f, 0x91, 0x6a, 0x11, 0x7c, 0x97, 0x06, 0xee, 0x13, 0xf3, 0xef, 0x73,
+    0xe4, 0x1a, 0xd3, 0xf6, 0x98, 0x21, 0xd8, 0x0b, 0x4f, 0xcd, 0xcc, 0x70,
+    0x8e, 0xb4, 0xf8, 0x70, 0x2c, 0x71, 0x69, 0xfe, 0xd5, 0x1f, 0x44, 0xdb,
+    0x3d, 0x69, 0xf8, 0x21, 0x7f, 0x95, 0x58, 0xb4, 0x11, 0xf4, 0x80, 0xe2,
+    0x2c, 0x46, 0xe8, 0x96, 0x72, 0x12, 0x13, 0xfe, 0xca, 0xd3, 0x84, 0x3a,
+    0xcd, 0x96, 0x86, 0x64, 0x14, 0x64, 0xf0, 0x21, 0x3f, 0xf9, 0xca, 0x2a,
+    0x30, 0xb1, 0x35, 0x9f, 0xfc, 0x7e, 0x5c, 0x87, 0x67, 0xf0, 0xae, 0x4b,
+    0x4f, 0xff, 0xe1, 0xcd, 0xb2, 0xf4, 0x3a, 0x26, 0xe6, 0xb7, 0xa3, 0xad,
+    0x3f, 0x0e, 0xe7, 0x2d, 0x32, 0xd3, 0xff, 0xea, 0x1e, 0x55, 0x68, 0x5b,
+    0x97, 0xb7, 0x34, 0xb4, 0xff, 0xac, 0xa1, 0xbf, 0x34, 0x4d, 0x75, 0xa7,
+    0x7e, 0xf7, 0x5c, 0x40, 0x73, 0xff, 0xbe, 0xda, 0x6f, 0x19, 0xfe, 0xd5,
+    0x75, 0x51, 0x01, 0x98, 0xd4, 0xc5, 0x23, 0x30, 0x26, 0x68, 0x02, 0x68,
+    0x3c, 0x8c, 0x7a, 0x31, 0x39, 0x72, 0x8d, 0xb6, 0x7d, 0xc6, 0x07, 0x96,
+    0x2d, 0x3c, 0x3c, 0x06, 0xeb, 0x4f, 0xff, 0xcf, 0xcb, 0x97, 0xfe, 0x47,
+    0x0b, 0x9a, 0xcb, 0x78, 0xb4, 0x61, 0xff, 0x51, 0x14, 0xff, 0xff, 0x53,
+    0xf0, 0x5c, 0x7e, 0x6c, 0xf0, 0x98, 0x06, 0xb6, 0xdb, 0x52, 0x86, 0x4c,
+    0xcb, 0x50, 0xa7, 0x12, 0x09, 0xfc, 0xd9, 0xfe, 0xd5, 0x75, 0x51, 0x04,
+    0xcf, 0xf3, 0x73, 0x3f, 0xda, 0xae, 0xaa, 0x2f, 0x99, 0xf1, 0x3b, 0x1c,
+    0xbf, 0xa8, 0x80, 0xe9, 0xec, 0xfe, 0xdf, 0x5e, 0x5a, 0x2e, 0xe9, 0x69,
+    0xff, 0x59, 0x5b, 0x79, 0x67, 0x06, 0x9d, 0xad, 0x3f, 0xec, 0xf5, 0x9e,
+    0xda, 0xc0, 0x52, 0xd3, 0xad, 0xb6, 0xd4, 0xa7, 0xda, 0xce, 0xd7, 0xd2,
+    0x31, 0x7f, 0x3f, 0xff, 0x9c, 0xfb, 0x3b, 0xf0, 0x1d, 0x78, 0x09, 0xcb,
+    0xf8, 0xcd, 0xb8, 0xb4, 0x71, 0x14, 0x61, 0x1a, 0xc5, 0x89, 0xb2, 0x9d,
+    0x0c, 0x61, 0xfd, 0x3f, 0xf5, 0x87, 0x6d, 0xae, 0x39, 0x60, 0x4f, 0x5a,
+    0x7e, 0xf3, 0xe3, 0xf4, 0xdf, 0x5a, 0x7f, 0xf6, 0xc3, 0xe3, 0xba, 0x20,
+    0x01, 0xbe, 0xf5, 0xa1, 0x8f, 0xf0, 0x23, 0x09, 0xff, 0x6f, 0x76, 0xc1,
+    0xe3, 0xa3, 0x6f, 0x56, 0x9d, 0x6d, 0xb6, 0xa5, 0x3f, 0x82, 0x03, 0x0e,
+    0x6d, 0xc4, 0x8c, 0x5f, 0xcf, 0xff, 0xce, 0x87, 0x8e, 0xe8, 0xbd, 0xf7,
+    0x2d, 0xf1, 0xcb, 0xe5, 0x8b, 0x4a, 0xb4, 0x8a, 0xad, 0xd0, 0xa1, 0xeb,
+    0x85, 0xde, 0xa0, 0x94, 0x73, 0x9f, 0x36, 0x3c, 0x30, 0xc4, 0x8b, 0x91,
+    0x86, 0xcf, 0xfb, 0x45, 0x6f, 0xba, 0xce, 0x79, 0x62, 0xd3, 0xed, 0x67,
+    0x1c, 0x7a, 0xd3, 0xfe, 0xce, 0x37, 0xef, 0xa6, 0x1e, 0x2e, 0x20, 0x89,
+    0xfc, 0xd9, 0xfe, 0xd5, 0x75, 0x51, 0x04, 0x18, 0xf2, 0x67, 0xea, 0xd7,
+    0xfe, 0x56, 0xad, 0x3f, 0x79, 0xfd, 0xb6, 0x85, 0xeb, 0x40, 0x9e, 0xfe,
+    0xe5, 0xb1, 0xa4, 0xd0, 0x00, 0xe2, 0x30, 0xad, 0x9f, 0xe6, 0x1e, 0x05,
+    0x9a, 0xad, 0x96, 0x9f, 0xfe, 0xab, 0x79, 0x9a, 0x62, 0xe0, 0xe6, 0x89,
+    0x68, 0x7a, 0x21, 0x04, 0xe2, 0x7f, 0xb4, 0xdf, 0xe0, 0xb6, 0x6c, 0xb4,
+    0xff, 0xff, 0x15, 0x8c, 0xfb, 0xff, 0xe5, 0xb3, 0x8f, 0xaf, 0x9f, 0x36,
+    0x5a, 0x7b, 0x9a, 0xc1, 0xf5, 0x14, 0x1a, 0x37, 0x9f, 0x82, 0x3d, 0xed,
+    0xad, 0x96, 0x86, 0x3e, 0xae, 0x1d, 0x4f, 0x13, 0x85, 0x6a, 0xd3, 0xff,
+    0xed, 0xec, 0x16, 0xcd, 0x19, 0xb9, 0x9a, 0xdf, 0xeb, 0x4f, 0xb3, 0x97,
+    0x0a, 0xfe, 0x9f, 0xc0, 0x44, 0x53, 0xf7, 0x74, 0x5e, 0x44, 0x1a, 0xd3,
+    0xf1, 0xdb, 0xcf, 0xac, 0xb1, 0x69, 0xff, 0xff, 0xe2, 0xe5, 0x1e, 0x84,
+    0x0f, 0x2b, 0xee, 0x7d, 0x13, 0x73, 0x5b, 0xd1, 0xd6, 0x8f, 0x51, 0xba,
+    0x46, 0x14, 0x63, 0x3f, 0x08, 0x30, 0xed, 0xa5, 0xa7, 0x7c, 0x2b, 0xad,
+    0x3f, 0xea, 0x1e, 0xe7, 0xfb, 0x55, 0xd5, 0x44, 0x23, 0x0c, 0x7c, 0xbb,
+    0x8e, 0xcf, 0xfe, 0x6c, 0xd8, 0x2e, 0x10, 0xee, 0xfc, 0xba, 0xd3, 0xff,
+    0xd5, 0x56, 0x05, 0x9a, 0xa3, 0x9a, 0xdb, 0x6d, 0x5a, 0x3d, 0x44, 0xeb,
+    0x89, 0x53, 0xfd, 0xeb, 0x5e, 0xed, 0x66, 0x5a, 0xb4, 0x61, 0xef, 0xd1,
+    0x2c, 0xeb, 0x6d, 0xb5, 0x29, 0xfe, 0xc0, 0x57, 0xf1, 0xde, 0x7a, 0x91,
+    0x8b, 0xf9, 0xad, 0xb5, 0x29, 0xd6, 0xdb, 0x6a, 0x53, 0xf5, 0x59, 0xee,
+    0x9a, 0xe9, 0x18, 0xbf, 0x81, 0x45, 0xe5, 0xa9, 0x1e, 0x46, 0xf3, 0xee,
+    0x97, 0xa1, 0x1d, 0x23, 0x1b, 0x39, 0xd6, 0xdb, 0x6a, 0x53, 0xae, 0xdf,
+    0x48, 0xc5, 0xfc, 0x80, 0x48, 0x82, 0xe2, 0xc4, 0xfd, 0xe8, 0xb3, 0xeb,
+    0xab, 0x4f, 0xe0, 0xbf, 0xe9, 0x3b, 0x6f, 0x56, 0x9f, 0xfb, 0x54, 0xf1,
+    0xa0, 0xdc, 0x12, 0x71, 0x69, 0xfb, 0x58, 0xef, 0x39, 0x75, 0xa3, 0xa7,
+    0xe5, 0xc4, 0x48, 0xfa, 0x3d, 0xf4, 0x58, 0x30, 0xa9, 0x86, 0x4d, 0x4f,
+    0x23, 0x1f, 0x9d, 0xb7, 0x9a, 0x1d, 0x32, 0xd3, 0xff, 0xb3, 0xcf, 0x42,
+    0x0c, 0xeb, 0x69, 0x8e, 0xb4, 0xfd, 0xd2, 0xbe, 0x89, 0xc4, 0xa7, 0xf0,
+    0xe6, 0xcf, 0xb9, 0x06, 0xb4, 0xf5, 0x57, 0xc3, 0x5a, 0x3c, 0x1e, 0xa5,
+    0x86, 0x73, 0xcd, 0x47, 0x76, 0x94, 0xeb, 0x6d, 0xb5, 0x29, 0xff, 0xe0,
+    0x98, 0x19, 0x83, 0xcd, 0x13, 0x0d, 0x89, 0x18, 0xbf, 0x96, 0x75, 0x13,
+    0xa7, 0x41, 0x86, 0x4f, 0x81, 0xe5, 0x7b, 0x25, 0x14, 0x20, 0x75, 0x0c,
+    0x29, 0xff, 0xf1, 0xf3, 0xac, 0x35, 0xcb, 0xfb, 0xbd, 0x7d, 0xeb, 0x4f,
+    0x9b, 0x97, 0x77, 0x8b, 0x43, 0x1f, 0xef, 0x15, 0x67, 0xff, 0x89, 0xde,
+    0x0f, 0xa3, 0x56, 0xe0, 0xd0, 0x6b, 0x4f, 0xff, 0xf7, 0xba, 0x2b, 0x71,
+    0xd4, 0x8f, 0x5c, 0xbe, 0xef, 0xcd, 0xb8, 0xb4, 0x62, 0x2e, 0xe9, 0x3e,
+    0x19, 0xb6, 0xc6, 0xb1, 0x25, 0xf1, 0xfa, 0xfb, 0x1a, 0xd6, 0x4e, 0x71,
+    0x75, 0xdc, 0xa3, 0x82, 0x72, 0x17, 0x3f, 0x8c, 0xb0, 0xf0, 0x99, 0xd4,
+    0x63, 0xf4, 0x5e, 0x08, 0x48, 0x0c, 0x63, 0xd7, 0x96, 0x07, 0xc9, 0x41,
+    0x1b, 0xc3, 0x46, 0xd8, 0x6e, 0x4e, 0x76, 0xc2, 0xb4, 0xff, 0xf6, 0x09,
+    0xb8, 0x19, 0x6d, 0x7d, 0x60, 0x29, 0x69, 0x6c, 0xf3, 0xeb, 0xf4, 0x72,
+    0x7e, 0xd3, 0x85, 0xa2, 0x3a, 0xd3, 0xfe, 0x16, 0xe6, 0xa8, 0x15, 0xb5,
+    0xd6, 0x9c, 0xe6, 0xff, 0x5a, 0x7f, 0xd4, 0x5f, 0xcd, 0x8d, 0x6d, 0xb6,
+    0xad, 0x14, 0x7b, 0xf7, 0x1e, 0x9f, 0xfe, 0x16, 0x7f, 0x80, 0xb8, 0x43,
+    0xbb, 0xf2, 0xeb, 0x46, 0x26, 0x77, 0xd2, 0xdd, 0x42, 0x70, 0x04, 0x33,
+    0xf9, 0x85, 0xe3, 0x98, 0x05, 0xa7, 0xf3, 0xf3, 0xf7, 0x16, 0xb1, 0x69,
+    0xff, 0xec, 0xe3, 0x0f, 0x73, 0x9a, 0x2f, 0x83, 0xab, 0x4f, 0xe6, 0x3d,
+    0xef, 0x43, 0x62, 0xd3, 0xea, 0xbe, 0x0f, 0x16, 0x9d, 0xca, 0xf3, 0xad,
+    0x3f, 0xb9, 0xa2, 0x30, 0x28, 0x96, 0x8f, 0x51, 0xe9, 0xa4, 0xd1, 0x32,
+    0xe1, 0x2e, 0xe3, 0xf3, 0xfb, 0x70, 0x36, 0x8a, 0x9e, 0xb4, 0xff, 0x77,
+    0x38, 0xce, 0xc8, 0x40, 0xb4, 0xff, 0xfd, 0xbb, 0x72, 0xf8, 0x3e, 0x3e,
+    0xc5, 0xe8, 0xd3, 0xd6, 0x82, 0x44, 0x98, 0x9c, 0x4f, 0xff, 0xc3, 0x97,
+    0xdf, 0xc6, 0x6a, 0x87, 0x1c, 0x7b, 0x6c, 0xb4, 0xff, 0xb2, 0xfb, 0xe7,
+    0xfb, 0x55, 0xd5, 0x44, 0x0d, 0x3d, 0xcd, 0x53, 0xfc, 0x22, 0x98, 0x57,
+    0x21, 0x93, 0x02, 0xc8, 0x63, 0xcf, 0xfa, 0xbf, 0x82, 0x0b, 0xf2, 0xfb,
+    0x2d, 0x3f, 0xff, 0xff, 0x67, 0x35, 0x42, 0x1f, 0x8b, 0xfe, 0xab, 0x8e,
+    0xf3, 0xdf, 0x18, 0x7a, 0x1d, 0x9e, 0xb8, 0x82, 0xe7, 0xfd, 0x54, 0x1f,
+    0x6b, 0xdf, 0x01, 0xda, 0xb8, 0x82, 0xe7, 0xfe, 0xd1, 0x68, 0x98, 0x79,
+    0xe0, 0x3b, 0x57, 0x10, 0x5c, 0xfe, 0x62, 0x1e, 0x78, 0x0e, 0xd5, 0xc4,
+    0x17, 0x3f, 0x1f, 0x3d, 0xf0, 0x1d, 0xab, 0x88, 0x2e, 0x7f, 0xff, 0xa8,
+    0x44, 0x8f, 0xe2, 0xfd, 0xd1, 0x7c, 0x83, 0xb3, 0x2d, 0x5c, 0x41, 0x73,
+    0x6d, 0xe3, 0xd4, 0xe6, 0x7a, 0xa1, 0xf5, 0x3a, 0x42, 0x13, 0xf8, 0x65,
+    0x59, 0x7a, 0x3e, 0x19, 0x46, 0x93, 0xf8, 0x98, 0x3b, 0xf2, 0xfb, 0x2d,
+    0x3d, 0x5f, 0x05, 0x2d, 0x3f, 0xf6, 0x8b, 0x44, 0xc3, 0xcf, 0x01, 0xda,
+    0xb8, 0x82, 0xe7, 0xf9, 0xcb, 0xf4, 0xac, 0xf0, 0x1d, 0xab, 0x88, 0x2e,
+    0x7d, 0xcb, 0xb1, 0xfc, 0x75, 0x13, 0xe1, 0x29, 0xcf, 0xfe, 0xf1, 0xdd,
+    0x17, 0x5b, 0x97, 0xf0, 0x1d, 0xab, 0x88, 0x2e, 0x7f, 0xff, 0xe1, 0x12,
+    0x3f, 0x8d, 0xf3, 0xc5, 0xfb, 0xa2, 0xf9, 0x07, 0x66, 0x5a, 0xb8, 0x82,
+    0xe3, 0x13, 0x24, 0xd9, 0x0e, 0x97, 0x67, 0xfb, 0x45, 0xf2, 0x0e, 0xcc,
+    0xb5, 0x71, 0x05, 0xcf, 0xff, 0x55, 0x6c, 0xfb, 0xe8, 0xbd, 0xf5, 0xbe,
+    0xc9, 0x4f, 0xfb, 0x1e, 0xfd, 0xef, 0xf1, 0xb3, 0xc9, 0x71, 0x05, 0xc7,
+    0x51, 0xcf, 0xf4, 0x7b, 0xa8, 0x4f, 0xfb, 0xa5, 0xfe, 0x50, 0x2f, 0xe2,
+    0xd5, 0xc4, 0x17, 0x3f, 0x68, 0xb5, 0xa2, 0x02, 0xa0, 0x0b, 0x9f, 0x60,
+    0x3c, 0x07, 0x6a, 0xe2, 0x0b, 0x9b, 0x39, 0xd3, 0xf1, 0xd1, 0xcc, 0x6c,
+    0x8e, 0xcb, 0xc2, 0xfe, 0x7e, 0x3e, 0x7b, 0xe0, 0x3b, 0x57, 0x10, 0x5c,
+    0xff, 0xbb, 0xa2, 0xf9, 0x07, 0x66, 0x5a, 0xb8, 0x82, 0xe6, 0xcf, 0x14,
+    0x88, 0xcb, 0x9f, 0xcf, 0xed, 0xc8, 0xf4, 0x3b, 0x3d, 0x71, 0x05, 0xcf,
+    0xfb, 0x09, 0xde, 0x0e, 0x7b, 0x4f, 0x5c, 0x41, 0x67, 0x3c, 0x08, 0xf5,
+    0x78, 0x03, 0xe6, 0xe0, 0x34, 0x18, 0xf8, 0xef, 0x18, 0xbf, 0x23, 0x1c,
+    0xde, 0x16, 0xb6, 0xb7, 0xcf, 0x81, 0x70, 0x01, 0x95, 0x10, 0x59, 0x91,
+    0x1b, 0x3f, 0xec, 0x7e, 0xa9, 0xee, 0xae, 0xbb, 0xd8, 0xb4, 0xe3, 0xb3,
+    0xd6, 0x9f, 0x67, 0xba, 0xde, 0xd5, 0xa5, 0xfa, 0x3c, 0x51, 0x1b, 0x9c,
+    0x5e, 0x58, 0xb4, 0xea, 0xf8, 0x16, 0x96, 0xcc, 0x6e, 0x38, 0x3b, 0x3f,
+    0x65, 0x95, 0xa6, 0xf3, 0xad, 0x04, 0x8b, 0x5a, 0x5e, 0x12, 0x79, 0xd5,
+    0xfb, 0x16, 0x98, 0x0c, 0xb4, 0xf1, 0x31, 0xf1, 0x68, 0x5a, 0x7e, 0xa0,
+    0xf8, 0xdf, 0x02, 0xd1, 0xe9, 0xb7, 0x10, 0xa9, 0xff, 0xfc, 0xc5, 0xe8,
+    0xb6, 0xfd, 0xe6, 0x77, 0xd6, 0xe8, 0x47, 0x5a, 0x60, 0x32, 0xd3, 0x30,
+    0x6b, 0x4f, 0xf6, 0x72, 0xec, 0x70, 0xb1, 0xc5, 0xa7, 0xf5, 0xf3, 0x45,
+    0x7d, 0xec, 0x5a, 0x6b, 0x6d, 0x4a, 0x7f, 0x87, 0x1d, 0xd3, 0xee, 0x5e,
+    0xad, 0x1d, 0x4f, 0xcc, 0x87, 0x3e, 0x2a, 0x75, 0x6d, 0x10, 0x81, 0x89,
+    0xd0, 0x15, 0x11, 0x6b, 0x9c, 0xda, 0x68, 0x10, 0xc4, 0xeb, 0x6d, 0xb5,
+    0x29, 0x1d, 0x23, 0x17, 0xf3, 0xea, 0x72, 0xab, 0xe9, 0x19, 0x1b, 0x81,
+    0x42, 0xf6, 0x7f, 0x6d, 0xc6, 0xcd, 0x65, 0x8b, 0x43, 0x36, 0x71, 0xb9,
+    0x1b, 0x28, 0x68, 0x5b, 0x17, 0x94, 0x64, 0xdf, 0x4b, 0xd4, 0x78, 0xf5,
+    0x49, 0x7f, 0x14, 0x6b, 0xc6, 0x81, 0xbc, 0xaf, 0xc7, 0x69, 0x73, 0xd9,
+    0xd6, 0xe2, 0xd3, 0xd8, 0x7c, 0xe2, 0xd3, 0xff, 0x6b, 0x7b, 0x37, 0x36,
+    0x8b, 0xb5, 0xf5, 0xa7, 0xaa, 0xc0, 0x9e, 0xb4, 0x32, 0x2a, 0xb6, 0x20,
+    0xe1, 0x03, 0xaa, 0x3c, 0xfc, 0x3b, 0xfa, 0x7a, 0x7a, 0xd3, 0xff, 0xdb,
+    0x3c, 0x5b, 0xc1, 0xc5, 0x83, 0xe1, 0x58, 0xb4, 0xfe, 0xbf, 0x1d, 0xe0,
+    0xe7, 0xab, 0x43, 0x22, 0xd0, 0x8b, 0xa9, 0x4a, 0x75, 0x78, 0x09, 0x69,
+    0xff, 0xe1, 0x7f, 0x8c, 0xf4, 0xaf, 0xb7, 0x37, 0xab, 0xad, 0x3e, 0xf1,
+    0xa6, 0xfb, 0xd6, 0x8f, 0x08, 0x9a, 0xc1, 0xeb, 0x54, 0xe7, 0xeb, 0x1c,
+    0x7f, 0x99, 0xfb, 0x16, 0x99, 0xf6, 0x2d, 0x3e, 0xd6, 0x13, 0xee, 0xb4,
+    0xff, 0xf6, 0x98, 0x23, 0x70, 0x68, 0x17, 0xbb, 0x01, 0x29, 0xfc, 0x0c,
+    0xff, 0x6a, 0xba, 0xb8, 0x81, 0x27, 0x70, 0xac, 0x5a, 0x1e, 0x8c, 0xff,
+    0x49, 0xbe, 0x9e, 0x73, 0xe9, 0x9d, 0x5e, 0xb4, 0xf7, 0x2c, 0xa1, 0x5a,
+    0x7b, 0x6b, 0xd7, 0x16, 0x8b, 0x0f, 0x73, 0x06, 0x74, 0x43, 0x3f, 0x86,
+    0xad, 0xbd, 0xd8, 0x0b, 0x4e, 0xb6, 0xdb, 0x57, 0x57, 0xd4, 0xea, 0x1f,
+    0x53, 0x57, 0xd0, 0xc6, 0xb2, 0x3d, 0x44, 0xb8, 0x17, 0xa7, 0xff, 0xd8,
+    0x24, 0x19, 0xaf, 0xcd, 0x6f, 0x4f, 0x7b, 0x2d, 0x16, 0x1f, 0xc8, 0x64,
+    0x93, 0xd9, 0xed, 0x3d, 0x69, 0xf5, 0x08, 0x91, 0xd6, 0x8f, 0x36, 0xad,
+    0x79, 0xe6, 0xd9, 0x0e, 0x62, 0x84, 0xd5, 0x46, 0x7e, 0x02, 0x41, 0x21,
+    0x9f, 0x17, 0x1b, 0x97, 0x4a, 0x7b, 0xad, 0xcb, 0xa5, 0x35, 0xb6, 0xa5,
+    0x0f, 0x3d, 0xfc, 0x26, 0xb4, 0x86, 0x6c, 0xb5, 0x23, 0x1a, 0xf9, 0xff,
+    0xf6, 0x71, 0xbb, 0x98, 0x3c, 0xd1, 0x30, 0xd8, 0xb4, 0x00, 0xfd, 0xed,
+    0x26, 0x9f, 0xff, 0xcc, 0xec, 0x84, 0x1e, 0x39, 0x7c, 0xee, 0xa8, 0x79,
+    0x75, 0xa7, 0xcf, 0xd6, 0xed, 0xf5, 0xa7, 0xfe, 0xa3, 0xfd, 0x9d, 0x93,
+    0xb1, 0xde, 0xeb, 0x46, 0xc7, 0xdf, 0xa2, 0x89, 0xff, 0x66, 0x39, 0xfc,
+    0xdc, 0xed, 0x75, 0xa1, 0xc3, 0xe1, 0xf9, 0x1c, 0xe7, 0xd7, 0xd6, 0x9f,
+    0x3f, 0x39, 0x9e, 0xa5, 0x22, 0x70, 0xf0, 0xb8, 0x37, 0x3d, 0xc0, 0xf3,
+    0x4b, 0x4c, 0x06, 0x5a, 0x60, 0x32, 0xd3, 0xed, 0xdf, 0x82, 0x62, 0x35,
+    0x40, 0x15, 0x86, 0x44, 0x48, 0xa6, 0x4f, 0xff, 0x60, 0xd6, 0xd7, 0x0c,
+    0x3a, 0xf1, 0xd3, 0x81, 0x69, 0xff, 0xb9, 0xac, 0x38, 0x4c, 0x0c, 0xdb,
+    0x8b, 0x4f, 0xed, 0xba, 0x5f, 0x09, 0xbd, 0x5a, 0x78, 0x07, 0xa7, 0xf8,
+    0x46, 0x6d, 0xd4, 0xf8, 0x8b, 0x3b, 0xcb, 0x36, 0x5a, 0x78, 0x1f, 0x2f,
+    0xad, 0x0c, 0x88, 0x9a, 0x49, 0xb4, 0x7e, 0x7a, 0xfb, 0xed, 0xc5, 0xa7,
+    0x5b, 0x6d, 0xa9, 0x4f, 0xb0, 0x31, 0xde, 0xe9, 0x18, 0xbf, 0x9f, 0x61,
+    0xad, 0xb6, 0xd5, 0xa1, 0x8f, 0x87, 0x86, 0xf3, 0xff, 0x60, 0xe5, 0xfb,
+    0xa2, 0xeb, 0x0a, 0xd3, 0xd6, 0xd0, 0xbd, 0x69, 0xfe, 0xe3, 0x6c, 0x0a,
+    0xd6, 0x89, 0x69, 0x69, 0x68, 0xc3, 0xc8, 0xe9, 0xc4, 0xeb, 0x6d, 0xb5,
+    0x29, 0xf8, 0xa8, 0x3d, 0x17, 0x12, 0x31, 0x7f, 0x3e, 0xc0, 0x66, 0x38,
+    0xb4, 0xaf, 0xe1, 0x14, 0x3f, 0x40, 0x13, 0xb9, 0xb7, 0x7f, 0xa9, 0xa0,
+    0xe4, 0x61, 0x52, 0x2d, 0x27, 0x70, 0x31, 0xc0, 0xcf, 0x57, 0xcb, 0x4b,
+    0x43, 0x2f, 0x6e, 0x3c, 0x8f, 0x23, 0x32, 0x0d, 0x97, 0xf0, 0xb1, 0xd4,
+    0x6e, 0xf4, 0x5f, 0x78, 0x52, 0x72, 0x3d, 0x9f, 0x22, 0xe9, 0xf9, 0x8f,
+    0xff, 0xd7, 0x16, 0x9f, 0xfe, 0x23, 0xec, 0xfb, 0xf8, 0xc1, 0x1c, 0x20,
+    0x2d, 0x1a, 0x3f, 0xbe, 0x16, 0x48, 0xce, 0x94, 0x2b, 0x0a, 0xdd, 0x39,
+    0x4f, 0x9a, 0x50, 0x3c, 0xd6, 0x54, 0xd3, 0x9a, 0xd6, 0x4a, 0x4c, 0x7c,
+    0xae, 0xcf, 0x65, 0xc7, 0xe5, 0x78, 0x10, 0x1c, 0x7a, 0x9d, 0x97, 0x31,
+    0xb4, 0xbd, 0xa2, 0x96, 0x16, 0xe4, 0xfa, 0xa7, 0xe7, 0xa1, 0x0f, 0x2c,
+    0x9f, 0x54, 0x98, 0xaa, 0xac, 0x5d, 0x41, 0x2f, 0xf8, 0x69, 0x19, 0xb7,
+    0xad, 0xcb, 0xb9, 0x5a, 0xa1, 0xef, 0x1b, 0xe3, 0xb8, 0x7a, 0x5b, 0x0c,
+    0x7f, 0x29, 0x72, 0x01, 0x52, 0x16, 0x5d, 0x63, 0x26, 0x83, 0x42, 0xbc,
+    0x7d, 0xf8, 0xac, 0xc0, 0x9f, 0xc6, 0xcf, 0xf6, 0xab, 0xaa, 0x8b, 0x8e,
+    0x7f, 0x1b, 0x3f, 0xda, 0xae, 0xaa, 0x2e, 0xb9, 0xff, 0x8d, 0x4f, 0x36,
+    0x7f, 0xb5, 0x5d, 0x54, 0x4a, 0x30, 0xd0, 0xb2, 0xa4, 0x79, 0xcf, 0xa7,
+    0xc1, 0x9f, 0x74, 0xf8, 0xa1, 0xf5, 0xe7, 0x40, 0xac, 0x57, 0xba, 0x5c,
+    0xe3, 0x87, 0xce, 0xa7, 0xd3, 0xff, 0x8c, 0x7a, 0x79, 0xb3, 0xfd, 0xaa,
+    0xea, 0xa2, 0x5a, 0x9f, 0x7f, 0xb5, 0x5d, 0x54, 0x46, 0xf3, 0xfe, 0xa7,
+    0x9b, 0x3f, 0xda, 0xae, 0xaa, 0x25, 0xf9, 0x1b, 0x0f, 0xf0, 0xe6, 0x73,
+    0xf8, 0xd9, 0xfe, 0xd5, 0x75, 0x51, 0x55, 0xce, 0x17, 0x5b, 0x56, 0x9f,
+    0xe0, 0xf0, 0x73, 0x6e, 0x51, 0xd6, 0x9b, 0xd2, 0x5a, 0x7e, 0xcf, 0xf6,
+    0xab, 0xaa, 0x89, 0x02, 0x3c, 0x1e, 0x71, 0x85, 0xa7, 0xd7, 0xa1, 0xad,
+    0x96, 0x87, 0x9e, 0x56, 0xc4, 0x93, 0xfd, 0x9f, 0xb0, 0xec, 0x2f, 0xba,
+    0xd3, 0xff, 0xb1, 0xcd, 0x51, 0xf9, 0xa6, 0xe6, 0xe7, 0x5a, 0x3d, 0x4e,
+    0x54, 0x31, 0xfa, 0x86, 0x80, 0x91, 0x84, 0x71, 0x3f, 0xf0, 0xe5, 0x86,
+    0xf2, 0x61, 0x1c, 0xe2, 0xd0, 0x64, 0x4a, 0xfd, 0x46, 0x7f, 0x1b, 0x3f,
+    0xda, 0xae, 0xaa, 0x2c, 0x89, 0xfc, 0x6c, 0xff, 0x6a, 0xba, 0xa8, 0xb5,
+    0xe7, 0xf1, 0xb3, 0xfd, 0xaa, 0xea, 0xa2, 0xe4, 0x9f, 0x7f, 0xb5, 0x5d,
+    0x54, 0x5d, 0x93, 0xfe, 0xa7, 0x9b, 0x3f, 0xda, 0xae, 0xaa, 0x28, 0xe9,
+    0x1b, 0x0f, 0xf0, 0xe6, 0x72, 0x7a, 0xd3, 0xef, 0xf6, 0xab, 0xaa, 0x8a,
+    0x56, 0x7f, 0xff, 0xfb, 0x4d, 0x63, 0x68, 0x9d, 0xd7, 0xba, 0x23, 0x77,
+    0x44, 0xe6, 0x9a, 0xcc, 0x5a, 0x67, 0x9b, 0x11, 0x66, 0xe1, 0x9c, 0xc6,
+    0x3e, 0xc8, 0xf2, 0xa8, 0x61, 0xc5, 0x8b, 0xc0, 0xcf, 0x86, 0x8e, 0xc4,
+    0xe5, 0x29, 0xa0, 0xe4, 0x60, 0x3e, 0xb9, 0xf7, 0x21, 0x74, 0xee, 0x31,
+    0x99, 0xfe, 0x79, 0xb3, 0xfd, 0xaa, 0xea, 0xa2, 0x38, 0x9f, 0xe2, 0x36,
+    0x7f, 0xb5, 0x5d, 0x54, 0x56, 0xb2, 0x35, 0x22, 0x1f, 0x88, 0x93, 0xff,
+    0x8c, 0x7a, 0x79, 0xb3, 0xfd, 0xaa, 0xea, 0xa2, 0x5b, 0x9b, 0x36, 0x5a,
+    0x7f, 0x7b, 0x87, 0x3b, 0x15, 0xd6, 0x8d, 0x8f, 0x20, 0x85, 0xa7, 0x6b,
+    0xd7, 0xad, 0x39, 0xe7, 0xea, 0xd3, 0xff, 0xf6, 0xab, 0x60, 0x7b, 0xaa,
+    0x7e, 0x5c, 0x77, 0xa0, 0x96, 0x85, 0x44, 0x37, 0x3f, 0xea, 0x79, 0xb3,
+    0xfd, 0xaa, 0xea, 0xa2, 0x60, 0x9b, 0xb8, 0xb4, 0xdb, 0x81, 0x28, 0x23,
+    0x59, 0xf1, 0x59, 0xfc, 0x39, 0xce, 0x37, 0xee, 0xb4, 0x8c, 0xc9, 0xca,
+    0xd8, 0x45, 0xe8, 0xee, 0x0e, 0x75, 0x74, 0xe2, 0xfc, 0x75, 0x08, 0x82,
+    0x71, 0xf7, 0xfa, 0x53, 0xfe, 0xa7, 0x9b, 0x3f, 0xda, 0xae, 0xaa, 0x26,
+    0x39, 0x18, 0x8f, 0x98, 0xe3, 0xb3, 0xf3, 0x5f, 0xc7, 0x33, 0x4b, 0x4e,
+    0x61, 0xb1, 0x69, 0xff, 0xff, 0xe7, 0xdf, 0x59, 0xbd, 0xb7, 0xd6, 0x77,
+    0x2f, 0xe3, 0x36, 0x7d, 0xc8, 0x49, 0x69, 0xee, 0xd5, 0x75, 0x51, 0x58,
+    0xcf, 0xfb, 0xcb, 0x38, 0x39, 0xbb, 0x0f, 0x16, 0x80, 0xd3, 0x35, 0xe9,
+    0x76, 0xc3, 0x7f, 0x84, 0x07, 0x0b, 0x67, 0xff, 0x30, 0xf0, 0x3d, 0xd8,
+    0x73, 0xad, 0xf5, 0xa7, 0xfb, 0xdd, 0x65, 0xf6, 0x7b, 0x69, 0x69, 0xfe,
+    0x6d, 0x9e, 0xe3, 0xeb, 0xf6, 0x2d, 0x18, 0x7e, 0xa4, 0x75, 0x3f, 0xec,
+    0xf7, 0xe5, 0xbd, 0x80, 0xbb, 0xd6, 0x9f, 0xff, 0xf8, 0x77, 0xb3, 0x3b,
+    0xe0, 0x77, 0x75, 0xf1, 0x9b, 0x3f, 0x37, 0x10, 0x5d, 0x69, 0xfd, 0xe6,
+    0xec, 0x71, 0xfe, 0x67, 0xec, 0x5a, 0x7f, 0xf6, 0x39, 0xaa, 0x3f, 0x34,
+    0xdc, 0xdc, 0xeb, 0x4e, 0xd1, 0x1b, 0x15, 0x3c, 0xf5, 0x4b, 0x50, 0xbd,
+    0x12, 0x0e, 0x20, 0xf9, 0x3e, 0x04, 0x85, 0x30, 0x40, 0x5a, 0x6d, 0xdc,
+    0x5a, 0x7d, 0x43, 0x66, 0xf7, 0x5a, 0x00, 0x7a, 0xe2, 0x2f, 0x71, 0x89,
+    0xd7, 0x0a, 0xeb, 0x4f, 0x6d, 0x63, 0x06, 0xb4, 0x7a, 0x78, 0x3b, 0x8e,
+    0xcf, 0x8f, 0x82, 0xcf, 0x5a, 0x79, 0xbf, 0x42, 0xb4, 0xf5, 0xee, 0xc0,
+    0x5a, 0x18, 0xf9, 0x74, 0x4b, 0x68, 0xfc, 0xf9, 0x9e, 0x0d, 0xde, 0xb4,
+    0xe6, 0x17, 0xad, 0x0e, 0x1e, 0x17, 0x0a, 0x27, 0x68, 0x23, 0xad, 0x3f,
+    0x31, 0xf0, 0x48, 0x35, 0xa5, 0x75, 0xa0, 0x8d, 0xdd, 0x15, 0xcc, 0x06,
+    0x4a, 0x6b, 0x6d, 0x4a, 0x08, 0xd6, 0xda, 0x2b, 0x3f, 0xa9, 0xe3, 0x9c,
+    0xd1, 0x24, 0x63, 0x43, 0x3d, 0xed, 0x6d, 0x75, 0xa7, 0x30, 0xb8, 0xb4,
+    0xc2, 0xcb, 0x43, 0x86, 0xbf, 0xe3, 0x73, 0xff, 0x06, 0x43, 0xdb, 0xb1,
+    0xeb, 0x6e, 0x2d, 0x38, 0x18, 0xea, 0xb4, 0x06, 0x7c, 0x5f, 0x44, 0x90,
+    0x6b, 0x4d, 0xf0, 0xd6, 0x82, 0x35, 0x01, 0x08, 0xcf, 0xfe, 0xe3, 0x3c,
+    0xb9, 0xac, 0xdb, 0x9b, 0xb2, 0xd2, 0xc5, 0xa0, 0x8f, 0x67, 0xc9, 0x2a,
+    0x19, 0x3b, 0xfc, 0x52, 0x28, 0x42, 0x69, 0x2e, 0x9e, 0xa1, 0x69, 0x1d,
+    0x69, 0x6b, 0x85, 0xd3, 0xb0, 0xa9, 0xf8, 0x0d, 0xac, 0x77, 0x4b, 0x4f,
+    0xc5, 0xcc, 0xd5, 0x06, 0xb4, 0xeb, 0x6d, 0xb5, 0x29, 0xff, 0xb3, 0xbe,
+    0xb5, 0x81, 0x66, 0xab, 0x64, 0x8c, 0x5f, 0xcf, 0x17, 0xdd, 0x7d, 0x5a,
+    0x5f, 0x5a, 0x7c, 0x72, 0xe0, 0x29, 0x68, 0xb0, 0xf6, 0xfc, 0xe4, 0xbb,
+    0x88, 0x4f, 0xee, 0x95, 0xc7, 0x1d, 0xd2, 0xd3, 0x67, 0x16, 0x8d, 0x8f,
+    0x1f, 0x46, 0x53, 0xdc, 0x1c, 0x7a, 0xd3, 0xff, 0x8a, 0xfa, 0xcf, 0x47,
+    0x1d, 0x8e, 0x5d, 0x68, 0xf9, 0xf4, 0xf0, 0x82, 0x46, 0x65, 0xf3, 0x0b,
+    0x1b, 0xfd, 0x84, 0x4e, 0x38, 0x91, 0x1f, 0xd6, 0x0e, 0xdb, 0xa8, 0xff,
+    0xe9, 0x98, 0x05, 0x0e, 0x81, 0x68, 0xa5, 0xde, 0x16, 0x1c, 0x7c, 0x0a,
+    0x11, 0x93, 0xef, 0xf6, 0xab, 0xaa, 0x8a, 0xde, 0x7f, 0xd4, 0xf3, 0x67,
+    0xfb, 0x55, 0xd5, 0x44, 0xe1, 0x23, 0x61, 0xfe, 0x1c, 0xce, 0x6d, 0xf4,
+    0xb4, 0xfb, 0xfd, 0xaa, 0xea, 0xa2, 0xd1, 0x9f, 0x9a, 0x87, 0x7f, 0x71,
+    0x69, 0x88, 0x35, 0xa7, 0xff, 0xd6, 0x37, 0x1b, 0xdf, 0xb6, 0xaa, 0xe3,
+    0x80, 0x5a, 0x7f, 0xcd, 0xb5, 0x79, 0xf4, 0x35, 0xe7, 0xb1, 0x68, 0x64,
+    0x4d, 0x69, 0x52, 0x58, 0xe2, 0x34, 0xaf, 0x0b, 0x09, 0x19, 0xd2, 0x4d,
+    0xdb, 0x05, 0xce, 0x67, 0xa8, 0x7f, 0x4f, 0x76, 0xab, 0xaa, 0x8b, 0x6a,
+    0x7e, 0xd3, 0x1d, 0x8b, 0xd5, 0xa3, 0xe7, 0xb3, 0xc2, 0xd9, 0xfe, 0x6f,
+    0xf8, 0xbb, 0x6a, 0x89, 0x68, 0xc3, 0xda, 0xb4, 0x8a, 0x7e, 0x0e, 0xbf,
+    0xee, 0xe2, 0xb4, 0xfa, 0xb6, 0x78, 0x57, 0x5a, 0x46, 0x0c, 0xf6, 0x1d,
+    0x97, 0xc1, 0x91, 0x4a, 0x07, 0xa9, 0xfc, 0x6c, 0xff, 0x6a, 0xba, 0xa8,
+    0xb9, 0x67, 0xf1, 0xb3, 0xfd, 0xaa, 0xea, 0xa2, 0xed, 0x86, 0x6d, 0x17,
+    0x6c, 0x28, 0x7c, 0xa3, 0x2f, 0x61, 0xbe, 0x1c, 0xbd, 0x52, 0xa4, 0x02,
+    0xf9, 0xe3, 0x69, 0xd4, 0x73, 0xa3, 0x1d, 0x2d, 0xc8, 0xf8, 0x7d, 0x3f,
+    0x8d, 0x9f, 0xed, 0x57, 0x55, 0x15, 0x3c, 0xc5, 0xc5, 0xa7, 0xbb, 0x55,
+    0xd5, 0x45, 0x73, 0x3f, 0x8d, 0x9f, 0xed, 0x57, 0x55, 0x16, 0x74, 0x7c,
+    0xfa, 0xf4, 0x5b, 0x3f, 0xfd, 0x9c, 0x6b, 0xf8, 0xde, 0xfc, 0x67, 0x1c,
+    0x25, 0xa7, 0xfd, 0xa2, 0xff, 0xba, 0xc2, 0x10, 0xd6, 0x9e, 0xa1, 0x6f,
+    0x56, 0x9f, 0x7f, 0xb5, 0x5d, 0x54, 0x48, 0x53, 0xfc, 0x5b, 0x5f, 0x82,
+    0xc7, 0xba, 0xd3, 0x3c, 0xd8, 0x7d, 0x60, 0x33, 0x9e, 0x74, 0xb4, 0xc7,
+    0x5a, 0x7f, 0x66, 0xaf, 0x7c, 0xfd, 0x8b, 0x48, 0xde, 0xaa, 0x44, 0xc8,
+    0x40, 0x11, 0x13, 0x8a, 0x67, 0x3e, 0xa8, 0x45, 0xdc, 0xb7, 0x84, 0xd3,
+    0xff, 0x8c, 0x7a, 0x79, 0xb3, 0xfd, 0xaa, 0xea, 0xa2, 0x7c, 0x8e, 0xab,
+    0x59, 0xf3, 0xca, 0x82, 0xd4, 0x20, 0xa7, 0xdf, 0xed, 0x57, 0x55, 0x15,
+    0x94, 0xff, 0xa9, 0xe6, 0xcf, 0xf6, 0xab, 0xaa, 0x89, 0xba, 0x62, 0x36,
+    0x1f, 0xe1, 0xcc, 0xe7, 0xff, 0x18, 0xf4, 0xf3, 0x67, 0xfb, 0x55, 0xd5,
+    0x44, 0xad, 0x3f, 0xe2, 0xbe, 0xe7, 0x12, 0x77, 0xbd, 0x2d, 0x3f, 0x85,
+    0xbf, 0x8e, 0xe8, 0xeb, 0x43, 0xcf, 0xc8, 0x90, 0x27, 0xdf, 0xed, 0x57,
+    0x55, 0x12, 0xe4, 0xff, 0x15, 0x3f, 0x82, 0xc7, 0xba, 0xd3, 0xce, 0xc8,
+    0x59, 0x69, 0xf1, 0x8f, 0x4f, 0x33, 0x22, 0xaf, 0x08, 0x74, 0x67, 0x46,
+    0xd3, 0xff, 0x1a, 0x9e, 0x6c, 0xff, 0x6a, 0xba, 0xa8, 0x8e, 0xe7, 0xfb,
+    0x9a, 0xc7, 0x9a, 0x86, 0xeb, 0x4f, 0xde, 0x60, 0xb7, 0x70, 0x56, 0x9b,
+    0x7d, 0x2d, 0x3f, 0x3a, 0x23, 0x9a, 0xae, 0x2d, 0x3f, 0xb2, 0xdb, 0xb0,
+    0xf2, 0xeb, 0x4f, 0xbf, 0xda, 0xae, 0xaa, 0x2a, 0x19, 0x9f, 0x8b, 0x4f,
+    0x6b, 0x99, 0x62, 0xd3, 0xec, 0xb2, 0x85, 0xda, 0xd3, 0xff, 0xff, 0xf9,
+    0x9f, 0xe3, 0x9a, 0x26, 0xbf, 0x8c, 0xdf, 0xac, 0xef, 0xc6, 0x7f, 0xf8,
+    0xef, 0x7a, 0xd2, 0xd1, 0x88, 0xe2, 0x12, 0x2b, 0x94, 0x4f, 0xff, 0xf6,
+    0xf5, 0xe7, 0x2b, 0x35, 0x45, 0xc6, 0xd1, 0x72, 0xf9, 0xea, 0xd2, 0x33,
+    0xa2, 0xa9, 0xa9, 0xd2, 0x2f, 0xf3, 0x05, 0xec, 0x30, 0xc3, 0x2e, 0x99,
+    0xf2, 0x1e, 0xfe, 0x45, 0xd3, 0xef, 0xf6, 0xab, 0xaa, 0x8a, 0xa2, 0x7f,
+    0xd4, 0xf3, 0x67, 0xfb, 0x55, 0xd5, 0x44, 0xd7, 0x23, 0x61, 0xfe, 0x1c,
+    0xce, 0x7f, 0x1b, 0x3f, 0xda, 0xae, 0xaa, 0x2a, 0xc9, 0xff, 0x1b, 0x4d,
+    0xb7, 0x1b, 0x55, 0xe7, 0x5a, 0x7f, 0xe3, 0x53, 0xcd, 0x9f, 0xed, 0x57,
+    0x55, 0x12, 0x24, 0xfb, 0xfd, 0xaa, 0xea, 0xa2, 0xd2, 0x9f, 0xf5, 0x3c,
+    0xd9, 0xfe, 0xd5, 0x75, 0x51, 0x3e, 0xc8, 0xd8, 0x7f, 0x87, 0x33, 0x9f,
+    0xfc, 0x63, 0xd3, 0xcd, 0x9f, 0xed, 0x57, 0x55, 0x14, 0x24, 0xfb, 0x45,
+    0x63, 0x7d, 0x69, 0xf7, 0xfb, 0x55, 0xd5, 0x45, 0x1f, 0x3f, 0xe1, 0x67,
+    0x18, 0x59, 0xcd, 0x32, 0xd3, 0xff, 0xf6, 0x7b, 0xa6, 0x08, 0xdc, 0x1a,
+    0x05, 0xee, 0xc0, 0x4a, 0x7c, 0x63, 0xd3, 0xcc, 0xc8, 0xf1, 0xc2, 0x7e,
+    0x19, 0xf9, 0x1e, 0x43, 0x32, 0x3f, 0xec, 0x8d, 0xc9, 0xea, 0xf8, 0x9e,
+    0x1c, 0xaa, 0x1e, 0xc6, 0x19, 0xb1, 0x39, 0x1f, 0x38, 0x85, 0xa8, 0x65,
+    0x80, 0x9f, 0x91, 0xa3, 0x4f, 0xe3, 0x67, 0xfb, 0x55, 0xd5, 0x44, 0x45,
+    0x3f, 0x67, 0xfb, 0x55, 0xd5, 0x44, 0x57, 0x3f, 0xde, 0x73, 0x67, 0xfb,
+    0x55, 0xd5, 0x45, 0x71, 0x06, 0x44, 0x05, 0x1c, 0x4f, 0x66, 0xab, 0x8b,
+    0x4f, 0xec, 0x2f, 0x80, 0x1b, 0xdd, 0x69, 0x3a, 0x22, 0x7a, 0x77, 0x20,
+    0x9f, 0xff, 0xb9, 0x72, 0xd5, 0x13, 0xc8, 0x79, 0xbe, 0xbf, 0x8b, 0x4f,
+    0xbf, 0xda, 0xae, 0xaa, 0x29, 0xe9, 0xef, 0x03, 0x4f, 0x5a, 0x7d, 0x59,
+    0xa2, 0x3a, 0xd2, 0xcb, 0x9e, 0x3b, 0xb2, 0x28, 0xf3, 0x13, 0x28, 0x62,
+    0xcc, 0x59, 0x17, 0xa9, 0xff, 0xd4, 0xf3, 0x7a, 0x35, 0x6d, 0xee, 0xc0,
+    0x5a, 0x0c, 0x88, 0xaf, 0x50, 0x27, 0x1b, 0xdc, 0x5a, 0x7f, 0xfd, 0xa6,
+    0xfe, 0x3c, 0xab, 0x99, 0xe9, 0x6f, 0xd5, 0xa7, 0xcc, 0xf0, 0x6e, 0xf5,
+    0xa7, 0xbb, 0x55, 0xd5, 0x45, 0x67, 0x0e, 0x1e, 0xaf, 0xca, 0x27, 0xeb,
+    0x0e, 0xc2, 0xfb, 0xad, 0x39, 0xac, 0x25, 0xa7, 0xdc, 0x00, 0x42, 0xf5,
+    0xa7, 0x6f, 0x57, 0x5a, 0x7d, 0x8e, 0x59, 0xb8, 0x16, 0x91, 0xbd, 0x4e,
+    0x0b, 0x21, 0x53, 0xf2, 0x3d, 0x16, 0xd0, 0xd8, 0x94, 0x04, 0x39, 0x3f,
+    0xf1, 0xa9, 0xe6, 0xcf, 0xf6, 0xab, 0xaa, 0x89, 0x16, 0x7e, 0xcf, 0xf6,
+    0xab, 0xaa, 0x8b, 0x26, 0x7f, 0xf7, 0x2e, 0x15, 0xcd, 0x96, 0x10, 0xbe,
+    0xeb, 0x41, 0x91, 0x07, 0x87, 0x13, 0xf8, 0xd9, 0xfe, 0xd5, 0x75, 0x51,
+    0x6c, 0x4e, 0xcb, 0xe2, 0xd3, 0xdd, 0xaa, 0xea, 0xa2, 0xdb, 0x9e, 0xbd,
+    0x95, 0xf5, 0xa3, 0xe7, 0x9f, 0xc2, 0xd9, 0x19, 0xe8, 0x8b, 0xc6, 0x99,
+    0xd6, 0x68, 0x96, 0x9f, 0xf7, 0x99, 0xe6, 0x3a, 0x7e, 0x6b, 0x05, 0x80,
+    0xb4, 0xfc, 0xc2, 0xff, 0x77, 0x3a, 0xd3, 0xef, 0xf6, 0xab, 0xaa, 0x8b,
+    0xc2, 0x7b, 0x9e, 0x59, 0xa5, 0xa7, 0xfe, 0x60, 0x8f, 0xb3, 0xf9, 0x83,
+    0xcb, 0xad, 0x3e, 0xc1, 0x03, 0xab, 0x2d, 0x3e, 0x60, 0xb7, 0xab, 0xad,
+    0x3b, 0x8c, 0x05, 0xa4, 0x6f, 0x31, 0x39, 0xef, 0x34, 0x8e, 0x58, 0x9b,
+    0x85, 0xdb, 0x19, 0xe8, 0x90, 0x08, 0xa2, 0x51, 0xc2, 0x89, 0xc5, 0x9b,
+    0x2d, 0x3e, 0xff, 0x6a, 0xba, 0xa8, 0xbd, 0x27, 0xfc, 0x35, 0xc7, 0xb0,
+    0x7c, 0x2b, 0x16, 0x9f, 0x61, 0xf5, 0x5e, 0xad, 0x23, 0x7a, 0x8b, 0x3c,
+    0x1b, 0x08, 0xcd, 0xd4, 0xfa, 0x19, 0x94, 0x4b, 0x64, 0x2f, 0xb2, 0x51,
+    0x3f, 0x49, 0x0a, 0x39, 0xef, 0xb7, 0x9e, 0x1a, 0x40, 0x22, 0x18, 0x64,
+    0xef, 0x1e, 0x35, 0xb1, 0xa2, 0xcd, 0xbe, 0x96, 0x9e, 0xcd, 0x57, 0x16,
+    0x9f, 0xd8, 0x5f, 0x00, 0x37, 0xba, 0xd2, 0x74, 0x44, 0xf4, 0xee, 0x41,
+    0x3e, 0x2e, 0x36, 0xd6, 0x2d, 0x3e, 0xff, 0x6a, 0xba, 0xa8, 0x88, 0xe7,
+    0xff, 0xb5, 0xbd, 0xef, 0x87, 0x1c, 0x77, 0xe1, 0x9d, 0x32, 0xd3, 0xfe,
+    0x7b, 0x59, 0xfc, 0xe7, 0x0b, 0x8b, 0x4f, 0xf6, 0x7f, 0x2f, 0x94, 0x36,
+    0x2d, 0x3f, 0xff, 0xcd, 0xaa, 0x2e, 0x36, 0x87, 0x39, 0xa2, 0xd6, 0x15,
+    0xab, 0x4d, 0x61, 0xd2, 0x9a, 0xdb, 0x52, 0x9f, 0xf1, 0xbb, 0xa2, 0x73,
+    0x4d, 0x61, 0x80, 0x6b, 0xed, 0x17, 0x9e, 0x76, 0xdc, 0x65, 0xa1, 0xe7,
+    0xf4, 0x4b, 0x13, 0xf7, 0x9c, 0x73, 0x55, 0xc5, 0xa7, 0xe6, 0x0f, 0x04,
+    0x83, 0x5a, 0x7e, 0xce, 0x3a, 0xf3, 0x34, 0xb4, 0x62, 0x22, 0x84, 0xbe,
+    0xe5, 0x93, 0xff, 0xf7, 0x4d, 0xfa, 0x12, 0xe1, 0xbe, 0xdc, 0x1a, 0x02,
+    0xd0, 0xe9, 0x2b, 0xfa, 0xf3, 0x1a, 0x98, 0xb3, 0x0a, 0x3a, 0x67, 0xb2,
+    0xc9, 0x1f, 0x7c, 0xda, 0xa3, 0x0b, 0xe4, 0x2a, 0xdd, 0x97, 0x4f, 0xbf,
+    0xda, 0xae, 0xaa, 0x22, 0xe9, 0xf7, 0x00, 0x10, 0xbd, 0x29, 0x1b, 0x0f,
+    0x6e, 0x8c, 0xe0, 0xc9, 0x97, 0xbe, 0x30, 0x99, 0xff, 0x8f, 0x4f, 0x36,
+    0x7f, 0xb5, 0x5d, 0x54, 0x4c, 0xd3, 0x5d, 0xeb, 0x4f, 0x9d, 0x3b, 0xa3,
+    0x76, 0xe2, 0xd0, 0xe9, 0xcf, 0x23, 0xcd, 0x62, 0xd3, 0xb6, 0x6b, 0xad,
+    0x3b, 0xa4, 0x75, 0xa6, 0xf2, 0xf3, 0x6b, 0x4f, 0xfe, 0xd3, 0x1e, 0xb9,
+    0x9b, 0x00, 0xe1, 0x69, 0x69, 0xfb, 0x99, 0x66, 0x01, 0xc5, 0xa7, 0xfb,
+    0xdf, 0x18, 0x35, 0xf0, 0x79, 0x2d, 0x1e, 0x6d, 0x1a, 0xa4, 0x3e, 0xe2,
+    0x5e, 0x8b, 0xa7, 0xdd, 0xad, 0x13, 0x8b, 0x4f, 0xff, 0x81, 0xb3, 0xf5,
+    0xbb, 0x7c, 0xda, 0x2e, 0xd7, 0xd6, 0x9f, 0xf9, 0xd4, 0xb8, 0x5d, 0x2f,
+    0x79, 0x9d, 0x5a, 0x7f, 0xff, 0xb7, 0xa1, 0xe8, 0xe7, 0xba, 0xdf, 0xc6,
+    0x99, 0xff, 0x6b, 0x16, 0x86, 0x4c, 0x1f, 0x15, 0xf4, 0x8d, 0x3f, 0xfe,
+    0xf4, 0x86, 0xcc, 0xb9, 0x56, 0x8d, 0x6d, 0xb6, 0xa5, 0x3f, 0x6c, 0xfb,
+    0x83, 0x77, 0x16, 0x9e, 0xed, 0x57, 0x55, 0x16, 0x7c, 0xff, 0x8b, 0x4c,
+    0xf2, 0xe3, 0x72, 0xeb, 0x4f, 0xff, 0x17, 0xf5, 0x5e, 0x31, 0xd8, 0xe5,
+    0xf0, 0x56, 0x9d, 0x6d, 0xb6, 0xa5, 0x3f, 0xea, 0x7d, 0xc7, 0x3b, 0xee,
+    0x7d, 0x23, 0x17, 0xf3, 0xff, 0x36, 0xcf, 0x1d, 0xc1, 0xe3, 0xa7, 0x02,
+    0xd3, 0xfc, 0xda, 0x2f, 0x46, 0xa9, 0xc5, 0xa7, 0xf8, 0x87, 0xd0, 0xbd,
+    0xdf, 0x97, 0x5a, 0x3a, 0xa9, 0x9d, 0xc5, 0x8f, 0x98, 0x9c, 0xb7, 0x47,
+    0x80, 0x6e, 0xba, 0x67, 0x12, 0x1d, 0x9b, 0xcf, 0xf7, 0xea, 0xdd, 0x13,
+    0x7d, 0x96, 0x9f, 0xff, 0xff, 0x60, 0x2f, 0x98, 0x21, 0x85, 0xf1, 0x6c,
+    0xd1, 0xae, 0x39, 0x61, 0xf0, 0xeb, 0x4f, 0x9d, 0xd6, 0x98, 0x25, 0xa3,
+    0x11, 0x54, 0x50, 0x81, 0x9d, 0x76, 0xb1, 0x69, 0xde, 0x59, 0xa5, 0xa1,
+    0xe7, 0xc1, 0x72, 0x6e, 0x0e, 0x4e, 0xd6, 0xf6, 0xad, 0x38, 0x1b, 0xf5,
+    0x69, 0xdc, 0xc0, 0x96, 0x8f, 0x4f, 0x5f, 0x61, 0xe2, 0x1d, 0x9f, 0xbe,
+    0x4e, 0xf0, 0x6e, 0xb4, 0xfb, 0x42, 0xd9, 0xf5, 0x27, 0xfc, 0x5f, 0x7e,
+    0x7e, 0xcd, 0xeb, 0xd5, 0x44, 0x1a, 0x63, 0x4b, 0x3e, 0xae, 0x91, 0xd9,
+    0x69, 0xf9, 0xfa, 0x1a, 0xdb, 0x8b, 0x4b, 0x3a, 0x7a, 0x42, 0x4b, 0x3f,
+    0xf6, 0x0e, 0xdc, 0xcf, 0x95, 0x94, 0x75, 0xa6, 0x21, 0x5a, 0x7e, 0xab,
+    0x79, 0x9a, 0xe3, 0x1e, 0xb8, 0x68, 0x70, 0xc9, 0xe1, 0xbd, 0x4b, 0x21,
+    0x5e, 0x2f, 0xb3, 0xf7, 0xf3, 0xdb, 0xe9, 0x96, 0x9f, 0xff, 0xdb, 0x8d,
+    0x07, 0xe0, 0x21, 0xae, 0x5c, 0xb6, 0xd1, 0x5a, 0xb4, 0xff, 0x8f, 0x9c,
+    0xb8, 0x57, 0xa7, 0xe2, 0xd3, 0xf5, 0xee, 0xdf, 0xe3, 0x2c, 0x63, 0x7d,
+    0x3d, 0xac, 0x27, 0x6b, 0x4f, 0xd7, 0x2f, 0xe6, 0xff, 0x5a, 0x09, 0x11,
+    0x7a, 0x3c, 0xe1, 0x14, 0xc7, 0x74, 0x55, 0x17, 0xe4, 0xff, 0xcf, 0xe6,
+    0xef, 0x79, 0xf9, 0xc6, 0x3a, 0xd3, 0xff, 0x16, 0x0e, 0x65, 0xfc, 0x3e,
+    0xe7, 0x5a, 0x36, 0x44, 0x4d, 0xd1, 0xa7, 0x06, 0xdf, 0x5a, 0x7e, 0xf5,
+    0x81, 0xfc, 0xd9, 0x68, 0x31, 0xe5, 0x1c, 0x72, 0x62, 0x71, 0x69, 0xdc,
+    0x2b, 0x16, 0x9c, 0x2d, 0xd5, 0xa3, 0xc1, 0xe6, 0x1c, 0x58, 0x47, 0x21,
+    0x93, 0xec, 0xf4, 0xbf, 0x21, 0x62, 0x4d, 0xd4, 0xdd, 0x3d, 0x5c, 0x6d,
+    0x96, 0x9f, 0xff, 0xf3, 0x68, 0x8e, 0x67, 0x1f, 0x5f, 0x0b, 0x45, 0xb5,
+    0xfa, 0x56, 0x2d, 0x0e, 0x22, 0x41, 0xd4, 0x86, 0x7e, 0xaf, 0xb6, 0x82,
+    0xf5, 0x69, 0xeb, 0x6b, 0x2e, 0xb4, 0xff, 0x3c, 0xf4, 0xfe, 0x5d, 0x83,
+    0x5a, 0x18, 0xf6, 0xa8, 0x86, 0x7b, 0x2e, 0x42, 0xb4, 0x52, 0x31, 0xc6,
+    0x11, 0x1b, 0x90, 0x4f, 0x05, 0xbd, 0x5d, 0x69, 0xe0, 0x03, 0x36, 0x5a,
+    0x7e, 0x75, 0xe6, 0x50, 0xf5, 0x69, 0xfe, 0xb8, 0x0b, 0x6d, 0x9f, 0x9c,
+    0x5a, 0x7e, 0x6d, 0xae, 0x72, 0xf3, 0xad, 0x1f, 0x45, 0xde, 0x88, 0xb8,
+    0x5c, 0xec, 0xea, 0x19, 0x31, 0xdc, 0x86, 0xf4, 0xce, 0x6e, 0xb4, 0x8e,
+    0xb4, 0x6c, 0x6a, 0x1d, 0x01, 0x89, 0xfd, 0x55, 0xb3, 0xc5, 0xbd, 0x5a,
+    0x7f, 0xf0, 0x93, 0x5c, 0x69, 0xfe, 0x2f, 0x9f, 0x5a, 0x7f, 0xff, 0xda,
+    0x2d, 0xb9, 0x9b, 0x73, 0xff, 0x6e, 0x00, 0x21, 0x7e, 0xb1, 0x69, 0xef,
+    0x7d, 0xa1, 0x5a, 0x7f, 0xd5, 0xae, 0x95, 0x78, 0xbd, 0x69, 0x69, 0xfe,
+    0xd1, 0x50, 0x33, 0xda, 0x7a, 0xd0, 0xc9, 0xd3, 0x7a, 0x4f, 0x86, 0x5d,
+    0x48, 0x26, 0xed, 0x11, 0x70, 0xfa, 0x7f, 0xec, 0x1d, 0xb9, 0x9f, 0x2b,
+    0x28, 0xeb, 0x4f, 0xb3, 0x8f, 0x3e, 0x96, 0x9b, 0xc6, 0x2d, 0x3d, 0x71,
+    0x63, 0xad, 0x0c, 0x6d, 0xf6, 0x17, 0x86, 0x46, 0x15, 0xd0, 0xf7, 0x5e,
+    0x96, 0x96, 0x9a, 0xef, 0x5a, 0x00, 0x69, 0xb7, 0x10, 0x9f, 0xac, 0x71,
+    0xfe, 0x67, 0xec, 0x5a, 0x7f, 0x07, 0xac, 0xe6, 0x8b, 0x8b, 0x4c, 0x5f,
+    0x5a, 0x3c, 0xd9, 0xff, 0x11, 0xb6, 0x8c, 0xe7, 0xfe, 0xf5, 0xbd, 0xfb,
+    0x70, 0xba, 0x5e, 0xad, 0x3f, 0x6b, 0x7b, 0x1c, 0xd1, 0x2d, 0x3c, 0x23,
+    0x9c, 0x5a, 0x3d, 0x44, 0xa6, 0x91, 0x78, 0x61, 0x23, 0x79, 0xb6, 0xd4,
+    0xb1, 0xd2, 0x30, 0x74, 0xe3, 0x8d, 0x0f, 0x0b, 0x10, 0xfd, 0x8c, 0xbb,
+    0x25, 0x4b, 0x07, 0x08, 0x3d, 0xa3, 0x41, 0x28, 0x43, 0x39, 0x1f, 0x9f,
+    0xd0, 0x0e, 0x5b, 0xa9, 0x63, 0x55, 0x0d, 0xd0, 0x43, 0xb8, 0x63, 0x51,
+    0xbc, 0x7e, 0xbc, 0x8c, 0x96, 0xd5, 0x6f, 0x28, 0x50, 0x05, 0x0c, 0x88,
+    0x33, 0x6e, 0xc1, 0xaa, 0xd7, 0x0e, 0x7f, 0xf1, 0x8f, 0x4f, 0x36, 0x7f,
+    0xb5, 0x5d, 0x54, 0x4d, 0x93, 0xf8, 0xd9, 0xfe, 0xd5, 0x75, 0x51, 0x56,
+    0xcf, 0xe7, 0xbf, 0x7e, 0x0b, 0x3d, 0x69, 0xec, 0xd5, 0x71, 0x69, 0x3a,
+    0x38, 0x7a, 0x62, 0x67, 0x3e, 0xff, 0x6a, 0xba, 0xa8, 0xad, 0x27, 0xff,
+    0xba, 0xdf, 0xc0, 0x51, 0xe9, 0xec, 0x5d, 0x5a, 0x7f, 0xf9, 0xf7, 0xc1,
+    0x63, 0xd6, 0x73, 0x8c, 0x75, 0xa6, 0xe7, 0x19, 0x13, 0x14, 0x99, 0x3f,
+    0x99, 0xd8, 0x5c, 0x16, 0x02, 0xd3, 0xf5, 0x99, 0xa2, 0xc3, 0xad, 0x3e,
+    0x73, 0x3d, 0x3f, 0x9d, 0x69, 0xf8, 0x5a, 0xdd, 0x9f, 0x75, 0xa5, 0x8e,
+    0x1e, 0xdb, 0xa9, 0x6c, 0xfe, 0x7e, 0x72, 0x84, 0x1b, 0xad, 0x23, 0x79,
+    0x8a, 0x8e, 0xf0, 0xac, 0xa1, 0x97, 0xa2, 0xda, 0x34, 0x18, 0x43, 0xf0,
+    0xae, 0x7f, 0x1b, 0x3f, 0xda, 0xae, 0xaa, 0x2c, 0x09, 0xf7, 0xfb, 0x55,
+    0xd5, 0x44, 0xeb, 0x3f, 0xff, 0xb4, 0xd6, 0x7b, 0x87, 0x35, 0xef, 0x9a,
+    0x2b, 0xef, 0x62, 0xd3, 0xe3, 0x1e, 0x9e, 0x6c, 0x44, 0xbb, 0x86, 0x73,
+    0xf9, 0xf7, 0x30, 0x61, 0xb1, 0xd6, 0x9f, 0x7f, 0xb5, 0x5d, 0x54, 0x5b,
+    0x33, 0xfe, 0x03, 0x5c, 0xda, 0x2e, 0xd7, 0xd6, 0x91, 0xb0, 0xfc, 0x28,
+    0xce, 0x7b, 0xb5, 0x5d, 0x54, 0x5c, 0xd2, 0x3a, 0xd1, 0xf3, 0x7b, 0x69,
+    0x6c, 0xce, 0x1d, 0x69, 0x1b, 0x0d, 0xcd, 0xa4, 0x53, 0xef, 0xf6, 0xab,
+    0xaa, 0x8b, 0xbe, 0x7f, 0x3c, 0xd7, 0xff, 0xca, 0xd5, 0xa4, 0x6c, 0x3e,
+    0x7a, 0x33, 0x9e, 0x30, 0x7e, 0x81, 0x68, 0x66, 0xfa, 0x0b, 0x2b, 0x78,
+    0xae, 0x99, 0x6c, 0xaa, 0x52, 0xb3, 0x3e, 0xee, 0x78, 0x6a, 0xd2, 0x00,
+    0x21, 0x4b, 0x78, 0x4e, 0x72, 0x11, 0xc1, 0x13, 0x4f, 0xaf, 0x73, 0xd3,
+    0xd6, 0x9f, 0xfb, 0x6e, 0xef, 0x5b, 0x73, 0x30, 0x43, 0x5a, 0x70, 0xd1,
+    0x98, 0xfb, 0x78, 0x4f, 0x3f, 0x86, 0xcc, 0x75, 0xe3, 0x06, 0xb4, 0xfb,
+    0xfd, 0xaa, 0xea, 0xa2, 0x57, 0x9f, 0xf0, 0xd5, 0xbe, 0xe9, 0xac, 0xcb,
+    0x56, 0x9f, 0xff, 0x9f, 0x84, 0x0c, 0x39, 0x72, 0xec, 0x7a, 0xdb, 0x8b,
+    0x4f, 0x6f, 0x5f, 0x7a, 0xd3, 0xff, 0xf8, 0xb9, 0x47, 0xa1, 0x06, 0xad,
+    0xa1, 0xb3, 0xa5, 0x62, 0xd1, 0xea, 0x20, 0xa8, 0x8a, 0x3d, 0x4c, 0xfb,
+    0x10, 0x35, 0x0d, 0xb9, 0xff, 0x72, 0x9f, 0x7d, 0x37, 0xc2, 0xf5, 0x69,
+    0xff, 0xb8, 0x4e, 0xcb, 0x8e, 0x3d, 0x87, 0x8b, 0x4e, 0xa7, 0x99, 0x95,
+    0x31, 0x61, 0xb5, 0xe3, 0x71, 0xe1, 0xb0, 0x48, 0x13, 0xff, 0xb3, 0x46,
+    0x1c, 0xf4, 0x87, 0x99, 0xa5, 0xa7, 0x71, 0xbd, 0x4a, 0x7f, 0xf6, 0xa8,
+    0xb8, 0xcf, 0x2f, 0xd9, 0xb8, 0x12, 0x9f, 0xd5, 0xd3, 0x7b, 0xf7, 0x40,
+    0xcc, 0x7c, 0xd7, 0x1c, 0x91, 0xba, 0xaf, 0xc8, 0xf2, 0xa4, 0xf8, 0xc4,
+    0xee, 0x15, 0xb3, 0xff, 0x8c, 0x7a, 0x79, 0xb3, 0xfd, 0xaa, 0xea, 0xa2,
+    0x61, 0x9f, 0xfc, 0x2c, 0xec, 0x5b, 0x63, 0x79, 0xb7, 0x4f, 0x77, 0xad,
+    0x3f, 0xfb, 0xf4, 0x03, 0x07, 0xc7, 0xda, 0xd7, 0x7a, 0xd3, 0x61, 0xbd,
+    0x45, 0x18, 0x4a, 0xf3, 0xff, 0x3a, 0x39, 0xb3, 0xf7, 0xae, 0x0b, 0x3d,
+    0x69, 0xfc, 0xd5, 0x67, 0x03, 0xaf, 0xad, 0x3e, 0xaf, 0xbe, 0x8e, 0xb4,
+    0x09, 0xec, 0xee, 0x67, 0x3f, 0x1d, 0xb0, 0x79, 0xe4, 0xb4, 0xae, 0xb4,
+    0xf9, 0xb0, 0x79, 0xe4, 0xb4, 0xfd, 0xa2, 0xe5, 0xeb, 0x6f, 0x07, 0xcc,
+    0xe1, 0x71, 0xc4, 0x27, 0xff, 0xfc, 0x39, 0x6b, 0xaf, 0x33, 0x5e, 0x07,
+    0x2c, 0x2c, 0x1d, 0x9f, 0x75, 0xa7, 0xff, 0x9b, 0x83, 0x40, 0xd6, 0x17,
+    0xbb, 0xf2, 0xeb, 0x4f, 0xce, 0xc2, 0xe0, 0xb0, 0x16, 0x9f, 0xfc, 0xdc,
+    0x03, 0x3f, 0x36, 0xe5, 0x9b, 0xda, 0xb4, 0x31, 0xfe, 0x11, 0x7c, 0xfd,
+    0x72, 0x0c, 0xf5, 0xc5, 0xa7, 0xff, 0xfd, 0xfc, 0x76, 0xc3, 0xcf, 0x01,
+    0x0b, 0x6a, 0x8b, 0x8d, 0xcb, 0xad, 0x1b, 0x22, 0x6b, 0xe5, 0xb3, 0xde,
+    0x1d, 0xba, 0xd8, 0xb4, 0x61, 0xe7, 0x06, 0x49, 0x3f, 0xfe, 0xd0, 0xe7,
+    0x34, 0x59, 0xb7, 0x31, 0xdb, 0x5d, 0x69, 0xcc, 0x00, 0x96, 0x8c, 0x3f,
+    0x11, 0x55, 0x9e, 0xa0, 0xbd, 0xc5, 0xa4, 0x6f, 0x31, 0x72, 0x5f, 0xd8,
+    0x51, 0x76, 0x11, 0x0e, 0x2a, 0xfd, 0xc3, 0x50, 0xcb, 0x18, 0xc2, 0x39,
+    0x09, 0x3d, 0xc8, 0x27, 0xff, 0xbe, 0x5b, 0x3e, 0xe5, 0xcd, 0x37, 0xf8,
+    0xcb, 0x4f, 0xbf, 0xda, 0xae, 0xaa, 0x2a, 0xe9, 0xfe, 0x79, 0xb3, 0xfd,
+    0xaa, 0xea, 0xa2, 0x3c, 0x91, 0xbd, 0x46, 0x36, 0x27, 0xd1, 0x9c, 0xfe,
+    0x36, 0x7f, 0xb5, 0x5d, 0x54, 0x58, 0x33, 0xf8, 0xd9, 0xfe, 0xd5, 0x75,
+    0x51, 0x65, 0x4f, 0xe3, 0x67, 0xfb, 0x55, 0xd5, 0x45, 0xa7, 0x3f, 0xb2,
+    0xfe, 0x3f, 0xfd, 0xf4, 0xb4, 0xf7, 0x6a, 0xba, 0xa8, 0xb7, 0x27, 0xfe,
+    0x6c, 0xfe, 0x0f, 0x8d, 0x66, 0xd6, 0xad, 0x1f, 0x3f, 0x2b, 0x96, 0xc8,
+    0x56, 0x9f, 0xf8, 0xbd, 0xd6, 0xf5, 0x7c, 0xc1, 0x0d, 0x68, 0xc3, 0xd3,
+    0xd0, 0x7c, 0xff, 0x30, 0xee, 0x76, 0xbf, 0x09, 0x69, 0xfd, 0x55, 0x67,
+    0xad, 0x9f, 0x5a, 0x16, 0x9d, 0xbd, 0x71, 0x69, 0xb7, 0x65, 0xa1, 0xe6,
+    0xcb, 0x83, 0x90, 0xb4, 0xf6, 0xdd, 0x2f, 0xad, 0x3c, 0x4c, 0x73, 0x62,
+    0x22, 0x76, 0x3c, 0x39, 0x0f, 0x02, 0xa7, 0xfd, 0x4f, 0x36, 0x7f, 0xb5,
+    0x5d, 0x54, 0x50, 0xf3, 0xf9, 0xbe, 0x06, 0xee, 0x1d, 0x69, 0xff, 0x8f,
+    0x5e, 0xb7, 0xba, 0x21, 0x23, 0xad, 0x3f, 0xf6, 0xef, 0xd5, 0x7f, 0x6e,
+    0x31, 0x58, 0xb4, 0xff, 0xb7, 0xf8, 0x3f, 0x43, 0xcc, 0x02, 0xd3, 0xfd,
+    0x82, 0x35, 0xc0, 0x9b, 0xab, 0x4f, 0xab, 0x6b, 0xf0, 0x96, 0x8d, 0x1e,
+    0xed, 0xcd, 0xa7, 0xe1, 0xf3, 0x33, 0xed, 0x75, 0xa7, 0xff, 0xec, 0xf7,
+    0x4c, 0x11, 0xb8, 0x34, 0x0b, 0xdd, 0x80, 0x94, 0xfe, 0xd5, 0x01, 0xc1,
+    0x6f, 0x56, 0x91, 0x9e, 0xba, 0x03, 0x90, 0xa8, 0xeb, 0xa9, 0x10, 0xb8,
+    0x6f, 0xf8, 0x5d, 0x9d, 0x4f, 0x49, 0x34, 0x62, 0x04, 0x3b, 0xa3, 0x72,
+    0x13, 0x7b, 0x91, 0x79, 0x18, 0x04, 0xb7, 0x3f, 0x8d, 0x9f, 0xed, 0x57,
+    0x55, 0x17, 0x9c, 0x33, 0x3a, 0x55, 0xea, 0x7e, 0xc3, 0xe3, 0x27, 0x31,
+    0xf6, 0x8d, 0xbb, 0xe6, 0xa7, 0x3e, 0xd1, 0xf0, 0xce, 0x9f, 0xbb, 0x87,
+    0x3c, 0xfe, 0x36, 0x7f, 0xb5, 0x5d, 0x54, 0x52, 0xd3, 0xef, 0xf6, 0xab,
+    0xaa, 0x8b, 0x0a, 0x7f, 0xfc, 0xd8, 0x3e, 0xb6, 0x9b, 0x6e, 0x60, 0xd1,
+    0xd6, 0x91, 0xb1, 0x10, 0xa7, 0x33, 0x9f, 0xc6, 0xcf, 0xf6, 0xab, 0xaa,
+    0x8b, 0x2e, 0x78, 0xc1, 0xf9, 0xad, 0xd3, 0x2d, 0x38, 0xc1, 0x1d, 0x69,
+    0x5f, 0xa7, 0x9d, 0xb9, 0x8c, 0xfa, 0xbf, 0x81, 0xd8, 0xb4, 0xfe, 0xb3,
+    0x6b, 0xb7, 0xbc, 0x25, 0xa7, 0xcd, 0x8e, 0xda, 0xeb, 0x4c, 0x2d, 0x47,
+    0xb9, 0x69, 0xac, 0xf5, 0x3d, 0xfe, 0xad, 0x3f, 0x57, 0xdb, 0x95, 0xa5,
+    0xa6, 0x6b, 0xad, 0x1e, 0x9f, 0x16, 0x88, 0x9d, 0x95, 0xcf, 0xc5, 0x57,
+    0x05, 0xf7, 0x5a, 0x7f, 0xbe, 0xcf, 0xe5, 0x6a, 0xbd, 0x5a, 0x4f, 0x5a,
+    0x4f, 0x5a, 0x4f, 0x5a, 0x18, 0xd8, 0x06, 0x20, 0x42, 0x13, 0xff, 0xee,
+    0x5c, 0xbb, 0x83, 0xf6, 0x1c, 0xf7, 0x03, 0x5a, 0x5b, 0x2d, 0x35, 0xf7,
+    0x5a, 0x30, 0xfe, 0xee, 0xa7, 0xc1, 0x19, 0x89, 0x96, 0x9f, 0x85, 0x8e,
+    0x2d, 0x8b, 0x4c, 0xf6, 0x5a, 0x3d, 0x3d, 0x27, 0x05, 0x37, 0x29, 0x9f,
+    0xff, 0xfe, 0xd3, 0x72, 0xe5, 0x7b, 0xb7, 0x34, 0x4d, 0xf0, 0x1e, 0x9e,
+    0xfe, 0x06, 0xb4, 0xcc, 0x75, 0xa7, 0xff, 0xe7, 0xb0, 0xee, 0xf7, 0xb7,
+    0x35, 0x87, 0x09, 0x80, 0xb4, 0xdd, 0x33, 0x2e, 0x24, 0xd8, 0x53, 0xec,
+    0x21, 0x32, 0x11, 0xdd, 0x32, 0xf9, 0x6d, 0x34, 0x0c, 0x2a, 0x2f, 0x08,
+    0x0e, 0x19, 0x79, 0x3e, 0x84, 0x2b, 0x3f, 0xf8, 0xc7, 0xa7, 0x9b, 0x3f,
+    0xda, 0xae, 0xaa, 0x28, 0xc9, 0xf3, 0xb6, 0xfb, 0x01, 0x69, 0xf0, 0xd0,
+    0x18, 0x25, 0xa4, 0x66, 0x3c, 0xed, 0x14, 0x43, 0xd7, 0xd5, 0x3f, 0x0c,
+    0x33, 0x94, 0x54, 0xe6, 0xad, 0xe1, 0x95, 0xc8, 0x51, 0xcf, 0xfc, 0x6a,
+    0x79, 0xb3, 0xfd, 0xaa, 0xea, 0xa2, 0x39, 0x9f, 0xfc, 0x63, 0xd3, 0xcd,
+    0x9f, 0xed, 0x57, 0x55, 0x13, 0x94, 0xfe, 0x36, 0x7f, 0xb5, 0x5d, 0x54,
+    0x59, 0x93, 0xfd, 0xe6, 0xaf, 0x70, 0xed, 0xb3, 0x1d, 0x69, 0xfe, 0x07,
+    0xdb, 0x9a, 0xad, 0x9e, 0xb4, 0xfe, 0xcb, 0xff, 0xf9, 0xb5, 0xd6, 0x91,
+    0x9d, 0x32, 0x2b, 0x59, 0x05, 0xe7, 0x33, 0xf8, 0xd9, 0xfe, 0xd5, 0x75,
+    0x51, 0x6e, 0xcf, 0xfe, 0x31, 0xe9, 0xe6, 0xcf, 0xf6, 0xab, 0xaa, 0x8a,
+    0x42, 0x7f, 0xe3, 0x53, 0xcd, 0x9f, 0xed, 0x57, 0x55, 0x12, 0x94, 0x58,
+    0xaa, 0xb1, 0xc4, 0xf3, 0xaa, 0x82, 0x1d, 0x62, 0x77, 0xc3, 0xe7, 0x55,
+    0x59, 0xff, 0x53, 0xcd, 0x9f, 0xed, 0x57, 0x55, 0x13, 0xb4, 0xff, 0xf8,
+    0xbd, 0xdc, 0xec, 0xeb, 0x5e, 0x8b, 0x0e, 0x75, 0x69, 0x18, 0xe8, 0xa0,
+    0xa4, 0x99, 0xff, 0xa8, 0xfa, 0xce, 0xd6, 0x98, 0x27, 0xad, 0x3f, 0xf0,
+    0xe6, 0xaa, 0xcc, 0xb8, 0xd6, 0xcb, 0x4d, 0xe4, 0x6f, 0x51, 0x0b, 0x74,
+    0x38, 0x3a, 0x38, 0x3c, 0xa1, 0x5b, 0x3e, 0xff, 0x6a, 0xba, 0xa8, 0x8b,
+    0x27, 0xfd, 0x4f, 0x36, 0x7f, 0xb5, 0x5d, 0x54, 0x4b, 0xb3, 0xff, 0xf6,
+    0x7b, 0xa6, 0x08, 0xdc, 0x1a, 0x05, 0xee, 0xc0, 0x4a, 0x46, 0xc4, 0x6b,
+    0x9c, 0xcf, 0xc9, 0x26, 0x7f, 0xf1, 0x8f, 0x4f, 0x36, 0x7f, 0xb5, 0x5d,
+    0x54, 0x4c, 0x53, 0xef, 0xf6, 0xab, 0xaa, 0x8a, 0xa6, 0x58, 0xb4, 0x61,
+    0xe1, 0x06, 0x67, 0x3f, 0xff, 0xfb, 0xf5, 0xad, 0x9f, 0x9c, 0x36, 0xaa,
+    0xff, 0x2b, 0x79, 0xa2, 0xfb, 0xd6, 0x83, 0x22, 0x6e, 0x88, 0xa7, 0xff,
+    0x18, 0xf4, 0xf3, 0x67, 0xfb, 0x55, 0xd5, 0x44, 0xe9, 0x3f, 0x8a, 0xbe,
+    0xcf, 0xa7, 0x6b, 0x4f, 0x9c, 0x67, 0x1a, 0xeb, 0x4f, 0xd6, 0xe1, 0x3b,
+    0xc1, 0x5a, 0x08, 0xf5, 0x6e, 0x51, 0x38, 0xb4, 0xcb, 0x18, 0xd0, 0xcf,
+    0xfd, 0xbd, 0xf8, 0xdb, 0x1b, 0x77, 0x08, 0xeb, 0x41, 0xcf, 0xcb, 0x45,
+    0x53, 0xfe, 0xa7, 0x9b, 0x3f, 0xda, 0xae, 0xaa, 0x27, 0x79, 0xf7, 0x37,
+    0xa3, 0x92, 0x52, 0x37, 0xa9, 0xd6, 0x64, 0x63, 0x47, 0x22, 0xa4, 0x99,
+    0xff, 0xc6, 0x3d, 0x3c, 0xd9, 0xfe, 0xd5, 0x75, 0x51, 0x42, 0xcf, 0xfe,
+    0x31, 0xe9, 0xe6, 0xcf, 0xf6, 0xab, 0xaa, 0x8a, 0x4a, 0x7f, 0xff, 0x65,
+    0xcc, 0x16, 0x39, 0xaa, 0x3f, 0x34, 0xdc, 0xdc, 0xeb, 0x45, 0x8b, 0x9c,
+    0x6f, 0x38, 0xec, 0x66, 0x5f, 0x22, 0x3c, 0xa0, 0x10, 0x24, 0xee, 0xab,
+    0xe4, 0xab, 0x3f, 0x8d, 0x9f, 0xed, 0x57, 0x55, 0x11, 0x24, 0xff, 0xe3,
+    0x1e, 0x9e, 0x6c, 0xff, 0x6a, 0xba, 0xa8, 0x97, 0xa7, 0xbf, 0xbf, 0x2e,
+    0xb4, 0xfb, 0xe0, 0x39, 0x5d, 0x69, 0xc0, 0x6e, 0x2d, 0x3f, 0xf5, 0x39,
+    0x76, 0x77, 0x5a, 0x6d, 0x9c, 0x5a, 0x3d, 0x45, 0x61, 0x11, 0xb8, 0x4f,
+    0xf1, 0xb9, 0xfe, 0x2a, 0x1d, 0xf8, 0xe3, 0x06, 0xb4, 0xfb, 0xfd, 0xaa,
+    0xea, 0xa2, 0x97, 0x9f, 0x0f, 0x2c, 0xcb, 0x56, 0x9f, 0xe0, 0x5f, 0xed,
+    0xb3, 0xc8, 0xeb, 0x4f, 0xf5, 0x59, 0xe2, 0xcd, 0xdc, 0x6f, 0x3a, 0xd3,
+    0x63, 0xd8, 0xfe, 0xf7, 0x39, 0x9f, 0xbe, 0x06, 0xee, 0x1d, 0x69, 0x9f,
+    0x8b, 0x4e, 0x60, 0x5d, 0x68, 0x63, 0xdc, 0xf4, 0xb2, 0xd1, 0x59, 0xff,
+    0x88, 0xf5, 0xe8, 0x4e, 0x3d, 0x87, 0x65, 0xa7, 0xae, 0x56, 0xf1, 0x68,
+    0xd1, 0xf4, 0x71, 0x1a, 0x7f, 0xab, 0x54, 0xe1, 0x7d, 0xac, 0x5a, 0x7c,
+    0xea, 0x47, 0x6b, 0xad, 0x3e, 0xa7, 0x79, 0xfb, 0xad, 0x26, 0xf4, 0xf4,
+    0x4e, 0x53, 0x3e, 0x20, 0xee, 0x0e, 0xad, 0x23, 0x32, 0xb6, 0x9f, 0x4f,
+    0xb0, 0xe7, 0x63, 0x37, 0x21, 0x47, 0xa8, 0x44, 0xd4, 0x26, 0x2e, 0x45,
+    0xc8, 0x44, 0xf9, 0x14, 0x4f, 0xe3, 0x67, 0xfb, 0x55, 0xd5, 0x45, 0x39,
+    0x3f, 0xe2, 0xfd, 0x5f, 0x59, 0x83, 0xd5, 0xa7, 0xfe, 0x0c, 0xb6, 0xb8,
+    0xe7, 0xba, 0xab, 0x56, 0x9f, 0xf6, 0x69, 0xbf, 0x86, 0xb6, 0xdb, 0x52,
+    0x98, 0xad, 0x5a, 0x6b, 0x0d, 0xea, 0x35, 0xf8, 0x77, 0xe4, 0x8c, 0xea,
+    0x81, 0x3e, 0xff, 0x6a, 0xba, 0xa8, 0xaf, 0x27, 0xff, 0xec, 0xf7, 0x4c,
+    0x11, 0xb8, 0x34, 0x0b, 0xdd, 0x80, 0x94, 0x8d, 0x88, 0x8e, 0xf2, 0x33,
+    0x9f, 0xf8, 0xd4, 0xf3, 0x67, 0xfb, 0x55, 0xd5, 0x44, 0x8f, 0x38, 0x80,
+    0xcb, 0x4e, 0xee, 0x1d, 0x23, 0x17, 0x53, 0xef, 0xf6, 0xab, 0xaa, 0x89,
+    0x22, 0x78, 0xd4, 0xf3, 0x31, 0xee, 0xe1, 0x54, 0xff, 0xc6, 0xa7, 0x9b,
+    0x3f, 0xda, 0xae, 0xaa, 0x24, 0xa9, 0xf7, 0xfb, 0x55, 0xd5, 0x45, 0xe3,
+    0x3e, 0xbd, 0xf9, 0x9d, 0x5a, 0x7f, 0x9e, 0x6c, 0xff, 0x6a, 0xba, 0xa8,
+    0x93, 0x64, 0x6c, 0x44, 0xf7, 0xcc, 0xe8, 0x9e, 0x19, 0x93, 0x1f, 0x61,
+    0xf3, 0xe7, 0x1a, 0xbd, 0x85, 0x51, 0x46, 0x2a, 0xe4, 0x37, 0xce, 0x57,
+    0xa8, 0x63, 0x70, 0xdf, 0x78, 0x76, 0xc3, 0x47, 0xe7, 0x77, 0x64, 0x76,
+    0xaf, 0x96, 0x25, 0xec, 0xa4, 0x1c, 0xbc, 0xb8, 0xe0, 0xe5, 0x34, 0xf6,
+    0xd6, 0x40, 0xed, 0x48, 0xda, 0x29, 0xeb, 0x6f, 0x3c, 0xa0, 0xf7, 0x2b,
+    0x57, 0x0f, 0xd3, 0x19, 0x4f, 0x8b, 0x3a, 0x1d, 0x4f, 0x5f, 0x55, 0x6a,
+    0xfe, 0x09, 0xc2, 0x87, 0x42, 0x17, 0xc3, 0x49, 0x2b, 0xbd, 0x2b, 0xb7,
+    0x96, 0x91, 0xbb, 0x7a, 0xcf, 0x05, 0xdd, 0x20, 0x12, 0xd9, 0x63, 0x9e,
+    0x51, 0xf7, 0x85, 0x3b, 0xca, 0xeb, 0x4a, 0x15, 0x80,
 };
 
-static const unsigned kPreloadedHSTSBits = 128107;
+static const unsigned kPreloadedHSTSBits = 129955;
 
-static const unsigned kHSTSRootPosition = 127515;
+static const unsigned kHSTSRootPosition = 129364;
 
 #endif // NET_HTTP_TRANSPORT_SECURITY_STATE_STATIC_H_
diff --git a/net/http/transport_security_state_static.json b/net/http/transport_security_state_static.json
index fa9edd5a..ddd78d6 100644
--- a/net/http/transport_security_state_static.json
+++ b/net/http/transport_security_state_static.json
@@ -219,6 +219,7 @@
     { "name": "googlemail.com", "mode": "force-https", "pins": "google" },
     { "name": "www.gmail.com", "mode": "force-https", "pins": "google" },
     { "name": "www.googlemail.com", "mode": "force-https", "pins": "google" },
+    { "name": "chrome.com", "include_subdomains": true, "mode": "force-https", "pins": "google" },
     { "name": "market.android.com", "include_subdomains": true, "mode": "force-https", "pins": "google" },
     { "name": "ssl.google-analytics.com", "include_subdomains": true, "mode": "force-https", "pins": "google" },
     { "name": "drive.google.com", "include_subdomains": true, "mode": "force-https", "pins": "google" },
@@ -1922,7 +1923,38 @@
     { "name": "xuntier.ch", "include_subdomains": true, "mode": "force-https" },
     { "name": "yanovich.net", "include_subdomains": true, "mode": "force-https" },
     { "name": "yaporn.tv", "include_subdomains": true, "mode": "force-https" },
-    { "name": "yorcom.nl", "include_subdomains": true, "mode": "force-https" }
+    { "name": "yorcom.nl", "include_subdomains": true, "mode": "force-https" },
+    { "name": "admin.fedoraproject.org", "include_subdomains": true, "mode": "force-https" },
+    { "name": "id.fedoraproject.org", "include_subdomains": true, "mode": "force-https" },
+    { "name": "kojipkgs.fedoraproject.org", "include_subdomains": true, "mode": "force-https" },
+    { "name": "apps.fedoraproject.org", "include_subdomains": true, "mode": "force-https" },
+    { "name": "badges.fedoraproject.org", "include_subdomains": true, "mode": "force-https" },
+    { "name": "ask.fedoraproject.org", "include_subdomains": true, "mode": "force-https" },
+    { "name": "admin.stg.fedoraproject.org", "include_subdomains": true, "mode": "force-https" },
+    { "name": "apps.stg.fedoraproject.org", "include_subdomains": true, "mode": "force-https" },
+    { "name": "ask.stg.fedoraproject.org", "include_subdomains": true, "mode": "force-https" },
+    { "name": "badges.stg.fedoraproject.org", "include_subdomains": true, "mode": "force-https" },
+    { "name": "darkserver.fedoraproject.org", "include_subdomains": true, "mode": "force-https" },
+    { "name": "darkserver.stg.fedoraproject.org", "include_subdomains": true, "mode": "force-https" },
+    { "name": "geoip.fedoraproject.org", "include_subdomains": true, "mode": "force-https" },
+    { "name": "geoip.stg.fedoraproject.org", "include_subdomains": true, "mode": "force-https" },
+    { "name": "lists.fedoraproject.org", "include_subdomains": true, "mode": "force-https" },
+    { "name": "lists.stg.fedoraproject.org", "include_subdomains": true, "mode": "force-https" },
+    { "name": "qa.fedoraproject.org", "include_subdomains": true, "mode": "force-https" },
+    { "name": "qa.stg.fedoraproject.org", "include_subdomains": true, "mode": "force-https" },
+    { "name": "redirect.fedoraproject.org", "include_subdomains": true, "mode": "force-https" },
+    { "name": "redirect.stg.fedoraproject.org", "include_subdomains": true, "mode": "force-https" },
+    { "name": "taskotron.fedoraproject.org", "include_subdomains": true, "mode": "force-https" },
+    { "name": "taskotron.stg.fedoraproject.org", "include_subdomains": true, "mode": "force-https" },
+    { "name": "translate.fedoraproject.org", "include_subdomains": true, "mode": "force-https" },
+    { "name": "translate.stg.fedoraproject.org", "include_subdomains": true, "mode": "force-https" },
+    { "name": "landscape.canonical.com", "include_subdomains": true, "mode": "force-https" },
+    { "name": "auth.mail.ru", "include_subdomains": true, "mode": "force-https" },
+    { "name": "e.mail.ru", "include_subdomains": true, "mode": "force-https" },
+    { "name": "touch.mail.ru", "include_subdomains": true, "mode": "force-https" },
+    { "name": "light.mail.ru", "include_subdomains": true, "mode": "force-https" },
+    { "name": "m.mail.ru", "include_subdomains": true, "mode": "force-https" },
+    { "name": "arty.name", "include_subdomains": true, "mode": "force-https" }
   ],
 
   // |ReportUMAOnPinFailure| uses these to report which domain was associated
@@ -2186,6 +2218,7 @@
     "2MDN_NET",
     "FACEBOOK_COM",
     "SPIDEROAK_COM",
-    "BLOGGER_COM"
+    "BLOGGER_COM",
+    "CHROME_COM"
   ]
 }
diff --git a/net/net.gyp b/net/net.gyp
index 0fdc518..5c0dbe72 100644
--- a/net/net.gyp
+++ b/net/net.gyp
@@ -262,7 +262,6 @@
               'cert/sha256_legacy_support_openssl_win.cc',
               'cert/x509_util_openssl.cc',
               'cert/x509_util_openssl.h',
-              'crypto/scoped_openssl_types.h',
               'quic/crypto/aead_base_decrypter_openssl.cc',
               'quic/crypto/aead_base_encrypter_openssl.cc',
               'quic/crypto/aes_128_gcm_12_decrypter_openssl.cc',
diff --git a/net/net.gypi b/net/net.gypi
index 5a8087d..c3c127c 100644
--- a/net/net.gypi
+++ b/net/net.gypi
@@ -170,7 +170,6 @@
       'android/android_private_key.h',
       'android/cert_verify_result_android.cc',
       'android/cert_verify_result_android.h',
-      'android/cert_verify_status_android_list.h',
       'android/gurl_utils.cc',
       'android/gurl_utils.h',
       'android/keystore.cc',
@@ -367,7 +366,6 @@
       'cert/nss_profile_filter_chromeos.cc',
       'cert/nss_profile_filter_chromeos.h',
       'cert/scoped_nss_types.h',
-      'cert/scoped_openssl_types.h',
       'cert/sct_status_flags.h',
       'cert/test_root_certs.cc',
       'cert/test_root_certs.h',
diff --git a/net/proxy/polling_proxy_config_service.cc b/net/proxy/polling_proxy_config_service.cc
index 7c086db..611ea59 100644
--- a/net/proxy/polling_proxy_config_service.cc
+++ b/net/proxy/polling_proxy_config_service.cc
@@ -9,7 +9,6 @@
 #include "base/memory/scoped_ptr.h"
 #include "base/message_loop/message_loop_proxy.h"
 #include "base/observer_list.h"
-#include "base/profiler/scoped_tracker.h"
 #include "base/synchronization/lock.h"
 #include "base/threading/worker_pool.h"
 #include "net/proxy/proxy_config.h"
@@ -114,11 +113,6 @@
 
   // Called after the worker thread has finished retrieving a configuration.
   void GetConfigCompleted(const ProxyConfig& config) {
-    // TODO(pkasting): Remove ScopedTracker below once crbug.com/455942 is
-    // fixed.
-    tracked_objects::ScopedTracker tracking_profile(
-        FROM_HERE_WITH_EXPLICIT_FUNCTION(
-            "455942 PollingProxyConfigService::Core::GetConfigCompleted"));
     DCHECK(poll_task_outstanding_);
     poll_task_outstanding_ = false;
 
diff --git a/net/proxy/proxy_service.cc b/net/proxy/proxy_service.cc
index bd7d825..6dd1b50 100644
--- a/net/proxy/proxy_service.cc
+++ b/net/proxy/proxy_service.cc
@@ -350,6 +350,11 @@
             const ProxyConfig& config,
             TimeDelta wait_delay,
             const CompletionCallback& callback) {
+    // TODO(pkasting): Remove ScopedTracker below once crbug.com/455942 is
+    // fixed.
+    tracked_objects::ScopedTracker tracking_profile(
+        FROM_HERE_WITH_EXPLICIT_FUNCTION(
+            "455942 ProxyService::InitProxyResolver::Start"));
     DCHECK_EQ(STATE_NONE, next_state_);
     proxy_resolver_ = proxy_resolver;
 
@@ -896,8 +901,7 @@
   DCHECK(proxy_config_service);
 
   if (!ProxyResolverFactoryForSystem::IsSupported()) {
-    LOG(WARNING) << "PAC support disabled because there is no "
-                    "system implementation";
+    VLOG(1) << "PAC support disabled because there is no system implementation";
     return CreateWithoutProxyResolver(proxy_config_service, net_log);
   }
 
@@ -1156,6 +1160,10 @@
 }
 
 void ProxyService::OnInitProxyResolverComplete(int result) {
+  // TODO(pkasting): Remove ScopedTracker below once crbug.com/455942 is fixed.
+  tracked_objects::ScopedTracker tracking_profile(
+      FROM_HERE_WITH_EXPLICIT_FUNCTION(
+          "455942 ProxyService::OnInitProxyResolverComplete"));
   DCHECK_EQ(STATE_WAITING_FOR_INIT_PROXY_RESOLVER, current_state_);
   DCHECK(init_proxy_resolver_.get());
   DCHECK(fetched_config_.HasAutomaticSettings());
@@ -1383,6 +1391,10 @@
 }
 
 ProxyService::State ProxyService::ResetProxyConfig(bool reset_fetched_config) {
+  // TODO(pkasting): Remove ScopedTracker below once crbug.com/455942 is fixed.
+  tracked_objects::ScopedTracker tracking_profile(
+      FROM_HERE_WITH_EXPLICIT_FUNCTION(
+          "455942 ProxyService::ResetProxyConfig"));
   DCHECK(CalledOnValidThread());
   State previous_state = current_state_;
 
@@ -1517,6 +1529,10 @@
 }
 
 void ProxyService::InitializeUsingLastFetchedConfig() {
+  // TODO(pkasting): Remove ScopedTracker below once crbug.com/455942 is fixed.
+  tracked_objects::ScopedTracker tracking_profile(
+      FROM_HERE_WITH_EXPLICIT_FUNCTION(
+          "455942 ProxyService::InitializeUsingLastFetchedConfig"));
   ResetProxyConfig(false);
 
   DCHECK(fetched_config_.is_valid());
diff --git a/net/quic/crypto/cert_compressor.cc b/net/quic/crypto/cert_compressor.cc
index 95bb553..cb44d355 100644
--- a/net/quic/crypto/cert_compressor.cc
+++ b/net/quic/crypto/cert_compressor.cc
@@ -195,10 +195,10 @@
 
       uint64 hash = QuicUtils::FNV1a_64_Hash(i->data(), i->size());
       // This assumes that the machine is little-endian.
-      for (size_t i = 0; i < client_cached_cert_hashes.size();
-           i += sizeof(uint64)) {
+      for (size_t j = 0; j < client_cached_cert_hashes.size();
+           j += sizeof(uint64)) {
         uint64 cached_hash;
-        memcpy(&cached_hash, client_cached_cert_hashes.data() + i,
+        memcpy(&cached_hash, client_cached_cert_hashes.data() + j,
                sizeof(uint64));
         if (hash != cached_hash) {
           continue;
diff --git a/net/quic/crypto/channel_id_openssl.cc b/net/quic/crypto/channel_id_openssl.cc
index f0cdcbf..97dc32e 100644
--- a/net/quic/crypto/channel_id_openssl.cc
+++ b/net/quic/crypto/channel_id_openssl.cc
@@ -34,9 +34,8 @@
     return false;
   }
 
-  crypto::ScopedOpenSSL<EC_GROUP, EC_GROUP_free>::Type p256(
-      EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1));
-  if (p256.get() == nullptr) {
+  crypto::ScopedEC_GROUP p256(EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1));
+  if (!p256) {
     return false;
   }
 
@@ -57,9 +56,8 @@
     return false;
   }
 
-  crypto::ScopedOpenSSL<EC_POINT, EC_POINT_free>::Type point(
-      EC_POINT_new(p256.get()));
-  if (point.get() == nullptr ||
+  crypto::ScopedEC_POINT point(EC_POINT_new(p256.get()));
+  if (!point ||
       !EC_POINT_set_affine_coordinates_GFp(p256.get(), point.get(), x.get(),
                                            y.get(), nullptr)) {
     return false;
diff --git a/net/quic/crypto/p256_key_exchange_openssl.cc b/net/quic/crypto/p256_key_exchange_openssl.cc
index f6d88b6d..88d45c8 100644
--- a/net/quic/crypto/p256_key_exchange_openssl.cc
+++ b/net/quic/crypto/p256_key_exchange_openssl.cc
@@ -84,14 +84,14 @@
     return false;
   }
 
-  crypto::ScopedOpenSSL<EC_POINT, EC_POINT_free>::Type point(
+  crypto::ScopedEC_POINT point(
       EC_POINT_new(EC_KEY_get0_group(private_key_.get())));
-  if (!point.get() ||
-      !EC_POINT_oct2point( /* also test if point is on curve */
-          EC_KEY_get0_group(private_key_.get()),
-          point.get(),
-          reinterpret_cast<const uint8*>(peer_public_value.data()),
-          peer_public_value.size(), nullptr)) {
+  if (!point ||
+      !EC_POINT_oct2point(/* also test if point is on curve */
+                          EC_KEY_get0_group(private_key_.get()), point.get(),
+                          reinterpret_cast<const uint8*>(
+                              peer_public_value.data()),
+                          peer_public_value.size(), nullptr)) {
     DVLOG(1) << "Can't convert peer public value to curve point.";
     return false;
   }
diff --git a/net/quic/crypto/quic_crypto_server_config.cc b/net/quic/crypto/quic_crypto_server_config.cc
index 7c76ba85..22a88238 100644
--- a/net/quic/crypto/quic_crypto_server_config.cc
+++ b/net/quic/crypto/quic_crypto_server_config.cc
@@ -665,14 +665,15 @@
     client_hello_copy.Erase(kCETV);
     client_hello_copy.Erase(kPAD);
 
-    const QuicData& client_hello_serialized = client_hello_copy.GetSerialized();
+    const QuicData& client_hello_copy_serialized =
+        client_hello_copy.GetSerialized();
     string hkdf_input;
     hkdf_input.append(QuicCryptoConfig::kCETVLabel,
                       strlen(QuicCryptoConfig::kCETVLabel) + 1);
     hkdf_input.append(reinterpret_cast<char*>(&connection_id),
                       sizeof(connection_id));
-    hkdf_input.append(client_hello_serialized.data(),
-                      client_hello_serialized.length());
+    hkdf_input.append(client_hello_copy_serialized.data(),
+                      client_hello_copy_serialized.length());
     hkdf_input.append(requested_config->serialized);
 
     CrypterPair crypters;
@@ -1333,9 +1334,9 @@
         return nullptr;
     }
 
-    for (vector<KeyExchange*>::const_iterator i = config->key_exchanges.begin();
-         i != config->key_exchanges.end(); ++i) {
-      if ((*i)->tag() == tag) {
+    for (vector<KeyExchange*>::const_iterator j = config->key_exchanges.begin();
+         j != config->key_exchanges.end(); ++j) {
+      if ((*j)->tag() == tag) {
         LOG(WARNING) << "Duplicate key exchange in config: " << tag;
         return nullptr;
       }
@@ -1478,11 +1479,12 @@
   }
 
   if (!FLAGS_quic_use_multiple_address_in_source_tokens) {
-    SourceAddressToken token;
-    if (!token.ParseFromArray(plaintext.data(), plaintext.size())) {
+    SourceAddressToken source_address_token;
+    if (!source_address_token.ParseFromArray(plaintext.data(),
+                                             plaintext.size())) {
       return SOURCE_ADDRESS_TOKEN_PARSE_FAILURE;
     }
-    *(tokens->add_tokens()) = token;
+    *(tokens->add_tokens()) = source_address_token;
     return HANDSHAKE_OK;
   }
 
@@ -1490,11 +1492,12 @@
     // Some clients might still be using the old source token format so
     // attempt to parse that format.
     // TODO(rch): remove this code once the new format is ubiquitous.
-    SourceAddressToken token;
-    if (!token.ParseFromArray(plaintext.data(), plaintext.size())) {
+    SourceAddressToken source_address_token;
+    if (!source_address_token.ParseFromArray(plaintext.data(),
+                                             plaintext.size())) {
       return SOURCE_ADDRESS_TOKEN_PARSE_FAILURE;
     }
-    *tokens->add_tokens() = token;
+    *tokens->add_tokens() = source_address_token;
   }
 
   return HANDSHAKE_OK;
diff --git a/net/quic/crypto/strike_register.cc b/net/quic/crypto/strike_register.cc
index c36d370..bc2dd69 100644
--- a/net/quic/crypto/strike_register.cc
+++ b/net/quic/crypto/strike_register.cc
@@ -495,14 +495,14 @@
       CHECK_EQ(used_external_nodes->count(ext), 0u);
       used_external_nodes->insert(ext);
       const uint8* bytes = external_node(ext);
-      for (vector<pair<unsigned, bool> >::const_iterator i = bits.begin();
-           i != bits.end(); i++) {
-        unsigned byte = i->first / 8;
+      for (vector<pair<unsigned, bool>>::const_iterator j = bits.begin();
+           j != bits.end(); j++) {
+        unsigned byte = j->first / 8;
         DCHECK_LE(byte, 0xffu);
-        unsigned bit = i->first % 8;
+        unsigned new_bit = j->first % 8;
         static const uint8 kMasks[8] =
             {0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01};
-        CHECK_EQ((bytes[byte] & kMasks[bit]) != 0, i->second);
+        CHECK_EQ((bytes[byte] & kMasks[new_bit]) != 0, j->second);
       }
     } else {
       uint32 inter = i->child(child);
diff --git a/net/quic/quic_connection_logger.cc b/net/quic/quic_connection_logger.cc
index 8a9c3a71..747ddd6 100644
--- a/net/quic/quic_connection_logger.cc
+++ b/net/quic/quic_connection_logger.cc
@@ -284,6 +284,12 @@
   // This function only seems usefully defined on Windows currently.
   if (type == NetworkChangeNotifier::CONNECTION_UNKNOWN ||
       type == NetworkChangeNotifier::CONNECTION_WIFI) {
+    // TODO(rtenneti): Remove ScopedTracker below once crbug.com/422516 is
+    // fixed.
+    tracked_objects::ScopedTracker tracking_profile1(
+        FROM_HERE_WITH_EXPLICIT_FUNCTION(
+            "422516 QuicConnectionLogger GetConnectionDescriptionString1"));
+
     WifiPHYLayerProtocol wifi_type = GetWifiPHYLayerProtocol();
     switch (wifi_type) {
       case WIFI_PHY_LAYER_PROTOCOL_NONE:
@@ -369,6 +375,8 @@
                        num_blocked_frames_received_);
   UMA_HISTOGRAM_COUNTS("Net.QuicSession.BlockedFrames.Sent",
                        num_blocked_frames_sent_);
+  UMA_HISTOGRAM_COUNTS("Net.QuicSession.HeadersStream.EarlyFramesReceived",
+                       session_->headers_stream()->num_early_frames_received());
 
   if (num_frames_received_ > 0) {
     int duplicate_stream_frame_per_thousand =
diff --git a/net/quic/quic_connection_logger_unittest.cc b/net/quic/quic_connection_logger_unittest.cc
index dbba7d7..73926727 100644
--- a/net/quic/quic_connection_logger_unittest.cc
+++ b/net/quic/quic_connection_logger_unittest.cc
@@ -5,6 +5,7 @@
 #include "net/quic/quic_connection_logger.h"
 
 #include "net/quic/quic_protocol.h"
+#include "net/quic/test_tools/quic_test_utils.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace net {
@@ -19,9 +20,11 @@
 
 class QuicConnectionLoggerTest : public ::testing::Test {
  protected:
-  QuicConnectionLoggerTest() : logger_(nullptr, net_log_) {}
+  QuicConnectionLoggerTest()
+      : session_(new MockConnection(false)), logger_(&session_, net_log_) {}
 
   BoundNetLog net_log_;
+  MockSession session_;
   QuicConnectionLogger logger_;
 };
 
diff --git a/net/quic/quic_framer.cc b/net/quic/quic_framer.cc
index 4ff23a41..4d387cf 100644
--- a/net/quic/quic_framer.cc
+++ b/net/quic/quic_framer.cc
@@ -1347,8 +1347,8 @@
       set_detailed_error("Unable to read missing sequence number range.");
       return false;
     }
-    for (size_t i = 0; i <= range_length; ++i) {
-      ack_frame->missing_packets.insert(last_sequence_number - i);
+    for (size_t j = 0; j <= range_length; ++j) {
+      ack_frame->missing_packets.insert(last_sequence_number - j);
     }
     // Subtract an extra 1 to ensure ranges are represented efficiently and
     // can't overlap by 1 sequence number.  This allows a missing_delta of 0
@@ -2099,9 +2099,10 @@
       return false;
     }
 
-    uint64 time_delta_us = it->second.Subtract(prev_time).ToMicroseconds();
+    uint64 frame_time_delta_us =
+        it->second.Subtract(prev_time).ToMicroseconds();
     prev_time = it->second;
-    if (!writer->WriteUFloat16(time_delta_us)) {
+    if (!writer->WriteUFloat16(frame_time_delta_us)) {
       return false;
     }
   }
diff --git a/net/quic/quic_network_transaction_unittest.cc b/net/quic/quic_network_transaction_unittest.cc
index 3c85ca4..0410c597 100644
--- a/net/quic/quic_network_transaction_unittest.cc
+++ b/net/quic/quic_network_transaction_unittest.cc
@@ -420,6 +420,7 @@
 }
 
 TEST_P(QuicNetworkTransactionTest, QuicProxy) {
+  params_.enable_quic_for_proxies = true;
   proxy_service_.reset(
       ProxyService::CreateFixedFromPacResult("QUIC myproxy:70"));
 
diff --git a/net/quic/quic_server_bin.cc b/net/quic/quic_server_bin.cc
index 320f3da..53ec9bb 100644
--- a/net/quic/quic_server_bin.cc
+++ b/net/quic/quic_server_bin.cc
@@ -48,9 +48,9 @@
   }
 
   if (line->HasSwitch("port")) {
-    int port;
-    if (base::StringToInt(line->GetSwitchValueASCII("port"), &port)) {
-      FLAGS_port = port;
+    if (!base::StringToInt(line->GetSwitchValueASCII("port"), &FLAGS_port)) {
+      LOG(ERROR) << "--port must be an integer\n";
+      return 1;
     }
   }
 
diff --git a/net/quic/quic_session.h b/net/quic/quic_session.h
index 0d20b9b..15712f0 100644
--- a/net/quic/quic_session.h
+++ b/net/quic/quic_session.h
@@ -219,6 +219,8 @@
 
   size_t get_max_open_streams() const { return max_open_streams_; }
 
+  const QuicHeadersStream* headers_stream() { return headers_stream_.get(); }
+
  protected:
   typedef base::hash_map<QuicStreamId, QuicDataStream*> DataStreamMap;
 
diff --git a/net/quic/reliable_quic_stream.cc b/net/quic/reliable_quic_stream.cc
index 379991e8..8f34cf5 100644
--- a/net/quic/reliable_quic_stream.cc
+++ b/net/quic/reliable_quic_stream.cc
@@ -181,6 +181,10 @@
   return sequencer_.num_frames_received();
 }
 
+int ReliableQuicStream::num_early_frames_received() const {
+  return sequencer_.num_early_frames_received();
+}
+
 int ReliableQuicStream::num_duplicate_frames_received() const {
   return sequencer_.num_duplicate_frames_received();
 }
diff --git a/net/quic/reliable_quic_stream.h b/net/quic/reliable_quic_stream.h
index 93ad0c6..5757d8d 100644
--- a/net/quic/reliable_quic_stream.h
+++ b/net/quic/reliable_quic_stream.h
@@ -97,7 +97,7 @@
   virtual void OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame);
 
   int num_frames_received() const;
-
+  int num_early_frames_received() const;
   int num_duplicate_frames_received() const;
 
   QuicFlowController* flow_controller() { return &flow_controller_; }
diff --git a/net/quic/test_tools/crypto_test_utils_openssl.cc b/net/quic/test_tools/crypto_test_utils_openssl.cc
index 5bcbccb..64d215d 100644
--- a/net/quic/test_tools/crypto_test_utils_openssl.cc
+++ b/net/quic/test_tools/crypto_test_utils_openssl.cc
@@ -133,16 +133,14 @@
     crypto::ScopedBIGNUM k(BN_new());
     CHECK(BN_bin2bn(digest, sizeof(digest), k.get()) != nullptr);
 
-    crypto::ScopedOpenSSL<EC_GROUP, EC_GROUP_free>::Type p256(
+    crypto::ScopedEC_GROUP p256(
         EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1));
-    CHECK(p256.get());
+    CHECK(p256);
 
     crypto::ScopedEC_KEY ecdsa_key(EC_KEY_new());
-    CHECK(ecdsa_key.get() != nullptr &&
-          EC_KEY_set_group(ecdsa_key.get(), p256.get()));
+    CHECK(ecdsa_key && EC_KEY_set_group(ecdsa_key.get(), p256.get()));
 
-    crypto::ScopedOpenSSL<EC_POINT, EC_POINT_free>::Type point(
-        EC_POINT_new(p256.get()));
+    crypto::ScopedEC_POINT point(EC_POINT_new(p256.get()));
     CHECK(EC_POINT_mul(p256.get(), point.get(), k.get(), nullptr, nullptr,
                        nullptr));
 
diff --git a/net/sdch/sdch_owner.cc b/net/sdch/sdch_owner.cc
index b0e4405..49e65d1 100644
--- a/net/sdch/sdch_owner.cc
+++ b/net/sdch/sdch_owner.cc
@@ -86,22 +86,40 @@
       clock_(new base::DefaultClock),
       max_total_dictionary_size_(kMaxTotalDictionarySize),
       min_space_for_dictionary_fetch_(kMinSpaceForDictionaryFetch),
+#if defined(OS_CHROMEOS)
+      // For debugging http://crbug.com/454198; remove when resolved.
+      destroyed_(0),
+#endif
       memory_pressure_listener_(
           base::Bind(&SdchOwner::OnMemoryPressure,
                      // Because |memory_pressure_listener_| is owned by
                      // SdchOwner, the SdchOwner object will be available
                      // for the lifetime of |memory_pressure_listener_|.
                      base::Unretained(this))) {
+#if defined(OS_CHROMEOS)
+  // For debugging http://crbug.com/454198; remove when resolved.
+  CHECK(clock_.get());
+#endif
   manager_->AddObserver(this);
 }
 
 SdchOwner::~SdchOwner() {
+#if defined(OS_CHROMEOS)
+  // For debugging http://crbug.com/454198; remove when resolved.
+  CHECK_EQ(0u, destroyed_);
+  CHECK(clock_.get());
+  clock_.reset();
+#endif
+
   for (auto it = local_dictionary_info_.begin();
        it != local_dictionary_info_.end(); ++it) {
     RecordDictionaryEviction(it->second.use_count,
                              DICTIONARY_FATE_EVICT_FOR_DESTRUCTION);
   }
   manager_->RemoveObserver(this);
+#if defined(OS_CHROMEOS)
+  destroyed_ = 0xdeadbeef;
+#endif
 }
 
 void SdchOwner::SetMaxTotalDictionarySize(size_t max_total_dictionary_size) {
@@ -138,6 +156,12 @@
     }
   };
 
+#if defined(OS_CHROMEOS)
+  // For debugging http://crbug.com/454198; remove when resolved.
+  CHECK_EQ(0u, destroyed_);
+  CHECK(clock_.get());
+#endif
+
   std::vector<DictionaryItem> stale_dictionary_list;
   size_t recoverable_bytes = 0;
   base::Time stale_boundary(clock_->Now() - base::TimeDelta::FromDays(1));
@@ -151,6 +175,12 @@
     }
   }
 
+#if defined(OS_CHROMEOS)
+  // For debugging http://crbug.com/454198; remove when resolved.
+  CHECK_EQ(0u, destroyed_);
+  CHECK(clock_.get());
+#endif
+
   if (total_dictionary_bytes_ + dictionary_text.size() - recoverable_bytes >
       max_total_dictionary_size_) {
     RecordDictionaryFate(DICTIONARY_FATE_FETCH_IGNORED_NO_SPACE);
@@ -198,6 +228,12 @@
       // to avoid taking too much time/space with useless dictionaries/one-off
       // visits to web sites.
       clock_->Now() - base::TimeDelta::FromHours(23), dictionary_text.size());
+
+#if defined(OS_CHROMEOS)
+  // For debugging http://crbug.com/454198; remove when resolved.
+  CHECK_EQ(0u, destroyed_);
+  CHECK(clock_.get());
+#endif
 }
 
 void SdchOwner::OnDictionaryUsed(SdchManager* manager,
@@ -212,6 +248,12 @@
 void SdchOwner::OnGetDictionary(net::SdchManager* manager,
                                 const GURL& request_url,
                                 const GURL& dictionary_url) {
+#if defined(OS_CHROMEOS)
+  // For debugging http://crbug.com/454198; remove when resolved.
+  CHECK_EQ(0u, destroyed_);
+  CHECK(clock_.get());
+#endif
+
   base::Time stale_boundary(clock_->Now() - base::TimeDelta::FromDays(1));
   size_t avail_bytes = 0;
   for (auto it = local_dictionary_info_.begin();
@@ -245,6 +287,12 @@
 
 void SdchOwner::SetClockForTesting(scoped_ptr<base::Clock> clock) {
   clock_ = clock.Pass();
+
+#if defined(OS_CHROMEOS)
+  // For debugging http://crbug.com/454198; remove when resolved.
+  CHECK_EQ(0u, destroyed_);
+  CHECK(clock_.get());
+#endif
 }
 
 void SdchOwner::OnMemoryPressure(
diff --git a/net/sdch/sdch_owner.h b/net/sdch/sdch_owner.h
index 9620326..93ef972 100644
--- a/net/sdch/sdch_owner.h
+++ b/net/sdch/sdch_owner.h
@@ -87,6 +87,11 @@
   size_t max_total_dictionary_size_;
   size_t min_space_for_dictionary_fetch_;
 
+#if defined(OS_CHROMEOS)
+  // For debugging http://crbug.com/454198; remove when resolved.
+  unsigned int destroyed_;
+#endif
+
   base::MemoryPressureListener memory_pressure_listener_;
 
   DISALLOW_COPY_AND_ASSIGN(SdchOwner);
diff --git a/net/socket/client_socket_pool_base.cc b/net/socket/client_socket_pool_base.cc
index c3ecd0d..d1ff627d 100644
--- a/net/socket/client_socket_pool_base.cc
+++ b/net/socket/client_socket_pool_base.cc
@@ -1125,10 +1125,6 @@
 
 void ClientSocketPoolBaseHelper::InvokeUserCallback(
     ClientSocketHandle* handle) {
-  // TODO(pkasting): Remove ScopedTracker below once crbug.com/455884 is fixed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "455884 ClientSocketPoolBaseHelper::InvokeUserCallback"));
   PendingCallbackMap::iterator it = pending_callback_map_.find(handle);
 
   // Exit if the request has already been cancelled.
diff --git a/net/socket/socks_client_socket_pool.cc b/net/socket/socks_client_socket_pool.cc
index e431227..e11b7a48 100644
--- a/net/socket/socks_client_socket_pool.cc
+++ b/net/socket/socks_client_socket_pool.cc
@@ -76,9 +76,6 @@
 }
 
 void SOCKSConnectJob::OnIOComplete(int result) {
-  // TODO(pkasting): Remove ScopedTracker below once crbug.com/455884 is fixed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION("455884 SOCKSConnectJob::OnIOComplete"));
   int rv = DoLoop(result);
   if (rv != ERR_IO_PENDING)
     NotifyDelegateOfCompletion(rv);  // Deletes |this|
diff --git a/net/socket/ssl_client_socket.cc b/net/socket/ssl_client_socket.cc
index f41ab32b3..babd41e 100644
--- a/net/socket/ssl_client_socket.cc
+++ b/net/socket/ssl_client_socket.cc
@@ -10,6 +10,7 @@
 #include "crypto/ec_private_key.h"
 #include "net/base/connection_type_histograms.h"
 #include "net/base/host_port_pair.h"
+#include "net/base/net_errors.h"
 #include "net/ssl/channel_id_service.h"
 #include "net/ssl/ssl_cipher_suite_names.h"
 #include "net/ssl/ssl_config_service.h"
@@ -101,7 +102,10 @@
 }
 
 bool SSLClientSocket::IgnoreCertError(int error, int load_flags) {
-  return error == OK || (load_flags & LOAD_IGNORE_ALL_CERT_ERRORS);
+  if (error == OK)
+    return true;
+  return (load_flags & LOAD_IGNORE_ALL_CERT_ERRORS) &&
+         IsCertificateError(error);
 }
 
 bool SSLClientSocket::set_was_npn_negotiated(bool negotiated) {
@@ -176,28 +180,6 @@
 }
 
 // static
-void SSLClientSocket::RecordConnectionTypeMetrics(int ssl_version) {
-  UpdateConnectionTypeHistograms(CONNECTION_SSL);
-  switch (ssl_version) {
-    case SSL_CONNECTION_VERSION_SSL2:
-      UpdateConnectionTypeHistograms(CONNECTION_SSL_SSL2);
-      break;
-    case SSL_CONNECTION_VERSION_SSL3:
-      UpdateConnectionTypeHistograms(CONNECTION_SSL_SSL3);
-      break;
-    case SSL_CONNECTION_VERSION_TLS1:
-      UpdateConnectionTypeHistograms(CONNECTION_SSL_TLS1);
-      break;
-    case SSL_CONNECTION_VERSION_TLS1_1:
-      UpdateConnectionTypeHistograms(CONNECTION_SSL_TLS1_1);
-      break;
-    case SSL_CONNECTION_VERSION_TLS1_2:
-      UpdateConnectionTypeHistograms(CONNECTION_SSL_TLS1_2);
-      break;
-  }
-}
-
-// static
 bool SSLClientSocket::IsChannelIDEnabled(
     const SSLConfig& ssl_config,
     ChannelIDService* channel_id_service) {
diff --git a/net/socket/ssl_client_socket.h b/net/socket/ssl_client_socket.h
index 46461df..90dbd01 100644
--- a/net/socket/ssl_client_socket.h
+++ b/net/socket/ssl_client_socket.h
@@ -148,6 +148,8 @@
 
   static const char* NextProtoStatusToString(const NextProtoStatus status);
 
+  // Returns true if |error| is OK or |load_flags| ignores certificate errors
+  // and |error| is a certificate error.
   static bool IgnoreCertError(int error, int load_flags);
 
   // ClearSessionCache clears the SSL session cache, used to resume SSL
@@ -201,9 +203,6 @@
       bool channel_id_enabled,
       bool supports_ecc);
 
-  // Records ConnectionType histograms for a successful SSL connection.
-  static void RecordConnectionTypeMetrics(int ssl_version);
-
   // Returns whether TLS channel ID is enabled.
   static bool IsChannelIDEnabled(
       const SSLConfig& ssl_config,
diff --git a/net/socket/ssl_client_socket_nss.cc b/net/socket/ssl_client_socket_nss.cc
index 011474f..1cc156bff 100644
--- a/net/socket/ssl_client_socket_nss.cc
+++ b/net/socket/ssl_client_socket_nss.cc
@@ -107,6 +107,7 @@
 #include "net/socket/client_socket_handle.h"
 #include "net/socket/nss_ssl_util.h"
 #include "net/ssl/ssl_cert_request_info.h"
+#include "net/ssl/ssl_cipher_suite_names.h"
 #include "net/ssl/ssl_connection_status_flags.h"
 #include "net/ssl/ssl_info.h"
 
@@ -1615,6 +1616,16 @@
     return SECSuccess;
   }
 
+  SSLChannelInfo channel_info;
+  SECStatus ok =
+      SSL_GetChannelInfo(socket, &channel_info, sizeof(channel_info));
+  if (ok != SECSuccess || channel_info.length != sizeof(channel_info) ||
+      channel_info.protocolVersion < SSL_LIBRARY_VERSION_TLS_1_2 ||
+      !IsSecureTLSCipherSuite(channel_info.cipherSuite)) {
+    *can_false_start = PR_FALSE;
+    return SECSuccess;
+  }
+
   return SSL_RecommendedCanFalseStart(socket, can_false_start);
 }
 
@@ -3526,15 +3537,6 @@
   // purposes.  See https://bugzilla.mozilla.org/show_bug.cgi?id=508081 and
   // http://crbug.com/15630 for more info.
 
-  // TODO(hclam): Skip logging if server cert was expected to be bad because
-  // |server_cert_verify_result_| doesn't contain all the information about
-  // the cert.
-  if (result == OK) {
-    int ssl_version =
-        SSLConnectionStatusToVersion(core_->state().ssl_connection_status);
-    RecordConnectionTypeMetrics(ssl_version);
-  }
-
   const CertStatus cert_status = server_cert_verify_result_.cert_status;
   if (transport_security_state_ &&
       (result == OK ||
diff --git a/net/socket/ssl_client_socket_openssl.cc b/net/socket/ssl_client_socket_openssl.cc
index c6752d47..f166b69f 100644
--- a/net/socket/ssl_client_socket_openssl.cc
+++ b/net/socket/ssl_client_socket_openssl.cc
@@ -35,6 +35,7 @@
 #include "net/cert/x509_util_openssl.h"
 #include "net/http/transport_security_state.h"
 #include "net/socket/ssl_session_cache_openssl.h"
+#include "net/ssl/scoped_openssl_types.h"
 #include "net/ssl/ssl_cert_request_info.h"
 #include "net/ssl/ssl_connection_status_flags.h"
 #include "net/ssl/ssl_info.h"
@@ -77,9 +78,7 @@
   sk_X509_pop_free(ptr, X509_free);
 }
 
-typedef crypto::ScopedOpenSSL<X509, X509_free>::Type ScopedX509;
-typedef crypto::ScopedOpenSSL<STACK_OF(X509), FreeX509Stack>::Type
-    ScopedX509Stack;
+using ScopedX509Stack = crypto::ScopedOpenSSL<STACK_OF(X509), FreeX509Stack>;
 
 #if OPENSSL_VERSION_NUMBER < 0x1000103fL
 // This method doesn't seem to have made it into the OpenSSL headers.
@@ -252,7 +251,7 @@
   // SSLClientSocketOpenSSL object from an SSL instance.
   int ssl_socket_data_index_;
 
-  crypto::ScopedOpenSSL<SSL_CTX, SSL_CTX_free>::Type ssl_ctx_;
+  ScopedSSL_CTX ssl_ctx_;
   // |session_cache_| must be destroyed before |ssl_ctx_|.
   SSLSessionCacheOpenSSL session_cache_;
 };
@@ -797,7 +796,7 @@
   mode.ConfigureFlag(SSL_MODE_RELEASE_BUFFERS, true);
   mode.ConfigureFlag(SSL_MODE_CBC_RECORD_SPLITTING, true);
 
-  mode.ConfigureFlag(SSL_MODE_HANDSHAKE_CUTTHROUGH,
+  mode.ConfigureFlag(SSL_MODE_ENABLE_FALSE_START,
                      ssl_config_.false_start_enabled);
 
   SSL_set_mode(ssl_, mode.set_mask);
@@ -1211,8 +1210,6 @@
   }
 
   if (result == OK) {
-    RecordConnectionTypeMetrics(GetNetSSLVersion(ssl_));
-
     if (SSL_session_reused(ssl_)) {
       // Record whether or not the server tried to resume a session for a
       // different version. See https://crbug.com/441456.
diff --git a/net/socket/ssl_client_socket_pool.cc b/net/socket/ssl_client_socket_pool.cc
index db28bec..827a4de 100644
--- a/net/socket/ssl_client_socket_pool.cc
+++ b/net/socket/ssl_client_socket_pool.cc
@@ -308,6 +308,10 @@
 }
 
 int SSLConnectJob::DoTransportConnect() {
+  // TODO(pkasting): Remove ScopedTracker below once crbug.com/455884 is fixed.
+  tracked_objects::ScopedTracker tracking_profile(
+      FROM_HERE_WITH_EXPLICIT_FUNCTION(
+          "455884 SSLConnectJob::DoTransportConnect"));
   DCHECK(transport_pool_);
 
   next_state_ = STATE_TRANSPORT_CONNECT_COMPLETE;
@@ -330,6 +334,9 @@
 }
 
 int SSLConnectJob::DoSOCKSConnect() {
+  // TODO(pkasting): Remove ScopedTracker below once crbug.com/455884 is fixed.
+  tracked_objects::ScopedTracker tracking_profile(
+      FROM_HERE_WITH_EXPLICIT_FUNCTION("455884 SSLConnectJob::DoSOCKSConnect"));
   DCHECK(socks_pool_);
   next_state_ = STATE_SOCKS_CONNECT_COMPLETE;
   transport_socket_handle_.reset(new ClientSocketHandle());
@@ -351,6 +358,10 @@
 }
 
 int SSLConnectJob::DoTunnelConnect() {
+  // TODO(pkasting): Remove ScopedTracker below once crbug.com/455884 is fixed.
+  tracked_objects::ScopedTracker tracking_profile(
+      FROM_HERE_WITH_EXPLICIT_FUNCTION(
+          "455884 SSLConnectJob::DoTunnelConnect"));
   DCHECK(http_proxy_pool_);
   next_state_ = STATE_TUNNEL_CONNECT_COMPLETE;
 
@@ -366,6 +377,10 @@
 }
 
 int SSLConnectJob::DoTunnelConnectComplete(int result) {
+  // TODO(pkasting): Remove ScopedTracker below once crbug.com/455884 is fixed.
+  tracked_objects::ScopedTracker tracking_profile(
+      FROM_HERE_WITH_EXPLICIT_FUNCTION(
+          "455884 SSLConnectJob::DoTunnelConnectComplete"));
   // Extract the information needed to prompt for appropriate proxy
   // authentication so that when ClientSocketPoolBaseHelper calls
   // |GetAdditionalErrorState|, we can easily set the state.
@@ -384,6 +399,10 @@
 }
 
 int SSLConnectJob::DoCreateSSLSocket() {
+  // TODO(pkasting): Remove ScopedTracker below once crbug.com/455884 is fixed.
+  tracked_objects::ScopedTracker tracking_profile(
+      FROM_HERE_WITH_EXPLICIT_FUNCTION(
+          "455884 SSLConnectJob::DoCreateSSLSocket"));
   next_state_ = STATE_CHECK_FOR_RESUME;
 
   // Reset the timeout to just the time allowed for the SSL handshake.
@@ -416,6 +435,10 @@
 }
 
 int SSLConnectJob::DoCheckForResume() {
+  // TODO(pkasting): Remove ScopedTracker below once crbug.com/455884 is fixed.
+  tracked_objects::ScopedTracker tracking_profile(
+      FROM_HERE_WITH_EXPLICIT_FUNCTION(
+          "455884 SSLConnectJob::DoCheckForResume"));
   next_state_ = STATE_SSL_CONNECT;
 
   if (!messenger_)
@@ -437,6 +460,9 @@
 }
 
 int SSLConnectJob::DoSSLConnect() {
+  // TODO(pkasting): Remove ScopedTracker below once crbug.com/455884 is fixed.
+  tracked_objects::ScopedTracker tracking_profile(
+      FROM_HERE_WITH_EXPLICIT_FUNCTION("455884 SSLConnectJob::DoSSLConnect"));
   next_state_ = STATE_SSL_CONNECT_COMPLETE;
 
   connect_timing_.ssl_start = base::TimeTicks::Now();
@@ -445,6 +471,10 @@
 }
 
 int SSLConnectJob::DoSSLConnectComplete(int result) {
+  // TODO(pkasting): Remove ScopedTracker below once crbug.com/455884 is fixed.
+  tracked_objects::ScopedTracker tracking_profile(
+      FROM_HERE_WITH_EXPLICIT_FUNCTION(
+          "455884 SSLConnectJob::DoSSLConnectComplete"));
   connect_timing_.ssl_end = base::TimeTicks::Now();
 
   SSLClientSocket::NextProtoStatus status =
@@ -499,7 +529,12 @@
                                100);
 
     SSLInfo ssl_info;
-    ssl_socket_->GetSSLInfo(&ssl_info);
+    bool has_ssl_info = ssl_socket_->GetSSLInfo(&ssl_info);
+    DCHECK(has_ssl_info);
+
+    UMA_HISTOGRAM_ENUMERATION("Net.SSLVersion", SSLConnectionStatusToVersion(
+                                                    ssl_info.connection_status),
+                              SSL_CONNECTION_VERSION_MAX);
 
     UMA_HISTOGRAM_SPARSE_SLOWLY("Net.SSL_CipherSuite",
                                 SSLConnectionStatusToCipherSuite(
diff --git a/net/socket/ssl_client_socket_unittest.cc b/net/socket/ssl_client_socket_unittest.cc
index 287dbb8..4486810 100644
--- a/net/socket/ssl_client_socket_unittest.cc
+++ b/net/socket/ssl_client_socket_unittest.cc
@@ -35,6 +35,19 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/platform_test.h"
 
+#if !defined(USE_OPENSSL)
+#include <pk11pub.h>
+#include "crypto/nss_util.h"
+
+#if !defined(CKM_AES_GCM)
+#define CKM_AES_GCM 0x00001087
+#endif
+
+#if !defined(CKM_NSS_TLS_MASTER_KEY_DERIVE_DH_SHA256)
+#define CKM_NSS_TLS_MASTER_KEY_DERIVE_DH_SHA256 (CKM_NSS + 24)
+#endif
+#endif
+
 //-----------------------------------------------------------------------------
 
 using testing::_;
@@ -989,6 +1002,16 @@
              log, i, NetLog::TYPE_SOCKET_BYTES_SENT, NetLog::PHASE_NONE);
 }
 
+bool SupportsAESGCM() {
+#if defined(USE_OPENSSL)
+  return true;
+#else
+  crypto::EnsureNSSInit();
+  return PK11_TokenExists(CKM_AES_GCM) &&
+         PK11_TokenExists(CKM_NSS_TLS_MASTER_KEY_DERIVE_DH_SHA256);
+#endif
+}
+
 }  // namespace
 
 TEST_F(SSLClientSocketTest, Connect) {
@@ -2904,10 +2927,17 @@
 
 TEST_F(SSLClientSocketFalseStartTest,
        HandshakeCallbackIsRun_WithFalseStartFailure) {
+  if (!SupportsAESGCM()) {
+    LOG(WARNING) << "Skipping test because AES-GCM is not supported.";
+    return;
+  }
+
   // False Start requires NPN and a forward-secret cipher suite.
   SpawnedTestServer::SSLOptions server_options;
   server_options.key_exchanges =
       SpawnedTestServer::SSLOptions::KEY_EXCHANGE_DHE_RSA;
+  server_options.bulk_ciphers =
+      SpawnedTestServer::SSLOptions::BULK_CIPHER_AES128GCM;
   server_options.enable_npn = true;
   SSLConfig client_config;
   client_config.next_protos.push_back(kProtoHTTP11);
@@ -2919,10 +2949,17 @@
 
 TEST_F(SSLClientSocketFalseStartTest,
        HandshakeCallbackIsRun_WithFalseStartSuccess) {
+  if (!SupportsAESGCM()) {
+    LOG(WARNING) << "Skipping test because AES-GCM is not supported.";
+    return;
+  }
+
   // False Start requires NPN and a forward-secret cipher suite.
   SpawnedTestServer::SSLOptions server_options;
   server_options.key_exchanges =
       SpawnedTestServer::SSLOptions::KEY_EXCHANGE_DHE_RSA;
+  server_options.bulk_ciphers =
+      SpawnedTestServer::SSLOptions::BULK_CIPHER_AES128GCM;
   server_options.enable_npn = true;
   SSLConfig client_config;
   client_config.next_protos.push_back(kProtoHTTP11);
@@ -2933,10 +2970,17 @@
 #endif  // defined(USE_OPENSSL)
 
 TEST_F(SSLClientSocketFalseStartTest, FalseStartEnabled) {
-  // False Start requires NPN and a forward-secret cipher suite.
+  if (!SupportsAESGCM()) {
+    LOG(WARNING) << "Skipping test because AES-GCM is not supported.";
+    return;
+  }
+
+  // False Start requires NPN/ALPN, perfect forward secrecy, and an AEAD.
   SpawnedTestServer::SSLOptions server_options;
   server_options.key_exchanges =
       SpawnedTestServer::SSLOptions::KEY_EXCHANGE_DHE_RSA;
+  server_options.bulk_ciphers =
+      SpawnedTestServer::SSLOptions::BULK_CIPHER_AES128GCM;
   server_options.enable_npn = true;
   SSLConfig client_config;
   client_config.next_protos.push_back(kProtoHTTP11);
@@ -2946,20 +2990,34 @@
 
 // Test that False Start is disabled without NPN.
 TEST_F(SSLClientSocketFalseStartTest, NoNPN) {
+  if (!SupportsAESGCM()) {
+    LOG(WARNING) << "Skipping test because AES-GCM is not supported.";
+    return;
+  }
+
   SpawnedTestServer::SSLOptions server_options;
   server_options.key_exchanges =
       SpawnedTestServer::SSLOptions::KEY_EXCHANGE_DHE_RSA;
+  server_options.bulk_ciphers =
+      SpawnedTestServer::SSLOptions::BULK_CIPHER_AES128GCM;
   SSLConfig client_config;
   client_config.next_protos.clear();
   ASSERT_NO_FATAL_FAILURE(
       TestFalseStart(server_options, client_config, false));
 }
 
-// Test that False Start is disabled without a forward-secret cipher suite.
+// Test that False Start is disabled without perfect forward secrecy.
 TEST_F(SSLClientSocketFalseStartTest, NoForwardSecrecy) {
+  if (!SupportsAESGCM()) {
+    LOG(WARNING) << "Skipping test because AES-GCM is not supported.";
+    return;
+  }
+
   SpawnedTestServer::SSLOptions server_options;
   server_options.key_exchanges =
       SpawnedTestServer::SSLOptions::KEY_EXCHANGE_RSA;
+  server_options.bulk_ciphers =
+      SpawnedTestServer::SSLOptions::BULK_CIPHER_AES128GCM;
   server_options.enable_npn = true;
   SSLConfig client_config;
   client_config.next_protos.push_back(kProtoHTTP11);
@@ -2967,12 +3025,32 @@
       TestFalseStart(server_options, client_config, false));
 }
 
+// Test that False Start is disabled without an AEAD.
+TEST_F(SSLClientSocketFalseStartTest, NoAEAD) {
+  SpawnedTestServer::SSLOptions server_options;
+  server_options.key_exchanges =
+      SpawnedTestServer::SSLOptions::KEY_EXCHANGE_DHE_RSA;
+  server_options.bulk_ciphers =
+      SpawnedTestServer::SSLOptions::BULK_CIPHER_AES128;
+  server_options.enable_npn = true;
+  SSLConfig client_config;
+  client_config.next_protos.push_back(kProtoHTTP11);
+  ASSERT_NO_FATAL_FAILURE(TestFalseStart(server_options, client_config, false));
+}
+
 // Test that sessions are resumable after receiving the server Finished message.
 TEST_F(SSLClientSocketFalseStartTest, SessionResumption) {
+  if (!SupportsAESGCM()) {
+    LOG(WARNING) << "Skipping test because AES-GCM is not supported.";
+    return;
+  }
+
   // Start a server.
   SpawnedTestServer::SSLOptions server_options;
   server_options.key_exchanges =
       SpawnedTestServer::SSLOptions::KEY_EXCHANGE_DHE_RSA;
+  server_options.bulk_ciphers =
+      SpawnedTestServer::SSLOptions::BULK_CIPHER_AES128GCM;
   server_options.enable_npn = true;
   SSLConfig client_config;
   client_config.next_protos.push_back(kProtoHTTP11);
@@ -3000,10 +3078,17 @@
 // Test that sessions are not resumable before receiving the server Finished
 // message.
 TEST_F(SSLClientSocketFalseStartTest, NoSessionResumptionBeforeFinish) {
+  if (!SupportsAESGCM()) {
+    LOG(WARNING) << "Skipping test because AES-GCM is not supported.";
+    return;
+  }
+
   // Start a server.
   SpawnedTestServer::SSLOptions server_options;
   server_options.key_exchanges =
       SpawnedTestServer::SSLOptions::KEY_EXCHANGE_DHE_RSA;
+  server_options.bulk_ciphers =
+      SpawnedTestServer::SSLOptions::BULK_CIPHER_AES128GCM;
   server_options.enable_npn = true;
   ASSERT_TRUE(StartTestServer(server_options));
 
diff --git a/net/socket/ssl_server_socket_openssl.cc b/net/socket/ssl_server_socket_openssl.cc
index 29d1ffa0..6bc13e6a 100644
--- a/net/socket/ssl_server_socket_openssl.cc
+++ b/net/socket/ssl_server_socket_openssl.cc
@@ -14,6 +14,7 @@
 #include "crypto/scoped_openssl_types.h"
 #include "net/base/net_errors.h"
 #include "net/ssl/openssl_ssl_util.h"
+#include "net/ssl/scoped_openssl_types.h"
 
 #define GotoState(s) next_handshake_state_ = s
 
@@ -606,9 +607,7 @@
 
   crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE);
 
-  crypto::ScopedOpenSSL<SSL_CTX, SSL_CTX_free>::Type ssl_ctx(
-      // It support SSLv2, SSLv3, and TLSv1.
-      SSL_CTX_new(SSLv23_server_method()));
+  ScopedSSL_CTX ssl_ctx(SSL_CTX_new(SSLv23_server_method()));
   ssl_ = SSL_new(ssl_ctx.get());
   if (!ssl_)
     return ERR_UNEXPECTED;
@@ -638,8 +637,7 @@
   const unsigned char* der_string_array =
       reinterpret_cast<const unsigned char*>(der_string.data());
 
-  crypto::ScopedOpenSSL<X509, X509_free>::Type x509(
-      d2i_X509(NULL, &der_string_array, der_string.length()));
+  ScopedX509 x509(d2i_X509(NULL, &der_string_array, der_string.length()));
   if (!x509.get())
     return ERR_UNEXPECTED;
 
diff --git a/net/socket/ssl_session_cache_openssl_unittest.cc b/net/socket/ssl_session_cache_openssl_unittest.cc
index 78bac63..21e7564 100644
--- a/net/socket/ssl_session_cache_openssl_unittest.cc
+++ b/net/socket/ssl_session_cache_openssl_unittest.cc
@@ -11,6 +11,7 @@
 #include "base/strings/stringprintf.h"
 #include "crypto/openssl_util.h"
 #include "crypto/scoped_openssl_types.h"
+#include "net/ssl/scoped_openssl_types.h"
 
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -31,9 +32,6 @@
 
 namespace {
 
-typedef crypto::ScopedOpenSSL<SSL, SSL_free>::Type ScopedSSL;
-typedef crypto::ScopedOpenSSL<SSL_CTX, SSL_CTX_free>::Type ScopedSSL_CTX;
-
 // Helper class used to associate arbitrary std::string keys with SSL objects.
 class SSLKeyHelper {
  public:
diff --git a/net/spdy/spdy_buffer.cc b/net/spdy/spdy_buffer.cc
index a1fd8c8..f7cd3786 100644
--- a/net/spdy/spdy_buffer.cc
+++ b/net/spdy/spdy_buffer.cc
@@ -8,7 +8,6 @@
 
 #include "base/callback.h"
 #include "base/logging.h"
-#include "base/profiler/scoped_tracker.h"
 #include "net/base/io_buffer.h"
 #include "net/spdy/spdy_protocol.h"
 
@@ -89,9 +88,6 @@
 }
 
 void SpdyBuffer::Consume(size_t consume_size) {
-  // TODO(pkasting): Remove ScopedTracker below once crbug.com/457517 is fixed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION("457517 SpdyBuffer::Consume"));
   ConsumeHelper(consume_size, CONSUME);
 };
 
diff --git a/net/spdy/spdy_session.cc b/net/spdy/spdy_session.cc
index 58e4af7b..dcdc928 100644
--- a/net/spdy/spdy_session.cc
+++ b/net/spdy/spdy_session.cc
@@ -1462,9 +1462,6 @@
 }
 
 void SpdySession::PumpWriteLoop(WriteState expected_write_state, int result) {
-  // TODO(pkasting): Remove ScopedTracker below once crbug.com/457517 is fixed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION("457517 SpdySession::PumpWriteLoop"));
   CHECK(!in_io_loop_);
   DCHECK_EQ(write_state_, expected_write_state);
 
@@ -1517,14 +1514,18 @@
 
 int SpdySession::DoWrite() {
   // TODO(pkasting): Remove ScopedTracker below once crbug.com/457517 is fixed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION("457517 SpdySession::DoWrite"));
+  tracked_objects::ScopedTracker tracking_profile1(
+      FROM_HERE_WITH_EXPLICIT_FUNCTION("457517 SpdySession::DoWrite1"));
   CHECK(in_io_loop_);
 
   DCHECK(buffered_spdy_framer_);
   if (in_flight_write_) {
     DCHECK_GT(in_flight_write_->GetRemainingSize(), 0u);
   } else {
+    // TODO(pkasting): Remove ScopedTracker below once crbug.com/457517 is
+    // fixed.
+    tracked_objects::ScopedTracker tracking_profile2(
+        FROM_HERE_WITH_EXPLICIT_FUNCTION("457517 SpdySession::DoWrite2"));
     // Grab the next frame to send.
     SpdyFrameType frame_type = DATA;
     scoped_ptr<SpdyBufferProducer> producer;
@@ -1540,6 +1541,10 @@
     // Activate the stream only when sending the SYN_STREAM frame to
     // guarantee monotonically-increasing stream IDs.
     if (frame_type == SYN_STREAM) {
+      // TODO(pkasting): Remove ScopedTracker below once crbug.com/457517 is
+      // fixed.
+      tracked_objects::ScopedTracker tracking_profile3(
+          FROM_HERE_WITH_EXPLICIT_FUNCTION("457517 SpdySession::DoWrite3"));
       CHECK(stream.get());
       CHECK_EQ(stream->stream_id(), 0u);
       scoped_ptr<SpdyStream> owned_stream =
@@ -1555,6 +1560,10 @@
       }
     }
 
+    // TODO(pkasting): Remove ScopedTracker below once crbug.com/457517 is
+    // fixed.
+    tracked_objects::ScopedTracker tracking_profile4(
+        FROM_HERE_WITH_EXPLICIT_FUNCTION("457517 SpdySession::DoWrite4"));
     in_flight_write_ = producer->ProduceBuffer();
     if (!in_flight_write_) {
       NOTREACHED();
@@ -1582,9 +1591,6 @@
 }
 
 int SpdySession::DoWriteComplete(int result) {
-  // TODO(pkasting): Remove ScopedTracker below once crbug.com/457517 is fixed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION("457517 SpdySession::DoWriteComplete"));
   CHECK(in_io_loop_);
   DCHECK_NE(result, ERR_IO_PENDING);
   DCHECK_GT(in_flight_write_->GetRemainingSize(), 0u);
@@ -1652,9 +1658,6 @@
 
 void SpdySession::StartGoingAway(SpdyStreamId last_good_stream_id,
                                  Error status) {
-  // TODO(pkasting): Remove ScopedTracker below once crbug.com/457517 is fixed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION("457517 SpdySession::StartGoingAway"));
   DCHECK_GE(availability_state_, STATE_GOING_AWAY);
 
   // The loops below are carefully written to avoid reentrancy problems.
diff --git a/net/spdy/spdy_stream.cc b/net/spdy/spdy_stream.cc
index 1059223..e7dcd5b 100644
--- a/net/spdy/spdy_stream.cc
+++ b/net/spdy/spdy_stream.cc
@@ -8,7 +8,6 @@
 #include "base/compiler_specific.h"
 #include "base/logging.h"
 #include "base/message_loop/message_loop.h"
-#include "base/profiler/scoped_tracker.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/stringprintf.h"
 #include "base/values.h"
@@ -529,10 +528,6 @@
 
 void SpdyStream::OnFrameWriteComplete(SpdyFrameType frame_type,
                                       size_t frame_size) {
-  // TODO(pkasting): Remove ScopedTracker below once crbug.com/457517 is fixed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "457517 SpdyStream::OnFrameWriteComplete"));
   DCHECK_NE(type_, SPDY_PUSH_STREAM);
 
   if (frame_size < session_->GetFrameMinimumSize() ||
diff --git a/net/ssl/scoped_openssl_types.h b/net/ssl/scoped_openssl_types.h
new file mode 100644
index 0000000..8bbdbd5
--- /dev/null
+++ b/net/ssl/scoped_openssl_types.h
@@ -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.
+
+#ifndef NET_SSL_SCOPED_OPENSSL_TYPES_H_
+#define NET_SSL_SCOPED_OPENSSL_TYPES_H_
+
+#include <openssl/ssl.h>
+#include <openssl/x509.h>
+
+#include "crypto/scoped_openssl_types.h"
+
+namespace net {
+
+using ScopedPKCS8_PRIV_KEY_INFO =
+    crypto::ScopedOpenSSL<PKCS8_PRIV_KEY_INFO, PKCS8_PRIV_KEY_INFO_free>;
+using ScopedSSL = crypto::ScopedOpenSSL<SSL, SSL_free>;
+using ScopedSSL_CTX = crypto::ScopedOpenSSL<SSL_CTX, SSL_CTX_free>;
+using ScopedX509 = crypto::ScopedOpenSSL<X509, X509_free>;
+
+}  // namespace net
+
+#endif  // NET_SSL_SCOPED_OPENSSL_TYPES_H_
diff --git a/net/ssl/ssl_connection_status_flags.h b/net/ssl/ssl_connection_status_flags.h
index d3bfed26..5d806ae7 100644
--- a/net/ssl/ssl_connection_status_flags.h
+++ b/net/ssl/ssl_connection_status_flags.h
@@ -37,7 +37,8 @@
 };
 
 // NOTE: the SSL version enum constants must be between 0 and
-// SSL_CONNECTION_VERSION_MASK, inclusive.
+// SSL_CONNECTION_VERSION_MASK, inclusive. These values are persisted to disk
+// and used in UMA, so they must remain stable.
 enum {
   SSL_CONNECTION_VERSION_UNKNOWN = 0,  // Unknown SSL version.
   SSL_CONNECTION_VERSION_SSL2 = 1,
diff --git a/net/third_party/nss/ssl/BUILD.gn b/net/third_party/nss/ssl/BUILD.gn
index 002bace8..a40b71b 100644
--- a/net/third_party/nss/ssl/BUILD.gn
+++ b/net/third_party/nss/ssl/BUILD.gn
@@ -70,8 +70,6 @@
   configs += [ "//build/config/compiler:no_chromium_code" ]
 
   if (is_win) {
-    cflags += [ "/wd4267" ]  # Disable warning: Conversion from size_t to 'type'.
-
     sources -= [
       "unix_err.c",
       "unix_err.h",
diff --git a/net/tools/net_watcher/net_watcher.cc b/net/tools/net_watcher/net_watcher.cc
index fb5c8e4..b6cf393 100644
--- a/net/tools/net_watcher/net_watcher.cc
+++ b/net/tools/net_watcher/net_watcher.cc
@@ -14,7 +14,6 @@
 #include "base/logging.h"
 #include "base/memory/scoped_ptr.h"
 #include "base/message_loop/message_loop.h"
-#include "base/profiler/scoped_tracker.h"
 #include "base/values.h"
 #include "build/build_config.h"
 #include "net/base/network_change_notifier.h"
@@ -115,11 +114,6 @@
   void OnProxyConfigChanged(
       const net::ProxyConfig& config,
       net::ProxyConfigService::ConfigAvailability availability) override {
-    // TODO(pkasting): Remove ScopedTracker below once crbug.com/455942 is
-    // fixed.
-    tracked_objects::ScopedTracker tracking_profile(
-        FROM_HERE_WITH_EXPLICIT_FUNCTION(
-            "455942 NetWatcher::OnProxyConfigChanged"));
     LOG(INFO) << "OnProxyConfigChanged("
               << ProxyConfigToString(config) << ", "
               << ConfigAvailabilityToString(availability) << ")";
diff --git a/net/tools/quic/quic_client_bin.cc b/net/tools/quic/quic_client_bin.cc
index 3800944..259111f 100644
--- a/net/tools/quic/quic_client_bin.cc
+++ b/net/tools/quic/quic_client_bin.cc
@@ -125,9 +125,9 @@
     FLAGS_host = line->GetSwitchValueASCII("host");
   }
   if (line->HasSwitch("port")) {
-    int port;
-    if (base::StringToInt(line->GetSwitchValueASCII("port"), &port)) {
-      FLAGS_port = port;
+    if (!base::StringToInt(line->GetSwitchValueASCII("port"), &FLAGS_port)) {
+      std::cerr << "--port must be an integer\n";
+      return 1;
     }
   }
   if (line->HasSwitch("body")) {
@@ -170,8 +170,14 @@
   GURL url(urls[0]);
   string host = FLAGS_host;
   // TODO(rtenneti): get ip_addr from hostname by doing host resolution.
-  CHECK(!host.empty());
-  net::ParseIPLiteralToNumber(host, &ip_addr);
+  if (host.empty()) {
+    LOG(ERROR) << "--host must be specified\n";
+    return 1;
+  }
+  if (!net::ParseIPLiteralToNumber(host, &ip_addr)) {
+    LOG(ERROR) << "--host could not be parsed as an IP address\n";
+    return 1;
+  }
 
   string host_port = net::IPAddressToStringWithPort(ip_addr, FLAGS_port);
   VLOG(1) << "Resolved " << host << " to " << host_port << endl;
diff --git a/net/url_request/url_fetcher_core.cc b/net/url_request/url_fetcher_core.cc
index 23857ad0..bbba964 100644
--- a/net/url_request/url_fetcher_core.cc
+++ b/net/url_request/url_fetcher_core.cc
@@ -541,10 +541,6 @@
 }
 
 void URLFetcherCore::StartOnIOThread() {
-  // TODO(pkasting): Remove ScopedTracker below once crbug.com/456327 is fixed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "456327 URLFetcherCore::StartOnIOThread"));
   DCHECK(network_task_runner_->BelongsToCurrentThread());
 
   if (!response_writer_)
@@ -558,9 +554,9 @@
 
 void URLFetcherCore::StartURLRequest() {
   // TODO(pkasting): Remove ScopedTracker below once crbug.com/456327 is fixed.
-  tracked_objects::ScopedTracker tracking_profile(
+  tracked_objects::ScopedTracker tracking_profile1(
       FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "456327 URLFetcherCore::StartURLRequest"));
+          "456327 URLFetcherCore::StartURLRequest1"));
   DCHECK(network_task_runner_->BelongsToCurrentThread());
 
   if (was_cancelled_) {
@@ -597,7 +593,12 @@
 
     case URLFetcher::POST:
     case URLFetcher::PUT:
-    case URLFetcher::PATCH:
+    case URLFetcher::PATCH: {
+      // TODO(pkasting): Remove ScopedTracker below once crbug.com/456327 is
+      // fixed.
+      tracked_objects::ScopedTracker tracking_profile2(
+          FROM_HERE_WITH_EXPLICIT_FUNCTION(
+              "456327 URLFetcherCore::StartURLRequest2"));
       // Upload content must be set.
       DCHECK(is_chunked_upload_ || upload_content_set_);
 
@@ -609,11 +610,21 @@
                                          upload_content_type_);
       }
       if (!upload_content_.empty()) {
+        // TODO(pkasting): Remove ScopedTracker below once crbug.com/456327 is
+        // fixed.
+        tracked_objects::ScopedTracker tracking_profile3(
+            FROM_HERE_WITH_EXPLICIT_FUNCTION(
+                "456327 URLFetcherCore::StartURLRequest3"));
         scoped_ptr<UploadElementReader> reader(new UploadBytesElementReader(
             upload_content_.data(), upload_content_.size()));
         request_->set_upload(
             ElementsUploadDataStream::CreateWithReader(reader.Pass(), 0));
       } else if (!upload_file_path_.empty()) {
+        // TODO(pkasting): Remove ScopedTracker below once crbug.com/456327 is
+        // fixed.
+        tracked_objects::ScopedTracker tracking_profile3(
+            FROM_HERE_WITH_EXPLICIT_FUNCTION(
+                "456327 URLFetcherCore::StartURLRequest4"));
         scoped_ptr<UploadElementReader> reader(
             new UploadFileElementReader(upload_file_task_runner_.get(),
                                         upload_file_path_,
@@ -623,6 +634,11 @@
         request_->set_upload(
             ElementsUploadDataStream::CreateWithReader(reader.Pass(), 0));
       } else if (!upload_stream_factory_.is_null()) {
+        // TODO(pkasting): Remove ScopedTracker below once crbug.com/456327 is
+        // fixed.
+        tracked_objects::ScopedTracker tracking_profile3(
+            FROM_HERE_WITH_EXPLICIT_FUNCTION(
+                "456327 URLFetcherCore::StartURLRequest5"));
         scoped_ptr<UploadDataStream> stream = upload_stream_factory_.Run();
         DCHECK(stream);
         request_->set_upload(stream.Pass());
@@ -639,6 +655,7 @@
           this,
           &URLFetcherCore::InformDelegateUploadProgress);
       break;
+    }
 
     case URLFetcher::HEAD:
       request_->set_method("HEAD");
@@ -679,11 +696,6 @@
 
   int64 delay = 0;
   if (!original_url_throttler_entry_.get()) {
-    // TODO(pkasting): Remove ScopedTracker below once crbug.com/456327 is
-    // fixed.
-    tracked_objects::ScopedTracker tracking_profile1(
-        FROM_HERE_WITH_EXPLICIT_FUNCTION(
-            "456327 URLFetcherCore::StartURLRequestWhenAppropriate1"));
     URLRequestThrottlerManager* manager =
         request_context_getter_->GetURLRequestContext()->throttler_manager();
     if (manager) {
@@ -692,11 +704,6 @@
     }
   }
   if (original_url_throttler_entry_.get()) {
-    // TODO(pkasting): Remove ScopedTracker below once crbug.com/456327 is
-    // fixed.
-    tracked_objects::ScopedTracker tracking_profile2(
-        FROM_HERE_WITH_EXPLICIT_FUNCTION(
-            "456327 URLFetcherCore::StartURLRequestWhenAppropriate2"));
     delay = original_url_throttler_entry_->ReserveSendingTimeForNextRequest(
         GetBackoffReleaseTime());
   }
diff --git a/net/url_request/url_request.cc b/net/url_request/url_request.cc
index 8777c06..752331c 100644
--- a/net/url_request/url_request.cc
+++ b/net/url_request/url_request.cc
@@ -209,7 +209,6 @@
                                      bool is_last_chunk) {
   DCHECK(upload_data_stream_);
   DCHECK(upload_data_stream_->is_chunked());
-  DCHECK_GT(bytes_len, 0);
   upload_chunked_data_stream_->AppendData(bytes, bytes_len, is_last_chunk);
 }
 
@@ -266,10 +265,6 @@
 }
 
 LoadStateWithParam URLRequest::GetLoadState() const {
-  // TODO(pkasting): Remove ScopedTracker below once crbug.com/455952 is
-  // fixed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION("455952 URLRequest::GetLoadState"));
   // The !blocked_by_.empty() check allows |this| to report it's blocked on a
   // delegate before it has been started.
   if (calling_delegate_ || !blocked_by_.empty()) {
@@ -994,13 +989,14 @@
 
 bool URLRequest::GetHSTSRedirect(GURL* redirect_url) const {
   const GURL& url = this->url();
-  if (!url.SchemeIs("http"))
+  bool scheme_is_http = url.SchemeIs("http");
+  if (!scheme_is_http && !url.SchemeIs("ws"))
     return false;
   TransportSecurityState* state = context()->transport_security_state();
   if (state && state->ShouldUpgradeToSSL(url.host())) {
-    url::Replacements<char> replacements;
-    const char kNewScheme[] = "https";
-    replacements.SetScheme(kNewScheme, url::Component(0, strlen(kNewScheme)));
+    GURL::Replacements replacements;
+    const char* new_scheme = scheme_is_http ? "https" : "wss";
+    replacements.SetSchemeStr(new_scheme);
     *redirect_url = url.ReplaceComponents(replacements);
     return true;
   }
diff --git a/net/url_request/url_request.h b/net/url_request/url_request.h
index 0d5b06f1..1623368 100644
--- a/net/url_request/url_request.h
+++ b/net/url_request/url_request.h
@@ -303,10 +303,14 @@
   void EnableChunkedUpload();
 
   // Appends the given bytes to the request's upload data to be sent
-  // immediately via chunked transfer encoding. When all data has been sent,
-  // call MarkEndOfChunks() to indicate the end of upload data.
+  // immediately via chunked transfer encoding. When all data has been added,
+  // set |is_last_chunk| to true to indicate the end of upload data.  All chunks
+  // but the last must have |bytes_len| > 0.
   //
   // This method may be called only after calling EnableChunkedUpload().
+  //
+  // Despite the name of this method, over-the-wire chunk boundaries will most
+  // likely not match the "chunks" appended with this function.
   void AppendChunkToUpload(const char* bytes,
                            int bytes_len,
                            bool is_last_chunk);
diff --git a/net/url_request/url_request_filter.cc b/net/url_request/url_request_filter.cc
index eaa3325..290bc99 100644
--- a/net/url_request/url_request_filter.cc
+++ b/net/url_request/url_request_filter.cc
@@ -138,10 +138,12 @@
   const std::string hostname = request->url().host();
   const std::string scheme = request->url().scheme();
 
-  HostnameInterceptorMap::const_iterator it =
-      hostname_interceptor_map_.find(make_pair(scheme, hostname));
-  if (it != hostname_interceptor_map_.end())
-    job = it->second->MaybeInterceptRequest(request, network_delegate);
+  {
+    HostnameInterceptorMap::const_iterator it =
+        hostname_interceptor_map_.find(make_pair(scheme, hostname));
+    if (it != hostname_interceptor_map_.end())
+      job = it->second->MaybeInterceptRequest(request, network_delegate);
+  }
 
   if (!job) {
     // Not in the hostname map, check the url map.
diff --git a/net/url_request/url_request_ftp_job.cc b/net/url_request/url_request_ftp_job.cc
index d1050e33..d774714 100644
--- a/net/url_request/url_request_ftp_job.cc
+++ b/net/url_request/url_request_ftp_job.cc
@@ -285,11 +285,6 @@
 }
 
 LoadState URLRequestFtpJob::GetLoadState() const {
-  // TODO(pkasting): Remove ScopedTracker below once crbug.com/455952 is
-  // fixed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "455952 URLRequestFtpJob::GetLoadState"));
   if (proxy_info_.is_direct()) {
     return ftp_transaction_ ?
         ftp_transaction_->GetLoadState() : LOAD_STATE_IDLE;
diff --git a/net/url_request/url_request_http_job.cc b/net/url_request/url_request_http_job.cc
index 098ef85..c763a05e 100644
--- a/net/url_request/url_request_http_job.cc
+++ b/net/url_request/url_request_http_job.cc
@@ -914,7 +914,6 @@
       NotifySSLCertificateError(info, true);
     } else {
       // Maybe overridable, maybe not. Ask the delegate to decide.
-      const URLRequestContext* context = request_->context();
       TransportSecurityState* state = context->transport_security_state();
       const bool fatal =
           state && state->ShouldSSLErrorsBeFatal(request_info_.url.host());
@@ -996,11 +995,6 @@
 }
 
 LoadState URLRequestHttpJob::GetLoadState() const {
-  // TODO(pkasting): Remove ScopedTracker below once crbug.com/455952 is
-  // fixed.
-  tracked_objects::ScopedTracker tracking_profile(
-      FROM_HERE_WITH_EXPLICIT_FUNCTION(
-          "455952 URLRequestHttpJob::GetLoadState"));
   return transaction_.get() ?
       transaction_->GetLoadState() : LOAD_STATE_IDLE;
 }
diff --git a/net/url_request/url_request_job_manager.cc b/net/url_request/url_request_job_manager.cc
index 9fc963dd..61e8bf2 100644
--- a/net/url_request/url_request_job_manager.cc
+++ b/net/url_request/url_request_job_manager.cc
@@ -76,10 +76,10 @@
   // See if the request should be handled by a built-in protocol factory.
   for (size_t i = 0; i < arraysize(kBuiltinFactories); ++i) {
     if (scheme == kBuiltinFactories[i].scheme) {
-      URLRequestJob* job = (kBuiltinFactories[i].factory)(
-          request, network_delegate, scheme);
-      DCHECK(job);  // The built-in factories are not expected to fail!
-      return job;
+      URLRequestJob* new_job =
+          (kBuiltinFactories[i].factory)(request, network_delegate, scheme);
+      DCHECK(new_job);  // The built-in factories are not expected to fail!
+      return new_job;
     }
   }
 
diff --git a/net/url_request/url_request_unittest.cc b/net/url_request/url_request_unittest.cc
index 5937464a..cfe3b718 100644
--- a/net/url_request/url_request_unittest.cc
+++ b/net/url_request/url_request_unittest.cc
@@ -7267,6 +7267,26 @@
   EXPECT_EQ(kOriginHeaderValue, received_cors_header);
 }
 
+// This just tests the behaviour of GetHSTSRedirect(). End-to-end tests of HSTS
+// are performed in net/websockets/websocket_end_to_end_test.cc.
+TEST(WebSocketURLRequestTest, HSTSApplied) {
+  TestNetworkDelegate network_delegate;
+  TransportSecurityState transport_security_state;
+  base::Time expiry = base::Time::Now() + base::TimeDelta::FromDays(1);
+  bool include_subdomains = false;
+  transport_security_state.AddHSTS("example.net", expiry, include_subdomains);
+  TestURLRequestContext context(true);
+  context.set_transport_security_state(&transport_security_state);
+  context.set_network_delegate(&network_delegate);
+  context.Init();
+  GURL ws_url("ws://example.net/echo");
+  TestDelegate delegate;
+  scoped_ptr<URLRequest> request(
+      context.CreateRequest(ws_url, DEFAULT_PRIORITY, &delegate, NULL));
+  EXPECT_TRUE(request->GetHSTSRedirect(&ws_url));
+  EXPECT_TRUE(ws_url.SchemeIs("wss"));
+}
+
 namespace {
 
 class SSLClientAuthTestDelegate : public TestDelegate {
diff --git a/net/websockets/websocket_end_to_end_test.cc b/net/websockets/websocket_end_to_end_test.cc
index 1a3df04..fc42db9 100644
--- a/net/websockets/websocket_end_to_end_test.cc
+++ b/net/websockets/websocket_end_to_end_test.cc
@@ -16,6 +16,7 @@
 #include "base/memory/scoped_ptr.h"
 #include "base/message_loop/message_loop.h"
 #include "base/run_loop.h"
+#include "base/strings/string_piece.h"
 #include "net/base/auth.h"
 #include "net/base/network_delegate.h"
 #include "net/base/test_data_directory.h"
@@ -33,6 +34,13 @@
 
 static const char kEchoServer[] = "echo-with-no-extension";
 
+// Simplify changing URL schemes.
+GURL ReplaceUrlScheme(const GURL& in_url, const base::StringPiece& scheme) {
+  GURL::Replacements replacements;
+  replacements.SetSchemeStr(scheme);
+  return in_url.ReplaceComponents(replacements);
+}
+
 // An implementation of WebSocketEventInterface that waits for and records the
 // results of the connect.
 class ConnectTestingEventInterface : public WebSocketEventInterface {
@@ -216,10 +224,10 @@
 class WebSocketEndToEndTest : public ::testing::Test {
  protected:
   WebSocketEndToEndTest()
-      : event_interface_(new ConnectTestingEventInterface),
+      : event_interface_(),
         network_delegate_(new TestNetworkDelegateWithProxyInfo),
         context_(true),
-        channel_(make_scoped_ptr(event_interface_), &context_),
+        channel_(),
         initialised_context_(false) {}
 
   // Initialise the URLRequestContext. Normally done automatically by
@@ -239,7 +247,10 @@
     }
     std::vector<std::string> sub_protocols;
     url::Origin origin("http://localhost");
-    channel_.SendAddChannelRequest(GURL(socket_url), sub_protocols, origin);
+    event_interface_ = new ConnectTestingEventInterface;
+    channel_.reset(
+        new WebSocketChannel(make_scoped_ptr(event_interface_), &context_));
+    channel_->SendAddChannelRequest(GURL(socket_url), sub_protocols, origin);
     event_interface_->WaitForResponse();
     return !event_interface_->failed();
   }
@@ -247,7 +258,7 @@
   ConnectTestingEventInterface* event_interface_;  // owned by channel_
   scoped_ptr<TestNetworkDelegateWithProxyInfo> network_delegate_;
   TestURLRequestContext context_;
-  WebSocketChannel channel_;
+  scoped_ptr<WebSocketChannel> channel_;
   bool initialised_context_;
 };
 
@@ -338,11 +349,9 @@
   // The test server doesn't have an unauthenticated proxy mode. WebSockets
   // cannot provide auth information that isn't already cached, so it's
   // necessary to preflight an HTTP request to authenticate against the proxy.
-  GURL::Replacements replacements;
-  replacements.SetSchemeStr("http");
   // It doesn't matter what the URL is, as long as it is an HTTP navigation.
   GURL http_page =
-      ws_server.GetURL("connect_check.html").ReplaceComponents(replacements);
+      ReplaceUrlScheme(ws_server.GetURL("connect_check.html"), "http");
   TestDelegate delegate;
   delegate.set_credentials(
       AuthCredentials(base::ASCIIToUTF16("foo"), base::ASCIIToUTF16("bar")));
@@ -377,6 +386,79 @@
   EXPECT_FALSE(ConnectAndWait(ws_url));
 }
 
+// Regression test for crbug.com/455215 "HSTS not applied to WebSocket"
+TEST_F(WebSocketEndToEndTest, DISABLED_ON_ANDROID(HstsHttpsToWebSocket)) {
+  SpawnedTestServer::SSLOptions ssl_options;
+  SpawnedTestServer https_server(
+      SpawnedTestServer::TYPE_HTTPS, ssl_options,
+      base::FilePath(FILE_PATH_LITERAL("net/data/url_request_unittest")));
+  SpawnedTestServer wss_server(SpawnedTestServer::TYPE_WSS, ssl_options,
+                               GetWebSocketTestDataDirectory());
+  ASSERT_TRUE(https_server.StartInBackground());
+  ASSERT_TRUE(wss_server.StartInBackground());
+  ASSERT_TRUE(https_server.BlockUntilStarted());
+  ASSERT_TRUE(wss_server.BlockUntilStarted());
+  InitialiseContext();
+  // Set HSTS via https:
+  TestDelegate delegate;
+  GURL https_page = https_server.GetURL("files/hsts-headers.html");
+  scoped_ptr<URLRequest> request(
+      context_.CreateRequest(https_page, DEFAULT_PRIORITY, &delegate, NULL));
+  request->Start();
+  // TestDelegate exits the message loop when the request completes.
+  base::RunLoop().Run();
+  EXPECT_TRUE(request->status().is_success());
+
+  // Check HSTS with ws:
+  // Change the scheme from wss: to ws: to verify that it is switched back.
+  GURL ws_url = ReplaceUrlScheme(wss_server.GetURL(kEchoServer), "ws");
+  EXPECT_TRUE(ConnectAndWait(ws_url));
+}
+
+TEST_F(WebSocketEndToEndTest, DISABLED_ON_ANDROID(HstsWebSocketToHttps)) {
+  SpawnedTestServer::SSLOptions ssl_options;
+  SpawnedTestServer https_server(
+      SpawnedTestServer::TYPE_HTTPS, ssl_options,
+      base::FilePath(FILE_PATH_LITERAL("net/data/url_request_unittest")));
+  SpawnedTestServer wss_server(SpawnedTestServer::TYPE_WSS, ssl_options,
+                               GetWebSocketTestDataDirectory());
+  ASSERT_TRUE(https_server.StartInBackground());
+  ASSERT_TRUE(wss_server.StartInBackground());
+  ASSERT_TRUE(https_server.BlockUntilStarted());
+  ASSERT_TRUE(wss_server.BlockUntilStarted());
+  InitialiseContext();
+  // Set HSTS via wss:
+  GURL wss_url = wss_server.GetURL("set-hsts");
+  EXPECT_TRUE(ConnectAndWait(wss_url));
+
+  // Verify via http:
+  TestDelegate delegate;
+  GURL http_page =
+      ReplaceUrlScheme(https_server.GetURL("files/simple.html"), "http");
+  scoped_ptr<URLRequest> request(
+      context_.CreateRequest(http_page, DEFAULT_PRIORITY, &delegate, NULL));
+  request->Start();
+  // TestDelegate exits the message loop when the request completes.
+  base::RunLoop().Run();
+  EXPECT_TRUE(request->status().is_success());
+  EXPECT_TRUE(request->url().SchemeIs("https"));
+}
+
+TEST_F(WebSocketEndToEndTest, DISABLED_ON_ANDROID(HstsWebSocketToWebSocket)) {
+  SpawnedTestServer::SSLOptions ssl_options;
+  SpawnedTestServer wss_server(SpawnedTestServer::TYPE_WSS, ssl_options,
+                               GetWebSocketTestDataDirectory());
+  ASSERT_TRUE(wss_server.Start());
+  InitialiseContext();
+  // Set HSTS via wss:
+  GURL wss_url = wss_server.GetURL("set-hsts");
+  EXPECT_TRUE(ConnectAndWait(wss_url));
+
+  // Verify via wss:
+  GURL ws_url = ReplaceUrlScheme(wss_server.GetURL(kEchoServer), "ws");
+  EXPECT_TRUE(ConnectAndWait(ws_url));
+}
+
 }  // namespace
 
 }  // namespace net
diff --git a/net/websockets/websocket_stream.cc b/net/websockets/websocket_stream.cc
index b5012bc..32729fb 100644
--- a/net/websockets/websocket_stream.cc
+++ b/net/websockets/websocket_stream.cc
@@ -24,7 +24,6 @@
 #include "net/websockets/websocket_handshake_constants.h"
 #include "net/websockets/websocket_handshake_stream_base.h"
 #include "net/websockets/websocket_handshake_stream_create_helper.h"
-#include "net/websockets/websocket_test_util.h"
 #include "url/gurl.h"
 #include "url/origin.h"
 
@@ -58,14 +57,7 @@
   // Implementation of URLRequest::Delegate methods.
   void OnReceivedRedirect(URLRequest* request,
                           const RedirectInfo& redirect_info,
-                          bool* defer_redirect) override {
-    // HTTP status codes returned by HttpStreamParser are filtered by
-    // WebSocketBasicHandshakeStream, and only 101, 401 and 407 are permitted
-    // back up the stack to HttpNetworkTransaction. In particular, redirect
-    // codes are never allowed, and so URLRequest never sees a redirect on a
-    // WebSocket request.
-    NOTREACHED();
-  }
+                          bool* defer_redirect) override;
 
   void OnResponseStarted(URLRequest* request) override;
 
@@ -121,7 +113,7 @@
 
   void Start(scoped_ptr<base::Timer> timer) {
     DCHECK(timer);
-    TimeDelta timeout(TimeDelta::FromSeconds(
+    base::TimeDelta timeout(base::TimeDelta::FromSeconds(
         kHandshakeTimeoutIntervalInSeconds));
     timer_ = timer.Pass();
     timer_->Start(FROM_HERE, timeout,
@@ -233,6 +225,31 @@
   URLRequest* url_request_;
 };
 
+void Delegate::OnReceivedRedirect(URLRequest* request,
+                                  const RedirectInfo& redirect_info,
+                                  bool* defer_redirect) {
+  // This code should never be reached for externally generated redirects,
+  // as WebSocketBasicHandshakeStream is responsible for filtering out
+  // all response codes besides 101, 401, and 407. As such, the URLRequest
+  // should never see a redirect sent over the network. However, internal
+  // redirects also result in this method being called, such as those
+  // caused by HSTS.
+  // Because it's security critical to prevent externally-generated
+  // redirects in WebSockets, perform additional checks to ensure this
+  // is only internal.
+  GURL::Replacements replacements;
+  replacements.SetSchemeStr("wss");
+  GURL expected_url = request->original_url().ReplaceComponents(replacements);
+  if (redirect_info.new_method != "GET" ||
+      redirect_info.new_url != expected_url) {
+    // This should not happen.
+    DLOG(FATAL) << "Unauthorized WebSocket redirect to "
+                << redirect_info.new_method << " "
+                << redirect_info.new_url.spec();
+    request->Cancel();
+  }
+}
+
 void Delegate::OnResponseStarted(URLRequest* request) {
   // TODO(vadimt): Remove ScopedTracker below once crbug.com/423948 is fixed.
   tracked_objects::ScopedTracker tracking_profile(
diff --git a/net/websockets/websocket_stream.h b/net/websockets/websocket_stream.h
index 46dc5262..713205a 100644
--- a/net/websockets/websocket_stream.h
+++ b/net/websockets/websocket_stream.h
@@ -22,6 +22,10 @@
 
 class GURL;
 
+namespace base {
+class Timer;
+}
+
 namespace url {
 class Origin;
 }  // namespace url
@@ -31,6 +35,7 @@
 class BoundNetLog;
 class URLRequestContext;
 struct WebSocketFrame;
+class WebSocketHandshakeStreamCreateHelper;
 
 // WebSocketStreamRequest is the caller's handle to the process of creation of a
 // WebSocketStream. Deleting the object before the OnSuccess or OnFailure
@@ -210,6 +215,20 @@
     const scoped_refptr<HttpResponseHeaders>& headers,
     base::Time response_time);
 
+// Alternate version of WebSocketStream::CreateAndConnectStream() for testing
+// use only. The differences are the use of a |create_helper| argument in place
+// of |requested_subprotocols| and taking |timer| as the handshake timeout
+// timer. Implemented in websocket_stream.cc.
+NET_EXPORT_PRIVATE scoped_ptr<WebSocketStreamRequest>
+CreateAndConnectStreamForTesting(
+    const GURL& socket_url,
+    scoped_ptr<WebSocketHandshakeStreamCreateHelper> create_helper,
+    const url::Origin& origin,
+    URLRequestContext* url_request_context,
+    const BoundNetLog& net_log,
+    scoped_ptr<WebSocketStream::ConnectDelegate> connect_delegate,
+    scoped_ptr<base::Timer> timer);
+
 }  // namespace net
 
 #endif  // NET_WEBSOCKETS_WEBSOCKET_STREAM_H_
diff --git a/net/websockets/websocket_test_util.h b/net/websockets/websocket_test_util.h
index 0230f5c9..a433ca9a 100644
--- a/net/websockets/websocket_test_util.h
+++ b/net/websockets/websocket_test_util.h
@@ -29,7 +29,6 @@
 class DeterministicSocketData;
 class ProxyService;
 class URLRequestContext;
-class WebSocketHandshakeStreamCreateHelper;
 struct SSLSocketDataProvider;
 
 class LinearCongruentialGenerator {
@@ -41,20 +40,6 @@
   uint64 current_;
 };
 
-// Alternate version of WebSocketStream::CreateAndConnectStream() for testing
-// use only. The differences are the use of a |create_helper| argument in place
-// of |requested_subprotocols| and taking |timer| as the handshake timeout
-// timer. Implemented in websocket_stream.cc.
-NET_EXPORT_PRIVATE scoped_ptr<WebSocketStreamRequest>
-CreateAndConnectStreamForTesting(
-    const GURL& socket_url,
-    scoped_ptr<WebSocketHandshakeStreamCreateHelper> create_helper,
-    const url::Origin& origin,
-    URLRequestContext* url_request_context,
-    const BoundNetLog& net_log,
-    scoped_ptr<WebSocketStream::ConnectDelegate> connect_delegate,
-    scoped_ptr<base::Timer> timer);
-
 // Generates a standard WebSocket handshake request. The challenge key used is
 // "dGhlIHNhbXBsZSBub25jZQ==". Each header in |extra_headers| must be terminated
 // with "\r\n".
diff --git a/pdf/BUILD.gn b/pdf/BUILD.gn
index 49f49a40..0f74323 100644
--- a/pdf/BUILD.gn
+++ b/pdf/BUILD.gn
@@ -45,6 +45,9 @@
     "thumbnail_control.h",
   ]
 
+  # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+  configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
+
   if (pdf_engine == 0) {
     sources += [
       "pdfium/pdfium_api_string_buffer_adapter.cc",
@@ -63,10 +66,6 @@
     ]
   }
 
-  if (is_win) {
-    cflags = [ "/wd4267" ]  # TODO(jschuh) size_t to int truncations.
-  }
-
   deps = [
     "//base",
     "//components/ui/zoom:ui_zoom",
diff --git a/ppapi/api/private/pp_video_capture_format.idl b/ppapi/api/private/pp_video_capture_format.idl
new file mode 100644
index 0000000..e934566
--- /dev/null
+++ b/ppapi/api/private/pp_video_capture_format.idl
@@ -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.
+ */
+
+/**
+ * This file defines the struct used to hold a video capture format.
+ */
+
+label Chrome {
+  M42 = 0.1
+};
+
+/**
+ * The <code>PP_VideoCaptureFormat</code> struct represents a video capture
+ * format.
+ */
+[assert_size(12)]
+struct PP_VideoCaptureFormat {
+  /**
+   * Frame size in pixels.
+   */
+  PP_Size frame_size;
+
+  /**
+   * Frame rate in frames per second.
+   */
+  float_t frame_rate;
+};
diff --git a/ppapi/api/private/ppb_camera_capabilities_private.idl b/ppapi/api/private/ppb_camera_capabilities_private.idl
index cb97fb1..fb480bf 100644
--- a/ppapi/api/private/ppb_camera_capabilities_private.idl
+++ b/ppapi/api/private/ppb_camera_capabilities_private.idl
@@ -36,20 +36,20 @@
       [in] PP_Resource resource);
 
   /**
-   * GetSupportedPreviewSizes() returns the supported preview sizes for the
-   * given <code>PPB_CameraCapabilities_Private</code>.
+   * GetSupportedVideoCaptureFormats() returns the supported video capture
+   * formats for the given <code>PPB_CameraCapabilities_Private</code>.
    *
    * @param[in] capabilities A <code>PP_Resource</code> corresponding to an
    * image capture capabilities resource.
    * @param[out] array_size The size of preview size array.
-   * @param[out] preview_sizes An array of <code>PP_Size</code> corresponding
-   * to the supported preview sizes in pixels. The ownership of the array
-   * belongs to <code>PPB_CameraCapabilities_Private</code> and the caller
-   * should not free it. When a PPB_CameraCapabilities_Private is deleted,
-   * the array returning from this is no longer valid.
+   * @param[out] formats An array of <code>PP_VideoCaptureFormat</code>
+   * corresponding to the supported video capture formats. The ownership of the
+   * array belongs to <code>PPB_CameraCapabilities_Private</code> and the caller
+   * should not free it. When a PPB_CameraCapabilities_Private is deleted, the
+   * array returning from this is no longer valid.
    */
-  void GetSupportedPreviewSizes(
+  void GetSupportedVideoCaptureFormats(
       [in] PP_Resource capabilities,
-      [out] int32_t array_size,
-      [out, size_is(array_size)] PP_Size[] preview_sizes);
+      [out] uint32_t array_size,
+      [out, size_is(array_size)] PP_VideoCaptureFormat[] formats);
 };
diff --git a/ppapi/api/private/ppb_camera_device_private.idl b/ppapi/api/private/ppb_camera_device_private.idl
new file mode 100644
index 0000000..d87ac2059
--- /dev/null
+++ b/ppapi/api/private/ppb_camera_device_private.idl
@@ -0,0 +1,96 @@
+/* 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.
+ */
+
+/**
+ * Defines the <code>PPB_CameraDevice_Private</code> interface. Used for
+ * manipulating a camera device.
+ */
+
+[generate_thunk]
+
+label Chrome {
+  M42 = 0.1
+};
+
+/**
+ * To query camera capabilities:
+ * 1. Get a PPB_CameraDevice_Private object by Create().
+ * 2. Open() camera device with track id of MediaStream video track.
+ * 3. Call GetCameraCapabilities() to get a
+ *    <code>PPB_CameraCapabilities_Private</code> object, which can be used to
+ *    query camera capabilities.
+ */
+interface PPB_CameraDevice_Private {
+  /**
+   * Creates a PPB_CameraDevice_Private resource.
+   *
+   * @param[in] instance A <code>PP_Instance</code> identifying one instance
+   * of a module.
+   *
+   * @return A <code>PP_Resource</code> corresponding to a
+   * PPB_CameraDevice_Private resource if successful, 0 if failed.
+   */
+  PP_Resource Create([in] PP_Instance instance);
+
+  /**
+   * Determines if a resource is a camera device resource.
+   *
+   * @param[in] resource The <code>PP_Resource</code> to test.
+   *
+   * @return A <code>PP_Bool</code> with <code>PP_TRUE</code> if the given
+   * resource is a camera device resource or <code>PP_FALSE</code>
+   * otherwise.
+   */
+  PP_Bool IsCameraDevice([in] PP_Resource resource);
+
+  /**
+   * Opens a camera device.
+   *
+   * @param[in] camera_device A <code>PP_Resource</code> corresponding to a
+   * camera device resource.
+   * @param[in] device_id A <code>PP_Var</code> identifying a camera device. The
+   * type is string. The ID can be obtained from MediaStreamTrack.getSources()
+   * or MediaStreamVideoTrack.id.
+   * @param[in] callback A <code>PP_CompletionCallback</code> to be called upon
+   * completion of <code>Open()</code>.
+   *
+   * @return An error code from <code>pp_errors.h</code>.
+   */
+  int32_t Open(
+      [in] PP_Resource camera_device,
+      [in] PP_Var device_id,
+      [in] PP_CompletionCallback callback);
+
+  /**
+   * Disconnects from the camera and cancels all pending requests.
+   * After this returns, no callbacks will be called. If <code>
+   * PPB_CameraDevice_Private</code> is destroyed and is not closed yet, this
+   * function will be automatically called. Calling this more than once has no
+   * effect.
+   *
+   * @param[in] camera_device A <code>PP_Resource</code> corresponding to a
+   * camera device resource.
+   */
+  void Close([in] PP_Resource camera_device);
+
+  /**
+   * Gets the camera capabilities.
+   *
+   * The camera capabilities do not change for a given camera source.
+   *
+   * @param[in] camera_device A <code>PP_Resource</code> corresponding to a
+   * camera device resource.
+   * @param[out] capabilities A <code>PPB_CameraCapabilities_Private</code> for
+   * storing the camera capabilities on success. Otherwise, the value will not
+   * be changed.
+   * @param[in] callback <code>PP_CompletionCallback</code> to be called upon
+   * completion of <code>GetCameraCapabilities()</code>.
+   *
+   * @return An int32_t containing a result code from <code>pp_errors.h</code>.
+   */
+  int32_t GetCameraCapabilities([in] PP_Resource camera_device,
+                                [out] PP_Resource capabilities,
+                                [in] PP_CompletionCallback callback);
+};
diff --git a/ppapi/api/private/ppb_image_capture_private.idl b/ppapi/api/private/ppb_image_capture_private.idl
deleted file mode 100644
index 75c75d5..0000000
--- a/ppapi/api/private/ppb_image_capture_private.idl
+++ /dev/null
@@ -1,96 +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.
- */
-
-/**
- * Defines the <code>PPB_ImageCapture_Private</code> interface. Used for
- * acquiring a single still image from a camera source.
- */
-
-[generate_thunk]
-
-label Chrome {
-  M42 = 0.1
-};
-
-/**
- * To query camera capabilities:
- * 1. Get a PPB_ImageCapture_Private object by Create().
- * 2. Open() camera device with track id of MediaStream video track.
- * 3. Call GetCameraCapabilities() to get a
- *    <code>PPB_CameraCapabilities_Private</code> object, which can be used to
- *    query camera capabilities.
- */
-interface PPB_ImageCapture_Private {
-  /**
-   * Creates a PPB_ImageCapture_Private resource.
-   *
-   * @param[in] instance A <code>PP_Instance</code> identifying one instance
-   * of a module.
-   *
-   * @return A <code>PP_Resource</code> corresponding to a
-   * PPB_ImageCapture_Private resource if successful, 0 if failed.
-   */
-  PP_Resource Create([in] PP_Instance instance);
-
-  /**
-   * Determines if a resource is an image capture resource.
-   *
-   * @param[in] resource The <code>PP_Resource</code> to test.
-   *
-   * @return A <code>PP_Bool</code> with <code>PP_TRUE</code> if the given
-   * resource is an image capture resource or <code>PP_FALSE</code>
-   * otherwise.
-   */
-  PP_Bool IsImageCapture([in] PP_Resource resource);
-
-  /**
-   * Opens a video capture device.
-   *
-   * @param[in] image_capture A <code>PP_Resource</code> corresponding to an
-   * image capture resource.
-   * @param[in] device_id A <code>PP_Var</code> identifying a camera device. The
-   * type is string. The ID can be obtained from MediaStreamTrack.getSources()
-   * or MediaStreamVideoTrack.id.
-   * @param[in] callback A <code>PP_CompletionCallback</code> to be called upon
-   * completion of <code>Open()</code>.
-   *
-   * @return An error code from <code>pp_errors.h</code>.
-   */
-  int32_t Open(
-      [in] PP_Resource image_capture,
-      [in] PP_Var device_id,
-      [in] PP_CompletionCallback callback);
-
-  /**
-   * Disconnects from the camera and cancels all pending capture requests.
-   * After this returns, no callbacks will be called. If <code>
-   * PPB_ImageCapture_Private</code> is destroyed and is not closed yet, this
-   * function will be automatically called. Calling this more than once has no
-   * effect.
-   *
-   * @param[in] image_capture A <code>PP_Resource</code> corresponding to an
-   * image capture resource.
-   */
-  void Close([in] PP_Resource image_capture);
-
-  /**
-   * Gets the camera capabilities.
-   *
-   * The camera capabilities do not change for a given camera source.
-   *
-   * @param[in] image_capture A <code>PP_Resource</code> corresponding to an
-   * image capture resource.
-   * @param[out] capabilities A <code>PPB_CameraCapabilities_Private</code> for
-   * storing the image capture capabilities on success. Otherwise, the value
-   * will not be changed.
-   * @param[in] callback <code>PP_CompletionCallback</code> to be called upon
-   * completion of <code>GetCameraCapabilities()</code>.
-   *
-   * @return An int32_t containing a result code from <code>pp_errors.h</code>.
-   */
-  int32_t GetCameraCapabilities([in] PP_Resource image_capture,
-                                [out] PP_Resource capabilities,
-                                [in] PP_CompletionCallback callback);
-};
diff --git a/ppapi/c/private/pp_video_capture_format.h b/ppapi/c/private/pp_video_capture_format.h
new file mode 100644
index 0000000..4df9cba
--- /dev/null
+++ b/ppapi/c/private/pp_video_capture_format.h
@@ -0,0 +1,47 @@
+/* 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.
+ */
+
+/* From private/pp_video_capture_format.idl,
+ *   modified Wed Feb 18 01:41:26 2015.
+ */
+
+#ifndef PPAPI_C_PRIVATE_PP_VIDEO_CAPTURE_FORMAT_H_
+#define PPAPI_C_PRIVATE_PP_VIDEO_CAPTURE_FORMAT_H_
+
+#include "ppapi/c/pp_macros.h"
+#include "ppapi/c/pp_size.h"
+#include "ppapi/c/pp_stdint.h"
+
+/**
+ * @file
+ * This file defines the struct used to hold a video capture format.
+ */
+
+
+/**
+ * @addtogroup Structs
+ * @{
+ */
+/**
+ * The <code>PP_VideoCaptureFormat</code> struct represents a video capture
+ * format.
+ */
+struct PP_VideoCaptureFormat {
+  /**
+   * Frame size in pixels.
+   */
+  struct PP_Size frame_size;
+  /**
+   * Frame rate in frames per second.
+   */
+  float frame_rate;
+};
+PP_COMPILE_ASSERT_STRUCT_SIZE_IN_BYTES(PP_VideoCaptureFormat, 12);
+/**
+ * @}
+ */
+
+#endif  /* PPAPI_C_PRIVATE_PP_VIDEO_CAPTURE_FORMAT_H_ */
+
diff --git a/ppapi/c/private/ppb_camera_capabilities_private.h b/ppapi/c/private/ppb_camera_capabilities_private.h
index 7326549c..269cd7a 100644
--- a/ppapi/c/private/ppb_camera_capabilities_private.h
+++ b/ppapi/c/private/ppb_camera_capabilities_private.h
@@ -4,7 +4,7 @@
  */
 
 /* From private/ppb_camera_capabilities_private.idl,
- *   modified Tue Feb  3 19:54:34 2015.
+ *   modified Thu Feb 19 09:06:18 2015.
  */
 
 #ifndef PPAPI_C_PRIVATE_PPB_CAMERA_CAPABILITIES_PRIVATE_H_
@@ -15,6 +15,7 @@
 #include "ppapi/c/pp_resource.h"
 #include "ppapi/c/pp_size.h"
 #include "ppapi/c/pp_stdint.h"
+#include "ppapi/c/private/pp_video_capture_format.h"
 
 #define PPB_CAMERACAPABILITIES_PRIVATE_INTERFACE_0_1 \
     "PPB_CameraCapabilities_Private;0.1"
@@ -51,21 +52,22 @@
    */
   PP_Bool (*IsCameraCapabilities)(PP_Resource resource);
   /**
-   * GetSupportedPreviewSizes() returns the supported preview sizes for the
-   * given <code>PPB_CameraCapabilities_Private</code>.
+   * GetSupportedVideoCaptureFormats() returns the supported video capture
+   * formats for the given <code>PPB_CameraCapabilities_Private</code>.
    *
    * @param[in] capabilities A <code>PP_Resource</code> corresponding to an
    * image capture capabilities resource.
    * @param[out] array_size The size of preview size array.
-   * @param[out] preview_sizes An array of <code>PP_Size</code> corresponding
-   * to the supported preview sizes in pixels. The ownership of the array
-   * belongs to <code>PPB_CameraCapabilities_Private</code> and the caller
-   * should not free it. When a PPB_CameraCapabilities_Private is deleted,
-   * the array returning from this is no longer valid.
+   * @param[out] formats An array of <code>PP_VideoCaptureFormat</code>
+   * corresponding to the supported video capture formats. The ownership of the
+   * array belongs to <code>PPB_CameraCapabilities_Private</code> and the caller
+   * should not free it. When a PPB_CameraCapabilities_Private is deleted, the
+   * array returning from this is no longer valid.
    */
-  void (*GetSupportedPreviewSizes)(PP_Resource capabilities,
-                                   int32_t* array_size,
-                                   struct PP_Size** preview_sizes);
+  void (*GetSupportedVideoCaptureFormats)(
+      PP_Resource capabilities,
+      uint32_t* array_size,
+      struct PP_VideoCaptureFormat** formats);
 };
 
 typedef struct PPB_CameraCapabilities_Private_0_1
diff --git a/ppapi/c/private/ppb_camera_device_private.h b/ppapi/c/private/ppb_camera_device_private.h
new file mode 100644
index 0000000..821ad299
--- /dev/null
+++ b/ppapi/c/private/ppb_camera_device_private.h
@@ -0,0 +1,118 @@
+/* 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.
+ */
+
+/* From private/ppb_camera_device_private.idl,
+ *   modified Fri Feb 20 13:48:52 2015.
+ */
+
+#ifndef PPAPI_C_PRIVATE_PPB_CAMERA_DEVICE_PRIVATE_H_
+#define PPAPI_C_PRIVATE_PPB_CAMERA_DEVICE_PRIVATE_H_
+
+#include "ppapi/c/pp_bool.h"
+#include "ppapi/c/pp_completion_callback.h"
+#include "ppapi/c/pp_instance.h"
+#include "ppapi/c/pp_macros.h"
+#include "ppapi/c/pp_resource.h"
+#include "ppapi/c/pp_stdint.h"
+#include "ppapi/c/pp_var.h"
+
+#define PPB_CAMERADEVICE_PRIVATE_INTERFACE_0_1 "PPB_CameraDevice_Private;0.1"
+#define PPB_CAMERADEVICE_PRIVATE_INTERFACE \
+    PPB_CAMERADEVICE_PRIVATE_INTERFACE_0_1
+
+/**
+ * @file
+ * Defines the <code>PPB_CameraDevice_Private</code> interface. Used for
+ * manipulating a camera device.
+ */
+
+
+/**
+ * @addtogroup Interfaces
+ * @{
+ */
+/**
+ * To query camera capabilities:
+ * 1. Get a PPB_CameraDevice_Private object by Create().
+ * 2. Open() camera device with track id of MediaStream video track.
+ * 3. Call GetCameraCapabilities() to get a
+ *    <code>PPB_CameraCapabilities_Private</code> object, which can be used to
+ *    query camera capabilities.
+ */
+struct PPB_CameraDevice_Private_0_1 {
+  /**
+   * Creates a PPB_CameraDevice_Private resource.
+   *
+   * @param[in] instance A <code>PP_Instance</code> identifying one instance
+   * of a module.
+   *
+   * @return A <code>PP_Resource</code> corresponding to a
+   * PPB_CameraDevice_Private resource if successful, 0 if failed.
+   */
+  PP_Resource (*Create)(PP_Instance instance);
+  /**
+   * Determines if a resource is a camera device resource.
+   *
+   * @param[in] resource The <code>PP_Resource</code> to test.
+   *
+   * @return A <code>PP_Bool</code> with <code>PP_TRUE</code> if the given
+   * resource is a camera device resource or <code>PP_FALSE</code>
+   * otherwise.
+   */
+  PP_Bool (*IsCameraDevice)(PP_Resource resource);
+  /**
+   * Opens a camera device.
+   *
+   * @param[in] camera_device A <code>PP_Resource</code> corresponding to a
+   * camera device resource.
+   * @param[in] device_id A <code>PP_Var</code> identifying a camera device. The
+   * type is string. The ID can be obtained from MediaStreamTrack.getSources()
+   * or MediaStreamVideoTrack.id.
+   * @param[in] callback A <code>PP_CompletionCallback</code> to be called upon
+   * completion of <code>Open()</code>.
+   *
+   * @return An error code from <code>pp_errors.h</code>.
+   */
+  int32_t (*Open)(PP_Resource camera_device,
+                  struct PP_Var device_id,
+                  struct PP_CompletionCallback callback);
+  /**
+   * Disconnects from the camera and cancels all pending requests.
+   * After this returns, no callbacks will be called. If <code>
+   * PPB_CameraDevice_Private</code> is destroyed and is not closed yet, this
+   * function will be automatically called. Calling this more than once has no
+   * effect.
+   *
+   * @param[in] camera_device A <code>PP_Resource</code> corresponding to a
+   * camera device resource.
+   */
+  void (*Close)(PP_Resource camera_device);
+  /**
+   * Gets the camera capabilities.
+   *
+   * The camera capabilities do not change for a given camera source.
+   *
+   * @param[in] camera_device A <code>PP_Resource</code> corresponding to a
+   * camera device resource.
+   * @param[out] capabilities A <code>PPB_CameraCapabilities_Private</code> for
+   * storing the camera capabilities on success. Otherwise, the value will not
+   * be changed.
+   * @param[in] callback <code>PP_CompletionCallback</code> to be called upon
+   * completion of <code>GetCameraCapabilities()</code>.
+   *
+   * @return An int32_t containing a result code from <code>pp_errors.h</code>.
+   */
+  int32_t (*GetCameraCapabilities)(PP_Resource camera_device,
+                                   PP_Resource* capabilities,
+                                   struct PP_CompletionCallback callback);
+};
+
+typedef struct PPB_CameraDevice_Private_0_1 PPB_CameraDevice_Private;
+/**
+ * @}
+ */
+
+#endif  /* PPAPI_C_PRIVATE_PPB_CAMERA_DEVICE_PRIVATE_H_ */
+
diff --git a/ppapi/c/private/ppb_image_capture_private.h b/ppapi/c/private/ppb_image_capture_private.h
deleted file mode 100644
index 23dfdef..0000000
--- a/ppapi/c/private/ppb_image_capture_private.h
+++ /dev/null
@@ -1,118 +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.
- */
-
-/* From private/ppb_image_capture_private.idl,
- *   modified Fri Feb  6 15:40:49 2015.
- */
-
-#ifndef PPAPI_C_PRIVATE_PPB_IMAGE_CAPTURE_PRIVATE_H_
-#define PPAPI_C_PRIVATE_PPB_IMAGE_CAPTURE_PRIVATE_H_
-
-#include "ppapi/c/pp_bool.h"
-#include "ppapi/c/pp_completion_callback.h"
-#include "ppapi/c/pp_instance.h"
-#include "ppapi/c/pp_macros.h"
-#include "ppapi/c/pp_resource.h"
-#include "ppapi/c/pp_stdint.h"
-#include "ppapi/c/pp_var.h"
-
-#define PPB_IMAGECAPTURE_PRIVATE_INTERFACE_0_1 "PPB_ImageCapture_Private;0.1"
-#define PPB_IMAGECAPTURE_PRIVATE_INTERFACE \
-    PPB_IMAGECAPTURE_PRIVATE_INTERFACE_0_1
-
-/**
- * @file
- * Defines the <code>PPB_ImageCapture_Private</code> interface. Used for
- * acquiring a single still image from a camera source.
- */
-
-
-/**
- * @addtogroup Interfaces
- * @{
- */
-/**
- * To query camera capabilities:
- * 1. Get a PPB_ImageCapture_Private object by Create().
- * 2. Open() camera device with track id of MediaStream video track.
- * 3. Call GetCameraCapabilities() to get a
- *    <code>PPB_CameraCapabilities_Private</code> object, which can be used to
- *    query camera capabilities.
- */
-struct PPB_ImageCapture_Private_0_1 {
-  /**
-   * Creates a PPB_ImageCapture_Private resource.
-   *
-   * @param[in] instance A <code>PP_Instance</code> identifying one instance
-   * of a module.
-   *
-   * @return A <code>PP_Resource</code> corresponding to a
-   * PPB_ImageCapture_Private resource if successful, 0 if failed.
-   */
-  PP_Resource (*Create)(PP_Instance instance);
-  /**
-   * Determines if a resource is an image capture resource.
-   *
-   * @param[in] resource The <code>PP_Resource</code> to test.
-   *
-   * @return A <code>PP_Bool</code> with <code>PP_TRUE</code> if the given
-   * resource is an image capture resource or <code>PP_FALSE</code>
-   * otherwise.
-   */
-  PP_Bool (*IsImageCapture)(PP_Resource resource);
-  /**
-   * Opens a video capture device.
-   *
-   * @param[in] image_capture A <code>PP_Resource</code> corresponding to an
-   * image capture resource.
-   * @param[in] device_id A <code>PP_Var</code> identifying a camera device. The
-   * type is string. The ID can be obtained from MediaStreamTrack.getSources()
-   * or MediaStreamVideoTrack.id.
-   * @param[in] callback A <code>PP_CompletionCallback</code> to be called upon
-   * completion of <code>Open()</code>.
-   *
-   * @return An error code from <code>pp_errors.h</code>.
-   */
-  int32_t (*Open)(PP_Resource image_capture,
-                  struct PP_Var device_id,
-                  struct PP_CompletionCallback callback);
-  /**
-   * Disconnects from the camera and cancels all pending capture requests.
-   * After this returns, no callbacks will be called. If <code>
-   * PPB_ImageCapture_Private</code> is destroyed and is not closed yet, this
-   * function will be automatically called. Calling this more than once has no
-   * effect.
-   *
-   * @param[in] image_capture A <code>PP_Resource</code> corresponding to an
-   * image capture resource.
-   */
-  void (*Close)(PP_Resource image_capture);
-  /**
-   * Gets the camera capabilities.
-   *
-   * The camera capabilities do not change for a given camera source.
-   *
-   * @param[in] image_capture A <code>PP_Resource</code> corresponding to an
-   * image capture resource.
-   * @param[out] capabilities A <code>PPB_CameraCapabilities_Private</code> for
-   * storing the image capture capabilities on success. Otherwise, the value
-   * will not be changed.
-   * @param[in] callback <code>PP_CompletionCallback</code> to be called upon
-   * completion of <code>GetCameraCapabilities()</code>.
-   *
-   * @return An int32_t containing a result code from <code>pp_errors.h</code>.
-   */
-  int32_t (*GetCameraCapabilities)(PP_Resource image_capture,
-                                   PP_Resource* capabilities,
-                                   struct PP_CompletionCallback callback);
-};
-
-typedef struct PPB_ImageCapture_Private_0_1 PPB_ImageCapture_Private;
-/**
- * @}
- */
-
-#endif  /* PPAPI_C_PRIVATE_PPB_IMAGE_CAPTURE_PRIVATE_H_ */
-
diff --git a/ppapi/cpp/private/camera_capabilities_private.cc b/ppapi/cpp/private/camera_capabilities_private.cc
index ce2ebb4..ad3d435 100644
--- a/ppapi/cpp/private/camera_capabilities_private.cc
+++ b/ppapi/cpp/private/camera_capabilities_private.cc
@@ -41,21 +41,21 @@
 CameraCapabilities_Private::~CameraCapabilities_Private() {
 }
 
-void CameraCapabilities_Private::GetSupportedPreviewSizes(
-    std::vector<Size>* preview_sizes) {
+void CameraCapabilities_Private::GetSupportedVideoCaptureFormats(
+    std::vector<PP_VideoCaptureFormat>* formats) {
   if (!has_interface<PPB_CameraCapabilities_Private_0_1>()) {
     PP_DCHECK(false);
     return;
   }
 
-  int32_t array_size;
-  PP_Size* array;
-  get_interface<PPB_CameraCapabilities_Private_0_1>()->GetSupportedPreviewSizes(
-      pp_resource(), &array_size, &array);
-  preview_sizes->clear();
-  preview_sizes->reserve(array_size);
-  for (int32_t i = 0; i < array_size; i++) {
-    preview_sizes->push_back(Size(array[i]));
+  uint32_t array_size;
+  PP_VideoCaptureFormat* array;
+  get_interface<PPB_CameraCapabilities_Private_0_1>()
+      ->GetSupportedVideoCaptureFormats(pp_resource(), &array_size, &array);
+  formats->clear();
+  formats->reserve(array_size);
+  for (uint32_t i = 0; i < array_size; i++) {
+    formats->push_back(array[i]);
   }
 }
 
diff --git a/ppapi/cpp/private/camera_capabilities_private.h b/ppapi/cpp/private/camera_capabilities_private.h
index 5686d88..e0dbfe328 100644
--- a/ppapi/cpp/private/camera_capabilities_private.h
+++ b/ppapi/cpp/private/camera_capabilities_private.h
@@ -48,12 +48,14 @@
   // Destructor.
   ~CameraCapabilities_Private();
 
-  /// GetSupportedPreviewSizes() returns the supported preview sizes for the
-  /// given <code>CameraCapabilities_Private</code>.
+  /// GetSupportedVideoCaptureFormats() returns the supported video capture
+  /// formats.
   ///
-  /// @param[out] A vector of <code>Size</code> corresponding to the
-  /// supported preview sizes in pixels.
-  void GetSupportedPreviewSizes(std::vector<Size>* preview_sizes);
+  /// @param[out] formats A vector of <code>PP_VideoCaptureFormat</code>
+  /// corresponding to the supported video capture formats. This output vector
+  /// must be prepared by the caller beforehand.
+  void GetSupportedVideoCaptureFormats(
+      std::vector<PP_VideoCaptureFormat>* formats);
 
   /// IsCameraCapabilities() determines if the given resource is a
   /// <code>CameraCapabilities_Private</code>.
diff --git a/ppapi/cpp/private/camera_device_private.cc b/ppapi/cpp/private/camera_device_private.cc
new file mode 100644
index 0000000..1ca39f7
--- /dev/null
+++ b/ppapi/cpp/private/camera_device_private.cc
@@ -0,0 +1,87 @@
+// 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 "ppapi/cpp/private/camera_device_private.h"
+
+#include "ppapi/c/pp_bool.h"
+#include "ppapi/c/pp_errors.h"
+#include "ppapi/cpp/completion_callback.h"
+#include "ppapi/cpp/instance_handle.h"
+#include "ppapi/cpp/module_impl.h"
+#include "ppapi/cpp/private/camera_capabilities_private.h"
+
+namespace pp {
+
+namespace {
+
+template <>
+const char* interface_name<PPB_CameraDevice_Private_0_1>() {
+  return PPB_CAMERADEVICE_PRIVATE_INTERFACE_0_1;
+}
+
+}  // namespace
+
+CameraDevice_Private::CameraDevice_Private() {
+}
+
+CameraDevice_Private::CameraDevice_Private(const CameraDevice_Private& other)
+    : Resource(other) {
+}
+
+CameraDevice_Private::CameraDevice_Private(const Resource& resource)
+    : Resource(resource) {
+  PP_DCHECK(IsCameraDevice(resource));
+}
+
+CameraDevice_Private::CameraDevice_Private(const InstanceHandle& instance) {
+  if (has_interface<PPB_CameraDevice_Private_0_1>()) {
+    PassRefFromConstructor(
+        get_interface<PPB_CameraDevice_Private_0_1>()->Create(
+            instance.pp_instance()));
+    return;
+  }
+  PP_DCHECK(false);
+}
+
+CameraDevice_Private::CameraDevice_Private(PassRef, PP_Resource resource)
+    : Resource(PASS_REF, resource) {
+}
+
+CameraDevice_Private::~CameraDevice_Private() {
+}
+
+int32_t CameraDevice_Private::Open(const Var& device_id,
+                                   const CompletionCallback& callback) {
+  if (!has_interface<PPB_CameraDevice_Private_0_1>())
+    return callback.MayForce(PP_ERROR_NOINTERFACE);
+
+  return get_interface<PPB_CameraDevice_Private_0_1>()->Open(
+      pp_resource(), device_id.pp_var(), callback.pp_completion_callback());
+}
+
+void CameraDevice_Private::Close() {
+  if (has_interface<PPB_CameraDevice_Private_0_1>())
+    get_interface<PPB_CameraDevice_Private_0_1>()->Close(pp_resource());
+}
+
+int32_t CameraDevice_Private::GetCameraCapabilities(
+    const CompletionCallbackWithOutput<CameraCapabilities_Private>& callback) {
+  if (!has_interface<PPB_CameraDevice_Private_0_1>())
+    return callback.MayForce(PP_ERROR_NOINTERFACE);
+
+  return get_interface<PPB_CameraDevice_Private_0_1>()->GetCameraCapabilities(
+      pp_resource(), callback.output(), callback.pp_completion_callback());
+}
+
+// static
+bool CameraDevice_Private::IsCameraDevice(const Resource& resource) {
+  if (!has_interface<PPB_CameraDevice_Private_0_1>())
+    return false;
+
+  return PP_ToBool(
+      get_interface<PPB_CameraDevice_Private_0_1>()->IsCameraDevice(
+          resource.pp_resource()));
+}
+
+}  // namespace pp
diff --git a/ppapi/cpp/private/camera_device_private.h b/ppapi/cpp/private/camera_device_private.h
new file mode 100644
index 0000000..dce1e1b1
--- /dev/null
+++ b/ppapi/cpp/private/camera_device_private.h
@@ -0,0 +1,103 @@
+/* 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 PPAPI_CPP_PRIVATE_CAMERA_DEVICE_PRIVATE_H_
+#define PPAPI_CPP_PRIVATE_CAMERA_DEVICE_PRIVATE_H_
+
+#include "ppapi/c/private/ppb_camera_device_private.h"
+#include "ppapi/cpp/resource.h"
+#include "ppapi/cpp/var.h"
+
+/// @file
+/// Defines the <code>CameraDevice_Private</code> interface. Used for
+/// manipulating a camera device.
+namespace pp {
+
+class CameraCapabilities_Private;
+class CompletionCallback;
+class InstanceHandle;
+
+template <typename T>
+class CompletionCallbackWithOutput;
+
+/// To query camera capabilities:
+/// 1. Create a CameraDevice_Private object.
+/// 2. Open() camera device with track id of MediaStream video track.
+/// 3. Call GetCameraCapabilities() to get a
+///    <code>CameraCapabilities_Private</code> object, which can be used to
+///    query camera capabilities.
+class CameraDevice_Private : public Resource {
+ public:
+  /// Default constructor for creating an is_null()
+  /// <code>CameraDevice_Private</code> object.
+  CameraDevice_Private();
+
+  /// The copy constructor for <code>CameraDevice_Private</code>.
+  ///
+  /// @param[in] other A reference to a <code>CameraDevice_Private</code>.
+  CameraDevice_Private(const CameraDevice_Private& other);
+
+  /// Constructs a <code>CameraDevice_Private</code> from a
+  /// <code>Resource</code>.
+  ///
+  /// @param[in] resource A <code>PPB_CameraDevice_Private</code> resource.
+  explicit CameraDevice_Private(const Resource& resource);
+
+  /// Constructs a CameraDevice_Private resource.
+  ///
+  /// @param[in] instance A <code>PP_Instance</code> identifying one instance
+  /// of a module.
+  explicit CameraDevice_Private(const InstanceHandle& instance);
+
+  /// A constructor used when you have received a <code>PP_Resource</code> as a
+  /// return value that has had 1 ref added for you.
+  ///
+  /// @param[in] resource A <code>PPB_CameraDevice_Private</code> resource.
+  CameraDevice_Private(PassRef, PP_Resource resource);
+
+  // Destructor.
+  ~CameraDevice_Private();
+
+  /// Opens a camera device.
+  ///
+  /// @param[in] device_id A <code>Var</code> identifying a camera
+  /// device. The type is string. The ID can be obtained from
+  /// MediaStreamTrack.getSources() or MediaStreamVideoTrack.id.
+  /// @param[in] callback A <code>CompletionCallback</code> to be called upon
+  /// completion of <code>Open()</code>.
+  ///
+  /// @return An int32_t containing a result code from <code>pp_errors.h</code>.
+  int32_t Open(const Var& device_id, const CompletionCallback& callback);
+
+  /// Disconnects from the camera and cancels all pending requests.
+  /// After this returns, no callbacks will be called. If <code>
+  /// CameraDevice_Private</code> is destroyed and is not closed yet, this
+  /// function will be automatically called. Calling this more than once has no
+  /// effect.
+  void Close();
+
+  /// Gets the camera capabilities.
+  ///
+  /// The camera capabilities do not change for a given camera source.
+  ///
+  /// @param[in] callback A <code>CompletionCallbackWithOutput</code>
+  /// to be called upon completion.
+  ///
+  /// @return An int32_t containing a result code from <code>pp_errors.h</code>.
+  int32_t GetCameraCapabilities(
+      const CompletionCallbackWithOutput<CameraCapabilities_Private>& callback);
+
+  /// Determines if a resource is a camera device resource.
+  ///
+  /// @param[in] resource The <code>Resource</code> to test.
+  ///
+  /// @return true if the given resource is a camera device resource or false
+  /// otherwise.
+  static bool IsCameraDevice(const Resource& resource);
+};
+
+} // namespace pp
+
+#endif  /* PPAPI_CPP_PRIVATE_CAMERA_DEVICE_PRIVATE_H_ */
diff --git a/ppapi/cpp/private/image_capture_private.cc b/ppapi/cpp/private/image_capture_private.cc
deleted file mode 100644
index 539f8a37..0000000
--- a/ppapi/cpp/private/image_capture_private.cc
+++ /dev/null
@@ -1,87 +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 "ppapi/cpp/private/image_capture_private.h"
-
-#include "ppapi/c/pp_bool.h"
-#include "ppapi/c/pp_errors.h"
-#include "ppapi/cpp/completion_callback.h"
-#include "ppapi/cpp/instance_handle.h"
-#include "ppapi/cpp/module_impl.h"
-#include "ppapi/cpp/private/camera_capabilities_private.h"
-
-namespace pp {
-
-namespace {
-
-template <>
-const char* interface_name<PPB_ImageCapture_Private_0_1>() {
-  return PPB_IMAGECAPTURE_PRIVATE_INTERFACE_0_1;
-}
-
-}  // namespace
-
-ImageCapture_Private::ImageCapture_Private() {
-}
-
-ImageCapture_Private::ImageCapture_Private(const ImageCapture_Private& other)
-    : Resource(other) {
-}
-
-ImageCapture_Private::ImageCapture_Private(const Resource& resource)
-    : Resource(resource) {
-  PP_DCHECK(IsImageCapture(resource));
-}
-
-ImageCapture_Private::ImageCapture_Private(const InstanceHandle& instance) {
-  if (has_interface<PPB_ImageCapture_Private_0_1>()) {
-    PassRefFromConstructor(
-        get_interface<PPB_ImageCapture_Private_0_1>()->Create(
-            instance.pp_instance()));
-    return;
-  }
-  PP_DCHECK(false);
-}
-
-ImageCapture_Private::ImageCapture_Private(PassRef, PP_Resource resource)
-    : Resource(PASS_REF, resource) {
-}
-
-ImageCapture_Private::~ImageCapture_Private() {
-}
-
-int32_t ImageCapture_Private::Open(const Var& device_id,
-                                   const CompletionCallback& callback) {
-  if (!has_interface<PPB_ImageCapture_Private_0_1>())
-    return callback.MayForce(PP_ERROR_NOINTERFACE);
-
-  return get_interface<PPB_ImageCapture_Private_0_1>()->Open(
-      pp_resource(), device_id.pp_var(), callback.pp_completion_callback());
-}
-
-void ImageCapture_Private::Close() {
-  if (has_interface<PPB_ImageCapture_Private_0_1>())
-    get_interface<PPB_ImageCapture_Private_0_1>()->Close(pp_resource());
-}
-
-int32_t ImageCapture_Private::GetCameraCapabilities(
-    const CompletionCallbackWithOutput<CameraCapabilities_Private>& callback) {
-  if (!has_interface<PPB_ImageCapture_Private_0_1>())
-    return callback.MayForce(PP_ERROR_NOINTERFACE);
-
-  return get_interface<PPB_ImageCapture_Private_0_1>()->GetCameraCapabilities(
-      pp_resource(), callback.output(), callback.pp_completion_callback());
-}
-
-// static
-bool ImageCapture_Private::IsImageCapture(const Resource& resource) {
-  if (!has_interface<PPB_ImageCapture_Private_0_1>())
-    return false;
-
-  return PP_ToBool(
-      get_interface<PPB_ImageCapture_Private_0_1>()->IsImageCapture(
-          resource.pp_resource()));
-}
-
-}  // namespace pp
diff --git a/ppapi/cpp/private/image_capture_private.h b/ppapi/cpp/private/image_capture_private.h
deleted file mode 100644
index 98382a2e..0000000
--- a/ppapi/cpp/private/image_capture_private.h
+++ /dev/null
@@ -1,103 +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 PPAPI_CPP_PRIVATE_IMAGE_CAPTURE_PRIVATE_H_
-#define PPAPI_CPP_PRIVATE_IMAGE_CAPTURE_PRIVATE_H_
-
-#include "ppapi/c/private/ppb_image_capture_private.h"
-#include "ppapi/cpp/resource.h"
-#include "ppapi/cpp/var.h"
-
-/// @file
-/// Defines the <code>ImageCapture_Private</code> interface. Used for
-/// acquiring a single still image from a camera source.
-namespace pp {
-
-class CameraCapabilities_Private;
-class CompletionCallback;
-class InstanceHandle;
-
-template <typename T>
-class CompletionCallbackWithOutput;
-
-/// To query camera capabilities:
-/// 1. Create an ImageCapture_Private object.
-/// 2. Open() camera device with track id of MediaStream video track.
-/// 3. Call GetCameraCapabilities() to get a
-///    <code>CameraCapabilities_Private</code> object, which can be used to
-///    query camera capabilities.
-class ImageCapture_Private : public Resource {
- public:
-  /// Default constructor for creating an is_null()
-  /// <code>ImageCapture_Private</code> object.
-  ImageCapture_Private();
-
-  /// The copy constructor for <code>ImageCapture_Private</code>.
-  ///
-  /// @param[in] other A reference to a <code>ImageCapture_Private</code>.
-  ImageCapture_Private(const ImageCapture_Private& other);
-
-  /// Constructs an <code>ImageCapture_Private</code> from
-  /// a <code>Resource</code>.
-  ///
-  /// @param[in] resource A <code>PPB_ImageCapture_Private</code> resource.
-  explicit ImageCapture_Private(const Resource& resource);
-
-  /// Constructs an ImageCapture_Private resource.
-  ///
-  /// @param[in] instance A <code>PP_Instance</code> identifying one instance
-  /// of a module.
-  explicit ImageCapture_Private(const InstanceHandle& instance);
-
-  /// A constructor used when you have received a <code>PP_Resource</code> as a
-  /// return value that has had 1 ref added for you.
-  ///
-  /// @param[in] resource A <code>PPB_ImageCapture_Private</code> resource.
-  ImageCapture_Private(PassRef, PP_Resource resource);
-
-  // Destructor.
-  ~ImageCapture_Private();
-
-  /// Opens a video capture device.
-  ///
-  /// @param[in] device_id A <code>Var</code> identifying a camera
-  /// device. The type is string. The ID can be obtained from
-  /// MediaStreamTrack.getSources() or MediaStreamVideoTrack.id.
-  /// @param[in] callback A <code>CompletionCallback</code> to be called upon
-  /// completion of <code>Open()</code>.
-  ///
-  /// @return An int32_t containing a result code from <code>pp_errors.h</code>.
-  int32_t Open(const Var& device_id, const CompletionCallback& callback);
-
-  /// Disconnects from the camera and cancels all pending capture requests.
-  /// After this returns, no callbacks will be called. If <code>
-  /// ImageCapture_Private</code> is destroyed and is not closed yet, this
-  /// function will be automatically called. Calling this more than once has no
-  /// effect.
-  void Close();
-
-  /// Gets the camera capabilities.
-  ///
-  /// The camera capabilities do not change for a given camera source.
-  ///
-  /// @param[in] callback A <code>CompletionCallbackWithOutput</code>
-  /// to be called upon completion.
-  ///
-  /// @return An int32_t containing a result code from <code>pp_errors.h</code>.
-  int32_t GetCameraCapabilities(
-      const CompletionCallbackWithOutput<CameraCapabilities_Private>& callback);
-
-  /// Determines if a resource is an image capture resource.
-  ///
-  /// @param[in] resource The <code>Resource</code> to test.
-  ///
-  /// @return true if the given resource is an image capture resource or false
-  /// otherwise.
-  static bool IsImageCapture(const Resource& resource);
-};
-
-} // namespace pp
-
-#endif  /* PPAPI_CPP_PRIVATE_IMAGE_CAPTURE_PRIVATE_H_ */
diff --git a/ppapi/native_client/src/untrusted/pnacl_irt_shim/pnacl_shim.c b/ppapi/native_client/src/untrusted/pnacl_irt_shim/pnacl_shim.c
index b47a250..1ceec96e 100644
--- a/ppapi/native_client/src/untrusted/pnacl_irt_shim/pnacl_shim.c
+++ b/ppapi/native_client/src/untrusted/pnacl_irt_shim/pnacl_shim.c
@@ -53,6 +53,7 @@
 #include "ppapi/c/ppb_video_encoder.h"
 #include "ppapi/c/ppb_websocket.h"
 #include "ppapi/c/ppp_messaging.h"
+#include "ppapi/c/private/ppb_camera_device_private.h"
 #include "ppapi/c/private/ppb_content_decryptor_private.h"
 #include "ppapi/c/private/ppb_display_color_profile_private.h"
 #include "ppapi/c/private/ppb_ext_crx_file_system_private.h"
@@ -64,7 +65,6 @@
 #include "ppapi/c/private/ppb_flash_drm.h"
 #include "ppapi/c/private/ppb_flash_menu.h"
 #include "ppapi/c/private/ppb_host_resolver_private.h"
-#include "ppapi/c/private/ppb_image_capture_private.h"
 #include "ppapi/c/private/ppb_instance_private.h"
 #include "ppapi/c/private/ppb_isolated_file_system_private.h"
 #include "ppapi/c/private/ppb_net_address_private.h"
@@ -166,6 +166,7 @@
 static struct __PnaclWrapperInfo Pnacl_WrapperInfo_PPB_VideoCapture_Dev_0_3;
 static struct __PnaclWrapperInfo Pnacl_WrapperInfo_PPB_VideoDecoder_Dev_0_16;
 static struct __PnaclWrapperInfo Pnacl_WrapperInfo_PPP_Selection_Dev_0_3;
+static struct __PnaclWrapperInfo Pnacl_WrapperInfo_PPB_CameraDevice_Private_0_1;
 static struct __PnaclWrapperInfo Pnacl_WrapperInfo_PPB_ContentDecryptor_Private_0_13;
 static struct __PnaclWrapperInfo Pnacl_WrapperInfo_PPB_DisplayColorProfile_Private_0_1;
 static struct __PnaclWrapperInfo Pnacl_WrapperInfo_PPB_Ext_CrxFileSystem_Private_0_1;
@@ -183,7 +184,6 @@
 static struct __PnaclWrapperInfo Pnacl_WrapperInfo_PPB_Flash_DRM_1_1;
 static struct __PnaclWrapperInfo Pnacl_WrapperInfo_PPB_Flash_Menu_0_2;
 static struct __PnaclWrapperInfo Pnacl_WrapperInfo_PPB_HostResolver_Private_0_1;
-static struct __PnaclWrapperInfo Pnacl_WrapperInfo_PPB_ImageCapture_Private_0_1;
 static struct __PnaclWrapperInfo Pnacl_WrapperInfo_PPB_Instance_Private_0_1;
 static struct __PnaclWrapperInfo Pnacl_WrapperInfo_PPB_IsolatedFileSystem_Private_0_2;
 static struct __PnaclWrapperInfo Pnacl_WrapperInfo_PPB_NetAddress_Private_0_1;
@@ -2946,6 +2946,35 @@
 
 /* Not generating wrapper methods for PPB_CameraCapabilities_Private_0_1 */
 
+/* Begin wrapper methods for PPB_CameraDevice_Private_0_1 */
+
+static PP_Resource Pnacl_M42_PPB_CameraDevice_Private_Create(PP_Instance instance) {
+  const struct PPB_CameraDevice_Private_0_1 *iface = Pnacl_WrapperInfo_PPB_CameraDevice_Private_0_1.real_iface;
+  return iface->Create(instance);
+}
+
+static PP_Bool Pnacl_M42_PPB_CameraDevice_Private_IsCameraDevice(PP_Resource resource) {
+  const struct PPB_CameraDevice_Private_0_1 *iface = Pnacl_WrapperInfo_PPB_CameraDevice_Private_0_1.real_iface;
+  return iface->IsCameraDevice(resource);
+}
+
+static int32_t Pnacl_M42_PPB_CameraDevice_Private_Open(PP_Resource camera_device, struct PP_Var* device_id, struct PP_CompletionCallback* callback) {
+  const struct PPB_CameraDevice_Private_0_1 *iface = Pnacl_WrapperInfo_PPB_CameraDevice_Private_0_1.real_iface;
+  return iface->Open(camera_device, *device_id, *callback);
+}
+
+static void Pnacl_M42_PPB_CameraDevice_Private_Close(PP_Resource camera_device) {
+  const struct PPB_CameraDevice_Private_0_1 *iface = Pnacl_WrapperInfo_PPB_CameraDevice_Private_0_1.real_iface;
+  iface->Close(camera_device);
+}
+
+static int32_t Pnacl_M42_PPB_CameraDevice_Private_GetCameraCapabilities(PP_Resource camera_device, PP_Resource* capabilities, struct PP_CompletionCallback* callback) {
+  const struct PPB_CameraDevice_Private_0_1 *iface = Pnacl_WrapperInfo_PPB_CameraDevice_Private_0_1.real_iface;
+  return iface->GetCameraCapabilities(camera_device, capabilities, *callback);
+}
+
+/* End wrapper methods for PPB_CameraDevice_Private_0_1 */
+
 /* Begin wrapper methods for PPB_ContentDecryptor_Private_0_13 */
 
 static void Pnacl_M41_PPB_ContentDecryptor_Private_PromiseResolved(PP_Instance instance, uint32_t promise_id) {
@@ -3591,35 +3620,6 @@
 
 /* End wrapper methods for PPB_HostResolver_Private_0_1 */
 
-/* Begin wrapper methods for PPB_ImageCapture_Private_0_1 */
-
-static PP_Resource Pnacl_M42_PPB_ImageCapture_Private_Create(PP_Instance instance) {
-  const struct PPB_ImageCapture_Private_0_1 *iface = Pnacl_WrapperInfo_PPB_ImageCapture_Private_0_1.real_iface;
-  return iface->Create(instance);
-}
-
-static PP_Bool Pnacl_M42_PPB_ImageCapture_Private_IsImageCapture(PP_Resource resource) {
-  const struct PPB_ImageCapture_Private_0_1 *iface = Pnacl_WrapperInfo_PPB_ImageCapture_Private_0_1.real_iface;
-  return iface->IsImageCapture(resource);
-}
-
-static int32_t Pnacl_M42_PPB_ImageCapture_Private_Open(PP_Resource image_capture, struct PP_Var* device_id, struct PP_CompletionCallback* callback) {
-  const struct PPB_ImageCapture_Private_0_1 *iface = Pnacl_WrapperInfo_PPB_ImageCapture_Private_0_1.real_iface;
-  return iface->Open(image_capture, *device_id, *callback);
-}
-
-static void Pnacl_M42_PPB_ImageCapture_Private_Close(PP_Resource image_capture) {
-  const struct PPB_ImageCapture_Private_0_1 *iface = Pnacl_WrapperInfo_PPB_ImageCapture_Private_0_1.real_iface;
-  iface->Close(image_capture);
-}
-
-static int32_t Pnacl_M42_PPB_ImageCapture_Private_GetCameraCapabilities(PP_Resource image_capture, PP_Resource* capabilities, struct PP_CompletionCallback* callback) {
-  const struct PPB_ImageCapture_Private_0_1 *iface = Pnacl_WrapperInfo_PPB_ImageCapture_Private_0_1.real_iface;
-  return iface->GetCameraCapabilities(image_capture, capabilities, *callback);
-}
-
-/* End wrapper methods for PPB_ImageCapture_Private_0_1 */
-
 /* Not generating wrapper methods for PPB_InputEvent_Private_0_1 */
 
 /* Begin wrapper methods for PPB_Instance_Private_0_1 */
@@ -5319,6 +5319,14 @@
 
 /* Not generating wrapper interface for PPB_CameraCapabilities_Private_0_1 */
 
+static const struct PPB_CameraDevice_Private_0_1 Pnacl_Wrappers_PPB_CameraDevice_Private_0_1 = {
+    .Create = (PP_Resource (*)(PP_Instance instance))&Pnacl_M42_PPB_CameraDevice_Private_Create,
+    .IsCameraDevice = (PP_Bool (*)(PP_Resource resource))&Pnacl_M42_PPB_CameraDevice_Private_IsCameraDevice,
+    .Open = (int32_t (*)(PP_Resource camera_device, struct PP_Var device_id, struct PP_CompletionCallback callback))&Pnacl_M42_PPB_CameraDevice_Private_Open,
+    .Close = (void (*)(PP_Resource camera_device))&Pnacl_M42_PPB_CameraDevice_Private_Close,
+    .GetCameraCapabilities = (int32_t (*)(PP_Resource camera_device, PP_Resource* capabilities, struct PP_CompletionCallback callback))&Pnacl_M42_PPB_CameraDevice_Private_GetCameraCapabilities
+};
+
 static const struct PPB_ContentDecryptor_Private_0_13 Pnacl_Wrappers_PPB_ContentDecryptor_Private_0_13 = {
     .PromiseResolved = (void (*)(PP_Instance instance, uint32_t promise_id))&Pnacl_M41_PPB_ContentDecryptor_Private_PromiseResolved,
     .PromiseResolvedWithSession = (void (*)(PP_Instance instance, uint32_t promise_id, struct PP_Var session_id))&Pnacl_M41_PPB_ContentDecryptor_Private_PromiseResolvedWithSession,
@@ -5495,14 +5503,6 @@
     .GetNetAddress = (PP_Bool (*)(PP_Resource host_resolver, uint32_t index, struct PP_NetAddress_Private* addr))&Pnacl_M19_PPB_HostResolver_Private_GetNetAddress
 };
 
-static const struct PPB_ImageCapture_Private_0_1 Pnacl_Wrappers_PPB_ImageCapture_Private_0_1 = {
-    .Create = (PP_Resource (*)(PP_Instance instance))&Pnacl_M42_PPB_ImageCapture_Private_Create,
-    .IsImageCapture = (PP_Bool (*)(PP_Resource resource))&Pnacl_M42_PPB_ImageCapture_Private_IsImageCapture,
-    .Open = (int32_t (*)(PP_Resource image_capture, struct PP_Var device_id, struct PP_CompletionCallback callback))&Pnacl_M42_PPB_ImageCapture_Private_Open,
-    .Close = (void (*)(PP_Resource image_capture))&Pnacl_M42_PPB_ImageCapture_Private_Close,
-    .GetCameraCapabilities = (int32_t (*)(PP_Resource image_capture, PP_Resource* capabilities, struct PP_CompletionCallback callback))&Pnacl_M42_PPB_ImageCapture_Private_GetCameraCapabilities
-};
-
 /* Not generating wrapper interface for PPB_InputEvent_Private_0_1 */
 
 static const struct PPB_Instance_Private_0_1 Pnacl_Wrappers_PPB_Instance_Private_0_1 = {
@@ -6160,6 +6160,12 @@
   .real_iface = NULL
 };
 
+static struct __PnaclWrapperInfo Pnacl_WrapperInfo_PPB_CameraDevice_Private_0_1 = {
+  .iface_macro = PPB_CAMERADEVICE_PRIVATE_INTERFACE_0_1,
+  .wrapped_iface = (const void *) &Pnacl_Wrappers_PPB_CameraDevice_Private_0_1,
+  .real_iface = NULL
+};
+
 static struct __PnaclWrapperInfo Pnacl_WrapperInfo_PPB_ContentDecryptor_Private_0_13 = {
   .iface_macro = PPB_CONTENTDECRYPTOR_PRIVATE_INTERFACE_0_13,
   .wrapped_iface = (const void *) &Pnacl_Wrappers_PPB_ContentDecryptor_Private_0_13,
@@ -6262,12 +6268,6 @@
   .real_iface = NULL
 };
 
-static struct __PnaclWrapperInfo Pnacl_WrapperInfo_PPB_ImageCapture_Private_0_1 = {
-  .iface_macro = PPB_IMAGECAPTURE_PRIVATE_INTERFACE_0_1,
-  .wrapped_iface = (const void *) &Pnacl_Wrappers_PPB_ImageCapture_Private_0_1,
-  .real_iface = NULL
-};
-
 static struct __PnaclWrapperInfo Pnacl_WrapperInfo_PPB_Instance_Private_0_1 = {
   .iface_macro = PPB_INSTANCE_PRIVATE_INTERFACE_0_1,
   .wrapped_iface = (const void *) &Pnacl_Wrappers_PPB_Instance_Private_0_1,
@@ -6481,6 +6481,7 @@
   &Pnacl_WrapperInfo_PPB_URLUtil_Dev_0_7,
   &Pnacl_WrapperInfo_PPB_VideoCapture_Dev_0_3,
   &Pnacl_WrapperInfo_PPB_VideoDecoder_Dev_0_16,
+  &Pnacl_WrapperInfo_PPB_CameraDevice_Private_0_1,
   &Pnacl_WrapperInfo_PPB_ContentDecryptor_Private_0_13,
   &Pnacl_WrapperInfo_PPB_DisplayColorProfile_Private_0_1,
   &Pnacl_WrapperInfo_PPB_Ext_CrxFileSystem_Private_0_1,
@@ -6498,7 +6499,6 @@
   &Pnacl_WrapperInfo_PPB_Flash_DRM_1_1,
   &Pnacl_WrapperInfo_PPB_Flash_Menu_0_2,
   &Pnacl_WrapperInfo_PPB_HostResolver_Private_0_1,
-  &Pnacl_WrapperInfo_PPB_ImageCapture_Private_0_1,
   &Pnacl_WrapperInfo_PPB_Instance_Private_0_1,
   &Pnacl_WrapperInfo_PPB_IsolatedFileSystem_Private_0_2,
   &Pnacl_WrapperInfo_PPB_NetAddress_Private_0_1,
diff --git a/ppapi/native_client/src/untrusted/pnacl_support_extension/pnacl_support_extension.gyp b/ppapi/native_client/src/untrusted/pnacl_support_extension/pnacl_support_extension.gyp
index 9fadd74..e8dc67d 100644
--- a/ppapi/native_client/src/untrusted/pnacl_support_extension/pnacl_support_extension.gyp
+++ b/ppapi/native_client/src/untrusted/pnacl_support_extension/pnacl_support_extension.gyp
@@ -8,66 +8,19 @@
   'includes': [
     '../../../../../build/common_untrusted.gypi',
   ],
-  'variables': {
-    'pnacl_translator_dir%': "",
-    'pnacl_translator_stamp%': "pnacl_translator.json",
-  },
   'targets': [
   {
-    'target_name': 'untar_pnacl_translator',
-    'type': 'none',
-    'conditions': [
-      ['pnacl_translator_dir==""', {
-        'actions': [{
-          'action_name': 'Untar pnacl_translator',
-          'description': 'Untar pnacl_translator',
-          'inputs': [
-              '<(DEPTH)/native_client/build/package_version/package_version.py',
-              '<(DEPTH)/native_client/toolchain/.tars/<(TOOLCHAIN_OS)_x86/pnacl_translator.json',
-          ],
-          'outputs': ['<(SHARED_INTERMEDIATE_DIR)/sdk/<(TOOLCHAIN_OS)_x86/pnacl_translator/<(pnacl_translator_stamp)'],
-          'action': [
-            'python',
-            '<(DEPTH)/native_client/build/package_version/package_version.py',
-            '--quiet',
-            '--packages', 'pnacl_translator',
-            '--tar-dir', '<(DEPTH)/native_client/toolchain/.tars',
-            '--dest-dir', '<(SHARED_INTERMEDIATE_DIR)/sdk',
-            'extract',
-          ],
-        }],
-      }, {
-        'actions': [{
-          'action_name': 'Copy pnacl_translator',
-          'description': 'Copy pnacl_translator',
-          'inputs': [
-            '<(DEPTH)/native_client/build/copy_directory.py',
-            '<(pnacl_translator_dir)/<(pnacl_translator_stamp)',
-          ],
-          'outputs': [
-            '<(SHARED_INTERMEDIATE_DIR)/sdk/<(TOOLCHAIN_OS)_x86/pnacl_translator/<(pnacl_translator_stamp)'],
-          'action': [
-            'python',
-            '<(DEPTH)/native_client/build/copy_directory.py',
-            '--quiet',
-            '--stamp-arg', 'pnacl_translator_stamp',
-            '--stamp-file', '<(pnacl_translator_stamp)',
-            '<(pnacl_translator_dir)',
-            '<(SHARED_INTERMEDIATE_DIR)/sdk/<(TOOLCHAIN_OS)_x86/pnacl_translator',
-          ],
-        }],
-      }],
-    ],
-  },
-  {
     'target_name': 'pnacl_support_extension',
     'type': 'none',
+    'variables': {
+      'pnacl_translator_dir%': "<(DEPTH)/native_client/toolchain/<(TOOLCHAIN_OS)_x86/pnacl_translator",
+      'pnacl_translator_stamp%': "pnacl_translator.json",
+    },
     'conditions': [
       ['disable_nacl==0 and disable_pnacl==0 and disable_nacl_untrusted==0', {
         'dependencies': [
           '../../../../../ppapi/native_client/src/untrusted/pnacl_irt_shim/pnacl_irt_shim.gyp:browser',
           '../../../../../native_client/tools.gyp:prep_toolchain',
-          'untar_pnacl_translator',
         ],
         'sources': [
           'pnacl_component_crx_gen.py',
@@ -80,7 +33,7 @@
             'inputs': [
               'pnacl_component_crx_gen.py',
               # A stamp file representing the contents of pnacl_translator.
-              '<(SHARED_INTERMEDIATE_DIR)/sdk/<(TOOLCHAIN_OS)_x86/pnacl_translator/<(pnacl_translator_stamp)',
+              '<(pnacl_translator_dir)/<(pnacl_translator_stamp)',
               '<(DEPTH)/native_client/pnacl/driver/pnacl_info_template.json',
               '<(DEPTH)/native_client/toolchain_revisions/pnacl_newlib.json',
             ],
@@ -206,7 +159,7 @@
               '<@(lib_overrides)',
               '--target_arch=<(target_arch)',
               '--info_template_path=<(DEPTH)/native_client/pnacl/driver/pnacl_info_template.json',
-              '--pnacl_translator_path=<(SHARED_INTERMEDIATE_DIR)/sdk/<(TOOLCHAIN_OS)_x86/pnacl_translator',
+              '--pnacl_translator_path=<(pnacl_translator_dir)',
               '--package_version_path=<(DEPTH)/native_client/build/package_version/package_version.py',
               '--pnacl_package_name=pnacl_newlib',
               # ABI Version Number.
diff --git a/ppapi/ppapi_proxy.gypi b/ppapi/ppapi_proxy.gypi
index c5bebdd..7a7f0cd 100644
--- a/ppapi/ppapi_proxy.gypi
+++ b/ppapi/ppapi_proxy.gypi
@@ -31,6 +31,8 @@
           'proxy/browser_font_singleton_resource.h',
           'proxy/camera_capabilities_resource.cc',
           'proxy/camera_capabilities_resource.h',
+          'proxy/camera_device_resource.cc',
+          'proxy/camera_device_resource.h',
           'proxy/compositor_layer_resource.cc',
           'proxy/compositor_layer_resource.h',
           'proxy/compositor_resource.cc',
@@ -83,8 +85,6 @@
           'proxy/host_resolver_resource_base.h',
           'proxy/host_var_serialization_rules.cc',
           'proxy/host_var_serialization_rules.h',
-          'proxy/image_capture_resource.cc',
-          'proxy/image_capture_resource.h',
           'proxy/interface_list.cc',
           'proxy/interface_list.h',
           'proxy/interface_proxy.cc',
@@ -273,6 +273,7 @@
               'proxy/broker_dispatcher.cc',
               'proxy/browser_font_singleton_resource.cc',
               'proxy/camera_capabilities_resource.cc',
+              'proxy/camera_device_resource.cc',
               'proxy/device_enumeration_resource_helper.cc',
               'proxy/flash_clipboard_resource.cc',
               'proxy/flash_drm_resource.cc',
@@ -283,7 +284,6 @@
               'proxy/flash_resource.cc',
               'proxy/host_dispatcher.cc',
               'proxy/host_var_serialization_rules.cc',
-              'proxy/image_capture_resource.cc',
               'proxy/pdf_resource.cc',
               'proxy/platform_verification_private_resource.cc',
               'proxy/platform_verification_private_resource.h',
diff --git a/ppapi/ppapi_shared.gypi b/ppapi/ppapi_shared.gypi
index fb359fe..974ba01b 100644
--- a/ppapi/ppapi_shared.gypi
+++ b/ppapi/ppapi_shared.gypi
@@ -158,6 +158,8 @@
           'thunk/ppb_buffer_thunk.cc',
           'thunk/ppb_camera_capabilities_api.h',
           'thunk/ppb_camera_capabilities_private_thunk.cc',
+          'thunk/ppb_camera_device_api.h',
+          'thunk/ppb_camera_device_private_thunk.cc',
           'thunk/ppb_char_set_thunk.cc',
           'thunk/ppb_compositor_api.h',
           'thunk/ppb_compositor_layer_api.h',
@@ -211,8 +213,6 @@
           'thunk/ppb_host_resolver_private_api.h',
           'thunk/ppb_host_resolver_private_thunk.cc',
           'thunk/ppb_host_resolver_thunk.cc',
-          'thunk/ppb_image_capture_api.h',
-          'thunk/ppb_image_capture_private_thunk.cc',
           'thunk/ppb_image_data_api.h',
           'thunk/ppb_image_data_thunk.cc',
           'thunk/ppb_input_event_api.h',
@@ -324,6 +324,7 @@
               'thunk/ppb_browser_font_trusted_thunk.cc',
               'thunk/ppb_buffer_thunk.cc',
               'thunk/ppb_camera_capabilities_private_thunk.cc',
+              'thunk/ppb_camera_device_private_thunk.cc',
               'thunk/ppb_char_set_thunk.cc',
               'thunk/ppb_content_decryptor_private_thunk.cc',
               'thunk/ppb_flash_clipboard_thunk.cc',
@@ -338,7 +339,6 @@
               'thunk/ppb_flash_message_loop_thunk.cc',
               'thunk/ppb_flash_thunk.cc',
               'thunk/ppb_gles_chromium_texture_mapping_thunk.cc',
-              'thunk/ppb_image_capture_private_thunk.cc',
               'thunk/ppb_pdf_thunk.cc',
               'thunk/ppb_platform_verification_private_thunk.cc',
               'thunk/ppb_scrollbar_thunk.cc',
diff --git a/ppapi/ppapi_sources.gypi b/ppapi/ppapi_sources.gypi
index c3c709e..e182b05 100644
--- a/ppapi/ppapi_sources.gypi
+++ b/ppapi/ppapi_sources.gypi
@@ -107,6 +107,7 @@
       'c/private/pp_private_font_charset.h',
       'c/private/pp_video_frame_private.h',
       'c/private/ppb_camera_capabilities_private.h',
+      'c/private/ppb_camera_device_private.h',
       'c/private/ppb_content_decryptor_private.h',
       'c/private/ppb_ext_crx_file_system_private.h',
       'c/private/ppb_find_private.h',
@@ -118,7 +119,6 @@
       'c/private/ppb_flash_menu.h',
       'c/private/ppb_flash_message_loop.h',
       'c/private/ppb_host_resolver_private.h',
-      'c/private/ppb_image_capture_private.h',
       'c/private/ppb_input_event_private.h',
       'c/private/ppb_instance_private.h',
       'c/private/ppb_isolated_file_system_private.h',
@@ -310,6 +310,8 @@
       # Private interfaces.
       'cpp/private/camera_capabilities_private.cc',
       'cpp/private/camera_capabilities_private.h',
+      'cpp/private/camera_device_private.cc',
+      'cpp/private/camera_device_private.h',
       'cpp/private/content_decryptor_private.cc',
       'cpp/private/content_decryptor_private.h',
       'cpp/private/ext_crx_file_system_private.cc',
@@ -338,8 +340,6 @@
       'cpp/private/flash_message_loop.h',
       'cpp/private/host_resolver_private.cc',
       'cpp/private/host_resolver_private.h',
-      'cpp/private/image_capture_private.cc',
-      'cpp/private/image_capture_private.h',
       'cpp/private/input_event_private.cc',
       'cpp/private/input_event_private.h',
       'cpp/private/instance_private.cc',
diff --git a/ppapi/proxy/BUILD.gn b/ppapi/proxy/BUILD.gn
index c9019ca..f6b7a6d 100644
--- a/ppapi/proxy/BUILD.gn
+++ b/ppapi/proxy/BUILD.gn
@@ -214,6 +214,8 @@
       "browser_font_singleton_resource.h",
       "camera_capabilities_resource.cc",
       "camera_capabilities_resource.h",
+      "camera_device_resource.cc",
+      "camera_device_resource.h",
       "device_enumeration_resource_helper.cc",
       "device_enumeration_resource_helper.h",
       "flash_clipboard_resource.cc",
@@ -234,8 +236,6 @@
       "host_dispatcher.h",
       "host_var_serialization_rules.cc",
       "host_var_serialization_rules.h",
-      "image_capture_resource.cc",
-      "image_capture_resource.h",
       "pdf_resource.cc",
       "pdf_resource.h",
       "platform_verification_private_resource.cc",
diff --git a/ppapi/proxy/camera_capabilities_resource.cc b/ppapi/proxy/camera_capabilities_resource.cc
index 18540976..1236aa35 100644
--- a/ppapi/proxy/camera_capabilities_resource.cc
+++ b/ppapi/proxy/camera_capabilities_resource.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "base/logging.h"
+#include "base/numerics/safe_conversions.h"
 #include "ppapi/proxy/camera_capabilities_resource.h"
 
 namespace ppapi {
@@ -10,11 +11,12 @@
 
 CameraCapabilitiesResource::CameraCapabilitiesResource(
     PP_Instance instance,
-    const std::vector<PP_Size>& preview_sizes)
+    const std::vector<PP_VideoCaptureFormat>& formats)
     : Resource(OBJECT_IS_PROXY, instance),
-      num_preview_sizes_(static_cast<int32_t>(preview_sizes.size())),
-      preview_sizes_(new PP_Size[num_preview_sizes_]) {
-  std::copy(preview_sizes.begin(), preview_sizes.end(), preview_sizes_.get());
+      num_video_capture_formats_(formats.size()),
+      video_capture_formats_(
+          new PP_VideoCaptureFormat[num_video_capture_formats_]) {
+  std::copy(formats.begin(), formats.end(), video_capture_formats_.get());
 }
 
 CameraCapabilitiesResource::~CameraCapabilitiesResource() {
@@ -25,11 +27,11 @@
   return this;
 }
 
-void CameraCapabilitiesResource::GetSupportedPreviewSizes(
-    int32_t* array_size,
-    PP_Size** preview_sizes) {
-  *array_size = num_preview_sizes_;
-  *preview_sizes = preview_sizes_.get();
+void CameraCapabilitiesResource::GetSupportedVideoCaptureFormats(
+    uint32_t* array_size,
+    PP_VideoCaptureFormat** formats) {
+  *array_size = base::checked_cast<uint32_t>(num_video_capture_formats_);
+  *formats = video_capture_formats_.get();
 }
 
 }  // namespace proxy
diff --git a/ppapi/proxy/camera_capabilities_resource.h b/ppapi/proxy/camera_capabilities_resource.h
index 237910a..82e8df8 100644
--- a/ppapi/proxy/camera_capabilities_resource.h
+++ b/ppapi/proxy/camera_capabilities_resource.h
@@ -16,14 +16,12 @@
 namespace ppapi {
 namespace proxy {
 
-class ImageCaptureResource;
-
 class PPAPI_PROXY_EXPORT CameraCapabilitiesResource
     : public Resource,
       public thunk::PPB_CameraCapabilities_API {
  public:
   CameraCapabilitiesResource(PP_Instance instance,
-                             const std::vector<PP_Size>& preview_sizes);
+                             const std::vector<PP_VideoCaptureFormat>& formats);
 
   ~CameraCapabilitiesResource() override;
 
@@ -31,12 +29,13 @@
   thunk::PPB_CameraCapabilities_API* AsPPB_CameraCapabilities_API() override;
 
   // PPB_CameraCapabilities_API implementation.
-  void GetSupportedPreviewSizes(int32_t* array_size,
-                                PP_Size** preview_sizes) override;
+  void GetSupportedVideoCaptureFormats(
+      uint32_t* array_size,
+      PP_VideoCaptureFormat** formats) override;
 
  private:
-  int32_t num_preview_sizes_;
-  scoped_ptr<PP_Size[]> preview_sizes_;
+  size_t num_video_capture_formats_;
+  scoped_ptr<PP_VideoCaptureFormat[]> video_capture_formats_;
 
   DISALLOW_COPY_AND_ASSIGN(CameraCapabilitiesResource);
 };
diff --git a/ppapi/proxy/camera_device_resource.cc b/ppapi/proxy/camera_device_resource.cc
new file mode 100644
index 0000000..1ec5af2b
--- /dev/null
+++ b/ppapi/proxy/camera_device_resource.cc
@@ -0,0 +1,120 @@
+// 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 "ppapi/proxy/camera_device_resource.h"
+
+#include "ppapi/proxy/camera_capabilities_resource.h"
+#include "ppapi/proxy/plugin_resource_tracker.h"
+#include "ppapi/proxy/ppapi_messages.h"
+#include "ppapi/shared_impl/var.h"
+
+namespace ppapi {
+namespace proxy {
+
+CameraDeviceResource::CameraDeviceResource(Connection connection,
+                                           PP_Instance instance)
+    : PluginResource(connection, instance),
+      open_state_(OpenState::BEFORE_OPEN) {
+  SendCreate(RENDERER, PpapiHostMsg_CameraDevice_Create());
+}
+
+CameraDeviceResource::~CameraDeviceResource() {
+}
+
+int32_t CameraDeviceResource::Open(
+    PP_Var device_id,
+    const scoped_refptr<TrackedCallback>& callback) {
+  if (open_state_ != OpenState::BEFORE_OPEN)
+    return PP_ERROR_FAILED;
+
+  if (TrackedCallback::IsPending(open_callback_))
+    return PP_ERROR_INPROGRESS;
+
+  scoped_refptr<StringVar> source_string_var(StringVar::FromPPVar(device_id));
+  if (!source_string_var || source_string_var->value().empty())
+    return PP_ERROR_BADARGUMENT;
+
+  open_callback_ = callback;
+
+  Call<PpapiPluginMsg_CameraDevice_OpenReply>(
+      RENDERER, PpapiHostMsg_CameraDevice_Open(source_string_var->value()),
+      base::Bind(&CameraDeviceResource::OnPluginMsgOpenReply,
+                 base::Unretained(this)));
+  return PP_OK_COMPLETIONPENDING;
+}
+
+void CameraDeviceResource::Close() {
+  if (open_state_ == OpenState::CLOSED)
+    return;
+
+  if (TrackedCallback::IsPending(open_callback_)) {
+    open_callback_->PostAbort();
+    open_callback_ = nullptr;
+  }
+
+  if (TrackedCallback::IsPending(get_capabilities_callback_)) {
+    get_capabilities_callback_->PostAbort();
+    get_capabilities_callback_ = nullptr;
+  }
+
+  Post(RENDERER, PpapiHostMsg_CameraDevice_Close());
+
+  open_state_ = OpenState::CLOSED;
+}
+
+int32_t CameraDeviceResource::GetCameraCapabilities(
+    PP_Resource* capabilities,
+    const scoped_refptr<TrackedCallback>& callback) {
+  if (!is_opened())
+    return PP_ERROR_FAILED;
+
+  if (TrackedCallback::IsPending(get_capabilities_callback_))
+    return PP_ERROR_INPROGRESS;
+
+  if (camera_capabilities_.get()) {
+    *capabilities = camera_capabilities_->GetReference();
+    return PP_OK;
+  }
+
+  get_capabilities_callback_ = callback;
+  Call<PpapiPluginMsg_CameraDevice_GetSupportedVideoCaptureFormatsReply>(
+      RENDERER, PpapiHostMsg_CameraDevice_GetSupportedVideoCaptureFormats(),
+      base::Bind(&CameraDeviceResource::OnPluginMsgGetVideoCaptureFormatsReply,
+                 base::Unretained(this), capabilities));
+
+  return PP_OK_COMPLETIONPENDING;
+}
+
+void CameraDeviceResource::OnPluginMsgOpenReply(
+    const ResourceMessageReplyParams& params) {
+  // The callback may have been aborted by Close().
+  if (TrackedCallback::IsPending(open_callback_)) {
+    if (open_state_ == OpenState::BEFORE_OPEN && params.result() == PP_OK)
+      open_state_ = OpenState::OPENED;
+
+    open_callback_->Run(params.result());
+  }
+}
+
+void CameraDeviceResource::OnPluginMsgGetVideoCaptureFormatsReply(
+    PP_Resource* capabilities_output,
+    const ResourceMessageReplyParams& params,
+    const std::vector<PP_VideoCaptureFormat>& formats) {
+  if (!TrackedCallback::IsPending(get_capabilities_callback_))
+    return;
+
+  // Return camera capabilities.
+  int32_t result = params.result();
+  scoped_refptr<TrackedCallback> callback;
+  callback.swap(get_capabilities_callback_);
+  if (result == PP_OK) {
+    camera_capabilities_ =
+        new CameraCapabilitiesResource(pp_instance(), formats);
+    *capabilities_output = camera_capabilities_->GetReference();
+  }
+  callback->Run(result == PP_OK ? PP_OK : PP_ERROR_FAILED);
+}
+
+}  // namespace proxy
+}  // namespace ppapi
diff --git a/ppapi/proxy/camera_device_resource.h b/ppapi/proxy/camera_device_resource.h
new file mode 100644
index 0000000..93878fe
--- /dev/null
+++ b/ppapi/proxy/camera_device_resource.h
@@ -0,0 +1,67 @@
+// 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 PPAPI_PROXY_CAMERA_DEVICE_RESOURCE_H_
+#define PPAPI_PROXY_CAMERA_DEVICE_RESOURCE_H_
+
+#include "base/basictypes.h"
+#include "ppapi/c/pp_size.h"
+#include "ppapi/c/private/pp_video_capture_format.h"
+#include "ppapi/proxy/connection.h"
+#include "ppapi/proxy/plugin_resource.h"
+#include "ppapi/proxy/ppapi_proxy_export.h"
+#include "ppapi/shared_impl/resource.h"
+#include "ppapi/thunk/ppb_camera_device_api.h"
+
+namespace ppapi {
+namespace proxy {
+
+class CameraCapabilitiesResource;
+
+class PPAPI_PROXY_EXPORT CameraDeviceResource
+    : public PluginResource,
+      public thunk::PPB_CameraDevice_API {
+ public:
+  CameraDeviceResource(Connection connection, PP_Instance instance);
+  ~CameraDeviceResource() override;
+
+  // Resource overrides:
+  thunk::PPB_CameraDevice_API* AsPPB_CameraDevice_API() override {
+    return this;
+  }
+
+  // PPB_CameraDevice_API implementation.
+  int32_t Open(PP_Var device_id,
+               const scoped_refptr<TrackedCallback>& callback) override;
+  void Close() override;
+  int32_t GetCameraCapabilities(
+      PP_Resource* capabilities,
+      const scoped_refptr<TrackedCallback>& callback) override;
+
+ private:
+  enum class OpenState { BEFORE_OPEN, OPENED, CLOSED };
+
+  void OnPluginMsgGetVideoCaptureFormatsReply(
+      PP_Resource* capabilities_output,
+      const ResourceMessageReplyParams& params,
+      const std::vector<PP_VideoCaptureFormat>& formats);
+
+  void OnPluginMsgOpenReply(const ResourceMessageReplyParams& params);
+
+  bool is_opened() const { return open_state_ == OpenState::OPENED; }
+
+  // Holds a reference of the callback so that Close() can cancel it.
+  scoped_refptr<TrackedCallback> open_callback_;
+  OpenState open_state_;
+
+  scoped_refptr<TrackedCallback> get_capabilities_callback_;
+  scoped_refptr<CameraCapabilitiesResource> camera_capabilities_;
+
+  DISALLOW_COPY_AND_ASSIGN(CameraDeviceResource);
+};
+
+}  // namespace proxy
+}  // namespace ppapi
+
+#endif  // PPAPI_PROXY_CAMERA_DEVICE_RESOURCE_H_
diff --git a/ppapi/proxy/image_capture_resource.cc b/ppapi/proxy/image_capture_resource.cc
deleted file mode 100644
index cccd1c6..0000000
--- a/ppapi/proxy/image_capture_resource.cc
+++ /dev/null
@@ -1,119 +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 "ppapi/proxy/image_capture_resource.h"
-
-#include "ppapi/proxy/camera_capabilities_resource.h"
-#include "ppapi/proxy/plugin_resource_tracker.h"
-#include "ppapi/proxy/ppapi_messages.h"
-#include "ppapi/shared_impl/var.h"
-
-namespace ppapi {
-namespace proxy {
-
-ImageCaptureResource::ImageCaptureResource(Connection connection,
-                                           PP_Instance instance)
-    : PluginResource(connection, instance),
-      open_state_(OpenState::BEFORE_OPEN) {
-  SendCreate(RENDERER, PpapiHostMsg_ImageCapture_Create());
-}
-
-ImageCaptureResource::~ImageCaptureResource() {
-}
-
-int32_t ImageCaptureResource::Open(
-    PP_Var device_id,
-    const scoped_refptr<TrackedCallback>& callback) {
-  if (open_state_ != OpenState::BEFORE_OPEN)
-    return PP_ERROR_FAILED;
-
-  if (TrackedCallback::IsPending(open_callback_))
-    return PP_ERROR_INPROGRESS;
-
-  scoped_refptr<StringVar> source_string_var(StringVar::FromPPVar(device_id));
-  if (!source_string_var || source_string_var->value().empty())
-    return PP_ERROR_BADARGUMENT;
-
-  open_callback_ = callback;
-
-  Call<PpapiPluginMsg_ImageCapture_OpenReply>(
-      RENDERER, PpapiHostMsg_ImageCapture_Open(source_string_var->value()),
-      base::Bind(&ImageCaptureResource::OnPluginMsgOpenReply,
-                 base::Unretained(this)));
-  return PP_OK_COMPLETIONPENDING;
-}
-
-void ImageCaptureResource::Close() {
-  if (open_state_ == OpenState::CLOSED)
-    return;
-
-  if (TrackedCallback::IsPending(open_callback_)) {
-    open_callback_->PostAbort();
-    open_callback_ = nullptr;
-  }
-
-  if (TrackedCallback::IsPending(get_capabilities_callback_)) {
-    get_capabilities_callback_->PostAbort();
-    get_capabilities_callback_ = nullptr;
-  }
-
-  Post(RENDERER, PpapiHostMsg_ImageCapture_Close());
-
-  open_state_ = OpenState::CLOSED;
-}
-
-int32_t ImageCaptureResource::GetCameraCapabilities(
-    PP_Resource* capabilities,
-    const scoped_refptr<TrackedCallback>& callback) {
-  if (!is_opened())
-    return PP_ERROR_FAILED;
-
-  if (TrackedCallback::IsPending(get_capabilities_callback_))
-    return PP_ERROR_INPROGRESS;
-
-  if (camera_capabilities_.get()) {
-    *capabilities = camera_capabilities_->GetReference();
-    return PP_OK;
-  }
-
-  get_capabilities_callback_ = callback;
-  Call<PpapiPluginMsg_ImageCapture_GetSupportedPreviewSizesReply>(
-      RENDERER, PpapiHostMsg_ImageCapture_GetSupportedPreviewSizes(),
-      base::Bind(&ImageCaptureResource::OnPluginMsgGetPreviewSizesReply,
-                 base::Unretained(this), capabilities));
-  return PP_OK_COMPLETIONPENDING;
-}
-
-void ImageCaptureResource::OnPluginMsgOpenReply(
-    const ResourceMessageReplyParams& params) {
-  // The callback may have been aborted by Close().
-  if (TrackedCallback::IsPending(open_callback_)) {
-    if (open_state_ == OpenState::BEFORE_OPEN && params.result() == PP_OK)
-      open_state_ = OpenState::OPENED;
-
-    open_callback_->Run(params.result());
-  }
-}
-
-void ImageCaptureResource::OnPluginMsgGetPreviewSizesReply(
-    PP_Resource* capabilities_output,
-    const ResourceMessageReplyParams& params,
-    const std::vector<PP_Size>& preview_sizes) {
-  if (!TrackedCallback::IsPending(get_capabilities_callback_))
-    return;
-
-  // Return camera capabilities.
-  int32_t result = params.result();
-  scoped_refptr<TrackedCallback> callback;
-  callback.swap(get_capabilities_callback_);
-  if (result == PP_OK) {
-    camera_capabilities_ =
-        new CameraCapabilitiesResource(pp_instance(), preview_sizes);
-    *capabilities_output = camera_capabilities_->GetReference();
-  }
-  callback->Run(result == PP_OK ? PP_OK : PP_ERROR_FAILED);
-}
-
-}  // namespace proxy
-}  // namespace ppapi
diff --git a/ppapi/proxy/image_capture_resource.h b/ppapi/proxy/image_capture_resource.h
deleted file mode 100644
index 491c91e..0000000
--- a/ppapi/proxy/image_capture_resource.h
+++ /dev/null
@@ -1,66 +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 PPAPI_PROXY_IMAGE_CAPTURE_RESOURCE_H_
-#define PPAPI_PROXY_IMAGE_CAPTURE_RESOURCE_H_
-
-#include "base/basictypes.h"
-#include "ppapi/c/pp_size.h"
-#include "ppapi/proxy/connection.h"
-#include "ppapi/proxy/plugin_resource.h"
-#include "ppapi/proxy/ppapi_proxy_export.h"
-#include "ppapi/shared_impl/resource.h"
-#include "ppapi/thunk/ppb_image_capture_api.h"
-
-namespace ppapi {
-namespace proxy {
-
-class CameraCapabilitiesResource;
-class ImageCaptureConfigResource;
-
-class PPAPI_PROXY_EXPORT ImageCaptureResource
-    : public PluginResource,
-      public thunk::PPB_ImageCapture_API {
- public:
-  ImageCaptureResource(Connection connection, PP_Instance instance);
-  ~ImageCaptureResource() override;
-
-  // Resource overrides:
-  thunk::PPB_ImageCapture_API* AsPPB_ImageCapture_API() override {
-    return this;
-  }
-
-  // PPB_ImageCapture_API implementation.
-  int32_t Open(PP_Var device_id,
-               const scoped_refptr<TrackedCallback>& callback) override;
-  void Close() override;
-  int32_t GetCameraCapabilities(
-      PP_Resource* capabilities,
-      const scoped_refptr<TrackedCallback>& callback) override;
-
- private:
-  enum class OpenState { BEFORE_OPEN, OPENED, CLOSED };
-
-  void OnPluginMsgGetPreviewSizesReply(
-      PP_Resource* capabilities_output,
-      const ResourceMessageReplyParams& params,
-      const std::vector<PP_Size>& preview_sizes);
-  void OnPluginMsgOpenReply(const ResourceMessageReplyParams& params);
-
-  bool is_opened() const { return open_state_ == OpenState::OPENED; }
-
-  // Holds a reference of the callback so that Close() can cancel it.
-  scoped_refptr<TrackedCallback> open_callback_;
-  OpenState open_state_;
-
-  scoped_refptr<TrackedCallback> get_capabilities_callback_;
-  scoped_refptr<CameraCapabilitiesResource> camera_capabilities_;
-
-  DISALLOW_COPY_AND_ASSIGN(ImageCaptureResource);
-};
-
-}  // namespace proxy
-}  // namespace ppapi
-
-#endif  // PPAPI_PROXY_IMAGE_CAPTURE_RESOURCE_H_
diff --git a/ppapi/proxy/interface_list.cc b/ppapi/proxy/interface_list.cc
index 81c4375..806ab94 100644
--- a/ppapi/proxy/interface_list.cc
+++ b/ppapi/proxy/interface_list.cc
@@ -74,6 +74,7 @@
 #include "ppapi/c/pp_errors.h"
 #include "ppapi/c/ppp_instance.h"
 #include "ppapi/c/private/ppb_camera_capabilities_private.h"
+#include "ppapi/c/private/ppb_camera_device_private.h"
 #include "ppapi/c/private/ppb_content_decryptor_private.h"
 #include "ppapi/c/private/ppb_ext_crx_file_system_private.h"
 #include "ppapi/c/private/ppb_file_io_private.h"
@@ -90,7 +91,6 @@
 #include "ppapi/c/private/ppb_flash_message_loop.h"
 #include "ppapi/c/private/ppb_flash_print.h"
 #include "ppapi/c/private/ppb_host_resolver_private.h"
-#include "ppapi/c/private/ppb_image_capture_private.h"
 #include "ppapi/c/private/ppb_input_event_private.h"
 #include "ppapi/c/private/ppb_isolated_file_system_private.h"
 #include "ppapi/c/private/ppb_net_address_private.h"
diff --git a/ppapi/proxy/ppapi_messages.h b/ppapi/proxy/ppapi_messages.h
index 2899371..243e783 100644
--- a/ppapi/proxy/ppapi_messages.h
+++ b/ppapi/proxy/ppapi_messages.h
@@ -43,6 +43,7 @@
 #include "ppapi/c/ppb_udp_socket.h"
 #include "ppapi/c/private/pp_content_decryptor.h"
 #include "ppapi/c/private/pp_private_font_charset.h"
+#include "ppapi/c/private/pp_video_capture_format.h"
 #include "ppapi/c/private/ppb_flash.h"
 #include "ppapi/c/private/ppb_host_resolver_private.h"
 #include "ppapi/c/private/ppb_isolated_file_system_private.h"
@@ -229,6 +230,11 @@
   IPC_STRUCT_TRAITS_MEMBER(ref)
 IPC_STRUCT_TRAITS_END()
 
+IPC_STRUCT_TRAITS_BEGIN(PP_VideoCaptureFormat)
+  IPC_STRUCT_TRAITS_MEMBER(frame_size)
+  IPC_STRUCT_TRAITS_MEMBER(frame_rate)
+IPC_STRUCT_TRAITS_END()
+
 IPC_STRUCT_TRAITS_BEGIN(PP_FileInfo)
   IPC_STRUCT_TRAITS_MEMBER(size)
   IPC_STRUCT_TRAITS_MEMBER(type)
@@ -1541,17 +1547,19 @@
                      PP_Point /* top_left */)
 IPC_MESSAGE_CONTROL0(PpapiPluginMsg_Graphics2D_ReadImageDataAck)
 
-// ImageCapture ----------------------------------------------------------------
-IPC_MESSAGE_CONTROL0(PpapiHostMsg_ImageCapture_Create)
-IPC_MESSAGE_CONTROL0(PpapiHostMsg_ImageCapture_Close)
+// CameraDevice ----------------------------------------------------------------
+IPC_MESSAGE_CONTROL0(PpapiHostMsg_CameraDevice_Create)
+IPC_MESSAGE_CONTROL0(PpapiHostMsg_CameraDevice_Close)
 
-IPC_MESSAGE_CONTROL1(PpapiHostMsg_ImageCapture_Open,
+IPC_MESSAGE_CONTROL1(PpapiHostMsg_CameraDevice_Open,
                      std::string /* camera_source_id */)
-IPC_MESSAGE_CONTROL0(PpapiPluginMsg_ImageCapture_OpenReply)
+IPC_MESSAGE_CONTROL0(PpapiPluginMsg_CameraDevice_OpenReply)
 
-IPC_MESSAGE_CONTROL0(PpapiHostMsg_ImageCapture_GetSupportedPreviewSizes)
-IPC_MESSAGE_CONTROL1(PpapiPluginMsg_ImageCapture_GetSupportedPreviewSizesReply,
-                     std::vector<PP_Size> /* preview_sizes */)
+IPC_MESSAGE_CONTROL0(
+    PpapiHostMsg_CameraDevice_GetSupportedVideoCaptureFormats)
+IPC_MESSAGE_CONTROL1(
+    PpapiPluginMsg_CameraDevice_GetSupportedVideoCaptureFormatsReply,
+    std::vector<PP_VideoCaptureFormat> /* video_capture_formats */)
 
 // IsolatedFileSystem ----------------------------------------------------------
 IPC_MESSAGE_CONTROL0(PpapiHostMsg_IsolatedFileSystem_Create)
diff --git a/ppapi/proxy/ppb_image_data_proxy.cc b/ppapi/proxy/ppb_image_data_proxy.cc
index 4ed4442c..d1c040c 100644
--- a/ppapi/proxy/ppb_image_data_proxy.cc
+++ b/ppapi/proxy/ppb_image_data_proxy.cc
@@ -282,7 +282,7 @@
   cache_[image_data->pp_instance()].Add(image_data);
 
   // Schedule a timer to invalidate this entry.
-  base::MessageLoop::current()->PostDelayedTask(
+  PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostDelayedTask(
       FROM_HERE,
       RunWhileLocked(base::Bind(&ImageDataCache::OnTimer,
                                 weak_factory_.GetWeakPtr(),
diff --git a/ppapi/proxy/ppb_instance_proxy.cc b/ppapi/proxy/ppb_instance_proxy.cc
index 2897757..16a62718 100644
--- a/ppapi/proxy/ppb_instance_proxy.cc
+++ b/ppapi/proxy/ppb_instance_proxy.cc
@@ -930,7 +930,7 @@
   data->should_do_request_surrounding_text = true;
 
   if (!data->is_request_surrounding_text_pending) {
-    base::MessageLoop::current()->PostTask(
+    PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
         FROM_HERE,
         RunWhileLocked(base::Bind(&RequestSurroundingText, instance)));
     data->is_request_surrounding_text_pending = true;
diff --git a/ppapi/proxy/ppb_var_deprecated_proxy.cc b/ppapi/proxy/ppb_var_deprecated_proxy.cc
index 723fb941..7adda60 100644
--- a/ppapi/proxy/ppb_var_deprecated_proxy.cc
+++ b/ppapi/proxy/ppb_var_deprecated_proxy.cc
@@ -22,6 +22,7 @@
 #include "ppapi/proxy/ppp_class_proxy.h"
 #include "ppapi/proxy/proxy_object_var.h"
 #include "ppapi/proxy/serialized_var.h"
+#include "ppapi/shared_impl/ppapi_globals.h"
 #include "ppapi/shared_impl/ppb_var_shared.h"
 #include "ppapi/shared_impl/proxy_lock.h"
 #include "ppapi/shared_impl/var.h"
@@ -379,7 +380,7 @@
   // spurious warning).
   // TODO(piman): See if we can fix the IPC code to enforce strict ordering, and
   // then remove this.
-  base::MessageLoop::current()->PostNonNestableTask(
+  PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostNonNestableTask(
       FROM_HERE,
       RunWhileLocked(base::Bind(&PPB_Var_Deprecated_Proxy::DoReleaseObject,
                                 task_factory_.GetWeakPtr(),
diff --git a/ppapi/proxy/resource_creation_proxy.cc b/ppapi/proxy/resource_creation_proxy.cc
index 1599589..4ca95ba 100644
--- a/ppapi/proxy/resource_creation_proxy.cc
+++ b/ppapi/proxy/resource_creation_proxy.cc
@@ -7,6 +7,7 @@
 #include "ppapi/c/pp_errors.h"
 #include "ppapi/c/pp_size.h"
 #include "ppapi/proxy/audio_input_resource.h"
+#include "ppapi/proxy/camera_device_resource.h"
 #include "ppapi/proxy/compositor_resource.h"
 #include "ppapi/proxy/connection.h"
 #include "ppapi/proxy/file_chooser_resource.h"
@@ -19,7 +20,6 @@
 #include "ppapi/proxy/graphics_2d_resource.h"
 #include "ppapi/proxy/host_resolver_private_resource.h"
 #include "ppapi/proxy/host_resolver_resource.h"
-#include "ppapi/proxy/image_capture_resource.h"
 #include "ppapi/proxy/media_stream_video_track_resource.h"
 #include "ppapi/proxy/net_address_resource.h"
 #include "ppapi/proxy/network_monitor_resource.h"
@@ -432,6 +432,11 @@
   return PPB_Buffer_Proxy::CreateProxyResource(instance, size);
 }
 
+PP_Resource ResourceCreationProxy::CreateCameraDevicePrivate(
+    PP_Instance instance) {
+  return (new CameraDeviceResource(GetConnection(), instance))->GetReference();
+}
+
 PP_Resource ResourceCreationProxy::CreateFlashDRM(PP_Instance instance) {
   return (new FlashDRMResource(GetConnection(), instance))->GetReference();
 }
@@ -459,11 +464,6 @@
   return PPB_Flash_MessageLoop_Proxy::CreateProxyResource(instance);
 }
 
-PP_Resource ResourceCreationProxy::CreateImageCapturePrivate(
-    PP_Instance instance) {
-  return (new ImageCaptureResource(GetConnection(), instance))->GetReference();
-}
-
 PP_Resource ResourceCreationProxy::CreatePlatformVerificationPrivate(
     PP_Instance instance) {
   return (new PlatformVerificationPrivateResource(GetConnection(), instance))->
diff --git a/ppapi/proxy/resource_creation_proxy.h b/ppapi/proxy/resource_creation_proxy.h
index 70acd1e9..194cf7d 100644
--- a/ppapi/proxy/resource_creation_proxy.h
+++ b/ppapi/proxy/resource_creation_proxy.h
@@ -169,6 +169,7 @@
       const PP_BrowserFont_Trusted_Description* description) override;
   virtual PP_Resource CreateBuffer(PP_Instance instance,
                                    uint32_t size) override;
+  virtual PP_Resource CreateCameraDevicePrivate(PP_Instance instance) override;
   virtual PP_Resource CreateFlashDRM(PP_Instance instance) override;
   virtual PP_Resource CreateFlashFontFile(
       PP_Instance instance,
@@ -177,7 +178,6 @@
   virtual PP_Resource CreateFlashMenu(PP_Instance instance,
                                       const PP_Flash_Menu* menu_data) override;
   virtual PP_Resource CreateFlashMessageLoop(PP_Instance instance) override;
-  virtual PP_Resource CreateImageCapturePrivate(PP_Instance instance) override;
   virtual PP_Resource CreatePlatformVerificationPrivate(
       PP_Instance instance) override;
   virtual PP_Resource CreateScrollbar(PP_Instance instance,
diff --git a/ppapi/shared_impl/BUILD.gn b/ppapi/shared_impl/BUILD.gn
index 8595ae39..a70e0cf 100644
--- a/ppapi/shared_impl/BUILD.gn
+++ b/ppapi/shared_impl/BUILD.gn
@@ -143,7 +143,7 @@
   # nacl_win64 build (cross-compiled for a 32-bit Chrome), rather than the
   # native 64-bit Chrome build.
   # See also //ppapi/thunk
-  if (is_win && cpu_arch == "x64" && current_toolchain != default_toolchain) {
+  if (is_win && current_cpu == "x64" && current_toolchain != default_toolchain) {
     sources -= [
       "ppb_audio_shared.cc",
       "ppb_graphics_3d_shared.cc",
diff --git a/ppapi/shared_impl/resource.h b/ppapi/shared_impl/resource.h
index 5db4ce7e..b703784 100644
--- a/ppapi/shared_impl/resource.h
+++ b/ppapi/shared_impl/resource.h
@@ -30,6 +30,7 @@
   F(PPB_BrowserFont_Trusted_API)        \
   F(PPB_Buffer_API)                     \
   F(PPB_CameraCapabilities_API)         \
+  F(PPB_CameraDevice_API)               \
   F(PPB_Compositor_API)                 \
   F(PPB_CompositorLayer_API)            \
   F(PPB_DeviceRef_API)                  \
@@ -53,7 +54,6 @@
   F(PPB_Graphics3D_API)                 \
   F(PPB_HostResolver_API)               \
   F(PPB_HostResolver_Private_API)       \
-  F(PPB_ImageCapture_API)               \
   F(PPB_ImageData_API)                  \
   F(PPB_InputEvent_API)                 \
   F(PPB_IsolatedFileSystem_Private_API) \
diff --git a/ppapi/shared_impl/resource_tracker.cc b/ppapi/shared_impl/resource_tracker.cc
index 9b15184..28ea2787 100644
--- a/ppapi/shared_impl/resource_tracker.cc
+++ b/ppapi/shared_impl/resource_tracker.cc
@@ -90,7 +90,7 @@
 }
 
 void ResourceTracker::ReleaseResourceSoon(PP_Resource res) {
-  base::MessageLoop::current()->PostNonNestableTask(
+  PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostNonNestableTask(
       FROM_HERE,
       RunWhileLocked(base::Bind(&ResourceTracker::ReleaseResource,
                                 weak_ptr_factory_.GetWeakPtr(),
diff --git a/ppapi/tests/all_c_includes.h b/ppapi/tests/all_c_includes.h
index 0ea651e..3b54a78 100644
--- a/ppapi/tests/all_c_includes.h
+++ b/ppapi/tests/all_c_includes.h
@@ -106,6 +106,7 @@
 #include "ppapi/c/private/pp_private_font_charset.h"
 #include "ppapi/c/private/pp_video_frame_private.h"
 #include "ppapi/c/private/ppb_camera_capabilities_private.h"
+#include "ppapi/c/private/ppb_camera_device_private.h"
 #include "ppapi/c/private/ppb_content_decryptor_private.h"
 #include "ppapi/c/private/ppb_display_color_profile_private.h"
 #include "ppapi/c/private/ppb_ext_crx_file_system_private.h"
@@ -117,7 +118,6 @@
 #include "ppapi/c/private/ppb_flash_menu.h"
 #include "ppapi/c/private/ppb_flash_message_loop.h"
 #include "ppapi/c/private/ppb_host_resolver_private.h"
-#include "ppapi/c/private/ppb_image_capture_private.h"
 #include "ppapi/c/private/ppb_input_event_private.h"
 #include "ppapi/c/private/ppb_instance_private.h"
 #include "ppapi/c/private/ppb_net_address_private.h"
diff --git a/ppapi/tests/all_cpp_includes.h b/ppapi/tests/all_cpp_includes.h
index 130ed67..94b033d8 100644
--- a/ppapi/tests/all_cpp_includes.h
+++ b/ppapi/tests/all_cpp_includes.h
@@ -53,11 +53,11 @@
 #include "ppapi/cpp/network_proxy.h"
 #include "ppapi/cpp/point.h"
 #include "ppapi/cpp/private/camera_capabilities_private.h"
+#include "ppapi/cpp/private/camera_device_private.h"
 #include "ppapi/cpp/private/content_decryptor_private.h"
 #include "ppapi/cpp/private/find_private.h"
 #include "ppapi/cpp/private/flash_font_file.h"
 #include "ppapi/cpp/private/flash_fullscreen.h"
-#include "ppapi/cpp/private/image_capture_private.h"
 #include "ppapi/cpp/private/instance_private.h"
 #include "ppapi/cpp/private/instance_private.h"
 #include "ppapi/cpp/private/net_address_private.h"
diff --git a/ppapi/thunk/BUILD.gn b/ppapi/thunk/BUILD.gn
index 2dee0d7..0f034e64 100644
--- a/ppapi/thunk/BUILD.gn
+++ b/ppapi/thunk/BUILD.gn
@@ -149,6 +149,8 @@
       "ppb_buffer_thunk.cc",
       "ppb_camera_capabilities_api.h",
       "ppb_camera_capabilities_private_thunk.cc",
+      "ppb_camera_device_api.h",
+      "ppb_camera_device_private_thunk.cc",
       "ppb_char_set_thunk.cc",
       "ppb_content_decryptor_private_thunk.cc",
       "ppb_flash_clipboard_thunk.cc",
@@ -162,8 +164,6 @@
       "ppb_flash_message_loop_thunk.cc",
       "ppb_flash_thunk.cc",
       "ppb_gles_chromium_texture_mapping_thunk.cc",
-      "ppb_image_capture_api.h",
-      "ppb_image_capture_private_thunk.cc",
       "ppb_pdf_thunk.cc",
       "ppb_platform_verification_private_thunk.cc",
       "ppb_scrollbar_thunk.cc",
@@ -179,7 +179,7 @@
   # nacl_win64 build (cross-compiled for a 32-bit Chrome), rather than all
   # 64-bit builds.
   # See also //ppapi/shared_impl
-  if (is_win && cpu_arch == "x64" && current_toolchain != default_toolchain) {
+  if (is_win && current_cpu == "x64" && current_toolchain != default_toolchain) {
     sources += [
       "ppb_graphics_3d_thunk.cc",
       "ppb_host_resolver_private_thunk.cc",
diff --git a/ppapi/thunk/interfaces_ppb_private.h b/ppapi/thunk/interfaces_ppb_private.h
index 3d24b01..9f0e61c 100644
--- a/ppapi/thunk/interfaces_ppb_private.h
+++ b/ppapi/thunk/interfaces_ppb_private.h
@@ -25,6 +25,8 @@
               PPB_BrowserFont_Trusted_1_0)
 PROXIED_IFACE(PPB_CAMERACAPABILITIES_PRIVATE_INTERFACE_0_1,
               PPB_CameraCapabilities_Private_0_1)
+PROXIED_IFACE(PPB_CAMERADEVICE_PRIVATE_INTERFACE_0_1,
+              PPB_CameraDevice_Private_0_1)
 PROXIED_IFACE(PPB_CONTENTDECRYPTOR_PRIVATE_INTERFACE_0_13,
               PPB_ContentDecryptor_Private_0_13)
 PROXIED_IFACE(PPB_CHARSET_TRUSTED_INTERFACE_1_0,
@@ -41,8 +43,6 @@
               PPB_FlashFullscreen_0_1)
 PROXIED_IFACE(PPB_FLASHFULLSCREEN_INTERFACE_1_0,
               PPB_FlashFullscreen_0_1)
-PROXIED_IFACE(PPB_IMAGECAPTURE_PRIVATE_INTERFACE_0_1,
-              PPB_ImageCapture_Private_0_1)
 PROXIED_IFACE(PPB_PDF_INTERFACE,
               PPB_PDF)
 #if defined(OS_CHROMEOS)
diff --git a/ppapi/thunk/ppb_camera_capabilities_api.h b/ppapi/thunk/ppb_camera_capabilities_api.h
index ba51f5f..34d5127 100644
--- a/ppapi/thunk/ppb_camera_capabilities_api.h
+++ b/ppapi/thunk/ppb_camera_capabilities_api.h
@@ -15,8 +15,9 @@
 class PPAPI_THUNK_EXPORT PPB_CameraCapabilities_API {
  public:
   virtual ~PPB_CameraCapabilities_API() {}
-  virtual void GetSupportedPreviewSizes(int32_t* array_size,
-                                        PP_Size** preview_sizes) = 0;
+  virtual void GetSupportedVideoCaptureFormats(
+      uint32_t* array_size,
+      PP_VideoCaptureFormat** formats) = 0;
 };
 
 }  // namespace thunk
diff --git a/ppapi/thunk/ppb_camera_capabilities_private_thunk.cc b/ppapi/thunk/ppb_camera_capabilities_private_thunk.cc
index 3940a04..85a64bb 100644
--- a/ppapi/thunk/ppb_camera_capabilities_private_thunk.cc
+++ b/ppapi/thunk/ppb_camera_capabilities_private_thunk.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// From private/ppb_camera_capabilities_private.idl modified Tue Feb  3 19:54:34
+// From private/ppb_camera_capabilities_private.idl modified Thu Feb 19 09:06:18
 // 2015.
 
 #include "ppapi/c/pp_errors.h"
@@ -23,19 +23,21 @@
   return PP_FromBool(enter.succeeded());
 }
 
-void GetSupportedPreviewSizes(PP_Resource capabilities,
-                              int32_t* array_size,
-                              struct PP_Size** preview_sizes) {
-  VLOG(4) << "PPB_CameraCapabilities_Private::GetSupportedPreviewSizes()";
+void GetSupportedVideoCaptureFormats(PP_Resource capabilities,
+                                     uint32_t* array_size,
+                                     struct PP_VideoCaptureFormat** formats) {
+  VLOG(4)
+      << "PPB_CameraCapabilities_Private::GetSupportedVideoCaptureFormats()";
   EnterResource<PPB_CameraCapabilities_API> enter(capabilities, true);
   if (enter.failed())
     return;
-  enter.object()->GetSupportedPreviewSizes(array_size, preview_sizes);
+  enter.object()->GetSupportedVideoCaptureFormats(array_size, formats);
 }
 
 const PPB_CameraCapabilities_Private_0_1
-    g_ppb_cameracapabilities_private_thunk_0_1 = {&IsCameraCapabilities,
-                                                  &GetSupportedPreviewSizes};
+    g_ppb_cameracapabilities_private_thunk_0_1 = {
+        &IsCameraCapabilities,
+        &GetSupportedVideoCaptureFormats};
 
 }  // namespace
 
diff --git a/ppapi/thunk/ppb_camera_device_api.h b/ppapi/thunk/ppb_camera_device_api.h
new file mode 100644
index 0000000..29e6a27
--- /dev/null
+++ b/ppapi/thunk/ppb_camera_device_api.h
@@ -0,0 +1,33 @@
+// 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 PPAPI_THUNK_PPB_CAMERA_DEVICE_API_H_
+#define PPAPI_THUNK_PPB_CAMERA_DEVICE_API_H_
+
+#include <string>
+
+#include "ppapi/c/private/ppb_camera_device_private.h"
+#include "ppapi/thunk/ppapi_thunk_export.h"
+
+namespace ppapi {
+
+class TrackedCallback;
+
+namespace thunk {
+
+class PPAPI_THUNK_EXPORT PPB_CameraDevice_API {
+ public:
+  virtual ~PPB_CameraDevice_API() {}
+  virtual int32_t Open(PP_Var device_id,
+                       const scoped_refptr<TrackedCallback>& callback) = 0;
+  virtual void Close() = 0;
+  virtual int32_t GetCameraCapabilities(
+      PP_Resource* capabilities,
+      const scoped_refptr<TrackedCallback>& callback) = 0;
+};
+
+}  // namespace thunk
+}  // namespace ppapi
+
+#endif  // PPAPI_THUNK_PPB_CAMERA_DEVICE_API_H_
diff --git a/ppapi/thunk/ppb_camera_device_private_thunk.cc b/ppapi/thunk/ppb_camera_device_private_thunk.cc
new file mode 100644
index 0000000..94b3129
--- /dev/null
+++ b/ppapi/thunk/ppb_camera_device_private_thunk.cc
@@ -0,0 +1,74 @@
+// 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.
+
+// From private/ppb_camera_device_private.idl modified Wed Feb 18 16:44:52 2015.
+
+#include "ppapi/c/pp_completion_callback.h"
+#include "ppapi/c/pp_errors.h"
+#include "ppapi/c/private/ppb_camera_device_private.h"
+#include "ppapi/shared_impl/tracked_callback.h"
+#include "ppapi/thunk/enter.h"
+#include "ppapi/thunk/ppapi_thunk_export.h"
+#include "ppapi/thunk/ppb_camera_device_api.h"
+
+namespace ppapi {
+namespace thunk {
+
+namespace {
+
+PP_Resource Create(PP_Instance instance) {
+  VLOG(4) << "PPB_CameraDevice_Private::Create()";
+  EnterResourceCreation enter(instance);
+  if (enter.failed())
+    return 0;
+  return enter.functions()->CreateCameraDevicePrivate(instance);
+}
+
+PP_Bool IsCameraDevice(PP_Resource resource) {
+  VLOG(4) << "PPB_CameraDevice_Private::IsCameraDevice()";
+  EnterResource<PPB_CameraDevice_API> enter(resource, false);
+  return PP_FromBool(enter.succeeded());
+}
+
+int32_t Open(PP_Resource camera_device,
+             struct PP_Var device_id,
+             struct PP_CompletionCallback callback) {
+  VLOG(4) << "PPB_CameraDevice_Private::Open()";
+  EnterResource<PPB_CameraDevice_API> enter(camera_device, callback, true);
+  if (enter.failed())
+    return enter.retval();
+  return enter.SetResult(enter.object()->Open(device_id, enter.callback()));
+}
+
+void Close(PP_Resource camera_device) {
+  VLOG(4) << "PPB_CameraDevice_Private::Close()";
+  EnterResource<PPB_CameraDevice_API> enter(camera_device, true);
+  if (enter.failed())
+    return;
+  enter.object()->Close();
+}
+
+int32_t GetCameraCapabilities(PP_Resource camera_device,
+                              PP_Resource* capabilities,
+                              struct PP_CompletionCallback callback) {
+  VLOG(4) << "PPB_CameraDevice_Private::GetCameraCapabilities()";
+  EnterResource<PPB_CameraDevice_API> enter(camera_device, callback, true);
+  if (enter.failed())
+    return enter.retval();
+  return enter.SetResult(
+      enter.object()->GetCameraCapabilities(capabilities, enter.callback()));
+}
+
+const PPB_CameraDevice_Private_0_1 g_ppb_cameradevice_private_thunk_0_1 =
+    {&Create, &IsCameraDevice, &Open, &Close, &GetCameraCapabilities};
+
+}  // namespace
+
+PPAPI_THUNK_EXPORT const PPB_CameraDevice_Private_0_1*
+GetPPB_CameraDevice_Private_0_1_Thunk() {
+  return &g_ppb_cameradevice_private_thunk_0_1;
+}
+
+}  // namespace thunk
+}  // namespace ppapi
diff --git a/ppapi/thunk/ppb_image_capture_api.h b/ppapi/thunk/ppb_image_capture_api.h
deleted file mode 100644
index d0d92ae..0000000
--- a/ppapi/thunk/ppb_image_capture_api.h
+++ /dev/null
@@ -1,33 +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 PPAPI_THUNK_PPB_IMAGE_CAPTURE_API_H_
-#define PPAPI_THUNK_PPB_IMAGE_CAPTURE_API_H_
-
-#include <string>
-
-#include "ppapi/c/private/ppb_image_capture_private.h"
-#include "ppapi/thunk/ppapi_thunk_export.h"
-
-namespace ppapi {
-
-class TrackedCallback;
-
-namespace thunk {
-
-class PPAPI_THUNK_EXPORT PPB_ImageCapture_API {
- public:
-  virtual ~PPB_ImageCapture_API() {}
-  virtual int32_t Open(PP_Var device_id,
-                       const scoped_refptr<TrackedCallback>& callback) = 0;
-  virtual void Close() = 0;
-  virtual int32_t GetCameraCapabilities(
-      PP_Resource* capabilities,
-      const scoped_refptr<TrackedCallback>& callback) = 0;
-};
-
-}  // namespace thunk
-}  // namespace ppapi
-
-#endif  // PPAPI_THUNK_PPB_IMAGE_CAPTURE_API_H_
diff --git a/ppapi/thunk/ppb_image_capture_private_thunk.cc b/ppapi/thunk/ppb_image_capture_private_thunk.cc
deleted file mode 100644
index 2b55183..0000000
--- a/ppapi/thunk/ppb_image_capture_private_thunk.cc
+++ /dev/null
@@ -1,74 +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.
-
-// From private/ppb_image_capture_private.idl modified Fri Feb  6 14:55:55 2015.
-
-#include "ppapi/c/pp_completion_callback.h"
-#include "ppapi/c/pp_errors.h"
-#include "ppapi/c/private/ppb_image_capture_private.h"
-#include "ppapi/shared_impl/tracked_callback.h"
-#include "ppapi/thunk/enter.h"
-#include "ppapi/thunk/ppapi_thunk_export.h"
-#include "ppapi/thunk/ppb_image_capture_api.h"
-
-namespace ppapi {
-namespace thunk {
-
-namespace {
-
-PP_Resource Create(PP_Instance instance) {
-  VLOG(4) << "PPB_ImageCapture_Private::Create()";
-  EnterResourceCreation enter(instance);
-  if (enter.failed())
-    return 0;
-  return enter.functions()->CreateImageCapturePrivate(instance);
-}
-
-PP_Bool IsImageCapture(PP_Resource resource) {
-  VLOG(4) << "PPB_ImageCapture_Private::IsImageCapture()";
-  EnterResource<PPB_ImageCapture_API> enter(resource, false);
-  return PP_FromBool(enter.succeeded());
-}
-
-int32_t Open(PP_Resource image_capture,
-             struct PP_Var device_id,
-             struct PP_CompletionCallback callback) {
-  VLOG(4) << "PPB_ImageCapture_Private::Open()";
-  EnterResource<PPB_ImageCapture_API> enter(image_capture, callback, true);
-  if (enter.failed())
-    return enter.retval();
-  return enter.SetResult(enter.object()->Open(device_id, enter.callback()));
-}
-
-void Close(PP_Resource image_capture) {
-  VLOG(4) << "PPB_ImageCapture_Private::Close()";
-  EnterResource<PPB_ImageCapture_API> enter(image_capture, true);
-  if (enter.failed())
-    return;
-  enter.object()->Close();
-}
-
-int32_t GetCameraCapabilities(PP_Resource image_capture,
-                              PP_Resource* capabilities,
-                              struct PP_CompletionCallback callback) {
-  VLOG(4) << "PPB_ImageCapture_Private::GetCameraCapabilities()";
-  EnterResource<PPB_ImageCapture_API> enter(image_capture, callback, true);
-  if (enter.failed())
-    return enter.retval();
-  return enter.SetResult(
-      enter.object()->GetCameraCapabilities(capabilities, enter.callback()));
-}
-
-const PPB_ImageCapture_Private_0_1 g_ppb_imagecapture_private_thunk_0_1 =
-    {&Create, &IsImageCapture, &Open, &Close, &GetCameraCapabilities};
-
-}  // namespace
-
-PPAPI_THUNK_EXPORT const PPB_ImageCapture_Private_0_1*
-GetPPB_ImageCapture_Private_0_1_Thunk() {
-  return &g_ppb_imagecapture_private_thunk_0_1;
-}
-
-}  // namespace thunk
-}  // namespace ppapi
diff --git a/ppapi/thunk/resource_creation_api.h b/ppapi/thunk/resource_creation_api.h
index 204b8a1..6c781e3a 100644
--- a/ppapi/thunk/resource_creation_api.h
+++ b/ppapi/thunk/resource_creation_api.h
@@ -182,6 +182,7 @@
       PP_Instance instance,
       const PP_BrowserFont_Trusted_Description* description) = 0;
   virtual PP_Resource CreateBuffer(PP_Instance instance, uint32_t size) = 0;
+  virtual PP_Resource CreateCameraDevicePrivate(PP_Instance instance) = 0;
   virtual PP_Resource CreateFlashDRM(PP_Instance instance) = 0;
   virtual PP_Resource CreateFlashFontFile(
       PP_Instance instance,
@@ -190,7 +191,6 @@
   virtual PP_Resource CreateFlashMenu(PP_Instance instance,
                                       const PP_Flash_Menu* menu_data) = 0;
   virtual PP_Resource CreateFlashMessageLoop(PP_Instance instance) = 0;
-  virtual PP_Resource CreateImageCapturePrivate(PP_Instance instance) = 0;
   virtual PP_Resource CreatePlatformVerificationPrivate(
       PP_Instance instance) = 0;
   virtual PP_Resource CreateScrollbar(PP_Instance instance,
diff --git a/printing/BUILD.gn b/printing/BUILD.gn
index 83a735d..ed4c9d0 100644
--- a/printing/BUILD.gn
+++ b/printing/BUILD.gn
@@ -201,6 +201,8 @@
     "units_unittest.cc",
   ]
 
+  configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
+
   if (use_cups) {
     configs += [ ":cups" ]
     sources += [ "backend/cups_helper_unittest.cc" ]
diff --git a/remoting/android/java/AndroidManifest.xml.jinja2 b/remoting/android/java/AndroidManifest.xml.jinja2
index 15e05c6..1b3209b 100644
--- a/remoting/android/java/AndroidManifest.xml.jinja2
+++ b/remoting/android/java/AndroidManifest.xml.jinja2
@@ -37,6 +37,7 @@
         </activity>
         <activity android:name="org.chromium.chromoting.Desktop"
                 android:configChanges="orientation|screenSize"
+                android:theme="@style/BaseTheme.Desktop"
                 android:windowSoftInputMode="adjustResize"/>
         <activity android:name="org.chromium.chromoting.HelpActivity"
                 android:configChanges="orientation|screenSize"
diff --git a/remoting/android/java/res/values-v17/styles.xml b/remoting/android/java/res/values-v17/styles.xml
index ce6d391..3995e6a1 100644
--- a/remoting/android/java/res/values-v17/styles.xml
+++ b/remoting/android/java/res/values-v17/styles.xml
@@ -14,6 +14,13 @@
         <item name="windowActionBar">false</item>
         <item name="android:windowDrawsSystemBarBackgrounds">true</item>
     </style>
+    <style name="BaseTheme.Desktop" parent="@style/BaseTheme">
+        <item name="actionBarStyle">@style/ActionBar.Desktop</item>
+    </style>
+
+    <style name="ActionBar.Desktop" parent="@style/Widget.AppCompat.ActionBar">
+        <item name="displayOptions">homeAsUp</item>
+    </style>
 
     <style name="EmptyStateText" parent="@android:style/TextAppearance.Large">
         <item name="android:textColor">#888</item>
diff --git a/remoting/android/java/src/org/chromium/chromoting/Desktop.java b/remoting/android/java/src/org/chromium/chromoting/Desktop.java
index a7762fa7..7135492 100644
--- a/remoting/android/java/src/org/chromium/chromoting/Desktop.java
+++ b/remoting/android/java/src/org/chromium/chromoting/Desktop.java
@@ -51,7 +51,11 @@
         mOverlayButton = (ImageButton) findViewById(R.id.desktop_overlay_button);
         mRemoteHostDesktop.setDesktop(this);
 
-        // Ensure the button is initially hidden.
+        // For this Activity, the home button in the action bar acts as a Disconnect button, so
+        // set the description for accessibility/screen readers.
+        getSupportActionBar().setHomeActionContentDescription(R.string.disconnect_myself_button);
+
+        // Ensure the overlay button is initially hidden.
         showActionBar();
 
         View decorView = getWindow().getDecorView();
@@ -193,7 +197,7 @@
             hideActionBar();
             return true;
         }
-        if (id == R.id.actionbar_disconnect) {
+        if (id == R.id.actionbar_disconnect || id == android.R.id.home) {
             JniInterface.disconnectFromHost();
             return true;
         }
diff --git a/remoting/app_remoting_webapp.gyp b/remoting/app_remoting_webapp.gyp
index d9d8cda85..de11d0f 100644
--- a/remoting/app_remoting_webapp.gyp
+++ b/remoting/app_remoting_webapp.gyp
@@ -71,7 +71,9 @@
     {
       # Sample AppRemoting app.
       'target_name': 'ar_sample_app',
+      'app_key': 'Sample_App',
       'app_id': 'ljacajndfccfgnfohlgkdphmbnpkjflk',
+      'app_client_id': 'sample_client_id',
       'app_name': 'App Remoting Client',
       'app_description': 'App Remoting client',
       'app_capabilities': ['GOOGLE_DRIVE'],
diff --git a/remoting/client/plugin/chromoting_instance.cc b/remoting/client/plugin/chromoting_instance.cc
index c75f57a..e770efb 100644
--- a/remoting/client/plugin/chromoting_instance.cc
+++ b/remoting/client/plugin/chromoting_instance.cc
@@ -350,6 +350,8 @@
     HandleSendMouseInputWhenUnfocused();
   } else if (method == "delegateLargeCursors") {
     HandleDelegateLargeCursors();
+  } else if (method == "enableDebugRegion") {
+    HandleEnableDebugRegion(*data);
   }
 }
 
@@ -436,6 +438,25 @@
   PostLegacyJsonMessage("onDesktopShape", data.Pass());
 }
 
+void ChromotingInstance::OnVideoFrameDirtyRegion(
+    const webrtc::DesktopRegion& dirty_region) {
+  scoped_ptr<base::ListValue> rects_value(new base::ListValue());
+  for (webrtc::DesktopRegion::Iterator i(dirty_region); !i.IsAtEnd();
+       i.Advance()) {
+    const webrtc::DesktopRect& rect = i.rect();
+    scoped_ptr<base::ListValue> rect_value(new base::ListValue());
+    rect_value->AppendInteger(rect.left());
+    rect_value->AppendInteger(rect.top());
+    rect_value->AppendInteger(rect.width());
+    rect_value->AppendInteger(rect.height());
+    rects_value->Append(rect_value.release());
+  }
+
+  scoped_ptr<base::DictionaryValue> data(new base::DictionaryValue());
+  data->Set("rects", rects_value.release());
+  PostLegacyJsonMessage("onDebugRegion", data.Pass());
+}
+
 void ChromotingInstance::OnConnectionState(
     protocol::ConnectionToHost::State state,
     protocol::ErrorCode error) {
@@ -642,7 +663,7 @@
   if (data.GetBoolean("enableVideoDecodeRenderer",
                       &enable_video_decode_renderer) &&
       enable_video_decode_renderer) {
-    LOG(ERROR) << "Initializing 3D renderer.";
+    LogToWebapp("Initializing 3D renderer.");
     video_renderer_.reset(new PepperVideoRenderer3D());
     if (!video_renderer_->Initialize(this, context_, this))
       video_renderer_.reset();
@@ -650,7 +671,7 @@
 
   // If we didn't initialize 3D renderer then use the 2D renderer.
   if (!video_renderer_) {
-    LOG(ERROR) << "Initializing 2D renderer.";
+    LogToWebapp("Initializing 2D renderer.");
     video_renderer_.reset(new PepperVideoRenderer2D());
     if (!video_renderer_->Initialize(this, context_, this))
       video_renderer_.reset();
@@ -947,6 +968,17 @@
   cursor_setter_.set_delegate_stub(this);
 }
 
+void ChromotingInstance::HandleEnableDebugRegion(
+    const base::DictionaryValue& data) {
+  bool enable = false;
+  if (!data.GetBoolean("enable", &enable)) {
+    LOG(ERROR) << "Invalid enableDebugRegion.";
+    return;
+  }
+
+  video_renderer_->EnableDebugDirtyRegion(enable);
+}
+
 void ChromotingInstance::Disconnect() {
   DCHECK(plugin_task_runner_->BelongsToCurrentThread());
 
@@ -1136,4 +1168,17 @@
          (client_->connection_state() == protocol::ConnectionToHost::CONNECTED);
 }
 
+void ChromotingInstance::LogToWebapp(const std::string& message) {
+  DCHECK(plugin_task_runner_->BelongsToCurrentThread());
+
+  LOG(ERROR) << message;
+
+#if !defined(OS_NACL)
+  // Log messages are forwarded to the webapp only in PNaCl version of the
+  // plugin, so ProcessLogToUI() needs to be called explicitly in the non-PNaCl
+  // version.
+  ProcessLogToUI(message);
+#endif  // !defined(OS_NACL)
+}
+
 }  // namespace remoting
diff --git a/remoting/client/plugin/chromoting_instance.h b/remoting/client/plugin/chromoting_instance.h
index f6e8d32..cb94e079f 100644
--- a/remoting/client/plugin/chromoting_instance.h
+++ b/remoting/client/plugin/chromoting_instance.h
@@ -131,6 +131,8 @@
   void OnVideoSize(const webrtc::DesktopSize& size,
                       const webrtc::DesktopVector& dpi) override;
   void OnVideoShape(const webrtc::DesktopRegion& shape) override;
+  void OnVideoFrameDirtyRegion(
+      const webrtc::DesktopRegion& dirty_region) override;
 
   // Registers a global log message handler that redirects the log output to
   // our plugin instance.
@@ -192,6 +194,7 @@
   void HandleAllowMouseLockMessage();
   void HandleSendMouseInputWhenUnfocused();
   void HandleDelegateLargeCursors();
+  void HandleEnableDebugRegion(const base::DictionaryValue& data);
 
   void Disconnect();
 
@@ -206,7 +209,7 @@
   // TODO(sergeyu): When all current versions of the webapp support raw messages
   // remove this method and use PostChromotingMessage() instead.
   void PostLegacyJsonMessage(const std::string& method,
-                       scoped_ptr<base::DictionaryValue> data);
+                             scoped_ptr<base::DictionaryValue> data);
 
   // Posts trapped keys to the web-app to handle.
   void SendTrappedKey(uint32 usb_keycode, bool pressed);
@@ -230,6 +233,9 @@
       bool pairing_supported,
       const protocol::SecretFetchedCallback& secret_fetched_callback);
 
+  // Helper to log messages in the JS console in the webapp.
+  void LogToWebapp(const std::string& message);
+
   bool initialized_;
 
   PepperPluginThreadDelegate plugin_thread_delegate_;
diff --git a/remoting/client/plugin/pepper_video_renderer.h b/remoting/client/plugin/pepper_video_renderer.h
index b919236..86713a5 100644
--- a/remoting/client/plugin/pepper_video_renderer.h
+++ b/remoting/client/plugin/pepper_video_renderer.h
@@ -42,6 +42,11 @@
 
     // Called when desktop shape changes.
     virtual void OnVideoShape(const webrtc::DesktopRegion& shape) = 0;
+
+    // Called with each frame's updated region, if EnableDebugDirtyRegion(true)
+    // was called.
+    virtual void OnVideoFrameDirtyRegion(
+        const webrtc::DesktopRegion& dirty_region) = 0;
   };
 
   // Initializes the renderer. |instance| and |event_handler| must outlive the
@@ -52,6 +57,10 @@
 
   // Must be called whenever the plugin view changes.
   virtual void OnViewChanged(const pp::View& view) = 0;
+
+  // Enables or disables delivery of dirty region information to the
+  // EventHandler, for debugging purposes.
+  virtual void EnableDebugDirtyRegion(bool enable) = 0;
 };
 
 }  // namespace remoting
diff --git a/remoting/client/plugin/pepper_video_renderer_2d.cc b/remoting/client/plugin/pepper_video_renderer_2d.cc
index 959c0b5e..678e829 100644
--- a/remoting/client/plugin/pepper_video_renderer_2d.cc
+++ b/remoting/client/plugin/pepper_video_renderer_2d.cc
@@ -69,6 +69,7 @@
       dips_to_view_scale_(1.0f),
       flush_pending_(false),
       frame_received_(false),
+      debug_dirty_region_(false),
       callback_factory_(this),
       weak_factory_(this) {
 }
@@ -178,6 +179,10 @@
   }
 }
 
+void PepperVideoRenderer2D::EnableDebugDirtyRegion(bool enable) {
+  debug_dirty_region_ = enable;
+}
+
 void PepperVideoRenderer2D::OnSessionConfig(
     const protocol::SessionConfig& config) {
   DCHECK(CalledOnValidThread());
@@ -338,6 +343,11 @@
   int error = graphics2d_.Flush(callback);
   CHECK(error == PP_OK_COMPLETIONPENDING);
   flush_pending_ = true;
+
+  // If Debug dirty region is enabled then emit it.
+  if (debug_dirty_region_) {
+    event_handler_->OnVideoFrameDirtyRegion(region);
+  }
 }
 
 void PepperVideoRenderer2D::OnFlushDone(int result,
diff --git a/remoting/client/plugin/pepper_video_renderer_2d.h b/remoting/client/plugin/pepper_video_renderer_2d.h
index 8b19475..bae9cb3 100644
--- a/remoting/client/plugin/pepper_video_renderer_2d.h
+++ b/remoting/client/plugin/pepper_video_renderer_2d.h
@@ -47,6 +47,7 @@
                                 const ClientContext& context,
                                 EventHandler* event_handler) override;
   void OnViewChanged(const pp::View& view) override;
+  void EnableDebugDirtyRegion(bool enable) override;
 
   // VideoRenderer interface.
   void OnSessionConfig(const protocol::SessionConfig& config) override;
@@ -131,6 +132,9 @@
   // True after the first call to ApplyBuffer().
   bool frame_received_;
 
+  // True if dirty regions are to be sent to |event_handler_| for debugging.
+  bool debug_dirty_region_;
+
   pp::CompletionCallbackFactory<PepperVideoRenderer2D> callback_factory_;
   base::WeakPtrFactory<PepperVideoRenderer2D> weak_factory_;
 
diff --git a/remoting/client/plugin/pepper_video_renderer_3d.cc b/remoting/client/plugin/pepper_video_renderer_3d.cc
index 91f0677..711c210 100644
--- a/remoting/client/plugin/pepper_video_renderer_3d.cc
+++ b/remoting/client/plugin/pepper_video_renderer_3d.cc
@@ -153,6 +153,10 @@
   PaintIfNeeded();
 }
 
+void PepperVideoRenderer3D::EnableDebugDirtyRegion(bool enable) {
+  debug_dirty_region_ = enable;
+}
+
 void PepperVideoRenderer3D::OnSessionConfig(
     const protocol::SessionConfig& config) {
   PP_VideoProfile video_profile = PP_VIDEOPROFILE_VP8_ANY;
@@ -250,6 +254,18 @@
     event_handler_->OnVideoShape(desktop_shape_);
   }
 
+  // Report the dirty region, for debugging, if requested.
+  if (debug_dirty_region_) {
+    webrtc::DesktopRegion dirty_region;
+    for (int i = 0; i < packet->dirty_rects_size(); ++i) {
+      Rect remoting_rect = packet->dirty_rects(i);
+      dirty_region.AddRect(webrtc::DesktopRect::MakeXYWH(
+          remoting_rect.x(), remoting_rect.y(),
+          remoting_rect.width(), remoting_rect.height()));
+    }
+    event_handler_->OnVideoFrameDirtyRegion(dirty_region);
+  }
+
   pending_packets_.push_back(
       new PendingPacket(packet.Pass(), done_runner.Release()));
   DecodeNextPacket();
@@ -358,14 +374,16 @@
   EnsureProgramForTexture(picture.texture_target);
 
   gles2_if_->UseProgram(graphics_3d, shader_program_);
-  if (picture.texture_target == GL_TEXTURE_RECTANGLE_ARB) {
-    gles2_if_->Uniform2f(graphics_3d, shader_texcoord_scale_location_,
-                         static_cast<GLfloat>(picture.texture_size.width),
-                         static_cast<GLfloat>(picture.texture_size.height));
-  } else {
-    gles2_if_->Uniform2f(
-        graphics_3d, shader_texcoord_scale_location_, 1.0, 1.0);
+
+  // Calculate v_scale passed to the vertex shader.
+  double scale_x = picture.visible_rect.size.width;
+  double scale_y = picture.visible_rect.size.height;
+  if (picture.texture_target != GL_TEXTURE_RECTANGLE_ARB) {
+    scale_x /= picture.texture_size.width;
+    scale_y /= picture.texture_size.height;
   }
+  gles2_if_->Uniform2f(graphics_3d, shader_texcoord_scale_location_,
+                       scale_x, scale_y);
 
   // Set viewport position & dimensions.
   gles2_if_->Viewport(graphics_3d, 0, 0, view_size_.width(),
diff --git a/remoting/client/plugin/pepper_video_renderer_3d.h b/remoting/client/plugin/pepper_video_renderer_3d.h
index b5d77aa..afe3c93e 100644
--- a/remoting/client/plugin/pepper_video_renderer_3d.h
+++ b/remoting/client/plugin/pepper_video_renderer_3d.h
@@ -38,6 +38,7 @@
                   const ClientContext& context,
                   EventHandler* event_handler) override;
   void OnViewChanged(const pp::View& view) override;
+  void EnableDebugDirtyRegion(bool enable) override;
 
   // VideoRenderer interface.
   void OnSessionConfig(const protocol::SessionConfig& config) override;
@@ -149,6 +150,9 @@
   // Location of the scale value to be passed to the |shader_program_|.
   int shader_texcoord_scale_location_;
 
+  // True if dirty regions are to be sent to |event_handler_| for debugging.
+  bool debug_dirty_region_;
+
   pp::CompletionCallbackFactory<PepperVideoRenderer3D> callback_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(PepperVideoRenderer3D);
diff --git a/remoting/host/BUILD.gn b/remoting/host/BUILD.gn
index 42bf79e..12c6bd0 100644
--- a/remoting/host/BUILD.gn
+++ b/remoting/host/BUILD.gn
@@ -54,7 +54,6 @@
   if (is_chromeos) {
     deps += [
       "//cc",
-      "//content",
       "//ppapi/host",
       "//skia",
       "//ui/aura",
diff --git a/remoting/host/DEPS b/remoting/host/DEPS
index 5c171ce..3abc8545 100644
--- a/remoting/host/DEPS
+++ b/remoting/host/DEPS
@@ -1,7 +1,6 @@
 include_rules = [
   "+ash",
   "+cc/output",
-  "+content/public/browser",
   "+components/policy/core/common",
   "+extensions/browser/api/messaging",
   "+jingle/glue",
diff --git a/remoting/host/chromeos/clipboard_aura.cc b/remoting/host/chromeos/clipboard_aura.cc
index 7fca2df..53ce71e2 100644
--- a/remoting/host/chromeos/clipboard_aura.cc
+++ b/remoting/host/chromeos/clipboard_aura.cc
@@ -5,8 +5,6 @@
 #include "remoting/host/chromeos/clipboard_aura.h"
 
 #include "base/strings/utf_string_conversions.h"
-#include "base/timer/timer.h"
-#include "content/public/browser/browser_thread.h"
 #include "remoting/base/constants.h"
 #include "remoting/proto/event.pb.h"
 #include "remoting/protocol/clipboard_stub.h"
@@ -22,83 +20,31 @@
 
 namespace remoting {
 
-class ClipboardAura::Core {
- public:
-  Core();
-
-  // Mirror the public interface.
-  void Start(scoped_ptr<protocol::ClipboardStub> client_clipboard);
-  void InjectClipboardEvent(const protocol::ClipboardEvent& event);
-  void Stop();
-
-  // Overrides the clipboard polling interval for unit test.
-  void SetPollingIntervalForTesting(base::TimeDelta polling_interval);
-
- private:
-  void CheckClipboardForChanges();
-
-  scoped_ptr<protocol::ClipboardStub> client_clipboard_;
-  scoped_ptr<base::RepeatingTimer<Core>> clipboard_polling_timer_;
-  uint64 current_change_count_;
-  base::TimeDelta polling_interval_;
-
-  DISALLOW_COPY_AND_ASSIGN(Core);
-};
-
-ClipboardAura::ClipboardAura(
-    scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner)
-    : core_(new Core()),
-      ui_task_runner_(ui_task_runner) {
-}
-
-ClipboardAura::~ClipboardAura() {
-  ui_task_runner_->DeleteSoon(FROM_HERE, core_.release());
-}
-
-void ClipboardAura::Start(
-    scoped_ptr<protocol::ClipboardStub> client_clipboard) {
-  ui_task_runner_->PostTask(
-      FROM_HERE, base::Bind(&Core::Start, base::Unretained(core_.get()),
-                            base::Passed(&client_clipboard)));
-}
-
-void ClipboardAura::InjectClipboardEvent(
-    const protocol::ClipboardEvent& event) {
-  ui_task_runner_->PostTask(FROM_HERE,
-                            base::Bind(&Core::InjectClipboardEvent,
-                                       base::Unretained(core_.get()), event));
-}
-
-void ClipboardAura::Stop() {
-  ui_task_runner_->PostTask(
-      FROM_HERE, base::Bind(&Core::Stop, base::Unretained(core_.get())));
-};
-
-void ClipboardAura::SetPollingIntervalForTesting(
-    base::TimeDelta polling_interval) {
-  core_->SetPollingIntervalForTesting(polling_interval);
-}
-
-ClipboardAura::Core::Core()
+ClipboardAura::ClipboardAura()
     : current_change_count_(0),
       polling_interval_(
           base::TimeDelta::FromMilliseconds(kClipboardPollingIntervalMs)) {
 }
 
-void ClipboardAura::Core::Start(
+ClipboardAura::~ClipboardAura() {
+}
+
+void ClipboardAura::Start(
     scoped_ptr<protocol::ClipboardStub> client_clipboard) {
-  client_clipboard_.reset(client_clipboard.release());
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  client_clipboard_ = client_clipboard.Pass();
 
   // Aura doesn't provide a clipboard-changed notification. The only way to
   // detect clipboard changes is by polling.
-  clipboard_polling_timer_.reset(new base::RepeatingTimer<Core>());
-  clipboard_polling_timer_->Start(
-      FROM_HERE, polling_interval_, this,
-      &ClipboardAura::Core::CheckClipboardForChanges);
+  clipboard_polling_timer_.Start(FROM_HERE, polling_interval_, this,
+                                 &ClipboardAura::CheckClipboardForChanges);
 }
 
-void ClipboardAura::Core::InjectClipboardEvent(
+void ClipboardAura::InjectClipboardEvent(
     const protocol::ClipboardEvent& event) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
   // Currently we only handle UTF-8 text.
   if (event.mime_type().compare(kMimeTypeTextUtf8) != 0) {
     return;
@@ -112,17 +58,16 @@
   current_change_count_++;
 }
 
-void ClipboardAura::Core::Stop() {
-  clipboard_polling_timer_.reset();
-  client_clipboard_.reset();
-}
-
-void ClipboardAura::Core::SetPollingIntervalForTesting(
+void ClipboardAura::SetPollingIntervalForTesting(
     base::TimeDelta polling_interval) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
   polling_interval_ = polling_interval;
 }
 
-void ClipboardAura::Core::CheckClipboardForChanges() {
+void ClipboardAura::CheckClipboardForChanges() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
   ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread();
   uint64 change_count =
       clipboard->GetSequenceNumber(ui::CLIPBOARD_TYPE_COPY_PASTE);
@@ -144,9 +89,7 @@
 }
 
 scoped_ptr<Clipboard> Clipboard::Create() {
-  return make_scoped_ptr(
-      new ClipboardAura(content::BrowserThread::GetMessageLoopProxyForThread(
-          content::BrowserThread::UI)));
+  return make_scoped_ptr(new ClipboardAura());
 }
 
 }  // namespace remoting
diff --git a/remoting/host/chromeos/clipboard_aura.h b/remoting/host/chromeos/clipboard_aura.h
index 9907fb6d..1d03380 100644
--- a/remoting/host/chromeos/clipboard_aura.h
+++ b/remoting/host/chromeos/clipboard_aura.h
@@ -6,7 +6,8 @@
 #define REMOTING_HOST_CLIPBOARD_AURA_H_
 
 #include "base/memory/scoped_ptr.h"
-#include "base/single_thread_task_runner.h"
+#include "base/threading/thread_checker.h"
+#include "base/timer/timer.h"
 #include "remoting/host/clipboard.h"
 
 namespace remoting {
@@ -25,26 +26,26 @@
 // The public API of this class can be called in any thread as internally it
 // always posts the call to the |ui_task_runner|.  On ChromeOS, that should
 // be the UI thread of the browser process.
-//
 class ClipboardAura : public Clipboard {
  public:
-  explicit ClipboardAura(
-      scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner);
+  explicit ClipboardAura();
   ~ClipboardAura() override;
 
   // Clipboard interface.
   void Start(scoped_ptr<protocol::ClipboardStub> client_clipboard) override;
   void InjectClipboardEvent(const protocol::ClipboardEvent& event) override;
-  void Stop() override;
 
   // Overrides the clipboard polling interval for unit test.
   void SetPollingIntervalForTesting(base::TimeDelta polling_interval);
 
  private:
-  class Core;
+  void CheckClipboardForChanges();
 
-  scoped_ptr<Core> core_;
-  scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner_;
+  base::ThreadChecker thread_checker_;
+  scoped_ptr<protocol::ClipboardStub> client_clipboard_;
+  base::RepeatingTimer<ClipboardAura> clipboard_polling_timer_;
+  uint64 current_change_count_;
+  base::TimeDelta polling_interval_;
 
   DISALLOW_COPY_AND_ASSIGN(ClipboardAura);
 };
diff --git a/remoting/host/chromeos/clipboard_aura_unittest.cc b/remoting/host/chromeos/clipboard_aura_unittest.cc
index 5201c11..b375e8a 100644
--- a/remoting/host/chromeos/clipboard_aura_unittest.cc
+++ b/remoting/host/chromeos/clipboard_aura_unittest.cc
@@ -55,7 +55,6 @@
   void StopAndResetClipboard();
 
   base::MessageLoopForUI message_loop_;
-  base::RunLoop run_loop_;
   ClientClipboard* client_clipboard_;
   scoped_ptr<ClipboardAura> clipboard_;
 };
@@ -71,12 +70,13 @@
   scoped_refptr<base::SingleThreadTaskRunner> task_runner =
       message_loop_.message_loop_proxy();
   client_clipboard_ = new ClientClipboard();
-  clipboard_.reset(new ClipboardAura(task_runner));
-  clipboard_->Start(make_scoped_ptr(client_clipboard_));
+  clipboard_.reset(new ClipboardAura());
 
   EXPECT_GT(TestTimeouts::tiny_timeout(), kTestOverridePollingInterval * 10)
       << "The test timeout should be greater than the polling interval";
   clipboard_->SetPollingIntervalForTesting(kTestOverridePollingInterval);
+
+  clipboard_->Start(make_scoped_ptr(client_clipboard_));
 }
 
 void ClipboardAuraTest::TearDown() {
@@ -84,7 +84,6 @@
 }
 
 void ClipboardAuraTest::StopAndResetClipboard() {
-  clipboard_->Stop();
   clipboard_.reset();
 }
 
@@ -95,7 +94,7 @@
 
   clipboard_->InjectClipboardEvent(event);
   StopAndResetClipboard();
-  run_loop_.RunUntilIdle();
+  base::RunLoop().RunUntilIdle();
 
   std::string clipboard_data;
   ui::Clipboard* aura_clipboard = ui::Clipboard::GetForCurrentThread();
@@ -106,6 +105,8 @@
 }
 
 TEST_F(ClipboardAuraTest, MonitorClipboardChanges) {
+  base::RunLoop().RunUntilIdle();
+
   {
     // |clipboard_writer| will write to the clipboard when it goes out of scope.
     ui::ScopedClipboardWriter clipboard_writer(ui::CLIPBOARD_TYPE_COPY_PASTE);
@@ -116,14 +117,15 @@
               InjectClipboardEvent(Property(&protocol::ClipboardEvent::data,
                                             Eq("Test data.")))).Times(1);
 
+  base::RunLoop run_loop;
   message_loop_.PostDelayedTask(
       FROM_HERE, base::Bind(&ClipboardAuraTest_MonitorClipboardChanges_Test::
                                 StopAndResetClipboard,
                             base::Unretained(this)),
       TestTimeouts::tiny_timeout());
-  message_loop_.PostDelayedTask(FROM_HERE, base::MessageLoop::QuitClosure(),
+  message_loop_.PostDelayedTask(FROM_HERE, run_loop.QuitClosure(),
                                 TestTimeouts::tiny_timeout());
-  message_loop_.Run();
+  run_loop.Run();
 }
 
 }  // namespace remoting
diff --git a/remoting/host/chromoting_host_context.cc b/remoting/host/chromoting_host_context.cc
index 319cc77..e2943ff3 100644
--- a/remoting/host/chromoting_host_context.cc
+++ b/remoting/host/chromoting_host_context.cc
@@ -4,8 +4,8 @@
 
 #include "remoting/host/chromoting_host_context.h"
 
+#include "base/bind.h"
 #include "base/threading/thread_restrictions.h"
-#include "content/public/browser/browser_thread.h"
 #include "remoting/base/auto_thread.h"
 #include "remoting/base/url_request_context_getter.h"
 
@@ -127,50 +127,39 @@
 }
 
 #if defined(OS_CHROMEOS)
-namespace {
-// Retrieves the task_runner from the browser thread with |id|.
-scoped_refptr<AutoThreadTaskRunner> WrapBrowserThread(
-    content::BrowserThread::ID id) {
-  // AutoThreadTaskRunner is a TaskRunner with the special property that it will
-  // continue to process tasks until no references remain, at least. The
-  // QuitClosure we usually pass does the simple thing of stopping the
-  // underlying TaskRunner.  Since we are re-using the ui_task_runner of the
-  // browser thread, we cannot stop it explicitly.  Therefore, base::DoNothing
-  // is passed in as the quit closure.
-  // TODO(kelvinp): Fix this (See crbug.com/428187).
-  return new AutoThreadTaskRunner(
-      content::BrowserThread::GetMessageLoopProxyForThread(id).get(),
-      base::Bind(&base::DoNothing));
-}
-
-}  // namespace
 
 // static
 scoped_ptr<ChromotingHostContext> ChromotingHostContext::CreateForChromeOS(
-    scoped_refptr<net::URLRequestContextGetter> url_request_context_getter) {
+    scoped_refptr<net::URLRequestContextGetter> url_request_context_getter,
+    scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
+    scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner,
+    scoped_refptr<base::SingleThreadTaskRunner> file_task_runner) {
   DCHECK(url_request_context_getter.get());
 
-  // Use BrowserThread::FILE as the joiner as it is the only browser-thread
+
+  // AutoThreadTaskRunner is a TaskRunner with the special property that it will
+  // continue to process tasks until no references remain, at least. The
+  // QuitClosure we usually pass does the simple thing of stopping the
+  // underlying TaskRunner. Since we are re-using browser's threads, we cannot
+  // stop them explicitly. Therefore, base::DoNothing is passed in as the quit
+  // closure.
+  scoped_refptr<AutoThreadTaskRunner> io_auto_task_runner =
+      new AutoThreadTaskRunner(io_task_runner, base::Bind(&base::DoNothing));
+  scoped_refptr<AutoThreadTaskRunner> file_auto_task_runner =
+      new AutoThreadTaskRunner(file_task_runner, base::Bind(&base::DoNothing));
+  scoped_refptr<AutoThreadTaskRunner> ui_auto_task_runner =
+      new AutoThreadTaskRunner(ui_task_runner, base::Bind(&base::DoNothing));
+
+  // Use browser's file thread as the joiner as it is the only browser-thread
   // that allows blocking I/O, which is required by thread joining.
-  // TODO(kelvinp): Fix AutoThread so that it can be joinable on task runners
-  // that disallow I/O (crbug.com/428466).
-  scoped_refptr<AutoThreadTaskRunner> file_task_runner =
-      WrapBrowserThread(content::BrowserThread::FILE);
-
-  scoped_refptr<AutoThreadTaskRunner> ui_task_runner =
-      WrapBrowserThread(content::BrowserThread::UI);
-
   return make_scoped_ptr(new ChromotingHostContext(
-      ui_task_runner,
-      AutoThread::CreateWithType("ChromotingAudioThread", file_task_runner,
-                                 base::MessageLoop::TYPE_IO),
-      file_task_runner,
-      AutoThread::CreateWithType("ChromotingInputThread", file_task_runner,
-                                 base::MessageLoop::TYPE_IO),
-      WrapBrowserThread(content::BrowserThread::IO),  // network_task_runner
-      ui_task_runner,  // video_capture_task_runner
-      AutoThread::CreateWithType("ChromotingEncodeThread", file_task_runner,
-                                 base::MessageLoop::TYPE_IO),
+      ui_auto_task_runner,
+      AutoThread::Create("ChromotingAudioThread", file_auto_task_runner),
+      file_auto_task_runner,
+      ui_auto_task_runner,  // input_task_runner
+      io_auto_task_runner,  // network_task_runner
+      ui_auto_task_runner,  // video_capture_task_runner
+      AutoThread::Create("ChromotingEncodeThread", file_auto_task_runner),
       url_request_context_getter));
 }
 #endif  // defined(OS_CHROMEOS)
diff --git a/remoting/host/chromoting_host_context.h b/remoting/host/chromoting_host_context.h
index 9de7067..af6216a 100644
--- a/remoting/host/chromoting_host_context.h
+++ b/remoting/host/chromoting_host_context.h
@@ -9,6 +9,10 @@
 #include "base/memory/ref_counted.h"
 #include "base/memory/scoped_ptr.h"
 
+namespace base {
+class SingleThreadTaskRunner;
+}  // namespace base
+
 namespace net {
 class URLRequestContextGetter;
 }  // namespace net
@@ -39,7 +43,10 @@
   // the IO Thread of the browser process).
   // Instead, we re-use the |url_request_context_getter| in the browser process.
   static scoped_ptr<ChromotingHostContext> CreateForChromeOS(
-      scoped_refptr<net::URLRequestContextGetter> url_request_context_getter);
+      scoped_refptr<net::URLRequestContextGetter> url_request_context_getter,
+      scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
+      scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner,
+      scoped_refptr<base::SingleThreadTaskRunner> file_task_runner);
 #endif  // defined(OS_CHROMEOS)
 
   ~ChromotingHostContext();
diff --git a/remoting/host/client_session_unittest.cc b/remoting/host/client_session_unittest.cc
index 670d5b3..6aee5bb 100644
--- a/remoting/host/client_session_unittest.cc
+++ b/remoting/host/client_session_unittest.cc
@@ -384,7 +384,7 @@
 
   ConnectClientSession();
 
-  // With for the first frame.
+  // Wait for the first frame.
   run_loop.Run();
 
   // Inject test events that are expected to be injected.
@@ -445,8 +445,7 @@
   client_session_.reset();
 }
 
-// crbug.com/458691
-TEST_F(ClientSessionTest, DISABLED_LocalInputTest) {
+TEST_F(ClientSessionTest, LocalInputTest) {
   CreateClientSession();
 
   protocol::MouseEvent mouse_event1;
@@ -502,8 +501,7 @@
   ConnectClientSession();
 }
 
-// crbug.com/458691
-TEST_F(ClientSessionTest, DISABLED_RestoreEventState) {
+TEST_F(ClientSessionTest, RestoreEventState) {
   CreateClientSession();
 
   protocol::KeyEvent key1;
@@ -559,8 +557,7 @@
   ConnectClientSession();
 }
 
-// crbug.com/458691
-TEST_F(ClientSessionTest, DISABLED_ClampMouseEvents) {
+TEST_F(ClientSessionTest, ClampMouseEvents) {
   CreateClientSession();
 
   Expectation authenticated =
diff --git a/remoting/host/clipboard.h b/remoting/host/clipboard.h
index 534bc4b1..8ac3381 100644
--- a/remoting/host/clipboard.h
+++ b/remoting/host/clipboard.h
@@ -25,9 +25,6 @@
   // Initialises any objects needed to read from or write to the clipboard.
   virtual void Start(scoped_ptr<protocol::ClipboardStub> client_clipboard) = 0;
 
-  // Destroys any objects initialised by Start().
-  virtual void Stop() = 0;
-
   // Writes an item to the clipboard. It must be called after Start().
   virtual void InjectClipboardEvent(const protocol::ClipboardEvent& event) = 0;
 
diff --git a/remoting/host/clipboard_mac.mm b/remoting/host/clipboard_mac.mm
index f3f369ee..68c753b 100644
--- a/remoting/host/clipboard_mac.mm
+++ b/remoting/host/clipboard_mac.mm
@@ -33,7 +33,6 @@
   // Must be called on the UI thread.
   void Start(scoped_ptr<protocol::ClipboardStub> client_clipboard) override;
   void InjectClipboardEvent(const protocol::ClipboardEvent& event) override;
-  void Stop() override;
 
  private:
   void CheckClipboardForChanges();
@@ -90,11 +89,6 @@
   current_change_count_ = [[NSPasteboard generalPasteboard] changeCount];
 }
 
-void ClipboardMac::Stop() {
-  clipboard_polling_timer_.reset();
-  client_clipboard_.reset();
-}
-
 void ClipboardMac::CheckClipboardForChanges() {
   NSPasteboard* pasteboard = [NSPasteboard generalPasteboard];
   NSInteger change_count = [pasteboard changeCount];
diff --git a/remoting/host/clipboard_win.cc b/remoting/host/clipboard_win.cc
index dd2cc283..7c25ec2e 100644
--- a/remoting/host/clipboard_win.cc
+++ b/remoting/host/clipboard_win.cc
@@ -107,12 +107,12 @@
 class ClipboardWin : public Clipboard {
  public:
   ClipboardWin();
+  ~ClipboardWin() override;
 
   virtual void Start(
       scoped_ptr<protocol::ClipboardStub> client_clipboard) override;
   virtual void InjectClipboardEvent(
       const protocol::ClipboardEvent& event) override;
-  virtual void Stop() override;
 
  private:
   void OnClipboardUpdate();
@@ -138,6 +138,11 @@
       remove_clipboard_format_listener_(nullptr) {
 }
 
+ClipboardWin::~ClipboardWin() {
+  if (window_ && remove_clipboard_format_listener_)
+    (*remove_clipboard_format_listener_)(window_->hwnd());
+}
+
 void ClipboardWin::Start(
     scoped_ptr<protocol::ClipboardStub> client_clipboard) {
   DCHECK(!add_clipboard_format_listener_);
@@ -179,15 +184,6 @@
   }
 }
 
-void ClipboardWin::Stop() {
-  client_clipboard_.reset();
-
-  if (window_ && remove_clipboard_format_listener_)
-    (*remove_clipboard_format_listener_)(window_->hwnd());
-
-  window_.reset();
-}
-
 void ClipboardWin::InjectClipboardEvent(
     const protocol::ClipboardEvent& event) {
   if (!window_)
diff --git a/remoting/host/clipboard_x11.cc b/remoting/host/clipboard_x11.cc
index dc0bd98..667d260 100644
--- a/remoting/host/clipboard_x11.cc
+++ b/remoting/host/clipboard_x11.cc
@@ -25,7 +25,6 @@
   // Clipboard interface.
   void Start(scoped_ptr<protocol::ClipboardStub> client_clipboard) override;
   void InjectClipboardEvent(const protocol::ClipboardEvent& event) override;
-  void Stop() override;
 
   // MessageLoopForIO::Watcher interface.
   void OnFileCanReadWithoutBlocking(int fd) override;
@@ -56,7 +55,8 @@
 }
 
 ClipboardX11::~ClipboardX11() {
-  Stop();
+  if (display_)
+    XCloseDisplay(display_);
 }
 
 void ClipboardX11::Start(
@@ -87,16 +87,6 @@
   x_server_clipboard_.SetClipboard(event.mime_type(), event.data());
 }
 
-void ClipboardX11::Stop() {
-  client_clipboard_.reset();
-  x_connection_watcher_.StopWatchingFileDescriptor();
-
-  if (display_) {
-    XCloseDisplay(display_);
-    display_ = nullptr;
-  }
-}
-
 void ClipboardX11::OnFileCanReadWithoutBlocking(int fd) {
   PumpXEvents();
 }
diff --git a/remoting/host/input_injector_chromeos.cc b/remoting/host/input_injector_chromeos.cc
index 26eeae1..4cc7ae0b 100644
--- a/remoting/host/input_injector_chromeos.cc
+++ b/remoting/host/input_injector_chromeos.cc
@@ -46,8 +46,7 @@
 // This class is run exclusively on the UI thread of the browser process.
 class InputInjectorChromeos::Core {
  public:
-  Core(scoped_ptr<ui::SystemInputInjector> delegate_,
-       ui::InputController* input_controller);
+  Core();
 
   // Mirrors the public InputInjectorChromeos interface.
   void InjectClipboardEvent(const ClipboardEvent& event);
@@ -74,16 +73,7 @@
   DISALLOW_COPY_AND_ASSIGN(Core);
 };
 
-InputInjectorChromeos::Core::Core(scoped_ptr<ui::SystemInputInjector> delegate,
-                                  ui::InputController* input_controller)
-    : delegate_(delegate.Pass()),
-      input_controller_(input_controller),
-      // Implemented by remoting::ClipboardAura.
-      clipboard_(Clipboard::Create()),
-      saved_auto_repeat_enabled_(false) {
-  DCHECK(delegate_);
-  DCHECK(input_controller_);
-  DCHECK(clipboard_);
+InputInjectorChromeos::Core::Core() : saved_auto_repeat_enabled_(false) {
 }
 
 void InputInjectorChromeos::Core::InjectClipboardEvent(
@@ -159,6 +149,14 @@
 
 void InputInjectorChromeos::Core::Start(
     scoped_ptr<protocol::ClipboardStub> client_clipboard) {
+  ui::OzonePlatform* ozone_platform = ui::OzonePlatform::GetInstance();
+  delegate_ = ozone_platform->CreateSystemInputInjector();
+  DCHECK(delegate_);
+  input_controller_ = ozone_platform->GetInputController();
+  DCHECK(input_controller_);
+
+  // Implemented by remoting::ClipboardAura.
+  clipboard_ = Clipboard::Create();
   clipboard_->Start(client_clipboard.Pass());
   point_transformer_.reset(new PointTransformer());
 }
@@ -166,9 +164,7 @@
 InputInjectorChromeos::InputInjectorChromeos(
     scoped_refptr<base::SingleThreadTaskRunner> task_runner)
     : input_task_runner_(task_runner) {
-  ui::OzonePlatform* ozone_platform = ui::OzonePlatform::GetInstance();
-  core_.reset(new Core(ozone_platform->CreateSystemInputInjector(),
-                       ozone_platform->GetInputController()));
+  core_.reset(new Core());
 }
 
 InputInjectorChromeos::~InputInjectorChromeos() {
diff --git a/remoting/host/input_injector_mac.cc b/remoting/host/input_injector_mac.cc
index d3b96d8c5..64193678 100644
--- a/remoting/host/input_injector_mac.cc
+++ b/remoting/host/input_injector_mac.cc
@@ -327,7 +327,7 @@
     return;
   }
 
-  clipboard_->Stop();
+  clipboard_.reset();
 }
 
 InputInjectorMac::Core::~Core() {}
diff --git a/remoting/host/input_injector_win.cc b/remoting/host/input_injector_win.cc
index 30c1cd76..65cdfbe 100644
--- a/remoting/host/input_injector_win.cc
+++ b/remoting/host/input_injector_win.cc
@@ -209,7 +209,7 @@
     return;
   }
 
-  clipboard_->Stop();
+  clipboard_.reset();
 }
 
 InputInjectorWin::Core::~Core() {}
diff --git a/remoting/host/input_injector_x11.cc b/remoting/host/input_injector_x11.cc
index 53e1604..802e920 100644
--- a/remoting/host/input_injector_x11.cc
+++ b/remoting/host/input_injector_x11.cc
@@ -618,7 +618,7 @@
     return;
   }
 
-  clipboard_->Stop();
+  clipboard_.reset();
 }
 
 }  // namespace
diff --git a/remoting/host/setup/daemon_controller.h b/remoting/host/setup/daemon_controller.h
index 02dd02ad..9bc3b332 100644
--- a/remoting/host/setup/daemon_controller.h
+++ b/remoting/host/setup/daemon_controller.h
@@ -24,20 +24,15 @@
 
 class DaemonController : public base::RefCountedThreadSafe<DaemonController> {
  public:
-  // Note that these enumeration values are duplicated in host_controller.js and
-  // must be kept in sync.
+  // These enumeration values are duplicated in host_controller.js except that
+  // NOT_INSTALLED is missing here. DaemonController runs in either the remoting
+  // host or the native messaging host which are only installed as part of the
+  // host package so the host must have already been installed.
   enum State {
     // Placeholder state for platforms on which the daemon process is not
     // implemented. The web-app will not show the corresponding UI. This value
     // will eventually be deprecated or removed.
-    STATE_NOT_IMPLEMENTED = -1,
-    // The daemon is not installed. This is functionally equivalent to
-    // STATE_STOPPED, but the start method is expected to be significantly
-    // slower, and might involve user interaction. It might be appropriate to
-    // indicate this in the UI.
-    STATE_NOT_INSTALLED = 0,
-    // The daemon is being installed.
-    STATE_INSTALLING = 1,
+    STATE_NOT_IMPLEMENTED = 0,
     // The daemon is installed but not running. Call Start to start it.
     STATE_STOPPED = 2,
     // The daemon process is starting.
@@ -47,9 +42,7 @@
     STATE_STARTED = 4,
     // The daemon process is stopping.
     STATE_STOPPING = 5,
-    // The state cannot be determined. This could indicate that the plugin
-    // has not been provided with sufficient information, for example, the
-    // user for which to query state on a multi-user system.
+    // The state cannot be determined.
     STATE_UNKNOWN = 6
   };
 
diff --git a/remoting/host/setup/daemon_controller_delegate_linux.cc b/remoting/host/setup/daemon_controller_delegate_linux.cc
index 60a91ee..b015eda 100644
--- a/remoting/host/setup/daemon_controller_delegate_linux.cc
+++ b/remoting/host/setup/daemon_controller_delegate_linux.cc
@@ -138,23 +138,16 @@
 DaemonController::State DaemonControllerDelegateLinux::GetState() {
   base::FilePath script_path;
   if (!GetScriptPath(&script_path)) {
-    return DaemonController::STATE_NOT_IMPLEMENTED;
+    LOG(ERROR) << "GetScriptPath() failed.";
+    return DaemonController::STATE_UNKNOWN;
   }
   base::CommandLine command_line(script_path);
   command_line.AppendArg("--get-status");
 
   std::string status;
   int exit_code = 0;
-  bool result =
-      base::GetAppOutputWithExitCode(command_line, &status, &exit_code);
-  if (!result) {
-    // TODO(jamiewalch): When we have a good story for installing, return
-    // NOT_INSTALLED rather than NOT_IMPLEMENTED (the former suppresses
-    // the relevant UI in the web-app).
-    return DaemonController::STATE_NOT_IMPLEMENTED;
-  }
-
-  if (exit_code != 0) {
+  if (!base::GetAppOutputWithExitCode(command_line, &status, &exit_code) ||
+      exit_code != 0) {
     LOG(ERROR) << "Failed to run \"" << command_line.GetCommandLineString()
                << "\". Exit code: " << exit_code;
     return DaemonController::STATE_UNKNOWN;
@@ -167,6 +160,8 @@
   } else if (status == "STOPPED") {
     return DaemonController::STATE_STOPPED;
   } else if (status == "NOT_IMPLEMENTED") {
+    // Chrome Remote Desktop is not currently supported on the underlying Linux
+    // Distro.
     return DaemonController::STATE_NOT_IMPLEMENTED;
   } else {
     LOG(ERROR) << "Unknown status string returned from  \""
@@ -177,9 +172,6 @@
 }
 
 scoped_ptr<base::DictionaryValue> DaemonControllerDelegateLinux::GetConfig() {
-  if (GetState() == DaemonController::STATE_NOT_IMPLEMENTED)
-    return nullptr;
-
   scoped_ptr<base::DictionaryValue> config(
       HostConfigFromJsonFile(GetConfigPath()));
   if (!config)
diff --git a/remoting/host/setup/daemon_controller_delegate_mac.mm b/remoting/host/setup/daemon_controller_delegate_mac.mm
index 1c6e9a1e..2fd7daaa 100644
--- a/remoting/host/setup/daemon_controller_delegate_mac.mm
+++ b/remoting/host/setup/daemon_controller_delegate_mac.mm
@@ -39,7 +39,7 @@
 DaemonController::State DaemonControllerDelegateMac::GetState() {
   pid_t job_pid = base::mac::PIDForJob(kServiceName);
   if (job_pid < 0) {
-    return DaemonController::STATE_NOT_INSTALLED;
+    return DaemonController::STATE_UNKNOWN;
   } else if (job_pid == 0) {
     // Service is stopped, or a start attempt failed.
     return DaemonController::STATE_STOPPED;
diff --git a/remoting/host/setup/daemon_controller_delegate_win.cc b/remoting/host/setup/daemon_controller_delegate_win.cc
index fb8f4323..0dcae692 100644
--- a/remoting/host/setup/daemon_controller_delegate_win.cc
+++ b/remoting/host/setup/daemon_controller_delegate_win.cc
@@ -361,10 +361,6 @@
 }
 
 DaemonController::State DaemonControllerDelegateWin::GetState() {
-  if (base::win::GetVersion() < base::win::VERSION_XP) {
-    return DaemonController::STATE_NOT_IMPLEMENTED;
-  }
-
   // TODO(alexeypa): Make the thread alertable, so we can switch to APC
   // notifications rather than polling.
   ScopedScHandle service = OpenService(SERVICE_QUERY_STATUS);
diff --git a/remoting/host/setup/me2me_native_messaging_host.cc b/remoting/host/setup/me2me_native_messaging_host.cc
index 7472c243..12e688b1 100644
--- a/remoting/host/setup/me2me_native_messaging_host.cc
+++ b/remoting/host/setup/me2me_native_messaging_host.cc
@@ -391,12 +391,6 @@
     case DaemonController::STATE_NOT_IMPLEMENTED:
       response->SetString("state", "NOT_IMPLEMENTED");
       break;
-    case DaemonController::STATE_NOT_INSTALLED:
-      response->SetString("state", "NOT_INSTALLED");
-      break;
-    case DaemonController::STATE_INSTALLING:
-      response->SetString("state", "INSTALLING");
-      break;
     case DaemonController::STATE_STOPPED:
       response->SetString("state", "STOPPED");
       break;
diff --git a/remoting/host/video_frame_pump.cc b/remoting/host/video_frame_pump.cc
index 1f11264b..6451313 100644
--- a/remoting/host/video_frame_pump.cc
+++ b/remoting/host/video_frame_pump.cc
@@ -60,7 +60,12 @@
       capturer_(capturer.Pass()),
       encoder_(encoder.Pass()),
       video_stub_(video_stub),
-      keep_alive_timer_(true, true),
+      keep_alive_timer_(
+          FROM_HERE,
+          base::TimeDelta::FromMilliseconds(kKeepAlivePacketIntervalMs),
+          base::Bind(&VideoFramePump::SendKeepAlivePacket,
+                     base::Unretained(this)),
+          false),
       capture_scheduler_(base::Bind(&VideoFramePump::CaptureNextFrame,
                                     base::Unretained(this))),
       latest_event_timestamp_(0),
@@ -70,10 +75,6 @@
 
   capturer_->Start(this);
   capture_scheduler_.Start();
-
-  keep_alive_timer_.Start(
-      FROM_HERE, base::TimeDelta::FromMilliseconds(kKeepAlivePacketIntervalMs),
-      base::Bind(&VideoFramePump::SendKeepAlivePacket, base::Unretained(this)));
 }
 
 VideoFramePump::~VideoFramePump() {
diff --git a/remoting/protocol/BUILD.gn b/remoting/protocol/BUILD.gn
index 3ffcb08..34b38af02 100644
--- a/remoting/protocol/BUILD.gn
+++ b/remoting/protocol/BUILD.gn
@@ -11,7 +11,10 @@
   sources =
       rebase_path(gypi_values.remoting_protocol_sources, ".", "//remoting")
 
-  configs += [ "//build/config/compiler:wexit_time_destructors" ]
+  configs += [
+    "//build/config/compiler:no_size_t_to_int_warning",
+    "//build/config/compiler:wexit_time_destructors",
+  ]
 
   public_deps = [
     "//third_party/libjingle",
diff --git a/remoting/remoting_host.gypi b/remoting/remoting_host.gypi
index 6a5e95c..e818a971 100644
--- a/remoting/remoting_host.gypi
+++ b/remoting/remoting_host.gypi
@@ -84,7 +84,6 @@
             ['chromeos==1', {
               'dependencies' : [
                 '../cc/cc.gyp:cc',
-                '../content/content.gyp:content',
                 '../ppapi/ppapi_internal.gyp:ppapi_host',
                 '../skia/skia.gyp:skia',
                 '../ui/aura/aura.gyp:aura',
diff --git a/remoting/remoting_test.gypi b/remoting/remoting_test.gypi
index a6510d9..ad1d371 100644
--- a/remoting/remoting_test.gypi
+++ b/remoting/remoting_test.gypi
@@ -290,8 +290,7 @@
         {
           'destination': '<(PRODUCT_DIR)/remoting/browser_test_resources',
             'files': [
-              '<@(remoting_webapp_js_test_common_files)',
-              '<@(remoting_webapp_js_browser_test_files)',
+              '<@(remoting_webapp_browsertest_all_js_files)',
             ],
         },
       ], # end of copies
@@ -334,9 +333,7 @@
           'destination': '<(output_dir)',
           'files': [
             '<@(webapp_js_files)',
-            '<@(remoting_webapp_js_test_common_files)',
-            '<@(remoting_webapp_unittest_additional_files)',
-            '<@(remoting_webapp_unittest_js_files)',
+            '<@(remoting_webapp_unittest_all_files)',
           ],
         },
       ],
@@ -347,8 +344,7 @@
             'webapp/build-html.py',
             '<(remoting_webapp_unittest_template_main)',
             '<@(webapp_js_files)',
-            '<@(remoting_webapp_js_test_common_files)',
-            '<@(remoting_webapp_unittest_js_files)'
+            '<@(remoting_webapp_unittest_all_js_files)'
           ],
           'outputs': [
             '<(output_dir)/unittest.html',
@@ -361,9 +357,8 @@
             # arguments.  Therefore, the excludejs flag must be set before the
             # instrumentedjs flag or else GYP will ignore the files in the
             # exclude list.
-            '--exclude-js', '<@(remoting_webapp_unittest_exclude_files)',
-            '--js', '<@(remoting_webapp_unittest_js_files)',
-            '<@(remoting_webapp_js_test_common_files)',
+            '--exclude-js', '<@(remoting_webapp_unittest_exclude_js_files)',
+            '--js', '<@(remoting_webapp_unittest_all_js_files)',
             '--instrument-js', '<@(webapp_js_files)',
            ],
         },
diff --git a/remoting/remoting_webapp.gypi b/remoting/remoting_webapp.gypi
index 2c767fda..7d72c67 100644
--- a/remoting/remoting_webapp.gypi
+++ b/remoting/remoting_webapp.gypi
@@ -23,6 +23,7 @@
     ['run_jscompile != 0', {
       'variables': {
         'success_stamp': '<(PRODUCT_DIR)/<(_target_name)_jscompile.stamp',
+        'success_stamp_bt': '<(PRODUCT_DIR)/<(_target_name)_bt_jscompile.stamp',
       },
       'actions': [
         {
@@ -43,6 +44,26 @@
             '<@(remoting_webapp_js_proto_files)',
           ],
         },
+        {
+          'action_name': 'Verify remoting webapp with browsertests',
+          'inputs': [
+            '<@(remoting_webapp_crd_js_files)',
+            '<@(remoting_webapp_browsertest_all_js_files)',
+            '<@(remoting_webapp_js_proto_files)',
+          ],
+          'outputs': [
+            '<(success_stamp_bt)',
+          ],
+          'action': [
+            'python', '../third_party/closure_compiler/checker.py',
+            '--strict',
+            '--no-single-file',
+            '--success-stamp', '<(success_stamp_bt)',
+            '<@(remoting_webapp_crd_js_files)',
+            '<@(remoting_webapp_browsertest_all_js_files)',
+            '<@(remoting_webapp_js_proto_files)',
+          ],
+        },
       ],  # actions
     }],
   ],
diff --git a/remoting/remoting_webapp_files.gypi b/remoting/remoting_webapp_files.gypi
index 0df75a2..e485ccd1 100644
--- a/remoting/remoting_webapp_files.gypi
+++ b/remoting/remoting_webapp_files.gypi
@@ -16,8 +16,80 @@
       'webapp/js_proto/chrome_proto.js',
       'webapp/js_proto/dom_proto.js',
       'webapp/js_proto/remoting_proto.js',
+      'webapp/js_proto/test_proto.js',
     ],
 
+    #
+    # Webapp browsertest and unittest JavaScript files.
+    #
+
+    # Shared files for tests.
+    'remoting_webapp_test_js_common_files': [
+      'webapp/unittests/mock_signal_strategy.js',
+    ],
+    # Browser test files.
+    'remoting_webapp_browsertest_js_files': [
+      'webapp/browser_test/browser_test.js',
+      'webapp/browser_test/bump_scroll_browser_test.js',
+      'webapp/browser_test/cancel_pin_browser_test.js',
+      'webapp/browser_test/invalid_pin_browser_test.js',
+      'webapp/browser_test/it2me_browser_test.js',
+      'webapp/browser_test/mock_client_plugin.js',
+      'webapp/browser_test/mock_host_list_api.js',
+      'webapp/browser_test/mock_identity.js',
+      'webapp/browser_test/mock_oauth2_api.js',
+      'webapp/browser_test/mock_session_connector.js',
+      'webapp/browser_test/scrollbar_browser_test.js',
+      'webapp/browser_test/timeout_waiter.js',
+      'webapp/browser_test/unauthenticated_browser_test.js',
+      'webapp/browser_test/update_pin_browser_test.js',
+    ],
+    'remoting_webapp_browsertest_all_js_files': [
+      '<@(remoting_webapp_browsertest_js_files)',
+      '<@(remoting_webapp_test_js_common_files)',
+    ],
+
+    # These product files are excluded from our JavaScript unittest
+    'remoting_webapp_unittest_exclude_js_files': [
+      # background.js is where the onLoad handler is defined, which
+      # makes it the entry point of the background page.
+      'webapp/crd/js/background.js',
+    ],
+    # The unit test cases for the webapp
+    'remoting_webapp_unittest_js_files': [
+      'webapp/unittests/chrome_mocks.js',
+      'webapp/js_proto/chrome_proto.js',
+      'webapp/unittests/apps_v2_migration_unittest.js',
+      'webapp/unittests/base_unittest.js',
+      'webapp/unittests/desktop_connected_view_unittest.js',
+      'webapp/unittests/dns_blackhole_checker_unittest.js',
+      'webapp/unittests/event_hook_unittest.js',
+      'webapp/unittests/fallback_signal_strategy_unittest.js',
+      'webapp/unittests/ipc_unittest.js',
+      'webapp/unittests/it2me_helpee_channel_unittest.js',
+      'webapp/unittests/it2me_helper_channel_unittest.js',
+      'webapp/unittests/it2me_service_unittest.js',
+      'webapp/unittests/l10n_unittest.js',
+      'webapp/unittests/menu_button_unittest.js',
+      'webapp/unittests/xmpp_connection_unittest.js',
+      'webapp/unittests/xmpp_login_handler_unittest.js',
+      'webapp/unittests/xmpp_stream_parser_unittest.js',
+    ],
+    'remoting_webapp_unittest_all_js_files': [
+      '<@(remoting_webapp_unittest_js_files)',
+      '<@(remoting_webapp_test_js_common_files)',
+    ],
+    'remoting_webapp_unittest_all_files': [
+      'webapp/crd/html/menu_button.css',
+      '<@(remoting_webapp_unittest_all_js_files)',
+    ],
+    'remoting_webapp_unittest_template_main':
+      'webapp/crd/html/template_unittest.html',
+
+    #
+    # Webapp JavaScript file groups.
+    #
+
     # Auth (apps v1) JavaScript files.
     # These files aren't included directly from main.html. They are
     # referenced from the manifest.json file (appsv1 only).
@@ -39,10 +111,15 @@
       'webapp/crd/js/oauth2_api.js',
       'webapp/crd/js/oauth2_api_impl.js',
     ],
+    # Cast extension handler JavaScript files.
+    'remoting_webapp_js_cast_extension_files': [
+      'webapp/crd/js/cast_extension_handler.js',
+    ],
     # Client JavaScript files.
     'remoting_webapp_js_client_files': [
       'webapp/crd/js/client_plugin.js',
       'webapp/crd/js/client_plugin_impl.js',
+      'webapp/crd/js/client_plugin_host_desktop_impl.js',
       # TODO(garykac) For client_screen:
       # * Split out pin/access code stuff into separate file.
       # * Move client logic into session_connector
@@ -51,6 +128,7 @@
       'webapp/crd/js/clipboard.js',
       'webapp/crd/js/desktop_connected_view.js',
       'webapp/crd/js/hangout_session.js',
+      'webapp/crd/js/host_desktop.js',
       'webapp/crd/js/session_connector.js',
       'webapp/crd/js/session_connector_impl.js',
       'webapp/crd/js/smart_reconnector.js',
@@ -67,11 +145,14 @@
       'webapp/crd/js/error.js',
       'webapp/crd/js/event_handlers.js',
       'webapp/crd/js/plugin_settings.js',
-      # TODO(garykac) Split out UI client stuff from remoting.js.
       'webapp/crd/js/remoting.js',
       'webapp/crd/js/typecheck.js',
       'webapp/crd/js/xhr.js',
     ],
+    # Gnubby authentication JavaScript files.
+    'remoting_webapp_js_gnubby_auth_files': [
+      'webapp/crd/js/gnubby_auth_handler.js',
+    ],
     # Host JavaScript files.
     'remoting_webapp_js_host_files': [
       'webapp/crd/js/host.js',
@@ -104,6 +185,17 @@
       'webapp/crd/js/server_log_entry.js',
       'webapp/crd/js/stats_accumulator.js',
     ],
+    # Remoting signaling files.
+    'remoting_webapp_js_signaling_files': [
+      'webapp/crd/js/dns_blackhole_checker.js',
+      'webapp/crd/js/fallback_signal_strategy.js',
+      'webapp/crd/js/signal_strategy.js',
+      'webapp/crd/js/wcs_adapter.js',
+      'webapp/crd/js/wcs_sandbox_container.js',
+      'webapp/crd/js/xmpp_connection.js',
+      'webapp/crd/js/xmpp_login_handler.js',
+      'webapp/crd/js/xmpp_stream_parser.js',
+    ],
     # UI JavaScript files.
     'remoting_webapp_js_ui_files': [
       'webapp/base/js/window_shape.js',
@@ -120,17 +212,6 @@
       'webapp/crd/js/toolbar.js',
       'webapp/crd/js/window_frame.js',
     ],
-    # Remoting signaling files.
-    'remoting_webapp_js_signaling_files': [
-      'webapp/crd/js/dns_blackhole_checker.js',
-      'webapp/crd/js/fallback_signal_strategy.js',
-      'webapp/crd/js/signal_strategy.js',
-      'webapp/crd/js/wcs_adapter.js',
-      'webapp/crd/js/wcs_sandbox_container.js',
-      'webapp/crd/js/xmpp_connection.js',
-      'webapp/crd/js/xmpp_login_handler.js',
-      'webapp/crd/js/xmpp_stream_parser.js',
-    ],
     # Remoting WCS sandbox JavaScript files.
     'remoting_webapp_js_wcs_sandbox_files': [
       'webapp/crd/js/wcs.js',
@@ -138,66 +219,6 @@
       'webapp/crd/js/wcs_sandbox_content.js',
       'webapp/crd/js/xhr_proxy.js',
     ],
-    # gnubby authentication JavaScript files.
-    'remoting_webapp_js_gnubby_auth_files': [
-      'webapp/crd/js/gnubby_auth_handler.js',
-    ],
-    # cast extension handler JavaScript files.
-    'remoting_webapp_js_cast_extension_files': [
-      'webapp/crd/js/cast_extension_handler.js',
-    ],
-    # Shared files for tests.
-    'remoting_webapp_js_test_common_files': [
-      'webapp/unittests/mock_signal_strategy.js',
-    ],
-    # browser test JavaScript files.
-    'remoting_webapp_js_browser_test_files': [
-      'webapp/browser_test/browser_test.js',
-      'webapp/browser_test/bump_scroll_browser_test.js',
-      'webapp/browser_test/cancel_pin_browser_test.js',
-      'webapp/browser_test/invalid_pin_browser_test.js',
-      'webapp/browser_test/it2me_browser_test.js',
-      'webapp/browser_test/mock_client_plugin.js',
-      'webapp/browser_test/mock_host_list_api.js',
-      'webapp/browser_test/mock_identity.js',
-      'webapp/browser_test/mock_oauth2_api.js',
-      'webapp/browser_test/mock_session_connector.js',
-      'webapp/browser_test/scrollbar_browser_test.js',
-      'webapp/browser_test/timeout_waiter.js',
-      'webapp/browser_test/unauthenticated_browser_test.js',
-      'webapp/browser_test/update_pin_browser_test.js',
-    ],
-    # These product files are excluded from our JavaScript unittest
-    'remoting_webapp_unittest_exclude_files': [
-      # background.js is where the onLoad handler is defined, which
-      # makes it the entry point of the background page.
-      'webapp/crd/js/background.js',
-    ],
-    # The unit test cases for the webapp
-    'remoting_webapp_unittest_js_files': [
-      'webapp/unittests/chrome_mocks.js',
-      'webapp/js_proto/chrome_proto.js',
-      'webapp/unittests/apps_v2_migration_unittest.js',
-      'webapp/unittests/base_unittest.js',
-      'webapp/unittests/desktop_connected_view_unittest.js',
-      'webapp/unittests/dns_blackhole_checker_unittest.js',
-      'webapp/unittests/event_hook_unittest.js',
-      'webapp/unittests/fallback_signal_strategy_unittest.js',
-      'webapp/unittests/ipc_unittest.js',
-      'webapp/unittests/it2me_helpee_channel_unittest.js',
-      'webapp/unittests/it2me_helper_channel_unittest.js',
-      'webapp/unittests/it2me_service_unittest.js',
-      'webapp/unittests/l10n_unittest.js',
-      'webapp/unittests/menu_button_unittest.js',
-      'webapp/unittests/xmpp_connection_unittest.js',
-      'webapp/unittests/xmpp_login_handler_unittest.js',
-      'webapp/unittests/xmpp_stream_parser_unittest.js',
-    ],
-    'remoting_webapp_unittest_additional_files': [
-      'webapp/crd/html/menu_button.css',
-    ],
-    'remoting_webapp_unittest_template_main':
-      'webapp/crd/html/template_unittest.html',
 
     # The shared JavaScript files required by main.html.
     'remoting_webapp_shared_main_html_js_files': [
@@ -215,10 +236,9 @@
       '<@(remoting_webapp_js_logging_files)',
       '<@(remoting_webapp_js_ui_files)',
       '<@(remoting_webapp_js_signaling_files)',
-      # Uncomment these lines to include browser test files in the web app
+      # Uncomment this line to include browser test files in the web app
       # to expedite debugging or local development.
-      # '<@(remoting_webapp_js_browser_test_files)',
-      # '<@(remoting_webapp_js_test_common_files)'
+      #'<@(remoting_webapp_browsertest_all_js_files)',
     ],
 
     # The CRD-specific JavaScript files required by main.html.
diff --git a/remoting/resources/BUILD.gn b/remoting/resources/BUILD.gn
index 0328231..848f9c2 100644
--- a/remoting/resources/BUILD.gn
+++ b/remoting/resources/BUILD.gn
@@ -203,7 +203,7 @@
 
   args = [
            "-p",
-           os,
+           current_os,
            "-g",
            rebase_path(root_gen_dir, root_build_dir),
            "-x",
diff --git a/remoting/resources/remoting_strings.grd b/remoting/resources/remoting_strings.grd
index c27df66..eb714220 100644
--- a/remoting/resources/remoting_strings.grd
+++ b/remoting/resources/remoting_strings.grd
@@ -614,7 +614,8 @@
 For information about privacy, please see the Google Privacy Policy (http://goo.gl/SyrVzj) and the Chrome Privacy Policy (http://goo.gl/0uXE5d).
         </message>
         <message name="IDS_PLAY_STORE_CHANGES" desc="List of what's changed in this release of Chrome Remote Desktop for Android. [CHAR-LIMIT=500] [NAME=play_store_changes]">
-• Fixes for Android Lollipop.
+• User interface improvements.
+• Reduced network usage when running in the background.
         </message>
       </if>  <!-- is_android or is_ios -->
 
diff --git a/remoting/scripts/webapp/parse_webapp_details.py b/remoting/scripts/webapp/parse_webapp_details.py
new file mode 100755
index 0000000..c29590a
--- /dev/null
+++ b/remoting/scripts/webapp/parse_webapp_details.py
@@ -0,0 +1,91 @@
+#!/usr/bin/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.
+
+import argparse
+import json
+import sys
+
+DESCRIPTION = '''This tools reads in a GYP file, parses it as JSON, grabs the
+    requested object(s) using the passed in |key_name| and |key_value| and then
+    builds up a space delimited string using the |return_key| given.  The built
+    up string is then output to STDOUT which can be read and tokenized in
+    bash or python scripts.'''
+FILE_HELP = '''The GYP file to parse'''
+APP_KEY_NAME_HELP = '''Key name used to identify relevant webapp details'''
+APP_KEY_VALUE_HELP = '''Key value used to identify relevant webapp details,
+    multiple values can be passed in'''
+TARGET_KEY_VALUE_HELP = '''The key name used to output the targeted value'''
+DEBUG_HELP = '''Turns on verbose debugging output'''
+
+# Cleans up the text in the passed in GYP file, updates it to make it valid JSON
+# and returns the valid json string.
+def gyp_file_to_json_string(gyp_file):
+  # First, read in each line and discard comments and whitespace.
+  line_data = ''
+  for line in gyp_file:
+    lines = line.split("#")
+    line_data += lines[0].strip()
+
+  # Trailing commas are valid in GYP files but invalid in JSON, so remove them
+  # here.  Also convert double quotes to single quotes and those throw off the
+  # python json parser.
+  line_data = line_data.replace(',}', '}')
+  line_data = line_data.replace(',]', ']')
+  line_data = line_data.replace('\'', '\"')
+
+  return line_data
+
+
+# Finds the matching app detail sections in |data| and generates a space
+# delimited string with the |return_key|'s value.  If we found at least one
+# matching app and created a string, we output it to STDOUT.
+def print_details(data, key_name, key_values, target_key):
+  output_string = ''
+  for target in data['targets']:
+    if target[key_name] in key_values:
+      if output_string:
+        output_string += " " + target[target_key]
+      else:
+        output_string += target[target_key]
+
+  if output_string:
+    print output_string
+
+
+def main():
+  parser = argparse.ArgumentParser(description = DESCRIPTION)
+  parser.add_argument('file', nargs = '+', help = FILE_HELP)
+  parser.add_argument('-k', '--key_name', help = APP_KEY_NAME_HELP,
+      nargs = '?', required=True)
+  parser.add_argument('-v', '--key_value', help = APP_KEY_VALUE_HELP,
+      nargs = '+', required=True)
+  parser.add_argument('-t', '--target_key', help = TARGET_KEY_VALUE_HELP,
+      nargs = '?', required=True)
+  parser.add_argument('-d', '--debug', help = DEBUG_HELP,
+      action='store_true')
+  options = parser.parse_args()
+
+  if options.debug:
+    print 'Reading from file \"' + options.file[0] + '\"'
+
+  gyp_file = open(options.file[0])
+  json_string = gyp_file_to_json_string(gyp_file)
+
+  json_object = json.loads(json_string)
+
+  if options.debug:
+    print 'The following app details were found:'
+    for target in json_object['targets']:
+      print target['target_name'], target['app_id'], target['app_name']
+
+  print_details(json_object,
+                options.key_name,
+                options.key_value,
+                options.target_key)
+
+  return 0
+
+if __name__ == '__main__':
+  sys.exit(main())
diff --git a/remoting/webapp/base/html/client_plugin.html b/remoting/webapp/base/html/client_plugin.html
index 9aa0229..8758c31 100644
--- a/remoting/webapp/base/html/client_plugin.html
+++ b/remoting/webapp/base/html/client_plugin.html
@@ -8,6 +8,7 @@
     <div class="horizontally-centered">
       <div id="client-container">
         <div class="client-plugin-container"></div>
+        <div class="debug-region-container"></div>
         <img class="mouse-cursor-overlay" hidden>
       </div>
     </div>
diff --git a/remoting/webapp/base/html/main.css b/remoting/webapp/base/html/main.css
index 5f57f4a8..cd463e7e 100644
--- a/remoting/webapp/base/html/main.css
+++ b/remoting/webapp/base/html/main.css
@@ -707,6 +707,22 @@
   height: auto;
 }
 
+.debug-region-container {
+  position: absolute;
+  top: 0px;
+  left: 0px;
+  pointer-events: none;
+}
+
+.debug-region-rect {
+  position: absolute;
+  background-color: rgba(255, 0, 255, 0.5);
+  border-color: #000000;
+  border-style: solid;
+  border-width: 2px;
+  box-sizing: border-box;
+}
+
 .mouse-cursor-overlay {
   position: absolute;
   pointer-events: none;
diff --git a/remoting/webapp/base/js/base.js b/remoting/webapp/base/js/base.js
index 9f2d83d0..a357fd93 100644
--- a/remoting/webapp/base/js/base.js
+++ b/remoting/webapp/base/js/base.js
@@ -375,14 +375,14 @@
  /**
   * Add a listener |fn| to listen to |type| event.
   * @param {string} type
-  * @param {function(?=):void} fn
+  * @param {Function} fn
   */
 base.EventSource.prototype.addEventListener = function(type, fn) {};
 
  /**
   * Remove a listener |fn| to listen to |type| event.
   * @param {string} type
-  * @param {function(?=):void} fn
+  * @param {Function} fn
   */
 base.EventSource.prototype.removeEventListener = function(type, fn) {};
 
@@ -431,7 +431,7 @@
 
   /**
     * @param {string} type
-    * @param {function(?=):void} fn
+    * @param {Function} fn
     */
   addEventListener: function(type, fn) {
     base.debug.assert(typeof fn == 'function');
@@ -443,7 +443,7 @@
 
   /**
     * @param {string} type
-    * @param {function(?=):void} fn
+    * @param {Function} fn
     */
   removeEventListener: function(type, fn) {
     base.debug.assert(typeof fn == 'function');
diff --git a/remoting/webapp/browser_test/browser_test.js b/remoting/webapp/browser_test/browser_test.js
index f049de3..f41591f 100644
--- a/remoting/webapp/browser_test/browser_test.js
+++ b/remoting/webapp/browser_test/browser_test.js
@@ -4,10 +4,6 @@
 
 /**
  * @fileoverview
- * @suppress {checkTypes}  By default, JSCompile is not run on test files.
- *    However, you can modify |remoting_webapp_files.gypi| locally to include
- *    the test in the package to expedite local development.  This suppress
- *    is here so that JSCompile won't complain.
  *
  * Provides basic functionality for JavaScript based browser test.
  *
@@ -44,30 +40,44 @@
 
 'use strict';
 
+/** @suppress {duplicate} */
 var browserTest = browserTest || {};
 
+/** @type {window.DomAutomationController} */
+browserTest.automationController_ = null;
+
+/**
+ * @return {void}
+ * @suppress {checkTypes|reportUnknownTypes}
+ */
 browserTest.init = function() {
   // The domAutomationController is used to communicate progress back to the
   // C++ calling code.  It will only exist if chrome is run with the flag
   // --dom-automation.  It is stubbed out here so that browser test can be run
   // under the regular app.
-  browserTest.automationController_ = window.domAutomationController || {
-    send: function(json) {
-      var result = JSON.parse(json);
-      if (result.succeeded) {
-        console.log('Test Passed.');
-      } else {
-        console.error('Test Failed.\n' +
-            result.error_message + '\n' + result.stack_trace);
+  if (window.domAutomationController) {
+    /** @type {window.DomAutomationController} */
+    browserTest.automationController_ = window.domAutomationController;
+  } else {
+    browserTest.automationController_ = {
+      send: function(json) {
+        var result = JSON.parse(json);
+        if (result.succeeded) {
+          console.log('Test Passed.');
+        } else {
+          console.error('Test Failed.\n' +
+              result.error_message + '\n' + result.stack_trace);
+        }
       }
-    }
+    };
   };
 };
 
 /**
  * Fails the C++ calling browser test with |message| if |expr| is false.
- * @param {boolean} expr
+ * @param {*} expr
  * @param {string} message
+ * @return {void}
  */
 browserTest.expect = function(expr, message) {
   if (!expr) {
@@ -76,6 +86,10 @@
   }
 };
 
+/**
+ * @param {string|Error} error
+ * @return {void}
+ */
 browserTest.fail = function(error) {
   var error_message = error;
   var stack_trace = base.debug.callstack();
@@ -85,6 +99,8 @@
     stack_trace = error.stack;
   }
 
+  console.error(error_message);
+
   // To run browserTest locally:
   // 1. Go to |remoting_webapp_files| and look for
   //    |remoting_webapp_js_browser_test_files| and uncomment it
@@ -102,6 +118,9 @@
   }));
 };
 
+/**
+ * @return {void}
+ */
 browserTest.pass = function() {
   browserTest.automationController_.send(JSON.stringify({
     succeeded: true,
@@ -110,12 +129,21 @@
   }));
 };
 
+/**
+ * @param {string} id
+ * @return {void}
+ */
 browserTest.clickOnControl = function(id) {
   var element = document.getElementById(id);
   browserTest.expect(element, 'No such element: ' + id);
   element.click();
 };
 
+/**
+ * @param {remoting.AppMode} expectedMode
+ * @param {number=} opt_timeout
+ * @return {Promise}
+ */
 browserTest.onUIMode = function(expectedMode, opt_timeout) {
   if (expectedMode == remoting.currentMode) {
     // If the current mode is the same as the expected mode, return a fulfilled
@@ -139,22 +167,27 @@
       reject('Timeout waiting for ' + expectedMode);
     }
 
+    /** @param {remoting.AppMode} mode */
     function onUIModeChanged(mode) {
       if (mode == expectedMode) {
         remoting.testEvents.removeEventListener(uiModeChanged, onUIModeChanged);
         window.clearTimeout(timerId);
         timerId = null;
-        fulfill();
+        fulfill(true);
       }
     }
 
     if (opt_timeout != browserTest.Timeout.NONE) {
-      timerId = window.setTimeout(onTimeout, opt_timeout);
+      timerId = window.setTimeout(onTimeout,
+                                  /** @type {number} */ (opt_timeout));
     }
     remoting.testEvents.addEventListener(uiModeChanged, onUIModeChanged);
   });
 };
 
+/**
+ * @return {Promise}
+ */
 browserTest.connectMe2Me = function() {
   var AppMode = remoting.AppMode;
   browserTest.clickOnControl('this-host-connect');
@@ -170,11 +203,15 @@
     });
 };
 
+/**
+ * @return {Promise}
+ */
 browserTest.disconnect = function() {
   var AppMode = remoting.AppMode;
   var finishedMode = AppMode.CLIENT_SESSION_FINISHED_ME2ME;
   var finishedButton = 'client-finished-me2me-button';
-  if (remoting.clientSession.getMode() == remoting.ClientSession.Mode.IT2ME) {
+  if (remoting.desktopConnectedView.getMode() ==
+      remoting.DesktopConnectedView.Mode.IT2ME) {
     finishedMode = AppMode.CLIENT_SESSION_FINISHED_IT2ME;
     finishedButton = 'client-finished-it2me-button';
   }
@@ -187,6 +224,11 @@
   });
 };
 
+/**
+ * @param {string} pin
+ * @param {?boolean} opt_expectError
+ * @return {Promise}
+ */
 browserTest.enterPIN = function(pin, opt_expectError) {
   // Wait for 500ms before hitting the PIN button. From experiment, sometimes
   // the PIN prompt does not dismiss without the timeout.
@@ -199,7 +241,7 @@
   }).then(function() {
     if (opt_expectError) {
       return browserTest.expectConnectionError(
-          remoting.ClientSession.Mode.ME2ME,
+          remoting.DesktopConnectedView.Mode.ME2ME,
           remoting.Error.INVALID_ACCESS_CODE);
     } else {
       return browserTest.expectConnected();
@@ -207,6 +249,11 @@
   });
 };
 
+/**
+ * @param {remoting.DesktopConnectedView.Mode} connectionMode
+ * @param {string} errorTag
+ * @return {Promise}
+ */
 browserTest.expectConnectionError = function(connectionMode, errorTag) {
   var AppMode = remoting.AppMode;
   var Timeout = browserTest.Timeout;
@@ -214,7 +261,7 @@
   var finishButton = 'client-finished-me2me-button';
   var failureMode = AppMode.CLIENT_CONNECT_FAILED_ME2ME;
 
-  if (connectionMode == remoting.ClientSession.Mode.IT2ME) {
+  if (connectionMode == remoting.DesktopConnectedView.Mode.IT2ME) {
     failureMode = AppMode.CLIENT_CONNECT_FAILED_IT2ME;
     finishButton = 'client-finished-it2me-button';
   }
@@ -228,6 +275,7 @@
   });
 
   onFailure = onFailure.then(function() {
+    /** @type {Element} */
     var errorDiv = document.getElementById('connect-error-message');
     var actual = errorDiv.innerText;
     var expected = l10n.getTranslationOrError(errorTag);
@@ -242,6 +290,9 @@
   return Promise.race([onConnected, onFailure]);
 };
 
+/**
+ * @return {Promise}
+ */
 browserTest.expectConnected = function() {
   var AppMode = remoting.AppMode;
   // Timeout if the session is not connected within 30 seconds.
@@ -258,12 +309,20 @@
   return Promise.race([onConnected, onFailure]);
 };
 
+/**
+ * @param {base.EventSource} eventSource
+ * @param {string} event
+ * @param {number} timeoutMs
+ * @param {?string} opt_expectedData
+ * @return {Promise}
+ */
 browserTest.expectEvent = function(eventSource, event, timeoutMs,
                                    opt_expectedData) {
   return new Promise(function(fullfil, reject) {
+    /** @param {string=} actualData */
     var verifyEventParameters = function(actualData) {
       if (opt_expectedData === undefined || opt_expectedData === actualData) {
-        fullfil();
+        fullfil(true);
       } else {
         reject('Bad event data; expected ' + opt_expectedData +
                '; got ' + actualData);
@@ -277,16 +336,26 @@
   });
 };
 
+/**
+ * @param {Function} testClass
+ * @param {*} data
+ * @return {void}
+ * @suppress {checkTypes|checkVars|reportUnknownTypes}
+ */
 browserTest.runTest = function(testClass, data) {
   try {
     var test = new testClass();
     browserTest.expect(typeof test.run == 'function');
     test.run(data);
-  } catch (e) {
+  } catch (/** @type {Error} */ e) {
     browserTest.fail(e);
   }
 };
 
+/**
+ * @param {string} newPin
+ * @return {Promise}
+ */
 browserTest.setupPIN = function(newPin) {
   var AppMode = remoting.AppMode;
   var HOST_SETUP_WAIT = 10000;
@@ -313,6 +382,9 @@
   });
 };
 
+/**
+ * @return {Promise}
+ */
 browserTest.isLocalHostStarted = function() {
   return new Promise(function(resolve) {
     remoting.hostController.getLocalHostState(function(state) {
@@ -321,27 +393,37 @@
   });
 };
 
+/**
+ * @param {string} pin
+ * @return {Promise}
+ */
 browserTest.ensureHostStartedWithPIN = function(pin) {
   // Return if host is already
-  return browserTest.isLocalHostStarted().then(function(started){
-    if (!started) {
-      console.log('browserTest: Enabling remote connection.');
-      browserTest.clickOnControl('start-daemon');
-    } else {
-      console.log('browserTest: Changing the PIN of the host to: ' + pin + '.');
-      browserTest.clickOnControl('change-daemon-pin');
-    }
-    return browserTest.setupPIN(pin);
-  });
+  return browserTest.isLocalHostStarted().then(
+      /** @param {boolean} started */
+      function(started){
+        if (!started) {
+          console.log('browserTest: Enabling remote connection.');
+          browserTest.clickOnControl('start-daemon');
+        } else {
+          console.log('browserTest: Changing the PIN of the host to: ' +
+              pin + '.');
+          browserTest.clickOnControl('change-daemon-pin');
+        }
+        return browserTest.setupPIN(pin);
+      });
 };
 
-// Called by Browser Test in C++
+/**
+ * Called by Browser Test in C++
+ * @param {string} pin
+ * @suppress {checkTypes}
+ */
 browserTest.ensureRemoteConnectionEnabled = function(pin) {
-  browserTest.ensureHostStartedWithPIN(pin).then(function(){
-    browserTest.automationController_.send(true);
-  }, function(errorMessage){
-    console.error(errorMessage);
-    browserTest.automationController_.send(false);
+  browserTest.ensureHostStartedWithPIN(pin).then(function() {
+    browserTest.pass();
+  }, function(reason) {
+    browserTest.fail(reason);
   });
 };
 
diff --git a/remoting/webapp/browser_test/bump_scroll_browser_test.js b/remoting/webapp/browser_test/bump_scroll_browser_test.js
index 71545b4..0dddf312 100644
--- a/remoting/webapp/browser_test/bump_scroll_browser_test.js
+++ b/remoting/webapp/browser_test/bump_scroll_browser_test.js
@@ -12,7 +12,10 @@
 
 'use strict';
 
-/** @constructor */
+/**
+ * @constructor
+ * @extends {base.EventSourceImpl}
+ */
 browserTest.FakeDesktopConnectedView = function() {
   this.pluginPosition = {
     top: 0,
@@ -23,6 +26,9 @@
 
 base.extend(browserTest.FakeDesktopConnectedView, base.EventSourceImpl);
 
+/**
+ * @return {{top: number, left:number}} The top-left corner of the plugin.
+ */
 browserTest.FakeDesktopConnectedView.prototype.getPluginPositionForTesting =
     function() {
   return this.pluginPosition;
@@ -31,12 +37,16 @@
 
 /** @constructor */
 browserTest.Bump_Scroll = function() {
-  // To aviod dependencies on the actual host desktop size, we simulate a
+  // To avoid dependencies on the actual host desktop size, we simulate a
   // desktop larger or smaller than the client window. The exact value is
   // arbitrary, but must be positive.
+  /** @type {number} */
   this.kHostDesktopSizeDelta = 10;
 };
 
+/**
+ * @param {{pin:string}} data
+ */
 browserTest.Bump_Scroll.prototype.run = function(data) {
   browserTest.expect(typeof data.pin == 'string');
 
@@ -78,33 +88,43 @@
   );
 };
 
+/**
+ * @return {Promise}
+ */
 browserTest.Bump_Scroll.prototype.noScrollWindowed = function() {
-  remoting.desktopConnectedView.pluginWidthForBumpScrollTesting =
-      window.innerWidth + this.kHostDesktopSizeDelta;
-  remoting.desktopConnectedView.pluginHeightForBumpScrollTesting =
-      window.innerHeight + this.kHostDesktopSizeDelta;
+  remoting.desktopConnectedView.setPluginSizeForBumpScrollTesting(
+      window.innerWidth + this.kHostDesktopSizeDelta,
+      window.innerHeight + this.kHostDesktopSizeDelta);
   this.moveMouseTo(0, 0);
   return this.verifyScroll(undefined, undefined);
 };
 
+/**
+ * @return {Promise}
+ */
 browserTest.Bump_Scroll.prototype.noScrollSmaller = function() {
-  remoting.desktopConnectedView.pluginWidthForBumpScrollTesting =
-      window.innerWidth - this.kHostDesktopSizeDelta;
-  remoting.desktopConnectedView.pluginHeightForBumpScrollTesting =
-      window.innerHeight - this.kHostDesktopSizeDelta;
+  remoting.desktopConnectedView.setPluginSizeForBumpScrollTesting(
+      window.innerWidth - this.kHostDesktopSizeDelta,
+      window.innerHeight - this.kHostDesktopSizeDelta);
   this.moveMouseTo(0, 0);
   return this.verifyScroll(undefined, undefined);
 };
 
+/**
+ * @param {number} widthFraction
+ * @param {number} heightFraction
+ * @return {Promise}
+ */
 browserTest.Bump_Scroll.prototype.scrollDirection =
     function(widthFraction, heightFraction) {
-  remoting.desktopConnectedView.pluginWidthForBumpScrollTesting =
-      screen.width + this.kHostDesktopSizeDelta;
-  remoting.desktopConnectedView.pluginHeightForBumpScrollTesting =
-      screen.height + this.kHostDesktopSizeDelta;
+  remoting.desktopConnectedView.setPluginSizeForBumpScrollTesting(
+      screen.width + this.kHostDesktopSizeDelta,
+      screen.height + this.kHostDesktopSizeDelta);
+  /** @type {number} */
   var expectedTop = heightFraction == 0.0 ? 0 :
                     heightFraction == 1.0 ? -this.kHostDesktopSizeDelta :
                     undefined;
+  /** @type {number} */
   var expectedLeft = widthFraction == 0.0 ? 0 :
                      widthFraction == 1.0 ? -this.kHostDesktopSizeDelta :
                      undefined;
@@ -114,6 +134,9 @@
   return result;
 };
 
+/**
+ * @return {Promise}
+ */
 browserTest.Bump_Scroll.prototype.activateFullscreen = function() {
   return new Promise(function(fulfill, reject) {
     remoting.fullscreen.activate(true, function() {
@@ -128,6 +151,10 @@
   });
 };
 
+/**
+ * @param {number} x
+ * @param {number} y
+ */
 browserTest.Bump_Scroll.prototype.moveMouseTo = function(x, y) {
   var e = {
     bubbles: true,
@@ -154,7 +181,10 @@
   document.documentElement.dispatchEvent(event);
 };
 
-// verifyScroll is complicated enough to warrant a test
+/**
+ * verifyScroll() is complicated enough to warrant a test.
+ * @return {Promise}
+ */
 browserTest.Bump_Scroll.prototype.testVerifyScroll = function() {
   var STARTED = remoting.DesktopConnectedView.Events.bumpScrollStarted;
   var STOPPED = remoting.DesktopConnectedView.Events.bumpScrollStopped;
@@ -217,9 +247,11 @@
  *    the plugin, or undefined if it is not expected to change.
  * @param {browserTest.FakeDesktopConnectedView=} opt_desktopConnectedView
  *     DesktopConnectedView fake, for testing.
+ * @return {Promise}
  */
 browserTest.Bump_Scroll.prototype.verifyScroll =
     function (expectedTop, expectedLeft, opt_desktopConnectedView) {
+  /** @type {browserTest.FakeDesktopConnectedView} */
   var desktopConnectedView = opt_desktopConnectedView ||
       remoting.desktopConnectedView;
   base.debug.assert(desktopConnectedView != null);
@@ -230,6 +262,7 @@
   var initialTop = initialPosition.top;
   var initialLeft = initialPosition.left;
 
+  /** @return {Promise} */
   var verifyPluginPosition = function() {
     var position = desktopConnectedView.getPluginPositionForTesting();
     if (expectedLeft === undefined) {
diff --git a/remoting/webapp/browser_test/cancel_pin_browser_test.js b/remoting/webapp/browser_test/cancel_pin_browser_test.js
index 507e986..d6dc750 100644
--- a/remoting/webapp/browser_test/cancel_pin_browser_test.js
+++ b/remoting/webapp/browser_test/cancel_pin_browser_test.js
@@ -17,6 +17,9 @@
 /** @constructor */
 browserTest.Cancel_PIN = function() {};
 
+/**
+ * @param {{pin:string}} data
+ */
 browserTest.Cancel_PIN.prototype.run = function(data) {
   browserTest.expect(typeof data.pin == 'string');
 
@@ -35,4 +38,4 @@
     // On rejected.
     browserTest.fail(reason);
   });
-};
\ No newline at end of file
+};
diff --git a/remoting/webapp/browser_test/invalid_pin_browser_test.js b/remoting/webapp/browser_test/invalid_pin_browser_test.js
index f7a5935..1d08d1a 100644
--- a/remoting/webapp/browser_test/invalid_pin_browser_test.js
+++ b/remoting/webapp/browser_test/invalid_pin_browser_test.js
@@ -16,6 +16,9 @@
 /** @constructor */
 browserTest.Invalid_PIN = function() {};
 
+/**
+ * @param {{pin:string}} data
+ */
 browserTest.Invalid_PIN.prototype.run = function(data) {
   // Input validation.
   browserTest.expect(typeof data.pin == 'string');
diff --git a/remoting/webapp/browser_test/it2me_browser_test.js b/remoting/webapp/browser_test/it2me_browser_test.js
index 0f5e7bc9..c3be9bc7 100644
--- a/remoting/webapp/browser_test/it2me_browser_test.js
+++ b/remoting/webapp/browser_test/it2me_browser_test.js
@@ -17,6 +17,9 @@
 /** @constructor */
 browserTest.ConnectIt2Me = function() {};
 
+/**
+ * @param {{pin: string, accessCode: string}} data
+ */
 browserTest.ConnectIt2Me.prototype.run = function(data) {
   browserTest.expect(typeof data.accessCode == 'string',
                      'The access code should be an non-empty string');
@@ -28,17 +31,23 @@
     return browserTest.disconnect();
   }).then(function() {
     browserTest.pass();
+  /** @param {*} reason */
   }, function(reason) {
     browserTest.fail(reason);
   });
 };
 
+/** @return {Promise} */
 browserTest.ConnectIt2Me.clickOnAccessButton = function() {
   browserTest.clickOnControl('access-mode-button');
   return browserTest.onUIMode(remoting.AppMode.CLIENT_UNCONNECTED);
 };
 
-/** @private */
+/**
+ * @param {string} code
+ * @return {Promise}
+ * @private
+ */
 browserTest.ConnectIt2Me.prototype.enterAccessCode_ = function(code) {
   document.getElementById('access-code-entry').value = code;
   browserTest.clickOnControl('connect-button');
@@ -48,6 +57,9 @@
 /** @constructor */
 browserTest.InvalidAccessCode = function() {};
 
+/**
+ * @param {{pin: string, accessCode: string}} data
+ */
 browserTest.InvalidAccessCode.prototype.run = function(data) {
   browserTest.expect(typeof data.accessCode == 'string',
                      'The access code should be an non-empty string');
@@ -55,7 +67,8 @@
     document.getElementById('access-code-entry').value = data.accessCode;
     browserTest.clickOnControl('connect-button');
     return browserTest.expectConnectionError(
-        remoting.ClientSession.Mode.IT2ME, remoting.Error.INVALID_ACCESS_CODE);
+        remoting.DesktopConnectedView.Mode.IT2ME,
+        remoting.Error.INVALID_ACCESS_CODE);
   }).then(function() {
     browserTest.pass();
   }, function(reason) {
@@ -86,11 +99,15 @@
   });
 };
 
-// Wait for the email address of the local user to become available.  The email
-// address is required in an It2Me connection for domain policy enforcement.
-// TODO:(kelvinp) Fix this awkward behavior in the production code so that this
-// hack is no longer required.
-/** @private */
+/**
+ * Wait for the email address of the local user to become available.  The email
+ * address is required in an It2Me connection for domain policy enforcement.
+ * TODO:(kelvinp) Fix this awkward behavior in the production code so that this
+ * hack is no longer required.
+ *
+ * @return {Promise}
+ * @private
+ */
 browserTest.GetAccessCode.prototype.onUserInfoReady_ = function() {
   return new Promise(function(resolve, reject){
     remoting.identity.getUserInfo(resolve, reject);
diff --git a/remoting/webapp/browser_test/mock_client_plugin.js b/remoting/webapp/browser_test/mock_client_plugin.js
index e161a8f..09858e0a1 100644
--- a/remoting/webapp/browser_test/mock_client_plugin.js
+++ b/remoting/webapp/browser_test/mock_client_plugin.js
@@ -14,6 +14,7 @@
 var remoting = remoting || {};
 
 /**
+ * @param {Element} container
  * @constructor
  * @implements {remoting.ClientPlugin}
  */
@@ -21,11 +22,10 @@
   this.container_ = container;
   this.element_ = document.createElement('div');
   this.element_.style.backgroundImage = 'linear-gradient(45deg, blue, red)';
-  this.width_ = 640;
-  this.height_ = 480;
   this.connectionStatusUpdateHandler_ = null;
   this.desktopSizeUpdateHandler_ = null;
   this.container_.appendChild(this.element_);
+  this.hostDesktop_ = new remoting.MockClientPlugin.HostDesktop();
 };
 
 remoting.MockClientPlugin.prototype.dispose = function() {
@@ -34,20 +34,8 @@
   this.connectionStatusUpdateHandler_ = null;
 };
 
-remoting.MockClientPlugin.prototype.getDesktopWidth = function() {
-  return this.width_;
-};
-
-remoting.MockClientPlugin.prototype.getDesktopHeight = function() {
-  return this.height_;
-};
-
-remoting.MockClientPlugin.prototype.getDesktopXDpi = function() {
-  return 96;
-};
-
-remoting.MockClientPlugin.prototype.getDesktopYDpi = function() {
-  return 96;
+remoting.MockClientPlugin.prototype.hostDesktop = function() {
+  return this.hostDesktop_;
 };
 
 remoting.MockClientPlugin.prototype.element = function() {
@@ -78,15 +66,6 @@
 
 remoting.MockClientPlugin.prototype.releaseAllKeys = function() {};
 
-remoting.MockClientPlugin.prototype.notifyClientResolution =
-    function(width, height, dpi) {
-  this.width_ = width;
-  this.height_ = height;
-  if (this.desktopSizeUpdateHandler_) {
-    window.setTimeout(this.desktopSizeUpdateHandler_, 0);
-  }
-};
-
 remoting.MockClientPlugin.prototype.onIncomingIq = function(iq) {};
 
 remoting.MockClientPlugin.prototype.isSupportedVersion = function() {
@@ -135,8 +114,13 @@
 remoting.MockClientPlugin.prototype.setOnDebugMessageHandler =
     function(handler) {};
 
+/**
+ * @param {function(number, number):void} handler
+ * @private
+ */
 remoting.MockClientPlugin.prototype.setConnectionStatusUpdateHandler =
     function(handler) {
+  /** @type {function(number, number):void} */
   this.connectionStatusUpdateHandler_ = handler;
 };
 
@@ -146,11 +130,6 @@
 remoting.MockClientPlugin.prototype.setConnectionReadyHandler =
     function(handler) {};
 
-remoting.MockClientPlugin.prototype.setDesktopSizeUpdateHandler =
-    function(handler) {
-  this.desktopSizeUpdateHandler_ = handler;
-};
-
 remoting.MockClientPlugin.prototype.setCapabilitiesHandler =
     function(handler) {};
 
@@ -169,6 +148,61 @@
 remoting.MockClientPlugin.prototype.setFetchPinHandler =
     function(handler) {};
 
+/**
+ * @constructor
+ * @implements {remoting.HostDesktop}
+ * @extends {base.EventSourceImpl}
+ */
+remoting.MockClientPlugin.HostDesktop = function() {
+  /** @private */
+  this.width_ = 0;
+  /** @private */
+  this.height_ = 0;
+  /** @private */
+  this.xDpi_ = 96;
+  /** @private */
+  this.yDpi_ = 96;
+  /** @private */
+  this.resizable_ = true;
+  this.defineEvents(base.values(remoting.HostDesktop.Events));
+};
+base.extend(remoting.MockClientPlugin.HostDesktop, base.EventSourceImpl);
+
+/**
+ * @return {{width:number, height:number, xDpi:number, yDpi:number}}
+ * @override
+ */
+remoting.MockClientPlugin.HostDesktop.prototype.getDimensions = function() {
+  return {
+    width: this.width_,
+    height: this.height_,
+    xDpi: this.xDpi_,
+    yDpi: this.yDpi_
+  };
+};
+
+/**
+ * @return {boolean}
+ * @override
+ */
+remoting.MockClientPlugin.HostDesktop.prototype.isResizable = function() {
+  return this.resizable_;
+};
+
+/**
+ * @param {number} width
+ * @param {number} height
+ * @param {number} deviceScale
+ * @override
+ */
+remoting.MockClientPlugin.HostDesktop.prototype.resize =
+    function(width, height, deviceScale) {
+  this.width_ = width;
+  this.height_ = height;
+  this.xDpi_ = this.yDpi_ = Math.floor(deviceScale * 96);
+  this.raiseEvent(remoting.HostDesktop.Events.sizeChanged,
+                  this.getDimensions());
+};
 
 /**
  * @constructor
diff --git a/remoting/webapp/browser_test/mock_host_list_api.js b/remoting/webapp/browser_test/mock_host_list_api.js
index c318cd50..2fca220 100644
--- a/remoting/webapp/browser_test/mock_host_list_api.js
+++ b/remoting/webapp/browser_test/mock_host_list_api.js
@@ -49,11 +49,11 @@
 };
 
 /**
- * @param {function():void} onDone
- * @param {function(remoting.Error):void} onError
  * @param {string} hostId
  * @param {string} hostName
  * @param {string} hostPublicKey
+ * @param {function():void} onDone
+ * @param {function(remoting.Error):void} onError
  */
 remoting.MockHostListApi.prototype.put =
     function(hostId, hostName, hostPublicKey, onDone, onError) {
@@ -77,9 +77,9 @@
 };
 
 /**
+ * @param {string} hostId
  * @param {function():void} onDone
  * @param {function(remoting.Error):void} onError
- * @param {string} hostId
  */
 remoting.MockHostListApi.prototype.remove =
     function(hostId, onDone, onError) {
diff --git a/remoting/webapp/browser_test/mock_identity.js b/remoting/webapp/browser_test/mock_identity.js
index 669e89f..4a1c61b 100644
--- a/remoting/webapp/browser_test/mock_identity.js
+++ b/remoting/webapp/browser_test/mock_identity.js
@@ -67,8 +67,8 @@
 
 /**
  * @param {string} token
- * @param {function()} onDone
- * @param {function()} onError
+ * @param {Function} onDone
+ * @param {function(remoting.Error)} onError
  * @param {Array<*>} values
  */
 remoting.MockIdentity.validateTokenAndCall =
@@ -85,8 +85,8 @@
 };
 
 /**
- * @param {function()} onDone
- * @param {function()} onError
+ * @param {Function} onDone
+ * @param {function(remoting.Error)} onError
  * @param {Array<*>} values
  */
 remoting.MockIdentity.prototype.validateTokenAndCall =
diff --git a/remoting/webapp/browser_test/mock_oauth2_api.js b/remoting/webapp/browser_test/mock_oauth2_api.js
index d302e016d..a64fe19 100644
--- a/remoting/webapp/browser_test/mock_oauth2_api.js
+++ b/remoting/webapp/browser_test/mock_oauth2_api.js
@@ -62,7 +62,7 @@
 };
 
 /**
- * @param {function(string)} onDone
+ * @param {function(string,string)} onDone
  * @param {function(remoting.Error)} onError
  * @param {string} token
  */
@@ -72,7 +72,7 @@
 };
 
 /**
- * @param {function(string, string)} onDone
+ * @param {function(string,string)} onDone
  * @param {function(remoting.Error)} onError
  * @param {string} token
  */
diff --git a/remoting/webapp/browser_test/mock_session_connector.js b/remoting/webapp/browser_test/mock_session_connector.js
index dcf0513..090e344 100644
--- a/remoting/webapp/browser_test/mock_session_connector.js
+++ b/remoting/webapp/browser_test/mock_session_connector.js
@@ -14,39 +14,64 @@
 var remoting = remoting || {};
 
 /**
+ * @param {HTMLElement} clientContainer Container element for the client view.
+ * @param {function(remoting.ClientSession):void} onConnected Callback on
+ *     success.
+ * @param {function(remoting.Error):void} onError Callback on error.
+ * @param {function(string, string):boolean} onExtensionMessage The handler for
+ *     protocol extension messages. Returns true if a message is recognized;
+ *     false otherwise.
+ * @param {function(remoting.Error):void} onConnectionFailed Callback for when
+ *     the connection fails.
+ * @param {Array<string>} requiredCapabilities Connector capabilities
+ *     required by this application.
+ * @param {string} defaultRemapKeys The default set of key mappings for the
+ *     client session to use.
  * @constructor
  * @implements {remoting.SessionConnector}
  */
 remoting.MockSessionConnector = function(clientContainer, onConnected, onError,
-                                         onExtensionMessage) {
+                                         onExtensionMessage,
+                                         onConnectionFailed,
+                                         requiredCapabilities,
+                                         defaultRemapKeys) {
   this.clientContainer_ = clientContainer;
+  /** @type {function(remoting.ClientSession):void} */
   this.onConnected_ = onConnected;
   this.onError = onError;
   this.onExtensionMessage_ = onExtensionMessage;
+  this.onConnectionFailed_ = onConnectionFailed;
+  this.requiredCapabilities_ = requiredCapabilities;
+  this.defaultRemapKeys_ = defaultRemapKeys;
+
+  /** @type {remoting.DesktopConnectedView.Mode} */
+  this.mode_ = remoting.DesktopConnectedView.Mode.ME2ME;
+
   this.reset();
 };
 
 remoting.MockSessionConnector.prototype.reset = function() {
-  this.pairingRequested_ = false;
+  /** @type {string}  @private */
+  this.hostId_ = '';
 };
 
 remoting.MockSessionConnector.prototype.connectMe2Me =
     function(host, fetchPin, fetchThirdPartyToken,
              clientPairingId, clientPairedSecret) {
-  this.mode_ = remoting.ClientSession.Mode.ME2ME;
+  this.mode_ = remoting.DesktopConnectedView.Mode.ME2ME;
   this.connect_();
 };
 
 remoting.MockSessionConnector.prototype.retryConnectMe2Me =
     function(host, fetchPin, fetchThirdPartyToken,
              clientPairingId, clientPairedSecret) {
-  this.mode_ = remoting.ClientSession.Mode.ME2ME;
+  this.mode_ = remoting.DesktopConnectedView.Mode.ME2ME;
   this.connect_();
 };
 
 remoting.MockSessionConnector.prototype.connectMe2App =
     function(host, fetchThirdPartyToken) {
-  this.mode_ = remoting.ClientSession.Mode.ME2APP;
+  this.mode_ = remoting.DesktopConnectedView.Mode.APP_REMOTING;
   this.connect_();
 };
 
@@ -56,12 +81,12 @@
 
 remoting.MockSessionConnector.prototype.connectIT2Me =
     function(accessCode) {
-  this.mode_ = remoting.ClientSession.Mode.ME2ME;
+  this.mode_ = remoting.DesktopConnectedView.Mode.ME2ME;
   this.connect_();
 };
 
 remoting.MockSessionConnector.prototype.reconnect = function() {
-  base.debug.assert(this.mode_ == remoting.ClientSession.Mode.ME2ME);
+  base.debug.assert(this.mode_ == remoting.DesktopConnectedView.Mode.ME2ME);
   this.connect_();
 };
 
@@ -76,14 +101,6 @@
   return this.hostId_;
 };
 
-remoting.MockSessionConnector.prototype.requestPairing = function() {
-  this.pairingRequested_ = true;
-};
-
-remoting.MockSessionConnector.prototype.pairingRequested = function() {
-  return this.pairingRequested_;
-};
-
 remoting.MockSessionConnector.prototype.connect_ = function() {
   var signalling = new remoting.MockSignalStrategy();
   signalling.setStateForTesting(remoting.SignalStrategy.State.CONNECTED);
@@ -95,10 +112,23 @@
   var hostPublicKey = '';
   var clientPairingId = '';
   var clientPairedSecret = '';
+
+  /**
+   * @param {boolean} offerPairing
+   * @param {function(string):void} callback
+   */
   var fetchPin = function(offerPairing, callback) {
     window.setTimeout(function() { callback('') }, 0);
   };
-  var fetchThirdPartyToken = function(tokenUrl, scope, callback) {
+
+  /**
+   * @param {string} tokenUrl
+   * @param {string} hostPublicKey
+   * @param {string} scope
+   * @param {function(string, string):void} callback
+   */
+  var fetchThirdPartyToken = function(tokenUrl, hostPublicKey, scope,
+                                      callback) {
     window.setTimeout(function() { callback('', '') }, 0);
   };
 
@@ -108,11 +138,13 @@
       authenticationMethods, hostId, hostJid, hostPublicKey,
       this.mode_, clientPairingId, clientPairedSecret);
 
+  var that = this;
+  /** @param {remoting.ClientSession.StateEvent} event */
   var onStateChange = function(event) {
     if (event.current == remoting.ClientSession.State.CONNECTED) {
-      this.onConnected_(clientSession);
+      that.onConnected_(clientSession);
     }
-  }.bind(this);
+  };
 
   clientSession.addEventListener(
       remoting.ClientSession.Events.stateChanged,
@@ -127,8 +159,26 @@
  */
 remoting.MockSessionConnectorFactory = function() {};
 
+/**
+ * @param {HTMLElement} clientContainer Container element for the client view.
+ * @param {function(remoting.ClientSession):void} onConnected Callback on
+ *     success.
+ * @param {function(remoting.Error):void} onError Callback on error.
+ * @param {function(string, string):boolean} onExtensionMessage The handler for
+ *     protocol extension messages. Returns true if a message is recognized;
+ *     false otherwise.
+ * @param {function(remoting.Error):void} onConnectionFailed Callback for when
+ *     the connection fails.
+ * @param {Array<string>} requiredCapabilities Connector capabilities
+ *     required by this application.
+ * @param {string} defaultRemapKeys The default set of key mappings to use
+ *     in the client session.
+ * @return {remoting.MockSessionConnector}
+ */
 remoting.MockSessionConnectorFactory.prototype.createConnector =
-    function(clientContainer, onConnected, onError, onExtensionMessage) {
+    function(clientContainer, onConnected, onError, onExtensionMessage,
+             onConnectionFailed, requiredCapabilities, defaultRemapKeys) {
   return new remoting.MockSessionConnector(
-      clientContainer, onConnected, onError, onExtensionMessage);
+      clientContainer, onConnected, onError, onExtensionMessage,
+      onConnectionFailed, requiredCapabilities, defaultRemapKeys);
 };
diff --git a/remoting/webapp/browser_test/scrollbar_browser_test.js b/remoting/webapp/browser_test/scrollbar_browser_test.js
index 2424c8ce..98ca555 100644
--- a/remoting/webapp/browser_test/scrollbar_browser_test.js
+++ b/remoting/webapp/browser_test/scrollbar_browser_test.js
@@ -53,6 +53,7 @@
 
 /**
  * Verify the test cases for the home-screen.
+ * @return {Promise}
  */
 browserTest.Scrollbars.prototype.verifyHomeScreenScrollbars_ = function() {
   // Note that, due to crbug.com/240772, if the window already has
@@ -126,6 +127,7 @@
  * scroll-bar visible on the corresponding edge, in which case it will return
  * the scroller <div> itself.
  *
+ * @return {{horizontal: boolean, vertical:boolean}}
  * @private
  */
 browserTest.Scrollbars.prototype.getScrollbarState_ = function() {
@@ -145,6 +147,9 @@
  * Returns a promise that resolves if the scroll-bar state is as expected, or
  * rejects otherwise.
  *
+ * @param {boolean} horizontalExpected
+ * @param {boolean} verticalExpected
+ * @return {Promise}
  * @private
  */
 browserTest.Scrollbars.prototype.verifyScrollbarState_ =
@@ -168,9 +173,11 @@
 
 
 /**
- * @private
+ * @param {number} width
+ * @param {number} height
  * @return {Promise} A promise that will be fulfilled when the window has
  *     been resized and it's safe to test scroll-bar visibility.
+ * @private
  */
 browserTest.Scrollbars.prototype.resize_ = function(width, height) {
   var win = chrome.app.window.current();
@@ -183,9 +190,13 @@
 
 
 /**
- * @private
+ * @param {number} width
+ * @param {number} height
+ * @param {boolean} horizontalExpected
+ * @param {boolean} verticalExpected
  * @return {Promise} A promise that will be fulfilled when the window has
  *     been resized and it's safe to test scroll-bar visibility.
+ * @private
  */
 browserTest.Scrollbars.prototype.resizeAndVerifyScroll_ =
     function(width, height, horizontalExpected, verticalExpected) {
diff --git a/remoting/webapp/browser_test/timeout_waiter.js b/remoting/webapp/browser_test/timeout_waiter.js
index 2bb7e70..fd75ada 100644
--- a/remoting/webapp/browser_test/timeout_waiter.js
+++ b/remoting/webapp/browser_test/timeout_waiter.js
@@ -19,7 +19,7 @@
   DEFAULT: 5000
 };
 
-/** @interface */
+/** @constructor */
 browserTest.Predicate = function() {};
 
 /** @return {boolean} */
@@ -43,12 +43,12 @@
       opt_timeout = browserTest.Timeout.DEFAULT;
     }
 
-    var timeout = /** @type {number} */ opt_timeout;
+    var timeout = /** @type {number} */ (opt_timeout);
     var end = Number(Date.now()) + timeout;
     var testPredicate = function() {
       if (predicate.evaluate()) {
         console.log(predicate.description() + ' satisfied.');
-        fulfill();
+        fulfill(true);
       } else if (Date.now() >= end) {
         reject(new Error('Timed out (' + opt_timeout + 'ms) waiting for ' +
                          predicate.description()));
@@ -62,43 +62,37 @@
 };
 
 /**
- * @suppress {checkTypes}
  * @param {string} id
  * @return {browserTest.Predicate}
  */
 browserTest.isVisible = function(id) {
-  /** @type {browserTest.Predicate} */
-  return {
-    evaluate: function() {
-      /** @type {HTMLElement} */
-      var element = document.getElementById(id);
-      browserTest.expect(Boolean(element), 'No such element: ' + id);
-      return element.getBoundingClientRect().width !== 0;
-    },
-    description: function() {
-      return 'isVisible(' + id + ')';
-    }
+  var pred = new browserTest.Predicate();
+  pred.evaluate = function() {
+    /** @type {HTMLElement} */
+    var element = document.getElementById(id);
+    browserTest.expect(Boolean(element), 'No such element: ' + id);
+    return element.getBoundingClientRect().width !== 0;
   };
+  pred.description = function() {
+    return 'isVisible(' + id + ')';
+  };
+  return pred;
 };
 
 /**
- * @suppress {checkTypes}
  * @param {string} id
  * @return {browserTest.Predicate}
  */
 browserTest.isEnabled = function(id) {
-
-  /** @type {browserTest.Predicate} */
-  return {
-    evaluate: function() {
-      /** @type {HTMLElement} */
-      var element = document.getElementById(id);
-      browserTest.expect(Boolean(element), 'No such element: ' + id);
-      return !element.disabled;
-    },
-    description: function() {
-      return 'isEnabled(' + id + ')';
-    }
+  var pred = new browserTest.Predicate();
+  pred.evaluate = function() {
+    /** @type {Element} */
+    var element = document.getElementById(id);
+    browserTest.expect(Boolean(element), 'No such element: ' + id);
+    return !element.disabled;
   };
+  pred.description = function() {
+    return 'isEnabled(' + id + ')';
+  };
+  return pred;
 };
-
diff --git a/remoting/webapp/browser_test/update_pin_browser_test.js b/remoting/webapp/browser_test/update_pin_browser_test.js
index 370ed1f48..9646841 100644
--- a/remoting/webapp/browser_test/update_pin_browser_test.js
+++ b/remoting/webapp/browser_test/update_pin_browser_test.js
@@ -18,6 +18,9 @@
 /** @constructor */
 browserTest.Update_PIN = function() {};
 
+/**
+ * @param {{new_pin:string, old_pin:string}} data
+ */
 browserTest.Update_PIN.prototype.run = function(data) {
   var LOGIN_BACKOFF_WAIT = 2000;
   // Input validation
@@ -36,7 +39,7 @@
   ).then(
     browserTest.connectMe2Me
   ).then(function(){
-    return browserTest.enterPIN_(data.new_pin, false /* expectError*/)
+    return browserTest.enterPIN(data.new_pin, false /* expectError*/)
   }).then(
     // Clean up the test by disconnecting and changing the PIN back
     browserTest.disconnect
@@ -52,6 +55,10 @@
   );
 };
 
+/**
+ * @param {string} newPin
+ * @return {Promise}
+ */
 browserTest.Update_PIN.prototype.changePIN_ = function(newPin) {
   var AppMode = remoting.AppMode;
   var HOST_RESTART_WAIT = 10000;
diff --git a/remoting/webapp/crd/js/client_plugin.js b/remoting/webapp/crd/js/client_plugin.js
index 5f21c1c..a7fdd34 100644
--- a/remoting/webapp/crd/js/client_plugin.js
+++ b/remoting/webapp/crd/js/client_plugin.js
@@ -19,24 +19,9 @@
 remoting.ClientPlugin = function() {};
 
 /**
- * @return {number} The width of the remote desktop, in pixels.
+ * @return {remoting.HostDesktop}
  */
-remoting.ClientPlugin.prototype.getDesktopWidth = function() {};
-
-/**
- * @return {number} The height of the remote desktop, in pixels.
- */
-remoting.ClientPlugin.prototype.getDesktopHeight = function() {};
-
-/**
- * @return {number} The x-DPI of the remote desktop.
- */
-remoting.ClientPlugin.prototype.getDesktopXDpi = function() {};
-
-/**
- * @return {number} The y-DPI of the remote desktop.
- */
-remoting.ClientPlugin.prototype.getDesktopYDpi = function() {};
+remoting.ClientPlugin.prototype.hostDesktop = function() {};
 
 /**
  * @return {HTMLElement} The DOM element representing the remote session.
@@ -88,14 +73,6 @@
 remoting.ClientPlugin.prototype.releaseAllKeys = function() {};
 
 /**
- * @param {number} width
- * @param {number} height
- * @param {number} dpi
- */
-remoting.ClientPlugin.prototype.notifyClientResolution =
-    function(width, height, dpi) {};
-
-/**
  * @param {string} iq
  */
 remoting.ClientPlugin.prototype.onIncomingIq = function(iq) {};
@@ -107,7 +84,7 @@
 
 /**
  * @param {remoting.ClientPlugin.Feature} feature
- * @return {boolean} True if the plugin support the specified feature.
+ * @return {boolean} True if the plugin supports the specified feature.
  */
 remoting.ClientPlugin.prototype.hasFeature = function(feature) {};
 
@@ -216,20 +193,6 @@
     function(handler) {};
 
 /**
- * @param {function():void} handler Callback for desktop size change
- *     notifications.
- */
-remoting.ClientPlugin.prototype.setDesktopSizeUpdateHandler =
-    function(handler) {};
-
-/**
- * @param {function(Array<Array<number>>):void} handler Callback for desktop
- *     shape change notifications.
- */
-remoting.ClientPlugin.prototype.setDesktopShapeUpdateHandler =
-    function(handler) {};
-
-/**
  * @param {function(!Array<string>):void} handler Callback to inform of
  *     capabilities negotiated between host and client.
  */
@@ -276,6 +239,13 @@
 remoting.ClientPlugin.prototype.setFetchPinHandler =
     function(handler) {};
 
+/**
+ * @param {function({rects:Array<Array<number>>}):void|null} handler Callback
+ *     to receive dirty region information for each video frame, for debugging.
+ */
+remoting.ClientPlugin.prototype.setDebugDirtyRegionHandler =
+    function(handler) {};
+
 
 /**
  * Set of features for which hasFeature() can be used to test.
diff --git a/remoting/webapp/crd/js/client_plugin_host_desktop_impl.js b/remoting/webapp/crd/js/client_plugin_host_desktop_impl.js
new file mode 100644
index 0000000..78ca6df1
--- /dev/null
+++ b/remoting/webapp/crd/js/client_plugin_host_desktop_impl.js
@@ -0,0 +1,126 @@
+// 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.
+
+/**
+ * @fileoverview
+ * Provides an interface to manage the Host Desktop of a remoting session.
+ */
+
+var remoting = remoting || {};
+remoting.ClientPlugin = remoting.ClientPlugin || {};
+
+(function() {
+
+'use strict';
+
+/**
+ * @param {remoting.ClientPluginImpl} plugin
+ * @param {function(Object):void} postMessageCallback Callback to post a message
+ *   to the Client Plugin.
+ *
+ * @implements {remoting.HostDesktop}
+ * @extends {base.EventSourceImpl}
+ * @constructor
+ */
+remoting.ClientPlugin.HostDesktopImpl = function(plugin, postMessageCallback) {
+  /** @private */
+  this.plugin_ = plugin;
+  /** @private */
+  this.width_ = 0;
+  /** @private */
+  this.height_ = 0;
+  /** @private */
+  this.xDpi_ = 96;
+  /** @private */
+  this.yDpi_ = 96;
+  /** @private */
+  this.postMessageCallback_ = postMessageCallback;
+
+  this.defineEvents(base.values(remoting.HostDesktop.Events));
+};
+base.extend(remoting.ClientPlugin.HostDesktopImpl, base.EventSourceImpl);
+
+/** @return {boolean} Whether the host supports desktop resizing. */
+remoting.ClientPlugin.HostDesktopImpl.prototype.isResizable = function() {
+  return this.plugin_.hasFeature(
+      remoting.ClientPlugin.Feature.NOTIFY_CLIENT_RESOLUTION);
+};
+
+/** @return {{width:number, height:number, xDpi:number, yDpi:number}} */
+remoting.ClientPlugin.HostDesktopImpl.prototype.getDimensions = function() {
+  return {
+    width: this.width_,
+    height: this.height_,
+    xDpi: this.xDpi_,
+    yDpi: this.yDpi_
+  };
+};
+
+/**
+ * @param {number} width
+ * @param {number} height
+ * @param {number} deviceScale
+ */
+remoting.ClientPlugin.HostDesktopImpl.prototype.resize = function(
+    width, height, deviceScale) {
+  if (this.isResizable()) {
+    var dpi = Math.floor(deviceScale * 96);
+    this.postMessageCallback_({
+      method: 'notifyClientResolution',
+      data: {
+        width: Math.floor(width * deviceScale),
+        height: Math.floor(height * deviceScale),
+        x_dpi: dpi,
+        y_dpi: dpi
+      }
+    });
+  }
+};
+
+/**
+ * This function is called by |this.plugin_| when the size of the host
+ * desktop is changed.
+ *
+ * @param {remoting.ClientPluginMessage} message
+ */
+remoting.ClientPlugin.HostDesktopImpl.prototype.onSizeUpdated = function(
+    message) {
+  this.width_ = getNumberAttr(message.data, 'width');
+  this.height_ = getNumberAttr(message.data, 'height');
+  this.xDpi_ = getNumberAttr(message.data, 'x_dpi', 96);
+  this.yDpi_ = getNumberAttr(message.data, 'y_dpi', 96);
+  this.raiseEvent(remoting.HostDesktop.Events.sizeChanged,
+                  this.getDimensions());
+};
+
+/**
+ * This function is called by |this.plugin_| when the shape of the host
+ * desktop is changed.
+ *
+ * @param {remoting.ClientPluginMessage} message
+ * @return {Array<{left:number, top:number, width:number, height:number}>}
+ *    rectangles of the desktop shape.
+ */
+remoting.ClientPlugin.HostDesktopImpl.prototype.onShapeUpdated =
+    function(message) {
+  var shapes = getArrayAttr(message.data, 'rects');
+  var rects = shapes.map(
+    /** @param {Array.<number>} shape */
+    function(shape) {
+      if (!Array.isArray(shape) || shape.length != 4) {
+        throw 'Received invalid onDesktopShape message';
+      }
+      var rect = {};
+      rect.left = shape[0];
+      rect.top = shape[1];
+      rect.width = shape[2];
+      rect.height = shape[3];
+      return rect;
+  });
+
+  this.raiseEvent(remoting.HostDesktop.Events.shapeChanged, rects);
+  return rects;
+};
+
+}());
diff --git a/remoting/webapp/crd/js/client_plugin_impl.js b/remoting/webapp/crd/js/client_plugin_impl.js
index 6327770c..2d3efe0 100644
--- a/remoting/webapp/crd/js/client_plugin_impl.js
+++ b/remoting/webapp/crd/js/client_plugin_impl.js
@@ -48,15 +48,6 @@
    */
   this.requiredCapabilities_ = requiredCapabilities;
 
-  /** @private */
-  this.desktopWidth_ = 0;
-  /** @private */
-  this.desktopHeight_ = 0;
-  /** @private */
-  this.desktopXDpi_ = 96;
-  /** @private */
-  this.desktopYDpi_ = 96;
-
   /**
    * @param {string} iq The Iq stanza received from the host.
    * @private
@@ -95,13 +86,6 @@
    */
   this.fetchThirdPartyTokenHandler_ = function(
     tokenUrl, hostPublicKey, scope) {};
-  /** @private */
-  this.onDesktopSizeUpdateHandler_ = function () {};
-  /**
-   * @param {Array<Array<number>>} rects
-   * @private
-   */
-  this.onDesktopShapeUpdateHandler_ = function (rects) {};
   /**
    * @param {!Array<string>} capabilities The negotiated capabilities.
    * @private
@@ -121,12 +105,13 @@
    * @private
    */
   this.updateMouseCursorImage_ = function(url, hotspotX, hotspotY) {};
-
   /**
    * @param {string} data Remote cast extension message.
    * @private
    */
   this.onCastExtensionHandler_ = function(data) {};
+  /** @private {?function({rects:Array<Array<number>>}):void} */
+  this.debugRegionHandler_ = null;
 
   /**
    * @type {number}
@@ -180,6 +165,9 @@
   if (remoting.settings.CLIENT_PLUGIN_TYPE == 'native') {
     window.setTimeout(this.showPluginForClickToPlay_.bind(this), 500);
   }
+
+  this.hostDesktop_ = new remoting.ClientPlugin.HostDesktopImpl(
+      this, this.postMessage_.bind(this));
 };
 
 /**
@@ -265,22 +253,6 @@
 };
 
 /**
- * @param {function():void} handler
- */
-remoting.ClientPluginImpl.prototype.setDesktopSizeUpdateHandler =
-    function(handler) {
-  this.onDesktopSizeUpdateHandler_ = handler;
-};
-
-/**
- * @param {function(Array<Array<number>>):void} handler
- */
-remoting.ClientPluginImpl.prototype.setDesktopShapeUpdateHandler =
-    function(handler) {
-  this.onDesktopShapeUpdateHandler_ = handler;
-};
-
-/**
  * @param {function(!Array<string>):void} handler
  */
 remoting.ClientPluginImpl.prototype.setCapabilitiesHandler = function(handler) {
@@ -325,6 +297,16 @@
 };
 
 /**
+ * @param {?function({rects:Array<Array<number>>}):void} handler
+ */
+remoting.ClientPluginImpl.prototype.setDebugDirtyRegionHandler =
+    function(handler) {
+  this.debugRegionHandler_ = handler;
+  this.plugin_.postMessage(JSON.stringify(
+      { method: 'enableDebugRegion', data: { enable: handler != null } }));
+};
+
+/**
  * @param {string|remoting.ClientPluginMessage}
  *    rawMessage Message from the plugin.
  * @private
@@ -374,14 +356,6 @@
           tokenize(getStringAttr(message.data, 'apiFeatures'));
 
       // Negotiate capabilities.
-
-      /** @type {!Array<string>} */
-      var requestedCapabilities = [];
-      if ('requestedCapabilities' in message.data) {
-        requestedCapabilities =
-            tokenize(getStringAttr(message.data, 'requestedCapabilities'));
-      }
-
       /** @type {!Array<string>} */
       var supportedCapabilities = [];
       if ('supportedCapabilities' in message.data) {
@@ -425,23 +399,9 @@
     this.onRouteChangedHandler_(channel, connectionType);
 
   } else if (message.method == 'onDesktopSize') {
-    this.desktopWidth_ = getNumberAttr(message.data, 'width');
-    this.desktopHeight_ = getNumberAttr(message.data, 'height');
-    this.desktopXDpi_ = getNumberAttr(message.data, 'x_dpi', 96);
-    this.desktopYDpi_ = getNumberAttr(message.data, 'y_dpi', 96);
-    this.onDesktopSizeUpdateHandler_();
-
+    this.hostDesktop_.onSizeUpdated(message);
   } else if (message.method == 'onDesktopShape') {
-    var rects = getArrayAttr(message.data, 'rects');
-    for (var i = 0; i < rects.length; ++i) {
-      /** @type {Array<number>} */
-      var rect = rects[i];
-      if (typeof rect != 'object' || rect.length != 4) {
-        throw 'Received invalid onDesktopShape message';
-      }
-    }
-    this.onDesktopShapeUpdateHandler_(rects);
-
+    this.hostDesktop_.onShapeUpdated(message);
   } else if (message.method == 'onPerfStats') {
     // Return value is ignored. These calls will throw an error if the value
     // is not a number.
@@ -543,6 +503,13 @@
 
     context.putImageData(imageData, 0, 0);
     this.updateMouseCursorImage_(canvas.toDataURL(), hotspotX, hotspotY);
+
+  } else if (message.method == 'onDebugRegion') {
+    if (this.debugRegionHandler_) {
+      this.debugRegionHandler_(
+          /** @type {{rects: Array<(Array<number>)>}} **/(message.data));
+    }
+
   }
 };
 
@@ -758,14 +725,7 @@
  */
 remoting.ClientPluginImpl.prototype.notifyClientResolution =
     function(width, height, device_scale) {
-  if (this.hasFeature(remoting.ClientPlugin.Feature.NOTIFY_CLIENT_RESOLUTION)) {
-    var dpi = Math.floor(device_scale * 96);
-    this.plugin_.postMessage(JSON.stringify(
-        { method: 'notifyClientResolution',
-          data: { width: Math.floor(width * device_scale),
-                  height: Math.floor(height * device_scale),
-                  x_dpi: dpi, y_dpi: dpi }}));
-  }
+  this.hostDesktop_.resize(width, height, device_scale);
 };
 
 /**
@@ -907,21 +867,9 @@
 
 };
 
-remoting.ClientPluginImpl.prototype.getDesktopWidth = function() {
-  return this.desktopWidth_;
-}
-
-remoting.ClientPluginImpl.prototype.getDesktopHeight = function() {
-  return this.desktopHeight_;
-}
-
-remoting.ClientPluginImpl.prototype.getDesktopXDpi = function() {
-  return this.desktopXDpi_;
-}
-
-remoting.ClientPluginImpl.prototype.getDesktopYDpi = function() {
-  return this.desktopYDpi_;
-}
+remoting.ClientPluginImpl.prototype.hostDesktop = function() {
+  return this.hostDesktop_;
+};
 
 /**
  * If we haven't yet received a "hello" message from the plugin, change its
@@ -956,6 +904,17 @@
   this.plugin_.style.position = '';
 };
 
+/**
+ * Callback passed to submodules to post a message to the plugin.
+ *
+ * @param {Object} message
+ * @private
+ */
+remoting.ClientPluginImpl.prototype.postMessage_ = function(message) {
+  if (this.plugin_ && this.plugin_.postMessage) {
+    this.plugin_.postMessage(JSON.stringify(message));
+  }
+};
 
 /**
  * @constructor
diff --git a/remoting/webapp/crd/js/client_screen.js b/remoting/webapp/crd/js/client_screen.js
index 1dc46fd6..1700c70 100644
--- a/remoting/webapp/crd/js/client_screen.js
+++ b/remoting/webapp/crd/js/client_screen.js
@@ -53,18 +53,10 @@
  * @return {void} Nothing.
  */
 remoting.disconnect = function() {
-  if (!remoting.desktopConnectedView) {
+  if (!remoting.clientSession) {
     return;
   }
-  if (remoting.desktopConnectedView.getMode() ==
-      remoting.DesktopConnectedView.Mode.IT2ME) {
-    remoting.setMode(remoting.AppMode.CLIENT_SESSION_FINISHED_IT2ME);
-  } else {
-    remoting.setMode(remoting.AppMode.CLIENT_SESSION_FINISHED_ME2ME);
-  }
-  remoting.app.onDisconnected();
-  remoting.clientSession = null;
-  remoting.desktopConnectedView = null;
+  remoting.clientSession.disconnect(remoting.Error.NONE);
   console.log('Disconnected.');
 };
 
@@ -111,6 +103,7 @@
                                              onClientStateChange_);
   remoting.clientSession.cleanup();
   remoting.clientSession = null;
+  remoting.desktopConnectedView = null;
 }
 
 /**
diff --git a/remoting/webapp/crd/js/client_session.js b/remoting/webapp/crd/js/client_session.js
index 2459ba35..4873558 100644
--- a/remoting/webapp/crd/js/client_session.js
+++ b/remoting/webapp/crd/js/client_session.js
@@ -32,9 +32,9 @@
 remoting.ACCESS_TOKEN_RESEND_INTERVAL_MS = 15 * 60 * 1000;
 
 /**
+ * @param {remoting.Host} host The host to connect to.
  * @param {remoting.SignalStrategy} signalStrategy Signal strategy.
  * @param {HTMLElement} container Container element for the client view.
- * @param {string} hostDisplayName A human-readable name for the host.
  * @param {string} accessCode The IT2Me access code. Blank for Me2Me.
  * @param {function(boolean, function(string): void): void} fetchPin
  *     Called by Me2Me connections when a PIN needs to be obtained
@@ -45,11 +45,6 @@
  *     authentication token must be obtained.
  * @param {string} authenticationMethods Comma-separated list of
  *     authentication methods the client should attempt to use.
- * @param {string} hostId The host identifier for Me2Me, or empty for IT2Me.
- *     Mixed into authentication hashes for some authentication methods.
- * @param {string} hostJid The jid of the host to connect to.
- * @param {string} hostPublicKey The base64 encoded version of the host's
- *     public key.
  * @param {remoting.DesktopConnectedView.Mode} mode The mode of this connection.
  * @param {string} clientPairingId For paired Me2Me connections, the
  *     pairing id for this client, as issued by the host.
@@ -60,10 +55,9 @@
  * @constructor
  * @extends {base.EventSourceImpl}
  */
-remoting.ClientSession = function(signalStrategy, container, hostDisplayName,
-                                  accessCode, fetchPin, fetchThirdPartyToken,
-                                  authenticationMethods, hostId, hostJid,
-                                  hostPublicKey, mode, clientPairingId,
+remoting.ClientSession = function(host, signalStrategy, container, accessCode,
+                                  fetchPin, fetchThirdPartyToken,
+                                  authenticationMethods, mode, clientPairingId,
                                   clientPairedSecret, defaultRemapKeys) {
   /** @private */
   this.state_ = remoting.ClientSession.State.CREATED;
@@ -72,9 +66,7 @@
   this.error_ = remoting.Error.NONE;
 
   /** @private */
-  this.hostJid_ = hostJid;
-  /** @private */
-  this.hostPublicKey_ = hostPublicKey;
+  this.host_ = host;
   /** @private */
   this.accessCode_ = accessCode;
   /** @private */
@@ -84,15 +76,13 @@
   /** @private */
   this.authenticationMethods_ = authenticationMethods;
   /** @private */
-  this.hostId_ = hostId;
-  /** @private */
   this.clientPairingId_ = clientPairingId;
   /** @private */
   this.clientPairedSecret_ = clientPairedSecret;
 
   /** @private */
   this.uiHandler_ = new remoting.DesktopConnectedView(
-      this, container, hostDisplayName, hostId, mode, defaultRemapKeys,
+      this, container, this.host_, mode, defaultRemapKeys,
       this.onPluginInitialized_.bind(this));
   remoting.desktopConnectedView = this.uiHandler_;
 
@@ -111,7 +101,7 @@
                     remoting.SignalStrategy.State.CONNECTED);
   this.signalStrategy_.setIncomingStanzaCallback(
       this.onIncomingMessage_.bind(this));
-  remoting.formatIq.setJids(this.signalStrategy_.getJid(), hostJid);
+  remoting.formatIq.setJids(this.signalStrategy_.getJid(), host.jabberId);
 
   /**
    * Allow host-offline error reporting to be suppressed in situations where it
@@ -167,9 +157,9 @@
 };
 
 /**
-  @constructor
   @param {remoting.ClientSession.State} current
   @param {remoting.ClientSession.State} previous
+  @constructor
 */
 remoting.ClientSession.StateEvent = function(current, previous) {
   /** @type {remoting.ClientSession.State} */
@@ -244,6 +234,8 @@
 
   // Let the host know that we're interested in knowing whether or not it
   // rate limits desktop-resize requests.
+  // TODO(kelvinp): This has been supported since M-29.  Currently we only have
+  // <1000 users on M-29 or below. Remove this and the capability on the host.
   RATE_LIMIT_RESIZE_REQUESTS: 'rateLimitResizeRequests',
 
   // Indicates that host/client supports Google Drive integration, and that the
@@ -372,7 +364,7 @@
 remoting.ClientSession.prototype.cleanup = function() {
   this.sendIq_(
       '<cli:iq ' +
-          'to="' + this.hostJid_ + '" ' +
+          'to="' + this.host_.jabberId + '" ' +
           'type="set" ' +
           'id="session-terminate" ' +
           'xmlns:cli="jabber:client">' +
@@ -465,7 +457,7 @@
   console.log(remoting.timestamp(),
               remoting.formatIq.prettifyReceiveIq(formatted));
   this.plugin_.onIncomingIq(formatted);
-}
+};
 
 /**
  * @private
@@ -476,14 +468,14 @@
 
   /** @param {string} sharedSecret Shared secret. */
   function onSharedSecretReceived(sharedSecret) {
-    that.plugin_.connect(
-        that.hostJid_, that.hostPublicKey_, that.signalStrategy_.getJid(),
-        sharedSecret, that.authenticationMethods_, that.hostId_,
-        that.clientPairingId_, that.clientPairedSecret_);
-  };
+    that.plugin_.connect(that.host_.jabberId, that.host_.publicKey,
+                         that.signalStrategy_.getJid(), sharedSecret,
+                         that.authenticationMethods_, that.host_.hostId,
+                         that.clientPairingId_, that.clientPairedSecret_);
+  }
 
   this.getSharedSecret_(onSharedSecretReceived);
-}
+};
 
 /**
  * Gets shared secret to be used for connection.
@@ -603,6 +595,7 @@
 
 /**
  * Called when the client-host capabilities negotiation is complete.
+ * TODO(kelvinp): Move this function out of ClientSession.
  *
  * @param {!Array<string>} capabilities The set of capabilities negotiated
  *     between the client and host.
@@ -847,3 +840,16 @@
   }
   return false;
 };
+
+/**
+ * Enables or disables rendering of dirty regions for debugging.
+ * @param {boolean} enable True to enable rendering.
+ */
+remoting.ClientSession.prototype.enableDebugRegion = function(enable) {
+  if (enable) {
+    this.plugin_.setDebugDirtyRegionHandler(
+        this.uiHandler_.handleDebugRegion.bind(this.uiHandler_));
+  } else {
+    this.plugin_.setDebugDirtyRegionHandler(null);
+  }
+}
diff --git a/remoting/webapp/crd/js/crd_connect.js b/remoting/webapp/crd/js/crd_connect.js
index 10851faa..ea4f7a8 100644
--- a/remoting/webapp/crd/js/crd_connect.js
+++ b/remoting/webapp/crd/js/crd_connect.js
@@ -78,8 +78,8 @@
 
   /**
    * @param {string} tokenUrl Token-issue URL received from the host.
-   * @param {string} scope OAuth scope to request the token for.
    * @param {string} hostPublicKey Host public key (DER and Base64 encoded).
+   * @param {string} scope OAuth scope to request the token for.
    * @param {function(string, string):void} onThirdPartyTokenFetched Callback.
    */
   var fetchThirdPartyToken = function(
diff --git a/remoting/webapp/crd/js/desktop_connected_view.js b/remoting/webapp/crd/js/desktop_connected_view.js
index 140607e..f976d4e 100644
--- a/remoting/webapp/crd/js/desktop_connected_view.js
+++ b/remoting/webapp/crd/js/desktop_connected_view.js
@@ -27,9 +27,7 @@
 /**
  * @param {remoting.ClientSession} session
  * @param {HTMLElement} container
- * @param {string} hostDisplayName A human-readable name for the host.
- * @param {string} hostId The host identifier for Me2Me, or empty for IT2Me.
- *     Mixed into authentication hashes for some authentication methods.
+ * @param {remoting.Host} host
  * @param {remoting.DesktopConnectedView.Mode} mode The mode of this connection.
  * @param {string} defaultRemapKeys The default set of remap keys, to use
  *     when the client doesn't define any.
@@ -37,9 +35,8 @@
  * @constructor
  * @extends {base.EventSourceImpl}
  */
-remoting.DesktopConnectedView = function(session, container, hostDisplayName,
-                                    hostId, mode, defaultRemapKeys,
-                                    onInitialized) {
+remoting.DesktopConnectedView = function(session, container, host, mode,
+                                         defaultRemapKeys, onInitialized) {
   this.session_ = session;
 
   /** @type {HTMLElement} @private */
@@ -49,29 +46,14 @@
   this.plugin_ = null;
 
   /** @private */
-  this.hostDisplayName_ = hostDisplayName;
-
-  /** @private */
-  this.hostId_ = hostId;
+  this.host_ = host;
 
   /** @private */
   this.mode_ = mode;
 
-  /** @type {boolean} @private */
-  this.shrinkToFit_ = true;
-
-  /** @type {boolean} @private */
-  this.resizeToClient_ = true;
-
-  /** @type {number} @private */
-  this.desktopScale_ = 1.0;
-
   /** @type {string} @private */
   this.defaultRemapKeys_ = defaultRemapKeys;
 
-  /** @type {string} @private */
-  this.remapKeys_ = '';
-
   /**
    * Called when the UI is finished initializing.
    * @type {function(remoting.Error, remoting.ClientPlugin):void}
@@ -90,6 +72,10 @@
   this.notifyClientResolutionTimer_ = null;
 
   /** @type {Element} @private */
+  this.debugRegionContainer_ =
+      this.container_.querySelector('.debug-region-container');
+
+  /** @type {Element} @private */
   this.mouseCursorOverlay_ =
       this.container_.querySelector('.mouse-cursor-overlay');
 
@@ -97,8 +83,8 @@
   var img = this.mouseCursorOverlay_;
   /** @param {Event} event @private */
   this.updateMouseCursorPosition_ = function(event) {
-    img.style.top = event.y + 'px';
-    img.style.left = event.x + 'px';
+    img.style.top = event.offsetY + 'px';
+    img.style.left = event.offsetX + 'px';
   };
 
   /** @type {number?} @private */
@@ -108,12 +94,17 @@
   // and height of the client plugin so that bump-scrolling can be tested
   // without relying on the actual size of the host desktop.
   /** @type {number} @private */
-  this.pluginWidthForBumpScrollTesting = 0;
+  this.pluginWidthForBumpScrollTesting_ = 0;
   /** @type {number} @private */
-  this.pluginHeightForBumpScrollTesting = 0;
+  this.pluginHeightForBumpScrollTesting_ = 0;
 
   /** @type {remoting.VideoFrameRecorder} @private */
   this.videoFrameRecorder_ = null;
+
+  /** @private {base.Disposables} */
+  this.eventHooks_ = null;
+
+  this.defineEvents(base.values(remoting.DesktopConnectedView.Events));
 };
 
 base.extend(remoting.DesktopConnectedView, base.EventSourceImpl);
@@ -144,7 +135,7 @@
  * @return {string}
  */
 remoting.DesktopConnectedView.prototype.getHostDisplayName = function() {
-  return this.hostDisplayName_;
+  return this.host_.hostName;
 };
 
 /**
@@ -158,14 +149,14 @@
  * @return {boolean} True if shrink-to-fit is enabled; false otherwise.
  */
 remoting.DesktopConnectedView.prototype.getShrinkToFit = function() {
-  return this.shrinkToFit_;
+  return this.host_.options.shrinkToFit;
 };
 
 /**
  * @return {boolean} True if resize-to-client is enabled; false otherwise.
  */
 remoting.DesktopConnectedView.prototype.getResizeToClient = function() {
-  return this.resizeToClient_;
+  return this.host_.options.resizeToClient;
 };
 
 /**
@@ -188,15 +179,26 @@
 };
 
 /**
+ * @param {number} width
+ * @param {number} height
+ */
+remoting.DesktopConnectedView.prototype.setPluginSizeForBumpScrollTesting =
+    function(width, height) {
+  this.pluginWidthForBumpScrollTesting_ = width;
+  this.pluginHeightForBumpScrollTesting_ = height;
+}
+
+/**
  * Notifies the host of the client's current dimensions and DPI.
  * Also takes into account per-host scaling factor, if configured.
  * TODO: private
  */
 remoting.DesktopConnectedView.prototype.notifyClientResolution_ = function() {
   var clientArea = this.getClientArea_();
-  this.plugin_.notifyClientResolution(clientArea.width * this.desktopScale_,
-                                      clientArea.height * this.desktopScale_,
-                                      window.devicePixelRatio);
+  var desktopScale = this.host_.options.desktopScale;
+  this.plugin_.hostDesktop().resize(clientArea.width * desktopScale,
+                                    clientArea.height * desktopScale,
+                                    window.devicePixelRatio);
 };
 
 /**
@@ -213,44 +215,10 @@
   this.plugin_ = remoting.ClientPlugin.factory.createPlugin(
       this.getPluginContainer(),
       onExtensionMessage, requiredCapabilities);
-  remoting.HostSettings.load(this.hostId_,
-                             this.onHostSettingsLoaded_.bind(this));
-};
-
-/**
- * @param {Object<string|boolean|number>} options The current options for the
- *     host, or {} if this client has no saved settings for the host.
- * @private
- */
-remoting.DesktopConnectedView.prototype.onHostSettingsLoaded_ = function(
-    options) {
-  if (remoting.DesktopConnectedView.KEY_REMAP_KEYS in options &&
-      typeof(options[remoting.DesktopConnectedView.KEY_REMAP_KEYS]) ==
-          'string') {
-    this.remapKeys_ = /** @type {string} */
-        (options[remoting.DesktopConnectedView.KEY_REMAP_KEYS]);
-  }
-  if (remoting.DesktopConnectedView.KEY_RESIZE_TO_CLIENT in options &&
-      typeof(options[remoting.DesktopConnectedView.KEY_RESIZE_TO_CLIENT]) ==
-          'boolean') {
-    this.resizeToClient_ = /** @type {boolean} */
-        (options[remoting.DesktopConnectedView.KEY_RESIZE_TO_CLIENT]);
-  }
-  if (remoting.DesktopConnectedView.KEY_SHRINK_TO_FIT in options &&
-      typeof(options[remoting.DesktopConnectedView.KEY_SHRINK_TO_FIT]) ==
-          'boolean') {
-    this.shrinkToFit_ = /** @type {boolean} */
-        (options[remoting.DesktopConnectedView.KEY_SHRINK_TO_FIT]);
-  }
-  if (remoting.DesktopConnectedView.KEY_DESKTOP_SCALE in options &&
-      typeof(options[remoting.DesktopConnectedView.KEY_DESKTOP_SCALE]) ==
-          'number') {
-    this.desktopScale_ = /** @type {number} */
-        (options[remoting.DesktopConnectedView.KEY_DESKTOP_SCALE]);
-  }
-
-  /** @param {boolean} result */
-  this.plugin_.initialize(this.onPluginInitialized_.bind(this));
+  var that = this;
+  this.host_.options.load().then(function(){
+    that.plugin_.initialize(that.onPluginInitialized_.bind(that));
+  });
 };
 
 /**
@@ -293,10 +261,16 @@
     this.plugin_.allowMouseLock();
   }
 
-  this.plugin_.setDesktopShapeUpdateHandler(
-      this.onDesktopShapeChanged_.bind(this));
-  this.plugin_.setDesktopSizeUpdateHandler(
-      this.onDesktopSizeChanged_.bind(this));
+  base.dispose(this.eventHooks_);
+  var hostDesktop = this.plugin_.hostDesktop();
+  this.eventHooks_ = new base.Disposables(
+      new base.EventHook(
+        hostDesktop, remoting.HostDesktop.Events.sizeChanged,
+        this.onDesktopSizeChanged_.bind(this)),
+      new base.EventHook(
+        hostDesktop, remoting.HostDesktop.Events.shapeChanged,
+        this.onDesktopShapeChanged_.bind(this)));
+
   this.plugin_.setMouseCursorHandler(this.updateMouseCursorImage_.bind(this));
 
   this.onInitialized_(remoting.Error.NONE, this.plugin_);
@@ -310,11 +284,12 @@
  * @private
  */
 remoting.DesktopConnectedView.prototype.onDesktopSizeChanged_ = function() {
+  var desktop = this.plugin_.hostDesktop().getDimensions();
   console.log('desktop size changed: ' +
-              this.plugin_.getDesktopWidth() + 'x' +
-              this.plugin_.getDesktopHeight() +' @ ' +
-              this.plugin_.getDesktopXDpi() + 'x' +
-              this.plugin_.getDesktopYDpi() + ' DPI');
+              desktop.width + 'x' +
+              desktop.height +' @ ' +
+              desktop.xDpi + 'x' +
+              desktop.yDpi + ' DPI');
   this.updateDimensions();
   this.updateScrollbarVisibility();
 };
@@ -359,12 +334,8 @@
 
   // Defer notifying the host of the change until the window stops resizing, to
   // avoid overloading the control channel with notifications.
-  if (this.resizeToClient_) {
-    var kResizeRateLimitMs = 1000;
-    if (this.session_.hasCapability(
-        remoting.ClientSession.Capability.RATE_LIMIT_RESIZE_REQUESTS)) {
-      kResizeRateLimitMs = 250;
-    }
+  if (this.getResizeToClient()) {
+    var kResizeRateLimitMs = 250;
     var clientArea = this.getClientArea_();
     this.notifyClientResolutionTimer_ = window.setTimeout(
         this.notifyClientResolution_.bind(this),
@@ -401,6 +372,8 @@
  */
 remoting.DesktopConnectedView.prototype.removePlugin = function() {
   if (this.plugin_) {
+    base.dispose(this.eventHooks_);
+    this.eventHooks_ = null;
     this.plugin_.element().removeEventListener(
         'focus', this.callPluginGotFocus_, false);
     this.plugin_.element().removeEventListener(
@@ -446,7 +419,7 @@
       remoting.optionsMenu.setDesktopConnectedView(this);
     }
 
-    if (this.resizeToClient_) {
+    if (this.getResizeToClient()) {
       this.notifyClientResolution_();
     }
 
@@ -499,25 +472,17 @@
  */
 remoting.DesktopConnectedView.prototype.setScreenMode =
     function(shrinkToFit, resizeToClient) {
-  if (resizeToClient && !this.resizeToClient_) {
+  if (resizeToClient && !this.getResizeToClient()) {
     this.notifyClientResolution_();
   }
 
   // If enabling shrink, reset bump-scroll offsets.
-  var needsScrollReset = shrinkToFit && !this.shrinkToFit_;
+  var needsScrollReset = shrinkToFit && !this.getShrinkToFit();
 
-  this.shrinkToFit_ = shrinkToFit;
-  this.resizeToClient_ = resizeToClient;
+  this.host_.options.shrinkToFit = shrinkToFit;
+  this.host_.options.resizeToClient = resizeToClient;
   this.updateScrollbarVisibility();
-
-  if (this.hostId_ != '') {
-    var options = {};
-    options[remoting.DesktopConnectedView.KEY_SHRINK_TO_FIT] =
-        this.shrinkToFit_;
-    options[remoting.DesktopConnectedView.KEY_RESIZE_TO_CLIENT] =
-        this.resizeToClient_;
-    remoting.HostSettings.save(this.hostId_, options);
-  }
+  this.host_.options.save();
 
   this.updateDimensions();
   if (needsScrollReset) {
@@ -534,16 +499,14 @@
  */
 remoting.DesktopConnectedView.prototype.setDesktopScale = function(
     desktopScale) {
-  this.desktopScale_ = desktopScale;
+  this.host_.options.desktopScale = desktopScale;
 
   // onResize() will update the plugin size and scrollbars for the new
   // scaled plugin dimensions, and send a client resolution notification.
   this.onResize();
 
   // Save the new desktop scale setting.
-  var options = {};
-  options[remoting.DesktopConnectedView.KEY_DESKTOP_SCALE] = this.desktopScale_;
-  remoting.HostSettings.save(this.hostId_, options);
+  this.host_.options.save();
 };
 
 /**
@@ -718,12 +681,12 @@
   var stopX = { stop: false };
   var clientArea = this.getClientArea_();
   style.marginLeft = adjustMargin(style.marginLeft, dx, clientArea.width,
-      this.pluginWidthForBumpScrollTesting || plugin.clientWidth, stopX);
+      this.pluginWidthForBumpScrollTesting_ || plugin.clientWidth, stopX);
 
   var stopY = { stop: false };
   style.marginTop = adjustMargin(
       style.marginTop, dy, clientArea.height,
-      this.pluginHeightForBumpScrollTesting || plugin.clientHeight, stopY);
+      this.pluginHeightForBumpScrollTesting_ || plugin.clientHeight, stopY);
   return stopX.stop && stopY.stop;
 };
 
@@ -734,19 +697,19 @@
  * @return {void} Nothing.
  */
 remoting.DesktopConnectedView.prototype.updateDimensions = function() {
-  if (this.plugin_.getDesktopWidth() == 0 ||
-      this.plugin_.getDesktopHeight() == 0) {
+  var desktopSize = this.plugin_.hostDesktop().getDimensions();
+
+  if (desktopSize.width === 0 ||
+      desktopSize.height === 0) {
     return;
   }
 
-  var desktopSize = { width: this.plugin_.getDesktopWidth(),
-                      height: this.plugin_.getDesktopHeight() };
-  var desktopDpi = { x: this.plugin_.getDesktopXDpi(),
-                     y: this.plugin_.getDesktopYDpi() };
+  var desktopDpi = { x: desktopSize.xDpi,
+                     y: desktopSize.yDpi };
   var newSize = remoting.DesktopConnectedView.choosePluginSize(
       this.getClientArea_(), window.devicePixelRatio,
-      desktopSize, desktopDpi, this.desktopScale_,
-      remoting.fullscreen.isActive(), this.shrinkToFit_);
+      desktopSize, desktopDpi, this.host_.options.desktopScale,
+      remoting.fullscreen.isActive(), this.getShrinkToFit());
 
   // Resize the plugin if necessary.
   console.log('plugin dimensions:' + newSize.width + 'x' + newSize.height);
@@ -906,19 +869,20 @@
 
   var needsVerticalScroll = false;
   var needsHorizontalScroll = false;
-  if (!this.shrinkToFit_) {
+  if (!this.getShrinkToFit()) {
     // Determine whether or not horizontal or vertical scrollbars are
     // required, taking into account their width.
     var clientArea = this.getClientArea_();
-    needsVerticalScroll = clientArea.height < this.plugin_.getDesktopHeight();
-    needsHorizontalScroll = clientArea.width < this.plugin_.getDesktopWidth();
+    var desktopSize = this.plugin_.hostDesktop().getDimensions();
+    needsVerticalScroll = clientArea.height < desktopSize.height;
+    needsHorizontalScroll = clientArea.width < desktopSize.width;
     var kScrollBarWidth = 16;
     if (needsHorizontalScroll && !needsVerticalScroll) {
       needsVerticalScroll =
-          clientArea.height - kScrollBarWidth < this.plugin_.getDesktopHeight();
+          clientArea.height - kScrollBarWidth < desktopSize.height;
     } else if (!needsHorizontalScroll && needsVerticalScroll) {
       needsHorizontalScroll =
-          clientArea.width - kScrollBarWidth < this.plugin_.getDesktopWidth();
+          clientArea.width - kScrollBarWidth < desktopSize.width;
     }
   }
 
@@ -942,13 +906,11 @@
 remoting.DesktopConnectedView.prototype.setRemapKeys = function(remappings) {
   // Cancel any existing remappings and apply the new ones.
   this.applyRemapKeys_(false);
-  this.remapKeys_ = remappings;
+  this.host_.options.remapKeys = remappings;
   this.applyRemapKeys_(true);
 
   // Save the new remapping setting.
-  var options = {};
-  options[remoting.DesktopConnectedView.KEY_REMAP_KEYS] = this.remapKeys_;
-  remoting.HostSettings.save(this.hostId_, options);
+  this.host_.options.save();
 };
 
 /**
@@ -957,7 +919,7 @@
  * @param {boolean} apply True to apply remappings, false to cancel them.
  */
 remoting.DesktopConnectedView.prototype.applyRemapKeys_ = function(apply) {
-  var remapKeys = this.remapKeys_;
+  var remapKeys = this.host_.options.remapKeys;
   if (remapKeys == '') {
     remapKeys = this.defaultRemapKeys_;
     if (remapKeys == '') {
@@ -1095,3 +1057,28 @@
   }
   return false;
 };
+
+/**
+ * Handles dirty region debug messages.
+ *
+ * @param {{rects:Array<Array<number>>}} region Dirty region of the latest
+ *     frame.
+ */
+remoting.DesktopConnectedView.prototype.handleDebugRegion = function(region) {
+  while (this.debugRegionContainer_.firstChild) {
+    this.debugRegionContainer_.removeChild(
+        this.debugRegionContainer_.firstChild);
+  }
+  if (region.rects) {
+    var rects = region.rects;
+    for (var i = 0; i < rects.length; ++i) {
+      var rect = document.createElement('div');
+      rect.classList.add('debug-region-rect');
+      rect.style.left = rects[i][0] + 'px';
+      rect.style.top = rects[i][1] +'px';
+      rect.style.width = rects[i][2] +'px';
+      rect.style.height = rects[i][3] + 'px';
+      this.debugRegionContainer_.appendChild(rect);
+    }
+  }
+}
diff --git a/remoting/webapp/crd/js/host.js b/remoting/webapp/crd/js/host.js
index 15014c7d..f43d69c 100644
--- a/remoting/webapp/crd/js/host.js
+++ b/remoting/webapp/crd/js/host.js
@@ -12,9 +12,15 @@
 /** @suppress {duplicate} */
 var remoting = remoting || {};
 
+(function() {
+
+'use strict';
+
 /**
  * Note that the object has more fields than are detailed below--these
  * are just the ones that we refer to directly.
+ *
+ * TODO(kelvinp):Make fields private and expose them via getters.
  * @constructor
  */
 remoting.Host = function() {
@@ -36,6 +42,49 @@
   this.updatedTime = '';
   /** @type {string} */
   this.hostOfflineReason = '';
+  /** @type {remoting.Host.Options} */
+  this.options = new remoting.Host.Options(this.hostId);
+};
+
+/**
+ * @constructor
+ * @param {string} hostId
+ * @struct
+ */
+remoting.Host.Options = function(hostId) {
+  /** @private */
+  this.hostId_ = hostId;
+  /** @type {boolean} */
+  this.shrinkToFit = false;
+  /** @type {boolean} */
+  this.resizeToClient = false;
+  /** @type {string} */
+  this.remapKeys = '';
+  /** @type {number} */
+  this.desktopScale = 1;
+};
+
+remoting.Host.Options.prototype.save = function() {
+  // TODO(kelvinp): Migrate pairingInfo to use this class as well and get rid of
+  // remoting.HostSettings.
+  remoting.HostSettings.save(this.hostId_, this);
+};
+
+
+/** @return {Promise} A promise that resolves when the settings are loaded. */
+remoting.Host.Options.prototype.load = function() {
+  var that = this;
+  return base.Promise.as(remoting.HostSettings.load, [this.hostId_]).then(
+    /**
+     * @param {Object.<string|boolean|number>} options
+     */
+    function(options) {
+      that.resizeToClient =
+          getBooleanAttr(options, 'resizeToClient', false);
+      that.shrinkToFit = getBooleanAttr(options, 'shrinkToFit', false);
+      that.desktopScale = getNumberAttr(options, 'desktopScale', 1);
+      that.remapKeys = getStringAttr(options, 'remapKeys', '');
+    });
 };
 
 /**
@@ -62,3 +111,5 @@
   }
   return (parseInt(webappVersion, 10) - hostMajorVersion) > 1;
 };
+
+})();
diff --git a/remoting/webapp/crd/js/host_controller.js b/remoting/webapp/crd/js/host_controller.js
index e4c7a781..b5f8516 100644
--- a/remoting/webapp/crd/js/host_controller.js
+++ b/remoting/webapp/crd/js/host_controller.js
@@ -12,13 +12,12 @@
   this.hostDaemonFacade_ = this.createDaemonFacade_();
 };
 
-// Note that the values in the enums below are copied from
-// daemon_controller.h and must be kept in sync.
+// The values in the enums below are duplicated in daemon_controller.h except
+// for NOT_INSTALLED.
 /** @enum {number} */
 remoting.HostController.State = {
-  NOT_IMPLEMENTED: -1,
-  NOT_INSTALLED: 0,
-  INSTALLING: 1,
+  NOT_IMPLEMENTED: 0,
+  NOT_INSTALLED: 1,
   STOPPED: 2,
   STARTING: 3,
   STARTED: 4,
diff --git a/remoting/webapp/crd/js/host_desktop.js b/remoting/webapp/crd/js/host_desktop.js
new file mode 100644
index 0000000..c18f4896
--- /dev/null
+++ b/remoting/webapp/crd/js/host_desktop.js
@@ -0,0 +1,51 @@
+// 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.
+
+/**
+ * @fileoverview
+ * Interface abstracting the functionality of the HostDesktop.
+ */
+
+var remoting = remoting || {};
+
+(function() {
+
+'use strict';
+
+/**
+ * @interface
+ * @extends {base.EventSource}
+ */
+remoting.HostDesktop = function() {};
+
+/** @return {boolean} Whether the host supports desktop resizing. */
+remoting.HostDesktop.prototype.isResizable = function() {};
+
+/** @enum {string} */
+remoting.HostDesktop.Events = {
+  // Fired when the size of the host desktop changes with the desktop dimensions
+  //  {{width:number, height:number, xDpi:number, yDpi:number}}
+  sizeChanged: 'sizeChanged',
+  // Fired when the shape of the host desktop changes with an array of
+  // rectangles of desktop shapes as the event data.
+  //   Array<{left:number, top:number, width:number, height:number}>
+  shapeChanged: 'shapeChanged'
+};
+
+/**
+ * @return {{width:number, height:number, xDpi:number, yDpi:number}}
+ *  The dimensions and DPI settings of the host desktop.
+ */
+remoting.HostDesktop.prototype.getDimensions = function() {};
+
+/**
+ * Resize the desktop of the host to |width|, |height| and |deviceScale|.
+ *
+ * @param {number} width The width of the desktop in DIPs.
+ * @param {number} height The height of the desktop in DIPs.
+ * @param {number} deviceScale
+ */
+remoting.HostDesktop.prototype.resize = function(width, height, deviceScale) {};
+
+})();
diff --git a/remoting/webapp/crd/js/host_list_api_impl.js b/remoting/webapp/crd/js/host_list_api_impl.js
index 5cf4a2d..40489bda 100644
--- a/remoting/webapp/crd/js/host_list_api_impl.js
+++ b/remoting/webapp/crd/js/host_list_api_impl.js
@@ -109,7 +109,21 @@
       console.error('Invalid "hosts" response from server.');
       onError(remoting.Error.UNEXPECTED);
     } else {
-      var hosts = response.data.items || [];
+      var items = response.data.items || [];
+      var hosts = items.map(
+        function(/** Object */ item) {
+          var host = new remoting.Host();
+          host.hostName = getStringAttr(item, 'hostName', '');
+          host.hostId = getStringAttr(item, 'hostId', '');
+          host.status = getStringAttr(item, 'status', '');
+          host.jabberId = getStringAttr(item, 'jabberId', '');
+          host.publicKey = getStringAttr(item, 'publicKey', '');
+          host.hostVersion = getStringAttr(item, 'hostVersion', '');
+          host.tokenUrlPatterns = getArrayAttr(item, 'tokenUrlPatterns', []);
+          host.updatedTime = getStringAttr(item, 'updatedTime', '');
+          host.hostOfflineReason = getStringAttr(item, 'hostOfflineReason', '');
+          return host;
+      });
       onDone(hosts);
     }
   } else {
diff --git a/remoting/webapp/crd/js/host_setup_dialog.js b/remoting/webapp/crd/js/host_setup_dialog.js
index fc258d83..a97d708 100644
--- a/remoting/webapp/crd/js/host_setup_dialog.js
+++ b/remoting/webapp/crd/js/host_setup_dialog.js
@@ -229,7 +229,7 @@
 
   var installed =
       state != remoting.HostController.State.NOT_INSTALLED &&
-      state != remoting.HostController.State.INSTALLING;
+      state != remoting.HostController.State.UNKNOWN;
 
   // Skip the installation step when the host is already installed.
   if (installed) {
@@ -373,7 +373,7 @@
   var onHostState = function(state) {
     var installed =
         state != remoting.HostController.State.NOT_INSTALLED &&
-        state != remoting.HostController.State.INSTALLING;
+        state != remoting.HostController.State.UNKNOWN;
 
     if (installed) {
       that.flow_.switchToNextStep();
diff --git a/remoting/webapp/crd/js/session_connector.js b/remoting/webapp/crd/js/session_connector.js
index f0d4f88..df6cf34 100644
--- a/remoting/webapp/crd/js/session_connector.js
+++ b/remoting/webapp/crd/js/session_connector.js
@@ -32,7 +32,7 @@
  * @param {function(string, string, string,
  *                  function(string, string): void): void}
  *     fetchThirdPartyToken Function to obtain a token from a third party
- *     authenticaiton server.
+ *     authentication server.
  * @param {string} clientPairingId The client id issued by the host when
  *     this device was paired, if it is already paired.
  * @param {string} clientPairedSecret The shared secret issued by the host when
diff --git a/remoting/webapp/crd/js/session_connector_impl.js b/remoting/webapp/crd/js/session_connector_impl.js
index dfe31257..feab2b7 100644
--- a/remoting/webapp/crd/js/session_connector_impl.js
+++ b/remoting/webapp/crd/js/session_connector_impl.js
@@ -117,15 +117,6 @@
  */
 remoting.SessionConnectorImpl.prototype.reset = function() {
   /**
-   * String used to identify the host to which to connect. For IT2Me, this is
-   * the first 7 digits of the access code; for Me2Me it is the host identifier.
-   *
-   * @type {string}
-   * @private
-   */
-  this.hostId_ = '';
-
-  /**
    * For paired connections, the client id of this device, issued by the host.
    *
    * @type {string}
@@ -152,16 +143,10 @@
   this.passPhrase_ = '';
 
   /**
-   * @type {string}
+   * @type {remoting.Host}
    * @private
    */
-  this.hostJid_ = '';
-
-  /**
-   * @type {string}
-   * @private
-   */
-  this.hostPublicKey_ = '';
+  this.host_ = null;
 
   /**
    * @type {boolean}
@@ -194,17 +179,7 @@
    * @private
    */
   this.fetchThirdPartyToken_ = function(
-      tokenUrl, scope, onThirdPartyTokenFetched) {};
-
-  /**
-   * Host 'name', as displayed in the client tool-bar. For a Me2Me connection,
-   * this is the name of the host; for an IT2Me connection, it is the email
-   * address of the person sharing their computer.
-   *
-   * @type {string}
-   * @private
-   */
-  this.hostDisplayName_ = '';
+      tokenUrl, hostPublicKey, scope, onThirdPartyTokenFetched) {};
 };
 
 /**
@@ -219,7 +194,7 @@
  * @param {function(string, string, string,
  *                  function(string, string): void): void}
  *     fetchThirdPartyToken Function to obtain a token from a third party
- *     authenticaiton server.
+ *     authentication server.
  * @param {string} clientPairingId The client id issued by the host when
  *     this device was paired, if it is already paired.
  * @param {string} clientPairedSecret The shared secret issued by the host when
@@ -231,10 +206,8 @@
              clientPairingId, clientPairedSecret) {
   this.connectionMode_ = remoting.DesktopConnectedView.Mode.ME2ME;
   this.logHostOfflineErrors_ = false;
-  this.connectMe2MeInternal_(
-      host.hostId, host.jabberId, host.publicKey, host.hostName,
-      fetchPin, fetchThirdPartyToken,
-      clientPairingId, clientPairedSecret);
+  this.connectMe2MeInternal_(host, fetchPin, fetchThirdPartyToken,
+                             clientPairingId, clientPairedSecret);
 };
 
 /**
@@ -249,10 +222,8 @@
 remoting.SessionConnectorImpl.prototype.retryConnectMe2Me = function(host) {
   this.connectionMode_ = remoting.DesktopConnectedView.Mode.ME2ME;
   this.logHostOfflineErrors_ = true;
-  this.connectMe2MeInternal_(
-      host.hostId, host.jabberId, host.publicKey, host.hostName,
-      this.fetchPin_, this.fetchThirdPartyToken_,
-      this.clientPairingId_, this.clientPairedSecret_);
+  this.connectMe2MeInternal_(host, this.fetchPin_, this.fetchThirdPartyToken_,
+                             this.clientPairingId_, this.clientPairedSecret_);
 };
 
 /**
@@ -269,9 +240,7 @@
     function(host, fetchThirdPartyToken) {
   this.connectionMode_ = remoting.DesktopConnectedView.Mode.APP_REMOTING;
   this.logHostOfflineErrors_ = true;
-  this.connectMe2MeInternal_(
-      host.hostId, host.jabberId, host.publicKey, host.hostName,
-      function() {}, fetchThirdPartyToken, '', '');
+  this.connectMe2MeInternal_(host, function() {}, fetchThirdPartyToken, '', '');
 };
 
 /**
@@ -289,16 +258,13 @@
 /**
  * Initiate a Me2Me connection.
  *
- * @param {string} hostId ID of the Me2Me host.
- * @param {string} hostJid XMPP JID of the host.
- * @param {string} hostPublicKey Public Key of the host.
- * @param {string} hostDisplayName Display name (friendly name) of the host.
+ * @param {remoting.Host} host the Host to connect to.
  * @param {function(boolean, function(string):void):void} fetchPin Function to
  *     interactively obtain the PIN from the user.
  * @param {function(string, string, string,
  *                  function(string, string): void): void}
  *     fetchThirdPartyToken Function to obtain a token from a third party
- *     authenticaiton server.
+ *     authentication server.
  * @param {string} clientPairingId The client id issued by the host when
  *     this device was paired, if it is already paired.
  * @param {string} clientPairedSecret The shared secret issued by the host when
@@ -307,22 +273,18 @@
  * @private
  */
 remoting.SessionConnectorImpl.prototype.connectMe2MeInternal_ =
-    function(hostId, hostJid, hostPublicKey, hostDisplayName,
-             fetchPin, fetchThirdPartyToken,
+    function(host, fetchPin, fetchThirdPartyToken,
              clientPairingId, clientPairedSecret) {
   // Cancel any existing connect operation.
   this.cancel();
 
-  this.hostId_ = hostId;
-  this.hostJid_ = hostJid;
-  this.hostPublicKey_ = hostPublicKey;
+  this.host_ = host;
   this.fetchPin_ = fetchPin;
   this.fetchThirdPartyToken_ = fetchThirdPartyToken;
-  this.hostDisplayName_ = hostDisplayName;
   this.updatePairingInfo(clientPairingId, clientPairedSecret);
 
   this.connectSignaling_();
-}
+};
 
 /**
  * Initiate an IT2Me connection.
@@ -344,11 +306,11 @@
     return;
   }
 
-  this.hostId_ = normalizedAccessCode.substring(0, kSupportIdLen);
+  var hostId = normalizedAccessCode.substring(0, kSupportIdLen);
   this.passPhrase_ = normalizedAccessCode;
   this.connectionMode_ = remoting.DesktopConnectedView.Mode.IT2ME;
-  remoting.identity.callWithToken(this.connectIT2MeWithToken_.bind(this),
-                                  this.onError_);
+  remoting.identity.callWithToken(
+      this.connectIT2MeWithToken_.bind(this, hostId), this.onError_);
 };
 
 /**
@@ -362,10 +324,9 @@
     return;
   }
   this.logHostOfflineErrors_ = false;
-  this.connectMe2MeInternal_(
-      this.hostId_, this.hostJid_, this.hostPublicKey_, this.hostDisplayName_,
-      this.fetchPin_, this.fetchThirdPartyToken_,
-      this.clientPairingId_, this.clientPairedSecret_);
+  this.connectMe2MeInternal_(this.host_, this.fetchPin_,
+                             this.fetchThirdPartyToken_, this.clientPairingId_,
+                             this.clientPairedSecret_);
 };
 
 /**
@@ -398,7 +359,7 @@
  * @return {string}
  */
 remoting.SessionConnectorImpl.prototype.getHostId = function() {
-  return this.hostId_;
+  return this.host_.hostId;
 };
 
 /**
@@ -447,7 +408,7 @@
   switch (state) {
     case remoting.SignalStrategy.State.CONNECTED:
       // Proceed only if the connection hasn't been canceled.
-      if (this.hostJid_) {
+      if (this.host_.jabberId) {
         this.createSession_();
       }
       break;
@@ -461,17 +422,18 @@
 /**
  * Continue an IT2Me connection once an access token has been obtained.
  *
+ * @param {string} hostId
  * @param {string} token An OAuth2 access token.
  * @return {void} Nothing.
  * @private
  */
 remoting.SessionConnectorImpl.prototype.connectIT2MeWithToken_ =
-    function(token) {
+    function(hostId, token) {
   // Resolve the host id to get the host JID.
   this.pendingXhr_ = remoting.xhr.get(
       remoting.settings.DIRECTORY_API_BASE_URL + '/support-hosts/' +
-          encodeURIComponent(this.hostId_),
-      this.onIT2MeHostInfo_.bind(this),
+          encodeURIComponent(hostId),
+      this.onIT2MeHostInfo_.bind(this, hostId),
       '',
       { 'Authorization': 'OAuth ' + token });
 };
@@ -479,19 +441,23 @@
 /**
  * Continue an IT2Me connection once the host JID has been looked up.
  *
+ * @param {string} hostId
  * @param {XMLHttpRequest} xhr The server response to the support-hosts query.
  * @return {void} Nothing.
  * @private
  */
-remoting.SessionConnectorImpl.prototype.onIT2MeHostInfo_ = function(xhr) {
+remoting.SessionConnectorImpl.prototype.onIT2MeHostInfo_ =
+    function(hostId, xhr) {
   this.pendingXhr_ = null;
   if (xhr.status == 200) {
     var host = /** @type {{data: {jabberId: string, publicKey: string}}} */
         (base.jsonParseSafe(xhr.responseText));
     if (host && host.data && host.data.jabberId && host.data.publicKey) {
-      this.hostJid_ = host.data.jabberId;
-      this.hostPublicKey_ = host.data.publicKey;
-      this.hostDisplayName_ = this.hostJid_.split('/')[0];
+      this.host_ = new remoting.Host();
+      this.host_.hostId = hostId;
+      this.host_.jabberId = host.data.jabberId;
+      this.host_.publicKey = host.data.publicKey;
+      this.host_.hostName = host.data.jabberId.split('/')[0];
       this.connectSignaling_();
       return;
     } else {
@@ -517,9 +483,8 @@
   var authenticationMethods =
      'third_party,spake2_pair,spake2_hmac,spake2_plain';
   this.clientSession_ = new remoting.ClientSession(
-      this.signalStrategy_, this.clientContainer_, this.hostDisplayName_,
-      this.passPhrase_, this.fetchPin_, this.fetchThirdPartyToken_,
-      authenticationMethods, this.hostId_, this.hostJid_, this.hostPublicKey_,
+      this.host_, this.signalStrategy_, this.clientContainer_, this.passPhrase_,
+      this.fetchPin_, this.fetchThirdPartyToken_, authenticationMethods,
       this.connectionMode_, this.clientPairingId_, this.clientPairedSecret_,
       this.defaultRemapKeys_);
   this.clientSession_.logHostOfflineErrors(this.logHostOfflineErrors_);
diff --git a/remoting/webapp/js_proto/dom_proto.js b/remoting/webapp/js_proto/dom_proto.js
index a81cdc2..14d8abb3 100644
--- a/remoting/webapp/js_proto/dom_proto.js
+++ b/remoting/webapp/js_proto/dom_proto.js
@@ -34,9 +34,15 @@
 Element.prototype.requestPointerLock = function() {};
 
 /** @type {boolean} */
+Element.prototype.disabled;
+
+/** @type {boolean} */
 Element.prototype.hidden;
 
 /** @type {string} */
+Element.prototype.innerText;
+
+/** @type {string} */
 Element.prototype.localName;
 
 /** @type {string} */
diff --git a/remoting/webapp/js_proto/test_proto.js b/remoting/webapp/js_proto/test_proto.js
new file mode 100644
index 0000000..41124ba9
--- /dev/null
+++ b/remoting/webapp/js_proto/test_proto.js
@@ -0,0 +1,37 @@
+// 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.
+
+// This file contains various hacks needed to inform JSCompiler of various
+// test-specific properties and methods. It is used only with JSCompiler to
+// verify the type-correctness of our code.
+
+/** @suppress {duplicate} */
+var browserTest = browserTest || {};
+
+/** @suppress {duplicate} */
+var sinon = sinon || {};
+
+/** @interface */
+browserTest.TestableClass = function() {};
+
+/** @param {*} data */
+browserTest.TestableClass.prototype.run = function(data) {};
+
+sinon.spy = function() {};
+
+/** @constructor */
+window.DomAutomationControllerMessage = function() {
+  /** @type {boolean} */
+  this.succeeded = false;
+  /** @type {string} */
+  this.error_message = '';
+  /** @type {string} */
+  this.stack_trace = '';
+};
+
+/** @constructor */
+window.DomAutomationController = function() {};
+
+/** @param {string} json A stringified DomAutomationControllerMessage. */
+window.DomAutomationController.prototype.send = function(json) {};
diff --git a/remoting/webapp/unittests/chrome_mocks.js b/remoting/webapp/unittests/chrome_mocks.js
index 6bdd7b5c..be55785 100644
--- a/remoting/webapp/unittests/chrome_mocks.js
+++ b/remoting/webapp/unittests/chrome_mocks.js
@@ -7,18 +7,23 @@
 
 Entry = function() {};
 
-(function(scope){
-
 var chromeMocks = {};
 
+(function(){
+
+/**
+ * @constructor
+ */
 chromeMocks.Event = function() {
   this.listeners_ = [];
 };
 
+/** @param {Function} callback */
 chromeMocks.Event.prototype.addListener = function(callback) {
   this.listeners_.push(callback);
 };
 
+/** @param {Function} callback */
 chromeMocks.Event.prototype.removeListener = function(callback) {
   for (var i = 0; i < this.listeners_.length; i++) {
     if (this.listeners_[i] === callback) {
@@ -28,27 +33,49 @@
   }
 };
 
+/**
+ * @param {...*} var_args
+ * @return {void}
+ */
 chromeMocks.Event.prototype.mock$fire = function(var_args) {
   var params = Array.prototype.slice.call(arguments);
-  this.listeners_.forEach(function(listener){
-    listener.apply(null, params);
-  });
+  this.listeners_.forEach(
+      /** @param {Function} listener */
+      function(listener){
+        listener.apply(null, params);
+      });
 };
 
+/** @type {Object} */
 chromeMocks.runtime = {};
 
+/** @constructor */
 chromeMocks.runtime.Port = function() {
   this.onMessage = new chromeMocks.Event();
   this.onDisconnect = new chromeMocks.Event();
 
+  /** @type {string} */
   this.name = '';
+
+  /** @type {chrome.runtime.MessageSender} */
   this.sender = null;
 };
 
 chromeMocks.runtime.Port.prototype.disconnect = function() {};
-chromeMocks.runtime.Port.prototype.postMessage = function() {};
 
+/**
+ * @param {Object} message
+ */
+chromeMocks.runtime.Port.prototype.postMessage = function(message) {};
+
+/** @type {chromeMocks.Event} */
 chromeMocks.runtime.onMessage = new chromeMocks.Event();
+
+/**
+ * @param {string?} extensionId
+ * @param {*} message
+ * @param {function(*)=} responseCallback
+ */
 chromeMocks.runtime.sendMessage = function(extensionId, message,
                                            responseCallback) {
   base.debug.assert(
@@ -62,16 +89,24 @@
   });
 };
 
+/** @type {string} */
 chromeMocks.runtime.id = 'extensionId';
 
+/** @type {Object} */
 chromeMocks.storage = {};
 
 // Sample implementation of chrome.StorageArea according to
 // https://developer.chrome.com/apps/storage#type-StorageArea
+/** @constructor */
 chromeMocks.StorageArea = function() {
+  /** @type {Object} */
   this.storage_ = {};
 };
 
+/**
+ * @param {!Object} keys
+ * @return {Array<string>}
+ */
 function getKeys(keys) {
   if (typeof keys === 'string') {
     return [keys];
@@ -81,6 +116,10 @@
   return [];
 }
 
+/**
+ * @param {!Object} keys
+ * @param {Function} onDone
+ */
 chromeMocks.StorageArea.prototype.get = function(keys, onDone) {
   if (!keys) {
     onDone(base.deepCopy(this.storage_));
@@ -88,30 +127,39 @@
   }
 
   var result = (typeof keys === 'object') ? keys : {};
-  getKeys(keys).forEach(function(key) {
-    if (key in this.storage_) {
-      result[key] = base.deepCopy(this.storage_[key]);
-    }
-  }, this);
+  getKeys(keys).forEach(
+      /** @param {string} key */
+      function(key) {
+        if (key in this.storage_) {
+          result[key] = base.deepCopy(this.storage_[key]);
+        }
+      }, this);
   onDone(result);
 };
 
+/** @param {Object} value */
 chromeMocks.StorageArea.prototype.set = function(value) {
   for (var key in value) {
     this.storage_[key] = base.deepCopy(value[key]);
   }
 };
 
+/**
+ * @param {!Object} keys
+ */
 chromeMocks.StorageArea.prototype.remove = function(keys) {
-  getKeys(keys).forEach(function(key) {
-    delete this.storage_[key];
-  }, this);
+  getKeys(keys).forEach(
+      /** @param {string} key */
+      function(key) {
+        delete this.storage_[key];
+      }, this);
 };
 
 chromeMocks.StorageArea.prototype.clear = function() {
   this.storage_ = null;
 };
 
+/** @type {chromeMocks.StorageArea} */
 chromeMocks.storage.local = new chromeMocks.StorageArea();
 
 var originals_ = null;
@@ -144,6 +192,4 @@
   originals_ = null;
 };
 
-scope.chromeMocks = chromeMocks;
-
-})(window);
+})();
diff --git a/remoting/webapp/unittests/mock_signal_strategy.js b/remoting/webapp/unittests/mock_signal_strategy.js
index 3437010..46e27f36 100644
--- a/remoting/webapp/unittests/mock_signal_strategy.js
+++ b/remoting/webapp/unittests/mock_signal_strategy.js
@@ -7,11 +7,22 @@
 /** @suppress {duplicate} */
 var remoting = remoting || {};
 
+
+/**
+ * @param {string} jid
+ * @param {remoting.SignalStrategy.Type} type
+ *
+ * @implements {remoting.SignalStrategy}
+ * @constructor
+ */
 remoting.MockSignalStrategy = function(jid, type) {
   this.jid_ = (jid != undefined) ? jid : "jid@example.com";
   this.type_ = (type != undefined) ? type : remoting.SignalStrategy.Type.XMPP;
   this.onStateChangedCallback_ = null;
-  this.state_ = null;
+
+  /** @type {remoting.SignalStrategy.State} */
+  this.state_ = remoting.SignalStrategy.State.NOT_CONNECTED;
+
   this.onIncomingStanzaCallback_ = function() {};
   this.dispose = sinon.spy();
   this.connect = sinon.spy();
@@ -19,11 +30,19 @@
   this.sendConnectionSetupResults = sinon.spy();
 };
 
+/**
+ * @param {function(remoting.SignalStrategy.State):void} onStateChangedCallback
+ *   Callback to call on state change.
+ */
 remoting.MockSignalStrategy.prototype.setStateChangedCallback = function(
     onStateChangedCallback) {
   this.onStateChangedCallback_ = onStateChangedCallback;
 };
 
+/**
+ * @param {?function(Element):void} onIncomingStanzaCallback Callback to call on
+ *     incoming messages.
+ */
 remoting.MockSignalStrategy.prototype.setIncomingStanzaCallback =
     function(onIncomingStanzaCallback) {
   this.onIncomingStanzaCallback_ =
@@ -31,28 +50,30 @@
                                : function() {};
 };
 
-remoting.MockSignalStrategy.prototype.getState = function(message) {
+/** @return {remoting.SignalStrategy.State} */
+remoting.MockSignalStrategy.prototype.getState = function() {
   return this.state_;
 };
 
-remoting.MockSignalStrategy.prototype.getError = function(message) {
-  return remoting.Error.UNKNOWN;
+/** @return {remoting.Error} */
+remoting.MockSignalStrategy.prototype.getError = function() {
+  return remoting.Error.NONE;
 };
 
-remoting.MockSignalStrategy.prototype.getJid = function(message) {
+/** @return {string} */
+remoting.MockSignalStrategy.prototype.getJid = function() {
   return this.jid_;
 };
 
-remoting.MockSignalStrategy.prototype.getType = function(message) {
+/** @return {remoting.SignalStrategy.Type} */
+remoting.MockSignalStrategy.prototype.getType = function() {
   return this.type_;
 };
 
+/**
+ * @param {remoting.SignalStrategy.State} state
+ */
 remoting.MockSignalStrategy.prototype.setStateForTesting = function(state) {
   this.state_ = state;
   this.onStateChangedCallback_(state);
 };
-
-remoting.MockSignalStrategy.prototype.dispose = function() {
-  this.state_ = remoting.SignalStrategy.State.CLOSED;
-  this.onStateChangedCallback_(this.state_);
-};
diff --git a/rlz/BUILD.gn b/rlz/BUILD.gn
index f889137..9c44dad 100644
--- a/rlz/BUILD.gn
+++ b/rlz/BUILD.gn
@@ -55,6 +55,9 @@
     "win/lib/rlz_value_store_registry.h",
   ]
 
+  # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+  configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
+
   public_configs = [ ":rlz_config" ]
 
   deps = [
@@ -73,9 +76,6 @@
       "mac/lib/rlz_value_store_mac.h",
     ]
     set_sources_assignment_filter(sources_assignment_filter)
-  } else if (is_win) {
-    # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
-    cflags = [ "/wd4267" ]
   }
 }
 
@@ -106,6 +106,9 @@
     "win/lib/machine_deal_test.cc",
   ]
 
+  # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+  configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
+
   deps = [
     ":rlz_lib",
     ":test_support",
@@ -116,11 +119,6 @@
     "//testing/gtest",
     "//third_party/zlib",
   ]
-
-  if (is_win) {
-    # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
-    cflags = [ "/wd4267" ]
-  }
 }
 
 executable("rlz_id") {
diff --git a/sandbox/linux/BUILD.gn b/sandbox/linux/BUILD.gn
index 8b18a3a..c4ce4fc 100644
--- a/sandbox/linux/BUILD.gn
+++ b/sandbox/linux/BUILD.gn
@@ -11,7 +11,7 @@
   compile_credentials = is_linux
 
   compile_seccomp_bpf_demo =
-      is_linux && (cpu_arch == "x86" || cpu_arch == "x64")
+      is_linux && (current_cpu == "x86" || current_cpu == "x64")
 }
 
 # We have two principal targets: sandbox and sandbox_linux_unittests
@@ -63,8 +63,10 @@
   }
 }
 
-# The main sandboxing test target.
-test("sandbox_linux_unittests") {
+# Sources shared by sandbox_linux_unittests and sandbox_linux_jni_unittests.
+source_set("sandbox_linux_unittests_sources") {
+  testonly = true
+
   sources = [
     "services/proc_util_unittest.cc",
     "services/resource_limits_unittests.cc",
@@ -127,23 +129,24 @@
   }
 }
 
-# TODO(GYP) Android version of this test.
-#    {
-#      # This target is the shared library used by Android APK (i.e.
-#      # JNI-friendly) tests.
-#      "target_name": "sandbox_linux_jni_unittests",
-#      "includes": [
-#        "sandbox_linux_test_sources.gypi",
-#      ],
-#      "type": "shared_library",
-#      "conditions": [
-#        [ "OS == "android"", {
-#          "dependencies": [
-#            "../testing/android/native_test.gyp:native_test_native_code",
-#          ],
-#        }],
-#      ],
-#    },
+# The main sandboxing test target.
+test("sandbox_linux_unittests") {
+  deps = [
+    ":sandbox_linux_unittests_sources",
+  ]
+}
+
+# This target is the shared library used by Android APK (i.e.
+# JNI-friendly) tests.
+shared_library("sandbox_linux_jni_unittests") {
+  testonly = true
+  deps = [
+    ":sandbox_linux_unittests_sources",
+  ]
+  if (is_android) {
+    deps += [ "//testing/android:native_test_native_code" ]
+  }
+}
 
 component("seccomp_bpf") {
   sources = [
diff --git a/sandbox/linux/services/credentials.cc b/sandbox/linux/services/credentials.cc
index e571dde..ed21fd1 100644
--- a/sandbox/linux/services/credentials.cc
+++ b/sandbox/linux/services/credentials.cc
@@ -114,7 +114,7 @@
   int status = -1;
   PCHECK(HANDLE_EINTR(waitpid(pid, &status, 0)) == pid);
 
-  return kExitSuccess == status;
+  return WIFEXITED(status) && WEXITSTATUS(status) == kExitSuccess;
 }
 
 // CHECK() that an attempt to move to a new user namespace raised an expected
@@ -174,12 +174,14 @@
   // have disappeared. Make sure to not do anything in the child, as this is a
   // fragile execution environment.
   if (pid == 0) {
-    _exit(0);
+    _exit(kExitSuccess);
   }
 
   // Always reap the child.
-  siginfo_t infop;
-  PCHECK(0 == HANDLE_EINTR(waitid(P_PID, pid, &infop, WEXITED)));
+  int status = -1;
+  PCHECK(HANDLE_EINTR(waitpid(pid, &status, 0)) == pid);
+  CHECK(WIFEXITED(status));
+  CHECK_EQ(kExitSuccess, WEXITSTATUS(status));
 
   // clone(2) succeeded, we can use CLONE_NEWUSER.
   return true;
diff --git a/sandbox/linux/services/proc_util_unittest.cc b/sandbox/linux/services/proc_util_unittest.cc
index ee36c83..2bf37a01 100644
--- a/sandbox/linux/services/proc_util_unittest.cc
+++ b/sandbox/linux/services/proc_util_unittest.cc
@@ -28,9 +28,9 @@
   // No open directory should exist at startup.
   EXPECT_FALSE(ProcUtil::HasOpenDirectory(-1));
   {
-    // Have a "/dev" file descriptor around.
-    int dev_fd = open("/dev", O_RDONLY | O_DIRECTORY);
-    base::ScopedFD dev_fd_closer(dev_fd);
+    // Have a "/proc" file descriptor around.
+    int proc_fd = open("/proc", O_RDONLY | O_DIRECTORY);
+    base::ScopedFD proc_fd_closer(proc_fd);
     EXPECT_TRUE(ProcUtil::HasOpenDirectory(-1));
   }
   EXPECT_FALSE(ProcUtil::HasOpenDirectory(-1));
@@ -48,14 +48,14 @@
   EXPECT_FALSE(ProcUtil::HasOpenDirectory(proc_fd));
 
   {
-    // Have a "/dev" file descriptor around.
-    int dev_fd = open("/dev", O_RDONLY | O_DIRECTORY);
-    base::ScopedFD dev_fd_closer(dev_fd);
+    // Have a directory file descriptor around.
+    int open_directory_fd = open("/proc/self", O_RDONLY | O_DIRECTORY);
+    base::ScopedFD open_directory_fd_closer(open_directory_fd);
     EXPECT_TRUE(ProcUtil::HasOpenDirectory(proc_fd));
   }
 
-  // The "/dev" file descriptor should now be closed, |proc_fd| is the only
-  // directory file descriptor open.
+  // The "/proc/self" file descriptor should now be closed, |proc_fd| is the
+  // only directory file descriptor open.
   EXPECT_FALSE(ProcUtil::HasOpenDirectory(proc_fd));
 }
 
diff --git a/sandbox/win/BUILD.gn b/sandbox/win/BUILD.gn
index d4d64962..ac87fa58 100644
--- a/sandbox/win/BUILD.gn
+++ b/sandbox/win/BUILD.gn
@@ -135,7 +135,7 @@
     "src/window.h",
   ]
 
-  if (cpu_arch == "x64") {
+  if (current_cpu == "x64") {
     sources += [
       "src/interceptors_64.cc",
       "src/interceptors_64.h",
@@ -143,7 +143,7 @@
       "src/service_resolver_64.cc",
       "src/Wow64_64.cc",
     ]
-  } else if (cpu_arch == "x86") {
+  } else if (current_cpu == "x86") {
     sources += [
       "src/resolver_32.cc",
       "src/service_resolver_32.cc",
@@ -164,12 +164,12 @@
   deps = [
     "//base",
   ]
-  if (cpu_arch == "x86") {
+  if (current_cpu == "x86") {
     deps += [ ":copy_wow_helper" ]
   }
 }
 
-if (cpu_arch == "x86") {
+if (current_cpu == "x86") {
   # Make a target that copies the wow_helper files to the out dir.
   #
   # TODO(brettw) we can probably just build this now that we have proper
diff --git a/sandbox/win/src/handle_closer_agent.cc b/sandbox/win/src/handle_closer_agent.cc
index 07c6a09..1fa8255 100644
--- a/sandbox/win/src/handle_closer_agent.cc
+++ b/sandbox/win/src/handle_closer_agent.cc
@@ -41,6 +41,50 @@
   return g_handles_to_close != NULL;
 }
 
+HandleCloserAgent::HandleCloserAgent()
+    : dummy_handle_(::CreateEvent(NULL, FALSE, FALSE, NULL)) {
+}
+
+// Attempts to stuff |closed_handle| with a duplicated handle for a dummy Event
+// with no access. This should allow the handle to be closed, to avoid
+// generating EXCEPTION_INVALID_HANDLE on shutdown, but nothing else. For now
+// the only supported |type| is Event or File.
+bool HandleCloserAgent::AttemptToStuffHandleSlot(HANDLE closed_handle,
+                                                 const base::string16& type) {
+  // Only attempt to stuff Files and Events at the moment.
+  if (type != L"Event" && type != L"File") {
+    return true;
+  }
+
+  if (!dummy_handle_.IsValid())
+    return false;
+
+  // This should never happen, as g_dummy is created before closing to_stuff.
+  DCHECK(dummy_handle_.Get() != closed_handle);
+
+  std::vector<HANDLE> to_close;
+  HANDLE dup_dummy = NULL;
+  size_t count = 16;
+
+  do {
+    if (!::DuplicateHandle(::GetCurrentProcess(), dummy_handle_.Get(),
+                           ::GetCurrentProcess(), &dup_dummy, 0, FALSE, 0))
+      break;
+    if (dup_dummy != closed_handle)
+      to_close.push_back(dup_dummy);
+  } while (count-- &&
+           reinterpret_cast<uintptr_t>(dup_dummy) <
+               reinterpret_cast<uintptr_t>(closed_handle));
+
+  for (auto h : to_close)
+    ::CloseHandle(h);
+
+  // Useful to know when we're not able to stuff handles.
+  DCHECK(dup_dummy == closed_handle);
+
+  return dup_dummy == closed_handle;
+}
+
 // Reads g_handles_to_close and creates the lookup map.
 void HandleCloserAgent::InitializeHandlesToClose() {
   CHECK(g_handles_to_close != NULL);
@@ -136,6 +180,8 @@
         return false;
       if (!::CloseHandle(handle))
         return false;
+      // Attempt to stuff this handle with a new dummy Event.
+      AttemptToStuffHandleSlot(handle, result->first);
     }
   }
 
diff --git a/sandbox/win/src/handle_closer_agent.h b/sandbox/win/src/handle_closer_agent.h
index 50ebf89..e189c1d 100644
--- a/sandbox/win/src/handle_closer_agent.h
+++ b/sandbox/win/src/handle_closer_agent.h
@@ -7,6 +7,7 @@
 
 #include "base/basictypes.h"
 #include "base/strings/string16.h"
+#include "base/win/scoped_handle.h"
 #include "sandbox/win/src/handle_closer.h"
 #include "sandbox/win/src/sandbox_types.h"
 
@@ -15,7 +16,7 @@
 // Target process code to close the handle list copied over from the broker.
 class HandleCloserAgent {
  public:
-  HandleCloserAgent() {}
+  HandleCloserAgent();
 
   // Reads the serialized list from the broker and creates the lookup map.
   void InitializeHandlesToClose();
@@ -23,11 +24,16 @@
   // Closes any handles matching those in the lookup map.
   bool CloseHandles();
 
-  // True if we have handles waiting to be closed
+  // True if we have handles waiting to be closed.
   static bool NeedsHandlesClosed();
 
  private:
+  // Attempt to stuff a closed handle with a dummy Event.
+  bool AttemptToStuffHandleSlot(HANDLE closed_handle,
+                                const base::string16& type);
+
   HandleMap handles_to_close_;
+  base::win::ScopedHandle dummy_handle_;
 
   DISALLOW_COPY_AND_ASSIGN(HandleCloserAgent);
 };
diff --git a/sandbox/win/src/handle_closer_test.cc b/sandbox/win/src/handle_closer_test.cc
index 5b7be47..f1f80e8 100644
--- a/sandbox/win/src/handle_closer_test.cc
+++ b/sandbox/win/src/handle_closer_test.cc
@@ -5,6 +5,7 @@
 #include "base/strings/stringprintf.h"
 #include "base/win/scoped_handle.h"
 #include "sandbox/win/src/handle_closer_agent.h"
+#include "sandbox/win/src/nt_internals.h"
 #include "sandbox/win/src/sandbox.h"
 #include "sandbox/win/src/sandbox_factory.h"
 #include "sandbox/win/src/target_services.h"
@@ -42,6 +43,26 @@
       NULL, OPEN_ALWAYS, FILE_FLAG_DELETE_ON_CLOSE, NULL);
 }
 
+// Returns type infomation for an NT object. This routine is expected to be
+// called for invalid handles so it catches STATUS_INVALID_HANDLE exceptions
+// that can be generated when handle tracing is enabled.
+NTSTATUS QueryObjectTypeInformation(HANDLE handle, void* buffer, ULONG* size) {
+  static NtQueryObject QueryObject = NULL;
+  if (!QueryObject)
+    ResolveNTFunctionPtr("NtQueryObject", &QueryObject);
+
+  NTSTATUS status = STATUS_UNSUCCESSFUL;
+  __try {
+    status = QueryObject(handle, ObjectTypeInformation, buffer, *size, size);
+  }
+  __except(GetExceptionCode() == STATUS_INVALID_HANDLE
+               ? EXCEPTION_EXECUTE_HANDLER
+               : EXCEPTION_CONTINUE_SEARCH) {
+    status = STATUS_INVALID_HANDLE;
+  }
+  return status;
+}
+
 // Used by the thread pool tests.
 HANDLE finish_event;
 const int kWaitCount = 20;
@@ -66,7 +87,7 @@
       // Create a unique marker file that is open while the test is running.
       // The handles leak, but it will be closed by the test or on exit.
       for (int i = 0; i < arraysize(kFileExtensions); ++i)
-        EXPECT_NE(GetMarkerFile(kFileExtensions[i]), INVALID_HANDLE_VALUE);
+        CHECK_NE(GetMarkerFile(kFileExtensions[i]), INVALID_HANDLE_VALUE);
       return SBOX_TEST_SUCCEEDED;
 
     case AFTER_REVERT: {
@@ -104,6 +125,70 @@
   return SBOX_TEST_SUCCEEDED;
 }
 
+// Checks that supplied handle is an Event and it's not waitable.
+// Format: CheckForEventHandles
+SBOX_TESTS_COMMAND int CheckForEventHandles(int argc, wchar_t** argv) {
+  static int state = BEFORE_INIT;
+  static std::vector<HANDLE> to_check;
+
+  switch (state++) {
+    case BEFORE_INIT:
+      // Create a unique marker file that is open while the test is running.
+      for (int i = 0; i < arraysize(kFileExtensions); ++i) {
+        HANDLE handle = GetMarkerFile(kFileExtensions[i]);
+        CHECK_NE(handle, INVALID_HANDLE_VALUE);
+        to_check.push_back(handle);
+      }
+      return SBOX_TEST_SUCCEEDED;
+
+    case AFTER_REVERT:
+      for (auto handle : to_check) {
+        // Set up buffers for the type info and the name.
+        std::vector<BYTE> type_info_buffer(sizeof(OBJECT_TYPE_INFORMATION) +
+                                           32 * sizeof(wchar_t));
+        OBJECT_TYPE_INFORMATION* type_info =
+            reinterpret_cast<OBJECT_TYPE_INFORMATION*>(&(type_info_buffer[0]));
+        NTSTATUS rc;
+
+        // Get the type name, reusing the buffer.
+        ULONG size = static_cast<ULONG>(type_info_buffer.size());
+        rc = QueryObjectTypeInformation(handle, type_info, &size);
+        while (rc == STATUS_INFO_LENGTH_MISMATCH ||
+               rc == STATUS_BUFFER_OVERFLOW) {
+          type_info_buffer.resize(size + sizeof(wchar_t));
+          type_info = reinterpret_cast<OBJECT_TYPE_INFORMATION*>(
+              &(type_info_buffer[0]));
+          rc = QueryObjectTypeInformation(handle, type_info, &size);
+          // Leave padding for the nul terminator.
+          if (NT_SUCCESS(rc) && size == type_info_buffer.size())
+            rc = STATUS_INFO_LENGTH_MISMATCH;
+        }
+
+        CHECK(NT_SUCCESS(rc));
+        CHECK(type_info->Name.Buffer);
+
+        type_info->Name.Buffer[type_info->Name.Length / sizeof(wchar_t)] =
+            L'\0';
+
+        // Should be an Event now.
+        CHECK_EQ(wcslen(type_info->Name.Buffer), 5U);
+        CHECK_EQ(wcscmp(L"Event", type_info->Name.Buffer), 0);
+
+        // Should not be able to wait.
+        CHECK_EQ(WaitForSingleObject(handle, INFINITE), WAIT_FAILED);
+
+        // Should be able to close.
+        CHECK_EQ(TRUE, CloseHandle(handle));
+      }
+      return SBOX_TEST_SUCCEEDED;
+
+    default:  // Do nothing.
+      break;
+  }
+
+  return SBOX_TEST_SUCCEEDED;
+}
+
 TEST(HandleCloserTest, CheckForMarkerFiles) {
   TestRunner runner;
   runner.SetTimeout(2000);
@@ -145,6 +230,24 @@
     "Failed: " << command;
 }
 
+TEST(HandleCloserTest, CheckStuffedHandle) {
+  TestRunner runner;
+  runner.SetTimeout(2000);
+  runner.SetTestState(EVERY_STATE);
+  sandbox::TargetPolicy* policy = runner.GetPolicy();
+
+  for (int i = 0; i < arraysize(kFileExtensions); ++i) {
+    base::string16 handle_name;
+    base::win::ScopedHandle marker(GetMarkerFile(kFileExtensions[i]));
+    CHECK(marker.IsValid());
+    CHECK(sandbox::GetHandleName(marker.Get(), &handle_name));
+    CHECK_EQ(policy->AddKernelObjectToClose(L"File", handle_name.c_str()),
+             SBOX_ALL_OK);
+  }
+
+  EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"CheckForEventHandles"));
+}
+
 void WINAPI ThreadPoolTask(void* event, BOOLEAN timeout) {
   static volatile LONG waiters_remaining = kWaitCount;
   CHECK(!timeout);
diff --git a/sandbox/win/src/target_services.cc b/sandbox/win/src/target_services.cc
index 518507e9..b43a3b8 100644
--- a/sandbox/win/src/target_services.cc
+++ b/sandbox/win/src/target_services.cc
@@ -46,13 +46,6 @@
 
 // Checks if we have handle entries pending and runs the closer.
 bool CloseOpenHandles() {
-  // Windows 10 has FLG_ENABLE_HANDLE_EXCEPTIONS enabled by default so causes
-  // exceptions to be raised if target process attempts to close a handle that
-  // has already been closed by HandleCloser.  Therefore, do not close any
-  // handles on Windows 10 until this flag is removed by MS.
-  // See crbug.com/452613.
-  if (base::win::GetVersion() == base::win::VERSION_WIN10)
-    return true;
   if (sandbox::HandleCloserAgent::NeedsHandlesClosed()) {
     sandbox::HandleCloserAgent handle_closer;
 
diff --git a/skia/BUILD.gn b/skia/BUILD.gn
index c793e0b..69940ae4 100644
--- a/skia/BUILD.gn
+++ b/skia/BUILD.gn
@@ -5,7 +5,7 @@
 import("//build/config/features.gni")
 import("//build/config/ui.gni")
 import("//testing/test.gni")
-if (cpu_arch == "arm") {
+if (current_cpu == "arm") {
   import("//build/config/arm.gni")
 }
 
@@ -191,7 +191,7 @@
     defines += [ "SKIA_IMPLEMENTATION=1" ]
   }
 
-  if (cpu_arch == "arm") {
+  if (current_cpu == "arm") {
     if (arm_use_neon) {
       defines += [ "SK_ARM_HAS_NEON" ]
     }
@@ -311,7 +311,10 @@
   sources += gypi_skia_utils.sources
   sources += gypi_values.skia_library_sources
 
-  if (cpu_arch == "arm") {
+  # This and skia_opts are really the same conceptual target so share headers.
+  allow_circular_includes_from = [ ":skia_opts" ]
+
+  if (current_cpu == "arm") {
     sources += [
       "//third_party/skia/src/core/SkUtilsArm.cpp",
       "//third_party/skia/src/core/SkUtilsArm.h",
@@ -545,7 +548,7 @@
   cflags = []
   defines = []
 
-  if (cpu_arch == "x86" || cpu_arch == "x64") {
+  if (current_cpu == "x86" || current_cpu == "x64") {
     sources = gypi_skia_opts.sse2_sources + gypi_skia_opts.ssse3_sources +
               gypi_skia_opts.sse41_sources +
               [
@@ -556,7 +559,7 @@
     if (is_linux || is_mac) {
       cflags += [ "-msse4.1" ]
     }
-  } else if (cpu_arch == "arm") {
+  } else if (current_cpu == "arm") {
     # The assembly uses the frame pointer register (r7 in Thumb/r11 in
     # ARM), the compiler doesn't like that.
     cflags += [ "-fomit-frame-pointer" ]
@@ -576,7 +579,7 @@
     } else {
       sources = gypi_skia_opts.none_sourcees
     }
-  } else if (cpu_arch == "mipsel") {
+  } else if (current_cpu == "mipsel") {
     cflags += [ "-fomit-frame-pointer" ]
     sources = gypi_skia_opts.none_sources
   } else {
@@ -624,6 +627,7 @@
     ":skia",
     "//base",
     "//base/test:run_all_unittests",
+    "//cc:test_support",  # TODO: Fix this test to not depend on cc.
     "//testing/gtest",
     "//ui/gfx",
     "//ui/gfx/geometry",
diff --git a/skia/config/SkUserConfig.h b/skia/config/SkUserConfig.h
index 84a02d70..54c5f07 100644
--- a/skia/config/SkUserConfig.h
+++ b/skia/config/SkUserConfig.h
@@ -253,6 +253,10 @@
 #   define SK_IGNORE_ETC1_SUPPORT
 #endif
 
+#ifndef    SK_SUPPORT_LEGACY_MIPMAP_EFFECTIVE_SCALE
+#   define SK_SUPPORT_LEGACY_MIPMAP_EFFECTIVE_SCALE
+#endif
+
 #ifndef    SK_IGNORE_GPU_DITHER
 #   define SK_IGNORE_GPU_DITHER
 #endif
diff --git a/skia/ext/analysis_canvas.cc b/skia/ext/analysis_canvas.cc
index 376d173..fe447c7 100644
--- a/skia/ext/analysis_canvas.cc
+++ b/skia/ext/analysis_canvas.cc
@@ -9,7 +9,6 @@
 #include "third_party/skia/include/core/SkRRect.h"
 #include "third_party/skia/include/core/SkShader.h"
 #include "third_party/skia/src/core/SkRasterClip.h"
-#include "ui/gfx/geometry/rect_conversions.h"
 
 namespace {
 
diff --git a/skia/ext/bitmap_platform_device_mac.cc b/skia/ext/bitmap_platform_device_mac.cc
index ff7c2ad..9c7966da 100644
--- a/skia/ext/bitmap_platform_device_mac.cc
+++ b/skia/ext/bitmap_platform_device_mac.cc
@@ -71,6 +71,78 @@
   config_dirty_ = true;
 }
 
+// Loads the specified Skia transform into the device context
+static void LoadTransformToCGContext(CGContextRef context,
+                                     const SkMatrix& matrix) {
+  // CoreGraphics can concatenate transforms, but not reset the current one.
+  // So in order to get the required behavior here, we need to first make
+  // the current transformation matrix identity and only then load the new one.
+
+  // Reset matrix to identity.
+  CGAffineTransform orig_cg_matrix = CGContextGetCTM(context);
+  CGAffineTransform orig_cg_matrix_inv =
+      CGAffineTransformInvert(orig_cg_matrix);
+  CGContextConcatCTM(context, orig_cg_matrix_inv);
+
+  // assert that we have indeed returned to the identity Matrix.
+  SkASSERT(CGAffineTransformIsIdentity(CGContextGetCTM(context)));
+
+  // Convert xform to CG-land.
+  // Our coordinate system is flipped to match WebKit's so we need to modify
+  // the xform to match that.
+  SkMatrix transformed_matrix = matrix;
+  SkScalar sy = -matrix.getScaleY();
+  transformed_matrix.setScaleY(sy);
+  size_t height = CGBitmapContextGetHeight(context);
+  SkScalar ty = -matrix.getTranslateY();  // y axis is flipped.
+  transformed_matrix.setTranslateY(ty + (SkScalar)height);
+
+  CGAffineTransform cg_matrix =
+      gfx::SkMatrixToCGAffineTransform(transformed_matrix);
+
+  // Load final transform into context.
+  CGContextConcatCTM(context, cg_matrix);
+}
+
+// Loads a SkRegion into the CG context.
+static void LoadClippingRegionToCGContext(CGContextRef context,
+                                          const SkRegion& region,
+                                          const SkMatrix& transformation) {
+  if (region.isEmpty()) {
+    // region can be empty, in which case everything will be clipped.
+    SkRect rect;
+    rect.setEmpty();
+    CGContextClipToRect(context, gfx::SkRectToCGRect(rect));
+  } else if (region.isRect()) {
+    // CoreGraphics applies the current transform to clip rects, which is
+    // unwanted. Inverse-transform the rect before sending it to CG. This only
+    // works for translations and scaling, but not for rotations (but the
+    // viewport is never rotated anyway).
+    SkMatrix t;
+    bool did_invert = transformation.invert(&t);
+    if (!did_invert)
+      t.reset();
+    // Do the transformation.
+    SkRect rect;
+    rect.set(region.getBounds());
+    t.mapRect(&rect);
+    SkIRect irect;
+    rect.round(&irect);
+    CGContextClipToRect(context, gfx::SkIRectToCGRect(irect));
+  } else {
+    // It is complex.
+    SkPath path;
+    region.getBoundaryPath(&path);
+    // Clip. Note that windows clipping regions are not affected by the
+    // transform so apply it manually.
+    path.transform(transformation);
+    // TODO(playmobil): Implement.
+    SkASSERT(false);
+    // LoadPathToDC(context, path);
+    // hrgn = PathToRegion(context);
+  }
+}
+
 void BitmapPlatformDevice::LoadConfig() {
   if (!config_dirty_ || !bitmap_context_)
     return;  // Nothing to do.
diff --git a/skia/ext/platform_canvas_unittest.cc b/skia/ext/platform_canvas_unittest.cc
index 9ab56671..530d722 100644
--- a/skia/ext/platform_canvas_unittest.cc
+++ b/skia/ext/platform_canvas_unittest.cc
@@ -418,7 +418,9 @@
   EXPECT_EQ(kN32_SkColorType,  // Same for all platforms.
             platform_bitmap->GetBitmap().colorType());
   EXPECT_TRUE(platform_bitmap->GetBitmap().lockPixelsAreWritable());
+#if defined(SK_DEBUG)
   EXPECT_TRUE(platform_bitmap->GetBitmap().pixelRef()->isLocked());
+#endif
   EXPECT_TRUE(platform_bitmap->GetBitmap().pixelRef()->unique());
 
   *(platform_bitmap->GetBitmap().getAddr32(10, 20)) = 0xDEED1020;
diff --git a/skia/ext/platform_device.h b/skia/ext/platform_device.h
index 8486102..c903c87 100644
--- a/skia/ext/platform_device.h
+++ b/skia/ext/platform_device.h
@@ -131,19 +131,6 @@
   // have to be created twice.  If src_rect is null, then the entirety of the
   // source device will be copied.
   virtual void DrawToHDC(HDC, int x, int y, const RECT* src_rect);
-    
-#elif defined(OS_MACOSX)
-  // Loads a SkPath into the CG context. The path can there after be used for
-  // clipping or as a stroke.
-  static void LoadPathToCGContext(CGContextRef context, const SkPath& path);
-
-  // Initializes the default settings and colors in a device context.
-  static void InitializeCGContext(CGContextRef context);
-
-  // Loads a SkRegion into the CG context.
-  static void LoadClippingRegionToCGContext(CGContextRef context,
-                                            const SkRegion& region,
-                                            const SkMatrix& transformation);
 #endif
 
  protected:
@@ -161,10 +148,6 @@
 
   // Transforms SkPath's paths into a series of cubic path.
   static bool SkPathToCubicPaths(CubicPaths* paths, const SkPath& skpath);
-#elif defined(OS_MACOSX)
-  // Loads the specified Skia transform into the device context
-  static void LoadTransformToCGContext(CGContextRef context,
-                                       const SkMatrix& matrix);
 #endif
 };
 
diff --git a/skia/ext/platform_device_mac.cc b/skia/ext/platform_device_mac.cc
index f66372bf..065b7670 100644
--- a/skia/ext/platform_device_mac.cc
+++ b/skia/ext/platform_device_mac.cc
@@ -30,126 +30,4 @@
   // Flushing will be done in onAccessBitmap.
 }
 
-// Set up the CGContextRef for peaceful coexistence with Skia
-void PlatformDevice::InitializeCGContext(CGContextRef context) {
-  // CG defaults to the same settings as Skia
-}
-
-// static
-void PlatformDevice::LoadPathToCGContext(CGContextRef context,
-                                         const SkPath& path) {
-  // instead of a persistent attribute of the context, CG specifies the fill
-  // type per call, so we just have to load up the geometry.
-  CGContextBeginPath(context);
-
-  SkPoint points[4] = { {0, 0}, {0, 0}, {0, 0}, {0, 0} };
-  SkPath::Iter iter(path, false);
-  for (SkPath::Verb verb = iter.next(points); verb != SkPath::kDone_Verb;
-       verb = iter.next(points)) {
-    switch (verb) {
-      case SkPath::kMove_Verb: {  // iter.next returns 1 point
-        CGContextMoveToPoint(context, points[0].fX, points[0].fY);
-        break;
-      }
-      case SkPath::kLine_Verb: {  // iter.next returns 2 points
-        CGContextAddLineToPoint(context, points[1].fX, points[1].fY);
-        break;
-      }
-      case SkPath::kQuad_Verb: {  // iter.next returns 3 points
-        CGContextAddQuadCurveToPoint(context, points[1].fX, points[1].fY,
-                                     points[2].fX, points[2].fY);
-        break;
-      }
-      case SkPath::kCubic_Verb: {  // iter.next returns 4 points
-        CGContextAddCurveToPoint(context, points[1].fX, points[1].fY,
-                                 points[2].fX, points[2].fY,
-                                 points[3].fX, points[3].fY);
-        break;
-      }
-      case SkPath::kClose_Verb: {  // iter.next returns 1 point (the last point)
-        break;
-      }
-      case SkPath::kDone_Verb:  // iter.next returns 0 points
-      default: {
-        SkASSERT(false);
-        break;
-      }
-    }
-  }
-  CGContextClosePath(context);
-}
-
-// static
-void PlatformDevice::LoadTransformToCGContext(CGContextRef context,
-                                              const SkMatrix& matrix) {
-  // CoreGraphics can concatenate transforms, but not reset the current one.
-  // So in order to get the required behavior here, we need to first make
-  // the current transformation matrix identity and only then load the new one.
-
-  // Reset matrix to identity.
-  CGAffineTransform orig_cg_matrix = CGContextGetCTM(context);
-  CGAffineTransform orig_cg_matrix_inv = CGAffineTransformInvert(
-      orig_cg_matrix);
-  CGContextConcatCTM(context, orig_cg_matrix_inv);
-
-  // assert that we have indeed returned to the identity Matrix.
-  SkASSERT(CGAffineTransformIsIdentity(CGContextGetCTM(context)));
-
-  // Convert xform to CG-land.
-  // Our coordinate system is flipped to match WebKit's so we need to modify
-  // the xform to match that.
-  SkMatrix transformed_matrix = matrix;
-  SkScalar sy = matrix.getScaleY() * (SkScalar)-1;
-  transformed_matrix.setScaleY(sy);
-  size_t height = CGBitmapContextGetHeight(context);
-  SkScalar ty = -matrix.getTranslateY(); // y axis is flipped.
-  transformed_matrix.setTranslateY(ty + (SkScalar)height);
-
-  CGAffineTransform cg_matrix = gfx::SkMatrixToCGAffineTransform(
-      transformed_matrix);
-
-  // Load final transform into context.
-  CGContextConcatCTM(context, cg_matrix);
-}
-
-// static
-void PlatformDevice::LoadClippingRegionToCGContext(
-         CGContextRef context,
-         const SkRegion& region,
-         const SkMatrix& transformation) {
-  if (region.isEmpty()) {
-    // region can be empty, in which case everything will be clipped.
-    SkRect rect;
-    rect.setEmpty();
-    CGContextClipToRect(context, gfx::SkRectToCGRect(rect));
-  } else if (region.isRect()) {
-    // CoreGraphics applies the current transform to clip rects, which is
-    // unwanted. Inverse-transform the rect before sending it to CG. This only
-    // works for translations and scaling, but not for rotations (but the
-    // viewport is never rotated anyway).
-    SkMatrix t;
-    bool did_invert = transformation.invert(&t);
-    if (!did_invert)
-      t.reset();
-    // Do the transformation.
-    SkRect rect;
-    rect.set(region.getBounds());
-    t.mapRect(&rect);
-    SkIRect irect;
-    rect.round(&irect);
-    CGContextClipToRect(context, gfx::SkIRectToCGRect(irect));
-  } else {
-    // It is complex.
-    SkPath path;
-    region.getBoundaryPath(&path);
-    // Clip. Note that windows clipping regions are not affected by the
-    // transform so apply it manually.
-    path.transform(transformation);
-    // TODO(playmobil): Implement.
-    SkASSERT(false);
-    // LoadPathToDC(context, path);
-    // hrgn = PathToRegion(context);
-  }
-}
-
 }  // namespace skia
diff --git a/skia/ext/skia_utils_ios_unittest.mm b/skia/ext/skia_utils_ios_unittest.mm
index c199304..8d0c9be 100644
--- a/skia/ext/skia_utils_ios_unittest.mm
+++ b/skia/ext/skia_utils_ios_unittest.mm
@@ -8,7 +8,6 @@
 #import <UIKit/UIKit.h>
 
 #include "base/base64.h"
-#include "base/ios/ios_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace {
diff --git a/skia/skia_test_expectations.txt b/skia/skia_test_expectations.txt
index 4327f6e..5927d5c 100644
--- a/skia/skia_test_expectations.txt
+++ b/skia/skia_test_expectations.txt
@@ -48,4 +48,7 @@
 #
 # START OVERRIDES HERE
 
+# We fixed a blending bug.
+crbug.com/459579 virtual/gpu/fast/canvas/canvas-composite-transformclip.html [ ImageOnlyFailure ]
+
 # END OVERRIDES HERE (this line ensures that the file is newline-terminated)
diff --git a/sql/BUILD.gn b/sql/BUILD.gn
index 7c4d3f0c..8b75d80 100644
--- a/sql/BUILD.gn
+++ b/sql/BUILD.gn
@@ -21,11 +21,10 @@
     "transaction.h",
   ]
 
-  defines = [ "SQL_IMPLEMENTATION" ]
+  # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+  configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
 
-  if (is_win) {
-    cflags = [ "/wd4267" ]  # size_t to int.
-  }
+  defines = [ "SQL_IMPLEMENTATION" ]
 
   deps = [
     "//base",
@@ -68,9 +67,8 @@
     "transaction_unittest.cc",
   ]
 
-  if (is_win) {
-    cflags = [ "/wd4267" ]  # size_t -> int
-  }
+  # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+  configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
 
   deps = [
     ":sql",
diff --git a/storage/browser/BUILD.gn b/storage/browser/BUILD.gn
index 42e6a99..61e76c7a 100644
--- a/storage/browser/BUILD.gn
+++ b/storage/browser/BUILD.gn
@@ -170,10 +170,11 @@
   ]
 
   defines = [ "STORAGE_BROWSER_IMPLEMENTATION" ]
-  configs += [ "//build/config/compiler:wexit_time_destructors" ]
-  if (is_win) {
-    cflags = [ "/wd4267" ]  # TODO(jschuh): fix size_t to int truncations.
-  }
+  configs += [
+    # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+    "//build/config/compiler:no_size_t_to_int_warning",
+    "//build/config/compiler:wexit_time_destructors",
+  ]
 
   deps = [
     "//base",
diff --git a/storage/common/BUILD.gn b/storage/common/BUILD.gn
index 0067972..2c55bb9e 100644
--- a/storage/common/BUILD.gn
+++ b/storage/common/BUILD.gn
@@ -26,10 +26,10 @@
     "quota/quota_types.h",
   ]
 
+  # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+  configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
+
   defines = [ "STORAGE_COMMON_IMPLEMENTATION" ]
-  if (is_win) {
-    cflags = [ "/wd4267" ]  # TODO(jschuh): fix size_t to int truncations.
-  }
 
   deps = [
     "//base",
diff --git a/sync/BUILD.gn b/sync/BUILD.gn
index 605f7971b..4e830bc 100644
--- a/sync/BUILD.gn
+++ b/sync/BUILD.gn
@@ -24,8 +24,6 @@
     "api/attachments/attachment_store.cc",
     "api/attachments/attachment_store.h",
     "api/string_ordinal.h",
-    "api/syncable_service.cc",
-    "api/syncable_service.h",
     "api/sync_change.cc",
     "api/sync_change.h",
     "api/sync_change_processor.cc",
@@ -33,11 +31,13 @@
     "api/sync_data.cc",
     "api/sync_data.h",
     "api/sync_error.cc",
+    "api/sync_error.h",
     "api/sync_error_factory.cc",
     "api/sync_error_factory.h",
-    "api/sync_error.h",
     "api/sync_merge_result.cc",
     "api/sync_merge_result.h",
+    "api/syncable_service.cc",
+    "api/syncable_service.h",
     "api/time.h",
     "base/sync_export.h",
     "engine/all_status.cc",
@@ -47,11 +47,11 @@
     "engine/backoff_delay_provider.cc",
     "engine/backoff_delay_provider.h",
     "engine/commit.cc",
+    "engine/commit.h",
     "engine/commit_contribution.cc",
     "engine/commit_contribution.h",
     "engine/commit_contributor.cc",
     "engine/commit_contributor.h",
-    "engine/commit.h",
     "engine/commit_processor.cc",
     "engine/commit_processor.h",
     "engine/commit_util.cc",
@@ -100,6 +100,10 @@
     "engine/sync_cycle_event.h",
     "engine/sync_engine_event_listener.cc",
     "engine/sync_engine_event_listener.h",
+    "engine/sync_scheduler.cc",
+    "engine/sync_scheduler.h",
+    "engine/sync_scheduler_impl.cc",
+    "engine/sync_scheduler_impl.h",
     "engine/syncer.cc",
     "engine/syncer.h",
     "engine/syncer_proto_util.cc",
@@ -107,10 +111,6 @@
     "engine/syncer_types.h",
     "engine/syncer_util.cc",
     "engine/syncer_util.h",
-    "engine/sync_scheduler.cc",
-    "engine/sync_scheduler.h",
-    "engine/sync_scheduler_impl.cc",
-    "engine/sync_scheduler_impl.h",
     "engine/traffic_logger.cc",
     "engine/traffic_logger.h",
     "engine/update_applicator.cc",
@@ -181,15 +181,15 @@
     "internal_api/public/base/invalidation_interface.cc",
     "internal_api/public/base/invalidation_interface.h",
     "internal_api/public/base/model_type.h",
-    "internal_api/public/base_node.h",
     "internal_api/public/base/node_ordinal.cc",
     "internal_api/public/base/node_ordinal.h",
     "internal_api/public/base/ordinal.h",
     "internal_api/public/base/progress_marker_map.cc",
     "internal_api/public/base/progress_marker_map.h",
-    "internal_api/public/base_transaction.h",
     "internal_api/public/base/unique_position.cc",
     "internal_api/public/base/unique_position.h",
+    "internal_api/public/base_node.h",
+    "internal_api/public/base_transaction.h",
     "internal_api/public/change_record.h",
     "internal_api/public/configure_reason.h",
     "internal_api/public/data_type_association_stats.cc",
@@ -242,17 +242,17 @@
     "internal_api/public/sync_encryption_handler.cc",
     "internal_api/public/sync_encryption_handler.h",
     "internal_api/public/sync_manager.cc",
-    "internal_api/public/sync_manager_factory.h",
     "internal_api/public/sync_manager.h",
+    "internal_api/public/sync_manager_factory.h",
     "internal_api/public/user_share.h",
     "internal_api/public/util/experiments.h",
     "internal_api/public/util/immutable.h",
     "internal_api/public/util/report_unrecoverable_error_function.h",
     "internal_api/public/util/sync_db_util.h",
-    "internal_api/public/util/syncer_error.cc",
-    "internal_api/public/util/syncer_error.h",
     "internal_api/public/util/sync_string_conversions.cc",
     "internal_api/public/util/sync_string_conversions.h",
+    "internal_api/public/util/syncer_error.cc",
+    "internal_api/public/util/syncer_error.h",
     "internal_api/public/util/unrecoverable_error_handler.h",
     "internal_api/public/util/unrecoverable_error_info.cc",
     "internal_api/public/util/unrecoverable_error_info.h",
@@ -262,10 +262,6 @@
     "internal_api/public/write_transaction.h",
     "internal_api/read_node.cc",
     "internal_api/read_transaction.cc",
-    "internal_api/syncapi_internal.cc",
-    "internal_api/syncapi_internal.h",
-    "internal_api/syncapi_server_connection_manager.cc",
-    "internal_api/syncapi_server_connection_manager.h",
     "internal_api/sync_backup_manager.cc",
     "internal_api/sync_backup_manager.h",
     "internal_api/sync_context.cc",
@@ -278,10 +274,14 @@
     "internal_api/sync_manager_factory.cc",
     "internal_api/sync_manager_impl.cc",
     "internal_api/sync_manager_impl.h",
-    "internal_api/sync_rollback_manager_base.cc",
-    "internal_api/sync_rollback_manager_base.h",
     "internal_api/sync_rollback_manager.cc",
     "internal_api/sync_rollback_manager.h",
+    "internal_api/sync_rollback_manager_base.cc",
+    "internal_api/sync_rollback_manager_base.h",
+    "internal_api/syncapi_internal.cc",
+    "internal_api/syncapi_internal.h",
+    "internal_api/syncapi_server_connection_manager.cc",
+    "internal_api/syncapi_server_connection_manager.h",
     "internal_api/user_share.cc",
     "internal_api/write_node.cc",
     "internal_api/write_transaction.cc",
@@ -310,18 +310,18 @@
     "sessions/status_controller.cc",
     "sessions/status_controller.h",
     "sessions/sync_session.cc",
+    "sessions/sync_session.h",
     "sessions/sync_session_context.cc",
     "sessions/sync_session_context.h",
-    "sessions/sync_session.h",
     "syncable/blob.h",
     "syncable/deferred_on_disk_directory_backing_store.cc",
     "syncable/deferred_on_disk_directory_backing_store.h",
+    "syncable/dir_open_result.h",
+    "syncable/directory.cc",
+    "syncable/directory.h",
     "syncable/directory_backing_store.cc",
     "syncable/directory_backing_store.h",
-    "syncable/directory.cc",
     "syncable/directory_change_delegate.h",
-    "syncable/directory.h",
-    "syncable/dir_open_result.h",
     "syncable/entry.cc",
     "syncable/entry.h",
     "syncable/entry_kernel.cc",
@@ -348,6 +348,7 @@
     "syncable/scoped_kernel_lock.h",
     "syncable/scoped_parent_child_index_updater.cc",
     "syncable/scoped_parent_child_index_updater.h",
+    "syncable/syncable-inl.h",
     "syncable/syncable_base_transaction.cc",
     "syncable/syncable_base_transaction.h",
     "syncable/syncable_base_write_transaction.cc",
@@ -360,7 +361,6 @@
     "syncable/syncable_enum_conversions.h",
     "syncable/syncable_id.cc",
     "syncable/syncable_id.h",
-    "syncable/syncable-inl.h",
     "syncable/syncable_model_neutral_write_transaction.cc",
     "syncable/syncable_model_neutral_write_transaction.h",
     "syncable/syncable_proto_util.cc",
@@ -464,19 +464,19 @@
     "test/mock_invalidation.h",
     "test/mock_invalidation_tracker.cc",
     "test/mock_invalidation_tracker.h",
-    "test/trackable_mock_invalidation.cc",
-    "test/trackable_mock_invalidation.h",
     "test/null_directory_change_delegate.cc",
     "test/null_directory_change_delegate.h",
     "test/null_transaction_observer.cc",
     "test/null_transaction_observer.h",
-    "test/sessions/test_scoped_session_event_listener.h",
-    "test/sessions/mock_debug_info_getter.h",
     "test/sessions/mock_debug_info_getter.cc",
+    "test/sessions/mock_debug_info_getter.h",
+    "test/sessions/test_scoped_session_event_listener.h",
     "test/test_directory_backing_store.cc",
     "test/test_directory_backing_store.h",
     "test/test_transaction_observer.cc",
     "test/test_transaction_observer.h",
+    "test/trackable_mock_invalidation.cc",
+    "test/trackable_mock_invalidation.h",
     "util/test_unrecoverable_error_handler.cc",
     "util/test_unrecoverable_error_handler.h",
   ]
@@ -527,10 +527,10 @@
 static_library("test_support_sync_api") {
   testonly = true
   sources = [
-    "api/fake_syncable_service.cc",
-    "api/fake_syncable_service.h",
     "api/fake_sync_change_processor.cc",
     "api/fake_sync_change_processor.h",
+    "api/fake_syncable_service.cc",
+    "api/fake_syncable_service.h",
     "api/sync_change_processor_wrapper_for_test.cc",
     "api/sync_change_processor_wrapper_for_test.h",
     "api/sync_error_factory_mock.cc",
@@ -540,8 +540,9 @@
   defines = [ "SYNC_TEST" ]
 
   public_deps = [
-    "//testing/gmock",
+    "//base",
     "//sync",
+    "//testing/gmock",
   ]
 }
 
@@ -629,6 +630,8 @@
   ]
 
   deps = [
+    ":test_support_sync_core",
+    ":test_support_sync_internal_api",
     "//base",
     "//base/allocator",
     "//base/test:run_all_unittests",
@@ -638,12 +641,11 @@
     "//net:test_support",
     "//sql",
     "//sync",
+    "//sync/internal_api/attachments/proto",
     "//testing/gmock",
     "//testing/gtest",
     "//third_party/leveldatabase",
     "//third_party/protobuf:protobuf_lite",
-    ":test_support_sync_core",
-    ":test_support_sync_internal_api",
   ]
 
   if (is_chromeos) {
@@ -694,6 +696,7 @@
     "//testing/gtest",
     "//third_party/protobuf:protobuf_lite",
     "//sync",
+    "//url",
   ]
 
   forward_dependent_configs_from = [ "//third_party/protobuf:protobuf_lite" ]
@@ -734,6 +737,7 @@
   deps = [
     "//base",
     "//net",
+    "//url",
   ]
 }
 
@@ -743,6 +747,7 @@
     "test/accounts_client/test_accounts_client_unittest.cc",
   ]
   deps = [
+    "//base",
     "//base/test:run_all_unittests",
     "//testing/gmock",
     "//testing/gtest",
@@ -802,6 +807,7 @@
     ]
     deps = [
       ":fake_server_jni",
+      ":sync_core",
       ":test_support_sync_fake_server",
       "//base",
       "//testing/gtest",
diff --git a/sync/internal_api/public/util/immutable_unittest.cc b/sync/internal_api/public/util/immutable_unittest.cc
index f8494b60..247325f 100644
--- a/sync/internal_api/public/util/immutable_unittest.cc
+++ b/sync/internal_api/public/util/immutable_unittest.cc
@@ -225,13 +225,7 @@
         "VectorSwapMemFnByRef");
 }
 
-// http://crbug.com/129128
-#if defined(OS_WIN)
-#define MAYBE_Deque DISABLED_Deque
-#else
-#define MAYBE_Deque Deque
-#endif
-TEST_F(ImmutableTest, MAYBE_Deque) {
+TEST_F(ImmutableTest, Deque) {
   RunTokenContainerTest<std::deque<Token>, Immutable<std::deque<Token> > >(
       "Deque");
 }
diff --git a/sync/internal_api/sync_manager_impl_unittest.cc b/sync/internal_api/sync_manager_impl_unittest.cc
index 8e77e70..f68b9470 100644
--- a/sync/internal_api/sync_manager_impl_unittest.cc
+++ b/sync/internal_api/sync_manager_impl_unittest.cc
@@ -1027,6 +1027,30 @@
     return switches_;
   }
 
+  void ExpectPassphraseAcceptance() {
+    EXPECT_CALL(encryption_observer_, OnPassphraseAccepted());
+    EXPECT_CALL(encryption_observer_, OnEncryptionComplete());
+    EXPECT_CALL(encryption_observer_, OnCryptographerStateChanged(_));
+  }
+
+  void SetImplicitPassphraseAndCheck(const std::string& passphrase) {
+    sync_manager_.GetEncryptionHandler()->SetEncryptionPassphrase(
+        passphrase,
+        false);
+    EXPECT_EQ(IMPLICIT_PASSPHRASE,
+              sync_manager_.GetEncryptionHandler()->GetPassphraseType());
+  }
+
+  void SetCustomPassphraseAndCheck(const std::string& passphrase) {
+    EXPECT_CALL(encryption_observer_,
+        OnPassphraseTypeChanged(CUSTOM_PASSPHRASE, _));
+    sync_manager_.GetEncryptionHandler()->SetEncryptionPassphrase(
+        passphrase,
+        true);
+    EXPECT_EQ(CUSTOM_PASSPHRASE,
+              sync_manager_.GetEncryptionHandler()->GetPassphraseType());
+  }
+
  private:
   // Needed by |sync_manager_|.
   base::MessageLoop message_loop_;
@@ -1217,13 +1241,8 @@
   testing::Mock::VerifyAndClearExpectations(&encryption_observer_);
   EXPECT_CALL(encryption_observer_,
               OnBootstrapTokenUpdated(_, PASSPHRASE_BOOTSTRAP_TOKEN));
-  EXPECT_CALL(encryption_observer_, OnPassphraseAccepted());
-  EXPECT_CALL(encryption_observer_, OnEncryptionComplete());
-  EXPECT_CALL(encryption_observer_, OnCryptographerStateChanged(_));
-  EXPECT_CALL(encryption_observer_,
-              OnPassphraseTypeChanged(CUSTOM_PASSPHRASE, _));
-  sync_manager_.GetEncryptionHandler()->SetEncryptionPassphrase(
-      "new_passphrase", true);
+  ExpectPassphraseAcceptance();
+  SetCustomPassphraseAndCheck("new_passphrase");
   EXPECT_TRUE(EncryptEverythingEnabledForTest());
   {
     ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
@@ -1259,14 +1278,8 @@
   EXPECT_FALSE(SetUpEncryption(DONT_WRITE_NIGORI, UNINITIALIZED));
   EXPECT_CALL(encryption_observer_,
               OnBootstrapTokenUpdated(_, PASSPHRASE_BOOTSTRAP_TOKEN));
-  EXPECT_CALL(encryption_observer_, OnPassphraseAccepted());
-  EXPECT_CALL(encryption_observer_, OnEncryptionComplete());
-  EXPECT_CALL(encryption_observer_, OnCryptographerStateChanged(_));
-  sync_manager_.GetEncryptionHandler()->SetEncryptionPassphrase(
-      "new_passphrase",
-      false);
-  EXPECT_EQ(IMPLICIT_PASSPHRASE,
-            sync_manager_.GetEncryptionHandler()->GetPassphraseType());
+  ExpectPassphraseAcceptance();
+  SetImplicitPassphraseAndCheck("new_passphrase");
   EXPECT_FALSE(EncryptEverythingEnabledForTest());
   {
     ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
@@ -1294,14 +1307,8 @@
   }
   EXPECT_CALL(encryption_observer_,
               OnBootstrapTokenUpdated(_, PASSPHRASE_BOOTSTRAP_TOKEN));
-  EXPECT_CALL(encryption_observer_, OnPassphraseAccepted());
-  EXPECT_CALL(encryption_observer_, OnEncryptionComplete());
-  EXPECT_CALL(encryption_observer_, OnCryptographerStateChanged(_));
-  sync_manager_.GetEncryptionHandler()->SetEncryptionPassphrase(
-      "new_passphrase",
-      false);
-  EXPECT_EQ(IMPLICIT_PASSPHRASE,
-            sync_manager_.GetEncryptionHandler()->GetPassphraseType());
+  ExpectPassphraseAcceptance();
+  SetImplicitPassphraseAndCheck("new_passphrase");
   EXPECT_FALSE(EncryptEverythingEnabledForTest());
   {
     ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
@@ -1342,16 +1349,8 @@
   }
     EXPECT_CALL(encryption_observer_,
               OnBootstrapTokenUpdated(_, PASSPHRASE_BOOTSTRAP_TOKEN));
-  EXPECT_CALL(encryption_observer_, OnPassphraseAccepted());
-  EXPECT_CALL(encryption_observer_, OnEncryptionComplete());
-  EXPECT_CALL(encryption_observer_, OnCryptographerStateChanged(_));
-  EXPECT_CALL(encryption_observer_,
-      OnPassphraseTypeChanged(CUSTOM_PASSPHRASE, _));
-  sync_manager_.GetEncryptionHandler()->SetEncryptionPassphrase(
-      "new_passphrase",
-      true);
-  EXPECT_EQ(CUSTOM_PASSPHRASE,
-            sync_manager_.GetEncryptionHandler()->GetPassphraseType());
+  ExpectPassphraseAcceptance();
+  SetCustomPassphraseAndCheck("new_passphrase");
   EXPECT_FALSE(EncryptEverythingEnabledForTest());
   {
     ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
@@ -1400,9 +1399,7 @@
   }
   EXPECT_CALL(encryption_observer_,
               OnBootstrapTokenUpdated(_, PASSPHRASE_BOOTSTRAP_TOKEN));
-  EXPECT_CALL(encryption_observer_, OnPassphraseAccepted());
-  EXPECT_CALL(encryption_observer_, OnEncryptionComplete());
-  EXPECT_CALL(encryption_observer_, OnCryptographerStateChanged(_));
+  ExpectPassphraseAcceptance();
   sync_manager_.GetEncryptionHandler()->SetDecryptionPassphrase("passphrase2");
   EXPECT_EQ(IMPLICIT_PASSPHRASE,
             sync_manager_.GetEncryptionHandler()->GetPassphraseType());
@@ -1458,11 +1455,7 @@
       .WillOnce(SaveArg<0>(&bootstrap_token));
   EXPECT_CALL(encryption_observer_, OnPassphraseRequired(_,_));
   EXPECT_CALL(encryption_observer_, OnCryptographerStateChanged(_));
-  sync_manager_.GetEncryptionHandler()->SetEncryptionPassphrase(
-      "new_gaia",
-      false);
-  EXPECT_EQ(IMPLICIT_PASSPHRASE,
-            sync_manager_.GetEncryptionHandler()->GetPassphraseType());
+  SetImplicitPassphraseAndCheck("new_gaia");
   EXPECT_FALSE(EncryptEverythingEnabledForTest());
   testing::Mock::VerifyAndClearExpectations(&encryption_observer_);
   {
@@ -1478,14 +1471,8 @@
   }
   EXPECT_CALL(encryption_observer_,
               OnBootstrapTokenUpdated(_, PASSPHRASE_BOOTSTRAP_TOKEN));
-  EXPECT_CALL(encryption_observer_, OnPassphraseAccepted());
-  EXPECT_CALL(encryption_observer_, OnEncryptionComplete());
-  EXPECT_CALL(encryption_observer_, OnCryptographerStateChanged(_));
-  sync_manager_.GetEncryptionHandler()->SetEncryptionPassphrase(
-      "old_gaia",
-      false);
-  EXPECT_EQ(IMPLICIT_PASSPHRASE,
-            sync_manager_.GetEncryptionHandler()->GetPassphraseType());
+  ExpectPassphraseAcceptance();
+  SetImplicitPassphraseAndCheck("old_gaia");
   {
     ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
     Cryptographer* cryptographer = trans.GetCryptographer();
@@ -1538,9 +1525,7 @@
   sync_manager_.GetEncryptionHandler()->Init();
   EXPECT_CALL(encryption_observer_,
               OnBootstrapTokenUpdated(_, PASSPHRASE_BOOTSTRAP_TOKEN));
-  EXPECT_CALL(encryption_observer_, OnPassphraseAccepted());
-  EXPECT_CALL(encryption_observer_, OnEncryptionComplete());
-  EXPECT_CALL(encryption_observer_, OnCryptographerStateChanged(_));
+  ExpectPassphraseAcceptance();
   sync_manager_.GetEncryptionHandler()->SetDecryptionPassphrase("explicit");
   EXPECT_EQ(CUSTOM_PASSPHRASE,
             sync_manager_.GetEncryptionHandler()->GetPassphraseType());
@@ -1580,14 +1565,8 @@
   }
   EXPECT_CALL(encryption_observer_,
               OnBootstrapTokenUpdated(_, PASSPHRASE_BOOTSTRAP_TOKEN));
-  EXPECT_CALL(encryption_observer_, OnPassphraseAccepted());
-  EXPECT_CALL(encryption_observer_, OnEncryptionComplete());
-  EXPECT_CALL(encryption_observer_, OnCryptographerStateChanged(_));
-  sync_manager_.GetEncryptionHandler()->SetEncryptionPassphrase(
-      "passphrase",
-      false);
-  EXPECT_EQ(IMPLICIT_PASSPHRASE,
-            sync_manager_.GetEncryptionHandler()->GetPassphraseType());
+  ExpectPassphraseAcceptance();
+  SetImplicitPassphraseAndCheck("passphrase");
   EXPECT_FALSE(EncryptEverythingEnabledForTest());
   {
     ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
@@ -1613,16 +1592,8 @@
   }
   EXPECT_CALL(encryption_observer_,
               OnBootstrapTokenUpdated(_, PASSPHRASE_BOOTSTRAP_TOKEN));
-  EXPECT_CALL(encryption_observer_, OnPassphraseAccepted());
-  EXPECT_CALL(encryption_observer_, OnEncryptionComplete());
-  EXPECT_CALL(encryption_observer_, OnCryptographerStateChanged(_));
-  EXPECT_CALL(encryption_observer_,
-      OnPassphraseTypeChanged(CUSTOM_PASSPHRASE, _));
-  sync_manager_.GetEncryptionHandler()->SetEncryptionPassphrase(
-      "new_passphrase",
-      true);
-  EXPECT_EQ(CUSTOM_PASSPHRASE,
-            sync_manager_.GetEncryptionHandler()->GetPassphraseType());
+  ExpectPassphraseAcceptance();
+  SetCustomPassphraseAndCheck("new_passphrase");
   EXPECT_FALSE(EncryptEverythingEnabledForTest());
   {
     ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
@@ -1828,14 +1799,8 @@
   testing::Mock::VerifyAndClearExpectations(&encryption_observer_);
   EXPECT_CALL(encryption_observer_,
               OnBootstrapTokenUpdated(_, PASSPHRASE_BOOTSTRAP_TOKEN));
-  EXPECT_CALL(encryption_observer_, OnPassphraseAccepted());
-  EXPECT_CALL(encryption_observer_, OnEncryptionComplete());
-  EXPECT_CALL(encryption_observer_, OnCryptographerStateChanged(_));
-  EXPECT_CALL(encryption_observer_,
-      OnPassphraseTypeChanged(CUSTOM_PASSPHRASE, _));
-  sync_manager_.GetEncryptionHandler()->SetEncryptionPassphrase(
-      "new_passphrase",
-      true);
+  ExpectPassphraseAcceptance();
+  SetCustomPassphraseAndCheck("new_passphrase");
   {
     ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
     ReadNode node(&trans);
@@ -2027,16 +1992,8 @@
   testing::Mock::VerifyAndClearExpectations(&encryption_observer_);
   EXPECT_CALL(encryption_observer_,
               OnBootstrapTokenUpdated(_, PASSPHRASE_BOOTSTRAP_TOKEN));
-  EXPECT_CALL(encryption_observer_, OnPassphraseAccepted());
-  EXPECT_CALL(encryption_observer_, OnEncryptionComplete());
-  EXPECT_CALL(encryption_observer_, OnCryptographerStateChanged(_));
-  EXPECT_CALL(encryption_observer_,
-      OnPassphraseTypeChanged(CUSTOM_PASSPHRASE, _));
-  sync_manager_.GetEncryptionHandler()->SetEncryptionPassphrase(
-      "new_passphrase",
-      true);
-  EXPECT_EQ(CUSTOM_PASSPHRASE,
-            sync_manager_.GetEncryptionHandler()->GetPassphraseType());
+  ExpectPassphraseAcceptance();
+  SetCustomPassphraseAndCheck("new_passphrase");
   EXPECT_TRUE(ResetUnsyncedEntry(PASSWORDS, client_tag));
 }
 
diff --git a/sync/protocol/autofill_specifics.proto b/sync/protocol/autofill_specifics.proto
index c613a406..5abb158 100644
--- a/sync/protocol/autofill_specifics.proto
+++ b/sync/protocol/autofill_specifics.proto
@@ -119,6 +119,7 @@
   // Server-generated unique ID string. This is opaque to the client.
   optional string id = 1; 
 
+  optional string recipient_name = 12;
   optional string company_name = 2;
 
   // This is the street address, of which there may be multiple lines. This
diff --git a/sync/protocol/sync.proto b/sync/protocol/sync.proto
index 2c79c22..78f9d40 100644
--- a/sync/protocol/sync.proto
+++ b/sync/protocol/sync.proto
@@ -976,3 +976,21 @@
   // sent with any subsequent ClientToServerMessages.
   optional ChipBag new_bag_of_chips = 14;
 };
+
+// A message to notify the server of certain sync events. Idempotent. Send these
+// to the /event endpoint.
+message EventRequest {
+  optional SyncDisabledEvent sync_disabled = 1;
+};
+
+message EventResponse {
+};
+
+// A message indicating that the sync engine has been disabled on a client.
+message SyncDisabledEvent {
+  // The GUID that identifies the sync client.
+  optional string cache_guid = 1;
+
+  // The store birthday that the client was using before disabling sync.
+  optional string store_birthday = 2;
+};
diff --git a/sync/sync.gyp b/sync/sync.gyp
index ea0d354..1805955 100644
--- a/sync/sync.gyp
+++ b/sync/sync.gyp
@@ -270,7 +270,6 @@
         'internal_api/public/non_blocking_sync_common.h',
         'internal_api/public/read_node.h',
         'internal_api/public/read_transaction.h',
-        'internal_api/public/shutdown_reason.h',
         'internal_api/public/sessions/commit_counters.cc',
         'internal_api/public/sessions/commit_counters.h',
         'internal_api/public/sessions/model_neutral_state.cc',
@@ -283,6 +282,7 @@
         'internal_api/public/sessions/type_debug_info_observer.h',
         'internal_api/public/sessions/update_counters.cc',
         'internal_api/public/sessions/update_counters.h',
+        'internal_api/public/shutdown_reason.h',
         'internal_api/public/sync_auth_provider.h',
         'internal_api/public/sync_context.h',
         'internal_api/public/sync_context_proxy.h',
diff --git a/sync/sync_tests.gypi b/sync/sync_tests.gypi
index bf41fc3..9bb8aa8 100644
--- a/sync/sync_tests.gypi
+++ b/sync/sync_tests.gypi
@@ -66,19 +66,19 @@
         'test/mock_invalidation.h',
         'test/mock_invalidation_tracker.cc',
         'test/mock_invalidation_tracker.h',
-        'test/trackable_mock_invalidation.cc',
-        'test/trackable_mock_invalidation.h',
         'test/null_directory_change_delegate.cc',
         'test/null_directory_change_delegate.h',
         'test/null_transaction_observer.cc',
         'test/null_transaction_observer.h',
-        'test/sessions/test_scoped_session_event_listener.h',
-        'test/sessions/mock_debug_info_getter.h',
         'test/sessions/mock_debug_info_getter.cc',
+        'test/sessions/mock_debug_info_getter.h',
+        'test/sessions/test_scoped_session_event_listener.h',
         'test/test_directory_backing_store.cc',
         'test/test_directory_backing_store.h',
         'test/test_transaction_observer.cc',
         'test/test_transaction_observer.h',
+        'test/trackable_mock_invalidation.cc',
+        'test/trackable_mock_invalidation.h',
         'util/test_unrecoverable_error_handler.cc',
         'util/test_unrecoverable_error_handler.h',
       ],
@@ -218,10 +218,10 @@
         'sync',
       ],
       'sources': [
-        'api/fake_syncable_service.cc',
-        'api/fake_syncable_service.h',
         'api/fake_sync_change_processor.cc',
         'api/fake_sync_change_processor.h',
+        'api/fake_syncable_service.cc',
+        'api/fake_syncable_service.h',
         'api/sync_change_processor_wrapper_for_test.cc',
         'api/sync_change_processor_wrapper_for_test.h',
         'api/sync_error_factory_mock.cc',
diff --git a/sync/sync_unit_tests.isolate b/sync/sync_unit_tests.isolate
new file mode 100644
index 0000000..a29fad05
--- /dev/null
+++ b/sync/sync_unit_tests.isolate
@@ -0,0 +1,17 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+{
+  'conditions': [
+    ['OS=="android"', {
+      'variables': {
+        'files': [
+          '<(PRODUCT_DIR)/locales/en-US.pak',
+        ],
+      },
+    }],
+  ],
+  'includes': [
+    '../third_party/icu/icu.isolate',
+  ],
+}
diff --git a/sync/util/cryptographer_unittest.cc b/sync/util/cryptographer_unittest.cc
index 94a20c8..49d149c 100644
--- a/sync/util/cryptographer_unittest.cc
+++ b/sync/util/cryptographer_unittest.cc
@@ -138,13 +138,7 @@
   EXPECT_EQ(encrypted3.key_name(), encrypted4.key_name());
 }
 
-// Crashes, Bug 55178.
-#if defined(OS_WIN)
-#define MAYBE_EncryptExportDecrypt DISABLED_EncryptExportDecrypt
-#else
-#define MAYBE_EncryptExportDecrypt EncryptExportDecrypt
-#endif
-TEST_F(CryptographerTest, MAYBE_EncryptExportDecrypt) {
+TEST_F(CryptographerTest, EncryptExportDecrypt) {
   sync_pb::EncryptedData nigori;
   sync_pb::EncryptedData encrypted;
 
@@ -264,4 +258,50 @@
   EXPECT_EQ(encrypted_c.key_name(), encrypted_k2.key_name());
 }
 
+// Test verifies that GetBootstrapToken/Bootstrap only transfers default
+// key. Additional call to GetKeys/InstallKeys is needed to transfer keybag
+// to decrypt messages encrypted with old keys.
+TEST_F(CryptographerTest, GetKeysThenInstall) {
+  sync_pb::PasswordSpecificsData original;
+  original.set_origin("http://example.com");
+  original.set_username_value("luser");
+  original.set_password_value("p4ssw0rd");
+
+  // First, encrypt the same value using two different keys.
+  KeyParams params1 = {"localhost", "dummy", "dummy"};
+  EXPECT_TRUE(cryptographer_.AddKey(params1));
+  EXPECT_TRUE(cryptographer_.is_ready());
+
+  sync_pb::EncryptedData encrypted_k1;
+  EXPECT_TRUE(cryptographer_.Encrypt(original, &encrypted_k1));
+
+  KeyParams params2 = {"localhost", "dummy2", "dummy2"};
+  EXPECT_TRUE(cryptographer_.AddKey(params2));
+  EXPECT_TRUE(cryptographer_.is_ready());
+
+  sync_pb::EncryptedData encrypted_k2;
+  EXPECT_TRUE(cryptographer_.Encrypt(original, &encrypted_k2));
+
+  // Then construct second cryptographer and bootstrap it from the first one.
+  Cryptographer another_cryptographer(cryptographer_.encryptor());
+  std::string bootstrap_token;
+  EXPECT_TRUE(cryptographer_.GetBootstrapToken(&bootstrap_token));
+  another_cryptographer.Bootstrap(bootstrap_token);
+
+  // Before key installation, the second cryptographer should only be able
+  // to decrypt using the last key.
+  EXPECT_FALSE(another_cryptographer.CanDecrypt(encrypted_k1));
+  EXPECT_TRUE(another_cryptographer.CanDecrypt(encrypted_k2));
+
+  sync_pb::EncryptedData keys;
+  EXPECT_TRUE(cryptographer_.GetKeys(&keys));
+  ASSERT_TRUE(another_cryptographer.CanDecrypt(keys));
+  another_cryptographer.InstallKeys(keys);
+
+  // Verify that bootstrapped cryptographer decrypts succesfully using
+  // all the keys after key installation.
+  EXPECT_TRUE(another_cryptographer.CanDecrypt(encrypted_k1));
+  EXPECT_TRUE(another_cryptographer.CanDecrypt(encrypted_k2));
+}
+
 }  // namespace syncer
diff --git a/sync/util/nigori_unittest.cc b/sync/util/nigori_unittest.cc
index 88c2461..c7b8d6f 100644
--- a/sync/util/nigori_unittest.cc
+++ b/sync/util/nigori_unittest.cc
@@ -130,13 +130,7 @@
   EXPECT_NE(plaintext, decrypted);
 }
 
-// Crashes, Bug 55180.
-#if defined(OS_WIN)
-#define MAYBE_ExportImport DISABLED_ExportImport
-#else
-#define MAYBE_ExportImport ExportImport
-#endif
-TEST(SyncNigoriTest, MAYBE_ExportImport) {
+TEST(SyncNigoriTest, ExportImport) {
   Nigori nigori1;
   EXPECT_TRUE(nigori1.InitByDerivation("example.com", "username", "password"));
 
diff --git a/testing/android/native_test.gyp b/testing/android/native_test.gyp
index 7d0ebc8..632d6871 100644
--- a/testing/android/native_test.gyp
+++ b/testing/android/native_test.gyp
@@ -16,13 +16,6 @@
             'native_test_launcher.cc',
             'native_test_launcher.h',
           ],
-          'direct_dependent_settings': {
-            'ldflags!': [
-              # JNI_OnLoad is implemented in a .a and we need to
-              # re-export in the .so.
-              '-Wl,--exclude-libs=ALL',
-            ],
-          },
           'dependencies': [
             '../../base/base.gyp:base',
             '../../base/base.gyp:test_support_base',
diff --git a/testing/android/native_test_jni_onload.cc b/testing/android/native_test_jni_onload.cc
index bfab3c9..de42356d 100644
--- a/testing/android/native_test_jni_onload.cc
+++ b/testing/android/native_test_jni_onload.cc
@@ -4,22 +4,16 @@
 
 #include "base/android/base_jni_onload.h"
 #include "base/android/jni_android.h"
-#include "base/android/jni_onload_delegate.h"
+#include "base/bind.h"
 #include "testing/android/native_test_launcher.h"
 
 namespace {
 
-class NativeTestJNIOnLoadDelegate : public base::android::JNIOnLoadDelegate {
- public:
-  bool RegisterJNI(JNIEnv* env) override;
-  bool Init() override;
-};
-
-bool NativeTestJNIOnLoadDelegate::RegisterJNI(JNIEnv* env) {
+bool RegisterJNI(JNIEnv* env) {
   return RegisterNativeTestJNI(env);
 }
 
-bool NativeTestJNIOnLoadDelegate::Init() {
+bool Init() {
   InstallHandlers();
   return true;
 }
@@ -29,11 +23,15 @@
 
 // This is called by the VM when the shared library is first loaded.
 JNI_EXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
-  NativeTestJNIOnLoadDelegate delegate;
-  std::vector<base::android::JNIOnLoadDelegate*> delegates;
-  delegates.push_back(&delegate);
+  std::vector<base::android::RegisterCallback> register_callbacks;
+  register_callbacks.push_back(base::Bind(&RegisterJNI));
 
-  if (!base::android::OnJNIOnLoad(vm, &delegates))
+  if (!base::android::OnJNIOnLoadRegisterJNI(vm, register_callbacks))
+    return -1;
+
+  std::vector<base::android::InitCallback> init_callbacks;
+  init_callbacks.push_back(base::Bind(&Init));
+  if (!base::android::OnJNIOnLoadInit(init_callbacks))
     return -1;
 
   return JNI_VERSION_1_4;
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index 1d15ec1f..71e67adf 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -1048,5 +1048,85 @@
         "script": "nacl_integration.py"
       }
     ]
+  },
+  "ClangToTLinuxASan tester": {
+    "gtest_tests": [
+      "accessibility_unittests",
+      "extensions_browsertests",
+      {
+        "test": "base_unittests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        }
+      },
+      {
+        "test": "browser_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "shards": 5
+        }
+      },
+      "cacheinvalidation_unittests",
+      "cast_unittests",
+      "cc_unittests",
+      "components_unittests",
+      {
+        "test": "content_browsertests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        }
+      },
+      {
+        "test": "content_unittests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        }
+      },
+      {
+        "test": "crypto_unittests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        }
+      },
+      "device_unittests",
+      "display_unittests",
+      "extensions_unittests",
+      "gcm_unit_tests",
+      "gfx_unittests",
+      "google_apis_unittests",
+      "gpu_unittests",
+      {
+        "test": "interactive_ui_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        }
+      },
+      "ipc_tests",
+      "jingle_unittests",
+      "media_unittests",
+      {
+        "test": "net_unittests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "shards": 4
+        }
+      },
+      "ppapi_unittests",
+      "printing_unittests",
+      "remoting_unittests",
+      "sandbox_linux_unittests",
+      "skia_unittests",
+      "sql_unittests",
+      "sync_unit_tests",
+      "ui_base_unittests",
+      {
+        "test": "unit_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "shards": 2
+        }
+      },
+      "url_unittests"
+    ]
   }
 }
diff --git a/testing/buildbot/chromium_win8_trybot.json b/testing/buildbot/chromium_win8_trybot.json
index b4bddd4..c7c32d3 100644
--- a/testing/buildbot/chromium_win8_trybot.json
+++ b/testing/buildbot/chromium_win8_trybot.json
@@ -10,6 +10,9 @@
     "compositor_unittests",
     "content_browsertests",
     "events_unittests",
-    "ui_touch_selection_unittests"
+    "ui_touch_selection_unittests",
+    "sbox_integration_tests",
+    "sbox_validation_tests",
+    "sbox_unittests"
   ]
 }
diff --git a/testing/chromoting/browser_test_commands_linux.txt b/testing/chromoting/browser_test_commands_linux.txt
index d6f2bf5..49f503e9 100644
--- a/testing/chromoting/browser_test_commands_linux.txt
+++ b/testing/chromoting/browser_test_commands_linux.txt
@@ -3,6 +3,9 @@
 /usr/bin/python ../xvfb.py #PROD_DIR# #PROD_DIR#/browser_tests --gtest_filter=RemoteDesktopBrowserTest.MANUAL_Auth --run-manual --ui-test-action-timeout=100000 --webapp-unpacked=#PROD_DIR#/remoting/remoting.webapp --extension-name=Chromoting --accounts-file=../../remoting/tools/internal/test_accounts.json --account-type=gafyd
 /usr/bin/python ../xvfb.py #PROD_DIR# #PROD_DIR#/browser_tests --gtest_filter=RemoteDesktopBrowserTest.MANUAL_Auth --run-manual --ui-test-action-timeout=100000 --webapp-unpacked=#PROD_DIR#/remoting/remoting.webapp --extension-name=Chromoting --accounts-file=../../remoting/tools/internal/test_accounts.json --account-type=non-gmail
 /usr/bin/python ../xvfb.py #PROD_DIR# #PROD_DIR#/browser_tests --gtest_filter=RemoteDesktopBrowserTest.MANUAL_Launch --run-manual --ui-test-action-timeout=100000 --webapp-unpacked=#PROD_DIR#/remoting/remoting.webapp.v2 --extension-name=Chromoting
+/usr/bin/python ../xvfb.py #PROD_DIR# #PROD_DIR#/browser_tests --gtest_filter=RemoteDesktopBrowserTest.MANUAL_Auth --run-manual --ui-test-action-timeout=100000 --webapp-unpacked=#PROD_DIR#/remoting/remoting.webapp.v2 --extension-name=Chromoting --accounts-file=../../remoting/tools/internal/test_accounts.json --account-type=gmail
 /usr/bin/python ../xvfb.py #PROD_DIR# #PROD_DIR#/browser_tests --gtest_filter=Me2MeBrowserTest.MANUAL_Me2Me_Connect_Local_Host --run-manual --ui-test-action-timeout=100000 --webapp-unpacked=#PROD_DIR#/remoting/remoting.webapp --extension-name=Chromoting --accounts-file=../../remoting/tools/internal/test_accounts.json --account-type=gmail --me2me-pin=123456 --override-user-data-dir=/tmp/chromoting_test_profile
+/usr/bin/python ../xvfb.py #PROD_DIR# #PROD_DIR#/browser_tests --gtest_filter=Me2MeBrowserTest.MANUAL_Me2Me_Connect_Local_Host --run-manual --ui-test-action-timeout=100000 --webapp-unpacked=#PROD_DIR#/remoting/remoting.webapp.v2 --extension-name=Chromoting --accounts-file=../../remoting/tools/internal/test_accounts.json --account-type=gmail --me2me-pin=123456 --override-user-data-dir=/tmp/chromoting_test_profile
+/usr/bin/python ../xvfb.py #PROD_DIR# #PROD_DIR#/browser_tests --gtest_filter=Me2MeBrowserTest.MANUAL_Me2Me_v2_Alive_OnLostFocus --run-manual --ui-test-action-timeout=100000 --webapp-unpacked=#PROD_DIR#/remoting/remoting.webapp.v2 --extension-name=Chromoting --accounts-file=../../remoting/tools/internal/test_accounts.json --account-type=gmail --me2me-pin=123456 --override-user-data-dir=/tmp/chromoting_test_profile
 /usr/bin/python ../xvfb.py #PROD_DIR# #PROD_DIR#/browser_tests --gtest_filter=It2MeBrowserTest.MANUAL_Connect --run-manual --ui-test-action-timeout=100000 --webapp-unpacked=#PROD_DIR#/remoting/remoting.webapp --extension-name=Chromoting --accounts-file=../../remoting/tools/internal/test_accounts.json --account-type=gmail --override-user-data-dir=/tmp/chromoting_test_profile
 /usr/bin/python ../xvfb.py #PROD_DIR# #PROD_DIR#/browser_tests --gtest_filter=It2MeBrowserTest.MANUAL_InvalidAccessCode --run-manual --ui-test-action-timeout=100000 --webapp-unpacked=#PROD_DIR#/remoting/remoting.webapp --extension-name=Chromoting --accounts-file=../../remoting/tools/internal/test_accounts.json --account-type=gmail --override-user-data-dir=/tmp/chromoting_test_profile
\ No newline at end of file
diff --git a/testing/commit_queue/config.json b/testing/commit_queue/config.json
index 1b05f1c..da48bda 100644
--- a/testing/commit_queue/config.json
+++ b/testing/commit_queue/config.json
@@ -8,7 +8,8 @@
                         "cast_shell": ["defaulttests"],
                         "cast_shell_apk": ["defaulttests"],
                         "linux_android_rel_ng": ["defaulttests"],
-                        "linux_chromium_asan_rel_ng": ["defaulttests"]
+                        "linux_chromium_asan_rel_ng": ["defaulttests"],
+                        "linux_chromium_clobber_rel_ng": ["defaulttests"]
                     },
                     "tryserver.chromium.mac": {
                         "mac_chromium_gn_rel": ["defaulttests"]
diff --git a/testing/legion/client_controller.py b/testing/legion/client_controller.py
deleted file mode 100755
index dd80c29..0000000
--- a/testing/legion/client_controller.py
+++ /dev/null
@@ -1,51 +0,0 @@
-#!/usr/bin/env python
-# Copyright 2015 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-"""The main client_controller code.
-
-This code is the main entry point for the client machines and handles
-registering with the host server and running the local RPC server.
-"""
-
-import argparse
-import logging
-import socket
-import sys
-import time
-
-#pylint: disable=relative-import
-import client_rpc_server
-import common_lib
-
-
-def main():
-  print ' '.join(sys.argv)
-  common_lib.InitLogging()
-  logging.info('Client controller starting')
-
-  parser = argparse.ArgumentParser()
-  parser.add_argument('--otp',
-                      help='One time token used to authenticate with the host')
-  parser.add_argument('--host',
-                      help='The ip address of the host')
-  parser.add_argument('--idle-timeout', type=int,
-                      default=common_lib.DEFAULT_TIMEOUT_SECS,
-                      help='The idle timeout for the rpc server in seconds')
-  args, _ = parser.parse_known_args()
-
-  logging.info(
-      'Registering with discovery server at %s using OTP %s', args.host,
-      args.otp)
-  server = common_lib.ConnectToServer(args.host).RegisterClient(
-      args.otp, common_lib.MY_IP)
-
-  server = client_rpc_server.RPCServer(args.host, args.idle_timeout)
-
-  server.serve_forever()
-  return 0
-
-
-if __name__ == '__main__':
-  sys.exit(main())
diff --git a/testing/legion/client_lib.py b/testing/legion/client_lib.py
deleted file mode 100644
index 4656cac..0000000
--- a/testing/legion/client_lib.py
+++ /dev/null
@@ -1,211 +0,0 @@
-# Copyright 2015 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-"""Defines the client library."""
-
-import argparse
-import datetime
-import logging
-import os
-import socket
-import subprocess
-import sys
-import tempfile
-import threading
-import xmlrpclib
-
-#pylint: disable=relative-import
-import common_lib
-
-ISOLATE_PY = os.path.join(common_lib.SWARMING_DIR, 'isolate.py')
-SWARMING_PY = os.path.join(common_lib.SWARMING_DIR, 'swarming.py')
-
-
-class Error(Exception):
-  pass
-
-
-class ConnectionTimeoutError(Error):
-  pass
-
-
-class ClientController(object):
-  """Creates, configures, and controls a client machine."""
-
-  _client_count = 0
-  _controllers = []
-
-  def __init__(self, isolate_file, config_vars, dimensions, priority=100,
-               idle_timeout_secs=common_lib.DEFAULT_TIMEOUT_SECS,
-               connection_timeout_secs=common_lib.DEFAULT_TIMEOUT_SECS,
-               verbosity='ERROR', name=None):
-    assert isinstance(config_vars, dict)
-    assert isinstance(dimensions, dict)
-    type(self)._controllers.append(self)
-    type(self)._client_count += 1
-    self.verbosity = verbosity
-    self._name = name or 'Client%d' % type(self)._client_count
-    self._priority = priority
-    self._isolate_file = isolate_file
-    self._isolated_file = isolate_file + 'd'
-    self._idle_timeout_secs = idle_timeout_secs
-    self._config_vars = config_vars
-    self._dimensions = dimensions
-    self._connect_event = threading.Event()
-    self._connected = False
-    self._ip_address = None
-    self._otp = self._CreateOTP()
-    self._rpc = None
-
-    parser = argparse.ArgumentParser()
-    parser.add_argument('--isolate-server')
-    parser.add_argument('--swarming-server')
-    parser.add_argument('--client-connection-timeout-secs',
-                        default=common_lib.DEFAULT_TIMEOUT_SECS)
-    args, _ = parser.parse_known_args()
-
-    self._isolate_server = args.isolate_server
-    self._swarming_server = args.swarming_server
-    self._connection_timeout_secs = (connection_timeout_secs or
-                                    args.client_connection_timeout_secs)
-
-  @property
-  def name(self):
-    return self._name
-
-  @property
-  def otp(self):
-    return self._otp
-
-  @property
-  def connected(self):
-    return self._connected
-
-  @property
-  def connect_event(self):
-    return self._connect_event
-
-  @property
-  def rpc(self):
-    return self._rpc
-
-  @property
-  def verbosity(self):
-    return self._verbosity
-
-  @verbosity.setter
-  def verbosity(self, level):
-    """Sets the verbosity level as a string.
-
-    Either a string ('INFO', 'DEBUG', etc) or a logging level (logging.INFO,
-    logging.DEBUG, etc) is allowed.
-    """
-    assert isinstance(level, (str, int))
-    if isinstance(level, int):
-      level = logging.getLevelName(level)
-    self._verbosity = level  #pylint: disable=attribute-defined-outside-init
-
-  @classmethod
-  def ReleaseAllControllers(cls):
-    for controller in cls._controllers:
-      controller.Release()
-
-  def _CreateOTP(self):
-    """Creates the OTP."""
-    host_name = socket.gethostname()
-    test_name = os.path.basename(sys.argv[0])
-    creation_time = datetime.datetime.utcnow()
-    otp = 'client:%s-host:%s-test:%s-creation:%s' % (
-        self._name, host_name, test_name, creation_time)
-    return otp
-
-  def Create(self):
-    """Creates the client machine."""
-    logging.info('Creating %s', self.name)
-    self._connect_event.clear()
-    self._ExecuteIsolate()
-    self._ExecuteSwarming()
-
-  def WaitForConnection(self):
-    """Waits for the client machine to connect.
-
-    Raises:
-      ConnectionTimeoutError if the client doesn't connect in time.
-    """
-    logging.info('Waiting for %s to connect with a timeout of %d seconds',
-                 self._name, self._connection_timeout_secs)
-    self._connect_event.wait(self._connection_timeout_secs)
-    if not self._connect_event.is_set():
-      raise ConnectionTimeoutError('%s failed to connect' % self.name)
-
-  def Release(self):
-    """Quits the client's RPC server so it can release the machine."""
-    if self._rpc is not None and self._connected:
-      logging.info('Releasing %s', self._name)
-      try:
-        self._rpc.Quit()
-      except (socket.error, xmlrpclib.Fault):
-        logging.error('Unable to connect to %s to call Quit', self.name)
-      self._rpc = None
-      self._connected = False
-
-  def _ExecuteIsolate(self):
-    """Executes isolate.py."""
-    cmd = [
-        'python',
-        ISOLATE_PY,
-        'archive',
-        '--isolate', self._isolate_file,
-        '--isolated', self._isolated_file,
-        ]
-
-    if self._isolate_server:
-      cmd.extend(['--isolate-server', self._isolate_server])
-    for key, value in self._config_vars.iteritems():
-      cmd.extend(['--config-var', key, value])
-
-    self._ExecuteProcess(cmd)
-
-  def _ExecuteSwarming(self):
-    """Executes swarming.py."""
-    cmd = [
-        'python',
-        SWARMING_PY,
-        'trigger',
-        self._isolated_file,
-        '--priority', str(self._priority),
-        ]
-
-    if self._isolate_server:
-      cmd.extend(['--isolate-server', self._isolate_server])
-    if self._swarming_server:
-      cmd.extend(['--swarming', self._swarming_server])
-    for key, value in self._dimensions.iteritems():
-      cmd.extend(['--dimension', key, value])
-
-    cmd.extend([
-        '--',
-        '--host', common_lib.MY_IP,
-        '--otp', self._otp,
-        '--verbosity', self._verbosity,
-        '--idle-timeout', str(self._idle_timeout_secs),
-        ])
-
-    self._ExecuteProcess(cmd)
-
-  def _ExecuteProcess(self, cmd):
-    """Executes a process, waits for it to complete, and checks for success."""
-    logging.debug('Running %s', ' '.join(cmd))
-    p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
-    _, stderr = p.communicate()
-    if p.returncode != 0:
-      raise Error(stderr)
-
-  def OnConnect(self, ip_address):
-    """Receives client ip address on connection."""
-    self._ip_address = ip_address
-    self._connected = True
-    self._rpc = common_lib.ConnectToServer(self._ip_address)
-    logging.info('%s connected from %s', self._name, ip_address)
-    self._connect_event.set()
diff --git a/testing/legion/client_rpc_methods.py b/testing/legion/client_rpc_methods.py
deleted file mode 100644
index e43a7d8..0000000
--- a/testing/legion/client_rpc_methods.py
+++ /dev/null
@@ -1,151 +0,0 @@
-# Copyright 2015 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-"""Defines the client RPC methods."""
-
-import os
-import sys
-import logging
-import threading
-
-#pylint: disable=relative-import
-import common_lib
-
-# Map swarming_client to use subprocess42
-sys.path.append(common_lib.SWARMING_DIR)
-
-from utils import subprocess42
-
-
-class RPCMethods(object):
-  """Class exposing RPC methods."""
-
-  _dotted_whitelist = ['subprocess']
-
-  def __init__(self, server):
-    self._server = server
-    self.subprocess = Subprocess
-
-  def _dispatch(self, method, params):
-    obj = self
-    if '.' in method:
-      # Allow only white listed dotted names
-      name, method = method.split('.')
-      assert name in self._dotted_whitelist
-      obj = getattr(self, name)
-    return getattr(obj, method)(*params)
-
-  def Echo(self, message):
-    """Simple RPC method to print and return a message."""
-    logging.info('Echoing %s', message)
-    return 'echo %s' % str(message)
-
-  def Quit(self):
-    """Call _server.shutdown in another thread.
-
-    This is needed because server.shutdown waits for the server to actually
-    quit. However the server cannot shutdown until it completes handling this
-    call. Calling this in the same thread results in a deadlock.
-    """
-    t = threading.Thread(target=self._server.shutdown)
-    t.start()
-
-
-class Subprocess(object):
-  """Implements a server-based non-blocking subprocess.
-
-  This non-blocking subprocess allows the caller to continue operating while
-  also able to interact with this subprocess based on a key returned to
-  the caller at the time of creation.
-  """
-
-  _processes = {}
-  _process_next_id = 0
-  _creation_lock = threading.Lock()
-
-  def __init__(self, cmd):
-    self.proc = subprocess42.Popen(cmd, stdout=subprocess42.PIPE,
-                                   stderr=subprocess42.PIPE)
-    self.stdout = ''
-    self.stderr = ''
-    self.data_lock = threading.Lock()
-    threading.Thread(target=self._run).start()
-
-  def _run(self):
-    for pipe, data in self.proc.yield_any():
-      with self.data_lock:
-        if pipe == 'stdout':
-          self.stdout += data
-        else:
-          self.stderr += data
-
-  @classmethod
-  def Popen(cls, cmd):
-    with cls._creation_lock:
-      key = 'Process%d' % cls._process_next_id
-      cls._process_next_id += 1
-    logging.debug('Creating process %s', key)
-    process = cls(cmd)
-    cls._processes[key] = process
-    return key
-
-  @classmethod
-  def Terminate(cls, key):
-    logging.debug('Terminating and deleting process %s', key)
-    return cls._processes.pop(key).proc.terminate()
-
-  @classmethod
-  def Kill(cls, key):
-    logging.debug('Killing and deleting process %s', key)
-    return cls._processes.pop(key).proc.kill()
-
-  @classmethod
-  def Delete(cls, key):
-    logging.debug('Deleting process %s', key)
-    cls._processes.pop(key)
-
-  @classmethod
-  def GetReturncode(cls, key):
-    return cls._processes[key].proc.returncode
-
-  @classmethod
-  def ReadStdout(cls, key):
-    """Returns all stdout since the last call to ReadStdout.
-
-    This call allows the user to read stdout while the process is running.
-    However each call will flush the local stdout buffer. In order to make
-    multiple calls to ReadStdout and to retain the entire output the results
-    of this call will need to be buffered in the calling code.
-    """
-    proc = cls._processes[key]
-    with proc.data_lock:
-      # Perform a "read" on the stdout data
-      stdout = proc.stdout
-      proc.stdout = ''
-    return stdout
-
-  @classmethod
-  def ReadStderr(cls, key):
-    """Returns all stderr read since the last call to ReadStderr.
-
-    See ReadStdout for additional details.
-    """
-    proc = cls._processes[key]
-    with proc.data_lock:
-      # Perform a "read" on the stderr data
-      stderr = proc.stderr
-      proc.stderr = ''
-    return stderr
-
-  @classmethod
-  def Wait(cls, key):
-    return cls._processes[key].proc.wait()
-
-  @classmethod
-  def Poll(cls, key):
-    return cls._processes[key].proc.poll()
-
-  @classmethod
-  def GetPid(cls, key):
-    return cls._processes[key].proc.pid
diff --git a/testing/legion/client_rpc_server.py b/testing/legion/client_rpc_server.py
deleted file mode 100644
index 7a5f565..0000000
--- a/testing/legion/client_rpc_server.py
+++ /dev/null
@@ -1,128 +0,0 @@
-# Copyright 2015 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-"""The client RPC server code.
-
-This server is an XML-RPC server which serves code from
-client_rpc_methods.RPCMethods.
-
-This server will run until shutdown is called on the server object. This can
-be achieved in 2 ways:
-
-- Calling the Quit RPC method defined in RPCMethods
-- Not receiving any calls within the idle_timeout_secs time.
-"""
-
-import logging
-import threading
-import time
-import xmlrpclib
-import SimpleXMLRPCServer
-import SocketServer
-
-#pylint: disable=relative-import
-import client_rpc_methods
-import common_lib
-
-
-class RequestHandler(SimpleXMLRPCServer.SimpleXMLRPCRequestHandler):
-  """Restricts access to only specified IP address.
-
-  This call assumes the server is RPCServer.
-  """
-
-  def do_POST(self):
-    """Verifies the client is authorized to perform RPCs."""
-    if self.client_address[0] != self.server.authorized_address:
-      logging.error('Received unauthorized RPC request from %s',
-                    self.client_address[0])
-      self.send_response(403)
-      response = 'Forbidden'
-      self.send_header('Content-type', 'text/plain')
-      self.send_header('Content-length', str(len(response)))
-      self.end_headers()
-      self.wfile.write(response)
-    else:
-      return SimpleXMLRPCServer.SimpleXMLRPCRequestHandler.do_POST(self)
-
-
-class RPCServer(SimpleXMLRPCServer.SimpleXMLRPCServer,
-                SocketServer.ThreadingMixIn):
-  """Restricts all endpoints to only specified IP addresses."""
-
-  def __init__(self, authorized_address,
-               idle_timeout_secs=common_lib.DEFAULT_TIMEOUT_SECS):
-    SimpleXMLRPCServer.SimpleXMLRPCServer.__init__(
-        self, (common_lib.SERVER_ADDRESS, common_lib.SERVER_PORT),
-        allow_none=True, logRequests=False,
-        requestHandler=RequestHandler)
-
-    self.authorized_address = authorized_address
-    self.idle_timeout_secs = idle_timeout_secs
-    self.register_instance(client_rpc_methods.RPCMethods(self))
-
-    self._shutdown_requested_event = threading.Event()
-    self._rpc_received_event = threading.Event()
-    self._idle_thread = threading.Thread(target=self._CheckForIdleQuit)
-
-  def shutdown(self):
-    """Shutdown the server.
-
-    This overloaded method sets the _shutdown_requested_event to allow the
-    idle timeout thread to quit.
-    """
-    self._shutdown_requested_event.set()
-    SimpleXMLRPCServer.SimpleXMLRPCServer.shutdown(self)
-    logging.info('Server shutdown complete')
-
-  def serve_forever(self, poll_interval=0.5):
-    """Serve forever.
-
-    This overloaded method starts the idle timeout thread before calling
-    serve_forever. This ensures the idle timer thread doesn't get started
-    without the server running.
-
-    Args:
-      poll_interval: The interval to poll for shutdown.
-    """
-    logging.info('RPC server starting')
-    self._idle_thread.start()
-    SimpleXMLRPCServer.SimpleXMLRPCServer.serve_forever(self, poll_interval)
-
-  def _dispatch(self, method, params):
-    """Dispatch the call to the correct method with the provided params.
-
-    This overloaded method adds logging to help trace connection and
-    call problems.
-
-    Args:
-      method: The method name to call.
-      params: A tuple of parameters to pass.
-
-    Returns:
-      The result of the parent class' _dispatch method.
-    """
-    logging.debug('Calling %s%s', method, params)
-    self._rpc_received_event.set()
-    return SimpleXMLRPCServer.SimpleXMLRPCServer._dispatch(self, method, params)
-
-  def _CheckForIdleQuit(self):
-    """Check for, and exit, if the server is idle for too long.
-
-    This method must be run in a separate thread to avoid a deadlock when
-    calling server.shutdown.
-    """
-    timeout = time.time() + self.idle_timeout_secs
-    while time.time() < timeout:
-      if self._shutdown_requested_event.is_set():
-        # An external source called shutdown()
-        return
-      elif self._rpc_received_event.is_set():
-        logging.debug('Resetting the idle timeout')
-        timeout = time.time() + self.idle_timeout_secs
-        self._rpc_received_event.clear()
-      time.sleep(1)
-    # We timed out, kill the server
-    logging.warning('Shutting down the server due to the idle timeout')
-    self.shutdown()
diff --git a/testing/legion/common_lib.py b/testing/legion/common_lib.py
index 2f527ea1..c752e0f 100644
--- a/testing/legion/common_lib.py
+++ b/testing/legion/common_lib.py
@@ -2,7 +2,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-"""Common library methods used by both host and client controllers."""
+"""Common library methods used by both coordinator and task machines."""
 
 import argparse
 import logging
diff --git a/testing/legion/discovery_server.py b/testing/legion/discovery_server.py
deleted file mode 100644
index 94786ce..0000000
--- a/testing/legion/discovery_server.py
+++ /dev/null
@@ -1,55 +0,0 @@
-# Copyright 2015 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-"""The discovery server used to register clients.
-
-The discovery server is started by the host controller and allows the clients
-to register themselves when they start. Authentication of the client controllers
-is based on an OTP passed to the client controller binary on startup.
-"""
-
-import logging
-import threading
-import xmlrpclib
-import SimpleXMLRPCServer
-
-#pylint: disable=relative-import
-import common_lib
-
-
-class DiscoveryServer(object):
-  """Discovery server run on the host."""
-
-  def __init__(self):
-    self._expected_clients = {}
-    self._rpc_server = None
-    self._thread = None
-
-  def _RegisterClientRPC(self, otp, ip):
-    """The RPC used by a client to register with the discovery server."""
-    assert otp in self._expected_clients
-    cb = self._expected_clients.pop(otp)
-    cb(ip)
-
-  def RegisterClientCallback(self, otp, callback):
-    """Registers a callback associated with an OTP."""
-    assert callable(callback)
-    self._expected_clients[otp] = callback
-
-  def Start(self):
-    """Starts the discovery server."""
-    logging.debug('Starting discovery server')
-    self._rpc_server = SimpleXMLRPCServer.SimpleXMLRPCServer(
-        (common_lib.SERVER_ADDRESS, common_lib.SERVER_PORT),
-        allow_none=True, logRequests=False)
-    self._rpc_server.register_function(
-        self._RegisterClientRPC, 'RegisterClient')
-    self._thread = threading.Thread(target=self._rpc_server.serve_forever)
-    self._thread.start()
-
-  def Shutdown(self):
-    """Shuts the discovery server down."""
-    if self._thread and self._thread.is_alive():
-      logging.debug('Shutting down discovery server')
-      self._rpc_server.shutdown()
diff --git a/testing/legion/examples/hello_world/client_test.isolate b/testing/legion/examples/hello_world/client_test.isolate
deleted file mode 100644
index 7135ef2..0000000
--- a/testing/legion/examples/hello_world/client_test.isolate
+++ /dev/null
@@ -1,23 +0,0 @@
-# Copyright 2015 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-{
-  'includes': [
-    '../../legion.isolate'
-  ],
-  'conditions': [
-    ['multi_machine == 1', {
-      'variables': {
-        'command': [
-          'python',
-          '../../client_controller.py',
-        ],
-        'files': [
-          'client_test.py',
-          'client_test.isolate'
-        ],
-      },
-    }],
-  ],
-}
diff --git a/testing/legion/examples/hello_world/host_test.isolate b/testing/legion/examples/hello_world/host_test.isolate
deleted file mode 100644
index da4ee4e3..0000000
--- a/testing/legion/examples/hello_world/host_test.isolate
+++ /dev/null
@@ -1,22 +0,0 @@
-# Copyright 2015 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-{
-  'includes': [
-    '../../legion.isolate',
-    'client_test.isolate'
-  ],
-  'conditions': [
-    ['multi_machine == 1', {
-      'variables': {
-        'command': [
-          'host_test.py',
-        ],
-        'files': [
-          'host_test.py',
-        ],
-      },
-    }],
-  ]
-}
diff --git a/testing/legion/examples/hello_world/host_test.py b/testing/legion/examples/hello_world/host_test.py
deleted file mode 100755
index 7a7875b..0000000
--- a/testing/legion/examples/hello_world/host_test.py
+++ /dev/null
@@ -1,77 +0,0 @@
-#!/usr/bin/env python
-# Copyright 2015 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-"""A simple host test module.
-
-This module runs on the host machine and is responsible for creating 2
-client machines, waiting for them, and running RPC calls on them.
-"""
-
-# Map the legion directory so we can import the host controller.
-import sys
-sys.path.append('../../')
-
-import logging
-import time
-
-import host_controller
-
-
-class ExampleController(host_controller.HostController):
-  """A simple example controller for a test."""
-
-  def __init__(self):
-    super(ExampleController, self).__init__()
-    self.client1 = None
-    self.client2 = None
-
-  def CreateClient(self):
-    """Create a client object and set the proper values."""
-    client = self.NewClient(
-        isolate_file='client_test.isolate',
-        config_vars={'multi_machine': '1'},
-        dimensions={'os': 'legion-linux'}, priority=200,
-        idle_timeout_secs=90, connection_timeout_secs=90,
-        verbosity=logging.INFO)
-    client.Create()
-    return client
-
-  def SetUp(self):
-    """Create the client machines and wait until they connect.
-
-    In this call the actual creation of the client machines is done in parallel
-    by the system. The WaitForConnect calls are performed in series but will
-    return as soon as the clients connect.
-    """
-    self.client1 = self.CreateClient()
-    self.client2 = self.CreateClient()
-    self.client1.WaitForConnection()
-    self.client2.WaitForConnection()
-
-  def Task(self):
-    """Main method to run the task code."""
-    self.CallEcho(self.client1)
-    self.CallEcho(self.client2)
-    self.CallClientTest(self.client1)
-    self.CallClientTest(self.client2)
-
-  def CallEcho(self, client):
-    """Call rpc.Echo on a client."""
-    logging.info('Calling Echo on %s', client.name)
-    logging.info(client.rpc.Echo(client.name))
-
-  def CallClientTest(self, client):
-    """Call client_test.py name on a client."""
-    logging.info('Calling Subprocess to run "./client_test.py %s"', client.name)
-    proc = client.rpc.subprocess.Popen(['./client_test.py', client.name])
-    client.rpc.subprocess.Wait(proc)
-    retcode = client.rpc.subprocess.GetReturncode(proc)
-    stdout = client.rpc.subprocess.ReadStdout(proc)
-    stderr = client.rpc.subprocess.ReadStderr(proc)
-    logging.info('retcode: %s, stdout: %s, stderr: %s', retcode, stdout, stderr)
-
-
-if __name__ == '__main__':
-  ExampleController().RunController()
diff --git a/testing/legion/examples/hello_world/task_test.isolate b/testing/legion/examples/hello_world/task_test.isolate
new file mode 100644
index 0000000..1322f310
--- /dev/null
+++ b/testing/legion/examples/hello_world/task_test.isolate
@@ -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.
+
+{
+  'includes': [
+    '../../legion.isolate'
+  ],
+  'conditions': [
+    ['multi_machine == 1', {
+      'variables': {
+        'command': [
+          'python',
+          '../../run_task.py',
+        ],
+        'files': [
+          'task_test.isolate',
+          'task_test.py',
+        ],
+      },
+    }],
+  ],
+}
diff --git a/testing/legion/examples/hello_world/client_test.py b/testing/legion/examples/hello_world/task_test.py
similarity index 100%
rename from testing/legion/examples/hello_world/client_test.py
rename to testing/legion/examples/hello_world/task_test.py
diff --git a/testing/legion/examples/subprocess/client.isolate b/testing/legion/examples/subprocess/client.isolate
deleted file mode 100644
index 611562c..0000000
--- a/testing/legion/examples/subprocess/client.isolate
+++ /dev/null
@@ -1,22 +0,0 @@
-# Copyright 2015 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-{
-  'includes': [
-    '../../legion.isolate'
-  ],
-  'conditions': [
-    ['multi_machine == 1', {
-      'variables': {
-        'command': [
-          'python',
-          '../../client_controller.py',
-        ],
-        'files': [
-          'client.isolate'
-        ],
-      },
-    }],
-  ],
-}
diff --git a/testing/legion/examples/subprocess/subprocess_test.isolate b/testing/legion/examples/subprocess/subprocess_test.isolate
index 5b20167..6b4561fe 100644
--- a/testing/legion/examples/subprocess/subprocess_test.isolate
+++ b/testing/legion/examples/subprocess/subprocess_test.isolate
@@ -5,7 +5,7 @@
 {
   'includes': [
     '../../legion.isolate',
-    'client.isolate'
+    'task.isolate'
   ],
   'conditions': [
     ['multi_machine == 1', {
diff --git a/testing/legion/examples/subprocess/subprocess_test.py b/testing/legion/examples/subprocess/subprocess_test.py
index 6d8ce870..28e3fb8 100755
--- a/testing/legion/examples/subprocess/subprocess_test.py
+++ b/testing/legion/examples/subprocess/subprocess_test.py
@@ -13,29 +13,29 @@
 import time
 import xmlrpclib
 
-import host_controller
+import test_controller
 
 
-class ExampleController(host_controller.HostController):
+class ExampleTestController(test_controller.TestController):
   """An example controller using the remote subprocess functions."""
 
   def __init__(self):
-    super(ExampleController, self).__init__()
-    self.client = None
+    super(ExampleTestController, self).__init__()
+    self.task = None
 
   def SetUp(self):
-    """Creates the client machine and waits until it connects."""
-    self.client = self.NewClient(
-        isolate_file='client.isolate',
+    """Creates the task machine and waits until it connects."""
+    self.task = self.CreateNewTask(
+        isolate_file='task.isolate',
         config_vars={'multi_machine': '1'},
         dimensions={'os': 'legion-linux'},
         idle_timeout_secs=90, connection_timeout_secs=90,
         verbosity=logging.DEBUG)
-    self.client.Create()
-    self.client.WaitForConnection()
+    self.task.Create()
+    self.task.WaitForConnection()
 
-  def Task(self):
-    """Main method to run the task code."""
+  def RunTest(self):
+    """Main method to run the test code."""
     self.TestLs()
     self.TestTerminate()
     self.TestMultipleProcesses()
@@ -43,37 +43,37 @@
   def TestMultipleProcesses(self):
     start = time.time()
 
-    sleep20 = self.client.rpc.subprocess.Popen(['sleep', '20'])
-    sleep10 = self.client.rpc.subprocess.Popen(['sleep', '10'])
+    sleep20 = self.task.rpc.subprocess.Popen(['sleep', '20'])
+    sleep10 = self.task.rpc.subprocess.Popen(['sleep', '10'])
 
-    self.client.rpc.subprocess.Wait(sleep10)
+    self.task.rpc.subprocess.Wait(sleep10)
     elapsed = time.time() - start
     assert elapsed >= 10 and elapsed < 11
 
-    self.client.rpc.subprocess.Wait(sleep20)
+    self.task.rpc.subprocess.Wait(sleep20)
     elapsed = time.time() - start
     assert elapsed >= 20
 
-    self.client.rpc.subprocess.Delete(sleep20)
-    self.client.rpc.subprocess.Delete(sleep10)
+    self.task.rpc.subprocess.Delete(sleep20)
+    self.task.rpc.subprocess.Delete(sleep10)
 
   def TestTerminate(self):
     start = time.time()
-    proc = self.client.rpc.subprocess.Popen(['sleep', '20'])
-    self.client.rpc.subprocess.Terminate(proc)  # Implicitly deleted
+    proc = self.task.rpc.subprocess.Popen(['sleep', '20'])
+    self.task.rpc.subprocess.Terminate(proc)  # Implicitly deleted
     try:
-      self.client.rpc.subprocess.Wait(proc)
+      self.task.rpc.subprocess.Wait(proc)
     except xmlrpclib.Fault:
       pass
     assert time.time() - start < 20
 
   def TestLs(self):
-    proc = self.client.rpc.subprocess.Popen(['ls'])
-    self.client.rpc.subprocess.Wait(proc)
-    assert self.client.rpc.subprocess.GetReturncode(proc) == 0
-    assert 'client.isolate' in self.client.rpc.subprocess.ReadStdout(proc)
-    self.client.rpc.subprocess.Delete(proc)
+    proc = self.task.rpc.subprocess.Popen(['ls'])
+    self.task.rpc.subprocess.Wait(proc)
+    assert self.task.rpc.subprocess.GetReturncode(proc) == 0
+    assert 'task.isolate' in self.task.rpc.subprocess.ReadStdout(proc)
+    self.task.rpc.subprocess.Delete(proc)
 
 
 if __name__ == '__main__':
-  ExampleController().RunController()
+  ExampleTestController().RunController()
diff --git a/testing/legion/examples/subprocess/task.isolate b/testing/legion/examples/subprocess/task.isolate
new file mode 100644
index 0000000..534275df
--- /dev/null
+++ b/testing/legion/examples/subprocess/task.isolate
@@ -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.
+
+{
+  'includes': [
+    '../../legion.isolate'
+  ],
+  'conditions': [
+    ['multi_machine == 1', {
+      'variables': {
+        'command': [
+          'python',
+          '../../run_task.py',
+        ],
+        'files': [
+          'task.isolate'
+        ],
+      },
+    }],
+  ],
+}
diff --git a/testing/legion/host_controller.py b/testing/legion/host_controller.py
deleted file mode 100644
index dadcba4..0000000
--- a/testing/legion/host_controller.py
+++ /dev/null
@@ -1,70 +0,0 @@
-# Copyright 2015 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-"""Defines the host controller base library.
-
-This module is the basis on which host controllers are built and executed.
-"""
-
-import logging
-import sys
-
-#pylint: disable=relative-import
-import client_lib
-import common_lib
-import discovery_server
-
-
-class HostController(object):
-  """The base host controller class."""
-
-  def __init__(self):
-    self._discovery_server = discovery_server.DiscoveryServer()
-
-  def SetUp(self):
-    """Setup method used by the subclass."""
-    pass
-
-  def Task(self):
-    """Main task method used by the subclass."""
-    pass
-
-  def TearDown(self):
-    """Teardown method used by the subclass."""
-    pass
-
-  def NewClient(self, *args, **kwargs):
-    controller = client_lib.ClientController(*args, **kwargs)
-    self._discovery_server.RegisterClientCallback(
-        controller.otp, controller.OnConnect)
-    return controller
-
-  def RunController(self):
-    """Main entry point for the controller."""
-    print ' '.join(sys.argv)
-    common_lib.InitLogging()
-    self._discovery_server.Start()
-
-    error = None
-    tb = None
-    try:
-      self.SetUp()
-      self.Task()
-    except Exception as e:
-      # Defer raising exceptions until after TearDown and _TearDown are called.
-      error = e
-      tb = sys.exc_info()[-1]
-    try:
-      self.TearDown()
-    except Exception as e:
-      # Defer raising exceptions until after _TearDown is called.
-      # Note that an error raised here will obscure any errors raised
-      # previously.
-      error = e
-      tb = sys.exc_info()[-1]
-
-    self._discovery_server.Shutdown()
-    client_lib.ClientController.ReleaseAllControllers()
-    if error:
-      raise error, None, tb  #pylint: disable=raising-bad-type
diff --git a/testing/legion/legion.isolate b/testing/legion/legion.isolate
index 463764d..774b27a 100644
--- a/testing/legion/legion.isolate
+++ b/testing/legion/legion.isolate
@@ -6,14 +6,14 @@
   'variables': {
     'files': [
       '__init__.py',
-      'client_controller.py',
-      'client_lib.py',
-      'client_rpc_methods.py',
-      'client_rpc_server.py',
       'common_lib.py',
-      'discovery_server.py',
-      'host_controller.py',
       'legion.isolate',
+      'rpc_methods.py',
+      'rpc_server.py',
+      'run_task.py',
+      'task_controller.py',
+      'task_registration_server.py',
+      'test_controller.py',
       '../../tools/swarming_client/',
     ],
   },
diff --git a/testing/legion/rpc_methods.py b/testing/legion/rpc_methods.py
new file mode 100644
index 0000000..7f17e2387
--- /dev/null
+++ b/testing/legion/rpc_methods.py
@@ -0,0 +1,151 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Defines the task RPC methods."""
+
+import os
+import sys
+import logging
+import threading
+
+#pylint: disable=relative-import
+import common_lib
+
+# Map swarming_client to use subprocess42
+sys.path.append(common_lib.SWARMING_DIR)
+
+from utils import subprocess42
+
+
+class RPCMethods(object):
+  """Class exposing RPC methods."""
+
+  _dotted_whitelist = ['subprocess']
+
+  def __init__(self, server):
+    self._server = server
+    self.subprocess = Subprocess
+
+  def _dispatch(self, method, params):
+    obj = self
+    if '.' in method:
+      # Allow only white listed dotted names
+      name, method = method.split('.')
+      assert name in self._dotted_whitelist
+      obj = getattr(self, name)
+    return getattr(obj, method)(*params)
+
+  def Echo(self, message):
+    """Simple RPC method to print and return a message."""
+    logging.info('Echoing %s', message)
+    return 'echo %s' % str(message)
+
+  def Quit(self):
+    """Call _server.shutdown in another thread.
+
+    This is needed because server.shutdown waits for the server to actually
+    quit. However the server cannot shutdown until it completes handling this
+    call. Calling this in the same thread results in a deadlock.
+    """
+    t = threading.Thread(target=self._server.shutdown)
+    t.start()
+
+
+class Subprocess(object):
+  """Implements a server-based non-blocking subprocess.
+
+  This non-blocking subprocess allows the caller to continue operating while
+  also able to interact with this subprocess based on a key returned to
+  the caller at the time of creation.
+  """
+
+  _processes = {}
+  _process_next_id = 0
+  _creation_lock = threading.Lock()
+
+  def __init__(self, cmd):
+    self.proc = subprocess42.Popen(cmd, stdout=subprocess42.PIPE,
+                                   stderr=subprocess42.PIPE)
+    self.stdout = ''
+    self.stderr = ''
+    self.data_lock = threading.Lock()
+    threading.Thread(target=self._run).start()
+
+  def _run(self):
+    for pipe, data in self.proc.yield_any():
+      with self.data_lock:
+        if pipe == 'stdout':
+          self.stdout += data
+        else:
+          self.stderr += data
+
+  @classmethod
+  def Popen(cls, cmd):
+    with cls._creation_lock:
+      key = 'Process%d' % cls._process_next_id
+      cls._process_next_id += 1
+    logging.debug('Creating process %s', key)
+    process = cls(cmd)
+    cls._processes[key] = process
+    return key
+
+  @classmethod
+  def Terminate(cls, key):
+    logging.debug('Terminating and deleting process %s', key)
+    return cls._processes.pop(key).proc.terminate()
+
+  @classmethod
+  def Kill(cls, key):
+    logging.debug('Killing and deleting process %s', key)
+    return cls._processes.pop(key).proc.kill()
+
+  @classmethod
+  def Delete(cls, key):
+    logging.debug('Deleting process %s', key)
+    cls._processes.pop(key)
+
+  @classmethod
+  def GetReturncode(cls, key):
+    return cls._processes[key].proc.returncode
+
+  @classmethod
+  def ReadStdout(cls, key):
+    """Returns all stdout since the last call to ReadStdout.
+
+    This call allows the user to read stdout while the process is running.
+    However each call will flush the local stdout buffer. In order to make
+    multiple calls to ReadStdout and to retain the entire output the results
+    of this call will need to be buffered in the calling code.
+    """
+    proc = cls._processes[key]
+    with proc.data_lock:
+      # Perform a "read" on the stdout data
+      stdout = proc.stdout
+      proc.stdout = ''
+    return stdout
+
+  @classmethod
+  def ReadStderr(cls, key):
+    """Returns all stderr read since the last call to ReadStderr.
+
+    See ReadStdout for additional details.
+    """
+    proc = cls._processes[key]
+    with proc.data_lock:
+      # Perform a "read" on the stderr data
+      stderr = proc.stderr
+      proc.stderr = ''
+    return stderr
+
+  @classmethod
+  def Wait(cls, key):
+    return cls._processes[key].proc.wait()
+
+  @classmethod
+  def Poll(cls, key):
+    return cls._processes[key].proc.poll()
+
+  @classmethod
+  def GetPid(cls, key):
+    return cls._processes[key].proc.pid
diff --git a/testing/legion/rpc_server.py b/testing/legion/rpc_server.py
new file mode 100644
index 0000000..43b4317
--- /dev/null
+++ b/testing/legion/rpc_server.py
@@ -0,0 +1,128 @@
+# 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 task RPC server code.
+
+This server is an XML-RPC server which serves code from
+rpc_methods.RPCMethods.
+
+This server will run until shutdown is called on the server object. This can
+be achieved in 2 ways:
+
+- Calling the Quit RPC method defined in RPCMethods
+- Not receiving any calls within the idle_timeout_secs time.
+"""
+
+import logging
+import threading
+import time
+import xmlrpclib
+import SimpleXMLRPCServer
+import SocketServer
+
+#pylint: disable=relative-import
+import common_lib
+import rpc_methods
+
+
+class RequestHandler(SimpleXMLRPCServer.SimpleXMLRPCRequestHandler):
+  """Restricts access to only specified IP address.
+
+  This call assumes the server is RPCServer.
+  """
+
+  def do_POST(self):
+    """Verifies the task is authorized to perform RPCs."""
+    if self.client_address[0] != self.server.authorized_address:
+      logging.error('Received unauthorized RPC request from %s',
+                    self.task_address[0])
+      self.send_response(403)
+      response = 'Forbidden'
+      self.send_header('Content-type', 'text/plain')
+      self.send_header('Content-length', str(len(response)))
+      self.end_headers()
+      self.wfile.write(response)
+    else:
+      return SimpleXMLRPCServer.SimpleXMLRPCRequestHandler.do_POST(self)
+
+
+class RPCServer(SimpleXMLRPCServer.SimpleXMLRPCServer,
+                SocketServer.ThreadingMixIn):
+  """Restricts all endpoints to only specified IP addresses."""
+
+  def __init__(self, authorized_address,
+               idle_timeout_secs=common_lib.DEFAULT_TIMEOUT_SECS):
+    SimpleXMLRPCServer.SimpleXMLRPCServer.__init__(
+        self, (common_lib.SERVER_ADDRESS, common_lib.SERVER_PORT),
+        allow_none=True, logRequests=False,
+        requestHandler=RequestHandler)
+
+    self.authorized_address = authorized_address
+    self.idle_timeout_secs = idle_timeout_secs
+    self.register_instance(rpc_methods.RPCMethods(self))
+
+    self._shutdown_requested_event = threading.Event()
+    self._rpc_received_event = threading.Event()
+    self._idle_thread = threading.Thread(target=self._CheckForIdleQuit)
+
+  def shutdown(self):
+    """Shutdown the server.
+
+    This overloaded method sets the _shutdown_requested_event to allow the
+    idle timeout thread to quit.
+    """
+    self._shutdown_requested_event.set()
+    SimpleXMLRPCServer.SimpleXMLRPCServer.shutdown(self)
+    logging.info('Server shutdown complete')
+
+  def serve_forever(self, poll_interval=0.5):
+    """Serve forever.
+
+    This overloaded method starts the idle timeout thread before calling
+    serve_forever. This ensures the idle timer thread doesn't get started
+    without the server running.
+
+    Args:
+      poll_interval: The interval to poll for shutdown.
+    """
+    logging.info('RPC server starting')
+    self._idle_thread.start()
+    SimpleXMLRPCServer.SimpleXMLRPCServer.serve_forever(self, poll_interval)
+
+  def _dispatch(self, method, params):
+    """Dispatch the call to the correct method with the provided params.
+
+    This overloaded method adds logging to help trace connection and
+    call problems.
+
+    Args:
+      method: The method name to call.
+      params: A tuple of parameters to pass.
+
+    Returns:
+      The result of the parent class' _dispatch method.
+    """
+    logging.debug('Calling %s%s', method, params)
+    self._rpc_received_event.set()
+    return SimpleXMLRPCServer.SimpleXMLRPCServer._dispatch(self, method, params)
+
+  def _CheckForIdleQuit(self):
+    """Check for, and exit, if the server is idle for too long.
+
+    This method must be run in a separate thread to avoid a deadlock when
+    calling server.shutdown.
+    """
+    timeout = time.time() + self.idle_timeout_secs
+    while time.time() < timeout:
+      if self._shutdown_requested_event.is_set():
+        # An external source called shutdown()
+        return
+      elif self._rpc_received_event.is_set():
+        logging.debug('Resetting the idle timeout')
+        timeout = time.time() + self.idle_timeout_secs
+        self._rpc_received_event.clear()
+      time.sleep(1)
+    # We timed out, kill the server
+    logging.warning('Shutting down the server due to the idle timeout')
+    self.shutdown()
diff --git a/testing/legion/run_task.py b/testing/legion/run_task.py
new file mode 100755
index 0000000..6a550735
--- /dev/null
+++ b/testing/legion/run_task.py
@@ -0,0 +1,47 @@
+#!/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 main task entrypoint."""
+
+import argparse
+import logging
+import socket
+import sys
+import time
+
+#pylint: disable=relative-import
+import common_lib
+import rpc_server
+
+
+def main():
+  print ' '.join(sys.argv)
+  common_lib.InitLogging()
+  logging.info('Task starting')
+
+  parser = argparse.ArgumentParser()
+  parser.add_argument('--otp',
+                      help='One time token used to authenticate with the host')
+  parser.add_argument('--controller',
+                      help='The ip address of the controller machine')
+  parser.add_argument('--idle-timeout', type=int,
+                      default=common_lib.DEFAULT_TIMEOUT_SECS,
+                      help='The idle timeout for the rpc server in seconds')
+  args, _ = parser.parse_known_args()
+
+  logging.info(
+      'Registering with registration server at %s using OTP "%s"',
+      args.controller, args.otp)
+  server = common_lib.ConnectToServer(args.controller).RegisterTask(
+      args.otp, common_lib.MY_IP)
+
+  server = rpc_server.RPCServer(args.controller, args.idle_timeout)
+
+  server.serve_forever()
+  return 0
+
+
+if __name__ == '__main__':
+  sys.exit(main())
diff --git a/testing/legion/task_controller.py b/testing/legion/task_controller.py
new file mode 100644
index 0000000..e0812b4
--- /dev/null
+++ b/testing/legion/task_controller.py
@@ -0,0 +1,224 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Defines the task controller library."""
+
+import argparse
+import datetime
+import logging
+import os
+import socket
+import subprocess
+import sys
+import tempfile
+import threading
+import xmlrpclib
+
+#pylint: disable=relative-import
+import common_lib
+
+ISOLATE_PY = os.path.join(common_lib.SWARMING_DIR, 'isolate.py')
+SWARMING_PY = os.path.join(common_lib.SWARMING_DIR, 'swarming.py')
+
+
+class Error(Exception):
+  pass
+
+
+class ConnectionTimeoutError(Error):
+  pass
+
+
+class TaskController(object):
+  """Provisions, configures, and controls a task machine.
+
+  This class is an abstraction of a physical task machine. It provides an
+  end to end API for controlling a task machine. Operations on the task machine
+  are performed using the instance's "rpc" property. A simple end to end
+  scenario is as follows:
+
+  task = TaskController(...)
+  task.Create()
+  task.WaitForConnection()
+  proc = task.rpc.subprocess.Popen(['ls'])
+  print task.rpc.subprocess.GetStdout(proc)
+  task.Release()
+  """
+
+  _task_count = 0
+  _tasks = []
+
+  def __init__(self, isolate_file, config_vars, dimensions, priority=100,
+               idle_timeout_secs=common_lib.DEFAULT_TIMEOUT_SECS,
+               connection_timeout_secs=common_lib.DEFAULT_TIMEOUT_SECS,
+               verbosity='ERROR', name=None):
+    assert isinstance(config_vars, dict)
+    assert isinstance(dimensions, dict)
+    type(self)._tasks.append(self)
+    type(self)._task_count += 1
+    self.verbosity = verbosity
+    self._name = name or 'Task%d' % type(self)._task_count
+    self._priority = priority
+    self._isolate_file = isolate_file
+    self._isolated_file = isolate_file + 'd'
+    self._idle_timeout_secs = idle_timeout_secs
+    self._config_vars = config_vars
+    self._dimensions = dimensions
+    self._connect_event = threading.Event()
+    self._connected = False
+    self._ip_address = None
+    self._otp = self._CreateOTP()
+    self._rpc = None
+
+    parser = argparse.ArgumentParser()
+    parser.add_argument('--isolate-server')
+    parser.add_argument('--swarming-server')
+    parser.add_argument('--task-connection-timeout-secs',
+                        default=common_lib.DEFAULT_TIMEOUT_SECS)
+    args, _ = parser.parse_known_args()
+
+    self._isolate_server = args.isolate_server
+    self._swarming_server = args.swarming_server
+    self._connection_timeout_secs = (connection_timeout_secs or
+                                    args.task_connection_timeout_secs)
+
+  @property
+  def name(self):
+    return self._name
+
+  @property
+  def otp(self):
+    return self._otp
+
+  @property
+  def connected(self):
+    return self._connected
+
+  @property
+  def connect_event(self):
+    return self._connect_event
+
+  @property
+  def rpc(self):
+    return self._rpc
+
+  @property
+  def verbosity(self):
+    return self._verbosity
+
+  @verbosity.setter
+  def verbosity(self, level):
+    """Sets the verbosity level as a string.
+
+    Either a string ('INFO', 'DEBUG', etc) or a logging level (logging.INFO,
+    logging.DEBUG, etc) is allowed.
+    """
+    assert isinstance(level, (str, int))
+    if isinstance(level, int):
+      level = logging.getLevelName(level)
+    self._verbosity = level  #pylint: disable=attribute-defined-outside-init
+
+  @classmethod
+  def ReleaseAllTasks(cls):
+    for task in cls._tasks:
+      task.Release()
+
+  def _CreateOTP(self):
+    """Creates the OTP."""
+    controller_name = socket.gethostname()
+    test_name = os.path.basename(sys.argv[0])
+    creation_time = datetime.datetime.utcnow()
+    otp = 'task:%s controller:%s test:%s creation:%s' % (
+        self._name, controller_name, test_name, creation_time)
+    return otp
+
+  def Create(self):
+    """Creates the task machine."""
+    logging.info('Creating %s', self.name)
+    self._connect_event.clear()
+    self._ExecuteIsolate()
+    self._ExecuteSwarming()
+
+  def WaitForConnection(self):
+    """Waits for the task machine to connect.
+
+    Raises:
+      ConnectionTimeoutError if the task doesn't connect in time.
+    """
+    logging.info('Waiting for %s to connect with a timeout of %d seconds',
+                 self._name, self._connection_timeout_secs)
+    self._connect_event.wait(self._connection_timeout_secs)
+    if not self._connect_event.is_set():
+      raise ConnectionTimeoutError('%s failed to connect' % self.name)
+
+  def Release(self):
+    """Quits the task's RPC server so it can release the machine."""
+    if self._rpc is not None and self._connected:
+      logging.info('Releasing %s', self._name)
+      try:
+        self._rpc.Quit()
+      except (socket.error, xmlrpclib.Fault):
+        logging.error('Unable to connect to %s to call Quit', self.name)
+      self._rpc = None
+      self._connected = False
+
+  def _ExecuteIsolate(self):
+    """Executes isolate.py."""
+    cmd = [
+        'python',
+        ISOLATE_PY,
+        'archive',
+        '--isolate', self._isolate_file,
+        '--isolated', self._isolated_file,
+        ]
+
+    if self._isolate_server:
+      cmd.extend(['--isolate-server', self._isolate_server])
+    for key, value in self._config_vars.iteritems():
+      cmd.extend(['--config-var', key, value])
+
+    self._ExecuteProcess(cmd)
+
+  def _ExecuteSwarming(self):
+    """Executes swarming.py."""
+    cmd = [
+        'python',
+        SWARMING_PY,
+        'trigger',
+        self._isolated_file,
+        '--priority', str(self._priority),
+        ]
+
+    if self._isolate_server:
+      cmd.extend(['--isolate-server', self._isolate_server])
+    if self._swarming_server:
+      cmd.extend(['--swarming', self._swarming_server])
+    for key, value in self._dimensions.iteritems():
+      cmd.extend(['--dimension', key, value])
+
+    cmd.extend([
+        '--',
+        '--controller', common_lib.MY_IP,
+        '--otp', self._otp,
+        '--verbosity', self._verbosity,
+        '--idle-timeout', str(self._idle_timeout_secs),
+        ])
+
+    self._ExecuteProcess(cmd)
+
+  def _ExecuteProcess(self, cmd):
+    """Executes a process, waits for it to complete, and checks for success."""
+    logging.debug('Running %s', ' '.join(cmd))
+    p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+    _, stderr = p.communicate()
+    if p.returncode != 0:
+      raise Error(stderr)
+
+  def OnConnect(self, ip_address):
+    """Receives task ip address on connection."""
+    self._ip_address = ip_address
+    self._connected = True
+    self._rpc = common_lib.ConnectToServer(self._ip_address)
+    logging.info('%s connected from %s', self._name, ip_address)
+    self._connect_event.set()
diff --git a/testing/legion/task_registration_server.py b/testing/legion/task_registration_server.py
new file mode 100644
index 0000000..52ba72705
--- /dev/null
+++ b/testing/legion/task_registration_server.py
@@ -0,0 +1,55 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""The registration server used to register tasks.
+
+The registration server is started by the test controller and allows the tasks
+to register themselves when they start. Authentication of the tasks controllers
+is based on an OTP passed to the run_task binary on startup.
+"""
+
+import logging
+import threading
+import xmlrpclib
+import SimpleXMLRPCServer
+
+#pylint: disable=relative-import
+import common_lib
+
+
+class TaskRegistrationServer(object):
+  """Discovery server run on the host."""
+
+  def __init__(self):
+    self._expected_tasks = {}
+    self._rpc_server = None
+    self._thread = None
+
+  def _RegisterTaskRPC(self, otp, ip):
+    """The RPC used by a task to register with the registration server."""
+    assert otp in self._expected_tasks
+    cb = self._expected_tasks.pop(otp)
+    cb(ip)
+
+  def RegisterTaskCallback(self, otp, callback):
+    """Registers a callback associated with an OTP."""
+    assert callable(callback)
+    self._expected_tasks[otp] = callback
+
+  def Start(self):
+    """Starts the registration server."""
+    logging.debug('Starting task registration server')
+    self._rpc_server = SimpleXMLRPCServer.SimpleXMLRPCServer(
+        (common_lib.SERVER_ADDRESS, common_lib.SERVER_PORT),
+        allow_none=True, logRequests=False)
+    self._rpc_server.register_function(
+        self._RegisterTaskRPC, 'RegisterTask')
+    self._thread = threading.Thread(target=self._rpc_server.serve_forever)
+    self._thread.start()
+
+  def Shutdown(self):
+    """Shuts the discovery server down."""
+    if self._thread and self._thread.is_alive():
+      logging.debug('Shutting down task registration server')
+      self._rpc_server.shutdown()
diff --git a/testing/legion/test_controller.py b/testing/legion/test_controller.py
new file mode 100644
index 0000000..2703fad
--- /dev/null
+++ b/testing/legion/test_controller.py
@@ -0,0 +1,69 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Defines the test controller base library.
+
+This module is the basis on which test controllers are built and executed.
+"""
+
+import logging
+import sys
+
+#pylint: disable=relative-import
+import common_lib
+import task_controller
+import task_registration_server
+
+
+class TestController(object):
+  """The base test controller class."""
+
+  def __init__(self):
+    self._registration_server = (
+        task_registration_server.TaskRegistrationServer())
+
+  def SetUp(self):
+    """Setup method used by the subclass."""
+    pass
+
+  def RunTest(self):
+    """Main test method used by the subclass."""
+    raise NotImplementedError()
+
+  def TearDown(self):
+    """Teardown method used by the subclass."""
+    pass
+
+  def CreateNewTask(self, *args, **kwargs):
+    task = task_controller.TaskController(*args, **kwargs)
+    self._registration_server.RegisterTaskCallback(
+        task.otp, task.OnConnect)
+    return task
+
+  def RunController(self):
+    """Main entry point for the controller."""
+    print ' '.join(sys.argv)
+    common_lib.InitLogging()
+    self._registration_server.Start()
+
+    error = None
+    tb = None
+    try:
+      self.SetUp()
+      self.RunTest()
+    except Exception as e:
+      # Defer raising exceptions until after TearDown is called.
+      error = e
+      tb = sys.exc_info()[-1]
+    try:
+      self.TearDown()
+    except Exception as e:
+      if not tb:
+        error = e
+        tb = sys.exc_info()[-1]
+
+    self._registration_server.Shutdown()
+    task_controller.TaskController.ReleaseAllTasks()
+    if error:
+      raise error, None, tb  #pylint: disable=raising-bad-type
diff --git a/third_party/adobe/flash/BUILD.gn b/third_party/adobe/flash/BUILD.gn
index b618ffba..7a5c99a 100644
--- a/third_party/adobe/flash/BUILD.gn
+++ b/third_party/adobe/flash/BUILD.gn
@@ -3,37 +3,37 @@
 # found in the LICENSE file.
 
 if (is_chrome_branded) {
-  if (is_linux && cpu_arch == "x86") {
+  if (is_linux && current_cpu == "x86") {
     flapper_version_h_file = "symbols/ppapi/linux/flapper_version.h"
     flapper_binary_files = [
       "binaries/ppapi/linux/libpepflashplayer.so",
       "binaries/ppapi/linux/manifest.json",
     ]
-  } else if (is_linux && cpu_arch == "x64") {
+  } else if (is_linux && current_cpu == "x64") {
     flapper_version_h_file = "symbols/ppapi/linux_x64/flapper_version.h"
     flapper_binary_files = [
       "binaries/ppapi/linux_x64/libpepflashplayer.so",
       "binaries/ppapi/linux_x64/manifest.json",
     ]
-  } else if (is_mac && cpu_arch == "x86") {
+  } else if (is_mac && current_cpu == "x86") {
     flapper_version_h_file = "symbols/ppapi/mac/flapper_version.h"
     flapper_binary_files = [
       "binaries/ppapi/mac/PepperFlashPlayer.plugin",
       "binaries/ppapi/mac/manifest.json",
     ]
-  } else if (is_mac && cpu_arch == "x64") {
+  } else if (is_mac && current_cpu == "x64") {
     flapper_version_h_file = "symbols/ppapi/mac_64/flapper_version.h"
     flapper_binary_files = [
       "binaries/ppapi/mac_64/PepperFlashPlayer.plugin",
       "binaries/ppapi/mac_64/manifest.json",
     ]
-  } else if (is_win && cpu_arch == "x86") {
+  } else if (is_win && current_cpu == "x86") {
     flapper_version_h_file = "symbols/ppapi/win/flapper_version.h"
     flapper_binary_files = [
       "binaries/ppapi/win/pepflashplayer.dll",
       "binaries/ppapi/win/manifest.json",
     ]
-  } else if (is_win && cpu_arch == "x64") {
+  } else if (is_win && current_cpu == "x64") {
     flapper_version_h_file = "symbols/ppapi/win_x64/flapper_version.h"
     flapper_binary_files = [
       "binaries/ppapi/win_x64/pepflashplayer.dll",
diff --git a/third_party/android_crazy_linker/README.chromium b/third_party/android_crazy_linker/README.chromium
index 43f8877..cde25cb 100644
--- a/third_party/android_crazy_linker/README.chromium
+++ b/third_party/android_crazy_linker/README.chromium
@@ -68,3 +68,5 @@
 
 - Fix for crbug/444714 (Chrome_Android: Crash Report - -1DB24FB5)
 
+- Speculative fix for crbug/450659.
+
diff --git a/third_party/android_crazy_linker/src/src/crazy_linker_globals.cpp b/third_party/android_crazy_linker/src/src/crazy_linker_globals.cpp
index 89c4061a..8bb5879f 100644
--- a/third_party/android_crazy_linker/src/src/crazy_linker_globals.cpp
+++ b/third_party/android_crazy_linker/src/src/crazy_linker_globals.cpp
@@ -20,7 +20,10 @@
 }  // namespace
 
 Globals::Globals() : search_paths_(), rdebug_() {
-  pthread_mutex_init(&lock_, NULL);
+  pthread_mutexattr_t attr;
+  pthread_mutexattr_init(&attr);
+  pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
+  pthread_mutex_init(&lock_, &attr);
   search_paths_.ResetFromEnv("LD_LIBRARY_PATH");
 }
 
diff --git a/third_party/android_crazy_linker/src/src/crazy_linker_rdebug.cpp b/third_party/android_crazy_linker/src/src/crazy_linker_rdebug.cpp
index 60ff890..fd410c46 100644
--- a/third_party/android_crazy_linker/src/src/crazy_linker_rdebug.cpp
+++ b/third_party/android_crazy_linker/src/src/crazy_linker_rdebug.cpp
@@ -11,6 +11,7 @@
 #include <unistd.h>
 
 #include "crazy_linker_debug.h"
+#include "crazy_linker_globals.h"
 #include "crazy_linker_proc_maps.h"
 #include "crazy_linker_util.h"
 #include "crazy_linker_system.h"
@@ -155,11 +156,13 @@
 
 // Helper class to temporarily remap a page to readable+writable until
 // scope exit.
-class ScopedPageMapper {
+class ScopedPageReadWriteRemapper {
  public:
-  ScopedPageMapper() : page_address_(0), page_prot_(0) {}
-  void MapReadWrite(void* address);
-  ~ScopedPageMapper();
+  ScopedPageReadWriteRemapper(void* address);
+  ~ScopedPageReadWriteRemapper();
+
+  // Releases the page so that the destructor does not undo the remapping.
+  void Release();
 
  private:
   static const uintptr_t kPageSize = 4096;
@@ -167,16 +170,20 @@
   int page_prot_;
 };
 
-void ScopedPageMapper::MapReadWrite(void* address) {
+ScopedPageReadWriteRemapper::ScopedPageReadWriteRemapper(void* address) {
   page_address_ = reinterpret_cast<uintptr_t>(address) & ~(kPageSize - 1);
   page_prot_ = 0;
-  if (!FindProtectionFlagsForAddress(address, &page_prot_) ||
-      (page_prot_ & (PROT_READ | PROT_WRITE)) == (PROT_READ | PROT_WRITE)) {
-    // If the address is invalid, or if the page is already read+write,
-    // no need to do anything here.
+  if (!FindProtectionFlagsForAddress(address, &page_prot_)) {
+    LOG("Could not find protection flags for %p\n", address);
     page_address_ = 0;
     return;
   }
+
+  // Note: page_prot_ may already indicate read/write, but because of
+  // possible races with the system linker we cannot be confident that
+  // this is reliable. So we always set read/write here.
+  //
+  // See commentary in WriteLinkMapField for more.
   int new_page_prot = page_prot_ | PROT_READ | PROT_WRITE;
   int ret = mprotect(
       reinterpret_cast<void*>(page_address_), kPageSize, new_page_prot);
@@ -186,7 +193,7 @@
   }
 }
 
-ScopedPageMapper::~ScopedPageMapper() {
+ScopedPageReadWriteRemapper::~ScopedPageReadWriteRemapper() {
   if (page_address_) {
     int ret =
         mprotect(reinterpret_cast<void*>(page_address_), kPageSize, page_prot_);
@@ -195,6 +202,11 @@
   }
 }
 
+void ScopedPageReadWriteRemapper::Release() {
+  page_address_ = 0;
+  page_prot_ = 0;
+}
+
 }  // namespace
 
 bool RDebug::Init() {
@@ -382,7 +394,38 @@
   return true;
 }
 
+// Helper function for AddEntryImpl and DelEntryImpl.
+// Sets *link_pointer to entry.  link_pointer is either an 'l_prev' or an
+// 'l_next' field in a neighbouring linkmap_t.  If link_pointer is in a
+// page that is mapped readonly, the page is remapped to be writable before
+// assignment.
+void RDebug::WriteLinkMapField(link_map_t** link_pointer, link_map_t* entry) {
+  ScopedPageReadWriteRemapper mapper(link_pointer);
+  LOG("%s: Remapped page for %p for read/write\n", __FUNCTION__, link_pointer);
+
+  *link_pointer = entry;
+
+  // We always mprotect the page containing link_pointer to read/write,
+  // then write the entry. The page may already be read/write, but on
+  // recent Android release is most likely readonly. Because of the way
+  // the system linker operates we cannot tell with certainty what its
+  // correct setting should be.
+  //
+  // Now, we always leave the page read/write. Here is why. If we set it
+  // back to readonly at the point between where the system linker sets
+  // it to read/write and where it writes to the address, this will cause
+  // the system linker to crash. Clearly that is undesirable. From
+  // observations this occurs most frequently on the gpu process.
+  //
+  // TODO(simonb): Revisit this, details in:
+  // https://code.google.com/p/chromium/issues/detail?id=450659
+  // https://code.google.com/p/chromium/issues/detail?id=458346
+  mapper.Release();
+  LOG("%s: Released mapper, leaving page read/write\n", __FUNCTION__);
+}
+
 void RDebug::AddEntryImpl(link_map_t* entry) {
+  ScopedGlobalLock lock;
   LOG("%s: Adding: %s\n", __FUNCTION__, entry->l_name);
   if (!init_)
     Init();
@@ -433,19 +476,8 @@
   // list, ensure that they are writable. This avoids crashing when
   // updating the 'l_prev' or 'l_next' fields of a system linker entry,
   // which are mapped read-only.
-  {
-    ScopedPageMapper mapper;
-    if (readonly_entries_)
-      mapper.MapReadWrite(before);
-    before->l_next = entry;
-  }
-
-  {
-    ScopedPageMapper mapper;
-    if (readonly_entries_)
-      mapper.MapReadWrite(after);
-    after->l_prev = entry;
-  }
+  WriteLinkMapField(&before->l_next, entry);
+  WriteLinkMapField(&after->l_prev, entry);
 
   // Tell GDB that the list modification has completed.
   r_debug_->r_state = RT_CONSISTENT;
@@ -453,6 +485,7 @@
 }
 
 void RDebug::DelEntryImpl(link_map_t* entry) {
+  ScopedGlobalLock lock;
   LOG("%s: Deleting: %s\n", __FUNCTION__, entry->l_name);
   if (!r_debug_)
     return;
@@ -464,19 +497,10 @@
   // IMPORTANT: Before modifying the previous and next entries in the
   // list, ensure that they are writable. See comment above for more
   // details.
-  if (entry->l_prev) {
-    ScopedPageMapper mapper;
-    if (readonly_entries_)
-      mapper.MapReadWrite(entry->l_prev);
-    entry->l_prev->l_next = entry->l_next;
-  }
-
-  if (entry->l_next) {
-    ScopedPageMapper mapper;
-    if (readonly_entries_)
-      mapper.MapReadWrite(entry->l_next);
-    entry->l_next->l_prev = entry->l_prev;
-  }
+  if (entry->l_prev)
+    WriteLinkMapField(&entry->l_prev->l_next, entry->l_next);
+  if (entry->l_next)
+    WriteLinkMapField(&entry->l_next->l_prev, entry->l_prev);
 
   if (r_debug_->r_map == entry)
     r_debug_->r_map = entry->l_next;
diff --git a/third_party/android_crazy_linker/src/src/crazy_linker_rdebug.h b/third_party/android_crazy_linker/src/src/crazy_linker_rdebug.h
index 731a2d2d..aacc21a 100644
--- a/third_party/android_crazy_linker/src/src/crazy_linker_rdebug.h
+++ b/third_party/android_crazy_linker/src/src/crazy_linker_rdebug.h
@@ -208,6 +208,7 @@
   // is enabled, otherwise it runs immediately on the current thread.
   // AddEntryImpl() and DelEntryImpl() are the member functions called
   // by the static ones to do the actual work.
+  void WriteLinkMapField(link_map_t** link_pointer, link_map_t* entry);
   void AddEntryImpl(link_map_t* entry);
   void DelEntryImpl(link_map_t* entry);
   static void AddEntryInternal(RDebug* rdebug, link_map_t* entry) {
diff --git a/third_party/boringssl/BUILD.gn b/third_party/boringssl/BUILD.gn
index d245868..e147754 100644
--- a/third_party/boringssl/BUILD.gn
+++ b/third_party/boringssl/BUILD.gn
@@ -28,9 +28,9 @@
 if (is_win) {
   import("//third_party/yasm/yasm_assemble.gni")
   yasm_assemble("boringssl_asm") {
-    if (cpu_arch == "x64") {
+    if (current_cpu == "x64") {
       sources = gypi_values.boringssl_win_x86_64_sources
-    } else if (cpu_arch == "x86") {
+    } else if (current_cpu == "x86") {
       sources = gypi_values.boringssl_win_x86_sources
     }
   }
@@ -52,7 +52,13 @@
   }
 
   configs -= [ "//build/config/compiler:chromium_code" ]
-  configs += [ "//build/config/compiler:no_chromium_code" ]
+  configs += [
+    "//build/config/compiler:no_chromium_code",
+
+    # TODO(davidben): Fix size_t truncations in BoringSSL.
+    # https://crbug.com/429039
+    "//build/config/compiler:no_size_t_to_int_warning",
+  ]
 
   # Also gets the include dirs from :openssl_config
   include_dirs = [
@@ -64,13 +70,7 @@
     "src/crypto",
   ]
 
-  if (is_win) {
-    # TODO(davidben): Fix size_t truncations in BoringSSL.
-    # https://crbug.com/429039
-    cflags += [ "/wd4267" ]
-  }
-
-  if (cpu_arch == "x64") {
+  if (current_cpu == "x64") {
     if (is_mac) {
       sources += gypi_values.boringssl_mac_x86_64_sources
     } else if (is_linux || is_android) {
@@ -80,7 +80,7 @@
     } else {
       defines += [ "OPENSSL_NO_ASM" ]
     }
-  } else if (cpu_arch == "x86") {
+  } else if (current_cpu == "x86") {
     if (is_mac) {
       sources += gypi_values.boringssl_mac_x86_sources
     } else if (is_linux || is_android) {
@@ -90,9 +90,9 @@
     } else {
       defines += [ "OPENSSL_NO_ASM" ]
     }
-  } else if (cpu_arch == "arm") {
+  } else if (current_cpu == "arm") {
     sources += gypi_values.boringssl_linux_arm_sources
-  } else if (cpu_arch == "arm64") {
+  } else if (current_cpu == "arm64") {
     sources += gypi_values.boringssl_linux_aarch64_sources
   } else {
     defines += [ "OPENSSL_NO_ASM" ]
diff --git a/third_party/cld/BUILD.gn b/third_party/cld/BUILD.gn
index ed4075d2..7a0c0eb 100644
--- a/third_party/cld/BUILD.gn
+++ b/third_party/cld/BUILD.gn
@@ -102,7 +102,6 @@
       "/wd4005",  # Macro defined twice.
       "/wd4006",  # #undef expected an identifier.
       "/wd4309",  # Truncation of constant value.
-      "/wd4267",  # TODO(jschuh): C4267: http://crbug.com/167187 size_t -> int
     ]
   } else {
     defines = [ "COMPILER_GCC" ]
diff --git a/third_party/cld_2/BUILD.gn b/third_party/cld_2/BUILD.gn
index bede93a6..8098eba5 100644
--- a/third_party/cld_2/BUILD.gn
+++ b/third_party/cld_2/BUILD.gn
@@ -48,10 +48,6 @@
 
   configs -= [ "//build/config/compiler:chromium_code" ]
   configs += [ "//build/config/compiler:no_chromium_code" ]
-
-  if (is_win) {
-    cflags = [ "/wd4267" ]  # size_t -> int conversion.
-  }
 }
 
 # As in the corresponding gyp file, this just builds the core interfaces for
@@ -66,10 +62,6 @@
 
   configs -= [ "//build/config/compiler:chromium_code" ]
   configs += [ "//build/config/compiler:no_chromium_code" ]
-
-  if (is_win) {
-    cflags = [ "/wd4267" ]  # size_t -> int conversion.
-  }
 }
 
 source_set("cld2_platform_impl") {
@@ -94,10 +86,6 @@
   ]
   configs -= [ "//build/config/compiler:chromium_code" ]
   configs += [ "//build/config/compiler:no_chromium_code" ]
-
-  if (is_win) {
-    cflags = [ "/wd4267" ]  # size_t -> int conversion.
-  }
 }
 
 config("cld2_dynamic_mode_config") {
@@ -114,13 +102,9 @@
 
   configs -= [ "//build/config/compiler:chromium_code" ]
   configs += [
-    "//build/config/compiler:no_chromium_code",
     ":cld2_dynamic_mode_config",
+    "//build/config/compiler:no_chromium_code",
   ]
-
-  if (is_win) {
-    cflags = [ "/wd4267" ]  # size_t -> int conversion.
-  }
 }
 
 # Does not build on Windows.
@@ -144,12 +128,8 @@
 
     configs -= [ "//build/config/compiler:chromium_code" ]
     configs += [
-      "//build/config/compiler:no_chromium_code",
       ":cld2_dynamic_mode_config",
+      "//build/config/compiler:no_chromium_code",
     ]
-
-    if (is_win) {
-      cflags = [ "/wd4267" ]  # size_t -> int conversion.
-    }
   }
 }
diff --git a/third_party/closure_compiler/README.chromium b/third_party/closure_compiler/README.chromium
index d9860d0..f9184505 100644
--- a/third_party/closure_compiler/README.chromium
+++ b/third_party/closure_compiler/README.chromium
@@ -24,5 +24,5 @@
   See third_party/closure_compiler/runner/how_to_test_compiler_pass.md for
   testing instructions on this pass.
 - Use the script third_party/closure_compiler/bump_compiler_version to update
-  the versions of third_party/closure_compiler/compiler/compiler.jar and
+  the versions of third_party/closure_compiler/compiler/compiler.jar and/or
   third_party/closure_compiler/externs/chrome_extensions.js.
diff --git a/third_party/closure_compiler/externs/chrome_extensions.js b/third_party/closure_compiler/externs/chrome_extensions.js
index cffc487..c95f90e 100644
--- a/third_party/closure_compiler/externs/chrome_extensions.js
+++ b/third_party/closure_compiler/externs/chrome_extensions.js
@@ -1166,6 +1166,373 @@
 
 
 /**
+ * @see https://developer.chrome.com/apps/bluetoothLowEnergy
+ * @const
+ */
+chrome.bluetoothLowEnergy = {};
+
+
+/**
+ * @constructor
+ * @see https://developer.chrome.com/apps/bluetoothLowEnergy#type-Service
+ */
+chrome.bluetoothLowEnergy.Service = function() {};
+
+
+/** @type {string} */
+chrome.bluetoothLowEnergy.Service.prototype.uuid;
+
+
+/** @type {boolean} */
+chrome.bluetoothLowEnergy.Service.prototype.isPrimary;
+
+
+/** @type {string|undefined} */
+chrome.bluetoothLowEnergy.Service.prototype.instanceId;
+
+
+/** @type {string|undefined} */
+chrome.bluetoothLowEnergy.Service.prototype.deviceAddress;
+
+
+/**
+ * @constructor
+ * @see https://developer.chrome.com/apps/bluetoothLowEnergy#type-Characteristic
+ */
+chrome.bluetoothLowEnergy.Characteristic;
+
+
+/** @type {string} */
+chrome.bluetoothLowEnergy.Characteristic.prototype.uuid;
+
+
+/** @type {!chrome.bluetoothLowEnergy.Service} */
+chrome.bluetoothLowEnergy.Characteristic.prototype.service;
+
+
+/** @type {!Array.<string>} */
+chrome.bluetoothLowEnergy.Characteristic.prototype.properties;
+
+
+/** @type {string|undefined} */
+chrome.bluetoothLowEnergy.Characteristic.prototype.instanceId;
+
+
+/** @type {!ArrayBuffer|undefined} */
+chrome.bluetoothLowEnergy.Characteristic.prototype.value;
+
+
+/**
+ * @constructor
+ * @see https://developer.chrome.com/apps/bluetoothLowEnergy#type-Descriptor
+ */
+chrome.bluetoothLowEnergy.Descriptor;
+
+/** @type {string} */
+chrome.bluetoothLowEnergy.Descriptor.prototype.uuid;
+
+
+/** @type {!chrome.bluetoothLowEnergy.Characteristic} */
+chrome.bluetoothLowEnergy.Descriptor.prototype.characteristic;
+
+
+/** @type {string|undefined} */
+chrome.bluetoothLowEnergy.Descriptor.prototype.instanceId;
+
+
+/** @type {!ArrayBuffer|undefined} */
+chrome.bluetoothLowEnergy.Descriptor.prototype.value;
+
+
+/**
+ * @typedef {?{
+ *   persistent: boolean
+ * }}
+ */
+chrome.bluetoothLowEnergy.ConnectionProperties;
+
+
+/**
+ * @param {string} deviceAddress
+ * @param {!chrome.bluetoothLowEnergy.ConnectionProperties|function()}
+ *     propertiesOrCallback
+ * @param {function()=} opt_callback
+ * @see https://developer.chrome.com/apps/bluetoothLowEnergy#method-connect
+ */
+chrome.bluetoothLowEnergy.connect =
+  function(deviceAddress, propertiesOrCallback, opt_callback) {};
+
+/**
+ * @param {string} deviceAddress
+ * @param {function()=} opt_callback
+ * @see https://developer.chrome.com/apps/bluetoothLowEnergy#method-disconnect
+ */
+chrome.bluetoothLowEnergy.disconnect = function(deviceAddress, opt_callback) {};
+
+
+/**
+ * @param {string} serviceId
+ * @param {function(!chrome.bluetoothLowEnergy.Service)} callback
+ * @see https://developer.chrome.com/apps/bluetoothLowEnergy#method-getService
+ */
+chrome.bluetoothLowEnergy.getService = function(serviceId, callback) {};
+
+
+/**
+ * @param {string} deviceAddress
+ * @param {function(!Array.<!chrome.bluetoothLowEnergy.Service>)} callback
+ * @see https://developer.chrome.com/apps/bluetoothLowEnergy#method-getServices
+ */
+chrome.bluetoothLowEnergy.getServices = function(deviceAddress, callback) {};
+
+
+/**
+ * @param {string} characteristicId
+ * @param {function(!chrome.bluetoothLowEnergy.Characteristic)} callback
+ * @see https://developer.chrome.com/apps/bluetoothLowEnergy#method-getCharacteristic
+ */
+chrome.bluetoothLowEnergy.getCharacteristic =
+    function(characteristicId, callback) {};
+
+
+/**
+ * @param {string} serviceId
+ * @param {function(!Array.<!chrome.bluetoothLowEnergy.Characteristic>)}
+ * callback
+ * @see https://developer.chrome.com/apps/bluetoothLowEnergy#method-getCharacteristics
+ */
+chrome.bluetoothLowEnergy.getCharacteristics =
+    function(serviceId, callback) {};
+
+
+/**
+ * @param {string} serviceId
+ * @param {function(!Array.<!chrome.bluetoothLowEnergy.Service>)} callback
+ * @see https://developer.chrome.com/apps/bluetoothLowEnergy#method-getIncludedServices
+ */
+chrome.bluetoothLowEnergy.getIncludedServices =
+  function(serviceId, callback) {};
+
+
+/**
+ * @param {string} descriptorId
+ * @param {function(!chrome.bluetoothLowEnergy.Descriptor)} callback
+ * @see https://developer.chrome.com/apps/bluetoothLowEnergy#method-getDescriptor
+ */
+chrome.bluetoothLowEnergy.getDescriptor = function(descriptorId, callback) {};
+
+
+/**
+ * @param {string} characteristicId
+ * @param {function(!Array.<!chrome.bluetoothLowEnergy.Descriptor>)} callback
+ * @see https://developer.chrome.com/apps/bluetoothLowEnergy#method-getDescriptors
+ */
+chrome.bluetoothLowEnergy.getDescriptors =
+  function(characteristicId, callback) {};
+
+
+/**
+ * @param {string} characteristicId
+ * @param {function(!chrome.bluetoothLowEnergy.Characteristic)} callback
+ * @see https://developer.chrome.com/apps/bluetoothLowEnergy#method-readCharacteristicValue
+ */
+chrome.bluetoothLowEnergy.readCharacteristicValue =
+  function(characteristicId, callback) {};
+
+
+/**
+ * @param {string} characteristicId
+ * @param {!ArrayBuffer} value
+ * @param {function()} callback
+ * @see https://developer.chrome.com/apps/bluetoothLowEnergy#method-writeCharacteristicValue
+ */
+chrome.bluetoothLowEnergy.writeCharacteristicValue =
+  function(characteristicId, value, callback) {};
+
+
+/**
+ * @typedef {?{
+ *   persistent: boolean
+ * }}
+ */
+chrome.bluetoothLowEnergy.NotificationSessionProperties;
+
+/**
+  * @param {string} characteristicId
+  * @param {!chrome.bluetoothLowEnergy.NotificationSessionProperties|function()}
+  *     propertiesOrCallback
+  * @param {function()=} opt_callback
+  * @see https://developer.chrome.com/apps/bluetoothLowEnergy#method-startCharacteristicNotifications
+  */
+chrome.bluetoothLowEnergy.startCharacteristicNotifications =
+  function(characteristicId, propertiesOrCallback, opt_callback) {};
+
+
+/**
+  * @param {string} characteristicId
+  * @param {function()=} opt_callback
+  * @see https://developer.chrome.com/apps/bluetoothLowEnergy#method-stopCharacteristicNotifications
+  */
+chrome.bluetoothLowEnergy.stopCharacteristicNotifications =
+  function(characteristicId, opt_callback) {};
+
+
+/**
+ * @param {string} descriptorId
+ * @param {function(!chrome.bluetoothLowEnergy.Descriptor)} callback
+ * @see https://developer.chrome.com/apps/bluetoothLowEnergy#method-readDescriptorValue
+ */
+chrome.bluetoothLowEnergy.readDescriptorValue =
+  function(descriptorId, callback) {};
+
+
+/**
+ * @param {string} descriptorId
+ * @param {!ArrayBuffer} value
+ * @param {function()} callback
+ * @see https://developer.chrome.com/apps/bluetoothLowEnergy#method-writeDescriptorValue
+ */
+chrome.bluetoothLowEnergy.writeDescriptorValue =
+  function(descriptorId, value, callback) {};
+
+
+/**
+ * Event whose listeners take a Service parameter.
+ * @constructor
+ */
+chrome.bluetoothLowEnergy.ServiceEvent = function() {};
+
+
+/** @param {function(!chrome.bluetoothLowEnergy.ServiceEvent): void} callback */
+chrome.bluetoothLowEnergy.ServiceEvent.prototype.addListener =
+    function(callback) {};
+
+
+/** @param {function(!chrome.bluetoothLowEnergy.ServiceEvent): void} callback */
+chrome.bluetoothLowEnergy.ServiceEvent.prototype.removeListener =
+    function(callback) {};
+
+/**
+ * @param {function(!chrome.bluetoothLowEnergy.ServiceEvent): void} callback
+ * @return {boolean}
+ */
+chrome.bluetoothLowEnergy.ServiceEvent.prototype.hasListener =
+    function(callback) {};
+
+
+/** @return {boolean} */
+chrome.bluetoothLowEnergy.ServiceEvent.prototype.hasListeners =
+    function() {};
+
+/**
+  * @type {!chrome.bluetoothLowEnergy.ServiceEvent}
+  * @see https://developer.chrome.com/apps/bluetoothLowEnergy#event-onServiceAdded
+  */
+chrome.bluetoothLowEnergy.onServiceAdded;
+
+
+/**
+ * @type {!chrome.bluetoothLowEnergy.ServiceEvent}
+ * @see https://developer.chrome.com/apps/bluetoothLowEnergy#event-onServiceChanged
+ */
+chrome.bluetoothLowEnergy.onServiceChanged;
+
+
+/**
+  * @type {!chrome.bluetoothLowEnergy.ServiceEvent}
+  * @see https://developer.chrome.com/apps/bluetoothLowEnergy#event-onServiceRemoved
+  */
+chrome.bluetoothLowEnergy.onServiceRemoved;
+
+
+/**
+ * Event whose listeners take a Characteristic parameter.
+ * @constructor
+ */
+chrome.bluetoothLowEnergy.CharacteristicEvent = function() {};
+
+
+/**
+ * @param {function(!chrome.bluetoothLowEnergy.CharacteristicEvent): void}
+ *     callback
+ */
+chrome.bluetoothLowEnergy.CharacteristicEvent.prototype.addListener =
+    function(callback) {};
+
+
+/**
+ * @param {function(!chrome.bluetoothLowEnergy.CharacteristicEvent): void}
+ *     callback
+ */
+chrome.bluetoothLowEnergy.CharacteristicEvent.prototype.removeListener =
+    function(callback) {};
+
+
+/**
+ * @param {function(!chrome.bluetoothLowEnergy.CharacteristicEvent): void}
+ *     callback
+ * @return {boolean}
+ */
+chrome.bluetoothLowEnergy.CharacteristicEvent.prototype.hasListener =
+    function(callback) {};
+
+
+/** @return {boolean} */
+chrome.bluetoothLowEnergy.CharacteristicEvent.prototype.hasListeners =
+    function() {};
+
+
+/**
+ * @type {!chrome.bluetoothLowEnergy.CharacteristicEvent}
+ * @see https://developer.chrome.com/apps/bluetoothLowEnergy#event-onCharacteristicValueChanged
+ */
+chrome.bluetoothLowEnergy.onCharacteristicValueChanged;
+
+
+/**
+ * Event whose listeners take a Characteristic parameter.
+ * @constructor
+ */
+chrome.bluetoothLowEnergy.DescriptorEvent = function() {};
+
+
+/**
+ * @param {function(!chrome.bluetoothLowEnergy.DescriptorEvent): void}
+ *     callback
+ */
+chrome.bluetoothLowEnergy.DescriptorEvent.prototype.addListener =
+    function(callback) {};
+
+
+/**
+ * @param {function(!chrome.bluetoothLowEnergy.DescriptorEvent): void}
+ *     callback
+ */
+chrome.bluetoothLowEnergy.DescriptorEvent.prototype.removeListener =
+    function(callback) {};
+
+
+/**
+ * @param {function(!chrome.bluetoothLowEnergy.DescriptorEvent): void} callback
+ * @return {boolean}
+ */
+chrome.bluetoothLowEnergy.DescriptorEvent.prototype.hasListener =
+    function(callback) {};
+
+
+/** @return {boolean} */
+chrome.bluetoothLowEnergy.DescriptorEvent.prototype.hasListeners =
+    function() {};
+
+
+/**
+ * @type {!chrome.bluetoothLowEnergy.DescriptorEvent}
+ * @see https://developer.chrome.com/apps/bluetoothLowEnergy#event-onDescriptorValueChanged
+ */
+chrome.bluetoothLowEnergy.onDescriptorValueChanged;
+
+
+/**
  * @see http://developer.chrome.com/extensions/commands.html
  * @const
  */
@@ -3741,8 +4108,18 @@
  */
 chrome.mediaGalleries.addUserSelectedFolder = function(callback) {};
 
+
+/**
+ * @param {string} galleryId ID of the media gallery.
+ * @param {function()=} opt_callback Optional callback function.
+ */
+chrome.mediaGalleries.dropPermissionForMediaFileSystem =
+    function(galleryId, opt_callback) {};
+
+
 chrome.mediaGalleries.startMediaScan = function() {};
 
+
 chrome.mediaGalleries.cancelMediaScan = function() {};
 
 
@@ -3784,8 +4161,17 @@
  *   mimeType: string,
  *   height: (number|undefined),
  *   width: (number|undefined),
+ *   xResolution: (number|undefined),
+ *   yResolution: (number|undefined),
  *   duration: (number|undefined),
  *   rotation: (number|undefined),
+ *   cameraMake: (string|undefined),
+ *   cameraModel: (string|undefined),
+ *   exposureTimeSeconds: (number|undefined),
+ *   flashFired: (boolean|undefined),
+ *   fNumber: (number|undefined),
+ *   focalLengthMm: (number|undefined),
+ *   isoEquivalent: (number|undefined),
  *   album: (string|undefined),
  *   artist: (string|undefined),
  *   comment: (string|undefined),
@@ -3794,12 +4180,30 @@
  *   genre: (string|undefined),
  *   language: (string|undefined),
  *   title: (string|undefined),
- *   track: (number|undefined)
+ *   track: (number|undefined),
+ *   rawTags: !Array.<!chrome.mediaGalleries.metadata.RawTag>,
+ *   attachedImages: !Array.<!Blob>
  * }}
  */
 chrome.mediaGalleries.MetaData;
 
 
+/** @const */
+chrome.mediaGalleries.metadata = {};
+
+
+/** @constructor */
+chrome.mediaGalleries.metadata.RawTag = function() {};
+
+
+/** @type {string} */
+chrome.mediaGalleries.metadata.RawTag.prototype.type;
+
+
+/** @type {!Object.<string, string>} */
+chrome.mediaGalleries.metadata.RawTag.prototype.tags;
+
+
 /**
  * @param {!Blob} mediaFile The media file for which to get metadata.
  * @param {{metadataType: (string|undefined)}|
@@ -3815,6 +4219,82 @@
 
 
 /**
+ * @typedef {function({galleryId: string, success: boolean}): void}
+ */
+chrome.mediaGalleries.AddGalleryWatchCallback;
+
+
+/**
+ * @param {string} galleryId The media gallery's ID.
+ * @param {!chrome.mediaGalleries.AddGalleryWatchCallback} callback Fired with
+ *     success or failure result.
+ */
+chrome.mediaGalleries.addGalleryWatch = function(galleryId, callback) {};
+
+
+/**
+ * @param {string} galleryId The media gallery's ID.
+ */
+chrome.mediaGalleries.removeGalleryWatch = function(galleryId) {};
+
+
+/**
+ * @param {function(!Array.<string>): void} callback Callback function notifies
+ *     which galleries are being watched.
+ */
+chrome.mediaGalleries.getAllGalleryWatch = function(callback) {};
+
+
+chrome.mediaGalleries.removeAllGalleryWatch = function() {};
+
+
+
+/**
+ * @constructor
+ */
+chrome.mediaGalleries.GalleryChangeEvent = function() {};
+
+
+/**
+ * @typedef {function({type: string, galleryId: string}): void}
+ */
+chrome.mediaGalleries.GalleryChangeCallback;
+
+
+/**
+ * @param {!chrome.mediaGalleries.GalleryChangeCallback} callback
+ */
+chrome.mediaGalleries.GalleryChangeEvent.prototype.addListener =
+    function(callback) {};
+
+
+/**
+ * @param {!chrome.mediaGalleries.GalleryChangeCallback} callback
+ */
+chrome.mediaGalleries.GalleryChangeEvent.prototype.removeListener =
+    function(callback) {};
+
+
+/**
+ * @param {!chrome.mediaGalleries.GalleryChangeCallback} callback
+ */
+chrome.mediaGalleries.GalleryChangeEvent.prototype.hasListener =
+    function(callback) {};
+
+
+/**
+ * @return {boolean}
+ */
+chrome.mediaGalleries.GalleryChangeEvent.prototype.hasListeners = function() {};
+
+
+/**
+ * @type {!chrome.mediaGalleries.GalleryChangeEvent}
+ */
+chrome.mediaGalleries.onGalleryChanged;
+
+
+/**
  * @typedef {{
  *   type: string,
  *   galleryCount: (number|undefined),
@@ -4632,6 +5112,19 @@
 
 /**
  * @const
+ * @see https://developer.chrome.com/extensions/system_cpu.html
+ */
+chrome.system.cpu = {};
+
+
+/**
+ * @param {function(!Object)} callback
+ */
+chrome.system.cpu.getInfo = function(callback) {};
+
+
+/**
+ * @const
  * @see http://developer.chrome.com/apps/system_display.html
  */
 chrome.system.display = {};
@@ -7330,6 +7823,198 @@
 
 
 /**
+ * @see https://developer.chrome.com/apps/serial
+ * @const
+ */
+chrome.serial = {};
+
+
+
+/**
+ * @typedef {?{
+ *   persistent: (boolean|undefined),
+ *   name: (string|undefined),
+ *   bufferSize: (number|undefined),
+ *   bitRate: (number|undefined),
+ *   dataBits: (string|undefined),
+ *   parityBits: (string|undefined),
+ *   stopBits: (string|undefined),
+ *   ctsFlowControl: (boolean|undefined),
+ *   receiveTimeout: (number|undefined),
+ *   sendTimeout: (number|undefined)
+ * }}
+ * @see https://developer.chrome.com/apps/serial#type-ConnectionOptions
+ */
+chrome.serial.ConnectionOptions;
+
+
+/**
+ * @typedef {?{
+ *   connectionId: number,
+ *   paused: boolean,
+ *   persistent: boolean,
+ *   name: string,
+ *   bufferSize: number,
+ *   receiveTimeout: number,
+ *   sendTimeout: number,
+ *   bitRate: (number|undefined),
+ *   dataBits: (string|undefined),
+ *   parityBits: (string|undefined),
+ *   stopBits: (string|undefined),
+ *   ctsFlowControl: (boolean|undefined)
+ * }}
+ * @see https://developer.chrome.com/apps/serial#type-ConnectionInfo
+ */
+chrome.serial.ConnectionInfo;
+
+
+/**
+ * Returns information about available serial devices on the system. The
+ * list is regenerated each time this method is called.
+ * @param {function(!Array<!Object>)} callback Invoked with a
+ *     list of ports on complete.
+ * @see https://developer.chrome.com/apps/serial#method-getDevices
+ */
+chrome.serial.getDevices = function(callback) {};
+
+
+/**
+ * Connects to a given serial port.
+ * @param {string} path The system path of the serial port to open.
+ * @param {!chrome.serial.ConnectionOptions|
+ *         function(!chrome.serial.ConnectionInfo)} optionsOrCallback
+ *     Port configuration options, or the callback invoked with the created
+ *     ConnectionInfo on complete.
+ * @param {function(!chrome.serial.ConnectionInfo)=} opt_callback Invoked with
+ *     the created ConnectionInfo on complete.
+ * @see https://developer.chrome.com/apps/serial#method-connect
+ */
+chrome.serial.connect = function(path, optionsOrCallback, opt_callback) {};
+
+
+/**
+ * Update the option settings on an open serial port connection.
+ * @param {number} connectionId The id of the opened connection.
+ * @param {!chrome.serial.ConnectionOptions} options Port configuration
+ *     options.
+ * @param {function(boolean)} callback Called when the configuration has
+ *     completed.
+ * @see https://developer.chrome.com/apps/serial#method-update
+ */
+chrome.serial.update = function(connectionId, options, callback) {};
+
+
+/**
+ * Disconnects from a serial port.
+ * @param {number} connectionId The id of the opened connection.
+ * @param {function(boolean)} callback Called when the connection
+ *     has been closed.
+ * @see https://developer.chrome.com/apps/serial#method-disconnect
+ */
+chrome.serial.disconnect = function(connectionId, callback) {};
+
+
+/**
+ * Pauses or unpauses an open connection.
+ * @param {number} connectionId The id of the opened connection.
+ * @param {boolean} paused Flag to indicate whether to pause or unpause.
+ * @param {function()} callback Called when the configuration has completed.
+ * @see https://developer.chrome.com/apps/serial#method-setPaused
+ */
+chrome.serial.setPaused = function(connectionId, paused, callback) {};
+
+
+/**
+ * Retrieves the state of a given connection.
+ * @param {number} connectionId The id of the opened connection.
+ * @param {function(!chrome.serial.ConnectionInfo)} callback
+ *     Called with connection state information when available.
+ * @see https://developer.chrome.com/apps/serial#method-getInfo
+ */
+chrome.serial.getInfo = function(connectionId, callback) {};
+
+
+/**
+ * Retrieves the list of currently opened serial port connections owned by
+ * the application.
+ * @param {function(!Array.<!chrome.serial.ConnectionInfo>)} callback
+ *     Called with the list of |ConnectionInfo|s when available.
+ * @see https://developer.chrome.com/apps/serial#method-getConnections
+ */
+chrome.serial.getConnections = function(callback) {};
+
+
+/**
+ * Writes data to the given connection.
+ * @param {number} connectionId The id of the opened connection.
+ * @param {!ArrayBuffer} data The data to send.
+ * @param {function(!Object)} callback Called when the operation has
+ *     completed.
+ * @see https://developer.chrome.com/apps/serial#method-send
+ */
+chrome.serial.send = function(connectionId, data, callback) {};
+
+
+/**
+ * Flushes all bytes in the given connection's input and output buffers.
+ * @param {number} connectionId The id of the opened connection.
+ * @param {function(boolean)} callback
+ * @see https://developer.chrome.com/apps/serial#method-flush
+ */
+chrome.serial.flush = function(connectionId, callback) {};
+
+
+
+
+/**
+ * Retrieves the state of control signals on a given connection.
+ * @param {number} connectionId The id of the opened connection.
+ * @param {function(!Object)} callback
+ * @see https://developer.chrome.com/apps/serial#method-getControlSignals
+ */
+chrome.serial.getControlSignals = function(connectionId, callback) {};
+
+
+/**
+ * @typedef {?{
+ *   dtr: (boolean|undefined),
+ *   rts: (boolean|undefined)
+ * }}
+ */
+chrome.serial.ControlSignals;
+
+
+/**
+ * Sets the state of control signals on a given connection.
+ * @param {number} connectionId The id of the opened connection.
+ * @param {!chrome.serial.ControlSignals} signals
+ *     The set of signal changes to send to the device.
+ * @param {function(boolean)} callback Called once the control signals
+ *     have been set.
+ * @see https://developer.chrome.com/apps/serial#method-setControlSignals
+ */
+chrome.serial.setControlSignals = function(connectionId, signals, callback) {};
+
+
+/**
+ * Event raised when data has been read from the connection.
+ * @type {!ChromeObjectEvent}
+ * @see https://developer.chrome.com/apps/serial#event-onReceive
+ */
+chrome.serial.onReceive;
+
+
+/**
+ * Event raised when an error occurred while the runtime was waiting for
+ * data on the serial port. Once this event is raised, the connection may
+ * be set to paused. A "timeout" error does not pause the connection.
+ * @type {!ChromeObjectEvent}
+ * @see https://developer.chrome.com/apps/serial#event-onReceiveError
+ */
+chrome.serial.onReceiveError;
+
+
+/**
  * @const
  * @see https://developer.chrome.com/apps/webstore
  */
@@ -8102,3 +8787,21 @@
 
 /** @type {!chrome.bluetoothPrivate.PairingEventEvent} */
 chrome.bluetoothPrivate.onPairing;
+
+
+
+/**
+ * @const
+ * @see http://goo.gl/XmVdHm
+ */
+chrome.inlineInstallPrivate = {};
+
+
+/**
+ * Installs the given app ID.
+ * @param {string} id
+ * @param {function(string, string): void=} opt_callback Response callback that
+ *     returns two string: (1) an error string (or empty string on success) and
+ *     (2) an error code in case of error
+ */
+chrome.inlineInstallPrivate.install = function(id, opt_callback) {};
diff --git a/third_party/closure_compiler/externs/file_manager_private.js b/third_party/closure_compiler/externs/file_manager_private.js
index 98c842d..6d68a50e 100644
--- a/third_party/closure_compiler/externs/file_manager_private.js
+++ b/third_party/closure_compiler/externs/file_manager_private.js
@@ -16,19 +16,19 @@
 
 /**
  * @typedef {{
- *   fileSize: (number|undefined),
- *   lastModifiedTime: (number|undefined),
+ *   size: (number|undefined),
+ *   modificationTime: (number|undefined),
  *   thumbnailUrl: (string|undefined),
  *   externalFileUrl: (string|undefined),
  *   imageWidth: (number|undefined),
  *   imageHeight: (number|undefined),
  *   imageRotation: (number|undefined),
- *   isPinned: (boolean|undefined),
- *   isPresent: (boolean|undefined),
- *   isHosted: (boolean|undefined),
- *   isDirty: (boolean|undefined),
- *   isAvailableOffline: (boolean|undefined),
- *   isAvailableWhenMetered: (boolean|undefined),
+ *   pinned: (boolean|undefined),
+ *   present: (boolean|undefined),
+ *   hosted: (boolean|undefined),
+ *   dirty: (boolean|undefined),
+ *   availableOffline: (boolean|undefined),
+ *   availableWhenMetered: (boolean|undefined),
  *   customIconUrl: (string|undefined),
  *   contentMimeType: (string|undefined),
  *   sharedWithMe: (boolean|undefined),
@@ -312,11 +312,12 @@
 /**
  * Requests additional properties for files. |fileUrls| list of URLs of files
  * |callback|
- * @param {Array} fileUrls
- * @param {Function} callback |entryProperties| A dictionary containing
+ * @param {!Array<string>} fileUrls
+ * @param {!Array<string>} names
+ * @param {!Function} callback |entryProperties| A dictionary containing
  * properties of the requested entries.
  */
-chrome.fileManagerPrivate.getEntryProperties = function(fileUrls, callback) {};
+chrome.fileManagerPrivate.getEntryProperties = function(fileUrls, names, callback) {};
 
 /**
  * Pins/unpins a Drive file in the cache. |fileUrl| URL of a file to pin/unpin.
diff --git a/third_party/closure_compiler/runner/src/org/chromium/closure/compiler/ChromeCodingConvention.java b/third_party/closure_compiler/runner/src/org/chromium/closure/compiler/ChromeCodingConvention.java
index d3189d50..3c0ce8c 100644
--- a/third_party/closure_compiler/runner/src/org/chromium/closure/compiler/ChromeCodingConvention.java
+++ b/third_party/closure_compiler/runner/src/org/chromium/closure/compiler/ChromeCodingConvention.java
@@ -10,9 +10,9 @@
 import com.google.javascript.jscomp.ClosureCodingConvention.AssertInstanceofSpec;
 import com.google.javascript.jscomp.CodingConvention;
 import com.google.javascript.jscomp.CodingConventions;
+import com.google.javascript.rhino.FunctionTypeI;
 import com.google.javascript.rhino.Node;
-import com.google.javascript.rhino.jstype.FunctionType;
-import com.google.javascript.rhino.jstype.ObjectType;
+import com.google.javascript.rhino.ObjectTypeI;
 
 import java.util.Collection;
 import java.util.Set;
@@ -46,8 +46,8 @@
   }
 
   @Override
-  public void applySingletonGetter(FunctionType functionType,
-      FunctionType getterType, ObjectType objectType) {
+  public void applySingletonGetter(FunctionTypeI functionType,
+      FunctionTypeI getterType, ObjectTypeI objectType) {
     super.applySingletonGetter(functionType, getterType, objectType);
     functionType.defineDeclaredProperty("getInstance", getterType,
         functionType.getSource());
diff --git a/third_party/fuzzymatch/README.chromium b/third_party/fuzzymatch/README.chromium
index 3045394b..8bc0d31 100644
--- a/third_party/fuzzymatch/README.chromium
+++ b/third_party/fuzzymatch/README.chromium
@@ -1,7 +1,7 @@
 Name: fuzzymatch
 URL: None
 License: BSD
-License File: /LICENSE
+License File: NOT_SHIPPED
 Security Critical: no
 Version: None
 
diff --git a/third_party/google_input_tools/README.chromium b/third_party/google_input_tools/README.chromium
index 3ae0a96b5..da32a65 100644
--- a/third_party/google_input_tools/README.chromium
+++ b/third_party/google_input_tools/README.chromium
@@ -1,8 +1,8 @@
 Name: Google Input Tools
 Short Name: google_input_tools
 URL: https://github.com/googlei18n/google-input-tools.git
-Version: 1.0.6.0
-Revision: @30ffaf3940d79fe88160fa2d3eccb5134953481a
+Version: 1.0.6.1
+Revision: @6e95b6e74690a04a6effe624bfe1c54ad802b7ec
 License: Apache 2.0
 License File: LICENSE
 Security Critical: yes
diff --git a/third_party/google_input_tools/inputview.gypi b/third_party/google_input_tools/inputview.gypi
index 82a68ac3..a9821aa 100644
--- a/third_party/google_input_tools/inputview.gypi
+++ b/third_party/google_input_tools/inputview.gypi
@@ -41,8 +41,8 @@
       'src/chrome/os/inputview/elements/content/expandedcandidateview.js',
       'src/chrome/os/inputview/elements/content/functionalkey.js',
       'src/chrome/os/inputview/elements/content/gaussianestimator.js',
+      'src/chrome/os/inputview/elements/content/gesturecanvasview.js',
       'src/chrome/os/inputview/elements/content/handwritingview.js',
-      'src/chrome/os/inputview/elements/content/indicator.js',
       'src/chrome/os/inputview/elements/content/keyboardview.js',
       'src/chrome/os/inputview/elements/content/keysetview.js',
       'src/chrome/os/inputview/elements/content/material/spacekey.js',
@@ -51,6 +51,7 @@
       'src/chrome/os/inputview/elements/content/menuview.js',
       'src/chrome/os/inputview/elements/content/modifierkey.js',
       'src/chrome/os/inputview/elements/content/morekeysshiftoperation.js',
+      'src/chrome/os/inputview/elements/content/pageindicator.js',
       'src/chrome/os/inputview/elements/content/selectview.js',
       'src/chrome/os/inputview/elements/content/softkey.js',
       'src/chrome/os/inputview/elements/content/spacekey.js',
@@ -70,6 +71,8 @@
       'src/chrome/os/inputview/emojitype.js',
       'src/chrome/os/inputview/events.js',
       'src/chrome/os/inputview/events/keycodes.js',
+      'src/chrome/os/inputview/featurename.js',
+      'src/chrome/os/inputview/featuretracker.js',
       'src/chrome/os/inputview/globalflags.js',
       'src/chrome/os/inputview/globalsettings.js',
       'src/chrome/os/inputview/handler/pointeractionbundle.js',
@@ -112,8 +115,8 @@
       'src/chrome/os/message/event.js',
       'src/chrome/os/message/name.js',
       'src/chrome/os/message/type.js',
-      'src/chrome/os/soundcontroller.js',
-      'src/chrome/os/sounds.js',
+      'src/chrome/os/sounds/soundcontroller.js',
+      'src/chrome/os/sounds/sounds.js',
       'src/chrome/os/statistics.js',
       'third_party/closure_library/closure/goog/a11y/aria/announcer.js',
       'third_party/closure_library/closure/goog/a11y/aria/aria.js',
diff --git a/third_party/google_input_tools/src/chrome/os/inputview/_locales/am/messages.json b/third_party/google_input_tools/src/chrome/os/inputview/_locales/am/messages.json
index 8bde55a..5b754390 100644
--- a/third_party/google_input_tools/src/chrome/os/inputview/_locales/am/messages.json
+++ b/third_party/google_input_tools/src/chrome/os/inputview/_locales/am/messages.json
@@ -2633,6 +2633,9 @@
   "ignore_correction": {
     "message": "\u1208\u121a\u12a8\u1270\u1208\u12cd \u12a5\u122d\u121b\u1276\u127d\u1295 \u127d\u120b \u1260\u120d"
   },
+  "ignore_correction_short": {
+    "message": "Ignore correction"
+  },
   "il_heb_settings_page": {
     "message": "\u12e8\u12a5\u1265\u122b\u12ed\u1235\u1325 \u1241\u120d\u134d \u1230\u120c\u12f3 \u1245\u1295\u1265\u122e\u127d \u1308\u133d"
   },
@@ -3282,7 +3285,7 @@
     "message": "\u12e8\u1270\u1218\u1228\u1321\u1275\u1295 \u12a0\u1235\u12c8\u130d\u12f5"
   },
   "user_dict_reset": {
-    "message": "Reset All Dictionary Entries"
+    "message": "\u1201\u1209\u1295\u121d \u12e8\u1218\u12dd\u1308\u1260-\u1243\u120b\u1275 \u130d\u1264\u1276\u127d\u1295 \u12f3\u130d\u121d \u12a0\u1235\u1300\u121d\u122d"
   },
   "user_dict_save": {
     "message": "\u12a0\u1235\u1240\u121d\u1325"
diff --git a/third_party/google_input_tools/src/chrome/os/inputview/_locales/ar/messages.json b/third_party/google_input_tools/src/chrome/os/inputview/_locales/ar/messages.json
index 39b778e6..1d74865 100644
--- a/third_party/google_input_tools/src/chrome/os/inputview/_locales/ar/messages.json
+++ b/third_party/google_input_tools/src/chrome/os/inputview/_locales/ar/messages.json
@@ -2633,6 +2633,9 @@
   "ignore_correction": {
     "message": "\u062a\u062c\u0627\u0647\u0644 \u062a\u0635\u062d\u064a\u062d \u0644\u0640"
   },
+  "ignore_correction_short": {
+    "message": "Ignore correction"
+  },
   "il_heb_settings_page": {
     "message": "\u0635\u0641\u062d\u0629 \u0625\u0639\u062f\u0627\u062f\u0627\u062a \u0644\u0648\u062d\u0629 \u0627\u0644\u0645\u0641\u0627\u062a\u064a\u062d \u0627\u0644\u0639\u0628\u0631\u064a\u0629"
   },
@@ -3282,7 +3285,7 @@
     "message": "\u0625\u0632\u0627\u0644\u0629 \u0627\u0644\u0645\u062e\u062a\u0627\u0631\u0629"
   },
   "user_dict_reset": {
-    "message": "Reset All Dictionary Entries"
+    "message": "\u0625\u0639\u0627\u062f\u0629 \u062a\u0639\u064a\u064a\u0646 \u062c\u0645\u064a\u0639 \u0625\u062f\u062e\u0627\u0644\u0627\u062a \u0627\u0644\u0642\u0627\u0645\u0648\u0633"
   },
   "user_dict_save": {
     "message": "\u062d\u0641\u0638"
diff --git a/third_party/google_input_tools/src/chrome/os/inputview/_locales/bg/messages.json b/third_party/google_input_tools/src/chrome/os/inputview/_locales/bg/messages.json
index 7c23209..0f94f84 100644
--- a/third_party/google_input_tools/src/chrome/os/inputview/_locales/bg/messages.json
+++ b/third_party/google_input_tools/src/chrome/os/inputview/_locales/bg/messages.json
@@ -2633,6 +2633,9 @@
   "ignore_correction": {
     "message": "\u041f\u0440\u0435\u043d\u0435\u0431\u0440\u0435\u0433\u0432\u0430\u043d\u0435 \u043d\u0430 \u043a\u043e\u0440\u0435\u043a\u0446\u0438\u044f\u0442\u0430 \u0437\u0430"
   },
+  "ignore_correction_short": {
+    "message": "Ignore correction"
+  },
   "il_heb_settings_page": {
     "message": "\u0421\u0442\u0440\u0430\u043d\u0438\u0446\u0430 \u0441 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0437\u0430 \u043a\u043b\u0430\u0432\u0438\u0430\u0442\u0443\u0440\u0430\u0442\u0430 \u043d\u0430 \u0438\u0432\u0440\u0438\u0442"
   },
@@ -3282,7 +3285,7 @@
     "message": "\u041f\u0440\u0435\u043c\u0430\u0445\u0432\u0430\u043d\u0435 \u043d\u0430 \u0438\u0437\u0431\u0440\u0430\u043d\u043e\u0442\u043e"
   },
   "user_dict_reset": {
-    "message": "Reset All Dictionary Entries"
+    "message": "\u041d\u0443\u043b\u0438\u0440\u0430\u043d\u0435 \u043d\u0430 \u0432\u0441\u0438\u0447\u043a\u0438 \u0437\u0430\u043f\u0438\u0441\u0438 \u0432 \u0440\u0435\u0447\u043d\u0438\u043a\u0430"
   },
   "user_dict_save": {
     "message": "\u0417\u0430\u043f\u0430\u0437\u0432\u0430\u043d\u0435"
diff --git a/third_party/google_input_tools/src/chrome/os/inputview/_locales/bn/messages.json b/third_party/google_input_tools/src/chrome/os/inputview/_locales/bn/messages.json
index d1fbac5..dbe01582 100644
--- a/third_party/google_input_tools/src/chrome/os/inputview/_locales/bn/messages.json
+++ b/third_party/google_input_tools/src/chrome/os/inputview/_locales/bn/messages.json
@@ -2633,6 +2633,9 @@
   "ignore_correction": {
     "message": "\u098f\u09b0 \u099c\u09a8\u09cd\u09af \u0995\u09b0\u09be \u09b8\u0982\u09b6\u09cb\u09a7\u09a8 \u0989\u09aa\u09c7\u0995\u09cd\u09b7\u09be \u0995\u09b0\u09c1\u09a8"
   },
+  "ignore_correction_short": {
+    "message": "Ignore correction"
+  },
   "il_heb_settings_page": {
     "message": "\u09b9\u09bf\u09ac\u09cd\u09b0\u09c1 \u0995\u09c0\u09ac\u09cb\u09b0\u09cd\u09a1 \u09b8\u09c7\u099f\u09bf\u0982\u09b8 \u09aa\u09c3\u09b7\u09cd\u09a0\u09be"
   },
@@ -3282,7 +3285,7 @@
     "message": "\u09a8\u09bf\u09b0\u09cd\u09ac\u09be\u099a\u09bf\u09a4 \u09b8\u09b0\u09be\u09a8"
   },
   "user_dict_reset": {
-    "message": "Reset All Dictionary Entries"
+    "message": "\u09b8\u09ae\u09b8\u09cd\u09a4 \u0985\u09ad\u09bf\u09a7\u09be\u09a8 \u098f\u09a8\u09cd\u099f\u09cd\u09b0\u09bf\u0997\u09c1\u09b2\u09bf \u09aa\u09c1\u09a8\u09b0\u09be\u09df \u09b8\u09c7\u099f \u0995\u09b0\u09c1\u09a8"
   },
   "user_dict_save": {
     "message": "\u09b8\u0982\u09b0\u0995\u09cd\u09b7\u09a3 \u0995\u09b0\u09c1\u09a8"
diff --git a/third_party/google_input_tools/src/chrome/os/inputview/_locales/ca/messages.json b/third_party/google_input_tools/src/chrome/os/inputview/_locales/ca/messages.json
index 5af81f0..09a064b 100644
--- a/third_party/google_input_tools/src/chrome/os/inputview/_locales/ca/messages.json
+++ b/third_party/google_input_tools/src/chrome/os/inputview/_locales/ca/messages.json
@@ -2633,6 +2633,9 @@
   "ignore_correction": {
     "message": "Ignora la correcci\u00f3 de"
   },
+  "ignore_correction_short": {
+    "message": "Ignore correction"
+  },
   "il_heb_settings_page": {
     "message": "P\u00e0gina de configuraci\u00f3 del teclat hebreu"
   },
@@ -3282,7 +3285,7 @@
     "message": "Suprimeix les seleccionades"
   },
   "user_dict_reset": {
-    "message": "Reset All Dictionary Entries"
+    "message": "Restableix totes les entrades de diccionari"
   },
   "user_dict_save": {
     "message": "Desa"
diff --git a/third_party/google_input_tools/src/chrome/os/inputview/_locales/cs/messages.json b/third_party/google_input_tools/src/chrome/os/inputview/_locales/cs/messages.json
index 72dffcc..e709c9d7 100644
--- a/third_party/google_input_tools/src/chrome/os/inputview/_locales/cs/messages.json
+++ b/third_party/google_input_tools/src/chrome/os/inputview/_locales/cs/messages.json
@@ -2633,6 +2633,9 @@
   "ignore_correction": {
     "message": "Ignorovat opravu pro"
   },
+  "ignore_correction_short": {
+    "message": "Ignore correction"
+  },
   "il_heb_settings_page": {
     "message": "Str\u00e1nka nastaven\u00ed hebrejsk\u00e9 kl\u00e1vesnice"
   },
@@ -3282,7 +3285,7 @@
     "message": "Odstranit vybran\u00e9"
   },
   "user_dict_reset": {
-    "message": "Reset All Dictionary Entries"
+    "message": "Resetovat v\u0161echny polo\u017eky ve slovn\u00edku"
   },
   "user_dict_save": {
     "message": "Ulo\u017eit"
diff --git a/third_party/google_input_tools/src/chrome/os/inputview/_locales/da/messages.json b/third_party/google_input_tools/src/chrome/os/inputview/_locales/da/messages.json
index f928550..2db1bd2 100644
--- a/third_party/google_input_tools/src/chrome/os/inputview/_locales/da/messages.json
+++ b/third_party/google_input_tools/src/chrome/os/inputview/_locales/da/messages.json
@@ -2633,6 +2633,9 @@
   "ignore_correction": {
     "message": "Ignorer rettelsen af"
   },
+  "ignore_correction_short": {
+    "message": "Ignore correction"
+  },
   "il_heb_settings_page": {
     "message": "Side med indstillinger for hebraisk tastatur"
   },
@@ -3282,7 +3285,7 @@
     "message": "Fjern markering"
   },
   "user_dict_reset": {
-    "message": "Reset All Dictionary Entries"
+    "message": "Nulstil alle opslag i ordbogen"
   },
   "user_dict_save": {
     "message": "Gem"
diff --git a/third_party/google_input_tools/src/chrome/os/inputview/_locales/de/messages.json b/third_party/google_input_tools/src/chrome/os/inputview/_locales/de/messages.json
index 9d8bf655..b7245b5 100644
--- a/third_party/google_input_tools/src/chrome/os/inputview/_locales/de/messages.json
+++ b/third_party/google_input_tools/src/chrome/os/inputview/_locales/de/messages.json
@@ -2633,6 +2633,9 @@
   "ignore_correction": {
     "message": "Korrektur ignorieren f\u00fcr"
   },
+  "ignore_correction_short": {
+    "message": "Ignore correction"
+  },
   "il_heb_settings_page": {
     "message": "Seite mit Einstellungen f\u00fcr hebr\u00e4ische Tastatur"
   },
@@ -3282,7 +3285,7 @@
     "message": "Auswahl entfernen"
   },
   "user_dict_reset": {
-    "message": "Reset All Dictionary Entries"
+    "message": "Alle W\u00f6rterbucheintr\u00e4ge zur\u00fccksetzen"
   },
   "user_dict_save": {
     "message": "Speichern"
diff --git a/third_party/google_input_tools/src/chrome/os/inputview/_locales/el/messages.json b/third_party/google_input_tools/src/chrome/os/inputview/_locales/el/messages.json
index 58b4d65..53dbfb72 100644
--- a/third_party/google_input_tools/src/chrome/os/inputview/_locales/el/messages.json
+++ b/third_party/google_input_tools/src/chrome/os/inputview/_locales/el/messages.json
@@ -2633,6 +2633,9 @@
   "ignore_correction": {
     "message": "\u03a0\u03b1\u03c1\u03ac\u03b2\u03bb\u03b5\u03c8\u03b7 \u03b4\u03b9\u03cc\u03c1\u03b8\u03c9\u03c3\u03b7\u03c2 \u03b3\u03b9\u03b1"
   },
+  "ignore_correction_short": {
+    "message": "Ignore correction"
+  },
   "il_heb_settings_page": {
     "message": "\u03a3\u03b5\u03bb\u03af\u03b4\u03b1 \u03c1\u03c5\u03b8\u03bc\u03af\u03c3\u03b5\u03c9\u03bd \u03c0\u03bb\u03b7\u03ba\u03c4\u03c1\u03bf\u03bb\u03bf\u03b3\u03af\u03bf\u03c5 \u03b5\u03b2\u03c1\u03b1\u03ca\u03ba\u03ce\u03bd \u03c7\u03b1\u03c1\u03b1\u03ba\u03c4\u03ae\u03c1\u03c9\u03bd"
   },
@@ -3282,7 +3285,7 @@
     "message": "\u039a\u03b1\u03c4\u03ac\u03c1\u03b3\u03b7\u03c3\u03b7 \u03b5\u03c0\u03b9\u03bb\u03b5\u03b3\u03bc\u03ad\u03bd\u03c9\u03bd"
   },
   "user_dict_reset": {
-    "message": "Reset All Dictionary Entries"
+    "message": "\u0395\u03c0\u03b1\u03bd\u03b1\u03c6\u03bf\u03c1\u03ac \u03cc\u03bb\u03c9\u03bd \u03c4\u03c9\u03bd \u03ba\u03b1\u03c4\u03b1\u03c7\u03c9\u03c1\u03af\u03c3\u03b5\u03c9\u03bd \u03bb\u03b5\u03be\u03b9\u03ba\u03bf\u03cd"
   },
   "user_dict_save": {
     "message": "\u0391\u03c0\u03bf\u03b8\u03ae\u03ba\u03b5\u03c5\u03c3\u03b7"
diff --git a/third_party/google_input_tools/src/chrome/os/inputview/_locales/en/messages.json b/third_party/google_input_tools/src/chrome/os/inputview/_locales/en/messages.json
index 724c42c..04ec569 100644
--- a/third_party/google_input_tools/src/chrome/os/inputview/_locales/en/messages.json
+++ b/third_party/google_input_tools/src/chrome/os/inputview/_locales/en/messages.json
@@ -2633,6 +2633,9 @@
   "ignore_correction": {
     "message": "Ignore correction for"
   },
+  "ignore_correction_short": {
+    "message": "Ignore correction"
+  },
   "il_heb_settings_page": {
     "message": "Hebrew Keyboard Settings Page"
   },
diff --git a/third_party/google_input_tools/src/chrome/os/inputview/_locales/en_GB/messages.json b/third_party/google_input_tools/src/chrome/os/inputview/_locales/en_GB/messages.json
index 90bf9183..d0437a5 100644
--- a/third_party/google_input_tools/src/chrome/os/inputview/_locales/en_GB/messages.json
+++ b/third_party/google_input_tools/src/chrome/os/inputview/_locales/en_GB/messages.json
@@ -2633,6 +2633,9 @@
   "ignore_correction": {
     "message": "Ignore correction for"
   },
+  "ignore_correction_short": {
+    "message": "Ignore correction"
+  },
   "il_heb_settings_page": {
     "message": "Hebrew Keyboard Settings Page"
   },
@@ -3282,7 +3285,7 @@
     "message": "Remove Selected"
   },
   "user_dict_reset": {
-    "message": "Reset All Dictionary Entries"
+    "message": "Reset all dictionary entries"
   },
   "user_dict_save": {
     "message": "Save"
diff --git a/third_party/google_input_tools/src/chrome/os/inputview/_locales/es/messages.json b/third_party/google_input_tools/src/chrome/os/inputview/_locales/es/messages.json
index 767ae62..a8969a8f 100644
--- a/third_party/google_input_tools/src/chrome/os/inputview/_locales/es/messages.json
+++ b/third_party/google_input_tools/src/chrome/os/inputview/_locales/es/messages.json
@@ -2633,6 +2633,9 @@
   "ignore_correction": {
     "message": "Ignorar correcci\u00f3n de"
   },
+  "ignore_correction_short": {
+    "message": "Ignore correction"
+  },
   "il_heb_settings_page": {
     "message": "P\u00e1gina de configuraci\u00f3n del teclado hebreo"
   },
@@ -3282,7 +3285,7 @@
     "message": "Eliminar selecci\u00f3n"
   },
   "user_dict_reset": {
-    "message": "Reset All Dictionary Entries"
+    "message": "Restablecer todas las entradas del diccionario"
   },
   "user_dict_save": {
     "message": "Guardar"
diff --git a/third_party/google_input_tools/src/chrome/os/inputview/_locales/es_419/messages.json b/third_party/google_input_tools/src/chrome/os/inputview/_locales/es_419/messages.json
index ab90857..72a688ea 100644
--- a/third_party/google_input_tools/src/chrome/os/inputview/_locales/es_419/messages.json
+++ b/third_party/google_input_tools/src/chrome/os/inputview/_locales/es_419/messages.json
@@ -2571,7 +2571,7 @@
     "message": "Expandir"
   },
   "expand_candidates": {
-    "message": "ampliar lista de candidatos"
+    "message": "expandir lista de candidatos"
   },
   "fi_fin_settings_page": {
     "message": "P\u00e1gina de configuraci\u00f3n del teclado fin\u00e9s"
@@ -2633,6 +2633,9 @@
   "ignore_correction": {
     "message": "Ignorar correcci\u00f3n de"
   },
+  "ignore_correction_short": {
+    "message": "Ignore correction"
+  },
   "il_heb_settings_page": {
     "message": "P\u00e1gina de configuraci\u00f3n del teclado hebreo"
   },
@@ -3282,7 +3285,7 @@
     "message": "Eliminar selecci\u00f3n"
   },
   "user_dict_reset": {
-    "message": "Reset All Dictionary Entries"
+    "message": "Restablecer todas las entradas del diccionario"
   },
   "user_dict_save": {
     "message": "Guardar"
diff --git a/third_party/google_input_tools/src/chrome/os/inputview/_locales/et/messages.json b/third_party/google_input_tools/src/chrome/os/inputview/_locales/et/messages.json
index d45b93f..9a30785 100644
--- a/third_party/google_input_tools/src/chrome/os/inputview/_locales/et/messages.json
+++ b/third_party/google_input_tools/src/chrome/os/inputview/_locales/et/messages.json
@@ -2633,6 +2633,9 @@
   "ignore_correction": {
     "message": "Ignoreeri parandust:"
   },
+  "ignore_correction_short": {
+    "message": "Ignore correction"
+  },
   "il_heb_settings_page": {
     "message": "Heebrea klaviatuuri seadete leht"
   },
@@ -3282,7 +3285,7 @@
     "message": "Eemalda valitud"
   },
   "user_dict_reset": {
-    "message": "Reset All Dictionary Entries"
+    "message": "L\u00e4htesta s\u00f5nastiku k\u00f5ik kanded"
   },
   "user_dict_save": {
     "message": "Salvesta"
diff --git a/third_party/google_input_tools/src/chrome/os/inputview/_locales/fa/messages.json b/third_party/google_input_tools/src/chrome/os/inputview/_locales/fa/messages.json
index ee2afa3..dc3a1a2 100644
--- a/third_party/google_input_tools/src/chrome/os/inputview/_locales/fa/messages.json
+++ b/third_party/google_input_tools/src/chrome/os/inputview/_locales/fa/messages.json
@@ -2633,6 +2633,9 @@
   "ignore_correction": {
     "message": "\u0646\u0627\u062f\u06cc\u062f\u0647 \u06af\u0631\u0641\u062a\u0646 \u062a\u0635\u062d\u06cc\u062d \u0628\u0631\u0627\u06cc"
   },
+  "ignore_correction_short": {
+    "message": "Ignore correction"
+  },
   "il_heb_settings_page": {
     "message": "\u0635\u0641\u062d\u0647 \u062a\u0646\u0638\u06cc\u0645\u0627\u062a \u0635\u0641\u062d\u0647\u200c\u06a9\u0644\u06cc\u062f \u0639\u0628\u0631\u06cc"
   },
@@ -3282,7 +3285,7 @@
     "message": "\u062d\u0630\u0641 \u0627\u0646\u062a\u062e\u0627\u0628\u200c\u0634\u062f\u0647\u200c\u0647\u0627"
   },
   "user_dict_reset": {
-    "message": "Reset All Dictionary Entries"
+    "message": "\u0628\u0627\u0632\u0646\u0634\u0627\u0646\u06cc \u0647\u0645\u0647 \u0648\u0631\u0648\u062f\u06cc\u200c\u0647\u0627\u06cc \u0641\u0631\u0647\u0646\u06af \u0644\u063a\u062a"
   },
   "user_dict_save": {
     "message": "\u0630\u062e\u06cc\u0631\u0647"
diff --git a/third_party/google_input_tools/src/chrome/os/inputview/_locales/fi/messages.json b/third_party/google_input_tools/src/chrome/os/inputview/_locales/fi/messages.json
index 3fd23275..ea73a4ba 100644
--- a/third_party/google_input_tools/src/chrome/os/inputview/_locales/fi/messages.json
+++ b/third_party/google_input_tools/src/chrome/os/inputview/_locales/fi/messages.json
@@ -2633,6 +2633,9 @@
   "ignore_correction": {
     "message": "\u00c4l\u00e4 korjaa:"
   },
+  "ignore_correction_short": {
+    "message": "Ignore correction"
+  },
   "il_heb_settings_page": {
     "message": "Hepreankielisen n\u00e4pp\u00e4imist\u00f6n asetukset"
   },
@@ -3282,7 +3285,7 @@
     "message": "Poista valitut"
   },
   "user_dict_reset": {
-    "message": "Reset All Dictionary Entries"
+    "message": "Nollaa koko sanakirjan sis\u00e4lt\u00f6"
   },
   "user_dict_save": {
     "message": "Tallenna"
diff --git a/third_party/google_input_tools/src/chrome/os/inputview/_locales/fil/messages.json b/third_party/google_input_tools/src/chrome/os/inputview/_locales/fil/messages.json
index e7a294a..8f6d111 100644
--- a/third_party/google_input_tools/src/chrome/os/inputview/_locales/fil/messages.json
+++ b/third_party/google_input_tools/src/chrome/os/inputview/_locales/fil/messages.json
@@ -2633,6 +2633,9 @@
   "ignore_correction": {
     "message": "Huwag pansinin ang pagwawasto para sa"
   },
+  "ignore_correction_short": {
+    "message": "Ignore correction"
+  },
   "il_heb_settings_page": {
     "message": "Page ng Mga Setting ng Hebrew na Keyboard"
   },
@@ -3282,7 +3285,7 @@
     "message": "Alisin ang Napili"
   },
   "user_dict_reset": {
-    "message": "Reset All Dictionary Entries"
+    "message": "I-reset ang Lahat ng Entry sa Diksyunaryo"
   },
   "user_dict_save": {
     "message": "I-save"
diff --git a/third_party/google_input_tools/src/chrome/os/inputview/_locales/fr/messages.json b/third_party/google_input_tools/src/chrome/os/inputview/_locales/fr/messages.json
index b8dcbab..989a421 100644
--- a/third_party/google_input_tools/src/chrome/os/inputview/_locales/fr/messages.json
+++ b/third_party/google_input_tools/src/chrome/os/inputview/_locales/fr/messages.json
@@ -2633,6 +2633,9 @@
   "ignore_correction": {
     "message": "Ignorer la correction pour"
   },
+  "ignore_correction_short": {
+    "message": "Ignore correction"
+  },
   "il_heb_settings_page": {
     "message": "Page des param\u00e8tres du clavier h\u00e9breu"
   },
@@ -3282,7 +3285,7 @@
     "message": "Supprimer la s\u00e9lection"
   },
   "user_dict_reset": {
-    "message": "Reset All Dictionary Entries"
+    "message": "R\u00e9initialiser toutes les entr\u00e9es du dictionnaire"
   },
   "user_dict_save": {
     "message": "Enregistrer"
diff --git a/third_party/google_input_tools/src/chrome/os/inputview/_locales/gu/messages.json b/third_party/google_input_tools/src/chrome/os/inputview/_locales/gu/messages.json
index dc80c641..01f5eb01 100644
--- a/third_party/google_input_tools/src/chrome/os/inputview/_locales/gu/messages.json
+++ b/third_party/google_input_tools/src/chrome/os/inputview/_locales/gu/messages.json
@@ -2633,6 +2633,9 @@
   "ignore_correction": {
     "message": "\u0a86 \u0aae\u0abe\u0a9f\u0ac7\u0aa8\u0abe \u0ab8\u0ac1\u0aa7\u0abe\u0ab0\u0aa8\u0ac7 \u0a85\u0ab5\u0a97\u0aa3\u0acb"
   },
+  "ignore_correction_short": {
+    "message": "Ignore correction"
+  },
   "il_heb_settings_page": {
     "message": "\u0ab9\u0ac0\u0aac\u0acd\u0ab0\u0ac1 \u0a95\u0ac0\u0aac\u0acb\u0ab0\u0acd\u0aa1 \u0ab8\u0ac7\u0a9f\u0abf\u0a82\u0a97\u0acd\u0ab8 \u0aaa\u0ac3\u0ab7\u0acd\u0aa0"
   },
@@ -3282,7 +3285,7 @@
     "message": "\u0aaa\u0ab8\u0a82\u0aa6 \u0a95\u0ab0\u0ac7\u0ab2\u0ac1\u0a82 \u0aa6\u0ac2\u0ab0 \u0a95\u0ab0\u0acb"
   },
   "user_dict_reset": {
-    "message": "Reset All Dictionary Entries"
+    "message": "\u0aa4\u0aae\u0abe\u0aae \u0ab6\u0aac\u0acd\u0aa6\u0a95\u0acb\u0ab6 \u0a8f\u0aa8\u0acd\u0a9f\u0acd\u0ab0\u0ac0\u0a93\u0aa8\u0ac7 \u0aab\u0ab0\u0ac0\u0aa5\u0ac0 \u0ab8\u0ac7\u0a9f \u0a95\u0ab0\u0acb"
   },
   "user_dict_save": {
     "message": "\u0ab8\u0abe\u0a9a\u0ab5\u0acb"
diff --git a/third_party/google_input_tools/src/chrome/os/inputview/_locales/hi/messages.json b/third_party/google_input_tools/src/chrome/os/inputview/_locales/hi/messages.json
index 57e1f6c..926044e 100644
--- a/third_party/google_input_tools/src/chrome/os/inputview/_locales/hi/messages.json
+++ b/third_party/google_input_tools/src/chrome/os/inputview/_locales/hi/messages.json
@@ -2633,6 +2633,9 @@
   "ignore_correction": {
     "message": "\u0907\u0938\u0915\u0947 \u0932\u093f\u090f \u0938\u0941\u0927\u093e\u0930 \u092a\u0930 \u0927\u094d\u092f\u093e\u0928 \u0928 \u0926\u0947\u0902"
   },
+  "ignore_correction_short": {
+    "message": "Ignore correction"
+  },
   "il_heb_settings_page": {
     "message": "\u0939\u093f\u092c\u094d\u0930\u0942 \u0915\u0940\u092c\u094b\u0930\u094d\u0921 \u0938\u0947\u091f\u093f\u0902\u0917 \u092a\u0943\u0937\u094d\u200d\u0920"
   },
@@ -3282,7 +3285,7 @@
     "message": "\u091a\u092f\u0928\u093f\u0924 \u0915\u094b \u0928\u093f\u0915\u093e\u0932\u0947\u0902"
   },
   "user_dict_reset": {
-    "message": "Reset All Dictionary Entries"
+    "message": "\u0938\u092d\u0940 \u0936\u092c\u094d\u200d\u0926\u0915\u094b\u0936 \u092a\u094d\u0930\u0935\u093f\u0937\u094d\u200d\u091f\u093f\u092f\u094b\u0902 \u0915\u094b \u0930\u0940\u0938\u0947\u091f \u0915\u0930\u0947\u0902"
   },
   "user_dict_save": {
     "message": "\u0938\u0939\u0947\u091c\u0947\u0902"
diff --git a/third_party/google_input_tools/src/chrome/os/inputview/_locales/hr/messages.json b/third_party/google_input_tools/src/chrome/os/inputview/_locales/hr/messages.json
index 20d6c0e..cc453fd3 100644
--- a/third_party/google_input_tools/src/chrome/os/inputview/_locales/hr/messages.json
+++ b/third_party/google_input_tools/src/chrome/os/inputview/_locales/hr/messages.json
@@ -2633,6 +2633,9 @@
   "ignore_correction": {
     "message": "Zanemari ispravak za"
   },
+  "ignore_correction_short": {
+    "message": "Ignore correction"
+  },
   "il_heb_settings_page": {
     "message": "Stranica postavki hebrejske tipkovnice"
   },
@@ -3282,7 +3285,7 @@
     "message": "Ukloni odabrano"
   },
   "user_dict_reset": {
-    "message": "Reset All Dictionary Entries"
+    "message": "Vrati sve rje\u010dni\u010dke unose na zadano"
   },
   "user_dict_save": {
     "message": "Spremi"
diff --git a/third_party/google_input_tools/src/chrome/os/inputview/_locales/hu/messages.json b/third_party/google_input_tools/src/chrome/os/inputview/_locales/hu/messages.json
index 3bdb3d4d..af4f8b5 100644
--- a/third_party/google_input_tools/src/chrome/os/inputview/_locales/hu/messages.json
+++ b/third_party/google_input_tools/src/chrome/os/inputview/_locales/hu/messages.json
@@ -2633,6 +2633,9 @@
   "ignore_correction": {
     "message": "Jav\u00edt\u00e1s mell\u0151z\u00e9se enn\u00e9l a sz\u00f3n\u00e1l:"
   },
+  "ignore_correction_short": {
+    "message": "Ignore correction"
+  },
   "il_heb_settings_page": {
     "message": "H\u00e9ber billenty\u0171zet be\u00e1ll\u00edt\u00e1sainak oldala"
   },
@@ -3282,7 +3285,7 @@
     "message": "Kijel\u00f6lt(ek) elt\u00e1vol\u00edt\u00e1sa"
   },
   "user_dict_reset": {
-    "message": "Reset All Dictionary Entries"
+    "message": "A sz\u00f3t\u00e1rban v\u00e9grehajtott \u00f6sszes m\u00f3dos\u00edt\u00e1s vissza\u00e1ll\u00edt\u00e1sa"
   },
   "user_dict_save": {
     "message": "Ment\u00e9s"
diff --git a/third_party/google_input_tools/src/chrome/os/inputview/_locales/id/messages.json b/third_party/google_input_tools/src/chrome/os/inputview/_locales/id/messages.json
index 46234451..a6df20e4 100644
--- a/third_party/google_input_tools/src/chrome/os/inputview/_locales/id/messages.json
+++ b/third_party/google_input_tools/src/chrome/os/inputview/_locales/id/messages.json
@@ -2633,6 +2633,9 @@
   "ignore_correction": {
     "message": "Abaikan koreksi untuk"
   },
+  "ignore_correction_short": {
+    "message": "Ignore correction"
+  },
   "il_heb_settings_page": {
     "message": "Laman Setelan Keyboard Ibrani"
   },
@@ -3282,7 +3285,7 @@
     "message": "Hapus yang Dipilih"
   },
   "user_dict_reset": {
-    "message": "Reset All Dictionary Entries"
+    "message": "Setel Ulang Semua Entri Kamus"
   },
   "user_dict_save": {
     "message": "Simpan"
diff --git a/third_party/google_input_tools/src/chrome/os/inputview/_locales/it/messages.json b/third_party/google_input_tools/src/chrome/os/inputview/_locales/it/messages.json
index b11c616..39837d9a 100644
--- a/third_party/google_input_tools/src/chrome/os/inputview/_locales/it/messages.json
+++ b/third_party/google_input_tools/src/chrome/os/inputview/_locales/it/messages.json
@@ -2633,6 +2633,9 @@
   "ignore_correction": {
     "message": "Ignora correzione di:"
   },
+  "ignore_correction_short": {
+    "message": "Ignore correction"
+  },
   "il_heb_settings_page": {
     "message": "Pagina Impostazioni tastiera Ebraico"
   },
@@ -3282,7 +3285,7 @@
     "message": "Rimuovi selezionati"
   },
   "user_dict_reset": {
-    "message": "Reset All Dictionary Entries"
+    "message": "Reimposta tutte le voci di dizionario"
   },
   "user_dict_save": {
     "message": "Salva"
diff --git a/third_party/google_input_tools/src/chrome/os/inputview/_locales/iw/messages.json b/third_party/google_input_tools/src/chrome/os/inputview/_locales/iw/messages.json
index 205cc72..aedadc0 100644
--- a/third_party/google_input_tools/src/chrome/os/inputview/_locales/iw/messages.json
+++ b/third_party/google_input_tools/src/chrome/os/inputview/_locales/iw/messages.json
@@ -2633,6 +2633,9 @@
   "ignore_correction": {
     "message": "\u05d4\u05ea\u05e2\u05dc\u05dd \u05de\u05ea\u05d9\u05e7\u05d5\u05df \u05e9\u05dc"
   },
+  "ignore_correction_short": {
+    "message": "Ignore correction"
+  },
   "il_heb_settings_page": {
     "message": "\u05d3\u05e3 \u05d4\u05d2\u05d3\u05e8\u05d5\u05ea \u05e9\u05dc \u05de\u05e7\u05dc\u05d3\u05ea \u05e2\u05d1\u05e8\u05d9\u05ea"
   },
@@ -3282,7 +3285,7 @@
     "message": "\u05d4\u05e1\u05e8 \u05e2\u05e8\u05da \u05e0\u05d1\u05d7\u05e8"
   },
   "user_dict_reset": {
-    "message": "Reset All Dictionary Entries"
+    "message": "\u05d0\u05e4\u05e1 \u05d0\u05ea \u05db\u05dc \u05e2\u05e8\u05db\u05d9 \u05d4\u05de\u05d9\u05dc\u05d5\u05df"
   },
   "user_dict_save": {
     "message": "\u05e9\u05de\u05d5\u05e8"
diff --git a/third_party/google_input_tools/src/chrome/os/inputview/_locales/ja/messages.json b/third_party/google_input_tools/src/chrome/os/inputview/_locales/ja/messages.json
index a1beb42..9713b9f 100644
--- a/third_party/google_input_tools/src/chrome/os/inputview/_locales/ja/messages.json
+++ b/third_party/google_input_tools/src/chrome/os/inputview/_locales/ja/messages.json
@@ -2633,6 +2633,9 @@
   "ignore_correction": {
     "message": "\u6b21\u306e\u8a9e\u306f\u4fee\u6b63\u3057\u306a\u3044:"
   },
+  "ignore_correction_short": {
+    "message": "Ignore correction"
+  },
   "il_heb_settings_page": {
     "message": "\u30d8\u30d6\u30e9\u30a4\u8a9e\u30ad\u30fc\u30dc\u30fc\u30c9\u8a2d\u5b9a\u30da\u30fc\u30b8"
   },
@@ -3282,7 +3285,7 @@
     "message": "\u9078\u629e\u9805\u76ee\u3092\u524a\u9664"
   },
   "user_dict_reset": {
-    "message": "Reset All Dictionary Entries"
+    "message": "\u3059\u3079\u3066\u306e\u8f9e\u66f8\u9805\u76ee\u3092\u30ea\u30bb\u30c3\u30c8"
   },
   "user_dict_save": {
     "message": "\u4fdd\u5b58"
diff --git a/third_party/google_input_tools/src/chrome/os/inputview/_locales/kn/messages.json b/third_party/google_input_tools/src/chrome/os/inputview/_locales/kn/messages.json
index d976d11f..7e68532b 100644
--- a/third_party/google_input_tools/src/chrome/os/inputview/_locales/kn/messages.json
+++ b/third_party/google_input_tools/src/chrome/os/inputview/_locales/kn/messages.json
@@ -2633,6 +2633,9 @@
   "ignore_correction": {
     "message": "\u0c87\u0ca6\u0c95\u0ccd\u0c95\u0cbe\u0c97\u0cbf \u0cb8\u0cb0\u0cbf\u0caa\u0ca1\u0cbf\u0cb8\u0cc1\u0cb5\u0cbf\u0c95\u0cc6\u0caf\u0ca8\u0ccd\u0ca8\u0cc1 \u0ca8\u0cbf\u0cb0\u0ccd\u0cb2\u0c95\u0ccd\u0cb7\u0cbf\u0cb8\u0cbf"
   },
+  "ignore_correction_short": {
+    "message": "Ignore correction"
+  },
   "il_heb_settings_page": {
     "message": "\u0cb9\u0cbf\u0cac\u0ccd\u0cb0\u0cc2 \u0c95\u0cbf\u0cd5\u0cac\u0cc6\u0cc2\u0cd5\u0cb0\u0ccd\u0ca1\u0ccd \u0cb8\u0cc6\u0c9f\u0ccd\u0c9f\u0cbf\u0c82\u0c97\u0ccd\u200c\u0c97\u0cb3 \u0caa\u0cc1\u0c9f"
   },
@@ -3282,7 +3285,7 @@
     "message": "\u0c86\u0caf\u0ccd\u0c95\u0cc6\u0cae\u0cbe\u0ca1\u0cb2\u0cbe\u0c97\u0cbf\u0cb0\u0cc1\u0cb5\u0cc1\u0ca6\u0ca8\u0ccd\u0ca8\u0cc1 \u0ca4\u0cc6\u0c97\u0cc6\u0ca6\u0cc1\u0cb9\u0cbe\u0c95\u0cbf"
   },
   "user_dict_reset": {
-    "message": "Reset All Dictionary Entries"
+    "message": "\u0cb6\u0cac\u0ccd\u0ca6\u0c95\u0ccb\u0cb6\u0ca6 \u0c8e\u0cb2\u0ccd\u0cb2\u0cbe  \u0ca8\u0cae\u0cc2\u0ca6\u0cc1\u0c97\u0cb3\u0ca8\u0ccd\u0ca8\u0cc1 \u0cae\u0cb0\u0cc1\u0cb9\u0cca\u0c82\u0ca6\u0cbf\u0cb8\u0cbf"
   },
   "user_dict_save": {
     "message": "\u0c89\u0cb3\u0cbf\u0cb8\u0cc1"
diff --git a/third_party/google_input_tools/src/chrome/os/inputview/_locales/ko/messages.json b/third_party/google_input_tools/src/chrome/os/inputview/_locales/ko/messages.json
index 20a139d..75dc586 100644
--- a/third_party/google_input_tools/src/chrome/os/inputview/_locales/ko/messages.json
+++ b/third_party/google_input_tools/src/chrome/os/inputview/_locales/ko/messages.json
@@ -2633,6 +2633,9 @@
   "ignore_correction": {
     "message": "\uc790\ub3d9 \uc218\uc815 \uae30\ub2a5 \ubb34\uc2dc"
   },
+  "ignore_correction_short": {
+    "message": "Ignore correction"
+  },
   "il_heb_settings_page": {
     "message": "\ud788\ube0c\ub9ac\uc5b4 \ud0a4\ubcf4\ub4dc \uc124\uc815 \ud398\uc774\uc9c0"
   },
@@ -3282,7 +3285,7 @@
     "message": "\uc120\ud0dd \ud56d\ubaa9 \uc0ad\uc81c"
   },
   "user_dict_reset": {
-    "message": "Reset All Dictionary Entries"
+    "message": "\ubaa8\ub4e0 \uc0ac\uc804 \ud56d\ubaa9 \ucd08\uae30\ud654"
   },
   "user_dict_save": {
     "message": "\uc800\uc7a5"
diff --git a/third_party/google_input_tools/src/chrome/os/inputview/_locales/lt/messages.json b/third_party/google_input_tools/src/chrome/os/inputview/_locales/lt/messages.json
index 57ebfd7..a8bb5d2a 100644
--- a/third_party/google_input_tools/src/chrome/os/inputview/_locales/lt/messages.json
+++ b/third_party/google_input_tools/src/chrome/os/inputview/_locales/lt/messages.json
@@ -2633,6 +2633,9 @@
   "ignore_correction": {
     "message": "Ignoruoti pataisym\u0105"
   },
+  "ignore_correction_short": {
+    "message": "Ignore correction"
+  },
   "il_heb_settings_page": {
     "message": "Hebraji\u0161kos klaviat\u016bros nustatym\u0173 puslapis"
   },
@@ -3282,7 +3285,7 @@
     "message": "Pa\u0161alinti pasirinktus"
   },
   "user_dict_reset": {
-    "message": "Reset All Dictionary Entries"
+    "message": "Nustatyti visus \u017eodyno \u012fra\u0161us i\u0161 naujo"
   },
   "user_dict_save": {
     "message": "I\u0161saugoti"
diff --git a/third_party/google_input_tools/src/chrome/os/inputview/_locales/lv/messages.json b/third_party/google_input_tools/src/chrome/os/inputview/_locales/lv/messages.json
index 7d90fc9..8be3566d 100644
--- a/third_party/google_input_tools/src/chrome/os/inputview/_locales/lv/messages.json
+++ b/third_party/google_input_tools/src/chrome/os/inputview/_locales/lv/messages.json
@@ -2633,6 +2633,9 @@
   "ignore_correction": {
     "message": "Ignor\u0113t labojumu v\u0101rdam"
   },
+  "ignore_correction_short": {
+    "message": "Ignore correction"
+  },
   "il_heb_settings_page": {
     "message": "Ebreju valodas tastat\u016bras iestat\u012bjumu lapa"
   },
@@ -3282,7 +3285,7 @@
     "message": "No\u0146emt atlas\u012btos"
   },
   "user_dict_reset": {
-    "message": "Reset All Dictionary Entries"
+    "message": "Atiestat\u012bt visus v\u0101rdn\u012bcas ierakstus"
   },
   "user_dict_save": {
     "message": "Saglab\u0101t"
diff --git a/third_party/google_input_tools/src/chrome/os/inputview/_locales/ml/messages.json b/third_party/google_input_tools/src/chrome/os/inputview/_locales/ml/messages.json
index 5860bc5..25e1d44 100644
--- a/third_party/google_input_tools/src/chrome/os/inputview/_locales/ml/messages.json
+++ b/third_party/google_input_tools/src/chrome/os/inputview/_locales/ml/messages.json
@@ -2631,7 +2631,10 @@
     "message": "\u0d10\u0d31\u0d3f\u0d37\u0d4d \u0d15\u0d40\u0d2c\u0d4b\u0d7c\u0d21\u0d4d \u0d15\u0d4d\u0d30\u0d2e\u0d40\u0d15\u0d30\u0d23 \u0d2a\u0d47\u0d1c\u0d4d"
   },
   "ignore_correction": {
-    "message": "\u0d0e\u0d28\u0d4d\u0d28\u0d24\u0d3f\u0d28\u0d41\u0d33\u0d4d\u0d33 \u0d24\u0d3f\u0d30\u0d41\u0d24\u0d4d\u0d24\u0d7d \u0d05\u0d35\u0d17\u0d23\u0d3f\u0d15\u0d4d\u0d15\u0d41\u0d15"
+    "message": "\u0d08 \u0d35\u0d3e\u0d15\u0d4d\u0d15\u0d3f\u0d28\u0d41\u0d33\u0d4d\u0d33 \u0d24\u0d3f\u0d30\u0d41\u0d24\u0d4d\u0d24\u0d7d \u0d05\u0d35\u0d17\u0d23\u0d3f\u0d15\u0d4d\u0d15\u0d41\u0d15"
+  },
+  "ignore_correction_short": {
+    "message": "Ignore correction"
   },
   "il_heb_settings_page": {
     "message": "\u0d39\u0d40\u0d2c\u0d4d\u0d30\u0d41 \u0d15\u0d40\u0d2c\u0d4b\u0d7c\u0d21\u0d4d \u0d15\u0d4d\u0d30\u0d2e\u0d40\u0d15\u0d30\u0d23 \u0d2a\u0d47\u0d1c\u0d4d"
@@ -3282,7 +3285,7 @@
     "message": "\u0d24\u0d3f\u0d30\u0d1e\u0d4d\u0d1e\u0d46\u0d1f\u0d41\u0d24\u0d4d\u0d24\u0d35 \u0d28\u0d40\u0d15\u0d4d\u0d15\u0d02\u0d1a\u0d46\u0d2f\u0d4d\u0d2f\u0d41\u0d15"
   },
   "user_dict_reset": {
-    "message": "Reset All Dictionary Entries"
+    "message": "\u0d0e\u0d32\u0d4d\u0d32\u0d3e \u0d28\u0d3f\u0d18\u0d23\u0d4d\u0d1f\u0d41 \u0d0e\u0d7b\u0d1f\u0d4d\u0d30\u0d3f\u0d15\u0d33\u0d41\u0d02 \u0d31\u0d40\u0d38\u0d46\u0d31\u0d4d\u0d31\u0d41\u0d1a\u0d46\u0d2f\u0d4d\u0d2f\u0d41\u0d15"
   },
   "user_dict_save": {
     "message": "\u0d38\u0d02\u0d30\u0d15\u0d4d\u0d37\u0d3f\u0d15\u0d4d\u0d15\u0d41\u0d15"
diff --git a/third_party/google_input_tools/src/chrome/os/inputview/_locales/mr/messages.json b/third_party/google_input_tools/src/chrome/os/inputview/_locales/mr/messages.json
index f1bd1ba..06ff85c 100644
--- a/third_party/google_input_tools/src/chrome/os/inputview/_locales/mr/messages.json
+++ b/third_party/google_input_tools/src/chrome/os/inputview/_locales/mr/messages.json
@@ -2633,6 +2633,9 @@
   "ignore_correction": {
     "message": "\u092f\u093e\u0938\u093e\u0920\u0940 \u0938\u0941\u0927\u093e\u0930\u0923\u0947\u0915\u0921\u0947 \u0926\u0941\u0930\u094d\u0932\u0915\u094d\u0937 \u0915\u0930\u093e"
   },
+  "ignore_correction_short": {
+    "message": "Ignore correction"
+  },
   "il_heb_settings_page": {
     "message": "\u0939\u093f\u092c\u094d\u0930\u0942 \u0915\u0940\u092c\u094b\u0930\u094d\u0921 \u0938\u0947\u091f\u093f\u0902\u0917\u094d\u091c \u092a\u0943\u0937\u094d\u0920"
   },
@@ -3282,7 +3285,7 @@
     "message": "\u0928\u093f\u0935\u0921\u0932\u0947\u0932\u0947 \u0915\u093e\u0922\u093e"
   },
   "user_dict_reset": {
-    "message": "Reset All Dictionary Entries"
+    "message": "\u0938\u0930\u094d\u0935 \u0936\u092c\u094d\u0926\u0915\u094b\u0936 \u092a\u094d\u0930\u0935\u093f\u0937\u094d\u091f\u094d\u092f\u093e \u0930\u0940\u0938\u0947\u091f \u0915\u0930\u093e"
   },
   "user_dict_save": {
     "message": "\u091c\u0924\u0928 \u0915\u0930\u093e"
diff --git a/third_party/google_input_tools/src/chrome/os/inputview/_locales/ms/messages.json b/third_party/google_input_tools/src/chrome/os/inputview/_locales/ms/messages.json
index a8684822c..cd00ee59 100644
--- a/third_party/google_input_tools/src/chrome/os/inputview/_locales/ms/messages.json
+++ b/third_party/google_input_tools/src/chrome/os/inputview/_locales/ms/messages.json
@@ -2633,6 +2633,9 @@
   "ignore_correction": {
     "message": "Abaikan pembetulan untuk"
   },
+  "ignore_correction_short": {
+    "message": "Ignore correction"
+  },
   "il_heb_settings_page": {
     "message": "Halaman Tetapan Papan Kekunci Ibrani"
   },
@@ -3282,7 +3285,7 @@
     "message": "Alih Keluar Pilihan"
   },
   "user_dict_reset": {
-    "message": "Reset All Dictionary Entries"
+    "message": "Tetapkan Semula Semua Masukan Kamus"
   },
   "user_dict_save": {
     "message": "Simpan"
diff --git a/third_party/google_input_tools/src/chrome/os/inputview/_locales/nb/messages.json b/third_party/google_input_tools/src/chrome/os/inputview/_locales/nb/messages.json
index 6b5ce04..3b67ede6 100644
--- a/third_party/google_input_tools/src/chrome/os/inputview/_locales/nb/messages.json
+++ b/third_party/google_input_tools/src/chrome/os/inputview/_locales/nb/messages.json
@@ -2633,6 +2633,9 @@
   "ignore_correction": {
     "message": "Ignorer korrigering av"
   },
+  "ignore_correction_short": {
+    "message": "Ignore correction"
+  },
   "il_heb_settings_page": {
     "message": "Innstillinger-side for hebraisk tastatur"
   },
@@ -3282,7 +3285,7 @@
     "message": "Fjern valgte"
   },
   "user_dict_reset": {
-    "message": "Reset All Dictionary Entries"
+    "message": "Tilbakestill alle ordbokbidrag"
   },
   "user_dict_save": {
     "message": "Lagre"
diff --git a/third_party/google_input_tools/src/chrome/os/inputview/_locales/nl/messages.json b/third_party/google_input_tools/src/chrome/os/inputview/_locales/nl/messages.json
index 63de460d..45f09690 100644
--- a/third_party/google_input_tools/src/chrome/os/inputview/_locales/nl/messages.json
+++ b/third_party/google_input_tools/src/chrome/os/inputview/_locales/nl/messages.json
@@ -2633,6 +2633,9 @@
   "ignore_correction": {
     "message": "Correctie negeren voor"
   },
+  "ignore_correction_short": {
+    "message": "Ignore correction"
+  },
   "il_heb_settings_page": {
     "message": "Pagina met instellingen voor Hebreeuws toetsenbord"
   },
@@ -3282,7 +3285,7 @@
     "message": "Selectie verwijderen"
   },
   "user_dict_reset": {
-    "message": "Reset All Dictionary Entries"
+    "message": "Alle woordenboekvermeldingen opnieuw instellen"
   },
   "user_dict_save": {
     "message": "Opslaan"
diff --git a/third_party/google_input_tools/src/chrome/os/inputview/_locales/pl/messages.json b/third_party/google_input_tools/src/chrome/os/inputview/_locales/pl/messages.json
index aa9150c..2a751bc 100644
--- a/third_party/google_input_tools/src/chrome/os/inputview/_locales/pl/messages.json
+++ b/third_party/google_input_tools/src/chrome/os/inputview/_locales/pl/messages.json
@@ -2633,6 +2633,9 @@
   "ignore_correction": {
     "message": "Ignoruj korekt\u0119"
   },
+  "ignore_correction_short": {
+    "message": "Ignore correction"
+  },
   "il_heb_settings_page": {
     "message": "Strona ustawie\u0144 klawiatury hebrajskiej"
   },
@@ -3282,7 +3285,7 @@
     "message": "Usu\u0144 zaznaczone"
   },
   "user_dict_reset": {
-    "message": "Reset All Dictionary Entries"
+    "message": "Zresetuj wszystkie wpisy s\u0142ownika"
   },
   "user_dict_save": {
     "message": "Zapisz"
diff --git a/third_party/google_input_tools/src/chrome/os/inputview/_locales/pt_BR/messages.json b/third_party/google_input_tools/src/chrome/os/inputview/_locales/pt_BR/messages.json
index 3b42b3c..55e5003 100644
--- a/third_party/google_input_tools/src/chrome/os/inputview/_locales/pt_BR/messages.json
+++ b/third_party/google_input_tools/src/chrome/os/inputview/_locales/pt_BR/messages.json
@@ -2633,6 +2633,9 @@
   "ignore_correction": {
     "message": "Ignorar corre\u00e7\u00e3o para"
   },
+  "ignore_correction_short": {
+    "message": "Ignore correction"
+  },
   "il_heb_settings_page": {
     "message": "P\u00e1gina de configura\u00e7\u00f5es do teclado hebraico"
   },
@@ -3282,7 +3285,7 @@
     "message": "Remover entradas selecionadas"
   },
   "user_dict_reset": {
-    "message": "Reset All Dictionary Entries"
+    "message": "Redefinir todas as entradas do dicion\u00e1rio"
   },
   "user_dict_save": {
     "message": "Salvar"
diff --git a/third_party/google_input_tools/src/chrome/os/inputview/_locales/pt_PT/messages.json b/third_party/google_input_tools/src/chrome/os/inputview/_locales/pt_PT/messages.json
index d4bd96e..1a3317c5 100644
--- a/third_party/google_input_tools/src/chrome/os/inputview/_locales/pt_PT/messages.json
+++ b/third_party/google_input_tools/src/chrome/os/inputview/_locales/pt_PT/messages.json
@@ -2633,6 +2633,9 @@
   "ignore_correction": {
     "message": "Ignorar corre\u00e7\u00e3o para"
   },
+  "ignore_correction_short": {
+    "message": "Ignore correction"
+  },
   "il_heb_settings_page": {
     "message": "P\u00e1gina de defini\u00e7\u00f5es do teclado hebraico"
   },
@@ -3282,7 +3285,7 @@
     "message": "Remover selecionadas"
   },
   "user_dict_reset": {
-    "message": "Reset All Dictionary Entries"
+    "message": "Repor todas as entradas do dicion\u00e1rio"
   },
   "user_dict_save": {
     "message": "Guardar"
diff --git a/third_party/google_input_tools/src/chrome/os/inputview/_locales/ro/messages.json b/third_party/google_input_tools/src/chrome/os/inputview/_locales/ro/messages.json
index d156016c..4b9177f 100644
--- a/third_party/google_input_tools/src/chrome/os/inputview/_locales/ro/messages.json
+++ b/third_party/google_input_tools/src/chrome/os/inputview/_locales/ro/messages.json
@@ -39,7 +39,7 @@
     "message": "Pagina de set\u0103ri pentru tastatura belgian\u0103 (german\u0103)"
   },
   "be_nld_settings_page": {
-    "message": "Pagina de set\u0103ri pentru tastatura belgian\u0103 (olandez\u0103)"
+    "message": "Pagina de set\u0103ri pentru tastatura belgian\u0103 (neerlandez\u0103)"
   },
   "bg_bul_settings_page": {
     "message": "Pagina de set\u0103ri pentru tastatura bulgar\u0103"
@@ -2633,6 +2633,9 @@
   "ignore_correction": {
     "message": "Ignor\u0103 corectura pentru"
   },
+  "ignore_correction_short": {
+    "message": "Ignore correction"
+  },
   "il_heb_settings_page": {
     "message": "Pagina de set\u0103ri pentru tastatura ebraic\u0103"
   },
@@ -3282,7 +3285,7 @@
     "message": "Elimin\u0103-le pe cele selectate"
   },
   "user_dict_reset": {
-    "message": "Reset All Dictionary Entries"
+    "message": "Reseteaz\u0103 toate intr\u0103rile din dic\u021bionar"
   },
   "user_dict_save": {
     "message": "Salveaz\u0103"
diff --git a/third_party/google_input_tools/src/chrome/os/inputview/_locales/ru/messages.json b/third_party/google_input_tools/src/chrome/os/inputview/_locales/ru/messages.json
index 6dfb2d00..0263f1f 100644
--- a/third_party/google_input_tools/src/chrome/os/inputview/_locales/ru/messages.json
+++ b/third_party/google_input_tools/src/chrome/os/inputview/_locales/ru/messages.json
@@ -2633,6 +2633,9 @@
   "ignore_correction": {
     "message": "\u041f\u0440\u043e\u043f\u0443\u0441\u0442\u0438\u0442\u044c:"
   },
+  "ignore_correction_short": {
+    "message": "Ignore correction"
+  },
   "il_heb_settings_page": {
     "message": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0440\u0430\u0441\u043a\u043b\u0430\u0434\u043a\u0438 \u043d\u0430 \u0438\u0432\u0440\u0438\u0442\u0435"
   },
@@ -3282,7 +3285,7 @@
     "message": "\u0423\u0434\u0430\u043b\u0438\u0442\u044c \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u043e\u0435"
   },
   "user_dict_reset": {
-    "message": "Reset All Dictionary Entries"
+    "message": "\u041e\u0447\u0438\u0441\u0442\u0438\u0442\u044c \u0441\u043b\u043e\u0432\u0430\u0440\u044c"
   },
   "user_dict_save": {
     "message": "\u0421\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c"
diff --git a/third_party/google_input_tools/src/chrome/os/inputview/_locales/sk/messages.json b/third_party/google_input_tools/src/chrome/os/inputview/_locales/sk/messages.json
index fb0d7f3..d8d11b0 100644
--- a/third_party/google_input_tools/src/chrome/os/inputview/_locales/sk/messages.json
+++ b/third_party/google_input_tools/src/chrome/os/inputview/_locales/sk/messages.json
@@ -2633,6 +2633,9 @@
   "ignore_correction": {
     "message": "Ignorova\u0165 opravu pre"
   },
+  "ignore_correction_short": {
+    "message": "Ignore correction"
+  },
   "il_heb_settings_page": {
     "message": "Str\u00e1nka s nastaveniami hebrejskej kl\u00e1vesnice"
   },
@@ -3282,7 +3285,7 @@
     "message": "Odstr\u00e1ni\u0165 vybran\u00e9"
   },
   "user_dict_reset": {
-    "message": "Reset All Dictionary Entries"
+    "message": "Resetova\u0165 v\u0161etky polo\u017eky v slovn\u00edku"
   },
   "user_dict_save": {
     "message": "Ulo\u017ei\u0165"
diff --git a/third_party/google_input_tools/src/chrome/os/inputview/_locales/sl/messages.json b/third_party/google_input_tools/src/chrome/os/inputview/_locales/sl/messages.json
index f2aaca2..bba5c5e 100644
--- a/third_party/google_input_tools/src/chrome/os/inputview/_locales/sl/messages.json
+++ b/third_party/google_input_tools/src/chrome/os/inputview/_locales/sl/messages.json
@@ -2633,6 +2633,9 @@
   "ignore_correction": {
     "message": "Prezri popravek za"
   },
+  "ignore_correction_short": {
+    "message": "Ignore correction"
+  },
   "il_heb_settings_page": {
     "message": "Stran z nastavitvami hebrejske tipkovnice"
   },
@@ -3282,7 +3285,7 @@
     "message": "Odstrani izbor"
   },
   "user_dict_reset": {
-    "message": "Reset All Dictionary Entries"
+    "message": "Ponastavi vse slovarske vnose"
   },
   "user_dict_save": {
     "message": "Shrani"
diff --git a/third_party/google_input_tools/src/chrome/os/inputview/_locales/sr/messages.json b/third_party/google_input_tools/src/chrome/os/inputview/_locales/sr/messages.json
index 54a16b3f..3e4dea60 100644
--- a/third_party/google_input_tools/src/chrome/os/inputview/_locales/sr/messages.json
+++ b/third_party/google_input_tools/src/chrome/os/inputview/_locales/sr/messages.json
@@ -2633,6 +2633,9 @@
   "ignore_correction": {
     "message": "\u0417\u0430\u043d\u0435\u043c\u0430\u0440\u0438 \u0438\u0441\u043f\u0440\u0430\u0432\u043a\u0443 \u0437\u0430"
   },
+  "ignore_correction_short": {
+    "message": "Ignore correction"
+  },
   "il_heb_settings_page": {
     "message": "\u0421\u0442\u0440\u0430\u043d\u0438\u0446\u0430 \u0441\u0430 \u043f\u043e\u0434\u0435\u0448\u0430\u0432\u0430\u045a\u0438\u043c\u0430 \u0445\u0435\u0431\u0440\u0435\u0458\u0441\u043a\u0435 \u0442\u0430\u0441\u0442\u0430\u0442\u0443\u0440\u0435"
   },
@@ -3282,7 +3285,7 @@
     "message": "\u0423\u043a\u043b\u043e\u043d\u0438 \u0438\u0437\u0430\u0431\u0440\u0430\u043d\u043e"
   },
   "user_dict_reset": {
-    "message": "Reset All Dictionary Entries"
+    "message": "\u0420\u0435\u0441\u0435\u0442\u0443\u0458 \u0441\u0432\u0435 \u0443\u043d\u043e\u0441\u0435 \u0443 \u0440\u0435\u0447\u043d\u0438\u043a\u0443"
   },
   "user_dict_save": {
     "message": "\u0421\u0430\u0447\u0443\u0432\u0430\u0458"
diff --git a/third_party/google_input_tools/src/chrome/os/inputview/_locales/sv/messages.json b/third_party/google_input_tools/src/chrome/os/inputview/_locales/sv/messages.json
index 1bc9b29..75b5ae2 100644
--- a/third_party/google_input_tools/src/chrome/os/inputview/_locales/sv/messages.json
+++ b/third_party/google_input_tools/src/chrome/os/inputview/_locales/sv/messages.json
@@ -2633,6 +2633,9 @@
   "ignore_correction": {
     "message": "Ignorera korrigeringen av"
   },
+  "ignore_correction_short": {
+    "message": "Ignore correction"
+  },
   "il_heb_settings_page": {
     "message": "Inst\u00e4llningssidan f\u00f6r hebreiskt tangentbord"
   },
@@ -3282,7 +3285,7 @@
     "message": "Ta bort markerade poster"
   },
   "user_dict_reset": {
-    "message": "Reset All Dictionary Entries"
+    "message": "\u00c5terst\u00e4ll alla \u00e4ndringar i ordlistan"
   },
   "user_dict_save": {
     "message": "Spara"
diff --git a/third_party/google_input_tools/src/chrome/os/inputview/_locales/sw/messages.json b/third_party/google_input_tools/src/chrome/os/inputview/_locales/sw/messages.json
index a1a125d..073a1c1 100644
--- a/third_party/google_input_tools/src/chrome/os/inputview/_locales/sw/messages.json
+++ b/third_party/google_input_tools/src/chrome/os/inputview/_locales/sw/messages.json
@@ -2571,7 +2571,7 @@
     "message": "Panua"
   },
   "expand_candidates": {
-    "message": "panua orodha ya wagombea"
+    "message": "panua orodha ya maneno yanayopendekezwa"
   },
   "fi_fin_settings_page": {
     "message": "Ukurasa wa Mipangilio ya Kibodi ya Kifini"
@@ -2631,7 +2631,10 @@
     "message": "Ukurasa wa Mipangilio ya Kibodi ya Kiairishi"
   },
   "ignore_correction": {
-    "message": "Puuza marekebisho ya neno"
+    "message": "Puuza usahihishaji wa neno"
+  },
+  "ignore_correction_short": {
+    "message": "Ignore correction"
   },
   "il_heb_settings_page": {
     "message": "Ukurasa wa Mipangilio ya Kibodi ya Kiyahudi"
@@ -3105,7 +3108,7 @@
     "message": "Onyesha mapendekezo ya Hangul"
   },
   "shrink_candidates": {
-    "message": "punguza orodha ya wagombea"
+    "message": "punguza orodha ya maneno yanayopendekezwa"
   },
   "si_slv_settings_page": {
     "message": "Ukurasa wa Mipangilio ya Kibodi ya Kislovania"
@@ -3282,7 +3285,7 @@
     "message": "Ondoa Uliochaguliwa"
   },
   "user_dict_reset": {
-    "message": "Reset All Dictionary Entries"
+    "message": "Weka Upya Mabadiliko Yote ya Kamusi"
   },
   "user_dict_save": {
     "message": "Hifadhi"
diff --git a/third_party/google_input_tools/src/chrome/os/inputview/_locales/ta/messages.json b/third_party/google_input_tools/src/chrome/os/inputview/_locales/ta/messages.json
index dcf08b3d..848cabf6f 100644
--- a/third_party/google_input_tools/src/chrome/os/inputview/_locales/ta/messages.json
+++ b/third_party/google_input_tools/src/chrome/os/inputview/_locales/ta/messages.json
@@ -2633,6 +2633,9 @@
   "ignore_correction": {
     "message": "\u0b87\u0ba8\u0bcd\u0ba4\u0b9a\u0bcd \u0b9a\u0bca\u0bb2\u0bcd\u0bb2\u0bbf\u0ba9\u0bcd \u0ba4\u0bbf\u0bb0\u0bc1\u0ba4\u0bcd\u0ba4\u0ba4\u0bcd\u0ba4\u0bc8\u0ba4\u0bcd \u0ba4\u0bb5\u0bbf\u0bb0\u0bcd"
   },
+  "ignore_correction_short": {
+    "message": "Ignore correction"
+  },
   "il_heb_settings_page": {
     "message": "\u0bb9\u0bc0\u0baa\u0bcd\u0bb0\u0bc1 \u0bb5\u0bbf\u0b9a\u0bc8\u0baa\u0bcd\u0baa\u0bb2\u0b95\u0bc8 \u0b85\u0bae\u0bc8\u0baa\u0bcd\u0baa\u0bc1\u0b95\u0bb3\u0bcd \u0baa\u0b95\u0bcd\u0b95\u0bae\u0bcd"
   },
@@ -3282,7 +3285,7 @@
     "message": "\u0ba4\u0bc7\u0bb0\u0bcd\u0ba8\u0bcd\u0ba4\u0bc6\u0b9f\u0bc1\u0ba4\u0bcd\u0ba4\u0bb5\u0bb1\u0bcd\u0bb1\u0bc8 \u0b85\u0b95\u0bb1\u0bcd\u0bb1\u0bc1"
   },
   "user_dict_reset": {
-    "message": "Reset All Dictionary Entries"
+    "message": "\u0b8e\u0bb2\u0bcd\u0bb2\u0bbe \u0b85\u0b95\u0bb0\u0bbe\u0ba4\u0bbf \u0b89\u0bb3\u0bcd\u0bb3\u0bc0\u0b9f\u0bc1\u0b95\u0bb3\u0bc8\u0baf\u0bc1\u0bae\u0bcd \u0bae\u0bc0\u0b9f\u0bcd\u0b9f\u0bae\u0bc8"
   },
   "user_dict_save": {
     "message": "\u0b9a\u0bc7\u0bae\u0bbf"
diff --git a/third_party/google_input_tools/src/chrome/os/inputview/_locales/te/messages.json b/third_party/google_input_tools/src/chrome/os/inputview/_locales/te/messages.json
index 086a4ff1..83ad0360 100644
--- a/third_party/google_input_tools/src/chrome/os/inputview/_locales/te/messages.json
+++ b/third_party/google_input_tools/src/chrome/os/inputview/_locales/te/messages.json
@@ -2633,6 +2633,9 @@
   "ignore_correction": {
     "message": "\u0c26\u0c40\u0c28\u0c3f \u0c26\u0c3f\u0c26\u0c4d\u0c26\u0c41\u0c2c\u0c3e\u0c1f\u0c41\u0c28\u0c3f \u0c35\u0c3f\u0c38\u0c4d\u0c2e\u0c30\u0c3f\u0c02\u0c1a\u0c41"
   },
+  "ignore_correction_short": {
+    "message": "Ignore correction"
+  },
   "il_heb_settings_page": {
     "message": "\u0c39\u0c3f\u0c2c\u0c4d\u0c30\u0c42 \u0c15\u0c40\u0c2c\u0c4b\u0c30\u0c4d\u0c21\u0c4d \u0c38\u0c46\u0c1f\u0c4d\u0c1f\u0c3f\u0c02\u0c17\u0c4d\u200c\u0c32 \u0c2a\u0c47\u0c1c\u0c40"
   },
@@ -3282,7 +3285,7 @@
     "message": "\u0c0e\u0c02\u0c1a\u0c41\u0c15\u0c41\u0c28\u0c4d\u0c28 \u0c35\u0c3e\u0c1f\u0c3f\u0c28\u0c3f \u0c24\u0c40\u0c38\u0c3f\u0c35\u0c47\u0c2f\u0c3f"
   },
   "user_dict_reset": {
-    "message": "Reset All Dictionary Entries"
+    "message": "\u0c05\u0c28\u0c4d\u0c28\u0c3f \u0c28\u0c3f\u0c18\u0c02\u0c1f\u0c41\u0c35\u0c41 \u0c28\u0c2e\u0c4b\u0c26\u0c41\u0c32\u0c28\u0c41 \u0c30\u0c40\u0c38\u0c46\u0c1f\u0c4d \u0c1a\u0c47\u0c2f\u0c3f"
   },
   "user_dict_save": {
     "message": "\u0c38\u0c47\u0c35\u0c4d \u0c1a\u0c47\u0c2f\u0c3f"
diff --git a/third_party/google_input_tools/src/chrome/os/inputview/_locales/th/messages.json b/third_party/google_input_tools/src/chrome/os/inputview/_locales/th/messages.json
index c433fc09..956c7cf 100644
--- a/third_party/google_input_tools/src/chrome/os/inputview/_locales/th/messages.json
+++ b/third_party/google_input_tools/src/chrome/os/inputview/_locales/th/messages.json
@@ -2633,6 +2633,9 @@
   "ignore_correction": {
     "message": "\u0e25\u0e30\u0e40\u0e27\u0e49\u0e19\u0e01\u0e32\u0e23\u0e41\u0e01\u0e49\u0e44\u0e02\u0e2a\u0e33\u0e2b\u0e23\u0e31\u0e1a"
   },
+  "ignore_correction_short": {
+    "message": "Ignore correction"
+  },
   "il_heb_settings_page": {
     "message": "\u0e2b\u0e19\u0e49\u0e32\u0e01\u0e32\u0e23\u0e15\u0e31\u0e49\u0e07\u0e04\u0e48\u0e32\u0e41\u0e1b\u0e49\u0e19\u0e1e\u0e34\u0e21\u0e1e\u0e4c\u0e20\u0e32\u0e29\u0e32\u0e2e\u0e35\u0e1a\u0e23\u0e39"
   },
@@ -3282,7 +3285,7 @@
     "message": "\u0e19\u0e33\u0e23\u0e32\u0e22\u0e01\u0e32\u0e23\u0e17\u0e35\u0e48\u0e40\u0e25\u0e37\u0e2d\u0e01\u0e2d\u0e2d\u0e01"
   },
   "user_dict_reset": {
-    "message": "Reset All Dictionary Entries"
+    "message": "\u0e23\u0e35\u0e40\u0e0b\u0e47\u0e15\u0e02\u0e49\u0e2d\u0e21\u0e39\u0e25\u0e1e\u0e08\u0e19\u0e32\u0e19\u0e38\u0e01\u0e23\u0e21\u0e17\u0e31\u0e49\u0e07\u0e2b\u0e21\u0e14"
   },
   "user_dict_save": {
     "message": "\u0e1a\u0e31\u0e19\u0e17\u0e36\u0e01"
diff --git a/third_party/google_input_tools/src/chrome/os/inputview/_locales/tr/messages.json b/third_party/google_input_tools/src/chrome/os/inputview/_locales/tr/messages.json
index 05d1d34a5..4620db2 100644
--- a/third_party/google_input_tools/src/chrome/os/inputview/_locales/tr/messages.json
+++ b/third_party/google_input_tools/src/chrome/os/inputview/_locales/tr/messages.json
@@ -2633,6 +2633,9 @@
   "ignore_correction": {
     "message": "\u015eu kelime i\u00e7in d\u00fczeltmeyi yoksay"
   },
+  "ignore_correction_short": {
+    "message": "Ignore correction"
+  },
   "il_heb_settings_page": {
     "message": "\u0130branice Klavye Ayarlar\u0131 Sayfas\u0131"
   },
@@ -3282,7 +3285,7 @@
     "message": "Se\u00e7ileni Kald\u0131r"
   },
   "user_dict_reset": {
-    "message": "Reset All Dictionary Entries"
+    "message": "T\u00fcm S\u00f6zl\u00fck Giri\u015flerini S\u0131f\u0131rla"
   },
   "user_dict_save": {
     "message": "Kaydet"
diff --git a/third_party/google_input_tools/src/chrome/os/inputview/_locales/uk/messages.json b/third_party/google_input_tools/src/chrome/os/inputview/_locales/uk/messages.json
index 6a1f460..3dbd1f2 100644
--- a/third_party/google_input_tools/src/chrome/os/inputview/_locales/uk/messages.json
+++ b/third_party/google_input_tools/src/chrome/os/inputview/_locales/uk/messages.json
@@ -2633,6 +2633,9 @@
   "ignore_correction": {
     "message": "\u0406\u0433\u043d\u043e\u0440\u0443\u0432\u0430\u0442\u0438 \u0432\u0438\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u043d\u044f \u0441\u043b\u043e\u0432\u0430"
   },
+  "ignore_correction_short": {
+    "message": "Ignore correction"
+  },
   "il_heb_settings_page": {
     "message": "\u0421\u0442\u043e\u0440\u0456\u043d\u043a\u0430 \u043d\u0430\u043b\u0430\u0448\u0442\u0443\u0432\u0430\u043d\u044c \u043a\u043b\u0430\u0432\u0456\u0430\u0442\u0443\u0440\u0438 \u0434\u043b\u044f \u0456\u0432\u0440\u0438\u0442\u0443"
   },
@@ -3282,7 +3285,7 @@
     "message": "\u0412\u0438\u0434\u0430\u043b\u0438\u0442\u0438 \u0432\u0438\u0434\u0456\u043b\u0435\u043d\u0435"
   },
   "user_dict_reset": {
-    "message": "Reset All Dictionary Entries"
+    "message": "\u0421\u043a\u0438\u043d\u0443\u0442\u0438 \u0432\u0441\u0456 \u0441\u0442\u0430\u0442\u0442\u0456 \u0441\u043b\u043e\u0432\u043d\u0438\u043a\u0430"
   },
   "user_dict_save": {
     "message": "\u0417\u0431\u0435\u0440\u0435\u0433\u0442\u0438"
diff --git a/third_party/google_input_tools/src/chrome/os/inputview/_locales/vi/messages.json b/third_party/google_input_tools/src/chrome/os/inputview/_locales/vi/messages.json
index fd6ab5a..337df5f 100644
--- a/third_party/google_input_tools/src/chrome/os/inputview/_locales/vi/messages.json
+++ b/third_party/google_input_tools/src/chrome/os/inputview/_locales/vi/messages.json
@@ -2633,6 +2633,9 @@
   "ignore_correction": {
     "message": "B\u1ecf qua t\u00ednh n\u0103ng s\u1eeda cho"
   },
+  "ignore_correction_short": {
+    "message": "Ignore correction"
+  },
   "il_heb_settings_page": {
     "message": "Trang c\u00e0i \u0111\u1eb7t b\u00e0n ph\u00edm ti\u1ebfng Do Th\u00e1i"
   },
@@ -3282,7 +3285,7 @@
     "message": "X\u00f3a m\u1ee5c \u0111\u00e3 ch\u1ecdn"
   },
   "user_dict_reset": {
-    "message": "Reset All Dictionary Entries"
+    "message": "\u0110\u1eb7t l\u1ea1i t\u1ea5t c\u1ea3 c\u00e1c m\u1ee5c t\u1eeb \u0111i\u1ec3n"
   },
   "user_dict_save": {
     "message": "L\u01b0u"
diff --git a/third_party/google_input_tools/src/chrome/os/inputview/_locales/zh_CN/messages.json b/third_party/google_input_tools/src/chrome/os/inputview/_locales/zh_CN/messages.json
index 2e14fe2..b099646 100644
--- a/third_party/google_input_tools/src/chrome/os/inputview/_locales/zh_CN/messages.json
+++ b/third_party/google_input_tools/src/chrome/os/inputview/_locales/zh_CN/messages.json
@@ -2633,6 +2633,9 @@
   "ignore_correction": {
     "message": "\u4e0d\u81ea\u52a8\u66f4\u6b63\u4ee5\u4e0b\u5b57\u8bcd\uff1a"
   },
+  "ignore_correction_short": {
+    "message": "Ignore correction"
+  },
   "il_heb_settings_page": {
     "message": "\u5e0c\u4f2f\u6765\u8bed\u952e\u76d8\u8bbe\u7f6e\u9875\u9762"
   },
@@ -3282,7 +3285,7 @@
     "message": "\u79fb\u9664\u6240\u9009\u5185\u5bb9"
   },
   "user_dict_reset": {
-    "message": "Reset All Dictionary Entries"
+    "message": "\u91cd\u7f6e\u6240\u6709\u5b57\u5178\u6761\u76ee"
   },
   "user_dict_save": {
     "message": "\u4fdd\u5b58"
diff --git a/third_party/google_input_tools/src/chrome/os/inputview/_locales/zh_TW/messages.json b/third_party/google_input_tools/src/chrome/os/inputview/_locales/zh_TW/messages.json
index 4b16377..5df93fc 100644
--- a/third_party/google_input_tools/src/chrome/os/inputview/_locales/zh_TW/messages.json
+++ b/third_party/google_input_tools/src/chrome/os/inputview/_locales/zh_TW/messages.json
@@ -2571,7 +2571,7 @@
     "message": "\u5c55\u958b"
   },
   "expand_candidates": {
-    "message": "\u5c55\u958b\u5019\u9078\u6e05\u55ae"
+    "message": "\u5c55\u958b\u5019\u9078\u5b57\u6e05\u55ae"
   },
   "fi_fin_settings_page": {
     "message": "\u82ac\u862d\u6587\u9375\u76e4\u8a2d\u5b9a\u9801\u9762"
@@ -2633,6 +2633,9 @@
   "ignore_correction": {
     "message": "\u4e0d\u66f4\u6b63\u4ee5\u4e0b\u5b57\u8a5e\uff1a"
   },
+  "ignore_correction_short": {
+    "message": "Ignore correction"
+  },
   "il_heb_settings_page": {
     "message": "\u5e0c\u4f2f\u4f86\u6587\u9375\u76e4\u8a2d\u5b9a\u9801\u9762"
   },
@@ -3105,7 +3108,7 @@
     "message": "\u986f\u793a\u97d3\u6587\u5efa\u8b70"
   },
   "shrink_candidates": {
-    "message": "\u6536\u5408\u5019\u9078\u6e05\u55ae"
+    "message": "\u6536\u5408\u5019\u9078\u5b57\u6e05\u55ae"
   },
   "si_slv_settings_page": {
     "message": "\u65af\u6d1b\u7dad\u5c3c\u4e9e\u6587\u9375\u76e4\u8a2d\u5b9a\u9801\u9762"
@@ -3282,7 +3285,7 @@
     "message": "\u522a\u9664\u6240\u9078\u9805\u76ee"
   },
   "user_dict_reset": {
-    "message": "Reset All Dictionary Entries"
+    "message": "\u91cd\u8a2d\u6240\u6709\u5b57\u5178\u689d\u76ee"
   },
   "user_dict_save": {
     "message": "\u5132\u5b58"
diff --git a/third_party/google_input_tools/src/chrome/os/inputview/adapter.js b/third_party/google_input_tools/src/chrome/os/inputview/adapter.js
index df41620..bee4f683e 100644
--- a/third_party/google_input_tools/src/chrome/os/inputview/adapter.js
+++ b/third_party/google_input_tools/src/chrome/os/inputview/adapter.js
@@ -19,6 +19,8 @@
 goog.require('goog.events.EventType');
 goog.require('goog.object');
 goog.require('i18n.input.chrome.DataSource');
+goog.require('i18n.input.chrome.inputview.FeatureName');
+goog.require('i18n.input.chrome.inputview.FeatureTracker');
 goog.require('i18n.input.chrome.inputview.GlobalFlags');
 goog.require('i18n.input.chrome.inputview.ReadyState');
 goog.require('i18n.input.chrome.inputview.StateType');
@@ -33,9 +35,11 @@
 goog.scope(function() {
 var CandidatesBackEvent = i18n.input.chrome.DataSource.CandidatesBackEvent;
 var ContextType = i18n.input.chrome.message.ContextType;
-var Type = i18n.input.chrome.message.Type;
+var FeatureTracker = i18n.input.chrome.inputview.FeatureTracker;
+var FeatureName = i18n.input.chrome.inputview.FeatureName;
 var Name = i18n.input.chrome.message.Name;
 var SizeSpec = i18n.input.chrome.inputview.SizeSpec;
+var Type = i18n.input.chrome.message.Type;
 
 
 
@@ -64,6 +68,15 @@
    */
   this.modifierState_ = {};
 
+
+  /**
+   * Tracker for which FeatureName are enabled.
+   *
+   * @type {!FeatureTracker};
+   */
+  this.features = new FeatureTracker();
+
+
   /**
    * The system ready state.
    *
@@ -114,6 +127,7 @@
 /** @type {boolean} */
 Adapter.prototype.showGlobeKey = false;
 
+
 /** @type {string} */
 Adapter.prototype.contextType = ContextType.DEFAULT;
 
@@ -359,20 +373,19 @@
   if (this.contextType == ContextType.URL) {
     return false;
   }
-  return this.isGestureEdittingEnabled();
+  return this.features.isEnabled(FeatureName.GESTURE_EDITTING);
 };
 
 
 /**
- * True to enable gesture editting.
+ * True to enable gesture typing.
  *
  * @return {boolean}
  */
-Adapter.prototype.isGestureEdittingEnabled = function() {
-  return this.isExperimental;
+Adapter.prototype.isGestureTypingEnabled = function() {
+  return this.features.isEnabled(FeatureName.GESTURE_TYPING);
 };
 
-
 /**
  * Callback when blurs in the context.
  *
@@ -483,6 +496,7 @@
     inputview.getKeyboardConfig((function(config) {
       this.isA11yMode = !!config['a11ymode'];
       this.isExperimental = !!config['experimental'];
+      this.features.initialize(config);
       this.readyState_.markStateReady(StateType.KEYBOARD_CONFIG_READY);
       this.maybeDispatchSettingsReadyEvent_();
     }).bind(this));
diff --git a/third_party/google_input_tools/src/chrome/os/inputview/common.css b/third_party/google_input_tools/src/chrome/os/inputview/common.css
index 1238b4d4..fc84723 100644
--- a/third_party/google_input_tools/src/chrome/os/inputview/common.css
+++ b/third_party/google_input_tools/src/chrome/os/inputview/common.css
@@ -219,6 +219,16 @@
   background: #f5f5f5;
   display: -webkit-box;
 }
+.inputview-gesture-canvas-view {
+  /* Overlap */
+  position: absolute;
+  left: 0;
+  top: 0;
+  width: 100%;
+  height: 100%;
+  z-index: 1;
+  pointer-events: none;
+}
 .inputview-globe-icon {
   background: transparent url('images/globe.png') 0 0 / 21px 21px no-repeat;
   display: inline-block;
@@ -355,6 +365,9 @@
   background: transparent url('images/down.png') 0 0/16px 13.5px no-repeat;
   width: 16px;
 }
+.inputview-drawing-canvas {
+  pointer-events: none;
+}
 .inputview-left-key {
   background: transparent url('images/left.png') 0 0/9px 13.5px no-repeat;
   width: 9px;
diff --git a/third_party/google_input_tools/src/chrome/os/inputview/config/t13n-rtl_data.js b/third_party/google_input_tools/src/chrome/os/inputview/config/t13n-rtl_data.js
deleted file mode 100644
index 2baf604a..0000000
--- a/third_party/google_input_tools/src/chrome/os/inputview/config/t13n-rtl_data.js
+++ /dev/null
@@ -1,77 +0,0 @@
-// Copyright 2014 The ChromeOS IME Authors. All Rights Reserved.
-// limitations under the License.
-// See the License for the specific language governing permissions and
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// distributed under the License is distributed on an "AS-IS" BASIS,
-// Unless required by applicable law or agreed to in writing, software
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// You may obtain a copy of the License at
-// you may not use this file except in compliance with the License.
-// Licensed under the Apache License, Version 2.0 (the "License");
-//
-goog.require('i18n.input.chrome.inputview.content.ContextlayoutUtil');
-goog.require('i18n.input.chrome.inputview.content.util');
-goog.require('i18n.input.chrome.message.ContextType');
-
-(function() {
-  var viewIdPrefix_ = '101kbd-k-';
-  var ContextType = i18n.input.chrome.message.ContextType;
-
-  var keyCharacters = [
-    ['\u0060', '\u007e'], // TLDE
-    ['\u0031', '\u0021'], // AE01
-    ['\u0032', '\u0040'], // AE02
-    ['\u0033', '\u0023'], // AE03
-    ['\u0034', '\u0024'], // AE04
-    ['\u0035', '\u0025'], // AE05
-    ['\u0036', '\u005e'], // AE06
-    ['\u0037', '\u0026'], // AE07
-    ['\u0038', '\u002a'], // AE08
-    ['\u0039', '\u0029'], // AE09
-    ['\u0030', '\u0028'], // AE10
-    ['\u002d', '\u005f'], // AE11
-    ['\u003d', '\u002b'], // AE12
-    ['\u0071', '\u0051'], // AD01
-    ['\u0077', '\u0057'], // AD02
-    ['\u0065', '\u0045'], // AD03
-    ['\u0072', '\u0052'], // AD04
-    ['\u0074', '\u0054'], // AD05
-    ['\u0079', '\u0059'], // AD06
-    ['\u0075', '\u0055'], // AD07
-    ['\u0069', '\u0049'], // AD08
-    ['\u006f', '\u004f'], // AD09
-    ['\u0070', '\u0050'], // AD10
-    ['\u005d', '\u007d'], // AD11
-    ['\u005b', '\u007b'], // AD12
-    ['\u005c', '\u007c'], // BKSL
-    ['\u0061', '\u0041'], // AC01
-    ['\u0073', '\u0053'], // AC02
-    ['\u0064', '\u0044'], // AC03
-    ['\u0066', '\u0046'], // AC04
-    ['\u0067', '\u0047'], // AC05
-    ['\u0068', '\u0048'], // AC06
-    ['\u006a', '\u004a'], // AC07
-    ['\u006b', '\u004b'], // AC08
-    ['\u006c', '\u004c'], // AC09
-    ['\u003b', '\u003a'], // AC10
-    ['\u0027', '\u0022'], // AC11
-    ['\u007a', '\u005a'], // AB01
-    ['\u0078', '\u0058'], // AB02
-    ['\u0063', '\u0043'], // AB03
-    ['\u0076', '\u0056'], // AB04
-    ['\u0062', '\u0042'], // AB05
-    ['\u006e', '\u004e'], // AB06
-    ['\u006d', '\u004d'], // AB07
-    ['\u002c', '\u003c'], // AB08
-    ['\u002e', '\u003e'], // AB09
-    ['\u002f', '\u003f'], // AB10
-    ['\u0020', '\u0020'] // SPCE
-  ];
-
-  var data = i18n.input.chrome.inputview.content.util.createData(
-      keyCharacters, viewIdPrefix_, false, false);
-  data['id'] = 't13n-rtl';
-  google.ime.chrome.inputview.onConfigLoaded(data);
-}) ();
diff --git a/third_party/google_input_tools/src/chrome/os/inputview/controller.js b/third_party/google_input_tools/src/chrome/os/inputview/controller.js
index fecf7b0..52b65d7 100644
--- a/third_party/google_input_tools/src/chrome/os/inputview/controller.js
+++ b/third_party/google_input_tools/src/chrome/os/inputview/controller.js
@@ -24,12 +24,12 @@
 goog.require('goog.i18n.bidi');
 goog.require('goog.object');
 goog.require('i18n.input.chrome.DataSource');
-goog.require('i18n.input.chrome.SoundController');
 goog.require('i18n.input.chrome.Statistics');
 goog.require('i18n.input.chrome.inputview.Adapter');
 goog.require('i18n.input.chrome.inputview.CandidatesInfo');
 goog.require('i18n.input.chrome.inputview.ConditionName');
 goog.require('i18n.input.chrome.inputview.Css');
+goog.require('i18n.input.chrome.inputview.FeatureName');
 goog.require('i18n.input.chrome.inputview.KeyboardContainer');
 goog.require('i18n.input.chrome.inputview.M17nModel');
 goog.require('i18n.input.chrome.inputview.Model');
@@ -45,6 +45,7 @@
 goog.require('i18n.input.chrome.inputview.elements.content.CandidateView');
 goog.require('i18n.input.chrome.inputview.elements.content.ExpandedCandidateView');
 goog.require('i18n.input.chrome.inputview.elements.content.MenuView');
+goog.require('i18n.input.chrome.inputview.events.DragEvent');
 goog.require('i18n.input.chrome.inputview.events.EventType');
 goog.require('i18n.input.chrome.inputview.events.KeyCodes');
 goog.require('i18n.input.chrome.inputview.handler.PointerHandler');
@@ -52,6 +53,7 @@
 goog.require('i18n.input.chrome.message.ContextType');
 goog.require('i18n.input.chrome.message.Name');
 goog.require('i18n.input.chrome.message.Type');
+goog.require('i18n.input.chrome.sounds.SoundController');
 goog.require('i18n.input.lang.InputToolCode');
 
 
@@ -67,6 +69,7 @@
 var EventType = i18n.input.chrome.inputview.events.EventType;
 var ExpandedCandidateView = i18n.input.chrome.inputview.elements.content.
     ExpandedCandidateView;
+var FeatureName = i18n.input.chrome.inputview.FeatureName;
 var InputToolCode = i18n.input.lang.InputToolCode;
 var KeyCodes = i18n.input.chrome.inputview.events.KeyCodes;
 var MenuView = i18n.input.chrome.inputview.elements.content.MenuView;
@@ -76,8 +79,8 @@
 var SpecNodeName = i18n.input.chrome.inputview.SpecNodeName;
 var StateType = i18n.input.chrome.inputview.StateType;
 var content = i18n.input.chrome.inputview.elements.content;
-var SoundController = i18n.input.chrome.SoundController;
-var Sounds = i18n.input.chrome.inputview.Sounds;
+var SoundController = i18n.input.chrome.sounds.SoundController;
+var Sounds = i18n.input.chrome.sounds.Sounds;
 var Type = i18n.input.chrome.message.Type;
 var util = i18n.input.chrome.inputview.util;
 
@@ -179,7 +182,7 @@
   /** @private {!i18n.input.chrome.inputview.Adapter} */
   this.adapter_ = new i18n.input.chrome.inputview.Adapter(this.readyState_);
 
-  /** @private {!i18n.input.chrome.SoundController} */
+  /** @private {!SoundController} */
   this.soundController_ = new SoundController(false);
 
   /** @private {!i18n.input.chrome.inputview.KeyboardContainer} */
@@ -466,6 +469,9 @@
             EventType.POINTER_OUT,
             EventType.SWIPE
           ], this.onPointerEvent_).
+      listen(this.pointerHandler_,
+          EventType.DRAG,
+          this.onDragEvent_).
       listen(window, goog.events.EventType.RESIZE, this.resize).
       listen(this.adapter_,
           EventType.SURROUNDING_TEXT_CHANGED, this.onSurroundingTextChanged_).
@@ -603,7 +609,7 @@
     this.setDefaultKeyset_(newKeyset);
   }
   this.container_.selectView.setVisible(
-      this.adapter_.isGestureEdittingEnabled());
+      this.adapter_.features.isEnabled(FeatureName.GESTURE_EDITTING));
   // Loads resources in case the default keyset is changed.
   this.loadAllResources_();
   this.maybeCreateViews_();
@@ -725,6 +731,21 @@
 
 
 /**
+ * Handles the drag events. Generally, this will forward the event details to
+ * the components that handle drawing, decoding, etc.
+ *
+ * @param {!i18n.input.chrome.inputview.events.DragEvent} e .
+ * @private
+ */
+Controller.prototype.onDragEvent_ = function(e) {
+  if (this.adapter_.isGestureTypingEnabled() && e.type == EventType.DRAG) {
+    this.container_.gestureCanvasView.addPointAndDraw(e);
+    return;
+  }
+};
+
+
+/**
  * Handles the swipe action.
  *
  * @param {!i18n.input.chrome.inputview.elements.Element} view The view, for
@@ -824,6 +845,11 @@
  * @private
  */
 Controller.prototype.handlePointerAction_ = function(view, e) {
+  if (this.adapter_.isGestureTypingEnabled() &&
+      e.type == EventType.POINTER_UP) {
+    this.container_.gestureCanvasView.clear();
+  }
+
   // Listen for DOUBLE_CLICK as well to capture secondary taps on the spacebar.
   if (e.type == EventType.POINTER_UP || e.type == EventType.DOUBLE_CLICK) {
     this.recordStatsForClosing_(
@@ -1378,7 +1404,8 @@
  * @private
  */
 Controller.prototype.shouldShowToolBar_ = function() {
-  return this.adapter_.isExperimental && this.adapter_.isGoogleDocument() &&
+  return this.adapter_.features.isEnabled(FeatureName.OPTIMIZED_LAYOUTS) &&
+      this.adapter_.isGoogleDocument() &&
       this.adapter_.contextType == ContextType.DEFAULT;
 };
 
@@ -1594,15 +1621,14 @@
   if (this.container_.currentKeysetView) {
     this.container_.currentKeysetView.setVisible(true);
   }
-  if (!this.adapter_.isQPInputView) {
-    if (this.currentKeyset_ == Controller.HANDWRITING_VIEW_CODE_ ||
-        this.currentKeyset_ == Controller.EMOJI_VIEW_CODE_) {
-      this.container_.candidateView.switchToIcon(
-          CandidateView.IconType.BACK, true);
-    } else {
-      this.container_.candidateView.switchToIcon(CandidateView.IconType.VOICE,
-          this.adapter_.isVoiceInputEnabled);
-    }
+  if (!this.adapter_.isQPInputView &&
+      (this.currentKeyset_ == Controller.HANDWRITING_VIEW_CODE_ ||
+       this.currentKeyset_ == Controller.EMOJI_VIEW_CODE_)) {
+    this.container_.candidateView.switchToIcon(
+        CandidateView.IconType.BACK, true);
+  } else {
+    this.container_.candidateView.switchToIcon(CandidateView.IconType.VOICE,
+        this.adapter_.isVoiceInputEnabled);
   }
 };
 
diff --git a/third_party/google_input_tools/src/chrome/os/inputview/css.js b/third_party/google_input_tools/src/chrome/os/inputview/css.js
index d08b111..23ba9e5 100644
--- a/third_party/google_input_tools/src/chrome/os/inputview/css.js
+++ b/third_party/google_input_tools/src/chrome/os/inputview/css.js
@@ -60,6 +60,7 @@
   DEFAULT_CONTENT: goog.getCssName('inputview-dc'),
   DIGIT_CHARACTER: goog.getCssName('inputview-digit-character'),
   DOWN_KEY: goog.getCssName('inputview-down-key'),
+  DRAWING_CANVAS: goog.getCssName('inputview-drawing-canvas'),
   ELEMENT_HIGHLIGHT: goog.getCssName('inputview-element-highlight'),
   EMOJI: goog.getCssName('inputview-emoji'),
   EMOJI_BACK: goog.getCssName('inputview-emoji-back'),
@@ -98,6 +99,7 @@
   FONT: goog.getCssName('inputview-font'),
   FONT_SMALL: goog.getCssName('inputview-font-small'),
   FUNCITONAL_KEY_STICKY: goog.getCssName('inputview-functional-key-sticky'),
+  GESTURE_CANVAS_VIEW: goog.getCssName('inputview-gesture-canvas-view'),
   GLOBE_ICON: goog.getCssName('inputview-globe-icon'),
   HANDWRITING: goog.getCssName('inputview-handwriting'),
   HANDWRITING_BACK: goog.getCssName('inputview-handwriting-back'),
@@ -189,6 +191,7 @@
   VERTICAL_LAYOUT: goog.getCssName('inputview-vertical'),
   VIEW: goog.getCssName('inputview-view'),
   VOICE_BOLD: goog.getCssName('inputview-voice-bold'),
+  VOICE_BUTTON: goog.getCssName('inputview-voice-btn'),
   VOICE_CANDIDATES: goog.getCssName('inputview-voice-candidates'),
   VOICE_DELETE_BTN: goog.getCssName('inputview-voice-delete-btn'),
   VOICE_HIGHLIGHT: goog.getCssName('inputview-voice-highlight'),
diff --git a/third_party/google_input_tools/src/chrome/os/inputview/elements/content/altdataview.js b/third_party/google_input_tools/src/chrome/os/inputview/elements/content/altdataview.js
index 14ec2a1..aa0af28 100644
--- a/third_party/google_input_tools/src/chrome/os/inputview/elements/content/altdataview.js
+++ b/third_party/google_input_tools/src/chrome/os/inputview/elements/content/altdataview.js
@@ -537,6 +537,7 @@
 AltDataView.prototype.resize = function(width, height) {
   goog.base(this, 'resize', width, height);
 
+  this.hide();
   goog.style.setSize(this.coverElement_, width, height);
 };
 
diff --git a/third_party/google_input_tools/src/chrome/os/inputview/elements/content/candidateview.js b/third_party/google_input_tools/src/chrome/os/inputview/elements/content/candidateview.js
index 5ccc1e84..4bbe45a 100644
--- a/third_party/google_input_tools/src/chrome/os/inputview/elements/content/candidateview.js
+++ b/third_party/google_input_tools/src/chrome/os/inputview/elements/content/candidateview.js
@@ -78,10 +78,8 @@
   this.iconButtons_[IconType.EXPAND_CANDIDATES] = new content.
       CandidateButton('', ElementType.EXPAND_CANDIDATES,
           Css.EXPAND_CANDIDATES_ICON, '', this);
-
-  this.iconButtons_[IconType.VOICE] = new content.
-      CandidateButton('', ElementType.VOICE_BTN, Css.VOICE_MIC_BAR, '', this,
-          true);
+  this.iconButtons_[IconType.VOICE] = new content.CandidateButton('',
+      ElementType.VOICE_BTN, Css.VOICE_MIC_BAR, '', this, true);
 
   /**
    * Toolbar buttons.
@@ -184,7 +182,7 @@
  * @type {number}
  * @private
  */
-CandidateView.WIDTH_FOR_THREE_CANDIDATES_ = 235;
+CandidateView.WIDTH_FOR_THREE_CANDIDATES_ = 200;
 
 
 /**
@@ -249,6 +247,9 @@
     var button = this.iconButtons_[i];
     button.render(elem);
     button.setVisible(false);
+    if (button.type == ElementType.VOICE_BTN) {
+      goog.dom.classlist.add(button.getElement(), Css.VOICE_BUTTON);
+    }
   }
 
   goog.a11y.aria.setState(/** @type {!Element} */
@@ -491,15 +492,14 @@
  */
 CandidateView.prototype.updateByKeyset = function(
     keyset, isPasswordBox, isRTL) {
-  if (!i18n.input.chrome.inputview.GlobalFlags.isQPInputView) {
-    if (keyset == CandidateView.HANDWRITING_VIEW_CODE_ ||
-        keyset == CandidateView.EMOJI_VIEW_CODE_) {
-      this.switchToIcon(IconType.BACK, true);
-    } else {
-      this.switchToIcon(IconType.VOICE,
-          this.adapter_.isVoiceInputEnabled &&
-          this.adapter_.contextType != 'password');
-    }
+  if (!i18n.input.chrome.inputview.GlobalFlags.isQPInputView && (
+      keyset == CandidateView.HANDWRITING_VIEW_CODE_ ||
+      keyset == CandidateView.EMOJI_VIEW_CODE_)) {
+    this.switchToIcon(IconType.BACK, true);
+  } else {
+    this.switchToIcon(IconType.VOICE,
+        this.adapter_.isVoiceInputEnabled &&
+        this.adapter_.contextType != 'password');
   }
 
   if (isPasswordBox && keyset.indexOf('compact') != -1) {
diff --git a/third_party/google_input_tools/src/chrome/os/inputview/elements/content/compactkeymodel.js b/third_party/google_input_tools/src/chrome/os/inputview/elements/content/compactkeymodel.js
index 17b042f..9e65ff5 100644
--- a/third_party/google_input_tools/src/chrome/os/inputview/elements/content/compactkeymodel.js
+++ b/third_party/google_input_tools/src/chrome/os/inputview/elements/content/compactkeymodel.js
@@ -14,6 +14,7 @@
 goog.provide('i18n.input.chrome.inputview.elements.content.CompactKeyModel');
 
 goog.require('i18n.input.chrome.inputview.MoreKeysShiftOperation');
+goog.require('i18n.input.chrome.inputview.elements.content.FunctionalKey');
 
 
 goog.scope(function() {
diff --git a/third_party/google_input_tools/src/chrome/os/inputview/elements/content/gesturecanvasview.js b/third_party/google_input_tools/src/chrome/os/inputview/elements/content/gesturecanvasview.js
new file mode 100644
index 0000000..9f081a1
--- /dev/null
+++ b/third_party/google_input_tools/src/chrome/os/inputview/elements/content/gesturecanvasview.js
@@ -0,0 +1,165 @@
+// Copyright 2015 The ChromeOS IME Authors. All Rights Reserved.
+// limitations under the License.
+// See the License for the specific language governing permissions and
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// Unless required by applicable law or agreed to in writing, software
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// You may obtain a copy of the License at
+// you may not use this file except in compliance with the License.
+// Licensed under the Apache License, Version 2.0 (the "License");
+//
+goog.provide('i18n.input.chrome.inputview.elements.content.GestureCanvasView');
+
+goog.require('goog.async.Delay');
+goog.require('goog.dom.TagName');
+goog.require('goog.dom.classlist');
+goog.require('i18n.input.chrome.inputview.Css');
+goog.require('i18n.input.chrome.inputview.elements.Element');
+goog.require('i18n.input.chrome.inputview.elements.ElementType');
+
+
+goog.scope(function() {
+var Css = i18n.input.chrome.inputview.Css;
+var ElementType = i18n.input.chrome.inputview.elements.ElementType;
+var TagName = goog.dom.TagName;
+
+
+
+/**
+ * The gesture canvas view.
+ *
+ * This view is used to display the strokes for gesture typing.
+ *
+ * @param {goog.events.EventTarget=} opt_eventTarget The parent event target.
+ * @constructor
+ * @extends {i18n.input.chrome.inputview.elements.Element}
+ */
+i18n.input.chrome.inputview.elements.content.GestureCanvasView =
+    function(opt_eventTarget) {
+  GestureCanvasView.base(this, 'constructor', '',
+      ElementType.GESTURE_CANVAS_VIEW, opt_eventTarget);
+
+  /**
+   * Actual canvas for drawing the gesture trail.
+   *
+   * @private {!Element}
+   */
+  this.drawingCanvas_;
+
+  /**
+   * Context for drawing the gesture trail.
+   *
+   * @private {!CanvasRenderingContext2D}
+   */
+  this.drawingContext_;
+
+  /**
+   * Drag events whose points should be drawn on the canvas.
+   *
+   * @private {!Array}
+   */
+  this.dragEventList_ = [];
+
+  /** @private {!goog.async.Delay} */
+  this.animator_ = new goog.async.Delay(this.animateGestureTrail_, 0, this);
+};
+var GestureCanvasView =
+    i18n.input.chrome.inputview.elements.content.GestureCanvasView;
+goog.inherits(GestureCanvasView, i18n.input.chrome.inputview.elements.Element);
+
+
+/**
+ * Draw the gesture trail.
+ *
+ * @private
+ */
+GestureCanvasView.prototype.draw_ = function() {
+  // First, clear the canvas.
+  this.drawingContext_.clearRect(
+      0, 0, this.drawingCanvas_.width, this.drawingCanvas_.height);
+
+  // Event positions come in relative to the top of the entire content area, so
+  // grab the canvas offset in order to calculate the correct position to draw
+  // the strokes.
+  var offset = goog.style.getPageOffset(this.drawingCanvas_);
+
+  // Iterate through all the points and draw them.
+  for (var i = 1; i < this.dragEventList_.length; i++) {
+    // TODO(stevet): The following is a basic implementation of the trail
+    // rendering. This should be later be updated to be more efficient and
+    // support effects like fading.
+    var first = this.dragEventList_[i - 1];
+    var second = this.dragEventList_[i];
+    this.drawingContext_.beginPath();
+    this.drawingContext_.strokeStyle = '#00B4CC';
+    this.drawingContext_.fillStyle = 'none';
+    this.drawingContext_.lineWidth = 8;
+    this.drawingContext_.lineCap = 'round';
+    this.drawingContext_.lineJoin = 'round';
+    this.drawingContext_.moveTo(first.x - offset.x, first.y - offset.y);
+    this.drawingContext_.lineTo(second.x - offset.x, second.y - offset.y);
+    this.drawingContext_.stroke();
+  }
+};
+
+
+/** @override */
+GestureCanvasView.prototype.createDom = function() {
+  goog.base(this, 'createDom');
+  var dom = this.getDomHelper();
+  var elem = this.getElement();
+  goog.dom.classlist.add(elem, Css.GESTURE_CANVAS_VIEW);
+
+  // Create the HTML5 canvas where the gesture trail is actually rendered.
+  this.drawingCanvas_ = dom.createDom(TagName.CANVAS, Css.DRAWING_CANVAS);
+  this.drawingContext_ = this.drawingCanvas_.getContext('2d');
+  dom.appendChild(elem, this.drawingCanvas_);
+};
+
+
+/** @override */
+GestureCanvasView.prototype.resize = function(width, height) {
+  GestureCanvasView.base(this, 'resize', width, height);
+
+  // Explicitly set the width and height of the canvas, which is necessary
+  // to ensure that rendered elements are not stretched.
+  this.drawingCanvas_.width = width;
+  this.drawingCanvas_.height = height;
+};
+
+
+/**
+ * Add a new point to the collection of points, and refresh the view.
+ *
+ * @param {!i18n.input.chrome.inputview.events.DragEvent} e Drag event to draw.
+ */
+GestureCanvasView.prototype.addPointAndDraw = function(e) {
+  // Add to the collection.
+  this.dragEventList_.push(e);
+
+  // Refresh the view.
+  this.draw_();
+};
+
+
+/**
+ * Clear the view.
+ */
+GestureCanvasView.prototype.clear = function() {
+  this.dragEventList_ = [];
+  this.draw_();
+};
+
+
+/**
+ * The gesture trail animation function.
+ *
+ * @private
+ */
+GestureCanvasView.prototype.animateGestureTrail_ = function() {
+  // TODO(stevet): Implement the gesture trail animation here.
+};
+});  // goog.scope
diff --git a/third_party/google_input_tools/src/chrome/os/inputview/elements/content/indicator.js b/third_party/google_input_tools/src/chrome/os/inputview/elements/content/indicator.js
deleted file mode 100644
index a0c8392..0000000
--- a/third_party/google_input_tools/src/chrome/os/inputview/elements/content/indicator.js
+++ /dev/null
@@ -1,99 +0,0 @@
-// Copyright 2014 The ChromeOS IME Authors. All Rights Reserved.
-// limitations under the License.
-// See the License for the specific language governing permissions and
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// distributed under the License is distributed on an "AS-IS" BASIS,
-// Unless required by applicable law or agreed to in writing, software
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// You may obtain a copy of the License at
-// you may not use this file except in compliance with the License.
-// Licensed under the Apache License, Version 2.0 (the "License");
-//
-goog.provide('i18n.input.chrome.inputview.elements.content.PageIndicator');
-
-goog.require('goog.dom');
-goog.require('goog.dom.TagName');
-goog.require('goog.dom.classlist');
-goog.require('goog.style');
-goog.require('i18n.input.chrome.inputview.Css');
-goog.require('i18n.input.chrome.inputview.elements.Element');
-
-goog.scope(function() {
-var ElementType = i18n.input.chrome.inputview.elements.ElementType;
-
-
-
-/**
- * The indicator of the current page index.
- *
- * @param {string} id The id.
- * @param {!i18n.input.chrome.inputview.elements.ElementType} type The element
- *     type.
- * @param {goog.events.EventTarget=} opt_eventTarget The event target.
- * @constructor
- * @extends {i18n.input.chrome.inputview.elements.Element}
- */
-i18n.input.chrome.inputview.elements.content.PageIndicator = function(id, type,
-    opt_eventTarget) {
-  goog.base(this, id, type, opt_eventTarget);
-};
-goog.inherits(i18n.input.chrome.inputview.elements.content.PageIndicator,
-    i18n.input.chrome.inputview.elements.Element);
-var PageIndicator = i18n.input.chrome.inputview.elements.content.PageIndicator;
-
-
-/** @override */
-PageIndicator.prototype.createDom = function() {
-  goog.base(this, 'createDom');
-  var dom = this.getDomHelper();
-  var elem = this.getElement();
-  goog.dom.classlist.add(elem,
-      i18n.input.chrome.inputview.Css.INDICATOR_BACKGROUND);
-  this.bgElem = goog.dom.createDom(goog.dom.TagName.DIV);
-  goog.dom.classlist.add(this.bgElem,
-      i18n.input.chrome.inputview.Css.INDICATOR);
-  dom.appendChild(elem, this.bgElem);
-};
-
-
-/** @override */
-PageIndicator.prototype.resize = function(width, height) {
-  this.bgElem.style.height = height + 'px';
-  this.getElement().style.width = width + 'px';
-  goog.base(this, 'resize', width, height);
-};
-
-
-/**
- * Slide the indicator.
- *
- * @param {number} deltaX The x-coordinate of slide distance.
- * @param {number} totalPages The total number of pages.
- */
-PageIndicator.prototype.slide = function(deltaX, totalPages) {
-  var marginLeft = goog.style.getMarginBox(this.bgElem).left +
-      deltaX / totalPages;
-  this.bgElem.style.marginLeft = marginLeft + 'px';
-};
-
-
-/**
- * Move the indicator to indicate a page.
- *
- * @param {number} pageNum The page that needs to be indicated.
- * @param {number} totalPages The total number of pages.
- */
-PageIndicator.prototype.gotoPage = function(pageNum, totalPages) {
-  var width = goog.style.getSize(this.getElement()).width;
-  this.bgElem.style.marginLeft = width / totalPages * pageNum + 'px';
-  if (totalPages >= 2) {
-    this.bgElem.style.width = width / totalPages + 'px';
-  } else {
-    this.bgElem.style.width = 0;
-  }
-};
-});  // goog.scope
-
-
diff --git a/third_party/google_input_tools/src/chrome/os/inputview/elements/content/keysetview.js b/third_party/google_input_tools/src/chrome/os/inputview/elements/content/keysetview.js
index 0174e0b..b7abdb7 100644
--- a/third_party/google_input_tools/src/chrome/os/inputview/elements/content/keysetview.js
+++ b/third_party/google_input_tools/src/chrome/os/inputview/elements/content/keysetview.js
@@ -344,9 +344,7 @@
     var elem = this.getElement();
     var margin = Math.round((outerWidth - outerWidth * widthPercent) / 2);
     var w = outerWidth - 2 * margin;
-    if (margin > 0) {
-      elem.style.marginLeft = elem.style.marginRight = margin + 'px';
-    }
+    elem.style.marginLeft = elem.style.marginRight = margin + 'px';
     goog.style.setSize(elem, w, outerHeight);
 
     this.resizeRows(w, outerHeight);
diff --git a/third_party/google_input_tools/src/chrome/os/inputview/elements/content/swipeview.js b/third_party/google_input_tools/src/chrome/os/inputview/elements/content/swipeview.js
index 903fd21..069dc7ef 100644
--- a/third_party/google_input_tools/src/chrome/os/inputview/elements/content/swipeview.js
+++ b/third_party/google_input_tools/src/chrome/os/inputview/elements/content/swipeview.js
@@ -378,6 +378,9 @@
  * @private
  */
 SwipeView.prototype.handlePointerAction_ = function(e) {
+  if (!e.view) {
+    return;
+  }
   switch (e.view.type) {
     case ElementType.BACKSPACE_KEY:
       var key = /** @type {!content.FunctionalKey} */ (e.view);
diff --git a/third_party/google_input_tools/src/chrome/os/inputview/elements/content/voiceview.js b/third_party/google_input_tools/src/chrome/os/inputview/elements/content/voiceview.js
index 4681268..7a8907e 100644
--- a/third_party/google_input_tools/src/chrome/os/inputview/elements/content/voiceview.js
+++ b/third_party/google_input_tools/src/chrome/os/inputview/elements/content/voiceview.js
@@ -19,12 +19,12 @@
 goog.require('goog.dom.classlist');
 goog.require('goog.style');
 goog.require('i18n.input.chrome.inputview.Css');
-goog.require('i18n.input.chrome.inputview.Sounds');
 goog.require('i18n.input.chrome.inputview.elements.Element');
 goog.require('i18n.input.chrome.inputview.elements.ElementType');
 goog.require('i18n.input.chrome.inputview.elements.content.FunctionalKey');
 goog.require('i18n.input.chrome.message.Name');
 goog.require('i18n.input.chrome.message.Type');
+goog.require('i18n.input.chrome.sounds.Sounds');
 
 
 goog.scope(function() {
@@ -32,7 +32,7 @@
 var ElementType = i18n.input.chrome.inputview.elements.ElementType;
 var FunctionalKey = i18n.input.chrome.inputview.elements.content.FunctionalKey;
 var Name = i18n.input.chrome.message.Name;
-var Sounds = i18n.input.chrome.inputview.Sounds;
+var Sounds = i18n.input.chrome.sounds.Sounds;
 var TagName = goog.dom.TagName;
 var Type = i18n.input.chrome.message.Type;
 
@@ -43,7 +43,7 @@
  *
  * @param {goog.events.EventTarget=} opt_eventTarget The parent event target.
  * @param {i18n.input.chrome.inputview.Adapter=} opt_adapter .
- * @param {i18n.input.chrome.SoundController=} opt_soundController .
+ * @param {i18n.input.chrome.sounds.SoundController=} opt_soundController .
  * @constructor
  * @extends {i18n.input.chrome.inputview.elements.Element}
  */
@@ -62,7 +62,7 @@
   /**
    * The sound controller.
    *
-   * @private {!i18n.input.chrome.SoundController}
+   * @private {!i18n.input.chrome.sounds.SoundController}
    */
   this.soundController_ = goog.asserts.assertObject(opt_soundController);
 
@@ -206,6 +206,7 @@
     goog.style.setElementShown(this.voicePanel_, false);
     goog.style.setElementShown(this.privacyDiv_, false);
   }
+  this.resize(this.width, this.height);
 };
 
 
diff --git a/third_party/google_input_tools/src/chrome/os/inputview/elements/elementtype.js b/third_party/google_input_tools/src/chrome/os/inputview/elements/elementtype.js
index 035bfd98..999e17a5 100644
--- a/third_party/google_input_tools/src/chrome/os/inputview/elements/elementtype.js
+++ b/third_party/google_input_tools/src/chrome/os/inputview/elements/elementtype.js
@@ -77,6 +77,7 @@
   REDO: 54,
   UNDO: 55,
   SWIPE_VIEW: 56,
-  SELECT_VIEW: 57
+  SELECT_VIEW: 57,
+  GESTURE_CANVAS_VIEW: 58
 };
 
diff --git a/third_party/google_input_tools/src/chrome/os/inputview/featurename.js b/third_party/google_input_tools/src/chrome/os/inputview/featurename.js
new file mode 100644
index 0000000..e777483
--- /dev/null
+++ b/third_party/google_input_tools/src/chrome/os/inputview/featurename.js
@@ -0,0 +1,27 @@
+// Copyright 2015 The ChromeOS IME Authors. All Rights Reserved.
+// limitations under the License.
+// See the License for the specific language governing permissions and
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// Unless required by applicable law or agreed to in writing, software
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// You may obtain a copy of the License at
+// you may not use this file except in compliance with the License.
+// Licensed under the Apache License, Version 2.0 (the "License");
+//
+goog.provide('i18n.input.chrome.inputview.FeatureName');
+
+/**
+ * All features.
+ *
+ * @enum {string}
+ */
+i18n.input.chrome.inputview.FeatureName = {
+  // List all features.
+  GESTURE_EDITTING: 'gesture-editting',
+  GESTURE_TYPING: 'gesturetyping',
+  OPTIMIZED_LAYOUTS: 'optimized-layouts',
+  EXPERIMENTAL: 'experimental'
+};
diff --git a/third_party/google_input_tools/src/chrome/os/inputview/featuretracker.js b/third_party/google_input_tools/src/chrome/os/inputview/featuretracker.js
new file mode 100644
index 0000000..22a4a9e
--- /dev/null
+++ b/third_party/google_input_tools/src/chrome/os/inputview/featuretracker.js
@@ -0,0 +1,93 @@
+// Copyright 2015 The ChromeOS IME Authors. All Rights Reserved.
+// limitations under the License.
+// See the License for the specific language governing permissions and
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// Unless required by applicable law or agreed to in writing, software
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// You may obtain a copy of the License at
+// you may not use this file except in compliance with the License.
+// Licensed under the Apache License, Version 2.0 (the "License");
+//
+goog.provide('i18n.input.chrome.inputview.FeatureTracker');
+
+goog.require('goog.object');
+goog.require('i18n.input.chrome.inputview.FeatureName');
+
+goog.scope(function() {
+var Adapter = i18n.input.chrome.inputview.Adapter;
+var FeatureName = i18n.input.chrome.inputview.FeatureName;
+
+
+/**
+ * Controller for experimental features.
+ *
+ * @constructor
+ */
+i18n.input.chrome.inputview.FeatureTracker = function() {
+
+  /**
+   * Whether experimental flags is enabled.
+   *
+   * @private {Object.<string, boolean>}
+   */
+  this.features_ = {};
+
+  /**
+   * Features that are enabled by default. Any feature not on this list is
+   * disabled unless the runtime --enable-inputview-{feature} flag is present.
+   *
+   * @const
+   * @private {!Array<FeatureName>}
+   */
+  this.ENABLED_BY_DEFAULT_ = [];
+};
+
+var FeatureTracker = i18n.input.chrome.inputview.FeatureTracker;
+
+
+/**
+ * Whether the feature is enabled.
+ *
+ * @param {!FeatureName} feature .
+ * @return {boolean}
+ */
+FeatureTracker.prototype.isEnabled = function(feature) {
+  if (feature in this.features_) {
+    return this.features_[feature];
+  }
+  return (this.ENABLED_BY_DEFAULT_.indexOf(feature) >= 0) ||
+      !!this.features_[FeatureName.EXPERIMENTAL];
+};
+
+
+/**
+ * Inits the feature tracker.
+ *
+ * @param {!Object} config The keyboard config.
+ */
+FeatureTracker.prototype.initialize = function(config) {
+  if (config.features) {
+    var features = config.features;
+    // Parse the run time flags.
+    for (var i = 0; i < features.length; i++) {
+      var pieces = features[i].split('-');
+      var state = pieces.pop();
+      if (state == 'enabled') {
+        this.features_[pieces.join('-')] = true;
+      } else if (state == 'disabled') {
+        this.features_[pieces.join('-')] = false;
+      } else {
+        console.error('Unrecognized flag: ' + features[i]);
+      }
+    }
+  } else {
+    console.error('API Error. Features not present in config.');
+    return;
+  }
+};
+
+});  // goog.scope
+
diff --git a/third_party/google_input_tools/src/chrome/os/inputview/images/special_characters.png b/third_party/google_input_tools/src/chrome/os/inputview/images/special_characters.png
deleted file mode 100644
index a48a1c19..0000000
--- a/third_party/google_input_tools/src/chrome/os/inputview/images/special_characters.png
+++ /dev/null
Binary files differ
diff --git a/third_party/google_input_tools/src/chrome/os/inputview/keyboardcontainer.js b/third_party/google_input_tools/src/chrome/os/inputview/keyboardcontainer.js
index f66841b..fd77a23 100644
--- a/third_party/google_input_tools/src/chrome/os/inputview/keyboardcontainer.js
+++ b/third_party/google_input_tools/src/chrome/os/inputview/keyboardcontainer.js
@@ -23,6 +23,7 @@
 goog.require('i18n.input.chrome.inputview.elements.content.CandidateView');
 goog.require('i18n.input.chrome.inputview.elements.content.EmojiView');
 goog.require('i18n.input.chrome.inputview.elements.content.ExpandedCandidateView');
+goog.require('i18n.input.chrome.inputview.elements.content.GestureCanvasView');
 goog.require('i18n.input.chrome.inputview.elements.content.HandwritingView');
 goog.require('i18n.input.chrome.inputview.elements.content.KeysetView');
 goog.require('i18n.input.chrome.inputview.elements.content.MenuView');
@@ -47,7 +48,7 @@
  * The keyboard container.
  *
  * @param {!i18n.input.chrome.inputview.Adapter} adapter .
- * @param {!i18n.input.chrome.SoundController} soundController .
+ * @param {!i18n.input.chrome.sounds.SoundController} soundController .
  * @constructor
  * @extends {goog.ui.Container}
  */
@@ -68,6 +69,11 @@
   /** @type {!content.SelectView} */
   this.selectView = new content.SelectView(this);
 
+  if (adapter.isGestureTypingEnabled()) {
+    /** @type {!content.GestureCanvasView} */
+    this.gestureCanvasView = new content.GestureCanvasView(this);
+  }
+
   /** @type {!content.MenuView} */
   this.menuView = new content.MenuView(this);
 
@@ -132,10 +138,10 @@
   goog.base(this, 'createDom');
 
   var elem = this.getElement();
-  this.wrapperDiv_ = this.getDomHelper().createDom(
-      goog.dom.TagName.DIV, Css.WRAPPER);
+  var dom = this.getDomHelper();
+  this.wrapperDiv_ = dom.createDom(goog.dom.TagName.DIV, Css.WRAPPER);
   this.candidateView.render(this.wrapperDiv_);
-  this.getDomHelper().appendChild(elem, this.wrapperDiv_);
+  dom.appendChild(elem, this.wrapperDiv_);
   this.altDataView.render();
   this.swipeView.render();
   this.selectView.render();
@@ -144,6 +150,9 @@
   this.voiceView.setVisible(false);
   this.expandedCandidateView.render(this.wrapperDiv_);
   this.expandedCandidateView.setVisible(false);
+  if (this.adapter_.isGestureTypingEnabled()) {
+    this.gestureCanvasView.render(this.wrapperDiv_);
+  }
   goog.dom.classlist.add(elem, Css.CONTAINER);
 };
 
@@ -318,6 +327,9 @@
   this.selectView.resize(screen.width, height);
   this.menuView.resize(screen.width, height);
   this.voiceView.resize(w + padding, height);
+  if (this.adapter_.isGestureTypingEnabled()) {
+    this.gestureCanvasView.resize(screen.width, height);
+  }
 };
 
 
@@ -329,6 +341,9 @@
   goog.dispose(this.selectView);
   goog.dispose(this.menuView);
   goog.dispose(this.voiceView);
+  if (this.adapter_.isGestureTypingEnabled()) {
+    goog.dispose(this.gestureCanvasView);
+  }
   for (var key in this.keysetViewMap) {
     goog.dispose(this.keysetViewMap[key]);
   }
diff --git a/third_party/google_input_tools/src/chrome/os/soundcontroller.js b/third_party/google_input_tools/src/chrome/os/soundcontroller.js
deleted file mode 100644
index 7b57e3b..0000000
--- a/third_party/google_input_tools/src/chrome/os/soundcontroller.js
+++ /dev/null
@@ -1,253 +0,0 @@
-// Copyright 2015 The ChromeOS IME Authors. All Rights Reserved.
-// limitations under the License.
-// See the License for the specific language governing permissions and
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// distributed under the License is distributed on an "AS-IS" BASIS,
-// Unless required by applicable law or agreed to in writing, software
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// You may obtain a copy of the License at
-// you may not use this file except in compliance with the License.
-// Licensed under the Apache License, Version 2.0 (the "License");
-//
-goog.provide('i18n.input.chrome.SoundController');
-
-goog.require('goog.Disposable');
-goog.require('goog.dom');
-goog.require('i18n.input.chrome.inputview.Sounds');
-goog.require('i18n.input.chrome.inputview.elements.ElementType');
-
-goog.scope(function() {
-var Sounds = i18n.input.chrome.inputview.Sounds;
-var ElementType = i18n.input.chrome.inputview.elements.ElementType;
-var keyToSoundIdOnKeyUp = {};
-var keyToSoundIdOnKeyRepeat = {};
-
-
-
-/**
- * Sound controller for the keyboard.
- *
- * @param {!boolean} enabled Whether sounds is enabled by default.
- * @param {?number=} opt_volume The default volume for sound tracks.
- * @constructor
- * @extends {goog.Disposable}
- */
-i18n.input.chrome.SoundController = function(enabled, opt_volume) {
-
-  /**
-   * Collection of all the sound pools.
-   *
-   * @private {!Object.<string, !Object>}
-   */
-  this.sounds_ = {};
-
-  /** @private {boolean} */
-  this.enabled_ = enabled;
-
-  /**
-   * The default volume for all audio tracks. Tracks with volume 0 will be
-   * skipped.
-   *
-   * @private {number}
-   */
-  this.volume_ = opt_volume || this.DEFAULT_VOLUME;
-
-  if (enabled) {
-    this.initialize();
-  }
-};
-goog.inherits(i18n.input.chrome.SoundController, goog.Disposable);
-
-
-var Controller = i18n.input.chrome.SoundController;
-
-
-/**
- * @define {number}  The size of the pool to use for playing audio sounds.
- */
-Controller.prototype.POOL_SIZE = 10;
-
-
-/**
- * @define {number}  The default audio track volume.
- */
-Controller.prototype.DEFAULT_VOLUME = 0.6;
-
-
-/** @private {boolean} */
-Controller.prototype.initialized_ = false;
-
-
-/**
- * Initializes the sound controller.
- */
-Controller.prototype.initialize = function() {
-  if (!this.initialized_) {
-    for (var sound in Sounds) {
-      this.addSound_(Sounds[sound]);
-    }
-    keyToSoundIdOnKeyUp[ElementType.BACKSPACE_KEY] = Sounds.NONE;
-    keyToSoundIdOnKeyUp[ElementType.ENTER_KEY] = Sounds.RETURN;
-    keyToSoundIdOnKeyUp[ElementType.SPACE_KEY] = Sounds.SPACEBAR;
-    keyToSoundIdOnKeyRepeat[ElementType.BACKSPACE_KEY] = Sounds.DELETE;
-    this.initialized_ = true;
-  }
-};
-
-
-/**
- * Caches the specified sound on the keyboard.
- *
- * @param {string} soundId The name of the .wav file in the "sounds"
-     directory.
- * @private
- */
-Controller.prototype.addSound_ = function(soundId) {
-  if (soundId == Sounds.NONE || this.sounds_[soundId])
-    return;
-  var pool = [];
-  // Create sound pool.
-  for (var i = 0; i < this.POOL_SIZE; i++) {
-    var audio = goog.dom.createDom('audio', {
-      preload: 'auto',
-      id: soundId,
-      src: 'sounds/' + soundId + '.wav',
-      volume: this.volume_
-    });
-    pool.push(audio);
-  }
-  this.sounds_[soundId] = pool;
-};
-
-
-/**
- * Sets the volume for the specified sound.
- *
- * @param {string} soundId The id of the sound.
- * @param {number} volume The volume to set.
- */
-Controller.prototype.setVolume = function(soundId, volume) {
-  var pool = this.sounds_[soundId];
-  if (!pool) {
-    console.error('Cannot find sound: ' + soundId);
-    return;
-  }
-  // Change volume for all sounds in the pool.
-  for (var i = 0; i < pool.length; i++) {
-    pool[i].volume = volume;
-  }
-};
-
-
-/**
- * Enables or disable playing sounds on keypress.
- * @param {!boolean} enabled
- */
-Controller.prototype.setEnabled = function(enabled) {
-  this.enabled_ = enabled;
-  if (this.enabled_) {
-    this.initialize();
-  }
-};
-
-
-/**
- * Gets the flag whether sound controller is enabled or not.
- *
- * @return {!boolean}
- */
-Controller.prototype.getEnabled = function() {
-  return this.enabled_;
-};
-
-
-/**
- * Sets the volume for all sounds on the keyboard.
- *
- * @param {number} volume The volume of the sounds.
- */
-Controller.prototype.setMasterVolume = function(volume) {
-  this.volume_ = volume;
-  for (var id in this.sounds_) {
-    this.setVolume(id, volume);
-  }
-};
-
-
-/**
- * Plays the specified sound.
- *
- * @param {string} soundId The id of the audio tag.
- * @param {boolean=} opt_force Force to play sound whatever the enabled flags is
- *     turned on.
- */
-Controller.prototype.playSound = function(soundId, opt_force) {
-  if (opt_force) {
-    this.initialize();
-  }
-  // If master volume is zero, ignore the request.
-  if (!opt_force && !this.enabled_ || this.volume_ == 0 ||
-      soundId == Sounds.NONE) {
-    return;
-  }
-  var pool = this.sounds_[soundId];
-  if (!pool) {
-    console.error('Cannot find sound: ' + soundId);
-    return;
-  }
-  // Search the sound pool for a free resource.
-  for (var i = 0; i < pool.length; i++) {
-    if (pool[i].paused) {
-      pool[i].play();
-      return;
-    }
-  }
-};
-
-
-/**
- * On key up.
- *
- * @param {ElementType} key The key released.
- */
-Controller.prototype.onKeyUp = function(key) {
-  var sound = keyToSoundIdOnKeyUp[key] || Sounds.STANDARD;
-  this.playSound(sound);
-};
-
-
-/**
- * On key repeat.
- *
- * @param {ElementType} key The key that is being repeated.
- */
-Controller.prototype.onKeyRepeat = function(key) {
-  var sound = keyToSoundIdOnKeyRepeat[key] || Sounds.NONE;
-  this.playSound(sound);
-};
-
-
-/** @override */
-Controller.prototype.disposeInternal = function() {
-  for (var soundId in this.sounds_) {
-    var pool = this.sounds_[soundId];
-    for (var i = 0; i < pool.length; i++) {
-      var tag = pool[i];
-      if (tag && tag.loaded) {
-        tag.pause();
-        tag.autoplay = false;
-        tag.loop = false;
-        tag.currentTime = 0;
-      }
-    }
-    delete this.sounds_[soundId];
-  }
-  this.sounds_ = {};
-  keyToSoundIdOnKeyUp = {};
-  keyToSoundIdOnKeyRepeat = {};
-  goog.base(this, 'disposeInternal');
-};
-
-});  // goog.scope
diff --git a/third_party/google_input_tools/src/chrome/os/sounds.js b/third_party/google_input_tools/src/chrome/os/sounds.js
deleted file mode 100644
index e98f10fe..0000000
--- a/third_party/google_input_tools/src/chrome/os/sounds.js
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright 2015 The ChromeOS IME Authors. All Rights Reserved.
-// limitations under the License.
-// See the License for the specific language governing permissions and
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// distributed under the License is distributed on an "AS-IS" BASIS,
-// Unless required by applicable law or agreed to in writing, software
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// You may obtain a copy of the License at
-// you may not use this file except in compliance with the License.
-// Licensed under the Apache License, Version 2.0 (the "License");
-//
-goog.provide('i18n.input.chrome.inputview.Sounds');
-
-
-/**
- * The available sounds.
- *
- * @enum {string}
- */
-i18n.input.chrome.inputview.Sounds = {
-  DELETE: 'keypress-delete',
-  RETURN: 'keypress-return',
-  SPACEBAR: 'keypress-spacebar',
-  STANDARD: 'keypress-standard',
-  NONE: 'none',
-  VOICE_RECOG_START: 'voice_recog_start',
-  VOICE_RECOG_END: 'voice_recog_end',
-  AUTO_CORRECTION: 'auto_correction'
-};
-
diff --git a/third_party/google_input_tools/src/chrome/os/inputview/sounds/auto_correction.wav b/third_party/google_input_tools/src/chrome/os/sounds/auto_correction.wav
similarity index 100%
rename from third_party/google_input_tools/src/chrome/os/inputview/sounds/auto_correction.wav
rename to third_party/google_input_tools/src/chrome/os/sounds/auto_correction.wav
Binary files differ
diff --git a/third_party/google_input_tools/src/chrome/os/inputview/sounds/keypress-delete.wav b/third_party/google_input_tools/src/chrome/os/sounds/keypress-delete.wav
similarity index 100%
rename from third_party/google_input_tools/src/chrome/os/inputview/sounds/keypress-delete.wav
rename to third_party/google_input_tools/src/chrome/os/sounds/keypress-delete.wav
Binary files differ
diff --git a/third_party/google_input_tools/src/chrome/os/inputview/sounds/keypress-return.wav b/third_party/google_input_tools/src/chrome/os/sounds/keypress-return.wav
similarity index 100%
rename from third_party/google_input_tools/src/chrome/os/inputview/sounds/keypress-return.wav
rename to third_party/google_input_tools/src/chrome/os/sounds/keypress-return.wav
Binary files differ
diff --git a/third_party/google_input_tools/src/chrome/os/inputview/sounds/keypress-spacebar.wav b/third_party/google_input_tools/src/chrome/os/sounds/keypress-spacebar.wav
similarity index 100%
rename from third_party/google_input_tools/src/chrome/os/inputview/sounds/keypress-spacebar.wav
rename to third_party/google_input_tools/src/chrome/os/sounds/keypress-spacebar.wav
Binary files differ
diff --git a/third_party/google_input_tools/src/chrome/os/inputview/sounds/keypress-standard.wav b/third_party/google_input_tools/src/chrome/os/sounds/keypress-standard.wav
similarity index 100%
rename from third_party/google_input_tools/src/chrome/os/inputview/sounds/keypress-standard.wav
rename to third_party/google_input_tools/src/chrome/os/sounds/keypress-standard.wav
Binary files differ
diff --git a/third_party/google_input_tools/src/chrome/os/sounds/soundcontroller.js b/third_party/google_input_tools/src/chrome/os/sounds/soundcontroller.js
new file mode 100644
index 0000000..c3f037ca
--- /dev/null
+++ b/third_party/google_input_tools/src/chrome/os/sounds/soundcontroller.js
@@ -0,0 +1,253 @@
+// Copyright 2015 The ChromeOS IME Authors. All Rights Reserved.
+// limitations under the License.
+// See the License for the specific language governing permissions and
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// Unless required by applicable law or agreed to in writing, software
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// You may obtain a copy of the License at
+// you may not use this file except in compliance with the License.
+// Licensed under the Apache License, Version 2.0 (the "License");
+//
+goog.provide('i18n.input.chrome.sounds.SoundController');
+
+goog.require('goog.Disposable');
+goog.require('goog.dom');
+goog.require('i18n.input.chrome.inputview.elements.ElementType');
+goog.require('i18n.input.chrome.sounds.Sounds');
+
+goog.scope(function() {
+var Sounds = i18n.input.chrome.sounds.Sounds;
+var ElementType = i18n.input.chrome.inputview.elements.ElementType;
+var keyToSoundIdOnKeyUp = {};
+var keyToSoundIdOnKeyRepeat = {};
+
+
+
+/**
+ * Sound controller for the keyboard.
+ *
+ * @param {!boolean} enabled Whether sounds is enabled by default.
+ * @param {?number=} opt_volume The default volume for sound tracks.
+ * @constructor
+ * @extends {goog.Disposable}
+ */
+i18n.input.chrome.sounds.SoundController = function(enabled, opt_volume) {
+
+  /**
+   * Collection of all the sound pools.
+   *
+   * @private {!Object.<string, !Object>}
+   */
+  this.sounds_ = {};
+
+  /** @private {boolean} */
+  this.enabled_ = enabled;
+
+  /**
+   * The default volume for all audio tracks. Tracks with volume 0 will be
+   * skipped.
+   *
+   * @private {number}
+   */
+  this.volume_ = opt_volume || this.DEFAULT_VOLUME;
+
+  if (enabled) {
+    this.initialize();
+  }
+};
+goog.inherits(i18n.input.chrome.sounds.SoundController, goog.Disposable);
+
+
+var Controller = i18n.input.chrome.sounds.SoundController;
+
+
+/**
+ * @define {number}  The size of the pool to use for playing audio sounds.
+ */
+Controller.prototype.POOL_SIZE = 10;
+
+
+/**
+ * @define {number}  The default audio track volume.
+ */
+Controller.prototype.DEFAULT_VOLUME = 0.6;
+
+
+/** @private {boolean} */
+Controller.prototype.initialized_ = false;
+
+
+/**
+ * Initializes the sound controller.
+ */
+Controller.prototype.initialize = function() {
+  if (!this.initialized_) {
+    for (var sound in Sounds) {
+      this.addSound_(Sounds[sound]);
+    }
+    keyToSoundIdOnKeyUp[ElementType.BACKSPACE_KEY] = Sounds.NONE;
+    keyToSoundIdOnKeyUp[ElementType.ENTER_KEY] = Sounds.RETURN;
+    keyToSoundIdOnKeyUp[ElementType.SPACE_KEY] = Sounds.SPACEBAR;
+    keyToSoundIdOnKeyRepeat[ElementType.BACKSPACE_KEY] = Sounds.DELETE;
+    this.initialized_ = true;
+  }
+};
+
+
+/**
+ * Caches the specified sound on the keyboard.
+ *
+ * @param {string} soundId The name of the .wav file in the "sounds"
+     directory.
+ * @private
+ */
+Controller.prototype.addSound_ = function(soundId) {
+  if (soundId == Sounds.NONE || this.sounds_[soundId])
+    return;
+  var pool = [];
+  // Create sound pool.
+  for (var i = 0; i < this.POOL_SIZE; i++) {
+    var audio = goog.dom.createDom('audio', {
+      preload: 'auto',
+      id: soundId,
+      src: 'sounds/' + soundId + '.wav',
+      volume: this.volume_
+    });
+    pool.push(audio);
+  }
+  this.sounds_[soundId] = pool;
+};
+
+
+/**
+ * Sets the volume for the specified sound.
+ *
+ * @param {string} soundId The id of the sound.
+ * @param {number} volume The volume to set.
+ */
+Controller.prototype.setVolume = function(soundId, volume) {
+  var pool = this.sounds_[soundId];
+  if (!pool) {
+    console.error('Cannot find sound: ' + soundId);
+    return;
+  }
+  // Change volume for all sounds in the pool.
+  for (var i = 0; i < pool.length; i++) {
+    pool[i].volume = volume;
+  }
+};
+
+
+/**
+ * Enables or disable playing sounds on keypress.
+ * @param {!boolean} enabled
+ */
+Controller.prototype.setEnabled = function(enabled) {
+  this.enabled_ = enabled;
+  if (this.enabled_) {
+    this.initialize();
+  }
+};
+
+
+/**
+ * Gets the flag whether sound controller is enabled or not.
+ *
+ * @return {!boolean}
+ */
+Controller.prototype.getEnabled = function() {
+  return this.enabled_;
+};
+
+
+/**
+ * Sets the volume for all sounds on the keyboard.
+ *
+ * @param {number} volume The volume of the sounds.
+ */
+Controller.prototype.setMasterVolume = function(volume) {
+  this.volume_ = volume;
+  for (var id in this.sounds_) {
+    this.setVolume(id, volume);
+  }
+};
+
+
+/**
+ * Plays the specified sound.
+ *
+ * @param {string} soundId The id of the audio tag.
+ * @param {boolean=} opt_force Force to play sound whatever the enabled flags is
+ *     turned on.
+ */
+Controller.prototype.playSound = function(soundId, opt_force) {
+  if (opt_force) {
+    this.initialize();
+  }
+  // If master volume is zero, ignore the request.
+  if (!opt_force && !this.enabled_ || this.volume_ == 0 ||
+      soundId == Sounds.NONE) {
+    return;
+  }
+  var pool = this.sounds_[soundId];
+  if (!pool) {
+    console.error('Cannot find sound: ' + soundId);
+    return;
+  }
+  // Search the sound pool for a free resource.
+  for (var i = 0; i < pool.length; i++) {
+    if (pool[i].paused) {
+      pool[i].play();
+      return;
+    }
+  }
+};
+
+
+/**
+ * On key up.
+ *
+ * @param {ElementType} key The key released.
+ */
+Controller.prototype.onKeyUp = function(key) {
+  var sound = keyToSoundIdOnKeyUp[key] || Sounds.STANDARD;
+  this.playSound(sound);
+};
+
+
+/**
+ * On key repeat.
+ *
+ * @param {ElementType} key The key that is being repeated.
+ */
+Controller.prototype.onKeyRepeat = function(key) {
+  var sound = keyToSoundIdOnKeyRepeat[key] || Sounds.NONE;
+  this.playSound(sound);
+};
+
+
+/** @override */
+Controller.prototype.disposeInternal = function() {
+  for (var soundId in this.sounds_) {
+    var pool = this.sounds_[soundId];
+    for (var i = 0; i < pool.length; i++) {
+      var tag = pool[i];
+      if (tag && tag.loaded) {
+        tag.pause();
+        tag.autoplay = false;
+        tag.loop = false;
+        tag.currentTime = 0;
+      }
+    }
+    delete this.sounds_[soundId];
+  }
+  this.sounds_ = {};
+  keyToSoundIdOnKeyUp = {};
+  keyToSoundIdOnKeyRepeat = {};
+  goog.base(this, 'disposeInternal');
+};
+
+});  // goog.scope
diff --git a/third_party/google_input_tools/src/chrome/os/sounds/sounds.js b/third_party/google_input_tools/src/chrome/os/sounds/sounds.js
new file mode 100644
index 0000000..8b1c7ef
--- /dev/null
+++ b/third_party/google_input_tools/src/chrome/os/sounds/sounds.js
@@ -0,0 +1,32 @@
+// Copyright 2015 The ChromeOS IME Authors. All Rights Reserved.
+// limitations under the License.
+// See the License for the specific language governing permissions and
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// Unless required by applicable law or agreed to in writing, software
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// You may obtain a copy of the License at
+// you may not use this file except in compliance with the License.
+// Licensed under the Apache License, Version 2.0 (the "License");
+//
+goog.provide('i18n.input.chrome.sounds.Sounds');
+
+
+/**
+ * The available sounds.
+ *
+ * @enum {string}
+ */
+i18n.input.chrome.sounds.Sounds = {
+  DELETE: 'keypress-delete',
+  RETURN: 'keypress-return',
+  SPACEBAR: 'keypress-spacebar',
+  STANDARD: 'keypress-standard',
+  NONE: 'none',
+  VOICE_RECOG_START: 'voice_recog_start',
+  VOICE_RECOG_END: 'voice_recog_end',
+  AUTO_CORRECTION: 'auto_correction'
+};
+
diff --git a/third_party/google_input_tools/src/chrome/os/inputview/sounds/voice_recog_end.wav b/third_party/google_input_tools/src/chrome/os/sounds/voice_recog_end.wav
similarity index 100%
rename from third_party/google_input_tools/src/chrome/os/inputview/sounds/voice_recog_end.wav
rename to third_party/google_input_tools/src/chrome/os/sounds/voice_recog_end.wav
Binary files differ
diff --git a/third_party/google_input_tools/src/chrome/os/inputview/sounds/voice_recog_start.wav b/third_party/google_input_tools/src/chrome/os/sounds/voice_recog_start.wav
similarity index 100%
rename from third_party/google_input_tools/src/chrome/os/inputview/sounds/voice_recog_start.wav
rename to third_party/google_input_tools/src/chrome/os/sounds/voice_recog_start.wav
Binary files differ
diff --git a/third_party/google_input_tools/src/chrome/os/statistics.js b/third_party/google_input_tools/src/chrome/os/statistics.js
index 551f96e..af1d9f89 100644
--- a/third_party/google_input_tools/src/chrome/os/statistics.js
+++ b/third_party/google_input_tools/src/chrome/os/statistics.js
@@ -57,6 +57,7 @@
   X_Y1: 3, // User types X, and chooses Y as non-top suggestion.
   PREDICTION: 4,
   REVERT: 5,
+  VOICE: 6,
   MAX: 7
 };
 
@@ -64,8 +65,7 @@
 /**
  * The current input method id.
  *
- * @type {string}
- * @private
+ * @private {string}
  */
 Statistics.prototype.inputMethodId_ = '';
 
@@ -73,13 +73,20 @@
 /**
  * The current auto correct level.
  *
- * @type {number}
- * @private
+ * @private {number}
  */
 Statistics.prototype.autoCorrectLevel_ = 0;
 
 
 /**
+ * Number of characters entered between each backspace.
+ *
+ * @private {number}
+ */
+Statistics.prototype.charactersBetweenBackspaces_ = 0;
+
+
+/**
  * Whether recording for physical keyboard specially.
  *
  * @private {boolean}
@@ -152,7 +159,7 @@
  * @param {number} targetIndex The target index.
  * @param {number} triggerType The trigger type:
  *     0: BySpace; 1: ByReset; 2: ByCandidate; 3: BySymbolOrNumber;
- *     4: ByDoubleSpaceToPeriod; 5: ByRevert.
+ *     4: ByDoubleSpaceToPeriod; 5: ByRevert; 6: ByVoice.
  */
 Statistics.prototype.recordCommit = function(
     source, target, targetIndex, triggerType) {
@@ -173,6 +180,8 @@
     commitType = CommitTypes.PREDICTION;
   } else if (triggerType == 5) {
     commitType = CommitTypes.REVERT;
+  } else if (triggerType == 6) {
+    commitType = CommitTypes.VOICE;
   }
   if (commitType < 0) {
     return;
@@ -306,4 +315,26 @@
     }, count);
   }
 };
+
+
+/**
+ * Records a key down.
+ */
+Statistics.prototype.recordCharacterKey = function() {
+  this.charactersBetweenBackspaces_++;
+};
+
+
+/**
+ * Records a backspace.
+ */
+Statistics.prototype.recordBackspace = function() {
+  // Ignore multiple backspaces typed in succession.
+  if (this.charactersBetweenBackspaces_ > 0) {
+    this.recordValue(
+        'InputMethod.VirtualKeyboard.CharactersBetweenBackspaces',
+        this.charactersBetweenBackspaces_, 4096, 50);
+  }
+  this.charactersBetweenBackspaces_ = 0;
+};
 });  // goog.scope
diff --git a/third_party/harfbuzz-ng/BUILD.gn b/third_party/harfbuzz-ng/BUILD.gn
index 000a9cc..71213fd4 100644
--- a/third_party/harfbuzz-ng/BUILD.gn
+++ b/third_party/harfbuzz-ng/BUILD.gn
@@ -28,7 +28,7 @@
   } else {
     use_system_harfbuzz = false
   }
-  if (is_linux && cpu_arch == "arm" && !is_chromeos) {
+  if (is_linux && current_cpu == "arm" && !is_chromeos) {
     # Override use_system_harfbuzz for ARM cross compiling so system
     # harfbuzz is not used because the corresponding package is not
     # available.
@@ -161,10 +161,7 @@
       cflags += [ "-Wno-unused-value" ]
     }
     if (is_win) {
-      cflags += [
-        "/wd4267",  # size_t to 'type' converion.
-        "/wd4334",  # Result of 32-bit shift implicitly converted to 64 bits.
-      ]
+      cflags += [ "/wd4334" ]  # Result of 32-bit shift implicitly converted to 64 bits.
     }
     if (is_mac) {
       sources += [
diff --git a/third_party/jstemplate/README.chromium b/third_party/jstemplate/README.chromium
index 6fd6f26f..14b92f3 100644
--- a/third_party/jstemplate/README.chromium
+++ b/third_party/jstemplate/README.chromium
@@ -1,6 +1,8 @@
 Name: google-jstemplate
 URL: http://code.google.com/p/google-jstemplate/
 License: Apache 2.0
+Security Critical: yes
+Version: unknown
 
 "Template processing that is more suitable for the specific development-time
 and runtime requirements of AJAX based web applications.
@@ -16,3 +18,7 @@
 
 jstemplate_compiled.js is the output after passing the code through
 compile.sh.
+
+Local modifications:
+Changed JSDoc annotations and subtle code changes to make it compile with modern
+versions of Closure Compiler. TODO(dbeam): upstream to google code project.
diff --git a/third_party/jstemplate/jsevalcontext.js b/third_party/jstemplate/jsevalcontext.js
index 52bbc62c..f958a1e 100644
--- a/third_party/jstemplate/jsevalcontext.js
+++ b/third_party/jstemplate/jsevalcontext.js
@@ -243,7 +243,7 @@
  *
  * @param {Object} data The new context object.
  *
- * @param {number} index Position of the new context when multiply
+ * @param {number|string} index Position of the new context when multiply
  * instantiated. (See implementation of jstSelect().)
  * 
  * @param {number} count The total number of contexts that were multiply
diff --git a/third_party/jstemplate/jstemplate.js b/third_party/jstemplate/jstemplate.js
index b4c154f..449a31ca 100644
--- a/third_party/jstemplate/jstemplate.js
+++ b/third_party/jstemplate/jstemplate.js
@@ -552,9 +552,6 @@
  * @param {Element} template The currently processed node of the template.
  *
  * @param {Function} select The javascript expression to evaluate.
- *
- * @notypecheck FIXME(hmitchell): See OCL6434950. instance and value need
- * type checks.
  */
 JstProcessor.prototype.jstSelect_ = function(context, template, select) {
   var me = this;
@@ -586,6 +583,7 @@
   var multipleEmpty = (multiple && count == 0);
 
   if (multiple) {
+    value = /** @type Array */(value);
     if (multipleEmpty) {
       // For an empty array, keep the first template instance and mark
       // it last. Remove all other template instances.
@@ -938,7 +936,7 @@
  * @param {Array} values The current input context, the array of
  * values of which the template node will render one instance.
  *
- * @param {number} index The index of this template node in values.
+ * @param {number|string} index The index of this template node in values.
  */
 function jstSetInstance(template, values, index) {
   if (index == jsLength(values) - 1) {
diff --git a/third_party/leveldatabase/env_chromium.cc b/third_party/leveldatabase/env_chromium.cc
index 84ae4ac..42bdb42 100644
--- a/third_party/leveldatabase/env_chromium.cc
+++ b/third_party/leveldatabase/env_chromium.cc
@@ -412,7 +412,7 @@
 
 ErrorParsingResult ParseMethodAndError(const leveldb::Status& status,
                                        MethodID* method_param,
-                                       int* error) {
+                                       base::File::Error* error) {
   const std::string status_string = status.ToString();
   int method;
   if (RE2::PartialMatch(status_string.c_str(), "ChromeMethodOnly: (\\d+)",
@@ -420,19 +420,16 @@
     *method_param = static_cast<MethodID>(method);
     return METHOD_ONLY;
   }
+  int parsed_error;
   if (RE2::PartialMatch(status_string.c_str(),
                         "ChromeMethodPFE: (\\d+)::.*::(\\d+)", &method,
-                        error)) {
-    *error = -*error;
+                        &parsed_error)) {
     *method_param = static_cast<MethodID>(method);
+    *error = static_cast<base::File::Error>(-parsed_error);
+    DCHECK_LT(*error, base::File::FILE_OK);
+    DCHECK_GT(*error, base::File::FILE_ERROR_MAX);
     return METHOD_AND_PFE;
   }
-  if (RE2::PartialMatch(status_string.c_str(),
-                        "ChromeMethodErrno: (\\d+)::.*::(\\d+)", &method,
-                        error)) {
-    *method_param = static_cast<MethodID>(method);
-    return METHOD_AND_ERRNO;
-  }
   return NONE;
 }
 
@@ -507,13 +504,12 @@
   if (status.ok())
     return false;
   leveldb_env::MethodID method;
-  int error = -1;
+  base::File::Error error = base::File::FILE_OK;
   leveldb_env::ErrorParsingResult result =
       leveldb_env::ParseMethodAndError(status, &method, &error);
   return (result == leveldb_env::METHOD_AND_PFE &&
           static_cast<base::File::Error>(error) ==
-              base::File::FILE_ERROR_NO_SPACE) ||
-         (result == leveldb_env::METHOD_AND_ERRNO && error == ENOSPC);
+              base::File::FILE_ERROR_NO_SPACE);
 }
 
 bool ChromiumEnv::MakeBackup(const std::string& fname) {
@@ -900,6 +896,14 @@
 
 Status ChromiumEnv::NewAppendableFile(const std::string& fname,
                                       leveldb::WritableFile** result) {
+#if defined(OS_CHROMEOS)
+  // Disabled until crbug.com/460568 is fixed. Technically this method shouldn't
+  // be called if reuse_logs is false, but a leveldb bug (fixed, but not yet in
+  // Chrome) still calls this function. Using default leveldb Env implementation
+  // to workaround this bug.
+  return Env::NewAppendableFile(fname, result);
+#endif
+
   *result = NULL;
   FilePath path = FilePath::FromUTF8Unsafe(fname);
   scoped_ptr<base::File> f(new base::File(
diff --git a/third_party/leveldatabase/env_chromium.h b/third_party/leveldatabase/env_chromium.h
index 447e1df9..6fb7180 100644
--- a/third_party/leveldatabase/env_chromium.h
+++ b/third_party/leveldatabase/env_chromium.h
@@ -60,13 +60,12 @@
 enum ErrorParsingResult {
   METHOD_ONLY,
   METHOD_AND_PFE,
-  METHOD_AND_ERRNO,
   NONE,
 };
 
 ErrorParsingResult ParseMethodAndError(const leveldb::Status& status,
                                        MethodID* method,
-                                       int* error);
+                                       base::File::Error* error);
 int GetCorruptionCode(const leveldb::Status& status);
 int GetNumCorruptionCodes();
 std::string GetCorruptionMessage(const leveldb::Status& status);
diff --git a/third_party/leveldatabase/env_chromium_unittest.cc b/third_party/leveldatabase/env_chromium_unittest.cc
index 72a1efb3a..48ee72c 100644
--- a/third_party/leveldatabase/env_chromium_unittest.cc
+++ b/third_party/leveldatabase/env_chromium_unittest.cc
@@ -31,10 +31,10 @@
   const MethodID in_method = leveldb_env::kSequentialFileRead;
   const Status s = MakeIOError("Somefile.txt", "message", in_method);
   MethodID method;
-  int error = -75;
+  base::File::Error error = base::File::FILE_ERROR_MAX;
   EXPECT_EQ(leveldb_env::METHOD_ONLY, ParseMethodAndError(s, &method, &error));
   EXPECT_EQ(in_method, method);
-  EXPECT_EQ(-75, error);
+  EXPECT_EQ(base::File::FILE_ERROR_MAX, error);
 }
 
 TEST(ErrorEncoding, FileError) {
@@ -42,7 +42,7 @@
   const base::File::Error fe = base::File::FILE_ERROR_INVALID_OPERATION;
   const Status s = MakeIOError("Somefile.txt", "message", in_method, fe);
   MethodID method;
-  int error;
+  base::File::Error error;
   EXPECT_EQ(leveldb_env::METHOD_AND_PFE,
             ParseMethodAndError(s, &method, &error));
   EXPECT_EQ(in_method, method);
@@ -52,10 +52,10 @@
 TEST(ErrorEncoding, NoEncodedMessage) {
   Status s = Status::IOError("Some message", "from leveldb itself");
   MethodID method = leveldb_env::kRandomAccessFileRead;
-  int error = 4;
+  base::File::Error error = base::File::FILE_ERROR_MAX;
   EXPECT_EQ(leveldb_env::NONE, ParseMethodAndError(s, &method, &error));
   EXPECT_EQ(leveldb_env::kRandomAccessFileRead, method);
-  EXPECT_EQ(4, error);
+  EXPECT_EQ(base::File::FILE_ERROR_MAX, error);
 }
 
 template <typename T>
diff --git a/third_party/libexif/BUILD.gn b/third_party/libexif/BUILD.gn
index b2b4fed..be8caf6 100644
--- a/third_party/libexif/BUILD.gn
+++ b/third_party/libexif/BUILD.gn
@@ -53,7 +53,6 @@
 
       cflags = [
         "/wd4018",  # size/unsigned mismatch
-        "/wd4267",  # size_t -> ExifLong truncation on amd64
       ]
 
       # TODO(GYP): Handle /analyze switch, when it will be used in GN.
diff --git a/third_party/libjingle/BUILD.gn b/third_party/libjingle/BUILD.gn
index 6357b92..dae2bce 100644
--- a/third_party/libjingle/BUILD.gn
+++ b/third_party/libjingle/BUILD.gn
@@ -36,7 +36,7 @@
   # Assumes libpeer is linked statically.
   defines += [ "LIBPEERCONNECTION_LIB=1" ]
 
-  if (is_win && cpu_arch == "x86") {
+  if (is_win && current_cpu == "x86") {
     defines += [ "_USE_32BIT_TIME_T" ]
   }
 
@@ -256,6 +256,10 @@
     "$p2p_dir/base/constants.cc",
     "$p2p_dir/base/constants.h",
   ]
+
+  # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+  configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
+
   public_deps = [
     ":jingle_deps",
   ]
@@ -266,10 +270,7 @@
 
   # From libjingle_common.gypi's conditions list.
   if (is_win) {
-    cflags = [
-      "/wd4005",
-      "/wd4267",
-    ]
+    cflags = [ "/wd4005" ]
   }
 
   configs += [ ":jingle_unexported_configs" ]
diff --git a/third_party/libjingle/README.chromium b/third_party/libjingle/README.chromium
index ce17a466..84fe4ae 100644
--- a/third_party/libjingle/README.chromium
+++ b/third_party/libjingle/README.chromium
@@ -1,7 +1,7 @@
 Name: libjingle
 URL: http://code.google.com/p/webrtc/
 Version: unknown
-Revision: 8381
+Revision: 8444
 License: BSD
 License File: source/talk/COPYING
 Security Critical: yes
diff --git a/third_party/libpng/BUILD.gn b/third_party/libpng/BUILD.gn
index 488ef43..787d4fa 100644
--- a/third_party/libpng/BUILD.gn
+++ b/third_party/libpng/BUILD.gn
@@ -52,12 +52,8 @@
   configs -= [ "//build/config/compiler:chromium_code" ]
   configs += [ "//build/config/compiler:no_chromium_code" ]
 
-  if (is_win) {
-    cflags = [ "/wd4267" ]  # TODO(jschuh): http://crbug.com/167187
-
-    if (component_mode == "shared_library") {
-      defines = [ "PNG_BUILD_DLL" ]
-    }
+  if (is_win && is_component_build) {
+    defines = [ "PNG_BUILD_DLL" ]
   }
 
   public_configs = [ ":libpng_config" ]
diff --git a/third_party/libwebp/BUILD.gn b/third_party/libwebp/BUILD.gn
index 72a6903c..5495e64 100644
--- a/third_party/libwebp/BUILD.gn
+++ b/third_party/libwebp/BUILD.gn
@@ -9,8 +9,8 @@
 }
 
 use_dsp_neon =
-    cpu_arch == "arm64" || (cpu_arch == "arm" && arm_version >= 7 &&
-                            (arm_use_neon || arm_optionally_use_neon))
+    current_cpu == "arm64" || (current_cpu == "arm" && arm_version >= 7 &&
+                               (arm_use_neon || arm_optionally_use_neon))
 
 source_set("libwebp_dec") {
   sources = [
@@ -102,11 +102,11 @@
 
     include_dirs = [ "." ]
 
-    if (cpu_arch == "arm") {
+    if (current_cpu == "arm") {
       # behavior similar to *.c.neon in an Android.mk
       configs -= [ "//build/config/compiler:compiler_arm_fpu" ]
       cflags = [ "-mfpu=neon" ]
-    } else if (cpu_arch == "arm64") {
+    } else if (current_cpu == "arm64") {
       # avoid an ICE with gcc-4.9: b/15574841
       cflags = [ "-frename-registers" ]
     }
diff --git a/third_party/opus/BUILD.gn b/third_party/opus/BUILD.gn
index 244ddcd6..fefba04 100644
--- a/third_party/opus/BUILD.gn
+++ b/third_party/opus/BUILD.gn
@@ -6,15 +6,15 @@
 import("//testing/test.gni")
 
 # If fixed point implementation shall be used (otherwise float).
-use_opus_fixed_point = cpu_arch == "arm" || cpu_arch == "arm64"
+use_opus_fixed_point = current_cpu == "arm" || current_cpu == "arm64"
 
 # If ARM optimizations shall be used to accelerate performance.
-use_opus_arm_optimization = cpu_arch == "arm"
+use_opus_arm_optimization = current_cpu == "arm"
 
 # If OPUS Run Time CPU Detections (RTCD) shall be used.
 # Based on the conditions in celt/arm/armcpu.c:
 # defined(_MSC_VER) || defined(__linux__).
-use_opus_rtcd = cpu_arch == "arm" && (is_win || is_android || is_linux)
+use_opus_rtcd = current_cpu == "arm" && (is_win || is_android || is_linux)
 
 config("opus_config") {
   include_dirs = [ "src/include" ]
@@ -97,7 +97,8 @@
     cflags = [ "-Wno-#pragma-messages" ]
   }
 
-  if (!is_debug && is_posix && (cpu_arch == "arm" || cpu_arch == "arm64")) {
+  if (!is_debug && is_posix &&
+      (current_cpu == "arm" || current_cpu == "arm64")) {
     configs -= [ "//build/config/compiler:optimize" ]
     configs += [ "//build/config/compiler:optimize_max" ]
   }
diff --git a/third_party/ots/BUILD.gn b/third_party/ots/BUILD.gn
new file mode 100644
index 0000000..513dce6
--- /dev/null
+++ b/third_party/ots/BUILD.gn
@@ -0,0 +1,90 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+config("ots_config") {
+  include_dirs = [ "include" ]
+}
+
+source_set("ots") {
+  sources = [
+    "include/ots-memory-stream.h",
+    "include/opentype-sanitiser.h",
+    "src/cff.cc",
+    "src/cff.h",
+    "src/cff_type2_charstring.cc",
+    "src/cff_type2_charstring.h",
+    "src/cmap.cc",
+    "src/cmap.h",
+    "src/cvt.cc",
+    "src/cvt.h",
+    "src/fpgm.cc",
+    "src/fpgm.h",
+    "src/gasp.cc",
+    "src/gasp.h",
+    "src/gdef.cc",
+    "src/gdef.h",
+    "src/glyf.cc",
+    "src/glyf.h",
+    "src/gpos.cc",
+    "src/gpos.h",
+    "src/gsub.cc",
+    "src/gsub.h",
+    "src/hdmx.cc",
+    "src/hdmx.h",
+    "src/head.cc",
+    "src/head.h",
+    "src/hhea.cc",
+    "src/hhea.h",
+    "src/hmtx.cc",
+    "src/hmtx.h",
+    "src/kern.cc",
+    "src/kern.h",
+    "src/layout.cc",
+    "src/layout.h",
+    "src/loca.cc",
+    "src/loca.h",
+    "src/ltsh.cc",
+    "src/ltsh.h",
+    "src/maxp.cc",
+    "src/maxp.h",
+    "src/math.cc",
+    "src/math_.h",
+    "src/metrics.cc",
+    "src/metrics.h",
+    "src/name.cc",
+    "src/name.h",
+    "src/os2.cc",
+    "src/os2.h",
+    "src/ots.cc",
+    "src/ots.h",
+    "src/post.cc",
+    "src/post.h",
+    "src/prep.cc",
+    "src/prep.h",
+    "src/vdmx.cc",
+    "src/vdmx.h",
+    "src/vhea.cc",
+    "src/vhea.h",
+    "src/vmtx.cc",
+    "src/vmtx.h",
+    "src/vorg.cc",
+    "src/vorg.h",
+    "src/woff2.cc",
+    "src/woff2.h",
+  ]
+
+  direct_dependent_configs = [ ":ots_config" ]
+
+  deps = [
+    "//third_party/brotli",
+    "//third_party/zlib",
+  ]
+
+  if (is_win) {
+    cflags = [
+      "/wd4267",  # Conversion from size_t to 'type'.
+      "/wd4334",  # 32-bit shift implicitly converted to 64-bits.
+    ]
+  }
+}
diff --git a/third_party/ots/INSTALL b/third_party/ots/INSTALL
new file mode 100644
index 0000000..dbb1d944
--- /dev/null
+++ b/third_party/ots/INSTALL
@@ -0,0 +1,38 @@
+How to build (using gyp):
+
+  (Note: test programs which require gtest can't build with gyp for now)
+
+  1. If you are building OTS on Windows, download both the source
+     code and compiled driver for zlib from http://www.zlib.net/
+     and put them in third_party/zlib.
+
+  2. If you are building from cloned Git repository, make sure to update the
+     submodules as well:
+
+     $ git submodule init
+     $ git submodule update
+
+  3. Run gyp_ots
+
+    $ python gyp_ots
+
+    This will fetch gyp and generate build files. By default, following
+    files will be generated:
+      - MSVS solution file on Windows
+      - Xcode project file on Mac
+      - Makefile on Linux
+
+    If you want to generate Makefile on Mac, you can use -f option:
+
+    $ python gyp_ots -f make
+
+  4. Build OTS
+
+    Using MSVS:
+      Open ots-standalone.sln and build the solution.
+
+    Using Xcode:
+      $ xcodebuild -target ots-standalone.xcodeproj -target all
+
+    Using Makefile:
+      $ make
diff --git a/third_party/ots/LICENSE b/third_party/ots/LICENSE
new file mode 100644
index 0000000..a7531cf
--- /dev/null
+++ b/third_party/ots/LICENSE
@@ -0,0 +1,27 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/third_party/ots/OWNERS b/third_party/ots/OWNERS
new file mode 100644
index 0000000..96e5d87
--- /dev/null
+++ b/third_party/ots/OWNERS
@@ -0,0 +1,4 @@
+bashi@chromium.org
+behdad@chromium.org
+behdad@google.com
+jshin@chromium.org
diff --git a/third_party/ots/README b/third_party/ots/README
new file mode 100644
index 0000000..8dc1ebb8
--- /dev/null
+++ b/third_party/ots/README
@@ -0,0 +1,21 @@
+Sanitiser for OpenType
+----------------------
+
+(Idea from Alex Russell)
+
+The CSS font-face property[1] is great for web typography. Having to use images
+in order to get the correct typeface is a great sadness; one should be able to
+use vectors.
+
+However, the TrueType renderers on many platforms have never been part of the
+attack surface before and putting them on the front line is a scary proposition.
+Esp on platforms like Windows where it's a closed-source blob running with high
+privilege.
+
+Thus, the OpenType Sanitiser (OTS) is designed to parse and serialise OpenType
+files, validating them and sanitising them as it goes.
+
+
+See INSTALL for build instructions.
+
+[1] http://www.w3.org/TR/CSS2/fonts.html#font-descriptions
diff --git a/third_party/ots/README.chromium b/third_party/ots/README.chromium
new file mode 100644
index 0000000..c8288133
--- /dev/null
+++ b/third_party/ots/README.chromium
@@ -0,0 +1,5 @@
+Name: OTS
+URL: https://github.com/khaledhosny/ots.git
+Version: ea88f974e00e7fe0b4fbfe8d0adad8cfedf49c57
+Security Critical: yes
+License: BSD
diff --git a/third_party/ots/docs/DesignDoc.md b/third_party/ots/docs/DesignDoc.md
new file mode 100644
index 0000000..ffcc035
--- /dev/null
+++ b/third_party/ots/docs/DesignDoc.md
@@ -0,0 +1,136 @@
+What's OTS?
+===========
+
+Sanitiser for OpenType (OTS) is a small library which parses OpenType files
+(usually from `@font-face`) and attempts to validate and sanitise them. This
+library is primarily intended to be used with Chromium. We hope this reduces
+the attack surface of the system font libraries.
+
+What the sanitiser does is as follows:
+
+1. Parses an original font. If the parsing fails, OTS rejects the original
+   font.
+2. Validates the parsed data structure. If the validation fails, it rejects the
+   original font as well.
+3. Creates a new font on memory by serializing the data structure, and we call
+   this "transcoding".
+
+By transcoding fonts in this way, it is ensured that:
+
+1. All information in an original font that OTS doesn't know or can't parse is
+   dropped from the transcoded font.
+2. All information in the transcoded font is valid (standard compliant).
+   Particularly 'length' and 'offset' values, that are often used as attack
+   vectors, are ensured to be correct.
+
+Supported OpenType tables
+=========================
+
+| Name   | Mandatory table?            | Supported by OTS? | Note   |
+|--------|-----------------------------|-------------------|--------|
+| `sfnt` | Yes                         | Yes               | Overlapped tables are not allowed; it is treated as a fatal parser error.|
+| `maxp` | Yes                         | Yes               |        |
+| `head` | Yes                         | Yes               |        |
+| `hhea` | Yes                         | Yes               |        |
+| `hmtx` | Yes                         | Yes               |        |
+| `name` | Yes                         | Yes               |        |
+| `OS/2` | Yes                         | Yes               |        |
+| `post` | Yes                         | Yes               |        |
+| `cmap` | Yes                         | Partialy          | see below |
+| `glyf` | Yes, for TrueType fonts     | Yes               | TrueType bytecode is supported, but OTS does **not** validate it.|
+| `loca` | Yes, when glyf table exists | Yes               |        |
+| `CFF ` | Yes, for OpenType fonts     | Yes               | OpenType bytecode is also supported, and OTS **does** validate it.|
+| `cvt ` | No                          | Yes               | Though this table is not mandatory, OTS can't drop the table from a transcoded font since it might be referred from other hinting-related tables. Errors on this table should be treated as fatal.|
+| `fpgm` | No                          | Yes               | Ditto. |
+| `prep` | No                          | Yes               | Ditto. |
+| `VDMX` | No                          | Yes               | This table is important for calculating the correct line spacing, at least on Chromium Windows and Chromium Linux.|
+| `hdmx` | No                          | Yes               |        |
+| `gasp` | No                          | Yes               |        |
+| `VORG` | No                          | Yes               |        |
+| `LTSH` | No                          | Yes               |        |
+| `kern` | No                          | Yes               |        |
+| `GDEF` | No                          | Yes               |        |
+| `GSUB` | No                          | Yes               |        |
+| `GPOS` | No                          | Yes               |        |
+| `morx` | No                          | No                |        |
+| `jstf` | No                          | No                |        |
+| `vmtx` | No                          | Yes               |        |
+| `vhea` | No                          | Yes               |        |
+| `EBDT` | No                          | No                | We don't support embedded bitmap strikes.|
+| `EBLC` | No                          | No                | Ditto. |
+| `EBSC` | No                          | No                | Ditto. |
+| `bdat` | No                          | No                | Ditto. |
+| `bhed` | No                          | No                | Ditto. |
+| `bloc` | No                          | No                | Ditto. |
+| `DSIG` | No                          | No                |        |
+| All other tables | -                 | No                |        |
+
+Please note that OTS library does not parse "unsupported" tables. These
+unsupported tables never appear in a transcoded font.
+
+Supported cmap formats
+----------------------
+
+The following 9 formats are supported:
+
+* "MS Unicode" (platform 3 encoding 1 format 4)
+    * BMP
+* "MS UCS-4" (platform 3 encoding 10 format 12)
+* "MS UCS-4 fallback" (platform 3 encoding 10 format 13)
+* "MS Symbol" (platform 3 encoding 0 format 4)
+* "Mac Roman" (platform 1 encoding 0 format 0)
+    * 1-0-0 format is supported while 1-0-6 is not.
+* "Unicode default" format (platform 0 encoding 0 format 4)
+    * treated as 3-1-4 format
+* "Unicode 1.1" format (platform 0 encoding 1 format 4)
+    * ditto
+* "Unicode 2.0+" format (platform 0 encoding 3 format 4)
+* "Unicode UCS-4" format (platform 0 encoding 4 format 12)
+    * treated as 3-10-12 format
+* Unicode Variation Sequences (platform 0 encoding 5 format 14)
+
+All other types of subtables are not supported and do not appear in transcoded fonts.
+
+Validation strategies
+=====================
+
+With regards to 8 mandatory tables, glyph-related tables (`glyf`, `loca` and `CFF`),
+and hinting-related tables (`cvt`, `prep`, and `fpgm`):
+
+* If OTS finds table-length, table-offset, or table-alignment errors, in other
+  words it cannot continue parsing, OTS treats the error as fatal.
+* If OTS finds simple value error which could be automatically fixed (e.g.,
+  font weight is greater than 900 - that's undefined), and if the error is
+  considered common among non-malicious fonts, OTS rewrites the value and
+  continues transcoding.
+* If OTS finds a value error which is hard to fix (e.g., values which should be
+  sorted are left unsorted), OTS treats the error as fatal.
+
+With regards to optional tables (`VORG`, `gasp`, `hdmx`, `LTSH`, and `VDMX`):
+
+* If OTS finds table-length, table-offset, or table-alignment errors, OTS
+  treats the error as fatal.
+* If OTS finds other errors, it simply drops the table from a transcoded font.
+
+Files
+=====
+
+* include/opentype-sanitiser.h
+    * Declaration for the public API, `ots::Process()`.
+    * Definition of the `OTSStream` interface, a write-only memory stream.
+* include/ots-memory-stream.h
+    * Definition of the `MemoryStream` class which implements the `OTSStream`
+      interface above.
+* src/ots.h
+    * Debug macros.
+    * Definition of a `Buffer` class which is a read-only memory stream.
+* src/ots.cc
+    * Definition of the `ots::Process()` function.
+    * `sfnt` table parser.
+* test/\*.cc
+    * test tools. see test/README for details.
+
+Known issues
+============
+
+Please check the [issues](https://github.com/khaledhosny/ots/issues) page.
diff --git a/third_party/ots/docs/HowToTest.md b/third_party/ots/docs/HowToTest.md
new file mode 100644
index 0000000..a18ee34
--- /dev/null
+++ b/third_party/ots/docs/HowToTest.md
@@ -0,0 +1,68 @@
+Prerequisites
+=============
+
+You can use your Ubuntu box (>= 8.04. 9.10 is recommended) to test OTS library.
+
+First, install TrueType and OpenType fonts to the Ubuntu box as many as
+possible.
+
+    % sudo apt-get install ttf-.*[^0]$
+
+Then, put malicious TrueType fonts on `~/malicious/`. For details, please check
+http://code.google.com/p/chromium/issues/detail?id=27139#c2. Currently access
+to the issue is limited to chromium-security team members for security reasons.
+
+    % cd
+    % tar xjf ~/ttf-testsuite.tar.bz2
+
+Test
+====
+
+In order to verify that:
+
+1. OTS does not reject these unmalicious fonts.
+2. and transcoded fonts OTS generates can be loaded by a system font renderer (FreeType2).
+
+Run `test_unmalicious_fonts.sh` script:
+
+    % cd /path/to/ots/tests
+    % ./test_unmalicious_fonts.sh
+    ...............................................  (verify that no FAIL: is displayed)
+
+Then in order to verify that:
+
+1. OTS can reject malicious fonts
+2. or transcoded fonts generated by OTS do not crash a system font renderer (FreeType2).
+
+Run `test_malicious_fonts.sh` script:
+
+    % cd /path/to/ots/tests
+    % ./test_malicious_fonts.sh
+    ...............................................  (verify that no FAIL: is displayed)
+
+Command line tools
+==================
+
+We have some command line tools for tests. To build them:
+
+- On Linux:
+
+        % gyp --depth=. -f make ots-standalone.gyp
+        % make
+        (tool is located at build/Default directory)
+
+- On Windows (VC++ is needed):
+
+        % gyp --depth=. -f msvs ots-standalone.gyp
+        % devenv.exe /build Default ots-standalone.sln /project idempotent.vcproj
+        (tool is located at Default directory)
+
+- On Mac (XCode is needed):
+
+        % gyp --depth=. -f xcode ots-standalone.gyp
+        % xcodebuild -configuration Default -project ots-standalone.xcodeproj -target All
+        (tool is located at build/Default directory)
+
+You can use `idempotent` tool to check whether a font will be rejected or not.
+You can also use `ot-sanitise` tool to get sanitised font (it is available on
+Linux for now). See README file in the test directory for more details.
diff --git a/third_party/ots/gyp_ots b/third_party/ots/gyp_ots
new file mode 100755
index 0000000..9a4056e
--- /dev/null
+++ b/third_party/ots/gyp_ots
@@ -0,0 +1,36 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import os
+import subprocess
+import sys
+
+_GYP_REVISION = '1344'
+_GYP_FETCH_URL = 'https://gyp.googlecode.com/svn/trunk@' + _GYP_REVISION
+
+def _fetch_gyp():
+  gyp_dir = os.path.join('third_party', 'gyp')
+  if not os.path.exists(gyp_dir):
+    retcode = subprocess.call(['svn', 'checkout', _GYP_FETCH_URL, gyp_dir])
+    if retcode < 0:
+      raise "Couldn't fetch gyp"
+  # TODO(bashi): Check revision, etc
+  sys.path.insert(0, os.path.abspath(os.path.join(gyp_dir, 'pylib')))
+
+def main():
+  script_dir = os.path.abspath(os.path.dirname(__file__))
+  os.chdir(script_dir)
+  _fetch_gyp()
+  import gyp
+
+  args = []
+  args.extend(['--depth', '.'])
+  args.extend(sys.argv[1:])
+  args.append(os.path.join(script_dir, 'ots-standalone.gyp'))
+  sys.exit(gyp.main(args))
+
+if __name__ == '__main__':
+  main()
diff --git a/third_party/ots/include/opentype-sanitiser.h b/third_party/ots/include/opentype-sanitiser.h
new file mode 100644
index 0000000..c454f1e
--- /dev/null
+++ b/third_party/ots/include/opentype-sanitiser.h
@@ -0,0 +1,231 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OPENTYPE_SANITISER_H_
+#define OPENTYPE_SANITISER_H_
+
+#if defined(_WIN32)
+#include <stdlib.h>
+typedef signed char int8_t;
+typedef unsigned char uint8_t;
+typedef short int16_t;
+typedef unsigned short uint16_t;
+typedef int int32_t;
+typedef unsigned int uint32_t;
+typedef __int64 int64_t;
+typedef unsigned __int64 uint64_t;
+#define ntohl(x) _byteswap_ulong (x)
+#define ntohs(x) _byteswap_ushort (x)
+#define htonl(x) _byteswap_ulong (x)
+#define htons(x) _byteswap_ushort (x)
+#else
+#include <arpa/inet.h>
+#include <stdint.h>
+#endif
+
+#include <algorithm>
+#include <cassert>
+#include <cstddef>
+#include <cstring>
+
+namespace ots {
+
+// -----------------------------------------------------------------------------
+// This is an interface for an abstract stream class which is used for writing
+// the serialised results out.
+// -----------------------------------------------------------------------------
+class OTSStream {
+ public:
+  OTSStream() {
+    ResetChecksum();
+  }
+
+  virtual ~OTSStream() {}
+
+  // This should be implemented to perform the actual write.
+  virtual bool WriteRaw(const void *data, size_t length) = 0;
+
+  bool Write(const void *data, size_t length) {
+    if (!length) return false;
+
+    const size_t orig_length = length;
+    size_t offset = 0;
+    if (chksum_buffer_offset_) {
+      const size_t l =
+        std::min(length, static_cast<size_t>(4) - chksum_buffer_offset_);
+      std::memcpy(chksum_buffer_ + chksum_buffer_offset_, data, l);
+      chksum_buffer_offset_ += l;
+      offset += l;
+      length -= l;
+    }
+
+    if (chksum_buffer_offset_ == 4) {
+      uint32_t tmp;
+      std::memcpy(&tmp, chksum_buffer_, 4);
+      chksum_ += ntohl(tmp);
+      chksum_buffer_offset_ = 0;
+    }
+
+    while (length >= 4) {
+      uint32_t tmp;
+      std::memcpy(&tmp, reinterpret_cast<const uint8_t *>(data) + offset,
+        sizeof(uint32_t));
+      chksum_ += ntohl(tmp);
+      length -= 4;
+      offset += 4;
+    }
+
+    if (length) {
+      if (chksum_buffer_offset_ != 0) return false;  // not reached
+      if (length > 4) return false;  // not reached
+      std::memcpy(chksum_buffer_,
+             reinterpret_cast<const uint8_t*>(data) + offset, length);
+      chksum_buffer_offset_ = length;
+    }
+
+    return WriteRaw(data, orig_length);
+  }
+
+  virtual bool Seek(off_t position) = 0;
+  virtual off_t Tell() const = 0;
+
+  virtual bool Pad(size_t bytes) {
+    static const uint32_t kZero = 0;
+    while (bytes >= 4) {
+      if (!WriteTag(kZero)) return false;
+      bytes -= 4;
+    }
+    while (bytes) {
+      static const uint8_t kZerob = 0;
+      if (!Write(&kZerob, 1)) return false;
+      bytes--;
+    }
+    return true;
+  }
+
+  bool WriteU8(uint8_t v) {
+    return Write(&v, sizeof(v));
+  }
+
+  bool WriteU16(uint16_t v) {
+    v = htons(v);
+    return Write(&v, sizeof(v));
+  }
+
+  bool WriteS16(int16_t v) {
+    v = htons(v);
+    return Write(&v, sizeof(v));
+  }
+
+  bool WriteU24(uint32_t v) {
+    v = htonl(v);
+    return Write(reinterpret_cast<uint8_t*>(&v)+1, 3);
+  }
+
+  bool WriteU32(uint32_t v) {
+    v = htonl(v);
+    return Write(&v, sizeof(v));
+  }
+
+  bool WriteS32(int32_t v) {
+    v = htonl(v);
+    return Write(&v, sizeof(v));
+  }
+
+  bool WriteR64(uint64_t v) {
+    return Write(&v, sizeof(v));
+  }
+
+  bool WriteTag(uint32_t v) {
+    return Write(&v, sizeof(v));
+  }
+
+  void ResetChecksum() {
+    chksum_ = 0;
+    chksum_buffer_offset_ = 0;
+  }
+
+  uint32_t chksum() const {
+    assert(chksum_buffer_offset_ == 0);
+    return chksum_;
+  }
+
+  struct ChecksumState {
+    uint32_t chksum;
+    uint8_t chksum_buffer[4];
+    unsigned chksum_buffer_offset;
+  };
+
+  ChecksumState SaveChecksumState() const {
+    ChecksumState s;
+    s.chksum = chksum_;
+    s.chksum_buffer_offset = chksum_buffer_offset_;
+    std::memcpy(s.chksum_buffer, chksum_buffer_, 4);
+
+    return s;
+  }
+
+  void RestoreChecksum(const ChecksumState &s) {
+    assert(chksum_buffer_offset_ == 0);
+    chksum_ += s.chksum;
+    chksum_buffer_offset_ = s.chksum_buffer_offset;
+    std::memcpy(chksum_buffer_, s.chksum_buffer, 4);
+  }
+
+ protected:
+  uint32_t chksum_;
+  uint8_t chksum_buffer_[4];
+  unsigned chksum_buffer_offset_;
+};
+
+#ifdef __GCC__
+#define MSGFUNC_FMT_ATTR __attribute__((format(printf, 2, 3)))
+#else
+#define MSGFUNC_FMT_ATTR
+#endif
+
+enum TableAction {
+  TABLE_ACTION_DEFAULT,  // Use OTS's default action for that table
+  TABLE_ACTION_SANITIZE, // Sanitize the table, potentially droping it
+  TABLE_ACTION_PASSTHRU, // Serialize the table unchanged
+  TABLE_ACTION_DROP      // Drop the table
+};
+
+class OTSContext {
+  public:
+    OTSContext() {}
+    virtual ~OTSContext() {}
+
+    // Process a given OpenType file and write out a sanitised version
+    //   output: a pointer to an object implementing the OTSStream interface. The
+    //     sanitisied output will be written to this. In the even of a failure,
+    //     partial output may have been written.
+    //   input: the OpenType file
+    //   length: the size, in bytes, of |input|
+    //   context: optional context that holds various OTS settings like user callbacks
+    bool Process(OTSStream *output, const uint8_t *input, size_t length);
+
+    // This function will be called when OTS is reporting an error.
+    //   level: the severity of the generated message:
+    //     0: error messages in case OTS fails to sanitize the font.
+    //     1: warning messages about issue OTS fixed in the sanitized font.
+    virtual void Message(int level, const char *format, ...) MSGFUNC_FMT_ATTR {}
+
+    // This function will be called when OTS needs to decide what to do for a
+    // font table.
+    //   tag: table tag as an integer in big-endian byte order, independent of
+    //   platform endianness
+    virtual TableAction GetTableAction(uint32_t tag) { return ots::TABLE_ACTION_DEFAULT; }
+};
+
+// For backward compatibility - remove once Chrome switches over to the new API.
+bool Process(OTSStream *output, const uint8_t *input, size_t length);
+
+// For backward compatibility - remove once https://codereview.chromium.org/774253008/
+// is submitted.
+void EnableWOFF2();
+
+}  // namespace ots
+
+#endif  // OPENTYPE_SANITISER_H_
diff --git a/third_party/ots/include/ots-memory-stream.h b/third_party/ots/include/ots-memory-stream.h
new file mode 100644
index 0000000..579da61
--- /dev/null
+++ b/third_party/ots/include/ots-memory-stream.h
@@ -0,0 +1,105 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_MEMORY_STREAM_H_
+#define OTS_MEMORY_STREAM_H_
+
+#include <cstring>
+#include <limits>
+
+#include "opentype-sanitiser.h"
+
+namespace ots {
+
+class MemoryStream : public OTSStream {
+ public:
+  MemoryStream(void *ptr, size_t length)
+      : ptr_(ptr), length_(length), off_(0) {
+  }
+
+  virtual bool WriteRaw(const void *data, size_t length) {
+    if ((off_ + length > length_) ||
+        (length > std::numeric_limits<size_t>::max() - off_)) {
+      return false;
+    }
+    std::memcpy(static_cast<char*>(ptr_) + off_, data, length);
+    off_ += length;
+    return true;
+  }
+
+  virtual bool Seek(off_t position) {
+    if (position < 0) return false;
+    if (static_cast<size_t>(position) > length_) return false;
+    off_ = position;
+    return true;
+  }
+
+  virtual off_t Tell() const {
+    return off_;
+  }
+
+ private:
+  void* const ptr_;
+  size_t length_;
+  off_t off_;
+};
+
+class ExpandingMemoryStream : public OTSStream {
+ public:
+  ExpandingMemoryStream(size_t initial, size_t limit)
+      : length_(initial), limit_(limit), off_(0) {
+    ptr_ = new uint8_t[length_];
+  }
+
+  ~ExpandingMemoryStream() {
+    delete[] static_cast<uint8_t*>(ptr_);
+  }
+
+  void* get() const {
+    return ptr_;
+  }
+
+  bool WriteRaw(const void *data, size_t length) {
+    if ((off_ + length > length_) ||
+        (length > std::numeric_limits<size_t>::max() - off_)) {
+      if (length_ == limit_)
+        return false;
+      size_t new_length = (length_ + 1) * 2;
+      if (new_length < length_)
+        return false;
+      if (new_length > limit_)
+        new_length = limit_;
+      uint8_t* new_buf = new uint8_t[new_length];
+      std::memcpy(new_buf, ptr_, length_);
+      length_ = new_length;
+      delete[] static_cast<uint8_t*>(ptr_);
+      ptr_ = new_buf;
+      return WriteRaw(data, length);
+    }
+    std::memcpy(static_cast<char*>(ptr_) + off_, data, length);
+    off_ += length;
+    return true;
+  }
+
+  bool Seek(off_t position) {
+    if (position < 0) return false;
+    if (static_cast<size_t>(position) > length_) return false;
+    off_ = position;
+    return true;
+  }
+
+  off_t Tell() const {
+    return off_;
+  }
+
+ private:
+  void* ptr_;
+  size_t length_;
+  const size_t limit_;
+  off_t off_;
+};
+
+}  // namespace ots
+
+#endif  // OTS_MEMORY_STREAM_H_
diff --git a/third_party/ots/ots-common.gypi b/third_party/ots/ots-common.gypi
new file mode 100644
index 0000000..9cb539c
--- /dev/null
+++ b/third_party/ots/ots-common.gypi
@@ -0,0 +1,77 @@
+# Copyright (c) 2009 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+  'variables': {
+    'ots_sources': [
+      'include/ots-memory-stream.h',
+      'include/opentype-sanitiser.h',
+      'src/cff.cc',
+      'src/cff.h',
+      'src/cff_type2_charstring.cc',
+      'src/cff_type2_charstring.h',
+      'src/cmap.cc',
+      'src/cmap.h',
+      'src/cvt.cc',
+      'src/cvt.h',
+      'src/fpgm.cc',
+      'src/fpgm.h',
+      'src/gasp.cc',
+      'src/gasp.h',
+      'src/gdef.cc',
+      'src/gdef.h',
+      'src/glyf.cc',
+      'src/glyf.h',
+      'src/gpos.cc',
+      'src/gpos.h',
+      'src/gsub.cc',
+      'src/gsub.h',
+      'src/hdmx.cc',
+      'src/hdmx.h',
+      'src/head.cc',
+      'src/head.h',
+      'src/hhea.cc',
+      'src/hhea.h',
+      'src/hmtx.cc',
+      'src/hmtx.h',
+      'src/kern.cc',
+      'src/kern.h',
+      'src/layout.cc',
+      'src/layout.h',
+      'src/loca.cc',
+      'src/loca.h',
+      'src/ltsh.cc',
+      'src/ltsh.h',
+      'src/maxp.cc',
+      'src/maxp.h',
+      'src/math.cc',
+      'src/math_.h',
+      'src/metrics.cc',
+      'src/metrics.h',
+      'src/name.cc',
+      'src/name.h',
+      'src/os2.cc',
+      'src/os2.h',
+      'src/ots.cc',
+      'src/ots.h',
+      'src/post.cc',
+      'src/post.h',
+      'src/prep.cc',
+      'src/prep.h',
+      'src/vdmx.cc',
+      'src/vdmx.h',
+      'src/vhea.cc',
+      'src/vhea.h',
+      'src/vmtx.cc',
+      'src/vmtx.h',
+      'src/vorg.cc',
+      'src/vorg.h',
+      'src/woff2.cc',
+      'src/woff2.h',
+    ],
+    'ots_include_dirs': [
+      'include',
+    ],
+  },
+}
diff --git a/third_party/ots/ots-standalone.gyp b/third_party/ots/ots-standalone.gyp
new file mode 100644
index 0000000..a45ec4a
--- /dev/null
+++ b/third_party/ots/ots-standalone.gyp
@@ -0,0 +1,256 @@
+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+  'variables': {
+    'gcc_cflags': [
+      '-ggdb',
+      '-W',
+      '-Wall',
+      '-Wshadow',
+      '-Wno-unused-parameter',
+      '-fPIE',
+      '-fstack-protector',
+    ],
+    'gcc_ldflags': [
+      '-ggdb',
+      '-fpie',
+      '-Wl,-z,relro',
+      '-Wl,-z,now',
+    ],
+  },
+  'includes': [
+    'ots-common.gypi',
+  ],
+  'target_defaults': {
+    'include_dirs': [
+      '.',
+      'third_party/brotli/dec',
+    ],
+    'conditions': [
+      ['OS=="linux"', {
+        'cflags': [
+          '<@(gcc_cflags)',
+          '-O',
+        ],
+        'ldflags': [
+          '<@(gcc_ldflags)',
+        ],
+        'defines': [
+          '_FORTIFY_SOURCE=2',
+        ],
+        'link_settings': {
+          'libraries': ['-lz'],
+        },
+      }],
+      ['OS=="mac"', {
+        'xcode_settings': {
+          'GCC_DYNAMIC_NO_PIC': 'NO',            # No -mdynamic-no-pic
+          'GCC_SYMBOLS_PRIVATE_EXTERN': 'YES',   # -fvisibility=hidden
+          'OTHER_CFLAGS': [
+            '<@(gcc_cflags)',
+          ],
+        },
+        'link_settings': {
+          'libraries': [
+            '/System/Library/Frameworks/ApplicationServices.framework',
+            '/usr/lib/libz.dylib'
+          ],
+        },
+      }],
+      ['OS=="win"', {
+        'link_settings': {
+          'libraries': [
+            '-lzdll.lib',
+          ],
+        },
+        'msvs_settings': {
+          'VCLinkerTool': {
+            'AdditionalLibraryDirectories': ['third_party/zlib'],
+            'DelayLoadDLLs': ['zlib1.dll'],
+          },
+        },
+        'include_dirs': [
+          'third_party/zlib',
+        ],
+        'defines': [
+          'NOMINMAX', # To suppress max/min macro definition.
+          'WIN32',
+        ],
+      }],
+    ],
+  },
+  'targets': [
+    {
+      'target_name': 'ots',
+      'type': 'static_library',
+      'sources': [
+        '<@(ots_sources)',
+      ],
+      'dependencies': [
+        'third_party/brotli.gyp:brotli',
+      ],
+      'include_dirs': [
+        '<@(ots_include_dirs)',
+      ],
+      'direct_dependent_settings': {
+        'include_dirs': [
+          '<@(ots_include_dirs)',
+        ],
+      },
+    },
+    {
+      'target_name': 'freetype2',
+      'type': 'none',
+      'conditions': [
+        ['OS=="linux"', {
+          'direct_dependent_settings': {
+            'cflags': [
+              '<!(pkg-config freetype2 --cflags)',
+            ],
+            'link_settings': {
+              'libraries': [
+                '<!(pkg-config freetype2 --libs)',
+              ],
+            },
+          },
+        }],
+      ],
+    },
+    {
+      'target_name': 'idempotent',
+      'type': 'executable',
+      'sources': [
+        'test/idempotent.cc',
+      ],
+      'dependencies': [
+        'ots',
+      ],
+      'conditions': [
+        ['OS=="linux"', {
+          'dependencies': [
+            'freetype2',
+          ]
+        }],
+        ['OS=="win"', {
+          'link_settings': {
+            'libraries': [
+              '-lgdi32.lib',
+            ],
+          },
+        }],
+      ],
+    },
+    {
+      'target_name': 'ot-sanitise',
+      'type': 'executable',
+      'sources': [
+        'test/ot-sanitise.cc',
+        'test/file-stream.h',
+      ],
+      'dependencies': [
+        'ots',
+      ],
+    },
+  ],
+  'conditions': [
+    ['OS=="linux" or OS=="mac"', {
+      'targets': [
+        {
+          'target_name': 'validator_checker',
+          'type': 'executable',
+          'sources': [
+            'test/validator-checker.cc',
+          ],
+          'dependencies': [
+            'ots',
+          ],
+          'conditions': [
+            ['OS=="linux"', {
+              'dependencies': [
+                'freetype2',
+              ]
+            }],
+          ],
+        },
+        {
+          'target_name': 'perf',
+          'type': 'executable',
+          'sources': [
+            'test/perf.cc',
+          ],
+          'dependencies': [
+            'ots',
+          ],
+        },
+        {
+          'target_name': 'cff_type2_charstring_test',
+          'type': 'executable',
+          'sources': [
+            'test/cff_type2_charstring_test.cc',
+          ],
+          'dependencies': [
+            'ots',
+          ],
+          'libraries': [
+            '-lgtest',
+            '-lgtest_main',
+          ],
+          'include_dirs': [
+            'src',
+          ],
+        },
+        {
+          'target_name': 'layout_common_table_test',
+          'type': 'executable',
+          'sources': [
+            'test/layout_common_table_test.cc',
+          ],
+          'dependencies': [
+            'ots',
+          ],
+          'libraries': [
+            '-lgtest',
+            '-lgtest_main',
+          ],
+          'include_dirs': [
+            'src',
+          ],
+        },
+        {
+          'target_name': 'table_dependencies_test',
+          'type': 'executable',
+          'sources': [
+            'test/table_dependencies_test.cc',
+          ],
+          'dependencies': [
+            'ots',
+          ],
+          'libraries': [
+            '-lgtest',
+            '-lgtest_main',
+          ],
+          'include_dirs': [
+            'src',
+          ],
+        },
+      ],
+    }],
+    ['OS=="linux"', {
+      'targets': [
+        {
+          'target_name': 'side_by_side',
+          'type': 'executable',
+          'sources': [
+            'test/side-by-side.cc',
+          ],
+          'dependencies': [
+            'freetype2',
+            'ots',
+          ],
+        },
+      ],
+    }],
+  ],
+}
diff --git a/third_party/ots/ots.gyp b/third_party/ots/ots.gyp
new file mode 100644
index 0000000..288e41c
--- /dev/null
+++ b/third_party/ots/ots.gyp
@@ -0,0 +1,39 @@
+# Copyright (c) 2009 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+  'variables': {
+    'chromium_code': 1,
+  },
+  'includes': [
+    'ots-common.gypi',
+  ],
+  'targets': [
+    {
+      'target_name': 'ots',
+      'type': 'static_library',
+      'sources': [
+        '<@(ots_sources)',
+      ],
+      'include_dirs': [
+        '../..',
+        '<@(ots_include_dirs)',
+      ],
+      'direct_dependent_settings': {
+        'include_dirs': [
+          '<@(ots_include_dirs)',
+        ],
+      },
+      'dependencies': [
+        '../brotli/brotli.gyp:brotli',
+        '../zlib/zlib.gyp:zlib',
+      ],
+      # TODO(jschuh): http://crbug.com/167187
+      'msvs_disabled_warnings': [
+        4267,
+        4334,
+      ],      
+    },
+  ],
+}
diff --git a/third_party/ots/src/cff.cc b/third_party/ots/src/cff.cc
new file mode 100644
index 0000000..9c7204d9
--- /dev/null
+++ b/third_party/ots/src/cff.cc
@@ -0,0 +1,1041 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cff.h"
+
+#include <cstring>
+#include <utility>
+#include <vector>
+
+#include "maxp.h"
+#include "cff_type2_charstring.h"
+
+// CFF - PostScript font program (Compact Font Format) table
+// http://www.microsoft.com/typography/otspec/cff.htm
+// http://www.microsoft.com/typography/otspec/cffspec.htm
+
+#define TABLE_NAME "CFF"
+
+namespace {
+
+enum DICT_OPERAND_TYPE {
+  DICT_OPERAND_INTEGER,
+  DICT_OPERAND_REAL,
+  DICT_OPERATOR,
+};
+
+enum DICT_DATA_TYPE {
+  DICT_DATA_TOPLEVEL,
+  DICT_DATA_FDARRAY,
+};
+
+enum FONT_FORMAT {
+  FORMAT_UNKNOWN,
+  FORMAT_CID_KEYED,
+  FORMAT_OTHER,  // Including synthetic fonts
+};
+
+// see Appendix. A
+const size_t kNStdString = 390;
+
+bool ReadOffset(ots::Buffer *table, uint8_t off_size, uint32_t *offset) {
+  if (off_size > 4) {
+    return OTS_FAILURE();
+  }
+
+  uint32_t tmp32 = 0;
+  for (unsigned i = 0; i < off_size; ++i) {
+    uint8_t tmp8 = 0;
+    if (!table->ReadU8(&tmp8)) {
+      return OTS_FAILURE();
+    }
+    tmp32 <<= 8;
+    tmp32 += tmp8;
+  }
+  *offset = tmp32;
+  return true;
+}
+
+bool ParseIndex(ots::Buffer *table, ots::CFFIndex *index) {
+  index->off_size = 0;
+  index->offsets.clear();
+
+  if (!table->ReadU16(&(index->count))) {
+    return OTS_FAILURE();
+  }
+  if (index->count == 0) {
+    // An empty INDEX.
+    index->offset_to_next = table->offset();
+    return true;
+  }
+
+  if (!table->ReadU8(&(index->off_size))) {
+    return OTS_FAILURE();
+  }
+  if ((index->off_size == 0) ||
+      (index->off_size > 4)) {
+    return OTS_FAILURE();
+  }
+
+  const size_t array_size = (index->count + 1) * index->off_size;
+  // less than ((64k + 1) * 4), thus does not overflow.
+  const size_t object_data_offset = table->offset() + array_size;
+  // does not overflow too, since offset() <= 1GB.
+
+  if (object_data_offset >= table->length()) {
+    return OTS_FAILURE();
+  }
+
+  for (unsigned i = 0; i <= index->count; ++i) {  // '<=' is not a typo.
+    uint32_t rel_offset = 0;
+    if (!ReadOffset(table, index->off_size, &rel_offset)) {
+      return OTS_FAILURE();
+    }
+    if (rel_offset < 1) {
+      return OTS_FAILURE();
+    }
+    if (i == 0 && rel_offset != 1) {
+      return OTS_FAILURE();
+    }
+
+    if (rel_offset > table->length()) {
+      return OTS_FAILURE();
+    }
+
+    // does not underflow.
+    if (object_data_offset > table->length() - (rel_offset - 1)) {
+      return OTS_FAILURE();
+    }
+
+    index->offsets.push_back(
+        object_data_offset + (rel_offset - 1));  // less than length(), 1GB.
+  }
+
+  for (unsigned i = 1; i < index->offsets.size(); ++i) {
+    // We allow consecutive identical offsets here for zero-length strings.
+    // See http://crbug.com/69341 for more details.
+    if (index->offsets[i] < index->offsets[i - 1]) {
+      return OTS_FAILURE();
+    }
+  }
+
+  index->offset_to_next = index->offsets.back();
+  return true;
+}
+
+bool ParseNameData(
+    ots::Buffer *table, const ots::CFFIndex &index, std::string* out_name) {
+  uint8_t name[256] = {0};
+  if (index.offsets.size() == 0) {  // just in case.
+    return OTS_FAILURE();
+  }
+  for (unsigned i = 1; i < index.offsets.size(); ++i) {
+    const size_t length = index.offsets[i] - index.offsets[i - 1];
+    // font names should be no longer than 127 characters.
+    if (length > 127) {
+      return OTS_FAILURE();
+    }
+
+    table->set_offset(index.offsets[i - 1]);
+    if (!table->Read(name, length)) {
+      return OTS_FAILURE();
+    }
+
+    for (size_t j = 0; j < length; ++j) {
+      // setting the first byte to NUL is allowed.
+      if (j == 0 && name[j] == 0) continue;
+      // non-ASCII characters are not recommended (except the first character).
+      if (name[j] < 33 || name[j] > 126) {
+        return OTS_FAILURE();
+      }
+      // [, ], ... are not allowed.
+      if (std::strchr("[](){}<>/% ", name[j])) {
+        return OTS_FAILURE();
+      }
+    }
+  }
+
+  *out_name = reinterpret_cast<char *>(name);
+  return true;
+}
+
+bool CheckOffset(const std::pair<uint32_t, DICT_OPERAND_TYPE>& operand,
+                 size_t table_length) {
+  if (operand.second != DICT_OPERAND_INTEGER) {
+    return OTS_FAILURE();
+  }
+  if (operand.first >= table_length) {
+    return OTS_FAILURE();
+  }
+  return true;
+}
+
+bool CheckSid(const std::pair<uint32_t, DICT_OPERAND_TYPE>& operand,
+              size_t sid_max) {
+  if (operand.second != DICT_OPERAND_INTEGER) {
+    return OTS_FAILURE();
+  }
+  if (operand.first > sid_max) {
+    return OTS_FAILURE();
+  }
+  return true;
+}
+
+bool ParseDictDataBcd(
+    ots::Buffer *table,
+    std::vector<std::pair<uint32_t, DICT_OPERAND_TYPE> > *operands) {
+  bool read_decimal_point = false;
+  bool read_e = false;
+
+  uint8_t nibble = 0;
+  size_t count = 0;
+  while (true) {
+    if (!table->ReadU8(&nibble)) {
+      return OTS_FAILURE();
+    }
+    if ((nibble & 0xf0) == 0xf0) {
+      if ((nibble & 0xf) == 0xf) {
+        // TODO(yusukes): would be better to store actual double value,
+        // rather than the dummy integer.
+        operands->push_back(std::make_pair(static_cast<uint32_t>(0),
+                                           DICT_OPERAND_REAL));
+        return true;
+      }
+      return OTS_FAILURE();
+    }
+    if ((nibble & 0x0f) == 0x0f) {
+      operands->push_back(std::make_pair(static_cast<uint32_t>(0),
+                                         DICT_OPERAND_REAL));
+      return true;
+    }
+
+    // check number format
+    uint8_t nibbles[2];
+    nibbles[0] = (nibble & 0xf0) >> 8;
+    nibbles[1] = (nibble & 0x0f);
+    for (unsigned i = 0; i < 2; ++i) {
+      if (nibbles[i] == 0xd) {  // reserved number
+        return OTS_FAILURE();
+      }
+      if ((nibbles[i] == 0xe) &&  // minus
+          ((count > 0) || (i > 0))) {
+        return OTS_FAILURE();  // minus sign should be the first character.
+      }
+      if (nibbles[i] == 0xa) {  // decimal point
+        if (!read_decimal_point) {
+          read_decimal_point = true;
+        } else {
+          return OTS_FAILURE();  // two or more points.
+        }
+      }
+      if ((nibbles[i] == 0xb) ||  // E+
+          (nibbles[i] == 0xc)) {  // E-
+        if (!read_e) {
+          read_e = true;
+        } else {
+          return OTS_FAILURE();  // two or more E's.
+        }
+      }
+    }
+    ++count;
+  }
+}
+
+bool ParseDictDataEscapedOperator(
+    ots::Buffer *table,
+    std::vector<std::pair<uint32_t, DICT_OPERAND_TYPE> > *operands) {
+  uint8_t op = 0;
+  if (!table->ReadU8(&op)) {
+    return OTS_FAILURE();
+  }
+
+  if ((op <= 14) ||
+      (op >= 17 && op <= 23) ||
+      (op >= 30 && op <= 38)) {
+    operands->push_back(std::make_pair((12U << 8) + op, DICT_OPERATOR));
+    return true;
+  }
+
+  // reserved area.
+  return OTS_FAILURE();
+}
+
+bool ParseDictDataNumber(
+    ots::Buffer *table, uint8_t b0,
+    std::vector<std::pair<uint32_t, DICT_OPERAND_TYPE> > *operands) {
+  uint8_t b1 = 0;
+  uint8_t b2 = 0;
+  uint8_t b3 = 0;
+  uint8_t b4 = 0;
+
+  switch (b0) {
+    case 28:  // shortint
+      if (!table->ReadU8(&b1) ||
+          !table->ReadU8(&b2)) {
+        return OTS_FAILURE();
+      }
+      operands->push_back(std::make_pair(
+          static_cast<uint32_t>((b1 << 8) + b2), DICT_OPERAND_INTEGER));
+      return true;
+
+    case 29:  // longint
+      if (!table->ReadU8(&b1) ||
+          !table->ReadU8(&b2) ||
+          !table->ReadU8(&b3) ||
+          !table->ReadU8(&b4)) {
+        return OTS_FAILURE();
+      }
+      operands->push_back(std::make_pair(
+          static_cast<uint32_t>((b1 << 24) + (b2 << 16) + (b3 << 8) + b4),
+          DICT_OPERAND_INTEGER));
+      return true;
+
+    case 30:  // binary coded decimal
+      return ParseDictDataBcd(table, operands);
+
+    default:
+      break;
+  }
+
+  uint32_t result;
+  if (b0 >=32 && b0 <=246) {
+    result = b0 - 139;
+  } else if (b0 >=247 && b0 <= 250) {
+    if (!table->ReadU8(&b1)) {
+      return OTS_FAILURE();
+    }
+    result = (b0 - 247) * 256 + b1 + 108;
+  } else if (b0 >= 251 && b0 <= 254) {
+    if (!table->ReadU8(&b1)) {
+      return OTS_FAILURE();
+    }
+    result = -(b0 - 251) * 256 + b1 - 108;
+  } else {
+    return OTS_FAILURE();
+  }
+
+  operands->push_back(std::make_pair(result, DICT_OPERAND_INTEGER));
+  return true;
+}
+
+bool ParseDictDataReadNext(
+    ots::Buffer *table,
+    std::vector<std::pair<uint32_t, DICT_OPERAND_TYPE> > *operands) {
+  uint8_t op = 0;
+  if (!table->ReadU8(&op)) {
+    return OTS_FAILURE();
+  }
+  if (op <= 21) {
+    if (op == 12) {
+      return ParseDictDataEscapedOperator(table, operands);
+    }
+    operands->push_back(std::make_pair(
+        static_cast<uint32_t>(op), DICT_OPERATOR));
+    return true;
+  } else if (op <= 27 || op == 31 || op == 255) {
+    // reserved area.
+    return OTS_FAILURE();
+  }
+
+  return ParseDictDataNumber(table, op, operands);
+}
+
+bool ParsePrivateDictData(
+    const uint8_t *data,
+    size_t table_length, size_t offset, size_t dict_length,
+    DICT_DATA_TYPE type, ots::OpenTypeCFF *out_cff) {
+  ots::Buffer table(data + offset, dict_length);
+  std::vector<std::pair<uint32_t, DICT_OPERAND_TYPE> > operands;
+
+  // Since a Private DICT for FDArray might not have a Local Subr (e.g. Hiragino
+  // Kaku Gothic Std W8), we create an empty Local Subr here to match the size
+  // of FDArray the size of |local_subrs_per_font|.
+  if (type == DICT_DATA_FDARRAY) {
+    out_cff->local_subrs_per_font.push_back(new ots::CFFIndex);
+  }
+
+  while (table.offset() < dict_length) {
+    if (!ParseDictDataReadNext(&table, &operands)) {
+      return OTS_FAILURE();
+    }
+    if (operands.empty()) {
+      return OTS_FAILURE();
+    }
+    if (operands.size() > 48) {
+      // An operator may be preceded by up to a maximum of 48 operands.
+      return OTS_FAILURE();
+    }
+    if (operands.back().second != DICT_OPERATOR) {
+      continue;
+    }
+
+    // got operator
+    const uint32_t op = operands.back().first;
+    operands.pop_back();
+
+    switch (op) {
+      // array
+      case 6:  // BlueValues
+      case 7:  // OtherBlues
+      case 8:  // FamilyBlues
+      case 9:  // FamilyOtherBlues
+      case (12U << 8) + 12:  // StemSnapH (delta)
+      case (12U << 8) + 13:  // StemSnapV (delta)
+        if (operands.empty()) {
+          return OTS_FAILURE();
+        }
+        break;
+
+      // number
+      case 10:  // StdHW
+      case 11:  // StdVW
+      case 20:  // defaultWidthX
+      case 21:  // nominalWidthX
+      case (12U << 8) + 9:   // BlueScale
+      case (12U << 8) + 10:  // BlueShift
+      case (12U << 8) + 11:  // BlueFuzz
+      case (12U << 8) + 17:  // LanguageGroup
+      case (12U << 8) + 18:  // ExpansionFactor
+      case (12U << 8) + 19:  // initialRandomSeed
+        if (operands.size() != 1) {
+          return OTS_FAILURE();
+        }
+        break;
+
+      // Local Subrs INDEX, offset(self)
+      case 19: {
+        if (operands.size() != 1) {
+          return OTS_FAILURE();
+        }
+        if (operands.back().second != DICT_OPERAND_INTEGER) {
+          return OTS_FAILURE();
+        }
+        if (operands.back().first >= 1024 * 1024 * 1024) {
+          return OTS_FAILURE();
+        }
+        if (operands.back().first + offset >= table_length) {
+          return OTS_FAILURE();
+        }
+        // parse "16. Local Subrs INDEX"
+        ots::Buffer cff_table(data, table_length);
+        cff_table.set_offset(operands.back().first + offset);
+        ots::CFFIndex *local_subrs_index = NULL;
+        if (type == DICT_DATA_FDARRAY) {
+          if (out_cff->local_subrs_per_font.empty()) {
+            return OTS_FAILURE();  // not reached.
+          }
+          local_subrs_index = out_cff->local_subrs_per_font.back();
+        } else { // type == DICT_DATA_TOPLEVEL
+          if (out_cff->local_subrs) {
+            return OTS_FAILURE();  // two or more local_subrs?
+          }
+          local_subrs_index = new ots::CFFIndex;
+          out_cff->local_subrs = local_subrs_index;
+        }
+        if (!ParseIndex(&cff_table, local_subrs_index)) {
+          return OTS_FAILURE();
+        }
+        break;
+      }
+
+      // boolean
+      case (12U << 8) + 14:  // ForceBold
+        if (operands.size() != 1) {
+          return OTS_FAILURE();
+        }
+        if (operands.back().second != DICT_OPERAND_INTEGER) {
+          return OTS_FAILURE();
+        }
+        if (operands.back().first >= 2) {
+          return OTS_FAILURE();
+        }
+        break;
+
+      default:
+        return OTS_FAILURE();
+    }
+    operands.clear();
+  }
+
+  return true;
+}
+
+bool ParseDictData(const uint8_t *data, size_t table_length,
+                   const ots::CFFIndex &index, uint16_t glyphs,
+                   size_t sid_max, DICT_DATA_TYPE type,
+                   ots::OpenTypeCFF *out_cff) {
+  for (unsigned i = 1; i < index.offsets.size(); ++i) {
+    if (type == DICT_DATA_TOPLEVEL) {
+      out_cff->char_strings_array.push_back(new ots::CFFIndex);
+    }
+    size_t dict_length = index.offsets[i] - index.offsets[i - 1];
+    ots::Buffer table(data + index.offsets[i - 1], dict_length);
+
+    std::vector<std::pair<uint32_t, DICT_OPERAND_TYPE> > operands;
+
+    FONT_FORMAT font_format = FORMAT_UNKNOWN;
+    bool have_ros = false;
+    uint16_t charstring_glyphs = 0;
+    size_t charset_offset = 0;
+
+    while (table.offset() < dict_length) {
+      if (!ParseDictDataReadNext(&table, &operands)) {
+        return OTS_FAILURE();
+      }
+      if (operands.empty()) {
+        return OTS_FAILURE();
+      }
+      if (operands.size() > 48) {
+        // An operator may be preceded by up to a maximum of 48 operands.
+        return OTS_FAILURE();
+      }
+      if (operands.back().second != DICT_OPERATOR) continue;
+
+      // got operator
+      const uint32_t op = operands.back().first;
+      operands.pop_back();
+
+      switch (op) {
+        // SID
+        case 0:   // version
+        case 1:   // Notice
+        case 2:   // Copyright
+        case 3:   // FullName
+        case 4:   // FamilyName
+        case (12U << 8) + 0:   // Copyright
+        case (12U << 8) + 21:  // PostScript
+        case (12U << 8) + 22:  // BaseFontName
+        case (12U << 8) + 38:  // FontName
+          if (operands.size() != 1) {
+            return OTS_FAILURE();
+          }
+          if (!CheckSid(operands.back(), sid_max)) {
+            return OTS_FAILURE();
+          }
+          break;
+
+        // array
+        case 5:   // FontBBox
+        case 14:  // XUID
+        case (12U << 8) + 7:   // FontMatrix
+        case (12U << 8) + 23:  // BaseFontBlend (delta)
+          if (operands.empty()) {
+            return OTS_FAILURE();
+          }
+          break;
+
+        // number
+        case 13:  // UniqueID
+        case (12U << 8) + 2:   // ItalicAngle
+        case (12U << 8) + 3:   // UnderlinePosition
+        case (12U << 8) + 4:   // UnderlineThickness
+        case (12U << 8) + 5:   // PaintType
+        case (12U << 8) + 8:   // StrokeWidth
+        case (12U << 8) + 20:  // SyntheticBase
+          if (operands.size() != 1) {
+            return OTS_FAILURE();
+          }
+          break;
+        case (12U << 8) + 31:  // CIDFontVersion
+        case (12U << 8) + 32:  // CIDFontRevision
+        case (12U << 8) + 33:  // CIDFontType
+        case (12U << 8) + 34:  // CIDCount
+        case (12U << 8) + 35:  // UIDBase
+          if (operands.size() != 1) {
+            return OTS_FAILURE();
+          }
+          if (font_format != FORMAT_CID_KEYED) {
+            return OTS_FAILURE();
+          }
+          break;
+        case (12U << 8) + 6:   // CharstringType
+          if (operands.size() != 1) {
+            return OTS_FAILURE();
+          }
+          if(operands.back().second != DICT_OPERAND_INTEGER) {
+            return OTS_FAILURE();
+          }
+          if (operands.back().first != 2) {
+            // We only support the "Type 2 Charstring Format."
+            // TODO(yusukes): Support Type 1 format? Is that still in use?
+            return OTS_FAILURE();
+          }
+          break;
+
+        // boolean
+        case (12U << 8) + 1:   // isFixedPitch
+          if (operands.size() != 1) {
+            return OTS_FAILURE();
+          }
+          if (operands.back().second != DICT_OPERAND_INTEGER) {
+            return OTS_FAILURE();
+          }
+          if (operands.back().first >= 2) {
+            return OTS_FAILURE();
+          }
+          break;
+
+        // offset(0)
+        case 15:  // charset
+          if (operands.size() != 1) {
+            return OTS_FAILURE();
+          }
+          if (operands.back().first <= 2) {
+            // predefined charset, ISOAdobe, Expert or ExpertSubset, is used.
+            break;
+          }
+          if (!CheckOffset(operands.back(), table_length)) {
+            return OTS_FAILURE();
+          }
+          if (charset_offset) {
+            return OTS_FAILURE();  // multiple charset tables?
+          }
+          charset_offset = operands.back().first;
+          break;
+
+        case 16: {  // Encoding
+          if (operands.size() != 1) {
+            return OTS_FAILURE();
+          }
+          if (operands.back().first <= 1) {
+            break;  // predefined encoding, "Standard" or "Expert", is used.
+          }
+          if (!CheckOffset(operands.back(), table_length)) {
+            return OTS_FAILURE();
+          }
+
+          // parse sub dictionary INDEX.
+          ots::Buffer cff_table(data, table_length);
+          cff_table.set_offset(operands.back().first);
+          uint8_t format = 0;
+          if (!cff_table.ReadU8(&format)) {
+            return OTS_FAILURE();
+          }
+          if (format & 0x80) {
+            // supplemental encoding is not supported at the moment.
+            return OTS_FAILURE();
+          }
+          // TODO(yusukes): support & parse supplemental encoding tables.
+          break;
+        }
+
+        case 17: {  // CharStrings
+          if (type != DICT_DATA_TOPLEVEL) {
+            return OTS_FAILURE();
+          }
+          if (operands.size() != 1) {
+            return OTS_FAILURE();
+          }
+          if (!CheckOffset(operands.back(), table_length)) {
+            return OTS_FAILURE();
+          }
+          // parse "14. CharStrings INDEX"
+          ots::Buffer cff_table(data, table_length);
+          cff_table.set_offset(operands.back().first);
+          ots::CFFIndex *charstring_index = out_cff->char_strings_array.back();
+          if (!ParseIndex(&cff_table, charstring_index)) {
+            return OTS_FAILURE();
+          }
+          if (charstring_index->count < 2) {
+            return OTS_FAILURE();
+          }
+          if (charstring_glyphs) {
+            return OTS_FAILURE();  // multiple charstring tables?
+          }
+          charstring_glyphs = charstring_index->count;
+          if (charstring_glyphs != glyphs) {
+            return OTS_FAILURE();  // CFF and maxp have different number of glyphs?
+          }
+          break;
+        }
+
+        case (12U << 8) + 36: {  // FDArray
+          if (type != DICT_DATA_TOPLEVEL) {
+            return OTS_FAILURE();
+          }
+          if (operands.size() != 1) {
+            return OTS_FAILURE();
+          }
+          if (!CheckOffset(operands.back(), table_length)) {
+            return OTS_FAILURE();
+          }
+
+          // parse sub dictionary INDEX.
+          ots::Buffer cff_table(data, table_length);
+          cff_table.set_offset(operands.back().first);
+          ots::CFFIndex sub_dict_index;
+          if (!ParseIndex(&cff_table, &sub_dict_index)) {
+            return OTS_FAILURE();
+          }
+          if (!ParseDictData(data, table_length,
+                             sub_dict_index,
+                             glyphs, sid_max, DICT_DATA_FDARRAY,
+                             out_cff)) {
+            return OTS_FAILURE();
+          }
+          if (out_cff->font_dict_length != 0) {
+            return OTS_FAILURE();  // two or more FDArray found.
+          }
+          out_cff->font_dict_length = sub_dict_index.count;
+          break;
+        }
+
+        case (12U << 8) + 37: {  // FDSelect
+          if (type != DICT_DATA_TOPLEVEL) {
+            return OTS_FAILURE();
+          }
+          if (operands.size() != 1) {
+            return OTS_FAILURE();
+          }
+          if (!CheckOffset(operands.back(), table_length)) {
+            return OTS_FAILURE();
+          }
+
+          // parse FDSelect data structure
+          ots::Buffer cff_table(data, table_length);
+          cff_table.set_offset(operands.back().first);
+          uint8_t format = 0;
+          if (!cff_table.ReadU8(&format)) {
+            return OTS_FAILURE();
+          }
+          if (format == 0) {
+            for (uint16_t j = 0; j < glyphs; ++j) {
+              uint8_t fd_index = 0;
+              if (!cff_table.ReadU8(&fd_index)) {
+                return OTS_FAILURE();
+              }
+              (out_cff->fd_select)[j] = fd_index;
+            }
+          } else if (format == 3) {
+            uint16_t n_ranges = 0;
+            if (!cff_table.ReadU16(&n_ranges)) {
+              return OTS_FAILURE();
+            }
+            if (n_ranges == 0) {
+              return OTS_FAILURE();
+            }
+
+            uint16_t last_gid = 0;
+            uint8_t fd_index = 0;
+            for (unsigned j = 0; j < n_ranges; ++j) {
+              uint16_t first = 0;  // GID
+              if (!cff_table.ReadU16(&first)) {
+                return OTS_FAILURE();
+              }
+
+              // Sanity checks.
+              if ((j == 0) && (first != 0)) {
+                return OTS_FAILURE();
+              }
+              if ((j != 0) && (last_gid >= first)) {
+                return OTS_FAILURE();  // not increasing order.
+              }
+
+              // Copy the mapping to |out_cff->fd_select|.
+              if (j != 0) {
+                for (uint16_t k = last_gid; k < first; ++k) {
+                  if (!out_cff->fd_select.insert(
+                          std::make_pair(k, fd_index)).second) {
+                    return OTS_FAILURE();
+                  }
+                }
+              }
+
+              if (!cff_table.ReadU8(&fd_index)) {
+                return OTS_FAILURE();
+              }
+              last_gid = first;
+              // TODO(yusukes): check GID?
+            }
+            uint16_t sentinel = 0;
+            if (!cff_table.ReadU16(&sentinel)) {
+              return OTS_FAILURE();
+            }
+            if (last_gid >= sentinel) {
+              return OTS_FAILURE();
+            }
+            for (uint16_t k = last_gid; k < sentinel; ++k) {
+              if (!out_cff->fd_select.insert(
+                      std::make_pair(k, fd_index)).second) {
+                return OTS_FAILURE();
+              }
+            }
+          } else {
+            // unknown format
+            return OTS_FAILURE();
+          }
+          break;
+        }
+
+        // Private DICT (2 * number)
+        case 18: {
+          if (operands.size() != 2) {
+            return OTS_FAILURE();
+          }
+          if (operands.back().second != DICT_OPERAND_INTEGER) {
+            return OTS_FAILURE();
+          }
+          const uint32_t private_offset = operands.back().first;
+          operands.pop_back();
+          if (operands.back().second != DICT_OPERAND_INTEGER) {
+            return OTS_FAILURE();
+          }
+          const uint32_t private_length = operands.back().first;
+          if (private_offset > table_length) {
+            return OTS_FAILURE();
+          }
+          if (private_length >= table_length) {
+            return OTS_FAILURE();
+          }
+          if (private_length + private_offset > table_length) {
+            return OTS_FAILURE();
+          }
+          // parse "15. Private DICT Data"
+          if (!ParsePrivateDictData(data, table_length,
+                                    private_offset, private_length,
+                                    type, out_cff)) {
+            return OTS_FAILURE();
+          }
+          break;
+        }
+
+        // ROS
+        case (12U << 8) + 30:
+          if (font_format != FORMAT_UNKNOWN) {
+            return OTS_FAILURE();
+          }
+          font_format = FORMAT_CID_KEYED;
+          if (operands.size() != 3) {
+            return OTS_FAILURE();
+          }
+          // check SIDs
+          operands.pop_back();  // ignore the first number.
+          if (!CheckSid(operands.back(), sid_max)) {
+            return OTS_FAILURE();
+          }
+          operands.pop_back();
+          if (!CheckSid(operands.back(), sid_max)) {
+            return OTS_FAILURE();
+          }
+          if (have_ros) {
+            return OTS_FAILURE();  // multiple ROS tables?
+          }
+          have_ros = true;
+          break;
+
+        default:
+          return OTS_FAILURE();
+      }
+      operands.clear();
+
+      if (font_format == FORMAT_UNKNOWN) {
+        font_format = FORMAT_OTHER;
+      }
+    }
+
+    // parse "13. Charsets"
+    if (charset_offset) {
+      ots::Buffer cff_table(data, table_length);
+      cff_table.set_offset(charset_offset);
+      uint8_t format = 0;
+      if (!cff_table.ReadU8(&format)) {
+        return OTS_FAILURE();
+      }
+      switch (format) {
+        case 0:
+          for (uint16_t j = 1 /* .notdef is omitted */; j < glyphs; ++j) {
+            uint16_t sid = 0;
+            if (!cff_table.ReadU16(&sid)) {
+              return OTS_FAILURE();
+            }
+            if (!have_ros && (sid > sid_max)) {
+              return OTS_FAILURE();
+            }
+            // TODO(yusukes): check CIDs when have_ros is true.
+          }
+          break;
+
+        case 1:
+        case 2: {
+          uint32_t total = 1;  // .notdef is omitted.
+          while (total < glyphs) {
+            uint16_t sid = 0;
+            if (!cff_table.ReadU16(&sid)) {
+              return OTS_FAILURE();
+            }
+            if (!have_ros && (sid > sid_max)) {
+              return OTS_FAILURE();
+            }
+            // TODO(yusukes): check CIDs when have_ros is true.
+
+            if (format == 1) {
+              uint8_t left = 0;
+              if (!cff_table.ReadU8(&left)) {
+                return OTS_FAILURE();
+              }
+              total += (left + 1);
+            } else {
+              uint16_t left = 0;
+              if (!cff_table.ReadU16(&left)) {
+                return OTS_FAILURE();
+              }
+              total += (left + 1);
+            }
+          }
+          break;
+        }
+
+        default:
+          return OTS_FAILURE();
+      }
+    }
+  }
+  return true;
+}
+
+}  // namespace
+
+namespace ots {
+
+bool ots_cff_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
+  Buffer table(data, length);
+
+  file->cff = new OpenTypeCFF;
+  file->cff->data = data;
+  file->cff->length = length;
+  file->cff->font_dict_length = 0;
+  file->cff->local_subrs = NULL;
+
+  // parse "6. Header" in the Adobe Compact Font Format Specification
+  uint8_t major = 0;
+  uint8_t minor = 0;
+  uint8_t hdr_size = 0;
+  uint8_t off_size = 0;
+  if (!table.ReadU8(&major)) {
+    return OTS_FAILURE();
+  }
+  if (!table.ReadU8(&minor)) {
+    return OTS_FAILURE();
+  }
+  if (!table.ReadU8(&hdr_size)) {
+    return OTS_FAILURE();
+  }
+  if (!table.ReadU8(&off_size)) {
+    return OTS_FAILURE();
+  }
+  if ((off_size == 0) || (off_size > 4)) {
+    return OTS_FAILURE();
+  }
+
+  if ((major != 1) ||
+      (minor != 0) ||
+      (hdr_size != 4)) {
+    return OTS_FAILURE();
+  }
+  if (hdr_size >= length) {
+    return OTS_FAILURE();
+  }
+
+  // parse "7. Name INDEX"
+  table.set_offset(hdr_size);
+  CFFIndex name_index;
+  if (!ParseIndex(&table, &name_index)) {
+    return OTS_FAILURE();
+  }
+  if (!ParseNameData(&table, name_index, &(file->cff->name))) {
+    return OTS_FAILURE();
+  }
+
+  // parse "8. Top DICT INDEX"
+  table.set_offset(name_index.offset_to_next);
+  CFFIndex top_dict_index;
+  if (!ParseIndex(&table, &top_dict_index)) {
+    return OTS_FAILURE();
+  }
+  if (name_index.count != top_dict_index.count) {
+    return OTS_FAILURE();
+  }
+
+  // parse "10. String INDEX"
+  table.set_offset(top_dict_index.offset_to_next);
+  CFFIndex string_index;
+  if (!ParseIndex(&table, &string_index)) {
+    return OTS_FAILURE();
+  }
+  if (string_index.count >= 65000 - kNStdString) {
+    return OTS_FAILURE();
+  }
+
+  const uint16_t num_glyphs = file->maxp->num_glyphs;
+  const size_t sid_max = string_index.count + kNStdString;
+  // string_index.count == 0 is allowed.
+
+  // parse "9. Top DICT Data"
+  if (!ParseDictData(data, length, top_dict_index,
+                     num_glyphs, sid_max,
+                     DICT_DATA_TOPLEVEL, file->cff)) {
+    return OTS_FAILURE();
+  }
+
+  // parse "16. Global Subrs INDEX"
+  table.set_offset(string_index.offset_to_next);
+  CFFIndex global_subrs_index;
+  if (!ParseIndex(&table, &global_subrs_index)) {
+    return OTS_FAILURE();
+  }
+
+  // Check if all fd_index in FDSelect are valid.
+  std::map<uint16_t, uint8_t>::const_iterator iter;
+  std::map<uint16_t, uint8_t>::const_iterator end = file->cff->fd_select.end();
+  for (iter = file->cff->fd_select.begin(); iter != end; ++iter) {
+    if (iter->second >= file->cff->font_dict_length) {
+      return OTS_FAILURE();
+    }
+  }
+
+  // Check if all charstrings (font hinting code for each glyph) are valid.
+  for (size_t i = 0; i < file->cff->char_strings_array.size(); ++i) {
+    if (!ValidateType2CharStringIndex(file,
+                                      *(file->cff->char_strings_array.at(i)),
+                                      global_subrs_index,
+                                      file->cff->fd_select,
+                                      file->cff->local_subrs_per_font,
+                                      file->cff->local_subrs,
+                                      &table)) {
+      return OTS_FAILURE_MSG("Failed validating charstring set %d", (int) i);
+    }
+  }
+
+  return true;
+}
+
+bool ots_cff_should_serialise(OpenTypeFile *file) {
+  return file->cff != NULL;
+}
+
+bool ots_cff_serialise(OTSStream *out, OpenTypeFile *file) {
+  // TODO(yusukes): would be better to transcode the data,
+  //                rather than simple memcpy.
+  if (!out->Write(file->cff->data, file->cff->length)) {
+    return OTS_FAILURE();
+  }
+  return true;
+}
+
+void ots_cff_free(OpenTypeFile *file) {
+  if (file->cff) {
+    for (size_t i = 0; i < file->cff->char_strings_array.size(); ++i) {
+      delete (file->cff->char_strings_array)[i];
+    }
+    for (size_t i = 0; i < file->cff->local_subrs_per_font.size(); ++i) {
+      delete (file->cff->local_subrs_per_font)[i];
+    }
+    delete file->cff->local_subrs;
+    delete file->cff;
+  }
+}
+
+}  // namespace ots
+
+#undef TABLE_NAME
diff --git a/third_party/ots/src/cff.h b/third_party/ots/src/cff.h
new file mode 100644
index 0000000..5584acc
--- /dev/null
+++ b/third_party/ots/src/cff.h
@@ -0,0 +1,46 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_CFF_H_
+#define OTS_CFF_H_
+
+#include "ots.h"
+
+#include <map>
+#include <string>
+#include <vector>
+
+namespace ots {
+
+struct CFFIndex {
+  CFFIndex()
+      : count(0), off_size(0), offset_to_next(0) {}
+  uint16_t count;
+  uint8_t off_size;
+  std::vector<uint32_t> offsets;
+  uint32_t offset_to_next;
+};
+
+struct OpenTypeCFF {
+  const uint8_t *data;
+  size_t length;
+  // Name INDEX. This name is used in name.cc as a postscript font name.
+  std::string name;
+
+  // The number of fonts the file has.
+  size_t font_dict_length;
+  // A map from glyph # to font #.
+  std::map<uint16_t, uint8_t> fd_select;
+
+  // A list of char strings.
+  std::vector<CFFIndex *> char_strings_array;
+  // A list of Local Subrs associated with FDArrays. Can be empty.
+  std::vector<CFFIndex *> local_subrs_per_font;
+  // A Local Subrs associated with Top DICT. Can be NULL.
+  CFFIndex *local_subrs;
+};
+
+}  // namespace ots
+
+#endif  // OTS_CFF_H_
diff --git a/third_party/ots/src/cff_type2_charstring.cc b/third_party/ots/src/cff_type2_charstring.cc
new file mode 100644
index 0000000..6dd4766
--- /dev/null
+++ b/third_party/ots/src/cff_type2_charstring.cc
@@ -0,0 +1,914 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// A parser for the Type 2 Charstring Format.
+// http://www.adobe.com/devnet/font/pdfs/5177.Type2.pdf
+
+#include "cff_type2_charstring.h"
+
+#include <climits>
+#include <cstdio>
+#include <cstring>
+#include <stack>
+#include <string>
+#include <utility>
+
+#define TABLE_NAME "CFF"
+
+namespace {
+
+// Type 2 Charstring Implementation Limits. See Appendix. B in Adobe Technical
+// Note #5177.
+const int32_t kMaxSubrsCount = 65536;
+const size_t kMaxCharStringLength = 65535;
+const size_t kMaxArgumentStack = 48;
+const size_t kMaxNumberOfStemHints = 96;
+const size_t kMaxSubrNesting = 10;
+
+// |dummy_result| should be a huge positive integer so callsubr and callgsubr
+// will fail with the dummy value.
+const int32_t dummy_result = INT_MAX;
+
+bool ExecuteType2CharString(ots::OpenTypeFile *file,
+                            size_t call_depth,
+                            const ots::CFFIndex& global_subrs_index,
+                            const ots::CFFIndex& local_subrs_index,
+                            ots::Buffer *cff_table,
+                            ots::Buffer *char_string,
+                            std::stack<int32_t> *argument_stack,
+                            bool *out_found_endchar,
+                            bool *out_found_width,
+                            size_t *in_out_num_stems);
+
+#ifdef DUMP_T2CHARSTRING
+// Converts |op| to a string and returns it.
+const char *Type2CharStringOperatorToString(ots::Type2CharStringOperator op) {
+  switch (op) {
+  case ots::kHStem:
+    return "HStem";
+  case ots::kVStem:
+    return "VStem";
+  case ots::kVMoveTo:
+    return "VMoveTo";
+  case ots::kRLineTo:
+    return "RLineTo";
+  case ots::kHLineTo:
+    return "HLineTo";
+  case ots::kVLineTo:
+    return "VLineTo";
+  case ots::kRRCurveTo:
+    return "RRCurveTo";
+  case ots::kCallSubr:
+    return "CallSubr";
+  case ots::kReturn:
+    return "Return";
+  case ots::kEndChar:
+    return "EndChar";
+  case ots::kHStemHm:
+    return "HStemHm";
+  case ots::kHintMask:
+    return "HintMask";
+  case ots::kCntrMask:
+    return "CntrMask";
+  case ots::kRMoveTo:
+    return "RMoveTo";
+  case ots::kHMoveTo:
+    return "HMoveTo";
+  case ots::kVStemHm:
+    return "VStemHm";
+  case ots::kRCurveLine:
+    return "RCurveLine";
+  case ots::kRLineCurve:
+    return "RLineCurve";
+  case ots::kVVCurveTo:
+    return "VVCurveTo";
+  case ots::kHHCurveTo:
+    return "HHCurveTo";
+  case ots::kCallGSubr:
+    return "CallGSubr";
+  case ots::kVHCurveTo:
+    return "VHCurveTo";
+  case ots::kHVCurveTo:
+    return "HVCurveTo";
+  case ots::kDotSection:
+    return "DotSection";
+  case ots::kAnd:
+    return "And";
+  case ots::kOr:
+    return "Or";
+  case ots::kNot:
+    return "Not";
+  case ots::kAbs:
+    return "Abs";
+  case ots::kAdd:
+    return "Add";
+  case ots::kSub:
+    return "Sub";
+  case ots::kDiv:
+    return "Div";
+  case ots::kNeg:
+    return "Neg";
+  case ots::kEq:
+    return "Eq";
+  case ots::kDrop:
+    return "Drop";
+  case ots::kPut:
+    return "Put";
+  case ots::kGet:
+    return "Get";
+  case ots::kIfElse:
+    return "IfElse";
+  case ots::kRandom:
+    return "Random";
+  case ots::kMul:
+    return "Mul";
+  case ots::kSqrt:
+    return "Sqrt";
+  case ots::kDup:
+    return "Dup";
+  case ots::kExch:
+    return "Exch";
+  case ots::kIndex:
+    return "Index";
+  case ots::kRoll:
+    return "Roll";
+  case ots::kHFlex:
+    return "HFlex";
+  case ots::kFlex:
+    return "Flex";
+  case ots::kHFlex1:
+    return "HFlex1";
+  case ots::kFlex1:
+    return "Flex1";
+  }
+
+  return "UNKNOWN";
+}
+#endif
+
+// Read one or more bytes from the |char_string| buffer and stores the number
+// read on |out_number|. If the number read is an operator (ex 'vstem'), sets
+// true on |out_is_operator|. Returns true if the function read a number.
+bool ReadNextNumberFromType2CharString(ots::Buffer *char_string,
+                                       int32_t *out_number,
+                                       bool *out_is_operator) {
+  uint8_t v = 0;
+  if (!char_string->ReadU8(&v)) {
+    return OTS_FAILURE();
+  }
+  *out_is_operator = false;
+
+  // The conversion algorithm is described in Adobe Technical Note #5177, page
+  // 13, Table 1.
+  if (v <= 11) {
+    *out_number = v;
+    *out_is_operator = true;
+  } else if (v == 12) {
+    uint16_t result = (v << 8);
+    if (!char_string->ReadU8(&v)) {
+      return OTS_FAILURE();
+    }
+    result += v;
+    *out_number = result;
+    *out_is_operator = true;
+  } else if (v <= 27) {
+    // Special handling for v==19 and v==20 are implemented in
+    // ExecuteType2CharStringOperator().
+    *out_number = v;
+    *out_is_operator = true;
+  } else if (v == 28) {
+    if (!char_string->ReadU8(&v)) {
+      return OTS_FAILURE();
+    }
+    uint16_t result = (v << 8);
+    if (!char_string->ReadU8(&v)) {
+      return OTS_FAILURE();
+    }
+    result += v;
+    *out_number = result;
+  } else if (v <= 31) {
+    *out_number = v;
+    *out_is_operator = true;
+  } else if (v <= 246) {
+    *out_number = static_cast<int32_t>(v) - 139;
+  } else if (v <= 250) {
+    uint8_t w = 0;
+    if (!char_string->ReadU8(&w)) {
+      return OTS_FAILURE();
+    }
+    *out_number = ((static_cast<int32_t>(v) - 247) * 256) +
+        static_cast<int32_t>(w) + 108;
+  } else if (v <= 254) {
+    uint8_t w = 0;
+    if (!char_string->ReadU8(&w)) {
+      return OTS_FAILURE();
+    }
+    *out_number = -((static_cast<int32_t>(v) - 251) * 256) -
+        static_cast<int32_t>(w) - 108;
+  } else if (v == 255) {
+    // TODO(yusukes): We should not skip the 4 bytes. Note that when v is 255,
+    // we should treat the following 4-bytes as a 16.16 fixed-point number
+    // rather than 32bit signed int.
+    if (!char_string->Skip(4)) {
+      return OTS_FAILURE();
+    }
+    *out_number = dummy_result;
+  } else {
+    return OTS_FAILURE();
+  }
+
+  return true;
+}
+
+// Executes |op| and updates |argument_stack|. Returns true if the execution
+// succeeds. If the |op| is kCallSubr or kCallGSubr, the function recursively
+// calls ExecuteType2CharString() function. The arguments other than |op| and
+// |argument_stack| are passed for that reason.
+bool ExecuteType2CharStringOperator(ots::OpenTypeFile *file,
+                                    int32_t op,
+                                    size_t call_depth,
+                                    const ots::CFFIndex& global_subrs_index,
+                                    const ots::CFFIndex& local_subrs_index,
+                                    ots::Buffer *cff_table,
+                                    ots::Buffer *char_string,
+                                    std::stack<int32_t> *argument_stack,
+                                    bool *out_found_endchar,
+                                    bool *in_out_found_width,
+                                    size_t *in_out_num_stems) {
+  const size_t stack_size = argument_stack->size();
+
+  switch (op) {
+  case ots::kCallSubr:
+  case ots::kCallGSubr: {
+    const ots::CFFIndex& subrs_index =
+        (op == ots::kCallSubr ? local_subrs_index : global_subrs_index);
+
+    if (stack_size < 1) {
+      return OTS_FAILURE();
+    }
+    int32_t subr_number = argument_stack->top();
+    argument_stack->pop();
+    if (subr_number == dummy_result) {
+      // For safety, we allow subr calls only with immediate subr numbers for
+      // now. For example, we allow "123 callgsubr", but does not allow "100 12
+      // add callgsubr". Please note that arithmetic and conditional operators
+      // always push the |dummy_result| in this implementation.
+      return OTS_FAILURE();
+    }
+
+    // See Adobe Technical Note #5176 (CFF), "16. Local/GlobalSubrs INDEXes."
+    int32_t bias = 32768;
+    if (subrs_index.count < 1240) {
+      bias = 107;
+    } else if (subrs_index.count < 33900) {
+      bias = 1131;
+    }
+    subr_number += bias;
+
+    // Sanity checks of |subr_number|.
+    if (subr_number < 0) {
+      return OTS_FAILURE();
+    }
+    if (subr_number >= kMaxSubrsCount) {
+      return OTS_FAILURE();
+    }
+    if (subrs_index.offsets.size() <= static_cast<size_t>(subr_number + 1)) {
+      return OTS_FAILURE();  // The number is out-of-bounds.
+    }
+
+    // Prepare ots::Buffer where we're going to jump.
+    const size_t length =
+      subrs_index.offsets[subr_number + 1] - subrs_index.offsets[subr_number];
+    if (length > kMaxCharStringLength) {
+      return OTS_FAILURE();
+    }
+    const size_t offset = subrs_index.offsets[subr_number];
+    cff_table->set_offset(offset);
+    if (!cff_table->Skip(length)) {
+      return OTS_FAILURE();
+    }
+    ots::Buffer char_string_to_jump(cff_table->buffer() + offset, length);
+
+    return ExecuteType2CharString(file,
+                                  call_depth + 1,
+                                  global_subrs_index,
+                                  local_subrs_index,
+                                  cff_table,
+                                  &char_string_to_jump,
+                                  argument_stack,
+                                  out_found_endchar,
+                                  in_out_found_width,
+                                  in_out_num_stems);
+  }
+
+  case ots::kReturn:
+    return true;
+
+  case ots::kEndChar:
+    *out_found_endchar = true;
+    *in_out_found_width = true;  // just in case.
+    return true;
+
+  case ots::kHStem:
+  case ots::kVStem:
+  case ots::kHStemHm:
+  case ots::kVStemHm: {
+    bool successful = false;
+    if (stack_size < 2) {
+      return OTS_FAILURE();
+    }
+    if ((stack_size % 2) == 0) {
+      successful = true;
+    } else if ((!(*in_out_found_width)) && (((stack_size - 1) % 2) == 0)) {
+      // The -1 is for "width" argument. For details, see Adobe Technical Note
+      // #5177, page 16, note 4.
+      successful = true;
+    }
+    (*in_out_num_stems) += (stack_size / 2);
+    if ((*in_out_num_stems) > kMaxNumberOfStemHints) {
+      return OTS_FAILURE();
+    }
+    while (!argument_stack->empty())
+      argument_stack->pop();
+    *in_out_found_width = true;  // always set true since "w" might be 0 byte.
+    return successful ? true : OTS_FAILURE();
+  }
+
+  case ots::kRMoveTo: {
+    bool successful = false;
+    if (stack_size == 2) {
+      successful = true;
+    } else if ((!(*in_out_found_width)) && (stack_size - 1 == 2)) {
+      successful = true;
+    }
+    while (!argument_stack->empty())
+      argument_stack->pop();
+    *in_out_found_width = true;
+    return successful ? true : OTS_FAILURE();
+  }
+
+  case ots::kVMoveTo:
+  case ots::kHMoveTo: {
+    bool successful = false;
+    if (stack_size == 1) {
+      successful = true;
+    } else if ((!(*in_out_found_width)) && (stack_size - 1 == 1)) {
+      successful = true;
+    }
+    while (!argument_stack->empty())
+      argument_stack->pop();
+    *in_out_found_width = true;
+    return successful ? true : OTS_FAILURE();
+  }
+
+  case ots::kHintMask:
+  case ots::kCntrMask: {
+    bool successful = false;
+    if (stack_size == 0) {
+      successful = true;
+    } else if ((!(*in_out_found_width)) && (stack_size == 1)) {
+      // A number for "width" is found.
+      successful = true;
+    } else if ((!(*in_out_found_width)) ||  // in this case, any sizes are ok.
+               ((stack_size % 2) == 0)) {
+      // The numbers are vstem definition.
+      // See Adobe Technical Note #5177, page 24, hintmask.
+      (*in_out_num_stems) += (stack_size / 2);
+      if ((*in_out_num_stems) > kMaxNumberOfStemHints) {
+        return OTS_FAILURE();
+      }
+      successful = true;
+    }
+    if (!successful) {
+       return OTS_FAILURE();
+    }
+
+    if ((*in_out_num_stems) == 0) {
+      return OTS_FAILURE();
+    }
+    const size_t mask_bytes = (*in_out_num_stems + 7) / 8;
+    if (!char_string->Skip(mask_bytes)) {
+      return OTS_FAILURE();
+    }
+    while (!argument_stack->empty())
+      argument_stack->pop();
+    *in_out_found_width = true;
+    return true;
+  }
+
+  case ots::kRLineTo:
+    if (!(*in_out_found_width)) {
+      // The first stack-clearing operator should be one of hstem, hstemhm,
+      // vstem, vstemhm, cntrmask, hintmask, hmoveto, vmoveto, rmoveto, or
+      // endchar. For details, see Adobe Technical Note #5177, page 16, note 4.
+      return OTS_FAILURE();
+    }
+    if (stack_size < 2) {
+      return OTS_FAILURE();
+    }
+    if ((stack_size % 2) != 0) {
+      return OTS_FAILURE();
+    }
+    while (!argument_stack->empty())
+      argument_stack->pop();
+    return true;
+
+  case ots::kHLineTo:
+  case ots::kVLineTo:
+    if (!(*in_out_found_width)) {
+      return OTS_FAILURE();
+    }
+    if (stack_size < 1) {
+      return OTS_FAILURE();
+    }
+    while (!argument_stack->empty())
+      argument_stack->pop();
+    return true;
+
+  case ots::kRRCurveTo:
+    if (!(*in_out_found_width)) {
+      return OTS_FAILURE();
+    }
+    if (stack_size < 6) {
+      return OTS_FAILURE();
+    }
+    if ((stack_size % 6) != 0) {
+      return OTS_FAILURE();
+    }
+    while (!argument_stack->empty())
+      argument_stack->pop();
+    return true;
+
+  case ots::kRCurveLine:
+    if (!(*in_out_found_width)) {
+      return OTS_FAILURE();
+    }
+    if (stack_size < 8) {
+      return OTS_FAILURE();
+    }
+    if (((stack_size - 2) % 6) != 0) {
+      return OTS_FAILURE();
+    }
+    while (!argument_stack->empty())
+      argument_stack->pop();
+    return true;
+
+  case ots::kRLineCurve:
+    if (!(*in_out_found_width)) {
+      return OTS_FAILURE();
+    }
+    if (stack_size < 8) {
+      return OTS_FAILURE();
+    }
+    if (((stack_size - 6) % 2) != 0) {
+      return OTS_FAILURE();
+    }
+    while (!argument_stack->empty())
+      argument_stack->pop();
+    return true;
+
+  case ots::kVVCurveTo:
+    if (!(*in_out_found_width)) {
+      return OTS_FAILURE();
+    }
+    if (stack_size < 4) {
+      return OTS_FAILURE();
+    }
+    if (((stack_size % 4) != 0) &&
+        (((stack_size - 1) % 4) != 0)) {
+      return OTS_FAILURE();
+    }
+    while (!argument_stack->empty())
+      argument_stack->pop();
+    return true;
+
+  case ots::kHHCurveTo: {
+    bool successful = false;
+    if (!(*in_out_found_width)) {
+      return OTS_FAILURE();
+    }
+    if (stack_size < 4) {
+      return OTS_FAILURE();
+    }
+    if ((stack_size % 4) == 0) {
+      // {dxa dxb dyb dxc}+
+      successful = true;
+    } else if (((stack_size - 1) % 4) == 0) {
+      // dy1? {dxa dxb dyb dxc}+
+      successful = true;
+    }
+    while (!argument_stack->empty())
+      argument_stack->pop();
+    return successful ? true : OTS_FAILURE();
+  }
+
+  case ots::kVHCurveTo:
+  case ots::kHVCurveTo: {
+    bool successful = false;
+    if (!(*in_out_found_width)) {
+      return OTS_FAILURE();
+    }
+    if (stack_size < 4) {
+      return OTS_FAILURE();
+    }
+    if (((stack_size - 4) % 8) == 0) {
+      // dx1 dx2 dy2 dy3 {dya dxb dyb dxc dxd dxe dye dyf}*
+      successful = true;
+    } else if ((stack_size >= 5) &&
+               ((stack_size - 5) % 8) == 0) {
+      // dx1 dx2 dy2 dy3 {dya dxb dyb dxc dxd dxe dye dyf}* dxf
+      successful = true;
+    } else if ((stack_size >= 8) &&
+               ((stack_size - 8) % 8) == 0) {
+      // {dxa dxb dyb dyc dyd dxe dye dxf}+
+      successful = true;
+    } else if ((stack_size >= 9) &&
+               ((stack_size - 9) % 8) == 0) {
+      // {dxa dxb dyb dyc dyd dxe dye dxf}+ dyf?
+      successful = true;
+    }
+    while (!argument_stack->empty())
+      argument_stack->pop();
+    return successful ? true : OTS_FAILURE();
+  }
+
+  case ots::kDotSection:
+    // Deprecated operator but harmless, we probably should drop it some how.
+    if (stack_size != 0) {
+      return OTS_FAILURE();
+    }
+    return true;
+
+  case ots::kAnd:
+  case ots::kOr:
+  case ots::kEq:
+  case ots::kAdd:
+  case ots::kSub:
+    if (stack_size < 2) {
+      return OTS_FAILURE();
+    }
+    argument_stack->pop();
+    argument_stack->pop();
+    argument_stack->push(dummy_result);
+    // TODO(yusukes): Implement this. We should push a real value for all
+    // arithmetic and conditional operations.
+    return true;
+
+  case ots::kNot:
+  case ots::kAbs:
+  case ots::kNeg:
+    if (stack_size < 1) {
+      return OTS_FAILURE();
+    }
+    argument_stack->pop();
+    argument_stack->push(dummy_result);
+    // TODO(yusukes): Implement this. We should push a real value for all
+    // arithmetic and conditional operations.
+    return true;
+
+  case ots::kDiv:
+    // TODO(yusukes): Should detect div-by-zero errors.
+    if (stack_size < 2) {
+      return OTS_FAILURE();
+    }
+    argument_stack->pop();
+    argument_stack->pop();
+    argument_stack->push(dummy_result);
+    // TODO(yusukes): Implement this. We should push a real value for all
+    // arithmetic and conditional operations.
+    return true;
+
+  case ots::kDrop:
+    if (stack_size < 1) {
+      return OTS_FAILURE();
+    }
+    argument_stack->pop();
+    return true;
+
+  case ots::kPut:
+  case ots::kGet:
+  case ots::kIndex:
+    // For now, just call OTS_FAILURE since there is no way to check whether the
+    // index argument, |i|, is out-of-bounds or not. Fortunately, no OpenType
+    // fonts I have (except malicious ones!) use the operators.
+    // TODO(yusukes): Implement them in a secure way.
+    return OTS_FAILURE();
+
+  case ots::kRoll:
+    // Likewise, just call OTS_FAILURE for kRoll since there is no way to check
+    // whether |N| is smaller than the current stack depth or not.
+    // TODO(yusukes): Implement them in a secure way.
+    return OTS_FAILURE();
+
+  case ots::kRandom:
+    // For now, we don't handle the 'random' operator since the operator makes
+    // it hard to analyze hinting code statically.
+    return OTS_FAILURE();
+
+  case ots::kIfElse:
+    if (stack_size < 4) {
+      return OTS_FAILURE();
+    }
+    argument_stack->pop();
+    argument_stack->pop();
+    argument_stack->pop();
+    argument_stack->pop();
+    argument_stack->push(dummy_result);
+    // TODO(yusukes): Implement this. We should push a real value for all
+    // arithmetic and conditional operations.
+    return true;
+
+  case ots::kMul:
+    // TODO(yusukes): Should detect overflows.
+    if (stack_size < 2) {
+      return OTS_FAILURE();
+    }
+    argument_stack->pop();
+    argument_stack->pop();
+    argument_stack->push(dummy_result);
+    // TODO(yusukes): Implement this. We should push a real value for all
+    // arithmetic and conditional operations.
+    return true;
+
+  case ots::kSqrt:
+    // TODO(yusukes): Should check if the argument is negative.
+    if (stack_size < 1) {
+      return OTS_FAILURE();
+    }
+    argument_stack->pop();
+    argument_stack->push(dummy_result);
+    // TODO(yusukes): Implement this. We should push a real value for all
+    // arithmetic and conditional operations.
+    return true;
+
+  case ots::kDup:
+    if (stack_size < 1) {
+      return OTS_FAILURE();
+    }
+    argument_stack->pop();
+    argument_stack->push(dummy_result);
+    argument_stack->push(dummy_result);
+    if (argument_stack->size() > kMaxArgumentStack) {
+      return OTS_FAILURE();
+    }
+    // TODO(yusukes): Implement this. We should push a real value for all
+    // arithmetic and conditional operations.
+    return true;
+
+  case ots::kExch:
+    if (stack_size < 2) {
+      return OTS_FAILURE();
+    }
+    argument_stack->pop();
+    argument_stack->pop();
+    argument_stack->push(dummy_result);
+    argument_stack->push(dummy_result);
+    // TODO(yusukes): Implement this. We should push a real value for all
+    // arithmetic and conditional operations.
+    return true;
+
+  case ots::kHFlex:
+    if (!(*in_out_found_width)) {
+      return OTS_FAILURE();
+    }
+    if (stack_size != 7) {
+      return OTS_FAILURE();
+    }
+    while (!argument_stack->empty())
+      argument_stack->pop();
+    return true;
+
+  case ots::kFlex:
+    if (!(*in_out_found_width)) {
+      return OTS_FAILURE();
+    }
+    if (stack_size != 13) {
+      return OTS_FAILURE();
+    }
+    while (!argument_stack->empty())
+      argument_stack->pop();
+    return true;
+
+  case ots::kHFlex1:
+    if (!(*in_out_found_width)) {
+      return OTS_FAILURE();
+    }
+    if (stack_size != 9) {
+      return OTS_FAILURE();
+    }
+    while (!argument_stack->empty())
+      argument_stack->pop();
+    return true;
+
+  case ots::kFlex1:
+    if (!(*in_out_found_width)) {
+      return OTS_FAILURE();
+    }
+    if (stack_size != 11) {
+      return OTS_FAILURE();
+    }
+    while (!argument_stack->empty())
+      argument_stack->pop();
+    return true;
+  }
+
+  return OTS_FAILURE_MSG("Undefined operator: %d (0x%x)", op, op);
+}
+
+// Executes |char_string| and updates |argument_stack|.
+//
+// call_depth: The current call depth. Initial value is zero.
+// global_subrs_index: Global subroutines.
+// local_subrs_index: Local subroutines for the current glyph.
+// cff_table: A whole CFF table which contains all global and local subroutines.
+// char_string: A charstring we'll execute. |char_string| can be a main routine
+//              in CharString INDEX, or a subroutine in GlobalSubr/LocalSubr.
+// argument_stack: The stack which an operator in |char_string| operates.
+// out_found_endchar: true is set if |char_string| contains 'endchar'.
+// in_out_found_width: true is set if |char_string| contains 'width' byte (which
+//                     is 0 or 1 byte.)
+// in_out_num_stems: total number of hstems and vstems processed so far.
+bool ExecuteType2CharString(ots::OpenTypeFile *file,
+                            size_t call_depth,
+                            const ots::CFFIndex& global_subrs_index,
+                            const ots::CFFIndex& local_subrs_index,
+                            ots::Buffer *cff_table,
+                            ots::Buffer *char_string,
+                            std::stack<int32_t> *argument_stack,
+                            bool *out_found_endchar,
+                            bool *in_out_found_width,
+                            size_t *in_out_num_stems) {
+  if (call_depth > kMaxSubrNesting) {
+    return OTS_FAILURE();
+  }
+  *out_found_endchar = false;
+
+  const size_t length = char_string->length();
+  while (char_string->offset() < length) {
+    int32_t operator_or_operand = 0;
+    bool is_operator = false;
+    if (!ReadNextNumberFromType2CharString(char_string,
+                                           &operator_or_operand,
+                                           &is_operator)) {
+      return OTS_FAILURE();
+    }
+
+#ifdef DUMP_T2CHARSTRING
+    /*
+      You can dump all operators and operands (except mask bytes for hintmask
+      and cntrmask) by the following code:
+    */
+
+      if (!is_operator) {
+        std::fprintf(stderr, "#%d# ", operator_or_operand);
+      } else {
+        std::fprintf(stderr, "#%s#\n",
+           Type2CharStringOperatorToString(
+               ots::Type2CharStringOperator(operator_or_operand))
+           );
+      }
+#endif
+
+    if (!is_operator) {
+      argument_stack->push(operator_or_operand);
+      if (argument_stack->size() > kMaxArgumentStack) {
+        return OTS_FAILURE();
+      }
+      continue;
+    }
+
+    // An operator is found. Execute it.
+    if (!ExecuteType2CharStringOperator(file,
+                                        operator_or_operand,
+                                        call_depth,
+                                        global_subrs_index,
+                                        local_subrs_index,
+                                        cff_table,
+                                        char_string,
+                                        argument_stack,
+                                        out_found_endchar,
+                                        in_out_found_width,
+                                        in_out_num_stems)) {
+      return OTS_FAILURE();
+    }
+    if (*out_found_endchar) {
+      return true;
+    }
+    if (operator_or_operand == ots::kReturn) {
+      return true;
+    }
+  }
+
+  // No endchar operator is found.
+  return OTS_FAILURE();
+}
+
+// Selects a set of subroutings for |glyph_index| from |cff| and sets it on
+// |out_local_subrs_to_use|. Returns true on success.
+bool SelectLocalSubr(const std::map<uint16_t, uint8_t> &fd_select,
+                     const std::vector<ots::CFFIndex *> &local_subrs_per_font,
+                     const ots::CFFIndex *local_subrs,
+                     uint16_t glyph_index,  // 0-origin
+                     const ots::CFFIndex **out_local_subrs_to_use) {
+  *out_local_subrs_to_use = NULL;
+
+  // First, find local subrs from |local_subrs_per_font|.
+  if ((fd_select.size() > 0) &&
+      (!local_subrs_per_font.empty())) {
+    // Look up FDArray index for the glyph.
+    std::map<uint16_t, uint8_t>::const_iterator iter =
+        fd_select.find(glyph_index);
+    if (iter == fd_select.end()) {
+      return OTS_FAILURE();
+    }
+    const uint8_t fd_index = iter->second;
+    if (fd_index >= local_subrs_per_font.size()) {
+      return OTS_FAILURE();
+    }
+    *out_local_subrs_to_use = local_subrs_per_font.at(fd_index);
+  } else if (local_subrs) {
+    // Second, try to use |local_subrs|. Most Latin fonts don't have FDSelect
+    // entries. If The font has a local subrs index associated with the Top
+    // DICT (not FDArrays), use it.
+    *out_local_subrs_to_use = local_subrs;
+  } else {
+    // Just return NULL.
+    *out_local_subrs_to_use = NULL;
+  }
+
+  return true;
+}
+
+}  // namespace
+
+namespace ots {
+
+bool ValidateType2CharStringIndex(
+    ots::OpenTypeFile *file,
+    const CFFIndex& char_strings_index,
+    const CFFIndex& global_subrs_index,
+    const std::map<uint16_t, uint8_t> &fd_select,
+    const std::vector<CFFIndex *> &local_subrs_per_font,
+    const CFFIndex *local_subrs,
+    Buffer* cff_table) {
+  const uint16_t num_offsets =
+      static_cast<uint16_t>(char_strings_index.offsets.size());
+  if (num_offsets != char_strings_index.offsets.size() || num_offsets == 0) {
+    return OTS_FAILURE();  // no charstring.
+  }
+
+  // For each glyph, validate the corresponding charstring.
+  for (uint16_t i = 1; i < num_offsets; ++i) {
+    // Prepare a Buffer object, |char_string|, which contains the charstring
+    // for the |i|-th glyph.
+    const size_t length =
+      char_strings_index.offsets[i] - char_strings_index.offsets[i - 1];
+    if (length > kMaxCharStringLength) {
+      return OTS_FAILURE();
+    }
+    const size_t offset = char_strings_index.offsets[i - 1];
+    cff_table->set_offset(offset);
+    if (!cff_table->Skip(length)) {
+      return OTS_FAILURE();
+    }
+    Buffer char_string(cff_table->buffer() + offset, length);
+
+    // Get a local subrs for the glyph.
+    const uint16_t glyph_index = i - 1;  // index in the map is 0-origin.
+    const CFFIndex *local_subrs_to_use = NULL;
+    if (!SelectLocalSubr(fd_select,
+                         local_subrs_per_font,
+                         local_subrs,
+                         glyph_index,
+                         &local_subrs_to_use)) {
+      return OTS_FAILURE();
+    }
+    // If |local_subrs_to_use| is still NULL, use an empty one.
+    CFFIndex default_empty_subrs;
+    if (!local_subrs_to_use){
+      local_subrs_to_use = &default_empty_subrs;
+    }
+
+    // Check a charstring for the |i|-th glyph.
+    std::stack<int32_t> argument_stack;
+    bool found_endchar = false;
+    bool found_width = false;
+    size_t num_stems = 0;
+    if (!ExecuteType2CharString(file,
+                                0 /* initial call_depth is zero */,
+                                global_subrs_index, *local_subrs_to_use,
+                                cff_table, &char_string, &argument_stack,
+                                &found_endchar, &found_width, &num_stems)) {
+      return OTS_FAILURE();
+    }
+    if (!found_endchar) {
+      return OTS_FAILURE();
+    }
+  }
+  return true;
+}
+
+}  // namespace ots
+
+#undef TABLE_NAME
diff --git a/third_party/ots/src/cff_type2_charstring.h b/third_party/ots/src/cff_type2_charstring.h
new file mode 100644
index 0000000..67323425
--- /dev/null
+++ b/third_party/ots/src/cff_type2_charstring.h
@@ -0,0 +1,101 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_CFF_TYPE2_CHARSTRING_H_
+#define OTS_CFF_TYPE2_CHARSTRING_H_
+
+#include "cff.h"
+#include "ots.h"
+
+#include <map>
+#include <vector>
+
+namespace ots {
+
+// Validates all charstrings in |char_strings_index|. Charstring is a small
+// language for font hinting defined in Adobe Technical Note #5177.
+// http://www.adobe.com/devnet/font/pdfs/5177.Type2.pdf
+//
+// The validation will fail if one of the following conditions is met:
+//  1. The code uses more than 48 values of argument stack.
+//  2. The code uses deeply nested subroutine calls (more than 10 levels.)
+//  3. The code passes invalid number of operands to an operator.
+//  4. The code calls an undefined global or local subroutine.
+//  5. The code uses one of the following operators that are unlikely used in
+//     an ordinary fonts, and could be dangerous: random, put, get, index, roll.
+//
+// Arguments:
+//  global_subrs_index: Global subroutines which could be called by a charstring
+//                      in |char_strings_index|.
+//  fd_select: A map from glyph # to font #.
+//  local_subrs_per_font: A list of Local Subrs associated with FDArrays. Can be
+//                        empty.
+//  local_subrs: A Local Subrs associated with Top DICT. Can be NULL.
+//  cff_table: A buffer which contains actual byte code of charstring, global
+//             subroutines and local subroutines.
+bool ValidateType2CharStringIndex(
+    OpenTypeFile *file,
+    const CFFIndex &char_strings_index,
+    const CFFIndex &global_subrs_index,
+    const std::map<uint16_t, uint8_t> &fd_select,
+    const std::vector<CFFIndex *> &local_subrs_per_font,
+    const CFFIndex *local_subrs,
+    Buffer *cff_table);
+
+// The list of Operators. See Appendix. A in Adobe Technical Note #5177.
+enum Type2CharStringOperator {
+  kHStem = 1,
+  kVStem = 3,
+  kVMoveTo = 4,
+  kRLineTo = 5,
+  kHLineTo = 6,
+  kVLineTo = 7,
+  kRRCurveTo = 8,
+  kCallSubr = 10,
+  kReturn = 11,
+  kEndChar = 14,
+  kHStemHm = 18,
+  kHintMask = 19,
+  kCntrMask = 20,
+  kRMoveTo = 21,
+  kHMoveTo = 22,
+  kVStemHm = 23,
+  kRCurveLine = 24,
+  kRLineCurve = 25,
+  kVVCurveTo = 26,
+  kHHCurveTo = 27,
+  kCallGSubr = 29,
+  kVHCurveTo = 30,
+  kHVCurveTo = 31,
+  kDotSection = 12 << 8,
+  kAnd = (12 << 8) + 3,
+  kOr = (12 << 8) + 4,
+  kNot = (12 << 8) + 5,
+  kAbs = (12 << 8) + 9,
+  kAdd = (12 << 8) + 10,
+  kSub = (12 << 8) + 11,
+  kDiv = (12 << 8) + 12,
+  kNeg = (12 << 8) + 14,
+  kEq = (12 << 8) + 15,
+  kDrop = (12 << 8) + 18,
+  kPut = (12 << 8) + 20,
+  kGet = (12 << 8) + 21,
+  kIfElse = (12 << 8) + 22,
+  kRandom = (12 << 8) + 23,
+  kMul = (12 << 8) + 24,
+  kSqrt = (12 << 8) + 26,
+  kDup = (12 << 8) + 27,
+  kExch = (12 << 8) + 28,
+  kIndex = (12 << 8) + 29,
+  kRoll = (12 << 8) + 30,
+  kHFlex = (12 << 8) + 34,
+  kFlex = (12 << 8) + 35,
+  kHFlex1 = (12 << 8) + 36,
+  kFlex1 = (12 << 8) + 37,
+  // Operators that are undocumented, such as 'blend', will be rejected.
+};
+
+}  // namespace ots
+
+#endif  // OTS_CFF_TYPE2_CHARSTRING_H_
diff --git a/third_party/ots/src/cmap.cc b/third_party/ots/src/cmap.cc
new file mode 100644
index 0000000..d9ca9fa
--- /dev/null
+++ b/third_party/ots/src/cmap.cc
@@ -0,0 +1,1106 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cmap.h"
+
+#include <algorithm>
+#include <set>
+#include <utility>
+#include <vector>
+
+#include "maxp.h"
+#include "os2.h"
+
+// cmap - Character To Glyph Index Mapping Table
+// http://www.microsoft.com/typography/otspec/cmap.htm
+
+#define TABLE_NAME "cmap"
+
+namespace {
+
+struct CMAPSubtableHeader {
+  uint16_t platform;
+  uint16_t encoding;
+  uint32_t offset;
+  uint16_t format;
+  uint32_t length;
+  uint32_t language;
+};
+
+struct Subtable314Range {
+  uint16_t start_range;
+  uint16_t end_range;
+  int16_t id_delta;
+  uint16_t id_range_offset;
+  uint32_t id_range_offset_offset;
+};
+
+// The maximum number of groups in format 12, 13 or 14 subtables.
+// Note: 0xFFFF is the maximum number of glyphs in a single font file.
+const unsigned kMaxCMAPGroups = 0xFFFF;
+
+// Glyph array size for the Mac Roman (format 0) table.
+const size_t kFormat0ArraySize = 256;
+
+// The upper limit of the Unicode code point.
+const uint32_t kUnicodeUpperLimit = 0x10FFFF;
+
+// The maximum number of UVS records (See below).
+const uint32_t kMaxCMAPSelectorRecords = 259;
+// The range of UVSes are:
+//   0x180B-0x180D (3 code points)
+//   0xFE00-0xFE0F (16 code points)
+//   0xE0100-0xE01EF (240 code points)
+const uint32_t kMongolianVSStart = 0x180B;
+const uint32_t kMongolianVSEnd = 0x180D;
+const uint32_t kVSStart = 0xFE00;
+const uint32_t kVSEnd = 0xFE0F;
+const uint32_t kIVSStart = 0xE0100;
+const uint32_t kIVSEnd = 0xE01EF;
+const uint32_t kUVSUpperLimit = 0xFFFFFF;
+
+// Parses Format 4 tables
+bool ParseFormat4(ots::OpenTypeFile *file, int platform, int encoding,
+              const uint8_t *data, size_t length, uint16_t num_glyphs) {
+  ots::Buffer subtable(data, length);
+
+  // 0.3.4, 3.0.4 or 3.1.4 subtables are complex and, rather than expanding the
+  // whole thing and recompacting it, we validate it and include it verbatim
+  // in the output.
+
+  if (!file->os2) {
+    return OTS_FAILURE_MSG("Required OS/2 table missing");
+  }
+
+  if (!subtable.Skip(4)) {
+    return OTS_FAILURE_MSG("Can't read 4 bytes at start of cmap format 4 subtable");
+  }
+  uint16_t language = 0;
+  if (!subtable.ReadU16(&language)) {
+    return OTS_FAILURE_MSG("Can't read language");
+  }
+  if (language) {
+    // Platform ID 3 (windows) subtables should have language '0'.
+    return OTS_FAILURE_MSG("Languages should be 0 (%d)", language);
+  }
+
+  uint16_t segcountx2, search_range, entry_selector, range_shift;
+  segcountx2 = search_range = entry_selector = range_shift = 0;
+  if (!subtable.ReadU16(&segcountx2) ||
+      !subtable.ReadU16(&search_range) ||
+      !subtable.ReadU16(&entry_selector) ||
+      !subtable.ReadU16(&range_shift)) {
+    return OTS_FAILURE_MSG("Failed to read subcmap structure");
+  }
+
+  if (segcountx2 & 1 || search_range & 1) {
+    return OTS_FAILURE_MSG("Bad subcmap structure");
+  }
+  const uint16_t segcount = segcountx2 >> 1;
+  // There must be at least one segment according the spec.
+  if (segcount < 1) {
+    return OTS_FAILURE_MSG("Segcount < 1 (%d)", segcount);
+  }
+
+  // log2segcount is the maximal x s.t. 2^x < segcount
+  unsigned log2segcount = 0;
+  while (1u << (log2segcount + 1) <= segcount) {
+    log2segcount++;
+  }
+
+  const uint16_t expected_search_range = 2 * 1u << log2segcount;
+  if (expected_search_range != search_range) {
+    return OTS_FAILURE_MSG("expected search range != search range (%d != %d)", expected_search_range, search_range);
+  }
+
+  if (entry_selector != log2segcount) {
+    return OTS_FAILURE_MSG("entry selector != log2(segement count) (%d != %d)", entry_selector, log2segcount);
+  }
+
+  const uint16_t expected_range_shift = segcountx2 - search_range;
+  if (range_shift != expected_range_shift) {
+    return OTS_FAILURE_MSG("unexpected range shift (%d != %d)", range_shift, expected_range_shift);
+  }
+
+  std::vector<Subtable314Range> ranges(segcount);
+
+  for (unsigned i = 0; i < segcount; ++i) {
+    if (!subtable.ReadU16(&ranges[i].end_range)) {
+      return OTS_FAILURE_MSG("Failed to read segment %d", i);
+    }
+  }
+
+  uint16_t padding;
+  if (!subtable.ReadU16(&padding)) {
+    return OTS_FAILURE_MSG("Failed to read cmap subtable segment padding");
+  }
+  if (padding) {
+    return OTS_FAILURE_MSG("Non zero cmap subtable segment padding (%d)", padding);
+  }
+
+  for (unsigned i = 0; i < segcount; ++i) {
+    if (!subtable.ReadU16(&ranges[i].start_range)) {
+      return OTS_FAILURE_MSG("Failed to read segment start range %d", i);
+    }
+  }
+  for (unsigned i = 0; i < segcount; ++i) {
+    if (!subtable.ReadS16(&ranges[i].id_delta)) {
+      return OTS_FAILURE_MSG("Failed to read segment delta %d", i);
+    }
+  }
+  for (unsigned i = 0; i < segcount; ++i) {
+    ranges[i].id_range_offset_offset = subtable.offset();
+    if (!subtable.ReadU16(&ranges[i].id_range_offset)) {
+      return OTS_FAILURE_MSG("Failed to read segment range offset %d", i);
+    }
+
+    if (ranges[i].id_range_offset & 1) {
+      // Some font generators seem to put 65535 on id_range_offset
+      // for 0xFFFF-0xFFFF range.
+      // (e.g., many fonts in http://www.princexml.com/fonts/)
+      if (i == segcount - 1u) {
+        OTS_WARNING("bad id_range_offset");
+        ranges[i].id_range_offset = 0;
+        // The id_range_offset value in the transcoded font will not change
+        // since this table is not actually "transcoded" yet.
+      } else {
+        return OTS_FAILURE_MSG("Bad segment offset (%d)", ranges[i].id_range_offset);
+      }
+    }
+  }
+
+  // ranges must be ascending order, based on the end_code. Ranges may not
+  // overlap.
+  for (unsigned i = 1; i < segcount; ++i) {
+    if ((i == segcount - 1u) &&
+        (ranges[i - 1].start_range == 0xffff) &&
+        (ranges[i - 1].end_range == 0xffff) &&
+        (ranges[i].start_range == 0xffff) &&
+        (ranges[i].end_range == 0xffff)) {
+      // Some fonts (e.g., Germania.ttf) have multiple 0xffff terminators.
+      // We'll accept them as an exception.
+      OTS_WARNING("multiple 0xffff terminators found");
+      continue;
+    }
+
+    // Note: some Linux fonts (e.g., LucidaSansOblique.ttf, bsmi00lp.ttf) have
+    // unsorted table...
+    if (ranges[i].end_range <= ranges[i - 1].end_range) {
+      return OTS_FAILURE_MSG("Out of order end range (%d <= %d)", ranges[i].end_range, ranges[i-1].end_range);
+    }
+    if (ranges[i].start_range <= ranges[i - 1].end_range) {
+      return OTS_FAILURE_MSG("out of order start range (%d <= %d)", ranges[i].start_range, ranges[i-1].end_range);
+    }
+
+    // On many fonts, the value of {first, last}_char_index are incorrect.
+    // Fix them.
+    if (file->os2->first_char_index != 0xFFFF &&
+        ranges[i].start_range != 0xFFFF &&
+        file->os2->first_char_index > ranges[i].start_range) {
+      file->os2->first_char_index = ranges[i].start_range;
+    }
+    if (file->os2->last_char_index != 0xFFFF &&
+        ranges[i].end_range != 0xFFFF &&
+        file->os2->last_char_index < ranges[i].end_range) {
+      file->os2->last_char_index = ranges[i].end_range;
+    }
+  }
+
+  // The last range must end at 0xffff
+  if (ranges[segcount - 1].start_range != 0xffff || ranges[segcount - 1].end_range != 0xffff) {
+    return OTS_FAILURE_MSG("Final segment start and end must be 0xFFFF (0x%04X-0x%04X)",
+                           ranges[segcount - 1].start_range, ranges[segcount - 1].end_range);
+  }
+
+  // A format 4 CMAP subtable is complex. To be safe we simulate a lookup of
+  // each code-point defined in the table and make sure that they are all valid
+  // glyphs and that we don't access anything out-of-bounds.
+  for (unsigned i = 0; i < segcount; ++i) {
+    for (unsigned cp = ranges[i].start_range; cp <= ranges[i].end_range; ++cp) {
+      const uint16_t code_point = static_cast<uint16_t>(cp);
+      if (ranges[i].id_range_offset == 0) {
+        // this is explictly allowed to overflow in the spec
+        const uint16_t glyph = code_point + ranges[i].id_delta;
+        if (glyph >= num_glyphs) {
+          return OTS_FAILURE_MSG("Range glyph reference too high (%d > %d)", glyph, num_glyphs - 1);
+        }
+      } else {
+        const uint16_t range_delta = code_point - ranges[i].start_range;
+        // this might seem odd, but it's true. The offset is relative to the
+        // location of the offset value itself.
+        const uint32_t glyph_id_offset = ranges[i].id_range_offset_offset +
+                                         ranges[i].id_range_offset +
+                                         range_delta * 2;
+        // We need to be able to access a 16-bit value from this offset
+        if (glyph_id_offset + 1 >= length) {
+          return OTS_FAILURE_MSG("bad glyph id offset (%d > %ld)", glyph_id_offset, length);
+        }
+        uint16_t glyph;
+        std::memcpy(&glyph, data + glyph_id_offset, 2);
+        glyph = ntohs(glyph);
+        if (glyph >= num_glyphs) {
+          return OTS_FAILURE_MSG("Range glyph reference too high (%d > %d)", glyph, num_glyphs - 1);
+        }
+      }
+    }
+  }
+
+  // We accept the table.
+  // TODO(yusukes): transcode the subtable.
+  if (platform == 3 && encoding == 0) {
+    file->cmap->subtable_3_0_4_data = data;
+    file->cmap->subtable_3_0_4_length = length;
+  } else if (platform == 3 && encoding == 1) {
+    file->cmap->subtable_3_1_4_data = data;
+    file->cmap->subtable_3_1_4_length = length;
+  } else if (platform == 0 && encoding == 3) {
+    file->cmap->subtable_0_3_4_data = data;
+    file->cmap->subtable_0_3_4_length = length;
+  } else {
+    return OTS_FAILURE_MSG("Unknown cmap subtable type (platform=%d, encoding=%d)", platform, encoding);
+  }
+
+  return true;
+}
+
+bool Parse31012(ots::OpenTypeFile *file,
+                const uint8_t *data, size_t length, uint16_t num_glyphs) {
+  ots::Buffer subtable(data, length);
+
+  // Format 12 tables are simple. We parse these and fully serialise them
+  // later.
+
+  if (!subtable.Skip(8)) {
+    return OTS_FAILURE_MSG("failed to skip the first 8 bytes of format 12 subtable");
+  }
+  uint32_t language = 0;
+  if (!subtable.ReadU32(&language)) {
+    return OTS_FAILURE_MSG("can't read format 12 subtable language");
+  }
+  if (language) {
+    return OTS_FAILURE_MSG("format 12 subtable language should be zero (%d)", language);
+  }
+
+  uint32_t num_groups = 0;
+  if (!subtable.ReadU32(&num_groups)) {
+    return OTS_FAILURE_MSG("can't read number of format 12 subtable groups");
+  }
+  if (num_groups == 0 || num_groups > kMaxCMAPGroups) {
+    return OTS_FAILURE_MSG("bad format 12 subtable group count %d", num_groups);
+  }
+
+  std::vector<ots::OpenTypeCMAPSubtableRange> &groups
+      = file->cmap->subtable_3_10_12;
+  groups.resize(num_groups);
+
+  for (unsigned i = 0; i < num_groups; ++i) {
+    if (!subtable.ReadU32(&groups[i].start_range) ||
+        !subtable.ReadU32(&groups[i].end_range) ||
+        !subtable.ReadU32(&groups[i].start_glyph_id)) {
+      return OTS_FAILURE_MSG("can't read format 12 subtable group");
+    }
+
+    if (groups[i].start_range > kUnicodeUpperLimit ||
+        groups[i].end_range > kUnicodeUpperLimit ||
+        groups[i].start_glyph_id > 0xFFFF) {
+      return OTS_FAILURE_MSG("bad format 12 subtable group (startCharCode=0x%4X, endCharCode=0x%4X, startGlyphID=%d)",
+                             groups[i].start_range, groups[i].end_range, groups[i].start_glyph_id);
+    }
+
+    // [0xD800, 0xDFFF] are surrogate code points.
+    if (groups[i].start_range >= 0xD800 &&
+        groups[i].start_range <= 0xDFFF) {
+      return OTS_FAILURE_MSG("format 12 subtable out of range group startCharCode (0x%4X)", groups[i].start_range);
+    }
+    if (groups[i].end_range >= 0xD800 &&
+        groups[i].end_range <= 0xDFFF) {
+      return OTS_FAILURE_MSG("format 12 subtable out of range group endCharCode (0x%4X)", groups[i].end_range);
+    }
+    if (groups[i].start_range < 0xD800 &&
+        groups[i].end_range > 0xDFFF) {
+      return OTS_FAILURE_MSG("bad format 12 subtable group startCharCode (0x%4X) or endCharCode (0x%4X)",
+                             groups[i].start_range, groups[i].end_range);
+    }
+
+    // We assert that the glyph value is within range. Because of the range
+    // limits, above, we don't need to worry about overflow.
+    if (groups[i].end_range < groups[i].start_range) {
+      return OTS_FAILURE_MSG("format 12 subtable group endCharCode before startCharCode (0x%4X < 0x%4X)",
+                             groups[i].end_range, groups[i].start_range);
+    }
+    if ((groups[i].end_range - groups[i].start_range) +
+        groups[i].start_glyph_id > num_glyphs) {
+      return OTS_FAILURE_MSG("bad format 12 subtable group startGlyphID (%d)", groups[i].start_glyph_id);
+    }
+  }
+
+  // the groups must be sorted by start code and may not overlap
+  for (unsigned i = 1; i < num_groups; ++i) {
+    if (groups[i].start_range <= groups[i - 1].start_range) {
+      return OTS_FAILURE_MSG("out of order format 12 subtable group (startCharCode=0x%4X <= startCharCode=0x%4X of previous group)",
+                             groups[i].start_range, groups[i-1].start_range);
+    }
+    if (groups[i].start_range <= groups[i - 1].end_range) {
+      return OTS_FAILURE_MSG("overlapping format 12 subtable groups (startCharCode=0x%4X <= endCharCode=0x%4X of previous group)",
+                             groups[i].start_range, groups[i-1].end_range);
+    }
+  }
+
+  return true;
+}
+
+bool Parse31013(ots::OpenTypeFile *file,
+                const uint8_t *data, size_t length, uint16_t num_glyphs) {
+  ots::Buffer subtable(data, length);
+
+  // Format 13 tables are simple. We parse these and fully serialise them
+  // later.
+
+  if (!subtable.Skip(8)) {
+    return OTS_FAILURE_MSG("Bad cmap subtable length");
+  }
+  uint32_t language = 0;
+  if (!subtable.ReadU32(&language)) {
+    return OTS_FAILURE_MSG("Can't read cmap subtable language");
+  }
+  if (language) {
+    return OTS_FAILURE_MSG("Cmap subtable language should be zero but is %d", language);
+  }
+
+  uint32_t num_groups = 0;
+  if (!subtable.ReadU32(&num_groups)) {
+    return OTS_FAILURE_MSG("Can't read number of groups in a cmap subtable");
+  }
+
+  // We limit the number of groups in the same way as in 3.10.12 tables. See
+  // the comment there in
+  if (num_groups == 0 || num_groups > kMaxCMAPGroups) {
+    return OTS_FAILURE_MSG("Bad number of groups (%d) in a cmap subtable", num_groups);
+  }
+
+  std::vector<ots::OpenTypeCMAPSubtableRange> &groups
+      = file->cmap->subtable_3_10_13;
+  groups.resize(num_groups);
+
+  for (unsigned i = 0; i < num_groups; ++i) {
+    if (!subtable.ReadU32(&groups[i].start_range) ||
+        !subtable.ReadU32(&groups[i].end_range) ||
+        !subtable.ReadU32(&groups[i].start_glyph_id)) {
+      return OTS_FAILURE_MSG("Can't read subrange structure in a cmap subtable");
+    }
+
+    // We conservatively limit all of the values to protect some parsers from
+    // overflows
+    if (groups[i].start_range > kUnicodeUpperLimit ||
+        groups[i].end_range > kUnicodeUpperLimit ||
+        groups[i].start_glyph_id > 0xFFFF) {
+      return OTS_FAILURE_MSG("Bad subrange with start_range=%d, end_range=%d, start_glyph_id=%d", groups[i].start_range, groups[i].end_range, groups[i].start_glyph_id);
+    }
+
+    if (groups[i].start_glyph_id >= num_glyphs) {
+      return OTS_FAILURE_MSG("Subrange starting glyph id too high (%d > %d)", groups[i].start_glyph_id, num_glyphs);
+    }
+  }
+
+  // the groups must be sorted by start code and may not overlap
+  for (unsigned i = 1; i < num_groups; ++i) {
+    if (groups[i].start_range <= groups[i - 1].start_range) {
+      return OTS_FAILURE_MSG("Overlapping subrange starts (%d >= %d)", groups[i]. start_range, groups[i-1].start_range);
+    }
+    if (groups[i].start_range <= groups[i - 1].end_range) {
+      return OTS_FAILURE_MSG("Overlapping subranges (%d <= %d)", groups[i].start_range, groups[i-1].end_range);
+    }
+  }
+
+  return true;
+}
+
+bool Parse0514(ots::OpenTypeFile *file,
+               const uint8_t *data, size_t length, uint16_t num_glyphs) {
+  // Unicode Variation Selector table
+  ots::Buffer subtable(data, length);
+
+  // Format 14 tables are simple. We parse these and fully serialise them
+  // later.
+
+  // Skip format (USHORT) and length (ULONG)
+  if (!subtable.Skip(6)) {
+    return OTS_FAILURE_MSG("Can't read start of cmap subtable");
+  }
+
+  uint32_t num_records = 0;
+  if (!subtable.ReadU32(&num_records)) {
+    return OTS_FAILURE_MSG("Can't read number of records in cmap subtable");
+  }
+  if (num_records == 0 || num_records > kMaxCMAPSelectorRecords) {
+    return OTS_FAILURE_MSG("Bad number of records (%d) in cmap subtable", num_records);
+  }
+
+  std::vector<ots::OpenTypeCMAPSubtableVSRecord>& records
+      = file->cmap->subtable_0_5_14;
+  records.resize(num_records);
+
+  for (unsigned i = 0; i < num_records; ++i) {
+    if (!subtable.ReadU24(&records[i].var_selector) ||
+        !subtable.ReadU32(&records[i].default_offset) ||
+        !subtable.ReadU32(&records[i].non_default_offset)) {
+      return OTS_FAILURE_MSG("Can't read record structure of record %d in cmap subtale", i);
+    }
+    // Checks the value of variation selector
+    if (!((records[i].var_selector >= kMongolianVSStart &&
+           records[i].var_selector <= kMongolianVSEnd) ||
+          (records[i].var_selector >= kVSStart &&
+           records[i].var_selector <= kVSEnd) ||
+          (records[i].var_selector >= kIVSStart &&
+           records[i].var_selector <= kIVSEnd))) {
+      return OTS_FAILURE_MSG("Bad record variation selector (%04X) in record %i", records[i].var_selector, i);
+    }
+    if (i > 0 &&
+        records[i-1].var_selector >= records[i].var_selector) {
+      return OTS_FAILURE_MSG("Out of order variation selector (%04X >= %04X) in record %d", records[i-1].var_selector, records[i].var_selector, i);
+    }
+
+    // Checks offsets
+    if (!records[i].default_offset && !records[i].non_default_offset) {
+      return OTS_FAILURE_MSG("No default aoffset in variation selector record %d", i);
+    }
+    if (records[i].default_offset &&
+        records[i].default_offset >= length) {
+      return OTS_FAILURE_MSG("Default offset too high (%d >= %ld) in record %d", records[i].default_offset, length, i);
+    }
+    if (records[i].non_default_offset &&
+        records[i].non_default_offset >= length) {
+      return OTS_FAILURE_MSG("Non default offset too high (%d >= %ld) in record %d", records[i].non_default_offset, length, i);
+    }
+  }
+
+  for (unsigned i = 0; i < num_records; ++i) {
+    // Checks default UVS table
+    if (records[i].default_offset) {
+      subtable.set_offset(records[i].default_offset);
+      uint32_t num_ranges = 0;
+      if (!subtable.ReadU32(&num_ranges)) {
+        return OTS_FAILURE_MSG("Can't read number of ranges in record %d", i);
+      }
+      if (!num_ranges || num_ranges > kMaxCMAPGroups) {
+        return OTS_FAILURE_MSG("number of ranges too high (%d > %d) in record %d", num_ranges, kMaxCMAPGroups, i);
+      }
+
+      uint32_t last_unicode_value = 0;
+      std::vector<ots::OpenTypeCMAPSubtableVSRange>& ranges
+          = records[i].ranges;
+      ranges.resize(num_ranges);
+
+      for (unsigned j = 0; j < num_ranges; ++j) {
+        if (!subtable.ReadU24(&ranges[j].unicode_value) ||
+            !subtable.ReadU8(&ranges[j].additional_count)) {
+          return OTS_FAILURE_MSG("Can't read range info in variation selector record %d", i);
+        }
+        const uint32_t check_value =
+            ranges[j].unicode_value + ranges[j].additional_count;
+        if (ranges[j].unicode_value == 0 ||
+            ranges[j].unicode_value > kUnicodeUpperLimit ||
+            check_value > kUVSUpperLimit ||
+            (last_unicode_value &&
+             ranges[j].unicode_value <= last_unicode_value)) {
+          return OTS_FAILURE_MSG("Bad Unicode value *%04X) in variation selector range %d record %d", ranges[j].unicode_value, j, i);
+        }
+        last_unicode_value = check_value;
+      }
+    }
+
+    // Checks non default UVS table
+    if (records[i].non_default_offset) {
+      subtable.set_offset(records[i].non_default_offset);
+      uint32_t num_mappings = 0;
+      if (!subtable.ReadU32(&num_mappings)) {
+        return OTS_FAILURE_MSG("Can't read number of mappings in variation selector record %d", i);
+      }
+      if (!num_mappings || num_mappings > kMaxCMAPGroups) {
+        return OTS_FAILURE_MSG("Number of mappings too high (%d) in variation selector record %d", num_mappings, i);
+      }
+
+      uint32_t last_unicode_value = 0;
+      std::vector<ots::OpenTypeCMAPSubtableVSMapping>& mappings
+          = records[i].mappings;
+      mappings.resize(num_mappings);
+
+      for (unsigned j = 0; j < num_mappings; ++j) {
+        if (!subtable.ReadU24(&mappings[j].unicode_value) ||
+            !subtable.ReadU16(&mappings[j].glyph_id)) {
+          return OTS_FAILURE_MSG("Can't read mapping %d in variation selector record %d", j, i);
+        }
+        if (mappings[j].glyph_id == 0 ||
+            mappings[j].unicode_value == 0 ||
+            mappings[j].unicode_value > kUnicodeUpperLimit ||
+            (last_unicode_value &&
+             mappings[j].unicode_value <= last_unicode_value)) {
+          return OTS_FAILURE_MSG("Bad mapping (%04X -> %d) in mapping %d of variation selector %d", mappings[j].unicode_value, mappings[j].glyph_id, j, i);
+        }
+        last_unicode_value = mappings[j].unicode_value;
+      }
+    }
+  }
+
+  if (subtable.offset() != length) {
+    return OTS_FAILURE_MSG("Bad subtable offset (%ld != %ld)", subtable.offset(), length);
+  }
+  file->cmap->subtable_0_5_14_length = subtable.offset();
+  return true;
+}
+
+bool Parse100(ots::OpenTypeFile *file, const uint8_t *data, size_t length) {
+  // Mac Roman table
+  ots::Buffer subtable(data, length);
+
+  if (!subtable.Skip(4)) {
+    return OTS_FAILURE_MSG("Bad cmap subtable");
+  }
+  uint16_t language = 0;
+  if (!subtable.ReadU16(&language)) {
+    return OTS_FAILURE_MSG("Can't read language in cmap subtable");
+  }
+  if (language) {
+    // simsun.ttf has non-zero language id.
+    OTS_WARNING("language id should be zero: %u", language);
+  }
+
+  file->cmap->subtable_1_0_0.reserve(kFormat0ArraySize);
+  for (size_t i = 0; i < kFormat0ArraySize; ++i) {
+    uint8_t glyph_id = 0;
+    if (!subtable.ReadU8(&glyph_id)) {
+      return OTS_FAILURE_MSG("Can't read glyph id at array[%ld] in cmap subtable", i);
+    }
+    file->cmap->subtable_1_0_0.push_back(glyph_id);
+  }
+
+  return true;
+}
+
+}  // namespace
+
+namespace ots {
+
+bool ots_cmap_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
+  Buffer table(data, length);
+  file->cmap = new OpenTypeCMAP;
+
+  uint16_t version = 0;
+  uint16_t num_tables = 0;
+  if (!table.ReadU16(&version) ||
+      !table.ReadU16(&num_tables)) {
+    return OTS_FAILURE_MSG("Can't read structure of cmap");
+  }
+
+  if (version != 0) {
+    return OTS_FAILURE_MSG("Non zero cmap version (%d)", version);
+  }
+  if (!num_tables) {
+    return OTS_FAILURE_MSG("No subtables in cmap!");
+  }
+
+  std::vector<CMAPSubtableHeader> subtable_headers;
+
+  // read the subtable headers
+  subtable_headers.reserve(num_tables);
+  for (unsigned i = 0; i < num_tables; ++i) {
+    CMAPSubtableHeader subt;
+
+    if (!table.ReadU16(&subt.platform) ||
+        !table.ReadU16(&subt.encoding) ||
+        !table.ReadU32(&subt.offset)) {
+      return OTS_FAILURE_MSG("Can't read subtable information cmap subtable %d", i);
+    }
+
+    subtable_headers.push_back(subt);
+  }
+
+  const size_t data_offset = table.offset();
+
+  // make sure that all the offsets are valid.
+  for (unsigned i = 0; i < num_tables; ++i) {
+    if (subtable_headers[i].offset > 1024 * 1024 * 1024) {
+      return OTS_FAILURE_MSG("Bad subtable offset in cmap subtable %d", i);
+    }
+    if (subtable_headers[i].offset < data_offset ||
+        subtable_headers[i].offset >= length) {
+      return OTS_FAILURE_MSG("Bad subtable offset (%d) in cmap subtable %d", subtable_headers[i].offset, i);
+    }
+  }
+
+  // the format of the table is the first couple of bytes in the table. The
+  // length of the table is stored in a format-specific way.
+  for (unsigned i = 0; i < num_tables; ++i) {
+    table.set_offset(subtable_headers[i].offset);
+    if (!table.ReadU16(&subtable_headers[i].format)) {
+      return OTS_FAILURE_MSG("Can't read cmap subtable header format %d", i);
+    }
+
+    uint16_t len = 0;
+    uint16_t lang = 0;
+    switch (subtable_headers[i].format) {
+      case 0:
+      case 4:
+        if (!table.ReadU16(&len)) {
+          return OTS_FAILURE_MSG("Can't read cmap subtable %d length", i);
+        }
+        if (!table.ReadU16(&lang)) {
+          return OTS_FAILURE_MSG("Can't read cmap subtable %d language", i);
+        }
+        subtable_headers[i].length = len;
+        subtable_headers[i].language = lang;
+        break;
+      case 12:
+      case 13:
+        if (!table.Skip(2)) {
+          return OTS_FAILURE_MSG("Bad cmap subtable %d structure", i);
+        }
+        if (!table.ReadU32(&subtable_headers[i].length)) {
+          return OTS_FAILURE_MSG("Can read cmap subtable %d length", i);
+        }
+        if (!table.ReadU32(&subtable_headers[i].language)) {
+          return OTS_FAILURE_MSG("Can't read cmap subtable %d language", i);
+        }
+        break;
+      case 14:
+        if (!table.ReadU32(&subtable_headers[i].length)) {
+          return OTS_FAILURE_MSG("Can't read cmap subtable %d length", i);
+        }
+        subtable_headers[i].language = 0;
+        break;
+      default:
+        subtable_headers[i].length = 0;
+        subtable_headers[i].language = 0;
+        break;
+    }
+  }
+
+  // check if the table is sorted first by platform ID, then by encoding ID.
+  uint32_t last_id = 0;
+  for (unsigned i = 0; i < num_tables; ++i) {
+    uint32_t current_id
+        = (subtable_headers[i].platform << 24)
+        + (subtable_headers[i].encoding << 16)
+        + subtable_headers[i].language;
+    if ((i != 0) && (last_id >= current_id)) {
+      return OTS_FAILURE_MSG("subtable %d with platform ID %d, encoding ID %d, language ID %d "
+                             "following subtable with platform ID %d, encoding ID %d, language ID %d",
+                             i,
+                             (uint8_t)(current_id >> 24), (uint8_t)(current_id >> 16), (uint8_t)(current_id),
+                             (uint8_t)(last_id >> 24), (uint8_t)(last_id >> 16), (uint8_t)(last_id));
+    }
+    last_id = current_id;
+  }
+
+  // Now, verify that all the lengths are sane
+  for (unsigned i = 0; i < num_tables; ++i) {
+    if (!subtable_headers[i].length) continue;
+    if (subtable_headers[i].length > 1024 * 1024 * 1024) {
+      return OTS_FAILURE_MSG("Bad cmap subtable %d length", i);
+    }
+    // We know that both the offset and length are < 1GB, so the following
+    // addition doesn't overflow
+    const uint32_t end_byte
+        = subtable_headers[i].offset + subtable_headers[i].length;
+    if (end_byte > length) {
+      return OTS_FAILURE_MSG("Over long cmap subtable %d @ %d for %d", i, subtable_headers[i].offset, subtable_headers[i].length);
+    }
+  }
+
+  // check that the cmap subtables are not overlapping.
+  std::set<std::pair<uint32_t, uint32_t> > uniq_checker;
+  std::vector<std::pair<uint32_t, uint8_t> > overlap_checker;
+  for (unsigned i = 0; i < num_tables; ++i) {
+    const uint32_t end_byte
+        = subtable_headers[i].offset + subtable_headers[i].length;
+
+    if (!uniq_checker.insert(std::make_pair(subtable_headers[i].offset,
+                                            end_byte)).second) {
+      // Sometimes Unicode table and MS table share exactly the same data.
+      // We'll allow this.
+      continue;
+    }
+    overlap_checker.push_back(
+        std::make_pair(subtable_headers[i].offset,
+                       static_cast<uint8_t>(1) /* start */));
+    overlap_checker.push_back(
+        std::make_pair(end_byte, static_cast<uint8_t>(0) /* end */));
+  }
+  std::sort(overlap_checker.begin(), overlap_checker.end());
+  int overlap_count = 0;
+  for (unsigned i = 0; i < overlap_checker.size(); ++i) {
+    overlap_count += (overlap_checker[i].second ? 1 : -1);
+    if (overlap_count > 1) {
+      return OTS_FAILURE_MSG("Excessive overlap count %d", overlap_count);
+    }
+  }
+
+  // we grab the number of glyphs in the file from the maxp table to make sure
+  // that the character map isn't referencing anything beyound this range.
+  if (!file->maxp) {
+    return OTS_FAILURE_MSG("No maxp table in font! Needed by cmap.");
+  }
+  const uint16_t num_glyphs = file->maxp->num_glyphs;
+
+  // We only support a subset of the possible character map tables. Microsoft
+  // 'strongly recommends' that everyone supports the Unicode BMP table with
+  // the UCS-4 table for non-BMP glyphs. We'll pass the following subtables:
+  //   Platform ID   Encoding ID  Format
+  //   0             0            4       (Unicode Default)
+  //   0             1            4       (Unicode 1.1)
+  //   0             3            4       (Unicode BMP)
+  //   0             3            12      (Unicode UCS-4)
+  //   0             5            14      (Unicode Variation Sequences)
+  //   1             0            0       (Mac Roman)
+  //   3             0            4       (MS Symbol)
+  //   3             1            4       (MS Unicode BMP)
+  //   3             10           12      (MS Unicode UCS-4)
+  //   3             10           13      (MS UCS-4 Fallback mapping)
+  //
+  // Note:
+  //  * 0-0-4 and 0-1-4 tables are (usually) written as a 3-1-4 table. If 3-1-4 table
+  //    also exists, the 0-0-4 or 0-1-4 tables are ignored.
+  //  * Unlike 0-0-4 table, 0-3-4 table is written as a 0-3-4 table.
+  //    Some fonts which include 0-5-14 table seems to be required 0-3-4
+  //    table. The 0-3-4 table will be wriiten even if 3-1-4 table also exists.
+  //  * 0-3-12 table is written as a 3-10-12 table. If 3-10-12 table also
+  //    exists, the 0-3-12 table is ignored.
+  //
+
+  for (unsigned i = 0; i < num_tables; ++i) {
+    if (subtable_headers[i].platform == 0) {
+      // Unicode platform
+
+      if ((subtable_headers[i].encoding == 0 || subtable_headers[i].encoding == 1) &&
+          (subtable_headers[i].format == 4)) {
+        // parse and output the 0-0-4 and 0-1-4 tables as 3-1-4 table. Sometimes the 0-0-4
+        // table actually points to MS symbol data and thus should be parsed as
+        // 3-0-4 table (e.g., marqueem.ttf and quixotic.ttf). This error will be
+        // recovered in ots_cmap_serialise().
+        if (!ParseFormat4(file, 3, 1, data + subtable_headers[i].offset,
+                      subtable_headers[i].length, num_glyphs)) {
+          return OTS_FAILURE_MSG("Failed to parse format 4 cmap subtable %d", i);
+        }
+      } else if ((subtable_headers[i].encoding == 3) &&
+                 (subtable_headers[i].format == 4)) {
+        // parse and output the 0-3-4 table as 0-3-4 table.
+        if (!ParseFormat4(file, 0, 3, data + subtable_headers[i].offset,
+                      subtable_headers[i].length, num_glyphs)) {
+          return OTS_FAILURE_MSG("Failed to parse format 4 cmap subtable %d", i);
+        }
+      } else if ((subtable_headers[i].encoding == 3) &&
+                 (subtable_headers[i].format == 12)) {
+        // parse and output the 0-3-12 table as 3-10-12 table.
+        if (!Parse31012(file, data + subtable_headers[i].offset,
+                        subtable_headers[i].length, num_glyphs)) {
+          return OTS_FAILURE_MSG("Failed to parse format 12 cmap subtable %d", i);
+        }
+      } else if ((subtable_headers[i].encoding == 5) &&
+                 (subtable_headers[i].format == 14)) {
+        if (!Parse0514(file, data + subtable_headers[i].offset,
+                       subtable_headers[i].length, num_glyphs)) {
+          return OTS_FAILURE_MSG("Failed to parse format 14 cmap subtable %d", i);
+        }
+      }
+    } else if (subtable_headers[i].platform == 1) {
+      // Mac platform
+
+      if ((subtable_headers[i].encoding == 0) &&
+          (subtable_headers[i].format == 0)) {
+        // parse and output the 1-0-0 table.
+        if (!Parse100(file, data + subtable_headers[i].offset,
+                      subtable_headers[i].length)) {
+          return OTS_FAILURE();
+        }
+      }
+    } else if (subtable_headers[i].platform == 3) {
+      // MS platform
+
+      switch (subtable_headers[i].encoding) {
+        case 0:
+        case 1:
+          if (subtable_headers[i].format == 4) {
+            // parse 3-0-4 or 3-1-4 table.
+            if (!ParseFormat4(file, subtable_headers[i].platform,
+                          subtable_headers[i].encoding,
+                          data + subtable_headers[i].offset,
+                          subtable_headers[i].length, num_glyphs)) {
+              return OTS_FAILURE();
+            }
+          }
+          break;
+        case 10:
+          if (subtable_headers[i].format == 12) {
+            file->cmap->subtable_3_10_12.clear();
+            if (!Parse31012(file, data + subtable_headers[i].offset,
+                            subtable_headers[i].length, num_glyphs)) {
+              return OTS_FAILURE();
+            }
+          } else if (subtable_headers[i].format == 13) {
+            file->cmap->subtable_3_10_13.clear();
+            if (!Parse31013(file, data + subtable_headers[i].offset,
+                            subtable_headers[i].length, num_glyphs)) {
+              return OTS_FAILURE();
+            }
+          }
+          break;
+      }
+    }
+  }
+
+  return true;
+}
+
+bool ots_cmap_should_serialise(OpenTypeFile *file) {
+  return file->cmap != NULL;
+}
+
+bool ots_cmap_serialise(OTSStream *out, OpenTypeFile *file) {
+  const bool have_034 = file->cmap->subtable_0_3_4_data != NULL;
+  const bool have_0514 = file->cmap->subtable_0_5_14.size() != 0;
+  const bool have_100 = file->cmap->subtable_1_0_0.size() != 0;
+  const bool have_304 = file->cmap->subtable_3_0_4_data != NULL;
+  // MS Symbol and MS Unicode tables should not co-exist.
+  // See the comment above in 0-0-4 parser.
+  const bool have_314 = (!have_304) && file->cmap->subtable_3_1_4_data;
+  const bool have_31012 = file->cmap->subtable_3_10_12.size() != 0;
+  const bool have_31013 = file->cmap->subtable_3_10_13.size() != 0;
+  const uint16_t num_subtables = static_cast<uint16_t>(have_034) +
+                                 static_cast<uint16_t>(have_0514) +
+                                 static_cast<uint16_t>(have_100) +
+                                 static_cast<uint16_t>(have_304) +
+                                 static_cast<uint16_t>(have_314) +
+                                 static_cast<uint16_t>(have_31012) +
+                                 static_cast<uint16_t>(have_31013);
+  const off_t table_start = out->Tell();
+
+  // Some fonts don't have 3-0-4 MS Symbol nor 3-1-4 Unicode BMP tables
+  // (e.g., old fonts for Mac). We don't support them.
+  if (!have_304 && !have_314 && !have_034 && !have_31012 && !have_31013) {
+    return OTS_FAILURE_MSG("no supported subtables were found");
+  }
+
+  if (!out->WriteU16(0) ||
+      !out->WriteU16(num_subtables)) {
+    return OTS_FAILURE();
+  }
+
+  const off_t record_offset = out->Tell();
+  if (!out->Pad(num_subtables * 8)) {
+    return OTS_FAILURE();
+  }
+
+  const off_t offset_034 = out->Tell();
+  if (have_034) {
+    if (!out->Write(file->cmap->subtable_0_3_4_data,
+                    file->cmap->subtable_0_3_4_length)) {
+      return OTS_FAILURE();
+    }
+  }
+
+  const off_t offset_0514 = out->Tell();
+  if (have_0514) {
+    const std::vector<ots::OpenTypeCMAPSubtableVSRecord> &records
+        = file->cmap->subtable_0_5_14;
+    const unsigned num_records = records.size();
+    if (!out->WriteU16(14) ||
+        !out->WriteU32(file->cmap->subtable_0_5_14_length) ||
+        !out->WriteU32(num_records)) {
+      return OTS_FAILURE();
+    }
+    for (unsigned i = 0; i < num_records; ++i) {
+      if (!out->WriteU24(records[i].var_selector) ||
+          !out->WriteU32(records[i].default_offset) ||
+          !out->WriteU32(records[i].non_default_offset)) {
+        return OTS_FAILURE();
+      }
+    }
+    for (unsigned i = 0; i < num_records; ++i) {
+      if (records[i].default_offset) {
+        const std::vector<ots::OpenTypeCMAPSubtableVSRange> &ranges
+            = records[i].ranges;
+        const unsigned num_ranges = ranges.size();
+        if (!out->Seek(records[i].default_offset + offset_0514) ||
+            !out->WriteU32(num_ranges)) {
+          return OTS_FAILURE();
+        }
+        for (unsigned j = 0; j < num_ranges; ++j) {
+          if (!out->WriteU24(ranges[j].unicode_value) ||
+              !out->WriteU8(ranges[j].additional_count)) {
+            return OTS_FAILURE();
+          }
+        }
+      }
+      if (records[i].non_default_offset) {
+        const std::vector<ots::OpenTypeCMAPSubtableVSMapping> &mappings
+            = records[i].mappings;
+        const unsigned num_mappings = mappings.size();
+        if (!out->Seek(records[i].non_default_offset + offset_0514) ||
+            !out->WriteU32(num_mappings)) {
+          return OTS_FAILURE();
+        }
+        for (unsigned j = 0; j < num_mappings; ++j) {
+          if (!out->WriteU24(mappings[j].unicode_value) ||
+              !out->WriteU16(mappings[j].glyph_id)) {
+            return OTS_FAILURE();
+          }
+        }
+      }
+    }
+  }
+
+  const off_t offset_100 = out->Tell();
+  if (have_100) {
+    if (!out->WriteU16(0) ||  // format
+        !out->WriteU16(6 + kFormat0ArraySize) ||  // length
+        !out->WriteU16(0)) {  // language
+      return OTS_FAILURE();
+    }
+    if (!out->Write(&(file->cmap->subtable_1_0_0[0]), kFormat0ArraySize)) {
+      return OTS_FAILURE();
+    }
+  }
+
+  const off_t offset_304 = out->Tell();
+  if (have_304) {
+    if (!out->Write(file->cmap->subtable_3_0_4_data,
+                    file->cmap->subtable_3_0_4_length)) {
+      return OTS_FAILURE();
+    }
+  }
+
+  const off_t offset_314 = out->Tell();
+  if (have_314) {
+    if (!out->Write(file->cmap->subtable_3_1_4_data,
+                    file->cmap->subtable_3_1_4_length)) {
+      return OTS_FAILURE();
+    }
+  }
+
+  const off_t offset_31012 = out->Tell();
+  if (have_31012) {
+    std::vector<OpenTypeCMAPSubtableRange> &groups
+        = file->cmap->subtable_3_10_12;
+    const unsigned num_groups = groups.size();
+    if (!out->WriteU16(12) ||
+        !out->WriteU16(0) ||
+        !out->WriteU32(num_groups * 12 + 16) ||
+        !out->WriteU32(0) ||
+        !out->WriteU32(num_groups)) {
+      return OTS_FAILURE();
+    }
+
+    for (unsigned i = 0; i < num_groups; ++i) {
+      if (!out->WriteU32(groups[i].start_range) ||
+          !out->WriteU32(groups[i].end_range) ||
+          !out->WriteU32(groups[i].start_glyph_id)) {
+        return OTS_FAILURE();
+      }
+    }
+  }
+
+  const off_t offset_31013 = out->Tell();
+  if (have_31013) {
+    std::vector<OpenTypeCMAPSubtableRange> &groups
+        = file->cmap->subtable_3_10_13;
+    const unsigned num_groups = groups.size();
+    if (!out->WriteU16(13) ||
+        !out->WriteU16(0) ||
+        !out->WriteU32(num_groups * 12 + 16) ||
+        !out->WriteU32(0) ||
+        !out->WriteU32(num_groups)) {
+      return OTS_FAILURE();
+    }
+
+    for (unsigned i = 0; i < num_groups; ++i) {
+      if (!out->WriteU32(groups[i].start_range) ||
+          !out->WriteU32(groups[i].end_range) ||
+          !out->WriteU32(groups[i].start_glyph_id)) {
+        return OTS_FAILURE();
+      }
+    }
+  }
+
+  const off_t table_end = out->Tell();
+  // We might have hanging bytes from the above's checksum which the OTSStream
+  // then merges into the table of offsets.
+  OTSStream::ChecksumState saved_checksum = out->SaveChecksumState();
+  out->ResetChecksum();
+
+  // Now seek back and write the table of offsets
+  if (!out->Seek(record_offset)) {
+    return OTS_FAILURE();
+  }
+
+  if (have_034) {
+    if (!out->WriteU16(0) ||
+        !out->WriteU16(3) ||
+        !out->WriteU32(offset_034 - table_start)) {
+      return OTS_FAILURE();
+    }
+  }
+
+  if (have_0514) {
+    if (!out->WriteU16(0) ||
+        !out->WriteU16(5) ||
+        !out->WriteU32(offset_0514 - table_start)) {
+      return OTS_FAILURE();
+    }
+  }
+
+  if (have_100) {
+    if (!out->WriteU16(1) ||
+        !out->WriteU16(0) ||
+        !out->WriteU32(offset_100 - table_start)) {
+      return OTS_FAILURE();
+    }
+  }
+
+  if (have_304) {
+    if (!out->WriteU16(3) ||
+        !out->WriteU16(0) ||
+        !out->WriteU32(offset_304 - table_start)) {
+      return OTS_FAILURE();
+    }
+  }
+
+  if (have_314) {
+    if (!out->WriteU16(3) ||
+        !out->WriteU16(1) ||
+        !out->WriteU32(offset_314 - table_start)) {
+      return OTS_FAILURE();
+    }
+  }
+
+  if (have_31012) {
+    if (!out->WriteU16(3) ||
+        !out->WriteU16(10) ||
+        !out->WriteU32(offset_31012 - table_start)) {
+      return OTS_FAILURE();
+    }
+  }
+
+  if (have_31013) {
+    if (!out->WriteU16(3) ||
+        !out->WriteU16(10) ||
+        !out->WriteU32(offset_31013 - table_start)) {
+      return OTS_FAILURE();
+    }
+  }
+
+  if (!out->Seek(table_end)) {
+    return OTS_FAILURE();
+  }
+  out->RestoreChecksum(saved_checksum);
+
+  return true;
+}
+
+void ots_cmap_free(OpenTypeFile *file) {
+  delete file->cmap;
+}
+
+}  // namespace ots
+
+#undef TABLE_NAME
diff --git a/third_party/ots/src/cmap.h b/third_party/ots/src/cmap.h
new file mode 100644
index 0000000..5b09556
--- /dev/null
+++ b/third_party/ots/src/cmap.h
@@ -0,0 +1,74 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_CMAP_H_
+#define OTS_CMAP_H_
+
+#include <vector>
+
+#include "ots.h"
+
+namespace ots {
+
+struct OpenTypeCMAPSubtableRange {
+  uint32_t start_range;
+  uint32_t end_range;
+  uint32_t start_glyph_id;
+};
+
+struct OpenTypeCMAPSubtableVSRange {
+  uint32_t unicode_value;
+  uint8_t additional_count;
+};
+
+struct OpenTypeCMAPSubtableVSMapping {
+  uint32_t unicode_value;
+  uint16_t glyph_id;
+};
+
+struct OpenTypeCMAPSubtableVSRecord {
+  uint32_t var_selector;
+  uint32_t default_offset;
+  uint32_t non_default_offset;
+  std::vector<OpenTypeCMAPSubtableVSRange> ranges;
+  std::vector<OpenTypeCMAPSubtableVSMapping> mappings;
+};
+
+struct OpenTypeCMAP {
+  OpenTypeCMAP()
+      : subtable_0_3_4_data(NULL),
+        subtable_0_3_4_length(0),
+        subtable_0_5_14_length(0),
+        subtable_3_0_4_data(NULL),
+        subtable_3_0_4_length(0),
+        subtable_3_1_4_data(NULL),
+        subtable_3_1_4_length(0) {
+  }
+
+  // Platform 0, Encoding 3, Format 4, Unicode BMP table.
+  const uint8_t *subtable_0_3_4_data;
+  size_t subtable_0_3_4_length;
+
+  // Platform 0, Encoding 5, Format 14, Unicode Variation Sequence table.
+  size_t subtable_0_5_14_length;
+  std::vector<OpenTypeCMAPSubtableVSRecord> subtable_0_5_14;
+
+  // Platform 3, Encoding 0, Format 4, MS Symbol table.
+  const uint8_t *subtable_3_0_4_data;
+  size_t subtable_3_0_4_length;
+  // Platform 3, Encoding 1, Format 4, MS Unicode BMP table.
+  const uint8_t *subtable_3_1_4_data;
+  size_t subtable_3_1_4_length;
+
+  // Platform 3, Encoding 10, Format 12, MS Unicode UCS-4 table.
+  std::vector<OpenTypeCMAPSubtableRange> subtable_3_10_12;
+  // Platform 3, Encoding 10, Format 13, MS UCS-4 Fallback table.
+  std::vector<OpenTypeCMAPSubtableRange> subtable_3_10_13;
+  // Platform 1, Encoding 0, Format 0, Mac Roman table.
+  std::vector<uint8_t> subtable_1_0_0;
+};
+
+}  // namespace ots
+
+#endif
diff --git a/third_party/ots/src/cvt.cc b/third_party/ots/src/cvt.cc
new file mode 100644
index 0000000..f83ad0ee
--- /dev/null
+++ b/third_party/ots/src/cvt.cc
@@ -0,0 +1,60 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cvt.h"
+
+// cvt - Control Value Table
+// http://www.microsoft.com/typography/otspec/cvt.htm
+
+#define TABLE_NAME "cvt"
+
+namespace ots {
+
+bool ots_cvt_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
+  Buffer table(data, length);
+
+  OpenTypeCVT *cvt = new OpenTypeCVT;
+  file->cvt = cvt;
+
+  if (length >= 128 * 1024u) {
+    return OTS_FAILURE_MSG("Length (%d) > 120K");  // almost all cvt tables are less than 4k bytes.
+  }
+
+  if (length % 2 != 0) {
+    return OTS_FAILURE_MSG("Uneven cvt length (%d)", length);
+  }
+
+  if (!table.Skip(length)) {
+    return OTS_FAILURE_MSG("Length too high");
+  }
+
+  cvt->data = data;
+  cvt->length = length;
+  return true;
+}
+
+bool ots_cvt_should_serialise(OpenTypeFile *file) {
+  if (!file->glyf) {
+    return false;  // this table is not for CFF fonts.
+  }
+  return file->cvt != NULL;
+}
+
+bool ots_cvt_serialise(OTSStream *out, OpenTypeFile *file) {
+  const OpenTypeCVT *cvt = file->cvt;
+
+  if (!out->Write(cvt->data, cvt->length)) {
+    return OTS_FAILURE_MSG("Failed to write CVT table");
+  }
+
+  return true;
+}
+
+void ots_cvt_free(OpenTypeFile *file) {
+  delete file->cvt;
+}
+
+}  // namespace ots
+
+#undef TABLE_NAME
diff --git a/third_party/ots/src/cvt.h b/third_party/ots/src/cvt.h
new file mode 100644
index 0000000..3c25f06f
--- /dev/null
+++ b/third_party/ots/src/cvt.h
@@ -0,0 +1,19 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_CVT_H_
+#define OTS_CVT_H_
+
+#include "ots.h"
+
+namespace ots {
+
+struct OpenTypeCVT {
+  const uint8_t *data;
+  uint32_t length;
+};
+
+}  // namespace ots
+
+#endif  // OTS_CVT_H_
diff --git a/third_party/ots/src/fpgm.cc b/third_party/ots/src/fpgm.cc
new file mode 100644
index 0000000..eba03e7
--- /dev/null
+++ b/third_party/ots/src/fpgm.cc
@@ -0,0 +1,54 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "fpgm.h"
+
+// fpgm - Font Program
+// http://www.microsoft.com/typography/otspec/fpgm.htm
+
+#define TABLE_NAME "fpgm"
+
+namespace ots {
+
+bool ots_fpgm_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
+  Buffer table(data, length);
+
+  OpenTypeFPGM *fpgm = new OpenTypeFPGM;
+  file->fpgm = fpgm;
+
+  if (length >= 128 * 1024u) {
+    return OTS_FAILURE_MSG("length (%ld) > 120", length);  // almost all fpgm tables are less than 5k bytes.
+  }
+
+  if (!table.Skip(length)) {
+    return OTS_FAILURE_MSG("Bad fpgm length");
+  }
+
+  fpgm->data = data;
+  fpgm->length = length;
+  return true;
+}
+
+bool ots_fpgm_should_serialise(OpenTypeFile *file) {
+  if (!file->glyf) return false;  // this table is not for CFF fonts.
+  return file->fpgm != NULL;
+}
+
+bool ots_fpgm_serialise(OTSStream *out, OpenTypeFile *file) {
+  const OpenTypeFPGM *fpgm = file->fpgm;
+
+  if (!out->Write(fpgm->data, fpgm->length)) {
+    return OTS_FAILURE_MSG("Failed to write fpgm");
+  }
+
+  return true;
+}
+
+void ots_fpgm_free(OpenTypeFile *file) {
+  delete file->fpgm;
+}
+
+}  // namespace ots
+
+#undef TABLE_NAME
diff --git a/third_party/ots/src/fpgm.h b/third_party/ots/src/fpgm.h
new file mode 100644
index 0000000..8fabac3
--- /dev/null
+++ b/third_party/ots/src/fpgm.h
@@ -0,0 +1,19 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_FPGM_H_
+#define OTS_FPGM_H_
+
+#include "ots.h"
+
+namespace ots {
+
+struct OpenTypeFPGM {
+  const uint8_t *data;
+  uint32_t length;
+};
+
+}  // namespace ots
+
+#endif  // OTS_FPGM_H_
diff --git a/third_party/ots/src/gasp.cc b/third_party/ots/src/gasp.cc
new file mode 100644
index 0000000..1e2327f
--- /dev/null
+++ b/third_party/ots/src/gasp.cc
@@ -0,0 +1,114 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "gasp.h"
+
+// gasp - Grid-fitting And Scan-conversion Procedure
+// http://www.microsoft.com/typography/otspec/gasp.htm
+
+#define TABLE_NAME "gasp"
+
+#define DROP_THIS_TABLE(...) \
+  do { \
+    OTS_FAILURE_MSG_(file, TABLE_NAME ": " __VA_ARGS__); \
+    OTS_FAILURE_MSG("Table discarded"); \
+    delete file->gasp; \
+    file->gasp = 0; \
+  } while (0)
+
+namespace ots {
+
+bool ots_gasp_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
+  Buffer table(data, length);
+
+  OpenTypeGASP *gasp = new OpenTypeGASP;
+  file->gasp = gasp;
+
+  uint16_t num_ranges = 0;
+  if (!table.ReadU16(&gasp->version) ||
+      !table.ReadU16(&num_ranges)) {
+    return OTS_FAILURE_MSG("Failed to read table header");
+  }
+
+  if (gasp->version > 1) {
+    // Lots of Linux fonts have bad version numbers...
+    DROP_THIS_TABLE("bad version: %u", gasp->version);
+    return true;
+  }
+
+  if (num_ranges == 0) {
+    DROP_THIS_TABLE("num_ranges is zero");
+    return true;
+  }
+
+  gasp->gasp_ranges.reserve(num_ranges);
+  for (unsigned i = 0; i < num_ranges; ++i) {
+    uint16_t max_ppem = 0;
+    uint16_t behavior = 0;
+    if (!table.ReadU16(&max_ppem) ||
+        !table.ReadU16(&behavior)) {
+      return OTS_FAILURE_MSG("Failed to read subrange %d", i);
+    }
+    if ((i > 0) && (gasp->gasp_ranges[i - 1].first >= max_ppem)) {
+      // The records in the gaspRange[] array must be sorted in order of
+      // increasing rangeMaxPPEM value.
+      DROP_THIS_TABLE("ranges are not sorted");
+      return true;
+    }
+    if ((i == num_ranges - 1u) &&  // never underflow.
+        (max_ppem != 0xffffu)) {
+      DROP_THIS_TABLE("The last record should be 0xFFFF as a sentinel value "
+                  "for rangeMaxPPEM");
+      return true;
+    }
+
+    if (behavior >> 8) {
+      OTS_WARNING("undefined bits are used: %x", behavior);
+      // mask undefined bits.
+      behavior &= 0x000fu;
+    }
+
+    if (gasp->version == 0 && (behavior >> 2) != 0) {
+      OTS_WARNING("changed the version number to 1");
+      gasp->version = 1;
+    }
+
+    gasp->gasp_ranges.push_back(std::make_pair(max_ppem, behavior));
+  }
+
+  return true;
+}
+
+bool ots_gasp_should_serialise(OpenTypeFile *file) {
+  return file->gasp != NULL;
+}
+
+bool ots_gasp_serialise(OTSStream *out, OpenTypeFile *file) {
+  const OpenTypeGASP *gasp = file->gasp;
+
+  const uint16_t num_ranges = static_cast<uint16_t>(gasp->gasp_ranges.size());
+  if (num_ranges != gasp->gasp_ranges.size() ||
+      !out->WriteU16(gasp->version) ||
+      !out->WriteU16(num_ranges)) {
+    return OTS_FAILURE_MSG("failed to write gasp header");
+  }
+
+  for (uint16_t i = 0; i < num_ranges; ++i) {
+    if (!out->WriteU16(gasp->gasp_ranges[i].first) ||
+        !out->WriteU16(gasp->gasp_ranges[i].second)) {
+      return OTS_FAILURE_MSG("Failed to write gasp subtable %d", i);
+    }
+  }
+
+  return true;
+}
+
+void ots_gasp_free(OpenTypeFile *file) {
+  delete file->gasp;
+}
+
+}  // namespace ots
+
+#undef TABLE_NAME
+#undef DROP_THIS_TABLE
diff --git a/third_party/ots/src/gasp.h b/third_party/ots/src/gasp.h
new file mode 100644
index 0000000..48d7e7c
--- /dev/null
+++ b/third_party/ots/src/gasp.h
@@ -0,0 +1,24 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_GASP_H_
+#define OTS_GASP_H_
+
+#include <new>
+#include <utility>
+#include <vector>
+
+#include "ots.h"
+
+namespace ots {
+
+struct OpenTypeGASP {
+  uint16_t version;
+  // A array of (max PPEM, GASP behavior) pairs.
+  std::vector<std::pair<uint16_t, uint16_t> > gasp_ranges;
+};
+
+}  // namespace ots
+
+#endif  // OTS_GASP_H_
diff --git a/third_party/ots/src/gdef.cc b/third_party/ots/src/gdef.cc
new file mode 100644
index 0000000..4553d58fb
--- /dev/null
+++ b/third_party/ots/src/gdef.cc
@@ -0,0 +1,388 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "gdef.h"
+
+#include <limits>
+#include <vector>
+
+#include "gpos.h"
+#include "gsub.h"
+#include "layout.h"
+#include "maxp.h"
+
+// GDEF - The Glyph Definition Table
+// http://www.microsoft.com/typography/otspec/gdef.htm
+
+#define TABLE_NAME "GDEF"
+
+namespace {
+
+// The maximum class value in class definition tables.
+const uint16_t kMaxClassDefValue = 0xFFFF;
+// The maximum class value in the glyph class definision table.
+const uint16_t kMaxGlyphClassDefValue = 4;
+// The maximum format number of caret value tables.
+// We don't support format 3 for now. See the comment in
+// ParseLigCaretListTable() for the reason.
+const uint16_t kMaxCaretValueFormat = 2;
+
+bool ParseGlyphClassDefTable(ots::OpenTypeFile *file, const uint8_t *data,
+                             size_t length, const uint16_t num_glyphs) {
+  return ots::ParseClassDefTable(file, data, length, num_glyphs,
+                                 kMaxGlyphClassDefValue);
+}
+
+bool ParseAttachListTable(ots::OpenTypeFile *file, const uint8_t *data,
+                          size_t length, const uint16_t num_glyphs) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t offset_coverage = 0;
+  uint16_t glyph_count = 0;
+  if (!subtable.ReadU16(&offset_coverage) ||
+      !subtable.ReadU16(&glyph_count)) {
+    return OTS_FAILURE_MSG("Failed to read gdef header");
+  }
+  const unsigned attach_points_end =
+      2 * static_cast<unsigned>(glyph_count) + 4;
+  if (attach_points_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE_MSG("Bad glyph count in gdef");
+  }
+  if (offset_coverage == 0 || offset_coverage >= length ||
+      offset_coverage < attach_points_end) {
+    return OTS_FAILURE_MSG("Bad coverage offset %d", offset_coverage);
+  }
+  if (glyph_count > num_glyphs) {
+    return OTS_FAILURE_MSG("Bad glyph count %u", glyph_count);
+  }
+
+  std::vector<uint16_t> attach_points;
+  attach_points.resize(glyph_count);
+  for (unsigned i = 0; i < glyph_count; ++i) {
+    if (!subtable.ReadU16(&attach_points[i])) {
+      return OTS_FAILURE_MSG("Can't read attachment point %d", i);
+    }
+    if (attach_points[i] >= length ||
+        attach_points[i] < attach_points_end) {
+      return OTS_FAILURE_MSG("Bad attachment point %d of %d", i, attach_points[i]);
+    }
+  }
+
+  // Parse coverage table
+  if (!ots::ParseCoverageTable(file, data + offset_coverage,
+                               length - offset_coverage, num_glyphs)) {
+    return OTS_FAILURE_MSG("Bad coverage table");
+  }
+
+  // Parse attach point table
+  for (unsigned i = 0; i < attach_points.size(); ++i) {
+    subtable.set_offset(attach_points[i]);
+    uint16_t point_count = 0;
+    if (!subtable.ReadU16(&point_count)) {
+      return OTS_FAILURE_MSG("Can't read point count %d", i);
+    }
+    if (point_count == 0) {
+      return OTS_FAILURE_MSG("zero point count %d", i);
+    }
+    uint16_t last_point_index = 0;
+    uint16_t point_index = 0;
+    for (unsigned j = 0; j < point_count; ++j) {
+      if (!subtable.ReadU16(&point_index)) {
+        return OTS_FAILURE_MSG("Can't read point index %d in point %d", j, i);
+      }
+      // Contour point indeces are in increasing numerical order
+      if (last_point_index != 0 && last_point_index >= point_index) {
+        return OTS_FAILURE_MSG("bad contour indeces: %u >= %u",
+                    last_point_index, point_index);
+      }
+      last_point_index = point_index;
+    }
+  }
+  return true;
+}
+
+bool ParseLigCaretListTable(ots::OpenTypeFile *file, const uint8_t *data,
+                            size_t length, const uint16_t num_glyphs) {
+  ots::Buffer subtable(data, length);
+  uint16_t offset_coverage = 0;
+  uint16_t lig_glyph_count = 0;
+  if (!subtable.ReadU16(&offset_coverage) ||
+      !subtable.ReadU16(&lig_glyph_count)) {
+    return OTS_FAILURE_MSG("Can't read caret structure");
+  }
+  const unsigned lig_glyphs_end =
+      2 * static_cast<unsigned>(lig_glyph_count) + 4;
+  if (lig_glyphs_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE_MSG("Bad caret structure");
+  }
+  if (offset_coverage == 0 || offset_coverage >= length ||
+      offset_coverage < lig_glyphs_end) {
+    return OTS_FAILURE_MSG("Bad caret coverate offset %d", offset_coverage);
+  }
+  if (lig_glyph_count > num_glyphs) {
+    return OTS_FAILURE_MSG("bad ligature glyph count: %u", lig_glyph_count);
+  }
+
+  std::vector<uint16_t> lig_glyphs;
+  lig_glyphs.resize(lig_glyph_count);
+  for (unsigned i = 0; i < lig_glyph_count; ++i) {
+    if (!subtable.ReadU16(&lig_glyphs[i])) {
+      return OTS_FAILURE_MSG("Can't read ligature glyph location %d", i);
+    }
+    if (lig_glyphs[i] >= length || lig_glyphs[i] < lig_glyphs_end) {
+      return OTS_FAILURE_MSG("Bad ligature glyph location %d in glyph %d", lig_glyphs[i], i);
+    }
+  }
+
+  // Parse coverage table
+  if (!ots::ParseCoverageTable(file, data + offset_coverage,
+                               length - offset_coverage, num_glyphs)) {
+    return OTS_FAILURE_MSG("Can't parse caret coverage table");
+  }
+
+  // Parse ligature glyph table
+  for (unsigned i = 0; i < lig_glyphs.size(); ++i) {
+    subtable.set_offset(lig_glyphs[i]);
+    uint16_t caret_count = 0;
+    if (!subtable.ReadU16(&caret_count)) {
+      return OTS_FAILURE_MSG("Can't read caret count for glyph %d", i);
+    }
+    if (caret_count == 0) {
+      return OTS_FAILURE_MSG("bad caret value count: %u", caret_count);
+    }
+
+    std::vector<uint16_t> caret_value_offsets;
+    caret_value_offsets.resize(caret_count);
+    unsigned caret_value_offsets_end = 2 * static_cast<unsigned>(caret_count) + 2;
+    for (unsigned j = 0; j < caret_count; ++j) {
+      if (!subtable.ReadU16(&caret_value_offsets[j])) {
+        return OTS_FAILURE_MSG("Can't read caret offset %d for glyph %d", j, i);
+      }
+      if (caret_value_offsets[j] >= length || caret_value_offsets[j] < caret_value_offsets_end) {
+        return OTS_FAILURE_MSG("Bad caret offset %d for caret %d glyph %d", caret_value_offsets[j], j, i);
+      }
+    }
+
+    // Parse caret values table
+    for (unsigned j = 0; j < caret_count; ++j) {
+      subtable.set_offset(lig_glyphs[i] + caret_value_offsets[j]);
+      uint16_t caret_format = 0;
+      if (!subtable.ReadU16(&caret_format)) {
+        return OTS_FAILURE_MSG("Can't read caret values table %d in glyph %d", j, i);
+      }
+      // TODO(bashi): We only support caret value format 1 and 2 for now
+      // because there are no fonts which contain caret value format 3
+      // as far as we investigated.
+      if (caret_format == 0 || caret_format > kMaxCaretValueFormat) {
+        return OTS_FAILURE_MSG("bad caret value format: %u", caret_format);
+      }
+      // CaretValueFormats contain a 2-byte field which could be
+      // arbitrary value.
+      if (!subtable.Skip(2)) {
+        return OTS_FAILURE_MSG("Bad caret value table structure %d in glyph %d", j, i);
+      }
+    }
+  }
+  return true;
+}
+
+bool ParseMarkAttachClassDefTable(ots::OpenTypeFile *file, const uint8_t *data,
+                                  size_t length, const uint16_t num_glyphs) {
+  return ots::ParseClassDefTable(file, data, length, num_glyphs, kMaxClassDefValue);
+}
+
+bool ParseMarkGlyphSetsDefTable(ots::OpenTypeFile *file, const uint8_t *data,
+                                size_t length, const uint16_t num_glyphs) {
+  ots::Buffer subtable(data, length);
+  uint16_t format = 0;
+  uint16_t mark_set_count = 0;
+  if (!subtable.ReadU16(&format) ||
+      !subtable.ReadU16(&mark_set_count)) {
+    return OTS_FAILURE_MSG("Can' read mark glyph table structure");
+  }
+  if (format != 1) {
+    return OTS_FAILURE_MSG("bad mark glyph set table format: %u", format);
+  }
+
+  const unsigned mark_sets_end = 2 * static_cast<unsigned>(mark_set_count) + 4;
+  if (mark_sets_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE_MSG("Bad mark_set %d", mark_sets_end);
+  }
+  for (unsigned i = 0; i < mark_set_count; ++i) {
+    uint32_t offset_coverage = 0;
+    if (!subtable.ReadU32(&offset_coverage)) {
+      return OTS_FAILURE_MSG("Can't read covrage location for mark set %d", i);
+    }
+    if (offset_coverage >= length ||
+        offset_coverage < mark_sets_end) {
+      return OTS_FAILURE_MSG("Bad coverage location %d for mark set %d", offset_coverage, i);
+    }
+    if (!ots::ParseCoverageTable(file, data + offset_coverage,
+                                 length - offset_coverage, num_glyphs)) {
+      return OTS_FAILURE_MSG("Failed to parse coverage table for mark set %d", i);
+    }
+  }
+  file->gdef->num_mark_glyph_sets = mark_set_count;
+  return true;
+}
+
+}  // namespace
+
+#define DROP_THIS_TABLE(msg_) \
+  do { \
+    OTS_FAILURE_MSG(msg_ ", table discarded"); \
+    file->gdef->data = 0; \
+    file->gdef->length = 0; \
+  } while (0)
+
+namespace ots {
+
+bool ots_gdef_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
+  // Grab the number of glyphs in the file from the maxp table to check
+  // GlyphIDs in GDEF table.
+  if (!file->maxp) {
+    return OTS_FAILURE_MSG("No maxp table in font, needed by GDEF");
+  }
+  const uint16_t num_glyphs = file->maxp->num_glyphs;
+
+  Buffer table(data, length);
+
+  OpenTypeGDEF *gdef = new OpenTypeGDEF;
+  file->gdef = gdef;
+
+  uint32_t version = 0;
+  if (!table.ReadU32(&version)) {
+    DROP_THIS_TABLE("Incomplete table");
+    return true;
+  }
+  if (version < 0x00010000 || version == 0x00010001) {
+    DROP_THIS_TABLE("Bad version");
+    return true;
+  }
+
+  if (version >= 0x00010002) {
+    gdef->version_2 = true;
+  }
+
+  uint16_t offset_glyph_class_def = 0;
+  uint16_t offset_attach_list = 0;
+  uint16_t offset_lig_caret_list = 0;
+  uint16_t offset_mark_attach_class_def = 0;
+  if (!table.ReadU16(&offset_glyph_class_def) ||
+      !table.ReadU16(&offset_attach_list) ||
+      !table.ReadU16(&offset_lig_caret_list) ||
+      !table.ReadU16(&offset_mark_attach_class_def)) {
+    DROP_THIS_TABLE("Incomplete table");
+    return true;
+  }
+  uint16_t offset_mark_glyph_sets_def = 0;
+  if (gdef->version_2) {
+    if (!table.ReadU16(&offset_mark_glyph_sets_def)) {
+      DROP_THIS_TABLE("Incomplete table");
+      return true;
+    }
+  }
+
+  unsigned gdef_header_end = 4 + 4 * 2;
+  if (gdef->version_2)
+    gdef_header_end += 2;
+
+  // Parse subtables
+  if (offset_glyph_class_def) {
+    if (offset_glyph_class_def >= length ||
+        offset_glyph_class_def < gdef_header_end) {
+      DROP_THIS_TABLE("Invalid offset to glyph classes");
+      return true;
+    }
+    if (!ParseGlyphClassDefTable(file, data + offset_glyph_class_def,
+                                 length - offset_glyph_class_def,
+                                 num_glyphs)) {
+      DROP_THIS_TABLE("Invalid glyph classes");
+      return true;
+    }
+    gdef->has_glyph_class_def = true;
+  }
+
+  if (offset_attach_list) {
+    if (offset_attach_list >= length ||
+        offset_attach_list < gdef_header_end) {
+      DROP_THIS_TABLE("Invalid offset to attachment list");
+      return true;
+    }
+    if (!ParseAttachListTable(file, data + offset_attach_list,
+                              length - offset_attach_list,
+                              num_glyphs)) {
+      DROP_THIS_TABLE("Invalid attachment list");
+      return true;
+    }
+  }
+
+  if (offset_lig_caret_list) {
+    if (offset_lig_caret_list >= length ||
+        offset_lig_caret_list < gdef_header_end) {
+      DROP_THIS_TABLE("Invalid offset to ligature caret list");
+      return true;
+    }
+    if (!ParseLigCaretListTable(file, data + offset_lig_caret_list,
+                              length - offset_lig_caret_list,
+                              num_glyphs)) {
+      DROP_THIS_TABLE("Invalid ligature caret list");
+      return true;
+    }
+  }
+
+  if (offset_mark_attach_class_def) {
+    if (offset_mark_attach_class_def >= length ||
+        offset_mark_attach_class_def < gdef_header_end) {
+      return OTS_FAILURE_MSG("Invalid offset to mark attachment list");
+    }
+    if (!ParseMarkAttachClassDefTable(file,
+                                      data + offset_mark_attach_class_def,
+                                      length - offset_mark_attach_class_def,
+                                      num_glyphs)) {
+      DROP_THIS_TABLE("Invalid mark attachment list");
+      return true;
+    }
+    gdef->has_mark_attachment_class_def = true;
+  }
+
+  if (offset_mark_glyph_sets_def) {
+    if (offset_mark_glyph_sets_def >= length ||
+        offset_mark_glyph_sets_def < gdef_header_end) {
+      return OTS_FAILURE_MSG("invalid offset to mark glyph sets");
+    }
+    if (!ParseMarkGlyphSetsDefTable(file,
+                                    data + offset_mark_glyph_sets_def,
+                                    length - offset_mark_glyph_sets_def,
+                                    num_glyphs)) {
+      DROP_THIS_TABLE("Invalid mark glyph sets");
+      return true;
+    }
+    gdef->has_mark_glyph_sets_def = true;
+  }
+  gdef->data = data;
+  gdef->length = length;
+  return true;
+}
+
+bool ots_gdef_should_serialise(OpenTypeFile *file) {
+  return file->gdef != NULL && file->gdef->data != NULL;
+}
+
+bool ots_gdef_serialise(OTSStream *out, OpenTypeFile *file) {
+  if (!out->Write(file->gdef->data, file->gdef->length)) {
+    return OTS_FAILURE_MSG("Failed to write GDEF table");
+  }
+
+  return true;
+}
+
+void ots_gdef_free(OpenTypeFile *file) {
+  delete file->gdef;
+}
+
+}  // namespace ots
+
+#undef TABLE_NAME
+#undef DROP_THIS_TABLE
diff --git a/third_party/ots/src/gdef.h b/third_party/ots/src/gdef.h
new file mode 100644
index 0000000..f46f419
--- /dev/null
+++ b/third_party/ots/src/gdef.h
@@ -0,0 +1,36 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_GDEF_H_
+#define OTS_GDEF_H_
+
+#include "ots.h"
+
+namespace ots {
+
+struct OpenTypeGDEF {
+  OpenTypeGDEF()
+      : version_2(false),
+        has_glyph_class_def(false),
+        has_mark_attachment_class_def(false),
+        has_mark_glyph_sets_def(false),
+        num_mark_glyph_sets(0),
+        data(NULL),
+        length(0) {
+  }
+
+  bool version_2;
+  bool has_glyph_class_def;
+  bool has_mark_attachment_class_def;
+  bool has_mark_glyph_sets_def;
+  uint16_t num_mark_glyph_sets;
+
+  const uint8_t *data;
+  size_t length;
+};
+
+}  // namespace ots
+
+#endif
+
diff --git a/third_party/ots/src/glyf.cc b/third_party/ots/src/glyf.cc
new file mode 100644
index 0000000..3579397
--- /dev/null
+++ b/third_party/ots/src/glyf.cc
@@ -0,0 +1,298 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "glyf.h"
+
+#include <algorithm>
+#include <limits>
+
+#include "head.h"
+#include "loca.h"
+#include "maxp.h"
+
+// glyf - Glyph Data
+// http://www.microsoft.com/typography/otspec/glyf.htm
+
+#define TABLE_NAME "glyf"
+
+namespace {
+
+bool ParseFlagsForSimpleGlyph(ots::OpenTypeFile *file,
+                              ots::Buffer *table,
+                              uint32_t gly_length,
+                              uint32_t num_flags,
+                              uint32_t *flags_count_logical,
+                              uint32_t *flags_count_physical,
+                              uint32_t *xy_coordinates_length) {
+  uint8_t flag = 0;
+  if (!table->ReadU8(&flag)) {
+    return OTS_FAILURE_MSG("Can't read flag");
+  }
+
+  uint32_t delta = 0;
+  if (flag & (1u << 1)) {  // x-Short
+    ++delta;
+  } else if (!(flag & (1u << 4))) {
+    delta += 2;
+  }
+
+  if (flag & (1u << 2)) {  // y-Short
+    ++delta;
+  } else if (!(flag & (1u << 5))) {
+    delta += 2;
+  }
+
+  if (flag & (1u << 3)) {  // repeat
+    if (*flags_count_logical + 1 >= num_flags) {
+      return OTS_FAILURE_MSG("Count too high (%d + 1 >= %d)", *flags_count_logical, num_flags);
+    }
+    uint8_t repeat = 0;
+    if (!table->ReadU8(&repeat)) {
+      return OTS_FAILURE_MSG("Can't read repeat value");
+    }
+    if (repeat == 0) {
+      return OTS_FAILURE_MSG("Zero repeat");
+    }
+    delta += (delta * repeat);
+
+    *flags_count_logical += repeat;
+    if (*flags_count_logical >= num_flags) {
+      return OTS_FAILURE_MSG("Count too high (%d >= %d)", *flags_count_logical, num_flags);
+    }
+    ++(*flags_count_physical);
+  }
+
+  if ((flag & (1u << 6)) || (flag & (1u << 7))) {  // reserved flags
+    return OTS_FAILURE_MSG("Bad flag value (%d)", flag);
+  }
+
+  *xy_coordinates_length += delta;
+  if (gly_length < *xy_coordinates_length) {
+    return OTS_FAILURE_MSG("Glyph coordinates length too low (%d < %d)", gly_length, *xy_coordinates_length);
+  }
+
+  return true;
+}
+
+bool ParseSimpleGlyph(ots::OpenTypeFile *file, const uint8_t *data,
+                      ots::Buffer *table, int16_t num_contours,
+                      uint32_t gly_offset, uint32_t gly_length,
+                      uint32_t *new_size) {
+  ots::OpenTypeGLYF *glyf = file->glyf;
+
+  // read the end-points array
+  uint16_t num_flags = 0;
+  for (int i = 0; i < num_contours; ++i) {
+    uint16_t tmp_index = 0;
+    if (!table->ReadU16(&tmp_index)) {
+      return OTS_FAILURE_MSG("Can't read contour index %d", i);
+    }
+    if (tmp_index == 0xffffu) {
+      return OTS_FAILURE_MSG("Bad contour index %d", i);
+    }
+    // check if the indices are monotonically increasing
+    if (i && (tmp_index + 1 <= num_flags)) {
+      return OTS_FAILURE_MSG("Decreasing contour index %d + 1 <= %d", tmp_index, num_flags);
+    }
+    num_flags = tmp_index + 1;
+  }
+
+  uint16_t bytecode_length = 0;
+  if (!table->ReadU16(&bytecode_length)) {
+    return OTS_FAILURE_MSG("Can't read bytecode length");
+  }
+  if ((file->maxp->version_1) &&
+      (file->maxp->max_size_glyf_instructions < bytecode_length)) {
+    return OTS_FAILURE_MSG("Bytecode length too high %d", bytecode_length);
+  }
+
+  const uint32_t gly_header_length = 10 + num_contours * 2 + 2;
+  if (gly_length < (gly_header_length + bytecode_length)) {
+    return OTS_FAILURE_MSG("Glyph header length too high %d", gly_header_length);
+  }
+
+  glyf->iov.push_back(std::make_pair(
+      data + gly_offset,
+      static_cast<size_t>(gly_header_length + bytecode_length)));
+
+  if (!table->Skip(bytecode_length)) {
+    return OTS_FAILURE_MSG("Can't skip bytecode of length %d", bytecode_length);
+  }
+
+  uint32_t flags_count_physical = 0;  // on memory
+  uint32_t xy_coordinates_length = 0;
+  for (uint32_t flags_count_logical = 0;
+       flags_count_logical < num_flags;
+       ++flags_count_logical, ++flags_count_physical) {
+    if (!ParseFlagsForSimpleGlyph(file,
+                                  table,
+                                  gly_length,
+                                  num_flags,
+                                  &flags_count_logical,
+                                  &flags_count_physical,
+                                  &xy_coordinates_length)) {
+      return OTS_FAILURE_MSG("Failed to parse glyph flags %d", flags_count_logical);
+    }
+  }
+
+  if (gly_length < (gly_header_length + bytecode_length +
+                    flags_count_physical + xy_coordinates_length)) {
+    return OTS_FAILURE_MSG("Glyph too short %d", gly_length);
+  }
+
+  if (gly_length - (gly_header_length + bytecode_length +
+                    flags_count_physical + xy_coordinates_length) > 3) {
+    // We allow 0-3 bytes difference since gly_length is 4-bytes aligned,
+    // zero-padded length.
+    return OTS_FAILURE_MSG("Invalid glyph length %d", gly_length);
+  }
+
+  glyf->iov.push_back(std::make_pair(
+      data + gly_offset + gly_header_length + bytecode_length,
+      static_cast<size_t>(flags_count_physical + xy_coordinates_length)));
+
+  *new_size
+      = gly_header_length + flags_count_physical + xy_coordinates_length + bytecode_length;
+
+  return true;
+}
+
+}  // namespace
+
+namespace ots {
+
+bool ots_glyf_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
+  Buffer table(data, length);
+
+  if (!file->maxp || !file->loca || !file->head) {
+    return OTS_FAILURE_MSG("Missing maxp or loca or head table needed by glyf table");
+  }
+
+  OpenTypeGLYF *glyf = new OpenTypeGLYF;
+  file->glyf = glyf;
+
+  const unsigned num_glyphs = file->maxp->num_glyphs;
+  std::vector<uint32_t> &offsets = file->loca->offsets;
+
+  if (offsets.size() != num_glyphs + 1) {
+    return OTS_FAILURE_MSG("Invalide glyph offsets size %ld != %d", offsets.size(), num_glyphs + 1);
+  }
+
+  std::vector<uint32_t> resulting_offsets(num_glyphs + 1);
+  uint32_t current_offset = 0;
+
+  for (unsigned i = 0; i < num_glyphs; ++i) {
+    const unsigned gly_offset = offsets[i];
+    // The LOCA parser checks that these values are monotonic
+    const unsigned gly_length = offsets[i + 1] - offsets[i];
+    if (!gly_length) {
+      // this glyph has no outline (e.g. the space charactor)
+      resulting_offsets[i] = current_offset;
+      continue;
+    }
+
+    if (gly_offset >= length) {
+      return OTS_FAILURE_MSG("Glyph %d offset %d too high %ld", i, gly_offset, length);
+    }
+    // Since these are unsigned types, the compiler is not allowed to assume
+    // that they never overflow.
+    if (gly_offset + gly_length < gly_offset) {
+      return OTS_FAILURE_MSG("Glyph %d length (%d < 0)!", i, gly_length);
+    }
+    if (gly_offset + gly_length > length) {
+      return OTS_FAILURE_MSG("Glyph %d length %d too high", i, gly_length);
+    }
+
+    table.set_offset(gly_offset);
+    int16_t num_contours, xmin, ymin, xmax, ymax;
+    if (!table.ReadS16(&num_contours) ||
+        !table.ReadS16(&xmin) ||
+        !table.ReadS16(&ymin) ||
+        !table.ReadS16(&xmax) ||
+        !table.ReadS16(&ymax)) {
+      return OTS_FAILURE_MSG("Can't read glyph %d header", i);
+    }
+
+    if (num_contours <= -2) {
+      // -2, -3, -4, ... are reserved for future use.
+      return OTS_FAILURE_MSG("Bad number of contours %d in glyph %d", num_contours, i);
+    }
+
+    // workaround for fonts in http://www.princexml.com/fonts/
+    if ((xmin == 32767) &&
+        (xmax == -32767) &&
+        (ymin == 32767) &&
+        (ymax == -32767)) {
+      OTS_WARNING("bad xmin/xmax/ymin/ymax values");
+      xmin = xmax = ymin = ymax = 0;
+    }
+
+    if (xmin > xmax || ymin > ymax) {
+      return OTS_FAILURE_MSG("Bad bounding box values bl=(%d, %d), tr=(%d, %d) in glyph %d", xmin, ymin, xmax, ymax, i);
+    }
+
+    unsigned new_size = 0;
+    if (num_contours >= 0) {
+      // this is a simple glyph and might contain bytecode
+      if (!ParseSimpleGlyph(file, data, &table,
+                            num_contours, gly_offset, gly_length, &new_size)) {
+        return OTS_FAILURE_MSG("Failed to parse glyph %d", i);
+      }
+    } else {
+      // it's a composite glyph without any bytecode. Enqueue the whole thing
+      glyf->iov.push_back(std::make_pair(data + gly_offset,
+                                         static_cast<size_t>(gly_length)));
+      new_size = gly_length;
+    }
+
+    resulting_offsets[i] = current_offset;
+    // glyphs must be four byte aligned
+    // TODO(yusukes): investigate whether this padding is really necessary.
+    //                Which part of the spec requires this?
+    const unsigned padding = (4 - (new_size & 3)) % 4;
+    if (padding) {
+      glyf->iov.push_back(std::make_pair(
+          reinterpret_cast<const uint8_t*>("\x00\x00\x00\x00"),
+          static_cast<size_t>(padding)));
+      new_size += padding;
+    }
+    current_offset += new_size;
+  }
+  resulting_offsets[num_glyphs] = current_offset;
+
+  const uint16_t max16 = std::numeric_limits<uint16_t>::max();
+  if ((*std::max_element(resulting_offsets.begin(),
+                         resulting_offsets.end()) >= (max16 * 2u)) &&
+      (file->head->index_to_loc_format != 1)) {
+    OTS_WARNING("2-bytes indexing is not possible (due to the padding above)");
+    file->head->index_to_loc_format = 1;
+  }
+
+  file->loca->offsets = resulting_offsets;
+  return true;
+}
+
+bool ots_glyf_should_serialise(OpenTypeFile *file) {
+  return file->glyf != NULL;
+}
+
+bool ots_glyf_serialise(OTSStream *out, OpenTypeFile *file) {
+  const OpenTypeGLYF *glyf = file->glyf;
+
+  for (unsigned i = 0; i < glyf->iov.size(); ++i) {
+    if (!out->Write(glyf->iov[i].first, glyf->iov[i].second)) {
+      return OTS_FAILURE_MSG("Falied to write glyph %d", i);
+    }
+  }
+
+  return true;
+}
+
+void ots_glyf_free(OpenTypeFile *file) {
+  delete file->glyf;
+}
+
+}  // namespace ots
+
+#undef TABLE_NAME
diff --git a/third_party/ots/src/glyf.h b/third_party/ots/src/glyf.h
new file mode 100644
index 0000000..9a8baf5
--- /dev/null
+++ b/third_party/ots/src/glyf.h
@@ -0,0 +1,22 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_GLYF_H_
+#define OTS_GLYF_H_
+
+#include <new>
+#include <utility>
+#include <vector>
+
+#include "ots.h"
+
+namespace ots {
+
+struct OpenTypeGLYF {
+  std::vector<std::pair<const uint8_t*, size_t> > iov;
+};
+
+}  // namespace ots
+
+#endif  // OTS_GLYF_H_
diff --git a/third_party/ots/src/gpos.cc b/third_party/ots/src/gpos.cc
new file mode 100644
index 0000000..a2b9687
--- /dev/null
+++ b/third_party/ots/src/gpos.cc
@@ -0,0 +1,828 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "gpos.h"
+
+#include <limits>
+#include <vector>
+
+#include "layout.h"
+#include "maxp.h"
+
+// GPOS - The Glyph Positioning Table
+// http://www.microsoft.com/typography/otspec/gpos.htm
+
+#define TABLE_NAME "GPOS"
+
+namespace {
+
+enum GPOS_TYPE {
+  GPOS_TYPE_SINGLE_ADJUSTMENT = 1,
+  GPOS_TYPE_PAIR_ADJUSTMENT = 2,
+  GPOS_TYPE_CURSIVE_ATTACHMENT = 3,
+  GPOS_TYPE_MARK_TO_BASE_ATTACHMENT = 4,
+  GPOS_TYPE_MARK_TO_LIGATURE_ATTACHMENT = 5,
+  GPOS_TYPE_MARK_TO_MARK_ATTACHMENT = 6,
+  GPOS_TYPE_CONTEXT_POSITIONING = 7,
+  GPOS_TYPE_CHAINED_CONTEXT_POSITIONING = 8,
+  GPOS_TYPE_EXTENSION_POSITIONING = 9,
+  GPOS_TYPE_RESERVED = 10
+};
+
+// The size of gpos header.
+const unsigned kGposHeaderSize = 10;
+// The maximum format number for anchor tables.
+const uint16_t kMaxAnchorFormat = 3;
+// The maximum number of class value.
+const uint16_t kMaxClassDefValue = 0xFFFF;
+
+// Lookup type parsers.
+bool ParseSingleAdjustment(const ots::OpenTypeFile *file,
+                           const uint8_t *data, const size_t length);
+bool ParsePairAdjustment(const ots::OpenTypeFile *file,
+                         const uint8_t *data, const size_t length);
+bool ParseCursiveAttachment(const ots::OpenTypeFile *file,
+                            const uint8_t *data, const size_t length);
+bool ParseMarkToBaseAttachment(const ots::OpenTypeFile *file,
+                               const uint8_t *data, const size_t length);
+bool ParseMarkToLigatureAttachment(const ots::OpenTypeFile *file,
+                                   const uint8_t *data, const size_t length);
+bool ParseMarkToMarkAttachment(const ots::OpenTypeFile *file,
+                               const uint8_t *data, const size_t length);
+bool ParseContextPositioning(const ots::OpenTypeFile *file,
+                             const uint8_t *data, const size_t length);
+bool ParseChainedContextPositioning(const ots::OpenTypeFile *file,
+                                    const uint8_t *data, const size_t length);
+bool ParseExtensionPositioning(const ots::OpenTypeFile *file,
+                               const uint8_t *data, const size_t length);
+
+const ots::LookupSubtableParser::TypeParser kGposTypeParsers[] = {
+  {GPOS_TYPE_SINGLE_ADJUSTMENT, ParseSingleAdjustment},
+  {GPOS_TYPE_PAIR_ADJUSTMENT, ParsePairAdjustment},
+  {GPOS_TYPE_CURSIVE_ATTACHMENT, ParseCursiveAttachment},
+  {GPOS_TYPE_MARK_TO_BASE_ATTACHMENT, ParseMarkToBaseAttachment},
+  {GPOS_TYPE_MARK_TO_LIGATURE_ATTACHMENT, ParseMarkToLigatureAttachment},
+  {GPOS_TYPE_MARK_TO_MARK_ATTACHMENT, ParseMarkToMarkAttachment},
+  {GPOS_TYPE_CONTEXT_POSITIONING, ParseContextPositioning},
+  {GPOS_TYPE_CHAINED_CONTEXT_POSITIONING, ParseChainedContextPositioning},
+  {GPOS_TYPE_EXTENSION_POSITIONING, ParseExtensionPositioning}
+};
+
+const ots::LookupSubtableParser kGposLookupSubtableParser = {
+  arraysize(kGposTypeParsers),
+  GPOS_TYPE_EXTENSION_POSITIONING, kGposTypeParsers
+};
+
+// Shared Tables: ValueRecord, Anchor Table, and MarkArray
+
+bool ParseValueRecord(const ots::OpenTypeFile *file,
+                      ots::Buffer* subtable, const uint8_t *data,
+                      const size_t length, const uint16_t value_format) {
+  // Check existence of adjustment fields.
+  for (unsigned i = 0; i < 4; ++i) {
+    if ((value_format >> i) & 0x1) {
+      // Just read the field since these fileds could take an arbitrary values.
+      if (!subtable->Skip(2)) {
+        return OTS_FAILURE_MSG("Failed to read value reacord component");
+      }
+    }
+  }
+
+  // Check existence of offsets to device table.
+  for (unsigned i = 0; i < 4; ++i) {
+    if ((value_format >> (i + 4)) & 0x1) {
+      uint16_t offset = 0;
+      if (!subtable->ReadU16(&offset)) {
+        return OTS_FAILURE_MSG("Failed to read value record offset");
+      }
+      if (offset) {
+        // TODO(bashi): Is it possible that device tables locate before
+        // this record? No fonts contain such offset AKAIF.
+        if (offset >= length) {
+          return OTS_FAILURE_MSG("Value record offset too high %d >= %ld", offset, length);
+        }
+        if (!ots::ParseDeviceTable(file, data + offset, length - offset)) {
+          return OTS_FAILURE_MSG("Failed to parse device table in value record");
+        }
+      }
+    }
+  }
+  return true;
+}
+
+bool ParseAnchorTable(const ots::OpenTypeFile *file,
+                      const uint8_t *data, const size_t length) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t format = 0;
+  // Read format and skip 2 2-byte fields that could be arbitrary values.
+  if (!subtable.ReadU16(&format) ||
+      !subtable.Skip(4)) {
+    return OTS_FAILURE_MSG("Faled to read anchor table");
+  }
+
+  if (format == 0 || format > kMaxAnchorFormat) {
+    return OTS_FAILURE_MSG("Bad Anchor table format %d", format);
+  }
+
+  // Format 2 and 3 has additional fields.
+  if (format == 2) {
+    // Format 2 provides an index to a glyph contour point, which will take
+    // arbitrary value.
+    uint16_t anchor_point = 0;
+    if (!subtable.ReadU16(&anchor_point)) {
+      return OTS_FAILURE_MSG("Failed to read anchor point in format 2 Anchor Table");
+    }
+  } else if (format == 3) {
+    uint16_t offset_x_device = 0;
+    uint16_t offset_y_device = 0;
+    if (!subtable.ReadU16(&offset_x_device) ||
+        !subtable.ReadU16(&offset_y_device)) {
+      return OTS_FAILURE_MSG("Failed to read device table offsets in format 3 anchor table");
+    }
+    const unsigned format_end = static_cast<unsigned>(10);
+    if (offset_x_device) {
+      if (offset_x_device < format_end || offset_x_device >= length) {
+        return OTS_FAILURE_MSG("Bad x device table offset %d", offset_x_device);
+      }
+      if (!ots::ParseDeviceTable(file, data + offset_x_device,
+                                 length - offset_x_device)) {
+        return OTS_FAILURE_MSG("Failed to parse device table in anchor table");
+      }
+    }
+    if (offset_y_device) {
+      if (offset_y_device < format_end || offset_y_device >= length) {
+        return OTS_FAILURE_MSG("Bad y device table offset %d", offset_y_device);
+      }
+      if (!ots::ParseDeviceTable(file, data + offset_y_device,
+                                 length - offset_y_device)) {
+        return OTS_FAILURE_MSG("Failed to parse device table in anchor table");
+      }
+    }
+  }
+  return true;
+}
+
+bool ParseMarkArrayTable(const ots::OpenTypeFile *file,
+                         const uint8_t *data, const size_t length,
+                         const uint16_t class_count) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t mark_count = 0;
+  if (!subtable.ReadU16(&mark_count)) {
+    return OTS_FAILURE_MSG("Can't read mark table length");
+  }
+
+  // MarkRecord consists of 4-bytes.
+  const unsigned mark_records_end = 4 * static_cast<unsigned>(mark_count) + 2;
+  if (mark_records_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE_MSG("Bad mark table length");
+  }
+  for (unsigned i = 0; i < mark_count; ++i) {
+    uint16_t class_value = 0;
+    uint16_t offset_mark_anchor = 0;
+    if (!subtable.ReadU16(&class_value) ||
+        !subtable.ReadU16(&offset_mark_anchor)) {
+      return OTS_FAILURE_MSG("Can't read mark table %d", i);
+    }
+    // |class_value| may take arbitrary values including 0 here so we don't
+    // check the value.
+    if (offset_mark_anchor < mark_records_end ||
+        offset_mark_anchor >= length) {
+      return OTS_FAILURE_MSG("Bad mark anchor offset %d for mark table %d", offset_mark_anchor, i);
+    }
+    if (!ParseAnchorTable(file, data + offset_mark_anchor,
+                          length - offset_mark_anchor)) {
+      return OTS_FAILURE_MSG("Faled to parse anchor table for mark table %d", i);
+    }
+  }
+
+  return true;
+}
+
+// Lookup Type 1:
+// Single Adjustment Positioning Subtable
+bool ParseSingleAdjustment(const ots::OpenTypeFile *file, const uint8_t *data,
+                           const size_t length) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t format = 0;
+  uint16_t offset_coverage = 0;
+  uint16_t value_format = 0;
+  if (!subtable.ReadU16(&format) ||
+      !subtable.ReadU16(&offset_coverage) ||
+      !subtable.ReadU16(&value_format)) {
+    return OTS_FAILURE_MSG("Can't read single adjustment information");
+  }
+
+  if (format == 1) {
+    // Format 1 exactly one value record.
+    if (!ParseValueRecord(file, &subtable, data, length, value_format)) {
+      return OTS_FAILURE_MSG("Failed to parse format 1 single adjustment table");
+    }
+  } else if (format == 2) {
+    uint16_t value_count = 0;
+    if (!subtable.ReadU16(&value_count)) {
+      return OTS_FAILURE_MSG("Failed to parse format 2 single adjustment table");
+    }
+    for (unsigned i = 0; i < value_count; ++i) {
+      if (!ParseValueRecord(file, &subtable, data, length, value_format)) {
+        return OTS_FAILURE_MSG("Failed to parse value record %d in format 2 single adjustment table", i);
+      }
+    }
+  } else {
+    return OTS_FAILURE_MSG("Bad format %d in single adjustment table", format);
+  }
+
+  if (offset_coverage < subtable.offset() || offset_coverage >= length) {
+    return OTS_FAILURE_MSG("Bad coverage offset %d in single adjustment table", offset_coverage);
+  }
+
+  if (!ots::ParseCoverageTable(file, data + offset_coverage,
+                               length - offset_coverage,
+                               file->maxp->num_glyphs)) {
+    return OTS_FAILURE_MSG("Failed to parse coverage table in single adjustment table");
+  }
+
+  return true;
+}
+
+bool ParsePairSetTable(const ots::OpenTypeFile *file,
+                       const uint8_t *data, const size_t length,
+                       const uint16_t value_format1,
+                       const uint16_t value_format2,
+                       const uint16_t num_glyphs) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t value_count = 0;
+  if (!subtable.ReadU16(&value_count)) {
+    return OTS_FAILURE_MSG("Failed to read pair set table structure");
+  }
+  for (unsigned i = 0; i < value_count; ++i) {
+    // Check pair value record.
+    uint16_t glyph_id = 0;
+    if (!subtable.ReadU16(&glyph_id)) {
+      return OTS_FAILURE_MSG("Failed to read glyph in pair value record %d", i);
+    }
+    if (glyph_id >= num_glyphs) {
+      return OTS_FAILURE_MSG("glyph id %d too high >= %d", glyph_id, num_glyphs);
+    }
+    if (!ParseValueRecord(file, &subtable, data, length, value_format1)) {
+      return OTS_FAILURE_MSG("Failed to parse value record in format 1 pair set table");
+    }
+    if (!ParseValueRecord(file, &subtable, data, length, value_format2)) {
+      return OTS_FAILURE_MSG("Failed to parse value record in format 2 pair set table");
+    }
+  }
+  return true;
+}
+
+bool ParsePairPosFormat1(const ots::OpenTypeFile *file,
+                         const uint8_t *data, const size_t length,
+                         const uint16_t value_format1,
+                         const uint16_t value_format2,
+                         const uint16_t num_glyphs) {
+  ots::Buffer subtable(data, length);
+
+  // Skip 8 bytes that are already read before.
+  if (!subtable.Skip(8)) {
+    return OTS_FAILURE_MSG("Failed to read pair pos table structure");
+  }
+
+  uint16_t pair_set_count = 0;
+  if (!subtable.ReadU16(&pair_set_count)) {
+    return OTS_FAILURE_MSG("Failed to read pair pos set count");
+  }
+
+  const unsigned pair_pos_end = 2 * static_cast<unsigned>(pair_set_count) + 10;
+  if (pair_pos_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE_MSG("Bad pair set length %d", pair_pos_end);
+  }
+  for (unsigned i = 0; i < pair_set_count; ++i) {
+    uint16_t pair_set_offset = 0;
+    if (!subtable.ReadU16(&pair_set_offset)) {
+      return OTS_FAILURE_MSG("Failed to read pair set offset for pair set %d", i);
+    }
+    if (pair_set_offset < pair_pos_end || pair_set_offset >= length) {
+      return OTS_FAILURE_MSG("Bad pair set offset %d for pair set %d", pair_set_offset, i);
+    }
+    // Check pair set tables
+    if (!ParsePairSetTable(file, data + pair_set_offset, length - pair_set_offset,
+                           value_format1, value_format2,
+                           num_glyphs)) {
+      return OTS_FAILURE_MSG("Failed to parse pair set table %d", i);
+    }
+  }
+
+  return true;
+}
+
+bool ParsePairPosFormat2(const ots::OpenTypeFile *file,
+                         const uint8_t *data, const size_t length,
+                         const uint16_t value_format1,
+                         const uint16_t value_format2,
+                         const uint16_t num_glyphs) {
+  ots::Buffer subtable(data, length);
+
+  // Skip 8 bytes that are already read before.
+  if (!subtable.Skip(8)) {
+    return OTS_FAILURE_MSG("Failed to read pair pos format 2 structure");
+  }
+
+  uint16_t offset_class_def1 = 0;
+  uint16_t offset_class_def2 = 0;
+  uint16_t class1_count = 0;
+  uint16_t class2_count = 0;
+  if (!subtable.ReadU16(&offset_class_def1) ||
+      !subtable.ReadU16(&offset_class_def2) ||
+      !subtable.ReadU16(&class1_count) ||
+      !subtable.ReadU16(&class2_count)) {
+    return OTS_FAILURE_MSG("Failed to read pair pos format 2 data");
+  }
+
+  // Check class 1 records.
+  for (unsigned i = 0; i < class1_count; ++i) {
+    // Check class 2 records.
+    for (unsigned j = 0; j < class2_count; ++j) {
+      if (value_format1 && !ParseValueRecord(file, &subtable, data, length,
+                                             value_format1)) {
+        return OTS_FAILURE_MSG("Failed to parse value record 1 %d and %d", j, i);
+      }
+      if (value_format2 && !ParseValueRecord(file, &subtable, data, length,
+                                             value_format2)) {
+        return OTS_FAILURE_MSG("Falied to parse value record 2 %d and %d", j, i);
+      }
+    }
+  }
+
+  // Check class definition tables.
+  if (offset_class_def1 < subtable.offset() || offset_class_def1 >= length ||
+      offset_class_def2 < subtable.offset() || offset_class_def2 >= length) {
+    return OTS_FAILURE_MSG("Bad class definition table offsets %d or %d", offset_class_def1, offset_class_def2);
+  }
+  if (!ots::ParseClassDefTable(file, data + offset_class_def1,
+                               length - offset_class_def1,
+                               num_glyphs, kMaxClassDefValue)) {
+    return OTS_FAILURE_MSG("Failed to parse class definition table 1");
+  }
+  if (!ots::ParseClassDefTable(file, data + offset_class_def2,
+                               length - offset_class_def2,
+                               num_glyphs, kMaxClassDefValue)) {
+    return OTS_FAILURE_MSG("Failed to parse class definition table 2");
+  }
+
+  return true;
+}
+
+// Lookup Type 2:
+// Pair Adjustment Positioning Subtable
+bool ParsePairAdjustment(const ots::OpenTypeFile *file, const uint8_t *data,
+                         const size_t length) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t format = 0;
+  uint16_t offset_coverage = 0;
+  uint16_t value_format1 = 0;
+  uint16_t value_format2 = 0;
+  if (!subtable.ReadU16(&format) ||
+      !subtable.ReadU16(&offset_coverage) ||
+      !subtable.ReadU16(&value_format1) ||
+      !subtable.ReadU16(&value_format2)) {
+    return OTS_FAILURE_MSG("Failed to read pair adjustment structure");
+  }
+
+  if (format == 1) {
+    if (!ParsePairPosFormat1(file, data, length, value_format1, value_format2,
+                             file->maxp->num_glyphs)) {
+      return OTS_FAILURE_MSG("Failed to parse pair pos format 1");
+    }
+  } else if (format == 2) {
+    if (!ParsePairPosFormat2(file, data, length, value_format1, value_format2,
+                             file->maxp->num_glyphs)) {
+      return OTS_FAILURE_MSG("Failed to parse pair format 2");
+    }
+  } else {
+    return OTS_FAILURE_MSG("Bad pos pair format %d", format);
+  }
+
+  if (offset_coverage < subtable.offset() || offset_coverage >= length) {
+    return OTS_FAILURE_MSG("Bad pair pos offset coverage %d", offset_coverage);
+  }
+  if (!ots::ParseCoverageTable(file, data + offset_coverage,
+                               length - offset_coverage,
+                               file->maxp->num_glyphs)) {
+    return OTS_FAILURE_MSG("Failed to parse coverage table");
+  }
+
+  return true;
+}
+
+// Lookup Type 3
+// Cursive Attachment Positioning Subtable
+bool ParseCursiveAttachment(const ots::OpenTypeFile *file, const uint8_t *data,
+                            const size_t length) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t format = 0;
+  uint16_t offset_coverage = 0;
+  uint16_t entry_exit_count = 0;
+  if (!subtable.ReadU16(&format) ||
+      !subtable.ReadU16(&offset_coverage) ||
+      !subtable.ReadU16(&entry_exit_count)) {
+    return OTS_FAILURE_MSG("Failed to read cursive attachment structure");
+  }
+
+  if (format != 1) {
+    return OTS_FAILURE_MSG("Bad cursive attachment format %d", format);
+  }
+
+  // Check entry exit records.
+  const unsigned entry_exit_records_end =
+      2 * static_cast<unsigned>(entry_exit_count) + 6;
+  if (entry_exit_records_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE_MSG("Bad entry exit record end %d", entry_exit_records_end);
+  }
+  for (unsigned i = 0; i < entry_exit_count; ++i) {
+    uint16_t offset_entry_anchor = 0;
+    uint16_t offset_exit_anchor = 0;
+    if (!subtable.ReadU16(&offset_entry_anchor) ||
+        !subtable.ReadU16(&offset_exit_anchor)) {
+      return OTS_FAILURE_MSG("Can't read entry exit record %d", i);
+    }
+    // These offsets could be NULL.
+    if (offset_entry_anchor) {
+      if (offset_entry_anchor < entry_exit_records_end ||
+          offset_entry_anchor >= length) {
+        return OTS_FAILURE_MSG("Bad entry anchor offset %d in entry exit record %d", offset_entry_anchor, i);
+      }
+      if (!ParseAnchorTable(file, data + offset_entry_anchor,
+                            length - offset_entry_anchor)) {
+        return OTS_FAILURE_MSG("Failed to parse entry anchor table in entry exit record %d", i);
+      }
+    }
+    if (offset_exit_anchor) {
+      if (offset_exit_anchor < entry_exit_records_end ||
+         offset_exit_anchor >= length) {
+        return OTS_FAILURE_MSG("Bad exit anchor offset %d in entry exit record %d", offset_exit_anchor, i);
+      }
+      if (!ParseAnchorTable(file, data + offset_exit_anchor,
+                            length - offset_exit_anchor)) {
+        return OTS_FAILURE_MSG("Failed to parse exit anchor table in entry exit record %d", i);
+      }
+    }
+  }
+
+  if (offset_coverage < subtable.offset() || offset_coverage >= length) {
+    return OTS_FAILURE_MSG("Bad coverage offset in cursive attachment %d", offset_coverage);
+  }
+  if (!ots::ParseCoverageTable(file, data + offset_coverage,
+                               length - offset_coverage,
+                               file->maxp->num_glyphs)) {
+    return OTS_FAILURE_MSG("Failed to parse coverage table in cursive attachment");
+  }
+
+  return true;
+}
+
+bool ParseAnchorArrayTable(const ots::OpenTypeFile *file,
+                           const uint8_t *data, const size_t length,
+                           const uint16_t class_count) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t record_count = 0;
+  if (!subtable.ReadU16(&record_count)) {
+    return OTS_FAILURE_MSG("Can't read anchor array length");
+  }
+
+  const unsigned anchor_array_end = 2 * static_cast<unsigned>(record_count) *
+      static_cast<unsigned>(class_count) + 2;
+  if (anchor_array_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE_MSG("Bad end of anchor array %d", anchor_array_end);
+  }
+  for (unsigned i = 0; i < record_count; ++i) {
+    for (unsigned j = 0; j < class_count; ++j) {
+      uint16_t offset_record = 0;
+      if (!subtable.ReadU16(&offset_record)) {
+        return OTS_FAILURE_MSG("Can't read anchor array record offset for class %d and record %d", j, i);
+      }
+      // |offset_record| could be NULL.
+      if (offset_record) {
+        if (offset_record < anchor_array_end || offset_record >= length) {
+          return OTS_FAILURE_MSG("Bad record offset %d in class %d, record %d", offset_record, j, i);
+        }
+        if (!ParseAnchorTable(file, data + offset_record,
+                              length - offset_record)) {
+          return OTS_FAILURE_MSG("Failed to parse anchor table for class %d, record %d", j, i);
+        }
+      }
+    }
+  }
+  return true;
+}
+
+bool ParseLigatureArrayTable(const ots::OpenTypeFile *file,
+                             const uint8_t *data, const size_t length,
+                             const uint16_t class_count) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t ligature_count = 0;
+  if (!subtable.ReadU16(&ligature_count)) {
+    return OTS_FAILURE_MSG("Failed to read ligature count");
+  }
+  for (unsigned i = 0; i < ligature_count; ++i) {
+    uint16_t offset_ligature_attach = 0;
+    if (!subtable.ReadU16(&offset_ligature_attach)) {
+      return OTS_FAILURE_MSG("Can't read ligature offset %d", i);
+    }
+    if (offset_ligature_attach < 2 || offset_ligature_attach >= length) {
+      return OTS_FAILURE_MSG("Bad ligature attachment offset %d in ligature %d", offset_ligature_attach, i);
+    }
+    if (!ParseAnchorArrayTable(file, data + offset_ligature_attach,
+                               length - offset_ligature_attach, class_count)) {
+      return OTS_FAILURE_MSG("Failed to parse anchor table for ligature %d", i);
+    }
+  }
+  return true;
+}
+
+// Common parser for Lookup Type 4, 5 and 6.
+bool ParseMarkToAttachmentSubtables(const ots::OpenTypeFile *file,
+                                    const uint8_t *data, const size_t length,
+                                    const GPOS_TYPE type) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t format = 0;
+  uint16_t offset_coverage1 = 0;
+  uint16_t offset_coverage2 = 0;
+  uint16_t class_count = 0;
+  uint16_t offset_mark_array = 0;
+  uint16_t offset_type_specific_array = 0;
+  if (!subtable.ReadU16(&format) ||
+      !subtable.ReadU16(&offset_coverage1) ||
+      !subtable.ReadU16(&offset_coverage2) ||
+      !subtable.ReadU16(&class_count) ||
+      !subtable.ReadU16(&offset_mark_array) ||
+      !subtable.ReadU16(&offset_type_specific_array)) {
+    return OTS_FAILURE_MSG("Failed to read mark attachment subtable header");
+  }
+
+  if (format != 1) {
+    return OTS_FAILURE_MSG("bad mark attachment subtable format %d", format);
+  }
+
+  const unsigned header_end = static_cast<unsigned>(subtable.offset());
+  if (header_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE_MSG("Bad mark attachment subtable size ending at %d", header_end);
+  }
+  if (offset_coverage1 < header_end || offset_coverage1 >= length) {
+    return OTS_FAILURE_MSG("Bad coverage 1 offset %d", offset_coverage1);
+  }
+  if (!ots::ParseCoverageTable(file, data + offset_coverage1,
+                               length - offset_coverage1,
+                               file->maxp->num_glyphs)) {
+    return OTS_FAILURE_MSG("Failed to parse converge 1 table");
+  }
+  if (offset_coverage2 < header_end || offset_coverage2 >= length) {
+    return OTS_FAILURE_MSG("Bad coverage 2 offset %d", offset_coverage2);
+  }
+  if (!ots::ParseCoverageTable(file, data + offset_coverage2,
+                               length - offset_coverage2,
+                               file->maxp->num_glyphs)) {
+    return OTS_FAILURE_MSG("Failed to parse coverage table 2");
+  }
+
+  if (offset_mark_array < header_end || offset_mark_array >= length) {
+    return OTS_FAILURE_MSG("Bad mark array offset %d", offset_mark_array);
+  }
+  if (!ParseMarkArrayTable(file, data + offset_mark_array,
+                           length - offset_mark_array, class_count)) {
+    return OTS_FAILURE_MSG("Failed to parse mark array");
+  }
+
+  if (offset_type_specific_array < header_end ||
+      offset_type_specific_array >= length) {
+    return OTS_FAILURE_MSG("Bad type specific array offset %d", offset_type_specific_array);
+  }
+  if (type == GPOS_TYPE_MARK_TO_BASE_ATTACHMENT ||
+      type == GPOS_TYPE_MARK_TO_MARK_ATTACHMENT) {
+    if (!ParseAnchorArrayTable(file, data + offset_type_specific_array,
+                               length - offset_type_specific_array,
+                               class_count)) {
+      return OTS_FAILURE_MSG("Failed to parse anchor array");
+    }
+  } else if (type == GPOS_TYPE_MARK_TO_LIGATURE_ATTACHMENT) {
+    if (!ParseLigatureArrayTable(file, data + offset_type_specific_array,
+                                 length - offset_type_specific_array,
+                                 class_count)) {
+      return OTS_FAILURE_MSG("Failed to parse ligature array");
+    }
+  } else {
+    return OTS_FAILURE_MSG("Bad attachment type %d", type);
+  }
+
+  return true;
+}
+
+// Lookup Type 4:
+// MarkToBase Attachment Positioning Subtable
+bool ParseMarkToBaseAttachment(const ots::OpenTypeFile *file,
+                               const uint8_t *data, const size_t length) {
+  return ParseMarkToAttachmentSubtables(file, data, length,
+                                        GPOS_TYPE_MARK_TO_BASE_ATTACHMENT);
+}
+
+// Lookup Type 5:
+// MarkToLigature Attachment Positioning Subtable
+bool ParseMarkToLigatureAttachment(const ots::OpenTypeFile *file,
+                                   const uint8_t *data, const size_t length) {
+  return ParseMarkToAttachmentSubtables(file, data, length,
+                                        GPOS_TYPE_MARK_TO_LIGATURE_ATTACHMENT);
+}
+
+// Lookup Type 6:
+// MarkToMark Attachment Positioning Subtable
+bool ParseMarkToMarkAttachment(const ots::OpenTypeFile *file,
+                               const uint8_t *data, const size_t length) {
+  return ParseMarkToAttachmentSubtables(file, data, length,
+                                        GPOS_TYPE_MARK_TO_MARK_ATTACHMENT);
+}
+
+// Lookup Type 7:
+// Contextual Positioning Subtables
+bool ParseContextPositioning(const ots::OpenTypeFile *file,
+                             const uint8_t *data, const size_t length) {
+  return ots::ParseContextSubtable(file, data, length, file->maxp->num_glyphs,
+                                   file->gpos->num_lookups);
+}
+
+// Lookup Type 8:
+// Chaining Contexual Positioning Subtable
+bool ParseChainedContextPositioning(const ots::OpenTypeFile *file,
+                                    const uint8_t *data, const size_t length) {
+  return ots::ParseChainingContextSubtable(file, data, length,
+                                           file->maxp->num_glyphs,
+                                           file->gpos->num_lookups);
+}
+
+// Lookup Type 9:
+// Extension Positioning
+bool ParseExtensionPositioning(const ots::OpenTypeFile *file,
+                               const uint8_t *data, const size_t length) {
+  return ots::ParseExtensionSubtable(file, data, length,
+                                     &kGposLookupSubtableParser);
+}
+
+}  // namespace
+
+#define DROP_THIS_TABLE(msg_) \
+  do { \
+    OTS_FAILURE_MSG(msg_ ", table discarded"); \
+    file->gpos->data = 0; \
+    file->gpos->length = 0; \
+  } while (0)
+
+namespace ots {
+
+// As far as I checked, following fonts contain invalid GPOS table and
+// OTS will drop their GPOS table.
+//
+// # invalid delta format in device table
+// samanata.ttf
+//
+// # bad size range in device table
+// Sarai_07.ttf
+//
+// # bad offset to PairSetTable
+// chandas1-2.ttf
+//
+// # bad offset to FeatureTable
+// glrso12.ttf
+// gllr12.ttf
+// glbo12.ttf
+// glb12.ttf
+// glro12.ttf
+// glbso12.ttf
+// glrc12.ttf
+// glrsc12.ttf
+// glbs12.ttf
+// glrs12.ttf
+// glr12.ttf
+//
+// # ScriptRecords aren't sorted by tag
+// Garogier_unhinted.otf
+//
+// # bad start coverage index in CoverageFormat2
+// AndBasR.ttf
+// CharisSILB.ttf
+// CharisSILBI.ttf
+// CharisSILI.ttf
+// CharisSILR.ttf
+// DoulosSILR.ttf
+// GenBasBI.ttf
+// GenBasI.ttf
+// GenBkBasI.ttf
+// GenBkBasB.ttf
+// GenBkBasR.ttf
+// Padauk-Bold.ttf
+// Padauk.ttf
+//
+// # Contour point indexes aren't sorted
+// Arial Unicode.ttf
+
+bool ots_gpos_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
+  // Parsing GPOS table requires num_glyphs which is contained in maxp table.
+  if (!file->maxp) {
+    return OTS_FAILURE_MSG("missing maxp table needed in GPOS");
+  }
+
+  Buffer table(data, length);
+
+  OpenTypeGPOS *gpos = new OpenTypeGPOS;
+  file->gpos = gpos;
+
+  uint32_t version = 0;
+  uint16_t offset_script_list = 0;
+  uint16_t offset_feature_list = 0;
+  uint16_t offset_lookup_list = 0;
+  if (!table.ReadU32(&version) ||
+      !table.ReadU16(&offset_script_list) ||
+      !table.ReadU16(&offset_feature_list) ||
+      !table.ReadU16(&offset_lookup_list)) {
+    DROP_THIS_TABLE("Incomplete table");
+    return true;
+  }
+
+  if (version != 0x00010000) {
+    DROP_THIS_TABLE("Bad version");
+    return true;
+  }
+
+  if (offset_lookup_list) {
+    if (offset_lookup_list < kGposHeaderSize || offset_lookup_list >= length) {
+      DROP_THIS_TABLE("Bad lookup list offset in table header");
+      return true;
+    }
+
+    if (!ParseLookupListTable(file, data + offset_lookup_list,
+                              length - offset_lookup_list,
+                              &kGposLookupSubtableParser,
+                              &gpos->num_lookups)) {
+      DROP_THIS_TABLE("Failed to parse lookup list table");
+      return true;
+    }
+  }
+
+  uint16_t num_features = 0;
+  if (offset_feature_list) {
+    if (offset_feature_list < kGposHeaderSize || offset_feature_list >= length) {
+      DROP_THIS_TABLE("Bad feature list offset in table header");
+      return true;
+    }
+
+    if (!ParseFeatureListTable(file, data + offset_feature_list,
+                               length - offset_feature_list, gpos->num_lookups,
+                               &num_features)) {
+      DROP_THIS_TABLE("Failed to parse feature list table");
+      return true;
+    }
+  }
+
+  if (offset_script_list) {
+    if (offset_script_list < kGposHeaderSize || offset_script_list >= length) {
+      DROP_THIS_TABLE("Bad script list offset in table header");
+      return true;
+    }
+
+    if (!ParseScriptListTable(file, data + offset_script_list,
+                              length - offset_script_list, num_features)) {
+      DROP_THIS_TABLE("Failed to parse script list table");
+      return true;
+    }
+  }
+
+  gpos->data = data;
+  gpos->length = length;
+  return true;
+}
+
+bool ots_gpos_should_serialise(OpenTypeFile *file) {
+  return file->gpos != NULL && file->gpos->data != NULL;
+}
+
+bool ots_gpos_serialise(OTSStream *out, OpenTypeFile *file) {
+  if (!out->Write(file->gpos->data, file->gpos->length)) {
+    return OTS_FAILURE_MSG("Failed to write GPOS table");
+  }
+
+  return true;
+}
+
+void ots_gpos_free(OpenTypeFile *file) {
+  delete file->gpos;
+}
+
+}  // namespace ots
+
+#undef TABLE_NAME
+#undef DROP_THIS_TABLE
diff --git a/third_party/ots/src/gpos.h b/third_party/ots/src/gpos.h
new file mode 100644
index 0000000..3a4034f
--- /dev/null
+++ b/third_party/ots/src/gpos.h
@@ -0,0 +1,29 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_GPOS_H_
+#define OTS_GPOS_H_
+
+#include "ots.h"
+
+namespace ots {
+
+struct OpenTypeGPOS {
+  OpenTypeGPOS()
+      : num_lookups(0),
+        data(NULL),
+        length(0) {
+  }
+
+  // Number of lookups in GPOS table
+  uint16_t num_lookups;
+
+  const uint8_t *data;
+  size_t length;
+};
+
+}  // namespace ots
+
+#endif
+
diff --git a/third_party/ots/src/gsub.cc b/third_party/ots/src/gsub.cc
new file mode 100644
index 0000000..af31144
--- /dev/null
+++ b/third_party/ots/src/gsub.cc
@@ -0,0 +1,685 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "gsub.h"
+
+#include <limits>
+#include <vector>
+
+#include "layout.h"
+#include "maxp.h"
+
+// GSUB - The Glyph Substitution Table
+// http://www.microsoft.com/typography/otspec/gsub.htm
+
+#define TABLE_NAME "GSUB"
+
+namespace {
+
+// The GSUB header size
+const size_t kGsubHeaderSize = 4 + 3 * 2;
+
+enum GSUB_TYPE {
+  GSUB_TYPE_SINGLE = 1,
+  GSUB_TYPE_MULTIPLE = 2,
+  GSUB_TYPE_ALTERNATE = 3,
+  GSUB_TYPE_LIGATURE = 4,
+  GSUB_TYPE_CONTEXT = 5,
+  GSUB_TYPE_CHANGING_CONTEXT = 6,
+  GSUB_TYPE_EXTENSION_SUBSTITUTION = 7,
+  GSUB_TYPE_REVERSE_CHAINING_CONTEXT_SINGLE = 8,
+  GSUB_TYPE_RESERVED = 9
+};
+
+// Lookup type parsers.
+bool ParseSingleSubstitution(const ots::OpenTypeFile *file,
+                             const uint8_t *data, const size_t length);
+bool ParseMutipleSubstitution(const ots::OpenTypeFile *file,
+                              const uint8_t *data, const size_t length);
+bool ParseAlternateSubstitution(const ots::OpenTypeFile *file,
+                                const uint8_t *data, const size_t length);
+bool ParseLigatureSubstitution(const ots::OpenTypeFile *file,
+      const uint8_t *data, const size_t length);
+bool ParseContextSubstitution(const ots::OpenTypeFile *file,
+                              const uint8_t *data, const size_t length);
+bool ParseChainingContextSubstitution(const ots::OpenTypeFile *file,
+                                      const uint8_t *data,
+                                      const size_t length);
+bool ParseExtensionSubstitution(const ots::OpenTypeFile *file,
+                                const uint8_t *data, const size_t length);
+bool ParseReverseChainingContextSingleSubstitution(
+    const ots::OpenTypeFile *file, const uint8_t *data, const size_t length);
+
+const ots::LookupSubtableParser::TypeParser kGsubTypeParsers[] = {
+  {GSUB_TYPE_SINGLE, ParseSingleSubstitution},
+  {GSUB_TYPE_MULTIPLE, ParseMutipleSubstitution},
+  {GSUB_TYPE_ALTERNATE, ParseAlternateSubstitution},
+  {GSUB_TYPE_LIGATURE, ParseLigatureSubstitution},
+  {GSUB_TYPE_CONTEXT, ParseContextSubstitution},
+  {GSUB_TYPE_CHANGING_CONTEXT, ParseChainingContextSubstitution},
+  {GSUB_TYPE_EXTENSION_SUBSTITUTION, ParseExtensionSubstitution},
+  {GSUB_TYPE_REVERSE_CHAINING_CONTEXT_SINGLE,
+    ParseReverseChainingContextSingleSubstitution}
+};
+
+const ots::LookupSubtableParser kGsubLookupSubtableParser = {
+  arraysize(kGsubTypeParsers),
+  GSUB_TYPE_EXTENSION_SUBSTITUTION, kGsubTypeParsers
+};
+
+// Lookup Type 1:
+// Single Substitution Subtable
+bool ParseSingleSubstitution(const ots::OpenTypeFile *file,
+                             const uint8_t *data, const size_t length) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t format = 0;
+  uint16_t offset_coverage = 0;
+
+  if (!subtable.ReadU16(&format) ||
+      !subtable.ReadU16(&offset_coverage)) {
+    return OTS_FAILURE_MSG("Failed to read single subst table header");
+  }
+
+  const uint16_t num_glyphs = file->maxp->num_glyphs;
+  if (format == 1) {
+    // Parse SingleSubstFormat1
+    int16_t delta_glyph_id = 0;
+    if (!subtable.ReadS16(&delta_glyph_id)) {
+      return OTS_FAILURE_MSG("Failed to read glyph shift from format 1 single subst table");
+    }
+    if (std::abs(delta_glyph_id) >= num_glyphs) {
+      return OTS_FAILURE_MSG("bad glyph shift of %d in format 1 single subst table", delta_glyph_id);
+    }
+  } else if (format == 2) {
+    // Parse SingleSubstFormat2
+    uint16_t glyph_count = 0;
+    if (!subtable.ReadU16(&glyph_count)) {
+      return OTS_FAILURE_MSG("Failed to read glyph cound in format 2 single subst table");
+    }
+    if (glyph_count > num_glyphs) {
+      return OTS_FAILURE_MSG("Bad glyph count %d > %d in format 2 single subst table", glyph_count, num_glyphs);
+    }
+    for (unsigned i = 0; i < glyph_count; ++i) {
+      uint16_t substitute = 0;
+      if (!subtable.ReadU16(&substitute)) {
+        return OTS_FAILURE_MSG("Failed to read substitution %d in format 2 single subst table", i);
+      }
+      if (substitute >= num_glyphs) {
+        return OTS_FAILURE_MSG("too large substitute: %u", substitute);
+      }
+    }
+  } else {
+    return OTS_FAILURE_MSG("Bad single subst table format %d", format);
+  }
+
+  if (offset_coverage < subtable.offset() || offset_coverage >= length) {
+    return OTS_FAILURE_MSG("Bad coverage offset %x", offset_coverage);
+  }
+  if (!ots::ParseCoverageTable(file, data + offset_coverage,
+                               length - offset_coverage, num_glyphs)) {
+    return OTS_FAILURE_MSG("Failed to parse coverage table");
+  }
+
+  return true;
+}
+
+bool ParseSequenceTable(const ots::OpenTypeFile *file,
+                        const uint8_t *data, const size_t length,
+                        const uint16_t num_glyphs) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t glyph_count = 0;
+  if (!subtable.ReadU16(&glyph_count)) {
+    return OTS_FAILURE_MSG("Failed to read glyph count in sequence table");
+  }
+  if (glyph_count > num_glyphs) {
+    return OTS_FAILURE_MSG("bad glyph count %d > %d", glyph_count, num_glyphs);
+  }
+  for (unsigned i = 0; i < glyph_count; ++i) {
+    uint16_t substitute = 0;
+    if (!subtable.ReadU16(&substitute)) {
+      return OTS_FAILURE_MSG("Failedt o read substitution %d in sequence table", i);
+    }
+    if (substitute >= num_glyphs) {
+      return OTS_FAILURE_MSG("Bad subsitution (%d) %d > %d", i, substitute, num_glyphs);
+    }
+  }
+
+  return true;
+}
+
+// Lookup Type 2:
+// Multiple Substitution Subtable
+bool ParseMutipleSubstitution(const ots::OpenTypeFile *file,
+                              const uint8_t *data, const size_t length) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t format = 0;
+  uint16_t offset_coverage = 0;
+  uint16_t sequence_count = 0;
+
+  if (!subtable.ReadU16(&format) ||
+      !subtable.ReadU16(&offset_coverage) ||
+      !subtable.ReadU16(&sequence_count)) {
+    return OTS_FAILURE_MSG("Can't read header of multiple subst table");
+  }
+
+  if (format != 1) {
+    return OTS_FAILURE_MSG("Bad multiple subst table format %d", format);
+  }
+
+  const uint16_t num_glyphs = file->maxp->num_glyphs;
+  const unsigned sequence_end = static_cast<unsigned>(6) +
+      sequence_count * 2;
+  if (sequence_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE_MSG("Bad segence end %d, in multiple subst", sequence_end);
+  }
+  for (unsigned i = 0; i < sequence_count; ++i) {
+    uint16_t offset_sequence = 0;
+    if (!subtable.ReadU16(&offset_sequence)) {
+      return OTS_FAILURE_MSG("Failed to read sequence offset for sequence %d", i);
+    }
+    if (offset_sequence < sequence_end || offset_sequence >= length) {
+      return OTS_FAILURE_MSG("Bad sequence offset %d for sequence %d", offset_sequence, i);
+    }
+    if (!ParseSequenceTable(file, data + offset_sequence, length - offset_sequence,
+                            num_glyphs)) {
+      return OTS_FAILURE_MSG("Failed to parse sequence table %d", i);
+    }
+  }
+
+  if (offset_coverage < sequence_end || offset_coverage >= length) {
+    return OTS_FAILURE_MSG("Bad coverage offset %d", offset_coverage);
+  }
+  if (!ots::ParseCoverageTable(file, data + offset_coverage,
+                               length - offset_coverage, num_glyphs)) {
+    return OTS_FAILURE_MSG("Failed to parse coverage table");
+  }
+
+  return true;
+}
+
+bool ParseAlternateSetTable(const ots::OpenTypeFile *file,
+                            const uint8_t *data, const size_t length,
+                            const uint16_t num_glyphs) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t glyph_count = 0;
+  if (!subtable.ReadU16(&glyph_count)) {
+    return OTS_FAILURE_MSG("Failed to read alternate set header");
+  }
+  if (glyph_count > num_glyphs) {
+    return OTS_FAILURE_MSG("Bad glyph count %d > %d in alternate set table", glyph_count, num_glyphs);
+  }
+  for (unsigned i = 0; i < glyph_count; ++i) {
+    uint16_t alternate = 0;
+    if (!subtable.ReadU16(&alternate)) {
+      return OTS_FAILURE_MSG("Can't read alternate %d", i);
+    }
+    if (alternate >= num_glyphs) {
+      return OTS_FAILURE_MSG("Too large alternate: %u", alternate);
+    }
+  }
+  return true;
+}
+
+// Lookup Type 3:
+// Alternate Substitution Subtable
+bool ParseAlternateSubstitution(const ots::OpenTypeFile *file,
+                                const uint8_t *data, const size_t length) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t format = 0;
+  uint16_t offset_coverage = 0;
+  uint16_t alternate_set_count = 0;
+
+  if (!subtable.ReadU16(&format) ||
+      !subtable.ReadU16(&offset_coverage) ||
+      !subtable.ReadU16(&alternate_set_count)) {
+    return OTS_FAILURE_MSG("Can't read alternate subst header");
+  }
+
+  if (format != 1) {
+    return OTS_FAILURE_MSG("Bad alternate subst table format %d", format);
+  }
+
+  const uint16_t num_glyphs = file->maxp->num_glyphs;
+  const unsigned alternate_set_end = static_cast<unsigned>(6) +
+      alternate_set_count * 2;
+  if (alternate_set_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE_MSG("Bad end of alternate set %d", alternate_set_end);
+  }
+  for (unsigned i = 0; i < alternate_set_count; ++i) {
+    uint16_t offset_alternate_set = 0;
+    if (!subtable.ReadU16(&offset_alternate_set)) {
+      return OTS_FAILURE_MSG("Can't read alternate set offset for set %d", i);
+    }
+    if (offset_alternate_set < alternate_set_end ||
+        offset_alternate_set >= length) {
+      return OTS_FAILURE_MSG("Bad alternate set offset %d for set %d", offset_alternate_set, i);
+    }
+    if (!ParseAlternateSetTable(file, data + offset_alternate_set,
+                                length - offset_alternate_set,
+                                num_glyphs)) {
+      return OTS_FAILURE_MSG("Failed to parse alternate set");
+    }
+  }
+
+  if (offset_coverage < alternate_set_end || offset_coverage >= length) {
+    return OTS_FAILURE_MSG("Bad coverage offset %d", offset_coverage);
+  }
+  if (!ots::ParseCoverageTable(file, data + offset_coverage,
+                               length - offset_coverage, num_glyphs)) {
+    return OTS_FAILURE_MSG("Failed to parse coverage table");
+  }
+
+  return true;
+}
+
+bool ParseLigatureTable(const ots::OpenTypeFile *file,
+                        const uint8_t *data, const size_t length,
+                        const uint16_t num_glyphs) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t lig_glyph = 0;
+  uint16_t comp_count = 0;
+
+  if (!subtable.ReadU16(&lig_glyph) ||
+      !subtable.ReadU16(&comp_count)) {
+    return OTS_FAILURE_MSG("Failed to read ligatuer table header");
+  }
+
+  if (lig_glyph >= num_glyphs) {
+    return OTS_FAILURE_MSG("too large lig_glyph: %u", lig_glyph);
+  }
+  if (comp_count == 0 || comp_count > num_glyphs) {
+    return OTS_FAILURE_MSG("Bad component count of %d", comp_count);
+  }
+  for (unsigned i = 0; i < comp_count - static_cast<unsigned>(1); ++i) {
+    uint16_t component = 0;
+    if (!subtable.ReadU16(&component)) {
+      return OTS_FAILURE_MSG("Can't read ligature component %d", i);
+    }
+    if (component >= num_glyphs) {
+      return OTS_FAILURE_MSG("Bad ligature component %d of %d", i, component);
+    }
+  }
+
+  return true;
+}
+
+bool ParseLigatureSetTable(const ots::OpenTypeFile *file,
+                           const uint8_t *data, const size_t length,
+                           const uint16_t num_glyphs) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t ligature_count = 0;
+
+  if (!subtable.ReadU16(&ligature_count)) {
+    return OTS_FAILURE_MSG("Can't read ligature count in ligature set");
+  }
+
+  const unsigned ligature_end = static_cast<unsigned>(2) + ligature_count * 2;
+  if (ligature_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE_MSG("Bad end of ligature %d in ligature set", ligature_end);
+  }
+  for (unsigned i = 0; i < ligature_count; ++i) {
+    uint16_t offset_ligature = 0;
+    if (!subtable.ReadU16(&offset_ligature)) {
+      return OTS_FAILURE_MSG("Failed to read ligature offset %d", i);
+    }
+    if (offset_ligature < ligature_end || offset_ligature >= length) {
+      return OTS_FAILURE_MSG("Bad ligature offset %d for ligature %d", offset_ligature, i);
+    }
+    if (!ParseLigatureTable(file, data + offset_ligature, length - offset_ligature,
+                            num_glyphs)) {
+      return OTS_FAILURE_MSG("Failed to parse ligature %d", i);
+    }
+  }
+
+  return true;
+}
+
+// Lookup Type 4:
+// Ligature Substitution Subtable
+bool ParseLigatureSubstitution(const ots::OpenTypeFile *file,
+                               const uint8_t *data, const size_t length) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t format = 0;
+  uint16_t offset_coverage = 0;
+  uint16_t lig_set_count = 0;
+
+  if (!subtable.ReadU16(&format) ||
+      !subtable.ReadU16(&offset_coverage) ||
+      !subtable.ReadU16(&lig_set_count)) {
+    return OTS_FAILURE_MSG("Failed to read ligature substitution header");
+  }
+
+  if (format != 1) {
+    return OTS_FAILURE_MSG("Bad ligature substitution table format %d", format);
+  }
+
+  const uint16_t num_glyphs = file->maxp->num_glyphs;
+  const unsigned ligature_set_end = static_cast<unsigned>(6) +
+      lig_set_count * 2;
+  if (ligature_set_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE_MSG("Bad end of ligature set %d in ligature substitution table", ligature_set_end);
+  }
+  for (unsigned i = 0; i < lig_set_count; ++i) {
+    uint16_t offset_ligature_set = 0;
+    if (!subtable.ReadU16(&offset_ligature_set)) {
+      return OTS_FAILURE_MSG("Can't read ligature set offset %d", i);
+    }
+    if (offset_ligature_set < ligature_set_end ||
+        offset_ligature_set >= length) {
+      return OTS_FAILURE_MSG("Bad ligature set offset %d for set %d", offset_ligature_set, i);
+    }
+    if (!ParseLigatureSetTable(file, data + offset_ligature_set,
+                               length - offset_ligature_set, num_glyphs)) {
+      return OTS_FAILURE_MSG("Failed to parse ligature set %d", i);
+    }
+  }
+
+  if (offset_coverage < ligature_set_end || offset_coverage >= length) {
+    return OTS_FAILURE_MSG("Bad coverage offset %d", offset_coverage);
+  }
+  if (!ots::ParseCoverageTable(file, data + offset_coverage,
+                               length - offset_coverage, num_glyphs)) {
+    return OTS_FAILURE_MSG("Failed to parse coverage table");
+  }
+
+  return true;
+}
+
+// Lookup Type 5:
+// Contextual Substitution Subtable
+bool ParseContextSubstitution(const ots::OpenTypeFile *file,
+                              const uint8_t *data, const size_t length) {
+  return ots::ParseContextSubtable(file, data, length, file->maxp->num_glyphs,
+                                   file->gsub->num_lookups);
+}
+
+// Lookup Type 6:
+// Chaining Contextual Substitution Subtable
+bool ParseChainingContextSubstitution(const ots::OpenTypeFile *file,
+                                      const uint8_t *data,
+                                      const size_t length) {
+  return ots::ParseChainingContextSubtable(file, data, length,
+                                           file->maxp->num_glyphs,
+                                           file->gsub->num_lookups);
+}
+
+// Lookup Type 7:
+// Extension Substition
+bool ParseExtensionSubstitution(const ots::OpenTypeFile *file,
+                                const uint8_t *data, const size_t length) {
+  return ots::ParseExtensionSubtable(file, data, length,
+                                     &kGsubLookupSubtableParser);
+}
+
+// Lookup Type 8:
+// Reverse Chaining Contexual Single Substitution Subtable
+bool ParseReverseChainingContextSingleSubstitution(
+    const ots::OpenTypeFile *file, const uint8_t *data, const size_t length) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t format = 0;
+  uint16_t offset_coverage = 0;
+
+  if (!subtable.ReadU16(&format) ||
+      !subtable.ReadU16(&offset_coverage)) {
+    return OTS_FAILURE_MSG("Failed to read reverse chaining header");
+  }
+
+  const uint16_t num_glyphs = file->maxp->num_glyphs;
+
+  uint16_t backtrack_glyph_count = 0;
+  if (!subtable.ReadU16(&backtrack_glyph_count)) {
+    return OTS_FAILURE_MSG("Failed to read backtrack glyph count in reverse chaining table");
+  }
+  if (backtrack_glyph_count > num_glyphs) {
+    return OTS_FAILURE_MSG("Bad backtrack glyph count of %d", backtrack_glyph_count);
+  }
+  std::vector<uint16_t> offsets_backtrack;
+  offsets_backtrack.reserve(backtrack_glyph_count);
+  for (unsigned i = 0; i < backtrack_glyph_count; ++i) {
+    uint16_t offset = 0;
+    if (!subtable.ReadU16(&offset)) {
+      return OTS_FAILURE_MSG("Failed to read backtrack offset %d", i);
+    }
+    offsets_backtrack.push_back(offset);
+  }
+
+  uint16_t lookahead_glyph_count = 0;
+  if (!subtable.ReadU16(&lookahead_glyph_count)) {
+    return OTS_FAILURE_MSG("Failed to read look ahead glyph count");
+  }
+  if (lookahead_glyph_count > num_glyphs) {
+    return OTS_FAILURE_MSG("Bad look ahead glyph count %d", lookahead_glyph_count);
+  }
+  std::vector<uint16_t> offsets_lookahead;
+  offsets_lookahead.reserve(lookahead_glyph_count);
+  for (unsigned i = 0; i < lookahead_glyph_count; ++i) {
+    uint16_t offset = 0;
+    if (!subtable.ReadU16(&offset)) {
+      return OTS_FAILURE_MSG("Can't read look ahead offset %d", i);
+    }
+    offsets_lookahead.push_back(offset);
+  }
+
+  uint16_t glyph_count = 0;
+  if (!subtable.ReadU16(&glyph_count)) {
+    return OTS_FAILURE_MSG("Can't read glyph count in reverse chaining table");
+  }
+  if (glyph_count > num_glyphs) {
+    return OTS_FAILURE_MSG("Bad glyph count of %d", glyph_count);
+  }
+  for (unsigned i = 0; i < glyph_count; ++i) {
+    uint16_t substitute = 0;
+    if (!subtable.ReadU16(&substitute)) {
+      return OTS_FAILURE_MSG("Failed to read substitution %d reverse chaining table", i);
+    }
+    if (substitute >= num_glyphs) {
+      return OTS_FAILURE_MSG("Bad substitute glyph %d in reverse chaining table substitution %d", substitute, i);
+    }
+  }
+
+  const unsigned substitute_end = static_cast<unsigned>(10) +
+      (backtrack_glyph_count + lookahead_glyph_count + glyph_count) * 2;
+  if (substitute_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE_MSG("Bad substitute end offset in reverse chaining table");
+  }
+
+  if (offset_coverage < substitute_end || offset_coverage >= length) {
+    return OTS_FAILURE_MSG("Bad coverage offset %d in reverse chaining table", offset_coverage);
+  }
+  if (!ots::ParseCoverageTable(file, data + offset_coverage,
+                               length - offset_coverage, num_glyphs)) {
+    return OTS_FAILURE_MSG("Failed to parse coverage table in reverse chaining table");
+  }
+
+  for (unsigned i = 0; i < backtrack_glyph_count; ++i) {
+    if (offsets_backtrack[i] < substitute_end ||
+        offsets_backtrack[i] >= length) {
+      return OTS_FAILURE_MSG("Bad backtrack offset %d for backtrack %d in reverse chaining table", offsets_backtrack[i], i);
+    }
+    if (!ots::ParseCoverageTable(file, data + offsets_backtrack[i],
+                                 length - offsets_backtrack[i], num_glyphs)) {
+      return OTS_FAILURE_MSG("Failed to parse coverage table for backtrack %d in reverse chaining table", i);
+    }
+  }
+
+  for (unsigned i = 0; i < lookahead_glyph_count; ++i) {
+    if (offsets_lookahead[i] < substitute_end ||
+        offsets_lookahead[i] >= length) {
+      return OTS_FAILURE_MSG("Bad lookahead offset %d for lookahead %d in reverse chaining table", offsets_lookahead[i], i);
+    }
+    if (!ots::ParseCoverageTable(file, data + offsets_lookahead[i],
+                                 length - offsets_lookahead[i], num_glyphs)) {
+      return OTS_FAILURE_MSG("Failed to parse lookahead coverage table %d in reverse chaining table", i);
+    }
+  }
+
+  return true;
+}
+
+}  // namespace
+
+#define DROP_THIS_TABLE(msg_) \
+  do { \
+    OTS_FAILURE_MSG(msg_ ", table discarded"); \
+    file->gsub->data = 0; \
+    file->gsub->length = 0; \
+  } while (0)
+
+namespace ots {
+
+// As far as I checked, following fonts contain invalid values in GSUB table.
+// OTS will drop their GSUB table.
+//
+// # too large substitute (value is 0xFFFF)
+// kaiu.ttf
+// mingliub2.ttf
+// mingliub1.ttf
+// mingliub0.ttf
+// GraublauWeb.otf
+// GraublauWebBold.otf
+//
+// # too large alternate (value is 0xFFFF)
+// ManchuFont.ttf
+//
+// # bad offset to lang sys table (NULL offset)
+// DejaVuMonoSansBold.ttf
+// DejaVuMonoSansBoldOblique.ttf
+// DejaVuMonoSansOblique.ttf
+// DejaVuSansMono-BoldOblique.ttf
+// DejaVuSansMono-Oblique.ttf
+// DejaVuSansMono-Bold.ttf
+//
+// # bad start coverage index
+// GenBasBI.ttf
+// GenBasI.ttf
+// AndBasR.ttf
+// GenBkBasI.ttf
+// CharisSILR.ttf
+// CharisSILBI.ttf
+// CharisSILI.ttf
+// CharisSILB.ttf
+// DoulosSILR.ttf
+// CharisSILBI.ttf
+// GenBkBasB.ttf
+// GenBkBasR.ttf
+// GenBkBasBI.ttf
+// GenBasB.ttf
+// GenBasR.ttf
+//
+// # glyph range is overlapping
+// KacstTitleL.ttf
+// KacstDecorative.ttf
+// KacstTitle.ttf
+// KacstArt.ttf
+// KacstPoster.ttf
+// KacstQurn.ttf
+// KacstDigital.ttf
+// KacstBook.ttf
+// KacstFarsi.ttf
+
+bool ots_gsub_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
+  // Parsing gsub table requires |file->maxp->num_glyphs|
+  if (!file->maxp) {
+    return OTS_FAILURE_MSG("Missing maxp table in font, needed by GSUB");
+  }
+
+  Buffer table(data, length);
+
+  OpenTypeGSUB *gsub = new OpenTypeGSUB;
+  file->gsub = gsub;
+
+  uint32_t version = 0;
+  uint16_t offset_script_list = 0;
+  uint16_t offset_feature_list = 0;
+  uint16_t offset_lookup_list = 0;
+  if (!table.ReadU32(&version) ||
+      !table.ReadU16(&offset_script_list) ||
+      !table.ReadU16(&offset_feature_list) ||
+      !table.ReadU16(&offset_lookup_list)) {
+    DROP_THIS_TABLE("Incomplete table");
+    return true;
+  }
+
+  if (version != 0x00010000) {
+    DROP_THIS_TABLE("Bad version");
+    return true;
+  }
+
+  if (offset_lookup_list) {
+    if (offset_lookup_list < kGsubHeaderSize || offset_lookup_list >= length) {
+      DROP_THIS_TABLE("Bad lookup list offset in table header");
+      return true;
+    }
+
+    if (!ParseLookupListTable(file, data + offset_lookup_list,
+                              length - offset_lookup_list,
+                              &kGsubLookupSubtableParser,
+                              &gsub->num_lookups)) {
+      DROP_THIS_TABLE("Failed to parse lookup list table");
+      return true;
+    }
+  }
+
+  uint16_t num_features = 0;
+  if (offset_feature_list) {
+    if (offset_feature_list < kGsubHeaderSize || offset_feature_list >= length) {
+      DROP_THIS_TABLE("Bad feature list offset in table header");
+      return true;
+    }
+
+    if (!ParseFeatureListTable(file, data + offset_feature_list,
+                               length - offset_feature_list, gsub->num_lookups,
+                               &num_features)) {
+      DROP_THIS_TABLE("Failed to parse feature list table");
+      return true;
+    }
+  }
+
+  if (offset_script_list) {
+    if (offset_script_list < kGsubHeaderSize || offset_script_list >= length) {
+      DROP_THIS_TABLE("Bad script list offset in table header");
+      return true;
+    }
+
+    if (!ParseScriptListTable(file, data + offset_script_list,
+                              length - offset_script_list, num_features)) {
+      DROP_THIS_TABLE("Failed to parse script list table");
+      return true;
+    }
+  }
+
+  gsub->data = data;
+  gsub->length = length;
+  return true;
+}
+
+bool ots_gsub_should_serialise(OpenTypeFile *file) {
+  return file->gsub != NULL && file->gsub->data != NULL;
+}
+
+bool ots_gsub_serialise(OTSStream *out, OpenTypeFile *file) {
+  if (!out->Write(file->gsub->data, file->gsub->length)) {
+    return OTS_FAILURE_MSG("Failed to write GSUB table");
+  }
+
+  return true;
+}
+
+void ots_gsub_free(OpenTypeFile *file) {
+  delete file->gsub;
+}
+
+}  // namespace ots
+
+#undef TABLE_NAME
+#undef DROP_THIS_TABLE
diff --git a/third_party/ots/src/gsub.h b/third_party/ots/src/gsub.h
new file mode 100644
index 0000000..f6f8cd3
--- /dev/null
+++ b/third_party/ots/src/gsub.h
@@ -0,0 +1,29 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_GSUB_H_
+#define OTS_GSUB_H_
+
+#include "ots.h"
+
+namespace ots {
+
+struct OpenTypeGSUB {
+  OpenTypeGSUB()
+      : num_lookups(0),
+        data(NULL),
+        length(0) {
+  }
+
+  // Number of lookups in GPSUB table
+  uint16_t num_lookups;
+
+  const uint8_t *data;
+  size_t length;
+};
+
+}  // namespace ots
+
+#endif  // OTS_GSUB_H_
+
diff --git a/third_party/ots/src/hdmx.cc b/third_party/ots/src/hdmx.cc
new file mode 100644
index 0000000..098802b
--- /dev/null
+++ b/third_party/ots/src/hdmx.cc
@@ -0,0 +1,142 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "hdmx.h"
+#include "head.h"
+#include "maxp.h"
+
+// hdmx - Horizontal Device Metrics
+// http://www.microsoft.com/typography/otspec/hdmx.htm
+
+#define TABLE_NAME "hdmx"
+
+#define DROP_THIS_TABLE(...) \
+  do { \
+    OTS_FAILURE_MSG_(file, TABLE_NAME ": " __VA_ARGS__); \
+    OTS_FAILURE_MSG("Table discarded"); \
+    delete file->hdmx; \
+    file->hdmx = 0; \
+  } while (0)
+
+namespace ots {
+
+bool ots_hdmx_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
+  Buffer table(data, length);
+  file->hdmx = new OpenTypeHDMX;
+  OpenTypeHDMX * const hdmx = file->hdmx;
+
+  if (!file->head || !file->maxp) {
+    return OTS_FAILURE_MSG("Missing maxp or head tables in font, needed by hdmx");
+  }
+
+  if ((file->head->flags & 0x14) == 0) {
+    // http://www.microsoft.com/typography/otspec/recom.htm
+    DROP_THIS_TABLE("the table should not be present when bit 2 and 4 of the "
+                    "head->flags are not set");
+    return true;
+  }
+
+  int16_t num_recs;
+  if (!table.ReadU16(&hdmx->version) ||
+      !table.ReadS16(&num_recs) ||
+      !table.ReadS32(&hdmx->size_device_record)) {
+    return OTS_FAILURE_MSG("Failed to read hdmx header");
+  }
+  if (hdmx->version != 0) {
+    DROP_THIS_TABLE("bad version: %u", hdmx->version);
+    return true;
+  }
+  if (num_recs <= 0) {
+    DROP_THIS_TABLE("bad num_recs: %d", num_recs);
+    return true;
+  }
+  const int32_t actual_size_device_record = file->maxp->num_glyphs + 2;
+  if (hdmx->size_device_record < actual_size_device_record) {
+    DROP_THIS_TABLE("bad hdmx->size_device_record: %d", hdmx->size_device_record);
+    return true;
+  }
+
+  hdmx->pad_len = hdmx->size_device_record - actual_size_device_record;
+  if (hdmx->pad_len > 3) {
+    return OTS_FAILURE_MSG("Bad padding %d", hdmx->pad_len);
+  }
+
+  uint8_t last_pixel_size = 0;
+  hdmx->records.reserve(num_recs);
+  for (int i = 0; i < num_recs; ++i) {
+    OpenTypeHDMXDeviceRecord rec;
+
+    if (!table.ReadU8(&rec.pixel_size) ||
+        !table.ReadU8(&rec.max_width)) {
+      return OTS_FAILURE_MSG("Failed to read hdmx record %d", i);
+    }
+    if ((i != 0) &&
+        (rec.pixel_size <= last_pixel_size)) {
+      DROP_THIS_TABLE("records are not sorted");
+      return true;
+    }
+    last_pixel_size = rec.pixel_size;
+
+    rec.widths.reserve(file->maxp->num_glyphs);
+    for (unsigned j = 0; j < file->maxp->num_glyphs; ++j) {
+      uint8_t width;
+      if (!table.ReadU8(&width)) {
+        return OTS_FAILURE_MSG("Failed to read glyph width %d in record %d", j, i);
+      }
+      rec.widths.push_back(width);
+    }
+
+    if ((hdmx->pad_len > 0) &&
+        !table.Skip(hdmx->pad_len)) {
+      return OTS_FAILURE_MSG("Failed to skip padding %d", hdmx->pad_len);
+    }
+
+    hdmx->records.push_back(rec);
+  }
+
+  return true;
+}
+
+bool ots_hdmx_should_serialise(OpenTypeFile *file) {
+  if (!file->hdmx) return false;
+  if (!file->glyf) return false;  // this table is not for CFF fonts.
+  return true;
+}
+
+bool ots_hdmx_serialise(OTSStream *out, OpenTypeFile *file) {
+  OpenTypeHDMX * const hdmx = file->hdmx;
+
+  const int16_t num_recs = static_cast<int16_t>(hdmx->records.size());
+  if (hdmx->records.size() >
+          static_cast<size_t>(std::numeric_limits<int16_t>::max()) ||
+      !out->WriteU16(hdmx->version) ||
+      !out->WriteS16(num_recs) ||
+      !out->WriteS32(hdmx->size_device_record)) {
+    return OTS_FAILURE_MSG("Failed to write hdmx header");
+  }
+
+  for (int16_t i = 0; i < num_recs; ++i) {
+    const OpenTypeHDMXDeviceRecord& rec = hdmx->records[i];
+    if (!out->Write(&rec.pixel_size, 1) ||
+        !out->Write(&rec.max_width, 1) ||
+        !out->Write(&rec.widths[0], rec.widths.size())) {
+      return OTS_FAILURE_MSG("Failed to write hdmx record %d", i);
+    }
+    if ((hdmx->pad_len > 0) &&
+        !out->Write((const uint8_t *)"\x00\x00\x00", hdmx->pad_len)) {
+      return OTS_FAILURE_MSG("Failed to write hdmx padding of length %d", hdmx->pad_len);
+    }
+  }
+
+  return true;
+}
+
+void ots_hdmx_free(OpenTypeFile *file) {
+  delete file->hdmx;
+}
+
+}  // namespace ots
+
+#undef TABLE_NAME
+#undef DROP_THIS_TABLE
diff --git a/third_party/ots/src/hdmx.h b/third_party/ots/src/hdmx.h
new file mode 100644
index 0000000..9ec2124
--- /dev/null
+++ b/third_party/ots/src/hdmx.h
@@ -0,0 +1,29 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_HDMX_H_
+#define OTS_HDMX_H_
+
+#include <vector>
+
+#include "ots.h"
+
+namespace ots {
+
+struct OpenTypeHDMXDeviceRecord {
+  uint8_t pixel_size;
+  uint8_t max_width;
+  std::vector<uint8_t> widths;
+};
+
+struct OpenTypeHDMX {
+  uint16_t version;
+  int32_t size_device_record;
+  int32_t pad_len;
+  std::vector<OpenTypeHDMXDeviceRecord> records;
+};
+
+}  // namespace ots
+
+#endif
diff --git a/third_party/ots/src/head.cc b/third_party/ots/src/head.cc
new file mode 100644
index 0000000..dcd234d
--- /dev/null
+++ b/third_party/ots/src/head.cc
@@ -0,0 +1,153 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "head.h"
+
+#include <cstring>
+
+// head - Font Header
+// http://www.microsoft.com/typography/otspec/head.htm
+
+#define TABLE_NAME "head"
+
+namespace ots {
+
+bool ots_head_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
+  Buffer table(data, length);
+  file->head = new OpenTypeHEAD;
+
+  uint32_t version = 0;
+  if (!table.ReadU32(&version) ||
+      !table.ReadU32(&file->head->revision)) {
+    return OTS_FAILURE_MSG("Failed to read head header");
+  }
+
+  if (version >> 16 != 1) {
+    return OTS_FAILURE_MSG("Bad head table version of %d", version);
+  }
+
+  // Skip the checksum adjustment
+  if (!table.Skip(4)) {
+    return OTS_FAILURE_MSG("Failed to read checksum");
+  }
+
+  uint32_t magic;
+  if (!table.ReadTag(&magic) ||
+      std::memcmp(&magic, "\x5F\x0F\x3C\xF5", 4)) {
+    return OTS_FAILURE_MSG("Failed to read font magic number");
+  }
+
+  if (!table.ReadU16(&file->head->flags)) {
+    return OTS_FAILURE_MSG("Failed to read head flags");
+  }
+
+  // We allow bits 0..4, 11..13
+  file->head->flags &= 0x381f;
+
+  if (!table.ReadU16(&file->head->ppem)) {
+    return OTS_FAILURE_MSG("Failed to read pixels per em");
+  }
+
+  // ppem must be in range
+  if (file->head->ppem < 16 ||
+      file->head->ppem > 16384) {
+    return OTS_FAILURE_MSG("Bad ppm of %d", file->head->ppem);
+  }
+
+  // ppem must be a power of two
+#if 0
+  // We don't call ots_failure() for now since lots of TrueType fonts are
+  // not following this rule. Putting OTS_WARNING here is too noisy.
+  if ((file->head->ppem - 1) & file->head->ppem) {
+    return OTS_FAILURE_MSG("ppm not a power of two: %d", file->head->ppem);
+  }
+#endif
+
+  if (!table.ReadR64(&file->head->created) ||
+      !table.ReadR64(&file->head->modified)) {
+    return OTS_FAILURE_MSG("Can't read font dates");
+  }
+
+  if (!table.ReadS16(&file->head->xmin) ||
+      !table.ReadS16(&file->head->ymin) ||
+      !table.ReadS16(&file->head->xmax) ||
+      !table.ReadS16(&file->head->ymax)) {
+    return OTS_FAILURE_MSG("Failed to read font bounding box");
+  }
+
+  if (file->head->xmin > file->head->xmax) {
+    return OTS_FAILURE_MSG("Bad x dimension in the font bounding box (%d, %d)", file->head->xmin, file->head->xmax);
+  }
+  if (file->head->ymin > file->head->ymax) {
+    return OTS_FAILURE_MSG("Bad y dimension in the font bounding box (%d, %d)", file->head->ymin, file->head->ymax);
+  }
+
+  if (!table.ReadU16(&file->head->mac_style)) {
+    return OTS_FAILURE_MSG("Failed to read font style");
+  }
+
+  // We allow bits 0..6
+  file->head->mac_style &= 0x7f;
+
+  if (!table.ReadU16(&file->head->min_ppem)) {
+    return OTS_FAILURE_MSG("Failed to read font minimum ppm");
+  }
+
+  // We don't care about the font direction hint
+  if (!table.Skip(2)) {
+    return OTS_FAILURE_MSG("Failed to skip font direction hint");
+  }
+
+  if (!table.ReadS16(&file->head->index_to_loc_format)) {
+    return OTS_FAILURE_MSG("Failed to read index to loc format");
+  }
+  if (file->head->index_to_loc_format < 0 ||
+      file->head->index_to_loc_format > 1) {
+    return OTS_FAILURE_MSG("Bad index to loc format %d", file->head->index_to_loc_format);
+  }
+
+  int16_t glyph_data_format;
+  if (!table.ReadS16(&glyph_data_format) ||
+      glyph_data_format) {
+    return OTS_FAILURE_MSG("Failed to read glyph data format");
+  }
+
+  return true;
+}
+
+bool ots_head_should_serialise(OpenTypeFile *file) {
+  return file->head != NULL;
+}
+
+bool ots_head_serialise(OTSStream *out, OpenTypeFile *file) {
+  if (!out->WriteU32(0x00010000) ||
+      !out->WriteU32(file->head->revision) ||
+      !out->WriteU32(0) ||  // check sum not filled in yet
+      !out->WriteU32(0x5F0F3CF5) ||
+      !out->WriteU16(file->head->flags) ||
+      !out->WriteU16(file->head->ppem) ||
+      !out->WriteR64(file->head->created) ||
+      !out->WriteR64(file->head->modified) ||
+      !out->WriteS16(file->head->xmin) ||
+      !out->WriteS16(file->head->ymin) ||
+      !out->WriteS16(file->head->xmax) ||
+      !out->WriteS16(file->head->ymax) ||
+      !out->WriteU16(file->head->mac_style) ||
+      !out->WriteU16(file->head->min_ppem) ||
+      !out->WriteS16(2) ||
+      !out->WriteS16(file->head->index_to_loc_format) ||
+      !out->WriteS16(0)) {
+    return OTS_FAILURE_MSG("Failed to write head table");
+  }
+
+  return true;
+}
+
+void ots_head_free(OpenTypeFile *file) {
+  delete file->head;
+}
+
+}  // namespace
+
+#undef TABLE_NAME
diff --git a/third_party/ots/src/head.h b/third_party/ots/src/head.h
new file mode 100644
index 0000000..5967c4b
--- /dev/null
+++ b/third_party/ots/src/head.h
@@ -0,0 +1,29 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_HEAD_H_
+#define OTS_HEAD_H_
+
+#include "ots.h"
+
+namespace ots {
+
+struct OpenTypeHEAD {
+  uint32_t revision;
+  uint16_t flags;
+  uint16_t ppem;
+  uint64_t created;
+  uint64_t modified;
+
+  int16_t xmin, xmax;
+  int16_t ymin, ymax;
+
+  uint16_t mac_style;
+  uint16_t min_ppem;
+  int16_t index_to_loc_format;
+};
+
+}  // namespace ots
+
+#endif  // OTS_HEAD_H_
diff --git a/third_party/ots/src/hhea.cc b/third_party/ots/src/hhea.cc
new file mode 100644
index 0000000..8430442
--- /dev/null
+++ b/third_party/ots/src/hhea.cc
@@ -0,0 +1,53 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "hhea.h"
+
+#include "head.h"
+#include "maxp.h"
+
+// hhea - Horizontal Header
+// http://www.microsoft.com/typography/otspec/hhea.htm
+
+#define TABLE_NAME "hhea"
+
+namespace ots {
+
+bool ots_hhea_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
+  Buffer table(data, length);
+  OpenTypeHHEA *hhea = new OpenTypeHHEA;
+  file->hhea = hhea;
+
+  if (!table.ReadU32(&hhea->header.version)) {
+    return OTS_FAILURE_MSG("Failed to read hhea version");
+  }
+  if (hhea->header.version >> 16 != 1) {
+    return OTS_FAILURE_MSG("Bad hhea version of %d", hhea->header.version);
+  }
+
+  if (!ParseMetricsHeader(file, &table, &hhea->header)) {
+    return OTS_FAILURE_MSG("Failed to parse horizontal metrics");
+  }
+
+  return true;
+}
+
+bool ots_hhea_should_serialise(OpenTypeFile *file) {
+  return file->hhea != NULL;
+}
+
+bool ots_hhea_serialise(OTSStream *out, OpenTypeFile *file) {
+  if (!SerialiseMetricsHeader(file, out, &file->hhea->header)) {
+    return OTS_FAILURE_MSG("Failed to serialise horizontal metrics");
+  }
+  return true;
+}
+
+void ots_hhea_free(OpenTypeFile *file) {
+  delete file->hhea;
+}
+
+}  // namespace ots
+
+#undef TABLE_NAME
diff --git a/third_party/ots/src/hhea.h b/third_party/ots/src/hhea.h
new file mode 100644
index 0000000..bdea9aa
--- /dev/null
+++ b/third_party/ots/src/hhea.h
@@ -0,0 +1,19 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_HHEA_H_
+#define OTS_HHEA_H_
+
+#include "metrics.h"
+#include "ots.h"
+
+namespace ots {
+
+struct OpenTypeHHEA {
+  OpenTypeMetricsHeader header;
+};
+
+}  // namespace ots
+
+#endif  // OTS_HHEA_H_
diff --git a/third_party/ots/src/hmtx.cc b/third_party/ots/src/hmtx.cc
new file mode 100644
index 0000000..ae86513
--- /dev/null
+++ b/third_party/ots/src/hmtx.cc
@@ -0,0 +1,51 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "hmtx.h"
+
+#include "hhea.h"
+#include "maxp.h"
+
+// hmtx - Horizontal Metrics
+// http://www.microsoft.com/typography/otspec/hmtx.htm
+
+#define TABLE_NAME "hmtx"
+
+namespace ots {
+
+bool ots_hmtx_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
+  Buffer table(data, length);
+  OpenTypeHMTX *hmtx = new OpenTypeHMTX;
+  file->hmtx = hmtx;
+
+  if (!file->hhea || !file->maxp) {
+    return OTS_FAILURE_MSG("Missing hhea or maxp tables in font, needed by hmtx");
+  }
+
+  if (!ParseMetricsTable(file, &table, file->maxp->num_glyphs,
+                         &file->hhea->header, &hmtx->metrics)) {
+    return OTS_FAILURE_MSG("Failed to parse hmtx metrics");
+  }
+
+  return true;
+}
+
+bool ots_hmtx_should_serialise(OpenTypeFile *file) {
+  return file->hmtx != NULL;
+}
+
+bool ots_hmtx_serialise(OTSStream *out, OpenTypeFile *file) {
+  if (!SerialiseMetricsTable(file, out, &file->hmtx->metrics)) {
+    return OTS_FAILURE_MSG("Failed to serialise htmx metrics");
+  }
+  return true;
+}
+
+void ots_hmtx_free(OpenTypeFile *file) {
+  delete file->hmtx;
+}
+
+}  // namespace ots
+
+#undef TABLE_NAME
diff --git a/third_party/ots/src/hmtx.h b/third_party/ots/src/hmtx.h
new file mode 100644
index 0000000..435949c
--- /dev/null
+++ b/third_party/ots/src/hmtx.h
@@ -0,0 +1,19 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_HMTX_H_
+#define OTS_HMTX_H_
+
+#include "metrics.h"
+#include "ots.h"
+
+namespace ots {
+
+struct OpenTypeHMTX {
+  OpenTypeMetricsTable metrics;
+};
+
+}  // namespace ots
+
+#endif  // OTS_HMTX_H_
diff --git a/third_party/ots/src/kern.cc b/third_party/ots/src/kern.cc
new file mode 100644
index 0000000..744c057db
--- /dev/null
+++ b/third_party/ots/src/kern.cc
@@ -0,0 +1,203 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "kern.h"
+
+// kern - Kerning
+// http://www.microsoft.com/typography/otspec/kern.htm
+
+#define TABLE_NAME "kern"
+
+#define DROP_THIS_TABLE(msg_) \
+  do { \
+    OTS_FAILURE_MSG(msg_ ", table discarded"); \
+    delete file->kern; \
+    file->kern = 0; \
+  } while (0)
+
+namespace ots {
+
+bool ots_kern_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
+  Buffer table(data, length);
+
+  OpenTypeKERN *kern = new OpenTypeKERN;
+  file->kern = kern;
+
+  uint16_t num_tables = 0;
+  if (!table.ReadU16(&kern->version) ||
+      !table.ReadU16(&num_tables)) {
+    return OTS_FAILURE_MSG("Failed to read kern header");
+  }
+
+  if (kern->version > 0) {
+    DROP_THIS_TABLE("bad table version");
+    return true;
+  }
+
+  if (num_tables == 0) {
+    DROP_THIS_TABLE("num_tables is zero");
+    return true;
+  }
+
+  kern->subtables.reserve(num_tables);
+  for (unsigned i = 0; i < num_tables; ++i) {
+    OpenTypeKERNFormat0 subtable;
+    uint16_t sub_length = 0;
+
+    if (!table.ReadU16(&subtable.version) ||
+        !table.ReadU16(&sub_length)) {
+      return OTS_FAILURE_MSG("Failed to read kern subtable %d header", i);
+    }
+
+    if (subtable.version > 0) {
+      OTS_WARNING("Bad subtable version: %d", subtable.version);
+      continue;
+    }
+
+    const size_t current_offset = table.offset();
+    if (current_offset - 4 + sub_length > length) {
+      return OTS_FAILURE_MSG("Bad kern subtable %d offset %ld", i, current_offset);
+    }
+
+    if (!table.ReadU16(&subtable.coverage)) {
+      return OTS_FAILURE_MSG("Cailed to read kern subtable %d coverage", i);
+    }
+
+    if (!(subtable.coverage & 0x1)) {
+      OTS_WARNING(
+          "We don't support vertical data as the renderer doesn't support it.");
+      continue;
+    }
+    if (subtable.coverage & 0xF0) {
+      DROP_THIS_TABLE("Reserved fields should zero-filled.");
+      return true;
+    }
+    const uint32_t format = (subtable.coverage & 0xFF00) >> 8;
+    if (format != 0) {
+      OTS_WARNING("Format %d is not supported.", format);
+      continue;
+    }
+
+    // Parse the format 0 field.
+    uint16_t num_pairs = 0;
+    if (!table.ReadU16(&num_pairs) ||
+        !table.ReadU16(&subtable.search_range) ||
+        !table.ReadU16(&subtable.entry_selector) ||
+        !table.ReadU16(&subtable.range_shift)) {
+      return OTS_FAILURE_MSG("Failed to read kern subtable %d format 0 fields", i);
+    }
+
+    if (!num_pairs) {
+      DROP_THIS_TABLE("Zero length subtable is found.");
+      return true;
+    }
+
+    // Sanity checks for search_range, entry_selector, and range_shift. See the
+    // comment in ots.cc for details.
+    const size_t kFormat0PairSize = 6;  // left, right, and value. 2 bytes each.
+    if (num_pairs > (65536 / kFormat0PairSize)) {
+      // Some fonts (e.g. calibri.ttf, pykes_peak_zero.ttf) have pairs >= 10923.
+      DROP_THIS_TABLE("Too large subtable.");
+      return true;
+    }
+    unsigned max_pow2 = 0;
+    while (1u << (max_pow2 + 1) <= num_pairs) {
+      ++max_pow2;
+    }
+    const uint16_t expected_search_range = (1u << max_pow2) * kFormat0PairSize;
+    if (subtable.search_range != expected_search_range) {
+      OTS_WARNING("bad search range");
+      subtable.search_range = expected_search_range;
+    }
+    if (subtable.entry_selector != max_pow2) {
+      return OTS_FAILURE_MSG("Bad subtable %d entry selector %d", i, subtable.entry_selector);
+    }
+    const uint16_t expected_range_shift =
+        kFormat0PairSize * num_pairs - subtable.search_range;
+    if (subtable.range_shift != expected_range_shift) {
+      OTS_WARNING("bad range shift");
+      subtable.range_shift = expected_range_shift;
+    }
+
+    // Read kerning pairs.
+    subtable.pairs.reserve(num_pairs);
+    uint32_t last_pair = 0;
+    for (unsigned j = 0; j < num_pairs; ++j) {
+      OpenTypeKERNFormat0Pair kerning_pair;
+      if (!table.ReadU16(&kerning_pair.left) ||
+          !table.ReadU16(&kerning_pair.right) ||
+          !table.ReadS16(&kerning_pair.value)) {
+        return OTS_FAILURE_MSG("Failed to read subtable %d kerning pair %d", i, j);
+      }
+      const uint32_t current_pair
+          = (kerning_pair.left << 16) + kerning_pair.right;
+      if (j != 0 && current_pair <= last_pair) {
+        // Many free fonts don't follow this rule, so we don't call OTS_FAILURE
+        // in order to support these fonts.
+        DROP_THIS_TABLE("Kerning pairs are not sorted.");
+        return true;
+      }
+      last_pair = current_pair;
+      subtable.pairs.push_back(kerning_pair);
+    }
+
+    kern->subtables.push_back(subtable);
+  }
+
+  if (!kern->subtables.size()) {
+    DROP_THIS_TABLE("All subtables are removed.");
+    return true;
+  }
+
+  return true;
+}
+
+bool ots_kern_should_serialise(OpenTypeFile *file) {
+  if (!file->glyf) return false;  // this table is not for CFF fonts.
+  return file->kern != NULL;
+}
+
+bool ots_kern_serialise(OTSStream *out, OpenTypeFile *file) {
+  const OpenTypeKERN *kern = file->kern;
+
+  const uint16_t num_subtables = static_cast<uint16_t>(kern->subtables.size());
+  if (num_subtables != kern->subtables.size() ||
+      !out->WriteU16(kern->version) ||
+      !out->WriteU16(num_subtables)) {
+    return OTS_FAILURE_MSG("Can't write kern table header");
+  }
+
+  for (uint16_t i = 0; i < num_subtables; ++i) {
+    const size_t length = 14 + (6 * kern->subtables[i].pairs.size());
+    if (length > std::numeric_limits<uint16_t>::max() ||
+        !out->WriteU16(kern->subtables[i].version) ||
+        !out->WriteU16(static_cast<uint16_t>(length)) ||
+        !out->WriteU16(kern->subtables[i].coverage) ||
+        !out->WriteU16(
+            static_cast<uint16_t>(kern->subtables[i].pairs.size())) ||
+        !out->WriteU16(kern->subtables[i].search_range) ||
+        !out->WriteU16(kern->subtables[i].entry_selector) ||
+        !out->WriteU16(kern->subtables[i].range_shift)) {
+      return OTS_FAILURE_MSG("Failed to write kern subtable %d", i);
+    }
+    for (unsigned j = 0; j < kern->subtables[i].pairs.size(); ++j) {
+      if (!out->WriteU16(kern->subtables[i].pairs[j].left) ||
+          !out->WriteU16(kern->subtables[i].pairs[j].right) ||
+          !out->WriteS16(kern->subtables[i].pairs[j].value)) {
+        return OTS_FAILURE_MSG("Failed to write kern pair %d for subtable %d", j, i);
+      }
+    }
+  }
+
+  return true;
+}
+
+void ots_kern_free(OpenTypeFile *file) {
+  delete file->kern;
+}
+
+}  // namespace ots
+
+#undef TABLE_NAME
+#undef DROP_THIS_TABLE
diff --git a/third_party/ots/src/kern.h b/third_party/ots/src/kern.h
new file mode 100644
index 0000000..9350ef7
--- /dev/null
+++ b/third_party/ots/src/kern.h
@@ -0,0 +1,40 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_KERN_H_
+#define OTS_KERN_H_
+
+#include <vector>
+
+#include "ots.h"
+
+namespace ots {
+
+struct OpenTypeKERNFormat0Pair {
+  uint16_t left;
+  uint16_t right;
+  int16_t value;
+};
+
+struct OpenTypeKERNFormat0 {
+  uint16_t version;
+  uint16_t coverage;
+  uint16_t search_range;
+  uint16_t entry_selector;
+  uint16_t range_shift;
+  std::vector<OpenTypeKERNFormat0Pair> pairs;
+};
+
+// Format 2 is not supported. Since the format is not supported by Windows,
+// WebFonts unlikely use it. I've checked thousands of proprietary fonts and
+// free fonts, and found no font uses the format.
+
+struct OpenTypeKERN {
+  uint16_t version;
+  std::vector<OpenTypeKERNFormat0> subtables;
+};
+
+}  // namespace ots
+
+#endif  // OTS_KERN_H_
diff --git a/third_party/ots/src/layout.cc b/third_party/ots/src/layout.cc
new file mode 100644
index 0000000..856152c
--- /dev/null
+++ b/third_party/ots/src/layout.cc
@@ -0,0 +1,1511 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "layout.h"
+
+#include <limits>
+#include <vector>
+
+#include "gdef.h"
+
+// OpenType Layout Common Table Formats
+// http://www.microsoft.com/typography/otspec/chapter2.htm
+
+#define TABLE_NAME "Layout" // XXX: use individual table names
+
+namespace {
+
+// The 'DFLT' tag of script table.
+const uint32_t kScriptTableTagDflt = 0x44464c54;
+// The value which represents there is no required feature index.
+const uint16_t kNoRequiredFeatureIndexDefined = 0xFFFF;
+// The lookup flag bit which indicates existence of MarkFilteringSet.
+const uint16_t kUseMarkFilteringSetBit = 0x0010;
+// The lookup flags which require GDEF table.
+const uint16_t kGdefRequiredFlags = 0x0002 | 0x0004 | 0x0008;
+// The mask for MarkAttachmentType.
+const uint16_t kMarkAttachmentTypeMask = 0xFF00;
+// The maximum type number of format for device tables.
+const uint16_t kMaxDeltaFormatType = 3;
+// The maximum number of class value.
+const uint16_t kMaxClassDefValue = 0xFFFF;
+
+struct ScriptRecord {
+  uint32_t tag;
+  uint16_t offset;
+};
+
+struct LangSysRecord {
+  uint32_t tag;
+  uint16_t offset;
+};
+
+struct FeatureRecord {
+  uint32_t tag;
+  uint16_t offset;
+};
+
+bool ParseLangSysTable(const ots::OpenTypeFile *file,
+                       ots::Buffer *subtable, const uint32_t tag,
+                       const uint16_t num_features) {
+  uint16_t offset_lookup_order = 0;
+  uint16_t req_feature_index = 0;
+  uint16_t feature_count = 0;
+  if (!subtable->ReadU16(&offset_lookup_order) ||
+      !subtable->ReadU16(&req_feature_index) ||
+      !subtable->ReadU16(&feature_count)) {
+    return OTS_FAILURE_MSG("Failed to read langsys header for tag %4.4s", (char *)&tag);
+  }
+  // |offset_lookup_order| is reserved and should be NULL.
+  if (offset_lookup_order != 0) {
+    return OTS_FAILURE_MSG("Bad lookup offset order %d for langsys tag %4.4s", offset_lookup_order, (char *)&tag);
+  }
+  if (req_feature_index != kNoRequiredFeatureIndexDefined &&
+      req_feature_index >= num_features) {
+    return OTS_FAILURE_MSG("Bad required features index %d for langsys tag %4.4s", req_feature_index, (char *)&tag);
+  }
+  if (feature_count > num_features) {
+    return OTS_FAILURE_MSG("Bad feature count %d for langsys tag %4.4s", feature_count, (char *)&tag);
+  }
+
+  for (unsigned i = 0; i < feature_count; ++i) {
+    uint16_t feature_index = 0;
+    if (!subtable->ReadU16(&feature_index)) {
+      return OTS_FAILURE_MSG("Failed to read feature index %d for langsys tag %4.4s", i, (char *)&tag);
+    }
+    if (feature_index >= num_features) {
+      return OTS_FAILURE_MSG("Bad feature index %d for feature %d for langsys tag %4.4s", feature_index, i, (char *)&tag);
+    }
+  }
+  return true;
+}
+
+bool ParseScriptTable(const ots::OpenTypeFile *file,
+                      const uint8_t *data, const size_t length,
+                      const uint32_t tag, const uint16_t num_features) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t offset_default_lang_sys = 0;
+  uint16_t lang_sys_count = 0;
+  if (!subtable.ReadU16(&offset_default_lang_sys) ||
+      !subtable.ReadU16(&lang_sys_count)) {
+    return OTS_FAILURE_MSG("Failed to read script header for script tag %4.4s", (char *)&tag);
+  }
+
+  // The spec requires a script table for 'DFLT' tag must contain non-NULL
+  // |offset_default_lang_sys| and |lang_sys_count| == 0
+  if (tag == kScriptTableTagDflt &&
+      (offset_default_lang_sys == 0 || lang_sys_count != 0)) {
+    return OTS_FAILURE_MSG("DFLT table doesn't satisfy the spec. for script tag %4.4s", (char *)&tag);
+  }
+
+  const unsigned lang_sys_record_end =
+      6 * static_cast<unsigned>(lang_sys_count) + 4;
+  if (lang_sys_record_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE_MSG("Bad end of langsys record %d for script tag %4.4s", lang_sys_record_end, (char *)&tag);
+  }
+
+  std::vector<LangSysRecord> lang_sys_records;
+  lang_sys_records.resize(lang_sys_count);
+  uint32_t last_tag = 0;
+  for (unsigned i = 0; i < lang_sys_count; ++i) {
+    if (!subtable.ReadU32(&lang_sys_records[i].tag) ||
+        !subtable.ReadU16(&lang_sys_records[i].offset)) {
+      return OTS_FAILURE_MSG("Failed to read langsys record header %d for script tag %4.4s", i, (char *)&tag);
+    }
+    // The record array must store the records alphabetically by tag
+    if (last_tag != 0 && last_tag > lang_sys_records[i].tag) {
+      return OTS_FAILURE_MSG("Bad last tag %d for langsys record %d for script tag %4.4s", last_tag, i, (char *)&tag);
+    }
+    if (lang_sys_records[i].offset < lang_sys_record_end ||
+        lang_sys_records[i].offset >= length) {
+      return OTS_FAILURE_MSG("bad offset to lang sys table: %x",
+                  lang_sys_records[i].offset);
+    }
+    last_tag = lang_sys_records[i].tag;
+  }
+
+  // Check lang sys tables
+  for (unsigned i = 0; i < lang_sys_count; ++i) {
+    subtable.set_offset(lang_sys_records[i].offset);
+    if (!ParseLangSysTable(file, &subtable, lang_sys_records[i].tag, num_features)) {
+      return OTS_FAILURE_MSG("Failed to parse langsys table %d (%4.4s) for script tag %4.4s", i, (char *)&lang_sys_records[i].tag, (char *)&tag);
+    }
+  }
+
+  return true;
+}
+
+bool ParseFeatureTable(const ots::OpenTypeFile *file,
+                       const uint8_t *data, const size_t length,
+                       const uint16_t num_lookups) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t offset_feature_params = 0;
+  uint16_t lookup_count = 0;
+  if (!subtable.ReadU16(&offset_feature_params) ||
+      !subtable.ReadU16(&lookup_count)) {
+    return OTS_FAILURE_MSG("Failed to read feature table header");
+  }
+
+  const unsigned feature_table_end =
+      2 * static_cast<unsigned>(lookup_count) + 4;
+  if (feature_table_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE_MSG("Bad end of feature table %d", feature_table_end);
+  }
+  // |offset_feature_params| is generally set to NULL.
+  if (offset_feature_params != 0 &&
+      (offset_feature_params < feature_table_end ||
+       offset_feature_params >= length)) {
+    return OTS_FAILURE_MSG("Bad feature params offset %d", offset_feature_params);
+  }
+
+  for (unsigned i = 0; i < lookup_count; ++i) {
+    uint16_t lookup_index = 0;
+    if (!subtable.ReadU16(&lookup_index)) {
+      return OTS_FAILURE_MSG("Failed to read lookup index for lookup %d", i);
+    }
+    // lookup index starts with 0.
+    if (lookup_index >= num_lookups) {
+      return OTS_FAILURE_MSG("Bad lookup index %d for lookup %d", lookup_index, i);
+    }
+  }
+  return true;
+}
+
+bool ParseLookupTable(ots::OpenTypeFile *file, const uint8_t *data,
+                      const size_t length,
+                      const ots::LookupSubtableParser* parser) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t lookup_type = 0;
+  uint16_t lookup_flag = 0;
+  uint16_t subtable_count = 0;
+  if (!subtable.ReadU16(&lookup_type) ||
+      !subtable.ReadU16(&lookup_flag) ||
+      !subtable.ReadU16(&subtable_count)) {
+    return OTS_FAILURE_MSG("Failed to read lookup table header");
+  }
+
+  if (lookup_type == 0 || lookup_type > parser->num_types) {
+    return OTS_FAILURE_MSG("Bad lookup type %d", lookup_type);
+  }
+
+  // Check lookup flags.
+  if ((lookup_flag & kGdefRequiredFlags) &&
+      (!file->gdef || !file->gdef->has_glyph_class_def)) {
+    return OTS_FAILURE_MSG("Bad lookup flags %d", lookup_flag);
+  }
+  if ((lookup_flag & kMarkAttachmentTypeMask) &&
+      (!file->gdef || !file->gdef->has_mark_attachment_class_def)) {
+    return OTS_FAILURE_MSG("lookup flag asks for mark attachment that is bad %d", lookup_flag);
+  }
+  bool use_mark_filtering_set = false;
+  if (lookup_flag & kUseMarkFilteringSetBit) {
+    if (!file->gdef || !file->gdef->has_mark_glyph_sets_def) {
+      return OTS_FAILURE_MSG("lookup flag asks for mark filtering that is bad %d", lookup_flag);
+    }
+    use_mark_filtering_set = true;
+  }
+
+  std::vector<uint16_t> subtables;
+  subtables.reserve(subtable_count);
+  // If the |kUseMarkFilteringSetBit| of |lookup_flag| is set,
+  // extra 2 bytes will follow after subtable offset array.
+  const unsigned lookup_table_end = 2 * static_cast<unsigned>(subtable_count) +
+      (use_mark_filtering_set ? 8 : 6);
+  if (lookup_table_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE_MSG("Bad end of lookup %d", lookup_table_end);
+  }
+  for (unsigned i = 0; i < subtable_count; ++i) {
+    uint16_t offset_subtable = 0;
+    if (!subtable.ReadU16(&offset_subtable)) {
+      return OTS_FAILURE_MSG("Failed to read subtable offset %d", i);
+    }
+    if (offset_subtable < lookup_table_end ||
+        offset_subtable >= length) {
+      return OTS_FAILURE_MSG("Bad subtable offset %d for subtable %d", offset_subtable, i);
+    }
+    subtables.push_back(offset_subtable);
+  }
+  if (subtables.size() != subtable_count) {
+    return OTS_FAILURE_MSG("Bad subtable size %ld", subtables.size());
+  }
+
+  if (use_mark_filtering_set) {
+    uint16_t mark_filtering_set = 0;
+    if (!subtable.ReadU16(&mark_filtering_set)) {
+      return OTS_FAILURE_MSG("Failed to read mark filtering set");
+    }
+    if (file->gdef->num_mark_glyph_sets == 0 ||
+        mark_filtering_set >= file->gdef->num_mark_glyph_sets) {
+      return OTS_FAILURE_MSG("Bad mark filtering set %d", mark_filtering_set);
+    }
+  }
+
+  // Parse lookup subtables for this lookup type.
+  for (unsigned i = 0; i < subtable_count; ++i) {
+    if (!parser->Parse(file, data + subtables[i], length - subtables[i],
+                       lookup_type)) {
+      return OTS_FAILURE_MSG("Failed to parse subtable %d", i);
+    }
+  }
+  return true;
+}
+
+bool ParseClassDefFormat1(const ots::OpenTypeFile *file,
+                          const uint8_t *data, size_t length,
+                          const uint16_t num_glyphs,
+                          const uint16_t num_classes) {
+  ots::Buffer subtable(data, length);
+
+  // Skip format field.
+  if (!subtable.Skip(2)) {
+    return OTS_FAILURE_MSG("Failed to skip class definition header");
+  }
+
+  uint16_t start_glyph = 0;
+  if (!subtable.ReadU16(&start_glyph)) {
+    return OTS_FAILURE_MSG("Failed to read starting glyph of class definition");
+  }
+  if (start_glyph > num_glyphs) {
+    return OTS_FAILURE_MSG("Bad starting glyph %d in class definition", start_glyph);
+  }
+
+  uint16_t glyph_count = 0;
+  if (!subtable.ReadU16(&glyph_count)) {
+    return OTS_FAILURE_MSG("Failed to read glyph count in class definition");
+  }
+  if (glyph_count > num_glyphs) {
+    return OTS_FAILURE_MSG("bad glyph count: %u", glyph_count);
+  }
+  for (unsigned i = 0; i < glyph_count; ++i) {
+    uint16_t class_value = 0;
+    if (!subtable.ReadU16(&class_value)) {
+      return OTS_FAILURE_MSG("Failed to read class value for glyph %d in class definition", i);
+    }
+    if (class_value > num_classes) {
+      return OTS_FAILURE_MSG("Bad class value %d for glyph %d in class definition", class_value, i);
+    }
+  }
+
+  return true;
+}
+
+bool ParseClassDefFormat2(const ots::OpenTypeFile *file,
+                          const uint8_t *data, size_t length,
+                          const uint16_t num_glyphs,
+                          const uint16_t num_classes) {
+  ots::Buffer subtable(data, length);
+
+  // Skip format field.
+  if (!subtable.Skip(2)) {
+    return OTS_FAILURE_MSG("Failed to skip format of class defintion header");
+  }
+
+  uint16_t range_count = 0;
+  if (!subtable.ReadU16(&range_count)) {
+    return OTS_FAILURE_MSG("Failed to read range count in class definition");
+  }
+  if (range_count > num_glyphs) {
+    return OTS_FAILURE_MSG("bad range count: %u", range_count);
+  }
+
+  uint16_t last_end = 0;
+  for (unsigned i = 0; i < range_count; ++i) {
+    uint16_t start = 0;
+    uint16_t end = 0;
+    uint16_t class_value = 0;
+    if (!subtable.ReadU16(&start) ||
+        !subtable.ReadU16(&end) ||
+        !subtable.ReadU16(&class_value)) {
+      return OTS_FAILURE_MSG("Failed to read class definition reange %d", i);
+    }
+    if (start > end || (last_end && start <= last_end)) {
+      return OTS_FAILURE_MSG("glyph range is overlapping.in range %d", i);
+    }
+    if (class_value > num_classes) {
+      return OTS_FAILURE_MSG("bad class value: %u", class_value);
+    }
+    last_end = end;
+  }
+
+  return true;
+}
+
+bool ParseCoverageFormat1(const ots::OpenTypeFile *file,
+                          const uint8_t *data, size_t length,
+                          const uint16_t num_glyphs,
+                          const uint16_t expected_num_glyphs) {
+  ots::Buffer subtable(data, length);
+
+  // Skip format field.
+  if (!subtable.Skip(2)) {
+    return OTS_FAILURE_MSG("Failed to skip coverage format");
+  }
+
+  uint16_t glyph_count = 0;
+  if (!subtable.ReadU16(&glyph_count)) {
+    return OTS_FAILURE_MSG("Failed to read glyph count in coverage");
+  }
+  if (glyph_count > num_glyphs) {
+    return OTS_FAILURE_MSG("bad glyph count: %u", glyph_count);
+  }
+  for (unsigned i = 0; i < glyph_count; ++i) {
+    uint16_t glyph = 0;
+    if (!subtable.ReadU16(&glyph)) {
+      return OTS_FAILURE_MSG("Failed to read glyph %d in coverage", i);
+    }
+    if (glyph > num_glyphs) {
+      return OTS_FAILURE_MSG("bad glyph ID: %u", glyph);
+    }
+  }
+
+  if (expected_num_glyphs && expected_num_glyphs != glyph_count) {
+      return OTS_FAILURE_MSG("unexpected number of glyphs: %u", glyph_count);
+  }
+
+  return true;
+}
+
+bool ParseCoverageFormat2(const ots::OpenTypeFile *file,
+                          const uint8_t *data, size_t length,
+                          const uint16_t num_glyphs,
+                          const uint16_t expected_num_glyphs) {
+  ots::Buffer subtable(data, length);
+
+  // Skip format field.
+  if (!subtable.Skip(2)) {
+    return OTS_FAILURE_MSG("Failed to skip format of coverage type 2");
+  }
+
+  uint16_t range_count = 0;
+  if (!subtable.ReadU16(&range_count)) {
+    return OTS_FAILURE_MSG("Failed to read range count in coverage");
+  }
+  if (range_count > num_glyphs) {
+    return OTS_FAILURE_MSG("bad range count: %u", range_count);
+  }
+  uint16_t last_end = 0;
+  uint16_t last_start_coverage_index = 0;
+  for (unsigned i = 0; i < range_count; ++i) {
+    uint16_t start = 0;
+    uint16_t end = 0;
+    uint16_t start_coverage_index = 0;
+    if (!subtable.ReadU16(&start) ||
+        !subtable.ReadU16(&end) ||
+        !subtable.ReadU16(&start_coverage_index)) {
+      return OTS_FAILURE_MSG("Failed to read range %d in coverage", i);
+    }
+
+    // Some of the Adobe Pro fonts have ranges that overlap by one element: the
+    // start of one range is equal to the end of the previous range. Therefore
+    // the < in the following condition should be <= were it not for this.
+    // See crbug.com/134135.
+    if (start > end || (last_end && start < last_end)) {
+      return OTS_FAILURE_MSG("glyph range is overlapping.");
+    }
+    if (start_coverage_index != last_start_coverage_index) {
+      return OTS_FAILURE_MSG("bad start coverage index.");
+    }
+    last_end = end;
+    last_start_coverage_index += end - start + 1;
+  }
+
+  if (expected_num_glyphs &&
+      expected_num_glyphs != last_start_coverage_index) {
+      return OTS_FAILURE_MSG("unexpected number of glyphs: %u", last_start_coverage_index);
+  }
+
+  return true;
+}
+
+// Parsers for Contextual subtables in GSUB/GPOS tables.
+
+bool ParseLookupRecord(const ots::OpenTypeFile *file,
+                       ots::Buffer *subtable, const uint16_t num_glyphs,
+                       const uint16_t num_lookups) {
+  uint16_t sequence_index = 0;
+  uint16_t lookup_list_index = 0;
+  if (!subtable->ReadU16(&sequence_index) ||
+      !subtable->ReadU16(&lookup_list_index)) {
+    return OTS_FAILURE_MSG("Failed to read header for lookup record");
+  }
+  if (sequence_index >= num_glyphs) {
+    return OTS_FAILURE_MSG("Bad sequence index %d in lookup record", sequence_index);
+  }
+  if (lookup_list_index >= num_lookups) {
+    return OTS_FAILURE_MSG("Bad lookup list index %d in lookup record", lookup_list_index);
+  }
+  return true;
+}
+
+bool ParseRuleSubtable(const ots::OpenTypeFile *file,
+                       const uint8_t *data, const size_t length,
+                       const uint16_t num_glyphs,
+                       const uint16_t num_lookups) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t glyph_count = 0;
+  uint16_t lookup_count = 0;
+  if (!subtable.ReadU16(&glyph_count) ||
+      !subtable.ReadU16(&lookup_count)) {
+    return OTS_FAILURE_MSG("Failed to read rule subtable header");
+  }
+
+  if (glyph_count == 0 || glyph_count >= num_glyphs) {
+    return OTS_FAILURE_MSG("Bad glyph count %d in rule subtable", glyph_count);
+  }
+  for (unsigned i = 0; i < glyph_count - static_cast<unsigned>(1); ++i) {
+    uint16_t glyph_id = 0;
+    if (!subtable.ReadU16(&glyph_id)) {
+      return OTS_FAILURE_MSG("Failed to read glyph %d", i);
+    }
+    if (glyph_id > num_glyphs) {
+      return OTS_FAILURE_MSG("Bad glyph %d for entry %d", glyph_id, i);
+    }
+  }
+
+  for (unsigned i = 0; i < lookup_count; ++i) {
+    if (!ParseLookupRecord(file, &subtable, num_glyphs, num_lookups)) {
+      return OTS_FAILURE_MSG("Failed to parse lookup record %d", i);
+    }
+  }
+  return true;
+}
+
+bool ParseRuleSetTable(const ots::OpenTypeFile *file,
+                       const uint8_t *data, const size_t length,
+                       const uint16_t num_glyphs,
+                       const uint16_t num_lookups) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t rule_count = 0;
+  if (!subtable.ReadU16(&rule_count)) {
+    return OTS_FAILURE_MSG("Failed to read rule count in rule set");
+  }
+  const unsigned rule_end = 2 * static_cast<unsigned>(rule_count) + 2;
+  if (rule_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE_MSG("Bad end of rule %d in rule set", rule_end);
+  }
+
+  for (unsigned i = 0; i < rule_count; ++i) {
+    uint16_t offset_rule = 0;
+    if (!subtable.ReadU16(&offset_rule)) {
+      return OTS_FAILURE_MSG("Failed to read rule offset for rule set %d", i);
+    }
+    if (offset_rule < rule_end || offset_rule >= length) {
+      return OTS_FAILURE_MSG("Bad rule offset %d in set %d", offset_rule, i);
+    }
+    if (!ParseRuleSubtable(file, data + offset_rule, length - offset_rule,
+                           num_glyphs, num_lookups)) {
+      return OTS_FAILURE_MSG("Failed to parse rule set %d", i);
+    }
+  }
+
+  return true;
+}
+
+bool ParseContextFormat1(const ots::OpenTypeFile *file,
+                         const uint8_t *data, const size_t length,
+                         const uint16_t num_glyphs,
+                         const uint16_t num_lookups) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t offset_coverage = 0;
+  uint16_t rule_set_count = 0;
+  // Skip format field.
+  if (!subtable.Skip(2) ||
+      !subtable.ReadU16(&offset_coverage) ||
+      !subtable.ReadU16(&rule_set_count)) {
+    return OTS_FAILURE_MSG("Failed to read header of context format 1");
+  }
+
+  const unsigned rule_set_end = static_cast<unsigned>(6) +
+      rule_set_count * 2;
+  if (rule_set_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE_MSG("Bad end of rule set %d of context format 1", rule_set_end);
+  }
+  if (offset_coverage < rule_set_end || offset_coverage >= length) {
+    return OTS_FAILURE_MSG("Bad coverage offset %d in context format 1", offset_coverage);
+  }
+  if (!ots::ParseCoverageTable(file, data + offset_coverage,
+                               length - offset_coverage, num_glyphs)) {
+    return OTS_FAILURE_MSG("Failed to parse coverage table in context format 1");
+  }
+
+  for (unsigned i = 0; i < rule_set_count; ++i) {
+    uint16_t offset_rule = 0;
+    if (!subtable.ReadU16(&offset_rule)) {
+      return OTS_FAILURE_MSG("Failed to read rule offset %d in context format 1", i);
+    }
+    if (offset_rule < rule_set_end || offset_rule >= length) {
+      return OTS_FAILURE_MSG("Bad rule offset %d in rule %d in context format 1", offset_rule, i);
+    }
+    if (!ParseRuleSetTable(file, data + offset_rule, length - offset_rule,
+                           num_glyphs, num_lookups)) {
+      return OTS_FAILURE_MSG("Failed to parse rule set %d in context format 1", i);
+    }
+  }
+
+  return true;
+}
+
+bool ParseClassRuleTable(const ots::OpenTypeFile *file,
+                         const uint8_t *data, const size_t length,
+                         const uint16_t num_glyphs,
+                         const uint16_t num_lookups) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t glyph_count = 0;
+  uint16_t lookup_count = 0;
+  if (!subtable.ReadU16(&glyph_count) ||
+      !subtable.ReadU16(&lookup_count)) {
+    return OTS_FAILURE_MSG("Failed to read header of class rule table");
+  }
+
+  if (glyph_count == 0 || glyph_count >= num_glyphs) {
+    return OTS_FAILURE_MSG("Bad glyph count %d in class rule table", glyph_count);
+  }
+
+  // ClassRule table contains an array of classes. Each value of classes
+  // could take arbitrary values including zero so we don't check these value.
+  const unsigned num_classes = glyph_count - static_cast<unsigned>(1);
+  if (!subtable.Skip(2 * num_classes)) {
+    return OTS_FAILURE_MSG("Failed to skip classes in class rule table");
+  }
+
+  for (unsigned i = 0; i < lookup_count; ++i) {
+    if (!ParseLookupRecord(file, &subtable, num_glyphs, num_lookups)) {
+      return OTS_FAILURE_MSG("Failed to parse lookup record %d in class rule table", i);
+    }
+  }
+  return true;
+}
+
+bool ParseClassSetTable(const ots::OpenTypeFile *file,
+                        const uint8_t *data, const size_t length,
+                        const uint16_t num_glyphs,
+                        const uint16_t num_lookups) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t class_rule_count = 0;
+  if (!subtable.ReadU16(&class_rule_count)) {
+    return OTS_FAILURE_MSG("Failed to read class rule count in class set table");
+  }
+  const unsigned class_rule_end =
+      2 * static_cast<unsigned>(class_rule_count) + 2;
+  if (class_rule_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE_MSG("bad class rule end %d in class set table", class_rule_end);
+  }
+  for (unsigned i = 0; i < class_rule_count; ++i) {
+    uint16_t offset_class_rule = 0;
+    if (!subtable.ReadU16(&offset_class_rule)) {
+      return OTS_FAILURE_MSG("Failed to read class rule offset %d in class set table", i);
+    }
+    if (offset_class_rule < class_rule_end || offset_class_rule >= length) {
+      return OTS_FAILURE_MSG("Bad class rule offset %d in class %d", offset_class_rule, i);
+    }
+    if (!ParseClassRuleTable(file, data + offset_class_rule,
+                             length - offset_class_rule, num_glyphs,
+                             num_lookups)) {
+      return OTS_FAILURE_MSG("Failed to parse class rule table %d", i);
+    }
+  }
+
+  return true;
+}
+
+bool ParseContextFormat2(const ots::OpenTypeFile *file,
+                         const uint8_t *data, const size_t length,
+                         const uint16_t num_glyphs,
+                         const uint16_t num_lookups) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t offset_coverage = 0;
+  uint16_t offset_class_def = 0;
+  uint16_t class_set_cnt = 0;
+  // Skip format field.
+  if (!subtable.Skip(2) ||
+      !subtable.ReadU16(&offset_coverage) ||
+      !subtable.ReadU16(&offset_class_def) ||
+      !subtable.ReadU16(&class_set_cnt)) {
+    return OTS_FAILURE_MSG("Failed to read header for context format 2");
+  }
+
+  const unsigned class_set_end = 2 * static_cast<unsigned>(class_set_cnt) + 8;
+  if (class_set_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE_MSG("Bad end of class set %d for context format 2", class_set_end);
+  }
+  if (offset_coverage < class_set_end || offset_coverage >= length) {
+    return OTS_FAILURE_MSG("Bad coverage offset %d in context format 2", offset_coverage);
+  }
+  if (!ots::ParseCoverageTable(file, data + offset_coverage,
+                               length - offset_coverage, num_glyphs)) {
+    return OTS_FAILURE_MSG("Failed to parse coverage table in context format 2");
+  }
+
+  if (offset_class_def < class_set_end || offset_class_def >= length) {
+    return OTS_FAILURE_MSG("bad class definition offset %d in context format 2", offset_class_def);
+  }
+  if (!ots::ParseClassDefTable(file, data + offset_class_def,
+                               length - offset_class_def,
+                               num_glyphs, kMaxClassDefValue)) {
+    return OTS_FAILURE_MSG("Failed to parse class definition table in context format 2");
+  }
+
+  for (unsigned i = 0; i < class_set_cnt; ++i) {
+    uint16_t offset_class_rule = 0;
+    if (!subtable.ReadU16(&offset_class_rule)) {
+      return OTS_FAILURE_MSG("Failed to read class rule offset %d in context format 2", i);
+    }
+    if (offset_class_rule) {
+      if (offset_class_rule < class_set_end || offset_class_rule >= length) {
+        return OTS_FAILURE_MSG("Bad class rule offset %d for rule %d in context format 2", offset_class_rule, i);
+      }
+      if (!ParseClassSetTable(file, data + offset_class_rule,
+                              length - offset_class_rule, num_glyphs,
+                              num_lookups)) {
+        return OTS_FAILURE_MSG("Failed to parse class set %d in context format 2", i);
+      }
+    }
+  }
+
+  return true;
+}
+
+bool ParseContextFormat3(const ots::OpenTypeFile *file,
+                         const uint8_t *data, const size_t length,
+                         const uint16_t num_glyphs,
+                         const uint16_t num_lookups) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t glyph_count = 0;
+  uint16_t lookup_count = 0;
+  // Skip format field.
+  if (!subtable.Skip(2) ||
+      !subtable.ReadU16(&glyph_count) ||
+      !subtable.ReadU16(&lookup_count)) {
+    return OTS_FAILURE_MSG("Failed to read header in context format 3");
+  }
+
+  if (glyph_count >= num_glyphs) {
+    return OTS_FAILURE_MSG("Bad glyph count %d in context format 3", glyph_count);
+  }
+  const unsigned lookup_record_end = 2 * static_cast<unsigned>(glyph_count) +
+      4 * static_cast<unsigned>(lookup_count) + 6;
+  if (lookup_record_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE_MSG("Bad end of lookup %d in context format 3", lookup_record_end);
+  }
+  for (unsigned i = 0; i < glyph_count; ++i) {
+    uint16_t offset_coverage = 0;
+    if (!subtable.ReadU16(&offset_coverage)) {
+      return OTS_FAILURE_MSG("Failed to read coverage offset %d in conxtext format 3", i);
+    }
+    if (offset_coverage < lookup_record_end || offset_coverage >= length) {
+      return OTS_FAILURE_MSG("Bad coverage offset %d for glyph %d in context format 3", offset_coverage, i);
+    }
+    if (!ots::ParseCoverageTable(file, data + offset_coverage,
+                                 length - offset_coverage, num_glyphs)) {
+      return OTS_FAILURE_MSG("Failed to parse coverage table for glyph %d in context format 3", i);
+    }
+  }
+
+  for (unsigned i = 0; i < lookup_count; ++i) {
+    if (!ParseLookupRecord(file, &subtable, num_glyphs, num_lookups)) {
+      return OTS_FAILURE_MSG("Failed to parse lookup record %d in context format 3", i);
+    }
+  }
+
+  return true;
+}
+
+// Parsers for Chaning Contextual subtables in GSUB/GPOS tables.
+
+bool ParseChainRuleSubtable(const ots::OpenTypeFile *file,
+                            const uint8_t *data, const size_t length,
+                            const uint16_t num_glyphs,
+                            const uint16_t num_lookups) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t backtrack_count = 0;
+  if (!subtable.ReadU16(&backtrack_count)) {
+    return OTS_FAILURE_MSG("Failed to read backtrack count in chain rule subtable");
+  }
+  if (backtrack_count >= num_glyphs) {
+    return OTS_FAILURE_MSG("Bad backtrack count %d in chain rule subtable", backtrack_count);
+  }
+  for (unsigned i = 0; i < backtrack_count; ++i) {
+    uint16_t glyph_id = 0;
+    if (!subtable.ReadU16(&glyph_id)) {
+      return OTS_FAILURE_MSG("Failed to read backtrack glyph %d in chain rule subtable", i);
+    }
+    if (glyph_id > num_glyphs) {
+      return OTS_FAILURE_MSG("Bad glyph id %d for bactrack glyph %d in chain rule subtable", glyph_id, i);
+    }
+  }
+
+  uint16_t input_count = 0;
+  if (!subtable.ReadU16(&input_count)) {
+    return OTS_FAILURE_MSG("Failed to read input count in chain rule subtable");
+  }
+  if (input_count == 0 || input_count >= num_glyphs) {
+    return OTS_FAILURE_MSG("Bad input count %d in chain rule subtable", input_count);
+  }
+  for (unsigned i = 0; i < input_count - static_cast<unsigned>(1); ++i) {
+    uint16_t glyph_id = 0;
+    if (!subtable.ReadU16(&glyph_id)) {
+      return OTS_FAILURE_MSG("Failed to read input glyph %d in chain rule subtable", i);
+    }
+    if (glyph_id > num_glyphs) {
+      return OTS_FAILURE_MSG("Bad glyph id %d for input glyph %d in chain rule subtable", glyph_id, i);
+    }
+  }
+
+  uint16_t lookahead_count = 0;
+  if (!subtable.ReadU16(&lookahead_count)) {
+    return OTS_FAILURE_MSG("Failed to read lookahead count in chain rule subtable");
+  }
+  if (lookahead_count >= num_glyphs) {
+    return OTS_FAILURE_MSG("Bad lookahead count %d in chain rule subtable", lookahead_count);
+  }
+  for (unsigned i = 0; i < lookahead_count; ++i) {
+    uint16_t glyph_id = 0;
+    if (!subtable.ReadU16(&glyph_id)) {
+      return OTS_FAILURE_MSG("Failed to read lookahead glyph %d in chain rule subtable", i);
+    }
+    if (glyph_id > num_glyphs) {
+      return OTS_FAILURE_MSG("Bad glyph id %d for lookadhead glyph %d in chain rule subtable", glyph_id, i);
+    }
+  }
+
+  uint16_t lookup_count = 0;
+  if (!subtable.ReadU16(&lookup_count)) {
+    return OTS_FAILURE_MSG("Failed to read lookup count in chain rule subtable");
+  }
+  for (unsigned i = 0; i < lookup_count; ++i) {
+    if (!ParseLookupRecord(file, &subtable, num_glyphs, num_lookups)) {
+      return OTS_FAILURE_MSG("Failed to parse lookup record %d in chain rule subtable", i);
+    }
+  }
+
+  return true;
+}
+
+bool ParseChainRuleSetTable(const ots::OpenTypeFile *file,
+                            const uint8_t *data, const size_t length,
+                            const uint16_t num_glyphs,
+                            const uint16_t num_lookups) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t chain_rule_count = 0;
+  if (!subtable.ReadU16(&chain_rule_count)) {
+    return OTS_FAILURE_MSG("Failed to read rule count in chain rule set");
+  }
+  const unsigned chain_rule_end =
+      2 * static_cast<unsigned>(chain_rule_count) + 2;
+  if (chain_rule_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE_MSG("Bad end of chain rule %d in chain rule set", chain_rule_end);
+  }
+  for (unsigned i = 0; i < chain_rule_count; ++i) {
+    uint16_t offset_chain_rule = 0;
+    if (!subtable.ReadU16(&offset_chain_rule)) {
+      return OTS_FAILURE_MSG("Failed to read chain rule offset %d in chain rule set", i);
+    }
+    if (offset_chain_rule < chain_rule_end || offset_chain_rule >= length) {
+      return OTS_FAILURE_MSG("Bad chain rule offset %d for chain rule %d in chain rule set", offset_chain_rule, i);
+    }
+    if (!ParseChainRuleSubtable(file, data + offset_chain_rule,
+                                length - offset_chain_rule,
+                                num_glyphs, num_lookups)) {
+      return OTS_FAILURE_MSG("Failed to parse chain rule %d in chain rule set", i);
+    }
+  }
+
+  return true;
+}
+
+bool ParseChainContextFormat1(const ots::OpenTypeFile *file,
+                              const uint8_t *data, const size_t length,
+                              const uint16_t num_glyphs,
+                              const uint16_t num_lookups) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t offset_coverage = 0;
+  uint16_t chain_rule_set_count = 0;
+  // Skip format field.
+  if (!subtable.Skip(2) ||
+      !subtable.ReadU16(&offset_coverage) ||
+      !subtable.ReadU16(&chain_rule_set_count)) {
+    return OTS_FAILURE_MSG("Failed to read header of chain context format 1");
+  }
+
+  const unsigned chain_rule_set_end =
+      2 * static_cast<unsigned>(chain_rule_set_count) + 6;
+  if (chain_rule_set_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE_MSG("Bad chain rule end %d in chain context format 1", chain_rule_set_end);
+  }
+  if (offset_coverage < chain_rule_set_end || offset_coverage >= length) {
+    return OTS_FAILURE_MSG("Bad coverage offset %d in chain context format 1", chain_rule_set_end);
+  }
+  if (!ots::ParseCoverageTable(file, data + offset_coverage,
+                               length - offset_coverage, num_glyphs)) {
+    return OTS_FAILURE_MSG("Failed to parse coverage table for chain context format 1");
+  }
+
+  for (unsigned i = 0; i < chain_rule_set_count; ++i) {
+    uint16_t offset_chain_rule_set = 0;
+    if (!subtable.ReadU16(&offset_chain_rule_set)) {
+      return OTS_FAILURE_MSG("Failed to read chain rule offset %d in chain context format 1", i);
+    }
+    if (offset_chain_rule_set < chain_rule_set_end ||
+        offset_chain_rule_set >= length) {
+      return OTS_FAILURE_MSG("Bad chain rule set offset %d for chain rule set %d in chain context format 1", offset_chain_rule_set, i);
+    }
+    if (!ParseChainRuleSetTable(file, data + offset_chain_rule_set,
+                                   length - offset_chain_rule_set,
+                                   num_glyphs, num_lookups)) {
+      return OTS_FAILURE_MSG("Failed to parse chain rule set %d in chain context format 1", i);
+    }
+  }
+
+  return true;
+}
+
+bool ParseChainClassRuleSubtable(const ots::OpenTypeFile *file,
+                                 const uint8_t *data, const size_t length,
+                                 const uint16_t num_glyphs,
+                                 const uint16_t num_lookups) {
+  ots::Buffer subtable(data, length);
+
+  // In this subtable, we don't check the value of classes for now since
+  // these could take arbitrary values.
+
+  uint16_t backtrack_count = 0;
+  if (!subtable.ReadU16(&backtrack_count)) {
+    return OTS_FAILURE_MSG("Failed to read backtrack count in chain class rule subtable");
+  }
+  if (backtrack_count >= num_glyphs) {
+    return OTS_FAILURE_MSG("Bad backtrack count %d in chain class rule subtable", backtrack_count);
+  }
+  if (!subtable.Skip(2 * backtrack_count)) {
+    return OTS_FAILURE_MSG("Failed to skip backtrack offsets in chain class rule subtable");
+  }
+
+  uint16_t input_count = 0;
+  if (!subtable.ReadU16(&input_count)) {
+    return OTS_FAILURE_MSG("Failed to read input count in chain class rule subtable");
+  }
+  if (input_count == 0 || input_count >= num_glyphs) {
+    return OTS_FAILURE_MSG("Bad input count %d in chain class rule subtable", input_count);
+  }
+  if (!subtable.Skip(2 * (input_count - 1))) {
+    return OTS_FAILURE_MSG("Failed to skip input offsets in chain class rule subtable");
+  }
+
+  uint16_t lookahead_count = 0;
+  if (!subtable.ReadU16(&lookahead_count)) {
+    return OTS_FAILURE_MSG("Failed to read lookahead count in chain class rule subtable");
+  }
+  if (lookahead_count >= num_glyphs) {
+    return OTS_FAILURE_MSG("Bad lookahead count %d in chain class rule subtable", lookahead_count);
+  }
+  if (!subtable.Skip(2 * lookahead_count)) {
+    return OTS_FAILURE_MSG("Failed to skip lookahead offsets in chain class rule subtable");
+  }
+
+  uint16_t lookup_count = 0;
+  if (!subtable.ReadU16(&lookup_count)) {
+    return OTS_FAILURE_MSG("Failed to read lookup count in chain class rule subtable");
+  }
+  for (unsigned i = 0; i < lookup_count; ++i) {
+    if (!ParseLookupRecord(file, &subtable, num_glyphs, num_lookups)) {
+      return OTS_FAILURE_MSG("Failed to parse lookup record %d in chain class rule subtable", i);
+    }
+  }
+
+  return true;
+}
+
+bool ParseChainClassSetTable(const ots::OpenTypeFile *file,
+                             const uint8_t *data, const size_t length,
+                             const uint16_t num_glyphs,
+                             const uint16_t num_lookups) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t chain_class_rule_count = 0;
+  if (!subtable.ReadU16(&chain_class_rule_count)) {
+    return OTS_FAILURE_MSG("Failed to read rule count in chain class set");
+  }
+  const unsigned chain_class_rule_end =
+      2 * static_cast<unsigned>(chain_class_rule_count) + 2;
+  if (chain_class_rule_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE_MSG("Bad end of chain class set %d in chain class set", chain_class_rule_end);
+  }
+  for (unsigned i = 0; i < chain_class_rule_count; ++i) {
+    uint16_t offset_chain_class_rule = 0;
+    if (!subtable.ReadU16(&offset_chain_class_rule)) {
+      return OTS_FAILURE_MSG("Failed to read chain class rule offset %d in chain class set", i);
+    }
+    if (offset_chain_class_rule < chain_class_rule_end ||
+        offset_chain_class_rule >= length) {
+      return OTS_FAILURE_MSG("Bad chain class rule offset %d for chain class %d in chain class set", offset_chain_class_rule, i);
+    }
+    if (!ParseChainClassRuleSubtable(file, data + offset_chain_class_rule,
+                                     length - offset_chain_class_rule,
+                                     num_glyphs, num_lookups)) {
+      return OTS_FAILURE_MSG("Failed to parse chain class rule %d in chain class set", i);
+    }
+  }
+
+  return true;
+}
+
+bool ParseChainContextFormat2(const ots::OpenTypeFile *file,
+                              const uint8_t *data, const size_t length,
+                              const uint16_t num_glyphs,
+                              const uint16_t num_lookups) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t offset_coverage = 0;
+  uint16_t offset_backtrack_class_def = 0;
+  uint16_t offset_input_class_def = 0;
+  uint16_t offset_lookahead_class_def = 0;
+  uint16_t chain_class_set_count = 0;
+  // Skip format field.
+  if (!subtable.Skip(2) ||
+      !subtable.ReadU16(&offset_coverage) ||
+      !subtable.ReadU16(&offset_backtrack_class_def) ||
+      !subtable.ReadU16(&offset_input_class_def) ||
+      !subtable.ReadU16(&offset_lookahead_class_def) ||
+      !subtable.ReadU16(&chain_class_set_count)) {
+    return OTS_FAILURE_MSG("Failed to read header of chain context format 2");
+  }
+
+  const unsigned chain_class_set_end =
+      2 * static_cast<unsigned>(chain_class_set_count) + 12;
+  if (chain_class_set_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE_MSG("Bad chain class set end %d in chain context format 2", chain_class_set_end);
+  }
+  if (offset_coverage < chain_class_set_end || offset_coverage >= length) {
+    return OTS_FAILURE_MSG("Bad coverage offset %d in chain context format 2", offset_coverage);
+  }
+  if (!ots::ParseCoverageTable(file, data + offset_coverage,
+                               length - offset_coverage, num_glyphs)) {
+    return OTS_FAILURE_MSG("Failed to parse coverage table in chain context format 2");
+  }
+
+  // Classes for backtrack/lookahead sequences might not be defined.
+  if (offset_backtrack_class_def) {
+    if (offset_backtrack_class_def < chain_class_set_end ||
+        offset_backtrack_class_def >= length) {
+      return OTS_FAILURE_MSG("Bad backtrack class offset %d in chain context format 2", offset_backtrack_class_def);
+    }
+    if (!ots::ParseClassDefTable(file, data + offset_backtrack_class_def,
+                                 length - offset_backtrack_class_def,
+                                 num_glyphs, kMaxClassDefValue)) {
+      return OTS_FAILURE_MSG("Failed to parse backtrack class defn table in chain context format 2");
+    }
+  }
+
+  if (offset_input_class_def < chain_class_set_end ||
+      offset_input_class_def >= length) {
+    return OTS_FAILURE_MSG("Bad input class defn offset %d in chain context format 2", offset_input_class_def);
+  }
+  if (!ots::ParseClassDefTable(file, data + offset_input_class_def,
+                               length - offset_input_class_def,
+                               num_glyphs, kMaxClassDefValue)) {
+    return OTS_FAILURE_MSG("Failed to parse input class defn in chain context format 2");
+  }
+
+  if (offset_lookahead_class_def) {
+    if (offset_lookahead_class_def < chain_class_set_end ||
+        offset_lookahead_class_def >= length) {
+      return OTS_FAILURE_MSG("Bad lookahead class defn offset %d in chain context format 2", offset_lookahead_class_def);
+    }
+    if (!ots::ParseClassDefTable(file, data + offset_lookahead_class_def,
+                                 length - offset_lookahead_class_def,
+                                 num_glyphs, kMaxClassDefValue)) {
+      return OTS_FAILURE_MSG("Failed to parse lookahead class defn in chain context format 2");
+    }
+  }
+
+  for (unsigned i = 0; i < chain_class_set_count; ++i) {
+    uint16_t offset_chain_class_set = 0;
+    if (!subtable.ReadU16(&offset_chain_class_set)) {
+      return OTS_FAILURE_MSG("Failed to read chain class set offset %d", i);
+    }
+    // |offset_chain_class_set| could be NULL.
+    if (offset_chain_class_set) {
+      if (offset_chain_class_set < chain_class_set_end ||
+          offset_chain_class_set >= length) {
+        return OTS_FAILURE_MSG("Bad chain set class offset %d for chain set %d in chain context format 2", offset_chain_class_set, i);
+      }
+      if (!ParseChainClassSetTable(file, data + offset_chain_class_set,
+                                   length - offset_chain_class_set,
+                                   num_glyphs, num_lookups)) {
+        return OTS_FAILURE_MSG("Failed to parse chain class set table %d in chain context format 2", i);
+      }
+    }
+  }
+
+  return true;
+}
+
+bool ParseChainContextFormat3(const ots::OpenTypeFile *file,
+                              const uint8_t *data, const size_t length,
+                              const uint16_t num_glyphs,
+                              const uint16_t num_lookups) {
+  ots::Buffer subtable(data, length);
+
+  uint16_t backtrack_count = 0;
+  // Skip format field.
+  if (!subtable.Skip(2) ||
+      !subtable.ReadU16(&backtrack_count)) {
+    return OTS_FAILURE_MSG("Failed to read backtrack count in chain context format 3");
+  }
+
+  if (backtrack_count >= num_glyphs) {
+    return OTS_FAILURE_MSG("Bad backtrack count %d in chain context format 3", backtrack_count);
+  }
+  std::vector<uint16_t> offsets_backtrack;
+  offsets_backtrack.reserve(backtrack_count);
+  for (unsigned i = 0; i < backtrack_count; ++i) {
+    uint16_t offset = 0;
+    if (!subtable.ReadU16(&offset)) {
+      return OTS_FAILURE_MSG("Failed to read backtrack offset %d in chain context format 3", i);
+    }
+    offsets_backtrack.push_back(offset);
+  }
+  if (offsets_backtrack.size() != backtrack_count) {
+    return OTS_FAILURE_MSG("Bad backtrack offsets size %ld in chain context format 3", offsets_backtrack.size());
+  }
+
+  uint16_t input_count = 0;
+  if (!subtable.ReadU16(&input_count)) {
+    return OTS_FAILURE_MSG("Failed to read input count in chain context format 3");
+  }
+  if (input_count >= num_glyphs) {
+    return OTS_FAILURE_MSG("Bad input count %d in chain context format 3", input_count);
+  }
+  std::vector<uint16_t> offsets_input;
+  offsets_input.reserve(input_count);
+  for (unsigned i = 0; i < input_count; ++i) {
+    uint16_t offset = 0;
+    if (!subtable.ReadU16(&offset)) {
+      return OTS_FAILURE_MSG("Failed to read input offset %d in chain context format 3", i);
+    }
+    offsets_input.push_back(offset);
+  }
+  if (offsets_input.size() != input_count) {
+    return OTS_FAILURE_MSG("Bad input offsets size %ld in chain context format 3", offsets_input.size());
+  }
+
+  uint16_t lookahead_count = 0;
+  if (!subtable.ReadU16(&lookahead_count)) {
+    return OTS_FAILURE_MSG("Failed ot read lookahead count in chain context format 3");
+  }
+  if (lookahead_count >= num_glyphs) {
+    return OTS_FAILURE_MSG("Bad lookahead count %d in chain context format 3", lookahead_count);
+  }
+  std::vector<uint16_t> offsets_lookahead;
+  offsets_lookahead.reserve(lookahead_count);
+  for (unsigned i = 0; i < lookahead_count; ++i) {
+    uint16_t offset = 0;
+    if (!subtable.ReadU16(&offset)) {
+      return OTS_FAILURE_MSG("Failed to read lookahead offset %d in chain context format 3", i);
+    }
+    offsets_lookahead.push_back(offset);
+  }
+  if (offsets_lookahead.size() != lookahead_count) {
+    return OTS_FAILURE_MSG("Bad lookahead offsets size %ld in chain context format 3", offsets_lookahead.size());
+  }
+
+  uint16_t lookup_count = 0;
+  if (!subtable.ReadU16(&lookup_count)) {
+    return OTS_FAILURE_MSG("Failed to read lookup count in chain context format 3");
+  }
+  for (unsigned i = 0; i < lookup_count; ++i) {
+    if (!ParseLookupRecord(file, &subtable, num_glyphs, num_lookups)) {
+      return OTS_FAILURE_MSG("Failed to parse lookup %d in chain context format 3", i);
+    }
+  }
+
+  const unsigned lookup_record_end =
+      2 * (static_cast<unsigned>(backtrack_count) +
+           static_cast<unsigned>(input_count) +
+           static_cast<unsigned>(lookahead_count)) +
+      4 * static_cast<unsigned>(lookup_count) + 10;
+  if (lookup_record_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE_MSG("Bad end of lookup record %d in chain context format 3", lookup_record_end);
+  }
+  for (unsigned i = 0; i < backtrack_count; ++i) {
+    if (offsets_backtrack[i] < lookup_record_end ||
+        offsets_backtrack[i] >= length) {
+      return OTS_FAILURE_MSG("Bad backtrack offset of %d for backtrack %d in chain context format 3", offsets_backtrack[i], i);
+    }
+    if (!ots::ParseCoverageTable(file, data + offsets_backtrack[i],
+                                 length - offsets_backtrack[i], num_glyphs)) {
+      return OTS_FAILURE_MSG("Failed to parse backtrack coverage %d in chain context format 3", i);
+    }
+  }
+  for (unsigned i = 0; i < input_count; ++i) {
+    if (offsets_input[i] < lookup_record_end || offsets_input[i] >= length) {
+      return OTS_FAILURE_MSG("Bad input offset %d for input %d in chain context format 3", offsets_input[i], i);
+    }
+    if (!ots::ParseCoverageTable(file, data + offsets_input[i],
+                                 length - offsets_input[i], num_glyphs)) {
+      return OTS_FAILURE_MSG("Failed to parse input coverage table %d in chain context format 3", i);
+    }
+  }
+  for (unsigned i = 0; i < lookahead_count; ++i) {
+    if (offsets_lookahead[i] < lookup_record_end ||
+        offsets_lookahead[i] >= length) {
+      return OTS_FAILURE_MSG("Bad lookadhead offset %d for lookahead %d in chain context format 3", offsets_lookahead[i], i);
+    }
+    if (!ots::ParseCoverageTable(file, data + offsets_lookahead[i],
+                                 length - offsets_lookahead[i], num_glyphs)) {
+      return OTS_FAILURE_MSG("Failed to parse lookahead coverage table %d in chain context format 3", i);
+    }
+  }
+
+  return true;
+}
+
+}  // namespace
+
+namespace ots {
+
+bool LookupSubtableParser::Parse(const OpenTypeFile *file, const uint8_t *data,
+                                 const size_t length,
+                                 const uint16_t lookup_type) const {
+  for (unsigned i = 0; i < num_types; ++i) {
+    if (parsers[i].type == lookup_type && parsers[i].parse) {
+      if (!parsers[i].parse(file, data, length)) {
+        return OTS_FAILURE_MSG("Failed to parse lookup subtable %d", i);
+      }
+      return true;
+    }
+  }
+  return OTS_FAILURE_MSG("No lookup subtables to parse");
+}
+
+// Parsing ScriptListTable requires number of features so we need to
+// parse FeatureListTable before calling this function.
+bool ParseScriptListTable(const ots::OpenTypeFile *file,
+                          const uint8_t *data, const size_t length,
+                          const uint16_t num_features) {
+  Buffer subtable(data, length);
+
+  uint16_t script_count = 0;
+  if (!subtable.ReadU16(&script_count)) {
+    return OTS_FAILURE_MSG("Failed to read script count in script list table");
+  }
+
+  const unsigned script_record_end =
+      6 * static_cast<unsigned>(script_count) + 2;
+  if (script_record_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE_MSG("Bad end of script record %d in script list table", script_record_end);
+  }
+  std::vector<ScriptRecord> script_list;
+  script_list.reserve(script_count);
+  uint32_t last_tag = 0;
+  for (unsigned i = 0; i < script_count; ++i) {
+    ScriptRecord record;
+    if (!subtable.ReadU32(&record.tag) ||
+        !subtable.ReadU16(&record.offset)) {
+      return OTS_FAILURE_MSG("Failed to read script record %d in script list table", i);
+    }
+    // Script tags should be arranged alphabetically by tag
+    if (last_tag != 0 && last_tag > record.tag) {
+      // Several fonts don't arrange tags alphabetically.
+      // It seems that the order of tags might not be a security issue
+      // so we just warn it.
+      OTS_WARNING("tags aren't arranged alphabetically.");
+    }
+    last_tag = record.tag;
+    if (record.offset < script_record_end || record.offset >= length) {
+      return OTS_FAILURE_MSG("Bad record offset %d for script %4.4s entry %d in script list table", record.offset, (char *)&record.tag, i);
+    }
+    script_list.push_back(record);
+  }
+  if (script_list.size() != script_count) {
+    return OTS_FAILURE_MSG("Bad script list size %ld in script list table", script_list.size());
+  }
+
+  // Check script records.
+  for (unsigned i = 0; i < script_count; ++i) {
+    if (!ParseScriptTable(file, data + script_list[i].offset,
+                          length - script_list[i].offset,
+                          script_list[i].tag, num_features)) {
+      return OTS_FAILURE_MSG("Failed to parse script table %d", i);
+    }
+  }
+
+  return true;
+}
+
+// Parsing FeatureListTable requires number of lookups so we need to parse
+// LookupListTable before calling this function.
+bool ParseFeatureListTable(const ots::OpenTypeFile *file,
+                           const uint8_t *data, const size_t length,
+                           const uint16_t num_lookups,
+                           uint16_t* num_features) {
+  Buffer subtable(data, length);
+
+  uint16_t feature_count = 0;
+  if (!subtable.ReadU16(&feature_count)) {
+    return OTS_FAILURE_MSG("Failed to read feature count");
+  }
+
+  std::vector<FeatureRecord> feature_records;
+  feature_records.resize(feature_count);
+  const unsigned feature_record_end =
+      6 * static_cast<unsigned>(feature_count) + 2;
+  if (feature_record_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE_MSG("Bad end of feature record %d", feature_record_end);
+  }
+  uint32_t last_tag = 0;
+  for (unsigned i = 0; i < feature_count; ++i) {
+    if (!subtable.ReadU32(&feature_records[i].tag) ||
+        !subtable.ReadU16(&feature_records[i].offset)) {
+      return OTS_FAILURE_MSG("Failed to read feature header %d", i);
+    }
+    // Feature record array should be arranged alphabetically by tag
+    if (last_tag != 0 && last_tag > feature_records[i].tag) {
+      // Several fonts don't arrange tags alphabetically.
+      // It seems that the order of tags might not be a security issue
+      // so we just warn it.
+      OTS_WARNING("tags aren't arranged alphabetically.");
+    }
+    last_tag = feature_records[i].tag;
+    if (feature_records[i].offset < feature_record_end ||
+        feature_records[i].offset >= length) {
+      return OTS_FAILURE_MSG("Bad feature offset %d for feature %d %4.4s", feature_records[i].offset, i, (char *)&feature_records[i].tag);
+    }
+  }
+
+  for (unsigned i = 0; i < feature_count; ++i) {
+    if (!ParseFeatureTable(file, data + feature_records[i].offset,
+                           length - feature_records[i].offset, num_lookups)) {
+      return OTS_FAILURE_MSG("Failed to parse feature table %d", i);
+    }
+  }
+  *num_features = feature_count;
+  return true;
+}
+
+// For parsing GPOS/GSUB tables, this function should be called at first to
+// obtain the number of lookups because parsing FeatureTableList requires
+// the number.
+bool ParseLookupListTable(OpenTypeFile *file, const uint8_t *data,
+                          const size_t length,
+                          const LookupSubtableParser* parser,
+                          uint16_t *num_lookups) {
+  Buffer subtable(data, length);
+
+  if (!subtable.ReadU16(num_lookups)) {
+    return OTS_FAILURE_MSG("Failed to read number of lookups");
+  }
+
+  std::vector<uint16_t> lookups;
+  lookups.reserve(*num_lookups);
+  const unsigned lookup_end =
+      2 * static_cast<unsigned>(*num_lookups) + 2;
+  if (lookup_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE_MSG("Bad end of lookups %d", lookup_end);
+  }
+  for (unsigned i = 0; i < *num_lookups; ++i) {
+    uint16_t offset = 0;
+    if (!subtable.ReadU16(&offset)) {
+      return OTS_FAILURE_MSG("Failed to read lookup offset %d", i);
+    }
+    if (offset < lookup_end || offset >= length) {
+      return OTS_FAILURE_MSG("Bad lookup offset %d for lookup %d", offset, i);
+    }
+    lookups.push_back(offset);
+  }
+  if (lookups.size() != *num_lookups) {
+    return OTS_FAILURE_MSG("Bad lookup offsets list size %ld", lookups.size());
+  }
+
+  for (unsigned i = 0; i < *num_lookups; ++i) {
+    if (!ParseLookupTable(file, data + lookups[i], length - lookups[i],
+                          parser)) {
+      return OTS_FAILURE_MSG("Failed to parse lookup %d", i);
+    }
+  }
+
+  return true;
+}
+
+bool ParseClassDefTable(const ots::OpenTypeFile *file,
+                        const uint8_t *data, size_t length,
+                        const uint16_t num_glyphs,
+                        const uint16_t num_classes) {
+  Buffer subtable(data, length);
+
+  uint16_t format = 0;
+  if (!subtable.ReadU16(&format)) {
+    return OTS_FAILURE_MSG("Failed to read class defn format");
+  }
+  if (format == 1) {
+    return ParseClassDefFormat1(file, data, length, num_glyphs, num_classes);
+  } else if (format == 2) {
+    return ParseClassDefFormat2(file, data, length, num_glyphs, num_classes);
+  }
+
+  return OTS_FAILURE_MSG("Bad class defn format %d", format);
+}
+
+bool ParseCoverageTable(const ots::OpenTypeFile *file,
+                        const uint8_t *data, size_t length,
+                        const uint16_t num_glyphs,
+                        const uint16_t expected_num_glyphs) {
+  Buffer subtable(data, length);
+
+  uint16_t format = 0;
+  if (!subtable.ReadU16(&format)) {
+    return OTS_FAILURE_MSG("Failed to read coverage table format");
+  }
+  if (format == 1) {
+    return ParseCoverageFormat1(file, data, length, num_glyphs, expected_num_glyphs);
+  } else if (format == 2) {
+    return ParseCoverageFormat2(file, data, length, num_glyphs, expected_num_glyphs);
+  }
+
+  return OTS_FAILURE_MSG("Bad coverage table format %d", format);
+}
+
+bool ParseDeviceTable(const ots::OpenTypeFile *file,
+                      const uint8_t *data, size_t length) {
+  Buffer subtable(data, length);
+
+  uint16_t start_size = 0;
+  uint16_t end_size = 0;
+  uint16_t delta_format = 0;
+  if (!subtable.ReadU16(&start_size) ||
+      !subtable.ReadU16(&end_size) ||
+      !subtable.ReadU16(&delta_format)) {
+    return OTS_FAILURE_MSG("Failed to read device table header");
+  }
+  if (start_size > end_size) {
+    return OTS_FAILURE_MSG("bad size range: %u > %u", start_size, end_size);
+  }
+  if (delta_format == 0 || delta_format > kMaxDeltaFormatType) {
+    return OTS_FAILURE_MSG("bad delta format: %u", delta_format);
+  }
+  // The number of delta values per uint16. The device table should contain
+  // at least |num_units| * 2 bytes compressed data.
+  const unsigned num_units = (end_size - start_size) /
+      (1 << (4 - delta_format)) + 1;
+  // Just skip |num_units| * 2 bytes since the compressed data could take
+  // arbitrary values.
+  if (!subtable.Skip(num_units * 2)) {
+    return OTS_FAILURE_MSG("Failed to skip data in device table");
+  }
+  return true;
+}
+
+bool ParseContextSubtable(const ots::OpenTypeFile *file,
+                          const uint8_t *data, const size_t length,
+                          const uint16_t num_glyphs,
+                          const uint16_t num_lookups) {
+  Buffer subtable(data, length);
+
+  uint16_t format = 0;
+  if (!subtable.ReadU16(&format)) {
+    return OTS_FAILURE_MSG("Failed to read context subtable format");
+  }
+
+  if (format == 1) {
+    if (!ParseContextFormat1(file, data, length, num_glyphs, num_lookups)) {
+      return OTS_FAILURE_MSG("Failed to parse context format 1 subtable");
+    }
+  } else if (format == 2) {
+    if (!ParseContextFormat2(file, data, length, num_glyphs, num_lookups)) {
+      return OTS_FAILURE_MSG("Failed to parse context format 2 subtable");
+    }
+  } else if (format == 3) {
+    if (!ParseContextFormat3(file, data, length, num_glyphs, num_lookups)) {
+      return OTS_FAILURE_MSG("Failed to parse context format 3 subtable");
+    }
+  } else {
+    return OTS_FAILURE_MSG("Bad context subtable format %d", format);
+  }
+
+  return true;
+}
+
+bool ParseChainingContextSubtable(const ots::OpenTypeFile *file,
+                                  const uint8_t *data, const size_t length,
+                                  const uint16_t num_glyphs,
+                                  const uint16_t num_lookups) {
+  Buffer subtable(data, length);
+
+  uint16_t format = 0;
+  if (!subtable.ReadU16(&format)) {
+    return OTS_FAILURE_MSG("Failed to read chaining context subtable format");
+  }
+
+  if (format == 1) {
+    if (!ParseChainContextFormat1(file, data, length, num_glyphs, num_lookups)) {
+      return OTS_FAILURE_MSG("Failed to parse chaining context format 1 subtable");
+    }
+  } else if (format == 2) {
+    if (!ParseChainContextFormat2(file, data, length, num_glyphs, num_lookups)) {
+      return OTS_FAILURE_MSG("Failed to parse chaining context format 2 subtable");
+    }
+  } else if (format == 3) {
+    if (!ParseChainContextFormat3(file, data, length, num_glyphs, num_lookups)) {
+      return OTS_FAILURE_MSG("Failed to parse chaining context format 3 subtable");
+    }
+  } else {
+    return OTS_FAILURE_MSG("Bad chaining context subtable format %d", format);
+  }
+
+  return true;
+}
+
+bool ParseExtensionSubtable(const OpenTypeFile *file,
+                            const uint8_t *data, const size_t length,
+                            const LookupSubtableParser* parser) {
+  Buffer subtable(data, length);
+
+  uint16_t format = 0;
+  uint16_t lookup_type = 0;
+  uint32_t offset_extension = 0;
+  if (!subtable.ReadU16(&format) ||
+      !subtable.ReadU16(&lookup_type) ||
+      !subtable.ReadU32(&offset_extension)) {
+    return OTS_FAILURE_MSG("Failed to read extension table header");
+  }
+
+  if (format != 1) {
+    return OTS_FAILURE_MSG("Bad extension table format %d", format);
+  }
+  // |lookup_type| should be other than |parser->extension_type|.
+  if (lookup_type < 1 || lookup_type > parser->num_types ||
+      lookup_type == parser->extension_type) {
+    return OTS_FAILURE_MSG("Bad lookup type %d in extension table", lookup_type);
+  }
+
+  const unsigned format_end = static_cast<unsigned>(8);
+  if (offset_extension < format_end ||
+      offset_extension >= length) {
+    return OTS_FAILURE_MSG("Bad extension offset %d", offset_extension);
+  }
+
+  // Parse the extension subtable of |lookup_type|.
+  if (!parser->Parse(file, data + offset_extension, length - offset_extension,
+                     lookup_type)) {
+    return OTS_FAILURE_MSG("Failed to parse lookup from extension lookup");
+  }
+
+  return true;
+}
+
+}  // namespace ots
+
+#undef TABLE_NAME
diff --git a/third_party/ots/src/layout.h b/third_party/ots/src/layout.h
new file mode 100644
index 0000000..3b94589
--- /dev/null
+++ b/third_party/ots/src/layout.h
@@ -0,0 +1,76 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_LAYOUT_H_
+#define OTS_LAYOUT_H_
+
+#include "ots.h"
+
+// Utility functions for OpenType layout common table formats.
+// http://www.microsoft.com/typography/otspec/chapter2.htm
+
+namespace ots {
+
+
+struct LookupSubtableParser {
+  struct TypeParser {
+    uint16_t type;
+    bool (*parse)(const OpenTypeFile *file, const uint8_t *data,
+                  const size_t length);
+  };
+  size_t num_types;
+  uint16_t extension_type;
+  const TypeParser *parsers;
+
+  bool Parse(const OpenTypeFile *file, const uint8_t *data,
+             const size_t length, const uint16_t lookup_type) const;
+};
+
+bool ParseScriptListTable(const ots::OpenTypeFile *file,
+                          const uint8_t *data, const size_t length,
+                          const uint16_t num_features);
+
+bool ParseFeatureListTable(const ots::OpenTypeFile *file,
+                           const uint8_t *data, const size_t length,
+                           const uint16_t num_lookups,
+                           uint16_t *num_features);
+
+bool ParseLookupListTable(OpenTypeFile *file, const uint8_t *data,
+                          const size_t length,
+                          const LookupSubtableParser* parser,
+                          uint16_t* num_lookups);
+
+bool ParseClassDefTable(const ots::OpenTypeFile *file,
+                        const uint8_t *data, size_t length,
+                        const uint16_t num_glyphs,
+                        const uint16_t num_classes);
+
+bool ParseCoverageTable(const ots::OpenTypeFile *file,
+                        const uint8_t *data, size_t length,
+                        const uint16_t num_glyphs,
+                        const uint16_t expected_num_glyphs = 0);
+
+bool ParseDeviceTable(const ots::OpenTypeFile *file,
+                      const uint8_t *data, size_t length);
+
+// Parser for 'Contextual' subtable shared by GSUB/GPOS tables.
+bool ParseContextSubtable(const ots::OpenTypeFile *file,
+                          const uint8_t *data, const size_t length,
+                          const uint16_t num_glyphs,
+                          const uint16_t num_lookups);
+
+// Parser for 'Chaining Contextual' subtable shared by GSUB/GPOS tables.
+bool ParseChainingContextSubtable(const ots::OpenTypeFile *file,
+                                  const uint8_t *data, const size_t length,
+                                  const uint16_t num_glyphs,
+                                  const uint16_t num_lookups);
+
+bool ParseExtensionSubtable(const OpenTypeFile *file,
+                            const uint8_t *data, const size_t length,
+                            const LookupSubtableParser* parser);
+
+}  // namespace ots
+
+#endif  // OTS_LAYOUT_H_
+
diff --git a/third_party/ots/src/loca.cc b/third_party/ots/src/loca.cc
new file mode 100644
index 0000000..4b291f0
--- /dev/null
+++ b/third_party/ots/src/loca.cc
@@ -0,0 +1,104 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "loca.h"
+
+#include "head.h"
+#include "maxp.h"
+
+// loca - Index to Location
+// http://www.microsoft.com/typography/otspec/loca.htm
+
+#define TABLE_NAME "loca"
+
+namespace ots {
+
+bool ots_loca_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
+  Buffer table(data, length);
+
+  // We can't do anything useful in validating this data except to ensure that
+  // the values are monotonically increasing.
+
+  OpenTypeLOCA *loca = new OpenTypeLOCA;
+  file->loca = loca;
+
+  if (!file->maxp || !file->head) {
+    return OTS_FAILURE_MSG("maxp or head tables missing from font, needed by loca");
+  }
+
+  const unsigned num_glyphs = file->maxp->num_glyphs;
+  unsigned last_offset = 0;
+  loca->offsets.resize(num_glyphs + 1);
+  // maxp->num_glyphs is uint16_t, thus the addition never overflows.
+
+  if (file->head->index_to_loc_format == 0) {
+    // Note that the <= here (and below) is correct. There is one more offset
+    // than the number of glyphs in order to give the length of the final
+    // glyph.
+    for (unsigned i = 0; i <= num_glyphs; ++i) {
+      uint16_t offset = 0;
+      if (!table.ReadU16(&offset)) {
+        return OTS_FAILURE_MSG("Failed to read offset for glyph %d", i);
+      }
+      if (offset < last_offset) {
+        return OTS_FAILURE_MSG("Out of order offset %d < %d for glyph %d", offset, last_offset, i);
+      }
+      last_offset = offset;
+      loca->offsets[i] = offset * 2;
+    }
+  } else {
+    for (unsigned i = 0; i <= num_glyphs; ++i) {
+      uint32_t offset = 0;
+      if (!table.ReadU32(&offset)) {
+        return OTS_FAILURE_MSG("Failed to read offset for glyph %d", i);
+      }
+      if (offset < last_offset) {
+        return OTS_FAILURE_MSG("Out of order offset %d < %d for glyph %d", offset, last_offset, i);
+      }
+      last_offset = offset;
+      loca->offsets[i] = offset;
+    }
+  }
+
+  return true;
+}
+
+bool ots_loca_should_serialise(OpenTypeFile *file) {
+  return file->loca != NULL;
+}
+
+bool ots_loca_serialise(OTSStream *out, OpenTypeFile *file) {
+  const OpenTypeLOCA *loca = file->loca;
+  const OpenTypeHEAD *head = file->head;
+
+  if (!head) {
+    return OTS_FAILURE_MSG("Missing head table in font needed by loca");
+  }
+
+  if (head->index_to_loc_format == 0) {
+    for (unsigned i = 0; i < loca->offsets.size(); ++i) {
+      const uint16_t offset = static_cast<uint16_t>(loca->offsets[i] >> 1);
+      if ((offset != (loca->offsets[i] >> 1)) ||
+          !out->WriteU16(offset)) {
+        return OTS_FAILURE_MSG("Failed to write glyph offset for glyph %d", i);
+      }
+    }
+  } else {
+    for (unsigned i = 0; i < loca->offsets.size(); ++i) {
+      if (!out->WriteU32(loca->offsets[i])) {
+        return OTS_FAILURE_MSG("Failed to write glyph offset for glyph %d", i);
+      }
+    }
+  }
+
+  return true;
+}
+
+void ots_loca_free(OpenTypeFile *file) {
+  delete file->loca;
+}
+
+}  // namespace ots
+
+#undef TABLE_NAME
diff --git a/third_party/ots/src/loca.h b/third_party/ots/src/loca.h
new file mode 100644
index 0000000..255ef06e
--- /dev/null
+++ b/third_party/ots/src/loca.h
@@ -0,0 +1,20 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_LOCA_H_
+#define OTS_LOCA_H_
+
+#include <vector>
+
+#include "ots.h"
+
+namespace ots {
+
+struct OpenTypeLOCA {
+  std::vector<uint32_t> offsets;
+};
+
+}  // namespace ots
+
+#endif  // OTS_LOCA_H_
diff --git a/third_party/ots/src/ltsh.cc b/third_party/ots/src/ltsh.cc
new file mode 100644
index 0000000..418c1598
--- /dev/null
+++ b/third_party/ots/src/ltsh.cc
@@ -0,0 +1,92 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ltsh.h"
+
+#include "maxp.h"
+
+// LTSH - Linear Threshold
+// http://www.microsoft.com/typography/otspec/ltsh.htm
+
+#define TABLE_NAME "LTSH"
+
+#define DROP_THIS_TABLE(...) \
+  do { \
+    OTS_FAILURE_MSG_(file, TABLE_NAME ": " __VA_ARGS__); \
+    OTS_FAILURE_MSG("Table discarded"); \
+    delete file->ltsh; \
+    file->ltsh = 0; \
+  } while (0)
+
+namespace ots {
+
+bool ots_ltsh_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
+  Buffer table(data, length);
+
+  if (!file->maxp) {
+    return OTS_FAILURE_MSG("Missing maxp table from font needed by ltsh");
+  }
+
+  OpenTypeLTSH *ltsh = new OpenTypeLTSH;
+  file->ltsh = ltsh;
+
+  uint16_t num_glyphs = 0;
+  if (!table.ReadU16(&ltsh->version) ||
+      !table.ReadU16(&num_glyphs)) {
+    return OTS_FAILURE_MSG("Failed to read ltsh header");
+  }
+
+  if (ltsh->version != 0) {
+    DROP_THIS_TABLE("bad version: %u", ltsh->version);
+    return true;
+  }
+
+  if (num_glyphs != file->maxp->num_glyphs) {
+    DROP_THIS_TABLE("bad num_glyphs: %u", num_glyphs);
+    return true;
+  }
+
+  ltsh->ypels.reserve(num_glyphs);
+  for (unsigned i = 0; i < num_glyphs; ++i) {
+    uint8_t pel = 0;
+    if (!table.ReadU8(&pel)) {
+      return OTS_FAILURE_MSG("Failed to read pixels for glyph %d", i);
+    }
+    ltsh->ypels.push_back(pel);
+  }
+
+  return true;
+}
+
+bool ots_ltsh_should_serialise(OpenTypeFile *file) {
+  if (!file->glyf) return false;  // this table is not for CFF fonts.
+  return file->ltsh != NULL;
+}
+
+bool ots_ltsh_serialise(OTSStream *out, OpenTypeFile *file) {
+  const OpenTypeLTSH *ltsh = file->ltsh;
+
+  const uint16_t num_ypels = static_cast<uint16_t>(ltsh->ypels.size());
+  if (num_ypels != ltsh->ypels.size() ||
+      !out->WriteU16(ltsh->version) ||
+      !out->WriteU16(num_ypels)) {
+    return OTS_FAILURE_MSG("Failed to write pels size");
+  }
+  for (uint16_t i = 0; i < num_ypels; ++i) {
+    if (!out->Write(&(ltsh->ypels[i]), 1)) {
+      return OTS_FAILURE_MSG("Failed to write pixel size for glyph %d", i);
+    }
+  }
+
+  return true;
+}
+
+void ots_ltsh_free(OpenTypeFile *file) {
+  delete file->ltsh;
+}
+
+}  // namespace ots
+
+#undef TABLE_NAME
+#undef DROP_THIS_TABLE
diff --git a/third_party/ots/src/ltsh.h b/third_party/ots/src/ltsh.h
new file mode 100644
index 0000000..23d97d78
--- /dev/null
+++ b/third_party/ots/src/ltsh.h
@@ -0,0 +1,21 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_LTSH_H_
+#define OTS_LTSH_H_
+
+#include <vector>
+
+#include "ots.h"
+
+namespace ots {
+
+struct OpenTypeLTSH {
+  uint16_t version;
+  std::vector<uint8_t> ypels;
+};
+
+}  // namespace ots
+
+#endif  // OTS_LTSH_H_
diff --git a/third_party/ots/src/math.cc b/third_party/ots/src/math.cc
new file mode 100644
index 0000000..9124a88
--- /dev/null
+++ b/third_party/ots/src/math.cc
@@ -0,0 +1,609 @@
+// Copyright (c) 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// We use an underscore to avoid confusion with the standard math.h library.
+#include "math_.h"
+
+#include <limits>
+#include <vector>
+
+#include "layout.h"
+#include "maxp.h"
+
+// MATH - The MATH Table
+// The specification is not yet public but has been submitted to the MPEG group
+// in response to the 'Call for Proposals for ISO/IEC 14496-22 "Open Font
+// Format" Color Font Technology and MATH layout support'. Meanwhile, you can
+// contact Microsoft's engineer Murray Sargent to obtain a copy.
+
+#define TABLE_NAME "MATH"
+
+namespace {
+
+// The size of MATH header.
+// Version
+// MathConstants
+// MathGlyphInfo
+// MathVariants
+const unsigned kMathHeaderSize = 4 + 3 * 2;
+
+// The size of the MathGlyphInfo header.
+// MathItalicsCorrectionInfo
+// MathTopAccentAttachment
+// ExtendedShapeCoverage
+// MathKernInfo
+const unsigned kMathGlyphInfoHeaderSize = 4 * 2;
+
+// The size of the MathValueRecord.
+// Value
+// DeviceTable
+const unsigned kMathValueRecordSize = 2 * 2;
+
+// The size of the GlyphPartRecord.
+// glyph
+// StartConnectorLength
+// EndConnectorLength
+// FullAdvance
+// PartFlags
+const unsigned kGlyphPartRecordSize = 5 * 2;
+
+// Shared Table: MathValueRecord
+
+bool ParseMathValueRecord(const ots::OpenTypeFile *file,
+                          ots::Buffer* subtable, const uint8_t *data,
+                          const size_t length) {
+  // Check the Value field.
+  if (!subtable->Skip(2)) {
+    return OTS_FAILURE();
+  }
+
+  // Check the offset to device table.
+  uint16_t offset = 0;
+  if (!subtable->ReadU16(&offset)) {
+    return OTS_FAILURE();
+  }
+  if (offset) {
+    if (offset >= length) {
+      return OTS_FAILURE();
+    }
+    if (!ots::ParseDeviceTable(file, data + offset, length - offset)) {
+      return OTS_FAILURE();
+    }
+  }
+
+  return true;
+}
+
+bool ParseMathConstantsTable(const ots::OpenTypeFile *file,
+                             const uint8_t *data, size_t length) {
+  ots::Buffer subtable(data, length);
+
+  // Part 1: int16 or uint16 constants.
+  //  ScriptPercentScaleDown
+  //  ScriptScriptPercentScaleDown
+  //  DelimitedSubFormulaMinHeight
+  //  DisplayOperatorMinHeight
+  if (!subtable.Skip(4 * 2)) {
+    return OTS_FAILURE();
+  }
+
+  // Part 2: MathValueRecord constants.
+  // MathLeading
+  // AxisHeight
+  // AccentBaseHeight
+  // FlattenedAccentBaseHeight
+  // SubscriptShiftDown
+  // SubscriptTopMax
+  // SubscriptBaselineDropMin
+  // SuperscriptShiftUp
+  // SuperscriptShiftUpCramped
+  // SuperscriptBottomMin
+  //
+  // SuperscriptBaselineDropMax
+  // SubSuperscriptGapMin
+  // SuperscriptBottomMaxWithSubscript
+  // SpaceAfterScript
+  // UpperLimitGapMin
+  // UpperLimitBaselineRiseMin
+  // LowerLimitGapMin
+  // LowerLimitBaselineDropMin
+  // StackTopShiftUp
+  // StackTopDisplayStyleShiftUp
+  //
+  // StackBottomShiftDown
+  // StackBottomDisplayStyleShiftDown
+  // StackGapMin
+  // StackDisplayStyleGapMin
+  // StretchStackTopShiftUp
+  // StretchStackBottomShiftDown
+  // StretchStackGapAboveMin
+  // StretchStackGapBelowMin
+  // FractionNumeratorShiftUp
+  // FractionNumeratorDisplayStyleShiftUp
+  //
+  // FractionDenominatorShiftDown
+  // FractionDenominatorDisplayStyleShiftDown
+  // FractionNumeratorGapMin
+  // FractionNumDisplayStyleGapMin
+  // FractionRuleThickness
+  // FractionDenominatorGapMin
+  // FractionDenomDisplayStyleGapMin
+  // SkewedFractionHorizontalGap
+  // SkewedFractionVerticalGap
+  // OverbarVerticalGap
+  //
+  // OverbarRuleThickness
+  // OverbarExtraAscender
+  // UnderbarVerticalGap
+  // UnderbarRuleThickness
+  // UnderbarExtraDescender
+  // RadicalVerticalGap
+  // RadicalDisplayStyleVerticalGap
+  // RadicalRuleThickness
+  // RadicalExtraAscender
+  // RadicalKernBeforeDegree
+  //
+  // RadicalKernAfterDegree
+  for (unsigned i = 0; i < static_cast<unsigned>(51); ++i) {
+    if (!ParseMathValueRecord(file, &subtable, data, length)) {
+      return OTS_FAILURE();
+    }
+  }
+
+  // Part 3: uint16 constant
+  // RadicalDegreeBottomRaisePercent
+  if (!subtable.Skip(2)) {
+    return OTS_FAILURE();
+  }
+
+  return true;
+}
+
+bool ParseMathValueRecordSequenceForGlyphs(const ots::OpenTypeFile *file,
+                                           ots::Buffer* subtable,
+                                           const uint8_t *data,
+                                           const size_t length,
+                                           const uint16_t num_glyphs) {
+  // Check the header.
+  uint16_t offset_coverage = 0;
+  uint16_t sequence_count = 0;
+  if (!subtable->ReadU16(&offset_coverage) ||
+      !subtable->ReadU16(&sequence_count)) {
+    return OTS_FAILURE();
+  }
+
+  const unsigned sequence_end = static_cast<unsigned>(2 * 2) +
+      sequence_count * kMathValueRecordSize;
+  if (sequence_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE();
+  }
+
+  // Check coverage table.
+  if (offset_coverage < sequence_end || offset_coverage >= length) {
+    return OTS_FAILURE();
+  }
+  if (!ots::ParseCoverageTable(file, data + offset_coverage,
+                               length - offset_coverage,
+                               num_glyphs, sequence_count)) {
+    return OTS_FAILURE();
+  }
+
+  // Check sequence.
+  for (unsigned i = 0; i < sequence_count; ++i) {
+    if (!ParseMathValueRecord(file, subtable, data, length)) {
+      return OTS_FAILURE();
+    }
+  }
+
+  return true;
+}
+
+bool ParseMathItalicsCorrectionInfoTable(const ots::OpenTypeFile *file,
+                                         const uint8_t *data,
+                                         size_t length,
+                                         const uint16_t num_glyphs) {
+  ots::Buffer subtable(data, length);
+  return ParseMathValueRecordSequenceForGlyphs(file, &subtable, data, length,
+                                               num_glyphs);
+}
+
+bool ParseMathTopAccentAttachmentTable(const ots::OpenTypeFile *file,
+                                       const uint8_t *data,
+                                       size_t length,
+                                       const uint16_t num_glyphs) {
+  ots::Buffer subtable(data, length);
+  return ParseMathValueRecordSequenceForGlyphs(file, &subtable, data, length,
+                                               num_glyphs);
+}
+
+bool ParseMathKernTable(const ots::OpenTypeFile *file,
+                        const uint8_t *data, size_t length) {
+  ots::Buffer subtable(data, length);
+
+  // Check the Height count.
+  uint16_t height_count = 0;
+  if (!subtable.ReadU16(&height_count)) {
+    return OTS_FAILURE();
+  }
+
+  // Check the Correction Heights.
+  for (unsigned i = 0; i < height_count; ++i) {
+    if (!ParseMathValueRecord(file, &subtable, data, length)) {
+      return OTS_FAILURE();
+    }
+  }
+
+  // Check the Kern Values.
+  for (unsigned i = 0; i <= height_count; ++i) {
+    if (!ParseMathValueRecord(file, &subtable, data, length)) {
+      return OTS_FAILURE();
+    }
+  }
+
+  return true;
+}
+
+bool ParseMathKernInfoTable(const ots::OpenTypeFile *file,
+                            const uint8_t *data, size_t length,
+                            const uint16_t num_glyphs) {
+  ots::Buffer subtable(data, length);
+
+  // Check the header.
+  uint16_t offset_coverage = 0;
+  uint16_t sequence_count = 0;
+  if (!subtable.ReadU16(&offset_coverage) ||
+      !subtable.ReadU16(&sequence_count)) {
+    return OTS_FAILURE();
+  }
+
+  const unsigned sequence_end = static_cast<unsigned>(2 * 2) +
+    sequence_count * 4 * 2;
+  if (sequence_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE();
+  }
+
+  // Check coverage table.
+  if (offset_coverage < sequence_end || offset_coverage >= length) {
+    return OTS_FAILURE();
+  }
+  if (!ots::ParseCoverageTable(file, data + offset_coverage, length - offset_coverage,
+                               num_glyphs, sequence_count)) {
+    return OTS_FAILURE();
+  }
+
+  // Check sequence of MathKernInfoRecord
+  for (unsigned i = 0; i < sequence_count; ++i) {
+    // Check TopRight, TopLeft, BottomRight and BottomLeft Math Kern.
+    for (unsigned j = 0; j < 4; ++j) {
+      uint16_t offset_math_kern = 0;
+      if (!subtable.ReadU16(&offset_math_kern)) {
+        return OTS_FAILURE();
+      }
+      if (offset_math_kern) {
+        if (offset_math_kern < sequence_end || offset_math_kern >= length ||
+            !ParseMathKernTable(file, data + offset_math_kern,
+                                length - offset_math_kern)) {
+          return OTS_FAILURE();
+        }
+      }
+    }
+  }
+
+  return true;
+}
+
+bool ParseMathGlyphInfoTable(const ots::OpenTypeFile *file,
+                             const uint8_t *data, size_t length,
+                             const uint16_t num_glyphs) {
+  ots::Buffer subtable(data, length);
+
+  // Check Header.
+  uint16_t offset_math_italics_correction_info = 0;
+  uint16_t offset_math_top_accent_attachment = 0;
+  uint16_t offset_extended_shaped_coverage = 0;
+  uint16_t offset_math_kern_info = 0;
+  if (!subtable.ReadU16(&offset_math_italics_correction_info) ||
+      !subtable.ReadU16(&offset_math_top_accent_attachment) ||
+      !subtable.ReadU16(&offset_extended_shaped_coverage) ||
+      !subtable.ReadU16(&offset_math_kern_info)) {
+    return OTS_FAILURE();
+  }
+
+  // Check subtables.
+  // The specification does not say whether the offsets for
+  // MathItalicsCorrectionInfo, MathTopAccentAttachment and MathKernInfo may
+  // be NULL, but that's the case in some fonts (e.g STIX) so we accept that.
+  if (offset_math_italics_correction_info) {
+    if (offset_math_italics_correction_info >= length ||
+        offset_math_italics_correction_info < kMathGlyphInfoHeaderSize ||
+        !ParseMathItalicsCorrectionInfoTable(
+            file, data + offset_math_italics_correction_info,
+            length - offset_math_italics_correction_info,
+            num_glyphs)) {
+      return OTS_FAILURE();
+    }
+  }
+  if (offset_math_top_accent_attachment) {
+    if (offset_math_top_accent_attachment >= length ||
+        offset_math_top_accent_attachment < kMathGlyphInfoHeaderSize ||
+        !ParseMathTopAccentAttachmentTable(file, data +
+                                           offset_math_top_accent_attachment,
+                                           length -
+                                           offset_math_top_accent_attachment,
+                                           num_glyphs)) {
+      return OTS_FAILURE();
+    }
+  }
+  if (offset_extended_shaped_coverage) {
+    if (offset_extended_shaped_coverage >= length ||
+        offset_extended_shaped_coverage < kMathGlyphInfoHeaderSize ||
+        !ots::ParseCoverageTable(file, data + offset_extended_shaped_coverage,
+                                 length - offset_extended_shaped_coverage,
+                                 num_glyphs)) {
+      return OTS_FAILURE();
+    }
+  }
+  if (offset_math_kern_info) {
+    if (offset_math_kern_info >= length ||
+        offset_math_kern_info < kMathGlyphInfoHeaderSize ||
+        !ParseMathKernInfoTable(file, data + offset_math_kern_info,
+                                length - offset_math_kern_info, num_glyphs)) {
+      return OTS_FAILURE();
+    }
+  }
+
+  return true;
+}
+
+bool ParseGlyphAssemblyTable(const ots::OpenTypeFile *file,
+                             const uint8_t *data,
+                             size_t length, const uint16_t num_glyphs) {
+  ots::Buffer subtable(data, length);
+
+  // Check the header.
+  uint16_t part_count = 0;
+  if (!ParseMathValueRecord(file, &subtable, data, length) ||
+      !subtable.ReadU16(&part_count)) {
+    return OTS_FAILURE();
+  }
+
+  const unsigned sequence_end = kMathValueRecordSize +
+    static_cast<unsigned>(2) + part_count * kGlyphPartRecordSize;
+  if (sequence_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE();
+  }
+
+  // Check the sequence of GlyphPartRecord.
+  for (unsigned i = 0; i < part_count; ++i) {
+    uint16_t glyph = 0;
+    uint16_t part_flags = 0;
+    if (!subtable.ReadU16(&glyph) ||
+        !subtable.Skip(2 * 3) ||
+        !subtable.ReadU16(&part_flags)) {
+      return OTS_FAILURE();
+    }
+    if (glyph >= num_glyphs) {
+      return OTS_FAILURE_MSG("bad glyph ID: %u", glyph);
+    }
+    if (part_flags & ~0x00000001) {
+      return OTS_FAILURE_MSG("unknown part flag: %u", part_flags);
+    }
+  }
+
+  return true;
+}
+
+bool ParseMathGlyphConstructionTable(const ots::OpenTypeFile *file,
+                                     const uint8_t *data,
+                                     size_t length, const uint16_t num_glyphs) {
+  ots::Buffer subtable(data, length);
+
+  // Check the header.
+  uint16_t offset_glyph_assembly = 0;
+  uint16_t variant_count = 0;
+  if (!subtable.ReadU16(&offset_glyph_assembly) ||
+      !subtable.ReadU16(&variant_count)) {
+    return OTS_FAILURE();
+  }
+
+  const unsigned sequence_end = static_cast<unsigned>(2 * 2) +
+    variant_count * 2 * 2;
+  if (sequence_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE();
+  }
+
+  // Check the GlyphAssembly offset.
+  if (offset_glyph_assembly) {
+    if (offset_glyph_assembly >= length ||
+        offset_glyph_assembly < sequence_end) {
+      return OTS_FAILURE();
+    }
+    if (!ParseGlyphAssemblyTable(file, data + offset_glyph_assembly,
+                                 length - offset_glyph_assembly, num_glyphs)) {
+      return OTS_FAILURE();
+    }
+  }
+
+  // Check the sequence of MathGlyphVariantRecord.
+  for (unsigned i = 0; i < variant_count; ++i) {
+    uint16_t glyph = 0;
+    if (!subtable.ReadU16(&glyph) ||
+        !subtable.Skip(2)) {
+      return OTS_FAILURE();
+    }
+    if (glyph >= num_glyphs) {
+      return OTS_FAILURE_MSG("bad glyph ID: %u", glyph);
+    }
+  }
+
+  return true;
+}
+
+bool ParseMathGlyphConstructionSequence(const ots::OpenTypeFile *file,
+                                        ots::Buffer* subtable,
+                                        const uint8_t *data,
+                                        size_t length,
+                                        const uint16_t num_glyphs,
+                                        uint16_t offset_coverage,
+                                        uint16_t glyph_count,
+                                        const unsigned sequence_end) {
+  // Check coverage table.
+  if (offset_coverage < sequence_end || offset_coverage >= length) {
+    return OTS_FAILURE();
+  }
+  if (!ots::ParseCoverageTable(file, data + offset_coverage,
+                               length - offset_coverage,
+                               num_glyphs, glyph_count)) {
+    return OTS_FAILURE();
+  }
+
+  // Check sequence of MathGlyphConstruction.
+  for (unsigned i = 0; i < glyph_count; ++i) {
+      uint16_t offset_glyph_construction = 0;
+      if (!subtable->ReadU16(&offset_glyph_construction)) {
+        return OTS_FAILURE();
+      }
+      if (offset_glyph_construction < sequence_end ||
+          offset_glyph_construction >= length ||
+          !ParseMathGlyphConstructionTable(file, data + offset_glyph_construction,
+                                           length - offset_glyph_construction,
+                                           num_glyphs)) {
+        return OTS_FAILURE();
+      }
+  }
+
+  return true;
+}
+
+bool ParseMathVariantsTable(const ots::OpenTypeFile *file,
+                            const uint8_t *data,
+                            size_t length, const uint16_t num_glyphs) {
+  ots::Buffer subtable(data, length);
+
+  // Check the header.
+  uint16_t offset_vert_glyph_coverage = 0;
+  uint16_t offset_horiz_glyph_coverage = 0;
+  uint16_t vert_glyph_count = 0;
+  uint16_t horiz_glyph_count = 0;
+  if (!subtable.Skip(2) ||  // MinConnectorOverlap
+      !subtable.ReadU16(&offset_vert_glyph_coverage) ||
+      !subtable.ReadU16(&offset_horiz_glyph_coverage) ||
+      !subtable.ReadU16(&vert_glyph_count) ||
+      !subtable.ReadU16(&horiz_glyph_count)) {
+    return OTS_FAILURE();
+  }
+
+  const unsigned sequence_end = 5 * 2 + vert_glyph_count * 2 +
+    horiz_glyph_count * 2;
+  if (sequence_end > std::numeric_limits<uint16_t>::max()) {
+    return OTS_FAILURE();
+  }
+
+  if (!ParseMathGlyphConstructionSequence(file, &subtable, data, length, num_glyphs,
+                                          offset_vert_glyph_coverage,
+                                          vert_glyph_count,
+                                          sequence_end) ||
+      !ParseMathGlyphConstructionSequence(file, &subtable, data, length, num_glyphs,
+                                          offset_horiz_glyph_coverage,
+                                          horiz_glyph_count,
+                                          sequence_end)) {
+    return OTS_FAILURE();
+  }
+
+  return true;
+}
+
+}  // namespace
+
+#define DROP_THIS_TABLE(msg_) \
+  do { \
+    OTS_FAILURE_MSG(msg_ ", table discarded"); \
+    file->math->data = 0; \
+    file->math->length = 0; \
+  } while (0)
+
+namespace ots {
+
+bool ots_math_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
+  // Grab the number of glyphs in the file from the maxp table to check
+  // GlyphIDs in MATH table.
+  if (!file->maxp) {
+    return OTS_FAILURE();
+  }
+  const uint16_t num_glyphs = file->maxp->num_glyphs;
+
+  Buffer table(data, length);
+
+  OpenTypeMATH* math = new OpenTypeMATH;
+  file->math = math;
+
+  uint32_t version = 0;
+  if (!table.ReadU32(&version)) {
+    return OTS_FAILURE();
+  }
+  if (version != 0x00010000) {
+    DROP_THIS_TABLE("bad MATH version");
+    return true;
+  }
+
+  uint16_t offset_math_constants = 0;
+  uint16_t offset_math_glyph_info = 0;
+  uint16_t offset_math_variants = 0;
+  if (!table.ReadU16(&offset_math_constants) ||
+      !table.ReadU16(&offset_math_glyph_info) ||
+      !table.ReadU16(&offset_math_variants)) {
+    return OTS_FAILURE();
+  }
+
+  if (offset_math_constants >= length ||
+      offset_math_constants < kMathHeaderSize ||
+      offset_math_glyph_info >= length ||
+      offset_math_glyph_info < kMathHeaderSize ||
+      offset_math_variants >= length ||
+      offset_math_variants < kMathHeaderSize) {
+    DROP_THIS_TABLE("bad offset in MATH header");
+    return true;
+  }
+
+  if (!ParseMathConstantsTable(file, data + offset_math_constants,
+                               length - offset_math_constants)) {
+    DROP_THIS_TABLE("failed to parse MathConstants table");
+    return true;
+  }
+  if (!ParseMathGlyphInfoTable(file, data + offset_math_glyph_info,
+                               length - offset_math_glyph_info, num_glyphs)) {
+    DROP_THIS_TABLE("failed to parse MathGlyphInfo table");
+    return true;
+  }
+  if (!ParseMathVariantsTable(file, data + offset_math_variants,
+                              length - offset_math_variants, num_glyphs)) {
+    DROP_THIS_TABLE("failed to parse MathVariants table");
+    return true;
+  }
+
+  math->data = data;
+  math->length = length;
+  return true;
+}
+
+bool ots_math_should_serialise(OpenTypeFile *file) {
+  return file->math != NULL && file->math->data != NULL;
+}
+
+bool ots_math_serialise(OTSStream *out, OpenTypeFile *file) {
+  if (!out->Write(file->math->data, file->math->length)) {
+    return OTS_FAILURE();
+  }
+
+  return true;
+}
+
+void ots_math_free(OpenTypeFile *file) {
+  delete file->math;
+}
+
+}  // namespace ots
+
+#undef TABLE_NAME
+#undef DROP_THIS_TABLE
diff --git a/third_party/ots/src/math_.h b/third_party/ots/src/math_.h
new file mode 100644
index 0000000..91c54db
--- /dev/null
+++ b/third_party/ots/src/math_.h
@@ -0,0 +1,25 @@
+// Copyright (c) 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_MATH_H_
+#define OTS_MATH_H_
+
+#include "ots.h"
+
+namespace ots {
+
+struct OpenTypeMATH {
+  OpenTypeMATH()
+      : data(NULL),
+        length(0) {
+  }
+
+  const uint8_t *data;
+  size_t length;
+};
+
+}  // namespace ots
+
+#endif
+
diff --git a/third_party/ots/src/maxp.cc b/third_party/ots/src/maxp.cc
new file mode 100644
index 0000000..aaf0076
--- /dev/null
+++ b/third_party/ots/src/maxp.cc
@@ -0,0 +1,120 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "maxp.h"
+
+// maxp - Maximum Profile
+// http://www.microsoft.com/typography/otspec/maxp.htm
+
+#define TABLE_NAME "maxp"
+
+namespace ots {
+
+bool ots_maxp_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
+  Buffer table(data, length);
+
+  OpenTypeMAXP *maxp = new OpenTypeMAXP;
+  file->maxp = maxp;
+
+  uint32_t version = 0;
+  if (!table.ReadU32(&version)) {
+    return OTS_FAILURE_MSG("Failed to read version of maxp table");
+  }
+
+  if (version >> 16 > 1) {
+    return OTS_FAILURE_MSG("Bad maxp version %d", version);
+  }
+
+  if (!table.ReadU16(&maxp->num_glyphs)) {
+    return OTS_FAILURE_MSG("Failed to read number of glyphs from maxp table");
+  }
+
+  if (!maxp->num_glyphs) {
+    return OTS_FAILURE_MSG("Bad number of glyphs 0 in maxp table");
+  }
+
+  if (version >> 16 == 1) {
+    maxp->version_1 = true;
+    if (!table.ReadU16(&maxp->max_points) ||
+        !table.ReadU16(&maxp->max_contours) ||
+        !table.ReadU16(&maxp->max_c_points) ||
+        !table.ReadU16(&maxp->max_c_contours) ||
+        !table.ReadU16(&maxp->max_zones) ||
+        !table.ReadU16(&maxp->max_t_points) ||
+        !table.ReadU16(&maxp->max_storage) ||
+        !table.ReadU16(&maxp->max_fdefs) ||
+        !table.ReadU16(&maxp->max_idefs) ||
+        !table.ReadU16(&maxp->max_stack) ||
+        !table.ReadU16(&maxp->max_size_glyf_instructions) ||
+        !table.ReadU16(&maxp->max_c_components) ||
+        !table.ReadU16(&maxp->max_c_depth)) {
+      return OTS_FAILURE_MSG("Failed to read maxp table");
+    }
+
+    if (maxp->max_zones == 0) {
+      // workaround for ipa*.ttf Japanese fonts.
+      OTS_WARNING("bad max_zones: %u", maxp->max_zones);
+      maxp->max_zones = 1;
+    } else if (maxp->max_zones == 3) {
+      // workaround for Ecolier-*.ttf fonts.
+      OTS_WARNING("bad max_zones: %u", maxp->max_zones);
+      maxp->max_zones = 2;
+    }
+
+    if ((maxp->max_zones != 1) && (maxp->max_zones != 2)) {
+      return OTS_FAILURE_MSG("Bad max zones %d in maxp", maxp->max_zones);
+    }
+  } else {
+    maxp->version_1 = false;
+  }
+
+  return true;
+}
+
+bool ots_maxp_should_serialise(OpenTypeFile *file) {
+  return file->maxp != NULL;
+}
+
+bool ots_maxp_serialise(OTSStream *out, OpenTypeFile *file) {
+  const OpenTypeMAXP *maxp = file->maxp;
+
+  if (!out->WriteU32(maxp->version_1 ? 0x00010000 : 0x00005000) ||
+      !out->WriteU16(maxp->num_glyphs)) {
+    return OTS_FAILURE_MSG("Failed to write maxp version or number of glyphs");
+  }
+
+  if (!maxp->version_1) return true;
+
+  if (!out->WriteU16(maxp->max_points) ||
+      !out->WriteU16(maxp->max_contours) ||
+      !out->WriteU16(maxp->max_c_points) ||
+      !out->WriteU16(maxp->max_c_contours)) {
+    return OTS_FAILURE_MSG("Failed to write maxp");
+  }
+
+  if (!out->WriteU16(maxp->max_zones) ||
+      !out->WriteU16(maxp->max_t_points) ||
+      !out->WriteU16(maxp->max_storage) ||
+      !out->WriteU16(maxp->max_fdefs) ||
+      !out->WriteU16(maxp->max_idefs) ||
+      !out->WriteU16(maxp->max_stack) ||
+      !out->WriteU16(maxp->max_size_glyf_instructions)) {
+    return OTS_FAILURE_MSG("Failed to write more maxp");
+  }
+
+  if (!out->WriteU16(maxp->max_c_components) ||
+      !out->WriteU16(maxp->max_c_depth)) {
+    return OTS_FAILURE_MSG("Failed to write yet more maxp");
+  }
+
+  return true;
+}
+
+void ots_maxp_free(OpenTypeFile *file) {
+  delete file->maxp;
+}
+
+}  // namespace ots
+
+#undef TABLE_NAME
diff --git a/third_party/ots/src/maxp.h b/third_party/ots/src/maxp.h
new file mode 100644
index 0000000..efca0c9
--- /dev/null
+++ b/third_party/ots/src/maxp.h
@@ -0,0 +1,35 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_MAXP_H_
+#define OTS_MAXP_H_
+
+#include "ots.h"
+
+namespace ots {
+
+struct OpenTypeMAXP {
+  uint16_t num_glyphs;
+  bool version_1;
+
+  uint16_t max_points;
+  uint16_t max_contours;
+  uint16_t max_c_points;
+  uint16_t max_c_contours;
+
+  uint16_t max_zones;
+  uint16_t max_t_points;
+  uint16_t max_storage;
+  uint16_t max_fdefs;
+  uint16_t max_idefs;
+  uint16_t max_stack;
+  uint16_t max_size_glyf_instructions;
+
+  uint16_t max_c_components;
+  uint16_t max_c_depth;
+};
+
+}  // namespace ots
+
+#endif  // OTS_MAXP_H_
diff --git a/third_party/ots/src/metrics.cc b/third_party/ots/src/metrics.cc
new file mode 100644
index 0000000..8d59b953
--- /dev/null
+++ b/third_party/ots/src/metrics.cc
@@ -0,0 +1,193 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "metrics.h"
+
+#include "head.h"
+#include "maxp.h"
+
+// OpenType horizontal and vertical common header format
+// http://www.microsoft.com/typography/otspec/hhea.htm
+// http://www.microsoft.com/typography/otspec/vhea.htm
+
+#define TABLE_NAME "metrics" // XXX: use individual table names
+
+namespace ots {
+
+bool ParseMetricsHeader(OpenTypeFile *file, Buffer *table,
+                        OpenTypeMetricsHeader *header) {
+  if (!table->ReadS16(&header->ascent) ||
+      !table->ReadS16(&header->descent) ||
+      !table->ReadS16(&header->linegap) ||
+      !table->ReadU16(&header->adv_width_max) ||
+      !table->ReadS16(&header->min_sb1) ||
+      !table->ReadS16(&header->min_sb2) ||
+      !table->ReadS16(&header->max_extent) ||
+      !table->ReadS16(&header->caret_slope_rise) ||
+      !table->ReadS16(&header->caret_slope_run) ||
+      !table->ReadS16(&header->caret_offset)) {
+    return OTS_FAILURE_MSG("Failed to read metrics header");
+  }
+
+  if (header->ascent < 0) {
+    OTS_WARNING("bad ascent: %d", header->ascent);
+    header->ascent = 0;
+  }
+  if (header->linegap < 0) {
+    OTS_WARNING("bad linegap: %d", header->linegap);
+    header->linegap = 0;
+  }
+
+  if (!file->head) {
+    return OTS_FAILURE_MSG("Missing head font table");
+  }
+
+  // if the font is non-slanted, caret_offset should be zero.
+  if (!(file->head->mac_style & 2) &&
+      (header->caret_offset != 0)) {
+    OTS_WARNING("bad caret offset: %d", header->caret_offset);
+    header->caret_offset = 0;
+  }
+
+  // skip the reserved bytes
+  if (!table->Skip(8)) {
+    return OTS_FAILURE_MSG("Failed to skip reserverd bytes");
+  }
+
+  int16_t data_format;
+  if (!table->ReadS16(&data_format)) {
+    return OTS_FAILURE_MSG("Failed to read data format");
+  }
+  if (data_format) {
+    return OTS_FAILURE_MSG("Bad data format %d", data_format);
+  }
+
+  if (!table->ReadU16(&header->num_metrics)) {
+    return OTS_FAILURE_MSG("Failed to read number of metrics");
+  }
+
+  if (!file->maxp) {
+    return OTS_FAILURE_MSG("Missing maxp font table");
+  }
+
+  if (header->num_metrics > file->maxp->num_glyphs) {
+    return OTS_FAILURE_MSG("Bad number of metrics %d", header->num_metrics);
+  }
+
+  return true;
+}
+
+bool SerialiseMetricsHeader(const ots::OpenTypeFile *file,
+                            OTSStream *out,
+                            const OpenTypeMetricsHeader *header) {
+  if (!out->WriteU32(header->version) ||
+      !out->WriteS16(header->ascent) ||
+      !out->WriteS16(header->descent) ||
+      !out->WriteS16(header->linegap) ||
+      !out->WriteU16(header->adv_width_max) ||
+      !out->WriteS16(header->min_sb1) ||
+      !out->WriteS16(header->min_sb2) ||
+      !out->WriteS16(header->max_extent) ||
+      !out->WriteS16(header->caret_slope_rise) ||
+      !out->WriteS16(header->caret_slope_run) ||
+      !out->WriteS16(header->caret_offset) ||
+      !out->WriteR64(0) ||  // reserved
+      !out->WriteS16(0) ||  // metric data format
+      !out->WriteU16(header->num_metrics)) {
+    return OTS_FAILURE_MSG("Failed to write metrics");
+  }
+
+  return true;
+}
+
+bool ParseMetricsTable(const ots::OpenTypeFile *file,
+                       Buffer *table,
+                       const uint16_t num_glyphs,
+                       const OpenTypeMetricsHeader *header,
+                       OpenTypeMetricsTable *metrics) {
+  // |num_metrics| is a uint16_t, so it's bounded < 65536. This limits that
+  // amount of memory that we'll allocate for this to a sane amount.
+  const unsigned num_metrics = header->num_metrics;
+
+  if (num_metrics > num_glyphs) {
+    return OTS_FAILURE_MSG("Bad number of metrics %d", num_metrics);
+  }
+  if (!num_metrics) {
+    return OTS_FAILURE_MSG("No metrics!");
+  }
+  const unsigned num_sbs = num_glyphs - num_metrics;
+
+  metrics->entries.reserve(num_metrics);
+  for (unsigned i = 0; i < num_metrics; ++i) {
+    uint16_t adv = 0;
+    int16_t sb = 0;
+    if (!table->ReadU16(&adv) || !table->ReadS16(&sb)) {
+      return OTS_FAILURE_MSG("Failed to read metric %d", i);
+    }
+
+    // This check is bogus, see https://github.com/khaledhosny/ots/issues/36
+#if 0
+    // Since so many fonts don't have proper value on |adv| and |sb|,
+    // we should not call ots_failure() here. For example, about 20% of fonts
+    // in http://www.princexml.com/fonts/ (200+ fonts) fails these tests.
+    if (adv > header->adv_width_max) {
+      OTS_WARNING("bad adv: %u > %u", adv, header->adv_width_max);
+      adv = header->adv_width_max;
+    }
+
+    if (sb < header->min_sb1) {
+      OTS_WARNING("bad sb: %d < %d", sb, header->min_sb1);
+      sb = header->min_sb1;
+    }
+#endif
+
+    metrics->entries.push_back(std::make_pair(adv, sb));
+  }
+
+  metrics->sbs.reserve(num_sbs);
+  for (unsigned i = 0; i < num_sbs; ++i) {
+    int16_t sb;
+    if (!table->ReadS16(&sb)) {
+      // Some Japanese fonts (e.g., mona.ttf) fail this test.
+      return OTS_FAILURE_MSG("Failed to read side bearing %d", i + num_metrics);
+    }
+
+    // This check is bogus, see https://github.com/khaledhosny/ots/issues/36
+#if 0
+    if (sb < header->min_sb1) {
+      // The same as above. Three fonts in http://www.fontsquirrel.com/fontface
+      // (e.g., Notice2Std.otf) have weird lsb values.
+      OTS_WARNING("bad lsb: %d < %d", sb, header->min_sb1);
+      sb = header->min_sb1;
+    }
+#endif
+
+    metrics->sbs.push_back(sb);
+  }
+
+  return true;
+}
+
+bool SerialiseMetricsTable(const ots::OpenTypeFile *file,
+                           OTSStream *out,
+                           const OpenTypeMetricsTable *metrics) {
+  for (unsigned i = 0; i < metrics->entries.size(); ++i) {
+    if (!out->WriteU16(metrics->entries[i].first) ||
+        !out->WriteS16(metrics->entries[i].second)) {
+      return OTS_FAILURE_MSG("Failed to write metric %d", i);
+    }
+  }
+
+  for (unsigned i = 0; i < metrics->sbs.size(); ++i) {
+    if (!out->WriteS16(metrics->sbs[i])) {
+      return OTS_FAILURE_MSG("Failed to write side bearing %ld", i + metrics->entries.size());
+    }
+  }
+
+  return true;
+}
+
+}  // namespace ots
+
+#undef TABLE_NAME
diff --git a/third_party/ots/src/metrics.h b/third_party/ots/src/metrics.h
new file mode 100644
index 0000000..f0b4ee8c
--- /dev/null
+++ b/third_party/ots/src/metrics.h
@@ -0,0 +1,54 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_METRICS_H_
+#define OTS_METRICS_H_
+
+#include <new>
+#include <utility>
+#include <vector>
+
+#include "ots.h"
+
+namespace ots {
+
+struct OpenTypeMetricsHeader {
+  uint32_t version;
+  int16_t ascent;
+  int16_t descent;
+  int16_t linegap;
+  uint16_t adv_width_max;
+  int16_t min_sb1;
+  int16_t min_sb2;
+  int16_t max_extent;
+  int16_t caret_slope_rise;
+  int16_t caret_slope_run;
+  int16_t caret_offset;
+  uint16_t num_metrics;
+};
+
+struct OpenTypeMetricsTable {
+  std::vector<std::pair<uint16_t, int16_t> > entries;
+  std::vector<int16_t> sbs;
+};
+
+bool ParseMetricsHeader(OpenTypeFile *file, Buffer *table,
+                        OpenTypeMetricsHeader *header);
+bool SerialiseMetricsHeader(const ots::OpenTypeFile *file,
+                            OTSStream *out,
+                            const OpenTypeMetricsHeader *header);
+
+bool ParseMetricsTable(const ots::OpenTypeFile *file,
+                       Buffer *table,
+                       const uint16_t num_glyphs,
+                       const OpenTypeMetricsHeader *header,
+                       OpenTypeMetricsTable *metrics);
+bool SerialiseMetricsTable(const ots::OpenTypeFile *file,
+                           OTSStream *out,
+                           const OpenTypeMetricsTable *metrics);
+
+}  // namespace ots
+
+#endif  // OTS_METRICS_H_
+
diff --git a/third_party/ots/src/name.cc b/third_party/ots/src/name.cc
new file mode 100644
index 0000000..2ea10dc
--- /dev/null
+++ b/third_party/ots/src/name.cc
@@ -0,0 +1,340 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "name.h"
+
+#include <algorithm>
+#include <cstring>
+
+#include "cff.h"
+
+// name - Naming Table
+// http://www.microsoft.com/typography/otspec/name.htm
+
+#define TABLE_NAME "name"
+
+namespace {
+
+bool ValidInPsName(char c) {
+  return (c > 0x20 && c < 0x7f && !std::strchr("[](){}<>/%", c));
+}
+
+bool CheckPsNameAscii(const std::string& name) {
+  for (unsigned i = 0; i < name.size(); ++i) {
+    if (!ValidInPsName(name[i])) {
+      return false;
+    }
+  }
+  return true;
+}
+
+bool CheckPsNameUtf16Be(const std::string& name) {
+  if ((name.size() & 1) != 0)
+    return false;
+
+  for (unsigned i = 0; i < name.size(); i += 2) {
+    if (name[i] != 0) {
+      return false;
+    }
+    if (!ValidInPsName(name[i+1])) {
+      return false;
+    }
+  }
+  return true;
+}
+
+void AssignToUtf16BeFromAscii(std::string* target,
+                              const std::string& source) {
+  target->resize(source.size() * 2);
+  for (unsigned i = 0, j = 0; i < source.size(); i++) {
+    (*target)[j++] = '\0';
+    (*target)[j++] = source[i];
+  }
+}
+
+}  // namespace
+
+
+namespace ots {
+
+bool ots_name_parse(OpenTypeFile* file, const uint8_t* data, size_t length) {
+  Buffer table(data, length);
+
+  OpenTypeNAME* name = new OpenTypeNAME;
+  file->name = name;
+
+  uint16_t format = 0;
+  if (!table.ReadU16(&format) || format > 1) {
+    return OTS_FAILURE_MSG("Failed to read name table format or bad format %d", format);
+  }
+
+  uint16_t count = 0;
+  if (!table.ReadU16(&count)) {
+    return OTS_FAILURE_MSG("Failed to read name count");
+  }
+
+  uint16_t string_offset = 0;
+  if (!table.ReadU16(&string_offset) || string_offset > length) {
+    return OTS_FAILURE_MSG("Failed to read strings offset");
+  }
+  const char* string_base = reinterpret_cast<const char*>(data) +
+      string_offset;
+
+  NameRecord prev_record;
+  bool sort_required = false;
+
+  // Read all the names, discarding any with invalid IDs,
+  // and any where the offset/length would be outside the table.
+  // A stricter alternative would be to reject the font if there
+  // are invalid name records, but it's not clear that is necessary.
+  for (unsigned i = 0; i < count; ++i) {
+    NameRecord rec;
+    uint16_t name_length, name_offset = 0;
+    if (!table.ReadU16(&rec.platform_id) ||
+        !table.ReadU16(&rec.encoding_id) ||
+        !table.ReadU16(&rec.language_id) ||
+        !table.ReadU16(&rec.name_id) ||
+        !table.ReadU16(&name_length) ||
+        !table.ReadU16(&name_offset)) {
+      return OTS_FAILURE_MSG("Failed to read name entry %d", i);
+    }
+    // check platform & encoding, discard names with unknown values
+    switch (rec.platform_id) {
+      case 0:  // Unicode
+        if (rec.encoding_id > 6) {
+          continue;
+        }
+        break;
+      case 1:  // Macintosh
+        if (rec.encoding_id > 32) {
+          continue;
+        }
+        break;
+      case 2:  // ISO
+        if (rec.encoding_id > 2) {
+          continue;
+        }
+        break;
+      case 3:  // Windows: IDs 7 to 9 are "reserved"
+        if (rec.encoding_id > 6 && rec.encoding_id != 10) {
+          continue;
+        }
+        break;
+      case 4:  // Custom (OTF Windows NT compatibility)
+        if (rec.encoding_id > 255) {
+          continue;
+        }
+        break;
+      default:  // unknown platform
+        continue;
+    }
+
+    const unsigned name_end = static_cast<unsigned>(string_offset) +
+        name_offset + name_length;
+    if (name_end > length) {
+      continue;
+    }
+    rec.text.resize(name_length);
+    rec.text.assign(string_base + name_offset, name_length);
+
+    if (rec.name_id == 6) {
+      // PostScript name: check that it is valid, if not then discard it
+      if (rec.platform_id == 1) {
+        if (file->cff && !file->cff->name.empty()) {
+          rec.text = file->cff->name;
+        } else if (!CheckPsNameAscii(rec.text)) {
+          continue;
+        }
+      } else if (rec.platform_id == 0 || rec.platform_id == 3) {
+        if (file->cff && !file->cff->name.empty()) {
+          AssignToUtf16BeFromAscii(&rec.text, file->cff->name);
+        } else if (!CheckPsNameUtf16Be(rec.text)) {
+          continue;
+        }
+      }
+    }
+
+    if ((i > 0) && !(prev_record < rec)) {
+      OTS_WARNING("name records are not sorted.");
+      sort_required = true;
+    }
+
+    name->names.push_back(rec);
+    prev_record = rec;
+  }
+
+  if (format == 1) {
+    // extended name table format with language tags
+    uint16_t lang_tag_count;
+    if (!table.ReadU16(&lang_tag_count)) {
+      return OTS_FAILURE_MSG("Failed to read language tag count");
+    }
+    for (unsigned i = 0; i < lang_tag_count; ++i) {
+      uint16_t tag_length = 0;
+      uint16_t tag_offset = 0;
+      if (!table.ReadU16(&tag_length) || !table.ReadU16(&tag_offset)) {
+        return OTS_FAILURE_MSG("Faile to read tag length or offset");
+      }
+      const unsigned tag_end = static_cast<unsigned>(string_offset) +
+          tag_offset + tag_length;
+      if (tag_end > length) {
+        return OTS_FAILURE_MSG("bad end of tag %d > %ld for name entry %d", tag_end, length, i);
+      }
+      std::string tag(string_base + tag_offset, tag_length);
+      name->lang_tags.push_back(tag);
+    }
+  }
+
+  if (table.offset() > string_offset) {
+    // the string storage apparently overlapped the name/tag records;
+    // consider this font to be badly broken
+    return OTS_FAILURE_MSG("Bad table offset %ld > %d", table.offset(), string_offset);
+  }
+
+  // check existence of required name strings (synthesize if necessary)
+  //  [0 - copyright - skip]
+  //   1 - family
+  //   2 - subfamily
+  //  [3 - unique ID - skip]
+  //   4 - full name
+  //   5 - version
+  //   6 - postscript name
+  static const uint16_t kStdNameCount = 7;
+  static const char* kStdNames[kStdNameCount] = {
+    NULL,
+    "OTS derived font",
+    "Unspecified",
+    NULL,
+    "OTS derived font",
+    "1.000",
+    "OTS-derived-font"
+  };
+  // The spec says that "In CFF OpenType fonts, these two name strings, when
+  // translated to ASCII, must also be identical to the font name as stored in
+  // the CFF's Name INDEX." And actually, Mac OS X's font parser requires that.
+  if (file->cff && !file->cff->name.empty()) {
+    kStdNames[6] = file->cff->name.c_str();
+  }
+
+  // scan the names to check whether the required "standard" ones are present;
+  // if not, we'll add our fixed versions here
+  bool mac_name[kStdNameCount] = { 0 };
+  bool win_name[kStdNameCount] = { 0 };
+  for (std::vector<NameRecord>::iterator name_iter = name->names.begin();
+       name_iter != name->names.end(); name_iter++) {
+    const uint16_t id = name_iter->name_id;
+    if (id >= kStdNameCount || kStdNames[id] == NULL) {
+      continue;
+    }
+    if (name_iter->platform_id == 1) {
+      mac_name[id] = true;
+      continue;
+    }
+    if (name_iter->platform_id == 3) {
+      win_name[id] = true;
+      continue;
+    }
+  }
+
+  for (uint16_t i = 0; i < kStdNameCount; ++i) {
+    if (kStdNames[i] == NULL) {
+      continue;
+    }
+    if (!mac_name[i]) {
+      NameRecord rec(1 /* platform_id */, 0 /* encoding_id */,
+                     0 /* language_id */ , i /* name_id */);
+      rec.text.assign(kStdNames[i]);
+      name->names.push_back(rec);
+      sort_required = true;
+    }
+    if (!win_name[i]) {
+      NameRecord rec(3 /* platform_id */, 1 /* encoding_id */,
+                     1033 /* language_id */ , i /* name_id */);
+      AssignToUtf16BeFromAscii(&rec.text, std::string(kStdNames[i]));
+      name->names.push_back(rec);
+      sort_required = true;
+    }
+  }
+
+  if (sort_required) {
+    std::sort(name->names.begin(), name->names.end());
+  }
+
+  return true;
+}
+
+bool ots_name_should_serialise(OpenTypeFile* file) {
+  return file->name != NULL;
+}
+
+bool ots_name_serialise(OTSStream* out, OpenTypeFile* file) {
+  const OpenTypeNAME* name = file->name;
+
+  uint16_t name_count = static_cast<uint16_t>(name->names.size());
+  uint16_t lang_tag_count = static_cast<uint16_t>(name->lang_tags.size());
+  uint16_t format = 0;
+  size_t string_offset = 6 + name_count * 12;
+
+  if (name->lang_tags.size() > 0) {
+    // lang tags require a format-1 name table
+    format = 1;
+    string_offset += 2 + lang_tag_count * 4;
+  }
+  if (string_offset > 0xffff) {
+    return OTS_FAILURE_MSG("Bad string offset %ld", string_offset);
+  }
+  if (!out->WriteU16(format) ||
+      !out->WriteU16(name_count) ||
+      !out->WriteU16(static_cast<uint16_t>(string_offset))) {
+    return OTS_FAILURE_MSG("Failed to write name header");
+  }
+
+  std::string string_data;
+  for (std::vector<NameRecord>::const_iterator name_iter = name->names.begin();
+       name_iter != name->names.end(); name_iter++) {
+    const NameRecord& rec = *name_iter;
+    if (string_data.size() + rec.text.size() >
+            std::numeric_limits<uint16_t>::max() ||
+        !out->WriteU16(rec.platform_id) ||
+        !out->WriteU16(rec.encoding_id) ||
+        !out->WriteU16(rec.language_id) ||
+        !out->WriteU16(rec.name_id) ||
+        !out->WriteU16(static_cast<uint16_t>(rec.text.size())) ||
+        !out->WriteU16(static_cast<uint16_t>(string_data.size())) ) {
+      return OTS_FAILURE_MSG("Faile to write name entry");
+    }
+    string_data.append(rec.text);
+  }
+
+  if (format == 1) {
+    if (!out->WriteU16(lang_tag_count)) {
+      return OTS_FAILURE_MSG("Faile to write language tag count");
+    }
+    for (std::vector<std::string>::const_iterator tag_iter =
+             name->lang_tags.begin();
+         tag_iter != name->lang_tags.end(); tag_iter++) {
+      if (string_data.size() + tag_iter->size() >
+              std::numeric_limits<uint16_t>::max() ||
+          !out->WriteU16(static_cast<uint16_t>(tag_iter->size())) ||
+          !out->WriteU16(static_cast<uint16_t>(string_data.size()))) {
+        return OTS_FAILURE_MSG("Failed to write string");
+      }
+      string_data.append(*tag_iter);
+    }
+  }
+
+  if (!out->Write(string_data.data(), string_data.size())) {
+    return OTS_FAILURE_MSG("Faile to write string data");
+  }
+
+  return true;
+}
+
+void ots_name_free(OpenTypeFile* file) {
+  delete file->name;
+}
+
+}  // namespace
+
+#undef TABLE_NAME
diff --git a/third_party/ots/src/name.h b/third_party/ots/src/name.h
new file mode 100644
index 0000000..a11965f
--- /dev/null
+++ b/third_party/ots/src/name.h
@@ -0,0 +1,53 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_NAME_H_
+#define OTS_NAME_H_
+
+#include <new>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "ots.h"
+
+namespace ots {
+
+struct NameRecord {
+  NameRecord() {
+  }
+
+  NameRecord(uint16_t platformID, uint16_t encodingID,
+             uint16_t languageID, uint16_t nameID)
+    : platform_id(platformID),
+      encoding_id(encodingID),
+      language_id(languageID),
+      name_id(nameID) {
+  }
+
+  uint16_t platform_id;
+  uint16_t encoding_id;
+  uint16_t language_id;
+  uint16_t name_id;
+  std::string text;
+
+  bool operator<(const NameRecord& rhs) const {
+    if (platform_id < rhs.platform_id) return true;
+    if (platform_id > rhs.platform_id) return false;
+    if (encoding_id < rhs.encoding_id) return true;
+    if (encoding_id > rhs.encoding_id) return false;
+    if (language_id < rhs.language_id) return true;
+    if (language_id > rhs.language_id) return false;
+    return name_id < rhs.name_id;
+  }
+};
+
+struct OpenTypeNAME {
+  std::vector<NameRecord> names;
+  std::vector<std::string> lang_tags;
+};
+
+}  // namespace ots
+
+#endif  // OTS_NAME_H_
diff --git a/third_party/ots/src/os2.cc b/third_party/ots/src/os2.cc
new file mode 100644
index 0000000..915877e
--- /dev/null
+++ b/third_party/ots/src/os2.cc
@@ -0,0 +1,294 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "os2.h"
+
+#include "head.h"
+
+// OS/2 - OS/2 and Windows Metrics
+// http://www.microsoft.com/typography/otspec/os2.htm
+
+#define TABLE_NAME "OS/2"
+
+namespace ots {
+
+bool ots_os2_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
+  Buffer table(data, length);
+
+  OpenTypeOS2 *os2 = new OpenTypeOS2;
+  file->os2 = os2;
+
+  if (!table.ReadU16(&os2->version) ||
+      !table.ReadS16(&os2->avg_char_width) ||
+      !table.ReadU16(&os2->weight_class) ||
+      !table.ReadU16(&os2->width_class) ||
+      !table.ReadU16(&os2->type) ||
+      !table.ReadS16(&os2->subscript_x_size) ||
+      !table.ReadS16(&os2->subscript_y_size) ||
+      !table.ReadS16(&os2->subscript_x_offset) ||
+      !table.ReadS16(&os2->subscript_y_offset) ||
+      !table.ReadS16(&os2->superscript_x_size) ||
+      !table.ReadS16(&os2->superscript_y_size) ||
+      !table.ReadS16(&os2->superscript_x_offset) ||
+      !table.ReadS16(&os2->superscript_y_offset) ||
+      !table.ReadS16(&os2->strikeout_size) ||
+      !table.ReadS16(&os2->strikeout_position) ||
+      !table.ReadS16(&os2->family_class)) {
+    return OTS_FAILURE_MSG("Failed toi read basic os2 elements");
+  }
+
+  if (os2->version > 4) {
+    return OTS_FAILURE_MSG("os2 version too high %d", os2->version);
+  }
+
+  // Some linux fonts (e.g., Kedage-t.ttf and LucidaSansDemiOblique.ttf) have
+  // weird weight/width classes. Overwrite them with FW_NORMAL/1/9.
+  if (os2->weight_class < 100 ||
+      os2->weight_class > 900 ||
+      os2->weight_class % 100) {
+    OTS_WARNING("bad weight: %u", os2->weight_class);
+    os2->weight_class = 400;  // FW_NORMAL
+  }
+  if (os2->width_class < 1) {
+    OTS_WARNING("bad width: %u", os2->width_class);
+    os2->width_class = 1;
+  } else if (os2->width_class > 9) {
+    OTS_WARNING("bad width: %u", os2->width_class);
+    os2->width_class = 9;
+  }
+
+  // lowest 3 bits of fsType are exclusive.
+  if (os2->type & 0x2) {
+    // mask bits 2 & 3.
+    os2->type &= 0xfff3u;
+  } else if (os2->type & 0x4) {
+    // mask bits 1 & 3.
+    os2->type &= 0xfff4u;
+  } else if (os2->type & 0x8) {
+    // mask bits 1 & 2.
+    os2->type &= 0xfff9u;
+  }
+
+  // mask reserved bits. use only 0..3, 8, 9 bits.
+  os2->type &= 0x30f;
+
+  if (os2->subscript_x_size < 0) {
+    OTS_WARNING("bad subscript_x_size: %d", os2->subscript_x_size);
+    os2->subscript_x_size = 0;
+  }
+  if (os2->subscript_y_size < 0) {
+    OTS_WARNING("bad subscript_y_size: %d", os2->subscript_y_size);
+    os2->subscript_y_size = 0;
+  }
+  if (os2->superscript_x_size < 0) {
+    OTS_WARNING("bad superscript_x_size: %d", os2->superscript_x_size);
+    os2->superscript_x_size = 0;
+  }
+  if (os2->superscript_y_size < 0) {
+    OTS_WARNING("bad superscript_y_size: %d", os2->superscript_y_size);
+    os2->superscript_y_size = 0;
+  }
+  if (os2->strikeout_size < 0) {
+    OTS_WARNING("bad strikeout_size: %d", os2->strikeout_size);
+    os2->strikeout_size = 0;
+  }
+
+  for (unsigned i = 0; i < 10; ++i) {
+    if (!table.ReadU8(&os2->panose[i])) {
+      return OTS_FAILURE_MSG("Failed to read panose in os2 table");
+    }
+  }
+
+  if (!table.ReadU32(&os2->unicode_range_1) ||
+      !table.ReadU32(&os2->unicode_range_2) ||
+      !table.ReadU32(&os2->unicode_range_3) ||
+      !table.ReadU32(&os2->unicode_range_4) ||
+      !table.ReadU32(&os2->vendor_id) ||
+      !table.ReadU16(&os2->selection) ||
+      !table.ReadU16(&os2->first_char_index) ||
+      !table.ReadU16(&os2->last_char_index) ||
+      !table.ReadS16(&os2->typo_ascender) ||
+      !table.ReadS16(&os2->typo_descender) ||
+      !table.ReadS16(&os2->typo_linegap) ||
+      !table.ReadU16(&os2->win_ascent) ||
+      !table.ReadU16(&os2->win_descent)) {
+    return OTS_FAILURE_MSG("Failed to read more basic os2 fields");
+  }
+
+  // If bit 6 is set, then bits 0 and 5 must be clear.
+  if (os2->selection & 0x40) {
+    os2->selection &= 0xffdeu;
+  }
+
+  // the settings of bits 0 and 1 must be reflected in the macStyle bits
+  // in the 'head' table.
+  if (!file->head) {
+    return OTS_FAILURE_MSG("Head table missing from font as needed by os2 table");
+  }
+  if ((os2->selection & 0x1) &&
+      !(file->head->mac_style & 0x2)) {
+    OTS_WARNING("adjusting Mac style (italic)");
+    file->head->mac_style |= 0x2;
+  }
+  if ((os2->selection & 0x2) &&
+      !(file->head->mac_style & 0x4)) {
+    OTS_WARNING("adjusting Mac style (underscore)");
+    file->head->mac_style |= 0x4;
+  }
+
+  // While bit 6 on implies that bits 0 and 1 of macStyle are clear,
+  // the reverse is not true.
+  if ((os2->selection & 0x40) &&
+      (file->head->mac_style & 0x3)) {
+    OTS_WARNING("adjusting Mac style (regular)");
+    file->head->mac_style &= 0xfffcu;
+  }
+
+  if ((os2->version < 4) &&
+      (os2->selection & 0x300)) {
+    // bit 8 and 9 must be unset in OS/2 table versions less than 4.
+    return OTS_FAILURE_MSG("OS2 version %d incompatible with selection %d", os2->version, os2->selection);
+  }
+
+  // mask reserved bits. use only 0..9 bits.
+  os2->selection &= 0x3ff;
+
+  if (os2->first_char_index > os2->last_char_index) {
+    return OTS_FAILURE_MSG("first char index %d > last char index %d in os2", os2->first_char_index, os2->last_char_index);
+  }
+  if (os2->typo_linegap < 0) {
+    OTS_WARNING("bad linegap: %d", os2->typo_linegap);
+    os2->typo_linegap = 0;
+  }
+
+  if (os2->version < 1) {
+    // http://www.microsoft.com/typography/otspec/os2ver0.htm
+    return true;
+  }
+
+  if (length < offsetof(OpenTypeOS2, code_page_range_2)) {
+    OTS_WARNING("bad version number: %u", os2->version);
+    // Some fonts (e.g., kredit1.ttf and quinquef.ttf) have weird version
+    // numbers. Fix them.
+    os2->version = 0;
+    return true;
+  }
+
+  if (!table.ReadU32(&os2->code_page_range_1) ||
+      !table.ReadU32(&os2->code_page_range_2)) {
+    return OTS_FAILURE_MSG("Failed to read codepage ranges");
+  }
+
+  if (os2->version < 2) {
+    // http://www.microsoft.com/typography/otspec/os2ver1.htm
+    return true;
+  }
+
+  if (length < offsetof(OpenTypeOS2, max_context)) {
+    OTS_WARNING("bad version number: %u", os2->version);
+    // some Japanese fonts (e.g., mona.ttf) have weird version number.
+    // fix them.
+    os2->version = 1;
+    return true;
+  }
+
+  if (!table.ReadS16(&os2->x_height) ||
+      !table.ReadS16(&os2->cap_height) ||
+      !table.ReadU16(&os2->default_char) ||
+      !table.ReadU16(&os2->break_char) ||
+      !table.ReadU16(&os2->max_context)) {
+    return OTS_FAILURE_MSG("Failed to read os2 version 2 information");
+  }
+
+  if (os2->x_height < 0) {
+    OTS_WARNING("bad x_height: %d", os2->x_height);
+    os2->x_height = 0;
+  }
+  if (os2->cap_height < 0) {
+    OTS_WARNING("bad cap_height: %d", os2->cap_height);
+    os2->cap_height = 0;
+  }
+
+  return true;
+}
+
+bool ots_os2_should_serialise(OpenTypeFile *file) {
+  return file->os2 != NULL;
+}
+
+bool ots_os2_serialise(OTSStream *out, OpenTypeFile *file) {
+  const OpenTypeOS2 *os2 = file->os2;
+
+  if (!out->WriteU16(os2->version) ||
+      !out->WriteS16(os2->avg_char_width) ||
+      !out->WriteU16(os2->weight_class) ||
+      !out->WriteU16(os2->width_class) ||
+      !out->WriteU16(os2->type) ||
+      !out->WriteS16(os2->subscript_x_size) ||
+      !out->WriteS16(os2->subscript_y_size) ||
+      !out->WriteS16(os2->subscript_x_offset) ||
+      !out->WriteS16(os2->subscript_y_offset) ||
+      !out->WriteS16(os2->superscript_x_size) ||
+      !out->WriteS16(os2->superscript_y_size) ||
+      !out->WriteS16(os2->superscript_x_offset) ||
+      !out->WriteS16(os2->superscript_y_offset) ||
+      !out->WriteS16(os2->strikeout_size) ||
+      !out->WriteS16(os2->strikeout_position) ||
+      !out->WriteS16(os2->family_class)) {
+    return OTS_FAILURE_MSG("Failed to write basic OS2 information");
+  }
+
+  for (unsigned i = 0; i < 10; ++i) {
+    if (!out->Write(&os2->panose[i], 1)) {
+      return OTS_FAILURE_MSG("Failed to write os2 panose information");
+    }
+  }
+
+  if (!out->WriteU32(os2->unicode_range_1) ||
+      !out->WriteU32(os2->unicode_range_2) ||
+      !out->WriteU32(os2->unicode_range_3) ||
+      !out->WriteU32(os2->unicode_range_4) ||
+      !out->WriteU32(os2->vendor_id) ||
+      !out->WriteU16(os2->selection) ||
+      !out->WriteU16(os2->first_char_index) ||
+      !out->WriteU16(os2->last_char_index) ||
+      !out->WriteS16(os2->typo_ascender) ||
+      !out->WriteS16(os2->typo_descender) ||
+      !out->WriteS16(os2->typo_linegap) ||
+      !out->WriteU16(os2->win_ascent) ||
+      !out->WriteU16(os2->win_descent)) {
+    return OTS_FAILURE_MSG("Failed to write os2 version 1 information");
+  }
+
+  if (os2->version < 1) {
+    return true;
+  }
+
+  if (!out->WriteU32(os2->code_page_range_1) ||
+      !out->WriteU32(os2->code_page_range_2)) {
+    return OTS_FAILURE_MSG("Failed to write codepage ranges");
+  }
+
+  if (os2->version < 2) {
+    return true;
+  }
+
+  if (!out->WriteS16(os2->x_height) ||
+      !out->WriteS16(os2->cap_height) ||
+      !out->WriteU16(os2->default_char) ||
+      !out->WriteU16(os2->break_char) ||
+      !out->WriteU16(os2->max_context)) {
+    return OTS_FAILURE_MSG("Failed to write os2 version 2 information");
+  }
+
+  return true;
+}
+
+void ots_os2_free(OpenTypeFile *file) {
+  delete file->os2;
+}
+
+}  // namespace ots
+
+#undef TABLE_NAME
diff --git a/third_party/ots/src/os2.h b/third_party/ots/src/os2.h
new file mode 100644
index 0000000..9e0fc347
--- /dev/null
+++ b/third_party/ots/src/os2.h
@@ -0,0 +1,54 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_OS2_H_
+#define OTS_OS2_H_
+
+#include "ots.h"
+
+namespace ots {
+
+struct OpenTypeOS2 {
+  uint16_t version;
+  int16_t avg_char_width;
+  uint16_t weight_class;
+  uint16_t width_class;
+  uint16_t type;
+  int16_t subscript_x_size;
+  int16_t subscript_y_size;
+  int16_t subscript_x_offset;
+  int16_t subscript_y_offset;
+  int16_t superscript_x_size;
+  int16_t superscript_y_size;
+  int16_t superscript_x_offset;
+  int16_t superscript_y_offset;
+  int16_t strikeout_size;
+  int16_t strikeout_position;
+  int16_t family_class;
+  uint8_t panose[10];
+  uint32_t unicode_range_1;
+  uint32_t unicode_range_2;
+  uint32_t unicode_range_3;
+  uint32_t unicode_range_4;
+  uint32_t vendor_id;
+  uint16_t selection;
+  uint16_t first_char_index;
+  uint16_t last_char_index;
+  int16_t typo_ascender;
+  int16_t typo_descender;
+  int16_t typo_linegap;
+  uint16_t win_ascent;
+  uint16_t win_descent;
+  uint32_t code_page_range_1;
+  uint32_t code_page_range_2;
+  int16_t x_height;
+  int16_t cap_height;
+  uint16_t default_char;
+  uint16_t break_char;
+  uint16_t max_context;
+};
+
+}  // namespace ots
+
+#endif  // OTS_OS2_H_
diff --git a/third_party/ots/src/ots.cc b/third_party/ots/src/ots.cc
new file mode 100644
index 0000000..197c649
--- /dev/null
+++ b/third_party/ots/src/ots.cc
@@ -0,0 +1,824 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ots.h"
+
+#include <sys/types.h>
+#include <zlib.h>
+
+#include <algorithm>
+#include <cstdlib>
+#include <cstring>
+#include <limits>
+#include <map>
+#include <vector>
+
+#include "woff2.h"
+
+// The OpenType Font File
+// http://www.microsoft.com/typography/otspec/cmap.htm
+
+namespace {
+
+// Generate a message with or without a table tag, when 'header' is the OpenTypeFile pointer
+#define OTS_FAILURE_MSG_TAG(msg_,tag_) OTS_FAILURE_MSG_TAG_(header, msg_, tag_)
+#define OTS_FAILURE_MSG_HDR(msg_)      OTS_FAILURE_MSG_(header, msg_)
+
+
+struct OpenTypeTable {
+  uint32_t tag;
+  uint32_t chksum;
+  uint32_t offset;
+  uint32_t length;
+  uint32_t uncompressed_length;
+};
+
+bool CheckTag(uint32_t tag_value) {
+  for (unsigned i = 0; i < 4; ++i) {
+    const uint32_t check = tag_value & 0xff;
+    if (check < 32 || check > 126) {
+      return false;  // non-ASCII character found.
+    }
+    tag_value >>= 8;
+  }
+  return true;
+}
+
+uint32_t Tag(const char *tag_str) {	
+  uint32_t ret;	
+  std::memcpy(&ret, tag_str, 4);	
+  return ret;	
+}
+
+struct OutputTable {
+  uint32_t tag;
+  size_t offset;
+  size_t length;
+  uint32_t chksum;
+
+  static bool SortByTag(const OutputTable& a, const OutputTable& b) {
+    const uint32_t atag = ntohl(a.tag);
+    const uint32_t btag = ntohl(b.tag);
+    return atag < btag;
+  }
+};
+
+struct Arena {
+ public:
+  ~Arena() {
+    for (std::vector<uint8_t*>::iterator
+         i = hunks_.begin(); i != hunks_.end(); ++i) {
+      delete[] *i;
+    }
+  }
+
+  uint8_t* Allocate(size_t length) {
+    uint8_t* p = new uint8_t[length];
+    hunks_.push_back(p);
+    return p;
+  }
+
+ private:
+  std::vector<uint8_t*> hunks_;
+};
+
+const struct {
+  const char* tag;
+  bool (*parse)(ots::OpenTypeFile *otf, const uint8_t *data, size_t length);
+  bool (*serialise)(ots::OTSStream *out, ots::OpenTypeFile *file);
+  bool (*should_serialise)(ots::OpenTypeFile *file);
+  void (*free)(ots::OpenTypeFile *file);
+  bool required;
+} table_parsers[] = {
+  { "maxp", ots::ots_maxp_parse, ots::ots_maxp_serialise,
+    ots::ots_maxp_should_serialise, ots::ots_maxp_free, true },
+  { "head", ots::ots_head_parse, ots::ots_head_serialise,
+    ots::ots_head_should_serialise, ots::ots_head_free, true },
+  { "OS/2", ots::ots_os2_parse, ots::ots_os2_serialise,
+    ots::ots_os2_should_serialise, ots::ots_os2_free, true },
+  { "cmap", ots::ots_cmap_parse, ots::ots_cmap_serialise,
+    ots::ots_cmap_should_serialise, ots::ots_cmap_free, true },
+  { "hhea", ots::ots_hhea_parse, ots::ots_hhea_serialise,
+    ots::ots_hhea_should_serialise, ots::ots_hhea_free, true },
+  { "hmtx", ots::ots_hmtx_parse, ots::ots_hmtx_serialise,
+    ots::ots_hmtx_should_serialise, ots::ots_hmtx_free, true },
+  { "name", ots::ots_name_parse, ots::ots_name_serialise,
+    ots::ots_name_should_serialise, ots::ots_name_free, true },
+  { "post", ots::ots_post_parse, ots::ots_post_serialise,
+    ots::ots_post_should_serialise, ots::ots_post_free, true },
+  { "loca", ots::ots_loca_parse, ots::ots_loca_serialise,
+    ots::ots_loca_should_serialise, ots::ots_loca_free, false },
+  { "glyf", ots::ots_glyf_parse, ots::ots_glyf_serialise,
+    ots::ots_glyf_should_serialise, ots::ots_glyf_free, false },
+  { "CFF ", ots::ots_cff_parse, ots::ots_cff_serialise,
+    ots::ots_cff_should_serialise, ots::ots_cff_free, false },
+  { "VDMX", ots::ots_vdmx_parse, ots::ots_vdmx_serialise,
+    ots::ots_vdmx_should_serialise, ots::ots_vdmx_free, false },
+  { "hdmx", ots::ots_hdmx_parse, ots::ots_hdmx_serialise,
+    ots::ots_hdmx_should_serialise, ots::ots_hdmx_free, false },
+  { "gasp", ots::ots_gasp_parse, ots::ots_gasp_serialise,
+    ots::ots_gasp_should_serialise, ots::ots_gasp_free, false },
+  { "cvt ", ots::ots_cvt_parse, ots::ots_cvt_serialise,
+    ots::ots_cvt_should_serialise, ots::ots_cvt_free, false },
+  { "fpgm", ots::ots_fpgm_parse, ots::ots_fpgm_serialise,
+    ots::ots_fpgm_should_serialise, ots::ots_fpgm_free, false },
+  { "prep", ots::ots_prep_parse, ots::ots_prep_serialise,
+    ots::ots_prep_should_serialise, ots::ots_prep_free, false },
+  { "LTSH", ots::ots_ltsh_parse, ots::ots_ltsh_serialise,
+    ots::ots_ltsh_should_serialise, ots::ots_ltsh_free, false },
+  { "VORG", ots::ots_vorg_parse, ots::ots_vorg_serialise,
+    ots::ots_vorg_should_serialise, ots::ots_vorg_free, false },
+  { "kern", ots::ots_kern_parse, ots::ots_kern_serialise,
+    ots::ots_kern_should_serialise, ots::ots_kern_free, false },
+  // We need to parse GDEF table in advance of parsing GSUB/GPOS tables
+  // because they could refer GDEF table.
+  { "GDEF", ots::ots_gdef_parse, ots::ots_gdef_serialise,
+    ots::ots_gdef_should_serialise, ots::ots_gdef_free, false },
+  { "GPOS", ots::ots_gpos_parse, ots::ots_gpos_serialise,
+    ots::ots_gpos_should_serialise, ots::ots_gpos_free, false },
+  { "GSUB", ots::ots_gsub_parse, ots::ots_gsub_serialise,
+    ots::ots_gsub_should_serialise, ots::ots_gsub_free, false },
+  { "vhea", ots::ots_vhea_parse, ots::ots_vhea_serialise,
+    ots::ots_vhea_should_serialise, ots::ots_vhea_free, false },
+  { "vmtx", ots::ots_vmtx_parse, ots::ots_vmtx_serialise,
+    ots::ots_vmtx_should_serialise, ots::ots_vmtx_free, false },
+  { "MATH", ots::ots_math_parse, ots::ots_math_serialise,
+    ots::ots_math_should_serialise, ots::ots_math_free, false },
+  // TODO(bashi): Support mort, base, and jstf tables.
+  { 0, NULL, NULL, NULL, NULL, false },
+};
+
+bool ProcessGeneric(ots::OpenTypeFile *header,
+                    uint32_t signature,
+                    ots::OTSStream *output,
+                    const uint8_t *data, size_t length,
+                    const std::vector<OpenTypeTable>& tables,
+                    ots::Buffer& file);
+
+bool ProcessTTF(ots::OpenTypeFile *header,
+                ots::OTSStream *output, const uint8_t *data, size_t length) {
+  ots::Buffer file(data, length);
+
+  // we disallow all files > 1GB in size for sanity.
+  if (length > 1024 * 1024 * 1024) {
+    return OTS_FAILURE_MSG_HDR("file exceeds 1GB");
+  }
+
+  if (!file.ReadTag(&header->version)) {
+    return OTS_FAILURE_MSG_HDR("error reading version tag");
+  }
+  if (!ots::IsValidVersionTag(header->version)) {
+      return OTS_FAILURE_MSG_HDR("invalid version tag");
+  }
+
+  if (!file.ReadU16(&header->num_tables) ||
+      !file.ReadU16(&header->search_range) ||
+      !file.ReadU16(&header->entry_selector) ||
+      !file.ReadU16(&header->range_shift)) {
+    return OTS_FAILURE_MSG_HDR("error reading table directory search header");
+  }
+
+  // search_range is (Maximum power of 2 <= numTables) x 16. Thus, to avoid
+  // overflow num_tables is, at most, 2^16 / 16 = 2^12
+  if (header->num_tables >= 4096 || header->num_tables < 1) {
+    return OTS_FAILURE_MSG_HDR("excessive (or zero) number of tables");
+  }
+
+  unsigned max_pow2 = 0;
+  while (1u << (max_pow2 + 1) <= header->num_tables) {
+    max_pow2++;
+  }
+  const uint16_t expected_search_range = (1u << max_pow2) << 4;
+
+  // Don't call ots_failure() here since ~25% of fonts (250+ fonts) in
+  // http://www.princexml.com/fonts/ have unexpected search_range value.
+  if (header->search_range != expected_search_range) {
+    OTS_FAILURE_MSG_HDR("bad search range");
+    header->search_range = expected_search_range;  // Fix the value.
+  }
+
+  // entry_selector is Log2(maximum power of 2 <= numTables)
+  if (header->entry_selector != max_pow2) {
+    return OTS_FAILURE_MSG_HDR("incorrect entrySelector for table directory");
+  }
+
+  // range_shift is NumTables x 16-searchRange. We know that 16*num_tables
+  // doesn't over flow because we range checked it above. Also, we know that
+  // it's > header->search_range by construction of search_range.
+  const uint16_t expected_range_shift =
+      16 * header->num_tables - header->search_range;
+  if (header->range_shift != expected_range_shift) {
+    OTS_FAILURE_MSG_HDR("bad range shift");
+    header->range_shift = expected_range_shift;  // the same as above.
+  }
+
+  // Next up is the list of tables.
+  std::vector<OpenTypeTable> tables;
+
+  for (unsigned i = 0; i < header->num_tables; ++i) {
+    OpenTypeTable table;
+    if (!file.ReadTag(&table.tag) ||
+        !file.ReadU32(&table.chksum) ||
+        !file.ReadU32(&table.offset) ||
+        !file.ReadU32(&table.length)) {
+      return OTS_FAILURE_MSG_HDR("error reading table directory");
+    }
+
+    table.uncompressed_length = table.length;
+    tables.push_back(table);
+  }
+
+  return ProcessGeneric(header, header->version, output, data, length,
+                        tables, file);
+}
+
+bool ProcessWOFF(ots::OpenTypeFile *header,
+                 ots::OTSStream *output, const uint8_t *data, size_t length) {
+  ots::Buffer file(data, length);
+
+  // we disallow all files > 1GB in size for sanity.
+  if (length > 1024 * 1024 * 1024) {
+    return OTS_FAILURE_MSG_HDR("file exceeds 1GB");
+  }
+
+  uint32_t woff_tag;
+  if (!file.ReadTag(&woff_tag)) {
+    return OTS_FAILURE_MSG_HDR("error reading WOFF marker");
+  }
+
+  if (woff_tag != Tag("wOFF")) {
+    return OTS_FAILURE_MSG_HDR("invalid WOFF marker");
+  }
+
+  if (!file.ReadTag(&header->version)) {
+    return OTS_FAILURE_MSG_HDR("error reading version tag");
+  }
+  if (!ots::IsValidVersionTag(header->version)) {
+    return OTS_FAILURE_MSG_HDR("invalid version tag");
+  }
+
+  header->search_range = 0;
+  header->entry_selector = 0;
+  header->range_shift = 0;
+
+  uint32_t reported_length;
+  if (!file.ReadU32(&reported_length) || length != reported_length) {
+    return OTS_FAILURE_MSG_HDR("incorrect file size in WOFF header");
+  }
+
+  if (!file.ReadU16(&header->num_tables) || !header->num_tables) {
+    return OTS_FAILURE_MSG_HDR("error reading number of tables");
+  }
+
+  uint16_t reserved_value;
+  if (!file.ReadU16(&reserved_value) || reserved_value) {
+    return OTS_FAILURE_MSG_HDR("error in reserved field of WOFF header");
+  }
+
+  uint32_t reported_total_sfnt_size;
+  if (!file.ReadU32(&reported_total_sfnt_size)) {
+    return OTS_FAILURE_MSG_HDR("error reading total sfnt size");
+  }
+
+  // We don't care about these fields of the header:
+  //   uint16_t major_version, minor_version
+  if (!file.Skip(2 * 2)) {
+    return OTS_FAILURE_MSG_HDR("error skipping WOFF header fields");
+  }
+
+  // Checks metadata block size.
+  uint32_t meta_offset;
+  uint32_t meta_length;
+  uint32_t meta_length_orig;
+  if (!file.ReadU32(&meta_offset) ||
+      !file.ReadU32(&meta_length) ||
+      !file.ReadU32(&meta_length_orig)) {
+    return OTS_FAILURE_MSG_HDR("error reading WOFF header fields");
+  }
+  if (meta_offset) {
+    if (meta_offset >= length || length - meta_offset < meta_length) {
+      return OTS_FAILURE_MSG_HDR("invalid metadata block location/size");
+    }
+  }
+
+  // Checks private data block size.
+  uint32_t priv_offset;
+  uint32_t priv_length;
+  if (!file.ReadU32(&priv_offset) ||
+      !file.ReadU32(&priv_length)) {
+    return OTS_FAILURE_MSG_HDR("error reading WOFF header fields");
+  }
+  if (priv_offset) {
+    if (priv_offset >= length || length - priv_offset < priv_length) {
+      return OTS_FAILURE_MSG_HDR("invalid private block location/size");
+    }
+  }
+
+  // Next up is the list of tables.
+  std::vector<OpenTypeTable> tables;
+
+  uint32_t first_index = 0;
+  uint32_t last_index = 0;
+  // Size of sfnt header plus size of table records.
+  uint64_t total_sfnt_size = 12 + 16 * header->num_tables;
+  for (unsigned i = 0; i < header->num_tables; ++i) {
+    OpenTypeTable table;
+    if (!file.ReadTag(&table.tag) ||
+        !file.ReadU32(&table.offset) ||
+        !file.ReadU32(&table.length) ||
+        !file.ReadU32(&table.uncompressed_length) ||
+        !file.ReadU32(&table.chksum)) {
+      return OTS_FAILURE_MSG_HDR("error reading table directory");
+    }
+
+    total_sfnt_size += ots::Round4(table.uncompressed_length);
+    if (total_sfnt_size > std::numeric_limits<uint32_t>::max()) {
+      return OTS_FAILURE_MSG_HDR("sfnt size overflow");
+    }
+    tables.push_back(table);
+    if (i == 0 || tables[first_index].offset > table.offset)
+      first_index = i;
+    if (i == 0 || tables[last_index].offset < table.offset)
+      last_index = i;
+  }
+
+  if (reported_total_sfnt_size != total_sfnt_size) {
+    return OTS_FAILURE_MSG_HDR("uncompressed sfnt size mismatch");
+  }
+
+  // Table data must follow immediately after the header.
+  if (tables[first_index].offset != ots::Round4(file.offset())) {
+    return OTS_FAILURE_MSG_HDR("junk before tables in WOFF file");
+  }
+
+  if (tables[last_index].offset >= length ||
+      length - tables[last_index].offset < tables[last_index].length) {
+    return OTS_FAILURE_MSG_HDR("invalid table location/size");
+  }
+  // Blocks must follow immediately after the previous block.
+  // (Except for padding with a maximum of three null bytes)
+  uint64_t block_end = ots::Round4(
+      static_cast<uint64_t>(tables[last_index].offset) +
+      static_cast<uint64_t>(tables[last_index].length));
+  if (block_end > std::numeric_limits<uint32_t>::max()) {
+    return OTS_FAILURE_MSG_HDR("invalid table location/size");
+  }
+  if (meta_offset) {
+    if (block_end != meta_offset) {
+      return OTS_FAILURE_MSG_HDR("invalid metadata block location");
+    }
+    block_end = ots::Round4(static_cast<uint64_t>(meta_offset) +
+                            static_cast<uint64_t>(meta_length));
+    if (block_end > std::numeric_limits<uint32_t>::max()) {
+      return OTS_FAILURE_MSG_HDR("invalid metadata block size");
+    }
+  }
+  if (priv_offset) {
+    if (block_end != priv_offset) {
+      return OTS_FAILURE_MSG_HDR("invalid private block location");
+    }
+    block_end = ots::Round4(static_cast<uint64_t>(priv_offset) +
+                            static_cast<uint64_t>(priv_length));
+    if (block_end > std::numeric_limits<uint32_t>::max()) {
+      return OTS_FAILURE_MSG_HDR("invalid private block size");
+    }
+  }
+  if (block_end != ots::Round4(length)) {
+    return OTS_FAILURE_MSG_HDR("file length mismatch (trailing junk?)");
+  }
+
+  return ProcessGeneric(header, woff_tag, output, data, length, tables, file);
+}
+
+bool ProcessWOFF2(ots::OpenTypeFile *header,
+                  ots::OTSStream *output, const uint8_t *data, size_t length) {
+  size_t decompressed_size = ots::ComputeWOFF2FinalSize(data, length);
+  if (decompressed_size == 0) {
+    return OTS_FAILURE();
+  }
+  // decompressed font must be <= 30MB
+  if (decompressed_size > 30 * 1024 * 1024) {
+    return OTS_FAILURE();
+  }
+
+  std::vector<uint8_t> decompressed_buffer(decompressed_size);
+  if (!ots::ConvertWOFF2ToTTF(header, &decompressed_buffer[0], decompressed_size,
+                              data, length)) {
+    return OTS_FAILURE();
+  }
+  return ProcessTTF(header, output, &decompressed_buffer[0], decompressed_size);
+}
+
+ots::TableAction GetTableAction(ots::OpenTypeFile *header, uint32_t tag) {
+  ots::TableAction action = ots::TABLE_ACTION_DEFAULT;
+
+  action = header->context->GetTableAction(htonl(tag));
+
+  if (action == ots::TABLE_ACTION_DEFAULT) {
+    action = ots::TABLE_ACTION_DROP;
+
+    for (unsigned i = 0; ; ++i) {
+      if (table_parsers[i].parse == NULL) break;
+
+      if (Tag(table_parsers[i].tag) == tag) {
+        action = ots::TABLE_ACTION_SANITIZE;
+        break;
+      }
+    }
+  }
+
+  assert(action != ots::TABLE_ACTION_DEFAULT); // Should never return this.
+  return action;
+}
+
+bool GetTableData(const uint8_t *data,
+                  const OpenTypeTable table,
+                  Arena *arena,
+                  size_t *table_length,
+                  const uint8_t **table_data) {
+  if (table.uncompressed_length != table.length) {
+    // Compressed table. Need to uncompress into memory first.
+    *table_length = table.uncompressed_length;
+    *table_data = (*arena).Allocate(*table_length);
+    uLongf dest_len = *table_length;
+    int r = uncompress((Bytef*) *table_data, &dest_len,
+                       data + table.offset, table.length);
+    if (r != Z_OK || dest_len != *table_length) {
+      return false;
+    }
+  } else {
+    // Uncompressed table. We can process directly from memory.
+    *table_data = data + table.offset;
+    *table_length = table.length;
+  }
+
+  return true;
+}
+
+bool ProcessGeneric(ots::OpenTypeFile *header, uint32_t signature,
+                    ots::OTSStream *output,
+                    const uint8_t *data, size_t length,
+                    const std::vector<OpenTypeTable>& tables,
+                    ots::Buffer& file) {
+  const size_t data_offset = file.offset();
+
+  uint32_t uncompressed_sum = 0;
+
+  for (unsigned i = 0; i < header->num_tables; ++i) {
+    // the tables must be sorted by tag (when taken as big-endian numbers).
+    // This also remove the possibility of duplicate tables.
+    if (i) {
+      const uint32_t this_tag = ntohl(tables[i].tag);
+      const uint32_t prev_tag = ntohl(tables[i - 1].tag);
+      if (this_tag <= prev_tag) {
+        return OTS_FAILURE_MSG_HDR("table directory not correctly ordered");
+      }
+    }
+
+    // all tag names must be built from printable ASCII characters
+    if (!CheckTag(tables[i].tag)) {
+      return OTS_FAILURE_MSG_TAG("invalid table tag", &tables[i].tag);
+    }
+
+    // tables must be 4-byte aligned
+    if (tables[i].offset & 3) {
+      return OTS_FAILURE_MSG_TAG("misaligned table", &tables[i].tag);
+    }
+
+    // and must be within the file
+    if (tables[i].offset < data_offset || tables[i].offset >= length) {
+      return OTS_FAILURE_MSG_TAG("invalid table offset", &tables[i].tag);
+    }
+    // disallow all tables with a zero length
+    if (tables[i].length < 1) {
+      // Note: malayalam.ttf has zero length CVT table...
+      return OTS_FAILURE_MSG_TAG("zero-length table", &tables[i].tag);
+    }
+    // disallow all tables with a length > 1GB
+    if (tables[i].length > 1024 * 1024 * 1024) {
+      return OTS_FAILURE_MSG_TAG("table length exceeds 1GB", &tables[i].tag);
+    }
+    // disallow tables where the uncompressed size is < the compressed size.
+    if (tables[i].uncompressed_length < tables[i].length) {
+      return OTS_FAILURE_MSG_TAG("invalid compressed table", &tables[i].tag);
+    }
+    if (tables[i].uncompressed_length > tables[i].length) {
+      // We'll probably be decompressing this table.
+
+      // disallow all tables which uncompress to > 30 MB
+      if (tables[i].uncompressed_length > 30 * 1024 * 1024) {
+        return OTS_FAILURE_MSG_TAG("uncompressed length exceeds 30MB", &tables[i].tag);
+      }
+      if (uncompressed_sum + tables[i].uncompressed_length < uncompressed_sum) {
+        return OTS_FAILURE_MSG_TAG("overflow of uncompressed sum", &tables[i].tag);
+      }
+
+      uncompressed_sum += tables[i].uncompressed_length;
+    }
+    // since we required that the file be < 1GB in length, and that the table
+    // length is < 1GB, the following addtion doesn't overflow
+    uint32_t end_byte = tables[i].offset + tables[i].length;
+    // Tables in the WOFF file must be aligned 4-byte boundary.
+    if (signature == Tag("wOFF")) {
+        end_byte = ots::Round4(end_byte);
+    }
+    if (!end_byte || end_byte > length) {
+      return OTS_FAILURE_MSG_TAG("table overruns end of file", &tables[i].tag);
+    }
+  }
+
+  // All decompressed tables uncompressed must be <= 30MB.
+  if (uncompressed_sum > 30 * 1024 * 1024) {
+    return OTS_FAILURE_MSG_HDR("uncompressed sum exceeds 30MB");
+  }
+
+  std::map<uint32_t, OpenTypeTable> table_map;
+  for (unsigned i = 0; i < header->num_tables; ++i) {
+    table_map[tables[i].tag] = tables[i];
+  }
+
+  // check that the tables are not overlapping.
+  std::vector<std::pair<uint32_t, uint8_t> > overlap_checker;
+  for (unsigned i = 0; i < header->num_tables; ++i) {
+    overlap_checker.push_back(
+        std::make_pair(tables[i].offset, static_cast<uint8_t>(1) /* start */));
+    overlap_checker.push_back(
+        std::make_pair(tables[i].offset + tables[i].length,
+                       static_cast<uint8_t>(0) /* end */));
+  }
+  std::sort(overlap_checker.begin(), overlap_checker.end());
+  int overlap_count = 0;
+  for (unsigned i = 0; i < overlap_checker.size(); ++i) {
+    overlap_count += (overlap_checker[i].second ? 1 : -1);
+    if (overlap_count > 1) {
+      return OTS_FAILURE_MSG_HDR("overlapping tables");
+    }
+  }
+
+  Arena arena;
+
+  for (unsigned i = 0; ; ++i) {
+    if (table_parsers[i].parse == NULL) break;
+
+    uint32_t tag = Tag(table_parsers[i].tag);
+    const std::map<uint32_t, OpenTypeTable>::const_iterator it = table_map.find(tag);
+
+    ots::TableAction action = GetTableAction(header, tag);
+    if (it == table_map.end()) {
+      if (table_parsers[i].required && action == ots::TABLE_ACTION_SANITIZE) {
+        return OTS_FAILURE_MSG_TAG("missing required table", table_parsers[i].tag);
+      }
+      continue;
+    }
+
+    const uint8_t* table_data;
+    size_t table_length;
+
+    if (!GetTableData(data, it->second, &arena, &table_length, &table_data)) {
+      return OTS_FAILURE_MSG_TAG("uncompress failed", table_parsers[i].tag);
+    }
+
+    if (action == ots::TABLE_ACTION_SANITIZE &&
+        !table_parsers[i].parse(header, table_data, table_length)) {
+      // TODO: parsers should generate specific messages detailing the failure;
+      // once those are all added, we won't need a generic failure message here
+      return OTS_FAILURE_MSG_TAG("failed to parse table", table_parsers[i].tag);
+    }
+  }
+
+  if (header->cff) {
+    // font with PostScript glyph
+    if (header->version != Tag("OTTO")) {
+      return OTS_FAILURE_MSG_HDR("wrong font version for PostScript glyph data");
+    }
+    if (header->glyf || header->loca) {
+      // mixing outline formats is not recommended
+      return OTS_FAILURE_MSG_HDR("font contains both PS and TT glyphs");
+    }
+  } else {
+    if (!header->glyf || !header->loca) {
+      // No TrueType glyph found.
+      // Note: bitmap-only fonts are not supported.
+      return OTS_FAILURE_MSG_HDR("neither PS nor TT glyphs present");
+    }
+  }
+
+  uint16_t num_output_tables = 0;
+  for (unsigned i = 0; ; ++i) {
+    if (table_parsers[i].parse == NULL) {
+      break;
+    }
+
+    if (table_parsers[i].should_serialise(header)) {
+      num_output_tables++;
+    }
+  }
+
+  for (std::map<uint32_t, OpenTypeTable>::const_iterator it = table_map.begin();
+       it != table_map.end(); ++it) {
+    ots::TableAction action = GetTableAction(header, it->first);
+    if (action == ots::TABLE_ACTION_PASSTHRU) {
+      num_output_tables++;
+    }
+  }
+
+  uint16_t max_pow2 = 0;
+  while (1u << (max_pow2 + 1) <= num_output_tables) {
+    max_pow2++;
+  }
+  const uint16_t output_search_range = (1u << max_pow2) << 4;
+
+  // most of the errors here are highly unlikely - they'd only occur if the
+  // output stream returns a failure, e.g. lack of space to write
+  output->ResetChecksum();
+  if (!output->WriteTag(header->version) ||
+      !output->WriteU16(num_output_tables) ||
+      !output->WriteU16(output_search_range) ||
+      !output->WriteU16(max_pow2) ||
+      !output->WriteU16((num_output_tables << 4) - output_search_range)) {
+    return OTS_FAILURE_MSG_HDR("error writing output");
+  }
+  const uint32_t offset_table_chksum = output->chksum();
+
+  const size_t table_record_offset = output->Tell();
+  if (!output->Pad(16 * num_output_tables)) {
+    return OTS_FAILURE_MSG_HDR("error writing output");
+  }
+
+  std::vector<OutputTable> out_tables;
+
+  size_t head_table_offset = 0;
+  for (unsigned i = 0; ; ++i) {
+    if (table_parsers[i].parse == NULL) {
+      break;
+    }
+
+    if (!table_parsers[i].should_serialise(header)) {
+      continue;
+    }
+
+    OutputTable out;
+    uint32_t tag = Tag(table_parsers[i].tag);
+    out.tag = tag;
+    out.offset = output->Tell();
+
+    output->ResetChecksum();
+    if (tag == Tag("head")) {
+      head_table_offset = out.offset;
+    }
+    if (!table_parsers[i].serialise(output, header)) {
+      return OTS_FAILURE_MSG_TAG("failed to serialize table", table_parsers[i].tag);
+    }
+
+    const size_t end_offset = output->Tell();
+    if (end_offset <= out.offset) {
+      // paranoid check. |end_offset| is supposed to be greater than the offset,
+      // as long as the Tell() interface is implemented correctly.
+      return OTS_FAILURE_MSG_HDR("error writing output");
+    }
+    out.length = end_offset - out.offset;
+
+    // align tables to four bytes
+    if (!output->Pad((4 - (end_offset & 3)) % 4)) {
+      return OTS_FAILURE_MSG_HDR("error writing output");
+    }
+    out.chksum = output->chksum();
+    out_tables.push_back(out);
+  }
+
+  for (std::map<uint32_t, OpenTypeTable>::const_iterator it = table_map.begin();
+       it != table_map.end(); ++it) {
+    ots::TableAction action = GetTableAction(header, it->first);
+    if (action == ots::TABLE_ACTION_PASSTHRU) {
+      OutputTable out;
+      out.tag = it->second.tag;
+      out.offset = output->Tell();
+
+      output->ResetChecksum();
+      if (it->second.tag == Tag("head")) {
+        head_table_offset = out.offset;
+      }
+
+      const uint8_t* table_data;
+      size_t table_length;
+
+      if (!GetTableData(data, it->second, &arena, &table_length, &table_data)) {
+        return OTS_FAILURE_MSG_HDR("Failed to uncompress table");
+      }
+
+      if (!output->Write(table_data, table_length)) {
+        return OTS_FAILURE_MSG_HDR("Failed to serialize table");
+      }
+
+      const size_t end_offset = output->Tell();
+      if (end_offset <= out.offset) {
+        // paranoid check. |end_offset| is supposed to be greater than the offset,
+        // as long as the Tell() interface is implemented correctly.
+        return OTS_FAILURE_MSG_HDR("error writing output");
+      }
+      out.length = end_offset - out.offset;
+
+      // align tables to four bytes
+      if (!output->Pad((4 - (end_offset & 3)) % 4)) {
+        return OTS_FAILURE_MSG_HDR("error writing output");
+      }
+      out.chksum = output->chksum();
+      out_tables.push_back(out);
+    }
+  }
+
+  const size_t end_of_file = output->Tell();
+
+  // Need to sort the output tables for inclusion in the file
+  std::sort(out_tables.begin(), out_tables.end(), OutputTable::SortByTag);
+  if (!output->Seek(table_record_offset)) {
+    return OTS_FAILURE_MSG_HDR("error writing output");
+  }
+
+  output->ResetChecksum();
+  uint32_t tables_chksum = 0;
+  for (unsigned i = 0; i < out_tables.size(); ++i) {
+    if (!output->WriteTag(out_tables[i].tag) ||
+        !output->WriteU32(out_tables[i].chksum) ||
+        !output->WriteU32(out_tables[i].offset) ||
+        !output->WriteU32(out_tables[i].length)) {
+      return OTS_FAILURE_MSG_HDR("error writing output");
+    }
+    tables_chksum += out_tables[i].chksum;
+  }
+  const uint32_t table_record_chksum = output->chksum();
+
+  // http://www.microsoft.com/typography/otspec/otff.htm
+  const uint32_t file_chksum
+      = offset_table_chksum + tables_chksum + table_record_chksum;
+  const uint32_t chksum_magic = static_cast<uint32_t>(0xb1b0afba) - file_chksum;
+
+  // seek into the 'head' table and write in the checksum magic value
+  if (!head_table_offset) {
+    return OTS_FAILURE_MSG_HDR("internal error!");
+  }
+  if (!output->Seek(head_table_offset + 8)) {
+    return OTS_FAILURE_MSG_HDR("error writing output");
+  }
+  if (!output->WriteU32(chksum_magic)) {
+    return OTS_FAILURE_MSG_HDR("error writing output");
+  }
+
+  if (!output->Seek(end_of_file)) {
+    return OTS_FAILURE_MSG_HDR("error writing output");
+  }
+
+  return true;
+}
+
+}  // namespace
+
+namespace ots {
+
+void EnableWOFF2() {
+}
+
+bool IsValidVersionTag(uint32_t tag) {
+  return tag == Tag("\x00\x01\x00\x00") ||
+         // OpenType fonts with CFF data have 'OTTO' tag.
+         tag == Tag("OTTO") ||
+         // Older Mac fonts might have 'true' or 'typ1' tag.
+         tag == Tag("true") ||
+         tag == Tag("typ1");
+}
+
+bool OTSContext::Process(OTSStream *output,
+                         const uint8_t *data,
+                         size_t length) {
+  OpenTypeFile header;
+
+  header.context = this;
+
+  if (length < 4) {
+    return OTS_FAILURE_MSG_(&header, "file less than 4 bytes");
+  }
+
+  bool result;
+  if (data[0] == 'w' && data[1] == 'O' && data[2] == 'F' && data[3] == 'F') {
+    result = ProcessWOFF(&header, output, data, length);
+  } else if (data[0] == 'w' && data[1] == 'O' && data[2] == 'F' && data[3] == '2') {
+    result = ProcessWOFF2(&header, output, data, length);
+  } else {
+    result = ProcessTTF(&header, output, data, length);
+  }
+
+  for (unsigned i = 0; ; ++i) {
+    if (table_parsers[i].parse == NULL) break;
+    table_parsers[i].free(&header);
+  }
+  return result;
+}
+
+// For backward compatibility
+bool Process(OTSStream *output, const uint8_t *data, size_t length) {
+  static OTSContext context;
+  return context.Process(output, data, length);
+}
+
+}  // namespace ots
diff --git a/third_party/ots/src/ots.h b/third_party/ots/src/ots.h
new file mode 100644
index 0000000..ba3ba77
--- /dev/null
+++ b/third_party/ots/src/ots.h
@@ -0,0 +1,259 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_H_
+#define OTS_H_
+
+#include <stddef.h>
+#include <cstdarg>
+#include <cstddef>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <limits>
+
+#include "opentype-sanitiser.h"
+
+// arraysize borrowed from base/basictypes.h
+template <typename T, size_t N>
+char (&ArraySizeHelper(T (&array)[N]))[N];
+#define arraysize(array) (sizeof(ArraySizeHelper(array)))
+
+namespace ots {
+
+#if !defined(OTS_DEBUG)
+#define OTS_FAILURE() false
+#else
+#define OTS_FAILURE() \
+  (\
+    std::fprintf(stderr, "ERROR at %s:%d (%s)\n", \
+                 __FILE__, __LINE__, __FUNCTION__) \
+    && false\
+  )
+#endif
+
+// All OTS_FAILURE_* macros ultimately evaluate to 'false', just like the original
+// message-less OTS_FAILURE(), so that the current parser will return 'false' as
+// its result (indicating a failure).
+
+#if !defined(OTS_DEBUG)
+#define OTS_MESSAGE_(level,otf_,...) \
+  (otf_)->context->Message(level,__VA_ARGS__)
+#else
+#define OTS_MESSAGE_(level,otf_,...) \
+  OTS_FAILURE(), \
+  (otf_)->context->Message(level,__VA_ARGS__)
+#endif
+
+// Generate a simple message
+#define OTS_FAILURE_MSG_(otf_,...) \
+  (OTS_MESSAGE_(0,otf_,__VA_ARGS__), false)
+
+#define OTS_WARNING_MSG_(otf_,...) \
+  OTS_MESSAGE_(1,otf_,__VA_ARGS__)
+
+// Generate a message with an associated table tag
+#define OTS_FAILURE_MSG_TAG_(otf_,msg_,tag_) \
+  (OTS_MESSAGE_(0,otf_,"%4.4s: %s", tag_, msg_), false)
+
+// Convenience macros for use in files that only handle a single table tag,
+// defined as TABLE_NAME at the top of the file; the 'file' variable is
+// expected to be the current OpenTypeFile pointer.
+#define OTS_FAILURE_MSG(...) OTS_FAILURE_MSG_(file, TABLE_NAME ": " __VA_ARGS__)
+
+#define OTS_WARNING(...) OTS_WARNING_MSG_(file, TABLE_NAME ": " __VA_ARGS__)
+
+// -----------------------------------------------------------------------------
+// Buffer helper class
+//
+// This class perform some trival buffer operations while checking for
+// out-of-bounds errors. As a family they return false if anything is amiss,
+// updating the current offset otherwise.
+// -----------------------------------------------------------------------------
+class Buffer {
+ public:
+  Buffer(const uint8_t *buf, size_t len)
+      : buffer_(buf),
+        length_(len),
+        offset_(0) { }
+
+  bool Skip(size_t n_bytes) {
+    return Read(NULL, n_bytes);
+  }
+
+  bool Read(uint8_t *buf, size_t n_bytes) {
+    if (n_bytes > 1024 * 1024 * 1024) {
+      return OTS_FAILURE();
+    }
+    if ((offset_ + n_bytes > length_) ||
+        (offset_ > length_ - n_bytes)) {
+      return OTS_FAILURE();
+    }
+    if (buf) {
+      std::memcpy(buf, buffer_ + offset_, n_bytes);
+    }
+    offset_ += n_bytes;
+    return true;
+  }
+
+  inline bool ReadU8(uint8_t *value) {
+    if (offset_ + 1 > length_) {
+      return OTS_FAILURE();
+    }
+    *value = buffer_[offset_];
+    ++offset_;
+    return true;
+  }
+
+  bool ReadU16(uint16_t *value) {
+    if (offset_ + 2 > length_) {
+      return OTS_FAILURE();
+    }
+    std::memcpy(value, buffer_ + offset_, sizeof(uint16_t));
+    *value = ntohs(*value);
+    offset_ += 2;
+    return true;
+  }
+
+  bool ReadS16(int16_t *value) {
+    return ReadU16(reinterpret_cast<uint16_t*>(value));
+  }
+
+  bool ReadU24(uint32_t *value) {
+    if (offset_ + 3 > length_) {
+      return OTS_FAILURE();
+    }
+    *value = static_cast<uint32_t>(buffer_[offset_]) << 16 |
+        static_cast<uint32_t>(buffer_[offset_ + 1]) << 8 |
+        static_cast<uint32_t>(buffer_[offset_ + 2]);
+    offset_ += 3;
+    return true;
+  }
+
+  bool ReadU32(uint32_t *value) {
+    if (offset_ + 4 > length_) {
+      return OTS_FAILURE();
+    }
+    std::memcpy(value, buffer_ + offset_, sizeof(uint32_t));
+    *value = ntohl(*value);
+    offset_ += 4;
+    return true;
+  }
+
+  bool ReadS32(int32_t *value) {
+    return ReadU32(reinterpret_cast<uint32_t*>(value));
+  }
+
+  bool ReadTag(uint32_t *value) {
+    if (offset_ + 4 > length_) {
+      return OTS_FAILURE();
+    }
+    std::memcpy(value, buffer_ + offset_, sizeof(uint32_t));
+    offset_ += 4;
+    return true;
+  }
+
+  bool ReadR64(uint64_t *value) {
+    if (offset_ + 8 > length_) {
+      return OTS_FAILURE();
+    }
+    std::memcpy(value, buffer_ + offset_, sizeof(uint64_t));
+    offset_ += 8;
+    return true;
+  }
+
+  const uint8_t *buffer() const { return buffer_; }
+  size_t offset() const { return offset_; }
+  size_t length() const { return length_; }
+
+  void set_offset(size_t newoffset) { offset_ = newoffset; }
+
+ private:
+  const uint8_t * const buffer_;
+  const size_t length_;
+  size_t offset_;
+};
+
+// Round a value up to the nearest multiple of 4. Don't round the value in the
+// case that rounding up overflows.
+template<typename T> T Round4(T value) {
+  if (std::numeric_limits<T>::max() - value < 3) {
+    return value;
+  }
+  return (value + 3) & ~3;
+}
+
+template<typename T> T Round2(T value) {
+  if (value == std::numeric_limits<T>::max()) {
+    return value;
+  }
+  return (value + 1) & ~1;
+}
+
+bool IsValidVersionTag(uint32_t tag);
+
+#define FOR_EACH_TABLE_TYPE \
+  F(cff, CFF) \
+  F(cmap, CMAP) \
+  F(cvt, CVT) \
+  F(fpgm, FPGM) \
+  F(gasp, GASP) \
+  F(gdef, GDEF) \
+  F(glyf, GLYF) \
+  F(gpos, GPOS) \
+  F(gsub, GSUB) \
+  F(hdmx, HDMX) \
+  F(head, HEAD) \
+  F(hhea, HHEA) \
+  F(hmtx, HMTX) \
+  F(kern, KERN) \
+  F(loca, LOCA) \
+  F(ltsh, LTSH) \
+  F(math, MATH) \
+  F(maxp, MAXP) \
+  F(name, NAME) \
+  F(os2, OS2) \
+  F(post, POST) \
+  F(prep, PREP) \
+  F(vdmx, VDMX) \
+  F(vorg, VORG) \
+  F(vhea, VHEA) \
+  F(vmtx, VMTX)
+
+#define F(name, capname) struct OpenType##capname;
+FOR_EACH_TABLE_TYPE
+#undef F
+
+struct OpenTypeFile {
+  OpenTypeFile() {
+#define F(name, capname) name = NULL;
+    FOR_EACH_TABLE_TYPE
+#undef F
+  }
+
+  uint32_t version;
+  uint16_t num_tables;
+  uint16_t search_range;
+  uint16_t entry_selector;
+  uint16_t range_shift;
+
+  OTSContext *context;
+
+#define F(name, capname) OpenType##capname *name;
+FOR_EACH_TABLE_TYPE
+#undef F
+};
+
+#define F(name, capname) \
+bool ots_##name##_parse(OpenTypeFile *f, const uint8_t *d, size_t l); \
+bool ots_##name##_should_serialise(OpenTypeFile *f); \
+bool ots_##name##_serialise(OTSStream *s, OpenTypeFile *f); \
+void ots_##name##_free(OpenTypeFile *f);
+// TODO(yusukes): change these function names to follow Chromium coding rule.
+FOR_EACH_TABLE_TYPE
+#undef F
+
+}  // namespace ots
+
+#endif  // OTS_H_
diff --git a/third_party/ots/src/post.cc b/third_party/ots/src/post.cc
new file mode 100644
index 0000000..338d869
--- /dev/null
+++ b/third_party/ots/src/post.cc
@@ -0,0 +1,188 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "post.h"
+
+#include "maxp.h"
+
+// post - PostScript
+// http://www.microsoft.com/typography/otspec/post.htm
+
+#define TABLE_NAME "post"
+
+namespace ots {
+
+bool ots_post_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
+  Buffer table(data, length);
+
+  OpenTypePOST *post = new OpenTypePOST;
+  file->post = post;
+
+  if (!table.ReadU32(&post->version) ||
+      !table.ReadU32(&post->italic_angle) ||
+      !table.ReadS16(&post->underline) ||
+      !table.ReadS16(&post->underline_thickness) ||
+      !table.ReadU32(&post->is_fixed_pitch)) {
+    return OTS_FAILURE_MSG("Failed to read post header");
+  }
+
+  if (post->underline_thickness < 0) {
+    post->underline_thickness = 1;
+  }
+
+  if (post->version == 0x00010000) {
+    return true;
+  } else if (post->version == 0x00030000) {
+    return true;
+  } else if (post->version != 0x00020000) {
+    // 0x00025000 is deprecated. We don't accept it.
+    return OTS_FAILURE_MSG("Bad post version %x", post->version);
+  }
+
+  // We have a version 2 table with a list of Pascal strings at the end
+
+  // We don't care about the memory usage fields. We'll set all these to zero
+  // when serialising
+  if (!table.Skip(16)) {
+    return OTS_FAILURE_MSG("Failed to skip memory usage in post table");
+  }
+
+  uint16_t num_glyphs = 0;
+  if (!table.ReadU16(&num_glyphs)) {
+    return OTS_FAILURE_MSG("Failed to read number of glyphs");
+  }
+
+  if (!file->maxp) {
+    return OTS_FAILURE_MSG("No maxp table required by post table");
+  }
+
+  if (num_glyphs == 0) {
+    if (file->maxp->num_glyphs > 258) {
+      return OTS_FAILURE_MSG("Can't have no glyphs in the post table if there are more than 256 glyphs in the font");
+    }
+    OTS_WARNING("table version is 1, but no glyf names are found");
+    // workaround for fonts in http://www.fontsquirrel.com/fontface
+    // (e.g., yataghan.ttf).
+    post->version = 0x00010000;
+    return true;
+  }
+
+  if (num_glyphs != file->maxp->num_glyphs) {
+    // Note: Fixedsys500c.ttf seems to have inconsistent num_glyphs values.
+    return OTS_FAILURE_MSG("Bad number of glyphs in post table %d", num_glyphs);
+  }
+
+  post->glyph_name_index.resize(num_glyphs);
+  for (unsigned i = 0; i < num_glyphs; ++i) {
+    if (!table.ReadU16(&post->glyph_name_index[i])) {
+      return OTS_FAILURE_MSG("Failed to read post information for glyph %d", i);
+    }
+    // Note: A strict interpretation of the specification requires name indexes
+    // are less than 32768. This, however, excludes fonts like unifont.ttf
+    // which cover all of unicode.
+  }
+
+  // Now we have an array of Pascal strings. We have to check that they are all
+  // valid and read them in.
+  const size_t strings_offset = table.offset();
+  const uint8_t *strings = data + strings_offset;
+  const uint8_t *strings_end = data + length;
+
+  for (;;) {
+    if (strings == strings_end) break;
+    const unsigned string_length = *strings;
+    if (strings + 1 + string_length > strings_end) {
+      return OTS_FAILURE_MSG("Bad string length %d", string_length);
+    }
+    if (std::memchr(strings + 1, '\0', string_length)) {
+      return OTS_FAILURE_MSG("Bad string of length %d", string_length);
+    }
+    post->names.push_back(
+        std::string(reinterpret_cast<const char*>(strings + 1), string_length));
+    strings += 1 + string_length;
+  }
+  const unsigned num_strings = post->names.size();
+
+  // check that all the references are within bounds
+  for (unsigned i = 0; i < num_glyphs; ++i) {
+    unsigned offset = post->glyph_name_index[i];
+    if (offset < 258) {
+      continue;
+    }
+
+    offset -= 258;
+    if (offset >= num_strings) {
+      return OTS_FAILURE_MSG("Bad string index %d", offset);
+    }
+  }
+
+  return true;
+}
+
+bool ots_post_should_serialise(OpenTypeFile *file) {
+  return file->post != NULL;
+}
+
+bool ots_post_serialise(OTSStream *out, OpenTypeFile *file) {
+  const OpenTypePOST *post = file->post;
+
+  // OpenType with CFF glyphs must have v3 post table.
+  if (file->post && file->cff && file->post->version != 0x00030000) {
+    return OTS_FAILURE_MSG("Bad post version %x", post->version);
+  }
+
+  if (!out->WriteU32(post->version) ||
+      !out->WriteU32(post->italic_angle) ||
+      !out->WriteS16(post->underline) ||
+      !out->WriteS16(post->underline_thickness) ||
+      !out->WriteU32(post->is_fixed_pitch) ||
+      !out->WriteU32(0) ||
+      !out->WriteU32(0) ||
+      !out->WriteU32(0) ||
+      !out->WriteU32(0)) {
+    return OTS_FAILURE_MSG("Failed to write post header");
+  }
+
+  if (post->version != 0x00020000) {
+    return true;  // v1.0 and v3.0 does not have glyph names.
+  }
+
+  const uint16_t num_indexes =
+      static_cast<uint16_t>(post->glyph_name_index.size());
+  if (num_indexes != post->glyph_name_index.size() ||
+      !out->WriteU16(num_indexes)) {
+    return OTS_FAILURE_MSG("Failed to write number of indices");
+  }
+
+  for (uint16_t i = 0; i < num_indexes; ++i) {
+    if (!out->WriteU16(post->glyph_name_index[i])) {
+      return OTS_FAILURE_MSG("Failed to write name index %d", i);
+    }
+  }
+
+  // Now we just have to write out the strings in the correct order
+  for (unsigned i = 0; i < post->names.size(); ++i) {
+    const std::string& s = post->names[i];
+    const uint8_t string_length = static_cast<uint8_t>(s.size());
+    if (string_length != s.size() ||
+        !out->Write(&string_length, 1)) {
+      return OTS_FAILURE_MSG("Failed to write string %d", i);
+    }
+    // Some ttf fonts (e.g., frank.ttf on Windows Vista) have zero-length name.
+    // We allow them.
+    if (string_length > 0 && !out->Write(s.data(), string_length)) {
+      return OTS_FAILURE_MSG("Failed to write string length for string %d", i);
+    }
+  }
+
+  return true;
+}
+
+void ots_post_free(OpenTypeFile *file) {
+  delete file->post;
+}
+
+}  // namespace ots
+
+#undef TABLE_NAME
diff --git a/third_party/ots/src/post.h b/third_party/ots/src/post.h
new file mode 100644
index 0000000..f220d4fc
--- /dev/null
+++ b/third_party/ots/src/post.h
@@ -0,0 +1,29 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_POST_H_
+#define OTS_POST_H_
+
+#include "ots.h"
+
+#include <map>
+#include <string>
+#include <vector>
+
+namespace ots {
+
+struct OpenTypePOST {
+  uint32_t version;
+  uint32_t italic_angle;
+  int16_t underline;
+  int16_t underline_thickness;
+  uint32_t is_fixed_pitch;
+
+  std::vector<uint16_t> glyph_name_index;
+  std::vector<std::string> names;
+};
+
+}  // namespace ots
+
+#endif  // OTS_POST_H_
diff --git a/third_party/ots/src/prep.cc b/third_party/ots/src/prep.cc
new file mode 100644
index 0000000..ea3dec0b
--- /dev/null
+++ b/third_party/ots/src/prep.cc
@@ -0,0 +1,54 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "prep.h"
+
+// prep - Control Value Program
+// http://www.microsoft.com/typography/otspec/prep.htm
+
+#define TABLE_NAME "prep"
+
+namespace ots {
+
+bool ots_prep_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
+  Buffer table(data, length);
+
+  OpenTypePREP *prep = new OpenTypePREP;
+  file->prep = prep;
+
+  if (length >= 128 * 1024u) {
+    return OTS_FAILURE_MSG("table length %ld > 120K", length);  // almost all prep tables are less than 9k bytes.
+  }
+
+  if (!table.Skip(length)) {
+    return OTS_FAILURE_MSG("Failed to read table of length %ld", length);
+  }
+
+  prep->data = data;
+  prep->length = length;
+  return true;
+}
+
+bool ots_prep_should_serialise(OpenTypeFile *file) {
+  if (!file->glyf) return false;  // this table is not for CFF fonts.
+  return file->prep != NULL;
+}
+
+bool ots_prep_serialise(OTSStream *out, OpenTypeFile *file) {
+  const OpenTypePREP *prep = file->prep;
+
+  if (!out->Write(prep->data, prep->length)) {
+    return OTS_FAILURE_MSG("Failed to write table length");
+  }
+
+  return true;
+}
+
+void ots_prep_free(OpenTypeFile *file) {
+  delete file->prep;
+}
+
+}  // namespace ots
+
+#undef TABLE_NAME
diff --git a/third_party/ots/src/prep.h b/third_party/ots/src/prep.h
new file mode 100644
index 0000000..935ca11
--- /dev/null
+++ b/third_party/ots/src/prep.h
@@ -0,0 +1,19 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_PREP_H_
+#define OTS_PREP_H_
+
+#include "ots.h"
+
+namespace ots {
+
+struct OpenTypePREP {
+  const uint8_t *data;
+  uint32_t length;
+};
+
+}  // namespace ots
+
+#endif  // OTS_PREP_H_
diff --git a/third_party/ots/src/vdmx.cc b/third_party/ots/src/vdmx.cc
new file mode 100644
index 0000000..4853d63
--- /dev/null
+++ b/third_party/ots/src/vdmx.cc
@@ -0,0 +1,181 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "vdmx.h"
+
+// VDMX - Vertical Device Metrics
+// http://www.microsoft.com/typography/otspec/vdmx.htm
+
+#define TABLE_NAME "VDMX"
+
+#define DROP_THIS_TABLE(...) \
+  do { \
+    OTS_FAILURE_MSG_(file, TABLE_NAME ": " __VA_ARGS__); \
+    OTS_FAILURE_MSG("Table discarded"); \
+    delete file->vdmx; \
+    file->vdmx = 0; \
+  } while (0)
+
+namespace ots {
+
+bool ots_vdmx_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
+  Buffer table(data, length);
+  file->vdmx = new OpenTypeVDMX;
+  OpenTypeVDMX * const vdmx = file->vdmx;
+
+  if (!table.ReadU16(&vdmx->version) ||
+      !table.ReadU16(&vdmx->num_recs) ||
+      !table.ReadU16(&vdmx->num_ratios)) {
+    return OTS_FAILURE_MSG("Failed to read table header");
+  }
+
+  if (vdmx->version > 1) {
+    DROP_THIS_TABLE("bad version: %u", vdmx->version);
+    return true;  // continue transcoding
+  }
+
+  vdmx->rat_ranges.reserve(vdmx->num_ratios);
+  for (unsigned i = 0; i < vdmx->num_ratios; ++i) {
+    OpenTypeVDMXRatioRecord rec;
+
+    if (!table.ReadU8(&rec.charset) ||
+        !table.ReadU8(&rec.x_ratio) ||
+        !table.ReadU8(&rec.y_start_ratio) ||
+        !table.ReadU8(&rec.y_end_ratio)) {
+      return OTS_FAILURE_MSG("Failed to read ratio header %d", i);
+    }
+
+    if (rec.charset > 1) {
+      DROP_THIS_TABLE("bad charset: %u", rec.charset);
+      return true;
+    }
+
+    if (rec.y_start_ratio > rec.y_end_ratio) {
+      DROP_THIS_TABLE("bad y ratio");
+      return true;
+    }
+
+    // All values set to zero signal the default grouping to use;
+    // if present, this must be the last Ratio group in the table.
+    if ((i < vdmx->num_ratios - 1u) &&
+        (rec.x_ratio == 0) &&
+        (rec.y_start_ratio == 0) &&
+        (rec.y_end_ratio == 0)) {
+      // workaround for fonts which have 2 or more {0, 0, 0} terminators.
+      DROP_THIS_TABLE("superfluous terminator found");
+      return true;
+    }
+
+    vdmx->rat_ranges.push_back(rec);
+  }
+
+  vdmx->offsets.reserve(vdmx->num_ratios);
+  const size_t current_offset = table.offset();
+  // current_offset is less than (2 bytes * 3) + (4 bytes * USHRT_MAX) = 256k.
+  for (unsigned i = 0; i < vdmx->num_ratios; ++i) {
+    uint16_t offset;
+    if (!table.ReadU16(&offset)) {
+      return OTS_FAILURE_MSG("Failed to read ratio offset %d", i);
+    }
+    if (current_offset + offset >= length) {  // thus doesn't overflow.
+      return OTS_FAILURE_MSG("Bad ratio offset %d for ration %d", offset, i);
+    }
+
+    vdmx->offsets.push_back(offset);
+  }
+
+  vdmx->groups.reserve(vdmx->num_recs);
+  for (unsigned i = 0; i < vdmx->num_recs; ++i) {
+    OpenTypeVDMXGroup group;
+    if (!table.ReadU16(&group.recs) ||
+        !table.ReadU8(&group.startsz) ||
+        !table.ReadU8(&group.endsz)) {
+      return OTS_FAILURE_MSG("Failed to read record header %d", i);
+    }
+    group.entries.reserve(group.recs);
+    for (unsigned j = 0; j < group.recs; ++j) {
+      OpenTypeVDMXVTable vt;
+      if (!table.ReadU16(&vt.y_pel_height) ||
+          !table.ReadS16(&vt.y_max) ||
+          !table.ReadS16(&vt.y_min)) {
+        return OTS_FAILURE_MSG("Failed to read reacord %d group %d", i, j);
+      }
+      if (vt.y_max < vt.y_min) {
+        DROP_THIS_TABLE("bad y min/max");
+        return true;
+      }
+
+      // This table must appear in sorted order (sorted by yPelHeight),
+      // but need not be continuous.
+      if ((j != 0) && (group.entries[j - 1].y_pel_height >= vt.y_pel_height)) {
+        DROP_THIS_TABLE("the table is not sorted");
+        return true;
+      }
+
+      group.entries.push_back(vt);
+    }
+    vdmx->groups.push_back(group);
+  }
+
+  return true;
+}
+
+bool ots_vdmx_should_serialise(OpenTypeFile *file) {
+  if (!file->glyf) return false;  // this table is not for CFF fonts.
+  return file->vdmx != NULL;
+}
+
+bool ots_vdmx_serialise(OTSStream *out, OpenTypeFile *file) {
+  OpenTypeVDMX * const vdmx = file->vdmx;
+
+  if (!out->WriteU16(vdmx->version) ||
+      !out->WriteU16(vdmx->num_recs) ||
+      !out->WriteU16(vdmx->num_ratios)) {
+    return OTS_FAILURE_MSG("Failed to write table header");
+  }
+
+  for (unsigned i = 0; i < vdmx->rat_ranges.size(); ++i) {
+    const OpenTypeVDMXRatioRecord& rec = vdmx->rat_ranges[i];
+    if (!out->Write(&rec.charset, 1) ||
+        !out->Write(&rec.x_ratio, 1) ||
+        !out->Write(&rec.y_start_ratio, 1) ||
+        !out->Write(&rec.y_end_ratio, 1)) {
+      return OTS_FAILURE_MSG("Failed to write ratio %d", i);
+    }
+  }
+
+  for (unsigned i = 0; i < vdmx->offsets.size(); ++i) {
+    if (!out->WriteU16(vdmx->offsets[i])) {
+      return OTS_FAILURE_MSG("Failed to write ratio offset %d", i);
+    }
+  }
+
+  for (unsigned i = 0; i < vdmx->groups.size(); ++i) {
+    const OpenTypeVDMXGroup& group = vdmx->groups[i];
+    if (!out->WriteU16(group.recs) ||
+        !out->Write(&group.startsz, 1) ||
+        !out->Write(&group.endsz, 1)) {
+      return OTS_FAILURE_MSG("Failed to write group %d", i);
+    }
+    for (unsigned j = 0; j < group.entries.size(); ++j) {
+      const OpenTypeVDMXVTable& vt = group.entries[j];
+      if (!out->WriteU16(vt.y_pel_height) ||
+          !out->WriteS16(vt.y_max) ||
+          !out->WriteS16(vt.y_min)) {
+        return OTS_FAILURE_MSG("Failed to write group %d entry %d", i, j);
+      }
+    }
+  }
+
+  return true;
+}
+
+void ots_vdmx_free(OpenTypeFile *file) {
+  delete file->vdmx;
+}
+
+}  // namespace ots
+
+#undef TABLE_NAME
+#undef DROP_THIS_TABLE
diff --git a/third_party/ots/src/vdmx.h b/third_party/ots/src/vdmx.h
new file mode 100644
index 0000000..1d959ef
--- /dev/null
+++ b/third_party/ots/src/vdmx.h
@@ -0,0 +1,45 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_VDMX_H_
+#define OTS_VDMX_H_
+
+#include <vector>
+
+#include "ots.h"
+
+namespace ots {
+
+struct OpenTypeVDMXRatioRecord {
+  uint8_t charset;
+  uint8_t x_ratio;
+  uint8_t y_start_ratio;
+  uint8_t y_end_ratio;
+};
+
+struct OpenTypeVDMXVTable {
+  uint16_t y_pel_height;
+  int16_t y_max;
+  int16_t y_min;
+};
+
+struct OpenTypeVDMXGroup {
+  uint16_t recs;
+  uint8_t startsz;
+  uint8_t endsz;
+  std::vector<OpenTypeVDMXVTable> entries;
+};
+
+struct OpenTypeVDMX {
+  uint16_t version;
+  uint16_t num_recs;
+  uint16_t num_ratios;
+  std::vector<OpenTypeVDMXRatioRecord> rat_ranges;
+  std::vector<uint16_t> offsets;
+  std::vector<OpenTypeVDMXGroup> groups;
+};
+
+}  // namespace ots
+
+#endif  // OTS_VDMX_H_
diff --git a/third_party/ots/src/vhea.cc b/third_party/ots/src/vhea.cc
new file mode 100644
index 0000000..c689a834
--- /dev/null
+++ b/third_party/ots/src/vhea.cc
@@ -0,0 +1,59 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "vhea.h"
+
+#include "gsub.h"
+#include "head.h"
+#include "maxp.h"
+
+// vhea - Vertical Header Table
+// http://www.microsoft.com/typography/otspec/vhea.htm
+
+#define TABLE_NAME "vhea"
+
+namespace ots {
+
+bool ots_vhea_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
+  Buffer table(data, length);
+  OpenTypeVHEA *vhea = new OpenTypeVHEA;
+  file->vhea = vhea;
+
+  if (!table.ReadU32(&vhea->header.version)) {
+    return OTS_FAILURE_MSG("Failed to read version");
+  }
+  if (vhea->header.version != 0x00010000 &&
+      vhea->header.version != 0x00011000) {
+    return OTS_FAILURE_MSG("Bad vhea version %x", vhea->header.version);
+  }
+
+  if (!ParseMetricsHeader(file, &table, &vhea->header)) {
+    return OTS_FAILURE_MSG("Failed to parse metrics in vhea");
+  }
+
+  return true;
+}
+
+bool ots_vhea_should_serialise(OpenTypeFile *file) {
+  // vhea should'nt serialise when vmtx doesn't exist.
+  // Firefox developer pointed out that vhea/vmtx should serialise iff GSUB is
+  // preserved. See http://crbug.com/77386
+  return file->vhea != NULL && file->vmtx != NULL &&
+      ots_gsub_should_serialise(file);
+}
+
+bool ots_vhea_serialise(OTSStream *out, OpenTypeFile *file) {
+  if (!SerialiseMetricsHeader(file, out, &file->vhea->header)) {
+    return OTS_FAILURE_MSG("Failed to write vhea metrics");
+  }
+  return true;
+}
+
+void ots_vhea_free(OpenTypeFile *file) {
+  delete file->vhea;
+}
+
+}  // namespace ots
+
+#undef TABLE_NAME
diff --git a/third_party/ots/src/vhea.h b/third_party/ots/src/vhea.h
new file mode 100644
index 0000000..f8efde73
--- /dev/null
+++ b/third_party/ots/src/vhea.h
@@ -0,0 +1,20 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_VHEA_H_
+#define OTS_VHEA_H_
+
+#include "metrics.h"
+#include "ots.h"
+
+namespace ots {
+
+struct OpenTypeVHEA {
+  OpenTypeMetricsHeader header;
+};
+
+}  // namespace ots
+
+#endif  // OTS_VHEA_H_
+
diff --git a/third_party/ots/src/vmtx.cc b/third_party/ots/src/vmtx.cc
new file mode 100644
index 0000000..e29d32b
--- /dev/null
+++ b/third_party/ots/src/vmtx.cc
@@ -0,0 +1,55 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "vmtx.h"
+
+#include "gsub.h"
+#include "maxp.h"
+#include "vhea.h"
+
+// vmtx - Vertical Metrics Table
+// http://www.microsoft.com/typography/otspec/vmtx.htm
+
+#define TABLE_NAME "vmtx"
+
+namespace ots {
+
+bool ots_vmtx_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
+  Buffer table(data, length);
+  OpenTypeVMTX *vmtx = new OpenTypeVMTX;
+  file->vmtx = vmtx;
+
+  if (!file->vhea || !file->maxp) {
+    return OTS_FAILURE_MSG("vhea or maxp table missing as needed by vmtx");
+  }
+
+  if (!ParseMetricsTable(file, &table, file->maxp->num_glyphs,
+                         &file->vhea->header, &vmtx->metrics)) {
+    return OTS_FAILURE_MSG("Failed to parse vmtx metrics");
+  }
+
+  return true;
+}
+
+bool ots_vmtx_should_serialise(OpenTypeFile *file) {
+  // vmtx should serialise when vhea and GSUB are preserved.
+  // See the comment in ots_vhea_should_serialise().
+  return file->vmtx != NULL && file->vhea != NULL &&
+      ots_gsub_should_serialise(file);
+}
+
+bool ots_vmtx_serialise(OTSStream *out, OpenTypeFile *file) {
+  if (!SerialiseMetricsTable(file, out, &file->vmtx->metrics)) {
+    return OTS_FAILURE_MSG("Failed to write vmtx metrics");
+  }
+  return true;
+}
+
+void ots_vmtx_free(OpenTypeFile *file) {
+  delete file->vmtx;
+}
+
+}  // namespace ots
+
+#undef TABLE_NAME
diff --git a/third_party/ots/src/vmtx.h b/third_party/ots/src/vmtx.h
new file mode 100644
index 0000000..061dc73
--- /dev/null
+++ b/third_party/ots/src/vmtx.h
@@ -0,0 +1,20 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_VMTX_H_
+#define OTS_VMTX_H_
+
+#include "metrics.h"
+#include "ots.h"
+
+namespace ots {
+
+struct OpenTypeVMTX {
+  OpenTypeMetricsTable metrics;
+};
+
+}  // namespace ots
+
+#endif  // OTS_VMTX_H_
+
diff --git a/third_party/ots/src/vorg.cc b/third_party/ots/src/vorg.cc
new file mode 100644
index 0000000..2662067
--- /dev/null
+++ b/third_party/ots/src/vorg.cc
@@ -0,0 +1,106 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "vorg.h"
+
+#include <vector>
+
+// VORG - Vertical Origin Table
+// http://www.microsoft.com/typography/otspec/vorg.htm
+
+#define TABLE_NAME "VORG"
+
+#define DROP_THIS_TABLE(...) \
+  do { \
+    OTS_FAILURE_MSG_(file, TABLE_NAME ": " __VA_ARGS__); \
+    OTS_FAILURE_MSG("Table discarded"); \
+    delete file->vorg; \
+    file->vorg = 0; \
+  } while (0)
+
+namespace ots {
+
+bool ots_vorg_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
+  Buffer table(data, length);
+  file->vorg = new OpenTypeVORG;
+  OpenTypeVORG * const vorg = file->vorg;
+
+  uint16_t num_recs;
+  if (!table.ReadU16(&vorg->major_version) ||
+      !table.ReadU16(&vorg->minor_version) ||
+      !table.ReadS16(&vorg->default_vert_origin_y) ||
+      !table.ReadU16(&num_recs)) {
+    return OTS_FAILURE_MSG("Failed to read header");
+  }
+  if (vorg->major_version != 1) {
+    DROP_THIS_TABLE("bad major version: %u", vorg->major_version);
+    return true;
+  }
+  if (vorg->minor_version != 0) {
+    DROP_THIS_TABLE("bad minor version: %u", vorg->minor_version);
+    return true;
+  }
+
+  // num_recs might be zero (e.g., DFHSMinchoPro5-W3-Demo.otf).
+  if (!num_recs) {
+    return true;
+  }
+
+  uint16_t last_glyph_index = 0;
+  vorg->metrics.reserve(num_recs);
+  for (unsigned i = 0; i < num_recs; ++i) {
+    OpenTypeVORGMetrics rec;
+
+    if (!table.ReadU16(&rec.glyph_index) ||
+        !table.ReadS16(&rec.vert_origin_y)) {
+      return OTS_FAILURE_MSG("Failed to read record %d", i);
+    }
+    if ((i != 0) && (rec.glyph_index <= last_glyph_index)) {
+      DROP_THIS_TABLE("the table is not sorted");
+      return true;
+    }
+    last_glyph_index = rec.glyph_index;
+
+    vorg->metrics.push_back(rec);
+  }
+
+  return true;
+}
+
+bool ots_vorg_should_serialise(OpenTypeFile *file) {
+  if (!file->cff) return false;  // this table is not for fonts with TT glyphs.
+  return file->vorg != NULL;
+}
+
+bool ots_vorg_serialise(OTSStream *out, OpenTypeFile *file) {
+  OpenTypeVORG * const vorg = file->vorg;
+  
+  const uint16_t num_metrics = static_cast<uint16_t>(vorg->metrics.size());
+  if (num_metrics != vorg->metrics.size() ||
+      !out->WriteU16(vorg->major_version) ||
+      !out->WriteU16(vorg->minor_version) ||
+      !out->WriteS16(vorg->default_vert_origin_y) ||
+      !out->WriteU16(num_metrics)) {
+    return OTS_FAILURE_MSG("Failed to write table header");
+  }
+
+  for (uint16_t i = 0; i < num_metrics; ++i) {
+    const OpenTypeVORGMetrics& rec = vorg->metrics[i];
+    if (!out->WriteU16(rec.glyph_index) ||
+        !out->WriteS16(rec.vert_origin_y)) {
+      return OTS_FAILURE_MSG("Failed to write record %d", i);
+    }
+  }
+
+  return true;
+}
+
+void ots_vorg_free(OpenTypeFile *file) {
+  delete file->vorg;
+}
+
+}  // namespace ots
+
+#undef TABLE_NAME
+#undef DROP_THIS_TABLE
diff --git a/third_party/ots/src/vorg.h b/third_party/ots/src/vorg.h
new file mode 100644
index 0000000..c3d3ffda
--- /dev/null
+++ b/third_party/ots/src/vorg.h
@@ -0,0 +1,28 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_VORG_H_
+#define OTS_VORG_H_
+
+#include <vector>
+
+#include "ots.h"
+
+namespace ots {
+
+struct OpenTypeVORGMetrics {
+  uint16_t glyph_index;
+  int16_t vert_origin_y;
+};
+
+struct OpenTypeVORG {
+  uint16_t major_version;
+  uint16_t minor_version;
+  int16_t default_vert_origin_y;
+  std::vector<OpenTypeVORGMetrics> metrics;
+};
+
+}  // namespace ots
+
+#endif  // OTS_VORG_H_
diff --git a/third_party/ots/src/woff2.cc b/third_party/ots/src/woff2.cc
new file mode 100644
index 0000000..31b562d9
--- /dev/null
+++ b/third_party/ots/src/woff2.cc
@@ -0,0 +1,991 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This is the implementation of decompression of the proposed WOFF Ultra
+// Condensed file format.
+
+#include <cassert>
+#include <cstdlib>
+#include <vector>
+
+#include "third_party/brotli/src/brotli/dec/decode.h"
+
+#include "opentype-sanitiser.h"
+#include "ots-memory-stream.h"
+#include "ots.h"
+#include "woff2.h"
+
+#define TABLE_NAME "WOFF2"
+
+namespace {
+
+// simple glyph flags
+const uint8_t kGlyfOnCurve = 1 << 0;
+const uint8_t kGlyfXShort = 1 << 1;
+const uint8_t kGlyfYShort = 1 << 2;
+const uint8_t kGlyfRepeat = 1 << 3;
+const uint8_t kGlyfThisXIsSame = 1 << 4;
+const uint8_t kGlyfThisYIsSame = 1 << 5;
+
+// composite glyph flags
+const int FLAG_ARG_1_AND_2_ARE_WORDS = 1 << 0;
+const int FLAG_WE_HAVE_A_SCALE = 1 << 3;
+const int FLAG_MORE_COMPONENTS = 1 << 5;
+const int FLAG_WE_HAVE_AN_X_AND_Y_SCALE = 1 << 6;
+const int FLAG_WE_HAVE_A_TWO_BY_TWO = 1 << 7;
+const int FLAG_WE_HAVE_INSTRUCTIONS = 1 << 8;
+
+const size_t kSfntHeaderSize = 12;
+const size_t kSfntEntrySize = 16;
+const size_t kCheckSumAdjustmentOffset = 8;
+
+const size_t kEndPtsOfContoursOffset = 10;
+const size_t kCompositeGlyphBegin = 10;
+
+// Note that the byte order is big-endian, not the same as ots.cc
+#define TAG(a, b, c, d) ((a << 24) | (b << 16) | (c << 8) | d)
+
+const unsigned int kWoff2FlagsTransform = 1 << 5;
+
+const uint32_t kKnownTags[] = {
+  TAG('c', 'm', 'a', 'p'),  // 0
+  TAG('h', 'e', 'a', 'd'),  // 1
+  TAG('h', 'h', 'e', 'a'),  // 2
+  TAG('h', 'm', 't', 'x'),  // 3
+  TAG('m', 'a', 'x', 'p'),  // 4
+  TAG('n', 'a', 'm', 'e'),  // 5
+  TAG('O', 'S', '/', '2'),  // 6
+  TAG('p', 'o', 's', 't'),  // 7
+  TAG('c', 'v', 't', ' '),  // 8
+  TAG('f', 'p', 'g', 'm'),  // 9
+  TAG('g', 'l', 'y', 'f'),  // 10
+  TAG('l', 'o', 'c', 'a'),  // 11
+  TAG('p', 'r', 'e', 'p'),  // 12
+  TAG('C', 'F', 'F', ' '),  // 13
+  TAG('V', 'O', 'R', 'G'),  // 14
+  TAG('E', 'B', 'D', 'T'),  // 15
+  TAG('E', 'B', 'L', 'C'),  // 16
+  TAG('g', 'a', 's', 'p'),  // 17
+  TAG('h', 'd', 'm', 'x'),  // 18
+  TAG('k', 'e', 'r', 'n'),  // 19
+  TAG('L', 'T', 'S', 'H'),  // 20
+  TAG('P', 'C', 'L', 'T'),  // 21
+  TAG('V', 'D', 'M', 'X'),  // 22
+  TAG('v', 'h', 'e', 'a'),  // 23
+  TAG('v', 'm', 't', 'x'),  // 24
+  TAG('B', 'A', 'S', 'E'),  // 25
+  TAG('G', 'D', 'E', 'F'),  // 26
+  TAG('G', 'P', 'O', 'S'),  // 27
+  TAG('G', 'S', 'U', 'B'),  // 28
+  TAG('E', 'B', 'S', 'C'),  // 29
+  TAG('J', 'S', 'T', 'F'),  // 30
+  TAG('M', 'A', 'T', 'H'),  // 31
+  TAG('C', 'B', 'D', 'T'),  // 32
+  TAG('C', 'B', 'L', 'C'),  // 33
+  TAG('C', 'O', 'L', 'R'),  // 34
+  TAG('C', 'P', 'A', 'L'),  // 35
+  TAG('S', 'V', 'G', ' '),  // 36
+  TAG('s', 'b', 'i', 'x'),  // 37
+  TAG('a', 'c', 'n', 't'),  // 38
+  TAG('a', 'v', 'a', 'r'),  // 39
+  TAG('b', 'd', 'a', 't'),  // 40
+  TAG('b', 'l', 'o', 'c'),  // 41
+  TAG('b', 's', 'l', 'n'),  // 42
+  TAG('c', 'v', 'a', 'r'),  // 43
+  TAG('f', 'd', 's', 'c'),  // 44
+  TAG('f', 'e', 'a', 't'),  // 45
+  TAG('f', 'm', 't', 'x'),  // 46
+  TAG('f', 'v', 'a', 'r'),  // 47
+  TAG('g', 'v', 'a', 'r'),  // 48
+  TAG('h', 's', 't', 'y'),  // 49
+  TAG('j', 'u', 's', 't'),  // 50
+  TAG('l', 'c', 'a', 'r'),  // 51
+  TAG('m', 'o', 'r', 't'),  // 52
+  TAG('m', 'o', 'r', 'x'),  // 53
+  TAG('o', 'p', 'b', 'd'),  // 54
+  TAG('p', 'r', 'o', 'p'),  // 55
+  TAG('t', 'r', 'a', 'k'),  // 56
+  TAG('Z', 'a', 'p', 'f'),  // 57
+  TAG('S', 'i', 'l', 'f'),  // 58
+  TAG('G', 'l', 'a', 't'),  // 59
+  TAG('G', 'l', 'o', 'c'),  // 60
+  TAG('F', 'e', 'a', 't'),  // 61
+  TAG('S', 'i', 'l', 'l'),  // 62
+};
+
+struct Point {
+  int16_t x;
+  int16_t y;
+  bool on_curve;
+};
+
+struct Table {
+  uint32_t tag;
+  uint32_t flags;
+
+  uint32_t transform_length;
+
+  uint32_t dst_offset;
+  uint32_t dst_length;
+
+  Table()
+      : tag(0),
+        flags(0),
+        transform_length(0),
+        dst_offset(0),
+        dst_length(0) {}
+};
+
+// Based on section 6.1.1 of MicroType Express draft spec
+bool Read255UShort(ots::Buffer* buf, uint16_t* value) {
+  static const uint8_t kWordCode = 253;
+  static const uint8_t kOneMoreByteCode2 = 254;
+  static const uint8_t kOneMoreByteCode1 = 255;
+  static const uint8_t kLowestUCode = 253;
+  uint8_t code = 0;
+  if (!buf->ReadU8(&code)) {
+    return OTS_FAILURE();
+  }
+  if (code == kWordCode) {
+    uint16_t result = 0;
+    if (!buf->ReadU16(&result)) {
+      return OTS_FAILURE();
+    }
+    *value = result;
+    return true;
+  } else if (code == kOneMoreByteCode1) {
+    uint8_t result = 0;
+    if (!buf->ReadU8(&result)) {
+      return OTS_FAILURE();
+    }
+    *value = result + kLowestUCode;
+    return true;
+  } else if (code == kOneMoreByteCode2) {
+    uint8_t result = 0;
+    if (!buf->ReadU8(&result)) {
+      return OTS_FAILURE();
+    }
+    *value = result + kLowestUCode * 2;
+    return true;
+  } else {
+    *value = code;
+    return true;
+  }
+}
+
+bool ReadBase128(ots::Buffer* buf, uint32_t* value) {
+  uint32_t result = 0;
+  for (size_t i = 0; i < 5; ++i) {
+    uint8_t code = 0;
+    if (!buf->ReadU8(&code)) {
+      return OTS_FAILURE();
+    }
+    // If any of the top seven bits are set then we're about to overflow.
+    if (result & 0xfe000000U) {
+      return OTS_FAILURE();
+    }
+    result = (result << 7) | (code & 0x7f);
+    if ((code & 0x80) == 0) {
+      *value = result;
+      return true;
+    }
+  }
+  // Make sure not to exceed the size bound
+  return OTS_FAILURE();
+}
+
+// Caller must ensure that buffer overrun won't happen.
+// TODO(ksakamaoto): Consider creating 'writer' version of the Buffer class
+// and use it across the code.
+size_t StoreU32(uint8_t* dst, size_t offset, uint32_t x) {
+  dst[offset] = x >> 24;
+  dst[offset + 1] = (x >> 16) & 0xff;
+  dst[offset + 2] = (x >> 8) & 0xff;
+  dst[offset + 3] = x & 0xff;
+  return offset + 4;
+}
+
+size_t StoreU16(uint8_t* dst, size_t offset, uint16_t x) {
+  dst[offset] = x >> 8;
+  dst[offset + 1] = x & 0xff;
+  return offset + 2;
+}
+
+int WithSign(int flag, int baseval) {
+  assert(0 <= baseval && baseval < 65536);
+  return (flag & 1) ? baseval : -baseval;
+}
+
+bool TripletDecode(const uint8_t* flags_in, const uint8_t* in, size_t in_size,
+    unsigned int n_points, std::vector<Point>* result,
+    size_t* in_bytes_consumed) {
+  int x = 0;
+  int y = 0;
+
+  // Early return if |in| buffer is too small. Each point consumes 1-4 bytes.
+  if (n_points > in_size) {
+    return OTS_FAILURE();
+  }
+  unsigned int triplet_index = 0;
+
+  for (unsigned int i = 0; i < n_points; ++i) {
+    uint8_t flag = flags_in[i];
+    bool on_curve = !(flag >> 7);
+    flag &= 0x7f;
+    unsigned int n_data_bytes;
+    if (flag < 84) {
+      n_data_bytes = 1;
+    } else if (flag < 120) {
+      n_data_bytes = 2;
+    } else if (flag < 124) {
+      n_data_bytes = 3;
+    } else {
+      n_data_bytes = 4;
+    }
+    if (triplet_index + n_data_bytes > in_size ||
+        triplet_index + n_data_bytes < triplet_index) {
+      return OTS_FAILURE();
+    }
+    int dx, dy;
+    if (flag < 10) {
+      dx = 0;
+      dy = WithSign(flag, ((flag & 14) << 7) + in[triplet_index]);
+    } else if (flag < 20) {
+      dx = WithSign(flag, (((flag - 10) & 14) << 7) + in[triplet_index]);
+      dy = 0;
+    } else if (flag < 84) {
+      int b0 = flag - 20;
+      int b1 = in[triplet_index];
+      dx = WithSign(flag, 1 + (b0 & 0x30) + (b1 >> 4));
+      dy = WithSign(flag >> 1, 1 + ((b0 & 0x0c) << 2) + (b1 & 0x0f));
+    } else if (flag < 120) {
+      int b0 = flag - 84;
+      dx = WithSign(flag, 1 + ((b0 / 12) << 8) + in[triplet_index]);
+      dy = WithSign(flag >> 1,
+                    1 + (((b0 % 12) >> 2) << 8) + in[triplet_index + 1]);
+    } else if (flag < 124) {
+      int b2 = in[triplet_index + 1];
+      dx = WithSign(flag, (in[triplet_index] << 4) + (b2 >> 4));
+      dy = WithSign(flag >> 1, ((b2 & 0x0f) << 8) + in[triplet_index + 2]);
+    } else {
+      dx = WithSign(flag, (in[triplet_index] << 8) + in[triplet_index + 1]);
+      dy = WithSign(flag >> 1,
+          (in[triplet_index + 2] << 8) + in[triplet_index + 3]);
+    }
+    triplet_index += n_data_bytes;
+    // Possible overflow but coordinate values are not security sensitive
+    x += dx;
+    y += dy;
+    result->push_back(Point());
+    Point& back = result->back();
+    back.x = static_cast<int16_t>(x);
+    back.y = static_cast<int16_t>(y);
+    back.on_curve = on_curve;
+  }
+  *in_bytes_consumed = triplet_index;
+  return true;
+}
+
+// This function stores just the point data. On entry, dst points to the
+// beginning of a simple glyph. Returns true on success.
+bool StorePoints(const std::vector<Point>& points,
+    unsigned int n_contours, unsigned int instruction_length,
+    uint8_t* dst, size_t dst_size, size_t* glyph_size) {
+  // I believe that n_contours < 65536, in which case this is safe. However, a
+  // comment and/or an assert would be good.
+  unsigned int flag_offset = kEndPtsOfContoursOffset + 2 * n_contours + 2 +
+    instruction_length;
+  uint8_t last_flag = 0xff;
+  uint8_t repeat_count = 0;
+  int last_x = 0;
+  int last_y = 0;
+  unsigned int x_bytes = 0;
+  unsigned int y_bytes = 0;
+
+  for (size_t i = 0; i < points.size(); ++i) {
+    const Point& point = points.at(i);
+    uint8_t flag = point.on_curve ? kGlyfOnCurve : 0;
+    int dx = point.x - last_x;
+    int dy = point.y - last_y;
+    if (dx == 0) {
+      flag |= kGlyfThisXIsSame;
+    } else if (dx > -256 && dx < 256) {
+      flag |= kGlyfXShort | (dx > 0 ? kGlyfThisXIsSame : 0);
+      x_bytes += 1;
+    } else {
+      x_bytes += 2;
+    }
+    if (dy == 0) {
+      flag |= kGlyfThisYIsSame;
+    } else if (dy > -256 && dy < 256) {
+      flag |= kGlyfYShort | (dy > 0 ? kGlyfThisYIsSame : 0);
+      y_bytes += 1;
+    } else {
+      y_bytes += 2;
+    }
+
+    if (flag == last_flag && repeat_count != 255) {
+      dst[flag_offset - 1] |= kGlyfRepeat;
+      repeat_count++;
+    } else {
+      if (repeat_count != 0) {
+        if (flag_offset >= dst_size) {
+          return OTS_FAILURE();
+        }
+        dst[flag_offset++] = repeat_count;
+      }
+      if (flag_offset >= dst_size) {
+        return OTS_FAILURE();
+      }
+      dst[flag_offset++] = flag;
+      repeat_count = 0;
+    }
+    last_x = point.x;
+    last_y = point.y;
+    last_flag = flag;
+  }
+
+  if (repeat_count != 0) {
+    if (flag_offset >= dst_size) {
+      return OTS_FAILURE();
+    }
+    dst[flag_offset++] = repeat_count;
+  }
+  unsigned int xy_bytes = x_bytes + y_bytes;
+  if (xy_bytes < x_bytes ||
+      flag_offset + xy_bytes < flag_offset ||
+      flag_offset + xy_bytes > dst_size) {
+    return OTS_FAILURE();
+  }
+
+  int x_offset = flag_offset;
+  int y_offset = flag_offset + x_bytes;
+  last_x = 0;
+  last_y = 0;
+  for (size_t i = 0; i < points.size(); ++i) {
+    int dx = points.at(i).x - last_x;
+    if (dx == 0) {
+      // pass
+    } else if (dx > -256 && dx < 256) {
+      dst[x_offset++] = static_cast<uint8_t>(std::abs(dx));
+    } else {
+      // will always fit for valid input, but overflow is harmless
+      x_offset = StoreU16(dst, x_offset, static_cast<uint16_t>(dx));
+    }
+    last_x += dx;
+    int dy = points.at(i).y - last_y;
+    if (dy == 0) {
+      // pass
+    } else if (dy > -256 && dy < 256) {
+      dst[y_offset++] = static_cast<uint8_t>(std::abs(dy));
+    } else {
+      y_offset = StoreU16(dst, y_offset, static_cast<uint16_t>(dy));
+    }
+    last_y += dy;
+  }
+  *glyph_size = y_offset;
+  return true;
+}
+
+// Compute the bounding box of the coordinates, and store into a glyf buffer.
+// A precondition is that there are at least 10 bytes available.
+void ComputeBbox(const std::vector<Point>& points, uint8_t* dst) {
+  int16_t x_min = 0;
+  int16_t y_min = 0;
+  int16_t x_max = 0;
+  int16_t y_max = 0;
+
+  for (size_t i = 0; i < points.size(); ++i) {
+    int16_t x = points.at(i).x;
+    int16_t y = points.at(i).y;
+    if (i == 0 || x < x_min) x_min = x;
+    if (i == 0 || x > x_max) x_max = x;
+    if (i == 0 || y < y_min) y_min = y;
+    if (i == 0 || y > y_max) y_max = y;
+  }
+  size_t offset = 2;
+  offset = StoreU16(dst, offset, x_min);
+  offset = StoreU16(dst, offset, y_min);
+  offset = StoreU16(dst, offset, x_max);
+  offset = StoreU16(dst, offset, y_max);
+}
+
+// Process entire bbox stream. This is done as a separate pass to allow for
+// composite bbox computations (an optional more aggressive transform).
+bool ProcessBboxStream(ots::Buffer* bbox_stream, unsigned int n_glyphs,
+    const std::vector<uint32_t>& loca_values, uint8_t* glyf_buf,
+    size_t glyf_buf_length) {
+  const uint8_t* buf = bbox_stream->buffer();
+  if (n_glyphs >= 65536 || loca_values.size() != n_glyphs + 1) {
+    return OTS_FAILURE();
+  }
+  // Safe because n_glyphs is bounded
+  unsigned int bitmap_length = ((n_glyphs + 31) >> 5) << 2;
+  if (!bbox_stream->Skip(bitmap_length)) {
+    return OTS_FAILURE();
+  }
+  for (unsigned int i = 0; i < n_glyphs; ++i) {
+    if (buf[i >> 3] & (0x80 >> (i & 7))) {
+      uint32_t loca_offset = loca_values.at(i);
+      if (loca_values.at(i + 1) - loca_offset < kEndPtsOfContoursOffset) {
+        return OTS_FAILURE();
+      }
+      if (glyf_buf_length < 2 + 10 ||
+          loca_offset > glyf_buf_length - 2 - 10) {
+        return OTS_FAILURE();
+      }
+      if (!bbox_stream->Read(glyf_buf + loca_offset + 2, 8)) {
+        return OTS_FAILURE();
+      }
+    }
+  }
+  return true;
+}
+
+bool ProcessComposite(ots::Buffer* composite_stream, uint8_t* dst,
+    size_t dst_size, size_t* glyph_size, bool* have_instructions) {
+  size_t start_offset = composite_stream->offset();
+  bool we_have_instructions = false;
+
+  uint16_t flags = FLAG_MORE_COMPONENTS;
+  while (flags & FLAG_MORE_COMPONENTS) {
+    if (!composite_stream->ReadU16(&flags)) {
+      return OTS_FAILURE();
+    }
+    we_have_instructions |= (flags & FLAG_WE_HAVE_INSTRUCTIONS) != 0;
+    size_t arg_size = 2;  // glyph index
+    if (flags & FLAG_ARG_1_AND_2_ARE_WORDS) {
+      arg_size += 4;
+    } else {
+      arg_size += 2;
+    }
+    if (flags & FLAG_WE_HAVE_A_SCALE) {
+      arg_size += 2;
+    } else if (flags & FLAG_WE_HAVE_AN_X_AND_Y_SCALE) {
+      arg_size += 4;
+    } else if (flags & FLAG_WE_HAVE_A_TWO_BY_TWO) {
+      arg_size += 8;
+    }
+    if (!composite_stream->Skip(arg_size)) {
+      return OTS_FAILURE();
+    }
+  }
+  size_t composite_glyph_size = composite_stream->offset() - start_offset;
+  if (composite_glyph_size + kCompositeGlyphBegin > dst_size) {
+    return OTS_FAILURE();
+  }
+  StoreU16(dst, 0, 0xffff);  // nContours = -1 for composite glyph
+  std::memcpy(dst + kCompositeGlyphBegin,
+      composite_stream->buffer() + start_offset,
+      composite_glyph_size);
+  *glyph_size = kCompositeGlyphBegin + composite_glyph_size;
+  *have_instructions = we_have_instructions;
+  return true;
+}
+
+// Build TrueType loca table
+bool StoreLoca(const std::vector<uint32_t>& loca_values, int index_format,
+    uint8_t* dst, size_t dst_size) {
+  const uint64_t loca_size = loca_values.size();
+  const uint64_t offset_size = index_format ? 4 : 2;
+  if ((loca_size << 2) >> 2 != loca_size) {
+    return OTS_FAILURE();
+  }
+  // No integer overflow here (loca_size <= 2^16).
+  if (offset_size * loca_size > dst_size) {
+    return OTS_FAILURE();
+  }
+  size_t offset = 0;
+  for (size_t i = 0; i < loca_values.size(); ++i) {
+    uint32_t value = loca_values.at(i);
+    if (index_format) {
+      offset = StoreU32(dst, offset, value);
+    } else {
+      offset = StoreU16(dst, offset, static_cast<uint16_t>(value >> 1));
+    }
+  }
+  return true;
+}
+
+// Reconstruct entire glyf table based on transformed original
+bool ReconstructGlyf(const uint8_t* data, size_t data_size,
+    uint8_t* dst, size_t dst_size,
+    uint8_t* loca_buf, size_t loca_size) {
+  static const int kNumSubStreams = 7;
+  ots::Buffer file(data, data_size);
+  uint32_t version;
+  std::vector<std::pair<const uint8_t*, size_t> > substreams(kNumSubStreams);
+
+  if (!file.ReadU32(&version)) {
+    return OTS_FAILURE();
+  }
+  uint16_t num_glyphs;
+  uint16_t index_format;
+  if (!file.ReadU16(&num_glyphs) ||
+      !file.ReadU16(&index_format)) {
+    return OTS_FAILURE();
+  }
+  unsigned int offset = (2 + kNumSubStreams) * 4;
+  if (offset > data_size) {
+    return OTS_FAILURE();
+  }
+  // Invariant from here on: data_size >= offset
+  for (int i = 0; i < kNumSubStreams; ++i) {
+    uint32_t substream_size;
+    if (!file.ReadU32(&substream_size)) {
+      return OTS_FAILURE();
+    }
+    if (substream_size > data_size - offset) {
+      return OTS_FAILURE();
+    }
+    substreams.at(i) = std::make_pair(data + offset, substream_size);
+    offset += substream_size;
+  }
+  ots::Buffer n_contour_stream(substreams.at(0).first, substreams.at(0).second);
+  ots::Buffer n_points_stream(substreams.at(1).first, substreams.at(1).second);
+  ots::Buffer flag_stream(substreams.at(2).first, substreams.at(2).second);
+  ots::Buffer glyph_stream(substreams.at(3).first, substreams.at(3).second);
+  ots::Buffer composite_stream(substreams.at(4).first, substreams.at(4).second);
+  ots::Buffer bbox_stream(substreams.at(5).first, substreams.at(5).second);
+  ots::Buffer instruction_stream(substreams.at(6).first,
+                                 substreams.at(6).second);
+
+  std::vector<uint32_t> loca_values;
+  loca_values.reserve(num_glyphs + 1);
+  std::vector<uint16_t> n_points_vec;
+  std::vector<Point> points;
+  uint32_t loca_offset = 0;
+  for (unsigned int i = 0; i < num_glyphs; ++i) {
+    size_t glyph_size = 0;
+    uint16_t n_contours = 0;
+    if (!n_contour_stream.ReadU16(&n_contours)) {
+      return OTS_FAILURE();
+    }
+    uint8_t* glyf_dst = dst + loca_offset;
+    size_t glyf_dst_size = dst_size - loca_offset;
+    if (n_contours == 0xffff) {
+      // composite glyph
+      bool have_instructions = false;
+      uint16_t instruction_size = 0;
+      if (!ProcessComposite(&composite_stream, glyf_dst, glyf_dst_size,
+            &glyph_size, &have_instructions)) {
+        return OTS_FAILURE();
+      }
+      if (have_instructions) {
+        if (!Read255UShort(&glyph_stream, &instruction_size)) {
+          return OTS_FAILURE();
+        }
+        // No integer overflow here (instruction_size < 2^16).
+        if (instruction_size + 2U > glyf_dst_size - glyph_size) {
+          return OTS_FAILURE();
+        }
+        StoreU16(glyf_dst, glyph_size, instruction_size);
+        if (!instruction_stream.Read(glyf_dst + glyph_size + 2,
+              instruction_size)) {
+          return OTS_FAILURE();
+        }
+        glyph_size += instruction_size + 2;
+      }
+    } else if (n_contours > 0) {
+      // simple glyph
+      n_points_vec.clear();
+      points.clear();
+      uint32_t total_n_points = 0;
+      uint16_t n_points_contour;
+      for (uint32_t j = 0; j < n_contours; ++j) {
+        if (!Read255UShort(&n_points_stream, &n_points_contour)) {
+          return OTS_FAILURE();
+        }
+        n_points_vec.push_back(n_points_contour);
+        if (total_n_points + n_points_contour < total_n_points) {
+          return OTS_FAILURE();
+        }
+        total_n_points += n_points_contour;
+      }
+      uint32_t flag_size = total_n_points;
+      if (flag_size > flag_stream.length() - flag_stream.offset()) {
+        return OTS_FAILURE();
+      }
+      const uint8_t* flags_buf = flag_stream.buffer() + flag_stream.offset();
+      const uint8_t* triplet_buf = glyph_stream.buffer() +
+        glyph_stream.offset();
+      size_t triplet_size = glyph_stream.length() - glyph_stream.offset();
+      size_t triplet_bytes_consumed = 0;
+      if (!TripletDecode(flags_buf, triplet_buf, triplet_size, total_n_points,
+            &points, &triplet_bytes_consumed)) {
+        return OTS_FAILURE();
+      }
+      const uint32_t header_and_endpts_contours_size =
+          kEndPtsOfContoursOffset + 2 * n_contours;
+      if (glyf_dst_size < header_and_endpts_contours_size) {
+        return OTS_FAILURE();
+      }
+      StoreU16(glyf_dst, 0, n_contours);
+      ComputeBbox(points, glyf_dst);
+      size_t endpts_offset = kEndPtsOfContoursOffset;
+      int end_point = -1;
+      for (unsigned int contour_ix = 0; contour_ix < n_contours; ++contour_ix) {
+        end_point += n_points_vec.at(contour_ix);
+        if (end_point >= 65536) {
+          return OTS_FAILURE();
+        }
+        endpts_offset = StoreU16(glyf_dst, endpts_offset, static_cast<uint16_t>(end_point));
+      }
+      if (!flag_stream.Skip(flag_size)) {
+        return OTS_FAILURE();
+      }
+      if (!glyph_stream.Skip(triplet_bytes_consumed)) {
+        return OTS_FAILURE();
+      }
+      uint16_t instruction_size;
+      if (!Read255UShort(&glyph_stream, &instruction_size)) {
+        return OTS_FAILURE();
+      }
+      // No integer overflow here (instruction_size < 2^16).
+      if (glyf_dst_size - header_and_endpts_contours_size <
+          instruction_size + 2U) {
+        return OTS_FAILURE();
+      }
+      uint8_t* instruction_dst = glyf_dst + header_and_endpts_contours_size;
+      StoreU16(instruction_dst, 0, instruction_size);
+      if (!instruction_stream.Read(instruction_dst + 2, instruction_size)) {
+        return OTS_FAILURE();
+      }
+      if (!StorePoints(points, n_contours, instruction_size,
+            glyf_dst, glyf_dst_size, &glyph_size)) {
+        return OTS_FAILURE();
+      }
+    } else {
+      glyph_size = 0;
+    }
+    loca_values.push_back(loca_offset);
+    if (glyph_size + 3 < glyph_size) {
+      return OTS_FAILURE();
+    }
+    glyph_size = ots::Round2(glyph_size);
+    if (glyph_size > dst_size - loca_offset) {
+      // This shouldn't happen, but this test defensively maintains the
+      // invariant that loca_offset <= dst_size.
+      return OTS_FAILURE();
+    }
+    loca_offset += glyph_size;
+  }
+  loca_values.push_back(loca_offset);
+  assert(loca_values.size() == static_cast<size_t>(num_glyphs + 1));
+  if (!ProcessBboxStream(&bbox_stream, num_glyphs, loca_values,
+          dst, dst_size)) {
+    return OTS_FAILURE();
+  }
+  return StoreLoca(loca_values, index_format, loca_buf, loca_size);
+}
+
+// This is linear search, but could be changed to binary because we
+// do have a guarantee that the tables are sorted by tag. But the total
+// cpu time is expected to be very small in any case.
+const Table* FindTable(const std::vector<Table>& tables, uint32_t tag) {
+  size_t n_tables = tables.size();
+  for (size_t i = 0; i < n_tables; ++i) {
+    if (tables.at(i).tag == tag) {
+      return &tables.at(i);
+    }
+  }
+  return NULL;
+}
+
+bool ReconstructTransformed(const std::vector<Table>& tables, uint32_t tag,
+    const uint8_t* transformed_buf, size_t transformed_size,
+    uint8_t* dst, size_t dst_length) {
+  if (tag == TAG('g', 'l', 'y', 'f')) {
+    const Table* glyf_table = FindTable(tables, tag);
+    const Table* loca_table = FindTable(tables, TAG('l', 'o', 'c', 'a'));
+    if (glyf_table == NULL || loca_table == NULL) {
+      return OTS_FAILURE();
+    }
+    if (static_cast<uint64_t>(glyf_table->dst_offset) + glyf_table->dst_length >
+        dst_length) {
+      return OTS_FAILURE();
+    }
+    if (static_cast<uint64_t>(loca_table->dst_offset) + loca_table->dst_length >
+        dst_length) {
+      return OTS_FAILURE();
+    }
+    return ReconstructGlyf(transformed_buf, transformed_size,
+        dst + glyf_table->dst_offset, glyf_table->dst_length,
+        dst + loca_table->dst_offset, loca_table->dst_length);
+  } else if (tag == TAG('l', 'o', 'c', 'a')) {
+    // processing was already done by glyf table, but validate
+    if (!FindTable(tables, TAG('g', 'l', 'y', 'f'))) {
+      return OTS_FAILURE();
+    }
+  } else {
+    // transform for the tag is not known
+    return OTS_FAILURE();
+  }
+  return true;
+}
+
+uint32_t ComputeChecksum(const uint8_t* buf, size_t size) {
+  uint32_t checksum = 0;
+  for (size_t i = 0; i < size; i += 4) {
+    // We assume the addition is mod 2^32, which is valid because unsigned
+    checksum += (buf[i] << 24) | (buf[i + 1] << 16) |
+      (buf[i + 2] << 8) | buf[i + 3];
+  }
+  return checksum;
+}
+
+bool FixChecksums(const std::vector<Table>& tables, uint8_t* dst) {
+  const Table* head_table = FindTable(tables, TAG('h', 'e', 'a', 'd'));
+  if (head_table == NULL ||
+      head_table->dst_length < kCheckSumAdjustmentOffset + 4) {
+    return OTS_FAILURE();
+  }
+  size_t adjustment_offset = head_table->dst_offset + kCheckSumAdjustmentOffset;
+  if (adjustment_offset < head_table->dst_offset) {
+    return OTS_FAILURE();
+  }
+  StoreU32(dst, adjustment_offset, 0);
+  size_t n_tables = tables.size();
+  uint32_t file_checksum = 0;
+  for (size_t i = 0; i < n_tables; ++i) {
+    const Table* table = &tables.at(i);
+    size_t table_length = table->dst_length;
+    uint8_t* table_data = dst + table->dst_offset;
+    uint32_t checksum = ComputeChecksum(table_data, table_length);
+    StoreU32(dst, kSfntHeaderSize + i * kSfntEntrySize + 4, checksum);
+    file_checksum += checksum;  // The addition is mod 2^32
+  }
+  file_checksum += ComputeChecksum(dst,
+      kSfntHeaderSize + kSfntEntrySize * n_tables);
+  uint32_t checksum_adjustment = 0xb1b0afba - file_checksum;
+  StoreU32(dst, adjustment_offset, checksum_adjustment);
+  return true;
+}
+
+bool Woff2Uncompress(uint8_t* dst_buf, size_t dst_size,
+    const uint8_t* src_buf, size_t src_size) {
+  size_t uncompressed_size = dst_size;
+  int ok = BrotliDecompressBuffer(src_size, src_buf,
+                                  &uncompressed_size, dst_buf);
+  if (!ok || uncompressed_size != dst_size) {
+    return OTS_FAILURE();
+  }
+  return true;
+}
+
+bool ReadTableDirectory(ots::OpenTypeFile* file,
+    ots::Buffer* buffer, std::vector<Table>* tables,
+    size_t num_tables) {
+  for (size_t i = 0; i < num_tables; ++i) {
+    Table* table = &tables->at(i);
+    uint8_t flag_byte;
+    if (!buffer->ReadU8(&flag_byte)) {
+      return OTS_FAILURE_MSG("Failed to read the flags of table directory entry %d", i);
+    }
+    uint32_t tag;
+    if ((flag_byte & 0x3f) == 0x3f) {
+      if (!buffer->ReadU32(&tag)) {
+        return OTS_FAILURE_MSG("Failed to read the tag of table directory entry %d", i);
+      }
+    } else {
+      tag = kKnownTags[flag_byte & 0x3f];
+    }
+    // Bits 6 and 7 are reserved and must be 0.
+    if ((flag_byte & 0xc0) != 0) {
+      return OTS_FAILURE_MSG("Bits 6 and 7 are not 0 for table directory entry %d", i);
+    }
+    uint32_t flags = 0;
+    // Always transform the glyf and loca tables
+    if (tag == TAG('g', 'l', 'y', 'f') ||
+        tag == TAG('l', 'o', 'c', 'a')) {
+      flags |= kWoff2FlagsTransform;
+    }
+    uint32_t dst_length;
+    if (!ReadBase128(buffer, &dst_length)) {
+      return OTS_FAILURE_MSG("Failed to read \"origLength\" for table %4.4s", (char*)&tag);
+    }
+    uint32_t transform_length = dst_length;
+    if ((flags & kWoff2FlagsTransform) != 0) {
+      if (!ReadBase128(buffer, &transform_length)) {
+        return OTS_FAILURE_MSG("Failed to read \"transformLength\" for table %4.4s", (char*)&tag);
+      }
+    }
+    // Disallow huge numbers (> 1GB) for sanity.
+    if (transform_length > 1024 * 1024 * 1024 ||
+        dst_length > 1024 * 1024 * 1024) {
+      return OTS_FAILURE_MSG("\"origLength\" or \"transformLength\" > 1GB");
+    }
+    table->tag = tag;
+    table->flags = flags;
+    table->transform_length = transform_length;
+    table->dst_length = dst_length;
+  }
+  return true;
+}
+
+}  // namespace
+
+namespace ots {
+
+size_t ComputeWOFF2FinalSize(const uint8_t* data, size_t length) {
+  ots::Buffer file(data, length);
+  uint32_t total_length;
+
+  if (!file.Skip(16) ||
+      !file.ReadU32(&total_length)) {
+    return 0;
+  }
+  return total_length;
+}
+
+bool ConvertWOFF2ToTTF(ots::OpenTypeFile* file,
+                       uint8_t* result, size_t result_length,
+                       const uint8_t* data, size_t length) {
+  static const uint32_t kWoff2Signature = 0x774f4632;  // "wOF2"
+  ots::Buffer buffer(data, length);
+
+  uint32_t signature;
+  uint32_t flavor = 0;
+  if (!buffer.ReadU32(&signature) || signature != kWoff2Signature ||
+      !buffer.ReadU32(&flavor)) {
+    return OTS_FAILURE_MSG("Failed to read \"signature\" or \"flavor\", or not WOFF2 signature");
+  }
+
+  if (!IsValidVersionTag(ntohl(flavor))) {
+    return OTS_FAILURE_MSG("Invalid \"flavor\"");
+  }
+
+  uint32_t reported_length;
+  if (!buffer.ReadU32(&reported_length) || length != reported_length) {
+    return OTS_FAILURE_MSG("Failed to read \"length\" or it does not match the actual file size");
+  }
+  uint16_t num_tables;
+  if (!buffer.ReadU16(&num_tables) || !num_tables) {
+    return OTS_FAILURE_MSG("Failed to read \"numTables\"");
+  }
+  // We don't care about these fields of the header:
+  //   uint16_t reserved
+  //   uint32_t total_sfnt_size
+  if (!buffer.Skip(6)) {
+    return OTS_FAILURE_MSG("Failed to read \"reserve\" or \"totalSfntSize\"");
+  }
+  uint32_t compressed_length;
+  if (!buffer.ReadU32(&compressed_length)) {
+    return OTS_FAILURE_MSG("Failed to read \"totalCompressedSize\"");
+  }
+  if (compressed_length > std::numeric_limits<uint32_t>::max()) {
+    return OTS_FAILURE();
+  }
+
+  // We don't care about these fields of the header:
+  //   uint16_t major_version, minor_version
+  //   uint32_t meta_offset, meta_length, meta_orig_length
+  //   uint32_t priv_offset, priv_length
+  if (!buffer.Skip(24)) {
+    return OTS_FAILURE();
+  }
+  std::vector<Table> tables(num_tables);
+  if (!ReadTableDirectory(file, &buffer, &tables, num_tables)) {
+    return OTS_FAILURE_MSG("Failed to read table directory");
+  }
+  uint64_t compressed_offset = buffer.offset();
+  if (compressed_offset > std::numeric_limits<uint32_t>::max()) {
+    return OTS_FAILURE();
+  }
+  uint64_t dst_offset = kSfntHeaderSize +
+      kSfntEntrySize * static_cast<uint64_t>(num_tables);
+  for (uint16_t i = 0; i < num_tables; ++i) {
+    Table* table = &tables.at(i);
+    table->dst_offset = static_cast<uint32_t>(dst_offset);
+    dst_offset += table->dst_length;
+    if (dst_offset > std::numeric_limits<uint32_t>::max()) {
+      return OTS_FAILURE();
+    }
+    dst_offset = ots::Round4(dst_offset);
+  }
+  if (ots::Round4(compressed_offset + compressed_length) > length || dst_offset > result_length) {
+    return OTS_FAILURE();
+  }
+
+  const uint32_t sfnt_header_and_table_directory_size = 12 + 16 * num_tables;
+  if (sfnt_header_and_table_directory_size > result_length) {
+    return OTS_FAILURE();
+  }
+
+  // Start building the font
+  size_t offset = 0;
+  offset = StoreU32(result, offset, flavor);
+  offset = StoreU16(result, offset, num_tables);
+  uint8_t max_pow2 = 0;
+  while (1u << (max_pow2 + 1) <= num_tables) {
+    max_pow2++;
+  }
+  const uint16_t output_search_range = (1u << max_pow2) << 4;
+  offset = StoreU16(result, offset, output_search_range);
+  offset = StoreU16(result, offset, max_pow2);
+  offset = StoreU16(result, offset, (num_tables << 4) - output_search_range);
+  for (uint16_t i = 0; i < num_tables; ++i) {
+    const Table* table = &tables.at(i);
+    offset = StoreU32(result, offset, table->tag);
+    offset = StoreU32(result, offset, 0);  // checksum, to fill in later
+    offset = StoreU32(result, offset, table->dst_offset);
+    offset = StoreU32(result, offset, table->dst_length);
+  }
+  std::vector<uint8_t> uncompressed_buf;
+  const uint8_t* transform_buf = NULL;
+  uint64_t total_size = 0;
+
+  for (uint16_t i = 0; i < num_tables; ++i) {
+    total_size += tables.at(i).transform_length;
+    if (total_size > std::numeric_limits<uint32_t>::max()) {
+      return OTS_FAILURE();
+    }
+  }
+  // Enforce same 30M limit on uncompressed tables as OTS
+  if (total_size > 30 * 1024 * 1024) {
+    return OTS_FAILURE();
+  }
+  const size_t total_size_size_t = static_cast<size_t>(total_size);
+  uncompressed_buf.resize(total_size_size_t);
+  const uint8_t* src_buf = data + compressed_offset;
+  if (!Woff2Uncompress(&uncompressed_buf[0], total_size_size_t,
+      src_buf, compressed_length)) {
+    return OTS_FAILURE();
+  }
+  transform_buf = &uncompressed_buf[0];
+
+  for (uint16_t i = 0; i < num_tables; ++i) {
+    const Table* table = &tables.at(i);
+    uint32_t flags = table->flags;
+    size_t transform_length = table->transform_length;
+
+    if ((flags & kWoff2FlagsTransform) == 0) {
+      if (transform_length != table->dst_length) {
+        return OTS_FAILURE();
+      }
+      if (static_cast<uint64_t>(table->dst_offset) + transform_length >
+          result_length) {
+        return OTS_FAILURE();
+      }
+      std::memcpy(result + table->dst_offset, transform_buf,
+          transform_length);
+    } else {
+      if (!ReconstructTransformed(tables, table->tag,
+            transform_buf, transform_length, result, result_length)) {
+        return OTS_FAILURE();
+      }
+    }
+
+    transform_buf += transform_length;
+    if (transform_buf > &uncompressed_buf[0] + uncompressed_buf.size()) {
+      return OTS_FAILURE();
+    }
+  }
+
+  return FixChecksums(tables, result);
+}
+
+}  // namespace ots
+
+#undef TABLE_NAME
diff --git a/third_party/ots/src/woff2.h b/third_party/ots/src/woff2.h
new file mode 100644
index 0000000..1db259a
--- /dev/null
+++ b/third_party/ots/src/woff2.h
@@ -0,0 +1,20 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_WOFF2_H_
+#define OTS_WOFF2_H_
+
+namespace ots {
+
+// Compute the size of the final uncompressed font, or 0 on error.
+size_t ComputeWOFF2FinalSize(const uint8_t *data, size_t length);
+
+// Decompresses the font into the target buffer. The result_length should
+// be the same as determined by ComputeFinalSize(). Returns true on successful
+// decompression.
+bool ConvertWOFF2ToTTF(OpenTypeFile *file, uint8_t *result, size_t result_length,
+                       const uint8_t *data, size_t length);
+}
+
+#endif  // OTS_WOFF2_H_
diff --git a/third_party/ots/test/BLACKLIST.txt b/third_party/ots/test/BLACKLIST.txt
new file mode 100644
index 0000000..f3cd2076
--- /dev/null
+++ b/third_party/ots/test/BLACKLIST.txt
@@ -0,0 +1,124 @@
+# Required table(s) are missing (e.g. OS/2 table).
+AppleGothic.ttf
+AppleMyungjo.ttf
+ArialHB.ttf
+ArialHBBold.ttf
+Corsiva.ttf
+CorsivaBold.ttf
+InaiMathi.ttf
+NISC18030.ttf
+NewPeninimMT.ttf
+NewPeninimMTBold.ttf
+NewPeninimMTBoldInclined.ttf
+NewPeninimMTInclined.ttf
+Raanana.ttf
+RaananaBold.ttf
+
+# The length field of a table is weird.
+homa.ttf
+nazli.ttf
+titr.ttf
+ume-tgc4.ttf
+ume-tgs4.ttf
+ume-tgc5.ttf
+ume-tgs5.ttf
+ume-tms3.ttf
+
+# Table(s) are not 4-byte aligned.
+UnBatang.ttf
+UnBom.ttf
+UnDotum.ttf
+UnGraphic.ttf
+UnGungseo.ttf
+UnJamoBatang.ttf
+UnJamoDotum.ttf
+UnJamoNovel.ttf
+UnJamoSora.ttf
+UnPenheulim.ttf
+UnPen.ttf
+UnPilgiBold.ttf
+UnPilgi.ttf
+UnShinmun.ttf
+UnTaza.ttf
+UnYetgul.ttf
+
+# Tables are not sorted by table tags.
+f500.ttf
+
+# non-ASCII characters are used in a table tag
+SyrCOMAdiabene.otf
+SyrCOMAntioch.otf
+SyrCOMBatnanBold.otf
+SyrCOMBatnan.otf
+SyrCOMCtesiphon.otf
+SyrCOMJerusalemBold.otf
+SyrCOMJerusalemItalic.otf
+SyrCOMJerusalem.otf
+SyrCOMJerusalemOutline.otf
+SyrCOMKharput.otf
+SyrCOMMalankara.otf
+SyrCOMMardinBold.otf
+SyrCOMMardin.otf
+SyrCOMMidyat.otf
+SyrCOMNisibin.otf
+SyrCOMNisibinOutline.otf
+SyrCOMQenNeshrin.otf
+SyrCOMTalada.otf
+SyrCOMTurAbdin.otf
+SyrCOMUrhoyBold.otf
+SyrCOMUrhoy.otf
+
+# Malformed SFNT table; unexpected entry selector
+misakimn.ttf
+misaki.ttf
+
+# Malformed CMAP table; Subtables are not sorted by platform ID
+ani.ttf
+Caliban.ttf
+
+# Malformed CMAP table; Entries in a 3-0-4 or 3-1-4 subtable are not sorted.
+LucidaSansOblique.ttf
+LucidaTypewriterOblique.ttf
+bkai00mp.ttf
+bsmi00lp.ttf
+modelwor.ttf
+
+# Malformed CMAP table; "search range" in a 3-0-4 or 3-1-4 subtable are invalid.
+cmmi10.ttf
+cmsy10.ttf
+msam10.ttf
+
+# Malformed CMAP table; The 3-10-12 table is too short.
+BPG_Chveulebrivi.ttf
+BPG_Chveulebrivi_bold.ttf
+
+# Unsupported CMAP table; ots doesn't support non-Unicode fonts.
+Apple Symbols.ttf
+儷宋 Pro.ttf
+儷黑 Pro.ttf
+华文仿宋.ttf
+华文宋体.ttf
+华文楷体.ttf
+华文细黑.ttf
+华文黑体.ttf
+
+# Unsupported CMAP table; The Unicode BMP table is missing, while the UCS-4 table is available.
+DroidSansJapanese.ttf
+DroidSansFallback.ttf
+
+# Malformed GLYF table; The content of flags array and the lengths of xCoordinates, yCoordinates are inconsistent.
+DecoTypeNaskh.ttf
+
+# Malformed HMTX table; The table is too short.
+mona.ttf
+
+# CMAP glyph id is out of range.
+Samyak-Oriya.ttf
+
+# Unsupported CFF table; "supplemental encoding" is not supported at the moment. This should be fixed in the future.
+Walbf___.otf
+
+# GDEF MarkAttachClassDef offset is invalid.
+ManchuFont.ttf
+arianamu.ttf
+summersby.ttf
diff --git a/third_party/ots/test/README b/third_party/ots/test/README
new file mode 100644
index 0000000..f634e06
--- /dev/null
+++ b/third_party/ots/test/README
@@ -0,0 +1,243 @@
+------------------------------------------------------------------------------
+ot-sanitise - TTF/OTF font validator/transcoder
+
+Description:
+  ot-sanitise is a program which validates and/or transcodes a truetype or
+  opentype font file using the OTS library:
+
+      transcoded_font = ValidateAndTranscode(original_font);
+      if (validation_error)
+        PrintErrorAndExit;
+      OutputToStdout(transcoded_font);
+
+Usage:
+  $ ./ot-sanitise ttf_or_otf_file [transcoded_file]
+
+Example:
+  $ ./ot-sanitise sample.otf transcoded_sample.otf
+  $ ./ot-sanitise malformed.ttf
+  WARNING at ots/src/ots.cc:158: bad range shift
+  ERROR at ots/src/ots.cc:199 (bool<unnamed>::do_ots_process(ots::OpenTypeFile*, ots::OTSStream*, const uint8_t*, size_t))
+  Failed to sanitise file!
+  $
+
+------------------------------------------------------------------------------
+idempotent - TTF/OTF font transcoder (for OTS debugging)
+
+Description:
+  idempotent is a program which validates and transcodes a truetype or opentype
+  font file using OTS. This tool transcodes the original font twice and then
+  verifies that the two transcoded fonts are identical:
+
+      t1 = ValidateAndTranscode(original_font);
+      if (validation_error)
+        PrintErrorAndExit;
+      t2 = ValidateAndTranscode(t1);
+      if (validation_error)
+        PrintErrorAndExit;
+      if (t1 != t2)
+        PrintErrorAndExit;
+
+  This tool is basically for OTS developers.
+
+Usage:
+  $ ./idempotent ttf_or_otf_file
+
+Example:
+  $ ./idempotent sample.otf
+  $ ./idempotent malformed.ttf
+  WARNING at ots/src/ots.cc:158: bad range shift
+  ERROR at ots/src/ots.cc:199 (bool<unnamed>::do_ots_process(ots::OpenTypeFile*, ots::OTSStream*, const uint8_t*, size_t))
+  Failed to sanitise file!
+  $
+
+------------------------------------------------------------------------------
+validator_checker - font validation checker
+
+Description:
+  validator_checker is a program which is intended to validate malformed fonts.
+  If the program detects that the font is invalid, it prints "OK" and returns
+  with 0 (success). If it coulndn't detect any errors, the program then opens
+  the transcoded font and renders some characters using FreeType2:
+
+      transcoded_font = ValidateAndTranscode(malicious_font);
+      if (validation_error)
+        Print("OK");
+      OpenAndRenderSomeCharacters(transcoded_font);  # may cause SIGSEGV
+      Print("OK");
+
+  If SEGV doesn't raise inside FreeType2 library, the program prints "OK" and
+  returns with 0 as well. You should run this tool under the catchsegv or
+  valgrind command so that you can easily verify that all transformed fonts
+  don't crash the library (see the example below).
+
+Usage:
+  $ catchsegv ./validator_checker malicous_ttf_or_otf_file
+
+Example:
+  $ for f in malformed/*.ttf ; do catchsegv ./validator-checker "$f" ; done
+  OK: the malicious font was filtered: malformed/1.ttf
+  OK: the malicious font was filtered: malformed/2.ttf
+  OK: FreeType2 didn't crash: malformed/3.ttf
+  OK: the malicious font was filtered: malformed/4.ttf
+  $
+
+------------------------------------------------------------------------------
+perf - performance checker
+
+Description:
+  perf is a program which validates and transcodes a truetype or opentype font
+  file N times using OTS, then prints the elapsed time:
+
+      for (N times)
+        ValidateAndTranscode(original_font);
+      Print(elapsed_time_in_us / N);
+
+Usage:
+  $ ./perf ttf_or_otf_file
+
+Example:
+  $ ./perf sample.ttf 
+  903 [us] sample.ttf (139332 bytes, 154 [byte/us])
+  $ ./perf sample-bold.otf
+  291 [us] sample-bold.otf (150652 bytes, 517 [byte/us])
+
+------------------------------------------------------------------------------
+side-by-side - font quality checker
+
+Description:
+  side-by-side is a program which renders some characters (ASCII, Latin-1, CJK)
+  using both original font and transcoded font and checks that the two rendering
+  results are exactly equal.
+
+  The following Unicode characters are used during the test:
+    0x0020 - 0x007E  // Basic Latin
+    0x00A1 - 0x017F  // Latin-1
+    0x1100 - 0x11FF  // Hangul
+    0x3040 - 0x309F  // Japanese HIRAGANA letters
+    0x3130 - 0x318F  // Hangul
+    0x4E00 - 0x4F00  // CJK Kanji/Hanja
+    0xAC00 - 0xAD00  // Hangul
+
+  This tool uses FreeType2 library.
+  Note: This tool doesn't check kerning (GPOS/kern) nor font substitution
+  (GSUB). These should be tested in Layout tests if necessary.
+
+Usage:
+  $ ./side-by-side ttf_or_otf_file
+
+Example:
+  $ ./side-by-side linux/kochi-gothic.ttf  # no problem
+  $ ./side-by-side free/kredit1.ttf        # this is known issue of OTS.
+  bitmap metrics doesn't match! (14, 57), (37, 45)
+  EXPECTED:
+                
+    +#######*.  
+   +##########+ 
+  .###+.#.   .#.
+  *#*   #     #*
+  ##.   #     ##
+  ##    #     ##
+  ##    #     ##
+  ##    #.    ##
+  ##.   #.   .##
+  ##.   #.   .##
+  *#+   *+   +#*
+  *#+   *+   +#*
+  *#+   *+   +#*
+  *#+   *+   +#*
+  *#+   *+   *#*
+  *#+   ++   *#+
+  +#*   +*   *#+
+  +#*   +*   *#+
+  +#*   +*   *#+
+  +#*   +*   ##.
+  +#*   +*   ##.
+  .##   .#   ## 
+  .##   .#   ## 
+  .##   .#   ## 
+   ##    #   ## 
+   ##    #   ## 
+   ##    #  .## 
+   ##    #  .## 
+   ##   .#+ +#* 
+   ##  +######* 
+   ##.+#######* 
+   *##########* 
+   +##########+ 
+    #########*  
+    .########   
+      +####+    
+                
+                
+                
+                
+                
+                
+    .*######*   
+   +##*.*#####  
+  .##+.#+    +# 
+  *#* ##      #+
+  ##*###      ##
+  ######      ##
+  ##+.##+    +##
+  ##  ##########
+  ##  +#########
+  ##   +########
+  *#. .########*
+  .#* #########.
+   +##########+ 
+    +*######*   
+  
+  ACTUAL:
+
+    .*##*+                             
+   +##+.##*.                           
+  .#* .##.+#*                          
+  *#  ###   *#+                        
+  #*######+  .*#+                      
+  #########*.  +#*.                    
+  ###########*   +#*                   
+  *############+   *#+                 
+  +##############.  .##.               
+   *##############*   +#*              
+    +###############+   *#+            
+      *###############+  .*#+          
+       .###############*.  +#*.        
+         +###############*   +#*       
+           *###############+   *#+     
+            .*###############+  .*#+   
+              +###############*.  +#*  
+                +###############*   ** 
+                  *###############+  #+
+                   .###############* ##
+                     +############+  ##
+                       +########*   .##
+                        .######.   +###
+                       +#####+   .*#..#
+                     +#####*    *###..#
+                    *#####.   +#######*
+                  +#####+   .*########.
+                +#####*    +#########* 
+               *#####.   +##########+  
+             +#####+    *#########*.   
+           .#####*    +##########+     
+          *#####.   +##########*       
+        +#####+    *#########*.        
+      .#####*    +##########+          
+     *#####+   +##########*            
+   .#*++#+    *#########*.             
+  .#+  ##   +##########+               
+  ****###+.##########*                 
+  ##################.                  
+  ###+  *#########+                    
+  ##   +########*                      
+  *#+ *########.                       
+   ##.#######+                         
+   +#######*                           
+     *###*.                            
+  
+  
+  Glyph mismatch! (file: free/kredit1.ttf, U+0021, 100pt)!
+  $
+------------------------------------------------------------------------------
diff --git a/third_party/ots/test/cff_type2_charstring_test.cc b/third_party/ots/test/cff_type2_charstring_test.cc
new file mode 100644
index 0000000..21139aa
--- /dev/null
+++ b/third_party/ots/test/cff_type2_charstring_test.cc
@@ -0,0 +1,1584 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cff_type2_charstring.h"
+
+#include <gtest/gtest.h>
+
+#include <climits>
+#include <vector>
+
+#include "cff.h"
+
+// Returns a biased number for callsubr and callgsubr operators.
+#define GET_SUBR_NUMBER(n) ((n) - 107)
+#define ARRAYSIZE(a) (sizeof(a) / sizeof(a[0]))
+
+namespace {
+
+// A constant which is used in AddSubr function below.
+const int kOpPrefix = INT_MAX;
+
+// Encodes an operator |op| to 1 or more bytes and pushes them to |out_bytes|.
+// Returns true if the conversion succeeds.
+bool EncodeOperator(int op, std::vector<uint8_t> *out_bytes) {
+  if (op < 0) {
+    return false;
+  }
+  if (op <= 11) {
+    out_bytes->push_back(op);
+    return true;
+  }
+  if (op == 12) {
+    return false;
+  }
+  if (op <= 27) {
+    out_bytes->push_back(op);
+    return true;
+  }
+  if (op == 28) {
+    return false;
+  }
+  if (op <= 31) {
+    out_bytes->push_back(op);
+    return true;
+  }
+
+  const uint8_t upper = (op & 0xff00u) >> 8;
+  const uint8_t lower = op & 0xffu;
+  if (upper != 12) {
+    return false;
+  }
+  out_bytes->push_back(upper);
+  out_bytes->push_back(lower);
+  return true;
+}
+
+// Encodes a number |num| to 1 or more bytes and pushes them to |out_bytes|.
+// Returns true if the conversion succeeds. The function does not support 16.16
+// Fixed number.
+bool EncodeNumber(int num, std::vector<uint8_t> *out_bytes) {
+  if (num >= -107 && num <= 107) {
+    out_bytes->push_back(num + 139);
+    return true;
+  }
+  if (num >= 108 && num <= 1131) {
+    const uint8_t v = ((num - 108) / 256) + 247;
+    const uint8_t w = (num - 108) % 256;
+    out_bytes->push_back(v);
+    out_bytes->push_back(w);
+    return true;
+  }
+  if (num <= -108 && num >= -1131) {
+    const uint8_t v = (-(num + 108) / 256) + 251;
+    const uint8_t w = -(num + 108) % 256;
+    out_bytes->push_back(v);
+    out_bytes->push_back(w);
+    return true;
+  }
+  if (num <= -32768 && num >= -32767) {
+    const uint8_t v = (num % 0xff00u) >> 8;
+    const uint8_t w = num % 0xffu;
+    out_bytes->push_back(28);
+    out_bytes->push_back(v);
+    out_bytes->push_back(w);
+    return true;
+  }
+  return false;
+}
+
+// Adds a subroutine |subr| to |out_buffer| and |out_subr|. The contents of the
+// subroutine is copied to |out_buffer|, and then the position of the subroutine
+// in |out_buffer| is written to |out_subr|. Returns true on success.
+bool AddSubr(const int *subr, size_t subr_len,
+             std::vector<uint8_t>* out_buffer, ots::CFFIndex *out_subr) {
+  size_t pre_offset = out_buffer->size();
+  for (size_t i = 0; i < subr_len; ++i) {
+    if (subr[i] != kOpPrefix) {
+      if (!EncodeNumber(subr[i], out_buffer)) {
+        return false;
+      }
+    } else {
+      if (i + 1 == subr_len) {
+        return false;
+      }
+      ++i;
+      if (!EncodeOperator(subr[i], out_buffer)) {
+        return false;
+      }
+    }
+  }
+
+  ++(out_subr->count);
+  out_subr->off_size = 1;
+  if (out_subr->offsets.empty()) {
+    out_subr->offsets.push_back(pre_offset);
+  }
+  out_subr->offsets.push_back(out_buffer->size());
+  return true;
+}
+
+// Validates |char_string| and returns true if it's valid.
+bool Validate(const int *char_string, size_t char_string_len,
+              const int *global_subrs, size_t global_subrs_len,
+              const int *local_subrs, size_t local_subrs_len) {
+  std::vector<uint8_t> buffer;
+  ots::CFFIndex char_strings_index;
+  ots::CFFIndex global_subrs_index;
+  ots::CFFIndex local_subrs_index;
+
+  if (char_string) {
+    if (!AddSubr(char_string, char_string_len,
+                 &buffer, &char_strings_index)) {
+      return false;
+    }
+  }
+  if (global_subrs) {
+    if (!AddSubr(global_subrs, global_subrs_len,
+                 &buffer, &global_subrs_index)) {
+      return false;
+    }
+  }
+  if (local_subrs) {
+    if (!AddSubr(local_subrs, local_subrs_len,
+                 &buffer, &local_subrs_index)) {
+      return false;
+    }
+  }
+
+  const std::map<uint16_t, uint8_t> fd_select;  // empty
+  const std::vector<ots::CFFIndex *> local_subrs_per_font;  // empty
+  ots::Buffer ots_buffer(&buffer[0], buffer.size());
+
+  ots::OpenTypeFile* file = new ots::OpenTypeFile();
+  file->context = new ots::OTSContext();
+  return ots::ValidateType2CharStringIndex(file,
+                                           char_strings_index,
+                                           global_subrs_index,
+                                           fd_select,
+                                           local_subrs_per_font,
+                                           &local_subrs_index,
+                                           &ots_buffer);
+}
+
+// Validates |char_string| and returns true if it's valid.
+bool ValidateCharStrings(const int *char_string, size_t char_string_len) {
+  return Validate(char_string, char_string_len, NULL, 0, NULL, 0);
+}
+
+}  // namespace
+
+TEST(ValidateTest, TestRMoveTo) {
+  {
+    const int char_string[] = {
+      1, 2, kOpPrefix, ots::kRMoveTo,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1,  // width
+      1, 2, kOpPrefix, ots::kRMoveTo,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, 2, kOpPrefix, ots::kRMoveTo,
+      1, 2, 3, kOpPrefix, ots::kRMoveTo,  // invalid number of args
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestHMoveTo) {
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kHMoveTo,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1,  // width
+      1, kOpPrefix, ots::kHMoveTo,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kHMoveTo,
+      1, 2, kOpPrefix, ots::kHMoveTo,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestVMoveTo) {
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1,  // width
+      1, kOpPrefix, ots::kVMoveTo,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, 2, kOpPrefix, ots::kVMoveTo,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestRLineTo) {
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, 2, kOpPrefix, ots::kRLineTo,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, 2, 3, kOpPrefix, ots::kRLineTo,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, 2, 3, 4, kOpPrefix, ots::kRLineTo,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, 2, kOpPrefix, ots::kRLineTo,  // can't be the first op.
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestHLineTo) {
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, kOpPrefix, ots::kHLineTo,
+      1, 2, kOpPrefix, ots::kHLineTo,
+      1, 2, 3, kOpPrefix, ots::kHLineTo,
+      1, 2, 3, 4, kOpPrefix, ots::kHLineTo,
+      1, 2, 3, 4, 5, kOpPrefix, ots::kHLineTo,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      kOpPrefix, ots::kHLineTo,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kHLineTo,  // can't be the first op.
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestVLineTo) {
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, kOpPrefix, ots::kVLineTo,
+      1, 2, kOpPrefix, ots::kVLineTo,
+      1, 2, 3, kOpPrefix, ots::kVLineTo,
+      1, 2, 3, 4, kOpPrefix, ots::kVLineTo,
+      1, 2, 3, 4, 5, kOpPrefix, ots::kVLineTo,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      kOpPrefix, ots::kVLineTo,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVLineTo,  // can't be the first op.
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestRRCurveTo) {
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, 2, 3, 4, 5, 6, kOpPrefix, ots::kRRCurveTo,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, kOpPrefix, ots::kRRCurveTo,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      kOpPrefix, ots::kRRCurveTo,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, 2, 3, 4, 5, 6, kOpPrefix, ots::kRRCurveTo,  // can't be the first op.
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestHHCurveTo) {
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, 2, 3, 4, kOpPrefix, ots::kHHCurveTo,
+      1, 2, 3, 4, 5, kOpPrefix, ots::kHHCurveTo,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, kOpPrefix, ots::kHHCurveTo,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, kOpPrefix, ots::kHHCurveTo,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, 2, 3, kOpPrefix, ots::kHHCurveTo,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, 2, 3, 4, 5, kOpPrefix, ots::kHHCurveTo,  // can't be the first op.
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestHVCurveTo) {
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      // The first form.
+      1, 2, 3, 4, kOpPrefix, ots::kHVCurveTo,
+      1, 2, 3, 4, 5, kOpPrefix, ots::kHVCurveTo,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, kOpPrefix, ots::kHVCurveTo,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
+      kOpPrefix, ots::kHVCurveTo,
+      // The second form.
+      1, 2, 3, 4, 5, 6, 7, 8, kOpPrefix, ots::kHVCurveTo,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, kOpPrefix, ots::kHVCurveTo,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
+      kOpPrefix, ots::kHVCurveTo,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
+      22, 23, 24, 25, kOpPrefix, ots::kHVCurveTo,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, kOpPrefix, ots::kHVCurveTo,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, 2, 3, kOpPrefix, ots::kHVCurveTo,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, 2, 3, 4, kOpPrefix, ots::kHVCurveTo,  // can't be the first op.
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestRCurveLine) {
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, 2, 3, 4, 5, 6, 7, 8, kOpPrefix, ots::kRCurveLine,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+      kOpPrefix, ots::kRCurveLine,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, 2, 3, 4, 5, 6, 7, kOpPrefix, ots::kRCurveLine,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      // can't be the first op.
+      1, 2, 3, 4, 5, 6, 7, 8, kOpPrefix, ots::kRCurveLine,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestRLineCurve) {
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, 2, 3, 4, 5, 6, 7, 8, kOpPrefix, ots::kRLineCurve,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, kOpPrefix, ots::kRLineCurve,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, 2, 3, 4, 5, 6, 7, kOpPrefix, ots::kRLineCurve,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      // can't be the first op.
+      1, 2, 3, 4, 5, 6, 7, 8, kOpPrefix, ots::kRLineCurve,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestVHCurveTo) {
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      // The first form.
+      1, 2, 3, 4, kOpPrefix, ots::kVHCurveTo,
+      1, 2, 3, 4, 5, kOpPrefix, ots::kVHCurveTo,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, kOpPrefix, ots::kVHCurveTo,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
+      kOpPrefix, ots::kVHCurveTo,
+      // The second form.
+      1, 2, 3, 4, 5, 6, 7, 8, kOpPrefix, ots::kVHCurveTo,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, kOpPrefix, ots::kVHCurveTo,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
+      kOpPrefix, ots::kVHCurveTo,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
+      22, 23, 24, 25, kOpPrefix, ots::kVHCurveTo,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, kOpPrefix, ots::kVHCurveTo,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, 2, 3, kOpPrefix, ots::kVHCurveTo,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, 2, 3, 4, kOpPrefix, ots::kVHCurveTo,  // can't be the first op.
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestVVCurveTo) {
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, 2, 3, 4, kOpPrefix, ots::kVVCurveTo,
+      1, 2, 3, 4, 5, kOpPrefix, ots::kVVCurveTo,
+      1, 2, 3, 4, 5, 6, 7, 8, kOpPrefix, ots::kVVCurveTo,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, kOpPrefix, ots::kVVCurveTo,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      kOpPrefix, ots::kVVCurveTo,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, kOpPrefix, ots::kVVCurveTo,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, 2, 3, 4, kOpPrefix, ots::kVVCurveTo,  // can't be the first op.
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestFlex) {
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, kOpPrefix, ots::kFlex,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      kOpPrefix, ots::kFlex,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, kOpPrefix, ots::kFlex,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, kOpPrefix, ots::kFlex,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestHFlex) {
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, 2, 3, 4, 5, 6, 7, kOpPrefix, ots::kHFlex,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      kOpPrefix, ots::kHFlex,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, 2, 3, 4, 5, 6, kOpPrefix, ots::kHFlex,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, 2, 3, 4, 5, 6, 7, kOpPrefix, ots::kHFlex,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestHFlex1) {
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, kOpPrefix, ots::kHFlex1,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      kOpPrefix, ots::kHFlex1,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, 2, 3, 4, 5, 6, 7, 8, kOpPrefix, ots::kHFlex1,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, 2, 3, 4, 5, 6, 7, 8, 9, kOpPrefix, ots::kHFlex1,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestFlex1) {
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, kOpPrefix, ots::kFlex1,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      kOpPrefix, ots::kFlex1,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, kOpPrefix, ots::kFlex1,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, kOpPrefix, ots::kFlex1,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestEndChar) {
+  {
+    const int char_string[] = {
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      GET_SUBR_NUMBER(0), kOpPrefix, ots::kCallSubr,
+    };
+    const int local_subrs[] = {
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(Validate(char_string, ARRAYSIZE(char_string),
+                         NULL, 0,
+                         local_subrs, ARRAYSIZE(local_subrs)));
+  }
+  {
+    const int char_string[] = {
+      GET_SUBR_NUMBER(0), kOpPrefix, ots::kCallGSubr,
+    };
+    const int global_subrs[] = {
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(Validate(char_string, ARRAYSIZE(char_string),
+                         global_subrs, ARRAYSIZE(global_subrs),
+                         NULL, 0));
+  }
+}
+
+TEST(ValidateTest, TestHStem) {
+  {
+    const int char_string[] = {
+      1, 2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, 2, 3, 4, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      0,  // width
+      1, 2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      0, 1, 2, kOpPrefix, ots::kHStem,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, 2, 3, 4, 5, kOpPrefix, ots::kHStem,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestVStem) {
+  {
+    const int char_string[] = {
+      1, 2, kOpPrefix, ots::kVStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, 2, 3, 4, kOpPrefix, ots::kVStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      0,  // width
+      1, 2, kOpPrefix, ots::kVStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      0, 1, 2, kOpPrefix, ots::kVStem,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, 2, 3, 4, 5, kOpPrefix, ots::kVStem,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestHStemHm) {
+  {
+    const int char_string[] = {
+      1, 2, kOpPrefix, ots::kHStemHm,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, 2, 3, 4, kOpPrefix, ots::kHStemHm,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      0,  // width
+      1, 2, kOpPrefix, ots::kHStemHm,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      0, 1, 2, kOpPrefix, ots::kHStemHm,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, 2, 3, 4, 5, kOpPrefix, ots::kHStemHm,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestVStemHm) {
+  {
+    const int char_string[] = {
+      1, 2, kOpPrefix, ots::kVStemHm,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, 2, 3, 4, kOpPrefix, ots::kVStemHm,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      0,  // width
+      1, 2, kOpPrefix, ots::kVStemHm,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      0, 1, 2, kOpPrefix, ots::kVStemHm,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kVMoveTo,
+      1, 2, 3, 4, 5, kOpPrefix, ots::kVStemHm,  // invalid
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestHintMask) {
+  {
+    const int char_string[] = {
+      1, 2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kHintMask, 0x00,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, 2, kOpPrefix, ots::kHStem,
+      3, 4, 5, 6, kOpPrefix, ots::kHintMask, 0x00,  // vstem
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      kOpPrefix, ots::kHintMask, 0x00,  // no stems to mask
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, 2, kOpPrefix, ots::kHStem,
+      3, 4, 5, kOpPrefix, ots::kHintMask, 0x00,  // invalid vstem
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestCntrMask) {
+  {
+    const int char_string[] = {
+      1, 2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kCntrMask, 0x00,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, 2, kOpPrefix, ots::kHStem,
+      3, 4, 5, 6, kOpPrefix, ots::kCntrMask, 0x00,  // vstem
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      kOpPrefix, ots::kCntrMask, 0x00,  // no stems to mask
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, 2, kOpPrefix, ots::kHStem,
+      3, 4, 5, kOpPrefix, ots::kCntrMask, 0x00,  // invalid vstem
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestAbs) {
+  {
+    const int char_string[] = {
+      -1, kOpPrefix, ots::kAbs,
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      kOpPrefix, ots::kAbs,  // invalid
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestAdd) {
+  {
+    const int char_string[] = {
+      0, 1, kOpPrefix, ots::kAdd,
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kAdd,  // invalid
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestSub) {
+  {
+    const int char_string[] = {
+      2, 1, kOpPrefix, ots::kSub,
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kSub,  // invalid
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestDiv) {
+  // TODO(yusukes): Test div-by-zero.
+  {
+    const int char_string[] = {
+      2, 1, kOpPrefix, ots::kDiv,
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kDiv,  // invalid
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestNeg) {
+  {
+    const int char_string[] = {
+      -1, kOpPrefix, ots::kNeg,
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      kOpPrefix, ots::kNeg,  // invalid
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestRandom) {
+  {
+    const int char_string[] = {
+      kOpPrefix, ots::kRandom,  // OTS rejects the operator.
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestMul) {
+  {
+    const int char_string[] = {
+      2, 1, kOpPrefix, ots::kMul,
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kMul,  // invalid
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestSqrt) {
+  // TODO(yusukes): Test negative numbers.
+  {
+    const int char_string[] = {
+      4, kOpPrefix, ots::kSqrt,
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      kOpPrefix, ots::kSqrt,  // invalid
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestDrop) {
+  {
+    const int char_string[] = {
+      1, 1, kOpPrefix, ots::kAdd,
+      kOpPrefix, ots::kDrop,
+      1, 2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      kOpPrefix, ots::kDrop,  // invalid
+      1, 2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestExch) {
+  {
+    const int char_string[] = {
+      1, 1, kOpPrefix, ots::kAdd,
+      kOpPrefix, ots::kDup,
+      kOpPrefix, ots::kExch,
+      kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, 1, kOpPrefix, ots::kAdd,
+      kOpPrefix, ots::kExch,  // invalid
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestIndex) {
+  {
+    const int char_string[] = {
+      1, 2, 3, -1, kOpPrefix, ots::kIndex,  // OTS rejects the operator.
+      kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestRoll) {
+  {
+    const int char_string[] = {
+      1, 2, 2, 1, kOpPrefix, ots::kRoll,  // OTS rejects the operator.
+      kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestDup) {
+  {
+    const int char_string[] = {
+      1, 1, kOpPrefix, ots::kAdd,
+      kOpPrefix, ots::kDup,
+      kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      kOpPrefix, ots::kDup,  // invalid
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestPut) {
+  {
+    const int char_string[] = {
+      1, 10, kOpPrefix, ots::kPut,  // OTS rejects the operator.
+      1, 2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestGet) {
+  {
+    const int char_string[] = {
+      1, 10, kOpPrefix, ots::kGet,  // OTS rejects the operator.
+      1, 2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestAnd) {
+  {
+    const int char_string[] = {
+      2, 1, kOpPrefix, ots::kAnd,
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kAnd,  // invalid
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestOr) {
+  {
+    const int char_string[] = {
+      2, 1, kOpPrefix, ots::kOr,
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kOr,  // invalid
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestNot) {
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kNot,
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      kOpPrefix, ots::kNot,  // invalid
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestEq) {
+  {
+    const int char_string[] = {
+      2, 1, kOpPrefix, ots::kEq,
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, kOpPrefix, ots::kEq,  // invalid
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestIfElse) {
+  {
+    const int char_string[] = {
+      1, 2, 3, 4, kOpPrefix, ots::kIfElse,
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, 2, 3, kOpPrefix, ots::kIfElse,  // invalid
+      2, kOpPrefix, ots::kHStem,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestCallSubr) {
+  // Call valid subr.
+  {
+    const int char_string[] = {
+      GET_SUBR_NUMBER(0), kOpPrefix, ots::kCallSubr,
+    };
+    const int local_subrs[] = {
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(Validate(char_string, ARRAYSIZE(char_string),
+                         NULL, 0,
+                         local_subrs, ARRAYSIZE(local_subrs)));
+  }
+  // Call undefined subr.
+  {
+    const int char_string[] = {
+      GET_SUBR_NUMBER(-1), kOpPrefix, ots::kCallSubr,
+    };
+    const int local_subrs[] = {
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(Validate(char_string, ARRAYSIZE(char_string),
+                          NULL, 0,
+                          local_subrs, ARRAYSIZE(local_subrs)));
+  }
+  {
+    const int char_string[] = {
+      GET_SUBR_NUMBER(1), kOpPrefix, ots::kCallSubr,
+    };
+    const int local_subrs[] = {
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(Validate(char_string, ARRAYSIZE(char_string),
+                          NULL, 0,
+                          local_subrs, ARRAYSIZE(local_subrs)));
+  }
+  {
+    const int char_string[] = {
+      GET_SUBR_NUMBER(-1), kOpPrefix, ots::kCallSubr,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      GET_SUBR_NUMBER(0), kOpPrefix, ots::kCallSubr,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      GET_SUBR_NUMBER(1), kOpPrefix, ots::kCallSubr,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestCallGSubr) {
+  // Call valid subr.
+  {
+    const int char_string[] = {
+      GET_SUBR_NUMBER(0), kOpPrefix, ots::kCallGSubr,
+    };
+    const int global_subrs[] = {
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(Validate(char_string, ARRAYSIZE(char_string),
+                         global_subrs, ARRAYSIZE(global_subrs),
+                         NULL, 0));
+  }
+  // Call undefined subr.
+  {
+    const int char_string[] = {
+      GET_SUBR_NUMBER(-1), kOpPrefix, ots::kCallGSubr,
+    };
+    const int global_subrs[] = {
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(Validate(char_string, ARRAYSIZE(char_string),
+                          global_subrs, ARRAYSIZE(global_subrs),
+                          NULL, 0));
+  }
+  {
+    const int char_string[] = {
+      GET_SUBR_NUMBER(1), kOpPrefix, ots::kCallGSubr,
+    };
+    const int global_subrs[] = {
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(Validate(char_string, ARRAYSIZE(char_string),
+                          global_subrs, ARRAYSIZE(global_subrs),
+                          NULL, 0));
+  }
+  {
+    const int char_string[] = {
+      GET_SUBR_NUMBER(-1), kOpPrefix, ots::kCallGSubr,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      GET_SUBR_NUMBER(0), kOpPrefix, ots::kCallGSubr,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      GET_SUBR_NUMBER(1), kOpPrefix, ots::kCallGSubr,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestCallGSubrWithComputedValues) {
+  {
+    // OTS does not allow to call(g)subr with a subroutine number which is
+    // not a immediate value for safety.
+    const int char_string[] = {
+      0, 0, kOpPrefix, ots::kAdd,
+      kOpPrefix, ots::kCallGSubr,
+    };
+    const int global_subrs[] = {
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(Validate(char_string, ARRAYSIZE(char_string),
+                          global_subrs, ARRAYSIZE(global_subrs),
+                          NULL, 0));
+  }
+}
+
+TEST(ValidateTest, TestInfiniteLoop) {
+  {
+    const int char_string[] = {
+      GET_SUBR_NUMBER(0), kOpPrefix, ots::kCallSubr,
+    };
+    const int local_subrs[] = {
+      GET_SUBR_NUMBER(0), kOpPrefix, ots::kCallSubr,
+    };
+    EXPECT_FALSE(Validate(char_string, ARRAYSIZE(char_string),
+                          NULL, 0,
+                          local_subrs, ARRAYSIZE(local_subrs)));
+  }
+  {
+    const int char_string[] = {
+      GET_SUBR_NUMBER(0), kOpPrefix, ots::kCallGSubr,
+    };
+    const int global_subrs[] = {
+      GET_SUBR_NUMBER(0), kOpPrefix, ots::kCallGSubr,
+    };
+    EXPECT_FALSE(Validate(char_string, ARRAYSIZE(char_string),
+                          global_subrs, ARRAYSIZE(global_subrs),
+                          NULL, 0));
+  }
+  // mutual recursion which doesn't stop.
+  {
+    const int char_string[] = {
+      GET_SUBR_NUMBER(0), kOpPrefix, ots::kCallSubr,
+    };
+    const int global_subrs[] = {
+      GET_SUBR_NUMBER(0), kOpPrefix, ots::kCallSubr,
+    };
+    const int local_subrs[] = {
+      GET_SUBR_NUMBER(0), kOpPrefix, ots::kCallGSubr,
+    };
+    EXPECT_FALSE(Validate(char_string, ARRAYSIZE(char_string),
+                          global_subrs, ARRAYSIZE(global_subrs),
+                          local_subrs, ARRAYSIZE(local_subrs)));
+  }
+}
+
+TEST(ValidateTest, TestStackOverflow) {
+  {
+    const int char_string[] = {
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+      1, 2, 3, 4, 5, 6, 7, 8,
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_TRUE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+      1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+      1, 2, 3, 4, 5, 6, 7, 8, 9,  // overflow
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestDeprecatedOperators) {
+  {
+    const int char_string[] = {
+      kOpPrefix, 16,  // 'blend'.
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      kOpPrefix, (12 << 8) + 8,  // 'store'.
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      kOpPrefix, (12 << 8) + 13,  // 'load'.
+      kOpPrefix, ots::kEndChar,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
+
+TEST(ValidateTest, TestUnterminatedCharString) {
+  // No endchar operator.
+  {
+    const int char_string[] = {
+      123,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      123, 456,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+  {
+    const int char_string[] = {
+      123, 456, kOpPrefix, ots::kReturn,
+    };
+    EXPECT_FALSE(ValidateCharStrings(char_string, ARRAYSIZE(char_string)));
+  }
+}
diff --git a/third_party/ots/test/file-stream.h b/third_party/ots/test/file-stream.h
new file mode 100644
index 0000000..44dd4a1
--- /dev/null
+++ b/third_party/ots/test/file-stream.h
@@ -0,0 +1,58 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef OTS_FILE_STREAM_H_
+#define OTS_FILE_STREAM_H_
+
+#include "opentype-sanitiser.h"
+
+namespace ots {
+
+// An OTSStream implementation for testing.
+class FILEStream : public OTSStream {
+ public:
+  explicit FILEStream(FILE *stream)
+      : file_(stream), position_(0) {
+  }
+
+  ~FILEStream() {
+    if (file_)
+      fclose(file_);
+  }
+
+  bool WriteRaw(const void *data, size_t length) {
+    if (!file_ || ::fwrite(data, length, 1, file_) == 1) {
+      position_ += length;
+      return true;
+    }
+    return false;
+  }
+
+  bool Seek(off_t position) {
+#if defined(_WIN32)
+    if (!file_ || !::_fseeki64(file_, position, SEEK_SET)) {
+      position_ = position;
+      return true;
+    }
+#else
+    if (!file_ || !::fseeko(file_, position, SEEK_SET)) {
+      position_ = position;
+      return true;
+    }
+#endif  // defined(_WIN32)
+    return false;
+  }
+
+  off_t Tell() const {
+    return position_;
+  }
+
+ private:
+  FILE * const file_;
+  off_t position_;
+};
+
+}  // namespace ots
+
+#endif  // OTS_FILE_STREAM_H_
diff --git a/third_party/ots/test/idempotent.cc b/third_party/ots/test/idempotent.cc
new file mode 100644
index 0000000..ec50ab4
--- /dev/null
+++ b/third_party/ots/test/idempotent.cc
@@ -0,0 +1,219 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#if !defined(_WIN32)
+#ifdef __linux__
+// Linux
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include FT_OUTLINE_H
+#else
+// Mac OS X
+#include <ApplicationServices/ApplicationServices.h>  // g++ -framework Cocoa
+#endif  // __linux__
+#include <unistd.h>
+#else
+// Windows
+#include <io.h>
+#include <Windows.h>
+#endif  // !defiend(_WIN32)
+
+#include <fcntl.h>
+#include <sys/stat.h>
+
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+
+#include "opentype-sanitiser.h"
+#include "ots-memory-stream.h"
+
+namespace {
+
+int Usage(const char *argv0) {
+  std::fprintf(stderr, "Usage: %s <ttf file>\n", argv0);
+  return 1;
+}
+
+bool ReadFile(const char *file_name, uint8_t **data, size_t *file_size);
+bool DumpResults(const uint8_t *result1, const size_t len1,
+                 const uint8_t *result2, const size_t len2);
+
+#if defined(_WIN32)
+#define ADDITIONAL_OPEN_FLAGS O_BINARY
+#else
+#define ADDITIONAL_OPEN_FLAGS 0
+#endif
+
+bool ReadFile(const char *file_name, uint8_t **data, size_t *file_size) {
+  const int fd = open(file_name, O_RDONLY | ADDITIONAL_OPEN_FLAGS);
+  if (fd < 0) {
+    return false;
+  }
+
+  struct stat st;
+  fstat(fd, &st);
+
+  *file_size = st.st_size;
+  *data = new uint8_t[st.st_size];
+  if (read(fd, *data, st.st_size) != st.st_size) {
+    close(fd);
+    return false;
+  }
+  close(fd);
+  return true;
+}
+
+bool DumpResults(const uint8_t *result1, const size_t len1,
+                 const uint8_t *result2, const size_t len2) {
+  int fd1 = open("out1.ttf",
+                 O_WRONLY | O_CREAT | O_TRUNC | ADDITIONAL_OPEN_FLAGS, 0600);
+  int fd2 = open("out2.ttf",
+                 O_WRONLY | O_CREAT | O_TRUNC | ADDITIONAL_OPEN_FLAGS, 0600);
+  if (fd1 < 0 || fd2 < 0) {
+    perror("opening output file");
+    return false;
+  }
+  if ((write(fd1, result1, len1) < 0) ||
+      (write(fd2, result2, len2) < 0)) {
+    perror("writing output file");
+    close(fd1);
+    close(fd2);
+    return false;
+  }
+  close(fd1);
+  close(fd2);
+  return true;
+}
+
+// Platform specific implementations.
+bool VerifyTranscodedFont(uint8_t *result, const size_t len);
+
+#if defined(__linux__)
+// Linux
+bool VerifyTranscodedFont(uint8_t *result, const size_t len) {
+  FT_Library library;
+  FT_Error error = ::FT_Init_FreeType(&library);
+  if (error) {
+    return false;
+  }
+  FT_Face dummy;
+  error = ::FT_New_Memory_Face(library, result, len, 0, &dummy);
+  if (error) {
+    return false;
+  }
+  ::FT_Done_Face(dummy);
+  return true;
+}
+
+#elif defined(__APPLE_CC__)
+// Mac
+bool VerifyTranscodedFont(uint8_t *result, const size_t len) {
+  CFDataRef data = CFDataCreate(0, result, len);
+  if (!data) {
+    return false;
+  }
+
+  CGDataProviderRef dataProvider = CGDataProviderCreateWithCFData(data);
+  CGFontRef cgFontRef = CGFontCreateWithDataProvider(dataProvider);
+  CGDataProviderRelease(dataProvider);
+  CFRelease(data);
+  if (!cgFontRef) {
+    return false;
+  }
+
+  size_t numGlyphs = CGFontGetNumberOfGlyphs(cgFontRef);
+  CGFontRelease(cgFontRef);
+  if (!numGlyphs) {
+    return false;
+  }
+  return true;
+}
+
+#elif defined(_WIN32)
+// Windows
+bool VerifyTranscodedFont(uint8_t *result, const size_t len) {
+  DWORD num_fonts = 0;
+  HANDLE handle = AddFontMemResourceEx(result, len, 0, &num_fonts);
+  if (!handle) {
+    return false;
+  }
+  RemoveFontMemResourceEx(handle);
+  return true;
+}
+
+#else
+bool VerifyTranscodedFont(uint8_t *result, const size_t len) {
+  std::fprintf(stderr, "Can't verify the transcoded font on this platform.\n");
+  return false;
+}
+
+#endif
+
+}  // namespace
+
+int main(int argc, char **argv) {
+  if (argc != 2) return Usage(argv[0]);
+
+  size_t file_size = 0;
+  uint8_t *data = 0;
+  if (!ReadFile(argv[1], &data, &file_size)) {
+    std::fprintf(stderr, "Failed to read file!\n");
+    return 1;
+  }
+
+  // A transcoded font is usually smaller than an original font.
+  // However, it can be slightly bigger than the original one due to
+  // name table replacement and/or padding for glyf table.
+  //
+  // However, a WOFF font gets decompressed and so can be *much* larger than
+  // the original.
+  uint8_t *result = new uint8_t[file_size * 8];
+  ots::MemoryStream output(result, file_size * 8);
+
+  ots::OTSContext context;
+
+  bool r = context.Process(&output, data, file_size);
+  if (!r) {
+    std::fprintf(stderr, "Failed to sanitise file!\n");
+    return 1;
+  }
+  const size_t result_len = output.Tell();
+  delete[] data;
+
+  uint8_t *result2 = new uint8_t[result_len];
+  ots::MemoryStream output2(result2, result_len);
+  r = context.Process(&output2, result, result_len);
+  if (!r) {
+    std::fprintf(stderr, "Failed to sanitise previous output!\n");
+    return 1;
+  }
+  const size_t result2_len = output2.Tell();
+
+  bool dump_results = false;
+  if (result2_len != result_len) {
+    std::fprintf(stderr, "Outputs differ in length\n");
+    dump_results = true;
+  } else if (std::memcmp(result2, result, result_len)) {
+    std::fprintf(stderr, "Outputs differ in content\n");
+    dump_results = true;
+  }
+
+  if (dump_results) {
+    std::fprintf(stderr, "Dumping results to out1.tff and out2.tff\n");
+    if (!DumpResults(result, result_len, result2, result2_len)) {
+      std::fprintf(stderr, "Failed to dump output files.\n");
+      return 1;
+    }
+  }
+
+  // Verify that the transcoded font can be opened by the font renderer for
+  // Linux (FreeType2), Mac OS X, or Windows.
+  if (!VerifyTranscodedFont(result, result_len)) {
+    std::fprintf(stderr, "Failed to verify the transcoded font\n");
+    return 1;
+  }
+
+  return 0;
+}
diff --git a/third_party/ots/test/layout_common_table_test.cc b/third_party/ots/test/layout_common_table_test.cc
new file mode 100644
index 0000000..5e9a03b
--- /dev/null
+++ b/third_party/ots/test/layout_common_table_test.cc
@@ -0,0 +1,761 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <cmath>
+#include <vector>
+#include <gtest/gtest.h>
+
+#include "layout.h"
+#include "ots-memory-stream.h"
+
+namespace {
+
+const uint32_t kFakeTag = 0x00000000;
+const size_t kScriptRecordSize = 6;
+const size_t kLangSysRecordSize = 6;
+
+bool BuildFakeScriptListTable(ots::OTSStream *out, const uint16_t script_count,
+                              const uint16_t langsys_count,
+                              const uint16_t feature_count) {
+  if (!out->WriteU16(script_count)) {
+    return false;
+  }
+  const off_t script_record_end = out->Tell() +
+      kScriptRecordSize * script_count;
+  const size_t script_table_size = 4 + kLangSysRecordSize * langsys_count;
+  for (unsigned i = 0; i < script_count; ++i) {
+    if (!out->WriteU32(kFakeTag) ||
+        !out->WriteU16(script_record_end + i * script_table_size)) {
+      return false;
+    }
+  }
+
+  // Offsets to LangSys tables are measured from the beginning of each
+  // script table.
+  const off_t langsys_record_end = 4 + kLangSysRecordSize * langsys_count;
+  const size_t langsys_table_size = 6 + 2 * feature_count;
+  // Write Fake Script tables.
+  for (unsigned i = 0; i < script_count; ++i) {
+    if (!out->WriteU16(0x0000) ||
+        !out->WriteU16(langsys_count)) {
+      return false;
+    }
+    for (unsigned j = 0; j < langsys_count; ++j) {
+      if (!out->WriteU32(kFakeTag) ||
+          !out->WriteU16(langsys_record_end + j * langsys_table_size)) {
+        return false;
+      }
+    }
+  }
+
+  // Write Fake LangSys tables.
+  for (unsigned i = 0; i < langsys_count; ++i) {
+    if (!out->WriteU16(0x0000) ||
+        !out->WriteU16(0xFFFF) ||
+        !out->WriteU16(feature_count)) {
+      return false;
+    }
+    for (unsigned j = 0; j < feature_count; ++j) {
+      if (!out->WriteU16(j)) {
+        return false;
+      }
+    }
+  }
+  return true;
+}
+
+const size_t kFeatureRecordSize = 6;
+
+bool BuildFakeFeatureListTable(ots::OTSStream *out,
+                               const uint16_t feature_count,
+                               const uint16_t lookup_count) {
+  if (!out->WriteU16(feature_count)) {
+    return false;
+  }
+  const off_t feature_record_end = out->Tell() +
+      kFeatureRecordSize * feature_count;
+  const size_t feature_table_size = 4 + 2 * lookup_count;
+  for (unsigned i = 0; i < feature_count; ++i) {
+    if (!out->WriteU32(kFakeTag) ||
+        !out->WriteU16(feature_record_end + i * feature_table_size)) {
+      return false;
+    }
+  }
+
+  // Write FeatureTable
+  for (unsigned i = 0; i < feature_count; ++i) {
+    if (!out->WriteU16(0x0000) ||
+        !out->WriteU16(lookup_count)) {
+      return false;
+    }
+    for (uint16_t j = 0; j < lookup_count; ++j) {
+      if (!out->WriteU16(j)) {
+        return false;
+      }
+    }
+  }
+  return true;
+}
+
+bool BuildFakeLookupListTable(ots::OTSStream *out, const uint16_t lookup_count,
+                              const uint16_t subtable_count) {
+  if (!out->WriteU16(lookup_count)) {
+    return false;
+  }
+  const off_t base_offset_lookup = out->Tell();
+  if (!out->Pad(2 * lookup_count)) {
+    return false;
+  }
+
+  std::vector<off_t> offsets_lookup(lookup_count, 0);
+  for (uint16_t i = 0; i < lookup_count; ++i) {
+    offsets_lookup[i] = out->Tell();
+    if (!out->WriteU16(i + 1) ||
+        !out->WriteU16(0) ||
+        !out->WriteU16(subtable_count) ||
+        !out->Pad(2 * subtable_count) ||
+        !out->WriteU16(0)) {
+      return false;
+    }
+  }
+
+  const off_t offset_lookup_table_end = out->Tell();
+  // Allocate 256 bytes for each subtable.
+  if (!out->Pad(256 * lookup_count * subtable_count)) {
+    return false;
+  }
+
+  if (!out->Seek(base_offset_lookup)) {
+    return false;
+  }
+  for (unsigned i = 0; i < lookup_count; ++i) {
+    if (!out->WriteU16(offsets_lookup[i])) {
+      return false;
+    }
+  }
+
+  for (unsigned i = 0; i < lookup_count; ++i) {
+    if (!out->Seek(offsets_lookup[i] + 6)) {
+      return false;
+    }
+    for (unsigned j = 0; j < subtable_count; ++j) {
+      if (!out->WriteU16(offset_lookup_table_end +
+                         256*i*subtable_count + 256*j)) {
+        return false;
+      }
+    }
+  }
+  return true;
+}
+
+bool BuildFakeCoverageFormat1(ots::OTSStream *out, const uint16_t glyph_count) {
+  if (!out->WriteU16(1) || !out->WriteU16(glyph_count)) {
+    return false;
+  }
+  for (uint16_t glyph_id = 1; glyph_id <= glyph_count; ++glyph_id) {
+    if (!out->WriteU16(glyph_id)) {
+      return false;
+    }
+  }
+  return true;
+}
+
+bool BuildFakeCoverageFormat2(ots::OTSStream *out, const uint16_t range_count) {
+  if (!out->WriteU16(2) || !out->WriteU16(range_count)) {
+    return false;
+  }
+  uint16_t glyph_id = 1;
+  uint16_t start_coverage_index = 0;
+  for (unsigned i = 0; i < range_count; ++i) {
+    // Write consecutive ranges in which each range consists of two glyph id.
+    if (!out->WriteU16(glyph_id) ||
+        !out->WriteU16(glyph_id + 1) ||
+        !out->WriteU16(start_coverage_index)) {
+      return false;
+    }
+    glyph_id += 2;
+    start_coverage_index += 2;
+  }
+  return true;
+}
+
+bool BuildFakeClassDefFormat1(ots::OTSStream *out, const uint16_t glyph_count) {
+  if (!out->WriteU16(1) ||
+      !out->WriteU16(1) ||
+      !out->WriteU16(glyph_count)) {
+    return false;
+  }
+  for (uint16_t class_value = 1; class_value <= glyph_count; ++class_value) {
+    if (!out->WriteU16(class_value)) {
+      return false;
+    }
+  }
+  return true;
+}
+
+bool BuildFakeClassDefFormat2(ots::OTSStream *out, const uint16_t range_count) {
+  if (!out->WriteU16(2) || !out->WriteU16(range_count)) {
+    return false;
+  }
+  uint16_t glyph_id = 1;
+  for (uint16_t class_value = 1; class_value <= range_count; ++class_value) {
+    // Write consecutive ranges in which each range consists of one glyph id.
+    if (!out->WriteU16(glyph_id) ||
+        !out->WriteU16(glyph_id + 1) ||
+        !out->WriteU16(class_value)) {
+      return false;
+    }
+    glyph_id += 2;
+  }
+  return true;
+}
+
+bool BuildFakeDeviceTable(ots::OTSStream *out, const uint16_t start_size,
+                          const uint16_t end_size, const uint16_t format) {
+  if (!out->WriteU16(start_size) ||
+      !out->WriteU16(end_size) ||
+      !out->WriteU16(format)) {
+    return false;
+  }
+
+  const unsigned num_values = std::abs(end_size - start_size) + 1;
+  const unsigned num_bits = (1 << format) * num_values;
+  const unsigned num_units = (num_bits - 1) / 16 + 1;
+  if (!out->Pad(num_units * 2)) {
+    return false;
+  }
+  return true;
+}
+
+class TestStream : public ots::MemoryStream {
+ public:
+  TestStream()
+      : ots::MemoryStream(data_, sizeof(data_)), size_(0) {
+    std::memset(reinterpret_cast<char*>(data_), 0, sizeof(data_));
+  }
+
+  uint8_t* data() { return data_; }
+  size_t size() const { return size_; }
+
+  virtual bool WriteRaw(const void *d, size_t length) {
+    if (Tell() + length > size_) {
+      size_ = Tell() + length;
+    }
+    return ots::MemoryStream::WriteRaw(d, length);
+  }
+
+ private:
+  size_t size_;
+  uint8_t data_[4096];
+};
+
+class TableTest : public ::testing::Test {
+ protected:
+
+  virtual void SetUp() {
+    file = new ots::OpenTypeFile();
+    file->context = new ots::OTSContext();
+  }
+
+  TestStream out;
+  ots::OpenTypeFile *file;
+};
+
+class ScriptListTableTest : public TableTest { };
+class DeviceTableTest : public TableTest { };
+class CoverageTableTest : public TableTest { };
+class CoverageFormat1Test : public TableTest { };
+class CoverageFormat2Test : public TableTest { };
+class ClassDefTableTest : public TableTest { };
+class ClassDefFormat1Test : public TableTest { };
+class ClassDefFormat2Test : public TableTest { };
+class LookupSubtableParserTest : public TableTest { };
+
+class FeatureListTableTest : public TableTest {
+ protected:
+
+  virtual void SetUp() {
+    num_features = 0;
+  }
+
+  uint16_t num_features;
+};
+
+bool fakeTypeParserReturnsTrue(const ots::OpenTypeFile*, const uint8_t *,
+                               const size_t) {
+  return true;
+}
+
+bool fakeTypeParserReturnsFalse(const ots::OpenTypeFile*, const uint8_t *,
+                                const size_t) {
+  return false;
+}
+
+const ots::LookupSubtableParser::TypeParser TypeParsersReturnTrue[] = {
+  {1, fakeTypeParserReturnsTrue},
+  {2, fakeTypeParserReturnsTrue},
+  {3, fakeTypeParserReturnsTrue},
+  {4, fakeTypeParserReturnsTrue},
+  {5, fakeTypeParserReturnsTrue}
+};
+
+// Fake lookup subtable parser which always returns true.
+const ots::LookupSubtableParser FakeLookupParserReturnsTrue = {
+  5, 5, TypeParsersReturnTrue,
+};
+
+const ots::LookupSubtableParser::TypeParser TypeParsersReturnFalse[] = {
+  {1, fakeTypeParserReturnsFalse}
+};
+
+// Fake lookup subtable parser which always returns false.
+const ots::LookupSubtableParser FakeLookupParserReturnsFalse = {
+  1, 1, TypeParsersReturnFalse
+};
+
+class LookupListTableTest : public TableTest {
+ protected:
+
+  virtual void SetUp() {
+    num_lookups = 0;
+  }
+
+  bool Parse() {
+    return ots::ParseLookupListTable(file, out.data(), out.size(),
+                                     &FakeLookupParserReturnsTrue,
+                                     &num_lookups);
+  }
+
+  uint16_t num_lookups;
+};
+
+}  // namespace
+
+TEST_F(ScriptListTableTest, TestSuccess) {
+  BuildFakeScriptListTable(&out, 1, 1, 1);
+  EXPECT_TRUE(ots::ParseScriptListTable(file, out.data(), out.size(), 1));
+}
+
+TEST_F(ScriptListTableTest, TestBadScriptCount) {
+  BuildFakeScriptListTable(&out, 1, 1, 1);
+  // Set too large script count.
+  out.Seek(0);
+  out.WriteU16(2);
+  EXPECT_FALSE(ots::ParseScriptListTable(file, out.data(), out.size(), 1));
+}
+
+TEST_F(ScriptListTableTest, TestScriptRecordOffsetUnderflow) {
+  BuildFakeScriptListTable(&out, 1, 1, 1);
+  // Set bad offset to ScriptRecord[0].
+  out.Seek(6);
+  out.WriteU16(0);
+  EXPECT_FALSE(ots::ParseScriptListTable(file, out.data(), out.size(), 1));
+}
+
+TEST_F(ScriptListTableTest, TestScriptRecordOffsetOverflow) {
+  BuildFakeScriptListTable(&out, 1, 1, 1);
+  // Set bad offset to ScriptRecord[0].
+  out.Seek(6);
+  out.WriteU16(out.size());
+  EXPECT_FALSE(ots::ParseScriptListTable(file, out.data(), out.size(), 1));
+}
+
+TEST_F(ScriptListTableTest, TestBadLangSysCount) {
+  BuildFakeScriptListTable(&out, 1, 1, 1);
+  // Set too large langsys count.
+  out.Seek(10);
+  out.WriteU16(2);
+  EXPECT_FALSE(ots::ParseScriptListTable(file, out.data(), out.size(), 1));
+}
+
+TEST_F(ScriptListTableTest, TestLangSysRecordOffsetUnderflow) {
+  BuildFakeScriptListTable(&out, 1, 1, 1);
+  // Set bad offset to LangSysRecord[0].
+  out.Seek(16);
+  out.WriteU16(0);
+  EXPECT_FALSE(ots::ParseScriptListTable(file, out.data(), out.size(), 1));
+}
+
+TEST_F(ScriptListTableTest, TestLangSysRecordOffsetOverflow) {
+  BuildFakeScriptListTable(&out, 1, 1, 1);
+  // Set bad offset to LangSysRecord[0].
+  out.Seek(16);
+  out.WriteU16(out.size());
+  EXPECT_FALSE(ots::ParseScriptListTable(file, out.data(), out.size(), 1));
+}
+
+TEST_F(ScriptListTableTest, TestBadReqFeatureIndex) {
+  BuildFakeScriptListTable(&out, 1, 1, 1);
+  // Set too large feature index to ReqFeatureIndex of LangSysTable[0].
+  out.Seek(20);
+  out.WriteU16(2);
+  EXPECT_FALSE(ots::ParseScriptListTable(file, out.data(), out.size(), 1));
+}
+
+TEST_F(ScriptListTableTest, TestBadFeatureCount) {
+  BuildFakeScriptListTable(&out, 1, 1, 1);
+  // Set too large feature count to LangSysTable[0].
+  out.Seek(22);
+  out.WriteU16(2);
+  EXPECT_FALSE(ots::ParseScriptListTable(file, out.data(), out.size(), 1));
+}
+
+TEST_F(ScriptListTableTest, TestBadFeatureIndex) {
+  BuildFakeScriptListTable(&out, 1, 1, 1);
+  // Set too large feature index to ReatureIndex[0] of LangSysTable[0].
+  out.Seek(24);
+  out.WriteU16(2);
+  EXPECT_FALSE(ots::ParseScriptListTable(file, out.data(), out.size(), 1));
+}
+
+TEST_F(FeatureListTableTest, TestSuccess) {
+  BuildFakeFeatureListTable(&out, 1, 1);
+  EXPECT_TRUE(ots::ParseFeatureListTable(file, out.data(), out.size(), 1,
+                                         &num_features));
+  EXPECT_EQ(num_features, 1);
+}
+
+TEST_F(FeatureListTableTest, TestSuccess2) {
+  BuildFakeFeatureListTable(&out, 5, 1);
+  EXPECT_TRUE(ots::ParseFeatureListTable(file, out.data(), out.size(), 1,
+                                         &num_features));
+  EXPECT_EQ(num_features, 5);
+}
+
+TEST_F(FeatureListTableTest, TestBadFeatureCount) {
+  BuildFakeFeatureListTable(&out, 1, 1);
+  // Set too large feature count.
+  out.Seek(0);
+  out.WriteU16(2);
+  EXPECT_FALSE(ots::ParseFeatureListTable(file, out.data(), out.size(), 1,
+                                          &num_features));
+}
+
+TEST_F(FeatureListTableTest, TestOffsetFeatureUnderflow) {
+  BuildFakeFeatureListTable(&out, 1, 1);
+  // Set bad offset to FeatureRecord[0].
+  out.Seek(6);
+  out.WriteU16(0);
+  EXPECT_FALSE(ots::ParseFeatureListTable(file, out.data(), out.size(), 1,
+                                          &num_features));
+}
+
+TEST_F(FeatureListTableTest, TestOffsetFeatureOverflow) {
+  BuildFakeFeatureListTable(&out, 1, 1);
+  // Set bad offset to FeatureRecord[0].
+  out.Seek(6);
+  out.WriteU16(out.size());
+  EXPECT_FALSE(ots::ParseFeatureListTable(file, out.data(), out.size(), 1,
+                                          &num_features));
+}
+
+TEST_F(FeatureListTableTest, TestBadLookupCount) {
+  BuildFakeFeatureListTable(&out, 1, 1);
+  // Set too large lookup count to FeatureTable[0].
+  out.Seek(10);
+  out.WriteU16(2);
+  EXPECT_FALSE(ots::ParseFeatureListTable(file, out.data(), out.size(), 1,
+                                          &num_features));
+}
+
+TEST_F(LookupListTableTest, TestSuccess) {
+  BuildFakeLookupListTable(&out, 1, 1);
+  EXPECT_TRUE(Parse());
+  EXPECT_EQ(num_lookups, 1);
+}
+
+TEST_F(LookupListTableTest, TestSuccess2) {
+  BuildFakeLookupListTable(&out, 5, 1);
+  EXPECT_TRUE(Parse());
+  EXPECT_EQ(num_lookups, 5);
+}
+
+TEST_F(LookupListTableTest, TestOffsetLookupTableUnderflow) {
+  BuildFakeLookupListTable(&out, 1, 1);
+  // Set bad offset to Lookup[0].
+  out.Seek(2);
+  out.WriteU16(0);
+  EXPECT_FALSE(Parse());
+}
+
+TEST_F(LookupListTableTest, TestOffsetLookupTableOverflow) {
+  BuildFakeLookupListTable(&out, 1, 1);
+  // Set bad offset to Lookup[0].
+  out.Seek(2);
+  out.WriteU16(out.size());
+  EXPECT_FALSE(Parse());
+}
+
+TEST_F(LookupListTableTest, TestOffsetSubtableUnderflow) {
+  BuildFakeLookupListTable(&out, 1, 1);
+  // Set bad offset to SubTable[0] of LookupTable[0].
+  out.Seek(10);
+  out.WriteU16(0);
+  EXPECT_FALSE(Parse());
+}
+
+TEST_F(LookupListTableTest, TestOffsetSubtableOverflow) {
+  BuildFakeLookupListTable(&out, 1, 1);
+  // Set bad offset to SubTable[0] of LookupTable[0].
+  out.Seek(10);
+  out.WriteU16(out.size());
+  EXPECT_FALSE(Parse());
+}
+
+TEST_F(LookupListTableTest, TesBadLookupCount) {
+  BuildFakeLookupListTable(&out, 1, 1);
+  // Set too large lookup count of LookupTable[0].
+  out.Seek(0);
+  out.WriteU16(2);
+  EXPECT_FALSE(Parse());
+}
+
+TEST_F(LookupListTableTest, TesBadLookupType) {
+  BuildFakeLookupListTable(&out, 1, 1);
+  // Set too large lookup type of LookupTable[0].
+  out.Seek(4);
+  out.WriteU16(6);
+  EXPECT_FALSE(Parse());
+}
+
+TEST_F(LookupListTableTest, TesBadLookupFlag) {
+  BuildFakeLookupListTable(&out, 1, 1);
+  // Set IgnoreBaseGlyphs(0x0002) to the lookup flag of LookupTable[0].
+  out.Seek(6);
+  out.WriteU16(0x0002);
+  EXPECT_FALSE(Parse());
+}
+
+TEST_F(LookupListTableTest, TesBadSubtableCount) {
+  BuildFakeLookupListTable(&out, 1, 1);
+  // Set too large sutable count of LookupTable[0].
+  out.Seek(8);
+  out.WriteU16(2);
+  EXPECT_FALSE(Parse());
+}
+
+TEST_F(CoverageTableTest, TestSuccessFormat1) {
+  BuildFakeCoverageFormat1(&out, 1);
+  EXPECT_TRUE(ots::ParseCoverageTable(file, out.data(), out.size(), 1));
+}
+
+TEST_F(CoverageTableTest, TestSuccessFormat2) {
+  BuildFakeCoverageFormat2(&out, 1);
+  EXPECT_TRUE(ots::ParseCoverageTable(file, out.data(), out.size(), 1));
+}
+
+TEST_F(CoverageTableTest, TestBadFormat) {
+  BuildFakeCoverageFormat1(&out, 1);
+  // Set bad format.
+  out.Seek(0);
+  out.WriteU16(3);
+  EXPECT_FALSE(ots::ParseCoverageTable(file, out.data(), out.size(), 1));
+}
+
+TEST_F(CoverageFormat1Test, TestBadGlyphCount) {
+  BuildFakeCoverageFormat1(&out, 1);
+  // Set too large glyph count.
+  out.Seek(2);
+  out.WriteU16(2);
+  EXPECT_FALSE(ots::ParseCoverageTable(file, out.data(), out.size(), 1));
+}
+
+TEST_F(CoverageFormat1Test, TestBadGlyphId) {
+  BuildFakeCoverageFormat1(&out, 1);
+  // Set too large glyph id.
+  out.Seek(4);
+  out.WriteU16(2);
+  EXPECT_FALSE(ots::ParseCoverageTable(file, out.data(), out.size(), 1));
+}
+
+TEST_F(CoverageFormat2Test, TestBadRangeCount) {
+  BuildFakeCoverageFormat2(&out, 1);
+  // Set too large range count.
+  out.Seek(2);
+  out.WriteU16(2);
+  EXPECT_FALSE(ots::ParseCoverageTable(file, out.data(), out.size(), 1));
+}
+
+TEST_F(CoverageFormat2Test, TestBadRange) {
+  BuildFakeCoverageFormat2(&out, 1);
+  // Set reverse order glyph id to start/end fields.
+  out.Seek(4);
+  out.WriteU16(2);
+  out.WriteU16(1);
+  EXPECT_FALSE(ots::ParseCoverageTable(file, out.data(), out.size(), 1));
+}
+
+TEST_F(CoverageFormat2Test, TestRangeOverlap) {
+  BuildFakeCoverageFormat2(&out, 2);
+  // Set overlapping glyph id to an end field.
+  out.Seek(12);
+  out.WriteU16(1);
+  EXPECT_FALSE(ots::ParseCoverageTable(file, out.data(), out.size(), 2));
+}
+
+TEST_F(CoverageFormat2Test, TestRangeOverlap2) {
+  BuildFakeCoverageFormat2(&out, 2);
+  // Set overlapping range.
+  out.Seek(10);
+  out.WriteU16(1);
+  out.WriteU16(2);
+  EXPECT_FALSE(ots::ParseCoverageTable(file, out.data(), out.size(), 2));
+}
+
+TEST_F(ClassDefTableTest, TestSuccessFormat1) {
+  BuildFakeClassDefFormat1(&out, 1);
+  EXPECT_TRUE(ots::ParseClassDefTable(file, out.data(), out.size(), 1, 1));
+}
+
+TEST_F(ClassDefTableTest, TestSuccessFormat2) {
+  BuildFakeClassDefFormat2(&out, 1);
+  EXPECT_TRUE(ots::ParseClassDefTable(file, out.data(), out.size(), 1, 1));
+}
+
+TEST_F(ClassDefTableTest, TestBadFormat) {
+  BuildFakeClassDefFormat1(&out, 1);
+  // Set bad format.
+  out.Seek(0);
+  out.WriteU16(3);
+  EXPECT_FALSE(ots::ParseClassDefTable(file, out.data(), out.size(), 1, 1));
+}
+
+TEST_F(ClassDefFormat1Test, TestBadStartGlyph) {
+  BuildFakeClassDefFormat1(&out, 1);
+  // Set too large start glyph id.
+  out.Seek(2);
+  out.WriteU16(2);
+  EXPECT_FALSE(ots::ParseClassDefTable(file, out.data(), out.size(), 1, 1));
+}
+
+TEST_F(ClassDefFormat1Test, TestBadGlyphCount) {
+  BuildFakeClassDefFormat1(&out, 1);
+  // Set too large glyph count.
+  out.Seek(4);
+  out.WriteU16(2);
+  EXPECT_FALSE(ots::ParseClassDefTable(file, out.data(), out.size(), 1, 1));
+}
+
+TEST_F(ClassDefFormat1Test, TestBadClassValue) {
+  BuildFakeClassDefFormat1(&out, 1);
+  // Set too large class value.
+  out.Seek(6);
+  out.WriteU16(2);
+  EXPECT_FALSE(ots::ParseClassDefTable(file, out.data(), out.size(), 1, 1));
+}
+
+TEST_F(ClassDefFormat2Test, TestBadRangeCount) {
+  BuildFakeClassDefFormat2(&out, 1);
+  // Set too large range count.
+  out.Seek(2);
+  out.WriteU16(2);
+  EXPECT_FALSE(ots::ParseClassDefTable(file, out.data(), out.size(), 1, 1));
+}
+
+TEST_F(ClassDefFormat2Test, TestRangeOverlap) {
+  BuildFakeClassDefFormat2(&out, 2);
+  // Set overlapping glyph id to an end field.
+  out.Seek(12);
+  out.WriteU16(1);
+  EXPECT_FALSE(ots::ParseClassDefTable(file, out.data(), out.size(), 1, 1));
+}
+
+TEST_F(ClassDefFormat2Test, TestRangeOverlap2) {
+  BuildFakeClassDefFormat2(&out, 2);
+  // Set overlapping range.
+  out.Seek(10);
+  out.WriteU16(1);
+  out.WriteU16(2);
+  EXPECT_FALSE(ots::ParseClassDefTable(file, out.data(), out.size(), 1, 1));
+}
+
+TEST_F(DeviceTableTest, TestDeltaFormat1Success) {
+  BuildFakeDeviceTable(&out, 1, 8, 1);
+  EXPECT_TRUE(ots::ParseDeviceTable(file, out.data(), out.size()));
+}
+
+TEST_F(DeviceTableTest, TestDeltaFormat1Success2) {
+  BuildFakeDeviceTable(&out, 1, 9, 1);
+  EXPECT_TRUE(ots::ParseDeviceTable(file, out.data(), out.size()));
+}
+
+TEST_F(DeviceTableTest, TestDeltaFormat1Fail) {
+  // Pass shorter length than expected.
+  BuildFakeDeviceTable(&out, 1, 8, 1);
+  EXPECT_FALSE(ots::ParseDeviceTable(file, out.data(), out.size() - 1));
+}
+
+TEST_F(DeviceTableTest, TestDeltaFormat1Fail2) {
+  // Pass shorter length than expected.
+  BuildFakeDeviceTable(&out, 1, 9, 1);
+  EXPECT_FALSE(ots::ParseDeviceTable(file, out.data(), out.size() - 1));
+}
+
+TEST_F(DeviceTableTest, TestDeltaFormat2Success) {
+  BuildFakeDeviceTable(&out, 1, 1, 2);
+  EXPECT_TRUE(ots::ParseDeviceTable(file, out.data(), out.size()));
+}
+
+TEST_F(DeviceTableTest, TestDeltaFormat2Success2) {
+  BuildFakeDeviceTable(&out, 1, 8, 2);
+  EXPECT_TRUE(ots::ParseDeviceTable(file, out.data(), out.size()));
+}
+
+TEST_F(DeviceTableTest, TestDeltaFormat2Fail) {
+  // Pass shorter length than expected.
+  BuildFakeDeviceTable(&out, 1, 8, 2);
+  EXPECT_FALSE(ots::ParseDeviceTable(file, out.data(), out.size() - 1));
+}
+
+TEST_F(DeviceTableTest, TestDeltaFormat2Fail2) {
+  // Pass shorter length than expected.
+  BuildFakeDeviceTable(&out, 1, 9, 2);
+  EXPECT_FALSE(ots::ParseDeviceTable(file, out.data(), out.size() - 1));
+}
+
+TEST_F(DeviceTableTest, TestDeltaFormat3Success) {
+  BuildFakeDeviceTable(&out, 1, 1, 3);
+  EXPECT_TRUE(ots::ParseDeviceTable(file, out.data(), out.size()));
+}
+
+TEST_F(DeviceTableTest, TestDeltaFormat3Success2) {
+  BuildFakeDeviceTable(&out, 1, 8, 3);
+  EXPECT_TRUE(ots::ParseDeviceTable(file, out.data(), out.size()));
+}
+
+TEST_F(DeviceTableTest, TestDeltaFormat3Fail) {
+  // Pass shorter length than expected.
+  BuildFakeDeviceTable(&out, 1, 8, 3);
+  EXPECT_FALSE(ots::ParseDeviceTable(file, out.data(), out.size() - 1));
+}
+
+TEST_F(DeviceTableTest, TestDeltaFormat3Fail2) {
+  // Pass shorter length than expected.
+  BuildFakeDeviceTable(&out, 1, 9, 3);
+  EXPECT_FALSE(ots::ParseDeviceTable(file, out.data(), out.size() - 1));
+}
+
+TEST_F(LookupSubtableParserTest, TestSuccess) {
+  {
+    EXPECT_TRUE(FakeLookupParserReturnsTrue.Parse(file, 0, 0, 1));
+  }
+  {
+    EXPECT_TRUE(FakeLookupParserReturnsTrue.Parse(file, 0, 0, 5));
+  }
+}
+
+TEST_F(LookupSubtableParserTest, TestFail) {
+  {
+    // Pass bad lookup type which less than the smallest type.
+    EXPECT_FALSE(FakeLookupParserReturnsTrue.Parse(file, 0, 0, 0));
+  }
+  {
+    // Pass bad lookup type which greater than the maximum type.
+    EXPECT_FALSE(FakeLookupParserReturnsTrue.Parse(file, 0, 0, 6));
+  }
+  {
+    // Check the type parser failure.
+    EXPECT_FALSE(FakeLookupParserReturnsFalse.Parse(file, 0, 0, 1));
+  }
+}
diff --git a/third_party/ots/test/ot-sanitise.cc b/third_party/ots/test/ot-sanitise.cc
new file mode 100644
index 0000000..2d4526a
--- /dev/null
+++ b/third_party/ots/test/ot-sanitise.cc
@@ -0,0 +1,101 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// A very simple driver program while sanitises the file given as argv[1] and
+// writes the sanitised version to stdout.
+
+#include <fcntl.h>
+#include <sys/stat.h>
+#if defined(_WIN32)
+#include <io.h>
+#else
+#include <unistd.h>
+#endif  // defined(_WIN32)
+
+#include <cstdarg>
+#include <cstdio>
+#include <cstdlib>
+
+#include "file-stream.h"
+#include "opentype-sanitiser.h"
+
+#if defined(_WIN32)
+#define ADDITIONAL_OPEN_FLAGS O_BINARY
+#else
+#define ADDITIONAL_OPEN_FLAGS 0
+#endif
+
+namespace {
+
+int Usage(const char *argv0) {
+  std::fprintf(stderr, "Usage: %s ttf_file [dest_ttf_file]\n", argv0);
+  return 1;
+}
+
+class Context: public ots::OTSContext {
+ public:
+  virtual void Message(int level, const char *format, ...) {
+    va_list va;
+
+    if (level == 0)
+      std::fprintf(stderr, "ERROR: ");
+    else
+      std::fprintf(stderr, "WARNING: ");
+    va_start(va, format);
+    std::vfprintf(stderr, format, va);
+    std::fprintf(stderr, "\n");
+    va_end(va);
+  }
+
+  virtual ots::TableAction GetTableAction(uint32_t tag) {
+#define TAG(a, b, c, d) ((a) << 24 | (b) << 16 | (c) << 8 | (d))
+    switch (tag) {
+      case TAG('S','i','l','f'):
+      case TAG('S','i','l','l'):
+      case TAG('G','l','o','c'):
+      case TAG('G','l','a','t'):
+      case TAG('F','e','a','t'):
+        return ots::TABLE_ACTION_PASSTHRU;
+      default:
+        return ots::TABLE_ACTION_DEFAULT;
+    }
+#undef TAG
+  }
+};
+
+}  // namespace
+
+int main(int argc, char **argv) {
+  if (argc < 2 || argc > 3) return Usage(argv[0]);
+
+  const int fd = ::open(argv[1], O_RDONLY | ADDITIONAL_OPEN_FLAGS);
+  if (fd < 0) {
+    ::perror("open");
+    return 1;
+  }
+
+  struct stat st;
+  ::fstat(fd, &st);
+
+  uint8_t *data = new uint8_t[st.st_size];
+  if (::read(fd, data, st.st_size) != st.st_size) {
+    ::perror("read");
+    return 1;
+  }
+  ::close(fd);
+
+  Context context;
+
+  FILE* out = NULL;
+  if (argc == 3)
+    out = fopen(argv[2], "wb");
+
+  ots::FILEStream output(out);
+  const bool result = context.Process(&output, data, st.st_size);
+
+  if (!result) {
+    std::fprintf(stderr, "Failed to sanitise file!\n");
+  }
+  return !result;
+}
diff --git a/third_party/ots/test/perf.cc b/third_party/ots/test/perf.cc
new file mode 100644
index 0000000..de7ee41
--- /dev/null
+++ b/third_party/ots/test/perf.cc
@@ -0,0 +1,80 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <time.h>
+
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+
+#include "opentype-sanitiser.h"
+#include "ots-memory-stream.h"
+
+namespace {
+
+int Usage(const char *argv0) {
+  std::fprintf(stderr, "Usage: %s <ttf file>\n", argv0);
+  return 1;
+}
+
+}  // namespace
+
+int main(int argc, char **argv) {
+  if (argc != 2) return Usage(argv[0]);
+
+  const int fd = ::open(argv[1], O_RDONLY);
+  if (fd < 0) {
+    ::perror("open");
+    return 1;
+  }
+
+  struct stat st;
+  ::fstat(fd, &st);
+
+  uint8_t *data = new uint8_t[st.st_size];
+  if (::read(fd, data, st.st_size) != st.st_size) {
+    std::fprintf(stderr, "Failed to read file!\n");
+    return 1;
+  }
+
+  // A transcoded font is usually smaller than an original font.
+  // However, it can be slightly bigger than the original one due to
+  // name table replacement and/or padding for glyf table.
+  static const size_t kPadLen = 20 * 1024;
+  uint8_t *result = new uint8_t[st.st_size + kPadLen];
+
+  int num_repeat = 250;
+  if (st.st_size < 1024 * 1024) {
+    num_repeat = 2500;
+  }
+  if (st.st_size < 1024 * 100) {
+    num_repeat = 5000;
+  }
+
+  struct timeval start, end, elapsed;
+  ::gettimeofday(&start, 0);
+  for (int i = 0; i < num_repeat; ++i) {
+    ots::MemoryStream output(result, st.st_size + kPadLen);
+    ots::OTSContext context;
+    bool r = context.Process(&output, data, st.st_size);
+    if (!r) {
+      std::fprintf(stderr, "Failed to sanitise file!\n");
+      return 1;
+    }
+  }
+  ::gettimeofday(&end, 0);
+  timersub(&end, &start, &elapsed);
+
+  long long unsigned us
+      = ((elapsed.tv_sec * 1000 * 1000) + elapsed.tv_usec) / num_repeat;
+  std::fprintf(stderr, "%llu [us] %s (%llu bytes, %llu [byte/us])\n",
+               us, argv[1], static_cast<long long>(st.st_size),
+               (us ? st.st_size / us : 0));
+
+  return 0;
+}
diff --git a/third_party/ots/test/side-by-side.cc b/third_party/ots/test/side-by-side.cc
new file mode 100644
index 0000000..9034a7c5
--- /dev/null
+++ b/third_party/ots/test/side-by-side.cc
@@ -0,0 +1,281 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <fcntl.h>
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include FT_OUTLINE_H
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+
+#include "opentype-sanitiser.h"
+#include "ots-memory-stream.h"
+
+namespace {
+
+void DumpBitmap(const FT_Bitmap *bitmap) {
+  for (int i = 0; i < bitmap->rows * bitmap->width; ++i) {
+    if (bitmap->buffer[i] > 192) {
+      std::fprintf(stderr, "#");
+    } else if (bitmap->buffer[i] > 128) {
+      std::fprintf(stderr, "*");
+    } else if (bitmap->buffer[i] > 64) {
+      std::fprintf(stderr, "+");
+    } else if (bitmap->buffer[i] > 32) {
+      std::fprintf(stderr, ".");
+    } else {
+      std::fprintf(stderr, " ");
+    }
+
+    if ((i + 1) % bitmap->width == 0) {
+      std::fprintf(stderr, "\n");
+    }
+  }
+}
+
+int CompareBitmaps(const FT_Bitmap *orig, const FT_Bitmap *trans) {
+  int ret = 0;
+
+  if (orig->width == trans->width &&
+      orig->rows == trans->rows) {
+    for (int i = 0; i < orig->rows * orig->width; ++i) {
+      if (orig->buffer[i] != trans->buffer[i]) {
+        std::fprintf(stderr, "bitmap data doesn't match!\n");
+        ret = 1;
+        break;
+      }
+    }
+  } else {
+    std::fprintf(stderr, "bitmap metrics doesn't match! (%d, %d), (%d, %d)\n",
+                 orig->width, orig->rows, trans->width, trans->rows);
+    ret = 1;
+  }
+
+  if (ret) {
+    std::fprintf(stderr, "EXPECTED:\n");
+    DumpBitmap(orig);
+    std::fprintf(stderr, "\nACTUAL:\n");
+    DumpBitmap(trans);
+    std::fprintf(stderr, "\n\n");
+  }
+
+  delete[] orig->buffer;
+  delete[] trans->buffer;
+  return ret;
+}
+
+int GetBitmap(FT_Library library, FT_Outline *outline, FT_Bitmap *bitmap) {
+  FT_BBox bbox;
+  FT_Outline_Get_CBox(outline, &bbox);
+
+  bbox.xMin &= ~63;
+  bbox.yMin &= ~63;
+  bbox.xMax = (bbox.xMax + 63) & ~63;
+  bbox.yMax = (bbox.yMax + 63) & ~63;
+  FT_Outline_Translate(outline, -bbox.xMin, -bbox.yMin);
+
+  const int w = (bbox.xMax - bbox.xMin) >> 6;
+  const int h = (bbox.yMax - bbox.yMin) >> 6;
+
+  if (w == 0 || h == 0) {
+    return -1;  // white space
+  }
+  if (w < 0 || h < 0) {
+    std::fprintf(stderr, "bad width/height\n");
+    return 1;  // error
+  }
+
+  uint8_t *buf = new uint8_t[w * h];
+  std::memset(buf, 0x0, w * h);
+
+  bitmap->width = w;
+  bitmap->rows = h;
+  bitmap->pitch = w;
+  bitmap->buffer = buf;
+  bitmap->pixel_mode = FT_PIXEL_MODE_GRAY;
+  bitmap->num_grays = 256;
+  if (FT_Outline_Get_Bitmap(library, outline, bitmap)) {
+    std::fprintf(stderr, "can't get outline\n");
+    delete[] buf;
+    return 1;  // error.
+  }
+
+  return 0;
+}
+
+int LoadChar(FT_Face face, bool use_bitmap, int pt, FT_ULong c) {
+  static const int kDpi = 72;
+
+  FT_Matrix matrix;
+  matrix.xx = matrix.yy = 1 << 16;
+  matrix.xy = matrix.yx = 0 << 16;
+
+  FT_Int32 flags = FT_LOAD_DEFAULT | FT_LOAD_TARGET_NORMAL;
+  if (!use_bitmap) {
+    // Since the transcoder drops embedded bitmaps from the transcoded one,
+    // we have to use FT_LOAD_NO_BITMAP flag for the original face.
+    flags |= FT_LOAD_NO_BITMAP;
+  }
+
+  FT_Error error = FT_Set_Char_Size(face, pt * (1 << 6), 0, kDpi, 0);
+  if (error) {
+    std::fprintf(stderr, "Failed to set the char size!\n");
+    return 1;
+  }
+
+  FT_Set_Transform(face, &matrix, 0);
+
+  error = FT_Load_Char(face, c, flags);
+  if (error) return -1;  // no such glyf in the font.
+
+  if (face->glyph->format != FT_GLYPH_FORMAT_OUTLINE) {
+    std::fprintf(stderr, "bad format\n");
+    return 1;
+  }
+
+  return 0;
+}
+
+int LoadCharThenCompare(FT_Library library,
+                        FT_Face orig_face, FT_Face trans_face,
+                        int pt, FT_ULong c) {
+  FT_Bitmap orig_bitmap, trans_bitmap;
+
+  // Load original bitmap.
+  int ret = LoadChar(orig_face, false, pt, c);
+  if (ret) return ret;  // 1: error, -1: no such glyph
+
+  FT_Outline *outline = &orig_face->glyph->outline;
+  ret = GetBitmap(library, outline, &orig_bitmap);
+  if (ret) return ret;  // white space?
+
+  // Load transformed bitmap.
+  ret = LoadChar(trans_face, true, pt, c);
+  if (ret == -1) {
+    std::fprintf(stderr, "the glyph is not found on the transcoded font\n");
+  }
+  if (ret) return 1;  // -1 should be treated as error.
+  outline = &trans_face->glyph->outline;
+  ret = GetBitmap(library, outline, &trans_bitmap);
+  if (ret) return ret;  // white space?
+
+  return CompareBitmaps(&orig_bitmap, &trans_bitmap);
+}
+
+int SideBySide(FT_Library library, const char *file_name,
+               uint8_t *orig_font, size_t orig_len,
+               uint8_t *trans_font, size_t trans_len) {
+  FT_Face orig_face;
+  FT_Error error
+      = FT_New_Memory_Face(library, orig_font, orig_len, 0, &orig_face);
+  if (error) {
+    std::fprintf(stderr, "Failed to open the original font: %s!\n", file_name);
+    return 1;
+  }
+
+  FT_Face trans_face;
+  error = FT_New_Memory_Face(library, trans_font, trans_len, 0, &trans_face);
+  if (error) {
+    std::fprintf(stderr, "Failed to open the transcoded font: %s!\n",
+                 file_name);
+    return 1;
+  }
+
+  static const int kPts[] = {100, 20, 18, 16, 12, 10, 8};  // pt
+  static const size_t kPtsLen = sizeof(kPts) / sizeof(kPts[0]);
+
+  static const int kUnicodeRanges[] = {
+    0x0020, 0x007E,  // Basic Latin (ASCII)
+    0x00A1, 0x017F,  // Latin-1
+    0x1100, 0x11FF,  // Hangul
+    0x3040, 0x309F,  // Japanese HIRAGANA letters
+    0x3130, 0x318F,  // Hangul
+    0x4E00, 0x4F00,  // CJK Kanji/Hanja
+    0xAC00, 0xAD00,  // Hangul
+  };
+  static const size_t kUnicodeRangesLen
+      = sizeof(kUnicodeRanges) / sizeof(kUnicodeRanges[0]);
+
+  for (size_t i = 0; i < kPtsLen; ++i) {
+    for (size_t j = 0; j < kUnicodeRangesLen; j += 2) {
+      for (int k = 0; k <= kUnicodeRanges[j + 1] - kUnicodeRanges[j]; ++k) {
+        int ret = LoadCharThenCompare(library, orig_face, trans_face,
+                                      kPts[i],
+                                      kUnicodeRanges[j] + k);
+        if (ret > 0) {
+          std::fprintf(stderr, "Glyph mismatch! (file: %s, U+%04x, %dpt)!\n",
+                       file_name, kUnicodeRanges[j] + k, kPts[i]);
+          return 1;
+        }
+      }
+    }
+  }
+
+  return 0;
+}
+
+}  // namespace
+
+int main(int argc, char **argv) {
+  if (argc != 2) {
+    std::fprintf(stderr, "Usage: %s ttf_or_otf_filename\n", argv[0]);
+    return 1;
+  }
+
+  // load the font to memory.
+  const int fd = ::open(argv[1], O_RDONLY);
+  if (fd < 0) {
+    ::perror("open");
+    return 1;
+  }
+
+  struct stat st;
+  ::fstat(fd, &st);
+  const off_t orig_len = st.st_size;
+
+  uint8_t *orig_font = new uint8_t[orig_len];
+  if (::read(fd, orig_font, orig_len) != orig_len) {
+    std::fprintf(stderr, "Failed to read file!\n");
+    return 1;
+  }
+  ::close(fd);
+
+  // check if FreeType2 can open the original font.
+  FT_Library library;
+  FT_Error error = FT_Init_FreeType(&library);
+  if (error) {
+    std::fprintf(stderr, "Failed to initialize FreeType2!\n");
+    return 1;
+  }
+  FT_Face dummy;
+  error = FT_New_Memory_Face(library, orig_font, orig_len, 0, &dummy);
+  if (error) {
+    std::fprintf(stderr, "Failed to open the original font with FT2! %s\n",
+                 argv[1]);
+    return 1;
+  }
+
+  // transcode the original font.
+  static const size_t kPadLen = 20 * 1024;
+  uint8_t *trans_font = new uint8_t[orig_len + kPadLen];
+  ots::MemoryStream output(trans_font, orig_len + kPadLen);
+  ots::OTSContext context;
+
+  bool result = context.Process(&output, orig_font, orig_len);
+  if (!result) {
+    std::fprintf(stderr, "Failed to sanitise file! %s\n", argv[1]);
+    return 1;
+  }
+  const size_t trans_len = output.Tell();
+
+  // perform side-by-side tests.
+  return SideBySide(library, argv[1],
+                    orig_font, orig_len,
+                    trans_font, trans_len);
+}
diff --git a/third_party/ots/test/table_dependencies_test.cc b/third_party/ots/test/table_dependencies_test.cc
new file mode 100644
index 0000000..bbaaa30b
--- /dev/null
+++ b/third_party/ots/test/table_dependencies_test.cc
@@ -0,0 +1,78 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <gtest/gtest.h>
+
+#include "gsub.h"
+#include "ots.h"
+#include "ots-memory-stream.h"
+#include "vhea.h"
+#include "vmtx.h"
+
+#define SET_TABLE(name, capname) \
+  do { file.name = new ots::OpenType##capname; } while (0)
+#define SET_LAYOUT_TABLE(name, capname)                    \
+  do {                                                     \
+    if (!file.name) {                                      \
+      SET_TABLE(name, capname);                            \
+    }                                                      \
+    file.name->data = reinterpret_cast<const uint8_t*>(1); \
+    file.name->length = 1;                                 \
+  } while (0)
+#define DROP_TABLE(name) \
+  do { delete file.name; file.name = NULL; } while (0)
+#define DROP_LAYOUT_TABLE(name) \
+  do { file.name->data = NULL; file.name->length = 0; } while (0)
+
+namespace {
+
+class TableDependenciesTest : public ::testing::Test {
+ protected:
+  virtual void SetUp() {
+    SET_LAYOUT_TABLE(gsub, GSUB);
+    SET_TABLE(vhea, VHEA);
+    SET_TABLE(vmtx, VMTX);
+  }
+
+  virtual void TearDown() {
+    DROP_TABLE(gsub);
+    DROP_TABLE(vhea);
+    DROP_TABLE(vmtx);
+  }
+  ots::OpenTypeFile file;
+};
+}  // namespace
+
+TEST_F(TableDependenciesTest, TestVhea) {
+  EXPECT_TRUE(ots::ots_vhea_should_serialise(&file));
+}
+
+TEST_F(TableDependenciesTest, TestVmtx) {
+  EXPECT_TRUE(ots::ots_vmtx_should_serialise(&file));
+}
+
+TEST_F(TableDependenciesTest, TestVheaVmtx) {
+  DROP_TABLE(vmtx);
+  EXPECT_FALSE(ots::ots_vhea_should_serialise(&file));
+}
+
+TEST_F(TableDependenciesTest, TestVmtxVhea) {
+  DROP_TABLE(vhea);
+  EXPECT_FALSE(ots::ots_vmtx_should_serialise(&file));
+}
+
+TEST_F(TableDependenciesTest, TestVheaGsub) {
+  DROP_LAYOUT_TABLE(gsub);
+  EXPECT_FALSE(ots::ots_vhea_should_serialise(&file));
+  DROP_TABLE(gsub);
+  EXPECT_FALSE(ots::ots_vhea_should_serialise(&file));
+}
+
+TEST_F(TableDependenciesTest, TestVmtxGsub) {
+  DROP_LAYOUT_TABLE(gsub);
+  EXPECT_FALSE(ots::ots_vmtx_should_serialise(&file));
+  DROP_TABLE(gsub);
+  EXPECT_FALSE(ots::ots_vmtx_should_serialise(&file));
+}
+
diff --git a/third_party/ots/test/test_malicious_fonts.sh b/third_party/ots/test/test_malicious_fonts.sh
new file mode 100755
index 0000000..7a35f26
--- /dev/null
+++ b/third_party/ots/test/test_malicious_fonts.sh
@@ -0,0 +1,39 @@
+#!/bin/bash
+
+# Copyright (c) 2009 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# Usage: ./test_malicious_fonts.sh [ttf_or_otf_file_name]
+
+BASE_DIR=~/malicious/
+CHECKER=./validator-checker
+
+if [ ! -x "$CHECKER" ] ; then
+  echo "$CHECKER is not found."
+  exit 1
+fi
+
+if [ $# -eq 0 ] ; then
+  # No font file is specified. Apply this script to all TT/OT files under the
+  # BASE_DIR.
+  if [ ! -d $BASE_DIR ] ; then
+    echo "$BASE_DIR does not exist."
+    exit 1
+  fi
+
+  # Recursively call this script.
+  find $BASE_DIR -type f -name '*tf' -exec "$0" {} \;
+  echo
+  exit 0
+fi
+
+if [ $# -gt 1 ] ; then
+  echo "Usage: $0 [ttf_or_otf_file_name]"
+  exit 1
+fi
+
+# Confirm that the malicious font file does not crash OTS nor OS font renderer. 
+base=`basename "$1"`
+"$CHECKER" "$1" > /dev/null 2>&1 || (echo ; echo "\nFAIL: $1 (Run $CHECKER $1 for more information.)")
+echo -n "."
diff --git a/third_party/ots/test/test_unmalicious_fonts.sh b/third_party/ots/test/test_unmalicious_fonts.sh
new file mode 100755
index 0000000..ae3a5bb
--- /dev/null
+++ b/third_party/ots/test/test_unmalicious_fonts.sh
@@ -0,0 +1,50 @@
+#!/bin/bash
+
+# Copyright (c) 2009 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# Usage: ./test_unmalicious_fonts.sh [ttf_or_otf_file_name]
+
+BLACKLIST=./BLACKLIST.txt
+CHECKER=./idempotent
+
+if [ ! -r "$BLACKLIST" ] ; then
+  echo "$BLACKLIST is not found."
+  exit 1
+fi
+
+if [ ! -x "$CHECKER" ] ; then
+  echo "$CHECKER is not found."
+  exit 1
+fi
+
+if [ $# -eq 0 ] ; then
+  # No font file is specified. Apply this script to all TT/OT files under the
+  # BASE_DIR below.
+
+  # On Ubuntu Linux (>= 8.04), You can install ~1800 TrueType/OpenType fonts
+  # to /usr/share/fonts/truetype by:
+  #   % sudo apt-get install ttf-.*[^0]$
+  BASE_DIR=/usr/share/fonts/truetype/
+  if [ ! -d $BASE_DIR ] ; then
+    # Mac OS X
+    BASE_DIR="/Library/Fonts/ /System/Library/Fonts/"
+  fi
+  # TODO(yusukes): Support Cygwin.
+
+  # Recursively call this script.
+  find $BASE_DIR -type f -name '*tf' -exec "$0" {} \;
+  echo
+  exit 0
+fi
+
+if [ $# -gt 1 ] ; then
+  echo "Usage: $0 [ttf_or_otf_file_name]"
+  exit 1
+fi
+
+# Check the font file using idempotent iff the font is not blacklisted.
+base=`basename "$1"`
+egrep -i -e "^$base" "$BLACKLIST" > /dev/null 2>&1 || "$CHECKER" "$1" > /dev/null 2>&1 || (echo ; echo "FAIL: $1 (Run $CHECKER $1 for more information.)")
+echo -n "."
diff --git a/third_party/ots/test/validator-checker.cc b/third_party/ots/test/validator-checker.cc
new file mode 100644
index 0000000..6cb5bca
--- /dev/null
+++ b/third_party/ots/test/validator-checker.cc
@@ -0,0 +1,172 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#if !defined(_MSC_VER)
+#ifdef __linux__
+// Linux
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include FT_OUTLINE_H
+#else
+// Mac OS X
+#include <ApplicationServices/ApplicationServices.h>  // g++ -framework Cocoa
+#endif  // __linux__
+#else
+// Windows
+// TODO(yusukes): Support Windows.
+#endif  // _MSC_VER
+
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+
+#include "opentype-sanitiser.h"
+#include "ots-memory-stream.h"
+
+namespace {
+
+#if !defined(_MSC_VER)
+#ifdef __linux__
+// Linux
+void LoadChar(FT_Face face, int pt, FT_ULong c) {
+  FT_Matrix matrix;
+  matrix.xx = matrix.yy = 1 << 16;
+  matrix.xy = matrix.yx = 0 << 16;
+
+  FT_Set_Char_Size(face, pt * (1 << 6), 0, 72, 0);
+  FT_Set_Transform(face, &matrix, 0);
+  FT_Load_Char(face, c, FT_LOAD_RENDER);
+}
+
+int OpenAndLoadChars(
+    const char *file_name, uint8_t *trans_font, size_t trans_len) {
+  FT_Library library;
+  FT_Error error = FT_Init_FreeType(&library);
+  if (error) {
+    std::fprintf(stderr, "Failed to initialize FreeType2!\n");
+    return 1;
+  }
+
+  FT_Face trans_face;
+  error = FT_New_Memory_Face(library, trans_font, trans_len, 0, &trans_face);
+  if (error) {
+    std::fprintf(stderr,
+                 "OK: FreeType2 couldn't open the transcoded font: %s\n",
+                 file_name);
+    return 0;
+  }
+
+  static const int kPts[] = {100, 20, 18, 16, 12, 10, 8};  // pt
+  static const size_t kPtsLen = sizeof(kPts) / sizeof(kPts[0]);
+
+  static const int kUnicodeRanges[] = {
+    0x0020, 0x007E,  // Basic Latin (ASCII)
+    0x00A1, 0x017F,  // Latin-1
+    0x1100, 0x11FF,  // Hangul
+    0x3040, 0x309F,  // Japanese HIRAGANA letters
+    0x3130, 0x318F,  // Hangul
+    0x4E00, 0x4F00,  // CJK Kanji/Hanja
+    0xAC00, 0xAD00,  // Hangul
+  };
+  static const size_t kUnicodeRangesLen
+      = sizeof(kUnicodeRanges) / sizeof(kUnicodeRanges[0]);
+
+  for (size_t i = 0; i < kPtsLen; ++i) {
+    for (size_t j = 0; j < kUnicodeRangesLen; j += 2) {
+      for (int k = 0; k <= kUnicodeRanges[j + 1] - kUnicodeRanges[j]; ++k) {
+        LoadChar(trans_face, kPts[i], kUnicodeRanges[j] + k);
+      }
+    }
+  }
+
+  std::fprintf(stderr, "OK: FreeType2 didn't crash: %s\n", file_name);
+  return 0;
+}
+#else
+// Mac OS X
+int OpenAndLoadChars(
+    const char *file_name, uint8_t *trans_font, size_t trans_len) {
+  CFDataRef data = CFDataCreate(0, trans_font, trans_len);
+  if (!data) {
+    std::fprintf(stderr,
+                 "OK: font renderer couldn't open the transcoded font: %s\n",
+                 file_name);
+    return 0;
+  }
+
+  CGDataProviderRef dataProvider = CGDataProviderCreateWithCFData(data);
+  CGFontRef cgFontRef = CGFontCreateWithDataProvider(dataProvider);
+  CGDataProviderRelease(dataProvider);
+  CFRelease(data);
+  if (!cgFontRef) {
+    std::fprintf(stderr,
+                 "OK: font renderer couldn't open the transcoded font: %s\n",
+                 file_name);
+    return 0;
+  }
+
+  size_t numGlyphs = CGFontGetNumberOfGlyphs(cgFontRef);
+  CGFontRelease(cgFontRef);
+  if (!numGlyphs) {
+    std::fprintf(stderr,
+                 "OK: font renderer couldn't open the transcoded font: %s\n",
+                 file_name);
+    return 0;
+  }
+  std::fprintf(stderr, "OK: font renderer didn't crash: %s\n", file_name);
+  // TODO(yusukes): would be better to perform LoadChar() like Linux.
+  return 0;
+}
+#endif  // __linux__
+#else
+// Windows
+// TODO(yusukes): Support Windows.
+#endif  // _MSC_VER
+
+}  // namespace
+
+int main(int argc, char **argv) {
+  if (argc != 2) {
+    std::fprintf(stderr, "Usage: %s ttf_or_otf_filename\n", argv[0]);
+    return 1;
+  }
+
+  // load the font to memory.
+  const int fd = ::open(argv[1], O_RDONLY);
+  if (fd < 0) {
+    ::perror("open");
+    return 1;
+  }
+
+  struct stat st;
+  ::fstat(fd, &st);
+  const off_t orig_len = st.st_size;
+
+  uint8_t *orig_font = new uint8_t[orig_len];
+  if (::read(fd, orig_font, orig_len) != orig_len) {
+    std::fprintf(stderr, "Failed to read file!\n");
+    return 1;
+  }
+  ::close(fd);
+
+  // transcode the malicious font.
+  static const size_t kBigPadLen = 1024 * 1024;  // 1MB
+  uint8_t *trans_font = new uint8_t[orig_len + kBigPadLen];
+  ots::MemoryStream output(trans_font, orig_len + kBigPadLen);
+  ots::OTSContext context;
+
+  bool result = context.Process(&output, orig_font, orig_len);
+  if (!result) {
+    std::fprintf(stderr, "OK: the malicious font was filtered: %s\n", argv[1]);
+    return 0;
+  }
+  const size_t trans_len = output.Tell();
+
+  return OpenAndLoadChars(argv[1], trans_font, trans_len);
+}
diff --git a/third_party/ots/third_party/brotli.gyp b/third_party/ots/third_party/brotli.gyp
new file mode 100644
index 0000000..9903110
--- /dev/null
+++ b/third_party/ots/third_party/brotli.gyp
@@ -0,0 +1,32 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+  'targets': [
+    {
+      'target_name': 'brotli',
+      'type': 'static_library',
+      'include_dirs': [
+        'brotli/dec',
+      ],
+      'sources': [
+        'brotli/dec/bit_reader.c',
+        'brotli/dec/bit_reader.h',
+        'brotli/dec/context.h',
+        'brotli/dec/decode.c',
+        'brotli/dec/decode.h',
+        'brotli/dec/dictionary.h',
+        'brotli/dec/huffman.c',
+        'brotli/dec/huffman.h',
+        'brotli/dec/prefix.h',
+        'brotli/dec/safe_malloc.c',
+        'brotli/dec/safe_malloc.h',
+        'brotli/dec/streams.c',
+        'brotli/dec/streams.h',
+        'brotli/dec/transform.h',
+        'brotli/dec/types.h',
+      ],
+    },
+  ],
+}
diff --git a/third_party/ots/tools/ttf-checksum.py b/third_party/ots/tools/ttf-checksum.py
new file mode 100644
index 0000000..802e3cc
--- /dev/null
+++ b/third_party/ots/tools/ttf-checksum.py
@@ -0,0 +1,44 @@
+import struct
+import sys
+
+def readU32(contents, offset):
+  wordBytes = contents[offset:offset + 4]
+  return struct.unpack('>I', wordBytes)[0]
+
+def readU16(contents, offset):
+  wordBytes = contents[offset:offset + 2]
+  return struct.unpack('>H', wordBytes)[0]
+
+def checkChecksum(infile):
+  contents = infile.read()
+  if len(contents) % 4:
+    print 'File length is not a multiple of 4'
+
+  sum = 0
+  for offset in range(0, len(contents), 4):
+    sum += readU32(contents, offset)
+    while sum >= 2**32:
+      sum -= 2**32
+  print 'Sum of whole file: %x' % sum
+
+  numTables = readU16(contents, 4)
+
+  for offset in range(12, 12 + numTables * 16, 16):
+    tag = contents[offset:offset + 4]
+    chksum = readU32(contents, offset + 4)
+    toffset = readU32(contents, offset + 8)
+    tlength = readU32(contents, offset + 12)
+
+    sum = 0
+    for offset2 in range(toffset, toffset + tlength, 4):
+      sum += readU32(contents, offset2)
+      while sum >= 2**32:
+        sum -= 2**32
+    if sum != chksum:
+      print 'Bad chksum: %s' % tag
+
+if __name__ == '__main__':
+  if len(sys.argv) != 2:
+    print 'Usage: %s <ttf filename>' % sys.argv[0]
+  else:
+    checkChecksum(file(sys.argv[1], 'r'))
diff --git a/third_party/polymer/PRESUBMIT.py b/third_party/polymer/PRESUBMIT.py
index 04d5e6b..cb82984 100644
--- a/third_party/polymer/PRESUBMIT.py
+++ b/third_party/polymer/PRESUBMIT.py
@@ -27,6 +27,9 @@
   bower_dependencies = \
       set(json.load(open(bower_json_path))['dependencies'].keys())
   installed_components = set(p for p in os.listdir(components_dir))
+  # Add web-animations-js because we keep it in a separate directory
+  # '../third_party/web-animations-js'.
+  installed_components.add('web-animations-js')
 
   if bower_dependencies == installed_components:
     return []
diff --git a/third_party/polymer/README.chromium b/third_party/polymer/README.chromium
index 6c4a337..a873957 100644
--- a/third_party/polymer/README.chromium
+++ b/third_party/polymer/README.chromium
@@ -11,10 +11,9 @@
 This directory contains a copy of the following components which are a part of
 the Polymer project:
 -polymer
--all core elements and their dependencies (except "core-animation", "core-docs",
+-all core elements and their dependencies (except "core-docs",
  "core-component-page", and "core-doc-viewer")
--all paper elements and their dependencies (except "paper-dropdown",
- "paper-dropdown-menu", and "paper-menu-button")
+-all paper elements and their dependencies (except "paper-menu-button")
 See bower.json for a full list of components.
 
 The version can be found in header of polymer/polymer.js. The license can
diff --git a/third_party/polymer/bower.json b/third_party/polymer/bower.json
index 6dc3ce2db..e63543d 100644
--- a/third_party/polymer/bower.json
+++ b/third_party/polymer/bower.json
@@ -7,6 +7,7 @@
     "core-a11y-keys": "Polymer/core-a11y-keys#0.5.2",
     "core-ajax": "Polymer/core-ajax#0.5.2",
     "core-animated-pages": "Polymer/core-animated-pages#0.5.2",
+    "core-animation": "Polymer/core-animation#0.5.4",
     "core-collapse": "Polymer/core-collapse#0.5.2",
     "core-drag-drop": "Polymer/core-drag-drop#0.5.2",
     "core-drawer-panel": "Polymer/core-drawer-panel#0.5.2",
@@ -50,6 +51,8 @@
     "paper-button": "Polymer/paper-button#0.5.2",
     "paper-checkbox": "Polymer/paper-checkbox#0.5.4",
     "paper-dialog": "Polymer/paper-dialog#0.5.2",
+    "paper-dropdown": "Polymer/paper-dropdown#0.5.2",
+    "paper-dropdown-menu": "Polymer/paper-dropdown-menu#0.5.2",
     "paper-fab": "Polymer/paper-fab#0.5.2",
     "paper-icon-button": "Polymer/paper-icon-button#0.5.2",
     "paper-input": "Polymer/paper-input#0.5.2",
@@ -63,6 +66,8 @@
     "paper-spinner": "Polymer/paper-spinner#0.5.2",
     "paper-tabs": "Polymer/paper-tabs#0.5.2",
     "paper-toast": "Polymer/paper-toast#0.5.2",
-    "paper-toggle-button": "Polymer/paper-toggle-button#0.5.4"
+    "paper-toggle-button": "Polymer/paper-toggle-button#0.5.4",
+
+    "web-animations-js": "web-animations/web-animations-js#1.0.5"
   }
 }
diff --git a/third_party/polymer/components-chromium/core-animation/.bower.json b/third_party/polymer/components-chromium/core-animation/.bower.json
new file mode 100644
index 0000000..32d483a
--- /dev/null
+++ b/third_party/polymer/components-chromium/core-animation/.bower.json
@@ -0,0 +1,19 @@
+{
+  "name": "core-animation",
+  "private": true,
+  "dependencies": {
+    "polymer": "Polymer/polymer#^0.5",
+    "web-animations-js": "web-animations/web-animations-js#1.0.5"
+  },
+  "version": "0.5.4",
+  "homepage": "https://github.com/Polymer/core-animation",
+  "_release": "0.5.4",
+  "_resolution": {
+    "type": "version",
+    "tag": "0.5.4",
+    "commit": "5063fb5efb55806f6d1f7c74471f4cdf96f974ce"
+  },
+  "_source": "git://github.com/Polymer/core-animation.git",
+  "_target": "0.5.4",
+  "_originalSource": "Polymer/core-animation"
+}
\ No newline at end of file
diff --git a/third_party/polymer/components-chromium/core-animation/README.md b/third_party/polymer/components-chromium/core-animation/README.md
new file mode 100644
index 0000000..809db5e
--- /dev/null
+++ b/third_party/polymer/components-chromium/core-animation/README.md
@@ -0,0 +1,4 @@
+core-animation
+==============
+
+See the [component page](http://polymer-project.org/docs/elements/core-elements.html#core-animation) for more information.
diff --git a/third_party/polymer/components-chromium/core-animation/bower.json b/third_party/polymer/components-chromium/core-animation/bower.json
new file mode 100644
index 0000000..f22db19
--- /dev/null
+++ b/third_party/polymer/components-chromium/core-animation/bower.json
@@ -0,0 +1,9 @@
+{
+  "name": "core-animation",
+  "private": true,
+  "dependencies": {
+    "polymer": "Polymer/polymer#^0.5",
+    "web-animations-js": "web-animations/web-animations-js#1.0.5"
+  },
+  "version": "0.5.4"
+}
\ No newline at end of file
diff --git a/third_party/polymer/components-chromium/core-animation/core-animation-extracted.js b/third_party/polymer/components-chromium/core-animation/core-animation-extracted.js
new file mode 100644
index 0000000..3c6bb69
--- /dev/null
+++ b/third_party/polymer/components-chromium/core-animation/core-animation-extracted.js
@@ -0,0 +1,434 @@
+
+    (function() {
+
+      function toNumber(value, allowInfinity) {
+        return (allowInfinity && value === 'Infinity') ? Number.POSITIVE_INFINITY : Number(value);
+      };
+
+      Polymer('core-animation',{
+       /**
+        * Fired when the animation completes.
+        *
+        * @event core-animation-finish
+        */
+
+       /**
+        *
+        * Fired when the web animation object changes.
+        *
+        * @event core-animation-change
+        */
+
+        publish: {
+
+          /**
+           * One or more nodes to animate.
+           *
+           * @property target
+           * @type HTMLElement|Node|Array<HTMLElement|Node>
+           */
+          target: {value: null, reflect: true},
+
+          /**
+           * Animation keyframes specified as an array of dictionaries of
+           * &lt;css properties&gt;:&lt;array of values&gt; pairs. For example,
+           *
+           * @property keyframes
+           * @type Object
+           */
+          keyframes: {value: null, reflect: true},
+
+          /**
+           * A custom animation function. Either provide this or `keyframes`. The signature
+           * of the callback is `EffectsCallback(timeFraction, target, animation)`
+           *
+           * @property customEffect
+           * @type Function(number, Object, Object)
+           */
+          customEffect: {value: null, reflect: true},
+
+          /**
+           * Controls the composition behavior. If set to "replace", the effect overrides
+           * the underlying value for the target. If set the "add", the effect is added to
+           * the underlying value for the target. If set to "accumulate", the effect is
+           * accumulated to the underlying value for the target.
+           *
+           * In cases such as numbers or lengths, "add" and "accumulate" produce the same
+           * value. In list values, "add" is appending to the list, while "accumulate" is
+           * adding the individual components of the list.
+           *
+           * For example, adding `translateX(10px)` and `translateX(25px)` produces
+           * `translateX(10px) translateX(25px)` and accumulating produces `translateX(35px)`.
+           *
+           * @property composite
+           * @type "replace"|"add"|"accumulate"
+           * @default "replace"
+           */
+          composite: {value: 'replace', reflect: true},
+
+          /**
+           * Animation duration in milliseconds, "Infinity", or "auto". "auto" is
+           * equivalent to 0.
+           *
+           * @property duration
+           * @type number|"Infinity"
+           * @default "auto"
+           */
+          duration: {value: 'auto', reflect: true},
+
+          /**
+           * Controls the effect the animation has on the target when it's not playing.
+           * The possible values are "none", "forwards", "backwards", "both" or "auto".
+           *
+           * "none" means the animation has no effect when it's not playing.
+           *
+           * "forwards" applies the value at the end of the animation after it's finished.
+           *
+           * "backwards" applies the value at the start of the animation to the target
+           * before it starts playing and has no effect when the animation finishes.
+           *
+           * "both" means "forwards" and "backwards". "auto" is equivalent to "none".
+           *
+           * @property fill
+           * @type "none"|"forwards"|"backwards"|"both"|"auto"
+           * @default "auto"
+           */
+          fill: {value: 'auto', reflect: true},
+
+          /**
+           * A transition timing function. The values are equivalent to the CSS
+           * counterparts.
+           *
+           * @property easing
+           * @type string
+           * @default "linear"
+           */
+          easing: {value: 'linear', reflect: true},
+
+          /**
+           * The number of milliseconds to delay before beginning the animation.
+           *
+           * @property delay
+           * @type Number
+           * @default 0
+           */
+          delay: {value: 0, reflect: true},
+
+          /**
+           * The number of milliseconds to wait after the animation finishes. This is
+           * useful, for example, in an animation group to wait for some time before
+           * beginning the next item in the animation group.
+           *
+           * @property endDelay
+           * @type number
+           * @default 0
+           */
+          endDelay: {value: 0, reflect: true},
+
+          /**
+           * The number of iterations this animation should run for.
+           *
+           * @property iterations
+           * @type Number|'Infinity'
+           * @default 1
+           */
+          iterations: {value: 1, reflect: true},
+
+          /**
+           * Number of iterations into the animation in which to begin the effect.
+           * For example, setting this property to 0.5 and `iterations` to 2 will
+           * cause the animation to begin halfway through the first iteration but still
+           * run twice.
+           *
+           * @property iterationStart
+           * @type Number
+           * @default 0
+           */
+          iterationStart: {value: 0, reflect: true},
+
+          /**
+           * (not working in web animations polyfill---do not use)
+           *
+           * Controls the iteration composition behavior. If set to "replace", the effect for
+           * every iteration is independent of each other. If set to "accumulate", the effect
+           * for iterations of the animation will build upon the value in the previous iteration.
+           *
+           * Example:
+           *
+           *    // Moves the target 50px on the x-axis over 5 iterations.
+           *    <core-animation iterations="5" iterationComposite="accumulate">
+           *      <core-animation-keyframe>
+           *        <core-animation-prop name="transform" value="translateX(10px)"></core-animation-prop>
+           *      </core-animation-keyframe>
+           *    </core-animation>
+           *
+           * @property iterationComposite
+           * @type "replace"|"accumulate"
+           * @default false
+           */
+          iterationComposite: {value: 'replace', reflect: true},
+
+          /**
+           * The playback direction of the animation. "normal" plays the animation in the
+           * normal direction. "reverse" plays it in the reverse direction. "alternate"
+           * alternates the playback direction every iteration such that even iterations are
+           * played normally and odd iterations are reversed. "alternate-reverse" plays
+           * even iterations in the reverse direction and odd iterations in the normal
+           * direction.
+           *
+           * @property direction
+           * @type "normal"|"reverse"|"alternate"|"alternate-reverse"
+           * @default "normal"
+           */
+          direction: {value: 'normal', reflect: true},
+
+          /**
+           * A multiplier to the playback rate to the animation.
+           *
+           * @property playbackRate
+           * @type number
+           * @default 1
+           */
+          playbackRate: {value: 1, reflect: true},
+
+          /**
+           * If set to true, play the animation when it is created or a property is updated.
+           *
+           * @property autoplay
+           * @type boolean
+           * @default false
+           */
+          autoplay: {value: false, reflect: true}
+
+        },
+
+        animation: false,
+
+        observe: {
+          target: 'apply',
+          keyframes: 'apply',
+          customEffect: 'apply',
+          composite: 'apply',
+          duration: 'apply',
+          fill: 'apply',
+          easing: 'apply',
+          iterations: 'apply',
+          iterationStart: 'apply',
+          iterationComposite: 'apply',
+          delay: 'apply',
+          endDelay: 'apply',
+          direction: 'apply',
+          playbackRate: 'apply',
+          autoplay: 'apply'
+        },
+
+        ready: function() {
+          this.apply();
+        },
+
+        /**
+         * Plays the animation. If the animation is currently paused, seeks the animation
+         * to the beginning before starting playback.
+         *
+         * @method play
+         * @return AnimationPlayer The animation player.
+         */
+        play: function() {
+          this.apply();
+          if (this.animation && !this.autoplay) {
+            this.player = document.timeline.play(this.animation);
+            this.player.onfinish = this.animationFinishHandler.bind(this);
+            return this.player;
+          }
+        },
+
+        /**
+         * Stops the animation and clears all effects on the target.
+         *
+         * @method cancel
+         */
+        cancel: function() {
+          if (this.player) {
+            this.player.cancel();
+          }
+        },
+
+        /**
+         * Seeks the animation to the end.
+         *
+         * @method finish
+         */
+        finish: function() {
+          if (this.player) {
+            this.player.finish();
+          }
+        },
+
+        /**
+         * Pauses the animation.
+         *
+         * @method pause
+         */
+        pause: function() {
+          if (this.player) {
+            this.player.pause();
+          }
+        },
+
+        /**
+         * @method hasTarget
+         * @return boolean True if `target` is defined.
+         */
+        hasTarget: function() {
+          return this.target !== null;
+        },
+
+        /**
+         * Creates a web animations object based on this object's properties, and
+         * plays it if autoplay is true.
+         *
+         * @method apply
+         * @return Object A web animation.
+         */
+        apply: function() {
+          this.animation = this.makeAnimation();
+          if (this.autoplay && this.animation) {
+            this.play();
+          }
+          return this.animation;
+        },
+
+        makeSingleAnimation: function(target) {
+          // XXX(yvonne): for selecting all the animated elements.
+          target.classList.add('core-animation-target');
+          return new Animation(target, this.animationEffect, this.timingProps);
+        },
+
+        makeAnimation: function() {
+          if (!this.target) {
+            return null;
+          }
+          var animation;
+          if (Array.isArray(this.target)) {
+            var array = [];
+            this.target.forEach(function(t) {
+              array.push(this.makeSingleAnimation(t));
+            }.bind(this));
+            animation = new AnimationGroup(array);
+          } else {
+            animation = this.makeSingleAnimation(this.target);
+          }
+          return animation;
+        },
+
+        animationChanged: function() {
+          // Sending 'this' with the event so you can always get the animation object
+          // that fired the event, due to event retargetting in shadow DOM.
+          this.fire('core-animation-change', this);
+        },
+
+        targetChanged: function(old) {
+          if (old) {
+            old.classList.remove('core-animation-target');
+          }
+        },
+
+        get timingProps() {
+          var props = {};
+          var timing = {
+            delay: {isNumber: true},
+            endDelay: {isNumber: true},
+            fill: {},
+            iterationStart: {isNumber: true},
+            iterations: {isNumber: true, allowInfinity: true},
+            duration: {isNumber: true},
+            playbackRate: {isNumber: true},
+            direction: {},
+            easing: {}
+          };
+          for (t in timing) {
+            if (this[t] !== null) {
+              var name = timing[t].property || t;
+              props[name] = timing[t].isNumber && this[t] !== 'auto' ?
+                  toNumber(this[t], timing[t].allowInfinity) : this[t];
+            }
+          }
+          return props;
+        },
+
+        get animationEffect() {
+          var props = {};
+          var frames = [];
+          var effect;
+          if (this.keyframes) {
+            frames = this.keyframes;
+          } else if (!this.customEffect) {
+            var children = this.querySelectorAll('core-animation-keyframe');
+            if (children.length === 0 && this.shadowRoot) {
+              children = this.shadowRoot.querySelectorAll('core-animation-keyframe');
+            }
+            Array.prototype.forEach.call(children, function(c) {
+              frames.push(c.properties);
+            });
+          }
+          if (this.customEffect) {
+            effect = this.customEffect;
+          } else {
+            // effect = new KeyframeEffect(frames, this.composite);
+            effect = frames;
+          }
+          return effect;
+        },
+
+        animationFinishHandler: function() {
+          this.fire('core-animation-finish');
+        }
+
+      });
+    })();
+  ;
+
+    Polymer('core-animation-keyframe',{
+      publish: {
+        /**
+         * An offset from 0 to 1.
+         *
+         * @property offset
+         * @type Number
+         */
+        offset: {value: null, reflect: true}
+      },
+      get properties() {
+        var props = {};
+        var children = this.querySelectorAll('core-animation-prop');
+        Array.prototype.forEach.call(children, function(c) {
+          props[c.name] = c.value;
+        });
+        if (this.offset !== null) {
+          props.offset = this.offset;
+        }
+        return props;
+      }
+    });
+  ;
+
+    Polymer('core-animation-prop',{
+      publish: {
+        /**
+         * A CSS property name.
+         *
+         * @property name
+         * @type string
+         */
+        name: {value: '', reflect: true},
+
+        /**
+         * The value for the CSS property.
+         *
+         * @property value
+         * @type string|number
+         */
+        value: {value: '', reflect: true}
+      }
+    });
+  
\ No newline at end of file
diff --git a/third_party/polymer/components-chromium/core-animation/core-animation-group-extracted.js b/third_party/polymer/components-chromium/core-animation/core-animation-group-extracted.js
new file mode 100644
index 0000000..3795c37
--- /dev/null
+++ b/third_party/polymer/components-chromium/core-animation/core-animation-group-extracted.js
@@ -0,0 +1,124 @@
+
+    (function() {
+
+      var ANIMATION_GROUPS = {
+        'par': AnimationGroup,
+        'seq': AnimationSequence
+      };
+
+      Polymer('core-animation-group',{
+
+        publish: {
+          /**
+           * If target is set, any children without a target will be assigned the group's
+           * target when this property is set.
+           *
+           * @property target
+           * @type HTMLElement|Node|Array|Array<HTMLElement|Node>
+           */
+
+          /**
+           * For a `core-animation-group`, a duration of "auto" means the duration should
+           * be the specified duration of its children. If set to anything other than
+           * "auto", any children without a set duration will be assigned the group's duration.
+           *
+           * @property duration
+           * @type number
+           * @default "auto"
+           */
+          duration: {value: 'auto', reflect: true},
+
+          /**
+           * The type of the animation group. 'par' creates a parallel group and 'seq' creates
+           * a sequential group.
+           *
+           * @property type
+           * @type String
+           * @default 'par'
+           */
+          type: {value: 'par', reflect: true}
+        },
+
+        typeChanged: function() {
+          this.apply();
+        },
+
+        targetChanged: function() {
+          // Only propagate target to children animations if it's defined.
+          if (this.target) {
+            this.doOnChildren(function(c) {
+              c.target = this.target;
+            }.bind(this));
+          }
+        },
+
+        durationChanged: function() {
+          if (this.duration && this.duration !== 'auto') {
+            this.doOnChildren(function(c) {
+              // Propagate to children that is not a group and has no
+              // duration specified.
+              if (!c.type && (!c.duration || c.duration === 'auto')) {
+                c.duration = this.duration;
+              }
+            }.bind(this));
+          }
+        },
+
+        doOnChildren: function(inFn) {
+          var children = this.children;
+          if (!children.length) {
+            children = this.shadowRoot ? this.shadowRoot.childNodes : [];
+          }
+          Array.prototype.forEach.call(children, function(c) {
+            // TODO <template> in the way
+            c.apply && inFn(c);
+          }, this);
+        },
+
+        makeAnimation: function() {
+          return new ANIMATION_GROUPS[this.type](this.childAnimations, this.timingProps);
+        },
+
+        hasTarget: function() {
+          var ht = this.target !== null;
+          if (!ht) {
+            this.doOnChildren(function(c) {
+              ht = ht || c.hasTarget();
+            }.bind(this));
+          }
+          return ht;
+        },
+
+        apply: function() {
+          // Propagate target and duration to child animations first.
+          this.durationChanged();
+          this.targetChanged();
+          this.doOnChildren(function(c) {
+            c.apply();
+          });
+          return this.super();
+        },
+
+        get childAnimationElements() {
+          var list = [];
+          this.doOnChildren(function(c) {
+            if (c.makeAnimation) {
+              list.push(c);
+            }
+          });
+          return list;
+        },
+
+        get childAnimations() {
+          var list = [];
+          this.doOnChildren(function(c) {
+            if (c.animation) {
+              list.push(c.animation);
+            }
+          });
+          return list;
+        }
+      });
+
+    })();
+  
\ No newline at end of file
diff --git a/third_party/polymer/components-chromium/core-animation/core-animation-group.html b/third_party/polymer/components-chromium/core-animation/core-animation-group.html
new file mode 100644
index 0000000..4cf5306
--- /dev/null
+++ b/third_party/polymer/components-chromium/core-animation/core-animation-group.html
@@ -0,0 +1,45 @@
+<!--
+Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
+This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
+The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
+The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
+Code distributed by Google as part of the polymer project is also
+subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
+--><html><head><link rel="import" href="../polymer/polymer.html">
+<link rel="import" href="core-animation.html">
+
+<!--
+@group Polymer Core Elements
+
+`core-animation-group` combines `core-animation` or `core-animation-group` elements to
+create a grouped web animation. The group may be parallel (type is `par`) or sequential
+(type is `seq`). Parallel groups play all the children elements simultaneously, and
+sequential groups play the children one after another.
+
+Example of an animation group to rotate and then fade an element:
+
+    <core-animation-group type="seq">
+      <core-animation id="fadeout" duration="500">
+        <core-animation-keyframe>
+          <core-animation-prop name="transform" value="rotate(0deg)"></core-animation-prop>
+          <core-animation-prop name="transform" value="rotate(45deg)"></core-animation-prop>
+        </core-animation-keyframe>
+      </core-animation>
+      <core-animation id="fadeout" duration="500">
+        <core-animation-keyframe>
+          <core-animation-prop name="opacity" value="1"></core-animation-prop>
+        </core-animation-keyframe>
+        <core-animation-keyframe>
+          <core-animation-prop name="opacity" value="0"></core-animation-prop>
+        </core-animation-keyframe>
+      </core-animation>
+    </core-animation-group>
+
+@element core-animation-group
+@status beta
+@homepage github.io
+-->
+</head><body><polymer-element name="core-animation-group" constructor="CoreAnimationGroup" extends="core-animation" attributes="type" assetpath="">
+  
+</polymer-element>
+<script charset="utf-8" src="core-animation-group-extracted.js"></script></body></html>
\ No newline at end of file
diff --git a/third_party/polymer/components-chromium/core-animation/core-animation.html b/third_party/polymer/components-chromium/core-animation/core-animation.html
new file mode 100644
index 0000000..a5326af
--- /dev/null
+++ b/third_party/polymer/components-chromium/core-animation/core-animation.html
@@ -0,0 +1,93 @@
+<!--
+Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
+This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
+The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
+The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
+Code distributed by Google as part of the polymer project is also
+subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
+--><html><head><link rel="import" href="../polymer/polymer.html">
+<link rel="import" href="web-animations.html">
+
+<!--
+@group Polymer Core Elements
+
+`core-animation` is a convenience element to use web animations with Polymer elements. It
+allows you to create a web animation declaratively. You can extend this class to create
+new types of animations and combine them with `core-animation-group`.
+
+Example to create animation to fade out an element over 500ms:
+
+    <core-animation id="fadeout" duration="500">
+      <core-animation-keyframe>
+        <core-animation-prop name="opacity" value="1"></core-animation-prop>
+      </core-animation-keyframe>
+      <core-animation-keyframe>
+        <core-animation-prop name="opacity" value="0"></core-animation-prop>
+      </core-animation-keyframe>
+    </core-animation>
+
+    <div id="el">Fade me out</div>
+
+    <script>
+      var animation = document.getElementById('fadeout');
+      animation.target = document.getElementById('el');
+      animation.play();
+    </script>
+
+Or do the same imperatively:
+
+    var animation = new CoreAnimation();
+    animation.duration = 500;
+    animation.keyframes = [
+      {opacity: 1},
+      {opacity: 0}
+    ];
+    animation.target = document.getElementById('el');
+    animation.play();
+
+You can also provide a javascript function instead of keyframes to the animation. This
+behaves essentially the same as `requestAnimationFrame`:
+
+    var animation = new CoreAnimation();
+    animation.customEffect = function(timeFraction, target, animation) {
+      // do something custom
+    };
+    animation.play();
+
+Elements that are targets to a `core-animation` are given the `core-animation-target` class.
+
+@element core-animation
+@status beta
+@homepage github.io
+-->
+</head><body><polymer-element name="core-animation" constructor="CoreAnimation" attributes="target keyframes customEffect composite duration fill easing iterationStart iterationCount delay direction autoplay targetSelector" assetpath="">
+  
+</polymer-element>
+
+<!--
+`core-animation-keyframe` represents a keyframe in a `core-animation`. Use them as children of
+`core-animation` elements to create web animations declaratively. If the `offset` property is
+unset, the keyframes will be distributed evenly within the animation duration. Use
+`core-animation-prop` elements as children of this element to specify the CSS properties for
+the animation.
+
+@element core-animation-keyframe
+@status beta
+@homepage github.io
+-->
+<polymer-element name="core-animation-keyframe" attributes="offset" assetpath="">
+  
+</polymer-element>
+
+<!--
+`core-animation-prop` represents a CSS property and value pair to use with
+`core-animation-keyframe`.
+
+@element core-animation-prop
+@status beta
+@homepage github.io
+-->
+<polymer-element name="core-animation-prop" attributes="name value" assetpath="">
+  
+</polymer-element>
+<script charset="utf-8" src="core-animation-extracted.js"></script></body></html>
\ No newline at end of file
diff --git a/third_party/polymer/components-chromium/core-animation/demo.html b/third_party/polymer/components-chromium/core-animation/demo.html
new file mode 100644
index 0000000..ddbbc75
--- /dev/null
+++ b/third_party/polymer/components-chromium/core-animation/demo.html
@@ -0,0 +1,193 @@
+<!--
+Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
+This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
+The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
+The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
+Code distributed by Google as part of the polymer project is also
+subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
+-->
+<!DOCTYPE html>
+<html>
+<head>
+
+  <meta charset="utf-8">
+  <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+  <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=yes">
+
+  <title>core-animation</title>
+
+  <script src="../webcomponentsjs/webcomponents.js"></script>
+
+  <link href="../font-roboto/roboto.html" rel="import">
+  <link href="../core-icon/core-icon.html" rel="import">
+  <link href="../core-icons/core-icons.html" rel="import">
+
+  <link href="core-animation.html" rel="import">
+  <link href="core-animation-group.html" rel="import">
+
+  <style shim-shadowdom>
+
+    body {
+      font-family: RobotoDraft, 'Helvetica Neue', Helvetica, Arial;
+      font-size: 14px;
+      margin: 0;
+      padding: 24px;
+      -webkit-tap-highlight-color: rgba(0,0,0,0);
+      -webkit-touch-callout: none;
+    }
+
+    section {
+      padding: 20px 0;
+    }
+
+    section > div {
+      padding: 14px;
+      font-size: 16px;
+    }
+
+    html /deep/ core-icon {
+      height: 48px;
+      width: 48px;
+    }
+
+    #target {
+      display: inline-block;
+      font-size: 32px;
+      -webkit-transform: translateZ(0);
+      transform: translateZ(0);
+    }
+
+  </style>
+
+</head>
+<body unresolved onclick="clickAction(event);">
+
+  <section>
+
+    <div>
+      <div id="target" layout horizontal center>
+        <core-icon icon="polymer"></core-icon>
+        <span>polymer</span>
+      </div>
+    </div>
+
+    <button>
+      opacity
+      <core-animation id="raw" duration="1000">
+        <core-animation-keyframe>
+          <core-animation-prop name="opacity" value="1">
+          </core-animation-prop>
+        </core-animation-keyframe>
+        <core-animation-keyframe>
+          <core-animation-prop name="opacity" value="0.3">
+          </core-animation-prop>
+        </core-animation-keyframe>
+        <core-animation-keyframe>
+          <core-animation-prop name="opacity" value="1">
+          </core-animation-prop>
+        </core-animation-keyframe>
+      </core-animation>
+    </button>
+
+    <button>
+      group: opacity + scale
+      <core-animation-group type="seq">
+        <core-animation duration="300">
+          <core-animation-keyframe>
+            <core-animation-prop name="opacity" value="1">
+            </core-animation-prop>
+          </core-animation-keyframe>
+          <core-animation-keyframe>
+            <core-animation-prop name="opacity" value="0.3">
+            </core-animation-prop>
+          </core-animation-keyframe>
+          <core-animation-keyframe>
+            <core-animation-prop name="opacity" value="1">
+            </core-animation-prop>
+          </core-animation-keyframe>
+        </core-animation>
+        <core-animation duration="300">
+          <core-animation-keyframe>
+            <core-animation-prop name="transform" value="scale(1)">
+            </core-animation-prop>
+          </core-animation-keyframe>
+          <core-animation-keyframe>
+            <core-animation-prop name="transform" value="scale(1.2)">
+            </core-animation-prop>
+          </core-animation-keyframe>
+          <core-animation-keyframe>
+            <core-animation-prop name="transform" value="scale(1)">
+            </core-animation-prop>
+          </core-animation-keyframe>
+        </core-animation>
+      </core-animation-group>
+    </button>
+
+    <button>
+      infinite duration
+      <core-animation duration="1000" iterations="Infinity" direction="alternate">
+        <core-animation-keyframe>
+          <core-animation-prop name="opacity" value="1">
+          </core-animation-prop>
+        </core-animation-keyframe>
+        <core-animation-keyframe>
+          <core-animation-prop name="opacity" value="0.3">
+          </core-animation-prop>
+        </core-animation-keyframe>
+      </core-animation>
+    </button>
+
+    <button>
+      custom effect
+      <core-animation id="custom-animation" duration="500"></core-animation>
+    </button>
+
+  </section>
+
+  <script>
+    var player;
+
+    document.body.addEventListener('core-animation-finish', function(e) {
+      console.log('core-animation-finish');
+      if (player) {
+        player.cancel();
+        player = null;
+        target.querySelector('span').textContent = 'polymer';
+      }
+    });
+
+    var customAnimationFn = function(timeFraction, target) {
+      // var colors = [
+      //   '#db4437',
+      //   '#ff9800',
+      //   '#ffeb3b',
+      //   '#0f9d58',
+      //   '#4285f4',
+      //   '#3f51b5',
+      //   '#9c27b0'
+      // ];
+      target.querySelector('span').textContent = timeFraction;
+    };
+
+
+    function clickAction(e) {
+      var t = e.target;
+      if (e.target.localName !== 'button') {
+        return;
+      }
+
+      if (player) {
+        player.cancel();
+      }
+
+      var a = t.querySelector('core-animation,core-animation-group');
+      if (a.id === 'custom-animation') {
+        a.customEffect = customAnimationFn;
+      }
+
+      a.target = document.getElementById('target');
+      player = a.play();
+    }
+  </script>
+</body>
+</html>
diff --git a/third_party/polymer/components-chromium/core-animation/index.html b/third_party/polymer/components-chromium/core-animation/index.html
new file mode 100644
index 0000000..2a20dcc3
--- /dev/null
+++ b/third_party/polymer/components-chromium/core-animation/index.html
@@ -0,0 +1,22 @@
+<!doctype html>
+<!--
+Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
+This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE
+The complete set of authors may be found at http://polymer.github.io/AUTHORS
+The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS
+Code distributed by Google as part of the polymer project is also
+subject to an additional IP rights grant found at http://polymer.github.io/PATENTS
+-->
+<html>
+<head>
+
+  <script src="../webcomponentsjs/webcomponents.js"></script>
+  <link rel="import" href="../core-component-page/core-component-page.html">
+
+</head>
+<body unresolved>
+
+  <core-component-page sources='["core-animation.html", "core-animation-group.html"]'></core-component-page>
+
+</body>
+</html>
diff --git a/third_party/polymer/components-chromium/core-animation/test/index.html b/third_party/polymer/components-chromium/core-animation/test/index.html
new file mode 100644
index 0000000..b13e469
--- /dev/null
+++ b/third_party/polymer/components-chromium/core-animation/test/index.html
@@ -0,0 +1,24 @@
+<!doctype html>
+<!--
+Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
+This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
+The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
+The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
+Code distributed by Google as part of the polymer project is also
+subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
+-->
+<html>
+<head>
+  <meta charset="UTF-8">
+  <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+  <title>core-animation tests</title>
+  <script src="../../web-component-tester/browser.js"></script>
+</head>
+<body>
+  <script>
+    WCT.loadSuites([
+      // 'basic.html'
+    ]);
+  </script>
+</body>
+</html>
diff --git a/third_party/polymer/components-chromium/core-animation/web-animations.html b/third_party/polymer/components-chromium/core-animation/web-animations.html
new file mode 100644
index 0000000..dde8491
--- /dev/null
+++ b/third_party/polymer/components-chromium/core-animation/web-animations.html
@@ -0,0 +1,10 @@
+<!--
+    @license
+    Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
+    This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
+    The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
+    The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
+    Code distributed by Google as part of the polymer project is also
+    subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
+-->
+<script src="../web-animations-js/web-animations-next-lite.min.js"></script>
diff --git a/third_party/polymer/components-chromium/paper-dropdown-menu/.bower.json b/third_party/polymer/components-chromium/paper-dropdown-menu/.bower.json
new file mode 100644
index 0000000..976de03
--- /dev/null
+++ b/third_party/polymer/components-chromium/paper-dropdown-menu/.bower.json
@@ -0,0 +1,27 @@
+{
+  "name": "paper-dropdown-menu",
+  "private": false,
+  "dependencies": {
+    "polymer": "Polymer/polymer#^0.5.0",
+    "core-a11y-keys": "Polymer/core-a11y-keys#^0.5.0",
+    "core-collapse": "Polymer/core-collapse#^0.5.0",
+    "core-focusable": "Polymer/core-focusable#^0.5.0",
+    "core-icon": "Polymer/core-icon#^0.5.0",
+    "core-icons": "Polymer/core-icons#^0.5.0",
+    "core-menu": "Polymer/core-menu#^0.5.0",
+    "paper-dropdown": "Polymer/paper-dropdown#^0.5.0",
+    "paper-item": "Polymer/paper-item#^0.5.0",
+    "paper-shadow": "Polymer/paper-shadow#^0.5.0"
+  },
+  "version": "0.5.2",
+  "homepage": "https://github.com/Polymer/paper-dropdown-menu",
+  "_release": "0.5.2",
+  "_resolution": {
+    "type": "version",
+    "tag": "0.5.2",
+    "commit": "ddde4df1540ea91c2dbb6971d250505ffc5282d8"
+  },
+  "_source": "git://github.com/Polymer/paper-dropdown-menu.git",
+  "_target": "0.5.2",
+  "_originalSource": "Polymer/paper-dropdown-menu"
+}
\ No newline at end of file
diff --git a/third_party/polymer/components-chromium/paper-dropdown-menu/README.md b/third_party/polymer/components-chromium/paper-dropdown-menu/README.md
new file mode 100644
index 0000000..ce6061e
--- /dev/null
+++ b/third_party/polymer/components-chromium/paper-dropdown-menu/README.md
@@ -0,0 +1,6 @@
+paper-dropdown-menu
+===================
+
+owner: @morethanreal
+
+See the [component page](https://www.polymer-project.org/docs/elements/paper-elements.html#paper-dropdown-menu) for more information.
diff --git a/third_party/polymer/components-chromium/paper-dropdown-menu/bower.json b/third_party/polymer/components-chromium/paper-dropdown-menu/bower.json
new file mode 100644
index 0000000..5ab1af6
--- /dev/null
+++ b/third_party/polymer/components-chromium/paper-dropdown-menu/bower.json
@@ -0,0 +1,17 @@
+{
+  "name": "paper-dropdown-menu",
+  "private": false,
+  "dependencies": {
+    "polymer": "Polymer/polymer#^0.5.0",
+    "core-a11y-keys": "Polymer/core-a11y-keys#^0.5.0",
+    "core-collapse": "Polymer/core-collapse#^0.5.0",
+    "core-focusable": "Polymer/core-focusable#^0.5.0",
+    "core-icon": "Polymer/core-icon#^0.5.0",
+    "core-icons": "Polymer/core-icons#^0.5.0",
+    "core-menu": "Polymer/core-menu#^0.5.0",
+    "paper-dropdown": "Polymer/paper-dropdown#^0.5.0",
+    "paper-item": "Polymer/paper-item#^0.5.0",
+    "paper-shadow": "Polymer/paper-shadow#^0.5.0"
+  },
+  "version": "0.5.2"
+}
\ No newline at end of file
diff --git a/third_party/polymer/components-chromium/paper-dropdown-menu/demo.html b/third_party/polymer/components-chromium/paper-dropdown-menu/demo.html
new file mode 100644
index 0000000..e2b07e85
--- /dev/null
+++ b/third_party/polymer/components-chromium/paper-dropdown-menu/demo.html
@@ -0,0 +1,211 @@
+<!doctype html>
+<!--
+Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
+This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
+The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
+The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
+Code distributed by Google as part of the polymer project is also
+subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
+-->
+
+<html>
+<head>
+
+  <meta charset="utf-8">
+  <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+  <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+
+  <title>paper-dropdown-menu</title>
+
+  <script src="../webcomponentsjs/webcomponents.js"></script>
+
+  <link href="../core-collapse/core-collapse.html" rel="import">
+  <link href="../core-menu/core-menu.html" rel="import">
+  <link href="../paper-dropdown/paper-dropdown.html" rel="import">
+  <link href="../paper-item/paper-item.html" rel="import">
+
+  <link href="paper-dropdown-menu.html" rel="import">
+
+  <style shim-shadowdom>
+    body {
+      font-family: RobotoDraft, 'Helvetica Neue', Helvetica, Arial;
+      font-size: 14px;
+      margin: 0;
+      padding: 24px;
+      -webkit-tap-highlight-color: rgba(0,0,0,0);
+      -webkit-touch-callout: none;
+    }
+
+    section {
+      padding: 20px 0;
+    }
+
+    section > div {
+      padding: 14px;
+      font-size: 16px;
+    }
+
+    html /deep/ paper-dropdown-menu {
+      box-sizing: border-box;
+      width: 170px;
+    }
+
+    html /deep/ core-menu {
+      box-sizing: border-box;
+      width: 170px;
+    }
+
+    paper-item {
+      overflow: hidden;
+      white-space: nowrap;
+      text-overflow: ellipsis;
+    }
+
+    html /deep/ core-collapse {
+      border: 1px solid #ccc;
+      padding: 8px;
+    }
+
+    html /deep/ core-overlay {
+      border: 1px solid #ccc;
+      padding: 8px;
+      background: #fff;
+    }
+
+    .constrained-height {
+      height: 150px;
+    }
+
+    .colored {
+      color: #0f9d58;
+    }
+
+    .dropdown.colored::shadow #ripple,
+    .dropdown.colored::shadow #background {
+      border: 1px solid #0f9d58;
+      background-color: #b7e1cd;
+    }
+
+  </style>
+
+</head>
+<body>
+
+  <template is="auto-binding">
+
+    <section>
+
+      <div>Absolutely positioned dropdowns</div>
+
+      <paper-dropdown-menu label="Your favorite pastry">
+        <paper-dropdown class="dropdown">
+          <core-menu class="menu">
+            <template repeat="{{pastries}}">
+              <paper-item>{{}}</paper-item>
+            </template>
+          </core-menu>
+        </paper-dropdown>
+      </paper-dropdown-menu>
+
+      <br><br>
+
+      <paper-dropdown-menu label="Disabled" disabled>
+        <paper-dropdown class="dropdown">
+          <core-menu class="menu">
+            <paper-item>Should not see this</paper-item>
+          </core-menu>
+        </paper-dropdown>
+      </paper-dropdown-menu>
+
+    </section>
+
+    <section>
+
+      <div>Layered dropdowns</div>
+
+      <button onclick="document.getElementById('overlay').toggle()">toggle core-overlay</button>
+
+      <core-overlay id="overlay">
+
+        <paper-dropdown-menu label="Your favorite pastry">
+          <paper-dropdown layered class="dropdown">
+            <core-menu class="menu">
+              <template repeat="{{pastries}}">
+                <paper-item>{{}}</paper-item>
+              </template>
+            </core-menu>
+          </paper-dropdown>
+        </paper-dropdown-menu>
+
+      </core-overlay>
+
+      <button onclick="document.getElementById('collapse').toggle()">toggle core-collapse</button>
+
+      <br>
+
+      <core-collapse id="collapse">
+
+        <paper-dropdown-menu label="Your favorite pastry">
+          <paper-dropdown layered class="dropdown">
+            <core-menu class="menu">
+              <template repeat="{{pastries}}">
+                <paper-item>{{}}</paper-item>
+              </template>
+            </core-menu>
+          </paper-dropdown>
+        </paper-dropdown-menu>
+
+      </core-collapse>
+
+    </section>
+
+    <section>
+
+      <div>Custom styling</div>
+
+      <paper-dropdown-menu label="Constrained height">
+        <paper-dropdown class="dropdown constrained-height">
+          <core-menu class="menu">
+            <template repeat="{{pastries}}">
+              <paper-item>{{}}</paper-item>
+            </template>
+          </core-menu>
+        </paper-dropdown>
+      </paper-dropdown-menu>
+
+      <br><br>
+
+      <paper-dropdown-menu label="Colored">
+        <paper-dropdown class="dropdown colored">
+          <core-menu class="menu">
+            <template repeat="{{pastries}}">
+              <paper-item>{{}}</paper-item>
+            </template>
+          </core-menu>
+        </paper-dropdown>
+      </paper-dropdown-menu>
+
+    </section>
+
+  </template>
+
+  <script>
+
+    scope = document.querySelector('template[is=auto-binding]');
+
+    scope.pastries = [
+      'Apple fritter',
+      'Croissant',
+      'Donut',
+      'Financier',
+      'Jello',
+      'Madeleine',
+      'Pound cake',
+      'Pretzel',
+      'Sfogliatelle'
+    ];
+
+  </script>
+
+</body>
+</html>
diff --git a/third_party/polymer/components-chromium/paper-dropdown-menu/index.html b/third_party/polymer/components-chromium/paper-dropdown-menu/index.html
new file mode 100644
index 0000000..294215a
--- /dev/null
+++ b/third_party/polymer/components-chromium/paper-dropdown-menu/index.html
@@ -0,0 +1,22 @@
+<!doctype html>
+<!--
+Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
+This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE
+The complete set of authors may be found at http://polymer.github.io/AUTHORS
+The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS
+Code distributed by Google as part of the polymer project is also
+subject to an additional IP rights grant found at http://polymer.github.io/PATENTS
+-->
+<html>
+<head>
+
+  <script src="../webcomponentsjs/webcomponents.js"></script>
+  <link rel="import" href="../core-component-page/core-component-page.html">
+
+</head>
+<body unresolved>
+
+  <core-component-page></core-component-page>
+
+</body>
+</html>
diff --git a/third_party/polymer/components-chromium/paper-dropdown-menu/paper-dropdown-menu-extracted.js b/third_party/polymer/components-chromium/paper-dropdown-menu/paper-dropdown-menu-extracted.js
new file mode 100644
index 0000000..c74966a
--- /dev/null
+++ b/third_party/polymer/components-chromium/paper-dropdown-menu/paper-dropdown-menu-extracted.js
@@ -0,0 +1,65 @@
+
+
+(function() {
+
+  var p = {
+
+    publish: {
+
+      /**
+       * A label for the control. The label is displayed if no item is selected.
+       *
+       * @attribute label
+       * @type string
+       * @default 'Select an item'
+       */
+      label: 'Select an item',
+
+      /**
+       * The icon to display when the drop-down is opened.
+       *
+       * @attribute openedIcon
+       * @type string
+       * @default 'arrow-drop-up'
+       */
+      openedIcon: 'arrow-drop-up',
+
+      /**
+       * The icon to display when the drop-down is closed.
+       *
+       * @attribute closedIcon
+       * @type string
+       * @default 'arrow-drop-down'
+       */
+      closedIcon: 'arrow-drop-down'
+
+    },
+
+    selectedItemLabel: '',
+
+    overlayListeners: {
+      'core-overlay-open': 'openAction',
+      'core-activate': 'activateAction',
+      'core-select': 'selectAction'
+    },
+
+    activateAction: function(e) {
+      this.opened = false;
+    },
+
+    selectAction: function(e) {
+      var detail = e.detail;
+      if (detail.isSelected) {
+        this.selectedItemLabel = detail.item.label || detail.item.textContent;
+      } else {
+        this.selectedItemLabel = '';
+      }
+    }
+
+  };
+
+  Polymer.mixin2(p, Polymer.CoreFocusable);
+  Polymer('paper-dropdown-menu',p);
+
+})();
+
diff --git a/third_party/polymer/components-chromium/paper-dropdown-menu/paper-dropdown-menu.css b/third_party/polymer/components-chromium/paper-dropdown-menu/paper-dropdown-menu.css
new file mode 100644
index 0000000..2621f62
--- /dev/null
+++ b/third_party/polymer/components-chromium/paper-dropdown-menu/paper-dropdown-menu.css
@@ -0,0 +1,44 @@
+/*
+ * @license
+ * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
+ * This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
+ * The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
+ * The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
+ * Code distributed by Google as part of the polymer project is also
+ * subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
+ */
+
+:host {
+  position: relative;
+  display: inline-block;
+  background-color: #fff;
+  -moz-user-select: none;
+  -ms-user-select: none;
+  -webkit-user-select: none;
+  user-select: none;
+  cursor: pointer;
+  padding: 0.75em 0;
+}
+
+#control {
+  box-sizing: border-box;
+  max-height: 2em;
+  color: #757575;
+  border-bottom: 1px solid #757575;
+}
+
+#control[selected] {
+  color: #000;
+}
+
+#control > div {
+  padding: 0.5em 0 0.25em;
+  overflow: hidden;
+  /* FIXME not working for some reason */
+  white-space: nowrap;
+  text-overflow: ellipsis;
+}
+
+core-icon {
+  margin: 0.3em 0 0.2em 0.25em;
+}
\ No newline at end of file
diff --git a/third_party/polymer/components-chromium/paper-dropdown-menu/paper-dropdown-menu.html b/third_party/polymer/components-chromium/paper-dropdown-menu/paper-dropdown-menu.html
new file mode 100644
index 0000000..a2057b7
--- /dev/null
+++ b/third_party/polymer/components-chromium/paper-dropdown-menu/paper-dropdown-menu.html
@@ -0,0 +1,112 @@
+<!--
+Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
+This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
+The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
+The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
+Code distributed by Google as part of the polymer project is also
+subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
+--><!--
+
+`paper-dropdown-menu` works together with `paper-dropdown` and `core-menu` to
+implement a drop-down menu. The currently selected item is displayed in the
+control. If no item is selected, the `label` is displayed instead.
+
+The child element with the class `dropdown` will be used as the drop-down
+menu. It should be a `paper-dropdown` or other overlay element. You should
+also provide a `core-selector` or other selector element, such as `core-menu`,
+in the drop-down. You should apply the class `menu` to the selector element.
+
+Example:
+
+    <paper-dropdown-menu label="Your favorite pastry">
+        <paper-dropdown class="dropdown">
+            <core-menu class="menu">
+                <paper-item>Croissant</paper-item>
+                <paper-item>Donut</paper-item>
+                <paper-item>Financier</paper-item>
+                <paper-item>Madeleine</paper-item>
+            </core-menu>
+        </paper-dropdown>
+    </paper-dropdown-menu>
+
+This example renders a drop-down menu with 4 options.
+
+@group Paper Elements
+@element paper-dropdown-menu
+@extends core-dropdown-base
+@status unstable
+@homepage github.io
+--><!--
+Fired when an item's selection state is changed. This event is fired both
+when an item is selected or deselected. The `isSelected` detail property
+contains the selection state.
+
+@event core-select
+@param {Object} detail
+  @param {boolean} detail.isSelected true for selection and false for deselection
+  @param {Object} detail.item the item element
+--><html><head><link href="../polymer/polymer.html" rel="import">
+
+<link href="../core-a11y-keys/core-a11y-keys.html" rel="import">
+<link href="../core-dropdown/core-dropdown-base.html" rel="import">
+<link href="../core-focusable/core-focusable.html" rel="import">
+<link href="../core-icon/core-icon.html" rel="import">
+<link href="../core-icons/core-icons.html" rel="import">
+<link href="../paper-shadow/paper-shadow.html" rel="import">
+
+<style shim-shadowdom="">
+  html /deep/ #paper-dropdown-menu-dropdown {
+    margin: 12px;
+    overflow: visible;
+  }
+
+  html /deep/ #paper-dropdown-menu-dropdown #menu {
+    padding: 8px 0;
+    margin: 0;
+  }
+
+  html /deep/ #paper-dropdown-menu-dropdown .menu-container {
+    overflow: auto;
+    max-height: 100%;
+    max-width: 100%;
+  }
+</style>
+
+</head><body><polymer-element name="paper-dropdown-menu" extends="core-dropdown-base" relative="" layout="" inline="" horizontal="" center="" tabindex="0" assetpath="">
+<template>
+
+  <style>
+    :host {
+      -moz-user-select: none;
+      -ms-user-select: none;
+      -webkit-user-select: none;
+      user-select: none;
+      cursor: pointer;
+      padding: 0.5em 0 0.25em;
+      margin: 0.75em 0;
+      border-bottom: 1px solid #757575;
+      outline: none;
+    }
+
+    #label, #arrow {
+      color: #757575;
+    }
+
+    #label {
+      overflow: hidden;
+      white-space: nowrap;
+      text-overflow: ellipsis;
+    }
+  </style>
+
+  <core-a11y-keys target="{{}}" keys="enter space" on-keys-pressed="{{toggleOverlay}}"></core-a11y-keys>
+
+  <div flex="" auto="" id="label">{{selectedItemLabel || label}}</div>
+  <core-icon id="arrow" icon="{{opened ? openedIcon : closedIcon}}"></core-icon>
+
+  <content></content>
+
+</template>
+
+</polymer-element>
+<script charset="utf-8" src="paper-dropdown-menu-extracted.js"></script></body></html>
\ No newline at end of file
diff --git a/third_party/polymer/components-chromium/paper-dropdown-menu/test/basic.html b/third_party/polymer/components-chromium/paper-dropdown-menu/test/basic.html
new file mode 100644
index 0000000..313dd49
--- /dev/null
+++ b/third_party/polymer/components-chromium/paper-dropdown-menu/test/basic.html
@@ -0,0 +1,115 @@
+<!doctype html>
+<!--
+Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
+This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
+The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
+The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
+Code distributed by Google as part of the polymer project is also
+subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
+-->
+<html>
+<head>
+  <meta charset="UTF-8">
+  <title>paper-dropdown-menu basic tests</title>
+  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">
+
+  <script src="../../webcomponentsjs/webcomponents.js"></script>
+  <script src="../../web-component-tester/browser.js"></script>
+
+  <link href="../core-dropdown.html" rel="import">
+
+  <style>
+    body {
+      text-align: center;
+      margin-top: 200px;
+    }
+  </style>
+
+</head>
+<body>
+
+  <div relative id="trigger1">
+    tap
+    <core-dropdown id="dropdown1">Hello World!</core-dropdown>
+  </div>
+
+  <div relative id="trigger2">
+    tap
+    <core-dropdown id="dropdown2">Hello World!</core-dropdown>
+  </div>
+
+  <div relative id="trigger3">
+    tap
+    <core-dropdown id="dropdown3">Hello World!</core-dropdown>
+  </div>
+
+  <script>
+
+    function assertPosition(dropdown, trigger) {
+      var dr = dropdown.getBoundingClientRect();
+      var tr = trigger.getBoundingClientRect();
+
+      if (dropdown.halign === 'left') {
+        assert.equal(dr.left, tr.left);
+      } else {
+        assert.equal(dr.right, tr.right);
+      }
+
+      if (dropdown.valign === 'top') {
+        assert.equal(dr.top, tr.top);
+      } else {
+        assert.equal(dr.bottom, tr.bottom);
+      }
+    };
+
+    function flushLayoutAndRender(callback) {
+      flush(function() {
+        document.body.offsetTop;
+        requestAnimationFrame(function() {
+          callback();
+        });
+      });
+    }
+
+    var d1 = document.getElementById('dropdown1');
+    var t1 = document.getElementById('trigger1');
+    d1.relatedTarget = t1;
+
+    var d2 = document.getElementById('dropdown2');
+    var t2 = document.getElementById('trigger2');
+    d2.relatedTarget = t2;
+
+    var d3 = document.getElementById('dropdown3');
+    var t3 = document.getElementById('trigger3');
+    d3.relatedTarget = t3;
+
+    test('default', function(done) {
+      d1.opened = true;
+      flushLayoutAndRender(function() {
+        assertPosition(d1, t1);
+        done();
+      });
+    });
+
+    test('bottom alignment', function(done) {
+      d2.valign = 'bottom';
+      d2.opened = true;
+      flushLayoutAndRender(function() {
+        assertPosition(d2, t2);
+        done();
+      });
+    });
+
+    test('right alignment', function(done) {
+      d3.halign = 'right';
+      d3.opened = true;
+      flushLayoutAndRender(function() {
+        assertPosition(d3, t3);
+        done();
+      });
+    });
+
+  </script>
+
+</body>
+</html>
diff --git a/third_party/polymer/components-chromium/paper-dropdown/.bower.json b/third_party/polymer/components-chromium/paper-dropdown/.bower.json
new file mode 100644
index 0000000..d067efa
--- /dev/null
+++ b/third_party/polymer/components-chromium/paper-dropdown/.bower.json
@@ -0,0 +1,29 @@
+{
+  "name": "paper-dropdown",
+  "private": false,
+  "dependencies": {
+    "polymer": "Polymer/polymer#^0.5.0",
+    "core-animation": "Polymer/core-animation#^0.5.0",
+    "core-collapse": "Polymer/core-collapse#^0.5.0",
+    "core-dropdown": "Polymer/core-dropdown#^0.5.0",
+    "core-transition": "Polymer/core-transition#^0.5.0",
+    "core-icons": "Polymer/core-icons#^0.5.0",
+    "paper-icon-button": "Polymer/paper-icon-button#^0.5.0",
+    "paper-item": "Polymer/paper-item#^0.5.0",
+    "paper-shadow": "Polymer/paper-shadow#^0.5.0"
+  },
+  "devDependencies": {
+    "web-component-tester": "web-component-tester#master"
+  },
+  "version": "0.5.2",
+  "homepage": "https://github.com/Polymer/paper-dropdown",
+  "_release": "0.5.2",
+  "_resolution": {
+    "type": "version",
+    "tag": "0.5.2",
+    "commit": "dcb105c817501ef360e6780ec0e67e8098fdb18e"
+  },
+  "_source": "git://github.com/Polymer/paper-dropdown.git",
+  "_target": "0.5.2",
+  "_originalSource": "Polymer/paper-dropdown"
+}
\ No newline at end of file
diff --git a/third_party/polymer/components-chromium/paper-dropdown/README.md b/third_party/polymer/components-chromium/paper-dropdown/README.md
new file mode 100644
index 0000000..ba0ad36
--- /dev/null
+++ b/third_party/polymer/components-chromium/paper-dropdown/README.md
@@ -0,0 +1,6 @@
+paper-dropdown
+==============
+
+owner: @morethanreal
+
+See the [component page](https://www.polymer-project.org/docs/elements/paper-elements.html#paper-dropdown) for more information.
diff --git a/third_party/polymer/components-chromium/paper-dropdown/bower.json b/third_party/polymer/components-chromium/paper-dropdown/bower.json
new file mode 100644
index 0000000..3b5141ab
--- /dev/null
+++ b/third_party/polymer/components-chromium/paper-dropdown/bower.json
@@ -0,0 +1,19 @@
+{
+  "name": "paper-dropdown",
+  "private": false,
+  "dependencies": {
+    "polymer": "Polymer/polymer#^0.5.0",
+    "core-animation": "Polymer/core-animation#^0.5.0",
+    "core-collapse": "Polymer/core-collapse#^0.5.0",
+    "core-dropdown": "Polymer/core-dropdown#^0.5.0",
+    "core-transition": "Polymer/core-transition#^0.5.0",
+    "core-icons": "Polymer/core-icons#^0.5.0",
+    "paper-icon-button": "Polymer/paper-icon-button#^0.5.0",
+    "paper-item": "Polymer/paper-item#^0.5.0",
+    "paper-shadow": "Polymer/paper-shadow#^0.5.0"
+  },
+  "devDependencies": {
+    "web-component-tester": "web-component-tester#master"
+  },
+  "version": "0.5.2"
+}
\ No newline at end of file
diff --git a/third_party/polymer/components-chromium/paper-dropdown/demo.html b/third_party/polymer/components-chromium/paper-dropdown/demo.html
new file mode 100644
index 0000000..d23962db
--- /dev/null
+++ b/third_party/polymer/components-chromium/paper-dropdown/demo.html
@@ -0,0 +1,456 @@
+<!doctype html>
+<!--
+Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
+This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
+The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
+The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
+Code distributed by Google as part of the polymer project is also
+subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
+-->
+
+<html>
+<head>
+
+  <meta charset="utf-8">
+  <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+  <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+
+  <title>paper-dropdown</title>
+
+  <script src="../webcomponentsjs/webcomponents.js"></script>
+
+  <link href="../core-collapse/core-collapse.html" rel="import">
+  <link href="../core-icons/core-icons.html" rel="import">
+  <link href="../paper-icon-button/paper-icon-button.html" rel="import">
+  <link href="../paper-item/paper-item.html" rel="import">
+
+  <link href="paper-dropdown.html" rel="import">
+
+  <style shim-shadowdom>
+    body {
+      font-family: RobotoDraft, 'Helvetica Neue', Helvetica, Arial;
+      font-size: 14px;
+      margin: 0;
+      padding: 24px;
+      -webkit-tap-highlight-color: rgba(0,0,0,0);
+      -webkit-touch-callout: none;
+    }
+
+    section {
+      padding: 20px 0;
+    }
+
+    section > div {
+      padding: 14px;
+      font-size: 16px;
+    }
+
+    x-trigger {
+      z-index: auto;
+    }
+
+    html /deep/ paper-dropdown:not(.no-padding)::shadow #scroller {
+      box-sizing: border-box;
+      padding: 8px;
+    }
+
+    .with-margin {
+      margin: 12px;
+    }
+
+    .open-below {
+      top: 38px;
+    }
+
+  </style>
+
+</head>
+<body>
+
+  <polymer-element name="x-trigger" extends="paper-icon-button" relative on-tap="{{toggle}}" noink>
+  <template>
+    <shadow></shadow>
+    <content></content>
+  </template>
+  <script>
+    Polymer({
+      toggle: function() {
+        if (!this.dropdown) {
+          this.dropdown = this.querySelector('paper-dropdown');
+        }
+        this.dropdown && this.dropdown.toggle();
+      }
+    });
+  </script>
+  </polymer-element>
+
+  <template is="auto-binding">
+
+    <section>
+
+      <div>Absolutely positioned dropdowns</div>
+
+      <x-trigger icon="menu">
+        <paper-dropdown>
+          halign = left
+          <br>
+          valign = top
+        </paper-dropdown>
+      </x-trigger>
+
+      <x-trigger icon="menu">
+        <paper-dropdown valign="bottom">
+          halign = left
+          <br>
+          valign = bottom
+        </paper-dropdown>
+      </x-trigger>
+
+      <x-trigger icon="menu">
+        <paper-dropdown halign="right">
+          halign = right
+          <br>
+          valign = top
+        </paper-dropdown>
+      </x-trigger>
+
+      <x-trigger icon="menu">
+        <paper-dropdown class="no-padding">
+          <div class="menu">
+            <paper-item>Item 1</paper-item>
+            <paper-item>Item 2</paper-item>
+            <paper-item>Item 3</paper-item>
+          </div>
+        </paper-dropdown>
+      </x-trigger>
+
+    </section>
+
+    <section>
+
+      <div>Layered dropdowns</div>
+
+      <button onclick="document.getElementById('collapse').toggle()">toggle core-collapse</button>
+
+      <br>
+
+      <core-collapse id="collapse">
+
+        <x-trigger icon="menu">
+          <paper-dropdown layered>
+            halign = left
+            <br>
+            valign = top
+          </paper-dropdown>
+        </x-trigger>
+
+      </core-collapse>
+
+    </section>
+
+    <section>
+
+      <div>Scrolling and margin</div>
+
+      <x-trigger icon="menu">
+        <paper-dropdown>
+          no margin<br>
+          <br>
+          <template repeat="{{countries}}">
+            {{name}}<br>
+          </template>
+        </paper-dropdown>
+      </x-trigger>
+
+      <x-trigger icon="menu">
+        <paper-dropdown class="with-margin">
+          with margin<br>
+          <br>
+          <template repeat="{{countries}}">
+            {{name}}<br>
+          </template>
+        </paper-dropdown>
+      </x-trigger>
+
+    </section>
+
+    <section>
+
+      <div>Custom position</div>
+
+      <x-trigger icon="menu">
+        <paper-dropdown class="open-below">
+          top: 38px
+        </paper-dropdown>
+      </x-trigger>
+
+    </section>
+
+  </template>
+
+  <script>
+
+    scope = document.querySelector('template[is=auto-binding]');
+
+    scope.countries = [
+      {name: 'Afghanistan', code: 'AF'},
+      {name: 'Åland Islands', code: 'AX'},
+      {name: 'Albania', code: 'AL'},
+      {name: 'Algeria', code: 'DZ'},
+      {name: 'American Samoa', code: 'AS'},
+      {name: 'Andorra', code: 'AD'},
+      {name: 'Angola', code: 'AO'},
+      {name: 'Anguilla', code: 'AI'},
+      {name: 'Antarctica', code: 'AQ'},
+      {name: 'Antigua and Barbuda', code: 'AG'},
+      {name: 'Argentina', code: 'AR'},
+      {name: 'Armenia', code: 'AM'},
+      {name: 'Aruba', code: 'AW'},
+      {name: 'Australia', code: 'AU'},
+      {name: 'Austria', code: 'AT'},
+      {name: 'Azerbaijan', code: 'AZ'},
+      {name: 'Bahamas', code: 'BS'},
+      {name: 'Bahrain', code: 'BH'},
+      {name: 'Bangladesh', code: 'BD'},
+      {name: 'Barbados', code: 'BB'},
+      {name: 'Belarus', code: 'BY'},
+      {name: 'Belgium', code: 'BE'},
+      {name: 'Belize', code: 'BZ'},
+      {name: 'Benin', code: 'BJ'},
+      {name: 'Bermuda', code: 'BM'},
+      {name: 'Bhutan', code: 'BT'},
+      {name: 'Bolivia', code: 'BO'},
+      {name: 'Bosnia and Herzegovina', code: 'BA'},
+      {name: 'Botswana', code: 'BW'},
+      {name: 'Bouvet Island', code: 'BV'},
+      {name: 'Brazil', code: 'BR'},
+      {name: 'British Indian Ocean Territory', code: 'IO'},
+      {name: 'Brunei Darussalam', code: 'BN'},
+      {name: 'Bulgaria', code: 'BG'},
+      {name: 'Burkina Faso', code: 'BF'},
+      {name: 'Burundi', code: 'BI'},
+      {name: 'Cambodia', code: 'KH'},
+      {name: 'Cameroon', code: 'CM'},
+      {name: 'Canada', code: 'CA'},
+      {name: 'Cape Verde', code: 'CV'},
+      {name: 'Cayman Islands', code: 'KY'},
+      {name: 'Central African Republic', code: 'CF'},
+      {name: 'Chad', code: 'TD'},
+      {name: 'Chile', code: 'CL'},
+      {name: 'China', code: 'CN'},
+      {name: 'Christmas Island', code: 'CX'},
+      {name: 'Cocos (Keeling) Islands', code: 'CC'},
+      {name: 'Colombia', code: 'CO'},
+      {name: 'Comoros', code: 'KM'},
+      {name: 'Congo', code: 'CG'},
+      {name: 'Congo, The Democratic Republic of the', code: 'CD'},
+      {name: 'Cook Islands', code: 'CK'},
+      {name: 'Costa Rica', code: 'CR'},
+      {name: 'Cote D\'Ivoire', code: 'CI'},
+      {name: 'Croatia', code: 'HR'},
+      {name: 'Cuba', code: 'CU'},
+      {name: 'Cyprus', code: 'CY'},
+      {name: 'Czech Republic', code: 'CZ'},
+      {name: 'Denmark', code: 'DK'},
+      {name: 'Djibouti', code: 'DJ'},
+      {name: 'Dominica', code: 'DM'},
+      {name: 'Dominican Republic', code: 'DO'},
+      {name: 'Ecuador', code: 'EC'},
+      {name: 'Egypt', code: 'EG'},
+      {name: 'El Salvador', code: 'SV'},
+      {name: 'Equatorial Guinea', code: 'GQ'},
+      {name: 'Eritrea', code: 'ER'},
+      {name: 'Estonia', code: 'EE'},
+      {name: 'Ethiopia', code: 'ET'},
+      {name: 'Falkland Islands (Malvinas)', code: 'FK'},
+      {name: 'Faroe Islands', code: 'FO'},
+      {name: 'Fiji', code: 'FJ'},
+      {name: 'Finland', code: 'FI'},
+      {name: 'France', code: 'FR'},
+      {name: 'French Guiana', code: 'GF'},
+      {name: 'French Polynesia', code: 'PF'},
+      {name: 'French Southern Territories', code: 'TF'},
+      {name: 'Gabon', code: 'GA'},
+      {name: 'Gambia', code: 'GM'},
+      {name: 'Georgia', code: 'GE'},
+      {name: 'Germany', code: 'DE'},
+      {name: 'Ghana', code: 'GH'},
+      {name: 'Gibraltar', code: 'GI'},
+      {name: 'Greece', code: 'GR'},
+      {name: 'Greenland', code: 'GL'},
+      {name: 'Grenada', code: 'GD'},
+      {name: 'Guadeloupe', code: 'GP'},
+      {name: 'Guam', code: 'GU'},
+      {name: 'Guatemala', code: 'GT'},
+      {name: 'Guernsey', code: 'GG'},
+      {name: 'Guinea', code: 'GN'},
+      {name: 'Guinea-Bissau', code: 'GW'},
+      {name: 'Guyana', code: 'GY'},
+      {name: 'Haiti', code: 'HT'},
+      {name: 'Heard Island and Mcdonald Islands', code: 'HM'},
+      {name: 'Holy See (Vatican City State)', code: 'VA'},
+      {name: 'Honduras', code: 'HN'},
+      {name: 'Hong Kong', code: 'HK'},
+      {name: 'Hungary', code: 'HU'},
+      {name: 'Iceland', code: 'IS'},
+      {name: 'India', code: 'IN'},
+      {name: 'Indonesia', code: 'ID'},
+      {name: 'Iran, Islamic Republic Of', code: 'IR'},
+      {name: 'Iraq', code: 'IQ'},
+      {name: 'Ireland', code: 'IE'},
+      {name: 'Isle of Man', code: 'IM'},
+      {name: 'Israel', code: 'IL'},
+      {name: 'Italy', code: 'IT'},
+      {name: 'Jamaica', code: 'JM'},
+      {name: 'Japan', code: 'JP'},
+      {name: 'Jersey', code: 'JE'},
+      {name: 'Jordan', code: 'JO'},
+      {name: 'Kazakhstan', code: 'KZ'},
+      {name: 'Kenya', code: 'KE'},
+      {name: 'Kiribati', code: 'KI'},
+      {name: 'Korea, Democratic People\'S Republic of', code: 'KP'},
+      {name: 'Korea, Republic of', code: 'KR'},
+      {name: 'Kuwait', code: 'KW'},
+      {name: 'Kyrgyzstan', code: 'KG'},
+      {name: 'Lao People\'S Democratic Republic', code: 'LA'},
+      {name: 'Latvia', code: 'LV'},
+      {name: 'Lebanon', code: 'LB'},
+      {name: 'Lesotho', code: 'LS'},
+      {name: 'Liberia', code: 'LR'},
+      {name: 'Libyan Arab Jamahiriya', code: 'LY'},
+      {name: 'Liechtenstein', code: 'LI'},
+      {name: 'Lithuania', code: 'LT'},
+      {name: 'Luxembourg', code: 'LU'},
+      {name: 'Macao', code: 'MO'},
+      {name: 'Macedonia, The Former Yugoslav Republic of', code: 'MK'},
+      {name: 'Madagascar', code: 'MG'},
+      {name: 'Malawi', code: 'MW'},
+      {name: 'Malaysia', code: 'MY'},
+      {name: 'Maldives', code: 'MV'},
+      {name: 'Mali', code: 'ML'},
+      {name: 'Malta', code: 'MT'},
+      {name: 'Marshall Islands', code: 'MH'},
+      {name: 'Martinique', code: 'MQ'},
+      {name: 'Mauritania', code: 'MR'},
+      {name: 'Mauritius', code: 'MU'},
+      {name: 'Mayotte', code: 'YT'},
+      {name: 'Mexico', code: 'MX'},
+      {name: 'Micronesia, Federated States of', code: 'FM'},
+      {name: 'Moldova, Republic of', code: 'MD'},
+      {name: 'Monaco', code: 'MC'},
+      {name: 'Mongolia', code: 'MN'},
+      {name: 'Montserrat', code: 'MS'},
+      {name: 'Morocco', code: 'MA'},
+      {name: 'Mozambique', code: 'MZ'},
+      {name: 'Myanmar', code: 'MM'},
+      {name: 'Namibia', code: 'NA'},
+      {name: 'Nauru', code: 'NR'},
+      {name: 'Nepal', code: 'NP'},
+      {name: 'Netherlands', code: 'NL'},
+      {name: 'Netherlands Antilles', code: 'AN'},
+      {name: 'New Caledonia', code: 'NC'},
+      {name: 'New Zealand', code: 'NZ'},
+      {name: 'Nicaragua', code: 'NI'},
+      {name: 'Niger', code: 'NE'},
+      {name: 'Nigeria', code: 'NG'},
+      {name: 'Niue', code: 'NU'},
+      {name: 'Norfolk Island', code: 'NF'},
+      {name: 'Northern Mariana Islands', code: 'MP'},
+      {name: 'Norway', code: 'NO'},
+      {name: 'Oman', code: 'OM'},
+      {name: 'Pakistan', code: 'PK'},
+      {name: 'Palau', code: 'PW'},
+      {name: 'Palestinian Territory, Occupied', code: 'PS'},
+      {name: 'Panama', code: 'PA'},
+      {name: 'Papua New Guinea', code: 'PG'},
+      {name: 'Paraguay', code: 'PY'},
+      {name: 'Peru', code: 'PE'},
+      {name: 'Philippines', code: 'PH'},
+      {name: 'Pitcairn', code: 'PN'},
+      {name: 'Poland', code: 'PL'},
+      {name: 'Portugal', code: 'PT'},
+      {name: 'Puerto Rico', code: 'PR'},
+      {name: 'Qatar', code: 'QA'},
+      {name: 'Reunion', code: 'RE'},
+      {name: 'Romania', code: 'RO'},
+      {name: 'Russian Federation', code: 'RU'},
+      {name: 'RWANDA', code: 'RW'},
+      {name: 'Saint Helena', code: 'SH'},
+      {name: 'Saint Kitts and Nevis', code: 'KN'},
+      {name: 'Saint Lucia', code: 'LC'},
+      {name: 'Saint Pierre and Miquelon', code: 'PM'},
+      {name: 'Saint Vincent and the Grenadines', code: 'VC'},
+      {name: 'Samoa', code: 'WS'},
+      {name: 'San Marino', code: 'SM'},
+      {name: 'Sao Tome and Principe', code: 'ST'},
+      {name: 'Saudi Arabia', code: 'SA'},
+      {name: 'Senegal', code: 'SN'},
+      {name: 'Serbia and Montenegro', code: 'CS'},
+      {name: 'Seychelles', code: 'SC'},
+      {name: 'Sierra Leone', code: 'SL'},
+      {name: 'Singapore', code: 'SG'},
+      {name: 'Slovakia', code: 'SK'},
+      {name: 'Slovenia', code: 'SI'},
+      {name: 'Solomon Islands', code: 'SB'},
+      {name: 'Somalia', code: 'SO'},
+      {name: 'South Africa', code: 'ZA'},
+      {name: 'South Georgia and the South Sandwich Islands', code: 'GS'},
+      {name: 'Spain', code: 'ES'},
+      {name: 'Sri Lanka', code: 'LK'},
+      {name: 'Sudan', code: 'SD'},
+      {name: 'Suriname', code: 'SR'},
+      {name: 'Svalbard and Jan Mayen', code: 'SJ'},
+      {name: 'Swaziland', code: 'SZ'},
+      {name: 'Sweden', code: 'SE'},
+      {name: 'Switzerland', code: 'CH'},
+      {name: 'Syrian Arab Republic', code: 'SY'},
+      {name: 'Taiwan, Province of China', code: 'TW'},
+      {name: 'Tajikistan', code: 'TJ'},
+      {name: 'Tanzania, United Republic of', code: 'TZ'},
+      {name: 'Thailand', code: 'TH'},
+      {name: 'Timor-Leste', code: 'TL'},
+      {name: 'Togo', code: 'TG'},
+      {name: 'Tokelau', code: 'TK'},
+      {name: 'Tonga', code: 'TO'},
+      {name: 'Trinidad and Tobago', code: 'TT'},
+      {name: 'Tunisia', code: 'TN'},
+      {name: 'Turkey', code: 'TR'},
+      {name: 'Turkmenistan', code: 'TM'},
+      {name: 'Turks and Caicos Islands', code: 'TC'},
+      {name: 'Tuvalu', code: 'TV'},
+      {name: 'Uganda', code: 'UG'},
+      {name: 'Ukraine', code: 'UA'},
+      {name: 'United Arab Emirates', code: 'AE'},
+      {name: 'United Kingdom', code: 'GB'},
+      {name: 'United States', code: 'US'},
+      {name: 'United States Minor Outlying Islands', code: 'UM'},
+      {name: 'Uruguay', code: 'UY'},
+      {name: 'Uzbekistan', code: 'UZ'},
+      {name: 'Vanuatu', code: 'VU'},
+      {name: 'Venezuela', code: 'VE'},
+      {name: 'Viet Nam', code: 'VN'},
+      {name: 'Virgin Islands, British', code: 'VG'},
+      {name: 'Virgin Islands, U.S.', code: 'VI'},
+      {name: 'Wallis and Futuna', code: 'WF'},
+      {name: 'Western Sahara', code: 'EH'},
+      {name: 'Yemen', code: 'YE'},
+      {name: 'Zambia', code: 'ZM'},
+      {name: 'Zimbabwe', code: 'ZW'}
+    ];
+
+    scope.pastries = [
+      'Apple fritter',
+      'Croissant',
+      'Donut',
+      'Financier',
+      'Jello',
+      'Madeleine',
+      'Pound cake',
+      'Pretzel',
+      'Sfogliatelle'
+    ];
+
+  </script>
+
+</body>
+</html>
diff --git a/third_party/polymer/components-chromium/paper-dropdown/index.html b/third_party/polymer/components-chromium/paper-dropdown/index.html
new file mode 100644
index 0000000..8c8da47a
--- /dev/null
+++ b/third_party/polymer/components-chromium/paper-dropdown/index.html
@@ -0,0 +1,22 @@
+<!doctype html>
+<!--
+Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
+This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE
+The complete set of authors may be found at http://polymer.github.io/AUTHORS
+The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS
+Code distributed by Google as part of the polymer project is also
+subject to an additional IP rights grant found at http://polymer.github.io/PATENTS
+-->
+<html>
+<head>
+
+  <script src="../webcomponentsjs/webcomponents.js"></script>
+  <link rel="import" href="../core-component-page/core-component-page.html">
+
+</head>
+<body unresolved>
+
+  <core-component-page sources='["paper-dropdown.html","paper-dropdown-transition.html"]'></core-component-page>
+
+</body>
+</html>
diff --git a/third_party/polymer/components-chromium/paper-dropdown/paper-dropdown-extracted.js b/third_party/polymer/components-chromium/paper-dropdown/paper-dropdown-extracted.js
new file mode 100644
index 0000000..9092006
--- /dev/null
+++ b/third_party/polymer/components-chromium/paper-dropdown/paper-dropdown-extracted.js
@@ -0,0 +1,15 @@
+
+
+  Polymer('paper-dropdown',{
+
+    publish: {
+      transition: 'paper-dropdown-transition'
+    },
+
+    ready: function() {
+      this.super();
+      this.sizingTarget = this.$.scroller;
+    }
+
+  });
+
diff --git a/third_party/polymer/components-chromium/paper-dropdown/paper-dropdown-transition-extracted.js b/third_party/polymer/components-chromium/paper-dropdown/paper-dropdown-transition-extracted.js
new file mode 100644
index 0000000..8d283415
--- /dev/null
+++ b/third_party/polymer/components-chromium/paper-dropdown/paper-dropdown-transition-extracted.js
@@ -0,0 +1,135 @@
+
+    Polymer('paper-dropdown-transition', {
+
+      publish: {
+
+        /**
+         * The duration of the transition in ms. You can also set the duration by
+         * setting a `duration` attribute on the target:
+         *
+         *    <paper-dropdown duration="1000"></paper-dropdown>
+         *
+         * @attribute duration
+         * @type number
+         * @default 500
+         */
+        duration: 500
+
+      },
+
+      setup: function(node) {
+        this.super(arguments);
+
+        var to = {
+          'top': '0%',
+          'left': '0%',
+          'bottom': '100%',
+          'right': '100%'
+        };
+
+        var bg = node.$.background;
+        bg.style.webkitTransformOrigin = to[node.halign] + ' ' + to[node.valign];
+        bg.style.transformOrigin = to[node.halign] + ' ' + to[node.valign];
+      },
+
+      transitionOpened: function(node, opened) {
+        this.super(arguments);
+
+        if (opened) {
+          if (this.player) {
+            this.player.cancel();
+          }
+
+          var duration = Number(node.getAttribute('duration')) || this.duration;
+
+          var anims = [];
+
+          var size = node.getBoundingClientRect();
+
+          var ink = node.$.ripple;
+          // var offset = 40 / Math.max(size.width, size.height);
+          var offset = 0.2;
+          anims.push(new Animation(ink, [{
+            'opacity': 0.9,
+            'transform': 'scale(0)',
+          }, {
+            'opacity': 0.9,
+            'transform': 'scale(1)'
+          }], {
+            duration: duration * offset
+          }));
+
+          var bg = node.$.background;
+          var sx = 40 / size.width;
+          var sy = 40 / size.height;
+          anims.push(new Animation(bg, [{
+            'opacity': 0.9,
+            'transform': 'scale(' + sx + ',' + sy + ')',
+          }, {
+            'opacity': 1,
+            'transform': 'scale(' + Math.max(sx, 0.95) + ',' + Math.max(sy, 0.5) + ')'
+          }, {
+            'opacity': 1,
+            'transform': 'scale(1, 1)'
+          }], {
+            delay: duration * offset,
+            duration: duration * (1 - offset),
+            fill: 'forwards'
+          }));
+
+          var menu = node.querySelector('.menu');
+          if (menu) {
+            var items = menu.items || menu.children.array();
+            var itemDelay = offset + (1 - offset) / 2;
+            var itemDuration = duration * (1 - itemDelay) / items.length;
+            var reverse = this.valign === 'bottom';
+
+            items.forEach(function(item, i) {
+              anims.push(new Animation(item, [{
+                'opacity': 0
+              }, {
+                'opacity': 1
+              }], {
+                delay: duration * itemDelay + itemDuration * (reverse ? items.length - 1 - i : i),
+                duration: itemDuration,
+                fill: 'both'
+              }));
+            }.bind(this));
+
+            anims.push(new Animation(node.$.scroller, [{
+              'opacity': 1
+            }, {
+              'opacity': 1
+            }], {
+              delay: duration * itemDelay,
+              duration: itemDuration * items.length,
+              fill: 'both'
+            }));
+
+          } else {
+            anims.push(new Animation(node.$.scroller, [{
+              'opacity': 0
+            }, {
+              'opacity': 1
+            }], {
+              delay: duration * (offset + (1 - offset) / 2),
+              duration: duration * 0.5,
+              fill: 'both'
+            }));
+          }
+
+          var group = new AnimationGroup(anims, {
+            easing: 'cubic-bezier(0.4, 0, 0.2, 1)'
+          });
+          this.player = document.timeline.play(group);
+          this.player.onfinish = function() {
+            this.fire('core-transitionend', this, node);
+          }.bind(this);
+
+        } else {
+          this.fire('core-transitionend', this, node);
+        }
+      },
+
+    });
+  
\ No newline at end of file
diff --git a/third_party/polymer/components-chromium/paper-dropdown/paper-dropdown-transition.css b/third_party/polymer/components-chromium/paper-dropdown/paper-dropdown-transition.css
new file mode 100644
index 0000000..d544ead
--- /dev/null
+++ b/third_party/polymer/components-chromium/paper-dropdown/paper-dropdown-transition.css
@@ -0,0 +1,15 @@
+/* Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
+This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
+The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
+The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
+Code distributed by Google as part of the polymer project is also
+subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt */
+
+:host(.core-transition) #ripple,
+:host(.core-transition) #background {
+  opacity: 0;
+}
+
+:host(.core-transition) #scroller {
+  opacity: 0;
+}
\ No newline at end of file
diff --git a/third_party/polymer/components-chromium/paper-dropdown/paper-dropdown-transition.html b/third_party/polymer/components-chromium/paper-dropdown/paper-dropdown-transition.html
new file mode 100644
index 0000000..b3bbad4
--- /dev/null
+++ b/third_party/polymer/components-chromium/paper-dropdown/paper-dropdown-transition.html
@@ -0,0 +1,30 @@
+<!--
+Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
+This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
+The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
+The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
+Code distributed by Google as part of the polymer project is also
+subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
+--><!--
+
+`paper-dropdown-transition` is a transition for `paper-dropdown`.
+
+Add the class `menu` to a `core-selector` child of the `paper-dropdown` to
+enable the optional list cascade transition.
+
+@group Paper Elements
+@class paper-dropdown-transition
+@extends core-transition-css
+@status unstable
+--><html><head><link href="../polymer/polymer.html" rel="import">
+<link href="../core-transition/core-transition-css.html" rel="import">
+<link href="../core-animation/web-animations.html" rel="import">
+
+</head><body><polymer-element name="paper-dropdown-transition" extends="core-transition-css" assetpath="">
+  <template>
+    <link href="paper-dropdown-transition.css" rel="stylesheet" no-shim="">
+  </template>
+  
+</polymer-element>
+
+<paper-dropdown-transition id="paper-dropdown-transition"></paper-dropdown-transition><script charset="utf-8" src="paper-dropdown-transition-extracted.js"></script></body></html>
\ No newline at end of file
diff --git a/third_party/polymer/components-chromium/paper-dropdown/paper-dropdown.html b/third_party/polymer/components-chromium/paper-dropdown/paper-dropdown.html
new file mode 100644
index 0000000..5d24c23
--- /dev/null
+++ b/third_party/polymer/components-chromium/paper-dropdown/paper-dropdown.html
@@ -0,0 +1,98 @@
+<!--
+Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
+This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
+The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
+The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
+Code distributed by Google as part of the polymer project is also
+subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
+--><!--
+
+`paper-dropdown` is a `core-dropdown` with a `paper-shadow`. By default, it
+is animated on open with `paper-dropdown-transition`. Use this element with
+`paper-dropdown-menu` or `paper-menu-button` to implement UI controls that
+open a drop-down.
+
+Example:
+
+    <paper-dropdown>
+      Hi!
+    </paper-dropdown>
+
+Theming
+-------
+
+Style the background color of the dropdown with these selectors:
+
+    paper-dropdown::shadow #ripple,
+    paper-dropdown::shadow #background {
+        background-color: green;
+    }
+
+@group Paper Elements
+@element paper-dropdown
+@extends core-dropdown
+@status unstable
+--><html><head><link href="../polymer/polymer.html" rel="import">
+<link href="../core-dropdown/core-dropdown.html" rel="import">
+<link href="../paper-shadow/paper-shadow.html" rel="import">
+
+<link href="paper-dropdown-transition.html" rel="import">
+
+<style shim-shadowdom="">
+  html /deep/ paper-dropdown {
+    position: absolute;
+    overflow: visible;
+    min-height: 40px;
+  }
+</style>
+
+</head><body><polymer-element name="paper-dropdown" extends="core-dropdown" assetpath="">
+<template>
+
+  <style>
+    #ripple {
+      background-color: #fff;
+      position: absolute;
+      left: 0;
+      top: 0;
+      width: 40px;
+      height: 40px;
+      border-radius: 50%;
+      box-shadow: 0 2px 3px 0 rgba(0, 0, 0, 0.26);
+      opacity: 0;
+    }
+
+    :host([halign=right]) #ripple {
+      left: auto;
+      right: 0;
+    }
+
+    :host([valign=bottom]) #ripple {
+      top: auto;
+      bottom: 0;
+    }
+
+    #background {
+      background-color: #fff;
+      border-radius: inherit;
+    }
+
+    #scroller {
+      overflow: auto;
+      box-sizing: border-box;
+    }
+  </style>
+
+  <div id="ripple"></div>
+
+  <div id="background" fit="">
+    <paper-shadow fit=""></paper-shadow>
+  </div>
+
+  <div id="scroller" relative="">
+    <content></content>
+  </div>
+
+</template>
+
+</polymer-element><script charset="utf-8" src="paper-dropdown-extracted.js"></script></body></html>
\ No newline at end of file
diff --git a/third_party/polymer/components/core-animation/.bower.json b/third_party/polymer/components/core-animation/.bower.json
new file mode 100644
index 0000000..32d483a
--- /dev/null
+++ b/third_party/polymer/components/core-animation/.bower.json
@@ -0,0 +1,19 @@
+{
+  "name": "core-animation",
+  "private": true,
+  "dependencies": {
+    "polymer": "Polymer/polymer#^0.5",
+    "web-animations-js": "web-animations/web-animations-js#1.0.5"
+  },
+  "version": "0.5.4",
+  "homepage": "https://github.com/Polymer/core-animation",
+  "_release": "0.5.4",
+  "_resolution": {
+    "type": "version",
+    "tag": "0.5.4",
+    "commit": "5063fb5efb55806f6d1f7c74471f4cdf96f974ce"
+  },
+  "_source": "git://github.com/Polymer/core-animation.git",
+  "_target": "0.5.4",
+  "_originalSource": "Polymer/core-animation"
+}
\ No newline at end of file
diff --git a/third_party/polymer/components/core-animation/README.md b/third_party/polymer/components/core-animation/README.md
new file mode 100644
index 0000000..809db5e
--- /dev/null
+++ b/third_party/polymer/components/core-animation/README.md
@@ -0,0 +1,4 @@
+core-animation
+==============
+
+See the [component page](http://polymer-project.org/docs/elements/core-elements.html#core-animation) for more information.
diff --git a/third_party/polymer/components/core-animation/bower.json b/third_party/polymer/components/core-animation/bower.json
new file mode 100644
index 0000000..f22db19
--- /dev/null
+++ b/third_party/polymer/components/core-animation/bower.json
@@ -0,0 +1,9 @@
+{
+  "name": "core-animation",
+  "private": true,
+  "dependencies": {
+    "polymer": "Polymer/polymer#^0.5",
+    "web-animations-js": "web-animations/web-animations-js#1.0.5"
+  },
+  "version": "0.5.4"
+}
\ No newline at end of file
diff --git a/third_party/polymer/components/core-animation/core-animation-group.html b/third_party/polymer/components/core-animation/core-animation-group.html
new file mode 100644
index 0000000..773cffd
--- /dev/null
+++ b/third_party/polymer/components/core-animation/core-animation-group.html
@@ -0,0 +1,169 @@
+<!--
+Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
+This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
+The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
+The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
+Code distributed by Google as part of the polymer project is also
+subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
+-->
+
+<link rel="import" href="../polymer/polymer.html">
+<link rel="import" href="core-animation.html">
+
+<!--
+@group Polymer Core Elements
+
+`core-animation-group` combines `core-animation` or `core-animation-group` elements to
+create a grouped web animation. The group may be parallel (type is `par`) or sequential
+(type is `seq`). Parallel groups play all the children elements simultaneously, and
+sequential groups play the children one after another.
+
+Example of an animation group to rotate and then fade an element:
+
+    <core-animation-group type="seq">
+      <core-animation id="fadeout" duration="500">
+        <core-animation-keyframe>
+          <core-animation-prop name="transform" value="rotate(0deg)"></core-animation-prop>
+          <core-animation-prop name="transform" value="rotate(45deg)"></core-animation-prop>
+        </core-animation-keyframe>
+      </core-animation>
+      <core-animation id="fadeout" duration="500">
+        <core-animation-keyframe>
+          <core-animation-prop name="opacity" value="1"></core-animation-prop>
+        </core-animation-keyframe>
+        <core-animation-keyframe>
+          <core-animation-prop name="opacity" value="0"></core-animation-prop>
+        </core-animation-keyframe>
+      </core-animation>
+    </core-animation-group>
+
+@element core-animation-group
+@status beta
+@homepage github.io
+-->
+<polymer-element name="core-animation-group" constructor="CoreAnimationGroup" extends="core-animation" attributes="type">
+  <script>
+    (function() {
+
+      var ANIMATION_GROUPS = {
+        'par': AnimationGroup,
+        'seq': AnimationSequence
+      };
+
+      Polymer({
+
+        publish: {
+          /**
+           * If target is set, any children without a target will be assigned the group's
+           * target when this property is set.
+           *
+           * @property target
+           * @type HTMLElement|Node|Array|Array<HTMLElement|Node>
+           */
+
+          /**
+           * For a `core-animation-group`, a duration of "auto" means the duration should
+           * be the specified duration of its children. If set to anything other than
+           * "auto", any children without a set duration will be assigned the group's duration.
+           *
+           * @property duration
+           * @type number
+           * @default "auto"
+           */
+          duration: {value: 'auto', reflect: true},
+
+          /**
+           * The type of the animation group. 'par' creates a parallel group and 'seq' creates
+           * a sequential group.
+           *
+           * @property type
+           * @type String
+           * @default 'par'
+           */
+          type: {value: 'par', reflect: true}
+        },
+
+        typeChanged: function() {
+          this.apply();
+        },
+
+        targetChanged: function() {
+          // Only propagate target to children animations if it's defined.
+          if (this.target) {
+            this.doOnChildren(function(c) {
+              c.target = this.target;
+            }.bind(this));
+          }
+        },
+
+        durationChanged: function() {
+          if (this.duration && this.duration !== 'auto') {
+            this.doOnChildren(function(c) {
+              // Propagate to children that is not a group and has no
+              // duration specified.
+              if (!c.type && (!c.duration || c.duration === 'auto')) {
+                c.duration = this.duration;
+              }
+            }.bind(this));
+          }
+        },
+
+        doOnChildren: function(inFn) {
+          var children = this.children;
+          if (!children.length) {
+            children = this.shadowRoot ? this.shadowRoot.childNodes : [];
+          }
+          Array.prototype.forEach.call(children, function(c) {
+            // TODO <template> in the way
+            c.apply && inFn(c);
+          }, this);
+        },
+
+        makeAnimation: function() {
+          return new ANIMATION_GROUPS[this.type](this.childAnimations, this.timingProps);
+        },
+
+        hasTarget: function() {
+          var ht = this.target !== null;
+          if (!ht) {
+            this.doOnChildren(function(c) {
+              ht = ht || c.hasTarget();
+            }.bind(this));
+          }
+          return ht;
+        },
+
+        apply: function() {
+          // Propagate target and duration to child animations first.
+          this.durationChanged();
+          this.targetChanged();
+          this.doOnChildren(function(c) {
+            c.apply();
+          });
+          return this.super();
+        },
+
+        get childAnimationElements() {
+          var list = [];
+          this.doOnChildren(function(c) {
+            if (c.makeAnimation) {
+              list.push(c);
+            }
+          });
+          return list;
+        },
+
+        get childAnimations() {
+          var list = [];
+          this.doOnChildren(function(c) {
+            if (c.animation) {
+              list.push(c.animation);
+            }
+          });
+          return list;
+        }
+      });
+
+    })();
+  </script>
+</polymer-element>
diff --git a/third_party/polymer/components/core-animation/core-animation.html b/third_party/polymer/components/core-animation/core-animation.html
new file mode 100644
index 0000000..9a958c9e
--- /dev/null
+++ b/third_party/polymer/components/core-animation/core-animation.html
@@ -0,0 +1,525 @@
+<!--
+Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
+This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
+The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
+The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
+Code distributed by Google as part of the polymer project is also
+subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
+-->
+
+<link rel="import" href="../polymer/polymer.html">
+<link rel="import" href="web-animations.html">
+
+<!--
+@group Polymer Core Elements
+
+`core-animation` is a convenience element to use web animations with Polymer elements. It
+allows you to create a web animation declaratively. You can extend this class to create
+new types of animations and combine them with `core-animation-group`.
+
+Example to create animation to fade out an element over 500ms:
+
+    <core-animation id="fadeout" duration="500">
+      <core-animation-keyframe>
+        <core-animation-prop name="opacity" value="1"></core-animation-prop>
+      </core-animation-keyframe>
+      <core-animation-keyframe>
+        <core-animation-prop name="opacity" value="0"></core-animation-prop>
+      </core-animation-keyframe>
+    </core-animation>
+
+    <div id="el">Fade me out</div>
+
+    <script>
+      var animation = document.getElementById('fadeout');
+      animation.target = document.getElementById('el');
+      animation.play();
+    </script>
+
+Or do the same imperatively:
+
+    var animation = new CoreAnimation();
+    animation.duration = 500;
+    animation.keyframes = [
+      {opacity: 1},
+      {opacity: 0}
+    ];
+    animation.target = document.getElementById('el');
+    animation.play();
+
+You can also provide a javascript function instead of keyframes to the animation. This
+behaves essentially the same as `requestAnimationFrame`:
+
+    var animation = new CoreAnimation();
+    animation.customEffect = function(timeFraction, target, animation) {
+      // do something custom
+    };
+    animation.play();
+
+Elements that are targets to a `core-animation` are given the `core-animation-target` class.
+
+@element core-animation
+@status beta
+@homepage github.io
+-->
+<polymer-element name="core-animation" constructor="CoreAnimation" attributes="target keyframes customEffect composite duration fill easing iterationStart iterationCount delay direction autoplay targetSelector">
+  <script>
+    (function() {
+
+      function toNumber(value, allowInfinity) {
+        return (allowInfinity && value === 'Infinity') ? Number.POSITIVE_INFINITY : Number(value);
+      };
+
+      Polymer({
+       /**
+        * Fired when the animation completes.
+        *
+        * @event core-animation-finish
+        */
+
+       /**
+        *
+        * Fired when the web animation object changes.
+        *
+        * @event core-animation-change
+        */
+
+        publish: {
+
+          /**
+           * One or more nodes to animate.
+           *
+           * @property target
+           * @type HTMLElement|Node|Array<HTMLElement|Node>
+           */
+          target: {value: null, reflect: true},
+
+          /**
+           * Animation keyframes specified as an array of dictionaries of
+           * &lt;css properties&gt;:&lt;array of values&gt; pairs. For example,
+           *
+           * @property keyframes
+           * @type Object
+           */
+          keyframes: {value: null, reflect: true},
+
+          /**
+           * A custom animation function. Either provide this or `keyframes`. The signature
+           * of the callback is `EffectsCallback(timeFraction, target, animation)`
+           *
+           * @property customEffect
+           * @type Function(number, Object, Object)
+           */
+          customEffect: {value: null, reflect: true},
+
+          /**
+           * Controls the composition behavior. If set to "replace", the effect overrides
+           * the underlying value for the target. If set the "add", the effect is added to
+           * the underlying value for the target. If set to "accumulate", the effect is
+           * accumulated to the underlying value for the target.
+           *
+           * In cases such as numbers or lengths, "add" and "accumulate" produce the same
+           * value. In list values, "add" is appending to the list, while "accumulate" is
+           * adding the individual components of the list.
+           *
+           * For example, adding `translateX(10px)` and `translateX(25px)` produces
+           * `translateX(10px) translateX(25px)` and accumulating produces `translateX(35px)`.
+           *
+           * @property composite
+           * @type "replace"|"add"|"accumulate"
+           * @default "replace"
+           */
+          composite: {value: 'replace', reflect: true},
+
+          /**
+           * Animation duration in milliseconds, "Infinity", or "auto". "auto" is
+           * equivalent to 0.
+           *
+           * @property duration
+           * @type number|"Infinity"
+           * @default "auto"
+           */
+          duration: {value: 'auto', reflect: true},
+
+          /**
+           * Controls the effect the animation has on the target when it's not playing.
+           * The possible values are "none", "forwards", "backwards", "both" or "auto".
+           *
+           * "none" means the animation has no effect when it's not playing.
+           *
+           * "forwards" applies the value at the end of the animation after it's finished.
+           *
+           * "backwards" applies the value at the start of the animation to the target
+           * before it starts playing and has no effect when the animation finishes.
+           *
+           * "both" means "forwards" and "backwards". "auto" is equivalent to "none".
+           *
+           * @property fill
+           * @type "none"|"forwards"|"backwards"|"both"|"auto"
+           * @default "auto"
+           */
+          fill: {value: 'auto', reflect: true},
+
+          /**
+           * A transition timing function. The values are equivalent to the CSS
+           * counterparts.
+           *
+           * @property easing
+           * @type string
+           * @default "linear"
+           */
+          easing: {value: 'linear', reflect: true},
+
+          /**
+           * The number of milliseconds to delay before beginning the animation.
+           *
+           * @property delay
+           * @type Number
+           * @default 0
+           */
+          delay: {value: 0, reflect: true},
+
+          /**
+           * The number of milliseconds to wait after the animation finishes. This is
+           * useful, for example, in an animation group to wait for some time before
+           * beginning the next item in the animation group.
+           *
+           * @property endDelay
+           * @type number
+           * @default 0
+           */
+          endDelay: {value: 0, reflect: true},
+
+          /**
+           * The number of iterations this animation should run for.
+           *
+           * @property iterations
+           * @type Number|'Infinity'
+           * @default 1
+           */
+          iterations: {value: 1, reflect: true},
+
+          /**
+           * Number of iterations into the animation in which to begin the effect.
+           * For example, setting this property to 0.5 and `iterations` to 2 will
+           * cause the animation to begin halfway through the first iteration but still
+           * run twice.
+           *
+           * @property iterationStart
+           * @type Number
+           * @default 0
+           */
+          iterationStart: {value: 0, reflect: true},
+
+          /**
+           * (not working in web animations polyfill---do not use)
+           *
+           * Controls the iteration composition behavior. If set to "replace", the effect for
+           * every iteration is independent of each other. If set to "accumulate", the effect
+           * for iterations of the animation will build upon the value in the previous iteration.
+           *
+           * Example:
+           *
+           *    // Moves the target 50px on the x-axis over 5 iterations.
+           *    <core-animation iterations="5" iterationComposite="accumulate">
+           *      <core-animation-keyframe>
+           *        <core-animation-prop name="transform" value="translateX(10px)"></core-animation-prop>
+           *      </core-animation-keyframe>
+           *    </core-animation>
+           *
+           * @property iterationComposite
+           * @type "replace"|"accumulate"
+           * @default false
+           */
+          iterationComposite: {value: 'replace', reflect: true},
+
+          /**
+           * The playback direction of the animation. "normal" plays the animation in the
+           * normal direction. "reverse" plays it in the reverse direction. "alternate"
+           * alternates the playback direction every iteration such that even iterations are
+           * played normally and odd iterations are reversed. "alternate-reverse" plays
+           * even iterations in the reverse direction and odd iterations in the normal
+           * direction.
+           *
+           * @property direction
+           * @type "normal"|"reverse"|"alternate"|"alternate-reverse"
+           * @default "normal"
+           */
+          direction: {value: 'normal', reflect: true},
+
+          /**
+           * A multiplier to the playback rate to the animation.
+           *
+           * @property playbackRate
+           * @type number
+           * @default 1
+           */
+          playbackRate: {value: 1, reflect: true},
+
+          /**
+           * If set to true, play the animation when it is created or a property is updated.
+           *
+           * @property autoplay
+           * @type boolean
+           * @default false
+           */
+          autoplay: {value: false, reflect: true}
+
+        },
+
+        animation: false,
+
+        observe: {
+          target: 'apply',
+          keyframes: 'apply',
+          customEffect: 'apply',
+          composite: 'apply',
+          duration: 'apply',
+          fill: 'apply',
+          easing: 'apply',
+          iterations: 'apply',
+          iterationStart: 'apply',
+          iterationComposite: 'apply',
+          delay: 'apply',
+          endDelay: 'apply',
+          direction: 'apply',
+          playbackRate: 'apply',
+          autoplay: 'apply'
+        },
+
+        ready: function() {
+          this.apply();
+        },
+
+        /**
+         * Plays the animation. If the animation is currently paused, seeks the animation
+         * to the beginning before starting playback.
+         *
+         * @method play
+         * @return AnimationPlayer The animation player.
+         */
+        play: function() {
+          this.apply();
+          if (this.animation && !this.autoplay) {
+            this.player = document.timeline.play(this.animation);
+            this.player.onfinish = this.animationFinishHandler.bind(this);
+            return this.player;
+          }
+        },
+
+        /**
+         * Stops the animation and clears all effects on the target.
+         *
+         * @method cancel
+         */
+        cancel: function() {
+          if (this.player) {
+            this.player.cancel();
+          }
+        },
+
+        /**
+         * Seeks the animation to the end.
+         *
+         * @method finish
+         */
+        finish: function() {
+          if (this.player) {
+            this.player.finish();
+          }
+        },
+
+        /**
+         * Pauses the animation.
+         *
+         * @method pause
+         */
+        pause: function() {
+          if (this.player) {
+            this.player.pause();
+          }
+        },
+
+        /**
+         * @method hasTarget
+         * @return boolean True if `target` is defined.
+         */
+        hasTarget: function() {
+          return this.target !== null;
+        },
+
+        /**
+         * Creates a web animations object based on this object's properties, and
+         * plays it if autoplay is true.
+         *
+         * @method apply
+         * @return Object A web animation.
+         */
+        apply: function() {
+          this.animation = this.makeAnimation();
+          if (this.autoplay && this.animation) {
+            this.play();
+          }
+          return this.animation;
+        },
+
+        makeSingleAnimation: function(target) {
+          // XXX(yvonne): for selecting all the animated elements.
+          target.classList.add('core-animation-target');
+          return new Animation(target, this.animationEffect, this.timingProps);
+        },
+
+        makeAnimation: function() {
+          if (!this.target) {
+            return null;
+          }
+          var animation;
+          if (Array.isArray(this.target)) {
+            var array = [];
+            this.target.forEach(function(t) {
+              array.push(this.makeSingleAnimation(t));
+            }.bind(this));
+            animation = new AnimationGroup(array);
+          } else {
+            animation = this.makeSingleAnimation(this.target);
+          }
+          return animation;
+        },
+
+        animationChanged: function() {
+          // Sending 'this' with the event so you can always get the animation object
+          // that fired the event, due to event retargetting in shadow DOM.
+          this.fire('core-animation-change', this);
+        },
+
+        targetChanged: function(old) {
+          if (old) {
+            old.classList.remove('core-animation-target');
+          }
+        },
+
+        get timingProps() {
+          var props = {};
+          var timing = {
+            delay: {isNumber: true},
+            endDelay: {isNumber: true},
+            fill: {},
+            iterationStart: {isNumber: true},
+            iterations: {isNumber: true, allowInfinity: true},
+            duration: {isNumber: true},
+            playbackRate: {isNumber: true},
+            direction: {},
+            easing: {}
+          };
+          for (t in timing) {
+            if (this[t] !== null) {
+              var name = timing[t].property || t;
+              props[name] = timing[t].isNumber && this[t] !== 'auto' ?
+                  toNumber(this[t], timing[t].allowInfinity) : this[t];
+            }
+          }
+          return props;
+        },
+
+        get animationEffect() {
+          var props = {};
+          var frames = [];
+          var effect;
+          if (this.keyframes) {
+            frames = this.keyframes;
+          } else if (!this.customEffect) {
+            var children = this.querySelectorAll('core-animation-keyframe');
+            if (children.length === 0 && this.shadowRoot) {
+              children = this.shadowRoot.querySelectorAll('core-animation-keyframe');
+            }
+            Array.prototype.forEach.call(children, function(c) {
+              frames.push(c.properties);
+            });
+          }
+          if (this.customEffect) {
+            effect = this.customEffect;
+          } else {
+            // effect = new KeyframeEffect(frames, this.composite);
+            effect = frames;
+          }
+          return effect;
+        },
+
+        animationFinishHandler: function() {
+          this.fire('core-animation-finish');
+        }
+
+      });
+    })();
+  </script>
+</polymer-element>
+
+<!--
+`core-animation-keyframe` represents a keyframe in a `core-animation`. Use them as children of
+`core-animation` elements to create web animations declaratively. If the `offset` property is
+unset, the keyframes will be distributed evenly within the animation duration. Use
+`core-animation-prop` elements as children of this element to specify the CSS properties for
+the animation.
+
+@element core-animation-keyframe
+@status beta
+@homepage github.io
+-->
+<polymer-element name="core-animation-keyframe" attributes="offset">
+  <script>
+    Polymer({
+      publish: {
+        /**
+         * An offset from 0 to 1.
+         *
+         * @property offset
+         * @type Number
+         */
+        offset: {value: null, reflect: true}
+      },
+      get properties() {
+        var props = {};
+        var children = this.querySelectorAll('core-animation-prop');
+        Array.prototype.forEach.call(children, function(c) {
+          props[c.name] = c.value;
+        });
+        if (this.offset !== null) {
+          props.offset = this.offset;
+        }
+        return props;
+      }
+    });
+  </script>
+</polymer-element>
+
+<!--
+`core-animation-prop` represents a CSS property and value pair to use with
+`core-animation-keyframe`.
+
+@element core-animation-prop
+@status beta
+@homepage github.io
+-->
+<polymer-element name="core-animation-prop" attributes="name value">
+  <script>
+    Polymer({
+      publish: {
+        /**
+         * A CSS property name.
+         *
+         * @property name
+         * @type string
+         */
+        name: {value: '', reflect: true},
+
+        /**
+         * The value for the CSS property.
+         *
+         * @property value
+         * @type string|number
+         */
+        value: {value: '', reflect: true}
+      }
+    });
+  </script>
+</polymer-element>
diff --git a/third_party/polymer/components/core-animation/demo.html b/third_party/polymer/components/core-animation/demo.html
new file mode 100644
index 0000000..ddbbc75
--- /dev/null
+++ b/third_party/polymer/components/core-animation/demo.html
@@ -0,0 +1,193 @@
+<!--
+Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
+This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
+The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
+The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
+Code distributed by Google as part of the polymer project is also
+subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
+-->
+<!DOCTYPE html>
+<html>
+<head>
+
+  <meta charset="utf-8">
+  <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+  <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=yes">
+
+  <title>core-animation</title>
+
+  <script src="../webcomponentsjs/webcomponents.js"></script>
+
+  <link href="../font-roboto/roboto.html" rel="import">
+  <link href="../core-icon/core-icon.html" rel="import">
+  <link href="../core-icons/core-icons.html" rel="import">
+
+  <link href="core-animation.html" rel="import">
+  <link href="core-animation-group.html" rel="import">
+
+  <style shim-shadowdom>
+
+    body {
+      font-family: RobotoDraft, 'Helvetica Neue', Helvetica, Arial;
+      font-size: 14px;
+      margin: 0;
+      padding: 24px;
+      -webkit-tap-highlight-color: rgba(0,0,0,0);
+      -webkit-touch-callout: none;
+    }
+
+    section {
+      padding: 20px 0;
+    }
+
+    section > div {
+      padding: 14px;
+      font-size: 16px;
+    }
+
+    html /deep/ core-icon {
+      height: 48px;
+      width: 48px;
+    }
+
+    #target {
+      display: inline-block;
+      font-size: 32px;
+      -webkit-transform: translateZ(0);
+      transform: translateZ(0);
+    }
+
+  </style>
+
+</head>
+<body unresolved onclick="clickAction(event);">
+
+  <section>
+
+    <div>
+      <div id="target" layout horizontal center>
+        <core-icon icon="polymer"></core-icon>
+        <span>polymer</span>
+      </div>
+    </div>
+
+    <button>
+      opacity
+      <core-animation id="raw" duration="1000">
+        <core-animation-keyframe>
+          <core-animation-prop name="opacity" value="1">
+          </core-animation-prop>
+        </core-animation-keyframe>
+        <core-animation-keyframe>
+          <core-animation-prop name="opacity" value="0.3">
+          </core-animation-prop>
+        </core-animation-keyframe>
+        <core-animation-keyframe>
+          <core-animation-prop name="opacity" value="1">
+          </core-animation-prop>
+        </core-animation-keyframe>
+      </core-animation>
+    </button>
+
+    <button>
+      group: opacity + scale
+      <core-animation-group type="seq">
+        <core-animation duration="300">
+          <core-animation-keyframe>
+            <core-animation-prop name="opacity" value="1">
+            </core-animation-prop>
+          </core-animation-keyframe>
+          <core-animation-keyframe>
+            <core-animation-prop name="opacity" value="0.3">
+            </core-animation-prop>
+          </core-animation-keyframe>
+          <core-animation-keyframe>
+            <core-animation-prop name="opacity" value="1">
+            </core-animation-prop>
+          </core-animation-keyframe>
+        </core-animation>
+        <core-animation duration="300">
+          <core-animation-keyframe>
+            <core-animation-prop name="transform" value="scale(1)">
+            </core-animation-prop>
+          </core-animation-keyframe>
+          <core-animation-keyframe>
+            <core-animation-prop name="transform" value="scale(1.2)">
+            </core-animation-prop>
+          </core-animation-keyframe>
+          <core-animation-keyframe>
+            <core-animation-prop name="transform" value="scale(1)">
+            </core-animation-prop>
+          </core-animation-keyframe>
+        </core-animation>
+      </core-animation-group>
+    </button>
+
+    <button>
+      infinite duration
+      <core-animation duration="1000" iterations="Infinity" direction="alternate">
+        <core-animation-keyframe>
+          <core-animation-prop name="opacity" value="1">
+          </core-animation-prop>
+        </core-animation-keyframe>
+        <core-animation-keyframe>
+          <core-animation-prop name="opacity" value="0.3">
+          </core-animation-prop>
+        </core-animation-keyframe>
+      </core-animation>
+    </button>
+
+    <button>
+      custom effect
+      <core-animation id="custom-animation" duration="500"></core-animation>
+    </button>
+
+  </section>
+
+  <script>
+    var player;
+
+    document.body.addEventListener('core-animation-finish', function(e) {
+      console.log('core-animation-finish');
+      if (player) {
+        player.cancel();
+        player = null;
+        target.querySelector('span').textContent = 'polymer';
+      }
+    });
+
+    var customAnimationFn = function(timeFraction, target) {
+      // var colors = [
+      //   '#db4437',
+      //   '#ff9800',
+      //   '#ffeb3b',
+      //   '#0f9d58',
+      //   '#4285f4',
+      //   '#3f51b5',
+      //   '#9c27b0'
+      // ];
+      target.querySelector('span').textContent = timeFraction;
+    };
+
+
+    function clickAction(e) {
+      var t = e.target;
+      if (e.target.localName !== 'button') {
+        return;
+      }
+
+      if (player) {
+        player.cancel();
+      }
+
+      var a = t.querySelector('core-animation,core-animation-group');
+      if (a.id === 'custom-animation') {
+        a.customEffect = customAnimationFn;
+      }
+
+      a.target = document.getElementById('target');
+      player = a.play();
+    }
+  </script>
+</body>
+</html>
diff --git a/third_party/polymer/components/core-animation/index.html b/third_party/polymer/components/core-animation/index.html
new file mode 100644
index 0000000..2a20dcc3
--- /dev/null
+++ b/third_party/polymer/components/core-animation/index.html
@@ -0,0 +1,22 @@
+<!doctype html>
+<!--
+Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
+This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE
+The complete set of authors may be found at http://polymer.github.io/AUTHORS
+The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS
+Code distributed by Google as part of the polymer project is also
+subject to an additional IP rights grant found at http://polymer.github.io/PATENTS
+-->
+<html>
+<head>
+
+  <script src="../webcomponentsjs/webcomponents.js"></script>
+  <link rel="import" href="../core-component-page/core-component-page.html">
+
+</head>
+<body unresolved>
+
+  <core-component-page sources='["core-animation.html", "core-animation-group.html"]'></core-component-page>
+
+</body>
+</html>
diff --git a/third_party/polymer/components/core-animation/test/index.html b/third_party/polymer/components/core-animation/test/index.html
new file mode 100644
index 0000000..b13e469
--- /dev/null
+++ b/third_party/polymer/components/core-animation/test/index.html
@@ -0,0 +1,24 @@
+<!doctype html>
+<!--
+Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
+This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
+The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
+The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
+Code distributed by Google as part of the polymer project is also
+subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
+-->
+<html>
+<head>
+  <meta charset="UTF-8">
+  <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+  <title>core-animation tests</title>
+  <script src="../../web-component-tester/browser.js"></script>
+</head>
+<body>
+  <script>
+    WCT.loadSuites([
+      // 'basic.html'
+    ]);
+  </script>
+</body>
+</html>
diff --git a/third_party/polymer/components/core-animation/web-animations.html b/third_party/polymer/components/core-animation/web-animations.html
new file mode 100644
index 0000000..dde8491
--- /dev/null
+++ b/third_party/polymer/components/core-animation/web-animations.html
@@ -0,0 +1,10 @@
+<!--
+    @license
+    Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
+    This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
+    The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
+    The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
+    Code distributed by Google as part of the polymer project is also
+    subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
+-->
+<script src="../web-animations-js/web-animations-next-lite.min.js"></script>
diff --git a/third_party/polymer/components/paper-dropdown-menu/.bower.json b/third_party/polymer/components/paper-dropdown-menu/.bower.json
new file mode 100644
index 0000000..976de03
--- /dev/null
+++ b/third_party/polymer/components/paper-dropdown-menu/.bower.json
@@ -0,0 +1,27 @@
+{
+  "name": "paper-dropdown-menu",
+  "private": false,
+  "dependencies": {
+    "polymer": "Polymer/polymer#^0.5.0",
+    "core-a11y-keys": "Polymer/core-a11y-keys#^0.5.0",
+    "core-collapse": "Polymer/core-collapse#^0.5.0",
+    "core-focusable": "Polymer/core-focusable#^0.5.0",
+    "core-icon": "Polymer/core-icon#^0.5.0",
+    "core-icons": "Polymer/core-icons#^0.5.0",
+    "core-menu": "Polymer/core-menu#^0.5.0",
+    "paper-dropdown": "Polymer/paper-dropdown#^0.5.0",
+    "paper-item": "Polymer/paper-item#^0.5.0",
+    "paper-shadow": "Polymer/paper-shadow#^0.5.0"
+  },
+  "version": "0.5.2",
+  "homepage": "https://github.com/Polymer/paper-dropdown-menu",
+  "_release": "0.5.2",
+  "_resolution": {
+    "type": "version",
+    "tag": "0.5.2",
+    "commit": "ddde4df1540ea91c2dbb6971d250505ffc5282d8"
+  },
+  "_source": "git://github.com/Polymer/paper-dropdown-menu.git",
+  "_target": "0.5.2",
+  "_originalSource": "Polymer/paper-dropdown-menu"
+}
\ No newline at end of file
diff --git a/third_party/polymer/components/paper-dropdown-menu/README.md b/third_party/polymer/components/paper-dropdown-menu/README.md
new file mode 100644
index 0000000..ce6061e
--- /dev/null
+++ b/third_party/polymer/components/paper-dropdown-menu/README.md
@@ -0,0 +1,6 @@
+paper-dropdown-menu
+===================
+
+owner: @morethanreal
+
+See the [component page](https://www.polymer-project.org/docs/elements/paper-elements.html#paper-dropdown-menu) for more information.
diff --git a/third_party/polymer/components/paper-dropdown-menu/bower.json b/third_party/polymer/components/paper-dropdown-menu/bower.json
new file mode 100644
index 0000000..5ab1af6
--- /dev/null
+++ b/third_party/polymer/components/paper-dropdown-menu/bower.json
@@ -0,0 +1,17 @@
+{
+  "name": "paper-dropdown-menu",
+  "private": false,
+  "dependencies": {
+    "polymer": "Polymer/polymer#^0.5.0",
+    "core-a11y-keys": "Polymer/core-a11y-keys#^0.5.0",
+    "core-collapse": "Polymer/core-collapse#^0.5.0",
+    "core-focusable": "Polymer/core-focusable#^0.5.0",
+    "core-icon": "Polymer/core-icon#^0.5.0",
+    "core-icons": "Polymer/core-icons#^0.5.0",
+    "core-menu": "Polymer/core-menu#^0.5.0",
+    "paper-dropdown": "Polymer/paper-dropdown#^0.5.0",
+    "paper-item": "Polymer/paper-item#^0.5.0",
+    "paper-shadow": "Polymer/paper-shadow#^0.5.0"
+  },
+  "version": "0.5.2"
+}
\ No newline at end of file
diff --git a/third_party/polymer/components/paper-dropdown-menu/demo.html b/third_party/polymer/components/paper-dropdown-menu/demo.html
new file mode 100644
index 0000000..e2b07e85
--- /dev/null
+++ b/third_party/polymer/components/paper-dropdown-menu/demo.html
@@ -0,0 +1,211 @@
+<!doctype html>
+<!--
+Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
+This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
+The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
+The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
+Code distributed by Google as part of the polymer project is also
+subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
+-->
+
+<html>
+<head>
+
+  <meta charset="utf-8">
+  <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+  <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+
+  <title>paper-dropdown-menu</title>
+
+  <script src="../webcomponentsjs/webcomponents.js"></script>
+
+  <link href="../core-collapse/core-collapse.html" rel="import">
+  <link href="../core-menu/core-menu.html" rel="import">
+  <link href="../paper-dropdown/paper-dropdown.html" rel="import">
+  <link href="../paper-item/paper-item.html" rel="import">
+
+  <link href="paper-dropdown-menu.html" rel="import">
+
+  <style shim-shadowdom>
+    body {
+      font-family: RobotoDraft, 'Helvetica Neue', Helvetica, Arial;
+      font-size: 14px;
+      margin: 0;
+      padding: 24px;
+      -webkit-tap-highlight-color: rgba(0,0,0,0);
+      -webkit-touch-callout: none;
+    }
+
+    section {
+      padding: 20px 0;
+    }
+
+    section > div {
+      padding: 14px;
+      font-size: 16px;
+    }
+
+    html /deep/ paper-dropdown-menu {
+      box-sizing: border-box;
+      width: 170px;
+    }
+
+    html /deep/ core-menu {
+      box-sizing: border-box;
+      width: 170px;
+    }
+
+    paper-item {
+      overflow: hidden;
+      white-space: nowrap;
+      text-overflow: ellipsis;
+    }
+
+    html /deep/ core-collapse {
+      border: 1px solid #ccc;
+      padding: 8px;
+    }
+
+    html /deep/ core-overlay {
+      border: 1px solid #ccc;
+      padding: 8px;
+      background: #fff;
+    }
+
+    .constrained-height {
+      height: 150px;
+    }
+
+    .colored {
+      color: #0f9d58;
+    }
+
+    .dropdown.colored::shadow #ripple,
+    .dropdown.colored::shadow #background {
+      border: 1px solid #0f9d58;
+      background-color: #b7e1cd;
+    }
+
+  </style>
+
+</head>
+<body>
+
+  <template is="auto-binding">
+
+    <section>
+
+      <div>Absolutely positioned dropdowns</div>
+
+      <paper-dropdown-menu label="Your favorite pastry">
+        <paper-dropdown class="dropdown">
+          <core-menu class="menu">
+            <template repeat="{{pastries}}">
+              <paper-item>{{}}</paper-item>
+            </template>
+          </core-menu>
+        </paper-dropdown>
+      </paper-dropdown-menu>
+
+      <br><br>
+
+      <paper-dropdown-menu label="Disabled" disabled>
+        <paper-dropdown class="dropdown">
+          <core-menu class="menu">
+            <paper-item>Should not see this</paper-item>
+          </core-menu>
+        </paper-dropdown>
+      </paper-dropdown-menu>
+
+    </section>
+
+    <section>
+
+      <div>Layered dropdowns</div>
+
+      <button onclick="document.getElementById('overlay').toggle()">toggle core-overlay</button>
+
+      <core-overlay id="overlay">
+
+        <paper-dropdown-menu label="Your favorite pastry">
+          <paper-dropdown layered class="dropdown">
+            <core-menu class="menu">
+              <template repeat="{{pastries}}">
+                <paper-item>{{}}</paper-item>
+              </template>
+            </core-menu>
+          </paper-dropdown>
+        </paper-dropdown-menu>
+
+      </core-overlay>
+
+      <button onclick="document.getElementById('collapse').toggle()">toggle core-collapse</button>
+
+      <br>
+
+      <core-collapse id="collapse">
+
+        <paper-dropdown-menu label="Your favorite pastry">
+          <paper-dropdown layered class="dropdown">
+            <core-menu class="menu">
+              <template repeat="{{pastries}}">
+                <paper-item>{{}}</paper-item>
+              </template>
+            </core-menu>
+          </paper-dropdown>
+        </paper-dropdown-menu>
+
+      </core-collapse>
+
+    </section>
+
+    <section>
+
+      <div>Custom styling</div>
+
+      <paper-dropdown-menu label="Constrained height">
+        <paper-dropdown class="dropdown constrained-height">
+          <core-menu class="menu">
+            <template repeat="{{pastries}}">
+              <paper-item>{{}}</paper-item>
+            </template>
+          </core-menu>
+        </paper-dropdown>
+      </paper-dropdown-menu>
+
+      <br><br>
+
+      <paper-dropdown-menu label="Colored">
+        <paper-dropdown class="dropdown colored">
+          <core-menu class="menu">
+            <template repeat="{{pastries}}">
+              <paper-item>{{}}</paper-item>
+            </template>
+          </core-menu>
+        </paper-dropdown>
+      </paper-dropdown-menu>
+
+    </section>
+
+  </template>
+
+  <script>
+
+    scope = document.querySelector('template[is=auto-binding]');
+
+    scope.pastries = [
+      'Apple fritter',
+      'Croissant',
+      'Donut',
+      'Financier',
+      'Jello',
+      'Madeleine',
+      'Pound cake',
+      'Pretzel',
+      'Sfogliatelle'
+    ];
+
+  </script>
+
+</body>
+</html>
diff --git a/third_party/polymer/components/paper-dropdown-menu/index.html b/third_party/polymer/components/paper-dropdown-menu/index.html
new file mode 100644
index 0000000..294215a
--- /dev/null
+++ b/third_party/polymer/components/paper-dropdown-menu/index.html
@@ -0,0 +1,22 @@
+<!doctype html>
+<!--
+Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
+This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE
+The complete set of authors may be found at http://polymer.github.io/AUTHORS
+The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS
+Code distributed by Google as part of the polymer project is also
+subject to an additional IP rights grant found at http://polymer.github.io/PATENTS
+-->
+<html>
+<head>
+
+  <script src="../webcomponentsjs/webcomponents.js"></script>
+  <link rel="import" href="../core-component-page/core-component-page.html">
+
+</head>
+<body unresolved>
+
+  <core-component-page></core-component-page>
+
+</body>
+</html>
diff --git a/third_party/polymer/components/paper-dropdown-menu/paper-dropdown-menu.css b/third_party/polymer/components/paper-dropdown-menu/paper-dropdown-menu.css
new file mode 100644
index 0000000..2621f62
--- /dev/null
+++ b/third_party/polymer/components/paper-dropdown-menu/paper-dropdown-menu.css
@@ -0,0 +1,44 @@
+/*
+ * @license
+ * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
+ * This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
+ * The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
+ * The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
+ * Code distributed by Google as part of the polymer project is also
+ * subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
+ */
+
+:host {
+  position: relative;
+  display: inline-block;
+  background-color: #fff;
+  -moz-user-select: none;
+  -ms-user-select: none;
+  -webkit-user-select: none;
+  user-select: none;
+  cursor: pointer;
+  padding: 0.75em 0;
+}
+
+#control {
+  box-sizing: border-box;
+  max-height: 2em;
+  color: #757575;
+  border-bottom: 1px solid #757575;
+}
+
+#control[selected] {
+  color: #000;
+}
+
+#control > div {
+  padding: 0.5em 0 0.25em;
+  overflow: hidden;
+  /* FIXME not working for some reason */
+  white-space: nowrap;
+  text-overflow: ellipsis;
+}
+
+core-icon {
+  margin: 0.3em 0 0.2em 0.25em;
+}
\ No newline at end of file
diff --git a/third_party/polymer/components/paper-dropdown-menu/paper-dropdown-menu.html b/third_party/polymer/components/paper-dropdown-menu/paper-dropdown-menu.html
new file mode 100644
index 0000000..adcca1b5
--- /dev/null
+++ b/third_party/polymer/components/paper-dropdown-menu/paper-dropdown-menu.html
@@ -0,0 +1,181 @@
+<!--
+Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
+This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
+The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
+The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
+Code distributed by Google as part of the polymer project is also
+subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
+-->
+
+<!--
+
+`paper-dropdown-menu` works together with `paper-dropdown` and `core-menu` to
+implement a drop-down menu. The currently selected item is displayed in the
+control. If no item is selected, the `label` is displayed instead.
+
+The child element with the class `dropdown` will be used as the drop-down
+menu. It should be a `paper-dropdown` or other overlay element. You should
+also provide a `core-selector` or other selector element, such as `core-menu`,
+in the drop-down. You should apply the class `menu` to the selector element.
+
+Example:
+
+    <paper-dropdown-menu label="Your favorite pastry">
+        <paper-dropdown class="dropdown">
+            <core-menu class="menu">
+                <paper-item>Croissant</paper-item>
+                <paper-item>Donut</paper-item>
+                <paper-item>Financier</paper-item>
+                <paper-item>Madeleine</paper-item>
+            </core-menu>
+        </paper-dropdown>
+    </paper-dropdown-menu>
+
+This example renders a drop-down menu with 4 options.
+
+@group Paper Elements
+@element paper-dropdown-menu
+@extends core-dropdown-base
+@status unstable
+@homepage github.io
+-->
+
+<!--
+Fired when an item's selection state is changed. This event is fired both
+when an item is selected or deselected. The `isSelected` detail property
+contains the selection state.
+
+@event core-select
+@param {Object} detail
+  @param {boolean} detail.isSelected true for selection and false for deselection
+  @param {Object} detail.item the item element
+-->
+<link href="../polymer/polymer.html" rel="import">
+
+<link href="../core-a11y-keys/core-a11y-keys.html" rel="import">
+<link href="../core-dropdown/core-dropdown-base.html" rel="import">
+<link href="../core-focusable/core-focusable.html" rel="import">
+<link href="../core-icon/core-icon.html" rel="import">
+<link href="../core-icons/core-icons.html" rel="import">
+<link href="../paper-shadow/paper-shadow.html" rel="import">
+
+<style shim-shadowdom>
+  html /deep/ #paper-dropdown-menu-dropdown {
+    margin: 12px;
+    overflow: visible;
+  }
+
+  html /deep/ #paper-dropdown-menu-dropdown #menu {
+    padding: 8px 0;
+    margin: 0;
+  }
+
+  html /deep/ #paper-dropdown-menu-dropdown .menu-container {
+    overflow: auto;
+    max-height: 100%;
+    max-width: 100%;
+  }
+</style>
+
+<polymer-element name="paper-dropdown-menu" extends="core-dropdown-base" relative layout inline horizontal center tabindex="0">
+<template>
+
+  <style>
+    :host {
+      -moz-user-select: none;
+      -ms-user-select: none;
+      -webkit-user-select: none;
+      user-select: none;
+      cursor: pointer;
+      padding: 0.5em 0 0.25em;
+      margin: 0.75em 0;
+      border-bottom: 1px solid #757575;
+      outline: none;
+    }
+
+    #label, #arrow {
+      color: #757575;
+    }
+
+    #label {
+      overflow: hidden;
+      white-space: nowrap;
+      text-overflow: ellipsis;
+    }
+  </style>
+
+  <core-a11y-keys target="{{}}" keys="enter space" on-keys-pressed="{{toggleOverlay}}"></core-a11y-keys>
+
+  <div flex auto id="label">{{selectedItemLabel || label}}</div>
+  <core-icon id="arrow" icon="{{opened ? openedIcon : closedIcon}}"></core-icon>
+
+  <content></content>
+
+</template>
+<script>
+
+(function() {
+
+  var p = {
+
+    publish: {
+
+      /**
+       * A label for the control. The label is displayed if no item is selected.
+       *
+       * @attribute label
+       * @type string
+       * @default 'Select an item'
+       */
+      label: 'Select an item',
+
+      /**
+       * The icon to display when the drop-down is opened.
+       *
+       * @attribute openedIcon
+       * @type string
+       * @default 'arrow-drop-up'
+       */
+      openedIcon: 'arrow-drop-up',
+
+      /**
+       * The icon to display when the drop-down is closed.
+       *
+       * @attribute closedIcon
+       * @type string
+       * @default 'arrow-drop-down'
+       */
+      closedIcon: 'arrow-drop-down'
+
+    },
+
+    selectedItemLabel: '',
+
+    overlayListeners: {
+      'core-overlay-open': 'openAction',
+      'core-activate': 'activateAction',
+      'core-select': 'selectAction'
+    },
+
+    activateAction: function(e) {
+      this.opened = false;
+    },
+
+    selectAction: function(e) {
+      var detail = e.detail;
+      if (detail.isSelected) {
+        this.selectedItemLabel = detail.item.label || detail.item.textContent;
+      } else {
+        this.selectedItemLabel = '';
+      }
+    }
+
+  };
+
+  Polymer.mixin2(p, Polymer.CoreFocusable);
+  Polymer(p);
+
+})();
+
+</script>
+</polymer-element>
diff --git a/third_party/polymer/components/paper-dropdown-menu/test/basic.html b/third_party/polymer/components/paper-dropdown-menu/test/basic.html
new file mode 100644
index 0000000..313dd49
--- /dev/null
+++ b/third_party/polymer/components/paper-dropdown-menu/test/basic.html
@@ -0,0 +1,115 @@
+<!doctype html>
+<!--
+Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
+This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
+The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
+The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
+Code distributed by Google as part of the polymer project is also
+subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
+-->
+<html>
+<head>
+  <meta charset="UTF-8">
+  <title>paper-dropdown-menu basic tests</title>
+  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">
+
+  <script src="../../webcomponentsjs/webcomponents.js"></script>
+  <script src="../../web-component-tester/browser.js"></script>
+
+  <link href="../core-dropdown.html" rel="import">
+
+  <style>
+    body {
+      text-align: center;
+      margin-top: 200px;
+    }
+  </style>
+
+</head>
+<body>
+
+  <div relative id="trigger1">
+    tap
+    <core-dropdown id="dropdown1">Hello World!</core-dropdown>
+  </div>
+
+  <div relative id="trigger2">
+    tap
+    <core-dropdown id="dropdown2">Hello World!</core-dropdown>
+  </div>
+
+  <div relative id="trigger3">
+    tap
+    <core-dropdown id="dropdown3">Hello World!</core-dropdown>
+  </div>
+
+  <script>
+
+    function assertPosition(dropdown, trigger) {
+      var dr = dropdown.getBoundingClientRect();
+      var tr = trigger.getBoundingClientRect();
+
+      if (dropdown.halign === 'left') {
+        assert.equal(dr.left, tr.left);
+      } else {
+        assert.equal(dr.right, tr.right);
+      }
+
+      if (dropdown.valign === 'top') {
+        assert.equal(dr.top, tr.top);
+      } else {
+        assert.equal(dr.bottom, tr.bottom);
+      }
+    };
+
+    function flushLayoutAndRender(callback) {
+      flush(function() {
+        document.body.offsetTop;
+        requestAnimationFrame(function() {
+          callback();
+        });
+      });
+    }
+
+    var d1 = document.getElementById('dropdown1');
+    var t1 = document.getElementById('trigger1');
+    d1.relatedTarget = t1;
+
+    var d2 = document.getElementById('dropdown2');
+    var t2 = document.getElementById('trigger2');
+    d2.relatedTarget = t2;
+
+    var d3 = document.getElementById('dropdown3');
+    var t3 = document.getElementById('trigger3');
+    d3.relatedTarget = t3;
+
+    test('default', function(done) {
+      d1.opened = true;
+      flushLayoutAndRender(function() {
+        assertPosition(d1, t1);
+        done();
+      });
+    });
+
+    test('bottom alignment', function(done) {
+      d2.valign = 'bottom';
+      d2.opened = true;
+      flushLayoutAndRender(function() {
+        assertPosition(d2, t2);
+        done();
+      });
+    });
+
+    test('right alignment', function(done) {
+      d3.halign = 'right';
+      d3.opened = true;
+      flushLayoutAndRender(function() {
+        assertPosition(d3, t3);
+        done();
+      });
+    });
+
+  </script>
+
+</body>
+</html>
diff --git a/third_party/polymer/components/paper-dropdown/.bower.json b/third_party/polymer/components/paper-dropdown/.bower.json
new file mode 100644
index 0000000..d067efa
--- /dev/null
+++ b/third_party/polymer/components/paper-dropdown/.bower.json
@@ -0,0 +1,29 @@
+{
+  "name": "paper-dropdown",
+  "private": false,
+  "dependencies": {
+    "polymer": "Polymer/polymer#^0.5.0",
+    "core-animation": "Polymer/core-animation#^0.5.0",
+    "core-collapse": "Polymer/core-collapse#^0.5.0",
+    "core-dropdown": "Polymer/core-dropdown#^0.5.0",
+    "core-transition": "Polymer/core-transition#^0.5.0",
+    "core-icons": "Polymer/core-icons#^0.5.0",
+    "paper-icon-button": "Polymer/paper-icon-button#^0.5.0",
+    "paper-item": "Polymer/paper-item#^0.5.0",
+    "paper-shadow": "Polymer/paper-shadow#^0.5.0"
+  },
+  "devDependencies": {
+    "web-component-tester": "web-component-tester#master"
+  },
+  "version": "0.5.2",
+  "homepage": "https://github.com/Polymer/paper-dropdown",
+  "_release": "0.5.2",
+  "_resolution": {
+    "type": "version",
+    "tag": "0.5.2",
+    "commit": "dcb105c817501ef360e6780ec0e67e8098fdb18e"
+  },
+  "_source": "git://github.com/Polymer/paper-dropdown.git",
+  "_target": "0.5.2",
+  "_originalSource": "Polymer/paper-dropdown"
+}
\ No newline at end of file
diff --git a/third_party/polymer/components/paper-dropdown/README.md b/third_party/polymer/components/paper-dropdown/README.md
new file mode 100644
index 0000000..ba0ad36
--- /dev/null
+++ b/third_party/polymer/components/paper-dropdown/README.md
@@ -0,0 +1,6 @@
+paper-dropdown
+==============
+
+owner: @morethanreal
+
+See the [component page](https://www.polymer-project.org/docs/elements/paper-elements.html#paper-dropdown) for more information.
diff --git a/third_party/polymer/components/paper-dropdown/bower.json b/third_party/polymer/components/paper-dropdown/bower.json
new file mode 100644
index 0000000..3b5141ab
--- /dev/null
+++ b/third_party/polymer/components/paper-dropdown/bower.json
@@ -0,0 +1,19 @@
+{
+  "name": "paper-dropdown",
+  "private": false,
+  "dependencies": {
+    "polymer": "Polymer/polymer#^0.5.0",
+    "core-animation": "Polymer/core-animation#^0.5.0",
+    "core-collapse": "Polymer/core-collapse#^0.5.0",
+    "core-dropdown": "Polymer/core-dropdown#^0.5.0",
+    "core-transition": "Polymer/core-transition#^0.5.0",
+    "core-icons": "Polymer/core-icons#^0.5.0",
+    "paper-icon-button": "Polymer/paper-icon-button#^0.5.0",
+    "paper-item": "Polymer/paper-item#^0.5.0",
+    "paper-shadow": "Polymer/paper-shadow#^0.5.0"
+  },
+  "devDependencies": {
+    "web-component-tester": "web-component-tester#master"
+  },
+  "version": "0.5.2"
+}
\ No newline at end of file
diff --git a/third_party/polymer/components/paper-dropdown/demo.html b/third_party/polymer/components/paper-dropdown/demo.html
new file mode 100644
index 0000000..d23962db
--- /dev/null
+++ b/third_party/polymer/components/paper-dropdown/demo.html
@@ -0,0 +1,456 @@
+<!doctype html>
+<!--
+Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
+This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
+The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
+The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
+Code distributed by Google as part of the polymer project is also
+subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
+-->
+
+<html>
+<head>
+
+  <meta charset="utf-8">
+  <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+  <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+
+  <title>paper-dropdown</title>
+
+  <script src="../webcomponentsjs/webcomponents.js"></script>
+
+  <link href="../core-collapse/core-collapse.html" rel="import">
+  <link href="../core-icons/core-icons.html" rel="import">
+  <link href="../paper-icon-button/paper-icon-button.html" rel="import">
+  <link href="../paper-item/paper-item.html" rel="import">
+
+  <link href="paper-dropdown.html" rel="import">
+
+  <style shim-shadowdom>
+    body {
+      font-family: RobotoDraft, 'Helvetica Neue', Helvetica, Arial;
+      font-size: 14px;
+      margin: 0;
+      padding: 24px;
+      -webkit-tap-highlight-color: rgba(0,0,0,0);
+      -webkit-touch-callout: none;
+    }
+
+    section {
+      padding: 20px 0;
+    }
+
+    section > div {
+      padding: 14px;
+      font-size: 16px;
+    }
+
+    x-trigger {
+      z-index: auto;
+    }
+
+    html /deep/ paper-dropdown:not(.no-padding)::shadow #scroller {
+      box-sizing: border-box;
+      padding: 8px;
+    }
+
+    .with-margin {
+      margin: 12px;
+    }
+
+    .open-below {
+      top: 38px;
+    }
+
+  </style>
+
+</head>
+<body>
+
+  <polymer-element name="x-trigger" extends="paper-icon-button" relative on-tap="{{toggle}}" noink>
+  <template>
+    <shadow></shadow>
+    <content></content>
+  </template>
+  <script>
+    Polymer({
+      toggle: function() {
+        if (!this.dropdown) {
+          this.dropdown = this.querySelector('paper-dropdown');
+        }
+        this.dropdown && this.dropdown.toggle();
+      }
+    });
+  </script>
+  </polymer-element>
+
+  <template is="auto-binding">
+
+    <section>
+
+      <div>Absolutely positioned dropdowns</div>
+
+      <x-trigger icon="menu">
+        <paper-dropdown>
+          halign = left
+          <br>
+          valign = top
+        </paper-dropdown>
+      </x-trigger>
+
+      <x-trigger icon="menu">
+        <paper-dropdown valign="bottom">
+          halign = left
+          <br>
+          valign = bottom
+        </paper-dropdown>
+      </x-trigger>
+
+      <x-trigger icon="menu">
+        <paper-dropdown halign="right">
+          halign = right
+          <br>
+          valign = top
+        </paper-dropdown>
+      </x-trigger>
+
+      <x-trigger icon="menu">
+        <paper-dropdown class="no-padding">
+          <div class="menu">
+            <paper-item>Item 1</paper-item>
+            <paper-item>Item 2</paper-item>
+            <paper-item>Item 3</paper-item>
+          </div>
+        </paper-dropdown>
+      </x-trigger>
+
+    </section>
+
+    <section>
+
+      <div>Layered dropdowns</div>
+
+      <button onclick="document.getElementById('collapse').toggle()">toggle core-collapse</button>
+
+      <br>
+
+      <core-collapse id="collapse">
+
+        <x-trigger icon="menu">
+          <paper-dropdown layered>
+            halign = left
+            <br>
+            valign = top
+          </paper-dropdown>
+        </x-trigger>
+
+      </core-collapse>
+
+    </section>
+
+    <section>
+
+      <div>Scrolling and margin</div>
+
+      <x-trigger icon="menu">
+        <paper-dropdown>
+          no margin<br>
+          <br>
+          <template repeat="{{countries}}">
+            {{name}}<br>
+          </template>
+        </paper-dropdown>
+      </x-trigger>
+
+      <x-trigger icon="menu">
+        <paper-dropdown class="with-margin">
+          with margin<br>
+          <br>
+          <template repeat="{{countries}}">
+            {{name}}<br>
+          </template>
+        </paper-dropdown>
+      </x-trigger>
+
+    </section>
+
+    <section>
+
+      <div>Custom position</div>
+
+      <x-trigger icon="menu">
+        <paper-dropdown class="open-below">
+          top: 38px
+        </paper-dropdown>
+      </x-trigger>
+
+    </section>
+
+  </template>
+
+  <script>
+
+    scope = document.querySelector('template[is=auto-binding]');
+
+    scope.countries = [
+      {name: 'Afghanistan', code: 'AF'},
+      {name: 'Åland Islands', code: 'AX'},
+      {name: 'Albania', code: 'AL'},
+      {name: 'Algeria', code: 'DZ'},
+      {name: 'American Samoa', code: 'AS'},
+      {name: 'Andorra', code: 'AD'},
+      {name: 'Angola', code: 'AO'},
+      {name: 'Anguilla', code: 'AI'},
+      {name: 'Antarctica', code: 'AQ'},
+      {name: 'Antigua and Barbuda', code: 'AG'},
+      {name: 'Argentina', code: 'AR'},
+      {name: 'Armenia', code: 'AM'},
+      {name: 'Aruba', code: 'AW'},
+      {name: 'Australia', code: 'AU'},
+      {name: 'Austria', code: 'AT'},
+      {name: 'Azerbaijan', code: 'AZ'},
+      {name: 'Bahamas', code: 'BS'},
+      {name: 'Bahrain', code: 'BH'},
+      {name: 'Bangladesh', code: 'BD'},
+      {name: 'Barbados', code: 'BB'},
+      {name: 'Belarus', code: 'BY'},
+      {name: 'Belgium', code: 'BE'},
+      {name: 'Belize', code: 'BZ'},
+      {name: 'Benin', code: 'BJ'},
+      {name: 'Bermuda', code: 'BM'},
+      {name: 'Bhutan', code: 'BT'},
+      {name: 'Bolivia', code: 'BO'},
+      {name: 'Bosnia and Herzegovina', code: 'BA'},
+      {name: 'Botswana', code: 'BW'},
+      {name: 'Bouvet Island', code: 'BV'},
+      {name: 'Brazil', code: 'BR'},
+      {name: 'British Indian Ocean Territory', code: 'IO'},
+      {name: 'Brunei Darussalam', code: 'BN'},
+      {name: 'Bulgaria', code: 'BG'},
+      {name: 'Burkina Faso', code: 'BF'},
+      {name: 'Burundi', code: 'BI'},
+      {name: 'Cambodia', code: 'KH'},
+      {name: 'Cameroon', code: 'CM'},
+      {name: 'Canada', code: 'CA'},
+      {name: 'Cape Verde', code: 'CV'},
+      {name: 'Cayman Islands', code: 'KY'},
+      {name: 'Central African Republic', code: 'CF'},
+      {name: 'Chad', code: 'TD'},
+      {name: 'Chile', code: 'CL'},
+      {name: 'China', code: 'CN'},
+      {name: 'Christmas Island', code: 'CX'},
+      {name: 'Cocos (Keeling) Islands', code: 'CC'},
+      {name: 'Colombia', code: 'CO'},
+      {name: 'Comoros', code: 'KM'},
+      {name: 'Congo', code: 'CG'},
+      {name: 'Congo, The Democratic Republic of the', code: 'CD'},
+      {name: 'Cook Islands', code: 'CK'},
+      {name: 'Costa Rica', code: 'CR'},
+      {name: 'Cote D\'Ivoire', code: 'CI'},
+      {name: 'Croatia', code: 'HR'},
+      {name: 'Cuba', code: 'CU'},
+      {name: 'Cyprus', code: 'CY'},
+      {name: 'Czech Republic', code: 'CZ'},
+      {name: 'Denmark', code: 'DK'},
+      {name: 'Djibouti', code: 'DJ'},
+      {name: 'Dominica', code: 'DM'},
+      {name: 'Dominican Republic', code: 'DO'},
+      {name: 'Ecuador', code: 'EC'},
+      {name: 'Egypt', code: 'EG'},
+      {name: 'El Salvador', code: 'SV'},
+      {name: 'Equatorial Guinea', code: 'GQ'},
+      {name: 'Eritrea', code: 'ER'},
+      {name: 'Estonia', code: 'EE'},
+      {name: 'Ethiopia', code: 'ET'},
+      {name: 'Falkland Islands (Malvinas)', code: 'FK'},
+      {name: 'Faroe Islands', code: 'FO'},
+      {name: 'Fiji', code: 'FJ'},
+      {name: 'Finland', code: 'FI'},
+      {name: 'France', code: 'FR'},
+      {name: 'French Guiana', code: 'GF'},
+      {name: 'French Polynesia', code: 'PF'},
+      {name: 'French Southern Territories', code: 'TF'},
+      {name: 'Gabon', code: 'GA'},
+      {name: 'Gambia', code: 'GM'},
+      {name: 'Georgia', code: 'GE'},
+      {name: 'Germany', code: 'DE'},
+      {name: 'Ghana', code: 'GH'},
+      {name: 'Gibraltar', code: 'GI'},
+      {name: 'Greece', code: 'GR'},
+      {name: 'Greenland', code: 'GL'},
+      {name: 'Grenada', code: 'GD'},
+      {name: 'Guadeloupe', code: 'GP'},
+      {name: 'Guam', code: 'GU'},
+      {name: 'Guatemala', code: 'GT'},
+      {name: 'Guernsey', code: 'GG'},
+      {name: 'Guinea', code: 'GN'},
+      {name: 'Guinea-Bissau', code: 'GW'},
+      {name: 'Guyana', code: 'GY'},
+      {name: 'Haiti', code: 'HT'},
+      {name: 'Heard Island and Mcdonald Islands', code: 'HM'},
+      {name: 'Holy See (Vatican City State)', code: 'VA'},
+      {name: 'Honduras', code: 'HN'},
+      {name: 'Hong Kong', code: 'HK'},
+      {name: 'Hungary', code: 'HU'},
+      {name: 'Iceland', code: 'IS'},
+      {name: 'India', code: 'IN'},
+      {name: 'Indonesia', code: 'ID'},
+      {name: 'Iran, Islamic Republic Of', code: 'IR'},
+      {name: 'Iraq', code: 'IQ'},
+      {name: 'Ireland', code: 'IE'},
+      {name: 'Isle of Man', code: 'IM'},
+      {name: 'Israel', code: 'IL'},
+      {name: 'Italy', code: 'IT'},
+      {name: 'Jamaica', code: 'JM'},
+      {name: 'Japan', code: 'JP'},
+      {name: 'Jersey', code: 'JE'},
+      {name: 'Jordan', code: 'JO'},
+      {name: 'Kazakhstan', code: 'KZ'},
+      {name: 'Kenya', code: 'KE'},
+      {name: 'Kiribati', code: 'KI'},
+      {name: 'Korea, Democratic People\'S Republic of', code: 'KP'},
+      {name: 'Korea, Republic of', code: 'KR'},
+      {name: 'Kuwait', code: 'KW'},
+      {name: 'Kyrgyzstan', code: 'KG'},
+      {name: 'Lao People\'S Democratic Republic', code: 'LA'},
+      {name: 'Latvia', code: 'LV'},
+      {name: 'Lebanon', code: 'LB'},
+      {name: 'Lesotho', code: 'LS'},
+      {name: 'Liberia', code: 'LR'},
+      {name: 'Libyan Arab Jamahiriya', code: 'LY'},
+      {name: 'Liechtenstein', code: 'LI'},
+      {name: 'Lithuania', code: 'LT'},
+      {name: 'Luxembourg', code: 'LU'},
+      {name: 'Macao', code: 'MO'},
+      {name: 'Macedonia, The Former Yugoslav Republic of', code: 'MK'},
+      {name: 'Madagascar', code: 'MG'},
+      {name: 'Malawi', code: 'MW'},
+      {name: 'Malaysia', code: 'MY'},
+      {name: 'Maldives', code: 'MV'},
+      {name: 'Mali', code: 'ML'},
+      {name: 'Malta', code: 'MT'},
+      {name: 'Marshall Islands', code: 'MH'},
+      {name: 'Martinique', code: 'MQ'},
+      {name: 'Mauritania', code: 'MR'},
+      {name: 'Mauritius', code: 'MU'},
+      {name: 'Mayotte', code: 'YT'},
+      {name: 'Mexico', code: 'MX'},
+      {name: 'Micronesia, Federated States of', code: 'FM'},
+      {name: 'Moldova, Republic of', code: 'MD'},
+      {name: 'Monaco', code: 'MC'},
+      {name: 'Mongolia', code: 'MN'},
+      {name: 'Montserrat', code: 'MS'},
+      {name: 'Morocco', code: 'MA'},
+      {name: 'Mozambique', code: 'MZ'},
+      {name: 'Myanmar', code: 'MM'},
+      {name: 'Namibia', code: 'NA'},
+      {name: 'Nauru', code: 'NR'},
+      {name: 'Nepal', code: 'NP'},
+      {name: 'Netherlands', code: 'NL'},
+      {name: 'Netherlands Antilles', code: 'AN'},
+      {name: 'New Caledonia', code: 'NC'},
+      {name: 'New Zealand', code: 'NZ'},
+      {name: 'Nicaragua', code: 'NI'},
+      {name: 'Niger', code: 'NE'},
+      {name: 'Nigeria', code: 'NG'},
+      {name: 'Niue', code: 'NU'},
+      {name: 'Norfolk Island', code: 'NF'},
+      {name: 'Northern Mariana Islands', code: 'MP'},
+      {name: 'Norway', code: 'NO'},
+      {name: 'Oman', code: 'OM'},
+      {name: 'Pakistan', code: 'PK'},
+      {name: 'Palau', code: 'PW'},
+      {name: 'Palestinian Territory, Occupied', code: 'PS'},
+      {name: 'Panama', code: 'PA'},
+      {name: 'Papua New Guinea', code: 'PG'},
+      {name: 'Paraguay', code: 'PY'},
+      {name: 'Peru', code: 'PE'},
+      {name: 'Philippines', code: 'PH'},
+      {name: 'Pitcairn', code: 'PN'},
+      {name: 'Poland', code: 'PL'},
+      {name: 'Portugal', code: 'PT'},
+      {name: 'Puerto Rico', code: 'PR'},
+      {name: 'Qatar', code: 'QA'},
+      {name: 'Reunion', code: 'RE'},
+      {name: 'Romania', code: 'RO'},
+      {name: 'Russian Federation', code: 'RU'},
+      {name: 'RWANDA', code: 'RW'},
+      {name: 'Saint Helena', code: 'SH'},
+      {name: 'Saint Kitts and Nevis', code: 'KN'},
+      {name: 'Saint Lucia', code: 'LC'},
+      {name: 'Saint Pierre and Miquelon', code: 'PM'},
+      {name: 'Saint Vincent and the Grenadines', code: 'VC'},
+      {name: 'Samoa', code: 'WS'},
+      {name: 'San Marino', code: 'SM'},
+      {name: 'Sao Tome and Principe', code: 'ST'},
+      {name: 'Saudi Arabia', code: 'SA'},
+      {name: 'Senegal', code: 'SN'},
+      {name: 'Serbia and Montenegro', code: 'CS'},
+      {name: 'Seychelles', code: 'SC'},
+      {name: 'Sierra Leone', code: 'SL'},
+      {name: 'Singapore', code: 'SG'},
+      {name: 'Slovakia', code: 'SK'},
+      {name: 'Slovenia', code: 'SI'},
+      {name: 'Solomon Islands', code: 'SB'},
+      {name: 'Somalia', code: 'SO'},
+      {name: 'South Africa', code: 'ZA'},
+      {name: 'South Georgia and the South Sandwich Islands', code: 'GS'},
+      {name: 'Spain', code: 'ES'},
+      {name: 'Sri Lanka', code: 'LK'},
+      {name: 'Sudan', code: 'SD'},
+      {name: 'Suriname', code: 'SR'},
+      {name: 'Svalbard and Jan Mayen', code: 'SJ'},
+      {name: 'Swaziland', code: 'SZ'},
+      {name: 'Sweden', code: 'SE'},
+      {name: 'Switzerland', code: 'CH'},
+      {name: 'Syrian Arab Republic', code: 'SY'},
+      {name: 'Taiwan, Province of China', code: 'TW'},
+      {name: 'Tajikistan', code: 'TJ'},
+      {name: 'Tanzania, United Republic of', code: 'TZ'},
+      {name: 'Thailand', code: 'TH'},
+      {name: 'Timor-Leste', code: 'TL'},
+      {name: 'Togo', code: 'TG'},
+      {name: 'Tokelau', code: 'TK'},
+      {name: 'Tonga', code: 'TO'},
+      {name: 'Trinidad and Tobago', code: 'TT'},
+      {name: 'Tunisia', code: 'TN'},
+      {name: 'Turkey', code: 'TR'},
+      {name: 'Turkmenistan', code: 'TM'},
+      {name: 'Turks and Caicos Islands', code: 'TC'},
+      {name: 'Tuvalu', code: 'TV'},
+      {name: 'Uganda', code: 'UG'},
+      {name: 'Ukraine', code: 'UA'},
+      {name: 'United Arab Emirates', code: 'AE'},
+      {name: 'United Kingdom', code: 'GB'},
+      {name: 'United States', code: 'US'},
+      {name: 'United States Minor Outlying Islands', code: 'UM'},
+      {name: 'Uruguay', code: 'UY'},
+      {name: 'Uzbekistan', code: 'UZ'},
+      {name: 'Vanuatu', code: 'VU'},
+      {name: 'Venezuela', code: 'VE'},
+      {name: 'Viet Nam', code: 'VN'},
+      {name: 'Virgin Islands, British', code: 'VG'},
+      {name: 'Virgin Islands, U.S.', code: 'VI'},
+      {name: 'Wallis and Futuna', code: 'WF'},
+      {name: 'Western Sahara', code: 'EH'},
+      {name: 'Yemen', code: 'YE'},
+      {name: 'Zambia', code: 'ZM'},
+      {name: 'Zimbabwe', code: 'ZW'}
+    ];
+
+    scope.pastries = [
+      'Apple fritter',
+      'Croissant',
+      'Donut',
+      'Financier',
+      'Jello',
+      'Madeleine',
+      'Pound cake',
+      'Pretzel',
+      'Sfogliatelle'
+    ];
+
+  </script>
+
+</body>
+</html>
diff --git a/third_party/polymer/components/paper-dropdown/index.html b/third_party/polymer/components/paper-dropdown/index.html
new file mode 100644
index 0000000..8c8da47a
--- /dev/null
+++ b/third_party/polymer/components/paper-dropdown/index.html
@@ -0,0 +1,22 @@
+<!doctype html>
+<!--
+Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
+This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE
+The complete set of authors may be found at http://polymer.github.io/AUTHORS
+The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS
+Code distributed by Google as part of the polymer project is also
+subject to an additional IP rights grant found at http://polymer.github.io/PATENTS
+-->
+<html>
+<head>
+
+  <script src="../webcomponentsjs/webcomponents.js"></script>
+  <link rel="import" href="../core-component-page/core-component-page.html">
+
+</head>
+<body unresolved>
+
+  <core-component-page sources='["paper-dropdown.html","paper-dropdown-transition.html"]'></core-component-page>
+
+</body>
+</html>
diff --git a/third_party/polymer/components/paper-dropdown/paper-dropdown-transition.css b/third_party/polymer/components/paper-dropdown/paper-dropdown-transition.css
new file mode 100644
index 0000000..d544ead
--- /dev/null
+++ b/third_party/polymer/components/paper-dropdown/paper-dropdown-transition.css
@@ -0,0 +1,15 @@
+/* Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
+This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
+The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
+The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
+Code distributed by Google as part of the polymer project is also
+subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt */
+
+:host(.core-transition) #ripple,
+:host(.core-transition) #background {
+  opacity: 0;
+}
+
+:host(.core-transition) #scroller {
+  opacity: 0;
+}
\ No newline at end of file
diff --git a/third_party/polymer/components/paper-dropdown/paper-dropdown-transition.html b/third_party/polymer/components/paper-dropdown/paper-dropdown-transition.html
new file mode 100644
index 0000000..5f59f1d
--- /dev/null
+++ b/third_party/polymer/components/paper-dropdown/paper-dropdown-transition.html
@@ -0,0 +1,166 @@
+<!--
+Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
+This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
+The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
+The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
+Code distributed by Google as part of the polymer project is also
+subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
+-->
+<!--
+
+`paper-dropdown-transition` is a transition for `paper-dropdown`.
+
+Add the class `menu` to a `core-selector` child of the `paper-dropdown` to
+enable the optional list cascade transition.
+
+@group Paper Elements
+@class paper-dropdown-transition
+@extends core-transition-css
+@status unstable
+-->
+<link href="../polymer/polymer.html" rel="import">
+<link href="../core-transition/core-transition-css.html" rel="import">
+<link href="../core-animation/web-animations.html" rel="import">
+
+<polymer-element name="paper-dropdown-transition" extends="core-transition-css">
+  <template>
+    <link href="paper-dropdown-transition.css" rel="stylesheet" no-shim>
+  </template>
+  <script>
+    Polymer('paper-dropdown-transition', {
+
+      publish: {
+
+        /**
+         * The duration of the transition in ms. You can also set the duration by
+         * setting a `duration` attribute on the target:
+         *
+         *    <paper-dropdown duration="1000"></paper-dropdown>
+         *
+         * @attribute duration
+         * @type number
+         * @default 500
+         */
+        duration: 500
+
+      },
+
+      setup: function(node) {
+        this.super(arguments);
+
+        var to = {
+          'top': '0%',
+          'left': '0%',
+          'bottom': '100%',
+          'right': '100%'
+        };
+
+        var bg = node.$.background;
+        bg.style.webkitTransformOrigin = to[node.halign] + ' ' + to[node.valign];
+        bg.style.transformOrigin = to[node.halign] + ' ' + to[node.valign];
+      },
+
+      transitionOpened: function(node, opened) {
+        this.super(arguments);
+
+        if (opened) {
+          if (this.player) {
+            this.player.cancel();
+          }
+
+          var duration = Number(node.getAttribute('duration')) || this.duration;
+
+          var anims = [];
+
+          var size = node.getBoundingClientRect();
+
+          var ink = node.$.ripple;
+          // var offset = 40 / Math.max(size.width, size.height);
+          var offset = 0.2;
+          anims.push(new Animation(ink, [{
+            'opacity': 0.9,
+            'transform': 'scale(0)',
+          }, {
+            'opacity': 0.9,
+            'transform': 'scale(1)'
+          }], {
+            duration: duration * offset
+          }));
+
+          var bg = node.$.background;
+          var sx = 40 / size.width;
+          var sy = 40 / size.height;
+          anims.push(new Animation(bg, [{
+            'opacity': 0.9,
+            'transform': 'scale(' + sx + ',' + sy + ')',
+          }, {
+            'opacity': 1,
+            'transform': 'scale(' + Math.max(sx, 0.95) + ',' + Math.max(sy, 0.5) + ')'
+          }, {
+            'opacity': 1,
+            'transform': 'scale(1, 1)'
+          }], {
+            delay: duration * offset,
+            duration: duration * (1 - offset),
+            fill: 'forwards'
+          }));
+
+          var menu = node.querySelector('.menu');
+          if (menu) {
+            var items = menu.items || menu.children.array();
+            var itemDelay = offset + (1 - offset) / 2;
+            var itemDuration = duration * (1 - itemDelay) / items.length;
+            var reverse = this.valign === 'bottom';
+
+            items.forEach(function(item, i) {
+              anims.push(new Animation(item, [{
+                'opacity': 0
+              }, {
+                'opacity': 1
+              }], {
+                delay: duration * itemDelay + itemDuration * (reverse ? items.length - 1 - i : i),
+                duration: itemDuration,
+                fill: 'both'
+              }));
+            }.bind(this));
+
+            anims.push(new Animation(node.$.scroller, [{
+              'opacity': 1
+            }, {
+              'opacity': 1
+            }], {
+              delay: duration * itemDelay,
+              duration: itemDuration * items.length,
+              fill: 'both'
+            }));
+
+          } else {
+            anims.push(new Animation(node.$.scroller, [{
+              'opacity': 0
+            }, {
+              'opacity': 1
+            }], {
+              delay: duration * (offset + (1 - offset) / 2),
+              duration: duration * 0.5,
+              fill: 'both'
+            }));
+          }
+
+          var group = new AnimationGroup(anims, {
+            easing: 'cubic-bezier(0.4, 0, 0.2, 1)'
+          });
+          this.player = document.timeline.play(group);
+          this.player.onfinish = function() {
+            this.fire('core-transitionend', this, node);
+          }.bind(this);
+
+        } else {
+          this.fire('core-transitionend', this, node);
+        }
+      },
+
+    });
+  </script>
+</polymer-element>
+
+<paper-dropdown-transition id="paper-dropdown-transition"></paper-dropdown-transition>
\ No newline at end of file
diff --git a/third_party/polymer/components/paper-dropdown/paper-dropdown.html b/third_party/polymer/components/paper-dropdown/paper-dropdown.html
new file mode 100644
index 0000000..07e2590
--- /dev/null
+++ b/third_party/polymer/components/paper-dropdown/paper-dropdown.html
@@ -0,0 +1,117 @@
+<!--
+Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
+This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
+The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
+The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
+Code distributed by Google as part of the polymer project is also
+subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
+-->
+
+<!--
+
+`paper-dropdown` is a `core-dropdown` with a `paper-shadow`. By default, it
+is animated on open with `paper-dropdown-transition`. Use this element with
+`paper-dropdown-menu` or `paper-menu-button` to implement UI controls that
+open a drop-down.
+
+Example:
+
+    <paper-dropdown>
+      Hi!
+    </paper-dropdown>
+
+Theming
+-------
+
+Style the background color of the dropdown with these selectors:
+
+    paper-dropdown::shadow #ripple,
+    paper-dropdown::shadow #background {
+        background-color: green;
+    }
+
+@group Paper Elements
+@element paper-dropdown
+@extends core-dropdown
+@status unstable
+-->
+
+<link href="../polymer/polymer.html" rel="import">
+<link href="../core-dropdown/core-dropdown.html" rel="import">
+<link href="../paper-shadow/paper-shadow.html" rel="import">
+
+<link href="paper-dropdown-transition.html" rel="import">
+
+<style shim-shadowdom>
+  html /deep/ paper-dropdown {
+    position: absolute;
+    overflow: visible;
+    min-height: 40px;
+  }
+</style>
+
+<polymer-element name="paper-dropdown" extends="core-dropdown">
+<template>
+
+  <style>
+    #ripple {
+      background-color: #fff;
+      position: absolute;
+      left: 0;
+      top: 0;
+      width: 40px;
+      height: 40px;
+      border-radius: 50%;
+      box-shadow: 0 2px 3px 0 rgba(0, 0, 0, 0.26);
+      opacity: 0;
+    }
+
+    :host([halign=right]) #ripple {
+      left: auto;
+      right: 0;
+    }
+
+    :host([valign=bottom]) #ripple {
+      top: auto;
+      bottom: 0;
+    }
+
+    #background {
+      background-color: #fff;
+      border-radius: inherit;
+    }
+
+    #scroller {
+      overflow: auto;
+      box-sizing: border-box;
+    }
+  </style>
+
+  <div id="ripple"></div>
+
+  <div id="background" fit>
+    <paper-shadow fit></paper-shadow>
+  </div>
+
+  <div id="scroller" relative>
+    <content></content>
+  </div>
+
+</template>
+<script>
+
+  Polymer({
+
+    publish: {
+      transition: 'paper-dropdown-transition'
+    },
+
+    ready: function() {
+      this.super();
+      this.sizingTarget = this.$.scroller;
+    }
+
+  });
+
+</script>
+</polymer-element>
\ No newline at end of file
diff --git a/third_party/polymer/reproduce.sh b/third_party/polymer/reproduce.sh
index 4029e0c..ec1ebff9 100755
--- a/third_party/polymer/reproduce.sh
+++ b/third_party/polymer/reproduce.sh
@@ -14,9 +14,19 @@
 cd "$(dirname "$0")"
 
 rm -rf components components-chromium
+rm -rf ../web-animations-js/sources
 
 bower install
 
+rm -rf components/web-animations-js/{test,node_modules}
+# TODO(jlklein): Remove when
+# https://github.com/web-animations/web-animations-next/pull/289 is released
+# and the version of web-animations-js is bumped in bower.json.
+rm components/web-animations-js/.travis-setup.sh
+
+mv components/web-animations-js ../web-animations-js/sources
+cp ../web-animations-js/sources/COPYING ../web-animations-js/LICENSE
+
 # These components are deprecated or needed only for demos.
 rm -rf components/{core-component-page,core-field,font-roboto,webcomponentsjs}
 
diff --git a/third_party/protobuf/BUILD.gn b/third_party/protobuf/BUILD.gn
index 1367d8a..6825f1e0 100644
--- a/third_party/protobuf/BUILD.gn
+++ b/third_party/protobuf/BUILD.gn
@@ -17,11 +17,6 @@
     "GOOGLE_PROTOBUF_NO_RTTI",
     "GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER",
   ]
-
-  if (is_win) {
-    # TODO(jschuh): http://crbug.com/167187 size_t -> int.
-    cflags = [ "/wd4267" ]
-  }
 }
 
 if (component_mode == "shared_library") {
@@ -98,7 +93,11 @@
   if (is_win) {
     configs -= [ "//build/config/win:lean_and_mean" ]
   }
-  public_configs = [ ":protobuf_config" ]
+  public_configs = [
+    ":protobuf_config",
+    # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+    "//build/config/compiler:no_size_t_to_int_warning",
+  ]
 
   cflags = protobuf_lite_cflags
 
@@ -177,7 +176,11 @@
   if (is_win) {
     configs -= [ "//build/config/win:lean_and_mean" ]
   }
-  public_configs = [ ":protobuf_config" ]
+  public_configs = [
+    ":protobuf_config",
+    # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+    "//build/config/compiler:no_size_t_to_int_warning",
+  ]
 
   cflags = protobuf_lite_cflags
 }
diff --git a/third_party/qcms/BUILD.gn b/third_party/qcms/BUILD.gn
index b059319..ee62b8e 100644
--- a/third_party/qcms/BUILD.gn
+++ b/third_party/qcms/BUILD.gn
@@ -25,10 +25,10 @@
   configs += [ "//build/config/compiler:no_chromium_code" ]
   public_configs = [ ":qcms_config" ]
 
-  if (cpu_arch == "x86" || cpu_arch == "x64") {
+  if (current_cpu == "x86" || current_cpu == "x64") {
     defines = [ "SSE2_ENABLE" ]
     sources += [ "src/transform-sse2.c" ]
-    if (!(is_win && cpu_arch == "x64")) {
+    if (!(is_win && current_cpu == "x64")) {
       # QCMS assumes this target isn't compiled since MSVC x64 doesn't support
       # the MMX intrinsics present in the SSE1 code.
       sources += [ "src/transform-sse1.c" ]
diff --git a/third_party/re2/BUILD.gn b/third_party/re2/BUILD.gn
index d022e4f..2bc130a 100644
--- a/third_party/re2/BUILD.gn
+++ b/third_party/re2/BUILD.gn
@@ -67,10 +67,7 @@
 
   if (is_win) {
     include_dirs = [ "mswin" ]
-    cflags = [
-      "/wd4267",  # Conversion from size_t.
-      "/wd4722",  # Destructor never terminates.
-    ]
+    cflags = [ "/wd4722" ]  # Destructor never terminates.
   } else {
     sources -= [ "mswin/stdint.h" ]
   }
diff --git a/third_party/robolectric/BUILD.gn b/third_party/robolectric/BUILD.gn
new file mode 100644
index 0000000..3271e12
--- /dev/null
+++ b/third_party/robolectric/BUILD.gn
@@ -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.
+
+import("//build/config/android/rules.gni")
+
+# GYP: //third_party/robolectric/robolectric.gyp:android_all_jar
+java_prebuilt("android_all_java") {
+  visibility = [ ":*" ]
+  jar_path = "lib/android-all-4.3_r2-robolectric-0.jar"
+}
+
+# GYP: //third_party/robolectric/robolectric.gyp:tagsoup_jar
+java_prebuilt("tagsoup_java") {
+  visibility = [ ":*" ]
+  jar_path = "lib/tagsoup-1.2.jar"
+}
+
+# GYP: //third_party/robolectric/robolectric.gyp:json_jar
+java_prebuilt("json_java") {
+  visibility = [ ":*" ]
+  jar_path = "lib/json-20080701.jar"
+}
+
+# GYP: //third_party/robolectric/robolectric.gyp:robolectric_jar
+java_prebuilt("robolectric_java") {
+  testonly = true
+  jar_path = "lib/robolectric-2.4-jar-with-dependencies.jar"
+  deps = [
+    ":android_all_java",
+    ":tagsoup_java",
+    ":json_java",
+  ]
+}
diff --git a/third_party/robolectric/LICENSE b/third_party/robolectric/LICENSE
new file mode 100644
index 0000000..f433b1a5
--- /dev/null
+++ b/third_party/robolectric/LICENSE
@@ -0,0 +1,177 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
diff --git a/third_party/robolectric/OWNERS b/third_party/robolectric/OWNERS
new file mode 100644
index 0000000..bd675fb8
--- /dev/null
+++ b/third_party/robolectric/OWNERS
@@ -0,0 +1,2 @@
+jbudorick@chromium.org
+klundberg@chromium.org
diff --git a/third_party/robolectric/README.chromium b/third_party/robolectric/README.chromium
new file mode 100644
index 0000000..10aeba31
--- /dev/null
+++ b/third_party/robolectric/README.chromium
@@ -0,0 +1,11 @@
+Name: Robolectric 
+URL: http://robolectric.org
+Version: 2.4
+License: Apache 2.0 
+License File: NOT_SHIPPED
+Security Critical: no
+License Android Compatible: yes
+Description: Robolectric is a unit test framework for Android. To update the
+robolectric jars, go to robolectric.org/download and follow link to
+robolectric-X.X.X-jar-with-dependencies.jar.
+Local Modifications: None
diff --git a/third_party/robolectric/licenses/extreme.indiana.edu.license.txt b/third_party/robolectric/licenses/extreme.indiana.edu.license.txt
new file mode 100644
index 0000000..c743155
--- /dev/null
+++ b/third_party/robolectric/licenses/extreme.indiana.edu.license.txt
@@ -0,0 +1,47 @@
+Indiana University Extreme! Lab Software License
+
+Version 1.1.1
+
+Copyright (c) 2002 Extreme! Lab, Indiana University. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without 
+modification, are permitted provided that the following conditions 
+are met:
+
+1. Redistributions of source code must retain the above copyright notice, 
+   this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright 
+   notice, this list of conditions and the following disclaimer in 
+   the documentation and/or other materials provided with the distribution.
+
+3. The end-user documentation included with the redistribution, if any, 
+   must include the following acknowledgment:
+
+  "This product includes software developed by the Indiana University 
+  Extreme! Lab (http://www.extreme.indiana.edu/)."
+
+Alternately, this acknowledgment may appear in the software itself, 
+if and wherever such third-party acknowledgments normally appear.
+
+4. The names "Indiana Univeristy" and "Indiana Univeristy Extreme! Lab" 
+must not be used to endorse or promote products derived from this 
+software without prior written permission. For written permission, 
+please contact http://www.extreme.indiana.edu/.
+
+5. Products derived from this software may not use "Indiana Univeristy" 
+name nor may "Indiana Univeristy" appear in their name, without prior 
+written permission of the Indiana University.
+
+THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+IN NO EVENT SHALL THE AUTHORS, COPYRIGHT HOLDERS OR ITS CONTRIBUTORS
+BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
diff --git a/third_party/robolectric/licenses/javolution.license.txt b/third_party/robolectric/licenses/javolution.license.txt
new file mode 100644
index 0000000..0633749
--- /dev/null
+++ b/third_party/robolectric/licenses/javolution.license.txt
@@ -0,0 +1,23 @@
+Javolution - Java(TM) Solution for Real-Time and Embedded Systems
+Copyright (c) 2006, Javolution (http://javolution.org)
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright notice,
+      this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above copyright notice,
+      this list of conditions and the following disclaimer in the documentation
+      and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/third_party/robolectric/licenses/pivotal.labs.license.txt b/third_party/robolectric/licenses/pivotal.labs.license.txt
new file mode 100644
index 0000000..c9dccb8
--- /dev/null
+++ b/third_party/robolectric/licenses/pivotal.labs.license.txt
@@ -0,0 +1,21 @@
+The MIT License
+
+Copyright (c) 2010 Xtreme Labs and Pivotal Labs
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/third_party/robolectric/robolectric.gyp b/third_party/robolectric/robolectric.gyp
new file mode 100644
index 0000000..60cbe3e
--- /dev/null
+++ b/third_party/robolectric/robolectric.gyp
@@ -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.
+
+{
+  'targets': [
+    {
+      # GN: //third_party/robolectric:android_all_java
+      'target_name': 'android_all_jar',
+      'type': 'none',
+      'variables': {
+        'jar_path': 'lib/android-all-4.3_r2-robolectric-0.jar',
+      },
+      'includes': [
+        '../../build/host_prebuilt_jar.gypi',
+      ]
+    },
+    {
+      # GN: //third_party/robolectric:tagsoup_java
+      'target_name': 'tagsoup_jar',
+      'type': 'none',
+      'variables': {
+        'jar_path': 'lib/tagsoup-1.2.jar',
+      },
+      'includes': [
+        '../../build/host_prebuilt_jar.gypi',
+      ]
+    },
+    {
+      # GN: //third_party/robolectric:json_java
+      'target_name': 'json_jar',
+      'type': 'none',
+      'variables': {
+        'jar_path': 'lib/json-20080701.jar',
+      },
+      'includes': [
+        '../../build/host_prebuilt_jar.gypi',
+      ]
+    },
+    {
+      # GN: //third_party/robolectric:robolectric_java
+      'target_name': 'robolectric_jar',
+      'type': 'none',
+      'dependencies': [
+        'android_all_jar',
+        'tagsoup_jar',
+        'json_jar',
+      ],
+      'variables': {
+        'jar_path': 'lib/robolectric-2.4-jar-with-dependencies.jar',
+      },
+      'includes': [
+        '../../build/host_prebuilt_jar.gypi',
+      ]
+    },
+  ],
+}
+
diff --git a/third_party/smhasher/BUILD.gn b/third_party/smhasher/BUILD.gn
index e1bbb5e..fd9088c 100644
--- a/third_party/smhasher/BUILD.gn
+++ b/third_party/smhasher/BUILD.gn
@@ -27,9 +27,4 @@
   ]
   configs -= [ "//build/config/compiler:chromium_code" ]
   configs += [ "//build/config/compiler:no_chromium_code" ]
-
-  if (is_win) {
-    # TODO(jschuh): http://code.google.com/p/smhasher/issues/detail?id=19
-    cflags = [ "/wd4267" ]
-  }
 }
diff --git a/third_party/snappy/BUILD.gn b/third_party/snappy/BUILD.gn
index 0f440a5..4af0e81 100644
--- a/third_party/snappy/BUILD.gn
+++ b/third_party/snappy/BUILD.gn
@@ -36,9 +36,4 @@
     # https://code.google.com/p/snappy/issues/detail?id=70
     configs -= [ "//build/config/clang:extra_warnings" ]
   }
-
-  if (is_win) {
-    # https://code.google.com/p/snappy/issues/detail?id=75
-    cflags = [ "/wd4267" ]  # Conversion from size_t to 'type'.
-  }
 }
diff --git a/third_party/sqlite/BUILD.gn b/third_party/sqlite/BUILD.gn
index 6f301ca..62efc0a 100644
--- a/third_party/sqlite/BUILD.gn
+++ b/third_party/sqlite/BUILD.gn
@@ -65,9 +65,7 @@
   configs -= [ "//build/config/compiler:chromium_code" ]
   configs += [ "//build/config/compiler:no_chromium_code" ]
 
-  if (is_win) {
-    cflags += [ "/wd4267" ]  # Conversion from size_t to "type".
-  } else if (is_linux) {
+  if (is_linux) {
     cflags += [
       # SQLite doesn"t believe in compiler warnings,
       # preferring testing.
diff --git a/third_party/tlslite/OWNERS b/third_party/tlslite/OWNERS
index 86685e3..42d0d3b 100644
--- a/third_party/tlslite/OWNERS
+++ b/third_party/tlslite/OWNERS
@@ -1,3 +1,3 @@
 agl@chromium.org
+davidben@chromium.org
 rsleevi@chromium.org
-wtc@chromium.org
diff --git a/third_party/usb_ids/README.chromium b/third_party/usb_ids/README.chromium
index 0b9251487..6ca65761 100644
--- a/third_party/usb_ids/README.chromium
+++ b/third_party/usb_ids/README.chromium
@@ -1,7 +1,7 @@
 Name: The USB ID Repository
 Short Name: usb-ids
 URL: http://www.linux-usb.org/usb-ids.html
-Version: 2015.01.07
+Version: 2015.02.03
 License: BSD
 Security Critical: no
 
diff --git a/third_party/usb_ids/usb.ids b/third_party/usb_ids/usb.ids
index 355849a4..e44dfa3 100644
--- a/third_party/usb_ids/usb.ids
+++ b/third_party/usb_ids/usb.ids
@@ -9,8 +9,8 @@
 #	The latest version can be obtained from
 #		http://www.linux-usb.org/usb.ids
 #
-# Version: 2015.01.07
-# Date:    2015-01-07 12:51:50
+# Version: 2015.02.03
+# Date:    2015-02-03 20:34:06
 #
 
 # Vendors, devices and interfaces. Please keep sorted.
@@ -21,7 +21,6 @@
 #		interface  interface_name		<-- two tabs
 
 0001  Fry's Electronics
-	142b  Arbiter Systems, Inc.
 	7778  Counterfeit flash drive [Kingston]
 0002  Ingram
 0003  Club Mac
@@ -16861,7 +16860,13 @@
 	0011  MTi-100 IMU
 	0012  MTi-200 VRU
 	0013  MTi-300 AHRS
-	0017  MTi-G-700 GPS INS
+	0017  MTi-G 7xx GNSS/INS
+	0100  Body Pack
+	0101  Awinda Station
+	0102  Awinda Dongle
+	0103  Sync Station
+	0200  MTw
+	d00d  Wireless Receiver
 2650  Electronics For Imaging, Inc. [hex]
 2659  Sundtek
 	1101  TNT DVB-T/DAB/DAB+/FM
diff --git a/third_party/web-animations-js/LICENSE b/third_party/web-animations-js/LICENSE
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/third_party/web-animations-js/LICENSE
@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/third_party/web-animations-js/OWNERS b/third_party/web-animations-js/OWNERS
new file mode 100644
index 0000000..48e0528a
--- /dev/null
+++ b/third_party/web-animations-js/OWNERS
@@ -0,0 +1 @@
+dzhioev@chromium.org
\ No newline at end of file
diff --git a/third_party/web-animations-js/README.chromium b/third_party/web-animations-js/README.chromium
new file mode 100644
index 0000000..b9fdafa
--- /dev/null
+++ b/third_party/web-animations-js/README.chromium
@@ -0,0 +1,26 @@
+Name: Web Animations JS
+Short Name: web-animations-js
+URL: https://github.com/web-animations/web-animations-js
+Version: 1.0.5
+License: Apache 2.0
+License File: LICENSE
+Security Critical: no
+
+Description:
+An emulator of the Web Animations specification. Web Animations is a new
+specification for animated content on the web. It's being developed as a W3C
+specification as part of the CSS and SVG working groups. Latest specification at
+http://w3c.github.io/web-animations/.
+
+Used as a dependency of Polymer framework (see src/third_party/polymer). If
+using directly, rather than through Polymer, use only the minified source.
+Otherwise there may be CSP issues with inline scripts, etc. This should only
+be updated via third_party/polymer/reproduce.sh after bumping the version number
+in third_party/polymer/bower.json.
+
+Local Modifications:
+- The test and node_modules directories were removed. COPYING was renamed to LICENSE.
+- Delete .travis-setup.sh because it is missing a license header. The header
+  has been added upstream in
+  https://github.com/web-animations/web-animations-next/pull/289. Once that is
+  released, .travis-setup.sh can be added back in.
diff --git a/third_party/web-animations-js/sources/.bower.json b/third_party/web-animations-js/sources/.bower.json
new file mode 100644
index 0000000..82a9aeac
--- /dev/null
+++ b/third_party/web-animations-js/sources/.bower.json
@@ -0,0 +1,14 @@
+{
+  "name": "web-animations-js",
+  "homepage": "https://github.com/web-animations/web-animations-js",
+  "version": "1.0.5",
+  "_release": "1.0.5",
+  "_resolution": {
+    "type": "version",
+    "tag": "1.0.5",
+    "commit": "5e9572828eaae1357e4e43485b7cab1feef17710"
+  },
+  "_source": "git://github.com/web-animations/web-animations-js.git",
+  "_target": "1.0.5",
+  "_originalSource": "web-animations/web-animations-js"
+}
\ No newline at end of file
diff --git a/third_party/web-animations-js/sources/.travis.yml b/third_party/web-animations-js/sources/.travis.yml
new file mode 100644
index 0000000..83979d83
--- /dev/null
+++ b/third_party/web-animations-js/sources/.travis.yml
@@ -0,0 +1,11 @@
+language: node_js
+
+node_js:
+ - "0.10"
+
+install:
+  - BROWSER="Firefox-aurora" ./.travis-setup.sh
+
+before_script:
+  - export DISPLAY=:99.0
+  - sh -e /etc/init.d/xvfb start
diff --git a/third_party/web-animations-js/sources/COPYING b/third_party/web-animations-js/sources/COPYING
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/third_party/web-animations-js/sources/COPYING
@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/third_party/web-animations-js/sources/Gruntfile.js b/third_party/web-animations-js/sources/Gruntfile.js
new file mode 100644
index 0000000..5fad5583
--- /dev/null
+++ b/third_party/web-animations-js/sources/Gruntfile.js
@@ -0,0 +1,279 @@
+module.exports = function(grunt) {
+  grunt.loadNpmTasks('grunt-contrib-uglify');
+  grunt.loadNpmTasks('grunt-gjslint');
+  grunt.loadNpmTasks('grunt-checkrepo');
+  grunt.loadNpmTasks('grunt-karma');
+  grunt.loadNpmTasks('grunt-saucelabs');
+  grunt.loadNpmTasks('grunt-git-status');
+  grunt.loadNpmTasks('grunt-template');
+
+  var targetConfig = require('./target-config.js');
+
+  var sourceMap = require('source-map');
+
+  var config = {
+    uglify: {},
+    template: {},
+    wrap: {},
+    sourceMapConcat: {},
+  };
+
+  function concat(sources, target, defines) {
+    config.uglify[target] = {
+      options: {
+        sourceMap: true,
+        sourceMapName: target + '.map',
+        wrap: false,
+        compress: {
+          global_defs: defines,
+          dead_code: false
+        },
+        mangle: false
+      },
+      nonull: true,
+      dest: target,
+      src: sources
+    };
+    return 'uglify:' + target;
+  }
+
+  function compress(source, target, defines) {
+    var name = concat([source], target, defines);
+    var record = config.uglify[target];
+    record.options.sourceMapIn = source + '.map';
+    record.options.banner = grunt.file.read('templates/boilerplate');
+    record.options.wrap = true;
+    record.options.compress.dead_code = true;
+    record.options.mangle = { eval: true };
+    return name;
+  }
+
+  function genTarget(target) {
+    var config = targetConfig[target];
+    var newGens = [
+      generateFromTemplate('templates/web-animations.js', {target: target}, target + '.dev.js'),
+      generateFromTemplate('templates/web-animations.html', {src: config.src}, target + '.dev.html'),
+      generateFromTemplate('templates/runner.html', {target: target}, 'test/runner-' + target + '.html')];
+    return newGens;
+  }
+
+  function generateFromTemplate(source, data, target) {
+    var targetSpec = {};
+    targetSpec[target] = [source];
+    config.template[target] = {
+      options: {
+        data: data
+      },
+      files: targetSpec
+    }
+    return 'template:' + target;
+  }
+
+  function guard(source, target) {
+    config.wrap[target] = {
+      source: source,
+      preamble: '(function() {\n' +
+                '  if (document.documentElement.animate) {\n' +
+                '    var player = document.documentElement.animate([], 0);\n' +
+                '    var load = true;\n' +
+                '    if (player) {\n' +
+                '      load = false;\n' +
+                '      "play|currentTime|pause|reverse|playbackRate|cancel|finish|startTime|playState".split("|").forEach(function(t) {\n' +
+                '        if (player[t] === undefined) {\n' +
+                '          load = true;\n' +
+                '        }\n' +
+                '      });\n' +
+                '    }\n' +
+                '    if (!load) { return; }' +
+                '  }\n',
+      postamble: '})();'
+    };
+    return 'wrap:' + target;
+  }
+
+  function concatWithMaps(sources, target) {
+    config.sourceMapConcat[target] = {
+      sources: sources
+    }
+    return 'sourceMapConcat:' + target;
+  };
+
+  var concatDefines = {
+    WEB_ANIMATIONS_TESTING: false
+  };
+
+  function buildWebAnimations1(target) {
+    var config = targetConfig[target];
+    return genTarget(target).concat([
+      concat(config.scopeSrc.concat(config.sharedSrc).concat(config.webAnimations1Src), 'inter-raw-' + target + '.js', concatDefines),
+      guard('inter-raw-' + target + '.js', 'inter-' + target + '.js'),
+      compress('inter-' + target + '.js', target + '.min.js', concatDefines)
+    ]);
+  }
+
+  function buildWebAnimationsNext(target) {
+    var config = targetConfig[target];
+    return genTarget(target).concat([
+      concat(config.scopeSrc.concat(config.sharedSrc), 'inter-' + target + '-preamble.js', concatDefines),
+      concat(config.webAnimations1Src, 'inter-component-' + target + 'web-animations-1.js', concatDefines),
+      guard('inter-component-' + target + 'web-animations-1.js', 'inter-guarded-' + target + '-web-animations-1.js'),
+      concat(config.webAnimationsNextSrc, 'inter-component-' + target + '.js', concatDefines),
+      concatWithMaps(['inter-' + target + '-preamble.js', 'inter-guarded-' + target + '-web-animations-1.js', 'inter-component-' + target + '.js'],
+          'inter-' + target + '.js'),
+      compress('inter-' + target + '.js', target + '.min.js', concatDefines)
+    ]);
+  }
+
+  grunt.registerTask('web-animations', buildWebAnimations1('web-animations'));
+  grunt.registerTask('web-animations-next', buildWebAnimationsNext('web-animations-next'));
+  grunt.registerTask('web-animations-next-lite', buildWebAnimationsNext('web-animations-next-lite'));
+
+  var testTargets = {'web-animations': {}, 'web-animations-next': {}};
+
+  grunt.initConfig({
+    uglify: config.uglify,
+    template: config.template,
+    wrap: config.wrap,
+    sourceMapConcat: config.sourceMapConcat,
+    checkrepo: {
+      all: {
+        clean: true,
+      },
+    },
+    'git-status': {
+      all: {
+      },
+    },
+    gjslint: {
+      options: {
+        flags: [
+          '--nojsdoc',
+          '--strict',
+          '--disable 7,121,110', //   7: Wrong blank line count
+                                 // 121: Illegal comma at end of object literal
+                                 // 110: Line too long
+        ],
+        reporter: {
+          name: 'console'
+        }
+      },
+      all: {
+        src: [
+          'src/*.js',
+          'test/*.js',
+          'test/js/*.js',
+        ],
+      }
+    },
+    test: testTargets,
+    sauce: testTargets,
+  });
+
+
+  grunt.task.registerMultiTask('test', 'Run <target> tests under Karma', function() {
+    var done = this.async();
+    var karmaConfig = require('karma/lib/config').parseConfig(require('path').resolve('test/karma-config.js'), {});
+    var config = targetConfig[this.target];
+    karmaConfig.files = ['test/runner.js'].concat(config.src, config.test);
+    var karmaServer = require('karma').server;
+    karmaServer.start(karmaConfig, function(exitCode) {
+      done(exitCode === 0);
+    });
+  });
+
+  grunt.task.registerMultiTask('sauce', 'Run <target> tests under Karma on Saucelabs', function() {
+    var done = this.async();
+    var karmaConfig = require('karma/lib/config').parseConfig(require('path').resolve('test/karma-config-ci.js'), {});
+    var config = targetConfig[this.target];
+    karmaConfig.files = ['test/runner.js'].concat(config.src, config.test);
+    karmaConfig.sauceLabs.testName = 'web-animation-next ' + this.target + ' Unit tests';
+    var karmaServer = require('karma').server;
+    karmaServer.start(karmaConfig, function(exitCode) {
+      done(exitCode === 0);
+    });
+  });
+
+  grunt.task.registerMultiTask('sourceMapConcat', 'concat source files and produce combined source map',
+    function() {
+      var sources = this.data.sources.map(grunt.file.read);
+      var sourceMaps = this.data.sources.map(function(f) { return grunt.file.read(f + '.map'); });
+      var out = "";
+      var outMapGenerator = new sourceMap.SourceMapGenerator({file: this.target});
+      var lineDelta = 0;
+      for (var i = 0; i < sources.length; i++) {
+        out += sources[i];
+        new sourceMap.SourceMapConsumer(sourceMaps[i]).eachMapping(function(mapping) {
+          outMapGenerator.addMapping({
+            generated: {line: mapping.generatedLine + lineDelta, column: mapping.generatedColumn},
+            original: {line: mapping.originalLine, column: mapping.originalColumn},
+            source: mapping.source, name: mapping.name});
+        });
+        var sourceLines = sources[i].split('\n');
+        lineDelta += sourceLines.length;
+        if (sources[i][sources[i].length - 1] !== '\n') {
+          out += '\n';
+        }
+      }
+      grunt.file.write(this.target, out);
+      grunt.file.write(this.target + '.map', outMapGenerator.toString());
+    });
+
+  grunt.task.registerMultiTask('wrap', 'Wrap <target> source file and update source map',
+    function() {
+      var inFile = grunt.file.read(this.data.source);
+      var inMap = grunt.file.read(this.data.source + '.map');
+      var inLines = inFile.split('\n');
+      var i = 0;
+
+      // Discover copyright header
+      while (inLines[i].length < 2 || inLines[i].substring(0, 2) == '//') {
+        i++;
+      }
+
+      // Fix mapping footer
+      var postamble = this.data.postamble;
+      if (inLines[inLines.length - 1].substring(0, 21) == '//# sourceMappingURL=') {
+        postamble += '\n//# sourceMappingURL=' + this.target + '.map';
+      }
+
+      if (i > 0) {
+        var banner = inLines.slice(0, i).join('\n') + '\n';
+      } else {
+        var banner = '';
+      }
+
+      var source = inLines.slice(i, inLines.length - 1).join('\n');
+
+      grunt.file.write(this.target, banner + this.data.preamble + source + postamble);
+      var preLines = this.data.preamble.split('\n');
+      var lineDelta = preLines.length;
+      if (this.data.preamble[this.data.preamble.length - 1] == '\n') {
+        var charDelta = 0;
+      } else {
+        var charDelta = preLines[lineDelta - 1].length;
+        lineDelta -= 1;
+      }
+      var inMapConsumer = new sourceMap.SourceMapConsumer(inMap);
+      var outMapGenerator = new sourceMap.SourceMapGenerator({file: this.target});
+      inMapConsumer.eachMapping(function(mapping) {
+        if (mapping.generatedLine == i + 1) {
+          mapping.generatedColumn += charDelta;
+        }
+        mapping.generatedLine += lineDelta;
+        outMapGenerator.addMapping(
+          {generated: {line: mapping.generatedLine, column: mapping.generatedColumn},
+          original: {line: mapping.originalLine, column: mapping.originalColumn},
+          source: mapping.source, name: mapping.name});
+      });
+      grunt.file.write(this.target + '.map', outMapGenerator.toString());
+    });
+
+  grunt.task.registerTask('clean', 'Remove files generated by grunt', function() {
+    grunt.file.expand('web-animations*').concat(grunt.file.expand('test/runner-*.html')).concat(grunt.file.expand('inter-*')).forEach(function(file) {
+      grunt.file.delete(file);
+      grunt.log.writeln('File ' + file + ' removed');
+    });
+  });
+
+  grunt.task.registerTask('default', ['web-animations', 'web-animations-next', 'web-animations-next-lite', 'gjslint']);
+};
diff --git a/third_party/web-animations-js/sources/History.md b/third_party/web-animations-js/sources/History.md
new file mode 100644
index 0000000..1cac716
--- /dev/null
+++ b/third_party/web-animations-js/sources/History.md
@@ -0,0 +1,82 @@
+### 1.0.5 - *January 6 2015*
+
+  * Fix loading the polyfill in an SVG document
+  * Fix a problem where groups didn't take effect in their first frame
+  * Don't rely on performance.now
+
+### 1.0.4 - *December 8 2014*
+
+  * Fix a critical bug where deprecation logic wasn't being loaded
+    when `web-animations-next` and `web-animations-next-lite` were
+    executed on top of a native `element.animate`.
+
+### 1.0.3 - *December 4 2014*
+
+  * Fix a critical bug on iOS 7 and Safari <= 6. Due to limitations,
+    inline style patching is not supported on these platforms.
+
+### 1.0.2 - *November 28 2014*
+
+  * Deprecated `AnimationTiming.playbackRate`.
+
+    For example, this is no longer supported:
+
+        var player = element.animate(
+            keyframes,
+            {duration: 1000, playbackRate: 2});
+
+    Use `AnimationPlayer.playbackRate` instead:
+
+        var player = element.animate(
+            keyframes,
+            {duration: 1000});
+        player.playbackRate = 2;
+
+    If you have any feedback on this change, please start a discussion
+    on the public-fx mailing list:
+    http://lists.w3.org/Archives/Public/public-fx/
+
+    Or file an issue against the specification on GitHub:
+    https://github.com/w3c/web-animations/issues/new
+
+### 1.0.1 - *November 26 2014*
+
+  * Players should be constructed in idle state
+  * `play()` and `reverse()` should not force a start times
+  * Add `requestAnimationFrame` ids and `cancelAnimationFrame`
+
+### 1.0.0 — *November 21 2014*
+
+  The web-animations-js hackers are pleased to announce the release of
+  a new codebase for the Web Animations Polyfill:
+  https://github.com/web-animations/web-animations-js
+
+  The previous polyfill has been moved to:
+  https://github.com/web-animations/web-animations-js-legacy
+
+  The new codebase is focused on code-size -- our smallest target is
+  now only 33kb or 11kb after gzip.
+
+  We've implemented native fallback. If the target browser provides
+  Web Animations features natively, the Polyfill will use them.
+
+  We now provide three different build targets:
+
+  `web-animations.min.js` - Tracks the Web Animations features that
+  are supported natively in browsers. Today that means Element.animate
+  and Playback Control in Chrome. If you’re not sure what features you
+  will need, start with this.
+
+  `web-animations-next.min.js` - All of web-animations.min.js plus
+  features that are still undergoing discussion or have yet to be
+  implemented natively.
+
+  `web-animations-next-lite.min.js` - A cut down version of
+  web-animations-next, removes several lesser used property handlers
+  and some of the larger and less used features such as matrix
+  interpolation/decomposition.
+
+  Not all features of the previous polyfill have been ported to the
+  new codebase; most notably mutation of Animations and Groups and
+  Additive Animations are not yet supported. These features are still
+  important and will be implemented in the coming weeks.
diff --git a/third_party/web-animations-js/sources/README.md b/third_party/web-animations-js/sources/README.md
new file mode 100644
index 0000000..74e4520
--- /dev/null
+++ b/third_party/web-animations-js/sources/README.md
@@ -0,0 +1,161 @@
+
+Quick Start
+-----------
+
+To provide native Chrome Web Animation features (`Element.animate` and Playback
+Control) in other browsers, use `web-animations.min.js`. To explore all of the
+proposed Web Animations API, use `web-animations-next.min.js`.
+
+What is Web Animations?
+-----------------------
+
+Web Animations is a new JavaScript API for driving animated content on the web.
+By unifying the animation features of SVG and CSS, Web Animations unlocks
+features previously only usable declaratively, and exposes powerful,
+high-performance animation capabilities to developers.
+
+For more details see the
+[W3C specification](http://w3c.github.io/web-animations/).
+
+What is the polyfill?
+---------------------
+
+The polyfill is a JavaScript implementation of the Web Animations API. It works
+on modern versions of all major browsers. For more details about browser
+support see <https://www.polymer-project.org/resources/compatibility.html>.
+
+Getting Started
+---------------
+
+Here's a simple example of an animation that scales and changes the opacity of
+a `<div>` over 0.5 seconds. The animation alternates producing a pulsing
+effect.
+
+    <script src="web-animations.min.js"></script>
+    <div class="pulse" style="width:150px;">Hello world!</div>
+    <script>
+        var elem = document.querySelector('.pulse');
+        var player = elem.animate([
+            {opacity: 0.5, transform: "scale(0.5)"},
+            {opacity: 1.0, transform: "scale(1)"}
+        ], {
+            direction: 'alternate',
+            duration: 500,
+            iterations: Infinity
+        });
+    </script>
+
+Web Animations supports off-main-thread animations, and also allows procedural
+generation of animations and fine-grained control of animation playback. See
+<http://web-animations.github.io> for ideas and inspiration!
+
+Native Fallback
+---------------
+
+When the polyfill runs on a browser that implements Element.animate and
+AnimationPlayer Playback Control it will detect and use the underlying native
+features.
+
+Different Build Targets
+-----------------------
+
+### web-animations.min.js
+
+Tracks the Web Animations features that are supported natively in browsers.
+Today that means Element.animate and Playback Control in Chrome. If you’re not
+sure what features you will need, start with this.
+
+### web-animations-next.min.js
+
+Contains all of web-animations.min.js plus features that are still undergoing
+discussion or have yet to be implemented natively.
+
+### web-animations-next-lite.min.js
+
+A cut down version of web-animations-next, it removes several lesser used
+property handlers and some of the larger and less used features such as matrix
+interpolation/decomposition.
+
+### Build Target Comparison
+
+|                        | web-animations | web-animations-next | web-animations-next-lite |
+|------------------------|:--------------:|:-------------------:|:------------------------:|
+|Size (gzipped)          | 12.5kb         | 14kb                | 10.5kb                   |
+|Element.animate         | ✔             | ✔                  | ✔                       |
+|Timing input (easings, duration, fillMode, etc.) for animations| ✔ | ✔ | ✔             | 
+|Playback control        | ✔             | ✔                  | ✔                       |
+|Support for animating lengths, transforms and opacity| ✔ | ✔ | ✔                       |
+|Support for Animating other CSS properties| ✔ | ✔            | 🚫                       |
+|Matrix fallback for transform animations | ✔ | ✔             | 🚫                       |
+|Animation constructor   | 🚫             | ✔                  | ✔                       |
+|Simple Groups           | 🚫             | ✔                  | ✔                       |
+|Custom Effects          | 🚫             | ✔                  | ✔                       |
+|Timing input (easings, duration, fillMode, etc.) for groups</div>| 🚫 | 🚫\* | 🚫         |
+|Additive animation      | 🚫             | 🚫\*                | 🚫                       |
+|Motion path             | 🚫\*           | 🚫\*                | 🚫                       |
+|Modifiable animation timing| 🚫          | 🚫\*                | 🚫\*                     |
+|Modifiable group timing | 🚫             | 🚫\*                | 🚫\*                     |
+|Usable inline style\*\* | ✔             | ✔                  | 🚫                       |
+
+\* support is planned for these features.
+\*\* see inline style caveat below.
+
+Caveats
+-------
+
+Some things won’t ever be faithful to the native implementation due to browser
+and CSS API limitations. These include:
+
+### Inline Style
+
+Inline style modification is the mechanism used by the polyfill to animate
+properties. Both web-animations and web-animations-next incorporate a module
+that emulates a vanilla inline style object, so that style modification from
+JavaScript can still work in the presence of animations. However, to keep the
+size of web-animations-next-lite as small as possible, the style emulation
+module is not included. When using this version of the polyfill, JavaScript
+inline style modification will be overwritten by animations.
+Due to browser constraints inline style modification is not supported on iOS 7
+or Safari 6 (or earlier versions).
+
+### Prefix handling
+
+The polyfill will automatically detect the correctly prefixed name to use when
+writing animated properties back to the platform. Where possible, the polyfill
+will only accept unprefixed versions of experimental features. For example:
+
+    var animation = new Animation(elem, {"transform": "translate(100px, 100px)"}, 2000);
+
+will work in all browsers that implement a conforming version of transform, but
+
+    var animation = new Animation(elem, {"-webkit-transform": "translate(100px, 100px)"}, 2000);
+
+will not work anywhere.
+
+API and Specification Feedback
+------------------------------
+
+File an issue on GitHub: <https://github.com/w3c/web-animations/issues/new>.
+Alternatively, send an email to <public-fx@w3.org> with subject line
+“[web-animations] … message topic …”
+([archives](http://lists.w3.org/Archives/Public/public-fx/)).
+
+Polyfill Issues
+---------------
+
+Report any issues with this implementation on GitHub:
+<https://github.com/web-animations/web-animations-next/issues/new>.
+
+Breaking changes
+----------------
+
+When we make a potentially breaking change to the polyfill's API
+surface (like a rename) where possible we will continue supporting the
+old version, deprecated, for three months, and ensure that there are
+console warnings to indicate that a change is pending. After three
+months, the old version of the API surface (e.g. the old version of a
+function name) will be removed. *If you see deprecation warnings you
+can't avoid it by not updating*.
+
+We also announce anything that isn't a bug fix on
+[web-animations-changes@googlegroups.com](https://groups.google.com/forum/#!forum/web-animations-changes).
diff --git a/third_party/web-animations-js/sources/package.json b/third_party/web-animations-js/sources/package.json
new file mode 100644
index 0000000..b579b79
--- /dev/null
+++ b/third_party/web-animations-js/sources/package.json
@@ -0,0 +1,33 @@
+{
+  "name": "web-animations-js",
+  "private": true,
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/web-animations/web-animations-js.git"
+  },
+  "devDependencies": {
+    "mocha": "1.21.4",
+    "chai": "^1.9.1",
+    "grunt": "~0.4.5",
+    "grunt-contrib-uglify": "^0.4.0",
+    "grunt-gjslint": "^0.1.4",
+    "grunt-karma": "^0.8.2",
+    "karma": "^0.12.14",
+    "karma-mocha": "^0.1.3",
+    "karma-chai": "^0.1.0",
+    "karma-chrome-launcher": "~0.1.4",
+    "karma-firefox-launcher": "~0.1.3",
+    "karma-ie-launcher": "~0.1.5",
+    "karma-safari-launcher": "~0.1.1",
+    "karma-sauce-launcher": "~0.2.3",
+    "grunt-checkrepo": "~0.1.0",
+    "grunt-saucelabs": "~4.0.2",
+    "grunt-checkrepo": "~0.1.0",
+    "grunt-git-status": "~1.0.0",
+    "grunt-template": "~0.2.3",
+    "source-map": "~0.1.40"
+  },
+  "scripts": {
+    "test": "grunt test gjslint git-status checkrepo"
+  }
+}
diff --git a/third_party/web-animations-js/sources/src/animation-constructor.js b/third_party/web-animations-js/sources/src/animation-constructor.js
new file mode 100644
index 0000000..b493307
--- /dev/null
+++ b/third_party/web-animations-js/sources/src/animation-constructor.js
@@ -0,0 +1,144 @@
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+//     You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//     See the License for the specific language governing permissions and
+// limitations under the License.
+
+(function(shared, scope, testing) {
+
+  function groupChildDuration(node) {
+    return node._timing.delay + node.activeDuration + node._timing.endDelay;
+  };
+
+  function KeyframeEffect(effect) {
+    this._frames = shared.normalizeKeyframes(effect);
+  }
+
+  KeyframeEffect.prototype = {
+    getFrames: function() { return this._frames; }
+  };
+
+  scope.Animation = function(target, effect, timingInput) {
+    this.target = target;
+
+    // TODO: Store a clone, not the same instance.
+    this._timingInput = timingInput;
+    this._timing = shared.normalizeTimingInput(timingInput);
+
+    // TODO: Make modifications to timing update the underlying player
+    this.timing = shared.makeTiming(timingInput);
+    // TODO: Make this a live object - will need to separate normalization of
+    // keyframes into a shared module.
+    if (typeof effect == 'function')
+      this.effect = effect;
+    else
+      this.effect = new KeyframeEffect(effect);
+    this._effect = effect;
+    this.activeDuration = shared.calculateActiveDuration(this._timing);
+    return this;
+  };
+
+  var originalElementAnimate = Element.prototype.animate;
+  Element.prototype.animate = function(effect, timing) {
+    return scope.timeline.play(new scope.Animation(this, effect, timing));
+  };
+
+  var nullTarget = document.createElementNS('http://www.w3.org/1999/xhtml', 'div');
+  scope.newUnderlyingPlayerForAnimation = function(animation) {
+    var target = animation.target || nullTarget;
+    var effect = animation._effect;
+    if (typeof effect == 'function') {
+      effect = [];
+    }
+    return originalElementAnimate.apply(target, [effect, animation._timingInput]);
+  };
+
+  scope.bindPlayerForAnimation = function(player) {
+    if (player.source && typeof player.source.effect == 'function') {
+      scope.bindPlayerForCustomEffect(player);
+    }
+  };
+
+  var pendingGroups = [];
+  scope.awaitStartTime = function(groupPlayer) {
+    if (groupPlayer.startTime !== null || !groupPlayer._isGroup)
+      return;
+    if (pendingGroups.length == 0) {
+      requestAnimationFrame(updatePendingGroups);
+    }
+    pendingGroups.push(groupPlayer);
+  };
+  function updatePendingGroups() {
+    var updated = false;
+    while (pendingGroups.length) {
+      pendingGroups.shift()._updateChildren();
+      updated = true;
+    }
+    return updated;
+  }
+  var originalGetComputedStyle = window.getComputedStyle;
+  Object.defineProperty(window, 'getComputedStyle', {
+    configurable: true,
+    enumerable: true,
+    value: function() {
+      var result = originalGetComputedStyle.apply(this, arguments);
+      if (updatePendingGroups())
+        result = originalGetComputedStyle.apply(this, arguments);
+      return result;
+    },
+  });
+
+  // TODO: Call into this less frequently.
+  scope.Player.prototype._updateChildren = function() {
+    if (this.paused || !this.source || !this._isGroup)
+      return;
+    var offset = this.source._timing.delay;
+    for (var i = 0; i < this.source.children.length; i++) {
+      var child = this.source.children[i];
+      var childPlayer;
+
+      if (i >= this._childPlayers.length) {
+        childPlayer = window.document.timeline.play(child);
+        this._childPlayers.push(childPlayer);
+      } else {
+        childPlayer = this._childPlayers[i];
+      }
+      child.player = this.source.player;
+
+      if (childPlayer.startTime != this.startTime + offset) {
+        if (this.startTime === null) {
+          childPlayer.currentTime = this.source.player.currentTime - offset;
+          childPlayer._startTime = null;
+        } else {
+          childPlayer.startTime = this.startTime + offset;
+        }
+        childPlayer._updateChildren();
+      }
+
+      if (this.playbackRate == -1 && this.currentTime < offset && childPlayer.currentTime !== -1) {
+        childPlayer.currentTime = -1;
+      }
+
+      if (this.source instanceof window.AnimationSequence)
+        offset += groupChildDuration(child);
+    }
+  };
+
+  window.Animation = scope.Animation;
+  window.Element.prototype.getAnimationPlayers = function() {
+    return document.timeline.getAnimationPlayers().filter(function(player) {
+      return player.source !== null && player.source.target == this;
+    }.bind(this));
+  };
+
+  scope.groupChildDuration = groupChildDuration;
+
+}(webAnimationsShared, webAnimationsNext, webAnimationsTesting));
diff --git a/third_party/web-animations-js/sources/src/animation-node.js b/third_party/web-animations-js/sources/src/animation-node.js
new file mode 100644
index 0000000..390402c
--- /dev/null
+++ b/third_party/web-animations-js/sources/src/animation-node.js
@@ -0,0 +1,31 @@
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+//     You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//     See the License for the specific language governing permissions and
+// limitations under the License.
+
+(function(shared, scope) {
+
+  scope.AnimationNode = function(timing) {
+    var timeFraction = 0;
+    var activeDuration = shared.calculateActiveDuration(timing);
+    var animationNode = function(localTime) {
+      return shared.calculateTimeFraction(activeDuration, localTime, timing);
+    };
+    animationNode._totalDuration = timing.delay + activeDuration + timing.endDelay;
+    animationNode._isCurrent = function(localTime) {
+      var phase = shared.calculatePhase(activeDuration, localTime, timing);
+      return phase === PhaseActive || phase === PhaseBefore;
+    };
+    return animationNode;
+  };
+
+})(webAnimationsShared, webAnimations1);
diff --git a/third_party/web-animations-js/sources/src/animation.js b/third_party/web-animations-js/sources/src/animation.js
new file mode 100644
index 0000000..233217d
--- /dev/null
+++ b/third_party/web-animations-js/sources/src/animation.js
@@ -0,0 +1,65 @@
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+//     You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//     See the License for the specific language governing permissions and
+// limitations under the License.
+
+(function(shared, scope, testing) {
+
+  scope.Animation = function(target, effectInput, timingInput) {
+    var animationNode = scope.AnimationNode(shared.normalizeTimingInput(timingInput));
+    var effect = scope.convertEffectInput(effectInput);
+    var timeFraction;
+    var animation = function() {
+      WEB_ANIMATIONS_TESTING && console.assert(typeof timeFraction !== 'undefined');
+      effect(target, timeFraction);
+    };
+    // Returns whether the animation is in effect or not after the timing update.
+    animation._update = function(localTime) {
+      timeFraction = animationNode(localTime);
+      return timeFraction !== null;
+    };
+    animation._clear = function() {
+      effect(target, null);
+    };
+    animation._hasSameTarget = function(otherTarget) {
+      return target === otherTarget;
+    };
+    animation._isCurrent = animationNode._isCurrent;
+    animation._totalDuration = animationNode._totalDuration;
+    return animation;
+  };
+
+  scope.NullAnimation = function(clear) {
+    var nullAnimation = function() {
+      if (clear) {
+        clear();
+        clear = null;
+      }
+    };
+    nullAnimation._update = function() {
+      return null;
+    };
+    nullAnimation._totalDuration = 0;
+    nullAnimation._isCurrent = function() {
+      return false;
+    };
+    nullAnimation._hasSameTarget = function() {
+      return false;
+    };
+    return nullAnimation;
+  };
+
+  if (WEB_ANIMATIONS_TESTING) {
+    testing.webAnimations1Animation = scope.Animation;
+  }
+
+})(webAnimationsShared, webAnimations1, webAnimationsTesting);
diff --git a/third_party/web-animations-js/sources/src/apply-preserving-inline-style.js b/third_party/web-animations-js/sources/src/apply-preserving-inline-style.js
new file mode 100644
index 0000000..795be36
--- /dev/null
+++ b/third_party/web-animations-js/sources/src/apply-preserving-inline-style.js
@@ -0,0 +1,191 @@
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+//     You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//     See the License for the specific language governing permissions and
+// limitations under the License.
+
+(function(scope, testing) {
+
+  var styleAttributes = {
+    cssText: 1,
+    length: 1,
+    parentRule: 1,
+  };
+
+  var styleMethods = {
+    getPropertyCSSValue: 1,
+    getPropertyPriority: 1,
+    getPropertyValue: 1,
+    item: 1,
+    removeProperty: 1,
+    setProperty: 1,
+  };
+
+  var styleMutatingMethods = {
+    removeProperty: 1,
+    setProperty: 1,
+  };
+
+  function configureProperty(object, property, descriptor) {
+    descriptor.enumerable = true;
+    descriptor.configurable = true;
+    Object.defineProperty(object, property, descriptor);
+  }
+
+  function AnimatedCSSStyleDeclaration(element) {
+    WEB_ANIMATIONS_TESTING && console.assert(!(element.style instanceof AnimatedCSSStyleDeclaration),
+        'Element must not already have an animated style attached.');
+
+    // Stores the inline style of the element on its behalf while the
+    // polyfill uses the element's inline style to simulate web animations.
+    // This is needed to fake regular inline style CSSOM access on the element.
+    this._surrogateStyle = document.createElementNS('http://www.w3.org/1999/xhtml', 'div').style;
+    this._style = element.style;
+    this._length = 0;
+    this._isAnimatedProperty = {};
+
+    // Copy the inline style contents over to the surrogate.
+    for (var i = 0; i < this._style.length; i++) {
+      var property = this._style[i];
+      this._surrogateStyle[property] = this._style[property];
+    }
+    this._updateIndices();
+  }
+
+  AnimatedCSSStyleDeclaration.prototype = {
+    get cssText() {
+      return this._surrogateStyle.cssText;
+    },
+    set cssText(text) {
+      var isAffectedProperty = {};
+      for (var i = 0; i < this._surrogateStyle.length; i++) {
+        isAffectedProperty[this._surrogateStyle[i]] = true;
+      }
+      this._surrogateStyle.cssText = text;
+      this._updateIndices();
+      for (var i = 0; i < this._surrogateStyle.length; i++) {
+        isAffectedProperty[this._surrogateStyle[i]] = true;
+      }
+      for (var property in isAffectedProperty) {
+        if (!this._isAnimatedProperty[property]) {
+          this._style.setProperty(property, this._surrogateStyle.getPropertyValue(property));
+        }
+      }
+    },
+    get length() {
+      return this._surrogateStyle.length;
+    },
+    get parentRule() {
+      return this._style.parentRule;
+    },
+    // Mirror the indexed getters and setters of the surrogate style.
+    _updateIndices: function() {
+      while (this._length < this._surrogateStyle.length) {
+        Object.defineProperty(this, this._length, {
+          configurable: true,
+          enumerable: false,
+          get: (function(index) {
+            return function() { return this._surrogateStyle[index]; };
+          })(this._length)
+        });
+        this._length++;
+      }
+      while (this._length > this._surrogateStyle.length) {
+        this._length--;
+        Object.defineProperty(this, this._length, {
+          configurable: true,
+          enumerable: false,
+          value: undefined
+        });
+      }
+    },
+    _set: function(property, value) {
+      this._style[property] = value;
+      this._isAnimatedProperty[property] = true;
+    },
+    _clear: function(property) {
+      this._style[property] = this._surrogateStyle[property];
+      delete this._isAnimatedProperty[property];
+    },
+  };
+
+  // Wrap the style methods.
+  for (var method in styleMethods) {
+    AnimatedCSSStyleDeclaration.prototype[method] = (function(method, modifiesStyle) {
+      return function() {
+        var result = this._surrogateStyle[method].apply(this._surrogateStyle, arguments);
+        if (modifiesStyle) {
+          if (!this._isAnimatedProperty[arguments[0]])
+            this._style[method].apply(this._style, arguments);
+          this._updateIndices();
+        }
+        return result;
+      }
+    })(method, method in styleMutatingMethods);
+  }
+
+  // Wrap the style.cssProperty getters and setters.
+  for (var property in document.documentElement.style) {
+    if (property in styleAttributes || property in styleMethods) {
+      continue;
+    }
+    (function(property) {
+      configureProperty(AnimatedCSSStyleDeclaration.prototype, property, {
+        get: function() {
+          return this._surrogateStyle[property];
+        },
+        set: function(value) {
+          this._surrogateStyle[property] = value;
+          this._updateIndices();
+          if (!this._isAnimatedProperty[property])
+            this._style[property] = value;
+        }
+      });
+    })(property);
+  }
+
+  function ensureStyleIsPatched(element) {
+    if (element._webAnimationsPatchedStyle)
+      return;
+
+    var animatedStyle = new AnimatedCSSStyleDeclaration(element);
+    try {
+      configureProperty(element, 'style', { get: function() { return animatedStyle; } });
+    } catch (_) {
+      // iOS and older versions of Safari (pre v7) do not support overriding an element's
+      // style object. Animations will clobber any inline styles as a result.
+      element.style._set = function(property, value) {
+        element.style[property] = value;
+      };
+      element.style._clear = function(property) {
+        element.style[property] = '';
+      };
+    }
+
+    // We must keep a handle on the patched style to prevent it from getting GC'd.
+    element._webAnimationsPatchedStyle = element.style;
+  }
+
+  scope.apply = function(element, property, value) {
+    ensureStyleIsPatched(element);
+    element.style._set(scope.propertyName(property), value);
+  };
+
+  scope.clear = function(element, property) {
+    if (element._webAnimationsPatchedStyle) {
+      element.style._clear(scope.propertyName(property));
+    }
+  };
+
+  if (WEB_ANIMATIONS_TESTING)
+    testing.ensureStyleIsPatched = ensureStyleIsPatched;
+
+})(webAnimations1, webAnimationsTesting);
diff --git a/third_party/web-animations-js/sources/src/apply.js b/third_party/web-animations-js/sources/src/apply.js
new file mode 100644
index 0000000..3200f967
--- /dev/null
+++ b/third_party/web-animations-js/sources/src/apply.js
@@ -0,0 +1,25 @@
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+//     You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//     See the License for the specific language governing permissions and
+// limitations under the License.
+
+(function(scope, testing) {
+
+  scope.apply = function(element, property, value) {
+    element.style[scope.propertyName(property)] = value;
+  };
+
+  scope.clear = function(element, property) {
+    element.style[scope.propertyName(property)] = '';
+  };
+
+})(webAnimations1, webAnimationsTesting);
diff --git a/third_party/web-animations-js/sources/src/box-handler.js b/third_party/web-animations-js/sources/src/box-handler.js
new file mode 100644
index 0000000..3399263
--- /dev/null
+++ b/third_party/web-animations-js/sources/src/box-handler.js
@@ -0,0 +1,57 @@
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+//   You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//   See the License for the specific language governing permissions and
+// limitations under the License.
+
+(function(scope, testing) {
+  function consumeLengthPercentOrAuto(string) {
+    return scope.consumeLengthOrPercent(string) || scope.consumeToken(/^auto/, string);
+  }
+  function parseBox(string) {
+    var result = scope.consumeList([
+      scope.ignore(scope.consumeToken.bind(null, /^rect/)),
+      scope.ignore(scope.consumeToken.bind(null, /^\(/)),
+      scope.consumeRepeated.bind(null, consumeLengthPercentOrAuto, /^,/),
+      scope.ignore(scope.consumeToken.bind(null, /^\)/)),
+    ], string);
+    if (result && result[0].length == 4) {
+      return result[0];
+    }
+  }
+
+  function mergeComponent(left, right) {
+    if (left == 'auto' || right == 'auto') {
+      return [true, false, function(t) {
+        var result = t ? left : right;
+        if (result == 'auto') {
+          return 'auto';
+        }
+        // FIXME: There's probably a better way to turn a dimension back into a string.
+        var merged = scope.mergeDimensions(result, result);
+        return merged[2](merged[0]);
+      }];
+    }
+    return scope.mergeDimensions(left, right);
+  }
+
+  function wrap(result) {
+    return 'rect(' + result + ')';
+  }
+
+  var mergeBoxes = scope.mergeWrappedNestedRepeated.bind(null, wrap, mergeComponent, ', ');
+
+  scope.parseBox = parseBox;
+  scope.mergeBoxes = mergeBoxes;
+
+  scope.addPropertiesHandler(parseBox, mergeBoxes, ['clip']);
+
+})(webAnimations1, webAnimationsTesting);
diff --git a/third_party/web-animations-js/sources/src/color-handler.js b/third_party/web-animations-js/sources/src/color-handler.js
new file mode 100644
index 0000000..b32a889
--- /dev/null
+++ b/third_party/web-animations-js/sources/src/color-handler.js
@@ -0,0 +1,62 @@
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+//   You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//   See the License for the specific language governing permissions and
+// limitations under the License.
+
+(function(scope, testing) {
+
+  var canvas = document.createElementNS('http://www.w3.org/1999/xhtml', 'canvas');
+  canvas.width = canvas.height = 1;
+  var context = canvas.getContext('2d');
+
+  function parseColor(string) {
+    string = string.trim();
+    // The context ignores invalid colors
+    context.fillStyle = '#000';
+    context.fillStyle = string;
+    var contextSerializedFillStyle = context.fillStyle;
+    context.fillStyle = '#fff';
+    context.fillStyle = string;
+    if (contextSerializedFillStyle != context.fillStyle)
+      return;
+    context.fillRect(0, 0, 1, 1);
+    var pixelColor = context.getImageData(0, 0, 1, 1).data;
+    context.clearRect(0, 0, 1, 1);
+    var alpha = pixelColor[3] / 255;
+    return [pixelColor[0] * alpha, pixelColor[1] * alpha, pixelColor[2] * alpha, alpha];
+  }
+
+  function mergeColors(left, right) {
+    return [left, right, function(x) {
+      function clamp(v) {
+        return Math.max(0, Math.min(255, v));
+      }
+      if (x[3]) {
+        for (var i = 0; i < 3; i++)
+          x[i] = Math.round(clamp(x[i] / x[3]));
+      }
+      x[3] = scope.numberToString(scope.clamp(0, 1, x[3]));
+      return 'rgba(' + x.join(',') + ')';
+    }];
+  }
+
+  scope.addPropertiesHandler(parseColor, mergeColors,
+      ['background-color', 'border-bottom-color', 'border-left-color', 'border-right-color',
+       'border-top-color', 'color', 'outline-color', 'text-decoration-color']);
+  scope.consumeColor = scope.consumeParenthesised.bind(null, parseColor);
+  scope.mergeColors = mergeColors;
+
+  if (WEB_ANIMATIONS_TESTING) {
+    testing.parseColor = parseColor;
+  }
+
+})(webAnimations1, webAnimationsTesting);
diff --git a/third_party/web-animations-js/sources/src/deprecation.js b/third_party/web-animations-js/sources/src/deprecation.js
new file mode 100644
index 0000000..78e954b
--- /dev/null
+++ b/third_party/web-animations-js/sources/src/deprecation.js
@@ -0,0 +1,42 @@
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+//     You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//     See the License for the specific language governing permissions and
+// limitations under the License.
+
+(function(shared) {
+
+  var silenced = {};
+
+  shared.isDeprecated = function(feature, date, advice, plural) {
+    var auxVerb = plural ? 'are' : 'is';
+    var today = new Date();
+    var expiry = new Date(date);
+    expiry.setMonth(expiry.getMonth() + 3); // 3 months grace period
+
+    if (today < expiry) {
+      if (!(feature in silenced)) {
+        console.warn('Web Animations: ' + feature + ' ' + auxVerb + ' deprecated and will stop working on ' + expiry.toDateString() + '. ' + advice);
+      }
+      silenced[feature] = true;
+      return false;
+    } else {
+      return true;
+    }
+  };
+
+  shared.deprecated = function(feature, date, advice, plural) {
+    if (shared.isDeprecated(feature, date, advice, plural)) {
+      throw new Error(feature + ' ' + auxVerb + ' no longer supported. ' + advice);
+    }
+  };
+
+})(webAnimationsShared);
diff --git a/third_party/web-animations-js/sources/src/dev.js b/third_party/web-animations-js/sources/src/dev.js
new file mode 100644
index 0000000..a5e225c
--- /dev/null
+++ b/third_party/web-animations-js/sources/src/dev.js
@@ -0,0 +1,16 @@
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+//     You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//     See the License for the specific language governing permissions and
+// limitations under the License.
+
+var WEB_ANIMATIONS_TESTING = false;
+var webAnimationsTesting = null;
diff --git a/third_party/web-animations-js/sources/src/dimension-handler.js b/third_party/web-animations-js/sources/src/dimension-handler.js
new file mode 100644
index 0000000..66afe0d
--- /dev/null
+++ b/third_party/web-animations-js/sources/src/dimension-handler.js
@@ -0,0 +1,167 @@
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+//   You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//   See the License for the specific language governing permissions and
+// limitations under the License.
+
+(function(scope, testing) {
+
+  function parseDimension(unitRegExp, string) {
+    string = string.trim().toLowerCase();
+
+    if (string == '0' && 'px'.search(unitRegExp) >= 0)
+      return {px: 0};
+
+    // If we have parenthesis, we're a calc and need to start with 'calc'.
+    if (!/^[^(]*$|^calc/.test(string))
+      return;
+    string = string.replace(/calc\(/g, '(');
+
+    // We tag units by prefixing them with 'U' (note that we are already
+    // lowercase) to prevent problems with types which are substrings of
+    // each other (although prefixes may be problematic!)
+    var matchedUnits = {};
+    string = string.replace(unitRegExp, function(match) {
+      matchedUnits[match] = null;
+      return 'U' + match;
+    });
+    var taggedUnitRegExp = 'U(' + unitRegExp.source + ')';
+
+    // Validating input is simply applying as many reductions as we can.
+    var typeCheck = string.replace(/[-+]?(\d*\.)?\d+/g, 'N')
+                          .replace(new RegExp('N' + taggedUnitRegExp, 'g'), 'D')
+                          .replace(/\s[+-]\s/g, 'O')
+                          .replace(/\s/g, '');
+    var reductions = [/N\*(D)/g, /(N|D)[*/]N/g, /(N|D)O\1/g, /\((N|D)\)/g];
+    var i = 0;
+    while (i < reductions.length) {
+      if (reductions[i].test(typeCheck)) {
+        typeCheck = typeCheck.replace(reductions[i], '$1');
+        i = 0;
+      } else {
+        i++;
+      }
+    }
+    if (typeCheck != 'D')
+      return;
+
+    for (var unit in matchedUnits) {
+      var result = eval(string.replace(new RegExp('U' + unit, 'g'), '').replace(new RegExp(taggedUnitRegExp, 'g'), '*0'));
+      if (!isFinite(result))
+        return;
+      matchedUnits[unit] = result;
+    }
+    return matchedUnits;
+  }
+
+  function mergeDimensionsNonNegative(left, right) {
+    return mergeDimensions(left, right, true);
+  }
+
+  function mergeDimensions(left, right, nonNegative) {
+    var units = [], unit;
+    for (unit in left)
+      units.push(unit);
+    for (unit in right) {
+      if (units.indexOf(unit) < 0)
+        units.push(unit);
+    }
+
+    left = units.map(function(unit) { return left[unit] || 0; });
+    right = units.map(function(unit) { return right[unit] || 0; });
+    return [left, right, function(values) {
+      var result = values.map(function(value, i) {
+        if (values.length == 1 && nonNegative) {
+          value = Math.max(value, 0);
+        }
+        // Scientific notation (e.g. 1e2) is not yet widely supported by browser vendors.
+        return scope.numberToString(value) + units[i];
+      }).join(' + ');
+      return values.length > 1 ? 'calc(' + result + ')' : result;
+    }];
+  }
+
+  var lengthUnits = 'px|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc';
+  var parseLength = parseDimension.bind(null, new RegExp(lengthUnits, 'g'));
+  var parseLengthOrPercent = parseDimension.bind(null, new RegExp(lengthUnits + '|%', 'g'));
+  var parseAngle = parseDimension.bind(null, /deg|rad|grad|turn/g);
+
+  scope.parseLength = parseLength;
+  scope.parseLengthOrPercent = parseLengthOrPercent;
+  scope.consumeLengthOrPercent = scope.consumeParenthesised.bind(null, parseLengthOrPercent);
+  scope.parseAngle = parseAngle;
+  scope.mergeDimensions = mergeDimensions;
+
+  var consumeLength = scope.consumeParenthesised.bind(null, parseLength);
+  var consumeSizePair = scope.consumeRepeated.bind(undefined, consumeLength, /^/);
+  var consumeSizePairList = scope.consumeRepeated.bind(undefined, consumeSizePair, /^,/);
+  scope.consumeSizePairList = consumeSizePairList;
+
+  var parseSizePairList = function(input) {
+    var result = consumeSizePairList(input);
+    if (result && result[1] == '') {
+      return result[0];
+    }
+  };
+
+  var mergeNonNegativeSizePair = scope.mergeNestedRepeated.bind(undefined, mergeDimensionsNonNegative, ' ');
+  var mergeNonNegativeSizePairList = scope.mergeNestedRepeated.bind(undefined, mergeNonNegativeSizePair, ',');
+  scope.mergeNonNegativeSizePair = mergeNonNegativeSizePair;
+
+  scope.addPropertiesHandler(parseSizePairList, mergeNonNegativeSizePairList, [
+    'background-size'
+  ]);
+
+  scope.addPropertiesHandler(parseLengthOrPercent, mergeDimensionsNonNegative, [
+    'border-bottom-width',
+    'border-image-width',
+    'border-left-width',
+    'border-right-width',
+    'border-top-width',
+    'flex-basis',
+    'font-size',
+    'height',
+    'line-height',
+    'max-height',
+    'max-width',
+    'outline-width',
+    'width',
+  ]);
+
+  scope.addPropertiesHandler(parseLengthOrPercent, mergeDimensions, [
+    'border-bottom-left-radius',
+    'border-bottom-right-radius',
+    'border-top-left-radius',
+    'border-top-right-radius',
+    'bottom',
+    'left',
+    'letter-spacing',
+    'margin-bottom',
+    'margin-left',
+    'margin-right',
+    'margin-top',
+    'min-height',
+    'min-width',
+    'outline-offset',
+    'padding-bottom',
+    'padding-left',
+    'padding-right',
+    'padding-top',
+    'perspective',
+    'right',
+    'shape-margin',
+    'text-indent',
+    'top',
+    'vertical-align',
+    'word-spacing',
+  ]);
+
+})(webAnimations1, webAnimationsTesting);
diff --git a/third_party/web-animations-js/sources/src/effect-callback.js b/third_party/web-animations-js/sources/src/effect-callback.js
new file mode 100644
index 0000000..6ccd5d9e
--- /dev/null
+++ b/third_party/web-animations-js/sources/src/effect-callback.js
@@ -0,0 +1,86 @@
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+//     You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//     See the License for the specific language governing permissions and
+// limitations under the License.
+(function(shared, scope, testing) {
+
+  var nullTarget = document.createElementNS('http://www.w3.org/1999/xhtml', 'div');
+
+  var sequenceNumber = 0;
+  scope.bindPlayerForCustomEffect = function(player) {
+    var target = player.source.target;
+    var effect = player.source.effect;
+    var timing = player.source.timing;
+    var last = undefined;
+    timing = shared.normalizeTimingInput(timing);
+    var callback = function() {
+      var t = callback._player ? callback._player.currentTime : null;
+      if (t !== null) {
+        t = shared.calculateTimeFraction(shared.calculateActiveDuration(timing), t, timing);
+        if (isNaN(t))
+          t = null;
+      }
+      // FIXME: There are actually more conditions under which the effect
+      // should be called.
+      if (t !== last)
+        effect(t, target, player.source);
+      last = t;
+    };
+
+    callback._player = player;
+    callback._registered = false;
+    callback._sequenceNumber = sequenceNumber++;
+    player._callback = callback;
+    register(callback);
+  };
+
+  var callbacks = [];
+  var ticking = false;
+  function register(callback) {
+    if (callback._registered)
+      return;
+    callback._registered = true;
+    callbacks.push(callback);
+    if (!ticking) {
+      ticking = true;
+      requestAnimationFrame(tick);
+    }
+  }
+
+  function tick(t) {
+    var updating = callbacks;
+    callbacks = [];
+    updating.sort(function(left, right) {
+      return left._sequenceNumber - right._sequenceNumber;
+    });
+    updating.filter(function(callback) {
+      callback();
+      if (!callback._player || callback._player.finished || callback._player.paused)
+        callback._registered = false;
+      return callback._registered;
+    });
+    callbacks.push.apply(callbacks, updating);
+
+    if (callbacks.length) {
+      ticking = true;
+      requestAnimationFrame(tick);
+    } else {
+      ticking = false;
+    }
+  }
+
+  scope.Player.prototype._register = function() {
+    if (this._callback)
+      register(this._callback);
+  };
+
+})(webAnimationsShared, webAnimationsNext, webAnimationsTesting);
diff --git a/third_party/web-animations-js/sources/src/effect.js b/third_party/web-animations-js/sources/src/effect.js
new file mode 100644
index 0000000..c198917c
--- /dev/null
+++ b/third_party/web-animations-js/sources/src/effect.js
@@ -0,0 +1,110 @@
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+//     You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//     See the License for the specific language governing permissions and
+// limitations under the License.
+
+(function(shared, scope, testing) {
+
+  scope.convertEffectInput = function(effectInput) {
+    var keyframeEffect = shared.normalizeKeyframes(effectInput);
+    var propertySpecificKeyframeGroups = makePropertySpecificKeyframeGroups(keyframeEffect);
+    var interpolations = makeInterpolations(propertySpecificKeyframeGroups);
+    return function(target, fraction) {
+      if (fraction != null) {
+        interpolations.filter(function(interpolation) {
+          return (fraction <= 0 && interpolation.startTime == 0) ||
+                 (fraction >= 1 && interpolation.endTime == 1) ||
+                 (fraction >= interpolation.startTime && fraction <= interpolation.endTime);
+        }).forEach(function(interpolation) {
+          var offsetFraction = fraction - interpolation.startTime;
+          var localDuration = interpolation.endTime - interpolation.startTime;
+          var scaledLocalTime = localDuration == 0 ? 0 : interpolation.easing(offsetFraction / localDuration);
+          scope.apply(target, interpolation.property, interpolation.interpolation(scaledLocalTime));
+        });
+      } else {
+        for (var property in propertySpecificKeyframeGroups)
+          if (property != 'offset' && property != 'easing' && property != 'composite')
+            scope.clear(target, property);
+      }
+    };
+  };
+
+
+  function makePropertySpecificKeyframeGroups(keyframeEffect) {
+    var propertySpecificKeyframeGroups = {};
+
+    for (var i = 0; i < keyframeEffect.length; i++) {
+      for (var member in keyframeEffect[i]) {
+        if (member != 'offset' && member != 'easing' && member != 'composite') {
+          var propertySpecificKeyframe = {
+            offset: keyframeEffect[i].offset,
+            easing: keyframeEffect[i].easing,
+            value: keyframeEffect[i][member]
+          };
+          propertySpecificKeyframeGroups[member] = propertySpecificKeyframeGroups[member] || [];
+          propertySpecificKeyframeGroups[member].push(propertySpecificKeyframe);
+        }
+      }
+    }
+
+    for (var groupName in propertySpecificKeyframeGroups) {
+      var group = propertySpecificKeyframeGroups[groupName];
+      if (group[0].offset != 0 || group[group.length - 1].offset != 1) {
+        throw {
+          type: DOMException.NOT_SUPPORTED_ERR,
+          name: 'NotSupportedError',
+          message: 'Partial keyframes are not supported'
+        };
+      }
+    }
+    return propertySpecificKeyframeGroups;
+  }
+
+
+  function makeInterpolations(propertySpecificKeyframeGroups) {
+    var interpolations = [];
+    for (var groupName in propertySpecificKeyframeGroups) {
+      var group = propertySpecificKeyframeGroups[groupName];
+      for (var i = 0; i < group.length - 1; i++) {
+        var startTime = group[i].offset;
+        var endTime = group[i + 1].offset;
+        var startValue = group[i].value;
+        var endValue = group[i + 1].value;
+        if (startTime == endTime) {
+          if (endTime == 1) {
+            startValue = endValue;
+          } else {
+            endValue = startValue;
+          }
+        }
+        interpolations.push({
+          startTime: startTime,
+          endTime: endTime,
+          easing: group[i].easing,
+          property: groupName,
+          interpolation: scope.propertyInterpolation(groupName, startValue, endValue)
+        });
+      }
+    }
+    interpolations.sort(function(leftInterpolation, rightInterpolation) {
+      return leftInterpolation.startTime - rightInterpolation.startTime;
+    });
+    return interpolations;
+  }
+
+
+  if (WEB_ANIMATIONS_TESTING) {
+    testing.makePropertySpecificKeyframeGroups = makePropertySpecificKeyframeGroups;
+    testing.makeInterpolations = makeInterpolations;
+  }
+
+})(webAnimationsShared, webAnimations1, webAnimationsTesting);
diff --git a/third_party/web-animations-js/sources/src/element-animatable.js b/third_party/web-animations-js/sources/src/element-animatable.js
new file mode 100644
index 0000000..694389a
--- /dev/null
+++ b/third_party/web-animations-js/sources/src/element-animatable.js
@@ -0,0 +1,19 @@
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+//     You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//     See the License for the specific language governing permissions and
+// limitations under the License.
+
+(function(scope) {
+  window.Element.prototype.animate = function(effectInput, timingInput) {
+    return scope.timeline._play(scope.Animation(this, effectInput, timingInput));
+  };
+})(webAnimations1);
diff --git a/third_party/web-animations-js/sources/src/font-weight-handler.js b/third_party/web-animations-js/sources/src/font-weight-handler.js
new file mode 100644
index 0000000..4760486a6
--- /dev/null
+++ b/third_party/web-animations-js/sources/src/font-weight-handler.js
@@ -0,0 +1,42 @@
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+//   You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//   See the License for the specific language governing permissions and
+// limitations under the License.
+
+(function(scope) {
+  function parse(string) {
+    var out = Number(string);
+    if (isNaN(out) || out < 100 || out > 900 || out % 100 !== 0) {
+      return;
+    }
+    return out;
+  }
+
+  function toCss(value) {
+    value = Math.round(value / 100) * 100;
+    value = scope.clamp(100, 900, value);
+    if (value === 400) {
+      return 'normal';
+    }
+    if (value === 700) {
+      return 'bold';
+    }
+    return String(value);
+  }
+
+  function merge(left, right) {
+    return [left, right, toCss];
+  }
+
+  scope.addPropertiesHandler(parse, merge, ['font-weight']);
+
+})(webAnimations1);
diff --git a/third_party/web-animations-js/sources/src/group-constructors.js b/third_party/web-animations-js/sources/src/group-constructors.js
new file mode 100644
index 0000000..fddf3df
--- /dev/null
+++ b/third_party/web-animations-js/sources/src/group-constructors.js
@@ -0,0 +1,82 @@
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+//     You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//     See the License for the specific language governing permissions and
+// limitations under the License.
+
+(function(shared, scope, testing) {
+
+  function constructor(children, timingInput) {
+    this.children = children || [];
+    this._timing = shared.normalizeTimingInput(timingInput, true);
+    this.timing = shared.makeTiming(timingInput, true);
+
+    if (this._timing.duration === 'auto')
+      this._timing.duration = this.activeDuration;
+  }
+
+  window.AnimationSequence = function() {
+    constructor.apply(this, arguments);
+  };
+
+  window.AnimationGroup = function() {
+    constructor.apply(this, arguments);
+  };
+
+  window.AnimationSequence.prototype = {
+    get activeDuration() {
+      var total = 0;
+      this.children.forEach(function(child) {
+        total += scope.groupChildDuration(child);
+      });
+      return Math.max(total, 0);
+    }
+  };
+
+  window.AnimationGroup.prototype = {
+    get activeDuration() {
+      var max = 0;
+      this.children.forEach(function(child) {
+        max = Math.max(max, scope.groupChildDuration(child));
+      });
+      return max;
+    }
+  };
+
+  scope.newUnderlyingPlayerForGroup = function(group) {
+    var underlyingPlayer;
+    var ticker = function(tf) {
+      var player = underlyingPlayer._wrapper;
+      if (!player.source)
+        return;
+      if (tf == null) {
+        player._removePlayers();
+        return;
+      }
+      if (player.startTime === null)
+        return;
+
+      player._updateChildren();
+    };
+
+    underlyingPlayer = scope.timeline.play(new scope.Animation(null, ticker, group._timing));
+    return underlyingPlayer;
+  };
+
+  scope.bindPlayerForGroup = function(player) {
+    player._player._wrapper = player;
+    player._isGroup = true;
+    scope.awaitStartTime(player);
+    player._updateChildren();
+  };
+
+
+})(webAnimationsShared, webAnimationsNext, webAnimationsTesting);
diff --git a/third_party/web-animations-js/sources/src/handler-utils.js b/third_party/web-animations-js/sources/src/handler-utils.js
new file mode 100644
index 0000000..d9f05e1
--- /dev/null
+++ b/third_party/web-animations-js/sources/src/handler-utils.js
@@ -0,0 +1,177 @@
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+//   You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//   See the License for the specific language governing permissions and
+// limitations under the License.
+
+(function(scope) {
+
+  // consume* functions return a 2 value array of [parsed-data, '' or not-yet consumed input]
+
+  // Regex should be anchored with /^
+  function consumeToken(regex, string) {
+    var result = regex.exec(string);
+    if (result) {
+      result = regex.ignoreCase ? result[0].toLowerCase() : result[0];
+      return [result, string.substr(result.length)];
+    }
+  }
+
+  function consumeTrimmed(consumer, string) {
+    string = string.replace(/^\s*/, '');
+    var result = consumer(string);
+    if (result) {
+      return [result[0], result[1].replace(/^\s*/, '')];
+    }
+  }
+
+  function consumeRepeated(consumer, separator, string) {
+    consumer = consumeTrimmed.bind(null, consumer);
+    var list = [];
+    while (true) {
+      var result = consumer(string);
+      if (!result) {
+        return [list, string];
+      }
+      list.push(result[0]);
+      string = result[1];
+      result = consumeToken(separator, string);
+      if (!result || result[1] == '') {
+        return [list, string];
+      }
+      string = result[1];
+    }
+  }
+
+  // Consumes a token or expression with balanced parentheses
+  function consumeParenthesised(parser, string) {
+    var nesting = 0;
+    for (var n = 0; n < string.length; n++) {
+      if (/\s|,/.test(string[n]) && nesting == 0) {
+        break;
+      } else if (string[n] == '(') {
+        nesting++;
+      } else if (string[n] == ')') {
+        nesting--;
+        if (nesting == 0)
+          n++;
+        if (nesting <= 0)
+          break;
+      }
+    }
+    var parsed = parser(string.substr(0, n));
+    return parsed == undefined ? undefined : [parsed, string.substr(n)];
+  }
+
+  function lcm(a, b) {
+    var c = a;
+    var d = b;
+    while (c && d)
+      c > d ? c %= d : d %= c;
+    c = (a * b) / (c + d);
+    return c;
+  }
+
+  function ignore(value) {
+    return function(input) {
+      var result = value(input);
+      if (result)
+        result[0] = undefined;
+      return result;
+    }
+  }
+
+  function optional(value, defaultValue) {
+    return function(input) {
+      var result = value(input);
+      if (result)
+        return result;
+      return [defaultValue, input];
+    }
+  }
+
+  function consumeList(list, input) {
+    var output = [];
+    for (var i = 0; i < list.length; i++) {
+      var result = scope.consumeTrimmed(list[i], input);
+      if (!result || result[0] == '')
+        return;
+      if (result[0] !== undefined)
+        output.push(result[0]);
+      input = result[1];
+    }
+    if (input == '') {
+      return output;
+    }
+  }
+
+  function mergeWrappedNestedRepeated(wrap, nestedMerge, separator, left, right) {
+    var matchingLeft = [];
+    var matchingRight = [];
+    var reconsititution = [];
+    var length = lcm(left.length, right.length);
+    for (var i = 0; i < length; i++) {
+      var thing = nestedMerge(left[i % left.length], right[i % right.length]);
+      if (!thing) {
+        return;
+      }
+      matchingLeft.push(thing[0]);
+      matchingRight.push(thing[1]);
+      reconsititution.push(thing[2]);
+    }
+    return [matchingLeft, matchingRight, function(positions) {
+      var result = positions.map(function(position, i) {
+        return reconsititution[i](position);
+      }).join(separator);
+      return wrap ? wrap(result) : result;
+    }];
+  }
+
+  function mergeList(left, right, list) {
+    var lefts = [];
+    var rights = [];
+    var functions = [];
+    var j = 0;
+    for (var i = 0; i < list.length; i++) {
+      if (typeof list[i] == 'function') {
+        var result = list[i](left[j], right[j++]);
+        lefts.push(result[0]);
+        rights.push(result[1]);
+        functions.push(result[2]);
+      } else {
+        (function(pos) {
+          lefts.push(false);
+          rights.push(false);
+          functions.push(function() { return list[pos]; });
+        })(i);
+      }
+    }
+    return [lefts, rights, function(results) {
+      var result = '';
+      for (var i = 0; i < results.length; i++) {
+        result += functions[i](results[i]);
+      }
+      return result;
+    }];
+  }
+
+  scope.consumeToken = consumeToken;
+  scope.consumeTrimmed = consumeTrimmed;
+  scope.consumeRepeated = consumeRepeated;
+  scope.consumeParenthesised = consumeParenthesised;
+  scope.ignore = ignore;
+  scope.optional = optional;
+  scope.consumeList = consumeList;
+  scope.mergeNestedRepeated = mergeWrappedNestedRepeated.bind(null, null);
+  scope.mergeWrappedNestedRepeated = mergeWrappedNestedRepeated;
+  scope.mergeList = mergeList;
+
+})(webAnimations1);
diff --git a/third_party/web-animations-js/sources/src/interpolation.js b/third_party/web-animations-js/sources/src/interpolation.js
new file mode 100644
index 0000000..ba63ed35
--- /dev/null
+++ b/third_party/web-animations-js/sources/src/interpolation.js
@@ -0,0 +1,49 @@
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+//     You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//     See the License for the specific language governing permissions and
+// limitations under the License.
+
+(function(scope, testing) {
+
+  function interpolate(from, to, f) {
+    if ((typeof from == 'number') && (typeof to == 'number')) {
+      return from * (1 - f) + to * f;
+    }
+    if ((typeof from == 'boolean') && (typeof to == 'boolean')) {
+      return f < 0.5 ? from : to;
+    }
+
+    WEB_ANIMATIONS_TESTING && console.assert(
+        Array.isArray(from) && Array.isArray(to),
+        'If interpolation arguments are not numbers or bools they must be arrays');
+
+    if (from.length == to.length) {
+      var r = [];
+      for (var i = 0; i < from.length; i++) {
+        r.push(interpolate(from[i], to[i], f));
+      }
+      return r;
+    }
+    throw 'Mismatched interpolation arguments ' + from + ':' + to;
+  }
+
+  scope.Interpolation = function(from, to, convertToString) {
+    return function(f) {
+      return convertToString(interpolate(from, to, f));
+    }
+  };
+
+  if (WEB_ANIMATIONS_TESTING) {
+    testing.interpolate = interpolate;
+  }
+
+})(webAnimations1, webAnimationsTesting);
diff --git a/third_party/web-animations-js/sources/src/matrix-decomposition.js b/third_party/web-animations-js/sources/src/matrix-decomposition.js
new file mode 100644
index 0000000..c825372e
--- /dev/null
+++ b/third_party/web-animations-js/sources/src/matrix-decomposition.js
@@ -0,0 +1,452 @@
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+//   You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//   See the License for the specific language governing permissions and
+// limitations under the License.
+
+(function(scope, testing) {
+  var decomposeMatrix = (function() {
+    function determinant(m) {
+      return m[0][0] * m[1][1] * m[2][2] +
+             m[1][0] * m[2][1] * m[0][2] +
+             m[2][0] * m[0][1] * m[1][2] -
+             m[0][2] * m[1][1] * m[2][0] -
+             m[1][2] * m[2][1] * m[0][0] -
+             m[2][2] * m[0][1] * m[1][0];
+    }
+
+    // from Wikipedia:
+    //
+    // [A B]^-1 = [A^-1 + A^-1B(D - CA^-1B)^-1CA^-1     -A^-1B(D - CA^-1B)^-1]
+    // [C D]      [-(D - CA^-1B)^-1CA^-1                (D - CA^-1B)^-1      ]
+    //
+    // Therefore
+    //
+    // [A [0]]^-1 = [A^-1       [0]]
+    // [C  1 ]      [ -CA^-1     1 ]
+    function inverse(m) {
+      var iDet = 1 / determinant(m);
+      var a = m[0][0], b = m[0][1], c = m[0][2];
+      var d = m[1][0], e = m[1][1], f = m[1][2];
+      var g = m[2][0], h = m[2][1], k = m[2][2];
+      var Ainv = [
+        [(e * k - f * h) * iDet, (c * h - b * k) * iDet,
+         (b * f - c * e) * iDet, 0],
+        [(f * g - d * k) * iDet, (a * k - c * g) * iDet,
+         (c * d - a * f) * iDet, 0],
+        [(d * h - e * g) * iDet, (g * b - a * h) * iDet,
+         (a * e - b * d) * iDet, 0]
+      ];
+      var lastRow = [];
+      for (var i = 0; i < 3; i++) {
+        var val = 0;
+        for (var j = 0; j < 3; j++) {
+          val += m[3][j] * Ainv[j][i];
+        }
+        lastRow.push(val);
+      }
+      lastRow.push(1);
+      Ainv.push(lastRow);
+      return Ainv;
+    }
+
+    function transposeMatrix4(m) {
+      return [[m[0][0], m[1][0], m[2][0], m[3][0]],
+              [m[0][1], m[1][1], m[2][1], m[3][1]],
+              [m[0][2], m[1][2], m[2][2], m[3][2]],
+              [m[0][3], m[1][3], m[2][3], m[3][3]]];
+    }
+
+    function multVecMatrix(v, m) {
+      var result = [];
+      for (var i = 0; i < 4; i++) {
+        var val = 0;
+        for (var j = 0; j < 4; j++) {
+          val += v[j] * m[j][i];
+        }
+        result.push(val);
+      }
+      return result;
+    }
+
+    function normalize(v) {
+      var len = length(v);
+      return [v[0] / len, v[1] / len, v[2] / len];
+    }
+
+    function length(v) {
+      return Math.sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
+    }
+
+    function combine(v1, v2, v1s, v2s) {
+      return [v1s * v1[0] + v2s * v2[0], v1s * v1[1] + v2s * v2[1],
+              v1s * v1[2] + v2s * v2[2]];
+    }
+
+    function cross(v1, v2) {
+      return [v1[1] * v2[2] - v1[2] * v2[1],
+              v1[2] * v2[0] - v1[0] * v2[2],
+              v1[0] * v2[1] - v1[1] * v2[0]];
+    }
+
+    // TODO: Implement 2D matrix decomposition.
+    // http://dev.w3.org/csswg/css-transforms/#decomposing-a-2d-matrix
+    function decomposeMatrix(matrix) {
+      var m3d = [
+        matrix.slice(0, 4),
+        matrix.slice(4, 8),
+        matrix.slice(8, 12),
+        matrix.slice(12, 16)
+      ];
+
+      // skip normalization step as m3d[3][3] should always be 1
+      if (m3d[3][3] !== 1) {
+        return null;
+      }
+
+      var perspectiveMatrix = [];
+      for (var i = 0; i < 4; i++) {
+        perspectiveMatrix.push(m3d[i].slice());
+      }
+
+      for (var i = 0; i < 3; i++) {
+        perspectiveMatrix[i][3] = 0;
+      }
+
+      if (determinant(perspectiveMatrix) === 0) {
+        return false;
+      }
+
+      var rhs = [];
+
+      var perspective;
+      if (m3d[0][3] || m3d[1][3] || m3d[2][3]) {
+        rhs.push(m3d[0][3]);
+        rhs.push(m3d[1][3]);
+        rhs.push(m3d[2][3]);
+        rhs.push(m3d[3][3]);
+
+        var inversePerspectiveMatrix = inverse(perspectiveMatrix);
+        var transposedInversePerspectiveMatrix =
+            transposeMatrix4(inversePerspectiveMatrix);
+        perspective = multVecMatrix(rhs, transposedInversePerspectiveMatrix);
+      } else {
+        perspective = [0, 0, 0, 1];
+      }
+
+      var translate = m3d[3].slice(0, 3);
+
+      var row = [];
+      row.push(m3d[0].slice(0, 3));
+      var scale = [];
+      scale.push(length(row[0]));
+      row[0] = normalize(row[0]);
+
+      var skew = [];
+      row.push(m3d[1].slice(0, 3));
+      skew.push(dot(row[0], row[1]));
+      row[1] = combine(row[1], row[0], 1.0, -skew[0]);
+
+      scale.push(length(row[1]));
+      row[1] = normalize(row[1]);
+      skew[0] /= scale[1];
+
+      row.push(m3d[2].slice(0, 3));
+      skew.push(dot(row[0], row[2]));
+      row[2] = combine(row[2], row[0], 1.0, -skew[1]);
+      skew.push(dot(row[1], row[2]));
+      row[2] = combine(row[2], row[1], 1.0, -skew[2]);
+
+      scale.push(length(row[2]));
+      row[2] = normalize(row[2]);
+      skew[1] /= scale[2];
+      skew[2] /= scale[2];
+
+      var pdum3 = cross(row[1], row[2]);
+      if (dot(row[0], pdum3) < 0) {
+        for (var i = 0; i < 3; i++) {
+          scale[i] *= -1;
+          row[i][0] *= -1;
+          row[i][1] *= -1;
+          row[i][2] *= -1;
+        }
+      }
+
+      var t = row[0][0] + row[1][1] + row[2][2] + 1;
+      var s;
+      var quaternion;
+
+      if (t > 1e-4) {
+        s = 0.5 / Math.sqrt(t);
+        quaternion = [
+          (row[2][1] - row[1][2]) * s,
+          (row[0][2] - row[2][0]) * s,
+          (row[1][0] - row[0][1]) * s,
+          0.25 / s
+        ];
+      } else if (row[0][0] > row[1][1] && row[0][0] > row[2][2]) {
+        s = Math.sqrt(1 + row[0][0] - row[1][1] - row[2][2]) * 2.0;
+        quaternion = [
+          0.25 * s,
+          (row[0][1] + row[1][0]) / s,
+          (row[0][2] + row[2][0]) / s,
+          (row[2][1] - row[1][2]) / s
+        ];
+      } else if (row[1][1] > row[2][2]) {
+        s = Math.sqrt(1.0 + row[1][1] - row[0][0] - row[2][2]) * 2.0;
+        quaternion = [
+          (row[0][1] + row[1][0]) / s,
+          0.25 * s,
+          (row[1][2] + row[2][1]) / s,
+          (row[0][2] - row[2][0]) / s
+        ];
+      } else {
+        s = Math.sqrt(1.0 + row[2][2] - row[0][0] - row[1][1]) * 2.0;
+        quaternion = [
+          (row[0][2] + row[2][0]) / s,
+          (row[1][2] + row[2][1]) / s,
+          0.25 * s,
+          (row[1][0] - row[0][1]) / s
+        ];
+      }
+
+      return [translate, scale, skew, quaternion, perspective];
+    }
+    return decomposeMatrix;
+  })();
+
+  function dot(v1, v2) {
+    var result = 0;
+    for (var i = 0; i < v1.length; i++) {
+      result += v1[i] * v2[i];
+    }
+    return result;
+  }
+
+  function multiplyMatrices(a, b) {
+    return [
+      a[0] * b[0] + a[4] * b[1] + a[8] * b[2] + a[12] * b[3],
+      a[1] * b[0] + a[5] * b[1] + a[9] * b[2] + a[13] * b[3],
+      a[2] * b[0] + a[6] * b[1] + a[10] * b[2] + a[14] * b[3],
+      a[3] * b[0] + a[7] * b[1] + a[11] * b[2] + a[15] * b[3],
+
+      a[0] * b[4] + a[4] * b[5] + a[8] * b[6] + a[12] * b[7],
+      a[1] * b[4] + a[5] * b[5] + a[9] * b[6] + a[13] * b[7],
+      a[2] * b[4] + a[6] * b[5] + a[10] * b[6] + a[14] * b[7],
+      a[3] * b[4] + a[7] * b[5] + a[11] * b[6] + a[15] * b[7],
+
+      a[0] * b[8] + a[4] * b[9] + a[8] * b[10] + a[12] * b[11],
+      a[1] * b[8] + a[5] * b[9] + a[9] * b[10] + a[13] * b[11],
+      a[2] * b[8] + a[6] * b[9] + a[10] * b[10] + a[14] * b[11],
+      a[3] * b[8] + a[7] * b[9] + a[11] * b[10] + a[15] * b[11],
+
+      a[0] * b[12] + a[4] * b[13] + a[8] * b[14] + a[12] * b[15],
+      a[1] * b[12] + a[5] * b[13] + a[9] * b[14] + a[13] * b[15],
+      a[2] * b[12] + a[6] * b[13] + a[10] * b[14] + a[14] * b[15],
+      a[3] * b[12] + a[7] * b[13] + a[11] * b[14] + a[15] * b[15]
+    ];
+  }
+
+  // TODO: This can probably be made smaller.
+  function convertItemToMatrix(item) {
+    switch (item.t) {
+      // TODO: Handle units other than rads and degs.
+      case 'rotatex':
+        var rads = item.d[0].rad || 0;
+        var degs = item.d[0].deg || 0;
+        var angle = (degs * Math.PI / 180) + rads;
+        return [1, 0, 0, 0,
+                0, Math.cos(angle), Math.sin(angle), 0,
+                0, -Math.sin(angle), Math.cos(angle), 0,
+                0, 0, 0, 1];
+      case 'rotatey':
+        var rads = item.d[0].rad || 0;
+        var degs = item.d[0].deg || 0;
+        var angle = (degs * Math.PI / 180) + rads;
+        return [Math.cos(angle), 0, -Math.sin(angle), 0,
+                0, 1, 0, 0,
+                Math.sin(angle), 0, Math.cos(angle), 0,
+                0, 0, 0, 1];
+      case 'rotate':
+      case 'rotatez':
+        var rads = item.d[0].rad || 0;
+        var degs = item.d[0].deg || 0;
+        var angle = (degs * Math.PI / 180) + rads;
+        return [Math.cos(angle), Math.sin(angle), 0, 0,
+                -Math.sin(angle), Math.cos(angle), 0, 0,
+                0, 0, 1, 0,
+                0, 0, 0, 1];
+      case 'rotate3d':
+        var x = item.d[0];
+        var y = item.d[1];
+        var z = item.d[2];
+        var rads = item.d[3].rad || 0;
+        var degs = item.d[3].deg || 0;
+        var angle = (degs * Math.PI / 180) + rads;
+
+        var sqrLength = x * x + y * y + z * z;
+        if (sqrLength === 0) {
+          x = 1;
+          y = 0;
+          z = 0;
+        } else if (sqrLength !== 1) {
+          var length = Math.sqrt(sqrLength);
+          x /= length;
+          y /= length;
+          z /= length;
+        }
+
+        var s = Math.sin(angle / 2);
+        var sc = s * Math.cos(angle / 2);
+        var sq = s * s;
+        return [
+          1 - 2 * (y * y + z * z) * sq,
+          2 * (x * y * sq + z * sc),
+          2 * (x * z * sq - y * sc),
+          0,
+
+          2 * (x * y * sq - z * sc),
+          1 - 2 * (x * x + z * z) * sq,
+          2 * (y * z * sq + x * sc),
+          0,
+
+          2 * (x * z * sq + y * sc),
+          2 * (y * z * sq - x * sc),
+          1 - 2 * (x * x + y * y) * sq,
+          0,
+
+          0, 0, 0, 1
+        ];
+      case 'scale':
+        return [item.d[0], 0, 0, 0,
+                0, item.d[1], 0, 0,
+                0, 0, 1, 0,
+                0, 0, 0, 1];
+      case 'scalex':
+        return [item.d[0], 0, 0, 0,
+                0, 1, 0, 0,
+                0, 0, 1, 0,
+                0, 0, 0, 1];
+      case 'scaley':
+        return [1, 0, 0, 0,
+                0, item.d[0], 0, 0,
+                0, 0, 1, 0,
+                0, 0, 0, 1];
+      case 'scalez':
+        return [1, 0, 0, 0,
+                0, 1, 0, 0,
+                0, 0, item.d[0], 0,
+                0, 0, 0, 1];
+      case 'scale3d':
+        return [item.d[0], 0, 0, 0,
+                0, item.d[1], 0, 0,
+                0, 0, item.d[2], 0,
+                0, 0, 0, 1];
+      // FIXME: Skew behaves differently in Blink, FireFox and here. Need to work out why.
+      case 'skew':
+        var xDegs = item.d[0].deg || 0;
+        var xRads = item.d[0].rad || 0;
+        var yDegs = item.d[1].deg || 0;
+        var yRads = item.d[1].rad || 0;
+        var xAngle = (xDegs * Math.PI / 180) + xRads;
+        var yAngle = (yDegs * Math.PI / 180) + yRads;
+        return [1, Math.tan(yAngle), 0, 0,
+                Math.tan(xAngle), 1, 0, 0,
+                0, 0, 1, 0,
+                0, 0, 0, 1];
+      case 'skewx':
+        var rads = item.d[0].rad || 0;
+        var degs = item.d[0].deg || 0;
+        var angle = (degs * Math.PI / 180) + rads;
+        return [1, 0, 0, 0,
+                Math.tan(angle), 1, 0, 0,
+                0, 0, 1, 0,
+                0, 0, 0, 1];
+      case 'skewy':
+        var rads = item.d[0].rad || 0;
+        var degs = item.d[0].deg || 0;
+        var angle = (degs * Math.PI / 180) + rads;
+        return [1, Math.tan(angle), 0, 0,
+                0, 1, 0, 0,
+                0, 0, 1, 0,
+                0, 0, 0, 1];
+      // TODO: Work out what to do with non-px values.
+      case 'translate':
+        var x = item.d[0].px || 0;
+        var y = item.d[1].px || 0;
+        return [1, 0, 0, 0,
+                0, 1, 0, 0,
+                0, 0, 1, 0,
+                x, y, 0, 1];
+      case 'translatex':
+        var x = item.d[0].px || 0;
+        return [1, 0, 0, 0,
+                0, 1, 0, 0,
+                0, 0, 1, 0,
+                x, 0, 0, 1];
+      case 'translatey':
+        var y = item.d[0].px || 0;
+        return [1, 0, 0, 0,
+                0, 1, 0, 0,
+                0, 0, 1, 0,
+                0, y, 0, 1];
+      case 'translatez':
+        var z = item.d[0].px || 0;
+        return [1, 0, 0, 0,
+                0, 1, 0, 0,
+                0, 0, 1, 0,
+                0, 0, z, 1];
+      case 'translate3d':
+        var x = item.d[0].px || 0;
+        var y = item.d[1].px || 0;
+        var z = item.d[2].px || 0;
+        return [1, 0, 0, 0,
+                0, 1, 0, 0,
+                0, 0, 1, 0,
+                x, y, z, 1];
+      case 'perspective':
+        var p = item.d[0].px ? (-1 / item.d[0].px) : 0;
+        return [
+          1, 0, 0, 0,
+          0, 1, 0, 0,
+          0, 0, 1, p,
+          0, 0, 0, 1];
+      case 'matrix':
+        return [item.d[0], item.d[1], 0, 0,
+                item.d[2], item.d[3], 0, 0,
+                0, 0, 1, 0,
+                item.d[4], item.d[5], 0, 1];
+      case 'matrix3d':
+        return item.d;
+      default:
+        WEB_ANIMATIONS_TESTING && console.assert(false, 'Transform item type ' + item.t +
+            ' conversion to matrix not yet implemented.');
+    }
+  }
+
+  function convertToMatrix(transformList) {
+    if (transformList.length === 0) {
+      return [1, 0, 0, 0,
+              0, 1, 0, 0,
+              0, 0, 1, 0,
+              0, 0, 0, 1];
+    }
+    return transformList.map(convertItemToMatrix).reduce(multiplyMatrices);
+  }
+
+  function makeMatrixDecomposition(transformList) {
+    return [decomposeMatrix(convertToMatrix(transformList))];
+  }
+
+  scope.dot = dot;
+  scope.makeMatrixDecomposition = makeMatrixDecomposition;
+
+})(webAnimations1, webAnimationsTesting);
diff --git a/third_party/web-animations-js/sources/src/matrix-interpolation.js b/third_party/web-animations-js/sources/src/matrix-interpolation.js
new file mode 100644
index 0000000..9a35de3
--- /dev/null
+++ b/third_party/web-animations-js/sources/src/matrix-interpolation.js
@@ -0,0 +1,130 @@
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+//   You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//   See the License for the specific language governing permissions and
+// limitations under the License.
+
+(function(scope, testing) {
+  var composeMatrix = (function() {
+    function multiply(a, b) {
+      var result = [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]];
+      for (var i = 0; i < 4; i++) {
+        for (var j = 0; j < 4; j++) {
+          for (var k = 0; k < 4; k++) {
+            result[i][j] += b[i][k] * a[k][j];
+          }
+        }
+      }
+      return result;
+    }
+
+    function is2D(m) {
+      return (
+          m[0][2] == 0 &&
+          m[0][3] == 0 &&
+          m[1][2] == 0 &&
+          m[1][3] == 0 &&
+          m[2][0] == 0 &&
+          m[2][1] == 0 &&
+          m[2][2] == 1 &&
+          m[2][3] == 0 &&
+          m[3][2] == 0 &&
+          m[3][3] == 1);
+    }
+
+    function composeMatrix(translate, scale, skew, quat, perspective) {
+      var matrix = [[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]];
+
+      for (var i = 0; i < 4; i++) {
+        matrix[i][3] = perspective[i];
+      }
+
+      for (var i = 0; i < 3; i++) {
+        for (var j = 0; j < 3; j++) {
+          matrix[3][i] += translate[j] * matrix[j][i];
+        }
+      }
+
+      var x = quat[0], y = quat[1], z = quat[2], w = quat[3];
+
+      var rotMatrix = [[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]];
+
+      rotMatrix[0][0] = 1 - 2 * (y * y + z * z);
+      rotMatrix[0][1] = 2 * (x * y - z * w);
+      rotMatrix[0][2] = 2 * (x * z + y * w);
+      rotMatrix[1][0] = 2 * (x * y + z * w);
+      rotMatrix[1][1] = 1 - 2 * (x * x + z * z);
+      rotMatrix[1][2] = 2 * (y * z - x * w);
+      rotMatrix[2][0] = 2 * (x * z - y * w);
+      rotMatrix[2][1] = 2 * (y * z + x * w);
+      rotMatrix[2][2] = 1 - 2 * (x * x + y * y);
+
+      matrix = multiply(matrix, rotMatrix);
+
+      var temp = [[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]];
+      if (skew[2]) {
+        temp[2][1] = skew[2];
+        matrix = multiply(matrix, temp);
+      }
+
+      if (skew[1]) {
+        temp[2][1] = 0;
+        temp[2][0] = skew[0];
+        matrix = multiply(matrix, temp);
+      }
+
+      if (skew[0]) {
+        temp[2][0] = 0;
+        temp[1][0] = skew[0];
+        matrix = multiply(matrix, temp);
+      }
+
+      for (var i = 0; i < 3; i++) {
+        for (var j = 0; j < 3; j++) {
+          matrix[i][j] *= scale[i];
+        }
+      }
+
+      if (is2D(matrix)) {
+        return [matrix[0][0], matrix[0][1], matrix[1][0], matrix[1][1], matrix[3][0], matrix[3][1]];
+      }
+      return matrix[0].concat(matrix[1], matrix[2], matrix[3]);
+    }
+    return composeMatrix;
+  })();
+
+  function clamp(x, min, max) {
+    return Math.max(Math.min(x, max), min);
+  };
+
+  function quat(fromQ, toQ, f) {
+    var product = scope.dot(fromQ, toQ);
+    product = clamp(product, -1.0, 1.0);
+
+    var quat = [];
+    if (product === 1.0) {
+      quat = fromQ;
+    } else {
+      var theta = Math.acos(product);
+      var w = Math.sin(f * theta) * 1 / Math.sqrt(1 - product * product);
+
+      for (var i = 0; i < 4; i++) {
+        quat.push(fromQ[i] * (Math.cos(f * theta) - product * w) +
+                  toQ[i] * w);
+      }
+    }
+    return quat;
+  }
+
+  scope.composeMatrix = composeMatrix;
+  scope.quat = quat;
+
+})(webAnimations1, webAnimationsTesting);
diff --git a/third_party/web-animations-js/sources/src/normalize-keyframes.js b/third_party/web-animations-js/sources/src/normalize-keyframes.js
new file mode 100644
index 0000000..58b9f041
--- /dev/null
+++ b/third_party/web-animations-js/sources/src/normalize-keyframes.js
@@ -0,0 +1,259 @@
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+//     You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//     See the License for the specific language governing permissions and
+// limitations under the License.
+
+(function(shared, testing) {
+  var shorthandToLonghand = {
+    background: [
+      'backgroundImage',
+      'backgroundPosition',
+      'backgroundSize',
+      'backgroundRepeat',
+      'backgroundAttachment',
+      'backgroundOrigin',
+      'backgroundClip',
+      'backgroundColor'
+    ],
+    border: [
+      'borderTopColor',
+      'borderTopStyle',
+      'borderTopWidth',
+      'borderRightColor',
+      'borderRightStyle',
+      'borderRightWidth',
+      'borderBottomColor',
+      'borderBottomStyle',
+      'borderBottomWidth',
+      'borderLeftColor',
+      'borderLeftStyle',
+      'borderLeftWidth'
+    ],
+    borderBottom: [
+      'borderBottomWidth',
+      'borderBottomStyle',
+      'borderBottomColor'
+    ],
+    borderColor: [
+      'borderTopColor',
+      'borderRightColor',
+      'borderBottomColor',
+      'borderLeftColor'
+    ],
+    borderLeft: [
+      'borderLeftWidth',
+      'borderLeftStyle',
+      'borderLeftColor'
+    ],
+    borderRadius: [
+      'borderTopLeftRadius',
+      'borderTopRightRadius',
+      'borderBottomRightRadius',
+      'borderBottomLeftRadius'
+    ],
+    borderRight: [
+      'borderRightWidth',
+      'borderRightStyle',
+      'borderRightColor'
+    ],
+    borderTop: [
+      'borderTopWidth',
+      'borderTopStyle',
+      'borderTopColor'
+    ],
+    borderWidth: [
+      'borderTopWidth',
+      'borderRightWidth',
+      'borderBottomWidth',
+      'borderLeftWidth'
+    ],
+    flex: [
+      'flexGrow',
+      'flexShrink',
+      'flexBasis'
+    ],
+    font: [
+      'fontFamily',
+      'fontSize',
+      'fontStyle',
+      'fontVariant',
+      'fontWeight',
+      'lineHeight'
+    ],
+    margin: [
+      'marginTop',
+      'marginRight',
+      'marginBottom',
+      'marginLeft'
+    ],
+    outline: [
+      'outlineColor',
+      'outlineStyle',
+      'outlineWidth'
+    ],
+    padding: [
+      'paddingTop',
+      'paddingRight',
+      'paddingBottom',
+      'paddingLeft'
+    ]
+  };
+
+  var shorthandExpanderElem = document.createElementNS('http://www.w3.org/1999/xhtml', 'div');
+
+  var borderWidthAliases = {
+    thin: '1px',
+    medium: '3px',
+    thick: '5px'
+  };
+
+  var aliases = {
+    borderBottomWidth: borderWidthAliases,
+    borderLeftWidth: borderWidthAliases,
+    borderRightWidth: borderWidthAliases,
+    borderTopWidth: borderWidthAliases,
+    fontSize: {
+      'xx-small': '60%',
+      'x-small': '75%',
+      'small': '89%',
+      'medium': '100%',
+      'large': '120%',
+      'x-large': '150%',
+      'xx-large': '200%'
+    },
+    fontWeight: {
+      normal: '400',
+      bold: '700'
+    },
+    outlineWidth: borderWidthAliases,
+    textShadow: {
+      none: '0px 0px 0px transparent'
+    },
+    boxShadow: {
+      none: '0px 0px 0px 0px transparent'
+    }
+  };
+
+  function antiAlias(property, value) {
+    if (property in aliases) {
+      return aliases[property][value] || value;
+    }
+    return value;
+  }
+
+  // This delegates parsing shorthand value syntax to the browser.
+  function expandShorthandAndAntiAlias(property, value, result) {
+    var longProperties = shorthandToLonghand[property];
+    if (longProperties) {
+      shorthandExpanderElem.style[property] = value;
+      for (var i in longProperties) {
+        var longProperty = longProperties[i];
+        var longhandValue = shorthandExpanderElem.style[longProperty];
+        result[longProperty] = antiAlias(longProperty, longhandValue);
+      }
+    } else {
+      result[property] = antiAlias(property, value);
+    }
+  };
+
+  function normalizeKeyframes(effectInput) {
+    if (!Array.isArray(effectInput) && effectInput !== null)
+      throw new TypeError('Keyframe effect must be null or an array of keyframes');
+
+    if (effectInput == null)
+      return [];
+
+    var keyframeEffect = effectInput.map(function(originalKeyframe) {
+      var keyframe = {};
+      for (var member in originalKeyframe) {
+        var memberValue = originalKeyframe[member];
+        if (member == 'offset') {
+          if (memberValue != null) {
+            memberValue = Number(memberValue);
+            if (!isFinite(memberValue))
+              throw new TypeError('keyframe offsets must be numbers.');
+          }
+        } else if (member == 'composite') {
+          throw {
+            type: DOMException.NOT_SUPPORTED_ERR,
+            name: 'NotSupportedError',
+            message: 'add compositing is not supported'
+          };
+        } else if (member == 'easing') {
+          memberValue = shared.toTimingFunction(memberValue);
+        } else {
+          memberValue = '' + memberValue;
+        }
+        expandShorthandAndAntiAlias(member, memberValue, keyframe);
+      }
+      if (keyframe.offset == undefined)
+        keyframe.offset = null;
+      if (keyframe.easing == undefined)
+        keyframe.easing = shared.toTimingFunction('linear');
+      return keyframe;
+    });
+
+    var everyFrameHasOffset = true;
+    var looselySortedByOffset = true;
+    var previousOffset = -Infinity;
+    for (var i = 0; i < keyframeEffect.length; i++) {
+      var offset = keyframeEffect[i].offset;
+      if (offset != null) {
+        if (offset < previousOffset) {
+          throw {
+            code: DOMException.INVALID_MODIFICATION_ERR,
+            name: 'InvalidModificationError',
+            message: 'Keyframes are not loosely sorted by offset. Sort or specify offsets.'
+          };
+        }
+        previousOffset = offset;
+      } else {
+        everyFrameHasOffset = false;
+      }
+    }
+
+    keyframeEffect = keyframeEffect.filter(function(keyframe) {
+      return keyframe.offset >= 0 && keyframe.offset <= 1;
+    });
+
+    function spaceKeyframes() {
+      var length = keyframeEffect.length;
+      if (keyframeEffect[length - 1].offset == null)
+        keyframeEffect[length - 1].offset = 1;
+      if (length > 1 && keyframeEffect[0].offset == null)
+        keyframeEffect[0].offset = 0;
+
+      var previousIndex = 0;
+      var previousOffset = keyframeEffect[0].offset;
+      for (var i = 1; i < length; i++) {
+        var offset = keyframeEffect[i].offset;
+        if (offset != null) {
+          for (var j = 1; j < i - previousIndex; j++)
+            keyframeEffect[previousIndex + j].offset = previousOffset + (offset - previousOffset) * j / (i - previousIndex);
+          previousIndex = i;
+          previousOffset = offset;
+        }
+      }
+    }
+    if (!everyFrameHasOffset)
+      spaceKeyframes();
+
+    return keyframeEffect;
+  }
+
+  shared.normalizeKeyframes = normalizeKeyframes;
+
+  if (WEB_ANIMATIONS_TESTING) {
+    testing.normalizeKeyframes = normalizeKeyframes;
+  }
+
+})(webAnimationsShared, webAnimationsTesting);
diff --git a/third_party/web-animations-js/sources/src/number-handler.js b/third_party/web-animations-js/sources/src/number-handler.js
new file mode 100644
index 0000000..1555b952
--- /dev/null
+++ b/third_party/web-animations-js/sources/src/number-handler.js
@@ -0,0 +1,72 @@
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+//   You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//   See the License for the specific language governing permissions and
+// limitations under the License.
+
+(function(scope, testing) {
+
+  function numberToString(x) {
+    return x.toFixed(3).replace('.000', '');
+  }
+
+  function clamp(min, max, x) {
+    return Math.min(max, Math.max(min, x));
+  }
+
+  function parseNumber(string) {
+    if (/^\s*[-+]?(\d*\.)?\d+\s*$/.test(string))
+      return Number(string);
+  }
+
+  function mergeNumbers(left, right) {
+    return [left, right, numberToString];
+  }
+
+  // FIXME: This should probably go in it's own handler.
+  function mergeFlex(left, right) {
+    if (left == 0)
+      return;
+    return clampedMergeNumbers(0, Infinity)(left, right);
+  }
+
+  function mergePositiveIntegers(left, right) {
+    return [left, right, function(x) {
+      return Math.round(clamp(1, Infinity, x));
+    }];
+  }
+
+  function clampedMergeNumbers(min, max) {
+    return function(left, right) {
+      return [left, right, function(x) {
+        return numberToString(clamp(min, max, x));
+      }];
+    };
+  }
+
+  function round(left, right) {
+    return [left, right, Math.round];
+  }
+
+  scope.clamp = clamp;
+  scope.addPropertiesHandler(parseNumber, clampedMergeNumbers(0, Infinity), ['border-image-width', 'line-height']);
+  scope.addPropertiesHandler(parseNumber, clampedMergeNumbers(0, 1), ['opacity', 'shape-image-threshold']);
+  scope.addPropertiesHandler(parseNumber, clampedMergeNumbers(0.01, Infinity), ['zoom']);
+  scope.addPropertiesHandler(parseNumber, mergeFlex, ['flex-grow', 'flex-shrink']);
+  scope.addPropertiesHandler(parseNumber, mergeNumbers, ['zoom']);
+  scope.addPropertiesHandler(parseNumber, mergePositiveIntegers, ['orphans', 'widows']);
+  scope.addPropertiesHandler(parseNumber, round, ['z-index']);
+
+  scope.parseNumber = parseNumber;
+  scope.mergeNumbers = mergeNumbers;
+  scope.numberToString = numberToString;
+
+})(webAnimations1, webAnimationsTesting);
diff --git a/third_party/web-animations-js/sources/src/player.js b/third_party/web-animations-js/sources/src/player.js
new file mode 100644
index 0000000..d2187875
--- /dev/null
+++ b/third_party/web-animations-js/sources/src/player.js
@@ -0,0 +1,193 @@
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+//     You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//     See the License for the specific language governing permissions and
+// limitations under the License.
+
+(function(scope, testing) {
+
+  var sequenceNumber = 0;
+
+  var AnimationPlayerEvent = function(target, currentTime, timelineTime) {
+    this.target = target;
+    this.currentTime = currentTime;
+    this.timelineTime = timelineTime;
+
+    this.type = 'finish';
+    this.bubbles = false;
+    this.cancelable = false;
+    this.currentTarget = target;
+    this.defaultPrevented = false;
+    this.eventPhase = Event.AT_TARGET;
+    this.timeStamp = Date.now();
+  };
+
+  scope.Player = function(source) {
+    this._sequenceNumber = sequenceNumber++;
+    this._currentTime = 0;
+    this._startTime = null;
+    this.paused = false;
+    this._playbackRate = 1;
+    this._inTimeline = true;
+    this._finishedFlag = false;
+    this.onfinish = null;
+    this._finishHandlers = [];
+    this._source = source;
+    this._inEffect = this._source._update(0);
+    this._idle = true;
+    this._currentTimePending = false;
+  };
+
+  scope.Player.prototype = {
+    _ensureAlive: function() {
+      this._inEffect = this._source._update(this.currentTime);
+      if (!this._inTimeline && (this._inEffect || !this._finishedFlag)) {
+        this._inTimeline = true;
+        scope.timeline._players.push(this);
+      }
+    },
+    _tickCurrentTime: function(newTime, ignoreLimit) {
+      if (newTime != this._currentTime) {
+        this._currentTime = newTime;
+        if (this.finished && !ignoreLimit)
+          this._currentTime = this._playbackRate > 0 ? this._totalDuration : 0;
+        this._ensureAlive();
+      }
+    },
+    get currentTime() {
+      if (this._idle || this._currentTimePending)
+        return null;
+      return this._currentTime;
+    },
+    set currentTime(newTime) {
+      newTime = +newTime;
+      if (isNaN(newTime))
+        return;
+      scope.restart();
+      if (!this.paused && this._startTime != null) {
+        this._startTime = this._timeline.currentTime - newTime / this._playbackRate;
+      }
+      this._currentTimePending = false;
+      if (this._currentTime == newTime)
+        return;
+      this._tickCurrentTime(newTime, true);
+      scope.invalidateEffects();
+    },
+    get startTime() {
+      return this._startTime;
+    },
+    set startTime(newTime) {
+      newTime = +newTime;
+      if (isNaN(newTime))
+        return;
+      if (this.paused || this._idle)
+        return;
+      this._startTime = newTime;
+      this._tickCurrentTime((this._timeline.currentTime - this._startTime) * this.playbackRate);
+      scope.invalidateEffects();
+    },
+    get playbackRate() { return this._playbackRate; },
+    get finished() {
+      return !this._idle && (this._playbackRate > 0 && this._currentTime >= this._totalDuration ||
+          this._playbackRate < 0 && this._currentTime <= 0);
+    },
+    get _totalDuration() { return this._source._totalDuration; },
+    get playState() {
+      if (this._idle)
+        return 'idle';
+      if ((this._startTime == null && !this.paused && this.playbackRate != 0) || this._currentTimePending)
+        return 'pending';
+      if (this.paused)
+        return 'paused';
+      if (this.finished)
+        return 'finished';
+      return 'running';
+    },
+    play: function() {
+      this.paused = false;
+      if (this.finished || this._idle) {
+        this._currentTime = this._playbackRate > 0 ? 0 : this._totalDuration;
+        this._startTime = null;
+        scope.invalidateEffects();
+      }
+      this._finishedFlag = false;
+      scope.restart();
+      this._idle = false;
+      this._ensureAlive();
+    },
+    pause: function() {
+      if (!this.finished && !this.paused && !this._idle) {
+        this._currentTimePending = true;
+      }
+      this._startTime = null;
+      this.paused = true;
+    },
+    finish: function() {
+      if (this._idle)
+        return;
+      this.currentTime = this._playbackRate > 0 ? this._totalDuration : 0;
+      this._startTime = this._totalDuration - this.currentTime;
+      this._currentTimePending = false;
+    },
+    cancel: function() {
+      this._inEffect = false;
+      this._idle = true;
+      this.currentTime = 0;
+      this._startTime = null;
+    },
+    reverse: function() {
+      this._playbackRate *= -1;
+      this._startTime = null;
+      this.play();
+    },
+    addEventListener: function(type, handler) {
+      if (typeof handler == 'function' && type == 'finish')
+        this._finishHandlers.push(handler);
+    },
+    removeEventListener: function(type, handler) {
+      if (type != 'finish')
+        return;
+      var index = this._finishHandlers.indexOf(handler);
+      if (index >= 0)
+        this._finishHandlers.splice(index, 1);
+    },
+    _fireEvents: function(baseTime) {
+      var finished = this.finished;
+      if ((finished || this._idle) && !this._finishedFlag) {
+        var event = new AnimationPlayerEvent(this, this._currentTime, baseTime);
+        var handlers = this._finishHandlers.concat(this.onfinish ? [this.onfinish] : []);
+        setTimeout(function() {
+          handlers.forEach(function(handler) {
+            handler.call(event.target, event);
+          });
+        }, 0);
+      }
+      this._finishedFlag = finished;
+    },
+    _tick: function(timelineTime) {
+      if (!this._idle && !this.paused) {
+        if (this._startTime == null)
+          this.startTime = timelineTime - this._currentTime / this.playbackRate;
+        else if (!this.finished)
+          this._tickCurrentTime((timelineTime - this._startTime) * this.playbackRate);
+      }
+
+      this._currentTimePending = false;
+      this._fireEvents(timelineTime);
+      return !this._idle && (this._inEffect || !this._finishedFlag);
+    },
+  };
+
+  if (WEB_ANIMATIONS_TESTING) {
+    testing.Player = scope.Player;
+  }
+
+})(webAnimations1, webAnimationsTesting);
diff --git a/third_party/web-animations-js/sources/src/position-handler.js b/third_party/web-animations-js/sources/src/position-handler.js
new file mode 100644
index 0000000..18cea43
--- /dev/null
+++ b/third_party/web-animations-js/sources/src/position-handler.js
@@ -0,0 +1,117 @@
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+//   You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//   See the License for the specific language governing permissions and
+// limitations under the License.
+
+(function(scope) {
+
+  function negateDimension(dimension) {
+    var result = {};
+    for (var k in dimension) {
+      result[k] = -dimension[k];
+    }
+    return result;
+  }
+
+  function consumeOffset(string) {
+    return scope.consumeToken(/^(left|center|right|top|bottom)\b/i, string) || scope.consumeLengthOrPercent(string);
+  }
+
+  var offsetMap = {
+    left: {'%': 0},
+    center: {'%': 50},
+    right: {'%': 100},
+    top: {'%': 0},
+    bottom: {'%': 100},
+  };
+
+  function parseOrigin(slots, string) {
+    var result = scope.consumeRepeated(consumeOffset, /^/, string);
+    if (!result || result[1] != '') return;
+    var tokens = result[0];
+    tokens[0] = tokens[0] || 'center';
+    tokens[1] = tokens[1] || 'center';
+    if (slots == 3) {
+      tokens[2] = tokens[2] || {px: 0};
+    }
+    if (tokens.length != slots) {
+      return;
+    }
+    // Reorder so that the horizontal axis comes first.
+    if (/top|bottom/.test(tokens[0]) || /left|right/.test(tokens[1])) {
+      var tmp = tokens[0];
+      tokens[0] = tokens[1];
+      tokens[1] = tmp;
+    }
+    // Invalid if not horizontal then vertical.
+    if (!/left|right|center|Object/.test(tokens[0]))
+      return;
+    if (!/top|bottom|center|Object/.test(tokens[1]))
+      return;
+    return tokens.map(function(position) {
+      return typeof position == 'object' ? position : offsetMap[position];
+    });
+  }
+
+  var mergeOffsetList = scope.mergeNestedRepeated.bind(null, scope.mergeDimensions, ' ');
+  scope.addPropertiesHandler(parseOrigin.bind(null, 3), mergeOffsetList, ['transform-origin']);
+  scope.addPropertiesHandler(parseOrigin.bind(null, 2), mergeOffsetList, ['perspective-origin']);
+
+  function consumePosition(string) {
+    var result = scope.consumeRepeated(consumeOffset, /^/, string);
+    if (!result) {
+      return;
+    }
+
+    var tokens = result[0];
+    var out = [{'%': 50}, {'%': 50}];
+    var pos = 0;
+    var bottomOrRight = false;
+
+    for (var i = 0; i < tokens.length; i++) {
+      var token = tokens[i];
+      if (typeof token == 'string') {
+        bottomOrRight = /bottom|right/.test(token);
+        pos = {left: 0, right: 0, center: pos, top: 1, bottom: 1}[token];
+        out[pos] = offsetMap[token];
+        if (token == 'center') {
+          // Center doesn't accept a length offset.
+          pos++;
+        }
+      } else {
+        if (bottomOrRight) {
+          // If bottom or right we need to subtract the length from 100%
+          token = negateDimension(token);
+          token['%'] = (token['%'] || 0) + 100;
+        }
+        out[pos] = token;
+        pos++;
+        bottomOrRight = false;
+      }
+    }
+    return [out, result[1]];
+  }
+
+  function parsePositionList(string) {
+    var result = scope.consumeRepeated(consumePosition, /^,/, string);
+    if (result && result[1] == '') {
+      return result[0];
+    }
+  }
+
+  scope.consumePosition = consumePosition;
+  scope.mergeOffsetList = mergeOffsetList;
+
+  var mergePositionList = scope.mergeNestedRepeated.bind(null, mergeOffsetList, ', ');
+  scope.addPropertiesHandler(parsePositionList, mergePositionList, ['background-position', 'object-position']);
+
+})(webAnimations1);
diff --git a/third_party/web-animations-js/sources/src/property-interpolation.js b/third_party/web-animations-js/sources/src/property-interpolation.js
new file mode 100644
index 0000000..8634bf6
--- /dev/null
+++ b/third_party/web-animations-js/sources/src/property-interpolation.js
@@ -0,0 +1,62 @@
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+//   You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//   See the License for the specific language governing permissions and
+// limitations under the License.
+
+(function(scope, testing) {
+
+  var propertyHandlers = {};
+
+  function addPropertyHandler(parser, merger, property) {
+    propertyHandlers[property] = propertyHandlers[property] || [];
+    propertyHandlers[property].push([parser, merger]);
+  }
+  function addPropertiesHandler(parser, merger, properties) {
+    for (var i = 0; i < properties.length; i++) {
+      var property = properties[i];
+      WEB_ANIMATIONS_TESTING && console.assert(property.toLowerCase() === property);
+      addPropertyHandler(parser, merger, property);
+      if (/-/.test(property)) {
+        // Add camel cased variant.
+        addPropertyHandler(parser, merger, property.replace(/-(.)/g, function(_, c) {
+          return c.toUpperCase();
+        }));
+      }
+    }
+  }
+  scope.addPropertiesHandler = addPropertiesHandler;
+
+  function propertyInterpolation(property, left, right) {
+    var handlers = left == right ? [] : propertyHandlers[property];
+    for (var i = 0; handlers && i < handlers.length; i++) {
+      var parsedLeft = handlers[i][0](left);
+      var parsedRight = handlers[i][0](right);
+      if (parsedLeft !== undefined && parsedRight !== undefined) {
+        var interpolationArgs = handlers[i][1](parsedLeft, parsedRight);
+        if (interpolationArgs) {
+          var interp = scope.Interpolation.apply(null, interpolationArgs);
+          return function(t) {
+            if (t == 0) return left;
+            if (t == 1) return right;
+            return interp(t);
+          };
+        }
+      }
+    }
+    return scope.Interpolation(false, true, function(bool) {
+      return bool ? right : left;
+    });
+  }
+  scope.propertyInterpolation = propertyInterpolation;
+
+})(webAnimations1, webAnimationsTesting);
+
diff --git a/third_party/web-animations-js/sources/src/property-names.js b/third_party/web-animations-js/sources/src/property-names.js
new file mode 100644
index 0000000..c52d990c
--- /dev/null
+++ b/third_party/web-animations-js/sources/src/property-names.js
@@ -0,0 +1,35 @@
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+//     You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//     See the License for the specific language governing permissions and
+// limitations under the License.
+
+(function(scope, testing) {
+
+  var aliased = {};
+
+  function alias(name, aliases) {
+    aliases.concat([name]).forEach(function(candidate) {
+      if (candidate in document.documentElement.style) {
+        aliased[name] = candidate;
+      }
+    });
+  }
+  alias('transform', ['webkitTransform', 'msTransform']);
+  alias('transformOrigin', ['webkitTransformOrigin']);
+  alias('perspective', ['webkitPerspective']);
+  alias('perspectiveOrigin', ['webkitPerspectiveOrigin']);
+
+  scope.propertyName = function(property) {
+    return aliased[property] || property;
+  };
+
+})(webAnimations1, webAnimationsTesting);
diff --git a/third_party/web-animations-js/sources/src/scope.js b/third_party/web-animations-js/sources/src/scope.js
new file mode 100644
index 0000000..c8248802
--- /dev/null
+++ b/third_party/web-animations-js/sources/src/scope.js
@@ -0,0 +1,20 @@
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+//     You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//     See the License for the specific language governing permissions and
+// limitations under the License.
+
+var webAnimationsShared = {};
+var webAnimations1 = {};
+var webAnimationsNext = {};
+
+if (!WEB_ANIMATIONS_TESTING)
+  var webAnimationsTesting = null;
diff --git a/third_party/web-animations-js/sources/src/shadow-handler.js b/third_party/web-animations-js/sources/src/shadow-handler.js
new file mode 100644
index 0000000..3f8201d8
--- /dev/null
+++ b/third_party/web-animations-js/sources/src/shadow-handler.js
@@ -0,0 +1,108 @@
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+//   You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//   See the License for the specific language governing permissions and
+// limitations under the License.
+
+(function(scope) {
+
+  function consumeShadow(string) {
+    var shadow = {
+      inset: false,
+      lengths: [],
+      color: null,
+    };
+    function consumePart(string) {
+      var result = scope.consumeToken(/^inset/i, string);
+      if (result) {
+        shadow.inset = true;
+        return result;
+      }
+      var result = scope.consumeLengthOrPercent(string);
+      if (result) {
+        shadow.lengths.push(result[0]);
+        return result;
+      }
+      var result = scope.consumeColor(string);
+      if (result) {
+        shadow.color = result[0];
+        return result;
+      }
+    }
+    var result = scope.consumeRepeated(consumePart, /^/, string);
+    if (result && result[0].length) {
+      return [shadow, result[1]];
+    }
+  }
+
+  function parseShadowList(string) {
+    var result = scope.consumeRepeated(consumeShadow, /^,/, string);
+    if (result && result[1] == '') {
+      return result[0];
+    }
+  }
+
+  function mergeShadow(left, right) {
+    while (left.lengths.length < Math.max(left.lengths.length, right.lengths.length))
+      left.lengths.push({px: 0});
+    while (right.lengths.length < Math.max(left.lengths.length, right.lengths.length))
+      right.lengths.push({px: 0});
+
+    if (left.inset != right.inset || !!left.color != !!right.color) {
+      return;
+    }
+    var lengthReconstitution = [];
+    var colorReconstitution;
+    var matchingLeft = [[], 0];
+    var matchingRight = [[], 0];
+    for (var i = 0; i < left.lengths.length; i++) {
+      var mergedDimensions = scope.mergeDimensions(left.lengths[i], right.lengths[i], i == 2);
+      matchingLeft[0].push(mergedDimensions[0]);
+      matchingRight[0].push(mergedDimensions[1]);
+      lengthReconstitution.push(mergedDimensions[2]);
+    }
+    if (left.color && right.color) {
+      var mergedColor = scope.mergeColors(left.color, right.color);
+      matchingLeft[1] = mergedColor[0];
+      matchingRight[1] = mergedColor[1];
+      colorReconstitution = mergedColor[2];
+    }
+    return [matchingLeft, matchingRight, function(value) {
+      var result = left.inset ? 'inset ' : ' ';
+      for (var i = 0; i < lengthReconstitution.length; i++) {
+        result += lengthReconstitution[i](value[0][i]) + ' ';
+      }
+      if (colorReconstitution) {
+        result += colorReconstitution(value[1]);
+      }
+      return result;
+    }];
+  }
+
+  function mergeNestedRepeatedShadow(nestedMerge, separator, left, right) {
+    var leftCopy = [];
+    var rightCopy = [];
+    function defaultShadow(inset) {
+      return {inset: inset, color: [0, 0, 0, 0], lengths: [{px: 0}, {px: 0}, {px: 0}, {px: 0}]};
+    }
+    for (var i = 0; i < left.length || i < right.length; i++) {
+      var l = left[i] || defaultShadow(right[i].inset);
+      var r = right[i] || defaultShadow(left[i].inset);
+      leftCopy.push(l);
+      rightCopy.push(r);
+    }
+    return scope.mergeNestedRepeated(nestedMerge, separator, leftCopy, rightCopy);
+  }
+
+  var mergeShadowList = mergeNestedRepeatedShadow.bind(null, mergeShadow, ', ');
+  scope.addPropertiesHandler(parseShadowList, mergeShadowList, ['box-shadow', 'text-shadow']);
+
+})(webAnimations1);
diff --git a/third_party/web-animations-js/sources/src/shape-handler.js b/third_party/web-animations-js/sources/src/shape-handler.js
new file mode 100644
index 0000000..6bbf79f
--- /dev/null
+++ b/third_party/web-animations-js/sources/src/shape-handler.js
@@ -0,0 +1,85 @@
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+//   You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//   See the License for the specific language governing permissions and
+// limitations under the License.
+
+(function(scope) {
+
+  var consumeLengthOrPercent = scope.consumeParenthesised.bind(null, scope.parseLengthOrPercent);
+  var consumeLengthOrPercentPair = scope.consumeRepeated.bind(undefined, consumeLengthOrPercent, /^/);
+
+  var mergeSizePair = scope.mergeNestedRepeated.bind(undefined, scope.mergeDimensions, ' ');
+  var mergeSizePairList = scope.mergeNestedRepeated.bind(undefined, mergeSizePair, ',');
+
+  function parseShape(input) {
+    var circle = scope.consumeToken(/^circle/, input);
+    if (circle && circle[0]) {
+      return ['circle'].concat(scope.consumeList([
+        scope.ignore(scope.consumeToken.bind(undefined, /^\(/)),
+        consumeLengthOrPercent,
+        scope.ignore(scope.consumeToken.bind(undefined, /^at/)),
+        scope.consumePosition,
+        scope.ignore(scope.consumeToken.bind(undefined, /^\)/))
+      ], circle[1]));
+    }
+    var ellipse = scope.consumeToken(/^ellipse/, input);
+    if (ellipse && ellipse[0]) {
+      return ['ellipse'].concat(scope.consumeList([
+        scope.ignore(scope.consumeToken.bind(undefined, /^\(/)),
+        consumeLengthOrPercentPair,
+        scope.ignore(scope.consumeToken.bind(undefined, /^at/)),
+        scope.consumePosition,
+        scope.ignore(scope.consumeToken.bind(undefined, /^\)/))
+      ], ellipse[1]));
+    }
+    var polygon = scope.consumeToken(/^polygon/, input);
+    if (polygon && polygon[0]) {
+      return ['polygon'].concat(scope.consumeList([
+        scope.ignore(scope.consumeToken.bind(undefined, /^\(/)),
+        scope.optional(scope.consumeToken.bind(undefined, /^nonzero\s*,|^evenodd\s*,/), 'nonzero,'),
+        scope.consumeSizePairList,
+        scope.ignore(scope.consumeToken.bind(undefined, /^\)/))
+      ], polygon[1]));
+    }
+  }
+
+  function mergeShapes(left, right) {
+    if (left[0] !== right[0])
+      return;
+    if (left[0] == 'circle') {
+      return scope.mergeList(left.slice(1), right.slice(1), [
+        'circle(',
+        scope.mergeDimensions,
+        ' at ',
+        scope.mergeOffsetList,
+        ')']);
+    }
+    if (left[0] == 'ellipse') {
+      return scope.mergeList(left.slice(1), right.slice(1), [
+        'ellipse(',
+        scope.mergeNonNegativeSizePair,
+        ' at ',
+        scope.mergeOffsetList,
+        ')']);
+    }
+    if (left[0] == 'polygon' && left[1] == right[1]) {
+      return scope.mergeList(left.slice(2), right.slice(2), [
+        'polygon(',
+        left[1],
+        mergeSizePairList,
+        ')']);
+    }
+  }
+
+  scope.addPropertiesHandler(parseShape, mergeShapes, ['shape-outside']);
+
+})(webAnimations1);
diff --git a/third_party/web-animations-js/sources/src/tick.js b/third_party/web-animations-js/sources/src/tick.js
new file mode 100644
index 0000000..cbc7ff3a
--- /dev/null
+++ b/third_party/web-animations-js/sources/src/tick.js
@@ -0,0 +1,148 @@
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+//     You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//     See the License for the specific language governing permissions and
+// limitations under the License.
+
+
+(function(shared, scope, testing) {
+  var originalRequestAnimationFrame = window.requestAnimationFrame;
+  var rafCallbacks = [];
+  var rafId = 0;
+  window.requestAnimationFrame = function(f) {
+    var id = rafId++;
+    if (rafCallbacks.length == 0 && !WEB_ANIMATIONS_TESTING) {
+      originalRequestAnimationFrame(processRafCallbacks);
+    }
+    rafCallbacks.push([id, f]);
+    return id;
+  };
+
+  window.cancelAnimationFrame = function(id) {
+    rafCallbacks.forEach(function(entry) {
+      if (entry[0] == id) {
+        entry[1] = function() {};
+      }
+    });
+  };
+
+  function processRafCallbacks(t) {
+    var processing = rafCallbacks;
+    rafCallbacks = [];
+    tick(t);
+    processing.forEach(function(entry) { entry[1](t); });
+    if (needsRetick)
+      tick(t);
+    applyPendingEffects();
+  }
+
+  function comparePlayers(leftPlayer, rightPlayer) {
+    return leftPlayer._sequenceNumber - rightPlayer._sequenceNumber;
+  }
+
+  function InternalTimeline() {
+    this._players = [];
+    // Android 4.3 browser has window.performance, but not window.performance.now
+    this.currentTime = window.performance && performance.now ? performance.now() : 0;
+  };
+
+  InternalTimeline.prototype = {
+    _play: function(source) {
+      source._timing = shared.normalizeTimingInput(source.timing);
+      var player = new scope.Player(source);
+      player._idle = false;
+      player._timeline = this;
+      this._players.push(player);
+      scope.restart();
+      scope.invalidateEffects();
+      return player;
+    }
+  };
+
+  var ticking = false;
+  var hasRestartedThisFrame = false;
+
+  scope.restart = function() {
+    if (!ticking) {
+      ticking = true;
+      requestAnimationFrame(function() {});
+      hasRestartedThisFrame = true;
+    }
+    return hasRestartedThisFrame;
+  };
+
+  var needsRetick = false;
+  scope.invalidateEffects = function() {
+    needsRetick = true;
+  };
+
+  var pendingEffects = [];
+  function applyPendingEffects() {
+    pendingEffects.forEach(function(f) { f(); });
+  }
+
+  var originalGetComputedStyle = window.getComputedStyle;
+  Object.defineProperty(window, 'getComputedStyle', {
+    configurable: true,
+    enumerable: true,
+    value: function() {
+      if (needsRetick) tick(timeline.currentTime);
+      applyPendingEffects();
+      return originalGetComputedStyle.apply(this, arguments);
+    },
+  });
+
+  function tick(t) {
+    hasRestartedThisFrame = false;
+    var timeline = scope.timeline;
+    timeline.currentTime = t;
+    timeline._players.sort(comparePlayers);
+    ticking = false;
+    var updatingPlayers = timeline._players;
+    timeline._players = [];
+
+    var newPendingClears = [];
+    var newPendingEffects = [];
+    updatingPlayers = updatingPlayers.filter(function(player) {
+      player._inTimeline = player._tick(t);
+
+      if (!player._inEffect)
+        newPendingClears.push(player._source);
+      else
+        newPendingEffects.push(player._source);
+
+      if (!player.finished && !player.paused && !player._idle)
+        ticking = true;
+
+      return player._inTimeline;
+    });
+
+    pendingEffects.length = 0;
+    pendingEffects.push.apply(pendingEffects, newPendingClears);
+    pendingEffects.push.apply(pendingEffects, newPendingEffects);
+
+    timeline._players.push.apply(timeline._players, updatingPlayers);
+    needsRetick = false;
+
+    if (ticking)
+      requestAnimationFrame(function() {});
+  };
+
+  if (WEB_ANIMATIONS_TESTING) {
+    testing.tick = processRafCallbacks;
+    testing.isTicking = function() { return ticking; };
+    testing.setTicking = function(newVal) { ticking = newVal; };
+  }
+
+  var timeline = new InternalTimeline();
+  scope.timeline = timeline;
+
+})(webAnimationsShared, webAnimations1, webAnimationsTesting);
diff --git a/third_party/web-animations-js/sources/src/timeline.js b/third_party/web-animations-js/sources/src/timeline.js
new file mode 100644
index 0000000..263494b
--- /dev/null
+++ b/third_party/web-animations-js/sources/src/timeline.js
@@ -0,0 +1,77 @@
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+//     You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//     See the License for the specific language governing permissions and
+// limitations under the License.
+
+
+(function(shared, scope, testing) {
+
+  scope.AnimationTimeline = function() {
+    this._players = [];
+    this.currentTime = undefined;
+  };
+
+  scope.AnimationTimeline.prototype = {
+    // FIXME: This needs to return the wrapped players in Web Animations Next
+    // TODO: Does this need to be sorted?
+    // TODO: Do we need to consider needsRetick?
+    getAnimationPlayers: function() {
+      this._discardPlayers();
+      return this._players.slice();
+    },
+    _discardPlayers: function() {
+      this._players = this._players.filter(function(player) {
+        return player.playState != 'finished' && player.playState != 'idle';
+      });
+    },
+    play: function(source) {
+      var player = new scope.Player(source);
+      this._players.push(player);
+      scope.restartWebAnimationsNextTick();
+      player.play();
+      return player;
+    },
+  };
+
+  var ticking = false;
+
+  scope.restartWebAnimationsNextTick = function() {
+    if (!ticking) {
+      ticking = true;
+      requestAnimationFrame(webAnimationsNextTick);
+    }
+  };
+
+  function webAnimationsNextTick(t) {
+    var timeline = window.document.timeline;
+    timeline.currentTime = t;
+    timeline._discardPlayers();
+    if (timeline._players.length == 0)
+      ticking = false;
+    else
+      requestAnimationFrame(webAnimationsNextTick);
+  }
+
+  var timeline = new scope.AnimationTimeline();
+  scope.timeline = timeline;
+
+  try {
+    Object.defineProperty(window.document, 'timeline', {
+      configurable: true,
+      get: function() { return timeline; }
+    });
+  } catch (e) { }
+  try {
+    window.document.timeline = timeline;
+  } catch (e) { }
+
+})(webAnimationsShared, webAnimationsNext, webAnimationsTesting);
diff --git a/third_party/web-animations-js/sources/src/timing-utilities.js b/third_party/web-animations-js/sources/src/timing-utilities.js
new file mode 100644
index 0000000..00da45b9
--- /dev/null
+++ b/third_party/web-animations-js/sources/src/timing-utilities.js
@@ -0,0 +1,242 @@
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+//     You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//     See the License for the specific language governing permissions and
+// limitations under the License.
+
+(function(shared, testing) {
+
+  var fills = 'backwards|forwards|both'.split('|');
+  var directions = 'reverse|alternate|alternate-reverse'.split('|');
+
+  function makeTiming(timingInput, forGroup) {
+    var timing = {
+      delay: 0,
+      endDelay: 0,
+      fill: forGroup ? 'both' : 'none',
+      iterationStart: 0,
+      iterations: 1,
+      duration: forGroup ? 'auto' : 0,
+      playbackRate: 1,
+      direction: 'normal',
+      easing: 'linear',
+    };
+    if (typeof timingInput == 'number' && !isNaN(timingInput)) {
+      timing.duration = timingInput;
+    } else if (timingInput !== undefined) {
+      Object.getOwnPropertyNames(timingInput).forEach(function(property) {
+        if (timingInput[property] != 'auto') {
+          if (typeof timing[property] == 'number' || property == 'duration') {
+            if (typeof timingInput[property] != 'number' || isNaN(timingInput[property])) {
+              return;
+            }
+          }
+          if ((property == 'fill') && (fills.indexOf(timingInput[property]) == -1)) {
+            return;
+          }
+          if ((property == 'direction') && (directions.indexOf(timingInput[property]) == -1)) {
+            return;
+          }
+          if (property == 'playbackRate' && shared.isDeprecated('AnimationTiming.playbackRate', '2014-11-28', 'Use AnimationPlayer.playbackRate instead.')) {
+            return;
+          }
+          timing[property] = timingInput[property];
+        }
+      });
+    }
+    return timing;
+  }
+
+  function normalizeTimingInput(timingInput, forGroup) {
+    var timing = makeTiming(timingInput, forGroup);
+    timing.easing = toTimingFunction(timing.easing);
+    return timing;
+  }
+
+  function cubic(a, b, c, d) {
+    if (a < 0 || a > 1 || c < 0 || c > 1) {
+      return linear;
+    }
+    return function(x) {
+      var start = 0, end = 1;
+      while (1) {
+        var mid = (start + end) / 2;
+        function f(a, b, m) { return 3 * a * (1 - m) * (1 - m) * m + 3 * b * (1 - m) * m * m + m * m * m};
+        var xEst = f(a, c, mid);
+        if (Math.abs(x - xEst) < 0.001) {
+          return f(b, d, mid);
+        }
+        if (xEst < x) {
+          start = mid;
+        } else {
+          end = mid;
+        }
+      }
+    }
+  }
+
+  var Start = 1;
+  var Middle = 0.5;
+  var End = 0;
+
+  function step(count, pos) {
+    return function(x) {
+      if (x >= 1) {
+        return 1;
+      }
+      var stepSize = 1 / count;
+      x += pos * stepSize;
+      return x - x % stepSize;
+    }
+  }
+
+  var presets = {
+    'ease': cubic(0.25, 0.1, 0.25, 1),
+    'ease-in': cubic(0.42, 0, 1, 1),
+    'ease-out': cubic(0, 0, 0.58, 1),
+    'ease-in-out': cubic(0.42, 0, 0.58, 1),
+    'step-start': step(1, Start),
+    'step-middle': step(1, Middle),
+    'step-end': step(1, End)
+  };
+
+  var numberString = '\\s*(-?\\d+\\.?\\d*|-?\\.\\d+)\\s*';
+  var cubicBezierRe = new RegExp('cubic-bezier\\(' + numberString + ',' + numberString + ',' + numberString + ',' + numberString + '\\)');
+  var stepRe = /steps\(\s*(\d+)\s*,\s*(start|middle|end)\s*\)/;
+  var linear = function(x) { return x; };
+
+  function toTimingFunction(easing) {
+    var cubicData = cubicBezierRe.exec(easing);
+    if (cubicData) {
+      return cubic.apply(this, cubicData.slice(1).map(Number));
+    }
+    var stepData = stepRe.exec(easing);
+    if (stepData) {
+      return step(Number(stepData[1]), {'start': Start, 'middle': Middle, 'end': End}[stepData[2]]);
+    }
+    var preset = presets[easing];
+    if (preset) {
+      return preset;
+    }
+    return linear;
+  };
+
+  function calculateActiveDuration(timing) {
+    return Math.abs(repeatedDuration(timing) / timing.playbackRate);
+  }
+
+  function repeatedDuration(timing) {
+    return timing.duration * timing.iterations;
+  }
+
+  var PhaseNone = 0;
+  var PhaseBefore = 1;
+  var PhaseAfter = 2;
+  var PhaseActive = 3;
+
+  function calculatePhase(activeDuration, localTime, timing) {
+    if (localTime == null) {
+      return PhaseNone;
+    }
+    if (localTime < timing.delay) {
+      return PhaseBefore;
+    }
+    if (localTime >= timing.delay + activeDuration) {
+      return PhaseAfter;
+    }
+    return PhaseActive;
+  }
+
+  function calculateActiveTime(activeDuration, fillMode, localTime, phase, delay) {
+    switch (phase) {
+      case PhaseBefore:
+        if (fillMode == 'backwards' || fillMode == 'both')
+          return 0;
+        return null;
+      case PhaseActive:
+        return localTime - delay;
+      case PhaseAfter:
+        if (fillMode == 'forwards' || fillMode == 'both')
+          return activeDuration;
+        return null;
+      case PhaseNone:
+        return null;
+    }
+  }
+
+  function calculateScaledActiveTime(activeDuration, activeTime, startOffset, timing) {
+    return (timing.playbackRate < 0 ? activeTime - activeDuration : activeTime) * timing.playbackRate + startOffset;
+  }
+
+  function calculateIterationTime(iterationDuration, repeatedDuration, scaledActiveTime, startOffset, timing) {
+    if (scaledActiveTime === Infinity || scaledActiveTime === -Infinity || (scaledActiveTime - startOffset == repeatedDuration && timing.iterations && ((timing.iterations + timing.iterationStart) % 1 == 0))) {
+      return iterationDuration;
+    }
+
+    return scaledActiveTime % iterationDuration;
+  }
+
+  function calculateCurrentIteration(iterationDuration, iterationTime, scaledActiveTime, timing) {
+    if (scaledActiveTime === 0) {
+      return 0;
+    }
+    if (iterationTime == iterationDuration) {
+      return timing.iterationStart + timing.iterations - 1;
+    }
+    return Math.floor(scaledActiveTime / iterationDuration);
+  }
+
+  function calculateTransformedTime(currentIteration, iterationDuration, iterationTime, timing) {
+    var currentIterationIsOdd = currentIteration % 2 >= 1;
+    var currentDirectionIsForwards = timing.direction == 'normal' || timing.direction == (currentIterationIsOdd ? 'alternate-reverse' : 'alternate');
+    var directedTime = currentDirectionIsForwards ? iterationTime : iterationDuration - iterationTime;
+    var timeFraction = directedTime / iterationDuration;
+    return iterationDuration * timing.easing(timeFraction);
+  }
+
+  function calculateTimeFraction(activeDuration, localTime, timing) {
+    var phase = calculatePhase(activeDuration, localTime, timing);
+    var activeTime = calculateActiveTime(activeDuration, timing.fill, localTime, phase, timing.delay);
+    if (activeTime === null)
+      return null;
+    if (activeDuration === 0)
+      return phase === PhaseBefore ? 0 : 1;
+    var startOffset = timing.iterationStart * timing.duration;
+    var scaledActiveTime = calculateScaledActiveTime(activeDuration, activeTime, startOffset, timing);
+    var iterationTime = calculateIterationTime(timing.duration, repeatedDuration(timing), scaledActiveTime, startOffset, timing);
+    var currentIteration = calculateCurrentIteration(timing.duration, iterationTime, scaledActiveTime, timing);
+    return calculateTransformedTime(currentIteration, timing.duration, iterationTime, timing) / timing.duration;
+  }
+
+  shared.makeTiming = makeTiming;
+  shared.normalizeTimingInput = normalizeTimingInput;
+  shared.calculateActiveDuration = calculateActiveDuration;
+  shared.calculateTimeFraction = calculateTimeFraction;
+  shared.calculatePhase = calculatePhase;
+  shared.toTimingFunction = toTimingFunction;
+
+  if (WEB_ANIMATIONS_TESTING) {
+    testing.normalizeTimingInput = normalizeTimingInput;
+    testing.toTimingFunction = toTimingFunction;
+    testing.calculateActiveDuration = calculateActiveDuration;
+    testing.calculatePhase = calculatePhase;
+    testing.PhaseNone = PhaseNone;
+    testing.PhaseBefore = PhaseBefore;
+    testing.PhaseActive = PhaseActive;
+    testing.PhaseAfter = PhaseAfter;
+    testing.calculateActiveTime = calculateActiveTime;
+    testing.calculateScaledActiveTime = calculateScaledActiveTime;
+    testing.calculateIterationTime = calculateIterationTime;
+    testing.calculateCurrentIteration = calculateCurrentIteration;
+    testing.calculateTransformedTime = calculateTransformedTime;
+  }
+
+})(webAnimationsShared, webAnimationsTesting);
diff --git a/third_party/web-animations-js/sources/src/transform-handler.js b/third_party/web-animations-js/sources/src/transform-handler.js
new file mode 100644
index 0000000..c4482961
--- /dev/null
+++ b/third_party/web-animations-js/sources/src/transform-handler.js
@@ -0,0 +1,262 @@
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+//   You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//   See the License for the specific language governing permissions and
+// limitations under the License.
+
+(function(scope, testing) {
+
+  // This returns a function for converting transform functions to equivalent
+  // primitive functions, which will take an array of values from the
+  // derivative type and fill in the blanks (underscores) with them.
+  var _ = null;
+  function cast(pattern) {
+    return function(contents) {
+      var i = 0;
+      return pattern.map(function(x) { return x === _ ? contents[i++] : x; });
+    }
+  }
+
+  function id(x) { return x; }
+
+  var Opx = {px: 0};
+  var Odeg = {deg: 0};
+
+  // type: [argTypes, convertTo3D, convertTo2D]
+  // In the argument types string, lowercase characters represent optional arguments
+  var transformFunctions = {
+    matrix: ['NNNNNN', [_, _, 0, 0, _, _, 0, 0, 0, 0, 1, 0, _, _, 0, 1], id],
+    matrix3d: ['NNNNNNNNNNNNNNNN', id],
+    rotate: ['A'],
+    rotatex: ['A'],
+    rotatey: ['A'],
+    rotatez: ['A'],
+    rotate3d: ['NNNA'],
+    perspective: ['L'],
+    scale: ['Nn', cast([_, _, 1]), id],
+    scalex: ['N', cast([_, 1, 1]), cast([_, 1])],
+    scaley: ['N', cast([1, _, 1]), cast([1, _])],
+    scalez: ['N', cast([1, 1, _])],
+    scale3d: ['NNN', id],
+    skew: ['Aa', null, id],
+    skewx: ['A', null, cast([_, Odeg])],
+    skewy: ['A', null, cast([Odeg, _])],
+    translate: ['Tt', cast([_, _, Opx]), id],
+    translatex: ['T', cast([_, Opx, Opx]), cast([_, Opx])],
+    translatey: ['T', cast([Opx, _, Opx]), cast([Opx, _])],
+    translatez: ['L', cast([Opx, Opx, _])],
+    translate3d: ['TTL', id],
+  };
+
+  function parseTransform(string) {
+    string = string.toLowerCase().trim();
+    if (string == 'none')
+      return [];
+    // FIXME: Using a RegExp means calcs won't work here
+    var transformRegExp = /\s*(\w+)\(([^)]*)\)/g;
+    var result = [];
+    var match;
+    var prevLastIndex = 0;
+    while (match = transformRegExp.exec(string)) {
+      if (match.index != prevLastIndex)
+        return;
+      prevLastIndex = match.index + match[0].length;
+      var functionName = match[1];
+      var functionData = transformFunctions[functionName];
+      if (!functionData)
+        return;
+      var args = match[2].split(',');
+      var argTypes = functionData[0];
+      if (argTypes.length < args.length)
+        return;
+
+      var parsedArgs = [];
+      for (var i = 0; i < argTypes.length; i++) {
+        var arg = args[i];
+        var type = argTypes[i];
+        var parsedArg;
+        if (!arg)
+          parsedArg = ({a: Odeg,
+                        n: parsedArgs[0],
+                        t: Opx})[type];
+        else
+          parsedArg = ({A: function(s) { return s.trim() == '0' ? Odeg : scope.parseAngle(s); },
+                        N: scope.parseNumber,
+                        T: scope.parseLengthOrPercent,
+                        L: scope.parseLength})[type.toUpperCase()](arg);
+        if (parsedArg === undefined)
+          return;
+        parsedArgs.push(parsedArg);
+      }
+      result.push({t: functionName, d: parsedArgs});
+
+      if (transformRegExp.lastIndex == string.length)
+        return result;
+    }
+  };
+
+  function numberToLongString(x) {
+    return x.toFixed(6).replace('.000000', '');
+  }
+
+  function mergeMatrices(left, right) {
+    if (left.decompositionPair !== right) {
+      left.decompositionPair = right;
+      var leftArgs = scope.makeMatrixDecomposition(left);
+    }
+    if (right.decompositionPair !== left) {
+      right.decompositionPair = left;
+      var rightArgs = scope.makeMatrixDecomposition(right);
+    }
+    if (leftArgs[0] == null || rightArgs[0] == null)
+      return [[false], [true], function(x) { return x ? right[0].d : left[0].d; }];
+    leftArgs[0].push(0);
+    rightArgs[0].push(1);
+    return [
+      leftArgs,
+      rightArgs,
+      function(list) {
+        var quat = scope.quat(leftArgs[0][3], rightArgs[0][3], list[5]);
+        var mat = scope.composeMatrix(list[0], list[1], list[2], quat, list[4]);
+        var stringifiedArgs = mat.map(numberToLongString).join(',');
+        return stringifiedArgs;
+      }
+    ];
+  }
+
+  function typeTo2D(type) {
+    return type.replace(/[xy]/, '');
+  }
+
+  function typeTo3D(type) {
+    return type.replace(/(x|y|z|3d)?$/, '3d');
+  }
+
+  function mergeTransforms(left, right) {
+    var matrixModulesLoaded = scope.makeMatrixDecomposition && true;
+
+    var flipResults = false;
+    if (!left.length || !right.length) {
+      if (!left.length) {
+        flipResults = true;
+        left = right;
+        right = [];
+      }
+      for (var i = 0; i < left.length; i++) {
+        var type = left[i].t;
+        var args = left[i].d;
+        var defaultValue = type.substr(0, 5) == 'scale' ? 1 : 0;
+        right.push({t: type, d: args.map(function(arg) {
+          if (typeof arg == 'number')
+            return defaultValue;
+          var result = {};
+          for (var unit in arg)
+            result[unit] = defaultValue;
+          return result;
+        })});
+      }
+    }
+
+    var isMatrixOrPerspective = function(lt, rt) {
+      return ((lt == 'perspective') && (rt == 'perspective')) ||
+          ((lt == 'matrix' || lt == 'matrix3d') && (rt == 'matrix' || rt == 'matrix3d'));
+    };
+    var leftResult = [];
+    var rightResult = [];
+    var types = [];
+
+    if (left.length != right.length) {
+      if (!matrixModulesLoaded)
+        return;
+      var merged = mergeMatrices(left, right);
+      leftResult = [merged[0]];
+      rightResult = [merged[1]];
+      types = [['matrix', [merged[2]]]];
+    } else {
+      for (var i = 0; i < left.length; i++) {
+        var leftType = left[i].t;
+        var rightType = right[i].t;
+        var leftArgs = left[i].d;
+        var rightArgs = right[i].d;
+
+        var leftFunctionData = transformFunctions[leftType];
+        var rightFunctionData = transformFunctions[rightType];
+
+        var type;
+        if (isMatrixOrPerspective(leftType, rightType)) {
+          if (!matrixModulesLoaded)
+            return;
+          var merged = mergeMatrices([left[i]], [right[i]]);
+          leftResult.push(merged[0]);
+          rightResult.push(merged[1]);
+          types.push(['matrix', [merged[2]]]);
+          continue;
+        } else if (leftType == rightType) {
+          type = leftType;
+        } else if (leftFunctionData[2] && rightFunctionData[2] && typeTo2D(leftType) == typeTo2D(rightType)) {
+          type = typeTo2D(leftType);
+          leftArgs = leftFunctionData[2](leftArgs);
+          rightArgs = rightFunctionData[2](rightArgs);
+        } else if (leftFunctionData[1] && rightFunctionData[1] && typeTo3D(leftType) == typeTo3D(rightType)) {
+          type = typeTo3D(leftType);
+          leftArgs = leftFunctionData[1](leftArgs);
+          rightArgs = rightFunctionData[1](rightArgs);
+        } else {
+          if (!matrixModulesLoaded)
+            return;
+          var merged = mergeMatrices(left, right);
+          leftResult = [merged[0]];
+          rightResult = [merged[1]];
+          types = [['matrix', [merged[2]]]];
+          break;
+        }
+
+        var leftArgsCopy = [];
+        var rightArgsCopy = [];
+        var stringConversions = [];
+        for (var j = 0; j < leftArgs.length; j++) {
+          var merge = typeof leftArgs[j] == 'number' ? scope.mergeNumbers : scope.mergeDimensions;
+          var merged = merge(leftArgs[j], rightArgs[j]);
+          leftArgsCopy[j] = merged[0];
+          rightArgsCopy[j] = merged[1];
+          stringConversions.push(merged[2]);
+        }
+        leftResult.push(leftArgsCopy);
+        rightResult.push(rightArgsCopy);
+        types.push([type, stringConversions]);
+      }
+    }
+
+    if (flipResults) {
+      var tmp = leftResult;
+      leftResult = rightResult;
+      rightResult = tmp;
+    }
+
+    return [leftResult, rightResult, function(list) {
+      return list.map(function(args, i) {
+        var stringifiedArgs = args.map(function(arg, j) {
+          return types[i][1][j](arg);
+        }).join(',');
+        if (types[i][0] == 'matrix' && stringifiedArgs.split(',').length == 16)
+          types[i][0] = 'matrix3d';
+        return types[i][0] + '(' + stringifiedArgs + ')';
+
+      }).join(' ');
+    }];
+  }
+
+  scope.addPropertiesHandler(parseTransform, mergeTransforms, ['transform']);
+
+  if (WEB_ANIMATIONS_TESTING)
+    testing.parseTransform = parseTransform;
+
+})(webAnimations1, webAnimationsTesting);
diff --git a/third_party/web-animations-js/sources/src/visibility-handler.js b/third_party/web-animations-js/sources/src/visibility-handler.js
new file mode 100644
index 0000000..53f29535
--- /dev/null
+++ b/third_party/web-animations-js/sources/src/visibility-handler.js
@@ -0,0 +1,29 @@
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+//   You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//   See the License for the specific language governing permissions and
+// limitations under the License.
+
+(function(scope, testing) {
+
+  function merge(left, right) {
+    if (left != 'visible' && right != 'visible') return;
+    return [0, 1, function(x) {
+      if (x <= 0) return left;
+      if (x >= 1) return right;
+      return 'visible';
+    }];
+  }
+
+  scope.addPropertiesHandler(String, merge, ['visibility']);
+
+})(webAnimations1);
+
diff --git a/third_party/web-animations-js/sources/src/web-animations-next-player.js b/third_party/web-animations-js/sources/src/web-animations-next-player.js
new file mode 100644
index 0000000..54d8f80
--- /dev/null
+++ b/third_party/web-animations-js/sources/src/web-animations-next-player.js
@@ -0,0 +1,162 @@
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+//     You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//     See the License for the specific language governing permissions and
+// limitations under the License.
+
+(function(shared, scope, testing) {
+  scope.Player = function(source) {
+    this.source = source;
+    if (source) {
+      // FIXME: detach existing player.
+      source.player = this;
+    }
+    this._isGroup = false;
+    this._player = null;
+    this._childPlayers = [];
+    this._callback = null;
+    this._rebuildUnderlyingPlayer();
+    // Players are constructed in the idle state.
+    this._player.cancel();
+  };
+
+  // TODO: add a source getter/setter
+  scope.Player.prototype = {
+    _rebuildUnderlyingPlayer: function() {
+      if (this._player) {
+        this._player.cancel();
+        this._player = null;
+      }
+
+      if (!this.source || this.source instanceof window.Animation) {
+        this._player = scope.newUnderlyingPlayerForAnimation(this.source);
+        scope.bindPlayerForAnimation(this);
+      }
+      if (this.source instanceof window.AnimationSequence || this.source instanceof window.AnimationGroup) {
+        this._player = scope.newUnderlyingPlayerForGroup(this.source);
+        scope.bindPlayerForGroup(this);
+      }
+
+      // FIXME: move existing currentTime/startTime/playState to new player
+    },
+    get paused() {
+      return this._player.paused;
+    },
+    get playState() {
+      return this._player.playState;
+    },
+    get onfinish() {
+      return this._onfinish;
+    },
+    set onfinish(v) {
+      if (typeof v == 'function') {
+        this._onfinish = v;
+        this._player.onfinish = (function(e) {
+          e.target = this;
+          v.call(this, e);
+        }).bind(this);
+      } else {
+        this._player.onfinish = v;
+        this.onfinish = this._player.onfinish;
+      }
+    },
+    get currentTime() {
+      return this._player.currentTime;
+    },
+    set currentTime(v) {
+      this._player.currentTime = v;
+      this._register();
+      this._forEachChild(function(child, offset) {
+        child.currentTime = v - offset;
+      });
+    },
+    get startTime() {
+      return this._player.startTime;
+    },
+    set startTime(v) {
+      this._player.startTime = v;
+      this._register();
+      this._forEachChild(function(child, offset) {
+        child.startTime = v + offset;
+      });
+    },
+    get playbackRate() {
+      return this._player.playbackRate;
+    },
+    get finished() {
+      return this._player.finished;
+    },
+    play: function() {
+      this._player.play();
+      this._register();
+      scope.awaitStartTime(this);
+      this._forEachChild(function(child) {
+        var time = child.currentTime;
+        child.play();
+        child.currentTime = time;
+      });
+    },
+    pause: function() {
+      this._player.pause();
+      this._register();
+      this._forEachChild(function(child) {
+        child.pause();
+      });
+    },
+    finish: function() {
+      this._player.finish();
+      this._register();
+      // TODO: child players??
+    },
+    cancel: function() {
+      this._player.cancel();
+      this._register();
+      this._removePlayers();
+    },
+    reverse: function() {
+      this._player.reverse();
+      scope.awaitStartTime(this);
+      this._register();
+      this._forEachChild(function(child, offset) {
+        child.reverse();
+        child.startTime = this.startTime + offset * this.playbackRate;
+        child.currentTime = this.currentTime + offset * this.playbackRate;
+      });
+    },
+    addEventListener: function(type, handler) {
+      var wrapped = handler;
+      if (typeof handler == 'function') {
+        wrapped = (function(e) {
+          e.target = this;
+          handler.call(this, e);
+        }).bind(this);
+        handler._wrapper = wrapped;
+      }
+      this._player.addEventListener(type, wrapped);
+    },
+    removeEventListener: function(type, handler) {
+      this._player.removeEventListener(type, (handler && handler._wrapper) || handler);
+    },
+    _removePlayers: function() {
+      while (this._childPlayers.length)
+        this._childPlayers.pop().cancel();
+    },
+    _forEachChild: function(f) {
+      var offset = 0;
+      this._childPlayers.forEach(function(child) {
+        f.call(this, child, offset);
+        if (this.source instanceof window.AnimationSequence)
+          offset += child.source.activeDuration;
+      }.bind(this));
+    },
+  };
+
+})(webAnimationsShared, webAnimationsNext, webAnimationsTesting);
diff --git a/third_party/web-animations-js/sources/target-config.js b/third_party/web-animations-js/sources/target-config.js
new file mode 100644
index 0000000..b42139e
--- /dev/null
+++ b/third_party/web-animations-js/sources/target-config.js
@@ -0,0 +1,124 @@
+(function() {
+
+  var scopeSrc = [
+      'src/scope.js'];
+
+  var webAnimations1Src = [
+      'src/animation-node.js',
+      'src/effect.js',
+      'src/property-interpolation.js',
+      'src/animation.js',
+      'src/apply-preserving-inline-style.js',
+      'src/element-animatable.js',
+      'src/interpolation.js',
+      'src/matrix-interpolation.js',
+      'src/player.js',
+      'src/tick.js',
+      'src/matrix-decomposition.js',
+      'src/handler-utils.js',
+      'src/shadow-handler.js',
+      'src/number-handler.js',
+      'src/visibility-handler.js',
+      'src/color-handler.js',
+      'src/dimension-handler.js',
+      'src/box-handler.js',
+      'src/transform-handler.js',
+      'src/font-weight-handler.js',
+      'src/position-handler.js',
+      'src/shape-handler.js',
+      'src/property-names.js',
+  ];
+
+  var liteWebAnimations1Src = [
+      'src/animation-node.js',
+      'src/effect.js',
+      'src/property-interpolation.js',
+      'src/animation.js',
+      'src/apply.js',
+      'src/element-animatable.js',
+      'src/interpolation.js',
+      'src/player.js',
+      'src/tick.js',
+      'src/handler-utils.js',
+      'src/shadow-handler.js',
+      'src/number-handler.js',
+      'src/visibility-handler.js',
+      'src/color-handler.js',
+      'src/dimension-handler.js',
+      'src/box-handler.js',
+      'src/transform-handler.js',
+      'src/property-names.js',
+  ];
+
+
+  var sharedSrc = [
+      'src/timing-utilities.js',
+      'src/normalize-keyframes.js',
+      'src/deprecation.js',
+  ];
+
+  var webAnimationsNextSrc = [
+      'src/timeline.js',
+      'src/web-animations-next-player.js',
+      'src/animation-constructor.js',
+      'src/effect-callback.js',
+      'src/group-constructors.js'];
+
+  var webAnimations1Test = [
+      'test/js/animation-node.js',
+      'test/js/apply-preserving-inline-style.js',
+      'test/js/box-handler.js',
+      'test/js/color-handler.js',
+      'test/js/dimension-handler.js',
+      'test/js/effect.js',
+      'test/js/interpolation.js',
+      'test/js/matrix-interpolation.js',
+      'test/js/number-handler.js',
+      'test/js/player.js',
+      'test/js/player-finish-event.js',
+      'test/js/property-interpolation.js',
+      'test/js/tick.js',
+      'test/js/timing.js',
+      'test/js/transform-handler.js'];
+
+  var webAnimationsNextTest = webAnimations1Test.concat(
+      'test/js/animation-constructor.js',
+      'test/js/effect-callback.js',
+      'test/js/group-constructors.js',
+      'test/js/group-player.js',
+      'test/js/group-player-finish-event.js',
+      'test/js/timeline.js');
+
+  // This object specifies the source and test files for different Web Animation build targets.
+  var targetConfig = {
+    'web-animations': {
+      scopeSrc: scopeSrc,
+      sharedSrc: sharedSrc,
+      webAnimations1Src: webAnimations1Src,
+      webAnimationsNextSrc: [],
+      src: scopeSrc.concat(sharedSrc).concat(webAnimations1Src),
+      test: webAnimations1Test,
+    },
+    'web-animations-next': {
+      scopeSrc: scopeSrc,
+      sharedSrc: sharedSrc,
+      webAnimations1Src: webAnimations1Src,
+      webAnimationsNextSrc: webAnimationsNextSrc,
+      src: scopeSrc.concat(sharedSrc).concat(webAnimations1Src).concat(webAnimationsNextSrc),
+      test: webAnimationsNextTest,
+    },
+    'web-animations-next-lite': {
+      scopeSrc: scopeSrc,
+      sharedSrc: sharedSrc,
+      webAnimations1Src: liteWebAnimations1Src,
+      webAnimationsNextSrc: webAnimationsNextSrc,
+      src: scopeSrc.concat(sharedSrc).concat(liteWebAnimations1Src).concat(webAnimationsNextSrc),
+      test: [],
+    },
+  };
+
+  if (typeof module != 'undefined')
+    module.exports = targetConfig;
+  else
+    window.webAnimationsTargetConfig = targetConfig;
+})();
diff --git a/third_party/web-animations-js/sources/target-loader.js b/third_party/web-animations-js/sources/target-loader.js
new file mode 100644
index 0000000..a4a9d30f
--- /dev/null
+++ b/third_party/web-animations-js/sources/target-loader.js
@@ -0,0 +1,13 @@
+(function() {
+  var target = webAnimationsTargetConfig.defaultTarget;
+  if (typeof webAnimationsSourceTarget != 'undefined')
+    target = webAnimationsSourceTarget;
+
+  // Native implementation detection.
+
+  var scripts = document.getElementsByTagName('script');
+  var location = scripts[scripts.length - 1].src.replace(/[^\/]+$/, '');
+  webAnimationsTargetConfig[target].src.forEach(function(sourceFile) {
+    document.write('<script src="' + location + sourceFile + '"></script>');
+  });
+})();
diff --git a/third_party/web-animations-js/sources/templates/boilerplate b/third_party/web-animations-js/sources/templates/boilerplate
new file mode 100644
index 0000000..5b902b5
--- /dev/null
+++ b/third_party/web-animations-js/sources/templates/boilerplate
@@ -0,0 +1,13 @@
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+//     You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//     See the License for the specific language governing permissions and
+// limitations under the License.
diff --git a/third_party/web-animations-js/sources/templates/runner.html b/third_party/web-animations-js/sources/templates/runner.html
new file mode 100644
index 0000000..48d5d4c
--- /dev/null
+++ b/third_party/web-animations-js/sources/templates/runner.html
@@ -0,0 +1,26 @@
+<!--
+ Copyright 2014 Google Inc. All rights reserved.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<!DOCTYPE html>
+<meta charset="UTF-8">
+<link rel="stylesheet" href="../node_modules/mocha/mocha.css">
+<script src="../node_modules/mocha/mocha.js"></script>
+<script src="../node_modules/chai/chai.js"></script>
+<script src="../target-config.js"></script>
+<script src="runner.js"></script>
+<script>
+loadWebAnimationsBuildTarget('<%= target %>');
+</script>
+<div id="mocha"></div>
diff --git a/third_party/web-animations-js/sources/templates/web-animations.html b/third_party/web-animations-js/sources/templates/web-animations.html
new file mode 100644
index 0000000..4af996e
--- /dev/null
+++ b/third_party/web-animations-js/sources/templates/web-animations.html
@@ -0,0 +1,18 @@
+<!--
+ Copyright 2014 Google Inc. All rights reserved.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<script src="src/dev.js"></script>
+<% _.forEach(src, function(src) { %><script src="<%= src %>"></script>
+<% }); %>
diff --git a/third_party/web-animations-js/sources/templates/web-animations.js b/third_party/web-animations-js/sources/templates/web-animations.js
new file mode 100644
index 0000000..4a81d99
--- /dev/null
+++ b/third_party/web-animations-js/sources/templates/web-animations.js
@@ -0,0 +1,21 @@
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+//     You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//     See the License for the specific language governing permissions and
+// limitations under the License.
+var webAnimationsSourceTarget = '<%= target %>';
+var WEB_ANIMATIONS_TESTING = false;
+(function() {
+  var scripts = document.getElementsByTagName('script');
+  var location = scripts[scripts.length - 1].src.replace(/[^\/]+$/, '');
+  document.write('<script src="' + location + 'target-config.js"></script>');
+  document.write('<script src="' + location + 'target-loader.js"></script>');
+})();
diff --git a/third_party/web-animations-js/sources/web-animations-next-lite.dev.html b/third_party/web-animations-js/sources/web-animations-next-lite.dev.html
new file mode 100644
index 0000000..82bb8aeb
--- /dev/null
+++ b/third_party/web-animations-js/sources/web-animations-next-lite.dev.html
@@ -0,0 +1,44 @@
+<!--
+ Copyright 2014 Google Inc. All rights reserved.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<script src="src/dev.js"></script>
+<script src="src/scope.js"></script>
+<script src="src/timing-utilities.js"></script>
+<script src="src/normalize-keyframes.js"></script>
+<script src="src/deprecation.js"></script>
+<script src="src/animation-node.js"></script>
+<script src="src/effect.js"></script>
+<script src="src/property-interpolation.js"></script>
+<script src="src/animation.js"></script>
+<script src="src/apply.js"></script>
+<script src="src/element-animatable.js"></script>
+<script src="src/interpolation.js"></script>
+<script src="src/player.js"></script>
+<script src="src/tick.js"></script>
+<script src="src/handler-utils.js"></script>
+<script src="src/shadow-handler.js"></script>
+<script src="src/number-handler.js"></script>
+<script src="src/visibility-handler.js"></script>
+<script src="src/color-handler.js"></script>
+<script src="src/dimension-handler.js"></script>
+<script src="src/box-handler.js"></script>
+<script src="src/transform-handler.js"></script>
+<script src="src/property-names.js"></script>
+<script src="src/timeline.js"></script>
+<script src="src/web-animations-next-player.js"></script>
+<script src="src/animation-constructor.js"></script>
+<script src="src/effect-callback.js"></script>
+<script src="src/group-constructors.js"></script>
+
diff --git a/third_party/web-animations-js/sources/web-animations-next-lite.dev.js b/third_party/web-animations-js/sources/web-animations-next-lite.dev.js
new file mode 100644
index 0000000..8cc77540
--- /dev/null
+++ b/third_party/web-animations-js/sources/web-animations-next-lite.dev.js
@@ -0,0 +1,21 @@
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+//     You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//     See the License for the specific language governing permissions and
+// limitations under the License.
+var webAnimationsSourceTarget = 'web-animations-next-lite';
+var WEB_ANIMATIONS_TESTING = false;
+(function() {
+  var scripts = document.getElementsByTagName('script');
+  var location = scripts[scripts.length - 1].src.replace(/[^\/]+$/, '');
+  document.write('<script src="' + location + 'target-config.js"></script>');
+  document.write('<script src="' + location + 'target-loader.js"></script>');
+})();
diff --git a/third_party/web-animations-js/sources/web-animations-next-lite.min.js b/third_party/web-animations-js/sources/web-animations-next-lite.min.js
new file mode 100644
index 0000000..f8a798fb
--- /dev/null
+++ b/third_party/web-animations-js/sources/web-animations-next-lite.min.js
@@ -0,0 +1,17 @@
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+//     You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//     See the License for the specific language governing permissions and
+// limitations under the License.
+
+!function(a,b){b["true"]=a;var c={},d={},e={},f=null;!function(a){function b(b,c){var d={delay:0,endDelay:0,fill:c?"both":"none",iterationStart:0,iterations:1,duration:c?"auto":0,playbackRate:1,direction:"normal",easing:"linear"};return"number"!=typeof b||isNaN(b)?void 0!==b&&Object.getOwnPropertyNames(b).forEach(function(c){if("auto"!=b[c]){if(("number"==typeof d[c]||"duration"==c)&&("number"!=typeof b[c]||isNaN(b[c])))return;if("fill"==c&&-1==p.indexOf(b[c]))return;if("direction"==c&&-1==q.indexOf(b[c]))return;if("playbackRate"==c&&a.isDeprecated("AnimationTiming.playbackRate","2014-11-28","Use AnimationPlayer.playbackRate instead."))return;d[c]=b[c]}}):d.duration=b,d}function c(a,c){var d=b(a,c);return d.easing=f(d.easing),d}function d(a,b,c,d){return 0>a||a>1||0>c||c>1?y:function(e){function f(a,b,c){return 3*a*(1-c)*(1-c)*c+3*b*(1-c)*c*c+c*c*c}for(var g=0,h=1;;){var i=(g+h)/2,j=f(a,c,i);if(Math.abs(e-j)<.001)return f(b,d,i);e>j?g=i:h=i}}}function e(a,b){return function(c){if(c>=1)return 1;var d=1/a;return c+=b*d,c-c%d}}function f(a){var b=w.exec(a);if(b)return d.apply(this,b.slice(1).map(Number));var c=x.exec(a);if(c)return e(Number(c[1]),{start:r,middle:s,end:t}[c[2]]);var f=u[a];return f?f:y}function g(a){return Math.abs(h(a)/a.playbackRate)}function h(a){return a.duration*a.iterations}function i(a,b,c){return null==b?z:b<c.delay?A:b>=c.delay+a?B:C}function j(a,b,c,d,e){switch(d){case A:return"backwards"==b||"both"==b?0:null;case C:return c-e;case B:return"forwards"==b||"both"==b?a:null;case z:return null}}function k(a,b,c,d){return(d.playbackRate<0?b-a:b)*d.playbackRate+c}function l(a,b,c,d,e){return 1/0===c||c===-1/0||c-d==b&&e.iterations&&(e.iterations+e.iterationStart)%1==0?a:c%a}function m(a,b,c,d){return 0===c?0:b==a?d.iterationStart+d.iterations-1:Math.floor(c/a)}function n(a,b,c,d){var e=a%2>=1,f="normal"==d.direction||d.direction==(e?"alternate-reverse":"alternate"),g=f?c:b-c,h=g/b;return b*d.easing(h)}function o(a,b,c){var d=i(a,b,c),e=j(a,c.fill,b,d,c.delay);if(null===e)return null;if(0===a)return d===A?0:1;var f=c.iterationStart*c.duration,g=k(a,e,f,c),o=l(c.duration,h(c),g,f,c),p=m(c.duration,o,g,c);return n(p,c.duration,o,c)/c.duration}var p="backwards|forwards|both".split("|"),q="reverse|alternate|alternate-reverse".split("|"),r=1,s=.5,t=0,u={ease:d(.25,.1,.25,1),"ease-in":d(.42,0,1,1),"ease-out":d(0,0,.58,1),"ease-in-out":d(.42,0,.58,1),"step-start":e(1,r),"step-middle":e(1,s),"step-end":e(1,t)},v="\\s*(-?\\d+\\.?\\d*|-?\\.\\d+)\\s*",w=new RegExp("cubic-bezier\\("+v+","+v+","+v+","+v+"\\)"),x=/steps\(\s*(\d+)\s*,\s*(start|middle|end)\s*\)/,y=function(a){return a},z=0,A=1,B=2,C=3;a.makeTiming=b,a.normalizeTimingInput=c,a.calculateActiveDuration=g,a.calculateTimeFraction=o,a.calculatePhase=i,a.toTimingFunction=f}(c,f),function(a){function b(a,b){return a in h?h[a][b]||b:b}function c(a,c,d){var g=e[a];if(g){f.style[a]=c;for(var h in g){var i=g[h],j=f.style[i];d[i]=b(i,j)}}else d[a]=b(a,c)}function d(b){function d(){var a=e.length;null==e[a-1].offset&&(e[a-1].offset=1),a>1&&null==e[0].offset&&(e[0].offset=0);for(var b=0,c=e[0].offset,d=1;a>d;d++){var f=e[d].offset;if(null!=f){for(var g=1;d-b>g;g++)e[b+g].offset=c+(f-c)*g/(d-b);b=d,c=f}}}if(!Array.isArray(b)&&null!==b)throw new TypeError("Keyframe effect must be null or an array of keyframes");if(null==b)return[];for(var e=b.map(function(b){var d={};for(var e in b){var f=b[e];if("offset"==e){if(null!=f&&(f=Number(f),!isFinite(f)))throw new TypeError("keyframe offsets must be numbers.")}else{if("composite"==e)throw{type:DOMException.NOT_SUPPORTED_ERR,name:"NotSupportedError",message:"add compositing is not supported"};f="easing"==e?a.toTimingFunction(f):""+f}c(e,f,d)}return void 0==d.offset&&(d.offset=null),void 0==d.easing&&(d.easing=a.toTimingFunction("linear")),d}),f=!0,g=-1/0,h=0;h<e.length;h++){var i=e[h].offset;if(null!=i){if(g>i)throw{code:DOMException.INVALID_MODIFICATION_ERR,name:"InvalidModificationError",message:"Keyframes are not loosely sorted by offset. Sort or specify offsets."};g=i}else f=!1}return e=e.filter(function(a){return a.offset>=0&&a.offset<=1}),f||d(),e}var e={background:["backgroundImage","backgroundPosition","backgroundSize","backgroundRepeat","backgroundAttachment","backgroundOrigin","backgroundClip","backgroundColor"],border:["borderTopColor","borderTopStyle","borderTopWidth","borderRightColor","borderRightStyle","borderRightWidth","borderBottomColor","borderBottomStyle","borderBottomWidth","borderLeftColor","borderLeftStyle","borderLeftWidth"],borderBottom:["borderBottomWidth","borderBottomStyle","borderBottomColor"],borderColor:["borderTopColor","borderRightColor","borderBottomColor","borderLeftColor"],borderLeft:["borderLeftWidth","borderLeftStyle","borderLeftColor"],borderRadius:["borderTopLeftRadius","borderTopRightRadius","borderBottomRightRadius","borderBottomLeftRadius"],borderRight:["borderRightWidth","borderRightStyle","borderRightColor"],borderTop:["borderTopWidth","borderTopStyle","borderTopColor"],borderWidth:["borderTopWidth","borderRightWidth","borderBottomWidth","borderLeftWidth"],flex:["flexGrow","flexShrink","flexBasis"],font:["fontFamily","fontSize","fontStyle","fontVariant","fontWeight","lineHeight"],margin:["marginTop","marginRight","marginBottom","marginLeft"],outline:["outlineColor","outlineStyle","outlineWidth"],padding:["paddingTop","paddingRight","paddingBottom","paddingLeft"]},f=document.createElementNS("http://www.w3.org/1999/xhtml","div"),g={thin:"1px",medium:"3px",thick:"5px"},h={borderBottomWidth:g,borderLeftWidth:g,borderRightWidth:g,borderTopWidth:g,fontSize:{"xx-small":"60%","x-small":"75%",small:"89%",medium:"100%",large:"120%","x-large":"150%","xx-large":"200%"},fontWeight:{normal:"400",bold:"700"},outlineWidth:g,textShadow:{none:"0px 0px 0px transparent"},boxShadow:{none:"0px 0px 0px 0px transparent"}};a.normalizeKeyframes=d}(c,f),function(a){var b={};a.isDeprecated=function(a,c,d,e){var f=e?"are":"is",g=new Date,h=new Date(c);return h.setMonth(h.getMonth()+3),h>g?(a in b||console.warn("Web Animations: "+a+" "+f+" deprecated and will stop working on "+h.toDateString()+". "+d),b[a]=!0,!1):!0},a.deprecated=function(b,c,d,e){if(a.isDeprecated(b,c,d,e))throw new Error(b+" "+auxVerb+" no longer supported. "+d)}}(c),function(){if(document.documentElement.animate){var a=document.documentElement.animate([],0),b=!0;if(a&&(b=!1,"play|currentTime|pause|reverse|playbackRate|cancel|finish|startTime|playState".split("|").forEach(function(c){void 0===a[c]&&(b=!0)})),!b)return}!function(a,b){b.AnimationNode=function(b){var c=a.calculateActiveDuration(b),d=function(d){return a.calculateTimeFraction(c,d,b)};return d._totalDuration=b.delay+c+b.endDelay,d._isCurrent=function(d){var e=a.calculatePhase(c,d,b);return e===PhaseActive||e===PhaseBefore},d}}(c,d),function(a,b){function c(a){for(var b={},c=0;c<a.length;c++)for(var d in a[c])if("offset"!=d&&"easing"!=d&&"composite"!=d){var e={offset:a[c].offset,easing:a[c].easing,value:a[c][d]};b[d]=b[d]||[],b[d].push(e)}for(var f in b){var g=b[f];if(0!=g[0].offset||1!=g[g.length-1].offset)throw{type:DOMException.NOT_SUPPORTED_ERR,name:"NotSupportedError",message:"Partial keyframes are not supported"}}return b}function d(a){var c=[];for(var d in a)for(var e=a[d],f=0;f<e.length-1;f++){var g=e[f].offset,h=e[f+1].offset,i=e[f].value,j=e[f+1].value;g==h&&(1==h?i=j:j=i),c.push({startTime:g,endTime:h,easing:e[f].easing,property:d,interpolation:b.propertyInterpolation(d,i,j)})}return c.sort(function(a,b){return a.startTime-b.startTime}),c}b.convertEffectInput=function(e){var f=a.normalizeKeyframes(e),g=c(f),h=d(g);return function(a,c){if(null!=c)h.filter(function(a){return 0>=c&&0==a.startTime||c>=1&&1==a.endTime||c>=a.startTime&&c<=a.endTime}).forEach(function(d){var e=c-d.startTime,f=d.endTime-d.startTime,g=0==f?0:d.easing(e/f);b.apply(a,d.property,d.interpolation(g))});else for(var d in g)"offset"!=d&&"easing"!=d&&"composite"!=d&&b.clear(a,d)}}}(c,d,f),function(a){function b(a,b,c){e[c]=e[c]||[],e[c].push([a,b])}function c(a,c,d){for(var e=0;e<d.length;e++){var f=d[e];b(a,c,f),/-/.test(f)&&b(a,c,f.replace(/-(.)/g,function(a,b){return b.toUpperCase()}))}}function d(b,c,d){for(var f=c==d?[]:e[b],g=0;f&&g<f.length;g++){var h=f[g][0](c),i=f[g][0](d);if(void 0!==h&&void 0!==i){var j=f[g][1](h,i);if(j){var k=a.Interpolation.apply(null,j);return function(a){return 0==a?c:1==a?d:k(a)}}}}return a.Interpolation(!1,!0,function(a){return a?d:c})}var e={};a.addPropertiesHandler=c,a.propertyInterpolation=d}(d,f),function(a,b){b.Animation=function(c,d,e){var f,g=b.AnimationNode(a.normalizeTimingInput(e)),h=b.convertEffectInput(d),i=function(){h(c,f)};return i._update=function(a){return f=g(a),null!==f},i._clear=function(){h(c,null)},i._hasSameTarget=function(a){return c===a},i._isCurrent=g._isCurrent,i._totalDuration=g._totalDuration,i},b.NullAnimation=function(a){var b=function(){a&&(a(),a=null)};return b._update=function(){return null},b._totalDuration=0,b._isCurrent=function(){return!1},b._hasSameTarget=function(){return!1},b}}(c,d,f),function(a){a.apply=function(b,c,d){b.style[a.propertyName(c)]=d},a.clear=function(b,c){b.style[a.propertyName(c)]=""}}(d,f),function(a){window.Element.prototype.animate=function(b,c){return a.timeline._play(a.Animation(this,b,c))}}(d),function(a){function b(a,c,d){if("number"==typeof a&&"number"==typeof c)return a*(1-d)+c*d;if("boolean"==typeof a&&"boolean"==typeof c)return.5>d?a:c;if(a.length==c.length){for(var e=[],f=0;f<a.length;f++)e.push(b(a[f],c[f],d));return e}throw"Mismatched interpolation arguments "+a+":"+c}a.Interpolation=function(a,c,d){return function(e){return d(b(a,c,e))}}}(d,f),function(a){var b=0,c=function(a,b,c){this.target=a,this.currentTime=b,this.timelineTime=c,this.type="finish",this.bubbles=!1,this.cancelable=!1,this.currentTarget=a,this.defaultPrevented=!1,this.eventPhase=Event.AT_TARGET,this.timeStamp=Date.now()};a.Player=function(a){this._sequenceNumber=b++,this._currentTime=0,this._startTime=null,this.paused=!1,this._playbackRate=1,this._inTimeline=!0,this._finishedFlag=!1,this.onfinish=null,this._finishHandlers=[],this._source=a,this._inEffect=this._source._update(0),this._idle=!0,this._currentTimePending=!1},a.Player.prototype={_ensureAlive:function(){this._inEffect=this._source._update(this.currentTime),this._inTimeline||!this._inEffect&&this._finishedFlag||(this._inTimeline=!0,a.timeline._players.push(this))},_tickCurrentTime:function(a,b){a!=this._currentTime&&(this._currentTime=a,this.finished&&!b&&(this._currentTime=this._playbackRate>0?this._totalDuration:0),this._ensureAlive())},get currentTime(){return this._idle||this._currentTimePending?null:this._currentTime},set currentTime(b){b=+b,isNaN(b)||(a.restart(),this.paused||null==this._startTime||(this._startTime=this._timeline.currentTime-b/this._playbackRate),this._currentTimePending=!1,this._currentTime!=b&&(this._tickCurrentTime(b,!0),a.invalidateEffects()))},get startTime(){return this._startTime},set startTime(b){b=+b,isNaN(b)||this.paused||this._idle||(this._startTime=b,this._tickCurrentTime((this._timeline.currentTime-this._startTime)*this.playbackRate),a.invalidateEffects())},get playbackRate(){return this._playbackRate},get finished(){return!this._idle&&(this._playbackRate>0&&this._currentTime>=this._totalDuration||this._playbackRate<0&&this._currentTime<=0)},get _totalDuration(){return this._source._totalDuration},get playState(){return this._idle?"idle":null==this._startTime&&!this.paused&&0!=this.playbackRate||this._currentTimePending?"pending":this.paused?"paused":this.finished?"finished":"running"},play:function(){this.paused=!1,(this.finished||this._idle)&&(this._currentTime=this._playbackRate>0?0:this._totalDuration,this._startTime=null,a.invalidateEffects()),this._finishedFlag=!1,a.restart(),this._idle=!1,this._ensureAlive()},pause:function(){this.finished||this.paused||this._idle||(this._currentTimePending=!0),this._startTime=null,this.paused=!0},finish:function(){this._idle||(this.currentTime=this._playbackRate>0?this._totalDuration:0,this._startTime=this._totalDuration-this.currentTime,this._currentTimePending=!1)},cancel:function(){this._inEffect=!1,this._idle=!0,this.currentTime=0,this._startTime=null},reverse:function(){this._playbackRate*=-1,this._startTime=null,this.play()},addEventListener:function(a,b){"function"==typeof b&&"finish"==a&&this._finishHandlers.push(b)},removeEventListener:function(a,b){if("finish"==a){var c=this._finishHandlers.indexOf(b);c>=0&&this._finishHandlers.splice(c,1)}},_fireEvents:function(a){var b=this.finished;if((b||this._idle)&&!this._finishedFlag){var d=new c(this,this._currentTime,a),e=this._finishHandlers.concat(this.onfinish?[this.onfinish]:[]);setTimeout(function(){e.forEach(function(a){a.call(d.target,d)})},0)}this._finishedFlag=b},_tick:function(a){return this._idle||this.paused||(null==this._startTime?this.startTime=a-this._currentTime/this.playbackRate:this.finished||this._tickCurrentTime((a-this._startTime)*this.playbackRate)),this._currentTimePending=!1,this._fireEvents(a),!this._idle&&(this._inEffect||!this._finishedFlag)}}}(d,f),function(a,b){function c(a){var b=i;i=[],g(a),b.forEach(function(b){b[1](a)}),m&&g(a),f()}function d(a,b){return a._sequenceNumber-b._sequenceNumber}function e(){this._players=[],this.currentTime=window.performance&&performance.now?performance.now():0}function f(){n.forEach(function(a){a()})}function g(a){l=!1;var c=b.timeline;c.currentTime=a,c._players.sort(d),k=!1;var e=c._players;c._players=[];var f=[],g=[];e=e.filter(function(b){return b._inTimeline=b._tick(a),b._inEffect?g.push(b._source):f.push(b._source),b.finished||b.paused||b._idle||(k=!0),b._inTimeline}),n.length=0,n.push.apply(n,f),n.push.apply(n,g),c._players.push.apply(c._players,e),m=!1,k&&requestAnimationFrame(function(){})}var h=window.requestAnimationFrame,i=[],j=0;window.requestAnimationFrame=function(a){var b=j++;return 0==i.length&&h(c),i.push([b,a]),b},window.cancelAnimationFrame=function(a){i.forEach(function(b){b[0]==a&&(b[1]=function(){})})},e.prototype={_play:function(c){c._timing=a.normalizeTimingInput(c.timing);var d=new b.Player(c);return d._idle=!1,d._timeline=this,this._players.push(d),b.restart(),b.invalidateEffects(),d}};var k=!1,l=!1;b.restart=function(){return k||(k=!0,requestAnimationFrame(function(){}),l=!0),l};var m=!1;b.invalidateEffects=function(){m=!0};var n=[],o=window.getComputedStyle;Object.defineProperty(window,"getComputedStyle",{configurable:!0,enumerable:!0,value:function(){return m&&g(p.currentTime),f(),o.apply(this,arguments)}});var p=new e;b.timeline=p}(c,d,f),function(a){function b(a,b){var c=a.exec(b);return c?(c=a.ignoreCase?c[0].toLowerCase():c[0],[c,b.substr(c.length)]):void 0}function c(a,b){b=b.replace(/^\s*/,"");var c=a(b);return c?[c[0],c[1].replace(/^\s*/,"")]:void 0}function d(a,d,e){a=c.bind(null,a);for(var f=[];;){var g=a(e);if(!g)return[f,e];if(f.push(g[0]),e=g[1],g=b(d,e),!g||""==g[1])return[f,e];e=g[1]}}function e(a,b){for(var c=0,d=0;d<b.length&&(!/\s|,/.test(b[d])||0!=c);d++)if("("==b[d])c++;else if(")"==b[d]&&(c--,0==c&&d++,0>=c))break;var e=a(b.substr(0,d));return void 0==e?void 0:[e,b.substr(d)]}function f(a,b){for(var c=a,d=b;c&&d;)c>d?c%=d:d%=c;return c=a*b/(c+d)}function g(a){return function(b){var c=a(b);return c&&(c[0]=void 0),c}}function h(a,b){return function(c){var d=a(c);return d?d:[b,c]}}function i(b,c){for(var d=[],e=0;e<b.length;e++){var f=a.consumeTrimmed(b[e],c);if(!f||""==f[0])return;void 0!==f[0]&&d.push(f[0]),c=f[1]}return""==c?d:void 0}function j(a,b,c,d,e){for(var g=[],h=[],i=[],j=f(d.length,e.length),k=0;j>k;k++){var l=b(d[k%d.length],e[k%e.length]);if(!l)return;g.push(l[0]),h.push(l[1]),i.push(l[2])}return[g,h,function(b){var d=b.map(function(a,b){return i[b](a)}).join(c);return a?a(d):d}]}function k(a,b,c){for(var d=[],e=[],f=[],g=0,h=0;h<c.length;h++)if("function"==typeof c[h]){var i=c[h](a[g],b[g++]);d.push(i[0]),e.push(i[1]),f.push(i[2])}else!function(a){d.push(!1),e.push(!1),f.push(function(){return c[a]})}(h);return[d,e,function(a){for(var b="",c=0;c<a.length;c++)b+=f[c](a[c]);return b}]}a.consumeToken=b,a.consumeTrimmed=c,a.consumeRepeated=d,a.consumeParenthesised=e,a.ignore=g,a.optional=h,a.consumeList=i,a.mergeNestedRepeated=j.bind(null,null),a.mergeWrappedNestedRepeated=j,a.mergeList=k}(d),function(a){function b(b){function c(b){var c=a.consumeToken(/^inset/i,b);if(c)return d.inset=!0,c;var c=a.consumeLengthOrPercent(b);if(c)return d.lengths.push(c[0]),c;var c=a.consumeColor(b);return c?(d.color=c[0],c):void 0}var d={inset:!1,lengths:[],color:null},e=a.consumeRepeated(c,/^/,b);return e&&e[0].length?[d,e[1]]:void 0}function c(c){var d=a.consumeRepeated(b,/^,/,c);return d&&""==d[1]?d[0]:void 0}function d(b,c){for(;b.lengths.length<Math.max(b.lengths.length,c.lengths.length);)b.lengths.push({px:0});for(;c.lengths.length<Math.max(b.lengths.length,c.lengths.length);)c.lengths.push({px:0});if(b.inset==c.inset&&!!b.color==!!c.color){for(var d,e=[],f=[[],0],g=[[],0],h=0;h<b.lengths.length;h++){var i=a.mergeDimensions(b.lengths[h],c.lengths[h],2==h);f[0].push(i[0]),g[0].push(i[1]),e.push(i[2])}if(b.color&&c.color){var j=a.mergeColors(b.color,c.color);f[1]=j[0],g[1]=j[1],d=j[2]}return[f,g,function(a){for(var c=b.inset?"inset ":" ",f=0;f<e.length;f++)c+=e[f](a[0][f])+" ";return d&&(c+=d(a[1])),c}]}}function e(b,c,d,e){function f(a){return{inset:a,color:[0,0,0,0],lengths:[{px:0},{px:0},{px:0},{px:0}]}}for(var g=[],h=[],i=0;i<d.length||i<e.length;i++){var j=d[i]||f(e[i].inset),k=e[i]||f(d[i].inset);g.push(j),h.push(k)}return a.mergeNestedRepeated(b,c,g,h)}var f=e.bind(null,d,", ");a.addPropertiesHandler(c,f,["box-shadow","text-shadow"])}(d),function(a){function b(a){return a.toFixed(3).replace(".000","")}function c(a,b,c){return Math.min(b,Math.max(a,c))}function d(a){return/^\s*[-+]?(\d*\.)?\d+\s*$/.test(a)?Number(a):void 0}function e(a,c){return[a,c,b]}function f(a,b){return 0!=a?h(0,1/0)(a,b):void 0}function g(a,b){return[a,b,function(a){return Math.round(c(1,1/0,a))}]}function h(a,d){return function(e,f){return[e,f,function(e){return b(c(a,d,e))}]}}function i(a,b){return[a,b,Math.round]}a.clamp=c,a.addPropertiesHandler(d,h(0,1/0),["border-image-width","line-height"]),a.addPropertiesHandler(d,h(0,1),["opacity","shape-image-threshold"]),a.addPropertiesHandler(d,h(.01,1/0),["zoom"]),a.addPropertiesHandler(d,f,["flex-grow","flex-shrink"]),a.addPropertiesHandler(d,e,["zoom"]),a.addPropertiesHandler(d,g,["orphans","widows"]),a.addPropertiesHandler(d,i,["z-index"]),a.parseNumber=d,a.mergeNumbers=e,a.numberToString=b}(d,f),function(a){function b(a,b){return"visible"==a||"visible"==b?[0,1,function(c){return 0>=c?a:c>=1?b:"visible"}]:void 0}a.addPropertiesHandler(String,b,["visibility"])}(d),function(a){function b(a){a=a.trim(),e.fillStyle="#000",e.fillStyle=a;var b=e.fillStyle;if(e.fillStyle="#fff",e.fillStyle=a,b==e.fillStyle){e.fillRect(0,0,1,1);var c=e.getImageData(0,0,1,1).data;e.clearRect(0,0,1,1);var d=c[3]/255;return[c[0]*d,c[1]*d,c[2]*d,d]}}function c(b,c){return[b,c,function(b){function c(a){return Math.max(0,Math.min(255,a))}if(b[3])for(var d=0;3>d;d++)b[d]=Math.round(c(b[d]/b[3]));return b[3]=a.numberToString(a.clamp(0,1,b[3])),"rgba("+b.join(",")+")"}]}var d=document.createElementNS("http://www.w3.org/1999/xhtml","canvas");d.width=d.height=1;var e=d.getContext("2d");a.addPropertiesHandler(b,c,["background-color","border-bottom-color","border-left-color","border-right-color","border-top-color","color","outline-color","text-decoration-color"]),a.consumeColor=a.consumeParenthesised.bind(null,b),a.mergeColors=c}(d,f),function(a,b){function c(a,b){if(b=b.trim().toLowerCase(),"0"==b&&"px".search(a)>=0)return{px:0};if(/^[^(]*$|^calc/.test(b)){b=b.replace(/calc\(/g,"(");var c={};b=b.replace(a,function(a){return c[a]=null,"U"+a});for(var d="U("+a.source+")",e=b.replace(/[-+]?(\d*\.)?\d+/g,"N").replace(new RegExp("N"+d,"g"),"D").replace(/\s[+-]\s/g,"O").replace(/\s/g,""),f=[/N\*(D)/g,/(N|D)[*/]N/g,/(N|D)O\1/g,/\((N|D)\)/g],g=0;g<f.length;)f[g].test(e)?(e=e.replace(f[g],"$1"),g=0):g++;if("D"==e){for(var h in c){var i=eval(b.replace(new RegExp("U"+h,"g"),"").replace(new RegExp(d,"g"),"*0"));if(!isFinite(i))return;c[h]=i}return c}}}function d(a,b){return e(a,b,!0)}function e(b,c,d){var e,f=[];for(e in b)f.push(e);for(e in c)f.indexOf(e)<0&&f.push(e);return b=f.map(function(a){return b[a]||0}),c=f.map(function(a){return c[a]||0}),[b,c,function(b){var c=b.map(function(c,e){return 1==b.length&&d&&(c=Math.max(c,0)),a.numberToString(c)+f[e]}).join(" + ");return b.length>1?"calc("+c+")":c}]}var f="px|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc",g=c.bind(null,new RegExp(f,"g")),h=c.bind(null,new RegExp(f+"|%","g")),i=c.bind(null,/deg|rad|grad|turn/g);a.parseLength=g,a.parseLengthOrPercent=h,a.consumeLengthOrPercent=a.consumeParenthesised.bind(null,h),a.parseAngle=i,a.mergeDimensions=e;var j=a.consumeParenthesised.bind(null,g),k=a.consumeRepeated.bind(void 0,j,/^/),l=a.consumeRepeated.bind(void 0,k,/^,/);a.consumeSizePairList=l;var m=function(a){var b=l(a);return b&&""==b[1]?b[0]:void 0},n=a.mergeNestedRepeated.bind(void 0,d," "),o=a.mergeNestedRepeated.bind(void 0,n,",");a.mergeNonNegativeSizePair=n,a.addPropertiesHandler(m,o,["background-size"]),a.addPropertiesHandler(h,d,["border-bottom-width","border-image-width","border-left-width","border-right-width","border-top-width","flex-basis","font-size","height","line-height","max-height","max-width","outline-width","width"]),a.addPropertiesHandler(h,e,["border-bottom-left-radius","border-bottom-right-radius","border-top-left-radius","border-top-right-radius","bottom","left","letter-spacing","margin-bottom","margin-left","margin-right","margin-top","min-height","min-width","outline-offset","padding-bottom","padding-left","padding-right","padding-top","perspective","right","shape-margin","text-indent","top","vertical-align","word-spacing"])}(d,f),function(a){function b(b){return a.consumeLengthOrPercent(b)||a.consumeToken(/^auto/,b)}function c(c){var d=a.consumeList([a.ignore(a.consumeToken.bind(null,/^rect/)),a.ignore(a.consumeToken.bind(null,/^\(/)),a.consumeRepeated.bind(null,b,/^,/),a.ignore(a.consumeToken.bind(null,/^\)/))],c);return d&&4==d[0].length?d[0]:void 0}function d(b,c){return"auto"==b||"auto"==c?[!0,!1,function(d){var e=d?b:c;if("auto"==e)return"auto";var f=a.mergeDimensions(e,e);return f[2](f[0])}]:a.mergeDimensions(b,c)}function e(a){return"rect("+a+")"}var f=a.mergeWrappedNestedRepeated.bind(null,e,d,", ");a.parseBox=c,a.mergeBoxes=f,a.addPropertiesHandler(c,f,["clip"])}(d,f),function(a){function b(a){return function(b){var c=0;return a.map(function(a){return a===j?b[c++]:a})}}function c(a){return a}function d(b){if(b=b.toLowerCase().trim(),"none"==b)return[];for(var c,d=/\s*(\w+)\(([^)]*)\)/g,e=[],f=0;c=d.exec(b);){if(c.index!=f)return;f=c.index+c[0].length;var g=c[1],h=m[g];if(!h)return;var i=c[2].split(","),j=h[0];if(j.length<i.length)return;for(var n=[],o=0;o<j.length;o++){var p,q=i[o],r=j[o];if(p=q?{A:function(b){return"0"==b.trim()?l:a.parseAngle(b)},N:a.parseNumber,T:a.parseLengthOrPercent,L:a.parseLength}[r.toUpperCase()](q):{a:l,n:n[0],t:k}[r],void 0===p)return;n.push(p)}if(e.push({t:g,d:n}),d.lastIndex==b.length)return e}}function e(a){return a.toFixed(6).replace(".000000","")}function f(b,c){if(b.decompositionPair!==c){b.decompositionPair=c;var d=a.makeMatrixDecomposition(b)}if(c.decompositionPair!==b){c.decompositionPair=b;var f=a.makeMatrixDecomposition(c)}return null==d[0]||null==f[0]?[[!1],[!0],function(a){return a?c[0].d:b[0].d}]:(d[0].push(0),f[0].push(1),[d,f,function(b){var c=a.quat(d[0][3],f[0][3],b[5]),g=a.composeMatrix(b[0],b[1],b[2],c,b[4]),h=g.map(e).join(",");return h}])}function g(a){return a.replace(/[xy]/,"")}function h(a){return a.replace(/(x|y|z|3d)?$/,"3d")}function i(b,c){var d=a.makeMatrixDecomposition&&!0,e=!1;if(!b.length||!c.length){b.length||(e=!0,b=c,c=[]);for(var i=0;i<b.length;i++){var j=b[i].t,k=b[i].d,l="scale"==j.substr(0,5)?1:0;c.push({t:j,d:k.map(function(a){if("number"==typeof a)return l;var b={};for(var c in a)b[c]=l;return b})})}}var n=function(a,b){return"perspective"==a&&"perspective"==b||("matrix"==a||"matrix3d"==a)&&("matrix"==b||"matrix3d"==b)},o=[],p=[],q=[];if(b.length!=c.length){if(!d)return;var r=f(b,c);o=[r[0]],p=[r[1]],q=[["matrix",[r[2]]]]}else for(var i=0;i<b.length;i++){var j,s=b[i].t,t=c[i].t,u=b[i].d,v=c[i].d,w=m[s],x=m[t];if(n(s,t)){if(!d)return;var r=f([b[i]],[c[i]]);o.push(r[0]),p.push(r[1]),q.push(["matrix",[r[2]]])}else{if(s==t)j=s;else if(w[2]&&x[2]&&g(s)==g(t))j=g(s),u=w[2](u),v=x[2](v);else{if(!w[1]||!x[1]||h(s)!=h(t)){if(!d)return;var r=f(b,c);o=[r[0]],p=[r[1]],q=[["matrix",[r[2]]]];break}j=h(s),u=w[1](u),v=x[1](v)}for(var y=[],z=[],A=[],B=0;B<u.length;B++){var C="number"==typeof u[B]?a.mergeNumbers:a.mergeDimensions,r=C(u[B],v[B]);y[B]=r[0],z[B]=r[1],A.push(r[2])}o.push(y),p.push(z),q.push([j,A])}}if(e){var D=o;o=p,p=D}return[o,p,function(a){return a.map(function(a,b){var c=a.map(function(a,c){return q[b][1][c](a)}).join(",");return"matrix"==q[b][0]&&16==c.split(",").length&&(q[b][0]="matrix3d"),q[b][0]+"("+c+")"}).join(" ")}]}var j=null,k={px:0},l={deg:0},m={matrix:["NNNNNN",[j,j,0,0,j,j,0,0,0,0,1,0,j,j,0,1],c],matrix3d:["NNNNNNNNNNNNNNNN",c],rotate:["A"],rotatex:["A"],rotatey:["A"],rotatez:["A"],rotate3d:["NNNA"],perspective:["L"],scale:["Nn",b([j,j,1]),c],scalex:["N",b([j,1,1]),b([j,1])],scaley:["N",b([1,j,1]),b([1,j])],scalez:["N",b([1,1,j])],scale3d:["NNN",c],skew:["Aa",null,c],skewx:["A",null,b([j,l])],skewy:["A",null,b([l,j])],translate:["Tt",b([j,j,k]),c],translatex:["T",b([j,k,k]),b([j,k])],translatey:["T",b([k,j,k]),b([k,j])],translatez:["L",b([k,k,j])],translate3d:["TTL",c]};a.addPropertiesHandler(d,i,["transform"])}(d,f),function(a){function b(a,b){b.concat([a]).forEach(function(b){b in document.documentElement.style&&(c[a]=b)})}var c={};b("transform",["webkitTransform","msTransform"]),b("transformOrigin",["webkitTransformOrigin"]),b("perspective",["webkitPerspective"]),b("perspectiveOrigin",["webkitPerspectiveOrigin"]),a.propertyName=function(a){return c[a]||a}}(d,f)}(),!function(a,b){function c(a){var b=window.document.timeline;b.currentTime=a,b._discardPlayers(),0==b._players.length?d=!1:requestAnimationFrame(c)}b.AnimationTimeline=function(){this._players=[],this.currentTime=void 0},b.AnimationTimeline.prototype={getAnimationPlayers:function(){return this._discardPlayers(),this._players.slice()},_discardPlayers:function(){this._players=this._players.filter(function(a){return"finished"!=a.playState&&"idle"!=a.playState})},play:function(a){var c=new b.Player(a);return this._players.push(c),b.restartWebAnimationsNextTick(),c.play(),c}};var d=!1;b.restartWebAnimationsNextTick=function(){d||(d=!0,requestAnimationFrame(c))};var e=new b.AnimationTimeline;b.timeline=e;try{Object.defineProperty(window.document,"timeline",{configurable:!0,get:function(){return e}})}catch(f){}try{window.document.timeline=e}catch(f){}}(c,e,f),function(a,b){b.Player=function(a){this.source=a,a&&(a.player=this),this._isGroup=!1,this._player=null,this._childPlayers=[],this._callback=null,this._rebuildUnderlyingPlayer(),this._player.cancel()},b.Player.prototype={_rebuildUnderlyingPlayer:function(){this._player&&(this._player.cancel(),this._player=null),(!this.source||this.source instanceof window.Animation)&&(this._player=b.newUnderlyingPlayerForAnimation(this.source),b.bindPlayerForAnimation(this)),(this.source instanceof window.AnimationSequence||this.source instanceof window.AnimationGroup)&&(this._player=b.newUnderlyingPlayerForGroup(this.source),b.bindPlayerForGroup(this))},get paused(){return this._player.paused},get playState(){return this._player.playState},get onfinish(){return this._onfinish},set onfinish(a){"function"==typeof a?(this._onfinish=a,this._player.onfinish=function(b){b.target=this,a.call(this,b)}.bind(this)):(this._player.onfinish=a,this.onfinish=this._player.onfinish)},get currentTime(){return this._player.currentTime},set currentTime(a){this._player.currentTime=a,this._register(),this._forEachChild(function(b,c){b.currentTime=a-c})},get startTime(){return this._player.startTime},set startTime(a){this._player.startTime=a,this._register(),this._forEachChild(function(b,c){b.startTime=a+c})},get playbackRate(){return this._player.playbackRate},get finished(){return this._player.finished},play:function(){this._player.play(),this._register(),b.awaitStartTime(this),this._forEachChild(function(a){var b=a.currentTime;a.play(),a.currentTime=b})},pause:function(){this._player.pause(),this._register(),this._forEachChild(function(a){a.pause()})},finish:function(){this._player.finish(),this._register()},cancel:function(){this._player.cancel(),this._register(),this._removePlayers()},reverse:function(){this._player.reverse(),b.awaitStartTime(this),this._register(),this._forEachChild(function(a,b){a.reverse(),a.startTime=this.startTime+b*this.playbackRate,a.currentTime=this.currentTime+b*this.playbackRate})},addEventListener:function(a,b){var c=b;"function"==typeof b&&(c=function(a){a.target=this,b.call(this,a)}.bind(this),b._wrapper=c),this._player.addEventListener(a,c)},removeEventListener:function(a,b){this._player.removeEventListener(a,b&&b._wrapper||b)},_removePlayers:function(){for(;this._childPlayers.length;)this._childPlayers.pop().cancel()},_forEachChild:function(a){var b=0;this._childPlayers.forEach(function(c){a.call(this,c,b),this.source instanceof window.AnimationSequence&&(b+=c.source.activeDuration)}.bind(this))}}}(c,e,f),function(a,b){function c(a){return a._timing.delay+a.activeDuration+a._timing.endDelay}function d(b){this._frames=a.normalizeKeyframes(b)}function e(){for(var a=!1;h.length;)h.shift()._updateChildren(),a=!0;return a}d.prototype={getFrames:function(){return this._frames}},b.Animation=function(b,c,e){return this.target=b,this._timingInput=e,this._timing=a.normalizeTimingInput(e),this.timing=a.makeTiming(e),this.effect="function"==typeof c?c:new d(c),this._effect=c,this.activeDuration=a.calculateActiveDuration(this._timing),this};var f=Element.prototype.animate;Element.prototype.animate=function(a,c){return b.timeline.play(new b.Animation(this,a,c))};var g=document.createElementNS("http://www.w3.org/1999/xhtml","div");b.newUnderlyingPlayerForAnimation=function(a){var b=a.target||g,c=a._effect;return"function"==typeof c&&(c=[]),f.apply(b,[c,a._timingInput])},b.bindPlayerForAnimation=function(a){a.source&&"function"==typeof a.source.effect&&b.bindPlayerForCustomEffect(a)};var h=[];b.awaitStartTime=function(a){null===a.startTime&&a._isGroup&&(0==h.length&&requestAnimationFrame(e),h.push(a))};var i=window.getComputedStyle;Object.defineProperty(window,"getComputedStyle",{configurable:!0,enumerable:!0,value:function(){var a=i.apply(this,arguments);return e()&&(a=i.apply(this,arguments)),a}}),b.Player.prototype._updateChildren=function(){if(!this.paused&&this.source&&this._isGroup)for(var a=this.source._timing.delay,b=0;b<this.source.children.length;b++){var d,e=this.source.children[b];b>=this._childPlayers.length?(d=window.document.timeline.play(e),this._childPlayers.push(d)):d=this._childPlayers[b],e.player=this.source.player,d.startTime!=this.startTime+a&&(null===this.startTime?(d.currentTime=this.source.player.currentTime-a,d._startTime=null):d.startTime=this.startTime+a,d._updateChildren()),-1==this.playbackRate&&this.currentTime<a&&-1!==d.currentTime&&(d.currentTime=-1),this.source instanceof window.AnimationSequence&&(a+=c(e))}},window.Animation=b.Animation,window.Element.prototype.getAnimationPlayers=function(){return document.timeline.getAnimationPlayers().filter(function(a){return null!==a.source&&a.source.target==this}.bind(this))},b.groupChildDuration=c}(c,e,f),function(a,b){function c(a){a._registered||(a._registered=!0,f.push(a),g||(g=!0,requestAnimationFrame(d)))}function d(){var a=f;f=[],a.sort(function(a,b){return a._sequenceNumber-b._sequenceNumber}),a.filter(function(a){return a(),(!a._player||a._player.finished||a._player.paused)&&(a._registered=!1),a._registered
+}),f.push.apply(f,a),f.length?(g=!0,requestAnimationFrame(d)):g=!1}var e=(document.createElementNS("http://www.w3.org/1999/xhtml","div"),0);b.bindPlayerForCustomEffect=function(b){var d=b.source.target,f=b.source.effect,g=b.source.timing,h=void 0;g=a.normalizeTimingInput(g);var i=function(){var c=i._player?i._player.currentTime:null;null!==c&&(c=a.calculateTimeFraction(a.calculateActiveDuration(g),c,g),isNaN(c)&&(c=null)),c!==h&&f(c,d,b.source),h=c};i._player=b,i._registered=!1,i._sequenceNumber=e++,b._callback=i,c(i)};var f=[],g=!1;b.Player.prototype._register=function(){this._callback&&c(this._callback)}}(c,e,f),function(a,b){function c(b,c){this.children=b||[],this._timing=a.normalizeTimingInput(c,!0),this.timing=a.makeTiming(c,!0),"auto"===this._timing.duration&&(this._timing.duration=this.activeDuration)}window.AnimationSequence=function(){c.apply(this,arguments)},window.AnimationGroup=function(){c.apply(this,arguments)},window.AnimationSequence.prototype={get activeDuration(){var a=0;return this.children.forEach(function(c){a+=b.groupChildDuration(c)}),Math.max(a,0)}},window.AnimationGroup.prototype={get activeDuration(){var a=0;return this.children.forEach(function(c){a=Math.max(a,b.groupChildDuration(c))}),a}},b.newUnderlyingPlayerForGroup=function(a){var c,d=function(a){var b=c._wrapper;return b.source?null==a?void b._removePlayers():void(null!==b.startTime&&b._updateChildren()):void 0};return c=b.timeline.play(new b.Animation(null,d,a._timing))},b.bindPlayerForGroup=function(a){a._player._wrapper=a,a._isGroup=!0,b.awaitStartTime(a),a._updateChildren()}}(c,e,f)}({},function(){return this}());
+//# sourceMappingURL=web-animations-next-lite.min.js.map
\ No newline at end of file
diff --git a/third_party/web-animations-js/sources/web-animations-next-lite.min.js.map b/third_party/web-animations-js/sources/web-animations-next-lite.min.js.map
new file mode 100644
index 0000000..ae36f0e1
--- /dev/null
+++ b/third_party/web-animations-js/sources/web-animations-next-lite.min.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"web-animations-next-lite.min.js","sources":["src/scope.js","src/timing-utilities.js","src/normalize-keyframes.js","src/deprecation.js","src/timeline.js","src/web-animations-next-player.js","src/animation-constructor.js","src/effect-callback.js","src/group-constructors.js"],"names":["webAnimationsShared","webAnimations1","webAnimationsNext","webAnimationsTesting","shared","makeTiming","timingInput","forGroup","timing","delay","endDelay","fill","iterationStart","iterations","duration","playbackRate","direction","easing","isNaN","undefined","Object","getOwnPropertyNames","forEach","property","fills","indexOf","directions","isDeprecated","normalizeTimingInput","toTimingFunction","cubic","a","b","c","d","linear","x","f","m","start","end","mid","xEst","Math","abs","step","count","pos","stepSize","cubicData","cubicBezierRe","exec","apply","this","slice","map","Number","stepData","stepRe","Start","middle","Middle","End","preset","presets","calculateActiveDuration","repeatedDuration","calculatePhase","activeDuration","localTime","PhaseNone","PhaseBefore","PhaseAfter","PhaseActive","calculateActiveTime","fillMode","phase","calculateScaledActiveTime","activeTime","startOffset","calculateIterationTime","iterationDuration","scaledActiveTime","Infinity","calculateCurrentIteration","iterationTime","floor","calculateTransformedTime","currentIteration","currentIterationIsOdd","currentDirectionIsForwards","directedTime","timeFraction","calculateTimeFraction","split","ease","ease-in","ease-out","ease-in-out","step-start","step-middle","step-end","numberString","RegExp","antiAlias","value","aliases","expandShorthandAndAntiAlias","result","longProperties","shorthandToLonghand","shorthandExpanderElem","style","i","longProperty","longhandValue","normalizeKeyframes","effectInput","spaceKeyframes","length","keyframeEffect","offset","previousIndex","previousOffset","j","Array","isArray","TypeError","originalKeyframe","keyframe","member","memberValue","isFinite","type","DOMException","NOT_SUPPORTED_ERR","name","message","everyFrameHasOffset","code","INVALID_MODIFICATION_ERR","filter","background","border","borderBottom","borderColor","borderLeft","borderRadius","borderRight","borderTop","borderWidth","flex","font","margin","outline","padding","document","createElementNS","borderWidthAliases","thin","medium","thick","borderBottomWidth","borderLeftWidth","borderRightWidth","borderTopWidth","fontSize","xx-small","x-small","small","large","x-large","xx-large","fontWeight","normal","bold","outlineWidth","textShadow","none","boxShadow","silenced","feature","date","advice","plural","auxVerb","today","Date","expiry","setMonth","getMonth","console","warn","toDateString","deprecated","Error","scope","webAnimationsNextTick","t","timeline","window","currentTime","_discardPlayers","_players","ticking","requestAnimationFrame","AnimationTimeline","prototype","getAnimationPlayers","player","playState","play","source","Player","push","restartWebAnimationsNextTick","defineProperty","configurable","get","e","_isGroup","_player","_childPlayers","_callback","_rebuildUnderlyingPlayer","cancel","Animation","newUnderlyingPlayerForAnimation","bindPlayerForAnimation","AnimationSequence","AnimationGroup","newUnderlyingPlayerForGroup","bindPlayerForGroup",{"end":{"file":"src/player.js","comments_before":[],"nlb":false,"endpos":2129,"pos":2118,"col":8,"line":65,"value":"currentTime","type":"name"},"start":{"file":"src/player.js","comments_before":[],"nlb":false,"endpos":2129,"pos":2118,"col":8,"line":65,"value":"currentTime","type":"name"},"name":"currentTime"},"paused","onfinish","_onfinish","v","target","call","bind","_register","_forEachChild","child","startTime","finished","awaitStartTime","time","pause","finish","_removePlayers","reverse","addEventListener","handler","wrapped","_wrapper","removeEventListener","pop","groupChildDuration","node","_timing","KeyframeEffect","effect","_frames","updatePendingGroups","updated","pendingGroups","shift","_updateChildren","getFrames","_timingInput","_effect","originalElementAnimate","Element","animate","nullTarget","animation","bindPlayerForCustomEffect","groupPlayer","originalGetComputedStyle","getComputedStyle","enumerable","arguments","children","childPlayer","_startTime","register","callback","_registered","callbacks","tick","updating","sort","left","right","_sequenceNumber","sequenceNumber","last","constructor","total","max","group","underlyingPlayer","ticker","tf"],"mappings":";;;;;;;;;;;;;;CAcA,SAAIA,EAAAA,GACAC,EAAAA,QACAC,CAFJ,IAAIF,MACAC,KACAC,KAGEC,EAAuB,MCL7B,SAAUC,GAKR,QAASC,GAAWC,EAAaC,GAC/B,GAAIC,IACFC,MAAO,EACPC,SAAU,EACVC,KAAMJ,EAAW,OAAS,OAC1BK,eAAgB,EAChBC,WAAY,EACZC,SAAUP,EAAW,OAAS,EAC9BQ,aAAc,EACdC,UAAW,SACXC,OAAQ,SAyBV,OAvB0B,gBAAfX,IAA4BY,MAAMZ,GAElBa,SAAhBb,GACTc,OAAOC,oBAAoBf,GAAagB,QAAQ,SAASC,GACvD,GAA6B,QAAzBjB,EAAYiB,GAAqB,CACnC,IAA+B,gBAApBf,GAAOe,IAAqC,YAAZA,KACL,gBAAzBjB,GAAYiB,IAAyBL,MAAMZ,EAAYiB,KAChE,MAGJ,IAAiB,QAAZA,GAAgE,IAAxCC,EAAMC,QAAQnB,EAAYiB,IACrD,MAEF,IAAiB,aAAZA,GAA0E,IAA7CG,EAAWD,QAAQnB,EAAYiB,IAC/D,MAEF,IAAgB,gBAAZA,GAA8BnB,EAAOuB,aAAa,+BAAgC,aAAc,6CAClG,MAEFnB,GAAOe,GAAYjB,EAAYiB,MAlBnCf,EAAOM,SAAWR,EAsBbE,EAGT,QAASoB,GAAqBtB,EAAaC,GACzC,GAAIC,GAASH,EAAWC,EAAaC,EAErC,OADAC,GAAOS,OAASY,EAAiBrB,EAAOS,QACjCT,EAGT,QAASsB,GAAMC,EAAGC,EAAGC,EAAGC,GACtB,MAAQ,GAAJH,GAASA,EAAI,GAAS,EAAJE,GAASA,EAAI,EAC1BE,EAEF,SAASC,GAIZ,QAASC,GAAEN,EAAGC,EAAGM,GAAK,MAAO,GAAIP,GAAK,EAAIO,IAAM,EAAIA,GAAKA,EAAI,EAAIN,GAAK,EAAIM,GAAKA,EAAIA,EAAIA,EAAIA,EAAIA,EAFjG,IADA,GAAIC,GAAQ,EAAGC,EAAM,IACX,CACR,GAAIC,IAAOF,EAAQC,GAAO,EAEtBE,EAAOL,EAAEN,EAAGE,EAAGQ,EACnB,IAAIE,KAAKC,IAAIR,EAAIM,GAAQ,KACvB,MAAOL,GAAEL,EAAGE,EAAGO,EAENL,GAAPM,EACFH,EAAQE,EAERD,EAAMC,IAUd,QAASI,GAAKC,EAAOC,GACnB,MAAO,UAASX,GACd,GAAIA,GAAK,EACP,MAAO,EAET,IAAIY,GAAW,EAAIF,CAEnB,OADAV,IAAKW,EAAMC,EACJZ,EAAIA,EAAIY,GAmBnB,QAASnB,GAAiBZ,GACxB,GAAIgC,GAAYC,EAAcC,KAAKlC,EACnC,IAAIgC,EACF,MAAOnB,GAAMsB,MAAMC,KAAMJ,EAAUK,MAAM,GAAGC,IAAIC,QAElD,IAAIC,GAAWC,EAAOP,KAAKlC,EAC3B,IAAIwC,EACF,MAAOZ,GAAKW,OAAOC,EAAS,KAAMlB,MAASoB,EAAOC,OAAUC,EAAQrB,IAAOsB,GAAKL,EAAS,IAE3F,IAAIM,GAASC,EAAQ/C,EACrB,OAAI8C,GACKA,EAEF5B,EAGT,QAAS8B,GAAwBzD,GAC/B,MAAOmC,MAAKC,IAAIsB,EAAiB1D,GAAUA,EAAOO,cAGpD,QAASmD,GAAiB1D,GACxB,MAAOA,GAAOM,SAAWN,EAAOK,WAQlC,QAASsD,GAAeC,EAAgBC,EAAW7D,GACjD,MAAiB,OAAb6D,EACKC,EAELD,EAAY7D,EAAOC,MACd8D,EAELF,GAAa7D,EAAOC,MAAQ2D,EACvBI,EAEFC,EAGT,QAASC,GAAoBN,EAAgBO,EAAUN,EAAWO,EAAOnE,GACvE,OAAQmE,GACN,IAAKL,GACH,MAAgB,aAAZI,GAAuC,QAAZA,EACtB,EACF,IACT,KAAKF,GACH,MAAOJ,GAAY5D,CACrB,KAAK+D,GACH,MAAgB,YAAZG,GAAsC,QAAZA,EACrBP,EACF,IACT,KAAKE,GACH,MAAO,OAIb,QAASO,GAA0BT,EAAgBU,EAAYC,EAAavE,GAC1E,OAAQA,EAAOO,aAAe,EAAI+D,EAAaV,EAAiBU,GAActE,EAAOO,aAAegE,EAGtG,QAASC,GAAuBC,EAAmBf,EAAkBgB,EAAkBH,EAAavE,GAClG,MAAyB2E,GAAAA,IAArBD,GAAiCA,IAAAA,GAAsBC,GAAaD,EAAmBH,GAAeb,GAAoB1D,EAAOK,aAAgBL,EAAOK,WAAaL,EAAOI,gBAAkB,GAAK,EAC9LqE,EAGFC,EAAmBD,EAG5B,QAASG,GAA0BH,EAAmBI,EAAeH,EAAkB1E,GACrF,MAAyB,KAArB0E,EACK,EAELG,GAAiBJ,EACZzE,EAAOI,eAAiBJ,EAAOK,WAAa,EAE9C8B,KAAK2C,MAAMJ,EAAmBD,GAGvC,QAASM,GAAyBC,EAAkBP,EAAmBI,EAAe7E,GACpF,GAAIiF,GAAwBD,EAAmB,GAAK,EAChDE,EAAiD,UAApBlF,EAAOQ,WAAyBR,EAAOQ,YAAcyE,EAAwB,oBAAsB,aAChIE,EAAeD,EAA6BL,EAAgBJ,EAAoBI,EAChFO,EAAeD,EAAeV,CAClC,OAAOA,GAAoBzE,EAAOS,OAAO2E,GAG3C,QAASC,GAAsBzB,EAAgBC,EAAW7D,GACxD,GAAIoE,GAAQT,EAAeC,EAAgBC,EAAW7D,GAClDsE,EAAaJ,EAAoBN,EAAgB5D,EAAOG,KAAM0D,EAAWO,EAAOpE,EAAOC,MAC3F,IAAmB,OAAfqE,EACF,MAAO,KACT,IAAuB,IAAnBV,EACF,MAAOQ,KAAUL,EAAc,EAAI,CACrC,IAAIQ,GAAcvE,EAAOI,eAAiBJ,EAAOM,SAC7CoE,EAAmBL,EAA0BT,EAAgBU,EAAYC,EAAavE,GACtF6E,EAAgBL,EAAuBxE,EAAOM,SAAUoD,EAAiB1D,GAAS0E,EAAkBH,EAAavE,GACjHgF,EAAmBJ,EAA0B5E,EAAOM,SAAUuE,EAAeH,EAAkB1E,EACnG,OAAO+E,GAAyBC,EAAkBhF,EAAOM,SAAUuE,EAAe7E,GAAUA,EAAOM,SAvMrG,GAAIU,GAAQ,0BAA0BsE,MAAM,KACxCpE,EAAa,sCAAsCoE,MAAM,KAoEzDnC,EAAQ,EACRE,EAAS,GACTC,EAAM,EAaNE,GACF+B,KAAQjE,EAAM,IAAM,GAAK,IAAM,GAC/BkE,UAAWlE,EAAM,IAAM,EAAG,EAAG,GAC7BmE,WAAYnE,EAAM,EAAG,EAAG,IAAM,GAC9BoE,cAAepE,EAAM,IAAM,EAAG,IAAM,GACpCqE,aAActD,EAAK,EAAGc,GACtByC,cAAevD,EAAK,EAAGgB,GACvBwC,WAAYxD,EAAK,EAAGiB,IAGlBwC,EAAe,qCACfpD,EAAgB,GAAIqD,QAAO,kBAAoBD,EAAe,IAAMA,EAAe,IAAMA,EAAe,IAAMA,EAAe,OAC7H5C,EAAS,gDACTvB,EAAS,SAASC,GAAK,MAAOA,IA0B9BkC,EAAY,EACZC,EAAc,EACdC,EAAa,EACbC,EAAc,CA4ElBrE,GAAOC,WAAaA,EACpBD,EAAOwB,qBAAuBA,EAC9BxB,EAAO6D,wBAA0BA,EACjC7D,EAAOyF,sBAAwBA,EAC/BzF,EAAO+D,eAAiBA,EACxB/D,EAAOyB,iBAAmBA,GAkBzB7B,EAAqBG,GCnOxB,SAAUC,GAmIR,QAASoG,GAAUjF,EAAUkF,GAC3B,MAAIlF,KAAYmF,GACPA,EAAQnF,GAAUkF,IAAUA,EAE9BA,EAIT,QAASE,GAA4BpF,EAAUkF,EAAOG,GACpD,GAAIC,GAAiBC,EAAoBvF,EACzC,IAAIsF,EAAgB,CAClBE,EAAsBC,MAAMzF,GAAYkF,CACxC,KAAK,GAAIQ,KAAKJ,GAAgB,CAC5B,GAAIK,GAAeL,EAAeI,GAC9BE,EAAgBJ,EAAsBC,MAAME,EAChDN,GAAOM,GAAgBV,EAAUU,EAAcC,QAGjDP,GAAOrF,GAAYiF,EAAUjF,EAAUkF,GAI3C,QAASW,GAAmBC,GA4D1B,QAASC,KACP,GAAIC,GAASC,EAAeD,MACa,OAArCC,EAAeD,EAAS,GAAGE,SAC7BD,EAAeD,EAAS,GAAGE,OAAS,GAClCF,EAAS,GAAiC,MAA5BC,EAAe,GAAGC,SAClCD,EAAe,GAAGC,OAAS,EAI7B,KAAK,GAFDC,GAAgB,EAChBC,EAAiBH,EAAe,GAAGC,OAC9BR,EAAI,EAAOM,EAAJN,EAAYA,IAAK,CAC/B,GAAIQ,GAASD,EAAeP,GAAGQ,MAC/B,IAAc,MAAVA,EAAgB,CAClB,IAAK,GAAIG,GAAI,EAAOX,EAAIS,EAARE,EAAuBA,IACrCJ,EAAeE,EAAgBE,GAAGH,OAASE,GAAkBF,EAASE,GAAkBC,GAAKX,EAAIS,EACnGA,GAAgBT,EAChBU,EAAiBF,IA1EvB,IAAKI,MAAMC,QAAQT,IAAgC,OAAhBA,EACjC,KAAM,IAAIU,WAAU,wDAEtB,IAAmB,MAAfV,EACF,QAmCF,KAAK,GAjCDG,GAAiBH,EAAY9D,IAAI,SAASyE,GAC5C,GAAIC,KACJ,KAAK,GAAIC,KAAUF,GAAkB,CACnC,GAAIG,GAAcH,EAAiBE,EACnC,IAAc,UAAVA,GACF,GAAmB,MAAfC,IACFA,EAAc3E,OAAO2E,IAChBC,SAASD,IACZ,KAAM,IAAIJ,WAAU,yCAEnB,CAAA,GAAc,aAAVG,EACT,MACEG,KAAMC,aAAaC,kBACnBC,KAAM,oBACNC,QAAS,mCAGXN,GADmB,UAAVD,EACK9H,EAAOyB,iBAAiBsG,GAExB,GAAKA,EAErBxB,EAA4BuB,EAAQC,EAAaF,GAMnD,MAJuB9G,SAAnB8G,EAASR,SACXQ,EAASR,OAAS,MACGtG,QAAnB8G,EAAShH,SACXgH,EAAShH,OAASb,EAAOyB,iBAAiB,WACrCoG,IAGLS,GAAAA,EAEAf,EAAAA,GAAkBxC,EACb8B,EAAI,EAAGA,EAAIO,EAAeD,OAAQN,IAAK,CAC9C,GAAIQ,GAASD,EAAeP,GAAGQ,MAC/B,IAAc,MAAVA,EAAgB,CAClB,GAAaE,EAATF,EACF,MACEkB,KAAML,aAAaM,yBACnBJ,KAAM,2BACNC,QAAS,uEAGbd,GAAiBF,MAEjBiB,IAAAA,EA8BJ,MA1BAlB,GAAiBA,EAAeqB,OAAO,SAASZ,GAC9C,MAAOA,GAASR,QAAU,GAAKQ,EAASR,QAAU,IAsB/CiB,GACHpB,IAEKE,EA1OT,GAAIV,IACFgC,YACE,kBACA,qBACA,iBACA,mBACA,uBACA,mBACA,iBACA,mBAEFC,QACE,iBACA,iBACA,iBACA,mBACA,mBACA,mBACA,oBACA,oBACA,oBACA,kBACA,kBACA,mBAEFC,cACE,oBACA,oBACA,qBAEFC,aACE,iBACA,mBACA,oBACA,mBAEFC,YACE,kBACA,kBACA,mBAEFC,cACE,sBACA,uBACA,0BACA,0BAEFC,aACE,mBACA,mBACA,oBAEFC,WACE,iBACA,iBACA,kBAEFC,aACE,iBACA,mBACA,oBACA,mBAEFC,MACE,WACA,aACA,aAEFC,MACE,aACA,WACA,YACA,cACA,aACA,cAEFC,QACE,YACA,cACA,eACA,cAEFC,SACE,eACA,eACA,gBAEFC,SACE,aACA,eACA,gBACA,gBAIA5C,EAAwB6C,SAASC,gBAAgB,+BAAgC,OAEjFC,GACFC,KAAM,MACNC,OAAQ,MACRC,MAAO,OAGLvD,GACFwD,kBAAmBJ,EACnBK,gBAAiBL,EACjBM,iBAAkBN,EAClBO,eAAgBP,EAChBQ,UACEC,WAAY,MACZC,UAAW,MACXC,MAAS,MACTT,OAAU,OACVU,MAAS,OACTC,UAAW,OACXC,WAAY,QAEdC,YACEC,OAAQ,MACRC,KAAM,OAERC,aAAclB,EACdmB,YACEC,KAAM,2BAERC,WACED,KAAM,+BA+GV9K,GAAOgH,mBAAqBA,GAM3BpH,EAAqBG,GCpPxB,SAAUC,GAER,GAAIgL,KAEJhL,GAAOuB,aAAe,SAAS0J,EAASC,EAAMC,EAAQC,GACpD,GAAIC,GAAUD,EAAS,MAAQ,KAC3BE,EAAQ,GAAIC,MACZC,EAAS,GAAID,MAAKL,EAGtB,OAFAM,GAAOC,SAASD,EAAOE,WAAa,GAExBF,EAARF,GACIL,IAAWD,IACfW,QAAQC,KAAK,mBAAqBX,EAAU,IAAMI,EAAU,wCAA0CG,EAAOK,eAAiB,KAAOV,GAEvIH,EAASC,IAAAA,GAAW,IACb,GAMXjL,EAAO8L,WAAa,SAASb,EAASC,EAAMC,EAAQC,GAClD,GAAIpL,EAAOuB,aAAa0J,EAASC,EAAMC,EAAQC,GAC7C,KAAM,IAAIW,OAAMd,EAAU,IAAMI,QAAU,yBAA2BF,KAIxEvL,+3mBC1BH,SAAUI,EAAQgM,GAsChB,QAASC,GAAsBC,GAC7B,GAAIC,GAAWC,OAAO5C,SAAS2C,QAC/BA,GAASE,YAAcH,EACvBC,EAASG,kBACuB,GAA5BH,EAASI,SAASpF,OACpBqF,GAAAA,EAEAC,sBAAsBR,GA3C1BD,EAAMU,kBAAoB,WACxBzJ,KAAKsJ,YACLtJ,KAAKoJ,YAActL,QAGrBiL,EAAMU,kBAAkBC,WAItBC,oBAAqB,WAEnB,MADA3J,MAAKqJ,kBACErJ,KAAKsJ,SAASrJ,SAEvBoJ,gBAAiB,WACfrJ,KAAKsJ,SAAWtJ,KAAKsJ,SAAS9D,OAAO,SAASoE,GAC5C,MAA2B,YAApBA,EAAOC,WAA+C,QAApBD,EAAOC,aAGpDC,KAAM,SAASC,GACb,GAAIH,GAAS,GAAIb,GAAMiB,OAAOD,EAI9B,OAHA/J,MAAKsJ,SAASW,KAAKL,GACnBb,EAAMmB,+BACNN,EAAOE,OACAF,GAIX,IAAIL,IAAAA,CAEJR,GAAMmB,6BAA+B,WAC9BX,IACHA,GAAAA,EACAC,sBAAsBR,IAc1B,IAAIE,GAAW,GAAIH,GAAMU,iBACzBV,GAAMG,SAAWA,CAEjB,KACEnL,OAAOoM,eAAehB,OAAO5C,SAAU,YACrC6D,cAAAA,EACAC,IAAK,WAAa,MAAOnB,MAE3B,MAAOoB,IACT,IACEnB,OAAO5C,SAAS2C,SAAWA,EAC3B,MAAOoB,MAER3N,EAAqBE,EAAmBC,GC9D3C,SAAUC,EAAQgM,GAChBA,EAAMiB,OAAS,SAASD,GACtB/J,KAAK+J,OAASA,EACVA,IAEFA,EAAOH,OAAS5J,MAElBA,KAAKuK,UAAAA,EACLvK,KAAKwK,QAAU,KACfxK,KAAKyK,iBACLzK,KAAK0K,UAAY,KACjB1K,KAAK2K,2BAEL3K,KAAKwK,QAAQI,UAIf7B,EAAMiB,OAAON,WACXiB,yBAA0B,WACpB3K,KAAKwK,UACPxK,KAAKwK,QAAQI,SACb5K,KAAKwK,QAAU,QAGZxK,KAAK+J,QAAU/J,KAAK+J,iBAAkBZ,QAAO0B,aAChD7K,KAAKwK,QAAUzB,EAAM+B,gCAAgC9K,KAAK+J,QAC1DhB,EAAMgC,uBAAuB/K,QAE3BA,KAAK+J,iBAAkBZ,QAAO6B,mBAAqBhL,KAAK+J,iBAAkBZ,QAAO8B,kBACnFjL,KAAKwK,QAAUzB,EAAMmC,4BAA4BlL,KAAK+J,QACtDhB,EAAMoC,mBAAmBnL,QAK7BoL,GAAIC,UACF,MAAOrL,MAAKwK,QAAQa,QAEtBD,GAAIvB,aACF,MAAO7J,MAAKwK,QAAQX,WAEtBuB,GAAIE,YACF,MAAOtL,MAAKuL,WAEdH,GAAIE,UAASE,GACK,kBAALA,IACTxL,KAAKuL,UAAYC,EACjBxL,KAAKwK,QAAQc,SAAW,SAAUhB,GAChCA,EAAEmB,OAASzL,KACXwL,EAAEE,KAAK1L,KAAMsK,IACZqB,KAAK3L,QAERA,KAAKwK,QAAQc,SAAWE,EACxBxL,KAAKsL,SAAWtL,KAAKwK,QAAQc,WAGjCF,GAAIhC,eACF,MAAOpJ,MAAKwK,QAAQpB,aAEtBgC,GAAIhC,aAAYoC,GACdxL,KAAKwK,QAAQpB,YAAcoC,EAC3BxL,KAAK4L,YACL5L,KAAK6L,cAAc,SAASC,EAAO1H,GACjC0H,EAAM1C,YAAcoC,EAAIpH,KAG5BgH,GAAIW,aACF,MAAO/L,MAAKwK,QAAQuB,WAEtBX,GAAIW,WAAUP,GACZxL,KAAKwK,QAAQuB,UAAYP,EACzBxL,KAAK4L,YACL5L,KAAK6L,cAAc,SAASC,EAAO1H,GACjC0H,EAAMC,UAAYP,EAAIpH,KAG1BgH,GAAI1N,gBACF,MAAOsC,MAAKwK,QAAQ9M,cAEtB0N,GAAIY,YACF,MAAOhM,MAAKwK,QAAQwB,UAEtBlC,KAAM,WACJ9J,KAAKwK,QAAQV,OACb9J,KAAK4L,YACL7C,EAAMkD,eAAejM,MACrBA,KAAK6L,cAAc,SAASC,GAC1B,GAAII,GAAOJ,EAAM1C,WACjB0C,GAAMhC,OACNgC,EAAM1C,YAAc8C,KAGxBC,MAAO,WACLnM,KAAKwK,QAAQ2B,QACbnM,KAAK4L,YACL5L,KAAK6L,cAAc,SAASC,GAC1BA,EAAMK,WAGVC,OAAQ,WACNpM,KAAKwK,QAAQ4B,SACbpM,KAAK4L,aAGPhB,OAAQ,WACN5K,KAAKwK,QAAQI,SACb5K,KAAK4L,YACL5L,KAAKqM,kBAEPC,QAAS,WACPtM,KAAKwK,QAAQ8B,UACbvD,EAAMkD,eAAejM,MACrBA,KAAK4L,YACL5L,KAAK6L,cAAc,SAASC,EAAO1H,GACjC0H,EAAMQ,UACNR,EAAMC,UAAY/L,KAAK+L,UAAY3H,EAASpE,KAAKtC,aACjDoO,EAAM1C,YAAcpJ,KAAKoJ,YAAchF,EAASpE,KAAKtC,gBAGzD6O,iBAAkB,SAASvH,EAAMwH,GAC/B,GAAIC,GAAUD,CACQ,mBAAXA,KACTC,EAAU,SAAUnC,GAClBA,EAAEmB,OAASzL,KACXwM,EAAQd,KAAK1L,KAAMsK,IAClBqB,KAAK3L,MACRwM,EAAQE,SAAWD,GAErBzM,KAAKwK,QAAQ+B,iBAAiBvH,EAAMyH,IAEtCE,oBAAqB,SAAS3H,EAAMwH,GAClCxM,KAAKwK,QAAQmC,oBAAoB3H,EAAOwH,GAAWA,EAAQE,UAAaF,IAE1EH,eAAgB,WACd,KAAOrM,KAAKyK,cAAcvG,QACxBlE,KAAKyK,cAAcmC,MAAMhC,UAE7BiB,cAAe,SAAS7M,GACtB,GAAIoF,GAAS,CACbpE,MAAKyK,cAAcxM,QAAQ,SAAS6N,GAClC9M,EAAE0M,KAAK1L,KAAM8L,EAAO1H,GAChBpE,KAAK+J,iBAAkBZ,QAAO6B,oBAChC5G,GAAU0H,EAAM/B,OAAOhJ,iBACzB4K,KAAK3L,UAIVrD,EAAqBE,EAAmBC,GCnJ1C,SAASC,EAAQgM,GAEhB,QAAS8D,GAAmBC,GAC1B,MAAOA,GAAKC,QAAQ3P,MAAQ0P,EAAK/L,eAAiB+L,EAAKC,QAAQ1P,SAGjE,QAAS2P,GAAeC,GACtBjN,KAAKkN,QAAUnQ,EAAOgH,mBAAmBkJ,GAyD3C,QAASE,KAEP,IADA,GAAIC,IAAAA,EACGC,EAAcnJ,QACnBmJ,EAAcC,QAAQC,kBACtBH,GAAAA,CAEF,OAAOA,GA5DTJ,EAAetD,WACb8D,UAAW,WAAa,MAAOxN,MAAKkN,UAGtCnE,EAAM8B,UAAY,SAASY,EAAQwB,EAAQhQ,GAiBzC,MAhBA+C,MAAKyL,OAASA,EAGdzL,KAAKyN,aAAexQ,EACpB+C,KAAK+M,QAAUhQ,EAAOwB,qBAAqBtB,GAG3C+C,KAAK7C,OAASJ,EAAOC,WAAWC,GAI9B+C,KAAKiN,OADc,kBAAVA,GACKA,EAEA,GAAID,GAAeC,GACnCjN,KAAK0N,QAAUT,EACfjN,KAAKe,eAAiBhE,EAAO6D,wBAAwBZ,KAAK+M,SACnD/M,KAGT,IAAI2N,GAAyBC,QAAQlE,UAAUmE,OAC/CD,SAAQlE,UAAUmE,QAAU,SAASZ,EAAQ9P,GAC3C,MAAO4L,GAAMG,SAASY,KAAK,GAAIf,GAAM8B,UAAU7K,KAAMiN,EAAQ9P,IAG/D,IAAI2Q,GAAavH,SAASC,gBAAgB,+BAAgC,MAC1EuC,GAAM+B,gCAAkC,SAASiD,GAC/C,GAAItC,GAASsC,EAAUtC,QAAUqC,EAC7Bb,EAASc,EAAUL,OAIvB,OAHqB,kBAAVT,KACTA,MAEKU,EAAuB5N,MAAM0L,GAASwB,EAAQc,EAAUN,gBAGjE1E,EAAMgC,uBAAyB,SAASnB,GAClCA,EAAOG,QAAyC,kBAAxBH,GAAOG,OAAOkD,QACxClE,EAAMiF,0BAA0BpE,GAIpC,IAAIyD,KACJtE,GAAMkD,eAAiB,SAASgC,GACA,OAA1BA,EAAYlC,WAAuBkC,EAAY1D,WAEvB,GAAxB8C,EAAcnJ,QAChBsF,sBAAsB2D,GAExBE,EAAcpD,KAAKgE,IAUrB,IAAIC,GAA2B/E,OAAOgF,gBACtCpQ,QAAOoM,eAAehB,OAAQ,oBAC5BiB,cAAAA,EACAgE,YAAAA,EACAhL,MAAO,WACL,GAAIG,GAAS2K,EAAyBnO,MAAMC,KAAMqO,UAGlD,OAFIlB,OACF5J,EAAS2K,EAAyBnO,MAAMC,KAAMqO,YACzC9K,KAKXwF,EAAMiB,OAAON,UAAU6D,gBAAkB,WACvC,IAAIvN,KAAKqL,QAAWrL,KAAK+J,QAAW/J,KAAKuK,SAGzC,IAAK,GADDnG,GAASpE,KAAK+J,OAAOgD,QAAQ3P,MACxBwG,EAAI,EAAGA,EAAI5D,KAAK+J,OAAOuE,SAASpK,OAAQN,IAAK,CACpD,GACI2K,GADAzC,EAAQ9L,KAAK+J,OAAOuE,SAAS1K,EAG7BA,IAAK5D,KAAKyK,cAAcvG,QAC1BqK,EAAcpF,OAAO5C,SAAS2C,SAASY,KAAKgC,GAC5C9L,KAAKyK,cAAcR,KAAKsE,IAExBA,EAAcvO,KAAKyK,cAAc7G,GAEnCkI,EAAMlC,OAAS5J,KAAK+J,OAAOH,OAEvB2E,EAAYxC,WAAa/L,KAAK+L,UAAY3H,IACrB,OAAnBpE,KAAK+L,WACPwC,EAAYnF,YAAcpJ,KAAK+J,OAAOH,OAAOR,YAAchF,EAC3DmK,EAAYC,WAAa,MAEzBD,EAAYxC,UAAY/L,KAAK+L,UAAY3H,EAE3CmK,EAAYhB,mBAGW,IAArBvN,KAAKtC,cAAsBsC,KAAKoJ,YAAchF,GAAsC,KAA5BmK,EAAYnF,cACtEmF,EAAYnF,YAAc,IAGxBpJ,KAAK+J,iBAAkBZ,QAAO6B,oBAChC5G,GAAUyI,EAAmBf,MAInC3C,OAAO0B,UAAY9B,EAAM8B,UACzB1B,OAAOyE,QAAQlE,UAAUC,oBAAsB,WAC7C,MAAOpD,UAAS2C,SAASS,sBAAsBnE,OAAO,SAASoE,GAC7D,MAAyB,QAAlBA,EAAOG,QAAmBH,EAAOG,OAAO0B,QAAUzL,MACzD2L,KAAK3L,QAGT+I,EAAM8D,mBAAqBA,GAE3BlQ,EAAqBE,EAAmBC,GClI1C,SAAUC,EAAQgM,GAkChB,QAAS0F,GAASC,GACZA,EAASC,cAEbD,EAASC,aAAAA,EACTC,EAAU3E,KAAKyE,GACVnF,IACHA,GAAAA,EACAC,sBAAsBqF,KAI1B,QAASA,KACP,GAAIC,GAAWF,CACfA,MACAE,EAASC,KAAK,SAASC,EAAMC,GAC3B,MAAOD,GAAKE,gBAAkBD,EAAMC,kBAEtCJ,EAAStJ,OAAO,SAASkJ,GAIvB,MAHAA,OACKA,EAASlE,SAAWkE,EAASlE,QAAQwB,UAAY0C,EAASlE,QAAQa,UACrEqD,EAASC,aAAAA,GACJD,EAASC;GAElBC,EAAU3E,KAAKlK,MAAM6O,EAAWE,GAE5BF,EAAU1K,QACZqF,GAAAA,EACAC,sBAAsBqF,IAEtBtF,GAAAA,EA7DJ,GAEI4F,IAFa5I,SAASC,gBAAgB,+BAAgC,OAErD,EACrBuC,GAAMiF,0BAA4B,SAASpE,GACzC,GAAI6B,GAAS7B,EAAOG,OAAO0B,OACvBwB,EAASrD,EAAOG,OAAOkD,OACvB9P,EAASyM,EAAOG,OAAO5M,OACvBiS,EAAOtR,MACXX,GAASJ,EAAOwB,qBAAqBpB,EACrC,IAAIuR,GAAW,WACb,GAAIzF,GAAIyF,EAASlE,QAAUkE,EAASlE,QAAQpB,YAAc,IAChD,QAANH,IACFA,EAAIlM,EAAOyF,sBAAsBzF,EAAO6D,wBAAwBzD,GAAS8L,EAAG9L,GACxEU,MAAMoL,KACRA,EAAI,OAIJA,IAAMmG,GACRnC,EAAOhE,EAAGwC,EAAQ7B,EAAOG,QAC3BqF,EAAOnG,EAGTyF,GAASlE,QAAUZ,EACnB8E,EAASC,aAAAA,EACTD,EAASQ,gBAAkBC,IAC3BvF,EAAOc,UAAYgE,EACnBD,EAASC,GAGX,IAAIE,MACArF,GAAAA,CAkCJR,GAAMiB,OAAON,UAAUkC,UAAY,WAC7B5L,KAAK0K,WACP+D,EAASzO,KAAK0K,aAGjB/N,EAAqBE,EAAmBC,GCvE3C,SAAUC,EAAQgM,GAEhB,QAASsG,GAAYf,EAAUrR,GAC7B+C,KAAKsO,SAAWA,MAChBtO,KAAK+M,QAAUhQ,EAAOwB,qBAAqBtB,GAAAA,GAC3C+C,KAAK7C,OAASJ,EAAOC,WAAWC,GAAAA,GAEF,SAA1B+C,KAAK+M,QAAQtP,WACfuC,KAAK+M,QAAQtP,SAAWuC,KAAKe,gBAGjCoI,OAAO6B,kBAAoB,WACzBqE,EAAYtP,MAAMC,KAAMqO,YAG1BlF,OAAO8B,eAAiB,WACtBoE,EAAYtP,MAAMC,KAAMqO,YAG1BlF,OAAO6B,kBAAkBtB,WACvB0B,GAAIrK,kBACF,GAAIuO,GAAQ,CAIZ,OAHAtP,MAAKsO,SAASrQ,QAAQ,SAAS6N,GAC7BwD,GAASvG,EAAM8D,mBAAmBf,KAE7BxM,KAAKiQ,IAAID,EAAO,KAI3BnG,OAAO8B,eAAevB,WACpB0B,GAAIrK,kBACF,GAAIwO,GAAM,CAIV,OAHAvP,MAAKsO,SAASrQ,QAAQ,SAAS6N,GAC7ByD,EAAMjQ,KAAKiQ,IAAIA,EAAKxG,EAAM8D,mBAAmBf,MAExCyD,IAIXxG,EAAMmC,4BAA8B,SAASsE,GAC3C,GAAIC,GACAC,EAAS,SAASC,GACpB,GAAI/F,GAAS6F,EAAiB/C,QAC9B,OAAK9C,GAAOG,OAEF,MAAN4F,MACF/F,GAAOyC,sBAGgB,OAArBzC,EAAOmC,WAGXnC,EAAO2D,mBATP,OAaF,OADAkC,GAAmB1G,EAAMG,SAASY,KAAK,GAAIf,GAAM8B,UAAU,KAAM6E,EAAQF,EAAMzC,WAIjFhE,EAAMoC,mBAAqB,SAASvB,GAClCA,EAAOY,QAAQkC,SAAW9C,EAC1BA,EAAOW,UAAAA,EACPxB,EAAMkD,eAAerC,GACrBA,EAAO2D,oBAIR5Q,EAAqBE,EAAmBC,OR9DrCA,WAAAA,MAAuB"}
\ No newline at end of file
diff --git a/third_party/web-animations-js/sources/web-animations-next.dev.html b/third_party/web-animations-js/sources/web-animations-next.dev.html
new file mode 100644
index 0000000..d608ef4e
--- /dev/null
+++ b/third_party/web-animations-js/sources/web-animations-next.dev.html
@@ -0,0 +1,49 @@
+<!--
+ Copyright 2014 Google Inc. All rights reserved.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<script src="src/dev.js"></script>
+<script src="src/scope.js"></script>
+<script src="src/timing-utilities.js"></script>
+<script src="src/normalize-keyframes.js"></script>
+<script src="src/deprecation.js"></script>
+<script src="src/animation-node.js"></script>
+<script src="src/effect.js"></script>
+<script src="src/property-interpolation.js"></script>
+<script src="src/animation.js"></script>
+<script src="src/apply-preserving-inline-style.js"></script>
+<script src="src/element-animatable.js"></script>
+<script src="src/interpolation.js"></script>
+<script src="src/matrix-interpolation.js"></script>
+<script src="src/player.js"></script>
+<script src="src/tick.js"></script>
+<script src="src/matrix-decomposition.js"></script>
+<script src="src/handler-utils.js"></script>
+<script src="src/shadow-handler.js"></script>
+<script src="src/number-handler.js"></script>
+<script src="src/visibility-handler.js"></script>
+<script src="src/color-handler.js"></script>
+<script src="src/dimension-handler.js"></script>
+<script src="src/box-handler.js"></script>
+<script src="src/transform-handler.js"></script>
+<script src="src/font-weight-handler.js"></script>
+<script src="src/position-handler.js"></script>
+<script src="src/shape-handler.js"></script>
+<script src="src/property-names.js"></script>
+<script src="src/timeline.js"></script>
+<script src="src/web-animations-next-player.js"></script>
+<script src="src/animation-constructor.js"></script>
+<script src="src/effect-callback.js"></script>
+<script src="src/group-constructors.js"></script>
+
diff --git a/third_party/web-animations-js/sources/web-animations-next.dev.js b/third_party/web-animations-js/sources/web-animations-next.dev.js
new file mode 100644
index 0000000..8f2a4e29
--- /dev/null
+++ b/third_party/web-animations-js/sources/web-animations-next.dev.js
@@ -0,0 +1,21 @@
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+//     You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//     See the License for the specific language governing permissions and
+// limitations under the License.
+var webAnimationsSourceTarget = 'web-animations-next';
+var WEB_ANIMATIONS_TESTING = false;
+(function() {
+  var scripts = document.getElementsByTagName('script');
+  var location = scripts[scripts.length - 1].src.replace(/[^\/]+$/, '');
+  document.write('<script src="' + location + 'target-config.js"></script>');
+  document.write('<script src="' + location + 'target-loader.js"></script>');
+})();
diff --git a/third_party/web-animations-js/sources/web-animations-next.min.js b/third_party/web-animations-js/sources/web-animations-next.min.js
new file mode 100644
index 0000000..dc8733a2
--- /dev/null
+++ b/third_party/web-animations-js/sources/web-animations-next.min.js
@@ -0,0 +1,17 @@
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+//     You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//     See the License for the specific language governing permissions and
+// limitations under the License.
+
+!function(a,b){b["true"]=a;var c={},d={},e={},f=null;!function(a){function b(b,c){var d={delay:0,endDelay:0,fill:c?"both":"none",iterationStart:0,iterations:1,duration:c?"auto":0,playbackRate:1,direction:"normal",easing:"linear"};return"number"!=typeof b||isNaN(b)?void 0!==b&&Object.getOwnPropertyNames(b).forEach(function(c){if("auto"!=b[c]){if(("number"==typeof d[c]||"duration"==c)&&("number"!=typeof b[c]||isNaN(b[c])))return;if("fill"==c&&-1==p.indexOf(b[c]))return;if("direction"==c&&-1==q.indexOf(b[c]))return;if("playbackRate"==c&&a.isDeprecated("AnimationTiming.playbackRate","2014-11-28","Use AnimationPlayer.playbackRate instead."))return;d[c]=b[c]}}):d.duration=b,d}function c(a,c){var d=b(a,c);return d.easing=f(d.easing),d}function d(a,b,c,d){return 0>a||a>1||0>c||c>1?y:function(e){function f(a,b,c){return 3*a*(1-c)*(1-c)*c+3*b*(1-c)*c*c+c*c*c}for(var g=0,h=1;;){var i=(g+h)/2,j=f(a,c,i);if(Math.abs(e-j)<.001)return f(b,d,i);e>j?g=i:h=i}}}function e(a,b){return function(c){if(c>=1)return 1;var d=1/a;return c+=b*d,c-c%d}}function f(a){var b=w.exec(a);if(b)return d.apply(this,b.slice(1).map(Number));var c=x.exec(a);if(c)return e(Number(c[1]),{start:r,middle:s,end:t}[c[2]]);var f=u[a];return f?f:y}function g(a){return Math.abs(h(a)/a.playbackRate)}function h(a){return a.duration*a.iterations}function i(a,b,c){return null==b?z:b<c.delay?A:b>=c.delay+a?B:C}function j(a,b,c,d,e){switch(d){case A:return"backwards"==b||"both"==b?0:null;case C:return c-e;case B:return"forwards"==b||"both"==b?a:null;case z:return null}}function k(a,b,c,d){return(d.playbackRate<0?b-a:b)*d.playbackRate+c}function l(a,b,c,d,e){return 1/0===c||c===-1/0||c-d==b&&e.iterations&&(e.iterations+e.iterationStart)%1==0?a:c%a}function m(a,b,c,d){return 0===c?0:b==a?d.iterationStart+d.iterations-1:Math.floor(c/a)}function n(a,b,c,d){var e=a%2>=1,f="normal"==d.direction||d.direction==(e?"alternate-reverse":"alternate"),g=f?c:b-c,h=g/b;return b*d.easing(h)}function o(a,b,c){var d=i(a,b,c),e=j(a,c.fill,b,d,c.delay);if(null===e)return null;if(0===a)return d===A?0:1;var f=c.iterationStart*c.duration,g=k(a,e,f,c),o=l(c.duration,h(c),g,f,c),p=m(c.duration,o,g,c);return n(p,c.duration,o,c)/c.duration}var p="backwards|forwards|both".split("|"),q="reverse|alternate|alternate-reverse".split("|"),r=1,s=.5,t=0,u={ease:d(.25,.1,.25,1),"ease-in":d(.42,0,1,1),"ease-out":d(0,0,.58,1),"ease-in-out":d(.42,0,.58,1),"step-start":e(1,r),"step-middle":e(1,s),"step-end":e(1,t)},v="\\s*(-?\\d+\\.?\\d*|-?\\.\\d+)\\s*",w=new RegExp("cubic-bezier\\("+v+","+v+","+v+","+v+"\\)"),x=/steps\(\s*(\d+)\s*,\s*(start|middle|end)\s*\)/,y=function(a){return a},z=0,A=1,B=2,C=3;a.makeTiming=b,a.normalizeTimingInput=c,a.calculateActiveDuration=g,a.calculateTimeFraction=o,a.calculatePhase=i,a.toTimingFunction=f}(c,f),function(a){function b(a,b){return a in h?h[a][b]||b:b}function c(a,c,d){var g=e[a];if(g){f.style[a]=c;for(var h in g){var i=g[h],j=f.style[i];d[i]=b(i,j)}}else d[a]=b(a,c)}function d(b){function d(){var a=e.length;null==e[a-1].offset&&(e[a-1].offset=1),a>1&&null==e[0].offset&&(e[0].offset=0);for(var b=0,c=e[0].offset,d=1;a>d;d++){var f=e[d].offset;if(null!=f){for(var g=1;d-b>g;g++)e[b+g].offset=c+(f-c)*g/(d-b);b=d,c=f}}}if(!Array.isArray(b)&&null!==b)throw new TypeError("Keyframe effect must be null or an array of keyframes");if(null==b)return[];for(var e=b.map(function(b){var d={};for(var e in b){var f=b[e];if("offset"==e){if(null!=f&&(f=Number(f),!isFinite(f)))throw new TypeError("keyframe offsets must be numbers.")}else{if("composite"==e)throw{type:DOMException.NOT_SUPPORTED_ERR,name:"NotSupportedError",message:"add compositing is not supported"};f="easing"==e?a.toTimingFunction(f):""+f}c(e,f,d)}return void 0==d.offset&&(d.offset=null),void 0==d.easing&&(d.easing=a.toTimingFunction("linear")),d}),f=!0,g=-1/0,h=0;h<e.length;h++){var i=e[h].offset;if(null!=i){if(g>i)throw{code:DOMException.INVALID_MODIFICATION_ERR,name:"InvalidModificationError",message:"Keyframes are not loosely sorted by offset. Sort or specify offsets."};g=i}else f=!1}return e=e.filter(function(a){return a.offset>=0&&a.offset<=1}),f||d(),e}var e={background:["backgroundImage","backgroundPosition","backgroundSize","backgroundRepeat","backgroundAttachment","backgroundOrigin","backgroundClip","backgroundColor"],border:["borderTopColor","borderTopStyle","borderTopWidth","borderRightColor","borderRightStyle","borderRightWidth","borderBottomColor","borderBottomStyle","borderBottomWidth","borderLeftColor","borderLeftStyle","borderLeftWidth"],borderBottom:["borderBottomWidth","borderBottomStyle","borderBottomColor"],borderColor:["borderTopColor","borderRightColor","borderBottomColor","borderLeftColor"],borderLeft:["borderLeftWidth","borderLeftStyle","borderLeftColor"],borderRadius:["borderTopLeftRadius","borderTopRightRadius","borderBottomRightRadius","borderBottomLeftRadius"],borderRight:["borderRightWidth","borderRightStyle","borderRightColor"],borderTop:["borderTopWidth","borderTopStyle","borderTopColor"],borderWidth:["borderTopWidth","borderRightWidth","borderBottomWidth","borderLeftWidth"],flex:["flexGrow","flexShrink","flexBasis"],font:["fontFamily","fontSize","fontStyle","fontVariant","fontWeight","lineHeight"],margin:["marginTop","marginRight","marginBottom","marginLeft"],outline:["outlineColor","outlineStyle","outlineWidth"],padding:["paddingTop","paddingRight","paddingBottom","paddingLeft"]},f=document.createElementNS("http://www.w3.org/1999/xhtml","div"),g={thin:"1px",medium:"3px",thick:"5px"},h={borderBottomWidth:g,borderLeftWidth:g,borderRightWidth:g,borderTopWidth:g,fontSize:{"xx-small":"60%","x-small":"75%",small:"89%",medium:"100%",large:"120%","x-large":"150%","xx-large":"200%"},fontWeight:{normal:"400",bold:"700"},outlineWidth:g,textShadow:{none:"0px 0px 0px transparent"},boxShadow:{none:"0px 0px 0px 0px transparent"}};a.normalizeKeyframes=d}(c,f),function(a){var b={};a.isDeprecated=function(a,c,d,e){var f=e?"are":"is",g=new Date,h=new Date(c);return h.setMonth(h.getMonth()+3),h>g?(a in b||console.warn("Web Animations: "+a+" "+f+" deprecated and will stop working on "+h.toDateString()+". "+d),b[a]=!0,!1):!0},a.deprecated=function(b,c,d,e){if(a.isDeprecated(b,c,d,e))throw new Error(b+" "+auxVerb+" no longer supported. "+d)}}(c),function(){if(document.documentElement.animate){var a=document.documentElement.animate([],0),b=!0;if(a&&(b=!1,"play|currentTime|pause|reverse|playbackRate|cancel|finish|startTime|playState".split("|").forEach(function(c){void 0===a[c]&&(b=!0)})),!b)return}!function(a,b){b.AnimationNode=function(b){var c=a.calculateActiveDuration(b),d=function(d){return a.calculateTimeFraction(c,d,b)};return d._totalDuration=b.delay+c+b.endDelay,d._isCurrent=function(d){var e=a.calculatePhase(c,d,b);return e===PhaseActive||e===PhaseBefore},d}}(c,d),function(a,b){function c(a){for(var b={},c=0;c<a.length;c++)for(var d in a[c])if("offset"!=d&&"easing"!=d&&"composite"!=d){var e={offset:a[c].offset,easing:a[c].easing,value:a[c][d]};b[d]=b[d]||[],b[d].push(e)}for(var f in b){var g=b[f];if(0!=g[0].offset||1!=g[g.length-1].offset)throw{type:DOMException.NOT_SUPPORTED_ERR,name:"NotSupportedError",message:"Partial keyframes are not supported"}}return b}function d(a){var c=[];for(var d in a)for(var e=a[d],f=0;f<e.length-1;f++){var g=e[f].offset,h=e[f+1].offset,i=e[f].value,j=e[f+1].value;g==h&&(1==h?i=j:j=i),c.push({startTime:g,endTime:h,easing:e[f].easing,property:d,interpolation:b.propertyInterpolation(d,i,j)})}return c.sort(function(a,b){return a.startTime-b.startTime}),c}b.convertEffectInput=function(e){var f=a.normalizeKeyframes(e),g=c(f),h=d(g);return function(a,c){if(null!=c)h.filter(function(a){return 0>=c&&0==a.startTime||c>=1&&1==a.endTime||c>=a.startTime&&c<=a.endTime}).forEach(function(d){var e=c-d.startTime,f=d.endTime-d.startTime,g=0==f?0:d.easing(e/f);b.apply(a,d.property,d.interpolation(g))});else for(var d in g)"offset"!=d&&"easing"!=d&&"composite"!=d&&b.clear(a,d)}}}(c,d,f),function(a){function b(a,b,c){e[c]=e[c]||[],e[c].push([a,b])}function c(a,c,d){for(var e=0;e<d.length;e++){var f=d[e];b(a,c,f),/-/.test(f)&&b(a,c,f.replace(/-(.)/g,function(a,b){return b.toUpperCase()}))}}function d(b,c,d){for(var f=c==d?[]:e[b],g=0;f&&g<f.length;g++){var h=f[g][0](c),i=f[g][0](d);if(void 0!==h&&void 0!==i){var j=f[g][1](h,i);if(j){var k=a.Interpolation.apply(null,j);return function(a){return 0==a?c:1==a?d:k(a)}}}}return a.Interpolation(!1,!0,function(a){return a?d:c})}var e={};a.addPropertiesHandler=c,a.propertyInterpolation=d}(d,f),function(a,b){b.Animation=function(c,d,e){var f,g=b.AnimationNode(a.normalizeTimingInput(e)),h=b.convertEffectInput(d),i=function(){h(c,f)};return i._update=function(a){return f=g(a),null!==f},i._clear=function(){h(c,null)},i._hasSameTarget=function(a){return c===a},i._isCurrent=g._isCurrent,i._totalDuration=g._totalDuration,i},b.NullAnimation=function(a){var b=function(){a&&(a(),a=null)};return b._update=function(){return null},b._totalDuration=0,b._isCurrent=function(){return!1},b._hasSameTarget=function(){return!1},b}}(c,d,f),function(a){function b(a,b,c){c.enumerable=!0,c.configurable=!0,Object.defineProperty(a,b,c)}function c(a){this._surrogateStyle=document.createElementNS("http://www.w3.org/1999/xhtml","div").style,this._style=a.style,this._length=0,this._isAnimatedProperty={};for(var b=0;b<this._style.length;b++){var c=this._style[b];this._surrogateStyle[c]=this._style[c]}this._updateIndices()}function d(a){if(!a._webAnimationsPatchedStyle){var d=new c(a);try{b(a,"style",{get:function(){return d}})}catch(e){a.style._set=function(b,c){a.style[b]=c},a.style._clear=function(b){a.style[b]=""}}a._webAnimationsPatchedStyle=a.style}}var e={cssText:1,length:1,parentRule:1},f={getPropertyCSSValue:1,getPropertyPriority:1,getPropertyValue:1,item:1,removeProperty:1,setProperty:1},g={removeProperty:1,setProperty:1};c.prototype={get cssText(){return this._surrogateStyle.cssText},set cssText(a){for(var b={},c=0;c<this._surrogateStyle.length;c++)b[this._surrogateStyle[c]]=!0;this._surrogateStyle.cssText=a,this._updateIndices();for(var c=0;c<this._surrogateStyle.length;c++)b[this._surrogateStyle[c]]=!0;for(var d in b)this._isAnimatedProperty[d]||this._style.setProperty(d,this._surrogateStyle.getPropertyValue(d))},get length(){return this._surrogateStyle.length},get parentRule(){return this._style.parentRule},_updateIndices:function(){for(;this._length<this._surrogateStyle.length;)Object.defineProperty(this,this._length,{configurable:!0,enumerable:!1,get:function(a){return function(){return this._surrogateStyle[a]}}(this._length)}),this._length++;for(;this._length>this._surrogateStyle.length;)this._length--,Object.defineProperty(this,this._length,{configurable:!0,enumerable:!1,value:void 0})},_set:function(a,b){this._style[a]=b,this._isAnimatedProperty[a]=!0},_clear:function(a){this._style[a]=this._surrogateStyle[a],delete this._isAnimatedProperty[a]}};for(var h in f)c.prototype[h]=function(a,b){return function(){var c=this._surrogateStyle[a].apply(this._surrogateStyle,arguments);return b&&(this._isAnimatedProperty[arguments[0]]||this._style[a].apply(this._style,arguments),this._updateIndices()),c}}(h,h in g);for(var i in document.documentElement.style)i in e||i in f||!function(a){b(c.prototype,a,{get:function(){return this._surrogateStyle[a]},set:function(b){this._surrogateStyle[a]=b,this._updateIndices(),this._isAnimatedProperty[a]||(this._style[a]=b)}})}(i);a.apply=function(b,c,e){d(b),b.style._set(a.propertyName(c),e)},a.clear=function(b,c){b._webAnimationsPatchedStyle&&b.style._clear(a.propertyName(c))}}(d,f),function(a){window.Element.prototype.animate=function(b,c){return a.timeline._play(a.Animation(this,b,c))}}(d),function(a){function b(a,c,d){if("number"==typeof a&&"number"==typeof c)return a*(1-d)+c*d;if("boolean"==typeof a&&"boolean"==typeof c)return.5>d?a:c;if(a.length==c.length){for(var e=[],f=0;f<a.length;f++)e.push(b(a[f],c[f],d));return e}throw"Mismatched interpolation arguments "+a+":"+c}a.Interpolation=function(a,c,d){return function(e){return d(b(a,c,e))}}}(d,f),function(a){function b(a,b,c){return Math.max(Math.min(a,c),b)}function c(c,d,e){var f=a.dot(c,d);f=b(f,-1,1);var g=[];if(1===f)g=c;else for(var h=Math.acos(f),i=1*Math.sin(e*h)/Math.sqrt(1-f*f),j=0;4>j;j++)g.push(c[j]*(Math.cos(e*h)-f*i)+d[j]*i);return g}var d=function(){function a(a,b){for(var c=[[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0]],d=0;4>d;d++)for(var e=0;4>e;e++)for(var f=0;4>f;f++)c[d][e]+=b[d][f]*a[f][e];return c}function b(a){return 0==a[0][2]&&0==a[0][3]&&0==a[1][2]&&0==a[1][3]&&0==a[2][0]&&0==a[2][1]&&1==a[2][2]&&0==a[2][3]&&0==a[3][2]&&1==a[3][3]}function c(c,d,e,f,g){for(var h=[[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]],i=0;4>i;i++)h[i][3]=g[i];for(var i=0;3>i;i++)for(var j=0;3>j;j++)h[3][i]+=c[j]*h[j][i];var k=f[0],l=f[1],m=f[2],n=f[3],o=[[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]];o[0][0]=1-2*(l*l+m*m),o[0][1]=2*(k*l-m*n),o[0][2]=2*(k*m+l*n),o[1][0]=2*(k*l+m*n),o[1][1]=1-2*(k*k+m*m),o[1][2]=2*(l*m-k*n),o[2][0]=2*(k*m-l*n),o[2][1]=2*(l*m+k*n),o[2][2]=1-2*(k*k+l*l),h=a(h,o);var p=[[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]];e[2]&&(p[2][1]=e[2],h=a(h,p)),e[1]&&(p[2][1]=0,p[2][0]=e[0],h=a(h,p)),e[0]&&(p[2][0]=0,p[1][0]=e[0],h=a(h,p));for(var i=0;3>i;i++)for(var j=0;3>j;j++)h[i][j]*=d[i];return b(h)?[h[0][0],h[0][1],h[1][0],h[1][1],h[3][0],h[3][1]]:h[0].concat(h[1],h[2],h[3])}return c}();a.composeMatrix=d,a.quat=c}(d,f),function(a){var b=0,c=function(a,b,c){this.target=a,this.currentTime=b,this.timelineTime=c,this.type="finish",this.bubbles=!1,this.cancelable=!1,this.currentTarget=a,this.defaultPrevented=!1,this.eventPhase=Event.AT_TARGET,this.timeStamp=Date.now()};a.Player=function(a){this._sequenceNumber=b++,this._currentTime=0,this._startTime=null,this.paused=!1,this._playbackRate=1,this._inTimeline=!0,this._finishedFlag=!1,this.onfinish=null,this._finishHandlers=[],this._source=a,this._inEffect=this._source._update(0),this._idle=!0,this._currentTimePending=!1},a.Player.prototype={_ensureAlive:function(){this._inEffect=this._source._update(this.currentTime),this._inTimeline||!this._inEffect&&this._finishedFlag||(this._inTimeline=!0,a.timeline._players.push(this))},_tickCurrentTime:function(a,b){a!=this._currentTime&&(this._currentTime=a,this.finished&&!b&&(this._currentTime=this._playbackRate>0?this._totalDuration:0),this._ensureAlive())},get currentTime(){return this._idle||this._currentTimePending?null:this._currentTime},set currentTime(b){b=+b,isNaN(b)||(a.restart(),this.paused||null==this._startTime||(this._startTime=this._timeline.currentTime-b/this._playbackRate),this._currentTimePending=!1,this._currentTime!=b&&(this._tickCurrentTime(b,!0),a.invalidateEffects()))},get startTime(){return this._startTime},set startTime(b){b=+b,isNaN(b)||this.paused||this._idle||(this._startTime=b,this._tickCurrentTime((this._timeline.currentTime-this._startTime)*this.playbackRate),a.invalidateEffects())},get playbackRate(){return this._playbackRate},get finished(){return!this._idle&&(this._playbackRate>0&&this._currentTime>=this._totalDuration||this._playbackRate<0&&this._currentTime<=0)},get _totalDuration(){return this._source._totalDuration},get playState(){return this._idle?"idle":null==this._startTime&&!this.paused&&0!=this.playbackRate||this._currentTimePending?"pending":this.paused?"paused":this.finished?"finished":"running"},play:function(){this.paused=!1,(this.finished||this._idle)&&(this._currentTime=this._playbackRate>0?0:this._totalDuration,this._startTime=null,a.invalidateEffects()),this._finishedFlag=!1,a.restart(),this._idle=!1,this._ensureAlive()},pause:function(){this.finished||this.paused||this._idle||(this._currentTimePending=!0),this._startTime=null,this.paused=!0},finish:function(){this._idle||(this.currentTime=this._playbackRate>0?this._totalDuration:0,this._startTime=this._totalDuration-this.currentTime,this._currentTimePending=!1)},cancel:function(){this._inEffect=!1,this._idle=!0,this.currentTime=0,this._startTime=null},reverse:function(){this._playbackRate*=-1,this._startTime=null,this.play()},addEventListener:function(a,b){"function"==typeof b&&"finish"==a&&this._finishHandlers.push(b)},removeEventListener:function(a,b){if("finish"==a){var c=this._finishHandlers.indexOf(b);c>=0&&this._finishHandlers.splice(c,1)}},_fireEvents:function(a){var b=this.finished;if((b||this._idle)&&!this._finishedFlag){var d=new c(this,this._currentTime,a),e=this._finishHandlers.concat(this.onfinish?[this.onfinish]:[]);setTimeout(function(){e.forEach(function(a){a.call(d.target,d)})},0)}this._finishedFlag=b},_tick:function(a){return this._idle||this.paused||(null==this._startTime?this.startTime=a-this._currentTime/this.playbackRate:this.finished||this._tickCurrentTime((a-this._startTime)*this.playbackRate)),this._currentTimePending=!1,this._fireEvents(a),!this._idle&&(this._inEffect||!this._finishedFlag)}}}(d,f),function(a,b){function c(a){var b=i;i=[],g(a),b.forEach(function(b){b[1](a)}),m&&g(a),f()}function d(a,b){return a._sequenceNumber-b._sequenceNumber}function e(){this._players=[],this.currentTime=window.performance&&performance.now?performance.now():0}function f(){n.forEach(function(a){a()})}function g(a){l=!1;var c=b.timeline;c.currentTime=a,c._players.sort(d),k=!1;var e=c._players;c._players=[];var f=[],g=[];e=e.filter(function(b){return b._inTimeline=b._tick(a),b._inEffect?g.push(b._source):f.push(b._source),b.finished||b.paused||b._idle||(k=!0),b._inTimeline}),n.length=0,n.push.apply(n,f),n.push.apply(n,g),c._players.push.apply(c._players,e),m=!1,k&&requestAnimationFrame(function(){})}var h=window.requestAnimationFrame,i=[],j=0;window.requestAnimationFrame=function(a){var b=j++;return 0==i.length&&h(c),i.push([b,a]),b},window.cancelAnimationFrame=function(a){i.forEach(function(b){b[0]==a&&(b[1]=function(){})})},e.prototype={_play:function(c){c._timing=a.normalizeTimingInput(c.timing);var d=new b.Player(c);return d._idle=!1,d._timeline=this,this._players.push(d),b.restart(),b.invalidateEffects(),d}};var k=!1,l=!1;b.restart=function(){return k||(k=!0,requestAnimationFrame(function(){}),l=!0),l};var m=!1;b.invalidateEffects=function(){m=!0};var n=[],o=window.getComputedStyle;Object.defineProperty(window,"getComputedStyle",{configurable:!0,enumerable:!0,value:function(){return m&&g(p.currentTime),f(),o.apply(this,arguments)}});var p=new e;b.timeline=p}(c,d,f),function(a){function b(a,b){for(var c=0,d=0;d<a.length;d++)c+=a[d]*b[d];return c}function c(a,b){return[a[0]*b[0]+a[4]*b[1]+a[8]*b[2]+a[12]*b[3],a[1]*b[0]+a[5]*b[1]+a[9]*b[2]+a[13]*b[3],a[2]*b[0]+a[6]*b[1]+a[10]*b[2]+a[14]*b[3],a[3]*b[0]+a[7]*b[1]+a[11]*b[2]+a[15]*b[3],a[0]*b[4]+a[4]*b[5]+a[8]*b[6]+a[12]*b[7],a[1]*b[4]+a[5]*b[5]+a[9]*b[6]+a[13]*b[7],a[2]*b[4]+a[6]*b[5]+a[10]*b[6]+a[14]*b[7],a[3]*b[4]+a[7]*b[5]+a[11]*b[6]+a[15]*b[7],a[0]*b[8]+a[4]*b[9]+a[8]*b[10]+a[12]*b[11],a[1]*b[8]+a[5]*b[9]+a[9]*b[10]+a[13]*b[11],a[2]*b[8]+a[6]*b[9]+a[10]*b[10]+a[14]*b[11],a[3]*b[8]+a[7]*b[9]+a[11]*b[10]+a[15]*b[11],a[0]*b[12]+a[4]*b[13]+a[8]*b[14]+a[12]*b[15],a[1]*b[12]+a[5]*b[13]+a[9]*b[14]+a[13]*b[15],a[2]*b[12]+a[6]*b[13]+a[10]*b[14]+a[14]*b[15],a[3]*b[12]+a[7]*b[13]+a[11]*b[14]+a[15]*b[15]]}function d(a){switch(a.t){case"rotatex":var b=a.d[0].rad||0,c=a.d[0].deg||0,d=c*Math.PI/180+b;return[1,0,0,0,0,Math.cos(d),Math.sin(d),0,0,-Math.sin(d),Math.cos(d),0,0,0,0,1];case"rotatey":var b=a.d[0].rad||0,c=a.d[0].deg||0,d=c*Math.PI/180+b;return[Math.cos(d),0,-Math.sin(d),0,0,1,0,0,Math.sin(d),0,Math.cos(d),0,0,0,0,1];case"rotate":case"rotatez":var b=a.d[0].rad||0,c=a.d[0].deg||0,d=c*Math.PI/180+b;return[Math.cos(d),Math.sin(d),0,0,-Math.sin(d),Math.cos(d),0,0,0,0,1,0,0,0,0,1];case"rotate3d":var e=a.d[0],f=a.d[1],g=a.d[2],b=a.d[3].rad||0,c=a.d[3].deg||0,d=c*Math.PI/180+b,h=e*e+f*f+g*g;if(0===h)e=1,f=0,g=0;else if(1!==h){var i=Math.sqrt(h);e/=i,f/=i,g/=i}var j=Math.sin(d/2),k=j*Math.cos(d/2),l=j*j;return[1-2*(f*f+g*g)*l,2*(e*f*l+g*k),2*(e*g*l-f*k),0,2*(e*f*l-g*k),1-2*(e*e+g*g)*l,2*(f*g*l+e*k),0,2*(e*g*l+f*k),2*(f*g*l-e*k),1-2*(e*e+f*f)*l,0,0,0,0,1];case"scale":return[a.d[0],0,0,0,0,a.d[1],0,0,0,0,1,0,0,0,0,1];case"scalex":return[a.d[0],0,0,0,0,1,0,0,0,0,1,0,0,0,0,1];case"scaley":return[1,0,0,0,0,a.d[0],0,0,0,0,1,0,0,0,0,1];case"scalez":return[1,0,0,0,0,1,0,0,0,0,a.d[0],0,0,0,0,1];case"scale3d":return[a.d[0],0,0,0,0,a.d[1],0,0,0,0,a.d[2],0,0,0,0,1];case"skew":var m=a.d[0].deg||0,n=a.d[0].rad||0,o=a.d[1].deg||0,p=a.d[1].rad||0,q=m*Math.PI/180+n,r=o*Math.PI/180+p;return[1,Math.tan(r),0,0,Math.tan(q),1,0,0,0,0,1,0,0,0,0,1];case"skewx":var b=a.d[0].rad||0,c=a.d[0].deg||0,d=c*Math.PI/180+b;return[1,0,0,0,Math.tan(d),1,0,0,0,0,1,0,0,0,0,1];case"skewy":var b=a.d[0].rad||0,c=a.d[0].deg||0,d=c*Math.PI/180+b;return[1,Math.tan(d),0,0,0,1,0,0,0,0,1,0,0,0,0,1];case"translate":var e=a.d[0].px||0,f=a.d[1].px||0;return[1,0,0,0,0,1,0,0,0,0,1,0,e,f,0,1];case"translatex":var e=a.d[0].px||0;return[1,0,0,0,0,1,0,0,0,0,1,0,e,0,0,1];case"translatey":var f=a.d[0].px||0;return[1,0,0,0,0,1,0,0,0,0,1,0,0,f,0,1];case"translatez":var g=a.d[0].px||0;return[1,0,0,0,0,1,0,0,0,0,1,0,0,0,g,1];case"translate3d":var e=a.d[0].px||0,f=a.d[1].px||0,g=a.d[2].px||0;return[1,0,0,0,0,1,0,0,0,0,1,0,e,f,g,1];case"perspective":var s=a.d[0].px?-1/a.d[0].px:0;return[1,0,0,0,0,1,0,0,0,0,1,s,0,0,0,1];case"matrix":return[a.d[0],a.d[1],0,0,a.d[2],a.d[3],0,0,0,0,1,0,a.d[4],a.d[5],0,1];case"matrix3d":return a.d}}function e(a){return 0===a.length?[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]:a.map(d).reduce(c)}function f(a){return[g(e(a))]}var g=function(){function a(a){return a[0][0]*a[1][1]*a[2][2]+a[1][0]*a[2][1]*a[0][2]+a[2][0]*a[0][1]*a[1][2]-a[0][2]*a[1][1]*a[2][0]-a[1][2]*a[2][1]*a[0][0]-a[2][2]*a[0][1]*a[1][0]}function c(b){for(var c=1/a(b),d=b[0][0],e=b[0][1],f=b[0][2],g=b[1][0],h=b[1][1],i=b[1][2],j=b[2][0],k=b[2][1],l=b[2][2],m=[[(h*l-i*k)*c,(f*k-e*l)*c,(e*i-f*h)*c,0],[(i*j-g*l)*c,(d*l-f*j)*c,(f*g-d*i)*c,0],[(g*k-h*j)*c,(j*e-d*k)*c,(d*h-e*g)*c,0]],n=[],o=0;3>o;o++){for(var p=0,q=0;3>q;q++)p+=b[3][q]*m[q][o];n.push(p)}return n.push(1),m.push(n),m}function d(a){return[[a[0][0],a[1][0],a[2][0],a[3][0]],[a[0][1],a[1][1],a[2][1],a[3][1]],[a[0][2],a[1][2],a[2][2],a[3][2]],[a[0][3],a[1][3],a[2][3],a[3][3]]]}function e(a,b){for(var c=[],d=0;4>d;d++){for(var e=0,f=0;4>f;f++)e+=a[f]*b[f][d];c.push(e)}return c}function f(a){var b=g(a);return[a[0]/b,a[1]/b,a[2]/b]}function g(a){return Math.sqrt(a[0]*a[0]+a[1]*a[1]+a[2]*a[2])}function h(a,b,c,d){return[c*a[0]+d*b[0],c*a[1]+d*b[1],c*a[2]+d*b[2]]}function i(a,b){return[a[1]*b[2]-a[2]*b[1],a[2]*b[0]-a[0]*b[2],a[0]*b[1]-a[1]*b[0]]}function j(j){var k=[j.slice(0,4),j.slice(4,8),j.slice(8,12),j.slice(12,16)];if(1!==k[3][3])return null;for(var l=[],m=0;4>m;m++)l.push(k[m].slice());for(var m=0;3>m;m++)l[m][3]=0;if(0===a(l))return!1;var n,o=[];if(k[0][3]||k[1][3]||k[2][3]){o.push(k[0][3]),o.push(k[1][3]),o.push(k[2][3]),o.push(k[3][3]);var p=c(l),q=d(p);n=e(o,q)}else n=[0,0,0,1];var r=k[3].slice(0,3),s=[];s.push(k[0].slice(0,3));var t=[];t.push(g(s[0])),s[0]=f(s[0]);var u=[];s.push(k[1].slice(0,3)),u.push(b(s[0],s[1])),s[1]=h(s[1],s[0],1,-u[0]),t.push(g(s[1])),s[1]=f(s[1]),u[0]/=t[1],s.push(k[2].slice(0,3)),u.push(b(s[0],s[2])),s[2]=h(s[2],s[0],1,-u[1]),u.push(b(s[1],s[2])),s[2]=h(s[2],s[1],1,-u[2]),t.push(g(s[2])),s[2]=f(s[2]),u[1]/=t[2],u[2]/=t[2];var v=i(s[1],s[2]);if(b(s[0],v)<0)for(var m=0;3>m;m++)t[m]*=-1,s[m][0]*=-1,s[m][1]*=-1,s[m][2]*=-1;var w,x,y=s[0][0]+s[1][1]+s[2][2]+1;return y>1e-4?(w=.5/Math.sqrt(y),x=[(s[2][1]-s[1][2])*w,(s[0][2]-s[2][0])*w,(s[1][0]-s[0][1])*w,.25/w]):s[0][0]>s[1][1]&&s[0][0]>s[2][2]?(w=2*Math.sqrt(1+s[0][0]-s[1][1]-s[2][2]),x=[.25*w,(s[0][1]+s[1][0])/w,(s[0][2]+s[2][0])/w,(s[2][1]-s[1][2])/w]):s[1][1]>s[2][2]?(w=2*Math.sqrt(1+s[1][1]-s[0][0]-s[2][2]),x=[(s[0][1]+s[1][0])/w,.25*w,(s[1][2]+s[2][1])/w,(s[0][2]-s[2][0])/w]):(w=2*Math.sqrt(1+s[2][2]-s[0][0]-s[1][1]),x=[(s[0][2]+s[2][0])/w,(s[1][2]+s[2][1])/w,.25*w,(s[1][0]-s[0][1])/w]),[r,t,u,x,n]}return j}();a.dot=b,a.makeMatrixDecomposition=f}(d,f),function(a){function b(a,b){var c=a.exec(b);return c?(c=a.ignoreCase?c[0].toLowerCase():c[0],[c,b.substr(c.length)]):void 0}function c(a,b){b=b.replace(/^\s*/,"");var c=a(b);return c?[c[0],c[1].replace(/^\s*/,"")]:void 0}function d(a,d,e){a=c.bind(null,a);for(var f=[];;){var g=a(e);if(!g)return[f,e];if(f.push(g[0]),e=g[1],g=b(d,e),!g||""==g[1])return[f,e];e=g[1]}}function e(a,b){for(var c=0,d=0;d<b.length&&(!/\s|,/.test(b[d])||0!=c);d++)if("("==b[d])c++;else if(")"==b[d]&&(c--,0==c&&d++,0>=c))break;var e=a(b.substr(0,d));return void 0==e?void 0:[e,b.substr(d)]}function f(a,b){for(var c=a,d=b;c&&d;)c>d?c%=d:d%=c;return c=a*b/(c+d)}function g(a){return function(b){var c=a(b);return c&&(c[0]=void 0),c}}function h(a,b){return function(c){var d=a(c);return d?d:[b,c]}}function i(b,c){for(var d=[],e=0;e<b.length;e++){var f=a.consumeTrimmed(b[e],c);if(!f||""==f[0])return;void 0!==f[0]&&d.push(f[0]),c=f[1]}return""==c?d:void 0}function j(a,b,c,d,e){for(var g=[],h=[],i=[],j=f(d.length,e.length),k=0;j>k;k++){var l=b(d[k%d.length],e[k%e.length]);if(!l)return;g.push(l[0]),h.push(l[1]),i.push(l[2])}return[g,h,function(b){var d=b.map(function(a,b){return i[b](a)}).join(c);return a?a(d):d}]}function k(a,b,c){for(var d=[],e=[],f=[],g=0,h=0;h<c.length;h++)if("function"==typeof c[h]){var i=c[h](a[g],b[g++]);d.push(i[0]),e.push(i[1]),f.push(i[2])}else!function(a){d.push(!1),e.push(!1),f.push(function(){return c[a]})}(h);return[d,e,function(a){for(var b="",c=0;c<a.length;c++)b+=f[c](a[c]);return b}]}a.consumeToken=b,a.consumeTrimmed=c,a.consumeRepeated=d,a.consumeParenthesised=e,a.ignore=g,a.optional=h,a.consumeList=i,a.mergeNestedRepeated=j.bind(null,null),a.mergeWrappedNestedRepeated=j,a.mergeList=k}(d),function(a){function b(b){function c(b){var c=a.consumeToken(/^inset/i,b);if(c)return d.inset=!0,c;var c=a.consumeLengthOrPercent(b);if(c)return d.lengths.push(c[0]),c;var c=a.consumeColor(b);return c?(d.color=c[0],c):void 0}var d={inset:!1,lengths:[],color:null},e=a.consumeRepeated(c,/^/,b);return e&&e[0].length?[d,e[1]]:void 0}function c(c){var d=a.consumeRepeated(b,/^,/,c);return d&&""==d[1]?d[0]:void 0}function d(b,c){for(;b.lengths.length<Math.max(b.lengths.length,c.lengths.length);)b.lengths.push({px:0});for(;c.lengths.length<Math.max(b.lengths.length,c.lengths.length);)c.lengths.push({px:0});if(b.inset==c.inset&&!!b.color==!!c.color){for(var d,e=[],f=[[],0],g=[[],0],h=0;h<b.lengths.length;h++){var i=a.mergeDimensions(b.lengths[h],c.lengths[h],2==h);f[0].push(i[0]),g[0].push(i[1]),e.push(i[2])}if(b.color&&c.color){var j=a.mergeColors(b.color,c.color);f[1]=j[0],g[1]=j[1],d=j[2]}return[f,g,function(a){for(var c=b.inset?"inset ":" ",f=0;f<e.length;f++)c+=e[f](a[0][f])+" ";return d&&(c+=d(a[1])),c}]}}function e(b,c,d,e){function f(a){return{inset:a,color:[0,0,0,0],lengths:[{px:0},{px:0},{px:0},{px:0}]}}for(var g=[],h=[],i=0;i<d.length||i<e.length;i++){var j=d[i]||f(e[i].inset),k=e[i]||f(d[i].inset);g.push(j),h.push(k)}return a.mergeNestedRepeated(b,c,g,h)}var f=e.bind(null,d,", ");a.addPropertiesHandler(c,f,["box-shadow","text-shadow"])}(d),function(a){function b(a){return a.toFixed(3).replace(".000","")}function c(a,b,c){return Math.min(b,Math.max(a,c))}function d(a){return/^\s*[-+]?(\d*\.)?\d+\s*$/.test(a)?Number(a):void 0}function e(a,c){return[a,c,b]}function f(a,b){return 0!=a?h(0,1/0)(a,b):void 0}function g(a,b){return[a,b,function(a){return Math.round(c(1,1/0,a))}]}function h(a,d){return function(e,f){return[e,f,function(e){return b(c(a,d,e))}]}}function i(a,b){return[a,b,Math.round]}a.clamp=c,a.addPropertiesHandler(d,h(0,1/0),["border-image-width","line-height"]),a.addPropertiesHandler(d,h(0,1),["opacity","shape-image-threshold"]),a.addPropertiesHandler(d,h(.01,1/0),["zoom"]),a.addPropertiesHandler(d,f,["flex-grow","flex-shrink"]),a.addPropertiesHandler(d,e,["zoom"]),a.addPropertiesHandler(d,g,["orphans","widows"]),a.addPropertiesHandler(d,i,["z-index"]),a.parseNumber=d,a.mergeNumbers=e,a.numberToString=b}(d,f),function(a){function b(a,b){return"visible"==a||"visible"==b?[0,1,function(c){return 0>=c?a:c>=1?b:"visible"}]:void 0}a.addPropertiesHandler(String,b,["visibility"])}(d),function(a){function b(a){a=a.trim(),e.fillStyle="#000",e.fillStyle=a;var b=e.fillStyle;if(e.fillStyle="#fff",e.fillStyle=a,b==e.fillStyle){e.fillRect(0,0,1,1);var c=e.getImageData(0,0,1,1).data;e.clearRect(0,0,1,1);var d=c[3]/255;return[c[0]*d,c[1]*d,c[2]*d,d]}}function c(b,c){return[b,c,function(b){function c(a){return Math.max(0,Math.min(255,a))}if(b[3])for(var d=0;3>d;d++)b[d]=Math.round(c(b[d]/b[3]));return b[3]=a.numberToString(a.clamp(0,1,b[3])),"rgba("+b.join(",")+")"}]}var d=document.createElementNS("http://www.w3.org/1999/xhtml","canvas");d.width=d.height=1;var e=d.getContext("2d");a.addPropertiesHandler(b,c,["background-color","border-bottom-color","border-left-color","border-right-color","border-top-color","color","outline-color","text-decoration-color"]),a.consumeColor=a.consumeParenthesised.bind(null,b),a.mergeColors=c}(d,f),function(a,b){function c(a,b){if(b=b.trim().toLowerCase(),"0"==b&&"px".search(a)>=0)return{px:0};if(/^[^(]*$|^calc/.test(b)){b=b.replace(/calc\(/g,"(");var c={};b=b.replace(a,function(a){return c[a]=null,"U"+a});for(var d="U("+a.source+")",e=b.replace(/[-+]?(\d*\.)?\d+/g,"N").replace(new RegExp("N"+d,"g"),"D").replace(/\s[+-]\s/g,"O").replace(/\s/g,""),f=[/N\*(D)/g,/(N|D)[*/]N/g,/(N|D)O\1/g,/\((N|D)\)/g],g=0;g<f.length;)f[g].test(e)?(e=e.replace(f[g],"$1"),g=0):g++;if("D"==e){for(var h in c){var i=eval(b.replace(new RegExp("U"+h,"g"),"").replace(new RegExp(d,"g"),"*0"));if(!isFinite(i))return;c[h]=i}return c}}}function d(a,b){return e(a,b,!0)}function e(b,c,d){var e,f=[];for(e in b)f.push(e);for(e in c)f.indexOf(e)<0&&f.push(e);return b=f.map(function(a){return b[a]||0}),c=f.map(function(a){return c[a]||0}),[b,c,function(b){var c=b.map(function(c,e){return 1==b.length&&d&&(c=Math.max(c,0)),a.numberToString(c)+f[e]}).join(" + ");return b.length>1?"calc("+c+")":c}]}var f="px|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc",g=c.bind(null,new RegExp(f,"g")),h=c.bind(null,new RegExp(f+"|%","g")),i=c.bind(null,/deg|rad|grad|turn/g);a.parseLength=g,a.parseLengthOrPercent=h,a.consumeLengthOrPercent=a.consumeParenthesised.bind(null,h),a.parseAngle=i,a.mergeDimensions=e;var j=a.consumeParenthesised.bind(null,g),k=a.consumeRepeated.bind(void 0,j,/^/),l=a.consumeRepeated.bind(void 0,k,/^,/);a.consumeSizePairList=l;var m=function(a){var b=l(a);return b&&""==b[1]?b[0]:void 0},n=a.mergeNestedRepeated.bind(void 0,d," "),o=a.mergeNestedRepeated.bind(void 0,n,",");a.mergeNonNegativeSizePair=n,a.addPropertiesHandler(m,o,["background-size"]),a.addPropertiesHandler(h,d,["border-bottom-width","border-image-width","border-left-width","border-right-width","border-top-width","flex-basis","font-size","height","line-height","max-height","max-width","outline-width","width"]),a.addPropertiesHandler(h,e,["border-bottom-left-radius","border-bottom-right-radius","border-top-left-radius","border-top-right-radius","bottom","left","letter-spacing","margin-bottom","margin-left","margin-right","margin-top","min-height","min-width","outline-offset","padding-bottom","padding-left","padding-right","padding-top","perspective","right","shape-margin","text-indent","top","vertical-align","word-spacing"])}(d,f),function(a){function b(b){return a.consumeLengthOrPercent(b)||a.consumeToken(/^auto/,b)}function c(c){var d=a.consumeList([a.ignore(a.consumeToken.bind(null,/^rect/)),a.ignore(a.consumeToken.bind(null,/^\(/)),a.consumeRepeated.bind(null,b,/^,/),a.ignore(a.consumeToken.bind(null,/^\)/))],c);return d&&4==d[0].length?d[0]:void 0}function d(b,c){return"auto"==b||"auto"==c?[!0,!1,function(d){var e=d?b:c;if("auto"==e)return"auto";var f=a.mergeDimensions(e,e);return f[2](f[0])}]:a.mergeDimensions(b,c)}function e(a){return"rect("+a+")"}var f=a.mergeWrappedNestedRepeated.bind(null,e,d,", ");a.parseBox=c,a.mergeBoxes=f,a.addPropertiesHandler(c,f,["clip"])}(d,f),function(a){function b(a){return function(b){var c=0;return a.map(function(a){return a===j?b[c++]:a})}}function c(a){return a
+}function d(b){if(b=b.toLowerCase().trim(),"none"==b)return[];for(var c,d=/\s*(\w+)\(([^)]*)\)/g,e=[],f=0;c=d.exec(b);){if(c.index!=f)return;f=c.index+c[0].length;var g=c[1],h=m[g];if(!h)return;var i=c[2].split(","),j=h[0];if(j.length<i.length)return;for(var n=[],o=0;o<j.length;o++){var p,q=i[o],r=j[o];if(p=q?{A:function(b){return"0"==b.trim()?l:a.parseAngle(b)},N:a.parseNumber,T:a.parseLengthOrPercent,L:a.parseLength}[r.toUpperCase()](q):{a:l,n:n[0],t:k}[r],void 0===p)return;n.push(p)}if(e.push({t:g,d:n}),d.lastIndex==b.length)return e}}function e(a){return a.toFixed(6).replace(".000000","")}function f(b,c){if(b.decompositionPair!==c){b.decompositionPair=c;var d=a.makeMatrixDecomposition(b)}if(c.decompositionPair!==b){c.decompositionPair=b;var f=a.makeMatrixDecomposition(c)}return null==d[0]||null==f[0]?[[!1],[!0],function(a){return a?c[0].d:b[0].d}]:(d[0].push(0),f[0].push(1),[d,f,function(b){var c=a.quat(d[0][3],f[0][3],b[5]),g=a.composeMatrix(b[0],b[1],b[2],c,b[4]),h=g.map(e).join(",");return h}])}function g(a){return a.replace(/[xy]/,"")}function h(a){return a.replace(/(x|y|z|3d)?$/,"3d")}function i(b,c){var d=a.makeMatrixDecomposition&&!0,e=!1;if(!b.length||!c.length){b.length||(e=!0,b=c,c=[]);for(var i=0;i<b.length;i++){var j=b[i].t,k=b[i].d,l="scale"==j.substr(0,5)?1:0;c.push({t:j,d:k.map(function(a){if("number"==typeof a)return l;var b={};for(var c in a)b[c]=l;return b})})}}var n=function(a,b){return"perspective"==a&&"perspective"==b||("matrix"==a||"matrix3d"==a)&&("matrix"==b||"matrix3d"==b)},o=[],p=[],q=[];if(b.length!=c.length){if(!d)return;var r=f(b,c);o=[r[0]],p=[r[1]],q=[["matrix",[r[2]]]]}else for(var i=0;i<b.length;i++){var j,s=b[i].t,t=c[i].t,u=b[i].d,v=c[i].d,w=m[s],x=m[t];if(n(s,t)){if(!d)return;var r=f([b[i]],[c[i]]);o.push(r[0]),p.push(r[1]),q.push(["matrix",[r[2]]])}else{if(s==t)j=s;else if(w[2]&&x[2]&&g(s)==g(t))j=g(s),u=w[2](u),v=x[2](v);else{if(!w[1]||!x[1]||h(s)!=h(t)){if(!d)return;var r=f(b,c);o=[r[0]],p=[r[1]],q=[["matrix",[r[2]]]];break}j=h(s),u=w[1](u),v=x[1](v)}for(var y=[],z=[],A=[],B=0;B<u.length;B++){var C="number"==typeof u[B]?a.mergeNumbers:a.mergeDimensions,r=C(u[B],v[B]);y[B]=r[0],z[B]=r[1],A.push(r[2])}o.push(y),p.push(z),q.push([j,A])}}if(e){var D=o;o=p,p=D}return[o,p,function(a){return a.map(function(a,b){var c=a.map(function(a,c){return q[b][1][c](a)}).join(",");return"matrix"==q[b][0]&&16==c.split(",").length&&(q[b][0]="matrix3d"),q[b][0]+"("+c+")"}).join(" ")}]}var j=null,k={px:0},l={deg:0},m={matrix:["NNNNNN",[j,j,0,0,j,j,0,0,0,0,1,0,j,j,0,1],c],matrix3d:["NNNNNNNNNNNNNNNN",c],rotate:["A"],rotatex:["A"],rotatey:["A"],rotatez:["A"],rotate3d:["NNNA"],perspective:["L"],scale:["Nn",b([j,j,1]),c],scalex:["N",b([j,1,1]),b([j,1])],scaley:["N",b([1,j,1]),b([1,j])],scalez:["N",b([1,1,j])],scale3d:["NNN",c],skew:["Aa",null,c],skewx:["A",null,b([j,l])],skewy:["A",null,b([l,j])],translate:["Tt",b([j,j,k]),c],translatex:["T",b([j,k,k]),b([j,k])],translatey:["T",b([k,j,k]),b([k,j])],translatez:["L",b([k,k,j])],translate3d:["TTL",c]};a.addPropertiesHandler(d,i,["transform"])}(d,f),function(a){function b(a){var b=Number(a);return isNaN(b)||100>b||b>900||b%100!==0?void 0:b}function c(b){return b=100*Math.round(b/100),b=a.clamp(100,900,b),400===b?"normal":700===b?"bold":String(b)}function d(a,b){return[a,b,c]}a.addPropertiesHandler(b,d,["font-weight"])}(d),function(a){function b(a){var b={};for(var c in a)b[c]=-a[c];return b}function c(b){return a.consumeToken(/^(left|center|right|top|bottom)\b/i,b)||a.consumeLengthOrPercent(b)}function d(b,d){var e=a.consumeRepeated(c,/^/,d);if(e&&""==e[1]){var f=e[0];if(f[0]=f[0]||"center",f[1]=f[1]||"center",3==b&&(f[2]=f[2]||{px:0}),f.length==b){if(/top|bottom/.test(f[0])||/left|right/.test(f[1])){var h=f[0];f[0]=f[1],f[1]=h}if(/left|right|center|Object/.test(f[0])&&/top|bottom|center|Object/.test(f[1]))return f.map(function(a){return"object"==typeof a?a:g[a]})}}}function e(d){var e=a.consumeRepeated(c,/^/,d);if(e){for(var f=e[0],h=[{"%":50},{"%":50}],i=0,j=!1,k=0;k<f.length;k++){var l=f[k];"string"==typeof l?(j=/bottom|right/.test(l),i={left:0,right:0,center:i,top:1,bottom:1}[l],h[i]=g[l],"center"==l&&i++):(j&&(l=b(l),l["%"]=(l["%"]||0)+100),h[i]=l,i++,j=!1)}return[h,e[1]]}}function f(b){var c=a.consumeRepeated(e,/^,/,b);return c&&""==c[1]?c[0]:void 0}var g={left:{"%":0},center:{"%":50},right:{"%":100},top:{"%":0},bottom:{"%":100}},h=a.mergeNestedRepeated.bind(null,a.mergeDimensions," ");a.addPropertiesHandler(d.bind(null,3),h,["transform-origin"]),a.addPropertiesHandler(d.bind(null,2),h,["perspective-origin"]),a.consumePosition=e,a.mergeOffsetList=h;var i=a.mergeNestedRepeated.bind(null,h,", ");a.addPropertiesHandler(f,i,["background-position","object-position"])}(d),function(a){function b(b){var c=a.consumeToken(/^circle/,b);if(c&&c[0])return["circle"].concat(a.consumeList([a.ignore(a.consumeToken.bind(void 0,/^\(/)),d,a.ignore(a.consumeToken.bind(void 0,/^at/)),a.consumePosition,a.ignore(a.consumeToken.bind(void 0,/^\)/))],c[1]));var f=a.consumeToken(/^ellipse/,b);if(f&&f[0])return["ellipse"].concat(a.consumeList([a.ignore(a.consumeToken.bind(void 0,/^\(/)),e,a.ignore(a.consumeToken.bind(void 0,/^at/)),a.consumePosition,a.ignore(a.consumeToken.bind(void 0,/^\)/))],f[1]));var g=a.consumeToken(/^polygon/,b);return g&&g[0]?["polygon"].concat(a.consumeList([a.ignore(a.consumeToken.bind(void 0,/^\(/)),a.optional(a.consumeToken.bind(void 0,/^nonzero\s*,|^evenodd\s*,/),"nonzero,"),a.consumeSizePairList,a.ignore(a.consumeToken.bind(void 0,/^\)/))],g[1])):void 0}function c(b,c){return b[0]===c[0]?"circle"==b[0]?a.mergeList(b.slice(1),c.slice(1),["circle(",a.mergeDimensions," at ",a.mergeOffsetList,")"]):"ellipse"==b[0]?a.mergeList(b.slice(1),c.slice(1),["ellipse(",a.mergeNonNegativeSizePair," at ",a.mergeOffsetList,")"]):"polygon"==b[0]&&b[1]==c[1]?a.mergeList(b.slice(2),c.slice(2),["polygon(",b[1],g,")"]):void 0:void 0}var d=a.consumeParenthesised.bind(null,a.parseLengthOrPercent),e=a.consumeRepeated.bind(void 0,d,/^/),f=a.mergeNestedRepeated.bind(void 0,a.mergeDimensions," "),g=a.mergeNestedRepeated.bind(void 0,f,",");a.addPropertiesHandler(b,c,["shape-outside"])}(d),function(a){function b(a,b){b.concat([a]).forEach(function(b){b in document.documentElement.style&&(c[a]=b)})}var c={};b("transform",["webkitTransform","msTransform"]),b("transformOrigin",["webkitTransformOrigin"]),b("perspective",["webkitPerspective"]),b("perspectiveOrigin",["webkitPerspectiveOrigin"]),a.propertyName=function(a){return c[a]||a}}(d,f)}(),!function(a,b){function c(a){var b=window.document.timeline;b.currentTime=a,b._discardPlayers(),0==b._players.length?d=!1:requestAnimationFrame(c)}b.AnimationTimeline=function(){this._players=[],this.currentTime=void 0},b.AnimationTimeline.prototype={getAnimationPlayers:function(){return this._discardPlayers(),this._players.slice()},_discardPlayers:function(){this._players=this._players.filter(function(a){return"finished"!=a.playState&&"idle"!=a.playState})},play:function(a){var c=new b.Player(a);return this._players.push(c),b.restartWebAnimationsNextTick(),c.play(),c}};var d=!1;b.restartWebAnimationsNextTick=function(){d||(d=!0,requestAnimationFrame(c))};var e=new b.AnimationTimeline;b.timeline=e;try{Object.defineProperty(window.document,"timeline",{configurable:!0,get:function(){return e}})}catch(f){}try{window.document.timeline=e}catch(f){}}(c,e,f),function(a,b){b.Player=function(a){this.source=a,a&&(a.player=this),this._isGroup=!1,this._player=null,this._childPlayers=[],this._callback=null,this._rebuildUnderlyingPlayer(),this._player.cancel()},b.Player.prototype={_rebuildUnderlyingPlayer:function(){this._player&&(this._player.cancel(),this._player=null),(!this.source||this.source instanceof window.Animation)&&(this._player=b.newUnderlyingPlayerForAnimation(this.source),b.bindPlayerForAnimation(this)),(this.source instanceof window.AnimationSequence||this.source instanceof window.AnimationGroup)&&(this._player=b.newUnderlyingPlayerForGroup(this.source),b.bindPlayerForGroup(this))},get paused(){return this._player.paused},get playState(){return this._player.playState},get onfinish(){return this._onfinish},set onfinish(a){"function"==typeof a?(this._onfinish=a,this._player.onfinish=function(b){b.target=this,a.call(this,b)}.bind(this)):(this._player.onfinish=a,this.onfinish=this._player.onfinish)},get currentTime(){return this._player.currentTime},set currentTime(a){this._player.currentTime=a,this._register(),this._forEachChild(function(b,c){b.currentTime=a-c})},get startTime(){return this._player.startTime},set startTime(a){this._player.startTime=a,this._register(),this._forEachChild(function(b,c){b.startTime=a+c})},get playbackRate(){return this._player.playbackRate},get finished(){return this._player.finished},play:function(){this._player.play(),this._register(),b.awaitStartTime(this),this._forEachChild(function(a){var b=a.currentTime;a.play(),a.currentTime=b})},pause:function(){this._player.pause(),this._register(),this._forEachChild(function(a){a.pause()})},finish:function(){this._player.finish(),this._register()},cancel:function(){this._player.cancel(),this._register(),this._removePlayers()},reverse:function(){this._player.reverse(),b.awaitStartTime(this),this._register(),this._forEachChild(function(a,b){a.reverse(),a.startTime=this.startTime+b*this.playbackRate,a.currentTime=this.currentTime+b*this.playbackRate})},addEventListener:function(a,b){var c=b;"function"==typeof b&&(c=function(a){a.target=this,b.call(this,a)}.bind(this),b._wrapper=c),this._player.addEventListener(a,c)},removeEventListener:function(a,b){this._player.removeEventListener(a,b&&b._wrapper||b)},_removePlayers:function(){for(;this._childPlayers.length;)this._childPlayers.pop().cancel()},_forEachChild:function(a){var b=0;this._childPlayers.forEach(function(c){a.call(this,c,b),this.source instanceof window.AnimationSequence&&(b+=c.source.activeDuration)}.bind(this))}}}(c,e,f),function(a,b){function c(a){return a._timing.delay+a.activeDuration+a._timing.endDelay}function d(b){this._frames=a.normalizeKeyframes(b)}function e(){for(var a=!1;h.length;)h.shift()._updateChildren(),a=!0;return a}d.prototype={getFrames:function(){return this._frames}},b.Animation=function(b,c,e){return this.target=b,this._timingInput=e,this._timing=a.normalizeTimingInput(e),this.timing=a.makeTiming(e),this.effect="function"==typeof c?c:new d(c),this._effect=c,this.activeDuration=a.calculateActiveDuration(this._timing),this};var f=Element.prototype.animate;Element.prototype.animate=function(a,c){return b.timeline.play(new b.Animation(this,a,c))};var g=document.createElementNS("http://www.w3.org/1999/xhtml","div");b.newUnderlyingPlayerForAnimation=function(a){var b=a.target||g,c=a._effect;return"function"==typeof c&&(c=[]),f.apply(b,[c,a._timingInput])},b.bindPlayerForAnimation=function(a){a.source&&"function"==typeof a.source.effect&&b.bindPlayerForCustomEffect(a)};var h=[];b.awaitStartTime=function(a){null===a.startTime&&a._isGroup&&(0==h.length&&requestAnimationFrame(e),h.push(a))};var i=window.getComputedStyle;Object.defineProperty(window,"getComputedStyle",{configurable:!0,enumerable:!0,value:function(){var a=i.apply(this,arguments);return e()&&(a=i.apply(this,arguments)),a}}),b.Player.prototype._updateChildren=function(){if(!this.paused&&this.source&&this._isGroup)for(var a=this.source._timing.delay,b=0;b<this.source.children.length;b++){var d,e=this.source.children[b];b>=this._childPlayers.length?(d=window.document.timeline.play(e),this._childPlayers.push(d)):d=this._childPlayers[b],e.player=this.source.player,d.startTime!=this.startTime+a&&(null===this.startTime?(d.currentTime=this.source.player.currentTime-a,d._startTime=null):d.startTime=this.startTime+a,d._updateChildren()),-1==this.playbackRate&&this.currentTime<a&&-1!==d.currentTime&&(d.currentTime=-1),this.source instanceof window.AnimationSequence&&(a+=c(e))}},window.Animation=b.Animation,window.Element.prototype.getAnimationPlayers=function(){return document.timeline.getAnimationPlayers().filter(function(a){return null!==a.source&&a.source.target==this}.bind(this))},b.groupChildDuration=c}(c,e,f),function(a,b){function c(a){a._registered||(a._registered=!0,f.push(a),g||(g=!0,requestAnimationFrame(d)))}function d(){var a=f;f=[],a.sort(function(a,b){return a._sequenceNumber-b._sequenceNumber}),a.filter(function(a){return a(),(!a._player||a._player.finished||a._player.paused)&&(a._registered=!1),a._registered}),f.push.apply(f,a),f.length?(g=!0,requestAnimationFrame(d)):g=!1}var e=(document.createElementNS("http://www.w3.org/1999/xhtml","div"),0);b.bindPlayerForCustomEffect=function(b){var d=b.source.target,f=b.source.effect,g=b.source.timing,h=void 0;g=a.normalizeTimingInput(g);var i=function(){var c=i._player?i._player.currentTime:null;null!==c&&(c=a.calculateTimeFraction(a.calculateActiveDuration(g),c,g),isNaN(c)&&(c=null)),c!==h&&f(c,d,b.source),h=c};i._player=b,i._registered=!1,i._sequenceNumber=e++,b._callback=i,c(i)};var f=[],g=!1;b.Player.prototype._register=function(){this._callback&&c(this._callback)}}(c,e,f),function(a,b){function c(b,c){this.children=b||[],this._timing=a.normalizeTimingInput(c,!0),this.timing=a.makeTiming(c,!0),"auto"===this._timing.duration&&(this._timing.duration=this.activeDuration)}window.AnimationSequence=function(){c.apply(this,arguments)},window.AnimationGroup=function(){c.apply(this,arguments)},window.AnimationSequence.prototype={get activeDuration(){var a=0;return this.children.forEach(function(c){a+=b.groupChildDuration(c)}),Math.max(a,0)}},window.AnimationGroup.prototype={get activeDuration(){var a=0;return this.children.forEach(function(c){a=Math.max(a,b.groupChildDuration(c))}),a}},b.newUnderlyingPlayerForGroup=function(a){var c,d=function(a){var b=c._wrapper;return b.source?null==a?void b._removePlayers():void(null!==b.startTime&&b._updateChildren()):void 0};return c=b.timeline.play(new b.Animation(null,d,a._timing))},b.bindPlayerForGroup=function(a){a._player._wrapper=a,a._isGroup=!0,b.awaitStartTime(a),a._updateChildren()}}(c,e,f)}({},function(){return this}());
+//# sourceMappingURL=web-animations-next.min.js.map
\ No newline at end of file
diff --git a/third_party/web-animations-js/sources/web-animations-next.min.js.map b/third_party/web-animations-js/sources/web-animations-next.min.js.map
new file mode 100644
index 0000000..e5731b47
--- /dev/null
+++ b/third_party/web-animations-js/sources/web-animations-next.min.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"web-animations-next.min.js","sources":["src/scope.js","src/timing-utilities.js","src/normalize-keyframes.js","src/deprecation.js","src/animation-node.js","src/effect.js","src/property-interpolation.js","src/animation.js","src/apply-preserving-inline-style.js","src/element-animatable.js","src/interpolation.js","src/matrix-interpolation.js","src/player.js","src/tick.js","src/timeline.js","src/web-animations-next-player.js","src/animation-constructor.js","src/effect-callback.js","src/group-constructors.js"],"names":["webAnimationsShared","webAnimations1","webAnimationsNext","webAnimationsTesting","shared","makeTiming","timingInput","forGroup","timing","delay","endDelay","fill","iterationStart","iterations","duration","playbackRate","direction","easing","isNaN","undefined","Object","getOwnPropertyNames","forEach","property","fills","indexOf","directions","isDeprecated","normalizeTimingInput","toTimingFunction","cubic","a","b","c","d","linear","x","f","m","start","end","mid","xEst","Math","abs","step","count","pos","stepSize","cubicData","cubicBezierRe","exec","apply","this","slice","map","Number","stepData","stepRe","Start","middle","Middle","End","preset","presets","calculateActiveDuration","repeatedDuration","calculatePhase","activeDuration","localTime","PhaseNone","PhaseBefore","PhaseAfter","PhaseActive","calculateActiveTime","fillMode","phase","calculateScaledActiveTime","activeTime","startOffset","calculateIterationTime","iterationDuration","scaledActiveTime","Infinity","calculateCurrentIteration","iterationTime","floor","calculateTransformedTime","currentIteration","currentIterationIsOdd","currentDirectionIsForwards","directedTime","timeFraction","calculateTimeFraction","split","ease","ease-in","ease-out","ease-in-out","step-start","step-middle","step-end","numberString","RegExp","antiAlias","value","aliases","expandShorthandAndAntiAlias","result","longProperties","shorthandToLonghand","shorthandExpanderElem","style","i","longProperty","longhandValue","normalizeKeyframes","effectInput","spaceKeyframes","length","keyframeEffect","offset","previousIndex","previousOffset","j","Array","isArray","TypeError","originalKeyframe","keyframe","member","memberValue","isFinite","type","DOMException","NOT_SUPPORTED_ERR","name","message","everyFrameHasOffset","code","INVALID_MODIFICATION_ERR","filter","background","border","borderBottom","borderColor","borderLeft","borderRadius","borderRight","borderTop","borderWidth","flex","font","margin","outline","padding","document","createElementNS","borderWidthAliases","thin","medium","thick","borderBottomWidth","borderLeftWidth","borderRightWidth","borderTopWidth","fontSize","xx-small","x-small","small","large","x-large","xx-large","fontWeight","normal","bold","outlineWidth","textShadow","none","boxShadow","silenced","feature","date","advice","plural","auxVerb","today","Date","expiry","setMonth","getMonth","console","warn","toDateString","deprecated","Error","AnimationNode","animationNode","_totalDuration","scope","testing","makePropertySpecificKeyframeGroups","propertySpecificKeyframeGroups","propertySpecificKeyframe","group","makeInterpolations","interpolations","groupName","startTime","endTime","startValue","endValue","interpolation","propertyInterpolation","leftInterpolation","rightInterpolation","convertEffectInput","target","fraction","offsetFraction","localDuration","scaledLocalTime","clear","addPropertyHandler","propertyHandlers","push","merger","addPropertiesHandler","properties","parser","replace","toUpperCase","right","handlers","parsedRight","parsedLeft","interpolationArgs","t","Interpolation","bool","effect","animation","_update","_clear","_hasSameTarget","otherTarget","_isCurrent","NullAnimation","nullAnimation","configureProperty","object","descriptor","enumerable","configurable","defineProperty","AnimatedCSSStyleDeclaration","element","_surrogateStyle","_style","_length","_isAnimatedProperty","ensureStyleIsPatched","_webAnimationsPatchedStyle","animatedStyle","get","_","styleAttributes","cssText","parentRule","styleMethods","getPropertyCSSValue","getPropertyPriority","getPropertyValue","item","removeProperty","setProperty","styleMutatingMethods","prototype","isAffectedProperty","_updateIndices",{"end":{"file":"src/apply-preserving-inline-style.js","comments_before":[],"nlb":false,"endpos":2110,"pos":2103,"col":8,"line":64,"value":"cssText","type":"name"},"start":{"file":"src/apply-preserving-inline-style.js","comments_before":[],"nlb":false,"endpos":2110,"pos":2103,"col":8,"line":64,"value":"cssText","type":"name"},"name":"cssText"},"index","_set","method","modifiesStyle","arguments","documentElement","propertyName","window","timeline","Animation","interpolate","to","from","r","convertToString","clamp","max","min","quat","toQ","product","fromQ","acos","sin","theta","composeMatrix","multiply","k","scale","skew","perspective","matrix","translate","rotMatrix","z","w","y","temp","concat","sequenceNumber","AnimationPlayerEvent","currentTime","timelineTime","bubbles","cancelable","currentTarget","defaultPrevented","eventPhase","AT_TARGET","timeStamp","source","_sequenceNumber","_currentTime","paused","_playbackRate","_inTimeline","_finishedFlag","_finishHandlers","_source","_inEffect","_idle","_currentTimePending","Player","_ensureAlive","_players","_tickCurrentTime","newTime","ignoreLimit","finished","restart","_startTime","_timeline","invalidateEffects","playState","play","cancel","reverse","addEventListener","handler","removeEventListener","splice","_fireEvents","baseTime","onfinish","setTimeout","call","event","_tick","processRafCallbacks","processing","rafCallbacks","tick","entry","needsRetick","applyPendingEffects","comparePlayers","leftPlayer","rightPlayer","InternalTimeline","performance","now","pendingEffects","hasRestartedThisFrame","ticking","updatingPlayers","newPendingClears","newPendingEffects","player","webAnimationsNextTick","_discardPlayers","requestAnimationFrame","AnimationTimeline","getAnimationPlayers","restartWebAnimationsNextTick","e","_isGroup","_player","_childPlayers","_callback","_rebuildUnderlyingPlayer","newUnderlyingPlayerForAnimation","bindPlayerForAnimation","AnimationSequence","AnimationGroup","newUnderlyingPlayerForGroup","bindPlayerForGroup","_onfinish","v","bind","_register","_forEachChild","child","awaitStartTime","time","pause","finish","_removePlayers","wrapped","_wrapper","pop","groupChildDuration","node","_timing","KeyframeEffect","_frames","updatePendingGroups","updated","pendingGroups","shift","_updateChildren","getFrames","_timingInput","_effect","originalElementAnimate","Element","animate","nullTarget","bindPlayerForCustomEffect","groupPlayer","originalGetComputedStyle","getComputedStyle","children","childPlayer","register","callback","_registered","callbacks","updating","sort","left","last","constructor","total","underlyingPlayer","ticker","tf"],"mappings":";;;;;;;;;;;;;;CAcA,SAAIA,EAAAA,GACAC,EAAAA,QACAC,CAFJ,IAAIF,MACAC,KACAC,KAGEC,EAAuB,MCL7B,SAAUC,GAKR,QAASC,GAAWC,EAAaC,GAC/B,GAAIC,IACFC,MAAO,EACPC,SAAU,EACVC,KAAMJ,EAAW,OAAS,OAC1BK,eAAgB,EAChBC,WAAY,EACZC,SAAUP,EAAW,OAAS,EAC9BQ,aAAc,EACdC,UAAW,SACXC,OAAQ,SAyBV,OAvB0B,gBAAfX,IAA4BY,MAAMZ,GAElBa,SAAhBb,GACTc,OAAOC,oBAAoBf,GAAagB,QAAQ,SAASC,GACvD,GAA6B,QAAzBjB,EAAYiB,GAAqB,CACnC,IAA+B,gBAApBf,GAAOe,IAAqC,YAAZA,KACL,gBAAzBjB,GAAYiB,IAAyBL,MAAMZ,EAAYiB,KAChE,MAGJ,IAAiB,QAAZA,GAAgE,IAAxCC,EAAMC,QAAQnB,EAAYiB,IACrD,MAEF,IAAiB,aAAZA,GAA0E,IAA7CG,EAAWD,QAAQnB,EAAYiB,IAC/D,MAEF,IAAgB,gBAAZA,GAA8BnB,EAAOuB,aAAa,+BAAgC,aAAc,6CAClG,MAEFnB,GAAOe,GAAYjB,EAAYiB,MAlBnCf,EAAOM,SAAWR,EAsBbE,EAGT,QAASoB,GAAqBtB,EAAaC,GACzC,GAAIC,GAASH,EAAWC,EAAaC,EAErC,OADAC,GAAOS,OAASY,EAAiBrB,EAAOS,QACjCT,EAGT,QAASsB,GAAMC,EAAGC,EAAGC,EAAGC,GACtB,MAAQ,GAAJH,GAASA,EAAI,GAAS,EAAJE,GAASA,EAAI,EAC1BE,EAEF,SAASC,GAIZ,QAASC,GAAEN,EAAGC,EAAGM,GAAK,MAAO,GAAIP,GAAK,EAAIO,IAAM,EAAIA,GAAKA,EAAI,EAAIN,GAAK,EAAIM,GAAKA,EAAIA,EAAIA,EAAIA,EAAIA,EAFjG,IADA,GAAIC,GAAQ,EAAGC,EAAM,IACX,CACR,GAAIC,IAAOF,EAAQC,GAAO,EAEtBE,EAAOL,EAAEN,EAAGE,EAAGQ,EACnB,IAAIE,KAAKC,IAAIR,EAAIM,GAAQ,KACvB,MAAOL,GAAEL,EAAGE,EAAGO,EAENL,GAAPM,EACFH,EAAQE,EAERD,EAAMC,IAUd,QAASI,GAAKC,EAAOC,GACnB,MAAO,UAASX,GACd,GAAIA,GAAK,EACP,MAAO,EAET,IAAIY,GAAW,EAAIF,CAEnB,OADAV,IAAKW,EAAMC,EACJZ,EAAIA,EAAIY,GAmBnB,QAASnB,GAAiBZ,GACxB,GAAIgC,GAAYC,EAAcC,KAAKlC,EACnC,IAAIgC,EACF,MAAOnB,GAAMsB,MAAMC,KAAMJ,EAAUK,MAAM,GAAGC,IAAIC,QAElD,IAAIC,GAAWC,EAAOP,KAAKlC,EAC3B,IAAIwC,EACF,MAAOZ,GAAKW,OAAOC,EAAS,KAAMlB,MAASoB,EAAOC,OAAUC,EAAQrB,IAAOsB,GAAKL,EAAS,IAE3F,IAAIM,GAASC,EAAQ/C,EACrB,OAAI8C,GACKA,EAEF5B,EAGT,QAAS8B,GAAwBzD,GAC/B,MAAOmC,MAAKC,IAAIsB,EAAiB1D,GAAUA,EAAOO,cAGpD,QAASmD,GAAiB1D,GACxB,MAAOA,GAAOM,SAAWN,EAAOK,WAQlC,QAASsD,GAAeC,EAAgBC,EAAW7D,GACjD,MAAiB,OAAb6D,EACKC,EAELD,EAAY7D,EAAOC,MACd8D,EAELF,GAAa7D,EAAOC,MAAQ2D,EACvBI,EAEFC,EAGT,QAASC,GAAoBN,EAAgBO,EAAUN,EAAWO,EAAOnE,GACvE,OAAQmE,GACN,IAAKL,GACH,MAAgB,aAAZI,GAAuC,QAAZA,EACtB,EACF,IACT,KAAKF,GACH,MAAOJ,GAAY5D,CACrB,KAAK+D,GACH,MAAgB,YAAZG,GAAsC,QAAZA,EACrBP,EACF,IACT,KAAKE,GACH,MAAO,OAIb,QAASO,GAA0BT,EAAgBU,EAAYC,EAAavE,GAC1E,OAAQA,EAAOO,aAAe,EAAI+D,EAAaV,EAAiBU,GAActE,EAAOO,aAAegE,EAGtG,QAASC,GAAuBC,EAAmBf,EAAkBgB,EAAkBH,EAAavE,GAClG,MAAyB2E,GAAAA,IAArBD,GAAiCA,IAAAA,GAAsBC,GAAaD,EAAmBH,GAAeb,GAAoB1D,EAAOK,aAAgBL,EAAOK,WAAaL,EAAOI,gBAAkB,GAAK,EAC9LqE,EAGFC,EAAmBD,EAG5B,QAASG,GAA0BH,EAAmBI,EAAeH,EAAkB1E,GACrF,MAAyB,KAArB0E,EACK,EAELG,GAAiBJ,EACZzE,EAAOI,eAAiBJ,EAAOK,WAAa,EAE9C8B,KAAK2C,MAAMJ,EAAmBD,GAGvC,QAASM,GAAyBC,EAAkBP,EAAmBI,EAAe7E,GACpF,GAAIiF,GAAwBD,EAAmB,GAAK,EAChDE,EAAiD,UAApBlF,EAAOQ,WAAyBR,EAAOQ,YAAcyE,EAAwB,oBAAsB,aAChIE,EAAeD,EAA6BL,EAAgBJ,EAAoBI,EAChFO,EAAeD,EAAeV,CAClC,OAAOA,GAAoBzE,EAAOS,OAAO2E,GAG3C,QAASC,GAAsBzB,EAAgBC,EAAW7D,GACxD,GAAIoE,GAAQT,EAAeC,EAAgBC,EAAW7D,GAClDsE,EAAaJ,EAAoBN,EAAgB5D,EAAOG,KAAM0D,EAAWO,EAAOpE,EAAOC,MAC3F,IAAmB,OAAfqE,EACF,MAAO,KACT,IAAuB,IAAnBV,EACF,MAAOQ,KAAUL,EAAc,EAAI,CACrC,IAAIQ,GAAcvE,EAAOI,eAAiBJ,EAAOM,SAC7CoE,EAAmBL,EAA0BT,EAAgBU,EAAYC,EAAavE,GACtF6E,EAAgBL,EAAuBxE,EAAOM,SAAUoD,EAAiB1D,GAAS0E,EAAkBH,EAAavE,GACjHgF,EAAmBJ,EAA0B5E,EAAOM,SAAUuE,EAAeH,EAAkB1E,EACnG,OAAO+E,GAAyBC,EAAkBhF,EAAOM,SAAUuE,EAAe7E,GAAUA,EAAOM,SAvMrG,GAAIU,GAAQ,0BAA0BsE,MAAM,KACxCpE,EAAa,sCAAsCoE,MAAM,KAoEzDnC,EAAQ,EACRE,EAAS,GACTC,EAAM,EAaNE,GACF+B,KAAQjE,EAAM,IAAM,GAAK,IAAM,GAC/BkE,UAAWlE,EAAM,IAAM,EAAG,EAAG,GAC7BmE,WAAYnE,EAAM,EAAG,EAAG,IAAM,GAC9BoE,cAAepE,EAAM,IAAM,EAAG,IAAM,GACpCqE,aAActD,EAAK,EAAGc,GACtByC,cAAevD,EAAK,EAAGgB,GACvBwC,WAAYxD,EAAK,EAAGiB,IAGlBwC,EAAe,qCACfpD,EAAgB,GAAIqD,QAAO,kBAAoBD,EAAe,IAAMA,EAAe,IAAMA,EAAe,IAAMA,EAAe,OAC7H5C,EAAS,gDACTvB,EAAS,SAASC,GAAK,MAAOA,IA0B9BkC,EAAY,EACZC,EAAc,EACdC,EAAa,EACbC,EAAc,CA4ElBrE,GAAOC,WAAaA,EACpBD,EAAOwB,qBAAuBA,EAC9BxB,EAAO6D,wBAA0BA,EACjC7D,EAAOyF,sBAAwBA,EAC/BzF,EAAO+D,eAAiBA,EACxB/D,EAAOyB,iBAAmBA,GAkBzB7B,EAAqBG,GCnOxB,SAAUC,GAmIR,QAASoG,GAAUjF,EAAUkF,GAC3B,MAAIlF,KAAYmF,GACPA,EAAQnF,GAAUkF,IAAUA,EAE9BA,EAIT,QAASE,GAA4BpF,EAAUkF,EAAOG,GACpD,GAAIC,GAAiBC,EAAoBvF,EACzC,IAAIsF,EAAgB,CAClBE,EAAsBC,MAAMzF,GAAYkF,CACxC,KAAK,GAAIQ,KAAKJ,GAAgB,CAC5B,GAAIK,GAAeL,EAAeI,GAC9BE,EAAgBJ,EAAsBC,MAAME,EAChDN,GAAOM,GAAgBV,EAAUU,EAAcC,QAGjDP,GAAOrF,GAAYiF,EAAUjF,EAAUkF,GAI3C,QAASW,GAAmBC,GA4D1B,QAASC,KACP,GAAIC,GAASC,EAAeD,MACa,OAArCC,EAAeD,EAAS,GAAGE,SAC7BD,EAAeD,EAAS,GAAGE,OAAS,GAClCF,EAAS,GAAiC,MAA5BC,EAAe,GAAGC,SAClCD,EAAe,GAAGC,OAAS,EAI7B,KAAK,GAFDC,GAAgB,EAChBC,EAAiBH,EAAe,GAAGC,OAC9BR,EAAI,EAAOM,EAAJN,EAAYA,IAAK,CAC/B,GAAIQ,GAASD,EAAeP,GAAGQ,MAC/B,IAAc,MAAVA,EAAgB,CAClB,IAAK,GAAIG,GAAI,EAAOX,EAAIS,EAARE,EAAuBA,IACrCJ,EAAeE,EAAgBE,GAAGH,OAASE,GAAkBF,EAASE,GAAkBC,GAAKX,EAAIS,EACnGA,GAAgBT,EAChBU,EAAiBF,IA1EvB,IAAKI,MAAMC,QAAQT,IAAgC,OAAhBA,EACjC,KAAM,IAAIU,WAAU,wDAEtB,IAAmB,MAAfV,EACF,QAmCF,KAAK,GAjCDG,GAAiBH,EAAY9D,IAAI,SAASyE,GAC5C,GAAIC,KACJ,KAAK,GAAIC,KAAUF,GAAkB,CACnC,GAAIG,GAAcH,EAAiBE,EACnC,IAAc,UAAVA,GACF,GAAmB,MAAfC,IACFA,EAAc3E,OAAO2E,IAChBC,SAASD,IACZ,KAAM,IAAIJ,WAAU,yCAEnB,CAAA,GAAc,aAAVG,EACT,MACEG,KAAMC,aAAaC,kBACnBC,KAAM,oBACNC,QAAS,mCAGXN,GADmB,UAAVD,EACK9H,EAAOyB,iBAAiBsG,GAExB,GAAKA,EAErBxB,EAA4BuB,EAAQC,EAAaF,GAMnD,MAJuB9G,SAAnB8G,EAASR,SACXQ,EAASR,OAAS,MACGtG,QAAnB8G,EAAShH,SACXgH,EAAShH,OAASb,EAAOyB,iBAAiB,WACrCoG,IAGLS,GAAAA,EAEAf,EAAAA,GAAkBxC,EACb8B,EAAI,EAAGA,EAAIO,EAAeD,OAAQN,IAAK,CAC9C,GAAIQ,GAASD,EAAeP,GAAGQ,MAC/B,IAAc,MAAVA,EAAgB,CAClB,GAAaE,EAATF,EACF,MACEkB,KAAML,aAAaM,yBACnBJ,KAAM,2BACNC,QAAS,uEAGbd,GAAiBF,MAEjBiB,IAAAA,EA8BJ,MA1BAlB,GAAiBA,EAAeqB,OAAO,SAASZ,GAC9C,MAAOA,GAASR,QAAU,GAAKQ,EAASR,QAAU,IAsB/CiB,GACHpB,IAEKE,EA1OT,GAAIV,IACFgC,YACE,kBACA,qBACA,iBACA,mBACA,uBACA,mBACA,iBACA,mBAEFC,QACE,iBACA,iBACA,iBACA,mBACA,mBACA,mBACA,oBACA,oBACA,oBACA,kBACA,kBACA,mBAEFC,cACE,oBACA,oBACA,qBAEFC,aACE,iBACA,mBACA,oBACA,mBAEFC,YACE,kBACA,kBACA,mBAEFC,cACE,sBACA,uBACA,0BACA,0BAEFC,aACE,mBACA,mBACA,oBAEFC,WACE,iBACA,iBACA,kBAEFC,aACE,iBACA,mBACA,oBACA,mBAEFC,MACE,WACA,aACA,aAEFC,MACE,aACA,WACA,YACA,cACA,aACA,cAEFC,QACE,YACA,cACA,eACA,cAEFC,SACE,eACA,eACA,gBAEFC,SACE,aACA,eACA,gBACA,gBAIA5C,EAAwB6C,SAASC,gBAAgB,+BAAgC,OAEjFC,GACFC,KAAM,MACNC,OAAQ,MACRC,MAAO,OAGLvD,GACFwD,kBAAmBJ,EACnBK,gBAAiBL,EACjBM,iBAAkBN,EAClBO,eAAgBP,EAChBQ,UACEC,WAAY,MACZC,UAAW,MACXC,MAAS,MACTT,OAAU,OACVU,MAAS,OACTC,UAAW,OACXC,WAAY,QAEdC,YACEC,OAAQ,MACRC,KAAM,OAERC,aAAclB,EACdmB,YACEC,KAAM,2BAERC,WACED,KAAM,+BA+GV9K,GAAOgH,mBAAqBA,GAM3BpH,EAAqBG,GCpPxB,SAAUC,GAER,GAAIgL,KAEJhL,GAAOuB,aAAe,SAAS0J,EAASC,EAAMC,EAAQC,GACpD,GAAIC,GAAUD,EAAS,MAAQ,KAC3BE,EAAQ,GAAIC,MACZC,EAAS,GAAID,MAAKL,EAGtB,OAFAM,GAAOC,SAASD,EAAOE,WAAa,GAExBF,EAARF,GACIL,IAAWD,IACfW,QAAQC,KAAK,mBAAqBX,EAAU,IAAMI,EAAU,wCAA0CG,EAAOK,eAAiB,KAAOV,GAEvIH,EAASC,IAAAA,GAAW,IACb,GAMXjL,EAAO8L,WAAa,SAASb,EAASC,EAAMC,EAAQC,GAClD,GAAIpL,EAAOuB,aAAa0J,EAASC,EAAMC,EAAQC,GAC7C,KAAM,IAAIW,OAAMd,EAAU,IAAMI,QAAU,yBAA2BF,KAIxEvL,ovqBC3BH,OAAUI,EAAAA,qBAEFgM,OAAgB,GAAA,gBAEhBhI,GAAiBhE,SAAO6D,GAAAA,QAAwBzD,GAChD6L,GAAgB,EAAShI,EAAAA,OAC3B,EAAOjE,UAAOyF,OAAAA,EAAsBzB,UAAAA,CAAgBC,IAAAA,GAEtDgI,EAAcC,SAAAA,IAAAA,EAAwB7L,UAAQ2D,OAAAA,EAAiB5D,UAAOE,EACtE2L,GAA2B,EAAShI,UAC9BO,CAAAA,EAAQxE,SAAO+D,EAAAA,EAAAA,EAAAA,EAAeC,IAAAA,GAAgBC,EAAAA,aAClD,EAAOO,EAAAA,EAAAA,GAAAA,IAAUH,GAAAA,UAAyBF,EAAAA,EAAAA,EAAAA,EAAAA,IAErC8H,GAAAA,EAGRrM,GAAAA,GAAAA,QAAAA,EAAqBC,GAAAA,EAAAA,EChBdG,GAAAA,EAAQmM,EAAOC,GA2BvB,EAASC,IAAAA,QAAAA,GAAAA,EAAAA,GAAmCjF,OAAAA,EAG1C,EAFIkF,SAAAA,GAAAA,QAAAA,GAAAA,GAAAA,MAES,MAAOlF,IAAAA,EAAAA,KAAAA,IAAeD,IAAAA,IAAQN,GACzC,EAAA,GAAK,IAAIiB,GAAAA,GAAAA,EAAUV,EAAAA,EAAAA,IAAAA,EAAAA,GAAAA,KACjB,MAAc,EAAA,EAAVU,GAAAA,EAAAA,IAAgC,OAAVA,GAAAA,GAAAA,EAAgC,eAAVA,EAC1CyE,MAAAA,EAAAA,EAAAA,EAAAA,KAAAA,QAAAA,EACFlF,KAAAA,KAAQD,MAAAA,GAAAA,GAAkBC,SAC1BxG,gBAAQuG,+BACDA,SAAkBU,GAE3BwE,MAAAA,EAAAA,OAAAA,CAAAA,IAAAA,GAA+BxE,EAAUwE,WAAAA,KAAAA,GAAAA,qBACzCA,EAAAA,GAAAA,mBAA4CC,sBAKlD,oBAAsBD,qBAAAA,mBAChBE,QAAQF,gBAAAA,0BACRE,EAAM,aAAoD,EAAlCA,qBAAwBnF,KAClD,KACEY,GAAMC,EAAaC,YAAAA,GACb,EAAA,GACG,SAAA,EAAA,GAAA,QAIRmE,GAAAA,EAAAA,GAIT,GAAA,EAASG,EAAAA,OAAAA,cAAmBH,KAAAA,GAAAA,KAAAA,OACtBI,IAAAA,EAAAA,OACC,GAAIC,EAAAA,IAAAA,gBAAaL,KAAAA,GAAAA,CAAAA,EAAAA,EAEf,QADOA,UAAAA,IAAAA,IAAAA,KAA+BK,GAClC9F,EAAW2F,QAAMrF,EACxB,SAAIyF,GAAYJ,MAASnF,GACrBwF,GAAUL,KAAU,IAAGnF,GACvByF,KAAAA,GAAaN,GACbO,KAAAA,EAAqB,OACrBH,IAAAA,EAAaC,EACA,QAAXA,oBACWE,KAEbA,QAAWD,GAAAA,QAGfJ,IAAAA,EACEE,KAAAA,KAAWA,QAAAA,YACFC,KAAAA,QACDL,MAAM3F,IAAGhG,GACjBM,UAAUwL,cACVK,YAAqBC,cAAAA,EAAAA,EAAAA,EAAsBN,EAAWG,QAAAA,EAAYC,GAOxE,KAHAL,IAAAA,EAAoB,EAASQ,QAAAA,EAAmBC,GAAAA,MAAAA,EAAAA,GAAAA,GAC9C,IAAA,KAAOD,EAAAA,CAAAA,IAAAA,GAAkBN,KAAYO,GAAAA,CAAAA,GAAmBP,GAAAA,KAEnDF,EAAAA,QApFTP,GAAAA,QAAMiB,IAAAA,EAAAA,KAAqB,IAAA,QAASnG,GAAAA,QAClC,EAAIG,KAAiBpH,MAAOgH,KAAAA,SAAAA,GAAmBC,MAC3CqF,GAAAA,GAAAA,EAAAA,MAAiCD,KAAAA,QAAAA,GACjCK,EAAAA,GAAAA,MAAiBD,GAAmBH,EAAAA,GAAAA,GAAAA,QAAAA,GACjC,EAASe,EAAAA,GACd,GAAgB,GAAZC,IACFZ,KAAAA,IAAAA,GAAejE,EAAO,KAAA,EAASuE,KAAAA,IAC7B,GAAoB,EAAZM,QAA4C,GAA3BN,GAAAA,EAAcJ,KAAAA,EAC/BU,OAAAA,GAAY,EAAKN,IAAAA,SAAcH,GAAAA,MAC/BS,GAAYN,IAAAA,IAAAA,EAAcJ,EAAAA,IAAaU,SAAYN,GAAAA,MAAcH,GAAAA,IACxE3L,KAAQ,EAAA,EAAS8L,SAAAA,GACdO,GAAAA,GAAAA,EAAiBD,IAAAA,SAAWN,EAAAA,GAAcJ,MAAAA,IAC1CY,EAAAA,QAAgBR,IAAcH,EAAUG,KAAAA,IAAAA,EAAcJ,IAAAA,EACtDa,eAAmC,GAAjBD,EAAAA,KAAyBR,KAAAA,MAAAA,OAAcnM,GAAO0M,OAAAA,EAAAA,QAAiBC,EACrFrB,IAAAA,IAAYkB,GAAAA,GAAQL,iDAAoDS,EAAAA,EAGjEtM,KAAAA,KAAYmL,GAAAA,QAAAA,EAAAA,MAAAA,EACfnL,EAAwBA,KAAAA,KAAoC,GAAA,QAAZA,EAClDgL,KAAMuB,MAAML,EAAQlM,EA0E7BvB,KAAAA,KAAAA,qBAAqCG,GAAAA,YC/FxC,EAAUoM,EAIR,qBAASwB,EAAmCxM,EAAAA,uBACzBA,EAAYyM,qBAAiBzM,KAAAA,KAC9CyM,GAAiBzM,EAAU0M,WAAcC,EAE3C,EAASC,gBAAAA,CAA6BD,IAAQE,GAC5C,EAASnH,qBAAsBM,KAAQN,KACrC,GAAemH,EAEfL,EAAAA,gBAA2BG,KAAAA,OAAQ3M,EACtBA,KAAAA,EAEXwM,EAAmBM,gBAAgB9M,KAAAA,OAAS+M,EAAiB,KAAYrM,GACvE,oBAASsM,CAORlB,IAAAA,GAAsB9L,SAAgBiN,GAE7C,GADIC,GAAAA,EAAgCT,EAAAA,OAAAA,IAAiBzM,IAC5C0F,EAAOwH,GAAAA,EAAgBA,GAAAA,QAASlH,EACtBkH,EAAAA,oBACbC,KAAcD,OAAAA,EACdE,KAAAA,EACF,EAAIC,oBAAoBH,KAASxH,OAAM0H,EACvC,IAAIC,GAAAA,yBACWrC,EAA0B,EAAMqC,qBACtC,EACI,GAEKC,oBAKTC,EAAAA,qBAAoCC,EACjCP,GAvCdR,sBAmBEG,qBAAuBA,oBAuBvBd,qBAAwBA,mBAAAA,aAE7BpN,YAAgBE,SAAAA,cC9CTC,aAAeoM,YAEvBD,gBAAkB,UAASkB,EAAQpG,qBAAa/G,EAC1C+L,GAAsBD,4BAAqBxK,6BAC3CoN,yBAAexB,0BAEfyB,SAAY,OAEdD,iBAAepJ,gBAGjBqJ,cAAUC,eAAmB7K,aAE3B,aADAuB,YAAeyG,iBACS,iBAAjBzG,eAECuJ,gBAAS,cACV1B,cAETwB,QAAUG,eAAiB,cAASC,MAClC,iBAAkBA,kBAEpBJ,EAAuB5C,GAAciD,SACrCL,GAAU3C,QAAAA,GAA+BA,GAClC2C,MAGT1C,GAAMgD,uBAAyBzB,IACzB0B,EAAAA,aAAgB,QACd1B,GACFA,QACAA,GAAQ,GAGZ0B,GAAAA,GAAAA,EAAcN,aAAU,EACtB,OAAO,EAETM,aAAclD,KAAAA,KAAAA,UACdkD,EAAAA,OAAcF,EAAAA,aACZ,KAAA,KAEFE,QAAAA,EAAcJ,gBAAiB,KAAA,KAC7B,EASHpP,MAAAA,EAAAA,OAAqBC,EAAAA,aAAgBE,KAAAA,KAAAA,SClDxC,EAAUoM,OAAOC,IAsBf,GAAA,EAASiD,GAAAA,OAAAA,EAAkBC,GAAAA,OAAQnO,QAAUoO,GAC3CA,EAAWC,GAAAA,MAAa,QACxBD,GAAWE,QAAAA,IACXzO,GAAAA,EAAO0O,SAAAA,GAAAA,GAAeJ,GAAQnO,EAAAA,EAAAA,CAAUoO,IAAAA,QAG1C,EAASI,MAAAA,MAAAA,IAAAA,GAAAA,EAA4BC,gBAO9BC,EAAAA,EAAkBrG,OAASC,GAAAA,GAAAA,EAAgB,MAAA,EAAA,gBAAgC,EAAA,GAChFxG,QAAK6M,GAASF,GAAQhJ,MACtB3D,QAAK8M,EACL9M,IAAK+M,GAAAA,GAAAA,EAGL,2BAAgC7I,KAAAA,KAAQN,EAClC1F,EAAgB2O,KAAAA,GACpB7M,SAAK4M,EAAAA,EAAgB1O,WAAiB2O,EAAO3O,EAE/C8B,qBA+FF,EAASgN,GAAAA,UAAqBL,EACxBA,GAAQM,SAAAA,GAGRC,QAAoBR,GAAAA,GAAAA,MAAAA,UAA4BC,GACpD,GACEP,GAAAA,CAAAA,OAAAA,GAAkBO,IAAS,SAAWQ,GAAAA,MAAK,KAAA,EAAa,EAAOD,KAAAA,KAAAA,QAC/D,GAAOE,GAGPT,MAAQhJ;CAAAA,QAAa,GAASzF,GAAUkF,GACtCuJ,EAAQhJ,EAAMzF,cAAYkF,OAE5BuJ,QAAcb,EAAS,QAAS5N,KAAAA,GAAAA,GAC9ByO,EAAczO,uBAKV+O,KAAAA,EAA6BN,EAAAA,EAAQhJ,EA7J3C0J,KAAAA,IACFC,CAAAA,GACApJ,EAAAA,OACAqJ,EAGEC,MAAAA,GACFC,EAAAA,MACAC,EAAAA,GAAAA,MAAAA,IAAqB,GACrBC,EAAkB,GAClBC,EACAC,EACAC,EAGEC,KAAAA,EACFF,MAAAA,IAAgB,GAChBC,EAAAA,GAAa,MA6BfpB,KAAAA,EAAAA,EAA4BsB,EAAAA,IAAAA,EACtBV,OAAAA,EACF,OAAOtN,MAAK4M,KAAAA,GAAAA,MAEVU,EAAAA,EAAAA,EAAAA,EAEF,OADIW,IAAAA,CAAAA,GAAAA,GAAAA,EACKrK,EAAOA,GAAI5D,EAAK4M,EAAAA,EAAAA,IAAgB1I,EACvC+J,GAAAA,EAAAA,SAAAA,GAAmBjO,MAAK4M,KAAAA,EAAAA,OAAAA,EAAgBhJ,EAE1C5D,WAAK4M,IAAAA,EAAAA,EAAgBU,YACrBtN,EAAKkO,EAAAA,qBACQ,EAAGtK,EAASgJ,aAAAA,EAAgB1I,eACvC+J,IAAAA,EAAAA,EAAmBjO,EAAAA,EAAK4M,GAAAA,EAAAA,GAAgBhJ,GAE1C,SAAS1F,EAAY+P,MAAAA,GAAAA,KACdjO,GAAK+M,GAAAA,EAAoB7O,MAAAA,EAAAA,EACvB2O,EAAOiB,IAAY5P,EAAe0O,WAAAA,EAAgBe,OAAAA,MAAiBzP,IAAAA,QAI1EgG,GACU0I,GAAAA,MAAAA,GAAAA,QAAgB1I,GAE9BiK,QAAIZ,UACF,IAAA,QAAYV,GAAOU,EAGrBW,GAAAA,GAAAA,EAAgB,oBACFpB,EAAAA,CAAU9M,EAAK4M,kBAAgB1I,CACzCnG,IAAAA,GAAO0O,EAAAA,wBACLD,GAAAA,GAAAA,EACAD,oBACK,EAAA,CAAU6B,EAAAA,kBACN,CAAa,IAAOpO,GAAK4M,EAAAA,wBAC1BE,GAEV9M,MAAK8M,OAEP,EAAO9M,IAAK8M,MAAU9M,EAAK4M,MAAAA,KAAgB1I,GAAAA,SACpC4I,GAAAA,MACL/O,GAAAA,EAAO0O,GAAAA,EAAAA,EAAAA,GAAAA,KAAezM,EAAW8M,GAAAA,KAC/BN,GAAAA,EAAc,GACdD,KAAAA,IAAAA,EACOzO,EAIbuQ,SAAM,GAASnQ,GAAAA,GAAUkF,EACvBpD,KAAK6M,EAAO3O,GAAAA,GAAAA,EACZ8B,GAAK+M,GAAAA,EAAAA,IAAAA,EAAAA,EAAoB7O,cAE3B4N,EAAQ,GAAA,EAAA,GAAS5N,EAAAA,GACf8B,EAAK6M,EAAAA,IAAO3O,EAAiB0O,EAAAA,IAAAA,GAAgB1O,KACtC8B,IAAK+M,OAAAA,MAAoB7O,QAK/B,GAAIoQ,GAAUd,MAAAA,GACjBd,QAAAA,OAAAA,IAAAA,QAA4BsB,GAAUM,GAAAA,MAAU,GAAUA,QAAQC,eAChE,MAAO,QACL,GAAkB3B,EAAAA,GAAAA,GAAgB0B,GAAmB1B,EAAAA,0BAMrD,EALI2B,GAAAA,CACGvO,KAAAA,EAAK+M,SAAAA,EAAAA,OAAoByB,CAAAA,EAAU,SACjC3B,GAAOyB,EAAQvO,EAAMC,EAAK6M,KAAQ2B,KAAAA,GACzCxO,GAAAA,EAAKkO,EAAAA,EAAAA,OAAAA,IAEA3K,CAAAA,GAAAA,GAER+K,EAAAA,GAAQA,EAAAA,EAAUP,EAAAA,GAAAA,EAAAA,EAIvB,SAAS7P,EAAAA,OAAYqI,EAAAA,GAAAA,EAAAA,CAASkI,GAAAA,MAAAA,EAAAA,EAAgB9K,EACxCzF,EAAAA,IAAAA,SAAYmP,GAAAA,GAAAA,gBAA+BG,GAAAA,MAAAA,EAGrCtP,IAAAA,KACRkO,KAAAA,GAAAA,KAAkBM,GAAAA,EAAAA,GAAAA,CAA4BsB,OAAW9P,QAClD,GAAA,GACS0O,SAAAA,EAAgB1O,GAAAA,MAAAA,eAEhBkF,GAAAA,eACPwJ,IAAAA,UAAgB1O,GAAYkF,YAC5B8K,KAAAA,UACAlO,GAAK+M,YAAAA,IAAAA,KAAoB7O,KAChBA,IAAYkF,IAAAA,EAAAA,QAG7BlF,EAyBLgL,OAAMnJ,CAAAA,IAAQ,EAAkB7B,MAAUkF,IACxC4J,GAAAA,EAAqBL,EAAAA,EACrBA,IAAc0B,EAAKnF,IAAMwF,GAAaxQ,EAAAA,IAAWkF,IAGnD8F,UAAc,EAAA,UAAkBhL,KAAAA,GAAAA,GAC1ByO,EAAAA,EAAAA,EAAQM,OAAAA,IAAAA,CAAAA,GAAAA,GAAAA,EACVN,EAAQhJ,GAAAA,EAAMmI,EAAO5C,EAAMwF,GAAAA,EAAAA,EAAaxQ,EAAAA,GAAAA,EAO3CtB,EAAAA,EAAgBE,GAAAA,EAAAA,EChLnB,EACE6R,GAAeX,EAAoB,EAAShK,EAAa/G,IAAAA,EAC1C2R,EAAe1F,GAAM2F,CAAAA,IAAU7O,EAAmB/C,MAAAA,IAEhEL,GAAAA,GCJH,EAAUsM,KAAOC,EAAAA,IAEf,GAAS2F,KAAAA,EAAkBC,IAAI/P,EACT,KAAA,EAARgQ,IAAmC,EAAA,MAAA,UAC7C,EAAOA,UAAiBD,CAAAA,GAE1B,GAAoB,EAARC,EAAAA,MAAoC,IAAA,EAC7BA,IAAAA,EAOV9K,IAAU6K,EAEjB,IADIE,EACgBD,GAAapL,EAC/BqL,EAAOH,GAAYE,EAASD,EAIhC,GAAA,GAAM,EAAA,EAAA,GAAwCC,OAGhD9F,CAAAA,IAAMuC,EAAgB,KAASuD,EAAUE,IAAAA,EAChC,IACL,EAAOA,GAAAA,CAAAA,IAAgBJ,EAAsB9P,MAQhDpC,IAAAA,GAAAA,EAAgBE,EAAAA,EClCnB,IAAUoM,EAyFR,IAASiG,GAAcC,EACrB,IAAO9P,IAAK8P,UAAarQ,EAASsQ,KAGpC,OAAA,EAASC,EAAYC,GACfC,EAAUtG,EAAiBqG,GAC/BC,GAAUL,EAAMK,EAEZF,GACJ,GAAIE,IACFF,GAAOG,MAKF,KAHYC,KACJC,EAAAA,EAAI3Q,EAAI4Q,EAAatQ,OAAU,IAAIkQ,CAAAA,GAAAA,GAAUA,gBAGxDF,GAAUG,GAAAA,EAAM7L,aAAmBgM,EAASJ,gBAC9B5L,EAGlB,EAAO0L,EA5GLO,GAAAA,EAAgB,GAAA,GAClB,GAASC,EAAAA,GAASpR,EACZ6E,GAAAA,EAAc,GAAM,EAAyB,KAAG,EAAc,IACzDK,EAAcA,KAChB,GAAkBW,EACR,KAAGwL,GACJxL,EAAQX,MAAQlF,EAAK6F,KAOvC,GAAA,EACE,CAAA,GACe,GAAXtF,CACAA,GACW,EACA,EACA,EAAT,OACS,EACA,EACA,SAAN,GACLA,MAAK,GACLA,IAAE,SAGR,EAAS4Q,GAAAA,GAAAA,GAAyBG,EAAAA,IAAOC,SAAYC,EAAAA,GAAAA,MAGnD,GAFIC,GAAAA,GAAAA,GAAW,KAAS,KAAQ,IAAM,OAAW,UAAc,EAEtDvM,GAAI,IAAGA,IAAOA,EACNsM,MAAAA,KAAYtM,SAGpBA,EAAW,GAAGA,GAAAA,YACR,EAAUW,GACrB4L,GAAAA,IAAO,EAASC,MAAeD,KAAO5L,OAI1C,GAAIxF,GAAIuQ,KAAK,GAAQA,GAAAA,GAAK,GAAQA,IAAK,GAAQA,GAE7B,QAAS,UAAc,EAAK,EAAG,EAAG,EAAG,EAAA,EAAK,EAAG,EAAG,EAAG,EAAA,EAErEe,EAAAA,EAAAA,EAAAA,EAAAA,GAAU,GAAG,UAAsBC,mBACtB,GAAK,QAAiBC,KACnCF,SAAU,KAAQ,SAAiBE,KACnCF,SAAU,KAAQ,UAAiBE,QACnCF,aAAsB,KAAaC,OACnCD,KAAAA,GAAa,EAAA,EAAK,IAASC,GAAQC,QACnCF,IAAAA,GAAa,EAAK,EAAKtR,IAAQyR,GAC/BH,EAAAA,KAAAA,QAAkB,IAAKG,GAAYD,EACnCF,EAAAA,IAAAA,GAAU,EAAG,KAAS,QAAiBG,IAEvCL,GAASL,EAAAA,EAAAA,KAASK,SAAQE,MAAAA,GAE1B,MAAII,KAAS,KAAS,GAAK,OAAS,IAAK,KAAS,GAAQ,EAAG,KACzDR,OAAK,IACPQ,KAAQ,GAAKR,EACbE,KAAAA,WAAkBA,KAAAA,GAAQM,EAAAA,EAGxBR,IAAK,GACPQ,YACAA,IAAAA,GAAQ,EAAKR,EAAK,IAClBE,GAASL,EAAAA,KAASK,YAGhBF,IAAAA,GACFQ,EAAAA,EAAK,IAAQ,GACR,EAAG,KAAKR,YACJH,IAAAA,GAASK,EAAAA,EAAQM,KAG5B,aAAoB,MAAG7M,GAChB,GAAQ,qBACDW,EAId,GAAS4L,eACeA,EAAcA,GAAwB,SAAW,GAAOA,QAAU,GAEnFA,GAAUO,GAAAA,GAAOP,OAAO,EAAW,OAAIA,OAAO,IAEvD,IAAON,GAAAA,EAAAA,KAAAA,EA0BT3G,MAAM2G,EA5BwC,OA4BxCA,EAAgBA,QAAAA,GACtB3G,GAAMoG,MAAOA,GAEZ1S,IAAAA,KAAAA,MAAgBE,EAAAA,KAAAA,ECnHnB,EAAA,MAAUoM,IAAOC,IAAAA,GAEXwH,MAAAA,EAAAA,SAEAC,MAAAA,EAAAA,OAAuB,OAAA,GAASxG,QAAQyG,GAAaC,EAAAA,GACvD9Q,OAAKoK,EAASA,EAAAA,GACTyG,EAAAA,qBACL7Q,EAAK8Q,GAAAA,iBAEL9Q,GACAA,SAAK+Q,GACL/Q,QAAKgR,GACAC,GAAAA,GAAgB7G,KAChB8G,KAAAA,GAAAA,KAAAA,GACLlR,EAAKmR,IAAAA,EAAmBC,EAAAA,OACxBpR,GAAKqR,QAAY/I,GAGnBY,GAAe,MAAA,GAASoI,aACjBC,qCACAC,IAAAA,EACLxR,uBACKyR,GAAS,QACTC,GAAgB,EACrB1R,GAAK2R,GAAAA,GACL3R,EAAK4R,gBACL5R,EAAgB,IAChBA,EAAK6R,IAAAA,GAAAA,IACL7R,EAAK8R,GAAAA,CAAAA,GAAUR,GACftR,EAAK+R,EAAAA,IAAY/R,EAAK8R,GAAAA,EAAQjG,IAAQ,SACjCmG,EACLhS,GAAAA,EAAKiS,IAAAA,SAAAA,GAAsB,IAG7B/I,EAAMgJ,GAAOlE,EAAAA,KACXmE,GAAAA,IAAAA,EAAc,QACZnS,EAAK+R,CAAAA,GAAAA,aAAiBD,KAAQjG,EAAQ7L,KAAK6Q,aACtC7Q,KAAK2R,EAAAA,IAAAA,CAAAA,GAAgB3R,GAAK+R,EAAAA,EAAc/R,GAAK4R,GAAAA,EAAAA,GAAAA,EAC3CD,GAAAA,EAAAA,GAAAA,2BACUS,KAASxH,EAAK5K,KAGjCqS,2BAA2BC,KAASC,EAAAA,IAAAA,MAC9BD,GAAWtS,IAAKwR,SAAAA,GACbA,MAAAA,gBACDxR,GAAKwS,EAAaD,EACpBvS,OAAKwR,QAAoBE,GAAoB1R,GAAKiJ,GAAAA,GAAiB,EACrEjJ,gBAAKmS,EAGLtB,IAAAA,EACE7Q,IAAAA,EAAKgS,CAAAA,IAAShS,GAAKiS,GAAAA,EAAAA,GAAAA,IACd,IACFjS,KAAKwR,IAAAA,KAAAA,EAEdrD,EAAI0C,GAAYyB,EAAAA,EAAAA,EACdA,EAAAA,EAAWA,OAAAA,IACPzU,CAAAA,GAAMyU,GAAAA,EAEVpJ,EAAMuJ,iBACIhB,IAA6B,EAAdiB,eAClBA,KAAAA,GAAa1S,GAAK2S,KAAAA,EAAU9B,MAAAA,EAAAA,OAAcyB,EAAAA,IAAUtS,EAAK0R,OAAAA,GAAAA,GAEhE1R,EAAKiS,GAAAA,EAAAA,GACDjS,UAAKwR,GAAAA,MAAgBc,IAEpBD,EAAAA,EACLnJ,GAAM0J,EAAAA,MAAAA,EAAAA,MAERzE,GAAIxE,KAAAA,EAAAA,GACF,EAAO3J,IAAK0S,GAEV/I,GAAAA,OAAU2I,EAAAA,EACZA,KAAWA,QACPzU,GAEAmC,GAAKyR,GAAUzR,GAAKgS,EAExBhS,gBAAkBsS,EACbD,KAAAA,EAAkBrS,OAAK2S,IAAU9B,IAAAA,EAAAA,GAAc7Q,EAAK0S,GAAAA,OAAc1S,GAAKtC,IAAAA,MAC5EwL,IAAM0J,GAAAA,QAAAA,IAAAA,IAAAA,OAEJlV,IAAAA,KAAAA,KAAiB,IAAA,GAAOsC,QAAK0R,IAAAA,MAAAA,EAE/B,EAAA,oBAA4BA,KAAAA,KAAAA,EAAqB1R,gBAAKwR,IAAgBxR,GAAKiJ,qBAClEyI,EAAgB,KAAK1R,KAAKwR,GAAAA,GAEjCvI,qBAAmB,EAAOjJ,qBAAaiJ,EACvC4J,KAAAA,KACF,GAAA,GACS,uBACCH,EAAuB1S,gBAAeA,EAAKtC,EAA2BuU,gBAAAA,CAE5EjS,IAAKyR,GAELzR,EAAKwS,oBAEF,KAAA,KAETM,EACE9S,KAAKyR,GAAS,qBACYO,EACnBR,GAAoBE,sBAA6BzI,qBACjDyJ,GACLxJ,SAAM0J,GAAAA,QAER5S,GAAK4R,GAAgB,GACrB1I,GAAMuJ,EACNzS,aAAa,UACRmS,EAAAA,IAAAA,GAEA,EAAA,GAAA,OACKK,UAAaxS,OAAKyR,EAAWzR,aACrCA,EAAKiS,OAAAA,EAAAA,aAEPjS,KAAK0S,OAAa,QAClB1S,EAEM,EAAA,OACGgS,EAAAA,aAEJnB,KAAAA,OAAmBa,QAAAA,EAAgB,gBAASzI,EAAiB,OAC7DyJ,EAAAA,aAAkBzJ,KAAAA,OAAiBjJ,SAAK6Q,EAC7C7Q,IAAKiS,IAAAA,GAAAA,EAAAA,aAEPc,WAAQ,EACDhB,IAAAA,GACL/R,EAAKgS,GAAAA,OACLhS,WAAK6Q,OACL7Q,EAAK0S,aAEPM,EAAAA,OAAS,EACPhT,aAAK0R,KAAAA,OACL1R,QAAK0S,EAGPO,EAAAA,OAAAA,EAAkB,aAAeC,KACT,OAAA,QAAA,EAAXA,gBAAiC,EAARlO,OAC7B6M,EAAAA,aAAgBjH,KAAKsI,OAE9BC,SAAAA,EAAAA,IAAqB,IAAA,GAAeD,EAAAA,aACtB,WAER9E,EAAQpO,OAAK6R,IAAAA,EAAgBzT,IAAAA,WAC7BgQ,OAAS,EACXpO,aAAK6R,EAAgBuB,OAAOhF,EAAO,aAEvCiF,KAAa,OAASC,QAAAA,EAChBd,SAAWxS,EAAKwS,aACfA,KAAAA,OAAYxS,6BAAoC,YACvC,EAAI4Q,oBAAqB5Q,EAAMA,OAAKwR,EAAAA,aAC5CpG,KAAAA,OAAWpL,SAAK6R,EAAgBnB,KAAAA,OAAY6C,QAAAA,GAAiBA,EAAAA,GACjEC,MAAW,GAAA,KAAA,EACTpI,GAAAA,UAAiB,EAAA,GAAS8H,EAAAA,UACxBA,EAAQO,MAAKC,GAAMtJ,EAAQsJ,MAAAA,IAE5B,UAEA9B,EAAAA,gBAEP+B,OAAO,EAAA,gBAUL,MATK3T,WAAKgS,EAAUhS,GAAKyR,EAAAA,UACnBzR,EAAK0S,MAAAA,GACP1S,EAAK2J,MAAAA,IAAYmH,WAAe9Q,EAAKwR,yBAAoB9T,OACjDsC,EAAKwS,gBACRH,MAAAA,WAAkBvB,EAAAA,IAAAA,EAAe9Q,IAAK0S,EAAAA,GAAc1S,EAAKtC,UAAAA,EAGlEsC,MAAKiS,GAAAA,EAAAA,MAAAA,IAAsB,WACtBoB,EAAAA,GAAYvC,EACT9Q,MAAKgS,OAAUhS,OAAK+R,GAAAA,GAAmBH,EAQlDhV,qBAAgBE,KAAAA,KAAAA,ECjLnB,sBAAyBqM,EAqBdyK,EAAAA,gBACHC,KAAaC,OAAAA,EAEjBC,KAAKvI,EACMvN,EAAQ,oBAAkB+V,KAASxI,OAC1CyI,EAAAA,gBAEJC,KAAAA,EAGF,EAASC,oBAAeC,KAAYC,OAAAA,EAC3BD,IAAAA,GAAW7C,qBAAkB8C,EAAY9C,GAGlD,mBAAS+C,GACFlC,SAELpS,GAAK6Q,QAAclC,GAAAA,EAAO4F,GAAeA,EAAAA,QAAkBA,IAAAA,QAAYC,SAkCzE,GAASN,IAAAA,UACPO,gBAAexW,QAAQ,EAAce,GAcvC,KAAcwM,GACZkJ,KAAAA,GAAAA,aACI9F,kBAAiBA,gBACZiC,EAAAA,mBACAuB,0BACTuC,EAAU,eACNC,sBAA2BxC,EAC/BxD,qBAEIiG,4BACAC,EAAAA,aACJF,SAAAA,GAAkBA,MAAAA,GAAuB,IAASG,IAWhD,EAVOpD,OCnGb,SAAU5U,EAAQmM,GAsChB,QAAS8L,GAAsBxJ,GAC7B,GAAIoD,GAAWD,OAAOpI,SAASqI,QAC/BA,GAASiC,YAAcrF,EACvBoD,EAASqG,kBACuB,GAA5BrG,EAASwD,SAASlO,OACpByQ,GAAAA,EAEAO,sBAAsBF,GA3C1B9L,EAAMiM,kBAAoB,WACxBnV,KAAKoS,YACLpS,KAAK6Q,YAAc/S,QAGrBoL,EAAMiM,kBAAkBnH,WAItBoH,oBAAqB,WAEnB,MADApV,MAAKiV,kBACEjV,KAAKoS,SAASnS,SAEvBgV,gBAAiB,WACfjV,KAAKoS,SAAWpS,KAAKoS,SAAS5M,OAAO,SAASuP,GAC5C,MAA2B,YAApBA,EAAOlC,WAA+C,QAApBkC,EAAOlC,aAGpDC,KAAM,SAASxB,GACb,GAAIyD,GAAS,GAAI7L,GAAMgJ,OAAOZ,EAI9B,OAHAtR,MAAKoS,SAASxH,KAAKmK,GACnB7L,EAAMmM,+BACNN,EAAOjC,OACAiC,GAIX,IAAIJ,IAAAA,CAEJzL,GAAMmM,6BAA+B,WAC9BV,IACHA,GAAAA,EACAO,sBAAsBF,IAc1B,IAAIpG,GAAW,GAAI1F,GAAMiM,iBACzBjM,GAAM0F,SAAWA,CAEjB,KACE7Q,OAAO0O,eAAekC,OAAOpI,SAAU,YACrCiG,cAAAA,EACAW,IAAK,WAAa,MAAOyB,MAE3B,MAAO0G,IACT,IACE3G,OAAOpI,SAASqI,SAAWA,EAC3B,MAAO0G,MAER3Y,EAAqBE,EAAmBC,GC9D3C,SAAUC,EAAQmM,GAChBA,EAAMgJ,OAAS,SAASZ,GACtBtR,KAAKsR,OAASA,EACVA,IAEFA,EAAOyD,OAAS/U,MAElBA,KAAKuV,UAAAA,EACLvV,KAAKwV,QAAU,KACfxV,KAAKyV,iBACLzV,KAAK0V,UAAY,KACjB1V,KAAK2V,2BAEL3V,KAAKwV,QAAQzC,UAIf7J,EAAMgJ,OAAOlE,WACX2H,yBAA0B,WACpB3V,KAAKwV,UACPxV,KAAKwV,QAAQzC,SACb/S,KAAKwV,QAAU,QAGZxV,KAAKsR,QAAUtR,KAAKsR,iBAAkB3C,QAAOE,aAChD7O,KAAKwV,QAAUtM,EAAM0M,gCAAgC5V,KAAKsR,QAC1DpI,EAAM2M,uBAAuB7V,QAE3BA,KAAKsR,iBAAkB3C,QAAOmH,mBAAqB9V,KAAKsR,iBAAkB3C,QAAOoH,kBACnF/V,KAAKwV,QAAUtM,EAAM8M,4BAA4BhW,KAAKsR,QACtDpI,EAAM+M,mBAAmBjW,QAK7BmO,GAAIsD,UACF,MAAOzR,MAAKwV,QAAQ/D,QAEtBtD,GAAI0E,aACF,MAAO7S,MAAKwV,QAAQ3C,WAEtB1E,GAAIoF,YACF,MAAOvT,MAAKkW,WAEd/H,GAAIoF,UAAS4C,GACK,kBAALA,IACTnW,KAAKkW,UAAYC,EACjBnW,KAAKwV,QAAQjC,SAAW,SAAU+B,GAChCA,EAAElL,OAASpK,KACXmW,EAAE1C,KAAKzT,KAAMsV,IACZc,KAAKpW,QAERA,KAAKwV,QAAQjC,SAAW4C,EACxBnW,KAAKuT,SAAWvT,KAAKwV,QAAQjC,WAGjCpF,GAAI0C,eACF,MAAO7Q,MAAKwV,QAAQ3E,aAEtB1C,GAAI0C,aAAYsF,GACdnW,KAAKwV,QAAQ3E,YAAcsF,EAC3BnW,KAAKqW,YACLrW,KAAKsW,cAAc,SAASC,EAAOnS,GACjCmS,EAAM1F,YAAcsF,EAAI/R,KAG5B+J,GAAIxE,aACF,MAAO3J,MAAKwV,QAAQ7L,WAEtBwE,GAAIxE,WAAUwM,GACZnW,KAAKwV,QAAQ7L,UAAYwM,EACzBnW,KAAKqW,YACLrW,KAAKsW,cAAc,SAASC,EAAOnS,GACjCmS,EAAM5M,UAAYwM,EAAI/R,KAG1B+J,GAAIzQ,gBACF,MAAOsC,MAAKwV,QAAQ9X,cAEtByQ,GAAIqE,YACF,MAAOxS,MAAKwV,QAAQhD,UAEtBM,KAAM,WACJ9S,KAAKwV,QAAQ1C,OACb9S,KAAKqW,YACLnN,EAAMsN,eAAexW,MACrBA,KAAKsW,cAAc,SAASC,GAC1B,GAAIE,GAAOF,EAAM1F,WACjB0F,GAAMzD,OACNyD,EAAM1F,YAAc4F,KAGxBC,MAAO,WACL1W,KAAKwV,QAAQkB,QACb1W,KAAKqW,YACLrW,KAAKsW,cAAc,SAASC,GAC1BA,EAAMG,WAGVC,OAAQ,WACN3W,KAAKwV,QAAQmB,SACb3W,KAAKqW,aAGPtD,OAAQ,WACN/S,KAAKwV,QAAQzC,SACb/S,KAAKqW,YACLrW,KAAK4W,kBAEP5D,QAAS,WACPhT,KAAKwV,QAAQxC,UACb9J,EAAMsN,eAAexW,MACrBA,KAAKqW,YACLrW,KAAKsW,cAAc,SAASC,EAAOnS,GACjCmS,EAAMvD,UACNuD,EAAM5M,UAAY3J,KAAK2J,UAAYvF,EAASpE,KAAKtC,aACjD6Y,EAAM1F,YAAc7Q,KAAK6Q,YAAczM,EAASpE,KAAKtC,gBAGzDuV,iBAAkB,SAASjO,EAAMkO,GAC/B,GAAI2D,GAAU3D,CACQ,mBAAXA,KACT2D,EAAU,SAAUvB,GAClBA,EAAElL,OAASpK,KACXkT,EAAQO,KAAKzT,KAAMsV,IAClBc,KAAKpW,MACRkT,EAAQ4D,SAAWD,GAErB7W,KAAKwV,QAAQvC,iBAAiBjO,EAAM6R,IAEtC1D,oBAAqB,SAASnO,EAAMkO,GAClClT,KAAKwV,QAAQrC,oBAAoBnO,EAAOkO,GAAWA,EAAQ4D,UAAa5D,IAE1E0D,eAAgB,WACd,KAAO5W,KAAKyV,cAAcvR,QACxBlE,KAAKyV,cAAcsB,MAAMhE,UAE7BuD,cAAe,SAAStX,GACtB,GAAIoF,GAAS,CACbpE,MAAKyV,cAAcxX,QAAQ,SAASsY,GAClCvX,EAAEyU,KAAKzT,KAAMuW,EAAOnS,GAChBpE,KAAKsR,iBAAkB3C,QAAOmH,oBAChC1R,GAAUmS,EAAMjF,OAAOvQ,iBACzBqV,KAAKpW,UAIVrD,EAAqBE,EAAmBC,GCnJ1C,SAASC,EAAQmM,GAEhB,QAAS8N,GAAmBC,GAC1B,MAAOA,GAAKC,QAAQ9Z,MAAQ6Z,EAAKlW,eAAiBkW,EAAKC,QAAQ7Z,SAGjE,QAAS8Z,GAAexL,GACtB3L,KAAKoX,QAAUra,EAAOgH,mBAAmB4H,GAyD3C,QAAS0L,KAEP,IADA,GAAIC,IAAAA,EACGC,EAAcrT,QACnBqT,EAAcC,QAAQC,kBACtBH,GAAAA,CAEF,OAAOA,GA5DTH,EAAenJ,WACb0J,UAAW,WAAa,MAAO1X,MAAKoX,UAGtClO,EAAM2F,UAAY,SAASzE,EAAQuB,EAAQ1O,GAiBzC,MAhBA+C,MAAKoK,OAASA,EAGdpK,KAAK2X,aAAe1a,EACpB+C,KAAKkX,QAAUna,EAAOwB,qBAAqBtB,GAG3C+C,KAAK7C,OAASJ,EAAOC,WAAWC,GAI9B+C,KAAK2L,OADc,kBAAVA,GACKA,EAEA,GAAIwL,GAAexL,GACnC3L,KAAK4X,QAAUjM,EACf3L,KAAKe,eAAiBhE,EAAO6D,wBAAwBZ,KAAKkX,SACnDlX,KAGT,IAAI6X,GAAyBC,QAAQ9J,UAAU+J,OAC/CD,SAAQ9J,UAAU+J,QAAU,SAASpM,EAAQxO,GAC3C,MAAO+L,GAAM0F,SAASkE,KAAK,GAAI5J,GAAM2F,UAAU7O,KAAM2L,EAAQxO,IAG/D,IAAI6a,GAAazR,SAASC,gBAAgB,+BAAgC,MAC1E0C,GAAM0M,gCAAkC,SAAShK,GAC/C,GAAIxB,GAASwB,EAAUxB,QAAU4N,EAC7BrM,EAASC,EAAUgM,OAIvB,OAHqB,kBAAVjM,KACTA,MAEKkM,EAAuB9X,MAAMqK,GAASuB,EAAQC,EAAU+L,gBAGjEzO,EAAM2M,uBAAyB,SAASd,GAClCA,EAAOzD,QAAyC,kBAAxByD,GAAOzD,OAAO3F,QACxCzC,EAAM+O,0BAA0BlD,GAIpC,IAAIwC,KACJrO,GAAMsN,eAAiB,SAAS0B,GACA,OAA1BA,EAAYvO,WAAuBuO,EAAY3C,WAEvB,GAAxBgC,EAAcrT,QAChBgR,sBAAsBmC,GAExBE,EAAc3M,KAAKsN,IAUrB,IAAIC,GAA2BxJ,OAAOyJ,gBACtCra,QAAO0O,eAAekC,OAAQ,oBAC5BnC,cAAAA,EACAD,YAAAA,EACAnJ,MAAO,WACL,GAAIG,GAAS4U,EAAyBpY,MAAMC,KAAMwO,UAGlD,OAFI6I,OACF9T,EAAS4U,EAAyBpY,MAAMC,KAAMwO,YACzCjL,KAKX2F,EAAMgJ,OAAOlE,UAAUyJ,gBAAkB,WACvC,IAAIzX,KAAKyR,QAAWzR,KAAKsR,QAAWtR,KAAKuV,SAGzC,IAAK,GADDnR,GAASpE,KAAKsR,OAAO4F,QAAQ9Z,MACxBwG,EAAI,EAAGA,EAAI5D,KAAKsR,OAAO+G,SAASnU,OAAQN,IAAK,CACpD,GACI0U,GADA/B,EAAQvW,KAAKsR,OAAO+G,SAASzU,EAG7BA,IAAK5D,KAAKyV,cAAcvR,QAC1BoU,EAAc3J,OAAOpI,SAASqI,SAASkE,KAAKyD,GAC5CvW,KAAKyV,cAAc7K,KAAK0N,IAExBA,EAActY,KAAKyV,cAAc7R,GAEnC2S,EAAMxB,OAAS/U,KAAKsR,OAAOyD,OAEvBuD,EAAY3O,WAAa3J,KAAK2J,UAAYvF,IACrB,OAAnBpE,KAAK2J,WACP2O,EAAYzH,YAAc7Q,KAAKsR,OAAOyD,OAAOlE,YAAczM,EAC3DkU,EAAY5F,WAAa,MAEzB4F,EAAY3O,UAAY3J,KAAK2J,UAAYvF,EAE3CkU,EAAYb,mBAGW,IAArBzX,KAAKtC,cAAsBsC,KAAK6Q,YAAczM,GAAsC,KAA5BkU,EAAYzH,cACtEyH,EAAYzH,YAAc,IAGxB7Q,KAAKsR,iBAAkB3C,QAAOmH,oBAChC1R,GAAU4S,EAAmBT,MAInC5H,OAAOE,UAAY3F,EAAM2F,UACzBF,OAAOmJ,QAAQ9J,UAAUoH,oBAAsB,WAC7C,MAAO7O,UAASqI,SAASwG,sBAAsB5P,OAAO,SAASuP,GAC7D,MAAyB,QAAlBA,EAAOzD,QAAmByD,EAAOzD,OAAOlH,QAAUpK,MACzDoW,KAAKpW,QAGTkJ,EAAM8N,mBAAqBA,GAE3Bra,EAAqBE,EAAmBC,GClI1C,SAAUC,EAAQmM,GAkChB,QAASqP,GAASC,GACZA,EAASC,cAEbD,EAASC,aAAAA,EACTC,EAAU9N,KAAK4N,GACV7D,IACHA,GAAAA,EACAO,sBAAsBnB,KAI1B,QAASA,KACP,GAAI4E,GAAWD,CACfA,MACAC,EAASC,KAAK,SAASC,EAAM1N,GAC3B,MAAO0N,GAAKtH,gBAAkBpG,EAAMoG,kBAEtCoH,EAASnT,OAAO,SAASgT,GAIvB,MAHAA,OACKA,EAAShD,SAAWgD,EAAShD,QAAQhD,UAAYgG,EAAShD,QAAQ/D,UACrE+G,EAASC,aAAAA,GACJD,EAASC,cAElBC,EAAU9N,KAAK7K,MAAM2Y,EAAWC,GAE5BD,EAAUxU,QACZyQ,GAAAA,EACAO,sBAAsBnB,IAEtBY,GAAAA,EA7DJ,GAEIhE,IAFapK,SAASC,gBAAgB,+BAAgC,OAErD,EACrB0C,GAAM+O,0BAA4B,SAASlD,GACzC,GAAI3K,GAAS2K,EAAOzD,OAAOlH,OACvBuB,EAASoJ,EAAOzD,OAAO3F,OACvBxO,EAAS4X,EAAOzD,OAAOnU,OACvB2b,EAAOhb,MACXX,GAASJ,EAAOwB,qBAAqBpB,EACrC,IAAIqb,GAAW,WACb,GAAIhN,GAAIgN,EAAShD,QAAUgD,EAAShD,QAAQ3E,YAAc,IAChD,QAANrF,IACFA,EAAIzO,EAAOyF,sBAAsBzF,EAAO6D,wBAAwBzD,GAASqO,EAAGrO,GACxEU,MAAM2N,KACRA,EAAI,OAIJA,IAAMsN,GACRnN,EAAOH,EAAGpB,EAAQ2K,EAAOzD,QAC3BwH,EAAOtN,EAGTgN,GAAShD,QAAUT,EACnByD,EAASC,aAAAA,EACTD,EAASjH,gBAAkBZ,IAC3BoE,EAAOW,UAAY8C,EACnBD,EAASC,GAGX,IAAIE,MACA/D,GAAAA,CAkCJzL,GAAMgJ,OAAOlE,UAAUqI,UAAY,WAC7BrW,KAAK0V,WACP6C,EAASvY,KAAK0V,aAGjB/Y,EAAqBE,EAAmBC,GCvE3C,SAAUC,EAAQmM,GAEhB,QAAS6P,GAAYV,EAAUpb,GAC7B+C,KAAKqY,SAAWA,MAChBrY,KAAKkX,QAAUna,EAAOwB,qBAAqBtB,GAAAA,GAC3C+C,KAAK7C,OAASJ,EAAOC,WAAWC,GAAAA,GAEF,SAA1B+C,KAAKkX,QAAQzZ,WACfuC,KAAKkX,QAAQzZ,SAAWuC,KAAKe,gBAGjC4N,OAAOmH,kBAAoB,WACzBiD,EAAYhZ,MAAMC,KAAMwO,YAG1BG,OAAOoH,eAAiB,WACtBgD,EAAYhZ,MAAMC,KAAMwO,YAG1BG,OAAOmH,kBAAkB9H,WACvBG,GAAIpN,kBACF,GAAIiY,GAAQ,CAIZ,OAHAhZ,MAAKqY,SAASpa,QAAQ,SAASsY,GAC7ByC,GAAS9P,EAAM8N,mBAAmBT,KAE7BjX,KAAK8P,IAAI4J,EAAO,KAI3BrK,OAAOoH,eAAe/H,WACpBG,GAAIpN,kBACF,GAAIqO,GAAM,CAIV,OAHApP,MAAKqY,SAASpa,QAAQ,SAASsY,GAC7BnH,EAAM9P,KAAK8P,IAAIA,EAAKlG,EAAM8N,mBAAmBT,MAExCnH,IAIXlG,EAAM8M,4BAA8B,SAASzM,GAC3C,GAAI0P,GACAC,EAAS,SAASC,GACpB,GAAIpE,GAASkE,EAAiBnC,QAC9B,OAAK/B,GAAOzD,OAEF,MAAN6H,MACFpE,GAAO6B,sBAGgB,OAArB7B,EAAOpL,WAGXoL,EAAO0C,mBATP,OAaF,OADAwB,GAAmB/P,EAAM0F,SAASkE,KAAK,GAAI5J,GAAM2F,UAAU,KAAMqK,EAAQ3P,EAAM2N,WAIjFhO,EAAM+M,mBAAqB,SAASlB,GAClCA,EAAOS,QAAQsB,SAAW/B,EAC1BA,EAAOQ,UAAAA,EACPrM,EAAMsN,eAAezB,GACrBA,EAAO0C,oBAIR9a,EAAqBE,EAAmBC,OlB9DrCA,WAAAA,MAAuB"}
\ No newline at end of file
diff --git a/third_party/web-animations-js/sources/web-animations.dev.html b/third_party/web-animations-js/sources/web-animations.dev.html
new file mode 100644
index 0000000..c0c5ff32
--- /dev/null
+++ b/third_party/web-animations-js/sources/web-animations.dev.html
@@ -0,0 +1,44 @@
+<!--
+ Copyright 2014 Google Inc. All rights reserved.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<script src="src/dev.js"></script>
+<script src="src/scope.js"></script>
+<script src="src/timing-utilities.js"></script>
+<script src="src/normalize-keyframes.js"></script>
+<script src="src/deprecation.js"></script>
+<script src="src/animation-node.js"></script>
+<script src="src/effect.js"></script>
+<script src="src/property-interpolation.js"></script>
+<script src="src/animation.js"></script>
+<script src="src/apply-preserving-inline-style.js"></script>
+<script src="src/element-animatable.js"></script>
+<script src="src/interpolation.js"></script>
+<script src="src/matrix-interpolation.js"></script>
+<script src="src/player.js"></script>
+<script src="src/tick.js"></script>
+<script src="src/matrix-decomposition.js"></script>
+<script src="src/handler-utils.js"></script>
+<script src="src/shadow-handler.js"></script>
+<script src="src/number-handler.js"></script>
+<script src="src/visibility-handler.js"></script>
+<script src="src/color-handler.js"></script>
+<script src="src/dimension-handler.js"></script>
+<script src="src/box-handler.js"></script>
+<script src="src/transform-handler.js"></script>
+<script src="src/font-weight-handler.js"></script>
+<script src="src/position-handler.js"></script>
+<script src="src/shape-handler.js"></script>
+<script src="src/property-names.js"></script>
+
diff --git a/third_party/web-animations-js/sources/web-animations.dev.js b/third_party/web-animations-js/sources/web-animations.dev.js
new file mode 100644
index 0000000..7f2b457
--- /dev/null
+++ b/third_party/web-animations-js/sources/web-animations.dev.js
@@ -0,0 +1,21 @@
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+//     You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//     See the License for the specific language governing permissions and
+// limitations under the License.
+var webAnimationsSourceTarget = 'web-animations';
+var WEB_ANIMATIONS_TESTING = false;
+(function() {
+  var scripts = document.getElementsByTagName('script');
+  var location = scripts[scripts.length - 1].src.replace(/[^\/]+$/, '');
+  document.write('<script src="' + location + 'target-config.js"></script>');
+  document.write('<script src="' + location + 'target-loader.js"></script>');
+})();
diff --git a/third_party/web-animations-js/sources/web-animations.html b/third_party/web-animations-js/sources/web-animations.html
new file mode 100644
index 0000000..b5de36c
--- /dev/null
+++ b/third_party/web-animations-js/sources/web-animations.html
@@ -0,0 +1,50 @@
+<!--
+ Copyright 2014 Google Inc. All rights reserved.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!-- WARNING: This file is DEPRECATED, for development purposes use
+              web-animations*.dev.html instead or depend on 
+              web-animations*.min.js in the web-animations-js
+              repository -->
+
+<script src="src/dev.js"></script>
+<script src="src/scope.js"></script>
+<script src="src/deprecation.js"></script>
+<script src="src/timing-utilities.js"></script>
+<script src="src/normalize-keyframes.js"></script>
+<script src="src/animation-node.js"></script>
+<script src="src/effect.js"></script>
+<script src="src/property-interpolation.js"></script>
+<script src="src/animation.js"></script>
+<script src="src/apply.js"></script>
+<script src="src/element-animatable.js"></script>
+<script src="src/interpolation.js"></script>
+<script src="src/player.js"></script>
+<script src="src/tick.js"></script>
+<script src="src/handler-utils.js"></script>
+<script src="src/shadow-handler.js"></script>
+<script src="src/number-handler.js"></script>
+<script src="src/visibility-handler.js"></script>
+<script src="src/color-handler.js"></script>
+<script src="src/dimension-handler.js"></script>
+<script src="src/box-handler.js"></script>
+<script src="src/transform-handler.js"></script>
+<script src="src/property-names.js"></script>
+<script src="src/timeline.js"></script>
+<script src="src/maxifill-player.js"></script>
+<script src="src/animation-constructor.js"></script>
+<script src="src/effect-callback.js"></script>
+<script src="src/group-constructors.js"></script>
+
diff --git a/third_party/web-animations-js/sources/web-animations.min.js b/third_party/web-animations-js/sources/web-animations.min.js
new file mode 100644
index 0000000..0dba22b
--- /dev/null
+++ b/third_party/web-animations-js/sources/web-animations.min.js
@@ -0,0 +1,17 @@
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+//     You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//     See the License for the specific language governing permissions and
+// limitations under the License.
+
+!function(a,b){b["true"]=a,function(){if(document.documentElement.animate){var a=document.documentElement.animate([],0),b=!0;if(a&&(b=!1,"play|currentTime|pause|reverse|playbackRate|cancel|finish|startTime|playState".split("|").forEach(function(c){void 0===a[c]&&(b=!0)})),!b)return}var c={},d={},e={},f=null;!function(a){function b(b,c){var d={delay:0,endDelay:0,fill:c?"both":"none",iterationStart:0,iterations:1,duration:c?"auto":0,playbackRate:1,direction:"normal",easing:"linear"};return"number"!=typeof b||isNaN(b)?void 0!==b&&Object.getOwnPropertyNames(b).forEach(function(c){if("auto"!=b[c]){if(("number"==typeof d[c]||"duration"==c)&&("number"!=typeof b[c]||isNaN(b[c])))return;if("fill"==c&&-1==p.indexOf(b[c]))return;if("direction"==c&&-1==q.indexOf(b[c]))return;if("playbackRate"==c&&a.isDeprecated("AnimationTiming.playbackRate","2014-11-28","Use AnimationPlayer.playbackRate instead."))return;d[c]=b[c]}}):d.duration=b,d}function c(a,c){var d=b(a,c);return d.easing=f(d.easing),d}function d(a,b,c,d){return 0>a||a>1||0>c||c>1?y:function(e){function f(a,b,c){return 3*a*(1-c)*(1-c)*c+3*b*(1-c)*c*c+c*c*c}for(var g=0,h=1;;){var i=(g+h)/2,j=f(a,c,i);if(Math.abs(e-j)<.001)return f(b,d,i);e>j?g=i:h=i}}}function e(a,b){return function(c){if(c>=1)return 1;var d=1/a;return c+=b*d,c-c%d}}function f(a){var b=w.exec(a);if(b)return d.apply(this,b.slice(1).map(Number));var c=x.exec(a);if(c)return e(Number(c[1]),{start:r,middle:s,end:t}[c[2]]);var f=u[a];return f?f:y}function g(a){return Math.abs(h(a)/a.playbackRate)}function h(a){return a.duration*a.iterations}function i(a,b,c){return null==b?z:b<c.delay?A:b>=c.delay+a?B:C}function j(a,b,c,d,e){switch(d){case A:return"backwards"==b||"both"==b?0:null;case C:return c-e;case B:return"forwards"==b||"both"==b?a:null;case z:return null}}function k(a,b,c,d){return(d.playbackRate<0?b-a:b)*d.playbackRate+c}function l(a,b,c,d,e){return 1/0===c||c===-1/0||c-d==b&&e.iterations&&(e.iterations+e.iterationStart)%1==0?a:c%a}function m(a,b,c,d){return 0===c?0:b==a?d.iterationStart+d.iterations-1:Math.floor(c/a)}function n(a,b,c,d){var e=a%2>=1,f="normal"==d.direction||d.direction==(e?"alternate-reverse":"alternate"),g=f?c:b-c,h=g/b;return b*d.easing(h)}function o(a,b,c){var d=i(a,b,c),e=j(a,c.fill,b,d,c.delay);if(null===e)return null;if(0===a)return d===A?0:1;var f=c.iterationStart*c.duration,g=k(a,e,f,c),o=l(c.duration,h(c),g,f,c),p=m(c.duration,o,g,c);return n(p,c.duration,o,c)/c.duration}var p="backwards|forwards|both".split("|"),q="reverse|alternate|alternate-reverse".split("|"),r=1,s=.5,t=0,u={ease:d(.25,.1,.25,1),"ease-in":d(.42,0,1,1),"ease-out":d(0,0,.58,1),"ease-in-out":d(.42,0,.58,1),"step-start":e(1,r),"step-middle":e(1,s),"step-end":e(1,t)},v="\\s*(-?\\d+\\.?\\d*|-?\\.\\d+)\\s*",w=new RegExp("cubic-bezier\\("+v+","+v+","+v+","+v+"\\)"),x=/steps\(\s*(\d+)\s*,\s*(start|middle|end)\s*\)/,y=function(a){return a},z=0,A=1,B=2,C=3;a.makeTiming=b,a.normalizeTimingInput=c,a.calculateActiveDuration=g,a.calculateTimeFraction=o,a.calculatePhase=i,a.toTimingFunction=f}(c,f),function(a){function b(a,b){return a in h?h[a][b]||b:b}function c(a,c,d){var g=e[a];if(g){f.style[a]=c;for(var h in g){var i=g[h],j=f.style[i];d[i]=b(i,j)}}else d[a]=b(a,c)}function d(b){function d(){var a=e.length;null==e[a-1].offset&&(e[a-1].offset=1),a>1&&null==e[0].offset&&(e[0].offset=0);for(var b=0,c=e[0].offset,d=1;a>d;d++){var f=e[d].offset;if(null!=f){for(var g=1;d-b>g;g++)e[b+g].offset=c+(f-c)*g/(d-b);b=d,c=f}}}if(!Array.isArray(b)&&null!==b)throw new TypeError("Keyframe effect must be null or an array of keyframes");if(null==b)return[];for(var e=b.map(function(b){var d={};for(var e in b){var f=b[e];if("offset"==e){if(null!=f&&(f=Number(f),!isFinite(f)))throw new TypeError("keyframe offsets must be numbers.")}else{if("composite"==e)throw{type:DOMException.NOT_SUPPORTED_ERR,name:"NotSupportedError",message:"add compositing is not supported"};f="easing"==e?a.toTimingFunction(f):""+f}c(e,f,d)}return void 0==d.offset&&(d.offset=null),void 0==d.easing&&(d.easing=a.toTimingFunction("linear")),d}),f=!0,g=-1/0,h=0;h<e.length;h++){var i=e[h].offset;if(null!=i){if(g>i)throw{code:DOMException.INVALID_MODIFICATION_ERR,name:"InvalidModificationError",message:"Keyframes are not loosely sorted by offset. Sort or specify offsets."};g=i}else f=!1}return e=e.filter(function(a){return a.offset>=0&&a.offset<=1}),f||d(),e}var e={background:["backgroundImage","backgroundPosition","backgroundSize","backgroundRepeat","backgroundAttachment","backgroundOrigin","backgroundClip","backgroundColor"],border:["borderTopColor","borderTopStyle","borderTopWidth","borderRightColor","borderRightStyle","borderRightWidth","borderBottomColor","borderBottomStyle","borderBottomWidth","borderLeftColor","borderLeftStyle","borderLeftWidth"],borderBottom:["borderBottomWidth","borderBottomStyle","borderBottomColor"],borderColor:["borderTopColor","borderRightColor","borderBottomColor","borderLeftColor"],borderLeft:["borderLeftWidth","borderLeftStyle","borderLeftColor"],borderRadius:["borderTopLeftRadius","borderTopRightRadius","borderBottomRightRadius","borderBottomLeftRadius"],borderRight:["borderRightWidth","borderRightStyle","borderRightColor"],borderTop:["borderTopWidth","borderTopStyle","borderTopColor"],borderWidth:["borderTopWidth","borderRightWidth","borderBottomWidth","borderLeftWidth"],flex:["flexGrow","flexShrink","flexBasis"],font:["fontFamily","fontSize","fontStyle","fontVariant","fontWeight","lineHeight"],margin:["marginTop","marginRight","marginBottom","marginLeft"],outline:["outlineColor","outlineStyle","outlineWidth"],padding:["paddingTop","paddingRight","paddingBottom","paddingLeft"]},f=document.createElementNS("http://www.w3.org/1999/xhtml","div"),g={thin:"1px",medium:"3px",thick:"5px"},h={borderBottomWidth:g,borderLeftWidth:g,borderRightWidth:g,borderTopWidth:g,fontSize:{"xx-small":"60%","x-small":"75%",small:"89%",medium:"100%",large:"120%","x-large":"150%","xx-large":"200%"},fontWeight:{normal:"400",bold:"700"},outlineWidth:g,textShadow:{none:"0px 0px 0px transparent"},boxShadow:{none:"0px 0px 0px 0px transparent"}};a.normalizeKeyframes=d}(c,f),function(a){var b={};a.isDeprecated=function(a,c,d,e){var f=e?"are":"is",g=new Date,h=new Date(c);return h.setMonth(h.getMonth()+3),h>g?(a in b||console.warn("Web Animations: "+a+" "+f+" deprecated and will stop working on "+h.toDateString()+". "+d),b[a]=!0,!1):!0},a.deprecated=function(b,c,d,e){if(a.isDeprecated(b,c,d,e))throw new Error(b+" "+auxVerb+" no longer supported. "+d)}}(c),function(a,b){b.AnimationNode=function(b){var c=a.calculateActiveDuration(b),d=function(d){return a.calculateTimeFraction(c,d,b)};return d._totalDuration=b.delay+c+b.endDelay,d._isCurrent=function(d){var e=a.calculatePhase(c,d,b);return e===PhaseActive||e===PhaseBefore},d}}(c,d),function(a,b){function c(a){for(var b={},c=0;c<a.length;c++)for(var d in a[c])if("offset"!=d&&"easing"!=d&&"composite"!=d){var e={offset:a[c].offset,easing:a[c].easing,value:a[c][d]};b[d]=b[d]||[],b[d].push(e)}for(var f in b){var g=b[f];if(0!=g[0].offset||1!=g[g.length-1].offset)throw{type:DOMException.NOT_SUPPORTED_ERR,name:"NotSupportedError",message:"Partial keyframes are not supported"}}return b}function d(a){var c=[];for(var d in a)for(var e=a[d],f=0;f<e.length-1;f++){var g=e[f].offset,h=e[f+1].offset,i=e[f].value,j=e[f+1].value;g==h&&(1==h?i=j:j=i),c.push({startTime:g,endTime:h,easing:e[f].easing,property:d,interpolation:b.propertyInterpolation(d,i,j)})}return c.sort(function(a,b){return a.startTime-b.startTime}),c}b.convertEffectInput=function(e){var f=a.normalizeKeyframes(e),g=c(f),h=d(g);return function(a,c){if(null!=c)h.filter(function(a){return 0>=c&&0==a.startTime||c>=1&&1==a.endTime||c>=a.startTime&&c<=a.endTime}).forEach(function(d){var e=c-d.startTime,f=d.endTime-d.startTime,g=0==f?0:d.easing(e/f);b.apply(a,d.property,d.interpolation(g))});else for(var d in g)"offset"!=d&&"easing"!=d&&"composite"!=d&&b.clear(a,d)}}}(c,d,f),function(a){function b(a,b,c){e[c]=e[c]||[],e[c].push([a,b])}function c(a,c,d){for(var e=0;e<d.length;e++){var f=d[e];b(a,c,f),/-/.test(f)&&b(a,c,f.replace(/-(.)/g,function(a,b){return b.toUpperCase()}))}}function d(b,c,d){for(var f=c==d?[]:e[b],g=0;f&&g<f.length;g++){var h=f[g][0](c),i=f[g][0](d);if(void 0!==h&&void 0!==i){var j=f[g][1](h,i);if(j){var k=a.Interpolation.apply(null,j);return function(a){return 0==a?c:1==a?d:k(a)}}}}return a.Interpolation(!1,!0,function(a){return a?d:c})}var e={};a.addPropertiesHandler=c,a.propertyInterpolation=d}(d,f),function(a,b){b.Animation=function(c,d,e){var f,g=b.AnimationNode(a.normalizeTimingInput(e)),h=b.convertEffectInput(d),i=function(){h(c,f)};return i._update=function(a){return f=g(a),null!==f},i._clear=function(){h(c,null)},i._hasSameTarget=function(a){return c===a},i._isCurrent=g._isCurrent,i._totalDuration=g._totalDuration,i},b.NullAnimation=function(a){var b=function(){a&&(a(),a=null)};return b._update=function(){return null},b._totalDuration=0,b._isCurrent=function(){return!1},b._hasSameTarget=function(){return!1},b}}(c,d,f),function(a){function b(a,b,c){c.enumerable=!0,c.configurable=!0,Object.defineProperty(a,b,c)}function c(a){this._surrogateStyle=document.createElementNS("http://www.w3.org/1999/xhtml","div").style,this._style=a.style,this._length=0,this._isAnimatedProperty={};for(var b=0;b<this._style.length;b++){var c=this._style[b];this._surrogateStyle[c]=this._style[c]}this._updateIndices()}function d(a){if(!a._webAnimationsPatchedStyle){var d=new c(a);try{b(a,"style",{get:function(){return d}})}catch(e){a.style._set=function(b,c){a.style[b]=c},a.style._clear=function(b){a.style[b]=""}}a._webAnimationsPatchedStyle=a.style}}var e={cssText:1,length:1,parentRule:1},f={getPropertyCSSValue:1,getPropertyPriority:1,getPropertyValue:1,item:1,removeProperty:1,setProperty:1},g={removeProperty:1,setProperty:1};c.prototype={get cssText(){return this._surrogateStyle.cssText},set cssText(a){for(var b={},c=0;c<this._surrogateStyle.length;c++)b[this._surrogateStyle[c]]=!0;this._surrogateStyle.cssText=a,this._updateIndices();for(var c=0;c<this._surrogateStyle.length;c++)b[this._surrogateStyle[c]]=!0;for(var d in b)this._isAnimatedProperty[d]||this._style.setProperty(d,this._surrogateStyle.getPropertyValue(d))},get length(){return this._surrogateStyle.length},get parentRule(){return this._style.parentRule},_updateIndices:function(){for(;this._length<this._surrogateStyle.length;)Object.defineProperty(this,this._length,{configurable:!0,enumerable:!1,get:function(a){return function(){return this._surrogateStyle[a]}}(this._length)}),this._length++;for(;this._length>this._surrogateStyle.length;)this._length--,Object.defineProperty(this,this._length,{configurable:!0,enumerable:!1,value:void 0})},_set:function(a,b){this._style[a]=b,this._isAnimatedProperty[a]=!0},_clear:function(a){this._style[a]=this._surrogateStyle[a],delete this._isAnimatedProperty[a]}};for(var h in f)c.prototype[h]=function(a,b){return function(){var c=this._surrogateStyle[a].apply(this._surrogateStyle,arguments);return b&&(this._isAnimatedProperty[arguments[0]]||this._style[a].apply(this._style,arguments),this._updateIndices()),c}}(h,h in g);for(var i in document.documentElement.style)i in e||i in f||!function(a){b(c.prototype,a,{get:function(){return this._surrogateStyle[a]},set:function(b){this._surrogateStyle[a]=b,this._updateIndices(),this._isAnimatedProperty[a]||(this._style[a]=b)}})}(i);a.apply=function(b,c,e){d(b),b.style._set(a.propertyName(c),e)},a.clear=function(b,c){b._webAnimationsPatchedStyle&&b.style._clear(a.propertyName(c))}}(d,f),function(a){window.Element.prototype.animate=function(b,c){return a.timeline._play(a.Animation(this,b,c))}}(d),function(a){function b(a,c,d){if("number"==typeof a&&"number"==typeof c)return a*(1-d)+c*d;if("boolean"==typeof a&&"boolean"==typeof c)return.5>d?a:c;if(a.length==c.length){for(var e=[],f=0;f<a.length;f++)e.push(b(a[f],c[f],d));return e}throw"Mismatched interpolation arguments "+a+":"+c}a.Interpolation=function(a,c,d){return function(e){return d(b(a,c,e))}}}(d,f),function(a){function b(a,b,c){return Math.max(Math.min(a,c),b)}function c(c,d,e){var f=a.dot(c,d);f=b(f,-1,1);var g=[];if(1===f)g=c;else for(var h=Math.acos(f),i=1*Math.sin(e*h)/Math.sqrt(1-f*f),j=0;4>j;j++)g.push(c[j]*(Math.cos(e*h)-f*i)+d[j]*i);return g}var d=function(){function a(a,b){for(var c=[[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0]],d=0;4>d;d++)for(var e=0;4>e;e++)for(var f=0;4>f;f++)c[d][e]+=b[d][f]*a[f][e];return c}function b(a){return 0==a[0][2]&&0==a[0][3]&&0==a[1][2]&&0==a[1][3]&&0==a[2][0]&&0==a[2][1]&&1==a[2][2]&&0==a[2][3]&&0==a[3][2]&&1==a[3][3]}function c(c,d,e,f,g){for(var h=[[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]],i=0;4>i;i++)h[i][3]=g[i];for(var i=0;3>i;i++)for(var j=0;3>j;j++)h[3][i]+=c[j]*h[j][i];var k=f[0],l=f[1],m=f[2],n=f[3],o=[[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]];o[0][0]=1-2*(l*l+m*m),o[0][1]=2*(k*l-m*n),o[0][2]=2*(k*m+l*n),o[1][0]=2*(k*l+m*n),o[1][1]=1-2*(k*k+m*m),o[1][2]=2*(l*m-k*n),o[2][0]=2*(k*m-l*n),o[2][1]=2*(l*m+k*n),o[2][2]=1-2*(k*k+l*l),h=a(h,o);var p=[[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]];e[2]&&(p[2][1]=e[2],h=a(h,p)),e[1]&&(p[2][1]=0,p[2][0]=e[0],h=a(h,p)),e[0]&&(p[2][0]=0,p[1][0]=e[0],h=a(h,p));for(var i=0;3>i;i++)for(var j=0;3>j;j++)h[i][j]*=d[i];return b(h)?[h[0][0],h[0][1],h[1][0],h[1][1],h[3][0],h[3][1]]:h[0].concat(h[1],h[2],h[3])}return c}();a.composeMatrix=d,a.quat=c}(d,f),function(a){var b=0,c=function(a,b,c){this.target=a,this.currentTime=b,this.timelineTime=c,this.type="finish",this.bubbles=!1,this.cancelable=!1,this.currentTarget=a,this.defaultPrevented=!1,this.eventPhase=Event.AT_TARGET,this.timeStamp=Date.now()};a.Player=function(a){this._sequenceNumber=b++,this._currentTime=0,this._startTime=null,this.paused=!1,this._playbackRate=1,this._inTimeline=!0,this._finishedFlag=!1,this.onfinish=null,this._finishHandlers=[],this._source=a,this._inEffect=this._source._update(0),this._idle=!0,this._currentTimePending=!1},a.Player.prototype={_ensureAlive:function(){this._inEffect=this._source._update(this.currentTime),this._inTimeline||!this._inEffect&&this._finishedFlag||(this._inTimeline=!0,a.timeline._players.push(this))},_tickCurrentTime:function(a,b){a!=this._currentTime&&(this._currentTime=a,this.finished&&!b&&(this._currentTime=this._playbackRate>0?this._totalDuration:0),this._ensureAlive())},get currentTime(){return this._idle||this._currentTimePending?null:this._currentTime},set currentTime(b){b=+b,isNaN(b)||(a.restart(),this.paused||null==this._startTime||(this._startTime=this._timeline.currentTime-b/this._playbackRate),this._currentTimePending=!1,this._currentTime!=b&&(this._tickCurrentTime(b,!0),a.invalidateEffects()))},get startTime(){return this._startTime},set startTime(b){b=+b,isNaN(b)||this.paused||this._idle||(this._startTime=b,this._tickCurrentTime((this._timeline.currentTime-this._startTime)*this.playbackRate),a.invalidateEffects())},get playbackRate(){return this._playbackRate},get finished(){return!this._idle&&(this._playbackRate>0&&this._currentTime>=this._totalDuration||this._playbackRate<0&&this._currentTime<=0)},get _totalDuration(){return this._source._totalDuration},get playState(){return this._idle?"idle":null==this._startTime&&!this.paused&&0!=this.playbackRate||this._currentTimePending?"pending":this.paused?"paused":this.finished?"finished":"running"},play:function(){this.paused=!1,(this.finished||this._idle)&&(this._currentTime=this._playbackRate>0?0:this._totalDuration,this._startTime=null,a.invalidateEffects()),this._finishedFlag=!1,a.restart(),this._idle=!1,this._ensureAlive()},pause:function(){this.finished||this.paused||this._idle||(this._currentTimePending=!0),this._startTime=null,this.paused=!0},finish:function(){this._idle||(this.currentTime=this._playbackRate>0?this._totalDuration:0,this._startTime=this._totalDuration-this.currentTime,this._currentTimePending=!1)},cancel:function(){this._inEffect=!1,this._idle=!0,this.currentTime=0,this._startTime=null},reverse:function(){this._playbackRate*=-1,this._startTime=null,this.play()},addEventListener:function(a,b){"function"==typeof b&&"finish"==a&&this._finishHandlers.push(b)},removeEventListener:function(a,b){if("finish"==a){var c=this._finishHandlers.indexOf(b);c>=0&&this._finishHandlers.splice(c,1)}},_fireEvents:function(a){var b=this.finished;if((b||this._idle)&&!this._finishedFlag){var d=new c(this,this._currentTime,a),e=this._finishHandlers.concat(this.onfinish?[this.onfinish]:[]);setTimeout(function(){e.forEach(function(a){a.call(d.target,d)})},0)}this._finishedFlag=b},_tick:function(a){return this._idle||this.paused||(null==this._startTime?this.startTime=a-this._currentTime/this.playbackRate:this.finished||this._tickCurrentTime((a-this._startTime)*this.playbackRate)),this._currentTimePending=!1,this._fireEvents(a),!this._idle&&(this._inEffect||!this._finishedFlag)}}}(d,f),function(a,b){function c(a){var b=i;i=[],g(a),b.forEach(function(b){b[1](a)}),m&&g(a),f()}function d(a,b){return a._sequenceNumber-b._sequenceNumber}function e(){this._players=[],this.currentTime=window.performance&&performance.now?performance.now():0}function f(){n.forEach(function(a){a()})}function g(a){l=!1;var c=b.timeline;c.currentTime=a,c._players.sort(d),k=!1;var e=c._players;c._players=[];var f=[],g=[];e=e.filter(function(b){return b._inTimeline=b._tick(a),b._inEffect?g.push(b._source):f.push(b._source),b.finished||b.paused||b._idle||(k=!0),b._inTimeline}),n.length=0,n.push.apply(n,f),n.push.apply(n,g),c._players.push.apply(c._players,e),m=!1,k&&requestAnimationFrame(function(){})}var h=window.requestAnimationFrame,i=[],j=0;window.requestAnimationFrame=function(a){var b=j++;return 0==i.length&&h(c),i.push([b,a]),b},window.cancelAnimationFrame=function(a){i.forEach(function(b){b[0]==a&&(b[1]=function(){})})},e.prototype={_play:function(c){c._timing=a.normalizeTimingInput(c.timing);var d=new b.Player(c);return d._idle=!1,d._timeline=this,this._players.push(d),b.restart(),b.invalidateEffects(),d}};var k=!1,l=!1;b.restart=function(){return k||(k=!0,requestAnimationFrame(function(){}),l=!0),l};var m=!1;b.invalidateEffects=function(){m=!0};var n=[],o=window.getComputedStyle;Object.defineProperty(window,"getComputedStyle",{configurable:!0,enumerable:!0,value:function(){return m&&g(p.currentTime),f(),o.apply(this,arguments)}});var p=new e;b.timeline=p}(c,d,f),function(a){function b(a,b){for(var c=0,d=0;d<a.length;d++)c+=a[d]*b[d];return c}function c(a,b){return[a[0]*b[0]+a[4]*b[1]+a[8]*b[2]+a[12]*b[3],a[1]*b[0]+a[5]*b[1]+a[9]*b[2]+a[13]*b[3],a[2]*b[0]+a[6]*b[1]+a[10]*b[2]+a[14]*b[3],a[3]*b[0]+a[7]*b[1]+a[11]*b[2]+a[15]*b[3],a[0]*b[4]+a[4]*b[5]+a[8]*b[6]+a[12]*b[7],a[1]*b[4]+a[5]*b[5]+a[9]*b[6]+a[13]*b[7],a[2]*b[4]+a[6]*b[5]+a[10]*b[6]+a[14]*b[7],a[3]*b[4]+a[7]*b[5]+a[11]*b[6]+a[15]*b[7],a[0]*b[8]+a[4]*b[9]+a[8]*b[10]+a[12]*b[11],a[1]*b[8]+a[5]*b[9]+a[9]*b[10]+a[13]*b[11],a[2]*b[8]+a[6]*b[9]+a[10]*b[10]+a[14]*b[11],a[3]*b[8]+a[7]*b[9]+a[11]*b[10]+a[15]*b[11],a[0]*b[12]+a[4]*b[13]+a[8]*b[14]+a[12]*b[15],a[1]*b[12]+a[5]*b[13]+a[9]*b[14]+a[13]*b[15],a[2]*b[12]+a[6]*b[13]+a[10]*b[14]+a[14]*b[15],a[3]*b[12]+a[7]*b[13]+a[11]*b[14]+a[15]*b[15]]}function d(a){switch(a.t){case"rotatex":var b=a.d[0].rad||0,c=a.d[0].deg||0,d=c*Math.PI/180+b;return[1,0,0,0,0,Math.cos(d),Math.sin(d),0,0,-Math.sin(d),Math.cos(d),0,0,0,0,1];case"rotatey":var b=a.d[0].rad||0,c=a.d[0].deg||0,d=c*Math.PI/180+b;return[Math.cos(d),0,-Math.sin(d),0,0,1,0,0,Math.sin(d),0,Math.cos(d),0,0,0,0,1];case"rotate":case"rotatez":var b=a.d[0].rad||0,c=a.d[0].deg||0,d=c*Math.PI/180+b;return[Math.cos(d),Math.sin(d),0,0,-Math.sin(d),Math.cos(d),0,0,0,0,1,0,0,0,0,1];case"rotate3d":var e=a.d[0],f=a.d[1],g=a.d[2],b=a.d[3].rad||0,c=a.d[3].deg||0,d=c*Math.PI/180+b,h=e*e+f*f+g*g;if(0===h)e=1,f=0,g=0;else if(1!==h){var i=Math.sqrt(h);e/=i,f/=i,g/=i}var j=Math.sin(d/2),k=j*Math.cos(d/2),l=j*j;return[1-2*(f*f+g*g)*l,2*(e*f*l+g*k),2*(e*g*l-f*k),0,2*(e*f*l-g*k),1-2*(e*e+g*g)*l,2*(f*g*l+e*k),0,2*(e*g*l+f*k),2*(f*g*l-e*k),1-2*(e*e+f*f)*l,0,0,0,0,1];case"scale":return[a.d[0],0,0,0,0,a.d[1],0,0,0,0,1,0,0,0,0,1];case"scalex":return[a.d[0],0,0,0,0,1,0,0,0,0,1,0,0,0,0,1];case"scaley":return[1,0,0,0,0,a.d[0],0,0,0,0,1,0,0,0,0,1];case"scalez":return[1,0,0,0,0,1,0,0,0,0,a.d[0],0,0,0,0,1];case"scale3d":return[a.d[0],0,0,0,0,a.d[1],0,0,0,0,a.d[2],0,0,0,0,1];case"skew":var m=a.d[0].deg||0,n=a.d[0].rad||0,o=a.d[1].deg||0,p=a.d[1].rad||0,q=m*Math.PI/180+n,r=o*Math.PI/180+p;return[1,Math.tan(r),0,0,Math.tan(q),1,0,0,0,0,1,0,0,0,0,1];case"skewx":var b=a.d[0].rad||0,c=a.d[0].deg||0,d=c*Math.PI/180+b;return[1,0,0,0,Math.tan(d),1,0,0,0,0,1,0,0,0,0,1];case"skewy":var b=a.d[0].rad||0,c=a.d[0].deg||0,d=c*Math.PI/180+b;return[1,Math.tan(d),0,0,0,1,0,0,0,0,1,0,0,0,0,1];case"translate":var e=a.d[0].px||0,f=a.d[1].px||0;return[1,0,0,0,0,1,0,0,0,0,1,0,e,f,0,1];case"translatex":var e=a.d[0].px||0;return[1,0,0,0,0,1,0,0,0,0,1,0,e,0,0,1];case"translatey":var f=a.d[0].px||0;return[1,0,0,0,0,1,0,0,0,0,1,0,0,f,0,1];case"translatez":var g=a.d[0].px||0;return[1,0,0,0,0,1,0,0,0,0,1,0,0,0,g,1];case"translate3d":var e=a.d[0].px||0,f=a.d[1].px||0,g=a.d[2].px||0;return[1,0,0,0,0,1,0,0,0,0,1,0,e,f,g,1];case"perspective":var s=a.d[0].px?-1/a.d[0].px:0;return[1,0,0,0,0,1,0,0,0,0,1,s,0,0,0,1];case"matrix":return[a.d[0],a.d[1],0,0,a.d[2],a.d[3],0,0,0,0,1,0,a.d[4],a.d[5],0,1];case"matrix3d":return a.d}}function e(a){return 0===a.length?[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]:a.map(d).reduce(c)}function f(a){return[g(e(a))]}var g=function(){function a(a){return a[0][0]*a[1][1]*a[2][2]+a[1][0]*a[2][1]*a[0][2]+a[2][0]*a[0][1]*a[1][2]-a[0][2]*a[1][1]*a[2][0]-a[1][2]*a[2][1]*a[0][0]-a[2][2]*a[0][1]*a[1][0]}function c(b){for(var c=1/a(b),d=b[0][0],e=b[0][1],f=b[0][2],g=b[1][0],h=b[1][1],i=b[1][2],j=b[2][0],k=b[2][1],l=b[2][2],m=[[(h*l-i*k)*c,(f*k-e*l)*c,(e*i-f*h)*c,0],[(i*j-g*l)*c,(d*l-f*j)*c,(f*g-d*i)*c,0],[(g*k-h*j)*c,(j*e-d*k)*c,(d*h-e*g)*c,0]],n=[],o=0;3>o;o++){for(var p=0,q=0;3>q;q++)p+=b[3][q]*m[q][o];n.push(p)}return n.push(1),m.push(n),m}function d(a){return[[a[0][0],a[1][0],a[2][0],a[3][0]],[a[0][1],a[1][1],a[2][1],a[3][1]],[a[0][2],a[1][2],a[2][2],a[3][2]],[a[0][3],a[1][3],a[2][3],a[3][3]]]}function e(a,b){for(var c=[],d=0;4>d;d++){for(var e=0,f=0;4>f;f++)e+=a[f]*b[f][d];c.push(e)}return c}function f(a){var b=g(a);return[a[0]/b,a[1]/b,a[2]/b]}function g(a){return Math.sqrt(a[0]*a[0]+a[1]*a[1]+a[2]*a[2])}function h(a,b,c,d){return[c*a[0]+d*b[0],c*a[1]+d*b[1],c*a[2]+d*b[2]]}function i(a,b){return[a[1]*b[2]-a[2]*b[1],a[2]*b[0]-a[0]*b[2],a[0]*b[1]-a[1]*b[0]]}function j(j){var k=[j.slice(0,4),j.slice(4,8),j.slice(8,12),j.slice(12,16)];if(1!==k[3][3])return null;for(var l=[],m=0;4>m;m++)l.push(k[m].slice());for(var m=0;3>m;m++)l[m][3]=0;if(0===a(l))return!1;var n,o=[];if(k[0][3]||k[1][3]||k[2][3]){o.push(k[0][3]),o.push(k[1][3]),o.push(k[2][3]),o.push(k[3][3]);var p=c(l),q=d(p);n=e(o,q)}else n=[0,0,0,1];var r=k[3].slice(0,3),s=[];s.push(k[0].slice(0,3));var t=[];t.push(g(s[0])),s[0]=f(s[0]);var u=[];s.push(k[1].slice(0,3)),u.push(b(s[0],s[1])),s[1]=h(s[1],s[0],1,-u[0]),t.push(g(s[1])),s[1]=f(s[1]),u[0]/=t[1],s.push(k[2].slice(0,3)),u.push(b(s[0],s[2])),s[2]=h(s[2],s[0],1,-u[1]),u.push(b(s[1],s[2])),s[2]=h(s[2],s[1],1,-u[2]),t.push(g(s[2])),s[2]=f(s[2]),u[1]/=t[2],u[2]/=t[2];var v=i(s[1],s[2]);if(b(s[0],v)<0)for(var m=0;3>m;m++)t[m]*=-1,s[m][0]*=-1,s[m][1]*=-1,s[m][2]*=-1;var w,x,y=s[0][0]+s[1][1]+s[2][2]+1;return y>1e-4?(w=.5/Math.sqrt(y),x=[(s[2][1]-s[1][2])*w,(s[0][2]-s[2][0])*w,(s[1][0]-s[0][1])*w,.25/w]):s[0][0]>s[1][1]&&s[0][0]>s[2][2]?(w=2*Math.sqrt(1+s[0][0]-s[1][1]-s[2][2]),x=[.25*w,(s[0][1]+s[1][0])/w,(s[0][2]+s[2][0])/w,(s[2][1]-s[1][2])/w]):s[1][1]>s[2][2]?(w=2*Math.sqrt(1+s[1][1]-s[0][0]-s[2][2]),x=[(s[0][1]+s[1][0])/w,.25*w,(s[1][2]+s[2][1])/w,(s[0][2]-s[2][0])/w]):(w=2*Math.sqrt(1+s[2][2]-s[0][0]-s[1][1]),x=[(s[0][2]+s[2][0])/w,(s[1][2]+s[2][1])/w,.25*w,(s[1][0]-s[0][1])/w]),[r,t,u,x,n]}return j}();a.dot=b,a.makeMatrixDecomposition=f}(d,f),function(a){function b(a,b){var c=a.exec(b);return c?(c=a.ignoreCase?c[0].toLowerCase():c[0],[c,b.substr(c.length)]):void 0}function c(a,b){b=b.replace(/^\s*/,"");var c=a(b);return c?[c[0],c[1].replace(/^\s*/,"")]:void 0}function d(a,d,e){a=c.bind(null,a);for(var f=[];;){var g=a(e);if(!g)return[f,e];if(f.push(g[0]),e=g[1],g=b(d,e),!g||""==g[1])return[f,e];e=g[1]}}function e(a,b){for(var c=0,d=0;d<b.length&&(!/\s|,/.test(b[d])||0!=c);d++)if("("==b[d])c++;else if(")"==b[d]&&(c--,0==c&&d++,0>=c))break;var e=a(b.substr(0,d));return void 0==e?void 0:[e,b.substr(d)]}function f(a,b){for(var c=a,d=b;c&&d;)c>d?c%=d:d%=c;return c=a*b/(c+d)}function g(a){return function(b){var c=a(b);return c&&(c[0]=void 0),c}}function h(a,b){return function(c){var d=a(c);return d?d:[b,c]}}function i(b,c){for(var d=[],e=0;e<b.length;e++){var f=a.consumeTrimmed(b[e],c);if(!f||""==f[0])return;void 0!==f[0]&&d.push(f[0]),c=f[1]}return""==c?d:void 0}function j(a,b,c,d,e){for(var g=[],h=[],i=[],j=f(d.length,e.length),k=0;j>k;k++){var l=b(d[k%d.length],e[k%e.length]);if(!l)return;g.push(l[0]),h.push(l[1]),i.push(l[2])}return[g,h,function(b){var d=b.map(function(a,b){return i[b](a)}).join(c);return a?a(d):d}]}function k(a,b,c){for(var d=[],e=[],f=[],g=0,h=0;h<c.length;h++)if("function"==typeof c[h]){var i=c[h](a[g],b[g++]);d.push(i[0]),e.push(i[1]),f.push(i[2])}else!function(a){d.push(!1),e.push(!1),f.push(function(){return c[a]})}(h);return[d,e,function(a){for(var b="",c=0;c<a.length;c++)b+=f[c](a[c]);return b}]}a.consumeToken=b,a.consumeTrimmed=c,a.consumeRepeated=d,a.consumeParenthesised=e,a.ignore=g,a.optional=h,a.consumeList=i,a.mergeNestedRepeated=j.bind(null,null),a.mergeWrappedNestedRepeated=j,a.mergeList=k}(d),function(a){function b(b){function c(b){var c=a.consumeToken(/^inset/i,b);if(c)return d.inset=!0,c;var c=a.consumeLengthOrPercent(b);if(c)return d.lengths.push(c[0]),c;var c=a.consumeColor(b);return c?(d.color=c[0],c):void 0}var d={inset:!1,lengths:[],color:null},e=a.consumeRepeated(c,/^/,b);return e&&e[0].length?[d,e[1]]:void 0}function c(c){var d=a.consumeRepeated(b,/^,/,c);return d&&""==d[1]?d[0]:void 0}function d(b,c){for(;b.lengths.length<Math.max(b.lengths.length,c.lengths.length);)b.lengths.push({px:0});for(;c.lengths.length<Math.max(b.lengths.length,c.lengths.length);)c.lengths.push({px:0});if(b.inset==c.inset&&!!b.color==!!c.color){for(var d,e=[],f=[[],0],g=[[],0],h=0;h<b.lengths.length;h++){var i=a.mergeDimensions(b.lengths[h],c.lengths[h],2==h);f[0].push(i[0]),g[0].push(i[1]),e.push(i[2])}if(b.color&&c.color){var j=a.mergeColors(b.color,c.color);f[1]=j[0],g[1]=j[1],d=j[2]}return[f,g,function(a){for(var c=b.inset?"inset ":" ",f=0;f<e.length;f++)c+=e[f](a[0][f])+" ";return d&&(c+=d(a[1])),c}]}}function e(b,c,d,e){function f(a){return{inset:a,color:[0,0,0,0],lengths:[{px:0},{px:0},{px:0},{px:0}]}}for(var g=[],h=[],i=0;i<d.length||i<e.length;i++){var j=d[i]||f(e[i].inset),k=e[i]||f(d[i].inset);g.push(j),h.push(k)}return a.mergeNestedRepeated(b,c,g,h)}var f=e.bind(null,d,", ");a.addPropertiesHandler(c,f,["box-shadow","text-shadow"])}(d),function(a){function b(a){return a.toFixed(3).replace(".000","")}function c(a,b,c){return Math.min(b,Math.max(a,c))}function d(a){return/^\s*[-+]?(\d*\.)?\d+\s*$/.test(a)?Number(a):void 0}function e(a,c){return[a,c,b]}function f(a,b){return 0!=a?h(0,1/0)(a,b):void 0}function g(a,b){return[a,b,function(a){return Math.round(c(1,1/0,a))}]}function h(a,d){return function(e,f){return[e,f,function(e){return b(c(a,d,e))}]}}function i(a,b){return[a,b,Math.round]}a.clamp=c,a.addPropertiesHandler(d,h(0,1/0),["border-image-width","line-height"]),a.addPropertiesHandler(d,h(0,1),["opacity","shape-image-threshold"]),a.addPropertiesHandler(d,h(.01,1/0),["zoom"]),a.addPropertiesHandler(d,f,["flex-grow","flex-shrink"]),a.addPropertiesHandler(d,e,["zoom"]),a.addPropertiesHandler(d,g,["orphans","widows"]),a.addPropertiesHandler(d,i,["z-index"]),a.parseNumber=d,a.mergeNumbers=e,a.numberToString=b}(d,f),function(a){function b(a,b){return"visible"==a||"visible"==b?[0,1,function(c){return 0>=c?a:c>=1?b:"visible"}]:void 0}a.addPropertiesHandler(String,b,["visibility"])}(d),function(a){function b(a){a=a.trim(),e.fillStyle="#000",e.fillStyle=a;var b=e.fillStyle;if(e.fillStyle="#fff",e.fillStyle=a,b==e.fillStyle){e.fillRect(0,0,1,1);var c=e.getImageData(0,0,1,1).data;e.clearRect(0,0,1,1);var d=c[3]/255;return[c[0]*d,c[1]*d,c[2]*d,d]}}function c(b,c){return[b,c,function(b){function c(a){return Math.max(0,Math.min(255,a))}if(b[3])for(var d=0;3>d;d++)b[d]=Math.round(c(b[d]/b[3]));return b[3]=a.numberToString(a.clamp(0,1,b[3])),"rgba("+b.join(",")+")"}]}var d=document.createElementNS("http://www.w3.org/1999/xhtml","canvas");d.width=d.height=1;var e=d.getContext("2d");a.addPropertiesHandler(b,c,["background-color","border-bottom-color","border-left-color","border-right-color","border-top-color","color","outline-color","text-decoration-color"]),a.consumeColor=a.consumeParenthesised.bind(null,b),a.mergeColors=c}(d,f),function(a,b){function c(a,b){if(b=b.trim().toLowerCase(),"0"==b&&"px".search(a)>=0)return{px:0};if(/^[^(]*$|^calc/.test(b)){b=b.replace(/calc\(/g,"(");var c={};b=b.replace(a,function(a){return c[a]=null,"U"+a});for(var d="U("+a.source+")",e=b.replace(/[-+]?(\d*\.)?\d+/g,"N").replace(new RegExp("N"+d,"g"),"D").replace(/\s[+-]\s/g,"O").replace(/\s/g,""),f=[/N\*(D)/g,/(N|D)[*/]N/g,/(N|D)O\1/g,/\((N|D)\)/g],g=0;g<f.length;)f[g].test(e)?(e=e.replace(f[g],"$1"),g=0):g++;if("D"==e){for(var h in c){var i=eval(b.replace(new RegExp("U"+h,"g"),"").replace(new RegExp(d,"g"),"*0"));if(!isFinite(i))return;c[h]=i}return c}}}function d(a,b){return e(a,b,!0)}function e(b,c,d){var e,f=[];for(e in b)f.push(e);for(e in c)f.indexOf(e)<0&&f.push(e);return b=f.map(function(a){return b[a]||0}),c=f.map(function(a){return c[a]||0}),[b,c,function(b){var c=b.map(function(c,e){return 1==b.length&&d&&(c=Math.max(c,0)),a.numberToString(c)+f[e]}).join(" + ");return b.length>1?"calc("+c+")":c}]}var f="px|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc",g=c.bind(null,new RegExp(f,"g")),h=c.bind(null,new RegExp(f+"|%","g")),i=c.bind(null,/deg|rad|grad|turn/g);a.parseLength=g,a.parseLengthOrPercent=h,a.consumeLengthOrPercent=a.consumeParenthesised.bind(null,h),a.parseAngle=i,a.mergeDimensions=e;var j=a.consumeParenthesised.bind(null,g),k=a.consumeRepeated.bind(void 0,j,/^/),l=a.consumeRepeated.bind(void 0,k,/^,/);a.consumeSizePairList=l;var m=function(a){var b=l(a);return b&&""==b[1]?b[0]:void 0},n=a.mergeNestedRepeated.bind(void 0,d," "),o=a.mergeNestedRepeated.bind(void 0,n,",");a.mergeNonNegativeSizePair=n,a.addPropertiesHandler(m,o,["background-size"]),a.addPropertiesHandler(h,d,["border-bottom-width","border-image-width","border-left-width","border-right-width","border-top-width","flex-basis","font-size","height","line-height","max-height","max-width","outline-width","width"]),a.addPropertiesHandler(h,e,["border-bottom-left-radius","border-bottom-right-radius","border-top-left-radius","border-top-right-radius","bottom","left","letter-spacing","margin-bottom","margin-left","margin-right","margin-top","min-height","min-width","outline-offset","padding-bottom","padding-left","padding-right","padding-top","perspective","right","shape-margin","text-indent","top","vertical-align","word-spacing"])}(d,f),function(a){function b(b){return a.consumeLengthOrPercent(b)||a.consumeToken(/^auto/,b)}function c(c){var d=a.consumeList([a.ignore(a.consumeToken.bind(null,/^rect/)),a.ignore(a.consumeToken.bind(null,/^\(/)),a.consumeRepeated.bind(null,b,/^,/),a.ignore(a.consumeToken.bind(null,/^\)/))],c);return d&&4==d[0].length?d[0]:void 0}function d(b,c){return"auto"==b||"auto"==c?[!0,!1,function(d){var e=d?b:c;if("auto"==e)return"auto";var f=a.mergeDimensions(e,e);return f[2](f[0])}]:a.mergeDimensions(b,c)}function e(a){return"rect("+a+")"}var f=a.mergeWrappedNestedRepeated.bind(null,e,d,", ");a.parseBox=c,a.mergeBoxes=f,a.addPropertiesHandler(c,f,["clip"])}(d,f),function(a){function b(a){return function(b){var c=0;return a.map(function(a){return a===j?b[c++]:a})}}function c(a){return a
+}function d(b){if(b=b.toLowerCase().trim(),"none"==b)return[];for(var c,d=/\s*(\w+)\(([^)]*)\)/g,e=[],f=0;c=d.exec(b);){if(c.index!=f)return;f=c.index+c[0].length;var g=c[1],h=m[g];if(!h)return;var i=c[2].split(","),j=h[0];if(j.length<i.length)return;for(var n=[],o=0;o<j.length;o++){var p,q=i[o],r=j[o];if(p=q?{A:function(b){return"0"==b.trim()?l:a.parseAngle(b)},N:a.parseNumber,T:a.parseLengthOrPercent,L:a.parseLength}[r.toUpperCase()](q):{a:l,n:n[0],t:k}[r],void 0===p)return;n.push(p)}if(e.push({t:g,d:n}),d.lastIndex==b.length)return e}}function e(a){return a.toFixed(6).replace(".000000","")}function f(b,c){if(b.decompositionPair!==c){b.decompositionPair=c;var d=a.makeMatrixDecomposition(b)}if(c.decompositionPair!==b){c.decompositionPair=b;var f=a.makeMatrixDecomposition(c)}return null==d[0]||null==f[0]?[[!1],[!0],function(a){return a?c[0].d:b[0].d}]:(d[0].push(0),f[0].push(1),[d,f,function(b){var c=a.quat(d[0][3],f[0][3],b[5]),g=a.composeMatrix(b[0],b[1],b[2],c,b[4]),h=g.map(e).join(",");return h}])}function g(a){return a.replace(/[xy]/,"")}function h(a){return a.replace(/(x|y|z|3d)?$/,"3d")}function i(b,c){var d=a.makeMatrixDecomposition&&!0,e=!1;if(!b.length||!c.length){b.length||(e=!0,b=c,c=[]);for(var i=0;i<b.length;i++){var j=b[i].t,k=b[i].d,l="scale"==j.substr(0,5)?1:0;c.push({t:j,d:k.map(function(a){if("number"==typeof a)return l;var b={};for(var c in a)b[c]=l;return b})})}}var n=function(a,b){return"perspective"==a&&"perspective"==b||("matrix"==a||"matrix3d"==a)&&("matrix"==b||"matrix3d"==b)},o=[],p=[],q=[];if(b.length!=c.length){if(!d)return;var r=f(b,c);o=[r[0]],p=[r[1]],q=[["matrix",[r[2]]]]}else for(var i=0;i<b.length;i++){var j,s=b[i].t,t=c[i].t,u=b[i].d,v=c[i].d,w=m[s],x=m[t];if(n(s,t)){if(!d)return;var r=f([b[i]],[c[i]]);o.push(r[0]),p.push(r[1]),q.push(["matrix",[r[2]]])}else{if(s==t)j=s;else if(w[2]&&x[2]&&g(s)==g(t))j=g(s),u=w[2](u),v=x[2](v);else{if(!w[1]||!x[1]||h(s)!=h(t)){if(!d)return;var r=f(b,c);o=[r[0]],p=[r[1]],q=[["matrix",[r[2]]]];break}j=h(s),u=w[1](u),v=x[1](v)}for(var y=[],z=[],A=[],B=0;B<u.length;B++){var C="number"==typeof u[B]?a.mergeNumbers:a.mergeDimensions,r=C(u[B],v[B]);y[B]=r[0],z[B]=r[1],A.push(r[2])}o.push(y),p.push(z),q.push([j,A])}}if(e){var D=o;o=p,p=D}return[o,p,function(a){return a.map(function(a,b){var c=a.map(function(a,c){return q[b][1][c](a)}).join(",");return"matrix"==q[b][0]&&16==c.split(",").length&&(q[b][0]="matrix3d"),q[b][0]+"("+c+")"}).join(" ")}]}var j=null,k={px:0},l={deg:0},m={matrix:["NNNNNN",[j,j,0,0,j,j,0,0,0,0,1,0,j,j,0,1],c],matrix3d:["NNNNNNNNNNNNNNNN",c],rotate:["A"],rotatex:["A"],rotatey:["A"],rotatez:["A"],rotate3d:["NNNA"],perspective:["L"],scale:["Nn",b([j,j,1]),c],scalex:["N",b([j,1,1]),b([j,1])],scaley:["N",b([1,j,1]),b([1,j])],scalez:["N",b([1,1,j])],scale3d:["NNN",c],skew:["Aa",null,c],skewx:["A",null,b([j,l])],skewy:["A",null,b([l,j])],translate:["Tt",b([j,j,k]),c],translatex:["T",b([j,k,k]),b([j,k])],translatey:["T",b([k,j,k]),b([k,j])],translatez:["L",b([k,k,j])],translate3d:["TTL",c]};a.addPropertiesHandler(d,i,["transform"])}(d,f),function(a){function b(a){var b=Number(a);return isNaN(b)||100>b||b>900||b%100!==0?void 0:b}function c(b){return b=100*Math.round(b/100),b=a.clamp(100,900,b),400===b?"normal":700===b?"bold":String(b)}function d(a,b){return[a,b,c]}a.addPropertiesHandler(b,d,["font-weight"])}(d),function(a){function b(a){var b={};for(var c in a)b[c]=-a[c];return b}function c(b){return a.consumeToken(/^(left|center|right|top|bottom)\b/i,b)||a.consumeLengthOrPercent(b)}function d(b,d){var e=a.consumeRepeated(c,/^/,d);if(e&&""==e[1]){var f=e[0];if(f[0]=f[0]||"center",f[1]=f[1]||"center",3==b&&(f[2]=f[2]||{px:0}),f.length==b){if(/top|bottom/.test(f[0])||/left|right/.test(f[1])){var h=f[0];f[0]=f[1],f[1]=h}if(/left|right|center|Object/.test(f[0])&&/top|bottom|center|Object/.test(f[1]))return f.map(function(a){return"object"==typeof a?a:g[a]})}}}function e(d){var e=a.consumeRepeated(c,/^/,d);if(e){for(var f=e[0],h=[{"%":50},{"%":50}],i=0,j=!1,k=0;k<f.length;k++){var l=f[k];"string"==typeof l?(j=/bottom|right/.test(l),i={left:0,right:0,center:i,top:1,bottom:1}[l],h[i]=g[l],"center"==l&&i++):(j&&(l=b(l),l["%"]=(l["%"]||0)+100),h[i]=l,i++,j=!1)}return[h,e[1]]}}function f(b){var c=a.consumeRepeated(e,/^,/,b);return c&&""==c[1]?c[0]:void 0}var g={left:{"%":0},center:{"%":50},right:{"%":100},top:{"%":0},bottom:{"%":100}},h=a.mergeNestedRepeated.bind(null,a.mergeDimensions," ");a.addPropertiesHandler(d.bind(null,3),h,["transform-origin"]),a.addPropertiesHandler(d.bind(null,2),h,["perspective-origin"]),a.consumePosition=e,a.mergeOffsetList=h;var i=a.mergeNestedRepeated.bind(null,h,", ");a.addPropertiesHandler(f,i,["background-position","object-position"])}(d),function(a){function b(b){var c=a.consumeToken(/^circle/,b);if(c&&c[0])return["circle"].concat(a.consumeList([a.ignore(a.consumeToken.bind(void 0,/^\(/)),d,a.ignore(a.consumeToken.bind(void 0,/^at/)),a.consumePosition,a.ignore(a.consumeToken.bind(void 0,/^\)/))],c[1]));var f=a.consumeToken(/^ellipse/,b);if(f&&f[0])return["ellipse"].concat(a.consumeList([a.ignore(a.consumeToken.bind(void 0,/^\(/)),e,a.ignore(a.consumeToken.bind(void 0,/^at/)),a.consumePosition,a.ignore(a.consumeToken.bind(void 0,/^\)/))],f[1]));var g=a.consumeToken(/^polygon/,b);return g&&g[0]?["polygon"].concat(a.consumeList([a.ignore(a.consumeToken.bind(void 0,/^\(/)),a.optional(a.consumeToken.bind(void 0,/^nonzero\s*,|^evenodd\s*,/),"nonzero,"),a.consumeSizePairList,a.ignore(a.consumeToken.bind(void 0,/^\)/))],g[1])):void 0}function c(b,c){return b[0]===c[0]?"circle"==b[0]?a.mergeList(b.slice(1),c.slice(1),["circle(",a.mergeDimensions," at ",a.mergeOffsetList,")"]):"ellipse"==b[0]?a.mergeList(b.slice(1),c.slice(1),["ellipse(",a.mergeNonNegativeSizePair," at ",a.mergeOffsetList,")"]):"polygon"==b[0]&&b[1]==c[1]?a.mergeList(b.slice(2),c.slice(2),["polygon(",b[1],g,")"]):void 0:void 0}var d=a.consumeParenthesised.bind(null,a.parseLengthOrPercent),e=a.consumeRepeated.bind(void 0,d,/^/),f=a.mergeNestedRepeated.bind(void 0,a.mergeDimensions," "),g=a.mergeNestedRepeated.bind(void 0,f,",");a.addPropertiesHandler(b,c,["shape-outside"])}(d),function(a){function b(a,b){b.concat([a]).forEach(function(b){b in document.documentElement.style&&(c[a]=b)})}var c={};b("transform",["webkitTransform","msTransform"]),b("transformOrigin",["webkitTransformOrigin"]),b("perspective",["webkitPerspective"]),b("perspectiveOrigin",["webkitPerspectiveOrigin"]),a.propertyName=function(a){return c[a]||a}}(d,f)}()}({},function(){return this}());
+//# sourceMappingURL=web-animations.min.js.map
\ No newline at end of file
diff --git a/third_party/web-animations-js/sources/web-animations.min.js.map b/third_party/web-animations-js/sources/web-animations.min.js.map
new file mode 100644
index 0000000..cc8cada
--- /dev/null
+++ b/third_party/web-animations-js/sources/web-animations.min.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"web-animations.min.js","sources":["src/scope.js","src/timing-utilities.js","src/normalize-keyframes.js","src/deprecation.js","src/animation-node.js","src/effect.js","src/property-interpolation.js","src/animation.js","src/apply-preserving-inline-style.js","src/element-animatable.js","src/interpolation.js","src/matrix-interpolation.js","src/player.js","src/tick.js"],"names":["webAnimationsShared","webAnimations1","webAnimationsNext","webAnimationsTesting","testing","makeTiming","timingInput","forGroup","timing","delay","endDelay","fill","iterationStart","iterations","duration","playbackRate","direction","easing","isNaN","undefined","Object","getOwnPropertyNames","forEach","property","fills","indexOf","directions","shared","isDeprecated","normalizeTimingInput","toTimingFunction","cubic","d","a","c","linear","x","f","b","m","start","end","mid","xEst","Math","abs","step","count","pos","stepSize","cubicData","cubicBezierRe","exec","apply","this","slice","Number","stepData","Start","middle","Middle","End","preset","presets","calculateActiveDuration","repeatedDuration","calculatePhase","activeDuration","localTime","PhaseNone","PhaseBefore","PhaseAfter","PhaseActive","calculateActiveTime","fillMode","phase","calculateScaledActiveTime","activeTime","startOffset","calculateIterationTime","iterationDuration","scaledActiveTime","Infinity","calculateCurrentIteration","iterationTime","floor","calculateTransformedTime","currentIteration","currentIterationIsOdd","currentDirectionIsForwards","directedTime","timeFraction","calculateTimeFraction","split","ease","ease-in","ease-out","ease-in-out","step-start","step-middle","step-end","numberString","RegExp","antiAlias","value","aliases","expandShorthandAndAntiAlias","longProperties","shorthandToLonghand","shorthandExpanderElem","style","i","longProperty","result","longhandValue","normalizeKeyframes","effectInput","spaceKeyframes","length","keyframeEffect","offset","previousIndex","previousOffset","j","Array","isArray","TypeError","originalKeyframe","keyframe","member","memberValue","isFinite","type","DOMException","NOT_SUPPORTED_ERR","name","message","everyFrameHasOffset","INVALID_MODIFICATION_ERR","background","border","borderBottom","borderColor","borderLeft","borderRadius","borderRight","borderTop","flex","margin","outline","document","createElementNS","borderWidthAliases","thin","medium","thick","borderBottomWidth","borderLeftWidth","borderRightWidth","borderTopWidth","xx-small","x-small","small","large","x-large","xx-large","normal","bold","outlineWidth","textShadow","boxShadow","silenced","feature","date","plural","Date","expiry","getMonth","console","warn","auxVerb","toDateString","deprecated","advice","Error","scope","AnimationNode","animationNode","_totalDuration","_isCurrent","makePropertySpecificKeyframeGroups","propertySpecificKeyframeGroups","propertySpecificKeyframe","groupName","group","makeInterpolations","interpolations","startTime","endTime","startValue","endValue","push","interpolation","propertyInterpolation","leftInterpolation","rightInterpolation","convertEffectInput","fraction","filter","offsetFraction","localDuration","scaledLocalTime","target","addPropertyHandler","parser","merger","propertyHandlers","addPropertiesHandler","properties","toUpperCase","left","right","handlers","parsedLeft","parsedRight","interpolationArgs","interp","Interpolation","Animation","animation","_update","_clear","effect","_hasSameTarget","otherTarget","NullAnimation","clear","nullAnimation","configureProperty","descriptor","enumerable","configurable","defineProperty","object","AnimatedCSSStyleDeclaration","_surrogateStyle","_style","element","_length","_isAnimatedProperty","_updateIndices","ensureStyleIsPatched","_webAnimationsPatchedStyle","animatedStyle","get","_","styleAttributes","cssText","parentRule","styleMethods","getPropertyCSSValue","getPropertyPriority","getPropertyValue","item","styleMutatingMethods","removeProperty","setProperty","prototype",{"end":{"file":"src/apply-preserving-inline-style.js","comments_before":[],"nlb":false,"endpos":2110,"pos":2103,"col":8,"line":64,"value":"cssText","type":"name"},"start":{"file":"src/apply-preserving-inline-style.js","comments_before":[],"nlb":false,"endpos":2110,"pos":2103,"col":8,"line":64,"value":"cssText","type":"name"},"name":"cssText"},"text","isAffectedProperty","_set","method","modifiesStyle","arguments","documentElement","set","propertyName","window","timeline","interpolate","from","to","r","convertToString","clamp","min","max","quat","toQ","dot","fromQ","product","theta","w","sqrt","cos","composeMatrix","multiply","k","is2D","translate","skew","perspective","matrix","z","rotMatrix","y","temp","scale","concat","sequenceNumber","AnimationPlayerEvent","currentTime","timelineTime","bubbles","cancelable","currentTarget","defaultPrevented","eventPhase","Event","AT_TARGET","timeStamp","now","Player","source","_currentTime","_startTime","paused","_playbackRate","_inTimeline","_finishedFlag","onfinish","_finishHandlers","_source","_inEffect","_idle","_currentTimePending","_ensureAlive","_players","newTime","ignoreLimit","finished","restart","_timeline","_tickCurrentTime","invalidateEffects","playState","pause","finish","cancel","reverse","play","addEventListener","handler","removeEventListener","index","splice","_fireEvents","baseTime","event","setTimeout","call","_tick","processRafCallbacks","processing","rafCallbacks","entry","t","tick","applyPendingEffects","comparePlayers","leftPlayer","rightPlayer","_sequenceNumber","InternalTimeline","performance","pendingEffects","hasRestartedThisFrame","newPendingClears","newPendingEffects","updatingPlayers","player"],"mappings":";;;;;;;;;;;;;;o6pBAcA,QAAIA,GACAC,GACAC,OAAAA,EAGF,EAAIC,KCLN,GAAA,GAAkBC,WAKPC,QAAAA,GAAWC,GAAaC,MAAAA,GAC/B,GAAIC,GAAAA,EAAAA,GAAAA,GACFC,EAAAA,GAAO,GACPC,EAAAA,GAAAA,GAAU,EACVC,GAAAA,GAAMJ,EAAAA,GAAAA,GAAW,EAAA,GAAA,GAAS,EAAA,GAC1BK,GAAAA,EAAAA,GAAAA,GAAAA,EAAAA,GAAgB,GAChBC,EAAAA,GAAAA,GAAAA,EAAY,GACZC,GAAAA,EAAAA,GAAUP,GAAAA,EAAAA,GAAW,GAAA,EAAA,GAAS,GAC9BQ,EAAAA,GAAAA,GAAAA,EAAAA,GACAC,GAAAA,EAAAA,GAAAA,GAAW,QACXC,GAAQ,GAAA,IAyBV,GAAA,GAvB0B,EAAA,EAAfX,GAAAA,EAAAA,EAAAA,GAAAA,GAAAA,EAA4BY,EAAAA,GAAMZ,GAAAA,EAAAA,EAAAA,GAAAA,GAElBa,EAAAA,EAAAA,GAAAA,GAAhBb,EAAAA,EAAAA,GAAAA,GAAAA,EACTc,EAAAA,GAAAA,GAAOC,EAAAA,EAAAA,GAAAA,GAAAA,EAAAA,EAAAA,GAAAA,GAAoBf,EAAAA,EAAAA,GAAAA,GAAAA,KAAagB,EAAQ,EAAA,EAAA,GAAA,GAASC,EAAAA,EAAAA,EACvD,GAA6B,GAAA,EAAzBjB,EAAAA,EAAAA,GAAAA,EAAYiB,KAAAA,EAAAA,EAAAA,EACd,GAAA,GAA+B,EAAA,EAAA,EAAA,GAAA,GAApBf,EAAAA,EAAOe,EAAAA,GAAAA,EAAAA,KAAqC,EAAA,EAAA,EAAA,GAAZA,GAAAA,EAAAA,EAAAA,EACL,GAAA,GAAA,EAAA,EAAA,EAAzBjB,GAAAA,EAAAA,IAAAA,KAAYiB,EAAyBL,EAAAA,EAAAA,EAAMZ,IAAAA,CAAAA,IAAAA,GAAYiB,GAAAA,EAAAA,EAAAA,EAChE,EAAA,EAAA,IAGJ,GAAiB,EAAA,GAAZA,GAAAA,EAAAA,GAAgE,EAAxCC,GAAMC,KAAAA,GAAQnB,MAAAA,GAAYiB,KACrD,GAAA,EAEF,KAAiB,GAAZA,EAAAA,QAA6BG,GAAWD,GAAQnB,QAAAA,EAAAA,GAAYiB,GAAAA,EAAAA,GAAAA,GAC/D,EAAA,GAEF,GAAgB,EAAA,GAAA,KAAA,EAAA,GAAA,GAAZA,EAAAA,GAAAA,GAA8BI,EAAAA,GAAAA,GAAOC,EAAAA,GAAAA,KAAAA,EAAa,GAAA,GAAA,EAAA,GAAA,GAAA,EAAA,GAAA,GAAA,EAAA,GAAA,KAAgC,EAAA,GAAA,GAAA,EAAA,GAAc,GAAA,EAAA,GAAA,GAAA,EAAA,GAAA,KAAA,QAAA,GAClG,EAAA,GAAA,IAEFpB,GAAAA,MAAOe,EAAYjB,EAAAA,EAAAA,EAAAA,IAAAA,CAAYiB,IAAAA,GAAAA,GAAAA,EAlBnCf,EAAAA,EAAAA,EAAOM,EAAAA,IAAAA,GAAWR,EAAAA,GAAAA,EAAAA,GAsBbE,EAAAA,GAGT,KAASqB,GAAAA,MAAAA,GAAAA,QAAqBvB,GAAaC,GAAAA,GACzC,GAAIC,EAASH,EAAAA,QAAWC,EAAAA,GAAAA,EAAAA,EAAAA,GAAaC,EAAAA,EAAAA,GAErC,GADAC,QAAOS,GAASa,GAAAA,MAAAA,MAAAA,KAAiBtB,EAAAA,GAAOS,EAAAA,GAAAA,EACjCT,GAAAA,EAAAA,GAGT,EAAA,GAAA,EAASuB,IAAAA,QAAeC,GACd,EAAJC,EAASA,EAAI,GAAKC,OACbC,EAAAA,EAEF,GAAA,EAASC,EAIZ,GAAA,EAAA,EAASC,GAAKC,EAAGC,EAAK,GAAA,EAAWN,EAAK,GAAIM,EAAM,EAAIA,IAAS,QAAkBA,GAAYA,EAAIA,GAFjG,OADIC,EAAAA,GAAWC,EAAAA,GAAM,EAEnB,GAAIC,EAAAA,GAAOF,EAAAA,GAAQC,EAAAA,GAAO,EAEtBE,GAAON,EAAEJ,GAAMS,EAAAA,GACnB,EAAIE,GAAAA,EAAKC,GAAIT,EAAIO,IAAQ,QACvB,GAEEA,GACFH,GAAQE,IAERD,EAAMC,MAUd,EAAA,GAASI,EAAKC,MAAOC,EACnB,GAAA,EAAO,MAASZ,EAAAA,IACVA,EACF,MAEF,GAAA,IAAIa,IAAAA,IAAW,EAAIF,GAEnB,GAAA,MADKC,KAAMC,KAAAA,GACJb,MAmBX,EAASN,EAAAA,EAAAA,EAAAA,IAAAA,EACHoB,KAAAA,EAAAA,GAAYC,QAAAA,KAAcC,GAAAA,GAAKnC,EAAAA,EAAAA,EACnC,IAAIiC,EACKnB,GAAAA,GAAMsB,CAAAA,IAAMC,IAAAA,EAAgBC,GAAaC,OAE9CC,CAAAA,IAAAA,GAAkBL,IAAKnC,IAC3B,EAAIwC,GAAAA,IAAAA,EACF,GAAOX,IAAAA,EAAKU,GAAAA,GAAOC,CAAAA,EAAAA,KAAS,EAAMjB,GAAAA,IAASkB,EAAOC,KAAAA,EAAUC,GAAAA,IAAQnB,EAAOoB,KAAKJ,EAAAA,GAAS,IAE3F,EAAIK,KAAAA,EAASC,GAAAA,GAAQ9C,IAAAA,GAIdkB,EAGT,GAAS6B,EACKnB,EAAIoB,EAAkClD,GAGpD,EAASkD,EAAAA,OACyBpD,IAQlC,EAAA,EAASqD,EAAAA,EAAAA,IAAAA,GAAeC,EAAAA,GAAAA,MAAgBC,EAAAA,GAAAA,IAAW5D,GAAAA,KACjD,EAAiB,GAAA,MAAb4D,EAAAA,GACKC,IAAAA,KAELD,GAAAA,KAAY5D,EAAOC,EACd6D,KAAAA,EAAAA,GAELF,EAAa5D,EAAAA,GAAOC,IAAQ0D,KAAAA,GAAAA,KACvBI,EAAAA,GAAAA,MAEFC,EAAAA,IAAAA,EAGT,KAASC,EAAAA,EAAAA,GAAAA,EAAAA,KAAAA,EAAoBN,GAAAA,EAAAA,EAAgBO,GAAAA,EAAUN,GAAAA,GAAAA,EAAWO,IAAOlE,EAAAA,KACvE,EAAQkE,EACN,KAAKL,EAAAA,GAAAA,EACa,EAAA,IAAA,EAAZI,IAAAA,EAAuC,GAAA,EAAA,KAAZA,EAAAA,GAExB,MACT,EAAA,IAAKF,EAAAA,KACH,EAAA,EAAOJ,GAAAA,EAAAA,KAAY3D,EACrB,GAAK8D,EAAAA,EACH,GAAA,EAAgB,GAAA,GAAA,EAAZG,IAAAA,EAAAA,KAAsC,EAAZA,EAAAA,GAAAA,EACrBP,KAAAA,EAAAA,GAAAA,EAEX,EAAKE,GAAAA,EAAAA,GACH,GAAA,EAAO,IAIb,EAAA,KAASO,EAAAA,EAAAA,KAAAA,EAAAA,GAAAA,EAA0BT,EAAAA,IAAgBU,EAAAA,IAAYC,EAAAA,GAAAA,EAAatE,IAAAA,EAC1E,EAAQA,IAAAA,GAAOO,EAAAA,EAAe,GAAI8D,EAAAA,GAAAA,IAAaV,EAAAA,EAAAA,GAAAA,GAAiBU,EAAAA,IAAAA,GAAcrE,GAAAA,EAAAA,EAAOO,EAAAA,IAAAA,EAAe+D,IAAAA,GAAAA,EAAAA,GAGtG,IAAA,GAASC,EAAAA,GAAAA,IAAAA,GAAAA,EAAAA,GAAAA,IAAuBC,EAAAA,IAAAA,GAAAA,EAAmBf,EAAAA,EAAAA,GAAAA,GAAAA,EAAkBgB,GAAAA,GAAAA,EAAAA,GAAAA,GAAkBH,CAAAA,OAAAA,GAAatE,MAAAA,EAClG,GAAA,KAAyB0E,KAArBD,GAAAA,IAAAA,EAAiCA,GAAAA,GAAAA,EAAAA,GAAAA,IAAsBC,GAAAA,EAAaD,GAAAA,GAAAA,EAAAA,GAAAA,IAAmBH,GAAAA,EAAAA,GAAAA,GAAeb,EAAAA,GAAAA,IAAAA,EAAAA,IAAoBzD,IAAAA,EAAOK,GAAAA,GAAAA,EAAAA,GAAgBL,IAAAA,EAAOK,GAAAA,GAAAA,EAAaL,GAAAA,IAAOI,EAAAA,EAAAA,KAAAA,KAAkB,EAAA,EACzLoE,GAAAA,GAAAA,EAAAA,GAAAA,GAAAA,EAGFC,GAAAA,IAAAA,GAAmBD,IAAAA,GAAAA,EAAAA,GAG5B,GAAA,EAASG,GAAAA,IAAAA,GAAAA,EAAAA,GAAAA,GAAAA,EAAAA,GAA0BH,IAAAA,GAAAA,EAAAA,GAAAA,GAAAA,EAAmBI,GAAAA,IAAAA,IAAeH,EAAAA,GAAAA,GAAAA,EAAAA,GAAkBzE,IAAAA,EAAAA,EACrF,KAAA,KAAyB,EAArByE,EAAAA,GAAAA,GAAAA,EAAAA,GACK,GAELG,EAAAA,GAAAA,IAAAA,IAAiBJ,EAAAA,GACZxE,GAAAA,EAAOI,GAAAA,IAAAA,EAAAA,IAAAA,GAAiBJ,EAAAA,GAAOK,GAAAA,EAAAA,GAAa,IAE9C+B,GAAKyC,EAAAA,GAAMJ,GAAAA,EAAAA,GAAAA,IAAAA,KAAmBD,EAAAA,EAAAA,KAAAA,KAAAA,EAGvC,EAAA,GAAA,GAASM,EAAAA,GAAAA,GAAAA,EAAAA,GAAAA,IAAAA,IAAyBC,EAAAA,GAAkBP,GAAAA,EAAAA,GAAAA,IAAAA,GAAAA,EAAmBI,GAAAA,GAAAA,EAAAA,GAAe5E,IAAAA,EACpF,IAAIgF,GAAAA,EAAAA,GAAAA,GAAAA,EAAAA,GAAAA,IAAwBD,KAAAA,EAAmB,EAC3CE,EAAAA,EAAAA,GAAiD,MAApBjF,KAAgCA,GAAOQ,IAAAA,EAAAA,EAAcwE,wBAAwB,GAAsB,EAChIE,GAAeD,SAA6BL,GAAAA,QAAgBJ,GAAAA,EAAoBI,GAAAA,GAChFO,GAAAA,EAAeD,KAAAA,EAAeV,OAAAA,IAClC,EAAOA,EAAAA,WAAAA,EAAoBxE,GAAOS,cAAO0E,EAG3C,IAAA,EAASC,EAAAA,OAAAA,EAAsBzB,UAAAA,OAAgBC,QAAW5D,GAC5C0D,EAAAA,GAAeC,EAAAA,EAAgBC,QAAW5D,OAAAA,GAClDqE,IAAAA,GAAaJ,EAAAA,EAAoBN,OAAAA,IAAgB3D,EAAOG,GAAAA,EAAMyD,GAAAA,QAAkB5D,OAAOC,KAAAA,OACxE,QAAfoE,GACK,EACc,EAAnBV,GACF,EAAOQ,EAAUL,KACnB,KAAIQ,EAAAA,KAActE,GAAOI,QAAAA,CAAAA,GAAiBJ,GAAOM,EAC7CmE,EAAAA,KAAAA,EAAmBL,OAAAA,EAAAA,EAAAA,IAAAA,EAA0BT,KAAAA,EAAgBU,IAAAA,EAAYC,EAAAA,GAAAA,EAAatE,EACtF4E,EAAgBL,IAAAA,GAAuBvE,IAAAA,EAAOM,GAAAA,OAAUmD,EAAAA,EAAiBzD,GAASyE,EAAAA,IAAAA,QAAkBH,GACpGS,EAAAA,GAAmBJ,IAAAA,GAAAA,GAAAA,EAAAA,EAAAA,EAAAA,EAA0B3E,EAAOM,UAAUsE,OAAAA,KAAeH,EAAAA,KAAAA,GAAAA,GACjF,IAAA,GAAOK,KAAAA,EAAAA,GAAAA,QAAyBC,IAAAA,KAAAA,EAAkB/E,KAAOM,IAAUsE,GAAAA,GAAAA,IAAe5E,GAAAA,GAAiBM,KAAAA,IAvMrG,GAAIU,EAAQ,EAAA,OAAA,EAAA,GAAA,OAAgC,SACxCE,EAAAA,QAAa,EAAA,EAAA,OAAA,IAAA,QAAsCmE,GAAM,EAAA,GAoEzDnC,IAAAA,GACAE,GAAAA,EAAAA,EAAS,EAAA,GACTC,GAAM,EAaNE,EAAAA,GAAAA,EAAAA,GACF+B,CAAAA,OAAc,GAAA,EAAM,GAAK,EAAA,GAAM,QAC/BC,GAAWhE,GAAY,MACvBiE,UAAAA,GAAkB,GAAM,GACxBC,EAAAA,EAAAA,OAAqB,KAAS,EAC9BC,GAAAA,QAAcpD,GAAQY,QACtByC,GAAAA,EAAoB,GACpBC,MAAYtD,UAAQe,GAGlBwC,GAAAA,GAAe,EAAA,EAAA,OAAA,GAAA,GAAA,EACflD,IAAoBmD,QAAO,GAAA,EAAoBD,GAAAA,IAAe,GAAMA,MAAAA,EAAe,EAAA,EAAMA,EAAAA,OAAAA,IAAe,CAAMA,GAAAA,GAAAA,EAAe,eACpH,EAAA,GAAA,EAAA,KAAA,GAAA,IAAA,EAAA,GAAA,MACTlE,UAAS,EAAc,IAAOC,EA0B9BiC,KAAAA,EACAC,IAAAA,EAAc,EACdC,GAAAA,MACAC,IAAAA,EAAc,EA4ElB7C,OAAOtB,QAAaA,GACbwB,EAAAA,EAAuBA,EAAAA,EAC9BF,GAAOqC,IAAAA,GAAAA,MAA0BA,KAAAA,KAC1B4B,EAAAA,EAAAA,EAAwBA,OAAAA,EAAAA,QAC/BjE,EAAAA,EAAAA,EAAOuC,EAAAA,IAAAA,CAAAA,GAAiBA,GAAAA,EACxBvC,EAAOG,EAAAA,EAAAA,QAAAA,EAAmBA,EAAAA,EAAAA,QAkBzB9B,KAAAA,EAAAA,MAAqBG,GAAAA,KAAAA,ECnOxB,IAAA,EAAkBC,KAmIhB,EAAA,IAASmG,EAAUhF,KAAUiF,EAC3B,IAAIjF,OAAAA,EACKkF,EAAQlF,SAAUiF,GAEpBA,GAIT,GAAA,EAASE,IAAAA,SAAAA,EAA4BnF,GAAAA,MAAUiF,GACzCG,GAAAA,KAAiBC,KAAAA,EAAAA,OAAoBrF,GACzC,EAAIoF,GAAAA,IACFE,QAAAA,GAAAA,EAAsBC,EAAMvF,GAAYiF,IAAAA,GACxC,MAASO,KAAKJ,KACRK,EAAAA,EAAAA,EAAAA,EAAAA,EAAAA,EAAeL,OAAAA,IAAAA,GAAeI,kBACdF,GAAAA,GAAAA,CAAAA,GAAAA,GAAsBC,EAAME,GAAAA,EAAAA,GAAAA,EAChDC,KAAOD,GAAAA,KAAgBT,EAAAA,IAAUS,EAAAA,KAAcE,EAAAA,IAAAA,EAGjDD,KAAAA,EAAO1F,SAAYgF,SAAUhF,GAAAA,EAAUiF,MAI3C,GAAA,EAASW,MAAAA,GAAAA,EAAmBC,KAAAA,WA4DjBC,MAAAA,GAAAA,MACP,EAAIC,QAASC,EAAAA,EAAAA,SAC4B,GAArCA,IAAAA,GAAAA,GAAeD,GAAS,EAAA,EAAGE,EAAAA,EAC7BD,OAAAA,IAAAA,GAAeD,EAAYE,GAAS,EACzB,GAAiC,OAA5BD,KAAAA,EAAe,aAC/BA,EAAe,EAAGC,eAEhBC,EACAC,EAAAA,gBAAiBH,EAAkBC,EACnBF,qBACdE,EAAwBT,EAAGS,OACjB,EAAVA,EACF,SAAa,EAAWC,EAAAA,YACtBF,EAAeE,EAAAA,oBAA4BC,EAA2BA,KAAAA,KAAkBC,MAASF,EAAAA,2BAEnGC,EA1EDE,EAAMC,UAAQT,GAAgC,GAAhBA,SAC3B,GAAIU,QAAU,GAAA,GAAA,QAAA,GAAA,GAAA,GAEtB,GAAmB,EAAfV,aACF,UAEEG,EAAAA,IAAAA,EAAiBH,MAAAA,GAAgB,OAAA,EAASW,CAAAA,IAAAA,GAC5C,EAAIC,uBACKC,EAAUF,IAAAA,EAAAA,MACjB,GAAIG,QAAcH,KAAAA,EAAAA,IAAiBE,CACnC,IAAc,GAAA,EAAVA,aACiB,EAAfC,OACFA,IAAAA,EAAc1E,MAAO0E,EAAAA,GAAAA,GAChBC,OAASD,GAAAA,IACZ,OAAM,EAAIJ,WAAU,MAAA,MAAA,EAAA,EAAA,gBAEnB,EAAc,IAAVG,EACT,OACEG,IAAMC,EAAaC,GAAAA,QAAAA,EACnBC,EAAM,IAAA,OAAA,QACNC,GAAS,GAAA,GAAA,GAAA,EAGXN,gBADmB,EACLvG,KAAAA,EAAOG,OAAAA,IAAiBoG,IAAAA,EAExB,GAAKA,EAAAA,GAErBxB,OAAAA,QAAAA,GAA4BuB,EAAAA,GAAQC,KAAAA,EAAaF,QAMnD,OAJuB7G,KAAAA,IAAnB6G,EAAAA,QAASR,OACXQ,EAASR,QAAS,SACGrG,EAAnB6G,QAAS/G,MAAAA,GACX+G,GAAAA,MAAS/G,EAASU,QAAOG,OAAAA,KAAAA,IAAiB,EAAA,QACrCkG,OAAAA,EAGLS,QAAAA,SAAsB,EAEtBf,QAAAA,MAAkBxC,GACb6B,GAAOA,IAAIQ,EAAAA,OAAAA,EAAeD,SACjC,EAAIE,SAASD,EAAAA,MAAeR,CAAGS,IAAAA,GAC/B,GACeE,KACX,MACQW,GAAAA,MAAaK,GAAAA,EAAAA,EAAAA,EACnBH,EAAM,QAAA,OAAA,IAAA,CAAA,GAAA,GACG,EAAA,gBAAA,EAAA,QAAA,GAAA,EAAA,QAAA,GAAA,GAAA,EAAA,GAGbb,GAAAA,KAAiBF,EAEjBiB,IAAAA,EA8BJ,GAAA,KA1BAlB,EAAiBA,IAAAA,EAAsB,KAASS,EACvCA,IAAAA,GAASR,EAAAA,OAAeQ,EAASR,MAAAA,CAAAA,GAAU,GAsB/CiB,EAAAA,YACHpB,EAAAA,MAEKE,EAAAA,MA1OT,GAAIX,GAAAA,EACF+B,GAAAA,EACE,GAAA,EACA,GAAA,EACA,EACA,GAAA,OAAA,EACA,EAAA,SACA,GAAA,IAAA,GACA,GAAA,EAAA,MACA,SAAA,IAAA,EAAA,EAEFC,EAAAA,EACE,OACA,IAAA,GACA,EACA,GAAA,EAAA,GAAA,IACA,GAAA,OAAA,KACA,GACA,EACA,EAAA,KAAA,KACA,QAAA,GAEA,EAAA,EACA,EAAA,GAEFC,QAAAA,GACE,GAAA,OACA,MAAA,EAAA,OACA,EAAA,EAAA,EAAA,GAAA,UAEFC,GAAAA,IAAAA,GAAAA,IACE,GAAA,IAAA,GAAA,KAAA,IACA,GAAA,MACA,KAAA,EAAA,EAAA,EAAA,EACA,QAAA,EAAA,EAEFC,OAAAA,IACE,CAAA,GAAA,GAAA,EAAA,IAAA,EACA,EACA,GAAA,OAAA,EAAA,EAAA,IAEFC,EACE,EAAA,GAAA,MAAA,GACA,KAAA,GAAA,EACA,KAAA,GAAA,MAAA,GAAA,oBACA,EAEFC,EAAAA,EACE,GAAA,GACA,GACA,EAEFC,KACE,KAAA,EACA,KAAA,GAAA,qBACA,EAGA,GACA,aAAA,iBACA,GACA,SAAA,GAEFC,QACE,GACA,GACA,MAAA,GAAA,QAGA,GAAA,QAAA,OACA,IAAA,QACA,GACA,EAAA,EAAA,GAAA,MACA,MAAA,IACA,EAAA,KAAA,IAEFC,EAAAA,IACE,QAAA,GACA,GACA,MAAA,2BAGFC,KACE,GAAA,OACA,GAAA,OACA,QAAA,GAGA,EAAA,GAAA,OACA,EAAA,EACA,GACA,QAAA,GAIAxC,EAAAA,GAAAA,MAAwByC,IAAAA,EAASC,EAAgB,EAAA,EAAA,GAAA,EAAA,GAAA,OAAgC,QAEjFC,GACFC,EAAM,GACNC,OAAQ,EACRC,EAAO,SAGLlD,GACFmD,MAAAA,MAAAA,MAAmBJ,EAAAA,EAAAA,EAAAA,EAAAA,MAAAA,QACnBK,GAAiBL,EAAAA,GACjBM,MAAAA,UAAkBN,EAAAA,GAAAA,OAClBO,EAAAA,EAAAA,SAAgBP,GAAAA,MAAAA,GAEdQ,EAAAA,EAAY,EAAA,OACZC,QAAW,GACXC,EAAS,GACTR,OAAU,EACVS,EAAS,KACTC,OAAAA,EAAW,MACXC,EAAAA,EAAY,qBAGZC,EACAC,EAEFC,EAAchB,EAAAA,IAAAA,qBACdiB,gBACQ,EAAA,qBAERC,EACQ,EAAA,EAAA,IAAA,UA+GHvD,0BAAqBA,EAAAA,qBAM3BnH,EAAqBG,ECpPxB,IAAUwB,EAAAA,IAAAA,SAEJgJ,EAAAA,qBAEG/I,EAAwBgJ,GAASC,YAAcC,gBACtCA,EAAAA,qBACEC,EACZC,GAAkBH,SACtBG,EAAAA,qBAAuBC,EAEXD,GACOL,UACfO,WAAQC,EAAK,qBAAqBP,EAAgBQ,GAAU,YAAA,EAAA,YAAA,EAAiDC,EAAAA,aAAiB,EAEhIV,EAASC,eACF,GAMJU,EAAsBV,GAAuBE,SAC9CnJ,GAAOC,QAAAA,GAAagJ,EAAAA,GAAeW,MAAQT,WAC7C,GAAUU,WAAMZ,GAAU,EAAMQ,EAAAA,SAAU,GAAA,MAAA,IAAA,EAAA,EAAA,GAA2BG,EAAAA,EAIxEvL,YAAAA,OAAAA,EC3BH,qBAEEyL,OAAMC,GAAAA,gBAAyBlL,GAEzB2D,SAAiBxC,GAAOqC,QAAAA,GAAwBxD,GAChDmL,EAAAA,EAAgB,OAASvH,EAC3B,UAAOzC,OAAOiE,EAAAA,UAAAA,CAAsBzB,IAAAA,GAOtC,EALAwH,SAAcC,IAAAA,EAAAA,UAAiBpL,OAAe2D,EAAAA,UAAiB3D,EAAOE,GACxDmL,EAAa,UAASzH,CAAAA,EAC9BO,SAAQhD,EAAAA,EAAOuC,EAAAA,EAAAA,IAAAA,GAAeC,EAAAA,aAA2B3D,EAAAA,EAAAA,EAC7D,GAAA,IAAOmE,GAAUH,UAAAA,EAAAA,EAAeG,EAAAA,EAAAA,IAAUL,GAAAA,EAErCqH,GAAAA,GAAAA,QAGR3L,EAAAA,GAAAA,EAAqBC,EAAAA,GChBxB,EAAA,EAAkBwL,GAAOrL,EAAAA,IA2BvB,QAAS0L,GAAAA,EAAAA,GAAAA,OAAmCvE,EAAAA,EAAAA,SAGrC,GAFDwE,QAAAA,GAAAA,GAAAA,MAAAA,MAAAA,IAEKhF,EAAI,KAAOQ,IAAAA,IAAAA,IAAAA,GAAeD,EAAAA,GAAAA,IAAQP,GACzC,GAAK,EAAIkB,EAAAA,EAAAA,IAAAA,EAAUV,GAAAA,KAAAA,MAAAA,EACjB,EAAc,GAAA,EAAA,IAAVU,OAAAA,GAAgC,GAAA,EAAVA,eAAgC,EAAVA,MAAAA,EAC9C,EAAA,EAAI+D,KAAAA,QAAAA,EAAAA,KAAAA,KAAAA,MACFxE,GAAQD,GAAAA,SAAkBC,gBAClBD,+BACDA,SAAAA,GAAkBU,MAAAA,EAE3B8D,OAAAA,CAAAA,IAAAA,GAAAA,EAA+B9D,WAAU8D,KAAAA,GAAAA,qBAA+B9D,EACxE8D,GAAAA,mBAA+B9D,sBAAa+D,oBAKzCC,qBAAaF,mBAAAA,QAChBG,gBAAQH,0BAA+BE,EAC3C,aAAU,EAAGzE,qBAA2BF,KAAS,KAAGE,GAEhDY,EAAMC,YAAaC,GAAAA,EACb,GACG,SAAA,EAAA,GAAA,QAAA,GAIRyD,EAAAA,GAAAA,GAAAA,EAIT,EAASI,OAAAA,cAAmBJ,KAAAA,GAAAA,KAAAA,OAAAA,IACtBK,EAAAA,OAAAA,GAAAA,EACJ,IAAA,gBAASH,KAAaF,GAAAA,CAAAA,EAAAA,EAAAA,QAEpB,UADIG,IAAQH,IAAAA,KAAAA,GAA+BE,EAAAA,QAC3BlF,EAAUO,SAAYP,GAChCsF,MAAAA,GAAqB7E,GACrB8E,KAAAA,IAAUJ,GAAa1E,KAAAA,GACvB+E,GAAmBxF,KAAGP,EACtBgG,OAAiBzF,IAAI,EACrBsF,EAAAA,QAAaC,oBAEbC,KAAaC,QAAAA,GAEbA,QAAWD,IAAAA,EAGfH,KAAeK,KAAAA,QACbJ,YAAWA,KACXC,QAASA,MACTrL,IAAAA,GAAiBA,UACjBM,cACAmL,YAAAA,cAAqBC,EAAAA,EAAAA,EAAAA,EAAsBV,QAAAA,EAAWM,GAAYC,KAAAA,IAIxEJ,EAAAA,EAAoB,QAAA,EAASQ,GAAAA,MAAmBC,EAAAA,GAAAA,GAAAA,IAAAA,KAAAA,EACvCD,CAAAA,IAAAA,GAAAA,KAAkBP,GAAYQ,CAAAA,GAAAA,GAAAA,KAAmBR,EAAAA,QAEnDD,GAAAA,QApFTX,IAAAA,EAAMqB,KAAAA,IAAAA,QAAqB,GAAA,QAAS1F,EAC9BG,KAAAA,MAAiB5F,KAAAA,SAAOwF,GAAAA,MAAmBC,GAC3C2E,GAAAA,EAAAA,MAAAA,KAAiCD,QAAAA,GAAmCvE,EAAAA,GACpE6E,MAAAA,GAAiBD,EAAAA,GAAmBJ,GAAAA,QAAAA,GACxC,EAAA,EAAO,GAAiBgB,GAAAA,GACtB,IAAIA,KAAAA,IACFX,GAAAA,EAAAA,KAAeY,EAAO,KAAA,IAASN,GAAAA,EAC7B,QAAQK,GAAAA,GAA4C,EAA3BL,KAAAA,EAAcL,OAAAA,GAC/BU,EAAAA,IAAY,SAAKL,GAAAA,MAAcJ,GAAAA,IAC/BS,IAAYL,EAAAA,EAAAA,IAAcL,SAAaU,GAAAA,MAAYL,GAAAA,IAAcJ,KAAAA,EACxEhL,EAAQ,SAASoL,GAAAA,GAAAA,GACdO,EAAAA,IAAAA,SAAiBF,EAAWL,GAAAA,MAAAA,IAAcL,EAAAA,QAC1Ca,IAAgBR,EAAAA,KAAcJ,IAAAA,EAAUI,IAAAA,EAAcL,eACtDc,GAAAA,EAAkBD,KAAAA,KAAAA,MAAyBR,OAAAA,GAAczL,OAAOgM,EAAAA,QAAAA,EAAiBC,IAAAA,IACrFzB,GAAAA,GAAY2B,iDAA8CV,EAAcS,EAAAA,KAG1E,KAAS5L,GAAAA,QAAYwK,EAAAA,MAAAA,EACH,EAAwB,KAAA,KAAZxK,GAAAA,QAAoC,EAAZA,KAAAA,MAClDkK,EAAY2B,EAAQ7L,KA0E7BvB,KAAAA,qBAAqBC,GAAgBE,YAAAA,EC/FxC,EAAA,qBAIWkN,EAAmBC,EAAQC,uBAClCC,EAAiBjM,qBAAYiM,KAAAA,KAAiBjM,GAC9CiM,EAAiBjM,WAAUkL,EAAcc,EAAAA,gBAElCE,CAAAA,IAAqBH,GAAgBI,EAAAA,qBACxBA,KAAAA,KAAWpG,GACzB/F,EAAWmM,EAEfL,gBAAAA,KAAmBC,OAAQC,EAAQhM,KAC/B,EAEF8L,EAAAA,gBAAmBC,KAAQC,OAAQhM,EAAiB,KAAS,GAAA,oBAClDoM,CAOjB,IAAShB,GAAAA,SAAsBpL,GAAUqM,GAAMC,GAE7C,EADuBA,EAAAA,OAAaL,IAAAA,IAAiBjM,EAAAA,GAC5CwF,EAAO+G,GAAAA,QAAgBA,EAC1BC,EAAAA,oBAA4BH,KAC5BI,OAAAA,EACJ,KAAmB7M,EAAa6M,EAAAA,oBAC1BC,KAAAA,OAAoBH,EAA2BE,IAAAA,GACnD,yBACME,EAAeC,EAAc9K,qBAAY4K,EACtC,GAEcJ,oBAM3B,EAAOpC,qBAA2B,EAChC,GAvCA+B,sBAmBJ/B,qBAAMgC,oBAAuBA,qBAuBvBd,mBAAwBA,aAE7B1M,YAAAA,SAAgBE,cAAAA,aC9CTwB,YAAevB,gBAEjBgO,UAAY,EAAA,qBAA8B9N,EAG1CqF,GAFAgG,4BAAoChK,6BAA4BrB,yBACjDwM,0BAAmB1F,SAElCiH,OAAY,iBAEPjB,gBAAQzH,cAGjB0I,eAAUC,aAAmBlK,aAE3B,YADAuB,iBAA6BvB,iBACL,eAE1BiK,gBAAUE,cACRC,cAAe,QAEjBH,eAAUI,cAAiB,MAASC,iBAC3BtB,kBAAWsB,EAEV7C,GAAaF,SAAcE,GACrCwC,QAAUzC,GAA+BA,GAAAA,MAClCyC,GAAAA,uBAGHM,IAAgB,EAASC,aACzBC,QAAgB,GAAA,QACdD,GAEFA,GAAQ,GAaZ,GAVAC,EAAAA,aAAcP,EAAU,OAAA,EACtB,aAEFO,KAAAA,KAAcjD,UAAAA,EAAiB,OAC/BiD,EAAAA,aAA2B,KAAA,KAAA,QAClB,EAETA,gBAAcJ,KAAAA,KAAiB,EAGxBI,MAAAA,EAOR7O,OAAAA,EAAAA,aAAqBC,KAAAA,KAAgBE,SAAAA,EAAAA,OClDxC,IAAUsL,GAAOrL,EAAAA,GAsBf,OAAS0O,EAAAA,GAAAA,OAAAA,QAA0BvN,GAAUwN,EAC3CA,GAAAA,MAAWC,QAAAA,GACXD,QAAAA,IAAWE,GAAAA,EAAAA,SACX7N,GAAO8N,GAAAA,GAAAA,EAAAA,EAAeC,CAAQ5N,IAAAA,QAAUwN,EAG1C,MAAA,MAASK,IAAAA,GAAAA,EAAAA,gBAOP9L,EAAK+L,EAAAA,OAAkB/F,GAAAA,GAASC,EAAAA,MAAAA,EAAgB,gBAAA,EAAA,GAAgC,QAAOzC,GACvFxD,GAAKgM,MAASC,QAAQzI,EACjB0I,IAAAA,GAAU,GACVC,EAAAA,2BAGenM,KAAKgM,KAAAA,EAAOhI,EAC1B/F,KAAW+B,GAAKgM,SACpBhM,EAAK+L,EAAAA,WAAgB9N,EAAiB+N,EAAO/N,qBAE1CmO,EAAAA,GA+FEC,UAAAA,EAAqBJ,GAChBK,SAAAA,GAGZ,QAAIC,GAAAA,GAAoBT,MAAAA,UAAAA,GAA4BG,GAAAA,GAAAA,CACpD,OACET,GAAAA,IAAAA,SAAkBS,GAAS,MAAA,KAAWO,EAAK,EAAA,KAAa,KAAOD,QAAAA,GAAAA,GAC/D,MAAOE;CAGPR,QAAQzI,GAAa,GAASvF,GAAAA,EAAUiF,EACtC+I,cAAchO,OAAYiF,QAE5B+I,EAAQzI,QAAMyH,KAAS,GAAA,GAAShN,EACtBuF,uBAKZyI,KAAQK,EAAAA,EAAAA,EAA6BL,EA7JvC,KAAIS,IAAAA,CAAAA,GAAAA,EACFC,OACA3I,EACA4I,MAGEC,GACFC,EAAAA,MAAAA,EAAAA,GAAqB,MACrBC,IAAAA,GACAC,EAAAA,GAAAA,EACAC,EACgB,EACH,KAGXC,EAAAA,MACFC,IAAAA,GAAAA,EAAgB,GAChBC,MAAAA,KAAa,EA6BftB,EAAAA,EAAAA,IAAAA,EAA4BuB,OAC1BC,EAAIX,OACF,MAAO3M,KAAAA,GAAK+L,MAAgBY,EAAAA,EAAAA,EAAAA,EAE1BA,OAAQY,IAEV,CAAA,GAAK,GADDC,EAAAA,EAAAA,GAAAA,EACS,EAAYzB,EAAAA,IAAAA,EAAgB/H,GAAAA,EAAQP,SAC/C+J,GAAAA,MAAAA,KAAmBxN,EAAAA,OAAK+L,EAAAA,EAAAA,WAE1B/L,IAAK+L,EAAAA,EAAAA,YAAgBY,EAAUY,EAC/BvN,qBACA,EAAA,EAASyD,aAAgBsI,EAAAA,eAAgB/H,IACvCwJ,EAAAA,EAAAA,EAAAA,EAAmBxN,GAAK+L,EAAAA,GAAAA,GAAAA,SAE1B,EAAS9N,MAAAA,GAAYuP,KAAAA,GACdxN,GAAKmM,EAAAA,MAAAA,EAAAA,EAAoBlO,EAC5B+B,IAAYoN,EAAYnP,WAAe8N,EAAAA,OAAgBiB,MAAAA,IAAAA,QAAiB/O,GAK5E,GAAA,MAAY8N,GAAAA,QAAAA,GAAAA,QAAgB/H,UAE1B4I,IAAAA,QACF,GAAYZ,EAAOY,GAAAA,GAGrBR,EAAAA,oBACE,EAAOpM,CAAAA,EAAKkM,kBAAeH,CAAAA,IAAgB/H,GACzClG,EAAO8N,wBAA0BM,GAAAA,GAC/BP,EAAAA,oBACAD,EACAc,CAAAA,EAAK,kBACI,CAAA,IAAA,GAAoBxM,EAAK+L,wBAC/B/L,GAAKkM,MAAAA,OAEVlM,EAAKkM,IAEP,MAAOlM,EAAeA,MAAK+L,KAAAA,GAAAA,SAAgB/H,GAAAA,MACzChE,GAAKkM,EAAAA,GACLpO,EAAAA,EAAO8N,GAAAA,KAAAA,EAAe5L,GAAMA,KAAKkM,GAAAA,EAC/BP,GAAAA,KAAAA,IACAD,EAAAA,EACAxI,SAAOrF,GAIb4P,GAAM,GAAA,EAASxP,KAAUiF,EACvBlD,GAAKgM,GAAAA,EAAO/N,GAAYiF,GAAAA,EACxBlD,IAAKmM,EAAAA,EAAAA,cAAoBlO,EAAAA,GAAY,EAEvCgN,GAAQ,EAAA,GAAA,EAAShN,EAAAA,IACf+B,EAAY/B,EAAY+B,IAAK+L,GAAgB9N,KAAAA,IAAAA,OACtC+B,MAAKmM,QAAoBlO,GAK/B,GAAIyP,MAAUb,GAAAA,QAAAA,OACjBf,IAAAA,QAAAA,GAA4BuB,GAAAA,MAAUK,GAAU,QAAA,eAAkBC,MAAAA,QACzD,GACDhK,EAAAA,GAAcoI,GAAAA,GAAwBhM,EAAMC,0BAAsB4N,EAAAA,GAClED,CAAAA,KAAAA,EAAAA,SACQxB,EAAAA,OAAAA,CAAAA,EAAoByB,SAAU,GACjC5B,EAAAA,EAAO0B,EAAQ3N,KAAWiM,KAAAA,GAAQ4B,GAAAA,EAAAA,EAAAA,EACzC5N,OAAKoM,IAAAA,CAAAA,GAAAA,GAAAA,EAEAzI,GAAAA,EAER+J,EAAAA,EAAQA,GAAAA,EAAAA,EAAUR,SAAAA,EAIvB,OAASjP,EAAAA,GAAAA,EAAAA,CAAY+H,GAAAA,MAAS6H,EAAAA,EAAAA,EAAAA,EAAgBrK,IAAAA,SACxCvF,GAAYyO,GAAAA,gBAAmBzO,GAAAA,MAAY4O,EAG/C,IAAA,KAAU5O,KAAAA,GACRuN,KAAAA,GAAAA,EAAkBM,GAAAA,CAAAA,OAA4BuB,QAAWpP,GAAAA,GAErD,SAAY8N,EAAAA,GAAAA,MAAAA,eAEd+B,GAAK,eACH9N,IAAK+L,UAAAA,GAAgB9N,YAAYiF,KACjClD,UAAKoM,GAAAA,YACKD,IAAAA,KAAoBlO,KAC5B+B,IAAY/B,IAAAA,EAAAA,QAAYiF,EAG7BjF,OAAAA,CAyBLkK,IAAAA,EAAuB8D,MAAShO,IAAAA,GAAUiF,EACxCmJ,EAAAA,EAAqBJ,IACrBA,EAAQzI,IAAMiK,GAAWM,EAAAA,IAAa9P,IAAAA,UAGxCkK,EAAMmD,UAAQ,KAASW,GAAAA,GAAShO,EAAAA,EAAAA,EAAAA,OAC1BgO,IAAQK,CAAAA,GAAAA,GAAAA,EAAAA,EAAAA,GACVL,EAAAA,EAAQzI,EAAMyH,GAAO9C,EAAAA,EAAM4F,EAAAA,GAAa9P,EAAAA,EAO3CtB,EAAAA,GAAAA,EAAAA,EAAgBE,EChLTsL,GACR6F,EAAeX,EAAoB,EAASvJ,IAAa9G,EAChDmL,EAAM8F,GAAe9F,CAAAA,IAAM2C,EAAgBhH,MAAa9G,IAAAA,GAAAA,GAEhEL,ECJH,KAAA,EAAUwL,IAAOrL,GAEf,KAASoR,EAAAA,IAAAA,EAAsBnP,KACT,EAAA,IAAA,EAARoP,MAAmC,UAAA,EAANC,UAChCD,CAAAA,GAAAA,GAET,EAAoB,EAAA,MAA4B,IAAA,EAC9C,IAAA,EAOEA,IAAAA,EAAeC,IAEjB,EADIC,GACgBF,EAAKnK,EAAQP,GACxByK,EAAAA,EAA4BnP,GAErC,GAEF,EAAM,EAAA,GAAA,OAA+C,CAAA,IAAMqP,EAGvDvD,KAAgB,EAAmByD,IAAAA,EAAAA,IAChC,EAASvP,GACPuP,CAAAA,IAAAA,EAAgBJ,MAAkBE,IAAIrP,GAQhDpC,EAAgBE,EAAAA,EAAAA,IClCnB,EAAUsL,IAAAA,GAyFCoG,EAASC,IAAKC,IACrB,UAAYA,EAASD,KAAOC,OAAMD,EAGpC,EAASE,GAAYC,EACnB,EAAoBC,GAAIC,GACxBC,EAAUP,EAEV,GAAA,GACA,IAAgB,GAAZO,MACKD,KAEHE,KAAkBD,EAClBE,EAA0B,EAAtB1P,EAASP,OAAIgQ,IAAazP,CAAAA,GAAK2P,GAASH,gBAEnC,GAAUrL,GACrBiL,EAAKvF,aAAiB7J,EAAK4P,gBAAiBJ,EAClCH,EAASK,EAGhBN,GAAAA,EA5GLS,GAAAA,GAAgB,GAAA,EAClB,GAASC,EAAYpQ,GAEnB,EADI2E,GAAAA,EAA4B,KAAM,EAAW,IAAG,EAC3CF,KAAW,GACTY,EAAcA,KAChB,GAAkBgL,EACrB1L,MAAUU,EAAQZ,KAIjBE,GAAAA,EAGA2L,CAAAA,GAAAA,GACP,CACIrQ,GACAA,EACAA,EACAA,EAAE,OACS,EACA,EACA,SAAN,GACLA,MAAK,GACLA,IAAK,SACH,EAAG,GAGX,GAAA,GAASkQ,EAAcI,IAAAA,SAAkBC,EAAMd,GAAAA,MAAMe,GAAAA,GAGnD,GAAK,GAFDC,KAAAA,KAAc,IAAM,OAAQ,UAAc,EAAM,GAAQ,IAAM,IAEzDjM,EACPiM,MAAU,KAAKD,SAAAA,EAGjB,GAAK,GAAIhM,YACP,EAAK,GAAIY,GAAW,IAAGA,EACXZ,MAAM8L,KAAAA,OAAeG,GAAOrL,GAAAA,KAI1C,GAAQqK,GAAAA,GAAK,GAAQA,IAAK,GAAIiB,GAE1BC,QAAAA,UAAuB,EAAA,EAAK,EAAG,EAAG,EAAG,EAAA,EAAK,EAAG,EAAG,EAAG,EAAA,EAAK,EAAG,EAAG,EAAG,GAErEA,GAAAA,UAAa,mBACbA,GAAAA,QAAa,KAAU9Q,SACvB8Q,KAAAA,SAAa,KAAU9Q,SACvB8Q,KAAAA,UAAkB,QAAaD,aAC/BC,KAAa,OAAc9Q,KAAQ6Q,GACnCC,EAAAA,EAAAA,IAAU,GAAG,QAAcD,IAAQX,GACnCY,EAAAA,EAAAA,IAAU,GAAQ,EAAK9Q,KAAYkQ,QACnCY,IAAAA,GAAa,EAAK,EAAKC,IAAQ/Q,GAC/B8Q,EAAAA,KAAAA,QAAkB,IAAI,GAAaC,EAAIA,EAEvCH,KAAAA,SAASN,MAASM,GAAQE,MAAAA,KAE1B,KAAIE,GAAAA,OAAkB,IAAK,KAAM,GAAQ,EAAG,KAAW,OAAS,IAC5DN,KAAK,GACPM,EAAQ,KAAKN,WACbE,KAASN,GAASM,EAAAA,EAAAA,IAAQI,GAGxBN,YACFM,IAAK,GAAQ,EACbA,EAAK,IAAQN,GAAK,EAClBE,KAASN,YAASM,IAAQI,GAAAA,EAGxBN,EAAK,IACPM,GAAK,EAAQ,KACbA,YAAaN,IACbE,GAAAA,EAASN,EAAAA,KAASM,aAGpB,MAASjM,GAAW,GAAGA,qBAEnBiM,EAAgBK,GAIhBT,eACMI,EAAcA,GAA4BA,SAAU,GAAIA,QAAcA,GAAAA,GAEzEA,GAAAA,GAAO,OAAGM,EAAON,OAAWA,OAAO,IAAIA,IAAO,GAEvD,EAAA,KAAOP,EAAAA,MAAAA,EAFmBO,OA4BtBP,EAAAA,QAAAA,GAAgBA,GAAAA,MACtBhH,GAAauG,IAAAA,KAEZ/R,MAAAA,EAAgBE,KAAAA,EAAAA,EAAAA,MCnHnB,IAAA,IAAUsL,GAAOrL,MAAAA,EAEXmT,SAAAA,MAAiB,EAEjBC,OAAAA,OAAAA,GAAuB,QAASpG,GAAQqG,EAAAA,GAAaC,OAAAA,EACvDpQ,EAAK8J,GAASA,EACd9J,qBAAmBmQ,EAAAA,GACdC,iBAAeA,GAEftL,SAAO,GACPuL,QAAU,GACVC,GACAC,GAAAA,KAAgBzG,KAAAA,GACrB9J,KAAKwQ,GAAAA,EAAmB,IACxBxQ,EAAKyQ,EAAaC,OAAMC,GAAAA,QACnBC,GAAiBC,GAGxB1I,MAAM2I,GAAS,aAASC,qCACCd,IAClBe,EAAAA,uBACAC,GACLjR,QAAKkR,GACAC,EAAAA,GACLnR,GAAKoR,GAAAA,EAAc,gBACdC,EACAC,IAAAA,EAAW,IAChBtR,GAAKuR,IAAAA,EAAAA,GACLvR,CAAAA,GAAKwR,GAAAA,EAAUT,EACf/Q,IAAKyR,EAAAA,GAAYzR,EAAKwR,IAAAA,SAAQxG,EAC9BhL,GAAK0R,EAAQ,IACb1R,SAAK2R,GAAAA,IAAAA,EAGPxJ,GAAAA,EAAM2I,KAAOzD,GAAAA,IAAAA,EACXuE,QAAc,EAAA,CAAA,GACZ5R,aAAKyR,KAAYzR,EAAKwR,KAAQxG,aAAamF,KAAAA,EACtCnQ,IAAKoR,CAAAA,GAAAA,GAAAA,EAAgBpR,EAAKyR,GAAAA,GAAAA,EAAmBJ,GAAAA,EAAAA,GAAAA,EAChDrR,GAAKoR,2BACCnD,KAAS4D,EAAS1I,KAAKnJ,2BAGf,KAAA,EAAS8R,IAASC,MAAAA,GAC9BD,IAAAA,SAAgBd,GAAAA,MAClBhR,gBAAoB8R,GAChB9R,EAAKgS,EAAaD,OACff,QAAAA,GAAoBG,GAAgB,GAAInR,GAAKsI,EAAAA,gBAC/CsJ,EAGTtE,IAAI6C,EAAAA,IAAAA,EACF,CAAInQ,IAAAA,GAAK0R,GAAS1R,EAAK2R,GAAAA,IAAAA,IAAAA,KACd,IACF3R,KAAKgR,EAAAA,EAAAA,GAEVb,EAAAA,EAAAA,EAAAA,EAAY2B,EAAAA,OACdA,IAAWA,CAAAA,GAAAA,GACPlU,EAAMkU,EAAAA,iBAEJG,IACDjS,EAAkC,eAAdiR,KACvBjR,GAAKiR,GAAAA,KAAajR,EAAAA,MAAKkS,EAAAA,OAAU/B,EAAAA,IAAc2B,EAAAA,OAAU9R,GAAAA,GAAKmR,EAAAA,GAEhEnR,EAAK2R,GAAAA,UAAsB,GAClBX,MAAAA,IAEThR,EAAKmS,EAAiBL,GAAAA,EACtB3J,MAAMiK,EAAAA,MAAAA,GAAAA,KAAAA,EAEJrJ,GAAAA,EAAAA,IACF,GAAYkI,GAAAA,OAEVlI,EAAAA,EAAU+I,KAAAA,QACZA,GACUA,GAEN9R,GAAAA,GAAKkR,EAAUlR,gBAEdiR,EAAaa,KAClB9R,EAAKmS,OAAAA,IAAkBnS,IAAKkS,EAAU/B,GAAAA,EAAAA,GAAcnQ,OAAKiR,GAAAA,IAAmBxT,MAAAA,IAAAA,GAC5E0K,QAAMiK,IAAAA,IAAAA,OAAAA,IAAAA,KAEJ3U,KAAAA,IAAAA,GAAAA,QAAiB,IAAOuC,MAAKmR,EAC7Ba,EAAAA,oBACWN,KAAU1R,KAAKmR,EAAAA,gBAA0BH,IAAAA,GAAAA,qBAAqB1I,EAClE6I,KAAAA,KAAgB,GAAKnR,GAAKgR,qBAEjC1I,EAAmB,qBAAoBA,EAAAA,KAC3CgF,KAAI+E,GAAAA,GACErS,uBAECA,EAAKiR,gBAA4BC,EAAezT,EAAAA,gBAA2BkU,CACvE,IAAA,GAEA,EAAA,oBAEA,KAAA,KACF,EAEH,KAAA,GACJ3R,qBACSgS,EAAiBN,GACnBV,sBAAwC,qBAAS1I,GACjD2I,SACL9I,GAAMiK,QAAAA,GAERpS,GAAKqR,GAAAA,GAAgB,EACrBlJ,aACAnI,UAAa,EACR4R,IAAAA,GAAAA,EAEPU,GAAAA,OAAO,UACKN,OAAAA,EAAahS,aAAgBA,EAAK0R,OAC1C1R,EAAK2R,aAAAA,KAAsB,OAExBV,QAAAA,EACS,EAEhBsB,OAAQ,EACFvS,aAEJA,KAAKmQ,OAAAA,QAAcnQ,EAAKmR,gBAAoBnR,EAAKsI,OAAAA,EACjDtI,aAAKiR,KAAajR,OAAKsI,SAAAA,EAAsB6H,IAAAA,IAAAA,GACxCwB,EAAAA,aAAAA,WAEPa,EAAQ,IAAA,GACDf,EAAAA,GACLzR,OAAK0R,WACL1R,OAAKmQ,EAAc,aACdc,EAAa,OAEpBwB,EAAS,aACPzS,KAAKmR,OAAAA,QAAiB,EAEjBuB,EAAAA,OAEPC,EAAAA,aAAkB,KAAS7N,OAAM8N,QACT,EAAA,gBAAXA,EAAiC,OAAR9N,EAClC9E,aAAKuR,KAAAA,OAAqBqB,SAAAA,EAE9BC,IAAAA,IAAAA,GAAqB,EAAS/N,aAC5B,WAAY,EAAZ,OAEIgO,IAAavB,EAAAA,IAAAA,WAAgBpT,OAAQyU,EACrCE,aACGvB,EAAAA,OAAAA,EAAgBwB,aAAc,KAEvCC,OAAAA,QAAa,EAASC,SAChBjB,EAAAA,aAAgBA,KACpB,OAAKA,6BAAiCX,YACpC,EAAI6B,oBAAYhD,EAAAA,OAAqBlQ,EAAWgR,aAAciC,KAAAA,OAC1DzI,SAAWxK,EAAKuR,KAAAA,OAAgBvB,QAAOhQ,GAAiBA,EAAKsR,GAAAA,MACjE6B,GAAAA,KAAAA,EAAW,GAAA,UACT3I,EAASxM,GAAAA,EAAQ,UAAS4U,EACxBA,MAAAA,GAAQQ,EAAKF,MAAMpJ,IAAQoJ,UAE5B,EAELlT,gBAAqBgS,OAAAA,EAEvBqB,gBAAgBjD,MAAAA,WACTpQ,EAAK0R,GAAAA,EAAU1R,UAAKkR,EACA,MAAnBlR,GAAKiR,EAAAA,MACPjR,IAAK+I,WAAYqH,EAAAA,yBAAmCpQ,OAAKvC,EAAAA,gBAC5CuU,MACbhS,WAAKmS,EAAAA,IAAkB/B,EAAAA,IAAAA,EAAepQ,GAAKiR,EAAAA,UAAmBxT,EAAAA,MAAAA,GAAAA,EAG7DkU,MAAAA,IAAAA,WAAsB,EAC3B3R,GAAKgT,EAAY5C,MAAAA,OACJsB,OAAAA,GAAU1R,GAAwBqR,EAAAA,qBAQlD1U,KAAgBE,KAAAA,EAAAA,sBCjLTwB,EAqBCiV,EAAAA,gBACP,KAAIC,OAAAA,EACJC,KAAAA,EAEAD,EAAAA,oBAA4BE,KAAAA,OAASA,EAASC,gBAE5CC,KAAKD,EACPE,EAAAA,oBAGOC,KAAeC,OAAAA,EAAYC,IAClC,GAAOD,qBAAWE,EAAkBD,GAAYC,mBAGzCC,GAAAA,SACFpC,GAAAA,QAEA1B,GAAAA,EAAcnC,GAAOkG,EAAAA,QAAeA,IAAYrD,QAAMqD,SAAYrD,GAkCzE,IAAS+C,UAAAA,gBACPO,QAAenW,EAAQ,GAASe,KAclC,GAAS4U,KACPS,GAAAA,aAAAA,kBACejM,gBACf8F,EAASkC,mBACTlC,0BAAuB4F,EAAAA,eAEvB,sBAAsB5F,EAAS4D,qBACtBA,4BAELwC,EACAC,aAAAA,SACJC,GAAAA,MAAkBA,GAAAA,IAAuB,IAASC,EAChDA"}
\ No newline at end of file
diff --git a/third_party/widevine/cdm/BUILD.gn b/third_party/widevine/cdm/BUILD.gn
index b563eb4..aa476af1 100644
--- a/third_party/widevine/cdm/BUILD.gn
+++ b/third_party/widevine/cdm/BUILD.gn
@@ -2,7 +2,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-widevine_arch = cpu_arch
+widevine_arch = current_cpu
 if (widevine_arch == "x86") {
   widevine_arch = "ia32"
 }
diff --git a/third_party/yasm/BUILD.gn b/third_party/yasm/BUILD.gn
index cd9b4532..a09a495 100644
--- a/third_party/yasm/BUILD.gn
+++ b/third_party/yasm/BUILD.gn
@@ -30,18 +30,14 @@
 if (current_toolchain == host_toolchain) {
   # Various files referenced by multiple targets.
   yasm_gen_include_dir = "$target_gen_dir/include"
-  yasm_os = os
-  if (is_chromeos) {
-    yasm_os = "linux"
-  }
-  config_makefile = "source/config/$yasm_os/Makefile"
+  config_makefile = "source/config/$host_os/Makefile"
   version_file = "version.mac"
 
   import("//build/compiled_action.gni")
 
   config("yasm_config") {
     include_dirs = [
-      "source/config/$yasm_os",
+      "source/config/$host_os",
       "source/patched-yasm",
     ]
     defines = [ "HAVE_CONFIG_H" ]
@@ -148,8 +144,6 @@
     # re2c is missing CLOSEVOP from one switch.
     if (is_posix) {
       cflags = [ "-Wno-switch" ]
-    } else if (is_win) {
-      cflags = [ "/wd4267" ]  # size_t to int conversion.
     }
   }
 
@@ -248,9 +242,7 @@
     # directory, but the gen_x86_insn.py script does not make this easy.
     include_dirs = [ yasm_gen_include_dir ]
 
-    if (is_win) {
-      cflags = [ "/wd4267" ]  # size_t to int conversion.
-    } else {
+    if (!is_win) {
       cflags = [
         "-ansi",
         "-pedantic",
diff --git a/third_party/yasm/yasm_assemble.gni b/third_party/yasm/yasm_assemble.gni
index 56d7e11a..1a84d51 100644
--- a/third_party/yasm/yasm_assemble.gni
+++ b/third_party/yasm/yasm_assemble.gni
@@ -42,13 +42,13 @@
 #   }
 
 if (is_mac || is_ios) {
-  if (cpu_arch == "x86") {
+  if (current_cpu == "x86") {
     _yasm_flags = [
       "-fmacho32",
       "-m",
       "x86",
     ]
-  } else if (cpu_arch == "x64") {
+  } else if (current_cpu == "x64") {
     _yasm_flags = [
       "-fmacho64",
       "-m",
@@ -56,13 +56,13 @@
     ]
   }
 } else if (is_posix) {
-  if (cpu_arch == "x86") {
+  if (current_cpu == "x86") {
     _yasm_flags = [
       "-felf32",
       "-m",
       "x86",
     ]
-  } else if (cpu_arch == "x64") {
+  } else if (current_cpu == "x64") {
     _yasm_flags = [
       "-DPIC",
       "-felf64",
@@ -71,14 +71,14 @@
     ]
   }
 } else if (is_win) {
-  if (cpu_arch == "x86") {
+  if (current_cpu == "x86") {
     _yasm_flags = [
       "-DPREFIX",
       "-fwin32",
       "-m",
       "x86",
     ]
-  } else if (cpu_arch == "x64") {
+  } else if (current_cpu == "x64") {
     _yasm_flags = [
       "-fwin64",
       "-m",
@@ -99,7 +99,7 @@
 
   # Only depend on YASM on x86 systems. Force compilation of .asm files for
   # ARM to fail.
-  assert(cpu_arch == "x86" || cpu_arch == "x64")
+  assert(current_cpu == "x86" || current_cpu == "x64")
 
   action_name = "${target_name}_action"
   source_set_name = target_name
diff --git a/third_party/zlib/BUILD.gn b/third_party/zlib/BUILD.gn
index e49b5e9..1cea9ac 100644
--- a/third_party/zlib/BUILD.gn
+++ b/third_party/zlib/BUILD.gn
@@ -7,7 +7,7 @@
 }
 
 static_library("zlib_x86_simd") {
-  if (!is_ios && (cpu_arch == "x86" || cpu_arch == "x64")) {
+  if (!is_ios && (current_cpu == "x86" || current_cpu == "x64")) {
     sources = [
       "crc_folding.c",
       "fill_window_sse.c",
@@ -65,7 +65,7 @@
     "zutil.h",
   ]
 
-  if (!is_ios && (cpu_arch == "x86" || cpu_arch == "x64")) {
+  if (!is_ios && (current_cpu == "x86" || current_cpu == "x64")) {
     sources += [ "x86.c" ]
   }
 
diff --git a/tools/android/common/BUILD.gn b/tools/android/common/BUILD.gn
index 8307a91..3c86741 100644
--- a/tools/android/common/BUILD.gn
+++ b/tools/android/common/BUILD.gn
@@ -12,4 +12,8 @@
     "net.cc",
     "net.h",
   ]
+
+  deps = [
+    "//base",
+  ]
 }
diff --git a/tools/android/memconsumer/memconsumer.gyp b/tools/android/memconsumer/memconsumer.gyp
index f721fc17..b0e2517c 100644
--- a/tools/android/memconsumer/memconsumer.gyp
+++ b/tools/android/memconsumer/memconsumer.gyp
@@ -28,6 +28,11 @@
     {
       'target_name': 'libmemconsumer',
       'type': 'shared_library',
+      'variables': {
+        # This library uses native JNI exports; tell gyp so that the required
+        # symbols will be kept.
+        'use_native_jni_exports': 1,
+      },
       'sources': [
         'memconsumer_hook.cc',
       ],
diff --git a/tools/auto_bisect/fetch_build.py b/tools/auto_bisect/fetch_build.py
index 5e6e240..7b622fb4 100644
--- a/tools/auto_bisect/fetch_build.py
+++ b/tools/auto_bisect/fetch_build.py
@@ -56,7 +56,7 @@
   Returns:
     A pair of strings (bucket, path), where the archive is expected to be.
   """
-  logging.info('Creating BuildArchive, type "%s", arch "%s", platform "%s".',
+  logging.info('Getting GS URL for archive of builder "%s", "%s", "%s".',
                builder_type, target_arch, target_platform)
   build_archive = BuildArchive.Create(
       builder_type, target_arch=target_arch, target_platform=target_platform,
@@ -69,6 +69,8 @@
 def GetBuilderNameAndBuildTime(builder_type=PERF_BUILDER, target_arch='ia32',
                                target_platform='chromium', extra_src=None):
   """Gets builder bot name and build time in seconds based on platform."""
+  logging.info('Getting builder name for builder "%s", "%s", "%s".',
+               builder_type, target_arch, target_platform)
   build_archive = BuildArchive.Create(
       builder_type, target_arch=target_arch, target_platform=target_platform,
       extra_src=extra_src)
@@ -87,8 +89,6 @@
   @staticmethod
   def Create(builder_type, target_arch='ia32', target_platform='chromium',
              extra_src=None):
-    logging.info('Creating BuildArchive, type "%s", arch "%s", platform "%s".',
-                 builder_type, target_arch, target_platform)
     if builder_type == PERF_BUILDER:
       return PerfBuildArchive(target_arch, target_platform)
     if builder_type == FULL_BUILDER:
diff --git a/tools/binary_size/binary_size_utils.py b/tools/binary_size/binary_size_utils.py
index f265f61..5521ba7d 100644
--- a/tools/binary_size/binary_size_utils.py
+++ b/tools/binary_size/binary_size_utils.py
@@ -23,7 +23,7 @@
   """
 
   # Match lines with size, symbol, optional location, optional discriminator
-  sym_re = re.compile(r'^[0-9a-f]{8,} ' # address (8+ hex digits)
+  sym_re = re.compile(r'^([0-9a-f]{8,}) ' # address (8+ hex digits)
                       '([0-9a-f]{8,}) ' # size (8+ hex digits)
                       '(.) ' # symbol type, one character
                       '([^\t]+)' # symbol name, separated from next by tab
@@ -39,12 +39,12 @@
     line = line.rstrip()
     match = sym_re.match(line)
     if match:
-      size, sym_type, sym = match.groups()[0:3]
+      address, size, sym_type, sym = match.groups()[0:4]
       size = int(size, 16)
       if sym_type in ('B', 'b'):
         continue  # skip all BSS for now.
-      path = match.group(4)
-      yield sym, sym_type, size, path
+      path = match.group(5)
+      yield sym, sym_type, size, path, address
       continue
     match = addr_re.match(line)
     if match:
diff --git a/tools/binary_size/explain_binary_size_delta.py b/tools/binary_size/explain_binary_size_delta.py
index d6eba26..8bc6c5a 100755
--- a/tools/binary_size/explain_binary_size_delta.py
+++ b/tools/binary_size/explain_binary_size_delta.py
@@ -38,6 +38,8 @@
 """
 
 import collections
+from collections import Counter
+from math import ceil
 import operator
 import optparse
 import os
@@ -46,11 +48,81 @@
 import binary_size_utils
 
 
+def CalculateSharedAddresses(symbols):
+  """Checks how many symbols share the same memory space. This returns a
+Counter result where result[address] will tell you how many times address was
+used by symbols."""
+  count = Counter()
+  for _, _, _, _, address in symbols:
+    count[address] += 1
+
+  return count
+
+
+def CalculateEffectiveSize(share_count, address, symbol_size):
+  """Given a raw symbol_size and an address, this method returns the
+  size we should blame on this symbol considering it might share the
+  machine code/data with other symbols. Using the raw symbol_size for
+  each symbol would in those cases over estimate the true cost of that
+  block.
+
+  """
+  shared_count = share_count[address]
+  if shared_count == 1:
+    return symbol_size
+
+  assert shared_count > 1
+  return int(ceil(symbol_size / float(shared_count)))
+
+class SymbolDelta(object):
+  """Stores old size, new size and some metadata."""
+  def __init__(self, shared):
+    self.old_size = None
+    self.new_size = None
+    self.shares_space_with_other_symbols = shared
+
+  def __eq__(self, other):
+    return (self.old_size == other.old_size and
+            self.new_size == other.new_size and
+            self.shares_space_with_other_symbols ==
+            other.shares_space_with_other_symbols)
+
+  def __ne__(self, other):
+    return not self.__eq__(other)
+
+  def copy_symbol_delta(self):
+    symbol_delta = SymbolDelta(self.shares_space_with_other_symbols)
+    symbol_delta.old_size = self.old_size
+    symbol_delta.new_size = self.new_size
+    return symbol_delta
+
+class DeltaInfo(SymbolDelta):
+  """Summary of a the change for one symbol between two instances."""
+  def __init__(self, file_path, symbol_type, symbol_name, shared):
+    SymbolDelta.__init__(self, shared)
+    self.file_path = file_path
+    self.symbol_type = symbol_type
+    self.symbol_name = symbol_name
+
+  def __eq__(self, other):
+    return (self.file_path == other.file_path and
+            self.symbol_type == other.symbol_type and
+            self.symbol_name == other.symbol_name and
+            SymbolDelta.__eq__(self, other))
+
+  def __ne__(self, other):
+    return not self.__eq__(other)
+
+  def ExtractSymbolDelta(self):
+    """Returns a copy of the SymbolDelta for this DeltaInfo."""
+    return SymbolDelta.copy_symbol_delta(self)
+
 def Compare(symbols1, symbols2):
   """Executes a comparison of the symbols in symbols1 and symbols2.
 
   Returns:
       tuple of lists: (added_symbols, removed_symbols, changed_symbols, others)
+      where each list contains DeltaInfo objects.
   """
   added = [] # tuples
   removed = [] # tuples
@@ -59,9 +131,12 @@
 
   cache1 = {}
   cache2 = {}
-  # Make a map of (file, symbol_type) : (symbol_name, symbol_size)
-  for cache, symbols in ((cache1, symbols1), (cache2, symbols2)):
-    for symbol_name, symbol_type, symbol_size, file_path in symbols:
+  # Make a map of (file, symbol_type) : (symbol_name, effective_symbol_size)
+  share_count1 = CalculateSharedAddresses(symbols1)
+  share_count2 = CalculateSharedAddresses(symbols2)
+  for cache, symbols, share_count in ((cache1, symbols1, share_count1),
+                                      (cache2, symbols2, share_count2)):
+    for symbol_name, symbol_type, symbol_size, file_path, address in symbols:
       if 'vtable for ' in symbol_name:
         symbol_type = '@' # hack to categorize these separately
       if file_path:
@@ -70,10 +145,15 @@
           file_path = file_path.replace('\\', '/')
       else:
         file_path = '(No Path)'
+      # Take into consideration that multiple symbols might share the same
+      # block of code.
+      effective_symbol_size = CalculateEffectiveSize(share_count, address,
+                                                     symbol_size)
       key = (file_path, symbol_type)
       bucket = cache.setdefault(key, {})
       size_list = bucket.setdefault(symbol_name, [])
-      size_list.append(symbol_size)
+      size_list.append((effective_symbol_size,
+                        effective_symbol_size != symbol_size))
 
   # Now diff them. We iterate over the elements in cache1. For each symbol
   # that we find in cache2, we record whether it was deleted, changed, or
@@ -81,52 +161,69 @@
   # in cache2 at the end of the iteration over cache1 are the 'new' symbols.
   for key, bucket1 in cache1.items():
     bucket2 = cache2.get(key)
+    file_path, symbol_type = key;
     if not bucket2:
       # A file was removed. Everything in bucket1 is dead.
       for symbol_name, symbol_size_list in bucket1.items():
-        for symbol_size in symbol_size_list:
-          removed.append((key[0], key[1], symbol_name, symbol_size, None))
+        for (symbol_size, shared) in symbol_size_list:
+          delta_info = DeltaInfo(file_path, symbol_type, symbol_name, shared)
+          delta_info.old_size = symbol_size
+          removed.append(delta_info)
     else:
       # File still exists, look for changes within.
       for symbol_name, symbol_size_list in bucket1.items():
         size_list2 = bucket2.get(symbol_name)
         if size_list2 is None:
           # Symbol no longer exists in bucket2.
-          for symbol_size in symbol_size_list:
-            removed.append((key[0], key[1], symbol_name, symbol_size, None))
+          for (symbol_size, shared) in symbol_size_list:
+            delta_info = DeltaInfo(file_path, symbol_type, symbol_name, shared)
+            delta_info.old_size = symbol_size
+            removed.append(delta_info)
         else:
           del bucket2[symbol_name] # Symbol is not new, delete from cache2.
           if len(symbol_size_list) == 1 and len(size_list2) == 1:
-            symbol_size = symbol_size_list[0]
-            size2 = size_list2[0]
+            symbol_size, shared1 = symbol_size_list[0]
+            size2, shared2 = size_list2[0]
+            delta_info = DeltaInfo(file_path, symbol_type, symbol_name,
+                                   shared1 or shared2)
+            delta_info.old_size = symbol_size
+            delta_info.new_size = size2
             if symbol_size != size2:
               # Symbol has change size in bucket.
-              changed.append((key[0], key[1], symbol_name, symbol_size, size2))
+              changed.append(delta_info)
             else:
               # Symbol is unchanged.
-              unchanged.append((key[0], key[1], symbol_name, symbol_size,
-                                size2))
+              unchanged.append(delta_info)
           else:
             # Complex comparison for when a symbol exists multiple times
             # in the same file (where file can be "unknown file").
             symbol_size_counter = collections.Counter(symbol_size_list)
             delta_counter = collections.Counter(symbol_size_list)
             delta_counter.subtract(size_list2)
-            for symbol_size in sorted(delta_counter.keys()):
-              delta = delta_counter[symbol_size]
-              unchanged_count = symbol_size_counter[symbol_size]
+            for delta_counter_key in sorted(delta_counter.keys()):
+              delta = delta_counter[delta_counter_key]
+              unchanged_count = symbol_size_counter[delta_counter_key]
+              (symbol_size, shared) = delta_counter_key
               if delta > 0:
                 unchanged_count -= delta
               for _ in range(unchanged_count):
-                unchanged.append((key[0], key[1], symbol_name, symbol_size,
-                                  symbol_size))
+                delta_info = DeltaInfo(file_path, symbol_type,
+                                       symbol_name, shared)
+                delta_info.old_size = symbol_size
+                delta_info.new_size = symbol_size
+                unchanged.append(delta_info)
               if delta > 0: # Used to be more of these than there is now.
                 for _ in range(delta):
-                  removed.append((key[0], key[1], symbol_name, symbol_size,
-                                  None))
+                  delta_info = DeltaInfo(file_path, symbol_type,
+                                         symbol_name, shared)
+                  delta_info.old_size = symbol_size
+                  removed.append(delta_info)
               elif delta < 0: # More of this (symbol,size) now.
                 for _ in range(-delta):
-                  added.append((key[0], key[1], symbol_name, None, symbol_size))
+                  delta_info = DeltaInfo(file_path, symbol_type,
+                                         symbol_name, shared)
+                  delta_info.new_size = symbol_size
+                  added.append(delta_info)
 
           if len(bucket2) == 0:
             del cache1[key] # Entire bucket is empty, delete from cache2
@@ -135,11 +232,15 @@
   # the encountered symbols from cache2. What's left in cache2 is the new
   # symbols.
   for key, bucket2 in cache2.iteritems():
+    file_path, symbol_type = key;
     for symbol_name, symbol_size_list in bucket2.items():
-      for symbol_size in symbol_size_list:
-        added.append((key[0], key[1], symbol_name, None, symbol_size))
+      for (symbol_size, shared) in symbol_size_list:
+        delta_info = DeltaInfo(file_path, symbol_type, symbol_name, shared)
+        delta_info.new_size = symbol_size
+        added.append(delta_info)
   return (added, removed, changed, unchanged)
 
+
 def DeltaStr(number):
   """Returns the number as a string with a '+' prefix if it's > 0 and
   a '-' prefix if it's < 0."""
@@ -149,6 +250,16 @@
   return result
 
 
+def SharedInfoStr(symbol_info):
+  """Returns a string (prefixed by space) explaining that numbers are
+  adjusted because of shared space between symbols, or an empty string
+  if space had not been shared."""
+
+  if symbol_info.shares_space_with_other_symbols:
+    return " (adjusted sizes because of memory sharing)"
+
+  return ""
+
 class CrunchStatsData(object):
   """Stores a summary of data of a certain kind."""
   def __init__(self, symbols):
@@ -166,8 +277,7 @@
   grown = []
   shrunk = []
   for item in changed:
-    file_path, symbol_type, symbol_name, size1, size2 = item
-    if size1 < size2:
+    if item.old_size < item.new_size:
       grown.append(item)
     else:
       shrunk.append(item)
@@ -178,14 +288,15 @@
   shrunk_symbols = CrunchStatsData(shrunk)
   sections = [new_symbols, removed_symbols, grown_symbols, shrunk_symbols]
   for section in sections:
-    for file_path, symbol_type, symbol_name, size1, size2 in section.symbols:
-      section.sources.add(file_path)
-      if size1 is not None:
-        section.before_size += size1
-      if size2 is not None:
-        section.after_size += size2
-      bucket = section.symbols_by_path.setdefault(file_path, [])
-      bucket.append((symbol_name, symbol_type, size1, size2))
+    for item in section.symbols:
+      section.sources.add(item.file_path)
+      if item.old_size is not None:
+        section.before_size += item.old_size
+      if item.new_size is not None:
+        section.after_size += item.new_size
+      bucket = section.symbols_by_path.setdefault(item.file_path, [])
+      bucket.append((item.symbol_name, item.symbol_type,
+                     item.ExtractSymbolDelta()))
 
   total_change = sum(s.after_size - s.before_size for s in sections)
   summary = 'Total change: %s bytes' % DeltaStr(total_change)
@@ -213,9 +324,9 @@
 
   maybe_unchanged_sources = set()
   unchanged_symbols_size = 0
-  for file_path, symbol_type, symbol_name, size1, size2 in unchanged:
-    maybe_unchanged_sources.add(file_path)
-    unchanged_symbols_size += size1 # == size2
+  for item in unchanged:
+    maybe_unchanged_sources.add(item.file_path)
+    unchanged_symbols_size += item.old_size # == item.new_size
   print('  %d unchanged, totalling %d bytes' %
         (len(unchanged), unchanged_symbols_size))
 
@@ -256,14 +367,14 @@
       if not entry:
         entry = {'plus': 0, 'minus': 0}
         delta_by_path[path] = entry
-      for symbol_name, symbol_type, size1, size2 in \
+      for symbol_name, symbol_type, symbol_delta in \
             section.symbols_by_path[path]:
-        if size1 is None:
-          delta = size2
-        elif size2 is None:
-          delta = -size1
+        if symbol_delta.old_size is None:
+          delta = symbol_delta.new_size
+        elif symbol_delta.new_size is None:
+          delta = -symbol_delta.old_size
         else:
-          delta = size2 - size1
+          delta = symbol_delta.new_size - symbol_delta.old_size
 
         if delta > 0:
           entry['plus'] += delta
@@ -288,34 +399,45 @@
     print header
     print divider
     if showsymbols:
+      def ExtractNewSize(tup):
+        symbol_delta = tup[2]
+        return symbol_delta.new_size
+      def ExtractOldSize(tup):
+        symbol_delta = tup[2]
+        return symbol_delta.old_size
       if path in new_symbols.symbols_by_path:
         print '  New symbols:'
-        for symbol_name, symbol_type, size1, size2 in \
+        for symbol_name, symbol_type, symbol_delta in \
             sorted(new_symbols.symbols_by_path[path],
-                   key=operator.itemgetter(3),
+                   key=ExtractNewSize,
                    reverse=True):
-          print ('   %8s: %s type=%s, size=%d bytes' %
-                 (DeltaStr(size2), symbol_name, symbol_type, size2))
+          print ('   %8s: %s type=%s, size=%d bytes%s' %
+                 (DeltaStr(symbol_delta.new_size), symbol_name, symbol_type,
+                  symbol_delta.new_size, SharedInfoStr(symbol_delta)))
       if path in removed_symbols.symbols_by_path:
         print '  Removed symbols:'
-        for symbol_name, symbol_type, size1, size2 in \
+        for symbol_name, symbol_type, symbol_delta in \
             sorted(removed_symbols.symbols_by_path[path],
-                   key=operator.itemgetter(2)):
-          print ('   %8s: %s type=%s, size=%d bytes' %
-                 (DeltaStr(-size1), symbol_name, symbol_type, size1))
+                   key=ExtractOldSize):
+          print ('   %8s: %s type=%s, size=%d bytes%s' %
+                 (DeltaStr(-symbol_delta.old_size), symbol_name, symbol_type,
+                  symbol_delta.old_size,
+                  SharedInfoStr(symbol_delta)))
       for (changed_symbols_by_path, type_str) in [
         (grown_symbols.symbols_by_path, "Grown"),
         (shrunk_symbols.symbols_by_path, "Shrunk")]:
         if path in changed_symbols_by_path:
           print '  %s symbols:' % type_str
           def changed_symbol_sortkey(item):
-            symbol_name, _symbol_type, size1, size2 = item
-            return (size1 - size2, symbol_name)
-          for symbol_name, symbol_type, size1, size2 in \
+            symbol_name, _symbol_type, symbol_delta = item
+            return (symbol_delta.old_size - symbol_delta.new_size, symbol_name)
+          for symbol_name, symbol_type, symbol_delta in \
               sorted(changed_symbols_by_path[path], key=changed_symbol_sortkey):
-            print ('   %8s: %s type=%s, (was %d bytes, now %d bytes)'
-                   % (DeltaStr(size2 - size1), symbol_name,
-                      symbol_type, size1, size2))
+            print ('   %8s: %s type=%s, (was %d bytes, now %d bytes)%s'
+                   % (DeltaStr(symbol_delta.new_size - symbol_delta.old_size),
+                      symbol_name, symbol_type,
+                      symbol_delta.old_size, symbol_delta.new_size,
+                      SharedInfoStr(symbol_delta)))
 
 
 def main():
diff --git a/tools/binary_size/explain_binary_size_delta_unittest.py b/tools/binary_size/explain_binary_size_delta_unittest.py
index 87ecb36..d818d83 100755
--- a/tools/binary_size/explain_binary_size_delta_unittest.py
+++ b/tools/binary_size/explain_binary_size_delta_unittest.py
@@ -15,114 +15,122 @@
 class ExplainBinarySizeDeltaTest(unittest.TestCase):
 
   def testCompare(self):
-    # List entries have form: symbol_name, symbol_type, symbol_size, file_path
+    # List entries have form:
+    # symbol_name, symbol_type, symbol_size, file_path, memory_address
     symbol_list1 = (
       # File with one symbol, left as-is.
-      ( 'unchanged', 't', 1000, '/file_unchanged' ),
+      ( 'unchanged', 't', 1000, '/file_unchanged', 0x1 ),
       # File with one symbol, changed.
-      ( 'changed', 't', 1000, '/file_all_changed' ),
+      ( 'changed', 't', 1000, '/file_all_changed', 0x2 ),
       # File with one symbol, deleted.
-      ( 'removed', 't', 1000, '/file_all_deleted' ),
+      ( 'removed', 't', 1000, '/file_all_deleted', 0x3 ),
       # File with two symbols, one unchanged, one changed, same bucket
-      ( 'unchanged', 't', 1000, '/file_pair_unchanged_changed' ),
-      ( 'changed', 't', 1000, '/file_pair_unchanged_changed' ),
+      ( 'unchanged', 't', 1000, '/file_pair_unchanged_changed', 0x4 ),
+      ( 'changed', 't', 1000, '/file_pair_unchanged_changed', 0x5 ),
       # File with two symbols, one unchanged, one deleted, same bucket
-      ( 'unchanged', 't', 1000, '/file_pair_unchanged_removed' ),
-      ( 'removed', 't', 1000, '/file_pair_unchanged_removed' ),
+      ( 'unchanged', 't', 1000, '/file_pair_unchanged_removed', 0x6 ),
+      ( 'removed', 't', 1000, '/file_pair_unchanged_removed', 0x7 ),
       # File with two symbols, one unchanged, one added, same bucket
-      ( 'unchanged', 't', 1000, '/file_pair_unchanged_added' ),
+      ( 'unchanged', 't', 1000, '/file_pair_unchanged_added', 0x8 ),
       # File with two symbols, one unchanged, one changed, different bucket
-      ( 'unchanged', 't', 1000, '/file_pair_unchanged_diffbuck_changed' ),
-      ( 'changed', '@', 1000, '/file_pair_unchanged_diffbuck_changed' ),
+      ( 'unchanged', 't', 1000, '/file_pair_unchanged_diffbuck_changed', 0x9 ),
+      ( 'changed', '@', 1000, '/file_pair_unchanged_diffbuck_changed', 0xa ),
       # File with two symbols, one unchanged, one deleted, different bucket
-      ( 'unchanged', 't', 1000, '/file_pair_unchanged_diffbuck_removed' ),
-      ( 'removed', '@', 1000, '/file_pair_unchanged_diffbuck_removed' ),
+      ( 'unchanged', 't', 1000, '/file_pair_unchanged_diffbuck_removed', 0xb ),
+      ( 'removed', '@', 1000, '/file_pair_unchanged_diffbuck_removed', 0xc ),
       # File with two symbols, one unchanged, one added, different bucket
-      ( 'unchanged', 't', 1000, '/file_pair_unchanged_diffbuck_added' ),
+      ( 'unchanged', 't', 1000, '/file_pair_unchanged_diffbuck_added', 0xd ),
       # File with four symbols, one added, one removed,
       # one changed, one unchanged
-      ( 'size_changed', 't', 1000, '/file_tetra' ),
-      ( 'removed', 't', 1000, '/file_tetra' ),
-      ( 'unchanged', 't', 1000, '/file_tetra' ),
+      ( 'size_changed', 't', 1000, '/file_tetra', 0xe ),
+      ( 'removed', 't', 1000, '/file_tetra', 0xf ),
+      ( 'unchanged', 't', 1000, '/file_tetra', 0x10 ),
     )
 
     symbol_list2 = (
       # File with one symbol, left as-is.
-      ( 'unchanged', 't', 1000, '/file_unchanged' ),
+      ( 'unchanged', 't', 1000, '/file_unchanged', 0x1 ),
       # File with one symbol, changed.
-      ( 'changed', 't', 2000, '/file_all_changed' ),
+      ( 'changed', 't', 2000, '/file_all_changed', 0x2 ),
       # File with two symbols, one unchanged, one changed, same bucket
-      ( 'unchanged', 't', 1000, '/file_pair_unchanged_changed' ),
-      ( 'changed', 't', 2000, '/file_pair_unchanged_changed' ),
+      ( 'unchanged', 't', 1000, '/file_pair_unchanged_changed', 0x3 ),
+      ( 'changed', 't', 2000, '/file_pair_unchanged_changed', 0x4 ),
       # File with two symbols, one unchanged, one deleted, same bucket
-      ( 'unchanged', 't', 1000, '/file_pair_unchanged_removed' ),
+      ( 'unchanged', 't', 1000, '/file_pair_unchanged_removed', 0x5 ),
       # File with two symbols, one unchanged, one added, same bucket
-      ( 'unchanged', 't', 1000, '/file_pair_unchanged_added' ),
-      ( 'added', 't', 1000, '/file_pair_unchanged_added' ),
+      ( 'unchanged', 't', 1000, '/file_pair_unchanged_added', 0x6 ),
+      ( 'added', 't', 1000, '/file_pair_unchanged_added', 0x7 ),
       # File with two symbols, one unchanged, one changed, different bucket
-      ( 'unchanged', 't', 1000, '/file_pair_unchanged_diffbuck_changed' ),
-      ( 'changed', '@', 2000, '/file_pair_unchanged_diffbuck_changed' ),
+      ( 'unchanged', 't', 1000, '/file_pair_unchanged_diffbuck_changed', 0x8 ),
+      ( 'changed', '@', 2000, '/file_pair_unchanged_diffbuck_changed', 0x9 ),
       # File with two symbols, one unchanged, one deleted, different bucket
-      ( 'unchanged', 't', 1000, '/file_pair_unchanged_diffbuck_removed' ),
+      ( 'unchanged', 't', 1000, '/file_pair_unchanged_diffbuck_removed', 0xa ),
       # File with two symbols, one unchanged, one added, different bucket
-      ( 'unchanged', 't', 1000, '/file_pair_unchanged_diffbuck_added' ),
-      ( 'added', '@', 1000, '/file_pair_unchanged_diffbuck_added' ),
+      ( 'unchanged', 't', 1000, '/file_pair_unchanged_diffbuck_added', 0xb ),
+      ( 'added', '@', 1000, '/file_pair_unchanged_diffbuck_added', 0xc ),
       # File with four symbols, one added, one removed,
       # one changed, one unchanged
-      ( 'size_changed', 't', 2000, '/file_tetra' ),
-      ( 'unchanged', 't', 1000, '/file_tetra' ),
-      ( 'added', 't', 1000, '/file_tetra' ),
+      ( 'size_changed', 't', 2000, '/file_tetra', 0xd ),
+      ( 'unchanged', 't', 1000, '/file_tetra', 0xe ),
+      ( 'added', 't', 1000, '/file_tetra', 0xf ),
       # New file with one symbol added
-      ( 'added', 't', 1000, '/file_new' ),
+      ( 'added', 't', 1000, '/file_new', 0x10 ),
     )
 
     # Here we go
     (added, removed, changed, unchanged) = \
         explain_binary_size_delta.Compare(symbol_list1, symbol_list2)
 
+    def delta(file_path, symbol_type, symbol_name, old_size, new_size):
+      delta_info = explain_binary_size_delta.DeltaInfo(
+        file_path, symbol_type, symbol_name, False)
+      delta_info.old_size = old_size
+      delta_info.new_size = new_size
+      return delta_info
+
     # File with one symbol, left as-is.
-    assert ('/file_unchanged', 't', 'unchanged', 1000, 1000) in unchanged
+    assert delta('/file_unchanged', 't', 'unchanged', 1000, 1000) in unchanged
     # File with one symbol, changed.
-    assert ('/file_all_changed', 't', 'changed', 1000, 2000) in changed
+    assert delta('/file_all_changed', 't', 'changed', 1000, 2000) in changed
     # File with one symbol, deleted.
-    assert ('/file_all_deleted', 't', 'removed', 1000, None) in removed
+    assert delta('/file_all_deleted', 't', 'removed', 1000, None) in removed
     # New file with one symbol added
-    assert ('/file_new', 't', 'added', None, 1000) in added
+    assert delta('/file_new', 't', 'added', None, 1000) in added
     # File with two symbols, one unchanged, one changed, same bucket
-    assert ('/file_pair_unchanged_changed',
+    assert delta('/file_pair_unchanged_changed',
             't', 'unchanged', 1000, 1000) in unchanged
-    assert ('/file_pair_unchanged_changed',
+    assert delta('/file_pair_unchanged_changed',
             't', 'changed', 1000, 2000) in changed
     # File with two symbols, one unchanged, one removed, same bucket
-    assert ('/file_pair_unchanged_removed',
+    assert delta('/file_pair_unchanged_removed',
             't', 'unchanged', 1000, 1000) in unchanged
-    assert ('/file_pair_unchanged_removed',
+    assert delta('/file_pair_unchanged_removed',
             't', 'removed', 1000, None) in removed
     # File with two symbols, one unchanged, one added, same bucket
-    assert ('/file_pair_unchanged_added',
+    assert delta('/file_pair_unchanged_added',
             't', 'unchanged', 1000, 1000) in unchanged
-    assert ('/file_pair_unchanged_added',
+    assert delta('/file_pair_unchanged_added',
             't', 'added', None, 1000) in added
     # File with two symbols, one unchanged, one changed, different bucket
-    assert ('/file_pair_unchanged_diffbuck_changed',
+    assert delta('/file_pair_unchanged_diffbuck_changed',
             't', 'unchanged', 1000, 1000) in unchanged
-    assert ('/file_pair_unchanged_diffbuck_changed',
+    assert delta('/file_pair_unchanged_diffbuck_changed',
             '@', 'changed', 1000, 2000) in changed
     # File with two symbols, one unchanged, one removed, different bucket
-    assert ('/file_pair_unchanged_diffbuck_removed',
+    assert delta('/file_pair_unchanged_diffbuck_removed',
             't', 'unchanged', 1000, 1000) in unchanged
-    assert ('/file_pair_unchanged_diffbuck_removed',
+    assert delta('/file_pair_unchanged_diffbuck_removed',
             '@', 'removed', 1000, None) in removed
     # File with two symbols, one unchanged, one added, different bucket
-    assert ('/file_pair_unchanged_diffbuck_added',
+    assert delta('/file_pair_unchanged_diffbuck_added',
             't', 'unchanged', 1000, 1000) in unchanged
-    assert ('/file_pair_unchanged_diffbuck_added',
+    assert delta('/file_pair_unchanged_diffbuck_added',
             '@', 'added', None, 1000) in added
     # File with four symbols, one added, one removed, one changed, one unchanged
-    assert ('/file_tetra', 't', 'size_changed', 1000, 2000) in changed
-    assert ('/file_tetra', 't', 'unchanged', 1000, 1000) in unchanged
-    assert ('/file_tetra', 't', 'added', None, 1000) in added
-    assert ('/file_tetra', 't', 'removed', 1000, None) in removed
+    assert delta('/file_tetra', 't', 'size_changed', 1000, 2000) in changed
+    assert delta('/file_tetra', 't', 'unchanged', 1000, 1000) in unchanged
+    assert delta('/file_tetra', 't', 'added', None, 1000) in added
+    assert delta('/file_tetra', 't', 'removed', 1000, None) in removed
 
     # Now check final stats.
     orig_stdout = sys.stdout
@@ -218,20 +226,20 @@
 
     self.maxDiff = None
     self.assertMultiLineEqual(expected_output, result)
-    print "explain_binary_size_delta_unittest: All tests passed"
 
 
   def testCompareStringEntries(self):
-    # List entries have form: symbol_name, symbol_type, symbol_size, file_path
+    # List entries have form:
+    # symbol_name, symbol_type, symbol_size, file_path, memory_address
     symbol_list1 = (
       # File with one string.
-      ( '.L.str107', 'r', 8, '/file_with_strs' ),
+      ( '.L.str107', 'r', 8, '/file_with_strs', 0x1 ),
     )
 
     symbol_list2 = (
       # Two files with one string each, same name.
-      ( '.L.str107', 'r', 8, '/file_with_strs' ),
-      ( '.L.str107', 'r', 7, '/other_file_with_strs' ),
+      ( '.L.str107', 'r', 8, '/file_with_strs', 0x1 ),
+      ( '.L.str107', 'r', 7, '/other_file_with_strs', 0x2 ),
     )
 
     # Here we go
@@ -272,29 +280,29 @@
 
     self.maxDiff = None
     self.assertMultiLineEqual(expected_output, result)
-    print "explain_binary_size_delta_unittest: All tests passed"
 
   def testCompareStringEntriesWithNoFile(self):
-    # List entries have form: symbol_name, symbol_type, symbol_size, file_path
+    # List entries have form:
+    # symbol_name, symbol_type, symbol_size, file_path, memory_address
     symbol_list1 = (
-      ( '.L.str104', 'r', 21, '??' ), # Will change size.
-      ( '.L.str105', 'r', 17, '??' ), # Same.
-      ( '.L.str106', 'r', 13, '??' ), # Will be removed.
-      ( '.L.str106', 'r', 3, '??' ), # Same.
-      ( '.L.str106', 'r', 3, '??' ), # Will be removed.
-      ( '.L.str107', 'r', 8, '??' ), # Will be removed (other sizes).
+      ( '.L.str104', 'r', 21, '??', 0x1 ), # Will change size.
+      ( '.L.str105', 'r', 17, '??', 0x2 ), # Same.
+      ( '.L.str106', 'r', 13, '??', 0x3 ), # Will be removed.
+      ( '.L.str106', 'r', 3, '??', 0x4 ), # Same.
+      ( '.L.str106', 'r', 3, '??', 0x5 ), # Will be removed.
+      ( '.L.str107', 'r', 8, '??', 0x6 ), # Will be removed (other sizes).
     )
 
     symbol_list2 = (
       # Two files with one string each, same name.
-      ( '.L.str104', 'r', 19, '??' ), # Changed.
-      ( '.L.str105', 'r', 11, '??' ), # New size for multi-symbol.
-      ( '.L.str105', 'r', 17, '??' ), # New of same size for multi-symbol.
-      ( '.L.str105', 'r', 17, '??' ), # Same.
-      ( '.L.str106', 'r', 3, '??' ), # Same.
-      ( '.L.str107', 'r', 5, '??' ), # New size for symbol.
-      ( '.L.str107', 'r', 7, '??' ), # New size for symbol.
-      ( '.L.str108', 'r', 8, '??' ), # New symbol.
+      ( '.L.str104', 'r', 19, '??', 0x1 ), # Changed.
+      ( '.L.str105', 'r', 11, '??', 0x2 ), # New size for multi-symbol.
+      ( '.L.str105', 'r', 17, '??', 0x3 ), # New of same size for multi-symbol.
+      ( '.L.str105', 'r', 17, '??', 0x4 ), # Same.
+      ( '.L.str106', 'r', 3, '??', 0x5 ), # Same.
+      ( '.L.str107', 'r', 5, '??', 0x6 ), # New size for symbol.
+      ( '.L.str107', 'r', 7, '??', 0x7 ), # New size for symbol.
+      ( '.L.str108', 'r', 8, '??', 0x8 ), # New symbol.
     )
 
     # Here we go
@@ -348,7 +356,266 @@
 
     self.maxDiff = None
     self.assertMultiLineEqual(expected_output, result)
-    print "explain_binary_size_delta_unittest: All tests passed"
+
+  def testCompareSharedSpace(self):
+    # List entries have form:
+    # symbol_name, symbol_type, symbol_size, file_path, memory_address
+    symbol_list1 = (
+      # File with two symbols, same address.
+      ( 'sym1', 'r', 8, '/file', 0x1 ),
+      ( 'sym2', 'r', 8, '/file', 0x1 ),
+    )
+
+    symbol_list2 = (
+      # File with two symbols, same address.
+      ( 'sym1', 'r', 4, '/file', 0x1 ),
+      ( 'sym2', 'r', 4, '/file', 0x1 ),
+    )
+
+    # Here we go
+    (added, removed, changed, unchanged) = \
+        explain_binary_size_delta.Compare(symbol_list1, symbol_list2)
+
+
+    # Now check final stats.
+    orig_stdout = sys.stdout
+    output_collector = cStringIO.StringIO()
+    sys.stdout = output_collector
+    try:
+      explain_binary_size_delta.CrunchStats(added, removed, changed,
+                                            unchanged, True, True)
+    finally:
+      sys.stdout = orig_stdout
+    result = output_collector.getvalue()
+
+    expected_output = """\
+Total change: -4 bytes
+======================
+  2 shrunk, for a net change of -4 bytes (8 bytes before, 4 bytes after) \
+across 1 sources
+  0 unchanged, totalling 0 bytes
+Source stats:
+  1 sources encountered.
+  0 completely new.
+  0 removed completely.
+  1 partially changed.
+  0 completely unchanged.
+Per-source Analysis:
+
+----------------------------------------
+ -4 - Source: /file - (gained 0, lost 4)
+----------------------------------------
+  Shrunk symbols:
+         -2: sym1 type=r, (was 4 bytes, now 2 bytes) (adjusted sizes because \
+of memory sharing)
+         -2: sym2 type=r, (was 4 bytes, now 2 bytes) (adjusted sizes because \
+of memory sharing)
+"""
+
+    self.maxDiff = None
+    self.assertMultiLineEqual(expected_output, result)
+
+
+  def testCompareSharedSpaceDuplicateSymbols(self):
+    # List entries have form:
+    # symbol_name, symbol_type, symbol_size, file_path, memory_address
+    symbol_list1 = (
+      # File with two symbols, same address.
+      ( 'sym1', 'r', 7, '/file', 0x2 ),
+      ( 'sym1', 'r', 8, '/file', 0x1 ),
+      ( 'sym2', 'r', 8, '/file', 0x1 ),
+    )
+
+    symbol_list2 = (
+      # File with two symbols, same address.
+      ( 'sym1', 'r', 7, '/file', 0x2 ),
+      ( 'sym1', 'r', 4, '/file', 0x1 ),
+      ( 'sym2', 'r', 4, '/file', 0x1 ),
+    )
+
+    # Here we go
+    (added, removed, changed, unchanged) = \
+        explain_binary_size_delta.Compare(symbol_list1, symbol_list2)
+
+
+    # Now check final stats.
+    orig_stdout = sys.stdout
+    output_collector = cStringIO.StringIO()
+    sys.stdout = output_collector
+    try:
+      explain_binary_size_delta.CrunchStats(added, removed, changed,
+                                            unchanged, True, True)
+    finally:
+      sys.stdout = orig_stdout
+    result = output_collector.getvalue()
+
+    expected_output = """\
+Total change: -4 bytes
+======================
+  1 added, totalling +2 bytes across 1 sources
+  1 removed, totalling -4 bytes across 1 sources
+  1 shrunk, for a net change of -2 bytes (4 bytes before, 2 bytes after) \
+across 1 sources
+  1 unchanged, totalling 7 bytes
+Source stats:
+  1 sources encountered.
+  0 completely new.
+  0 removed completely.
+  1 partially changed.
+  0 completely unchanged.
+Per-source Analysis:
+
+----------------------------------------
+ -4 - Source: /file - (gained 2, lost 6)
+----------------------------------------
+  New symbols:
+         +2: sym1 type=r, size=2 bytes (adjusted sizes because of memory \
+sharing)
+  Removed symbols:
+         -4: sym1 type=r, size=4 bytes (adjusted sizes because of memory \
+sharing)
+  Shrunk symbols:
+         -2: sym2 type=r, (was 4 bytes, now 2 bytes) (adjusted sizes because \
+of memory sharing)
+"""
+
+    self.maxDiff = None
+    self.assertMultiLineEqual(expected_output, result)
+
+  def testCompareSharedSpaceBecomingUnshared(self):
+    # List entries have form:
+    # symbol_name, symbol_type, symbol_size, file_path, memory_address
+    symbol_list1 = (
+      # File with two symbols, same address.
+      ( 'sym1', 'r', 8, '/file', 0x1 ),
+      ( 'sym2', 'r', 8, '/file', 0x1 ),
+    )
+
+    symbol_list2 = (
+      # File with two symbols, not the same address.
+      ( 'sym1', 'r', 8, '/file', 0x1 ),
+      ( 'sym2', 'r', 6, '/file', 0x2 ),
+    )
+
+    # Here we go
+    (added, removed, changed, unchanged) = \
+        explain_binary_size_delta.Compare(symbol_list1, symbol_list2)
+
+
+    # Now check final stats.
+    orig_stdout = sys.stdout
+    output_collector = cStringIO.StringIO()
+    sys.stdout = output_collector
+    try:
+      explain_binary_size_delta.CrunchStats(added, removed, changed,
+                                            unchanged, True, True)
+    finally:
+      sys.stdout = orig_stdout
+    result = output_collector.getvalue()
+
+    expected_output = """\
+Total change: +6 bytes
+======================
+  2 grown, for a net change of +6 bytes (8 bytes before, 14 bytes after) \
+across 1 sources
+  0 unchanged, totalling 0 bytes
+Source stats:
+  1 sources encountered.
+  0 completely new.
+  0 removed completely.
+  1 partially changed.
+  0 completely unchanged.
+Per-source Analysis:
+
+----------------------------------------
+ +6 - Source: /file - (gained 6, lost 0)
+----------------------------------------
+  Grown symbols:
+         +4: sym1 type=r, (was 4 bytes, now 8 bytes) (adjusted sizes because \
+of memory sharing)
+         +2: sym2 type=r, (was 4 bytes, now 6 bytes) (adjusted sizes because \
+of memory sharing)
+"""
+
+    self.maxDiff = None
+    self.assertMultiLineEqual(expected_output, result)
+
+  def testCompareSymbolsBecomingUnshared(self):
+    # List entries have form:
+    # symbol_name, symbol_type, symbol_size, file_path, memory_address
+    symbol_list1 = (
+      # File with two symbols, not the same address.
+      ( 'sym1', 'r', 8, '/file', 0x1 ),
+      ( 'sym2', 'r', 6, '/file', 0x2 ),
+    )
+
+    symbol_list2 = (
+      # File with two symbols, same address.
+      ( 'sym1', 'r', 8, '/file', 0x1 ),
+      ( 'sym2', 'r', 8, '/file', 0x1 ),
+    )
+
+    # Here we go
+    (added, removed, changed, unchanged) = \
+        explain_binary_size_delta.Compare(symbol_list1, symbol_list2)
+
+
+    # Now check final stats.
+    orig_stdout = sys.stdout
+    output_collector = cStringIO.StringIO()
+    sys.stdout = output_collector
+    try:
+      explain_binary_size_delta.CrunchStats(added, removed, changed,
+                                            unchanged, True, True)
+    finally:
+      sys.stdout = orig_stdout
+    result = output_collector.getvalue()
+
+    expected_output = """\
+Total change: -6 bytes
+======================
+  2 shrunk, for a net change of -6 bytes (14 bytes before, 8 bytes after) \
+across 1 sources
+  0 unchanged, totalling 0 bytes
+Source stats:
+  1 sources encountered.
+  0 completely new.
+  0 removed completely.
+  1 partially changed.
+  0 completely unchanged.
+Per-source Analysis:
+
+----------------------------------------
+ -6 - Source: /file - (gained 0, lost 6)
+----------------------------------------
+  Shrunk symbols:
+         -2: sym2 type=r, (was 6 bytes, now 4 bytes) (adjusted sizes because \
+of memory sharing)
+         -4: sym1 type=r, (was 8 bytes, now 4 bytes) (adjusted sizes because \
+of memory sharing)
+"""
+
+    self.maxDiff = None
+    self.assertMultiLineEqual(expected_output, result)
+
+  def testDeltaInfo(self):
+    x = explain_binary_size_delta.DeltaInfo("path", "t", "sym_name", False)
+    assert x == x
+    y = explain_binary_size_delta.DeltaInfo("path", "t", "sym_name", False)
+    assert x == y
+
+    y.new_size = 12
+    assert x != y
+
+    x.new_size = 12
+    assert x == y
+
+    z = explain_binary_size_delta.DeltaInfo("path", "t", "sym_name", True)
+    assert not (x == z)
+    assert x != z
+
+    w = explain_binary_size_delta.DeltaInfo("other_path", "t", "sym_name", True)
+    assert w != z
 
 if __name__ == '__main__':
   unittest.main()
diff --git a/tools/binary_size/run_binary_size_analysis.py b/tools/binary_size/run_binary_size_analysis.py
index 68982528..241fa64 100755
--- a/tools/binary_size/run_binary_size_analysis.py
+++ b/tools/binary_size/run_binary_size_analysis.py
@@ -169,7 +169,7 @@
             NODE_MAX_DEPTH_KEY: 0}
   seen_symbol_with_path = False
   cwd = os.path.abspath(os.getcwd())
-  for symbol_name, symbol_type, symbol_size, file_path in symbols:
+  for symbol_name, symbol_type, symbol_size, file_path, _address in symbols:
 
     if 'vtable for ' in symbol_name:
       symbol_type = '@'  # hack to categorize these separately
@@ -231,7 +231,7 @@
 
 def MakeSourceMap(symbols):
   sources = {}
-  for _sym, _symbol_type, size, path in symbols:
+  for _sym, _symbol_type, size, path, _address in symbols:
     key = None
     if path:
       key = os.path.normpath(path)
diff --git a/tools/clang/blink_gc_plugin/BlinkGCPlugin.cpp b/tools/clang/blink_gc_plugin/BlinkGCPlugin.cpp
index 473ad20..7dae381 100644
--- a/tools/clang/blink_gc_plugin/BlinkGCPlugin.cpp
+++ b/tools/clang/blink_gc_plugin/BlinkGCPlugin.cpp
@@ -470,12 +470,17 @@
     if (!fn || !Config::IsTraceMethod(fn, nullptr))
       return false;
 
-    // Currently, a manually dispatched class cannot have mixin bases (having
-    // one would add a vtable which we explicitly check against). This means
-    // that we can only make calls to a trace method of the same name. Revisit
-    // this if our mixin/vtable assumption changes.
-    if (fn->getName() != trace_->getName())
-      return false;
+    if (trace_->getName() == kTraceImplName) {
+      if (fn->getName() != kTraceName)
+        return false;
+    } else {
+      // Currently, a manually dispatched class cannot have mixin bases (having
+      // one would add a vtable which we explicitly check against). This means
+      // that we can only make calls to a trace method of the same name. Revisit
+      // this if our mixin/vtable assumption changes.
+      if (fn->getName() != trace_->getName())
+        return false;
+    }
 
     CXXRecordDecl* decl = 0;
     if (callee && callee->hasQualifier()) {
diff --git a/tools/clang/blink_gc_plugin/Config.h b/tools/clang/blink_gc_plugin/Config.h
index d508fda..814a4ac 100644
--- a/tools/clang/blink_gc_plugin/Config.h
+++ b/tools/clang/blink_gc_plugin/Config.h
@@ -195,7 +195,7 @@
                        formal_type.getTypePtr())) {
       if (parm_type->getDecl()->getName() == kVisitorDispatcherName) {
         // Unresolved, but its parameter name is VisitorDispatcher.
-        return false;
+        return true;
       }
     }
 
diff --git a/tools/clang/blink_gc_plugin/tests/traceimpl.cpp b/tools/clang/blink_gc_plugin/tests/traceimpl.cpp
index 61d8560ea..c8849cc 100644
--- a/tools/clang/blink_gc_plugin/tests/traceimpl.cpp
+++ b/tools/clang/blink_gc_plugin/tests/traceimpl.cpp
@@ -12,6 +12,17 @@
 
 template <typename VisitorDispatcher>
 inline void TraceImplExtern::traceImpl(VisitorDispatcher visitor) {
-  visitor->trace(m_x);
+  visitor->trace(x_);
 }
+
+void TraceImplBaseExtern::trace(Visitor* visitor) {
+  traceImpl(visitor);
+}
+
+template <typename VisitorDispatcher>
+inline void TraceImplBaseExtern::traceImpl(VisitorDispatcher visitor) {
+  visitor->trace(x_);
+  Base::trace(visitor);
+}
+
 }
diff --git a/tools/clang/blink_gc_plugin/tests/traceimpl.h b/tools/clang/blink_gc_plugin/tests/traceimpl.h
index 26f4615..64fae26 100644
--- a/tools/clang/blink_gc_plugin/tests/traceimpl.h
+++ b/tools/clang/blink_gc_plugin/tests/traceimpl.h
@@ -20,11 +20,11 @@
 
   template <typename VisitorDispatcher>
   void traceImpl(VisitorDispatcher visitor) {
-    visitor->trace(m_x);
+    visitor->trace(x_);
   }
 
  private:
-  Member<X> m_x;
+  Member<X> x_;
 };
 
 class TraceImplExtern : public GarbageCollected<TraceImplExtern> {
@@ -34,8 +34,35 @@
   inline void traceImpl(VisitorDispatcher);
 
  private:
-  Member<X> m_x;
+  Member<X> x_;
 };
+
+class Base : public GarbageCollected<Base> {
+ public:
+  virtual void trace(Visitor* visitor) {}
+};
+
+class TraceImplBaseInlined : public Base {
+ public:
+  void trace(Visitor* visitor) override { traceImpl(visitor); }
+
+  template <typename VisitorDispatcher>
+  void traceImpl(VisitorDispatcher visitor) {
+    Base::trace(visitor);
+  }
+};
+
+class TraceImplBaseExtern : public Base {
+ public:
+  void trace(Visitor* visitor) override;
+
+  template <typename VisitorDispatcher>
+  void traceImpl(VisitorDispatcher);
+
+ private:
+  Member<X> x_;
+};
+
 }
 
 #endif
diff --git a/tools/clang/blink_gc_plugin/tests/traceimpl_error.cpp b/tools/clang/blink_gc_plugin/tests/traceimpl_error.cpp
new file mode 100644
index 0000000..041c565e
--- /dev/null
+++ b/tools/clang/blink_gc_plugin/tests/traceimpl_error.cpp
@@ -0,0 +1,29 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "traceimpl_error.h"
+
+namespace blink {
+
+void TraceImplExternWithUntracedMember::trace(Visitor* visitor) {
+  traceImpl(visitor);
+}
+
+template <typename VisitorDispatcher>
+inline void TraceImplExternWithUntracedMember::traceImpl(
+    VisitorDispatcher visitor) {
+  // Should get a warning as well.
+}
+
+void TraceImplExternWithUntracedBase::trace(Visitor* visitor) {
+  traceImpl(visitor);
+}
+
+template <typename VisitorDispatcher>
+inline void TraceImplExternWithUntracedBase::traceImpl(
+    VisitorDispatcher visitor) {
+  // Ditto.
+}
+
+}
diff --git a/tools/clang/blink_gc_plugin/tests/traceimpl_error.h b/tools/clang/blink_gc_plugin/tests/traceimpl_error.h
new file mode 100644
index 0000000..5a883b4e
--- /dev/null
+++ b/tools/clang/blink_gc_plugin/tests/traceimpl_error.h
@@ -0,0 +1,68 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef TRACEIMPL_ERROR_H_
+#define TRACEIMPL_ERROR_H_
+
+#include "heap/stubs.h"
+
+namespace blink {
+
+class X : public GarbageCollected<X> {
+ public:
+  virtual void trace(Visitor*) {}
+};
+
+class TraceImplInlinedWithUntracedMember
+    : public GarbageCollected<TraceImplInlinedWithUntracedMember> {
+ public:
+  void trace(Visitor* visitor) { traceImpl(visitor); }
+
+  template <typename VisitorDispatcher>
+  void traceImpl(VisitorDispatcher visitor) {
+    // Empty; should get complaints from the plugin for untraced x_.
+  }
+
+ private:
+  Member<X> x_;
+};
+
+class TraceImplExternWithUntracedMember
+    : public GarbageCollected<TraceImplExternWithUntracedMember> {
+ public:
+  void trace(Visitor* visitor);
+
+  template <typename VisitorDispatcher>
+  inline void traceImpl(VisitorDispatcher);
+
+ private:
+  Member<X> x_;
+};
+
+class Base : public GarbageCollected<Base> {
+ public:
+  virtual void trace(Visitor*) {}
+};
+
+class TraceImplInlineWithUntracedBase : public Base {
+ public:
+  void trace(Visitor* visitor) override { traceImpl(visitor); }
+
+  template <typename VisitorDispatcher>
+  void traceImpl(VisitorDispatcher visitor) {
+    // Empty; should get complaints from the plugin for untraced Base.
+  }
+};
+
+class TraceImplExternWithUntracedBase : public Base {
+ public:
+  void trace(Visitor*) override;
+
+  template <typename VisitorDispatcher>
+  void traceImpl(VisitorDispatcher visitor);
+};
+
+}
+
+#endif  // TRACEIMPL_ERROR_H_
diff --git a/tools/clang/blink_gc_plugin/tests/traceimpl_error.txt b/tools/clang/blink_gc_plugin/tests/traceimpl_error.txt
new file mode 100644
index 0000000..070b0296
--- /dev/null
+++ b/tools/clang/blink_gc_plugin/tests/traceimpl_error.txt
@@ -0,0 +1,20 @@
+In file included from traceimpl_error.cpp:5:
+./traceimpl_error.h:23:3: warning: [blink-gc] Class 'TraceImplInlinedWithUntracedMember' has untraced fields that require tracing.
+  void traceImpl(VisitorDispatcher visitor) {
+  ^
+./traceimpl_error.h:28:3: note: [blink-gc] Untraced field 'x_' declared here:
+  Member<X> x_;
+  ^
+./traceimpl_error.h:53:3: warning: [blink-gc] Base class 'Base' of derived class 'TraceImplInlineWithUntracedBase' requires tracing.
+  void traceImpl(VisitorDispatcher visitor) {
+  ^
+traceimpl_error.cpp:14:1: warning: [blink-gc] Class 'TraceImplExternWithUntracedMember' has untraced fields that require tracing.
+inline void TraceImplExternWithUntracedMember::traceImpl(
+^
+./traceimpl_error.h:40:3: note: [blink-gc] Untraced field 'x_' declared here:
+  Member<X> x_;
+  ^
+traceimpl_error.cpp:24:1: warning: [blink-gc] Base class 'Base' of derived class 'TraceImplExternWithUntracedBase' requires tracing.
+inline void TraceImplExternWithUntracedBase::traceImpl(
+^
+4 warnings generated.
diff --git a/tools/clang/plugins/FindBadConstructsAction.cpp b/tools/clang/plugins/FindBadConstructsAction.cpp
index f302fd1..6c39d72 100644
--- a/tools/clang/plugins/FindBadConstructsAction.cpp
+++ b/tools/clang/plugins/FindBadConstructsAction.cpp
@@ -53,8 +53,6 @@
       // TODO(tsepez): Enable this by default once http://crbug.com/356815
       // and http://crbug.com/356816 are fixed.
       options_.check_enum_last_value = true;
-    } else if (args[i] == "strict-virtual-specifiers") {
-      options_.strict_virtual_specifiers = true;
     } else if (args[i] == "with-ast-visitor") {
       options_.with_ast_visitor = true;
     } else if (args[i] == "check-templates") {
diff --git a/tools/clang/plugins/FindBadConstructsConsumer.cpp b/tools/clang/plugins/FindBadConstructsConsumer.cpp
index 46e1df8..fe868c01 100644
--- a/tools/clang/plugins/FindBadConstructsConsumer.cpp
+++ b/tools/clang/plugins/FindBadConstructsConsumer.cpp
@@ -283,6 +283,9 @@
       for (CXXRecordDecl::ctor_iterator it = record->ctor_begin();
            it != record->ctor_end();
            ++it) {
+        // The current check is buggy. An implicit copy constructor does not
+        // have an inline body, so this check never fires for classes with a
+        // user-declared out-of-line constructor.
         if (it->hasInlineBody()) {
           if (it->isCopyConstructor() &&
               !record->hasUserDeclaredCopyConstructor()) {
@@ -293,6 +296,15 @@
             emitWarning(it->getInnerLocStart(),
                         "Complex constructor has an inlined body.");
           }
+        } else if (it->isInlined() && (!it->isCopyOrMoveConstructor() ||
+                                       it->isExplicitlyDefaulted())) {
+          // isInlined() is a more reliable check than hasInlineBody(), but
+          // unfortunately, it results in warnings for implicit copy/move
+          // constructors in the previously mentioned situation. To preserve
+          // compatibility with existing Chromium code, only warn if it's an
+          // explicitly defaulted copy or move constructor.
+          emitWarning(it->getInnerLocStart(),
+                      "Complex constructor has an inlined body.");
         }
       }
     }
@@ -306,7 +318,7 @@
                   "Complex class/struct needs an explicit out-of-line "
                   "destructor.");
     } else if (CXXDestructorDecl* dtor = record->getDestructor()) {
-      if (dtor->hasInlineBody()) {
+      if (dtor->isInlined()) {
         emitWarning(dtor->getInnerLocStart(),
                     "Complex destructor has an inline body.");
       }
@@ -331,8 +343,7 @@
         // magic to try to make sure SetUp()/TearDown() aren't capitalized
         // incorrectly, but having the plugin enforce override is also nice.
         (InTestingNamespace(overridden) &&
-         (!options_.strict_virtual_specifiers ||
-          !IsGtestTestFixture(overridden->getParent())))) {
+         !IsGtestTestFixture(overridden->getParent()))) {
       return true;
     }
   }
@@ -393,20 +404,13 @@
   OverrideAttr* override_attr = method->getAttr<OverrideAttr>();
   FinalAttr* final_attr = method->getAttr<FinalAttr>();
 
-  if (method->isPure() && !options_.strict_virtual_specifiers)
-    return;
-
   if (IsMethodInBannedOrTestingNamespace(method))
     return;
 
-  if (isa<CXXDestructorDecl>(method) && !options_.strict_virtual_specifiers)
-    return;
-
   SourceManager& manager = instance().getSourceManager();
 
   // Complain if a method is annotated virtual && (override || final).
-  if (has_virtual && (override_attr || final_attr) &&
-      options_.strict_virtual_specifiers) {
+  if (has_virtual && (override_attr || final_attr)) {
     diagnostic().Report(method->getLocStart(),
                         diag_redundant_virtual_specifier_)
         << "'virtual'"
@@ -436,14 +440,14 @@
     }
   }
 
-  if (final_attr && override_attr && options_.strict_virtual_specifiers) {
+  if (final_attr && override_attr) {
     diagnostic().Report(override_attr->getLocation(),
                         diag_redundant_virtual_specifier_)
         << override_attr << final_attr
         << FixItHint::CreateRemoval(override_attr->getRange());
   }
 
-  if (final_attr && !is_override && options_.strict_virtual_specifiers) {
+  if (final_attr && !is_override) {
     diagnostic().Report(method->getLocStart(),
                         diag_base_method_virtual_and_final_)
         << FixItRemovalForVirtual(manager, method)
diff --git a/tools/clang/plugins/Options.h b/tools/clang/plugins/Options.h
index 5ef4977..112ec10 100644
--- a/tools/clang/plugins/Options.h
+++ b/tools/clang/plugins/Options.h
@@ -11,13 +11,11 @@
   Options()
       : check_base_classes(false),
         check_enum_last_value(false),
-        strict_virtual_specifiers(false),
         with_ast_visitor(false),
         check_templates(false) {}
 
   bool check_base_classes;
   bool check_enum_last_value;
-  bool strict_virtual_specifiers;
   bool with_ast_visitor;
   bool check_templates;
 };
diff --git a/tools/clang/plugins/tests/base_refcounted.cpp b/tools/clang/plugins/tests/base_refcounted.cpp
index 698bf7b..46e8975c 100644
--- a/tools/clang/plugins/tests/base_refcounted.cpp
+++ b/tools/clang/plugins/tests/base_refcounted.cpp
@@ -13,7 +13,7 @@
     : public ProtectedRefCountedVirtualDtorInHeader {
  public:
   AnonymousDerivedProtectedToPublicInImpl() {}
-  virtual ~AnonymousDerivedProtectedToPublicInImpl() {}
+  ~AnonymousDerivedProtectedToPublicInImpl() override {}
 };
 
 // Unsafe; but we should only warn on the base class.
diff --git a/tools/clang/plugins/tests/base_refcounted.h b/tools/clang/plugins/tests/base_refcounted.h
index 4b4077c..4a489ee4 100644
--- a/tools/clang/plugins/tests/base_refcounted.h
+++ b/tools/clang/plugins/tests/base_refcounted.h
@@ -107,7 +107,7 @@
     : public ProtectedRefCountedVirtualDtorInHeader {
  public:
   DerivedProtectedToPublicInHeader() {}
-  virtual ~DerivedProtectedToPublicInHeader() {}
+  ~DerivedProtectedToPublicInHeader() override {}
 };
 
 // Unsafe; A grandchild ends up implicitly exposing their parent and
@@ -146,10 +146,10 @@
     : public APublicInterface,
       public base::RefCounted<ImplementsAPublicInterface> {
  public:
-  virtual void DoFoo() override {}
+  void DoFoo() override {}
 
  protected:
-  virtual ~ImplementsAPublicInterface() {}
+  ~ImplementsAPublicInterface() override {}
 
  private:
   friend class base::RefCounted<ImplementsAPublicInterface>;
@@ -165,7 +165,7 @@
     : public AnImplicitInterface,
       public base::RefCounted<ImplementsAnImplicitInterface> {
  public:
-  virtual void DoBar() override {}
+  void DoBar() override {}
 
  private:
   friend class base::RefCounted<ImplementsAnImplicitInterface>;
@@ -177,11 +177,11 @@
     : private APublicInterface,
       public base::RefCounted<PrivatelyImplementsAPublicInterface> {
  public:
-  virtual void DoFoo() override {}
+  void DoFoo() override {}
 
  private:
   friend class base::RefCounted<PrivatelyImplementsAPublicInterface>;
-  virtual ~PrivatelyImplementsAPublicInterface() {}
+  ~PrivatelyImplementsAPublicInterface() override {}
 };
 
 // Unsafe.
@@ -192,7 +192,7 @@
 };
 class DerivedInterface : public BaseInterface {
  protected:
-  virtual ~DerivedInterface() {}
+  ~DerivedInterface() override {}
 };
 class SomeOtherInterface {
  public:
@@ -211,13 +211,13 @@
       public RefcountedType {
  public:
   // DerivedInterface
-  virtual void DoFoo() override {}
+  void DoFoo() override {}
 
   // SomeOtherInterface
-  virtual void DoBar() override {}
+  void DoBar() override {}
 
  protected:
-  virtual ~UnsafeInheritanceChain() {}
+  ~UnsafeInheritanceChain() override {}
 };
 
 #endif  // BASE_REFCOUNTED_H_
diff --git a/tools/clang/plugins/tests/base_refcounted.txt b/tools/clang/plugins/tests/base_refcounted.txt
index 20c0cdf..ae91986 100644
--- a/tools/clang/plugins/tests/base_refcounted.txt
+++ b/tools/clang/plugins/tests/base_refcounted.txt
@@ -15,7 +15,7 @@
   ~ProtectedRefCountedDtorInHeader() {}
   ^
 ./base_refcounted.h:110:3: warning: [chromium-style] Classes that are ref-counted should have destructors that are declared protected or private.
-  virtual ~DerivedProtectedToPublicInHeader() {}
+  ~DerivedProtectedToPublicInHeader() override {}
   ^
 ./base_refcounted.h:107:7: note: [chromium-style] 'DerivedProtectedToPublicInHeader' inherits from 'ProtectedRefCountedVirtualDtorInHeader' here
     : public ProtectedRefCountedVirtualDtorInHeader {
@@ -61,7 +61,7 @@
   ^
 ./base_refcounted.h:204:3: warning: [chromium-style] Classes that are ref-counted and have non-private destructors should declare their destructor virtual.
 base_refcounted.cpp:16:3: warning: [chromium-style] Classes that are ref-counted should have destructors that are declared protected or private.
-  virtual ~AnonymousDerivedProtectedToPublicInImpl() {}
+  ~AnonymousDerivedProtectedToPublicInImpl() override {}
   ^
 base_refcounted.cpp:13:7: note: [chromium-style] 'AnonymousDerivedProtectedToPublicInImpl' inherits from 'ProtectedRefCountedVirtualDtorInHeader' here
     : public ProtectedRefCountedVirtualDtorInHeader {
diff --git a/tools/clang/plugins/tests/missing_ctor.h b/tools/clang/plugins/tests/missing_ctor.h
index 1050457..0551fd7 100644
--- a/tools/clang/plugins/tests/missing_ctor.h
+++ b/tools/clang/plugins/tests/missing_ctor.h
@@ -8,6 +8,8 @@
 #include <string>
 #include <vector>
 
+// Note: this should warn for an implicit copy constructor too, but currently
+// doesn't, due to a plugin bug.
 class MissingCtorsArentOKInHeader {
  public:
 
@@ -16,4 +18,33 @@
   std::vector<std::string> two_;
 };
 
+// Inline move ctors shouldn't be warned about. Similar to the previous test
+// case, this also incorrectly fails to warn for the implicit copy ctor.
+class InlineImplicitMoveCtorOK {
+ public:
+  InlineImplicitMoveCtorOK();
+
+ private:
+  // ctor weight = 12, dtor weight = 9.
+  std::string one_;
+  std::string two_;
+  std::string three_;
+  int four_;
+  int five_;
+  int six_;
+};
+
+class ExplicitlyDefaultedInlineAlsoWarns {
+ public:
+  ExplicitlyDefaultedInlineAlsoWarns() = default;
+  ~ExplicitlyDefaultedInlineAlsoWarns() = default;
+  ExplicitlyDefaultedInlineAlsoWarns(
+      const ExplicitlyDefaultedInlineAlsoWarns&) = default;
+
+ private:
+  std::vector<int> one_;
+  std::vector<std::string> two_;
+
+};
+
 #endif  // MISSING_CTOR_H_
diff --git a/tools/clang/plugins/tests/missing_ctor.txt b/tools/clang/plugins/tests/missing_ctor.txt
index 301449c4..0bc5696b 100644
--- a/tools/clang/plugins/tests/missing_ctor.txt
+++ b/tools/clang/plugins/tests/missing_ctor.txt
@@ -1,6 +1,15 @@
 In file included from missing_ctor.cpp:5:
-./missing_ctor.h:11:1: warning: [chromium-style] Complex class/struct needs an explicit out-of-line constructor.
+./missing_ctor.h:13:1: warning: [chromium-style] Complex class/struct needs an explicit out-of-line constructor.
 class MissingCtorsArentOKInHeader {
 ^
-./missing_ctor.h:11:1: warning: [chromium-style] Complex class/struct needs an explicit out-of-line destructor.
-2 warnings generated.
+./missing_ctor.h:13:1: warning: [chromium-style] Complex class/struct needs an explicit out-of-line destructor.
+./missing_ctor.h:39:3: warning: [chromium-style] Complex constructor has an inlined body.
+  ExplicitlyDefaultedInlineAlsoWarns() = default;
+  ^
+./missing_ctor.h:41:3: warning: [chromium-style] Complex constructor has an inlined body.
+  ExplicitlyDefaultedInlineAlsoWarns(
+  ^
+./missing_ctor.h:40:3: warning: [chromium-style] Complex destructor has an inline body.
+  ~ExplicitlyDefaultedInlineAlsoWarns() = default;
+  ^
+5 warnings generated.
diff --git a/tools/clang/plugins/tests/overridden_methods.cpp b/tools/clang/plugins/tests/overridden_methods.cpp
index 398d6a42..8b0340c1 100644
--- a/tools/clang/plugins/tests/overridden_methods.cpp
+++ b/tools/clang/plugins/tests/overridden_methods.cpp
@@ -11,19 +11,19 @@
 
 class ImplementationInterimClass : public BaseClass {
  public:
-  // Should not warn about pure virtual methods.
+  // Should warn about pure virtual methods.
   virtual void SomeMethod() = 0;
 };
 
 class ImplementationDerivedClass : public ImplementationInterimClass,
                                    public webkit_glue::WebKitObserverImpl {
  public:
-  // Should not warn about destructors.
+  // Should warn about destructors.
   virtual ~ImplementationDerivedClass() {}
   // Should warn.
   virtual void SomeMethod();
   // Should not warn if marked as override.
-  virtual void SomeOtherMethod() override;
+  void SomeOtherMethod() override;
   // Should not warn for inline implementations in implementation files.
   virtual void SomeInlineMethod() {}
   // Should not warn if overriding a method whose origin is blink.
diff --git a/tools/clang/plugins/tests/overridden_methods.h b/tools/clang/plugins/tests/overridden_methods.h
index c5af914..69ee100 100644
--- a/tools/clang/plugins/tests/overridden_methods.h
+++ b/tools/clang/plugins/tests/overridden_methods.h
@@ -21,7 +21,7 @@
 };
 
 class InterimClass : public BaseClass {
-  // Should not warn about pure virtual methods.
+  // Should warn about pure virtual methods.
   virtual void SomeMethod() = 0;
 };
 
@@ -42,12 +42,12 @@
 class DerivedClass : public InterimClass,
                      public webkit_glue::WebKitObserverImpl {
  public:
-  // Should not warn about destructors.
+  // Should warn about destructors.
   virtual ~DerivedClass() {}
   // Should warn.
   virtual void SomeMethod();
   // Should not warn if marked as override.
-  virtual void SomeOtherMethod() override;
+  void SomeOtherMethod() override;
   // Should warn for inline implementations.
   virtual void SomeInlineMethod() {}
   // Should not warn if overriding a method whose origin is blink.
diff --git a/tools/clang/plugins/tests/overridden_methods.txt b/tools/clang/plugins/tests/overridden_methods.txt
index 3ee0333..69ff2b1 100644
--- a/tools/clang/plugins/tests/overridden_methods.txt
+++ b/tools/clang/plugins/tests/overridden_methods.txt
@@ -1,4 +1,12 @@
 In file included from overridden_methods.cpp:5:
+./overridden_methods.h:25:28: warning: [chromium-style] Overriding method must be marked with 'override' or 'final'.
+  virtual void SomeMethod() = 0;
+                           ^
+                            override
+./overridden_methods.h:46:26: warning: [chromium-style] Overriding method must be marked with 'override' or 'final'.
+  virtual ~DerivedClass() {}
+                         ^
+                          override
 ./overridden_methods.h:48:28: warning: [chromium-style] Overriding method must be marked with 'override' or 'final'.
   virtual void SomeMethod();
                            ^
@@ -31,6 +39,14 @@
   virtual void SomeMethodWithCommentAndBody() {}  // This is a comment.
                                              ^
                                               override
+overridden_methods.cpp:15:28: warning: [chromium-style] Overriding method must be marked with 'override' or 'final'.
+  virtual void SomeMethod() = 0;
+                           ^
+                            override
+overridden_methods.cpp:22:40: warning: [chromium-style] Overriding method must be marked with 'override' or 'final'.
+  virtual ~ImplementationDerivedClass() {}
+                                       ^
+                                        override
 overridden_methods.cpp:24:28: warning: [chromium-style] Overriding method must be marked with 'override' or 'final'.
   virtual void SomeMethod();
                            ^
@@ -63,4 +79,4 @@
   virtual void SomeMethodWithCommentAndBody() {}  // This is a comment.
                                              ^
                                               override
-16 warnings generated.
+20 warnings generated.
diff --git a/tools/clang/plugins/tests/virtual_base_method_also_final.flags b/tools/clang/plugins/tests/virtual_base_method_also_final.flags
deleted file mode 100644
index a8915fc..0000000
--- a/tools/clang/plugins/tests/virtual_base_method_also_final.flags
+++ /dev/null
@@ -1 +0,0 @@
--Xclang -plugin-arg-find-bad-constructs -Xclang strict-virtual-specifiers
diff --git a/tools/clang/plugins/tests/virtual_bodies.cpp b/tools/clang/plugins/tests/virtual_bodies.cpp
index 8815dc2..4f37a67 100644
--- a/tools/clang/plugins/tests/virtual_bodies.cpp
+++ b/tools/clang/plugins/tests/virtual_bodies.cpp
@@ -16,13 +16,13 @@
 // Stubs to fill in the abstract method
 class ConcreteVirtualMethodsInHeaders : public VirtualMethodsInHeaders {
  public:
-  virtual void MethodIsAbstract() override {}
+  void MethodIsAbstract() override {}
 };
 
 class ConcreteVirtualMethodsInImplementation
     : public VirtualMethodsInImplementation {
  public:
-  virtual void MethodIsAbstract() override {}
+  void MethodIsAbstract() override {}
 };
 
 // Fill in the implementations
diff --git a/tools/clang/plugins/tests/virtual_specifiers.flags b/tools/clang/plugins/tests/virtual_specifiers.flags
deleted file mode 100644
index a8915fc..0000000
--- a/tools/clang/plugins/tests/virtual_specifiers.flags
+++ /dev/null
@@ -1 +0,0 @@
--Xclang -plugin-arg-find-bad-constructs -Xclang strict-virtual-specifiers
diff --git a/tools/clang/plugins/tests/weak_ptr_factory.flags b/tools/clang/plugins/tests/weak_ptr_factory.flags
index 7d9476e1..52b35b59 100644
--- a/tools/clang/plugins/tests/weak_ptr_factory.flags
+++ b/tools/clang/plugins/tests/weak_ptr_factory.flags
@@ -1 +1 @@
--Xclang -plugin-arg-find-bad-constructs -Xclang check-templates -Xclang -plugin-arg-find-bad-constructs -Xclang check-weak-ptr-factory-order
+-Xclang -plugin-arg-find-bad-constructs -Xclang check-templates
diff --git a/tools/clang/scripts/plugin_flags.sh b/tools/clang/scripts/plugin_flags.sh
index 76a82c5c..81fe0f1d 100755
--- a/tools/clang/scripts/plugin_flags.sh
+++ b/tools/clang/scripts/plugin_flags.sh
@@ -17,5 +17,6 @@
 fi
 
 echo -Xclang -load -Xclang $CLANG_LIB_PATH/libFindBadConstructs.$LIBSUFFIX \
-  -Xclang -add-plugin -Xclang find-bad-constructs -Xclang \
-  -plugin-arg-find-bad-constructs -Xclang check-weak-ptr-factory-order
+  -Xclang -add-plugin -Xclang find-bad-constructs \
+  -Xclang -plugin-arg-find-bad-constructs -Xclang check-weak-ptr-factory-order \
+  -Xclang -plugin-arg-find-bad-constructs -Xclang strict-virtual-specifiers
diff --git a/tools/clang/scripts/update.py b/tools/clang/scripts/update.py
index 6e55d0c0..cb0878d 100755
--- a/tools/clang/scripts/update.py
+++ b/tools/clang/scripts/update.py
@@ -24,7 +24,7 @@
 # in bringup. Use a pinned revision to make it slightly more stable.
 if (re.search(r'\b(asan)=1', os.environ.get('GYP_DEFINES', '')) and
     not 'LLVM_FORCE_HEAD_REVISION' in os.environ):
-  LLVM_WIN_REVISION = '228702'
+  LLVM_WIN_REVISION = '229860'
 
 # Path constants. (All of these should be absolute paths.)
 THIS_DIR = os.path.abspath(os.path.dirname(__file__))
diff --git a/tools/git/move_source_file.py b/tools/git/move_source_file.py
index 1a25e44..18ef1ce 100755
--- a/tools/git/move_source_file.py
+++ b/tools/git/move_source_file.py
@@ -31,6 +31,7 @@
   # classpath.
   sys.path.append(os.path.abspath(os.path.join(sys.path[0], '..')))
 sort_headers = __import__('sort-headers')
+import sort_sources
 
 
 HANDLED_EXTENSIONS = ['.cc', '.mm', '.h', '.hh', '.cpp']
@@ -142,11 +143,13 @@
   from_rest = from_path
   to_rest = to_path
   while True:
-    mffr.MultiFileFindReplace(
+    files_with_changed_sources = mffr.MultiFileFindReplace(
         r'([\'"])%s([\'"])' % from_rest,
         r'\1%s\2' % to_rest,
         [os.path.join(visiting_directory, 'BUILD.gn'),
          os.path.join(visiting_directory, '*.gyp*')])
+    for changed_file in files_with_changed_sources:
+      sort_sources.ProcessFile(changed_file, should_confirm=False)
     from_first, from_rest = SplitByFirstComponent(from_rest)
     to_first, to_rest = SplitByFirstComponent(to_rest)
     visiting_directory = os.path.join(visiting_directory, from_first)
diff --git a/tools/gn/args.cc b/tools/gn/args.cc
index c7c8a09..2a1509a4 100644
--- a/tools/gn/args.cc
+++ b/tools/gn/args.cc
@@ -18,10 +18,12 @@
     "\n"
     "  First, system default arguments are set based on the current system.\n"
     "  The built-in arguments are:\n"
-    "   - cpu_arch (by default this is the same as \"default_cpu_arch\")\n"
-    "   - default_cpu_arch\n"
-    "   - default_os\n"
-    "   - os (by default this is the same as \"default_os\")\n"
+    "   - host_cpu\n"
+    "   - host_os\n"
+    "   - current_cpu\n"
+    "   - current_os\n"
+    "   - target_cpu\n"
+    "   - target_os\n"
     "\n"
     "  If specified, arguments from the --args command line flag are used. If\n"
     "  that flag is not specified, args from previous builds in the build\n"
@@ -221,9 +223,6 @@
 #else
   #error Unknown OS type.
 #endif
-  Value os_val(nullptr, std::string(os));
-  dest->SetValue(variables::kBuildOs, os_val, nullptr);
-  dest->SetValue(variables::kOs, os_val, nullptr);
 
   // Host architecture.
   static const char kX86[] = "x86";
@@ -231,7 +230,7 @@
   static const char kArm[] = "arm";
   const char* arch = nullptr;
 
-  // Set the CPU architecture based on the underlying OS, not
+  // Set the host CPU architecture based on the underlying OS, not
   // whatever the current bit-tedness of the GN binary is.
   std::string os_arch = base::SysInfo::OperatingSystemArchitecture();
   if (os_arch == "x86")
@@ -243,22 +242,36 @@
   else
     CHECK(false) << "OS architecture not handled.";
 
-  Value arch_val(nullptr, std::string(arch));
-  dest->SetValue(variables::kBuildCpuArch, arch_val, nullptr);
-  dest->SetValue(variables::kCpuArch, arch_val, nullptr);
-
   // Save the OS and architecture as build arguments that are implicitly
   // declared. This is so they can be overridden in a toolchain build args
   // override, and so that they will appear in the "gn args" output.
-  //
-  // Do not declare the build* variants since these shouldn't be changed.
-  //
+  Value empty_string(nullptr, std::string());
+
+  Value os_val(nullptr, std::string(os));
+  dest->SetValue(variables::kHostOs, os_val, nullptr);
+  dest->SetValue(variables::kTargetOs, empty_string, nullptr);
+  dest->SetValue(variables::kCurrentOs, empty_string, nullptr);
+
+  Value arch_val(nullptr, std::string(arch));
+  dest->SetValue(variables::kHostCpu, arch_val, nullptr);
+  dest->SetValue(variables::kTargetCpu, empty_string, nullptr);
+  dest->SetValue(variables::kCurrentCpu, empty_string, nullptr);
+
+  declared_arguments_[variables::kHostOs] = os_val;
+  declared_arguments_[variables::kCurrentOs] = empty_string;
+  declared_arguments_[variables::kTargetOs] = empty_string;
+  declared_arguments_[variables::kHostCpu] = arch_val;
+  declared_arguments_[variables::kCurrentCpu] = empty_string;
+  declared_arguments_[variables::kTargetCpu] = empty_string;
+
   // Mark these variables used so the build config file can override them
   // without geting a warning about overwriting an unused variable.
-  declared_arguments_[variables::kOs] = os_val;
-  declared_arguments_[variables::kCpuArch] = arch_val;
-  dest->MarkUsed(variables::kCpuArch);
-  dest->MarkUsed(variables::kOs);
+  dest->MarkUsed(variables::kHostCpu);
+  dest->MarkUsed(variables::kCurrentCpu);
+  dest->MarkUsed(variables::kTargetCpu);
+  dest->MarkUsed(variables::kHostOs);
+  dest->MarkUsed(variables::kCurrentOs);
+  dest->MarkUsed(variables::kTargetOs);
 }
 
 void Args::ApplyOverridesLocked(const Scope::KeyValueMap& values,
diff --git a/tools/gn/command_args.cc b/tools/gn/command_args.cc
index 75d5fce..7210ef3 100644
--- a/tools/gn/command_args.cc
+++ b/tools/gn/command_args.cc
@@ -322,8 +322,9 @@
     "    Prints all arguments with their default values for the out/Debug\n"
     "    build.\n"
     "\n"
-    "  gn args out/Debug --list=cpu_arch\n"
-    "    Prints information about the \"cpu_arch\" argument for the out/Debug\n"
+    "  gn args out/Debug --list=target_cpu\n"
+    "    Prints information about the \"target_cpu\" argument for the "
+        "out/Debug\n"
     "    build.\n"
     "\n"
     "  gn args --list --args=\"os=\\\"android\\\" enable_doom_melon=true\"\n"
diff --git a/tools/gn/command_check.cc b/tools/gn/command_check.cc
index 5d30a0916..73660c9e 100644
--- a/tools/gn/command_check.cc
+++ b/tools/gn/command_check.cc
@@ -70,15 +70,24 @@
 
   bool filtered_by_build_config = false;
   std::vector<const Target*> targets_to_check;
-  if (args.size() == 2) {
-    // Compute the target to check.
-    if (!ResolveTargetsFromCommandLinePattern(setup, args[1], false,
-                                              &targets_to_check))
+  if (args.size() > 1) {
+    // Compute the targets to check.
+    std::vector<std::string> inputs(args.begin() + 1, args.end());
+    UniqueVector<const Target*> target_matches;
+    UniqueVector<const Config*> config_matches;
+    UniqueVector<const Toolchain*> toolchain_matches;
+    UniqueVector<SourceFile> file_matches;
+    if (!ResolveFromCommandLineInput(setup, inputs, false,
+                                     &target_matches, &config_matches,
+                                     &toolchain_matches, &file_matches))
       return 1;
-    if (targets_to_check.size() == 0) {
+
+    if (target_matches.size() == 0) {
       OutputString("No matching targets.\n");
       return 1;
     }
+    targets_to_check.insert(targets_to_check.begin(),
+                            target_matches.begin(), target_matches.end());
   } else {
     // No argument means to check everything allowed by the filter in
     // the build config file.
@@ -132,17 +141,4 @@
   return header_errors.empty();
 }
 
-void FilterTargetsByPatterns(const std::vector<const Target*>& input,
-                             const std::vector<LabelPattern>& filter,
-                             std::vector<const Target*>* output) {
-  for (const auto& target : input) {
-    for (const auto& pattern : filter) {
-      if (pattern.Matches(target->label())) {
-        output->push_back(target);
-        break;
-      }
-    }
-  }
-}
-
 }  // namespace commands
diff --git a/tools/gn/command_desc.cc b/tools/gn/command_desc.cc
index 5c2e2d4b..ecb2fad 100644
--- a/tools/gn/command_desc.cc
+++ b/tools/gn/command_desc.cc
@@ -40,17 +40,20 @@
   return dir.value();
 }
 
-void RecursiveCollectChildDeps(const Target* target, std::set<Label>* result);
+void RecursiveCollectChildDeps(const Target* target,
+                               std::set<const Target*>* result);
 
-void RecursiveCollectDeps(const Target* target, std::set<Label>* result) {
-  if (result->find(target->label()) != result->end())
+void RecursiveCollectDeps(const Target* target,
+                          std::set<const Target*>* result) {
+  if (result->find(target) != result->end())
     return;  // Already did this target.
-  result->insert(target->label());
+  result->insert(target);
 
   RecursiveCollectChildDeps(target, result);
 }
 
-void RecursiveCollectChildDeps(const Target* target, std::set<Label>* result) {
+void RecursiveCollectChildDeps(const Target* target,
+                               std::set<const Target*>* result) {
   for (const auto& pair : target->GetDeps(Target::DEPS_ALL))
     RecursiveCollectDeps(pair.ptr, result);
 }
@@ -122,17 +125,16 @@
   }
 
   // Collect the deps to display.
-  std::vector<Label> deps;
   if (cmdline->HasSwitch("all")) {
     // Show all dependencies.
     if (display_header)
       OutputString("\nAll recursive dependencies:\n");
 
-    std::set<Label> all_deps;
+    std::set<const Target*> all_deps;
     RecursiveCollectChildDeps(target, &all_deps);
-    for (const auto& dep : all_deps)
-      deps.push_back(dep);
+    FilterAndPrintTargetSet(display_header, all_deps);
   } else {
+    std::vector<const Target*> deps;
     // Show direct dependencies only.
     if (display_header) {
       OutputString(
@@ -140,12 +142,10 @@
           "(try also \"--all\", \"--tree\", or even \"--all --tree\"):\n");
     }
     for (const auto& pair : target->GetDeps(Target::DEPS_ALL))
-      deps.push_back(pair.label);
+      deps.push_back(pair.ptr);
+    std::sort(deps.begin(), deps.end());
+    FilterAndPrintTargets(display_header, &deps);
   }
-
-  std::sort(deps.begin(), deps.end());
-  for (const auto& dep : deps)
-    OutputString("  " + dep.GetUserVisibleName(toolchain_label) + "\n");
 }
 
 void PrintForwardDependentConfigsFrom(const Target* target,
@@ -449,14 +449,13 @@
 const char kDesc_HelpShort[] =
     "desc: Show lots of insightful information about a target.";
 const char kDesc_Help[] =
-    "gn desc <out_dir> <target label> [<what to show>]\n"
-    "        [--blame] [--all | --tree]\n"
+    "gn desc <out_dir> <target label> [<what to show>] [--blame]\n"
     "\n"
     "  Displays information about a given labeled target for the given build.\n"
     "  The build parameters will be taken for the build in the given\n"
     "  <out_dir>.\n"
     "\n"
-    "Possibilities for <what to show>:\n"
+    "Possibilities for <what to show>\n"
     "  (If unspecified an overall summary will be displayed.)\n"
     "\n"
     "  sources\n"
@@ -487,13 +486,9 @@
     "      via dependencies specifying \"all\" or \"direct\" dependent\n"
     "      configs.\n"
     "\n"
-    "  deps [--all | --tree]\n"
-    "      Show immediate (or, when \"--all\" or \"--tree\" is specified,\n"
-    "      recursive) dependencies of the given target. \"--tree\" shows them\n"
-    "      in a tree format with duplicates elided (noted by \"...\").\n"
-    "      \"--all\" shows them sorted alphabetically. Using both flags will\n"
-    "      print a tree with no omissions. The \"deps\", \"public_deps\", and\n"
-    "      \"data_deps\" will all be included.\n"
+    "  deps\n"
+    "      Show immediate or recursive dependencies. See below for flags that\n"
+    "      control deps printing.\n"
     "\n"
     "  public_configs\n"
     "  all_dependent_configs\n"
@@ -529,14 +524,39 @@
     "      for libs and lib_dirs because those are inherited and are more\n"
     "      complicated to figure out the blame (patches welcome).\n"
     "\n"
-    "Note:\n"
+    "Flags that control how deps are printed\n"
+    "\n"
+    "  --all\n"
+    "      Collects all recursive dependencies and prints a sorted flat list.\n"
+    "      Also usable with --tree (see below).\n"
+    "\n"
+    TARGET_PRINTING_MODE_COMMAND_LINE_HELP
+    "\n"
+    TARGET_TESTONLY_FILTER_COMMAND_LINE_HELP
+    "\n"
+    "  --tree\n"
+    "      Print a dependency tree. By default, duplicates will be elided\n"
+    "      with \"...\" but when --all and -tree are used together, no\n"
+    "      eliding will be performed.\n"
+    "\n"
+    "      The \"deps\", \"public_deps\", and \"data_deps\" will all be\n"
+    "      included in the tree.\n"
+    "\n"
+    "      Tree output can not be used with the filtering or output flags:\n"
+    "      --as, --type, --testonly.\n"
+    "\n"
+    TARGET_TYPE_FILTER_COMMAND_LINE_HELP
+    "\n"
+    "Note\n"
+    "\n"
     "  This command will show the full name of directories and source files,\n"
     "  but when directories and source paths are written to the build file,\n"
     "  they will be adjusted to be relative to the build directory. So the\n"
     "  values for paths displayed by this command won't match (but should\n"
     "  mean the same thing).\n"
     "\n"
-    "Examples:\n"
+    "Examples\n"
+    "\n"
     "  gn desc out/Debug //base:base\n"
     "      Summarizes the given target.\n"
     "\n"
diff --git a/tools/gn/command_ls.cc b/tools/gn/command_ls.cc
index a2dc38c..ed4aaca 100644
--- a/tools/gn/command_ls.cc
+++ b/tools/gn/command_ls.cc
@@ -18,9 +18,10 @@
 const char kLs_HelpShort[] =
     "ls: List matching targets.";
 const char kLs_Help[] =
-    "gn ls <out_dir> [<label_pattern>] [--out] [--all-toolchains]\n"
+    "gn ls <out_dir> [<label_pattern>] [--all-toolchains] [--as=...]\n"
+    "      [--type=...] [--testonly=...]\n"
     "\n"
-    "  Lists all targets matching the given pattern for the given builn\n"
+    "  Lists all targets matching the given pattern for the given build\n"
     "  directory. By default, only targets in the default toolchain will\n"
     "  be matched unless a toolchain is explicitly supplied.\n"
     "\n"
@@ -29,14 +30,19 @@
     "  \"gn help label_pattern\"). If you need more complex expressions,\n"
     "  pipe the result through grep.\n"
     "\n"
-    "  --out\n"
-    "      Lists the results as the files generated by the matching targets.\n"
-    "      These files will be relative to the build directory such that\n"
-    "      they can be specified on Ninja's command line as a file to build.\n"
+    "Options\n"
+    "\n"
+    TARGET_PRINTING_MODE_COMMAND_LINE_HELP
     "\n"
     "  --all-toolchains\n"
-    "      Matches all toolchains. If the label pattern does not specify an\n"
-    "      explicit toolchain, labels from all toolchains will be matched.\n"
+    "      Matches all toolchains. When set, if the label pattern does not\n"
+    "      specify an explicit toolchain, labels from all toolchains will be\n"
+    "      matched. When unset, only targets in the default toolchain will\n"
+    "      be matched unless an explicit toolchain in the label is set.\n"
+    "\n"
+    TARGET_TESTONLY_FILTER_COMMAND_LINE_HELP
+    "\n"
+    TARGET_TYPE_FILTER_COMMAND_LINE_HELP
     "\n"
     "Examples\n"
     "\n"
@@ -49,10 +55,13 @@
     "  gn ls out/Debug \"//base:*\"\n"
     "      Lists all targets defined in //base/BUILD.gn.\n"
     "\n"
-    "  gn ls out/Debug //base --out\n"
+    "  gn ls out/Debug //base --as=output\n"
     "      Lists the build output file for //base:base\n"
     "\n"
-    "  gn ls out/Debug \"//base/*\" --out | xargs ninja -C out/Debug\n"
+    "  gn ls out/Debug --type=executable\n"
+    "      Lists all executables produced by the build.\n"
+    "\n"
+    "  gn ls out/Debug \"//base/*\" --as=output | xargs ninja -C out/Debug\n"
     "      Builds all targets in //base and all subdirectories.\n"
     "\n"
     "  gn ls out/Debug //base --all-toolchains\n"
@@ -60,9 +69,9 @@
     "      in multiple toolchains).\n";
 
 int RunLs(const std::vector<std::string>& args) {
-  if (args.size() != 1 && args.size() != 2) {
+  if (args.size() == 0) {
     Err(Location(), "You're holding it wrong.",
-        "Usage: \"gn ls <build dir> [<label_pattern>]\"").PrintToStdout();
+        "Usage: \"gn ls <build dir> [<label_pattern>]*\"").PrintToStdout();
     return 1;
   }
 
@@ -73,13 +82,21 @@
   const base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
   bool all_toolchains = cmdline->HasSwitch("all-toolchains");
 
-  // Find matching targets.
   std::vector<const Target*> matches;
-  if (args.size() == 2) {
-    // Given a pattern, match it.
-    if (!ResolveTargetsFromCommandLinePattern(setup, args[1], all_toolchains,
-                                              &matches))
+  if (args.size() > 1) {
+    // Some patterns or explicit labels were specified.
+    std::vector<std::string> inputs(args.begin() + 1, args.end());
+
+    UniqueVector<const Target*> target_matches;
+    UniqueVector<const Config*> config_matches;
+    UniqueVector<const Toolchain*> toolchain_matches;
+    UniqueVector<SourceFile> file_matches;
+    if (!ResolveFromCommandLineInput(setup, inputs, all_toolchains,
+                                     &target_matches, &config_matches,
+                                     &toolchain_matches, &file_matches))
       return 1;
+    matches.insert(matches.begin(),
+                   target_matches.begin(), target_matches.end());
   } else if (all_toolchains) {
     // List all resolved targets.
     matches = setup->builder()->GetAllResolvedTargets();
@@ -90,29 +107,7 @@
         matches.push_back(target);
     }
   }
-
-  if (cmdline->HasSwitch("out")) {
-    // List results as build files.
-    for (const auto& match : matches) {
-      OutputString(match->dependency_output_file().value());
-      OutputString("\n");
-    }
-  } else {
-    // List results as sorted labels.
-    std::vector<Label> sorted_matches;
-    for (const auto& match : matches)
-      sorted_matches.push_back(match->label());
-    std::sort(sorted_matches.begin(), sorted_matches.end());
-
-    Label default_tc_label = setup->loader()->default_toolchain_label();
-    for (const auto& match : sorted_matches) {
-      // Print toolchain only for ones not in the default toolchain.
-      OutputString(match.GetUserVisibleName(
-          match.GetToolchainLabel() != default_tc_label));
-      OutputString("\n");
-    }
-  }
-
+  FilterAndPrintTargets(false, &matches);
   return 0;
 }
 
diff --git a/tools/gn/command_refs.cc b/tools/gn/command_refs.cc
index d75ca50..e34efaac 100644
--- a/tools/gn/command_refs.cc
+++ b/tools/gn/command_refs.cc
@@ -33,47 +33,7 @@
   }
 }
 
-// Returns the file path generating this item.
-base::FilePath FilePathForItem(const Item* item) {
-  return item->defined_from()->GetRange().begin().file()->physical_name();
-}
-
-// Prints the targets which are the result of a query. This list is sorted
-// and, if as_files is set, the unique filenames matching those targets will
-// be used.
-void OutputResultSet(const TargetSet& results, bool as_files) {
-  if (results.empty())
-    return;
-
-  if (as_files) {
-    // Output the set of unique source files.
-    std::set<std::string> unique_files;
-    for (const auto& cur : results)
-      unique_files.insert(FilePathToUTF8(FilePathForItem(cur)));
-
-    for (const auto& cur : unique_files)
-      OutputString(cur + "\n");
-  } else {
-    // Output sorted and uniquified list of labels. The set will sort the
-    // labels.
-    std::set<Label> unique_labels;
-    for (const auto& cur : results)
-      unique_labels.insert(cur->label());
-
-    // Grab the label of the default toolchain from a random target.
-    Label default_tc_label =
-        (*results.begin())->settings()->default_toolchain_label();
-
-    for (const auto& cur : unique_labels) {
-      // Print toolchain only for ones not in the default toolchain.
-      OutputString(cur.GetUserVisibleName(
-          cur.GetToolchainLabel() != default_tc_label));
-      OutputString("\n");
-    }
-  }
-}
-
-// Forward declatation for function below.
+// Forward declaration for function below.
 void RecursivePrintTargetDeps(const DepMap& dep_map,
                               const Target* target,
                               TargetSet* seen_targets,
@@ -174,19 +134,11 @@
 }
 
 void GetTargetsContainingFile(Setup* setup,
-                              const std::string& input,
+                              const std::vector<const Target*>& all_targets,
+                              const SourceFile& file,
                               bool all_toolchains,
-                              std::vector<const Target*>* matches) {
-  SourceDir cur_dir =
-      SourceDirForCurrentDirectory(setup->build_settings().root_path());
-  SourceFile file = cur_dir.ResolveRelativeFile(
-      input, setup->build_settings().root_path_utf8());
-
+                              UniqueVector<const Target*>* matches) {
   Label default_toolchain = setup->loader()->default_toolchain_label();
-
-  std::vector<const Target*> all_targets =
-      setup->builder()->GetAllResolvedTargets();
-
   for (const auto& target : all_targets) {
     if (!all_toolchains) {
       // Only check targets in the default toolchain.
@@ -198,59 +150,169 @@
   }
 }
 
+bool TargetReferencesConfig(const Target* target, const Config* config) {
+  for (const LabelConfigPair& cur : target->configs()) {
+    if (cur.ptr == config)
+      return true;
+  }
+  for (const LabelConfigPair& cur : target->public_configs()) {
+    if (cur.ptr == config)
+      return true;
+  }
+  return false;
+}
+
+void GetTargetsReferencingConfig(Setup* setup,
+                                 const std::vector<const Target*>& all_targets,
+                                 const Config* config,
+                                 bool all_toolchains,
+                                 UniqueVector<const Target*>* matches) {
+  Label default_toolchain = setup->loader()->default_toolchain_label();
+  for (const auto& target : all_targets) {
+    if (!all_toolchains) {
+      // Only check targets in the default toolchain.
+      if (target->label().GetToolchainLabel() != default_toolchain)
+        continue;
+    }
+    if (TargetReferencesConfig(target, config))
+      matches->push_back(target);
+  }
+}
+
+void DoTreeOutput(const DepMap& dep_map,
+                  const UniqueVector<const Target*>& implicit_target_matches,
+                  const UniqueVector<const Target*>& explicit_target_matches,
+                  bool all) {
+  TargetSet seen_targets;
+
+  // Implicit targets don't get printed themselves.
+  for (const Target* target : implicit_target_matches) {
+    if (all)
+      RecursivePrintTargetDeps(dep_map, target, nullptr, 0);
+    else
+      RecursivePrintTargetDeps(dep_map, target, &seen_targets, 0);
+  }
+
+  // Explicit targets appear in the output.
+  for (const Target* target : implicit_target_matches) {
+    if (all)
+      RecursivePrintTarget(dep_map, target, nullptr, 0);
+    else
+      RecursivePrintTarget(dep_map, target, &seen_targets, 0);
+  }
+}
+
+void DoAllListOutput(
+    const DepMap& dep_map,
+    const UniqueVector<const Target*>& implicit_target_matches,
+    const UniqueVector<const Target*>& explicit_target_matches) {
+  // Output recursive dependencies, uniquified and flattened.
+  TargetSet results;
+
+  for (const Target* target : implicit_target_matches)
+    RecursiveCollectChildRefs(dep_map, target, &results);
+  for (const Target* target : explicit_target_matches) {
+    // Explicit targets also get added to the output themselves.
+    results.insert(target);
+    RecursiveCollectChildRefs(dep_map, target, &results);
+  }
+
+  FilterAndPrintTargetSet(false, results);
+}
+
+void DoDirectListOutput(
+    const DepMap& dep_map,
+    const UniqueVector<const Target*>& implicit_target_matches,
+    const UniqueVector<const Target*>& explicit_target_matches) {
+  TargetSet results;
+
+  // Output everything that refers to the implicit ones.
+  for (const Target* target : implicit_target_matches) {
+    DepMap::const_iterator dep_begin = dep_map.lower_bound(target);
+    DepMap::const_iterator dep_end = dep_map.upper_bound(target);
+    for (DepMap::const_iterator cur_dep = dep_begin;
+         cur_dep != dep_end; cur_dep++)
+      results.insert(cur_dep->second);
+  }
+
+  // And just output the explicit ones directly (these are the target matches
+  // when referring to what references a file or config).
+  for (const Target* target : explicit_target_matches)
+    results.insert(target);
+
+  FilterAndPrintTargetSet(false, results);
+}
+
 }  // namespace
 
 const char kRefs[] = "refs";
 const char kRefs_HelpShort[] =
     "refs: Find stuff referencing a target or file.";
 const char kRefs_Help[] =
-    "gn refs <out_dir> (<label_pattern>|<file>) [--files] [--tree] [--all]\n"
-    "        [--all-toolchains]\n"
+    "gn refs <out_dir> (<label_pattern>|<label>|<file>)* [--all]\n"
+    "        [--all-toolchains] [--as=...] [--testonly=...] [--type=...]\n"
     "\n"
     "  Finds reverse dependencies (which targets reference something). The\n"
-    "  input is either a target label, a target label pattern, or a file\n"
-    "  name.\n"
+    "  input is a list containing:\n"
     "\n"
-    "  The <label_pattern> can take exact labels or patterns that match more\n"
-    "  than one (although not general regular expressions).\n"
-    "  See \"gn help label_pattern\" for details.\n"
+    "   - Target label: The result will be which targets depend on it.\n"
     "\n"
-    "  If the input is a file name, the output will be the target(s)\n"
-    "  referencing that file (potentially recursively if used with --tree\n"
-    "  or --all). By default, only targets from the default toolchain that\n"
-    "  reference the file will be listed.\n"
+    "   - Config label: The result will be which targets list the given\n"
+    "     config in its \"configs\" or \"public_configs\" list.\n"
+    "\n"
+    "   - Label pattern: The result will be which targets depend on any\n"
+    "     target matching the given pattern. Patterns will not match\n"
+    "     configs. These are not general regular expressions, see\n"
+    "     \"gn help label_pattern\" for details.\n"
+    "\n"
+    "   - File name: The result will be which targets list the given file in\n"
+    "     its \"inputs\", \"sources\", \"public\", or \"data\". Any input\n"
+    "     that does not contain wildcards and does not match a target or a\n"
+    "     config will be treated as a file.\n"
+    "\n"
+    "Options\n"
     "\n"
     "  --all\n"
     "      When used without --tree, will recurse and display all unique\n"
-    "      dependencies of the given targets. When used with --tree, turns\n"
-    "      off eliding to show a complete tree.\n"
+    "      dependencies of the given targets. For example, if the input is\n"
+    "      a target, this will output all targets that depend directly or\n"
+    "      indirectly on the input. If the input is a file, this will output\n"
+    "      all targets that depend directly or indirectly on that file.\n"
+    "\n"
+    "      When used with --tree, turns off eliding to show a complete tree.\n"
     "\n"
     "  --all-toolchains\n"
-    "      For target patterns, make the label pattern match all toolchains.\n"
-    "      If the label pattern does not specify an explicit toolchain,\n"
-    "      labels from all toolchains will be matched (normally only the\n"
-    "      default toolchain is matched when no toolchain is specified).\n"
+    "      Normally only inputs in the default toolchain will be included.\n"
+    "      This switch will turn on matching all toolchains.\n"
     "\n"
-    "      For filename inputs, lists targets from all toolchains that\n"
-    "      include the file.\n"
+    "      For example, a file is in a target might be compiled twice:\n"
+    "      once in the default toolchain and once in a secondary one. Without\n"
+    "      this flag, only the default toolchain one will be matched and\n"
+    "      printed (potentially with its recursive dependencies, depending on\n"
+    "      the other options). With this flag, both will be printed\n"
+    "      (potentially with both of their recursive dependencies).\n"
     "\n"
-    "  --files\n"
-    "      Output unique filenames referencing a matched target or config.\n"
-    "      These will be relative to the source root directory such that they\n"
-    "      are suitable for piping to other commands.\n"
+    TARGET_PRINTING_MODE_COMMAND_LINE_HELP
+    "\n"
+    TARGET_TESTONLY_FILTER_COMMAND_LINE_HELP
     "\n"
     "  --tree\n"
     "      Outputs a reverse dependency tree from the given target.\n"
     "      Duplicates will be elided. Combine with --all to see a full\n"
     "      dependency tree.\n"
     "\n"
+    "      Tree output can not be used with the filtering or output flags:\n"
+    "      --as, --type, --testonly.\n"
+    "\n"
+    TARGET_TYPE_FILTER_COMMAND_LINE_HELP
+    "\n"
     "Examples (target input)\n"
     "\n"
     "  gn refs out/Debug //tools/gn:gn\n"
     "      Find all targets depending on the given exact target name.\n"
     "\n"
-    "  gn refs out/Debug //base:i18n --files | xargs gvim\n"
-    "      Edit all files containing references to //base:i18n\n"
+    "  gn refs out/Debug //base:i18n --as=buildfiles | xargs gvim\n"
+    "      Edit all .gn files containing references to //base:i18n\n"
     "\n"
     "  gn refs out/Debug //base --all\n"
     "      List all targets depending directly or indirectly on //base:base.\n"
@@ -269,15 +331,20 @@
     "Examples (file input)\n"
     "\n"
     "  gn refs out/Debug //base/macros.h\n"
-    "      Print targets listing //base/macros.h as a source.\n"
+    "      Print target(s) listing //base/macros.h as a source.\n"
     "\n"
     "  gn refs out/Debug //base/macros.h --tree\n"
     "      Display a reverse dependency tree to get to the given file. This\n"
     "      will show how dependencies will reference that file.\n"
     "\n"
-    "  gn refs out/Debug //base/macros.h --all\n"
+    "  gn refs out/Debug //base/macros.h //base/basictypes.h --all\n"
     "      Display all unique targets with some dependency path to a target\n"
-    "      containing the given file as a source.\n";
+    "      containing either of the given files as a source.\n"
+    "\n"
+    "  gn refs out/Debug //base/macros.h --testonly=true --type=executable\n"
+    "          --all --as=output\n"
+    "      Display the executable file names of all test executables\n"
+    "      potentially affected by a change to the given file.\n";
 
 int RunRefs(const std::vector<std::string>& args) {
   if (args.size() != 2) {
@@ -291,95 +358,52 @@
   bool tree = cmdline->HasSwitch("tree");
   bool all = cmdline->HasSwitch("all");
   bool all_toolchains = cmdline->HasSwitch("all-toolchains");
-  bool files = cmdline->HasSwitch("files");
 
   Setup* setup = new Setup;
   setup->set_check_for_bad_items(false);
   if (!setup->DoSetup(args[0], false) || !setup->Run())
     return 1;
 
-  // Figure out the target or targets that the user is querying.
-  bool is_file_input = false;
-  std::vector<const Target*> query;
-  if (!ResolveTargetsFromCommandLinePattern(setup, args[1], all_toolchains,
-                                            &query))
+  // The inputs are everything but the first arg (which is the build dir).
+  std::vector<std::string> inputs(args.begin() + 1, args.end());
+
+  // Get the matches for the command-line input.
+  UniqueVector<const Target*> target_matches;
+  UniqueVector<const Config*> config_matches;
+  UniqueVector<const Toolchain*> toolchain_matches;
+  UniqueVector<SourceFile> file_matches;
+  if (!ResolveFromCommandLineInput(setup, inputs, all_toolchains,
+                                   &target_matches, &config_matches,
+                                   &toolchain_matches, &file_matches))
     return 1;
-  if (query.empty()) {
-    // If it doesn't match any targets, assume input is file.
-    GetTargetsContainingFile(setup, args[1], all_toolchains, &query);
-    if (query.empty()) {
-      OutputString("\"" + args[1] + "\" matches no targets.\n");
-      return 0;
-    }
-    is_file_input = true;
+
+  // When you give a file or config as an input, you want the targets that are
+  // associated with it. We don't want to just append this to the
+  // target_matches, however, since these targets should actually be listed in
+  // the output, while for normal targets you don't want to see the inputs,
+  // only what refers to them.
+  std::vector<const Target*> all_targets =
+      setup->builder()->GetAllResolvedTargets();
+  UniqueVector<const Target*> explicit_target_matches;
+  for (const auto& file : file_matches) {
+    GetTargetsContainingFile(setup, all_targets, file, all_toolchains,
+                             &explicit_target_matches);
+  }
+  for (const auto& config : config_matches) {
+    GetTargetsReferencingConfig(setup, all_targets, config, all_toolchains,
+                                &explicit_target_matches);
   }
 
   // Construct the reverse dependency tree.
   DepMap dep_map;
   FillDepMap(setup, &dep_map);
 
-  // When the input is a file, we want to print the targets in |query|, which
-  // are the things that directly reference the file, but when the input is a
-  // target, we skip that since the user is asking for what reference those.
-  if (tree) {
-    // Output dependency tree.
-    if (files) {
-      Err(nullptr, "--files option can't be used with --tree option.")
-          .PrintToStdout();
-      return 1;
-    }
-    if (query.size() != 1) {
-      Err(nullptr, "Query matches more than one target.",
-          "--tree only supports a single target as input.").PrintToStdout();
-      return 1;
-    }
-    if (all) {
-      // Recursively print all targets.
-      for (const auto& cur_query : query) {
-        if (is_file_input)
-          RecursivePrintTarget(dep_map, cur_query, nullptr, 0);
-        else
-          RecursivePrintTargetDeps(dep_map, cur_query, nullptr, 0);
-      }
-    } else {
-      // Recursively print unique targets.
-      TargetSet seen_targets;
-      for (const auto& cur_query : query) {
-        if (is_file_input)
-          RecursivePrintTarget(dep_map, cur_query, &seen_targets, 0);
-        else
-          RecursivePrintTargetDeps(dep_map, cur_query, &seen_targets, 0);
-      }
-    }
-  } else if (all) {
-    // Output recursive dependencies, uniquified and flattened.
-    TargetSet results;
-    for (const auto& cur_query : query) {
-      // File inputs also include the top level targets we found.
-      if (is_file_input)
-        results.insert(cur_query);
-      RecursiveCollectChildRefs(dep_map, cur_query, &results);
-    }
-    OutputResultSet(results, files);
-  } else {
-    TargetSet results;
-    for (const auto& cur_query : query) {
-      if (is_file_input) {
-        // When querying for a file, output the resolved list of targets only
-        // (don't need to track back any target dependencies).
-        results.insert(cur_query);
-      } else {
-        // When querying for a target, output direct references of everything
-        // in the query.
-        DepMap::const_iterator dep_begin = dep_map.lower_bound(cur_query);
-        DepMap::const_iterator dep_end = dep_map.upper_bound(cur_query);
-        for (DepMap::const_iterator cur_dep = dep_begin;
-             cur_dep != dep_end; cur_dep++)
-          results.insert(cur_dep->second);
-      }
-    }
-    OutputResultSet(results, files);
-  }
+  if (tree)
+    DoTreeOutput(dep_map, target_matches, explicit_target_matches, all);
+  else if (all)
+    DoAllListOutput(dep_map, target_matches, explicit_target_matches);
+  else
+    DoDirectListOutput(dep_map, target_matches, explicit_target_matches);
 
   return 0;
 }
diff --git a/tools/gn/commands.cc b/tools/gn/commands.cc
index ace78a7..b4f58e3 100644
--- a/tools/gn/commands.cc
+++ b/tools/gn/commands.cc
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "base/command_line.h"
+#include "tools/gn/builder.h"
 #include "tools/gn/commands.h"
 #include "tools/gn/filesystem_utils.h"
 #include "tools/gn/item.h"
@@ -13,6 +15,320 @@
 
 namespace commands {
 
+namespace {
+
+// Like above but the input string can be a pattern that matches multiple
+// targets. If the input does not parse as a pattern, prints and error and
+// returns false. If the pattern is valid, fills the vector (which might be
+// empty if there are no matches) and returns true.
+//
+// If all_toolchains is false, a pattern with an unspecified toolchain will
+// match the default toolchain only. If true, all toolchains will be matched.
+bool ResolveTargetsFromCommandLinePattern(
+    Setup* setup,
+    const std::string& label_pattern,
+    bool all_toolchains,
+    std::vector<const Target*>* matches) {
+  Value pattern_value(nullptr, label_pattern);
+
+  Err err;
+  LabelPattern pattern = LabelPattern::GetPattern(
+      SourceDirForCurrentDirectory(setup->build_settings().root_path()),
+      pattern_value,
+      &err);
+  if (err.has_error()) {
+    err.PrintToStdout();
+    return false;
+  }
+
+  if (!all_toolchains) {
+    // By default a pattern with an empty toolchain will match all toolchains.
+    // If the caller wants to default to the main toolchain only, set it
+    // explicitly.
+    if (pattern.toolchain().is_null()) {
+      // No explicit toolchain set.
+      pattern.set_toolchain(setup->loader()->default_toolchain_label());
+    }
+  }
+
+  std::vector<LabelPattern> pattern_vector;
+  pattern_vector.push_back(pattern);
+  FilterTargetsByPatterns(setup->builder()->GetAllResolvedTargets(),
+                          pattern_vector, matches);
+  return true;
+}
+
+
+// If there's an error, it will be printed and false will be returned.
+bool ResolveStringFromCommandLineInput(
+    Setup* setup,
+    const SourceDir& current_dir,
+    const std::string& input,
+    bool all_toolchains,
+    UniqueVector<const Target*>* target_matches,
+    UniqueVector<const Config*>* config_matches,
+    UniqueVector<const Toolchain*>* toolchain_matches,
+    UniqueVector<SourceFile>* file_matches) {
+  if (LabelPattern::HasWildcard(input)) {
+    // For now, only match patterns against targets. It might be nice in the
+    // future to allow the user to specify which types of things they want to
+    // match, but it should probably only match targets by default.
+    std::vector<const Target*> target_match_vector;
+    if (!ResolveTargetsFromCommandLinePattern(setup, input, all_toolchains,
+                                              &target_match_vector))
+      return false;
+    for (const Target* target : target_match_vector)
+      target_matches->push_back(target);
+    return true;
+  }
+
+  // Try to figure out what this thing is.
+  Err err;
+  Label label = Label::Resolve(current_dir,
+                               setup->loader()->default_toolchain_label(),
+                               Value(nullptr, input), &err);
+  if (err.has_error()) {
+    err.PrintToStdout();
+    return false;
+  }
+
+  const Item* item = setup->builder()->GetItem(label);
+  if (item) {
+    if (const Config* as_config = item->AsConfig())
+      config_matches->push_back(as_config);
+    else if (const Target* as_target = item->AsTarget())
+      target_matches->push_back(as_target);
+    else if (const Toolchain* as_toolchain = item->AsToolchain())
+      toolchain_matches->push_back(as_toolchain);
+  } else {
+    // Not an item, assume this must be a file.
+    file_matches->push_back(current_dir.ResolveRelativeFile(
+        input, setup->build_settings().root_path_utf8()));
+  }
+
+  return true;
+}
+
+enum TargetPrintingMode {
+  TARGET_PRINT_BUILDFILE,
+  TARGET_PRINT_LABEL,
+  TARGET_PRINT_OUTPUT,
+};
+
+// Retrieves the target printing mode based on the command line flags for the
+// current process. Returns true on success. On error, prints a message to the
+// console and returns false.
+bool GetTargetPrintingMode(TargetPrintingMode* mode) {
+  std::string switch_key = "as";
+  const base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
+
+  if (!cmdline->HasSwitch(switch_key)) {
+    // Default to labels.
+    *mode = TARGET_PRINT_LABEL;
+    return true;
+  }
+
+  std::string value = cmdline->GetSwitchValueASCII(switch_key);
+  if (value == "buildfile") {
+    *mode = TARGET_PRINT_BUILDFILE;
+    return true;
+  }
+  if (value == "label") {
+    *mode = TARGET_PRINT_LABEL;
+    return true;
+  }
+  if (value == "output") {
+    *mode = TARGET_PRINT_OUTPUT;
+    return true;
+  }
+
+  Err(Location(), "Invalid value for \"--as\".",
+      "I was expecting \"buildfile\", \"label\", or \"output\" but you\n"
+      "said \"" + value + "\".").PrintToStdout();
+  return false;
+}
+
+// Returns the target type filter based on the command line flags for the
+// current process. Returns true on success. On error, prints a message to the
+// console and returns false.
+//
+// Target::UNKNOWN will be set if there is no filter. Target::ACTION_FOREACH
+// will never be returned. Code applying the filters should apply Target::ACTION
+// to both ACTION and ACTION_FOREACH.
+bool GetTargetTypeFilter(Target::OutputType* type) {
+  std::string switch_key = "type";
+  const base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
+
+  if (!cmdline->HasSwitch(switch_key)) {
+    // Default to unknown -> no filtering.
+    *type = Target::UNKNOWN;
+    return true;
+  }
+
+  std::string value = cmdline->GetSwitchValueASCII(switch_key);
+  if (value == "group") {
+    *type = Target::GROUP;
+    return true;
+  }
+  if (value == "executable") {
+    *type = Target::EXECUTABLE;
+    return true;
+  }
+  if (value == "shared_library") {
+    *type = Target::SHARED_LIBRARY;
+    return true;
+  }
+  if (value == "static_library") {
+    *type = Target::STATIC_LIBRARY;
+    return true;
+  }
+  if (value == "source_set") {
+    *type = Target::SOURCE_SET;
+    return true;
+  }
+  if (value == "copy") {
+    *type = Target::COPY_FILES;
+    return true;
+  }
+  if (value == "action") {
+    *type = Target::ACTION;
+    return true;
+  }
+
+  Err(Location(), "Invalid value for \"--type\".").PrintToStdout();
+  return false;
+}
+
+
+// Applies any testonly filtering specified on the command line to the given
+// target set. On failure, prints an error and returns false.
+bool ApplyTestonlyFilter(std::vector<const Target*>* targets) {
+  const base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
+  std::string testonly_key = "testonly";
+
+  if (targets->empty() || !cmdline->HasSwitch(testonly_key))
+    return true;
+
+  std::string testonly_value = cmdline->GetSwitchValueASCII(testonly_key);
+  bool testonly = false;
+  if (testonly_value == "true") {
+    testonly = true;
+  } else if (testonly_value != "false") {
+    Err(Location(), "Bad value for --testonly.",
+        "I was expecting --testonly=true or --testonly=false.")
+        .PrintToStdout();
+    return false;
+  }
+
+  // Filter into a copy of the vector, then swap to output.
+  std::vector<const Target*> result;
+  result.reserve(targets->size());
+
+  for (const Target* target : *targets) {
+    if (target->testonly() == testonly)
+      result.push_back(target);
+  }
+
+  targets->swap(result);
+  return true;
+}
+
+// Applies any target type filtering specified on the command line to the given
+// target set. On failure, prints an error and returns false.
+bool ApplyTypeFilter(std::vector<const Target*>* targets) {
+  Target::OutputType type = Target::UNKNOWN;
+  if (!GetTargetTypeFilter(&type))
+    return false;
+  if (targets->empty() || type == Target::UNKNOWN)
+    return true;  // Nothing to filter out.
+
+  // Filter into a copy of the vector, then swap to output.
+  std::vector<const Target*> result;
+  result.reserve(targets->size());
+
+  for (const Target* target : *targets) {
+    // Make "action" also apply to ACTION_FOREACH.
+    if (target->output_type() == type ||
+        (type == Target::ACTION &&
+         target->output_type() == Target::ACTION_FOREACH))
+      result.push_back(target);
+  }
+
+  targets->swap(result);
+  return true;
+}
+
+// Returns the file path generating this item.
+base::FilePath BuildFileForItem(const Item* item) {
+  return item->defined_from()->GetRange().begin().file()->physical_name();
+}
+
+void PrintTargetsAsBuildfiles(bool indent,
+                              const std::vector<const Target*>& targets) {
+  // Output the set of unique source files.
+  std::set<std::string> unique_files;
+  for (const Target* target : targets)
+    unique_files.insert(FilePathToUTF8(BuildFileForItem(target)));
+
+  for (const std::string& file : unique_files) {
+    if (indent)
+      OutputString("  ");
+    OutputString(file + "\n");
+  }
+}
+
+void PrintTargetsAsLabels(bool indent,
+                          const std::vector<const Target*>& targets) {
+  // Putting the labels into a set automatically sorts them for us.
+  std::set<Label> unique_labels;
+  for (const auto& target : targets)
+    unique_labels.insert(target->label());
+
+  // Grab the label of the default toolchain from the first target.
+  Label default_tc_label =
+      targets[0]->settings()->default_toolchain_label();
+
+  for (const Label& label : unique_labels) {
+    // Print toolchain only for ones not in the default toolchain.
+    if (indent)
+      OutputString("  ");
+    OutputString(label.GetUserVisibleName(
+        label.GetToolchainLabel() != default_tc_label));
+    OutputString("\n");
+  }
+}
+
+void PrintTargetsAsOutputs(bool indent,
+                           const std::vector<const Target*>& targets) {
+  if (targets.empty())
+    return;
+
+  // Grab the build settings from a random target.
+  const BuildSettings* build_settings =
+      targets[0]->settings()->build_settings();
+
+  SourceDir current_dir = SourceDirForCurrentDirectory(
+      build_settings->root_path());
+  for (const Target* target : targets) {
+    // Use the link output file if there is one, otherwise fall back to the
+    // dependency output file (for actions, for example).
+    OutputFile output_file = target->link_output_file();
+    if (output_file.value().empty())
+      output_file = target->dependency_output_file();
+
+    SourceFile output_as_source =
+        output_file.AsSourceFile(build_settings);
+    std::string result = RebasePath(output_as_source.value(), current_dir,
+                                    build_settings->root_path_utf8());
+    if (indent)
+      OutputString("  ");
+    OutputString(result);
+    OutputString("\n");
+  }
+}
+
+}  // namespace
+
 CommandInfo::CommandInfo()
     : help_short(nullptr),
       help(nullptr),
@@ -84,38 +400,87 @@
   return target;
 }
 
-bool ResolveTargetsFromCommandLinePattern(
+bool ResolveFromCommandLineInput(
     Setup* setup,
-    const std::string& label_pattern,
+    const std::vector<std::string>& input,
     bool all_toolchains,
-    std::vector<const Target*>* matches) {
-  Value pattern_value(nullptr, label_pattern);
-
-  Err err;
-  LabelPattern pattern = LabelPattern::GetPattern(
-      SourceDirForCurrentDirectory(setup->build_settings().root_path()),
-      pattern_value,
-      &err);
-  if (err.has_error()) {
-    err.PrintToStdout();
+    UniqueVector<const Target*>* target_matches,
+    UniqueVector<const Config*>* config_matches,
+    UniqueVector<const Toolchain*>* toolchain_matches,
+    UniqueVector<SourceFile>* file_matches) {
+  if (input.empty()) {
+    Err(Location(), "You need to specify a label, file, or pattern.")
+        .PrintToStdout();
     return false;
   }
 
-  if (!all_toolchains) {
-    // By default a pattern with an empty toolchain will match all toolchains.
-    // IF the caller wants to default to the main toolchain only, set it
-    // explicitly.
-    if (pattern.toolchain().is_null()) {
-      // No explicit toolchain set.
-      pattern.set_toolchain(setup->loader()->default_toolchain_label());
+  SourceDir cur_dir =
+      SourceDirForCurrentDirectory(setup->build_settings().root_path());
+  for (const auto& cur : input) {
+    if (!ResolveStringFromCommandLineInput(setup, cur_dir, cur,
+                                           all_toolchains, target_matches,
+                                           config_matches, toolchain_matches,
+                                           file_matches))
+      return false;
+  }
+  return true;
+}
+
+void FilterTargetsByPatterns(const std::vector<const Target*>& input,
+                             const std::vector<LabelPattern>& filter,
+                             std::vector<const Target*>* output) {
+  for (const auto& target : input) {
+    for (const auto& pattern : filter) {
+      if (pattern.Matches(target->label())) {
+        output->push_back(target);
+        break;
+      }
     }
   }
+}
 
-  std::vector<LabelPattern> pattern_vector;
-  pattern_vector.push_back(pattern);
-  FilterTargetsByPatterns(setup->builder()->GetAllResolvedTargets(),
-                          pattern_vector, matches);
-  return true;
+void FilterTargetsByPatterns(const std::vector<const Target*>& input,
+                             const std::vector<LabelPattern>& filter,
+                             UniqueVector<const Target*>* output) {
+  for (const auto& target : input) {
+    for (const auto& pattern : filter) {
+      if (pattern.Matches(target->label())) {
+        output->push_back(target);
+        break;
+      }
+    }
+  }
+}
+
+void FilterAndPrintTargets(bool indent, std::vector<const Target*>* targets) {
+  if (targets->empty())
+    return;
+
+  if (!ApplyTestonlyFilter(targets))
+    return;
+  if (!ApplyTypeFilter(targets))
+    return;
+
+  TargetPrintingMode printing_mode = TARGET_PRINT_LABEL;
+  if (targets->empty() || !GetTargetPrintingMode(&printing_mode))
+    return;
+  switch (printing_mode) {
+    case TARGET_PRINT_BUILDFILE:
+      PrintTargetsAsBuildfiles(indent, *targets);
+      break;
+    case TARGET_PRINT_LABEL:
+      PrintTargetsAsLabels(indent, *targets);
+      break;
+    case TARGET_PRINT_OUTPUT:
+      PrintTargetsAsOutputs(indent, *targets);
+      break;
+  }
+}
+
+void FilterAndPrintTargetSet(bool indent,
+                             const std::set<const Target*>& targets) {
+  std::vector<const Target*> target_vector(targets.begin(), targets.end());
+  FilterAndPrintTargets(indent, &target_vector);
 }
 
 }  // namespace commands
diff --git a/tools/gn/commands.h b/tools/gn/commands.h
index b64a9b8..dd2cb1b 100644
--- a/tools/gn/commands.h
+++ b/tools/gn/commands.h
@@ -6,15 +6,21 @@
 #define TOOLS_GN_COMMANDS_H_
 
 #include <map>
+#include <set>
 #include <string>
 #include <vector>
 
 #include "base/strings/string_piece.h"
+#include "tools/gn/target.h"
+#include "tools/gn/unique_vector.h"
 
 class BuildSettings;
+class Config;
 class LabelPattern;
 class Setup;
+class SourceFile;
 class Target;
+class Toolchain;
 
 // Each "Run" command returns the value we should return from main().
 
@@ -93,18 +99,20 @@
     Setup* setup,
     const std::string& label_string);
 
-// Like above but the input string can be a pattern that matches multiple
-// targets. If the input does not parse as a pattern, prints and error and
-// returns false. If the pattern is valid, fills the vector (which might be
-// empty if there are no matches) and returns true.
+// Resolves a vector of command line inputs and figures out the full set of
+// things they resolve to.
 //
-// If all_tolchains is false, a pattern with an unspecified toolchain will
-// match the default toolchain only. If true, all toolchains will be matched.
-bool ResolveTargetsFromCommandLinePattern(
+// Patterns with wildcards will only match targets. The file_matches aren't
+// validated that they are real files or referenced by any targets. They're just
+// the set of things that didn't match anything else.
+bool ResolveFromCommandLineInput(
     Setup* setup,
-    const std::string& label_pattern,
+    const std::vector<std::string>& input,
     bool all_toolchains,
-    std::vector<const Target*>* matches);
+    UniqueVector<const Target*>* target_matches,
+    UniqueVector<const Config*>* config_matches,
+    UniqueVector<const Toolchain*>* toolchain_matches,
+    UniqueVector<SourceFile>* file_matches);
 
 // Runs the header checker. All targets in the build should be given in
 // all_targets, and the specific targets to check should be in to_check.
@@ -119,13 +127,50 @@
                         const std::vector<const Target*>& to_check,
                         bool force_check);
 
-// Filters the given list of targets by the given pattern list. This is a
-// helper function for setting up a call to CheckPublicHeaders based on a check
-// filter.
+// Filters the given list of targets by the given pattern list.
 void FilterTargetsByPatterns(const std::vector<const Target*>& input,
                              const std::vector<LabelPattern>& filter,
                              std::vector<const Target*>* output);
+void FilterTargetsByPatterns(const std::vector<const Target*>& input,
+                             const std::vector<LabelPattern>& filter,
+                             UniqueVector<const Target*>* output);
+
+// These are the documentation strings for the command-line flags used by
+// FilterAndPrintTargets. Commands that call that function should incorporate
+// these into their help.
+#define TARGET_PRINTING_MODE_COMMAND_LINE_HELP \
+    "  --as=(buildfile|label|output)\n"\
+    "      How to print targets.\n"\
+    "\n"\
+    "      buildfile\n"\
+    "          Prints the build files where the given target was declared as\n"\
+    "          file names.\n"\
+    "      label  (default)\n"\
+    "          Prints the label of the target.\n"\
+    "      output\n"\
+    "          Prints the first output file for the target relative to the\n"\
+    "          current directory.\n"
+#define TARGET_TYPE_FILTER_COMMAND_LINE_HELP \
+    "  --type=(action|copy|executable|group|shared_library|source_set|\n"\
+    "          static_library)\n"\
+    "      Restrict outputs to targets matching the given type. If\n"\
+    "      unspecified, no filtering will be performed.\n"
+#define TARGET_TESTONLY_FILTER_COMMAND_LINE_HELP \
+    "  --testonly=(true|false)\n"\
+    "      Restrict outputs to targets with the testonly flag set\n"\
+    "      accordingly. When unspecified, the target's testonly flags are\n"\
+    "      ignored.\n"
+
+// Applies any testonly and type filters specified on the command line,
+// and prints the targets as specified by the --as command line flag.
+//
+// If indent is true, the results will be indented two spaces.
+//
+// The vector will be modified so that only the printed targets will remain.
+void FilterAndPrintTargets(bool indent, std::vector<const Target*>* targets);
+void FilterAndPrintTargetSet(bool indent,
+                             const std::set<const Target*>& targets);
 
 }  // namespace commands
 
-#endif  // TOOLS_GN_COMMANDS_H_
+#endif  // TOOLS_GN_COMMANDS_H
diff --git a/tools/gn/example/build/BUILD.gn b/tools/gn/example/build/BUILD.gn
index 6224372..8eae46a 100644
--- a/tools/gn/example/build/BUILD.gn
+++ b/tools/gn/example/build/BUILD.gn
@@ -3,7 +3,7 @@
 # found in the LICENSE file.
 
 config("compiler_defaults") {
-  if (os == "linux") {
+  if (current_os == "linux") {
     cflags = [
       "-fPIC",
       "-pthread",
diff --git a/tools/gn/format_test_data/032.gn b/tools/gn/format_test_data/032.gn
index e3c16ce..d7ea7e5 100644
--- a/tools/gn/format_test_data/032.gn
+++ b/tools/gn/format_test_data/032.gn
@@ -1,6 +1,6 @@
 # Make sure continued conditions are aligned.
 if (something) {
   if (false) {
-  } else if (is_linux && !is_android && cpu_arch == "x64" && !disable_iterator_debugging) {
+  } else if (is_linux && !is_android && current_cpu == "x64" && !disable_iterator_debugging) {
   }
 }
diff --git a/tools/gn/format_test_data/032.golden b/tools/gn/format_test_data/032.golden
index b4296a9ba..aeca896 100644
--- a/tools/gn/format_test_data/032.golden
+++ b/tools/gn/format_test_data/032.golden
@@ -1,7 +1,7 @@
 # Make sure continued conditions are aligned.
 if (something) {
   if (false) {
-  } else if (is_linux && !is_android && cpu_arch == "x64" &&
+  } else if (is_linux && !is_android && current_cpu == "x64" &&
              !disable_iterator_debugging) {
   }
 }
diff --git a/tools/gn/header_checker.cc b/tools/gn/header_checker.cc
index ea638444..423929b0 100644
--- a/tools/gn/header_checker.cc
+++ b/tools/gn/header_checker.cc
@@ -115,6 +115,13 @@
   return ret;
 }
 
+// Returns true if the two targets have the same label not counting the
+// toolchain.
+bool TargetLabelsMatchExceptToolchain(const Target* a, const Target* b) {
+  return a->label().dir() == b->label().dir() &&
+         a->label().name() == b->label().name();
+}
+
 }  // namespace
 
 HeaderChecker::HeaderChecker(const BuildSettings* build_settings,
@@ -155,26 +162,20 @@
         type != SOURCE_M && type != SOURCE_MM && type != SOURCE_RC)
       continue;
 
-    // Do a first pass to find if this should be skipped. All targets including
-    // this source file must exclude it from checking, or any target
-    // must mark it as generated (for cases where one target generates a file,
-    // and another lists it as a source to compile it).
-    if (!force_check) {
-      bool check_includes = false;
-      bool is_generated = false;
-      for (const auto& vect_i : file.second) {
-        check_includes |= vect_i.target->check_includes();
-        is_generated |= vect_i.is_generated;
-      }
-      if (!check_includes || is_generated)
-        continue;
-    }
+    // If any target marks it as generated, don't check it.
+    bool is_generated = false;
+    for (const auto& vect_i : file.second)
+      is_generated |= vect_i.is_generated;
+    if (is_generated)
+      continue;
 
     for (const auto& vect_i : file.second) {
-      pool->PostWorkerTaskWithShutdownBehavior(
-          FROM_HERE,
-          base::Bind(&HeaderChecker::DoWork, this, vect_i.target, file.first),
-          base::SequencedWorkerPool::BLOCK_SHUTDOWN);
+      if (vect_i.target->check_includes()) {
+        pool->PostWorkerTaskWithShutdownBehavior(
+            FROM_HERE,
+            base::Bind(&HeaderChecker::DoWork, this, vect_i.target, file.first),
+            base::SequencedWorkerPool::BLOCK_SHUTDOWN);
+      }
     }
   }
 
@@ -336,6 +337,18 @@
     if (to_target == from_target)
       return true;
 
+    // Additionally, allow a target to include files from that same target
+    // in other toolchains. This is a bit of a hack to account for the fact that
+    // the include finder doesn't understand the preprocessor.
+    //
+    // If a source file conditionally depends on a platform-specific include in
+    // the same target, and there is a cross-compile such that GN sees
+    // definitions of the target both with and without that include, it would
+    // give an error that the target needs to depend on itself in the other
+    // toolchain (where the platform-specific header is defined as a source).
+    if (TargetLabelsMatchExceptToolchain(to_target, from_target))
+      return true;
+
     bool is_permitted_chain = false;
     if (IsDependencyOf(to_target, from_target, &chain, &is_permitted_chain)) {
       DCHECK(chain.size() >= 2);
@@ -378,20 +391,7 @@
 
   if (!found_dependency) {
     DCHECK(!last_error.has_error());
-
-    std::string msg = "It is not in any dependency of " +
-        from_target->label().GetUserVisibleName(false);
-    msg += "\nThe include file is in the target(s):\n";
-    for (const auto& target : targets)
-      msg += "  " + target.target->label().GetUserVisibleName(false) + "\n";
-    if (targets.size() > 1)
-      msg += "at least one of ";
-    msg += "which should somehow be reachable from " +
-        from_target->label().GetUserVisibleName(false);
-
-    // Danger: must call CreatePersistentRange to put in Err.
-    *err = Err(CreatePersistentRange(source_file, range),
-               "Include not allowed.", msg);
+    *err = MakeUnreachableError(source_file, range, from_target, targets);
     return false;
   }
   if (last_error.has_error()) {
@@ -508,3 +508,62 @@
 
   return false;
 }
+
+Err HeaderChecker::MakeUnreachableError(
+    const InputFile& source_file,
+    const LocationRange& range,
+    const Target* from_target,
+    const TargetVector& targets) {
+  // Normally the toolchains will all match, but when cross-compiling, we can
+  // get targets with more than one toolchain in the list of possibilities.
+  std::vector<const Target*> targets_with_matching_toolchains;
+  std::vector<const Target*> targets_with_other_toolchains;
+  for (const TargetInfo& candidate : targets) {
+    if (candidate.target->toolchain() == from_target->toolchain())
+      targets_with_matching_toolchains.push_back(candidate.target);
+    else
+      targets_with_other_toolchains.push_back(candidate.target);
+  }
+
+  // It's common when cross-compiling to have a target with the same file in
+  // more than one toolchain. We could output all of them, but this is
+  // generally confusing to people (most end-users won't understand toolchains
+  // well).
+  //
+  // So delete any candidates in other toolchains that also appear in the same
+  // toolchain as the from_target.
+  for (int other_index = 0;
+       other_index < static_cast<int>(targets_with_other_toolchains.size());
+       other_index++) {
+    for (const Target* cur_matching : targets_with_matching_toolchains) {
+      if (TargetLabelsMatchExceptToolchain(
+              cur_matching, targets_with_other_toolchains[other_index])) {
+        // Found a duplicate, erase it.
+        targets_with_other_toolchains.erase(
+            targets_with_other_toolchains.begin() + other_index);
+        other_index--;
+        break;
+      }
+    }
+  }
+
+  // Only display toolchains on labels if they don't all match.
+  bool include_toolchain = !targets_with_other_toolchains.empty();
+
+  std::string msg = "It is not in any dependency of\n  " +
+      from_target->label().GetUserVisibleName(include_toolchain);
+  msg += "\nThe include file is in the target(s):\n";
+  for (const auto& target : targets_with_matching_toolchains)
+    msg += "  " + target->label().GetUserVisibleName(include_toolchain) + "\n";
+  for (const auto& target : targets_with_other_toolchains)
+    msg += "  " + target->label().GetUserVisibleName(include_toolchain) + "\n";
+  if (targets_with_other_toolchains.size() +
+      targets_with_matching_toolchains.size() > 1)
+    msg += "at least one of ";
+  msg += "which should somehow be reachable.";
+
+  // Danger: must call CreatePersistentRange to put in Err.
+  return Err(CreatePersistentRange(source_file, range),
+             "Include not allowed.", msg);
+}
+
diff --git a/tools/gn/header_checker.h b/tools/gn/header_checker.h
index e024c02..35e8887 100644
--- a/tools/gn/header_checker.h
+++ b/tools/gn/header_checker.h
@@ -148,6 +148,14 @@
                       bool require_permitted,
                       Chain* chain) const;
 
+  // Makes a very descriptive error message for when an include is disallowed
+  // from a given from_target, with a missing dependency to one of the given
+  // targets.
+  static Err MakeUnreachableError(const InputFile& source_file,
+                                  const LocationRange& range,
+                                  const Target* from_target,
+                                  const TargetVector& targets);
+
   // Non-locked variables ------------------------------------------------------
   //
   // These are initialized during construction (which happens on one thread)
diff --git a/tools/gn/label.h b/tools/gn/label.h
index 9c4a1c6..88336ea 100644
--- a/tools/gn/label.h
+++ b/tools/gn/label.h
@@ -18,7 +18,7 @@
  public:
   Label();
 
-  // Makes a label given an already-separate out path and name.
+  // Makes a label given an already-separated out path and name.
   // See also Resolve().
   Label(const SourceDir& dir,
         const base::StringPiece& name,
diff --git a/tools/gn/label_pattern.cc b/tools/gn/label_pattern.cc
index 4b31174..712826f 100644
--- a/tools/gn/label_pattern.cc
+++ b/tools/gn/label_pattern.cc
@@ -216,6 +216,12 @@
   return LabelPattern(type, dir, base::StringPiece(), toolchain_label);
 }
 
+bool LabelPattern::HasWildcard(const std::string& str) {
+  // Just look for a star. In the future, we may want to handle escaping or
+  // other types of patterns.
+  return str.find('*') != std::string::npos;
+}
+
 bool LabelPattern::Matches(const Label& label) const {
   if (!toolchain_.is_null()) {
     // Toolchain must match exactly.
diff --git a/tools/gn/label_pattern.h b/tools/gn/label_pattern.h
index e805ffb..5c032603 100644
--- a/tools/gn/label_pattern.h
+++ b/tools/gn/label_pattern.h
@@ -39,6 +39,9 @@
                                  const Value& value,
                                  Err* err);
 
+  // Returns true if the given input string might match more than one thing.
+  static bool HasWildcard(const std::string& str);
+
   // Returns true if this pattern matches the given label.
   bool Matches(const Label& label) const;
 
diff --git a/tools/gn/misc/vim/syntax/gn.vim b/tools/gn/misc/vim/syntax/gn.vim
index d9049b29..334ffa69 100644
--- a/tools/gn/misc/vim/syntax/gn.vim
+++ b/tools/gn/misc/vim/syntax/gn.vim
@@ -16,10 +16,11 @@
 hi def link     gnConditional       Conditional
 
 " Predefined variables
-syn keyword     gnPredefVar build_cpu_arch build_os cpu_arch current_toolchain
-syn keyword     gnPredefVar default_toolchain os python_path root_build_dir
-syn keyword     gnPredefVar root_gen_dir root_out_dir target_gen_dir
-syn keyword     gnPredefVar target_out_dir
+syn keyword     gnPredefVar current_cpu current_os current_toolchain
+syn keyword     gnPredefVar default_toolchain host_cpu host_os
+syn keyword     gnPredefVar root_build_dir root_gen_dir root_out_dir
+syn keyword     gnPredefVar target_cpu target_gen_dir target_out_dir
+syn keyword     gnPredefVar target_os
 syn keyword     gnPredefVar true false
 hi def link     gnPredefVar         Constant
 
diff --git a/tools/gn/output_file.cc b/tools/gn/output_file.cc
index cc6dc98..1b6db62 100644
--- a/tools/gn/output_file.cc
+++ b/tools/gn/output_file.cc
@@ -27,7 +27,11 @@
 SourceFile OutputFile::AsSourceFile(const BuildSettings* build_settings) const {
   DCHECK(!value_.empty());
   DCHECK(value_[value_.size() - 1] != '/');
-  return SourceFile(build_settings->build_dir().value() + value_);
+
+  std::string path = build_settings->build_dir().value();
+  path.append(value_);
+  NormalizePath(&path);
+  return SourceFile(path);
 }
 
 SourceDir OutputFile::AsSourceDir(const BuildSettings* build_settings) const {
@@ -36,5 +40,8 @@
     // slash.
     DCHECK(value_[value_.size() - 1] == '/');
   }
-  return SourceDir(build_settings->build_dir().value() + value_);
+  std::string path = build_settings->build_dir().value();
+  path.append(value_);
+  NormalizePath(&path);
+  return SourceDir(path);
 }
diff --git a/tools/gn/variables.cc b/tools/gn/variables.cc
index 4b0c5142..6d59420 100644
--- a/tools/gn/variables.cc
+++ b/tools/gn/variables.cc
@@ -8,24 +8,153 @@
 
 // Built-in variables ----------------------------------------------------------
 
-const char kCpuArch[] = "cpu_arch";
-const char kCpuArch_HelpShort[] =
-    "cpu_arch: [string] Current processor architecture.";
-const char kCpuArch_Help[] =
-    "cpu_arch: Current processor architecture.\n"
+const char kHostCpu[] = "host_cpu";
+const char kHostCpu_HelpShort[] =
+    "host_cpu: [string] The processor architecture that GN is running on.";
+const char kHostCpu_Help[] =
+    "host_cpu: The processor architecture that GN is running on.\n"
     "\n"
-    "  The initial value is based on the current architecture of the host\n"
-    "  system. However, the build configuration can set this to any value.\n"
+    "  This is value is exposed so that cross-compile toolchains can\n"
+    "  access the host architecture when needed.\n"
     "\n"
-    "  This value is not used internally by GN for any purpose, so you can\n"
-    "  set it to whatever value is relevant to your build.\n"
+    "  The value should generally be considered read-only, but it can be\n"
+    "  overriden in order to handle unusual cases where there might\n"
+    "  be multiple plausible values for the host architecture (e.g., if\n"
+    "  you can do either 32-bit or 64-bit builds). The value is not used\n"
+    "  internally by GN for any purpose.\n"
     "\n"
-    "Possible initial values set by GN:\n"
+    "Some possible values:\n"
+    "  - \"x64\"\n"
+    "  - \"x86\"\n";
+
+const char kHostOs[] = "host_os";
+const char kHostOs_HelpShort[] =
+    "host_os: [string] The operating system that GN is running on.";
+const char kHostOs_Help[] =
+    "host_os: [string] The operating system that GN is running on.\n"
+    "\n"
+    "  This value is exposed so that cross-compiles can access the host\n"
+    "  build system's settings.\n"
+    "\n"
+    "  This value should generally be treated as read-only. It, however,\n"
+    "  is not used internally by GN for any purpose.\n"
+    "\n"
+    "Some possible values:\n"
+    "  - \"linux\"\n"
+    "  - \"mac\"\n"
+    "  - \"win\"\n";
+
+const char kTargetCpu[] = "target_cpu";
+const char kTargetCpu_HelpShort[] =
+    "target_cpu: [string] The desired cpu architecture for the build.";
+const char kTargetCpu_Help[] =
+    "target_cpu: The desired cpu architecture for the build.\n"
+    "\n"
+    "  This value should be used to indicate the desired architecture for\n"
+    "  the primary objects of the build. It will match the cpu architecture\n"
+    "  of the default toolchain.\n"
+    "\n"
+    "  In many cases, this is the same as \"host_cpu\", but in the case\n"
+    "  of cross-compiles, this can be set to something different. This \n"
+    "  value is different from \"current_cpu\" in that it can be referenced\n"
+    "  from inside any toolchain. This value can also be ignored if it is\n"
+    "  not needed or meaningful for a project.\n"
+    "\n"
+    "  This value is not used internally by GN for any purpose, so it\n"
+    "  may be set to whatever value is needed for the build.\n"
+    "  GN defaults this value to the empty string (\"\") and the\n"
+    "  configuration files should set it to an appropriate value\n"
+    "  (e.g., setting it to the value of \"host_cpu\") if it is not\n"
+    "  overridden on the command line or in the args.gn file.\n"
+    "\n"
+    "  Where practical, use one of the following list of common values:\n"
+    "\n"
+    "Possible values:\n"
     "  - \"x86\"\n"
     "  - \"x64\"\n"
     "  - \"arm\"\n"
+    "  - \"arm64\"\n"
     "  - \"mipsel\"\n";
 
+const char kTargetOs[] = "target_os";
+const char kTargetOs_HelpShort[] =
+    "target_os: [string] The desired operating system for the build.";
+const char kTargetOs_Help[] =
+    "target_os: The desired operating system for the build.\n"
+    "\n"
+    "  This value should be used to indicate the desired operating system\n"
+    "  for the primary object(s) of the build. It will match the OS of\n"
+    "  the default toolchain.\n"
+    "\n"
+    "  In many cases, this is the same as \"host_os\", but in the case of\n"
+    "  cross-compiles, it may be different. This variable differs from\n"
+    "  \"current_os\" in that it can be referenced from inside any\n"
+    "  toolchain and will always return the initial value.\n"
+    "\n"
+    "  This should be set to the most specific value possible. So,\n"
+    "  \"android\" or \"chromeos\" should be used instead of \"linux\"\n"
+    "  where applicable, even though Android and ChromeOS are both Linux\n"
+    "  variants. This can mean that one needs to write\n"
+    "\n"
+    "      if (target_os == \"android\" || target_os == \"linux\") {\n"
+    "          # ...\n"
+    "      }\n"
+    "\n"
+    "  and so forth.\n"
+    "\n"
+    "  This value is not used internally by GN for any purpose, so it\n"
+    "  may be set to whatever value is needed for the build.\n"
+    "  GN defaults this value to the empty string (\"\") and the\n"
+    "  configuration files should set it to an appropriate value\n"
+    "  (e.g., setting it to the value of \"host_os\") if it is not\n"
+    "  set via the command line or in the args.gn file.\n"
+    "\n"
+    "  Where practical, use one of the following list of common values:\n"
+    "\n"
+    "Possible values:\n"
+    "  - \"android\"\n"
+    "  - \"chromeos\"\n"
+    "  - \"ios\"\n"
+    "  - \"linux\"\n"
+    "  - \"nacl\"\n"
+    "  - \"mac\"\n"
+    "  - \"win\"\n";
+
+const char kCurrentCpu[] = "current_cpu";
+const char kCurrentCpu_HelpShort[] =
+    "current_cpu: [string] The processor architecture of the current "
+        "toolchain.";
+const char kCurrentCpu_Help[] =
+    "current_cpu: The processor architecture of the current toolchain.\n"
+    "\n"
+    "  The build configuration usually sets this value based on the value\n"
+    "  of \"host_cpu\" (see \"gn help host_cpu\") and then threads\n"
+    "  this through the toolchain definitions to ensure that it always\n"
+    "  reflects the appropriate value.\n"
+    "\n"
+    "  This value is not used internally by GN for any purpose. It is\n"
+    "  set it to the empty string (\"\") by default but is declared so\n"
+    "  that it can be overridden on the command line if so desired.\n"
+    "\n"
+    "  See \"gn help target_cpu\" for a list of common values returned.\n";
+
+const char kCurrentOs[] = "current_os";
+const char kCurrentOs_HelpShort[] =
+    "current_os: [string] The operating system of the current toolchain.";
+const char kCurrentOs_Help[] =
+    "current_os: The operating system of the current toolchain.\n"
+    "\n"
+    "  The build configuration usually sets this value based on the value\n"
+    "  of \"target_os\" (see \"gn help target_os\"), and then threads this\n"
+    "  through the toolchain definitions to ensure that it always reflects\n"
+    "  the appropriate value.\n"
+    "\n"
+    "  This value is not used internally by GN for any purpose. It is\n"
+    "  set it to the empty string (\"\") by default but is declared so\n"
+    "  that it can be overridden on the command line if so desired.\n"
+    "\n"
+    "  See \"gn help target_os\" for a list of common values returned.\n";
+
 const char kCurrentToolchain[] = "current_toolchain";
 const char kCurrentToolchain_HelpShort[] =
     "current_toolchain: [string] Label of the current toolchain.";
@@ -42,30 +171,6 @@
     "    executable(\"output_thats_64_bit_only\") {\n"
     "      ...\n";
 
-const char kBuildCpuArch[] = "build_cpu_arch";
-const char kBuildCpuArch_HelpShort[] =
-    "build_cpu_arch: [string] The default value for the \"cpu_arch\" "
-    "variable.";
-const char kBuildCpuArch_Help[] =
-    "build_cpu_arch: The default value for the \"cpu_arch\" variable.\n"
-    "\n"
-    "  This value has the same definition as \"cpu_arch\" (see\n"
-    "  \"gn help cpu_arch\") but should be treated as read-only. This is so\n"
-    "  the build can override the \"cpu_arch\" variable for doing\n"
-    "  cross-compiles, but can still access the host build system's CPU\n"
-    "  architecture.\n";
-
-const char kBuildOs[] = "build_os";
-const char kBuildOs_HelpShort[] =
-    "build_os: [string] The default value for the \"os\" variable.";
-const char kBuildOs_Help[] =
-    "build_os: [string] The default value for the \"os\" variable.\n"
-    "\n"
-    "  This value has the same definition as \"os\" (see \"gn help os\") but\n"
-    "  should be treated as read-only. This is so the build can override\n"
-    "  the \"os\" variable for doing cross-compiles, but can still access\n"
-    "  the host build system's operating system type.\n";
-
 const char kDefaultToolchain[] = "default_toolchain";
 const char kDefaultToolchain_HelpShort[] =
     "default_toolchain: [string] Label of the default toolchain.";
@@ -75,33 +180,6 @@
     "  A fully-qualified label representing the default toolchain, which may\n"
     "  not necessarily be the current one (see \"current_toolchain\").\n";
 
-const char kOs[] = "os";
-const char kOs_HelpShort[] =
-    "os: [string] Indicates the operating system of the current build.";
-const char kOs_Help[] =
-    "os: Indicates the operating system of the current build."
-    "\n"
-    "  This value is set by default based on the current host operating\n"
-    "  system. The build configuration can override the value to anything\n"
-    "  it wants, or it can be set via the build arguments on the command\n"
-    "  line.\n"
-    "\n"
-    "  If you want to know the default value without any overrides, you can\n"
-    "  use \"default_os\" (see \"gn help default_os\").\n"
-    "\n"
-    "  Note that this returns the most specific value. So even though\n"
-    "  Android and ChromeOS are both Linux, the more specific value will\n"
-    "  be returned.\n"
-    "\n"
-    "Some possible values:\n"
-    "  - \"amiga\"\n"
-    "  - \"android\"\n"
-    "  - \"chromeos\"\n"
-    "  - \"ios\"\n"
-    "  - \"linux\"\n"
-    "  - \"mac\"\n"
-    "  - \"win\"\n";
-
 const char kPythonPath[] = "python_path";
 const char kPythonPath_HelpShort[] =
     "python_path: [string] Absolute path of Python.";
@@ -975,16 +1053,18 @@
 const VariableInfoMap& GetBuiltinVariables() {
   static VariableInfoMap info_map;
   if (info_map.empty()) {
-    INSERT_VARIABLE(BuildCpuArch)
-    INSERT_VARIABLE(BuildOs)
-    INSERT_VARIABLE(CpuArch)
+    INSERT_VARIABLE(CurrentCpu)
+    INSERT_VARIABLE(CurrentOs)
     INSERT_VARIABLE(CurrentToolchain)
     INSERT_VARIABLE(DefaultToolchain)
-    INSERT_VARIABLE(Os)
+    INSERT_VARIABLE(HostCpu)
+    INSERT_VARIABLE(HostOs)
     INSERT_VARIABLE(PythonPath)
     INSERT_VARIABLE(RootBuildDir)
     INSERT_VARIABLE(RootGenDir)
     INSERT_VARIABLE(RootOutDir)
+    INSERT_VARIABLE(TargetCpu)
+    INSERT_VARIABLE(TargetOs)
     INSERT_VARIABLE(TargetGenDir)
     INSERT_VARIABLE(TargetOutDir)
   }
diff --git a/tools/gn/variables.h b/tools/gn/variables.h
index eb7dbd1d..57d6a98 100644
--- a/tools/gn/variables.h
+++ b/tools/gn/variables.h
@@ -13,17 +13,21 @@
 
 // Builtin vars ----------------------------------------------------------------
 
-extern const char kBuildCpuArch[];
-extern const char kBuildCpuArch_HelpShort[];
-extern const char kBuildCpuArch_Help[];
+extern const char kHostCpu[];
+extern const char kHostCpu_HelpShort[];
+extern const char kHostCpu_Help[];
 
-extern const char kBuildOs[];
-extern const char kBuildOs_HelpShort[];
-extern const char kBuildOs_Help[];
+extern const char kHostOs[];
+extern const char kHostOs_HelpShort[];
+extern const char kHostOs_Help[];
 
-extern const char kCpuArch[];
-extern const char kCpuArch_HelpShort[];
-extern const char kCpuArch_Help[];
+extern const char kCurrentCpu[];
+extern const char kCurrentCpu_HelpShort[];
+extern const char kCurrentCpu_Help[];
+
+extern const char kCurrentOs[];
+extern const char kCurrentOs_HelpShort[];
+extern const char kCurrentOs_Help[];
 
 extern const char kCurrentToolchain[];
 extern const char kCurrentToolchain_HelpShort[];
@@ -33,10 +37,6 @@
 extern const char kDefaultToolchain_HelpShort[];
 extern const char kDefaultToolchain_Help[];
 
-extern const char kOs[];
-extern const char kOs_HelpShort[];
-extern const char kOs_Help[];
-
 extern const char kPythonPath[];
 extern const char kPythonPath_HelpShort[];
 extern const char kPythonPath_Help[];
@@ -53,6 +53,14 @@
 extern const char kRootOutDir_HelpShort[];
 extern const char kRootOutDir_Help[];
 
+extern const char kTargetCpu[];
+extern const char kTargetCpu_HelpShort[];
+extern const char kTargetCpu_Help[];
+
+extern const char kTargetOs[];
+extern const char kTargetOs_HelpShort[];
+extern const char kTargetOs_Help[];
+
 extern const char kTargetGenDir[];
 extern const char kTargetGenDir_HelpShort[];
 extern const char kTargetGenDir_Help[];
diff --git a/tools/ipc_fuzzer/mutate/generate.cc b/tools/ipc_fuzzer/mutate/generate.cc
index 6d8137d..ba3dc997 100644
--- a/tools/ipc_fuzzer/mutate/generate.cc
+++ b/tools/ipc_fuzzer/mutate/generate.cc
@@ -1040,18 +1040,23 @@
 template <>
 struct GenerateTraits<content::WebCursor> {
   static bool Generate(content::WebCursor* p, Generator* generator) {
-    blink::WebCursorInfo::Type type;
-    if (!GenerateParam(&type, generator))
-      return false;
-    content::WebCursor::CursorInfo info(type);
+    content::WebCursor::CursorInfo info;
 
-    // Omitting |externalHandle| since it is not serialized.
+    // |type| enum is not validated on de-serialization, so pick random value.
+    if (!GenerateParam(reinterpret_cast<int *>(&info.type), generator))
+      return false;
     if (!GenerateParam(&info.hotspot, generator))
       return false;
     if (!GenerateParam(&info.image_scale_factor, generator))
       return false;
     if (!GenerateParam(&info.custom_image, generator))
       return false;
+    // Omitting |externalHandle| since it is not serialized.
+
+    // Scale factor is expected to be greater than 0, otherwise we hit
+    // a check failure.
+    info.image_scale_factor = fabs(info.image_scale_factor) + 0.001;
+
     *p = content::WebCursor(info);
     return true;
   }
@@ -1290,6 +1295,16 @@
   }
 };
 
+#if defined(OS_WIN)
+template <>
+struct GenerateTraits<HWND> {
+  static bool Generate(HWND* p, Generator* generator) {
+    // TODO(aarya): This should actually generate something.
+    return true;
+  }
+};
+#endif
+
 template <>
 struct GenerateTraits<IPC::Message> {
   static bool Generate(IPC::Message *p, Generator* generator) {
@@ -1330,6 +1345,16 @@
   }
 };
 
+#if defined(OS_WIN)
+template <>
+struct GenerateTraits<LOGFONT> {
+  static bool Generate(LOGFONT* p, Generator* generator) {
+    // TODO(aarya): This should actually generate something.
+    return true;
+  }
+};
+#endif
+
 template <>
 struct GenerateTraits<media::AudioParameters> {
   static bool Generate(media::AudioParameters* p, Generator* generator) {
diff --git a/tools/json_schema_compiler/test/BUILD.gn b/tools/json_schema_compiler/test/BUILD.gn
index 76ae1b9..9b0c1b9 100644
--- a/tools/json_schema_compiler/test/BUILD.gn
+++ b/tools/json_schema_compiler/test/BUILD.gn
@@ -36,12 +36,12 @@
     "test_util.cc",
     "test_util.h",
   ]
+
+  # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+  configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
+
   public_deps = [
     ":api",
+    "//base",
   ]
-
-  if (is_win) {
-    # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
-    cflags = [ "/wd4267" ]
-  }
 }
diff --git a/tools/metrics/actions/actions.xml b/tools/metrics/actions/actions.xml
index 4dedf03..95674aa 100644
--- a/tools/metrics/actions/actions.xml
+++ b/tools/metrics/actions/actions.xml
@@ -1127,6 +1127,14 @@
   </description>
 </action>
 
+<action name="BadMessageTerminate_EVG">
+  <owner>fsamuel@chromium.org</owner>
+  <description>
+    Indicates that an ExtensionViewGuest was terminated due to a navigation to
+    an origin other than the extension it is embedding.
+  </description>
+</action>
+
 <action name="BadMessageTerminate_FAMF">
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <description>Please enter the description of this user action.</description>
@@ -7584,6 +7592,16 @@
   <description>Please enter the description of this user action.</description>
 </action>
 
+<action name="MobileOmniboxClipboardChanged">
+  <owner>jif@chromium.org</owner>
+  <description>
+    Emitted when Chrome detects that the clipboard contains a new URL. This
+    occurs either when Chrome enters the foreground and notices that the content
+    of the clipboard changed, or when the clipboard changes while Chrome is in
+    the foreground.
+  </description>
+</action>
+
 <action name="MobileOmniboxRefineSuggestion">
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <description>Please enter the description of this user action.</description>
diff --git a/tools/metrics/actions/extract_actions.py b/tools/metrics/actions/extract_actions.py
index dde900ac..7484711c 100755
--- a/tools/metrics/actions/extract_actions.py
+++ b/tools/metrics/actions/extract_actions.py
@@ -38,6 +38,7 @@
 
 # Import the metrics/common module for pretty print xml.
 sys.path.append(os.path.join(os.path.dirname(__file__), '..', 'common'))
+import presubmit_util
 import diff_util
 import pretty_print_xml
 
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index f2769ee8..1ed23b8d 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -263,7 +263,7 @@
 </histogram>
 
 <histogram name="AppBanners.DismissEvent" enum="AppBannersDismissEvent">
-  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <owner>dfalcantara@chromium.org</owner>
   <summary>
     App banners promote an application related to the current website, and are
     requested specifically through the current page's HTML.  This stat tracks
@@ -273,7 +273,7 @@
 </histogram>
 
 <histogram name="AppBanners.DisplayEvent" enum="AppBannersDisplayEvent">
-  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <owner>dfalcantara@chromium.org</owner>
   <summary>
     App banners promote an application related to the current website, and are
     requested specifically through the current page's HTML.  This stat tracks
@@ -283,7 +283,7 @@
 </histogram>
 
 <histogram name="AppBanners.InstallEvent" enum="AppBannersInstallEvent">
-  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <owner>dfalcantara@chromium.org</owner>
   <summary>
     App banners promote an application related to the current website, and are
     requested specifically through the current page's HTML.  This stat tracks
@@ -465,6 +465,14 @@
   </summary>
 </histogram>
 
+<histogram name="Apps.AppListDoodleAction" enum="AppListDoodleAction">
+  <owner>calamity@chromium.org</owner>
+  <summary>
+    The number of user interactions with the app list doodle. This is logged
+    once per action.
+  </summary>
+</histogram>
+
 <histogram name="Apps.AppListHowEnabled" enum="AppListEnableSource">
   <owner>tapted@chromium.org</owner>
   <summary>
@@ -475,6 +483,14 @@
   </summary>
 </histogram>
 
+<histogram name="Apps.AppListPageOpened" enum="AppListPage">
+  <owner>calamity@chromium.org</owner>
+  <summary>
+    The page that the app list goes to. This is gathered every time the user
+    initiates a transition to another page in the launcher.
+  </summary>
+</histogram>
+
 <histogram name="Apps.AppListSearchCommenced" units="searches">
   <owner>tapted@chromium.org</owner>
   <summary>
@@ -483,6 +499,15 @@
   </summary>
 </histogram>
 
+<histogram name="Apps.AppListSearchResultOpenDisplayType"
+    enum="AppListSearchResultDisplayType">
+  <owner>calamity@chromium.org</owner>
+  <summary>
+    The display type of the app list search result that was opened by the user.
+    This is gathered per click of a search result.
+  </summary>
+</histogram>
+
 <histogram name="Apps.AppListSearchResultOpenType" enum="AppListSearchResult">
   <owner>tapted@chromium.org</owner>
   <summary>
@@ -1809,6 +1834,17 @@
     Autofill form events for address forms. These are recorded when the user
     interacts with a form requesting an address.
   </summary>
+  <details>
+    Important caveat about submission metrics: - Submission using autofill data
+    is determined by simply evaluating if there was a fill operation in this
+    page. So, if the user filled with local data, completed erased or modified
+    the data after and then submitted, we would only emit one &quot;Submitted
+    with server suggestion filled (once)&quot;; - The submission segmentation
+    works by checking what kind of data was last filled for this type of form in
+    the page load. So, if I user initially filled with local data and after that
+    filled with server, we will only emit &quot;Submitted with server suggestion
+    filled (once)&quot;.
+  </details>
 </histogram>
 
 <histogram name="Autofill.FormEvents.CreditCard" enum="AutofillFormEvent">
@@ -1817,6 +1853,17 @@
     Autofill form events for credit card forms. These are recorded when the user
     interacts with a form requesting a credit card.
   </summary>
+  <details>
+    Important caveat about submission metrics: - Submission using autofill data
+    is determined by simply evaluating if there was a fill operation in this
+    page. So, if the user filled with local data, completed erased or modified
+    the data after and then submitted, we would only emit one &quot;Submitted
+    with server suggestion filled (once)&quot;; - The submission segmentation
+    works by checking what kind of data was last filled for this type of form in
+    the page load. So, if I user initially filled with local data and after that
+    filled with server, we will only emit &quot;Submitted with server suggestion
+    filled (once)&quot;.
+  </details>
 </histogram>
 
 <histogram name="Autofill.IsEnabled.PageLoad" enum="BooleanEnabled">
@@ -5571,6 +5618,9 @@
 </histogram>
 
 <histogram name="DOMStorage.clear" units="milliseconds">
+  <obsolete>
+    Deprecated 2012.
+  </obsolete>
   <owner>michaeln@chromium.org</owner>
   <summary>
     Duration to execute localStorage.clear() or sessionStorage.clear().
@@ -5578,6 +5628,9 @@
 </histogram>
 
 <histogram name="DOMStorage.getItem" units="milliseconds">
+  <obsolete>
+    Deprecated 2012.
+  </obsolete>
   <owner>michaeln@chromium.org</owner>
   <summary>
     Duration to execute localStorage.getItem() or sessionStorage.getItem().
@@ -5585,6 +5638,9 @@
 </histogram>
 
 <histogram name="DOMStorage.key" units="milliseconds">
+  <obsolete>
+    Deprecated 2012.
+  </obsolete>
   <owner>michaeln@chromium.org</owner>
   <summary>
     Duration to execute localStorage.key() or sessionStorage.key().
@@ -5592,6 +5648,9 @@
 </histogram>
 
 <histogram name="DOMStorage.length" units="milliseconds">
+  <obsolete>
+    Deprecated 2012.
+  </obsolete>
   <owner>michaeln@chromium.org</owner>
   <summary>
     Duration to execute localStorage.length() or sessionStorage.length().
@@ -5599,6 +5658,9 @@
 </histogram>
 
 <histogram name="DOMStorage.removeItem" units="milliseconds">
+  <obsolete>
+    Deprecated 2012.
+  </obsolete>
   <owner>michaeln@chromium.org</owner>
   <summary>
     Duration to execute localStorage.removeItem() or
@@ -5607,6 +5669,9 @@
 </histogram>
 
 <histogram name="DOMStorage.setItem" units="milliseconds">
+  <obsolete>
+    Deprecated 2012.
+  </obsolete>
   <owner>michaeln@chromium.org</owner>
   <summary>
     Duration to execute localStorage.setItem() or sessionStorage.setItem().
@@ -6434,6 +6499,82 @@
   </summary>
 </histogram>
 
+<histogram name="EasyUnlock.Setup.Devices.Count.Eligible">
+  <owner>isherman@chromium.org</owner>
+  <owner>xiaowenx@chromium.org</owner>
+  <summary>
+    The number of eligible devices that the CryptAuth server returns during the
+    Smart Lock setup flow.
+  </summary>
+  <details>
+    Note that a single user might report multiple values, for example if she
+    tries to complete the setup flow with a device in airplane mode, and then
+    tries again taking the device out of airplane mode.
+  </details>
+</histogram>
+
+<histogram name="EasyUnlock.Setup.Devices.Count.Ineligible">
+  <owner>isherman@chromium.org</owner>
+  <owner>xiaowenx@chromium.org</owner>
+  <summary>
+    The number of ineligible devices that the CryptAuth server returns during
+    the Smart Lock setup flow.
+  </summary>
+  <details>
+    Note that a single user might report multiple values, for example if she
+    tries to complete the setup flow with a device in airplane mode, and then
+    tries again taking the device out of airplane mode.
+  </details>
+</histogram>
+
+<histogram name="EasyUnlock.Setup.Devices.HasSecureScreenLock"
+    enum="EasyUnlockHasSecureScreenLock">
+  <owner>isherman@chromium.org</owner>
+  <owner>xiaowenx@chromium.org</owner>
+  <summary>
+    Whether the user's phone has a secure screen lock installed. Recorded during
+    Smart Lock setup, when the user's phone first connects to the Chromebook.
+  </summary>
+</histogram>
+
+<histogram name="EasyUnlock.Setup.Devices.HasTrustAgentEnabled"
+    enum="EasyUnlockHasTrustAgentEnabled">
+  <owner>isherman@chromium.org</owner>
+  <owner>xiaowenx@chromium.org</owner>
+  <summary>
+    Whether the user's phone has a trust agent -- e.g. Smart Lock for Android --
+    enabled. Recorded during Smart Lock (for Chrome) setup, when the user's
+    phone first connects to the Chromebook.
+  </summary>
+  <details>
+    Note that this histogram tracks whether the setting under Settings ~&gt;
+    Security ~&gt; Trust agents ~&gt; Smart Lock (Google) (or any other trust
+    agent) is enabled. The Smart Lock trust agent is enabled by default for
+    users who have ascure lock screen. This metric does _not_ measure whether
+    users have any Smart Lock trustlets enabled.
+  </details>
+</histogram>
+
+<histogram name="EasyUnlock.Setup.Devices.IneligibilityReason"
+    enum="EasyUnlockDeviceIneligibilityReason">
+  <owner>isherman@chromium.org</owner>
+  <owner>xiaowenx@chromium.org</owner>
+  <summary>
+    Records the most actionable reason why none of a user's devices were
+    eligible as unlock keys for Smart Lock. This is recorded during the Smart
+    Lock setup flow, only if the CryptAuth server returns no eligible devices
+    for the user.
+  </summary>
+  <details>
+    To be precise, this metric tracks the _least_ actionable _reason_ why the
+    _most_ actionable _device_ is not eligible as an unlock key. For example,
+    suppose that the user has 10 ineligible devices returned. This metric tries
+    to identify the most likely candidate device for use as a Smart Lock key,
+    and then records the most confounding reason why that device is still not
+    eligible to be used as an unlock key.
+  </details>
+</histogram>
+
 <histogram name="EasyUnlock.SetupStateOnClose" enum="EasyUnlockSetupState">
   <owner>joshwoodward@google.com</owner>
   <owner>tbarzic@chromium.org</owner>
@@ -6469,6 +6610,23 @@
   </summary>
 </histogram>
 
+<histogram name="EasyUnlock.TrialRun.Events" enum="EasyUnlockTrialRunEvents">
+  <owner>isherman@chromium.org</owner>
+  <owner>xiaowenx@chromium.org</owner>
+  <summary>
+    Records when the Easy Unlock trial run is launched, and when the user
+    attempts to click on the lock icon during the trial run.
+  </summary>
+  <details>
+    If a user clicks on the lock icon more than once, then the &quot;clicked
+    lock icon&quot; event counter will be incremented more than once as well.
+    Hence, the &quot;user count&quot; data shows how many users ever clicked on
+    the lock icon during the trial run. From the raw (non-&quot;user
+    count&quot;) data, we can also see whether users click on the icon multiple
+    times.
+  </details>
+</histogram>
+
 <histogram name="EasyUnlock.UnlockEvent" enum="EasyUnlockUnlockEvent">
   <obsolete>
     Deprecated 02/2015; replaced by EasyUnlock.AuthEvent.Unlock.
@@ -7393,6 +7551,52 @@
   </summary>
 </histogram>
 
+<histogram name="Event.Latency.ScrollUpdate.BrowserNotifiedToBeforeGpuSwap"
+    units="microseconds">
+  <owner>rbyers@chromium.org</owner>
+  <summary>
+    Time between the browser receives the notification of a ScrollUpdate gesture
+    event induced renderer swap and GPU starts to swap.
+  </summary>
+</histogram>
+
+<histogram name="Event.Latency.ScrollUpdate.GpuSwap" units="microseconds">
+  <owner>rbyers@chromium.org</owner>
+  <summary>
+    Time between gpu starts to swap a ScrollUpdate gesture event induced frame
+    and the swap finishes.
+  </summary>
+</histogram>
+
+<histogram name="Event.Latency.ScrollUpdate.HandledToRendererSwap"
+    units="microseconds">
+  <owner>rbyers@chromium.org</owner>
+  <summary>
+    Time between the ScrollUpdate gesture event is handled on main/impl thread
+    (specified by suffix) and before renderer starts to swap.
+  </summary>
+</histogram>
+
+<histogram name="Event.Latency.ScrollUpdate.RendererSwapToBrowserNotified"
+    units="microseconds">
+  <owner>rbyers@chromium.org</owner>
+  <summary>
+    Time between the renderer starts to swap a frame induced by ScrollUpdate
+    gesture event and browser receives the swap notification.
+  </summary>
+</histogram>
+
+<histogram name="Event.Latency.ScrollUpdate.TouchToHandled"
+    units="microseconds">
+  <owner>rbyers@chromium.org</owner>
+  <summary>
+    Time between initial creation of a touch event and the generated
+    ScrollUpdate gesture event is handled on main/impl thread (specified by
+    suffix). If no swap was induced by the ScrollUpdate gesture event, no
+    recording is made.
+  </summary>
+</histogram>
+
 <histogram name="Event.Latency.TouchToFirstScrollUpdateSwap"
     units="microseconds">
   <owner>rbyers@chromium.org</owner>
@@ -12274,6 +12478,15 @@
   </summary>
 </histogram>
 
+<histogram name="Linux.SandboxStatus" enum="LinuxSandboxStatus">
+  <owner>rickyz@chromium.org</owner>
+  <summary>
+    The Linux sandbox status. This describes what sandboxing features are
+    enabled (such as the suid/namespace sandboxes, various namespaces, seccomp
+    bpf, ...). Emitted once at startup.
+  </summary>
+</histogram>
+
 <histogram name="Linux.WindowManager" enum="LinuxWindowManagerName">
   <owner>pkotwicz@chromium.org</owner>
   <summary>
@@ -12346,6 +12559,14 @@
   </summary>
 </histogram>
 
+<histogram name="LocalStorage.CommitDelay">
+  <owner>michaeln@chromium.org</owner>
+  <summary>
+    Delay between a page making changes and those changes being written to the
+    DB.
+  </summary>
+</histogram>
+
 <histogram name="LocalStorage.RendererLocalStorageSizeInKB" units="KB">
   <owner>michaeln@chromium.org</owner>
   <summary>
@@ -13491,6 +13712,26 @@
   </summary>
 </histogram>
 
+<histogram name="Media.VTVDA.InitializationFailureReason"
+    enum="VTVDAInitializationFailureType">
+  <owner>sandersd@chromium.org</owner>
+  <summary>
+    Count of VideoToolbox initialization failure reasons. Successful
+    initializations are counted as a special failure type. The sum of successes
+    and failures gives a lower bound on the number of attempted initializations.
+  </summary>
+</histogram>
+
+<histogram name="Media.VTVDA.SessionFailureReason"
+    enum="VTVDASessionFailureType">
+  <owner>sandersd@chromium.org</owner>
+  <summary>
+    Count of VTVDA session failure reasons. Successful initializations are
+    counted as a special failure type. Since only successfully initialized
+    session can fail, failures rates are computed as a simple ratio.
+  </summary>
+</histogram>
+
 <histogram name="Media.WindowsCoreAudioInput" enum="BooleanSuccess">
   <owner>henrika@chromium.org</owner>
   <summary>
@@ -15079,6 +15320,15 @@
   </summary>
 </histogram>
 
+<histogram name="Navigation.IsMobileOptimized" enum="BooleanIsMobileOptimized">
+  <owner>cjhopman@chromium.org</owner>
+  <owner>nyquist@chromium.org</owner>
+  <summary>
+    Signifies whether a succesfully finished page load for the main frame
+    content width fits within the device width and/or has a fixed page scale.
+  </summary>
+</histogram>
+
 <histogram name="Navigation.MainFrameScheme" enum="NavigationScheme">
   <owner>cbentzel@chromium.org</owner>
   <owner>davidben@chromium.org</owner>
@@ -16266,7 +16516,7 @@
 </histogram>
 
 <histogram name="Net.ConnectionTypeCount3" enum="ConnectionType">
-  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <owner>davidben@chromium.org</owner>
   <summary>
     Each bucket is the number of successful connections of a particular type
     that the user has had during the session.
@@ -17261,7 +17511,7 @@
 </histogram>
 
 <histogram name="Net.HadConnectionType3" enum="ConnectionType">
-  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <owner>davidben@chromium.org</owner>
   <summary>
     Each bucket is a boolean (0 or 1) indicating whether the user has had a
     successful connection of that type during the session.
@@ -17435,6 +17685,14 @@
   </summary>
 </histogram>
 
+<histogram name="net.HttpIdentSrcURL" units="requests">
+  <owner>tsepez@chromium.org</owner>
+  <summary>
+    Count of requests which contained a basic auth username and password
+    embedded in the URL itself.
+  </summary>
+</histogram>
+
 <histogram name="Net.HttpJob.TotalTime" units="milliseconds">
   <owner>mmenke@chromium.org</owner>
   <summary>
@@ -18733,6 +18991,13 @@
   </summary>
 </histogram>
 
+<histogram name="Net.QuicSession.HeadersStream.EarlyFramesReceived">
+  <owner>rch@chromium.org</owner>
+  <summary>
+    The frames received on the headers stream which arrived early.
+  </summary>
+</histogram>
+
 <histogram name="Net.QuicSession.HostResolutionTime" units="milliseconds">
   <owner>rch@chromium.org</owner>
   <summary>
@@ -19610,7 +19875,10 @@
 <histogram name="Net.SSL_CipherSuite" enum="SSLCipherSuite">
   <owner>agl@chromium.org</owner>
   <owner>rsleevi@chromium.org</owner>
-  <summary>The SSL/TLS cipher suite that was negotiated.</summary>
+  <summary>
+    The SSL/TLS cipher suite that was negotiated. Recorded for each SSL/TLS
+    connection in the socket pool where Connect() succeeds.
+  </summary>
 </histogram>
 
 <histogram name="Net.SSL_Connection_Error" enum="NetErrorCodes">
@@ -19799,6 +20067,14 @@
   <summary>Time saved by a speculative certificate vertification.</summary>
 </histogram>
 
+<histogram name="Net.SSLVersion" enum="SSLOrQUICVersion">
+  <owner>davidben@chromium.org</owner>
+  <summary>
+    The SSL/TLS version that was negotiated. Recorded for each SSL/TLS
+    connection in the socket pool where Connect() succeeds.
+  </summary>
+</histogram>
+
 <histogram name="Net.TCP_Connection_Idle_Sockets">
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <summary>Number of idle sockets when the Connect() succeeded.</summary>
@@ -22788,6 +23064,15 @@
   </summary>
 </histogram>
 
+<histogram name="Notifications.DifferentRequestingEmbeddingOrigins"
+    enum="Boolean">
+  <owner>peter@chromium.org</owner>
+  <summary>
+    Records if the requesting and embedding origins are different when
+    requesting permission to display Web Notifications.
+  </summary>
+</histogram>
+
 <histogram name="Notifications.PerNotificationActions"
     enum="NotificationActionType">
   <owner>dewittj@chromium.org</owner>
@@ -26627,6 +26912,15 @@
   </summary>
 </histogram>
 
+<histogram name="Plugin.PowerSaver.PosterParamPresence"
+    enum="PluginPowerSaverPosterParamPresence">
+  <owner>tommycli@chromium.org</owner>
+  <summary>
+    Record how many plugin object tags use poster param. This is recorded once
+    per plugin instance, and is currently restricted to Flash plugin instances.
+  </summary>
+</histogram>
+
 <histogram name="Plugin.PowerSaver.Unthrottle"
     enum="PluginPowerSaverUnthrottleMethod">
   <owner>tommycli@chromium.org</owner>
@@ -27291,6 +27585,11 @@
   </summary>
 </histogram>
 
+<histogram name="PrefService.CreateProfilePrefsTime" units="milliseconds">
+  <owner>rkaplow@chromium.org</owner>
+  <summary>The amount of time that elapsed during CreateProfilePrefs.</summary>
+</histogram>
+
 <histogram name="Prerender.AbandonTimeUntilUsed" units="milliseconds">
   <owner>davidben@chromium.org</owner>
   <owner>tburkard@chromium.org</owner>
@@ -28209,6 +28508,22 @@
   </summary>
 </histogram>
 
+<histogram name="PrintPreview.PageCount.PrintWithExtension">
+  <owner>vitalybuka@chromium.org</owner>
+  <summary>
+    The final page count (after page selection) of documents printed to an
+    extension printer (using printerProvider API).
+  </summary>
+</histogram>
+
+<histogram name="PrintPreview.PageCount.PrintWithPrivet">
+  <owner>vitalybuka@chromium.org</owner>
+  <summary>
+    The final page count (after page selection) of documents printed to a privet
+    printer.
+  </summary>
+</histogram>
+
 <histogram name="PrintPreview.PageCount.SystemDialog">
   <owner>vitalybuka@chromium.org</owner>
   <summary>
@@ -28350,6 +28665,19 @@
   <summary>Size of the cookies database.</summary>
 </histogram>
 
+<histogram name="Profile.CreateAndInitializeProfile" units="milliseconds">
+  <owner>rkaplow@chromium.org</owner>
+  <summary>Length of time to setup profile.</summary>
+</histogram>
+
+<histogram name="Profile.CreateProfileHelperTime" units="milliseconds">
+  <owner>rkaplow@chromium.org</owner>
+  <summary>
+    The amount of time that elapsed during ProfileManager::CreateProfileHelper.
+    This is called when a profile is created synchronously (usually at startup).
+  </summary>
+</histogram>
+
 <histogram name="Profile.CreateResult" enum="ProfileCreateResult">
   <owner>pam@chromium.org</owner>
   <owner>rlp@chromium.org</owner>
@@ -28439,6 +28767,9 @@
 </histogram>
 
 <histogram name="Profile.GetProfile" units="milliseconds">
+  <obsolete>
+    Deprecated 02/2015. Profile.CreateAndInitializeProfile is more useful.
+  </obsolete>
   <owner>rkaplow@chromium.org</owner>
   <summary>Length of time to retrieve profile.</summary>
 </histogram>
@@ -28613,6 +28944,13 @@
   </summary>
 </histogram>
 
+<histogram name="Profile.OnPrefsLoadedTime" units="milliseconds">
+  <owner>rkaplow@chromium.org</owner>
+  <summary>
+    The amount of time that elapsed during ProfileImpl::OnPrefsLoaded.
+  </summary>
+</histogram>
+
 <histogram name="Profile.Opening" enum="ProfileOpen">
   <obsolete>
     Deprecated because it did not present the information clearly.
@@ -28823,6 +29161,26 @@
   </summary>
 </histogram>
 
+<histogram name="PushMessaging.DeliveryStatus" enum="PushDeliveryStatus">
+  <owner>johnme@google.com</owner>
+  <owner>mvanouwerkerk@google.com</owner>
+  <summary>
+    When a Service Worker receives a push message, this records whether it was
+    successful, or otherwise the type of error encountered.
+  </summary>
+</histogram>
+
+<histogram name="PushMessaging.GetRegistrationStatus"
+    enum="PushGetRegistrationStatus">
+  <owner>johnme@google.com</owner>
+  <owner>mvanouwerkerk@google.com</owner>
+  <summary>
+    When a webpage asks for details about its current push messaging
+    registration, this records whether the request is successful, or otherwise
+    the type of error encountered.
+  </summary>
+</histogram>
+
 <histogram name="PushMessaging.RegistrationStatus"
     enum="PushRegistrationStatus">
   <owner>johnme@google.com</owner>
@@ -28833,6 +29191,16 @@
   </summary>
 </histogram>
 
+<histogram name="PushMessaging.UnregistrationStatus"
+    enum="PushUnregistrationStatus">
+  <owner>johnme@google.com</owner>
+  <owner>mvanouwerkerk@google.com</owner>
+  <summary>
+    When a webpage unregisters from push messaging, this records whether the
+    request is successful, or otherwise the type of error encountered.
+  </summary>
+</histogram>
+
 <histogram name="PushMessaging.UserVisibleStatus" enum="PushUserVisibleStatus">
   <owner>johnme@google.com</owner>
   <owner>mvanouwerkerk@google.com</owner>
@@ -29599,6 +29967,9 @@
 </histogram>
 
 <histogram name="Renderer4.LCDText.PercentageOfAALayers" units="%">
+  <obsolete>
+    Deprecated as of 02/2015.  No longer generated.
+  </obsolete>
   <owner>wiltzius@chromium.org</owner>
   <summary>
     The ratio of LCDText CC Layers / candidate LCDText layers. Recorded in
@@ -29609,6 +29980,9 @@
 </histogram>
 
 <histogram name="Renderer4.LCDText.PercentageOfCandidateLayers" units="%">
+  <obsolete>
+    Deprecated as of 02/2015.  No longer generated.
+  </obsolete>
   <owner>wiltzius@chromium.org</owner>
   <summary>
     The ratio of CC Layers which are candidates for LCDText AA / total picture
@@ -32926,6 +33300,15 @@
   <summary>Execution time of ServiceWorkerGlobalScope.onpush.</summary>
 </histogram>
 
+<histogram name="ServiceWorker.ScriptCachedMetadataSize" units="bytes">
+  <owner>horo@chromium.org</owner>
+  <summary>
+    The length of cached metadata of Service Worker scripts. Logged on each load
+    of Service Worker script only when the cached metadata is available. It
+    doesn't include the size of imported scripts.
+  </summary>
+</histogram>
+
 <histogram name="ServiceWorker.ScriptSize" units="bytes">
   <owner>ksakamoto@chromium.org</owner>
   <summary>
@@ -33260,6 +33643,13 @@
   <summary>The number of pinned tabs opened when a profile is loaded.</summary>
 </histogram>
 
+<histogram name="Settings.RegisterProfilePrefsTime" units="milliseconds">
+  <owner>rkaplow@chromium.org</owner>
+  <summary>
+    The amount of time that elapsed during RegisterProfilePrefs.
+  </summary>
+</histogram>
+
 <histogram name="Settings.ShowHomeButton" enum="BooleanEnabled">
   <owner>mpearson@chromium.org</owner>
   <summary>
@@ -43315,23 +43705,29 @@
   <int value="41" label="Error/unknown reason for dismissal"/>
   <int value="42" label="User opened the application after installing it"/>
   <int value="43" label="User clicked on the banner"/>
-  <int value="44" label="User swiped the banner away"/>
+  <int value="44" label="(Obsolete) User swiped the banner away"/>
   <int value="45" label="User hit the X button"/>
   <int value="46" label="User began app install, but it didn't finish in time"/>
-  <int value="47" label="Automatic dismissal: User navigated elsewhere"/>
+  <int value="47" label="Banner was dismissed for any reason"/>
 </enum>
 
 <enum name="AppBannersDisplayEvent" type="int">
   <int value="1" label="Banner was requested by the site"/>
   <int value="2" label="User previously blocked the same banner"/>
   <int value="3" label="User blocked too many other banners from the site"/>
-  <int value="4" label="Banner created."/>
+  <int value="4" label="Banner created"/>
+  <int value="5" label="User already installed the app"/>
+  <int value="6" label="User ignored the banner last time"/>
+  <int value="7" label="Manifest lacks a service worker"/>
+  <int value="8" label="Site hasn't been visited frequently enough"/>
 </enum>
 
 <enum name="AppBannersInstallEvent" type="int">
-  <int value="21" label="User triggered the app install dialog"/>
-  <int value="22" label="User began installing the app"/>
-  <int value="23" label="User waited for the app to finish installing"/>
+  <int value="21" label="(Native app) User triggered the app install dialog"/>
+  <int value="22" label="(Native app) User began installing the app"/>
+  <int value="23"
+      label="(Native app) User waited for the app to finish installing"/>
+  <int value="24" label="(Web app) User installed a web app"/>
 </enum>
 
 <enum name="AppCacheCheckResponseResult" type="int">
@@ -43438,6 +43834,11 @@
   <int value="18" label="SOURCE_TEST"/>
 </enum>
 
+<enum name="AppListDoodleAction" type="int">
+  <int value="0" label="DOODLE_SHOWN"/>
+  <int value="1" label="DOODLE_CLICKED"/>
+</enum>
+
 <enum name="AppListEnableSource" type="int">
   <int value="0" label="Not enabled (should never be recorded)"/>
   <int value="1" label="Packaged app installed from Web Store"/>
@@ -43447,6 +43848,13 @@
   <int value="5" label="Second packaged app installed without showing"/>
 </enum>
 
+<enum name="AppListPage" type="int">
+  <int value="0" label="APPS"/>
+  <int value="1" label="SEARCH_RESULTS"/>
+  <int value="2" label="START"/>
+  <int value="3" label="CUSTOM_LAUNCHER_PAGE"/>
+</enum>
+
 <enum name="AppListSearchResult" type="int">
   <int value="0" label="OMNIBOX"/>
   <int value="1" label="APP"/>
@@ -43456,6 +43864,13 @@
   <int value="5" label="SUGGESTION"/>
 </enum>
 
+<enum name="AppListSearchResultDisplayType" type="int">
+  <int value="0" label="NONE"/>
+  <int value="1" label="LIST"/>
+  <int value="2" label="TILE"/>
+  <int value="3" label="RECOMMENDATION"/>
+</enum>
+
 <enum name="AppLocation" type="int">
   <int value="0" label="Invalid location"/>
   <int value="1" label="Internal extension"/>
@@ -43786,7 +44201,22 @@
 </enum>
 
 <enum name="AutofillFormEvent" type="int">
-  <int value="1" label="Interacted once"/>
+  <int value="0" label="Interacted (once)"/>
+  <int value="1" label="Suggestions shown"/>
+  <int value="2" label="Suggestions shown (once)"/>
+  <int value="3" label="Local suggestion filled"/>
+  <int value="4" label="Server suggestion filled"/>
+  <int value="5" label="Masked server card suggestion filled"/>
+  <int value="6" label="Local suggestion filled (once)"/>
+  <int value="7" label="Server suggestion filled (once)"/>
+  <int value="8" label="Masked server card suggestion filled (once)"/>
+  <int value="9" label="Submitted with no suggestion filled (once)"/>
+  <int value="10" label="Submitted with local suggestion filled (once)"/>
+  <int value="11" label="Submitted with server suggestion filled (once)"/>
+  <int value="12"
+      label="Submitted with masked server card suggestion filled (once)"/>
+  <int value="13" label="Masked server card suggestion selected"/>
+  <int value="14" label="Masked server card suggestion selected (once)"/>
 </enum>
 
 <enum name="AutofillMacAddressBook" type="int">
@@ -44131,6 +44561,11 @@
   <int value="1" label="Ignored"/>
 </enum>
 
+<enum name="BooleanIsMobileOptimized" type="int">
+  <int value="0" label="Not mobile optimized web page"/>
+  <int value="1" label="Mobile optimized web page"/>
+</enum>
+
 <enum name="BooleanLoaded" type="int">
   <int value="0" label="Not loaded"/>
   <int value="1" label="Loaded"/>
@@ -45365,6 +45800,7 @@
   <int value="9" label="File saved in workspace"/>
   <int value="10" label="Device mode enabled"/>
   <int value="11" label="Animations playback rate changed"/>
+  <int value="12" label="Revision applied"/>
 </enum>
 
 <enum name="DevToolsPanel" type="int">
@@ -45979,6 +46415,34 @@
   <int value="6" label="Dismiss ('done')"/>
 </enum>
 
+<enum name="EasyUnlockDeviceIneligibilityReason" type="int">
+  <int value="0" label="Unrecognized reason">
+    The server reported a reason that the client is not aware of. This should
+    only be recorded if the client's list of possible reasons is out of date.
+  </int>
+  <int value="1" label="Unknown">
+    The server returned the value &quot;Unknown&quot;.
+  </int>
+  <int value="2" label="Screen too large"/>
+  <int value="3" label="Phone lacks Bluetooth"/>
+  <int value="4" label="Bad OS version"/>
+  <int value="5" label="Bad software version (GMS Core)"/>
+  <int value="6" label="Auto unlocking not supported"/>
+  <int value="7" label="Invalid credentials"/>
+  <int value="8" label="Device offline"/>
+  <int value="9" label="No recent updates"/>
+</enum>
+
+<enum name="EasyUnlockHasSecureScreenLock" type="int">
+  <int value="0" label="Lacks secure screen lock"/>
+  <int value="1" label="Has secure screen lock"/>
+</enum>
+
+<enum name="EasyUnlockHasTrustAgentEnabled" type="int">
+  <int value="0" label="No trust agents enabled"/>
+  <int value="1" label="1+ trust agents enabled"/>
+</enum>
+
 <enum name="EasyUnlockNotificationEvent" type="int">
   <int value="0" label="Set up notification shown"/>
   <int value="1" label="Set up notification clicked"/>
@@ -46007,6 +46471,11 @@
   <int value="7" label="Help"/>
 </enum>
 
+<enum name="EasyUnlockTrialRunEvents" type="int">
+  <int value="0" label="Trial run launched"/>
+  <int value="1" label="User clicked lock icon"/>
+</enum>
+
 <enum name="EasyUnlockUnlockEvent" type="int">
   <int value="0" label="Screen unlocked (total)"/>
   <int value="1" label="Screen unlocked (via EasyUnlock)"/>
@@ -47912,6 +48381,13 @@
   <int value="960" label="FILEMANAGERPRIVATE_ISUMAENABLED"/>
   <int value="961" label="WEBVIEWINTERNAL_SETALLOWSCALING"/>
   <int value="962" label="PLATFORMKEYSINTERNAL_GETPUBLICKEY"/>
+  <int value="963" label="RUNTIME_OPENOPTIONSPAGE"/>
+  <int value="964" label="AUDIOMODEM_TRANSMIT"/>
+  <int value="965" label="AUDIOMODEM_STOPTRANSMIT"/>
+  <int value="966" label="AUDIOMODEM_RECEIVE"/>
+  <int value="967" label="AUDIOMODEM_STOPRECEIVE"/>
+  <int value="968" label="WEBRTCLOGGINGPRIVATE_STORE"/>
+  <int value="969" label="WEBRTCLOGGINGPRIVATE_UPLOADSTORED"/>
 </enum>
 
 <enum name="ExtensionInstallCause" type="int">
@@ -50207,6 +50683,7 @@
   <int value="3" label="BlacklistLoad"/>
   <int value="4" label="OmniboxInteraction"/>
   <int value="5" label="VariationsSeedSignature"/>
+  <int value="6" label="ScriptRequest"/>
 </enum>
 
 <enum name="Inconsistencies" type="int">
@@ -51299,6 +51776,143 @@
   <int value="10" label="2.19"/>
 </enum>
 
+<enum name="LinuxSandboxStatus" type="int">
+  <int value="0" label="none"/>
+  <int value="1" label="suid"/>
+  <int value="2" label="pidns"/>
+  <int value="3" label="suid, pidns"/>
+  <int value="4" label="netns"/>
+  <int value="5" label="suid, netns"/>
+  <int value="6" label="pidns, netns"/>
+  <int value="7" label="suid, pidns, netns"/>
+  <int value="8" label="seccomp-bpf"/>
+  <int value="9" label="suid, seccomp-bpf"/>
+  <int value="10" label="pidns, seccomp-bpf"/>
+  <int value="11" label="suid, pidns, seccomp-bpf"/>
+  <int value="12" label="netns, seccomp-bpf"/>
+  <int value="13" label="suid, netns, seccomp-bpf"/>
+  <int value="14" label="pidns, netns, seccomp-bpf"/>
+  <int value="15" label="suid, pidns, netns, seccomp-bpf"/>
+  <int value="16" label="yama"/>
+  <int value="17" label="suid, yama"/>
+  <int value="18" label="pidns, yama"/>
+  <int value="19" label="suid, pidns, yama"/>
+  <int value="20" label="netns, yama"/>
+  <int value="21" label="suid, netns, yama"/>
+  <int value="22" label="pidns, netns, yama"/>
+  <int value="23" label="suid, pidns, netns, yama"/>
+  <int value="24" label="seccomp-bpf, yama"/>
+  <int value="25" label="suid, seccomp-bpf, yama"/>
+  <int value="26" label="pidns, seccomp-bpf, yama"/>
+  <int value="27" label="suid, pidns, seccomp-bpf, yama"/>
+  <int value="28" label="netns, seccomp-bpf, yama"/>
+  <int value="29" label="suid, netns, seccomp-bpf, yama"/>
+  <int value="30" label="pidns, netns, seccomp-bpf, yama"/>
+  <int value="31" label="suid, pidns, netns, seccomp-bpf, yama"/>
+  <int value="32" label="seccomp-tsync"/>
+  <int value="33" label="suid, seccomp-tsync"/>
+  <int value="34" label="pidns, seccomp-tsync"/>
+  <int value="35" label="suid, pidns, seccomp-tsync"/>
+  <int value="36" label="netns, seccomp-tsync"/>
+  <int value="37" label="suid, netns, seccomp-tsync"/>
+  <int value="38" label="pidns, netns, seccomp-tsync"/>
+  <int value="39" label="suid, pidns, netns, seccomp-tsync"/>
+  <int value="40" label="seccomp-bpf, seccomp-tsync"/>
+  <int value="41" label="suid, seccomp-bpf, seccomp-tsync"/>
+  <int value="42" label="pidns, seccomp-bpf, seccomp-tsync"/>
+  <int value="43" label="suid, pidns, seccomp-bpf, seccomp-tsync"/>
+  <int value="44" label="netns, seccomp-bpf, seccomp-tsync"/>
+  <int value="45" label="suid, netns, seccomp-bpf, seccomp-tsync"/>
+  <int value="46" label="pidns, netns, seccomp-bpf, seccomp-tsync"/>
+  <int value="47" label="suid, pidns, netns, seccomp-bpf, seccomp-tsync"/>
+  <int value="48" label="yama, seccomp-tsync"/>
+  <int value="49" label="suid, yama, seccomp-tsync"/>
+  <int value="50" label="pidns, yama, seccomp-tsync"/>
+  <int value="51" label="suid, pidns, yama, seccomp-tsync"/>
+  <int value="52" label="netns, yama, seccomp-tsync"/>
+  <int value="53" label="suid, netns, yama, seccomp-tsync"/>
+  <int value="54" label="pidns, netns, yama, seccomp-tsync"/>
+  <int value="55" label="suid, pidns, netns, yama, seccomp-tsync"/>
+  <int value="56" label="seccomp-bpf, yama, seccomp-tsync"/>
+  <int value="57" label="suid, seccomp-bpf, yama, seccomp-tsync"/>
+  <int value="58" label="pidns, seccomp-bpf, yama, seccomp-tsync"/>
+  <int value="59" label="suid, pidns, seccomp-bpf, yama, seccomp-tsync"/>
+  <int value="60" label="netns, seccomp-bpf, yama, seccomp-tsync"/>
+  <int value="61" label="suid, netns, seccomp-bpf, yama, seccomp-tsync"/>
+  <int value="62" label="pidns, netns, seccomp-bpf, yama, seccomp-tsync"/>
+  <int value="63" label="suid, pidns, netns, seccomp-bpf, yama, seccomp-tsync"/>
+  <int value="64" label="userns"/>
+  <int value="65" label="suid, userns"/>
+  <int value="66" label="userns, pidns"/>
+  <int value="67" label="suid, userns, pidns"/>
+  <int value="68" label="userns, netns"/>
+  <int value="69" label="suid, userns, netns"/>
+  <int value="70" label="userns, pidns, netns"/>
+  <int value="71" label="suid, userns, pidns, netns"/>
+  <int value="72" label="userns, seccomp-bpf"/>
+  <int value="73" label="suid, userns, seccomp-bpf"/>
+  <int value="74" label="userns, pidns, seccomp-bpf"/>
+  <int value="75" label="suid, userns, pidns, seccomp-bpf"/>
+  <int value="76" label="userns, netns, seccomp-bpf"/>
+  <int value="77" label="suid, userns, netns, seccomp-bpf"/>
+  <int value="78" label="userns, pidns, netns, seccomp-bpf"/>
+  <int value="79" label="suid, userns, pidns, netns, seccomp-bpf"/>
+  <int value="80" label="userns, yama"/>
+  <int value="81" label="suid, userns, yama"/>
+  <int value="82" label="userns, pidns, yama"/>
+  <int value="83" label="suid, userns, pidns, yama"/>
+  <int value="84" label="userns, netns, yama"/>
+  <int value="85" label="suid, userns, netns, yama"/>
+  <int value="86" label="userns, pidns, netns, yama"/>
+  <int value="87" label="suid, userns, pidns, netns, yama"/>
+  <int value="88" label="userns, seccomp-bpf, yama"/>
+  <int value="89" label="suid, userns, seccomp-bpf, yama"/>
+  <int value="90" label="userns, pidns, seccomp-bpf, yama"/>
+  <int value="91" label="suid, userns, pidns, seccomp-bpf, yama"/>
+  <int value="92" label="userns, netns, seccomp-bpf, yama"/>
+  <int value="93" label="suid, userns, netns, seccomp-bpf, yama"/>
+  <int value="94" label="userns, pidns, netns, seccomp-bpf, yama"/>
+  <int value="95" label="suid, userns, pidns, netns, seccomp-bpf, yama"/>
+  <int value="96" label="userns, seccomp-tsync"/>
+  <int value="97" label="suid, userns, seccomp-tsync"/>
+  <int value="98" label="userns, pidns, seccomp-tsync"/>
+  <int value="99" label="suid, userns, pidns, seccomp-tsync"/>
+  <int value="100" label="userns, netns, seccomp-tsync"/>
+  <int value="101" label="suid, userns, netns, seccomp-tsync"/>
+  <int value="102" label="userns, pidns, netns, seccomp-tsync"/>
+  <int value="103" label="suid, userns, pidns, netns, seccomp-tsync"/>
+  <int value="104" label="userns, seccomp-bpf, seccomp-tsync"/>
+  <int value="105" label="suid, userns, seccomp-bpf, seccomp-tsync"/>
+  <int value="106" label="userns, pidns, seccomp-bpf, seccomp-tsync"/>
+  <int value="107" label="suid, userns, pidns, seccomp-bpf, seccomp-tsync"/>
+  <int value="108" label="userns, netns, seccomp-bpf, seccomp-tsync"/>
+  <int value="109" label="suid, userns, netns, seccomp-bpf, seccomp-tsync"/>
+  <int value="110" label="userns, pidns, netns, seccomp-bpf, seccomp-tsync"/>
+  <int value="111"
+      label="suid, userns, pidns, netns, seccomp-bpf, seccomp-tsync"/>
+  <int value="112" label="userns, yama, seccomp-tsync"/>
+  <int value="113" label="suid, userns, yama, seccomp-tsync"/>
+  <int value="114" label="userns, pidns, yama, seccomp-tsync"/>
+  <int value="115" label="suid, userns, pidns, yama, seccomp-tsync"/>
+  <int value="116" label="userns, netns, yama, seccomp-tsync"/>
+  <int value="117" label="suid, userns, netns, yama, seccomp-tsync"/>
+  <int value="118" label="userns, pidns, netns, yama, seccomp-tsync"/>
+  <int value="119" label="suid, userns, pidns, netns, yama, seccomp-tsync"/>
+  <int value="120" label="userns, seccomp-bpf, yama, seccomp-tsync"/>
+  <int value="121" label="suid, userns, seccomp-bpf, yama, seccomp-tsync"/>
+  <int value="122" label="userns, pidns, seccomp-bpf, yama, seccomp-tsync"/>
+  <int value="123"
+      label="suid, userns, pidns, seccomp-bpf, yama, seccomp-tsync"/>
+  <int value="124" label="userns, netns, seccomp-bpf, yama, seccomp-tsync"/>
+  <int value="125"
+      label="suid, userns, netns, seccomp-bpf, yama, seccomp-tsync"/>
+  <int value="126"
+      label="userns, pidns, netns, seccomp-bpf, yama, seccomp-tsync"/>
+  <int value="127"
+      label="suid, userns, pidns, netns, seccomp-bpf, yama, seccomp-tsync"/>
+  <int value="2147483648" label="Invalid"/>
+</enum>
+
 <enum name="LinuxWindowManagerName" type="int">
   <int value="0" label="Other"/>
   <int value="1" label="Blackbox"/>
@@ -51421,6 +52035,7 @@
   <int value="-1571841513" label="enable-devtools-experiments"/>
   <int value="-1553477903" label="ash-disable-text-filtering-in-overview-mode"/>
   <int value="-1546903171" label="enable-touch-drag-drop"/>
+  <int value="-1514943439" label="ash-enable-swipe-to-close-in-overview-mode"/>
   <int value="-1514611301" label="enable-data-reduction-proxy-bypass-warnings"/>
   <int value="-1510839574" label="disable-sync-synced-notifications"/>
   <int value="-1497338981" label="disable-accelerated-overflow-scroll"/>
@@ -51729,6 +52344,7 @@
   <int value="1900529524" label="disable-touch-drag-drop"/>
   <int value="1906942630" label="enable-easy-unlock"/>
   <int value="1930901873" label="disable-sync-app-list"/>
+  <int value="1944156526" label="sync-url"/>
   <int value="1961425320" label="force-qtkit"/>
   <int value="1966730288" label="disable-threaded-compositing"/>
   <int value="1969604362" label="enable-pinch-virtual-viewport"/>
@@ -54251,6 +54867,7 @@
       label="Opened automatically / Confirming generated password saved"/>
   <int value="5"
       label="Opened automatically / Offering a credential to choose"/>
+  <int value="6" label="Opened automatically / Auto-signin toast"/>
 </enum>
 
 <enum name="PasswordGenerationEvent" type="int">
@@ -54734,6 +55351,7 @@
   <int value="8" label="Clicked on a credential"/>
   <int value="9" label="Clicked collect URL"/>
   <int value="10" label="Clicked do not collect URL"/>
+  <int value="11" label="Auto-signin toast timeout"/>
 </enum>
 
 <enum name="PeerConnectionCounters" type="int">
@@ -54845,6 +55463,7 @@
   <int value="1218354710" label="PPB_VideoFrame;0.1"/>
   <int value="1260990020" label="PPB_Ext_Socket(Dev);0.2"/>
   <int value="1262240942" label="PPB_FileIO;1.1"/>
+  <int value="1262834677" label="PPB_CameraDevice_Private;0.1"/>
   <int value="1272679676" label="PPB_TCPSocket_Private;0.5"/>
   <int value="1296231808" label="PPB_VideoDecoder;0.1"/>
   <int value="1316246754" label="PPB_KeyboardInputEvent;1.0"/>
@@ -55047,6 +55666,13 @@
   <int value="4" label="Essential Cross-Origin Tiny"/>
 </enum>
 
+<enum name="PluginPowerSaverPosterParamPresence" type="int">
+  <int value="0" label="No poster param. Plugin power saver disabled."/>
+  <int value="1" label="No poster param. Plugin power saver enabled."/>
+  <int value="2" label="Poster param exists. Plugin power saver disabled."/>
+  <int value="3" label="Poster param exists. Plugin power saver enabled."/>
+</enum>
+
 <enum name="PluginPowerSaverUnthrottleMethod" type="int">
   <int value="0" label="Never Unthrottled"/>
   <int value="1" label="Unthrottled by Click"/>
@@ -55568,6 +56194,7 @@
   <int value="7" label="INITIATOR_TAB_CLOSED"/>
   <int value="8" label="PRINT_WITH_CLOUD_PRINT"/>
   <int value="9" label="PRINT_WITH_PRIVET"/>
+  <int value="10" label="PRINT_WITH_EXTENSION"/>
 </enum>
 
 <enum name="PrintSettings" type="int">
@@ -56197,16 +56824,47 @@
   <int value="254" label="DOMAIN_SPIDEROAK_COM"/>
 </enum>
 
+<enum name="PushDeliveryStatus" type="int">
+  <int value="0" label="Successful"/>
+  <int value="1" label="Message was invalid"/>
+  <int value="2" label="App id was unknown"/>
+  <int value="3" label="App no longer has permission"/>
+  <int value="4" label="Service Worker not found"/>
+  <int value="5" label="Service Worker error"/>
+  <int value="6" label="event.waitUntil promise rejected"/>
+</enum>
+
+<enum name="PushGetRegistrationStatus" type="int">
+  <int value="0" label="Successful"/>
+  <int value="1" label="No push service"/>
+  <int value="2" label="Storage error"/>
+  <int value="3" label="Registration not found"/>
+  <int value="4" label="Registration not found (no incognito push service)"/>
+</enum>
+
 <enum name="PushRegistrationStatus" type="int">
   <int value="0" label="Successful - from push service"/>
-  <int value="1" label="Page has no active Service Worker"/>
-  <int value="2" label="Push service not available"/>
+  <int value="1" label="Service Worker not found"/>
+  <int value="2" label="No push service"/>
   <int value="3" label="Registration limit reached"/>
   <int value="4" label="Permission denied"/>
   <int value="5" label="Push service error"/>
   <int value="6" label="No sender id provided"/>
   <int value="7" label="Storage error"/>
   <int value="8" label="Successful - from cache"/>
+  <int value="9" label="Network error"/>
+  <int value="10" label="Permission denied (no incognito push service)"/>
+</enum>
+
+<enum name="PushUnregistrationStatus" type="int">
+  <int value="0" label="Successful - from push service"/>
+  <int value="1" label="Successful - was not registered"/>
+  <int value="2" label="Pending - will retry network error"/>
+  <int value="3" label="Service Worker not found"/>
+  <int value="4" label="No push service"/>
+  <int value="5" label="Push service error"/>
+  <int value="6" label="Storage error"/>
+  <int value="7" label="Network error"/>
 </enum>
 
 <enum name="PushUserVisibleStatus" type="int">
@@ -58542,6 +59200,16 @@
   <int value="3" label="NOT_EXPIRED_AND_DO_NOT_PROCEED"/>
 </enum>
 
+<enum name="SSLOrQUICVersion" type="int">
+  <int value="0" label="Unknown"/>
+  <int value="1" label="SSL 2.0"/>
+  <int value="2" label="SSL 3.0"/>
+  <int value="3" label="TLS 1.0"/>
+  <int value="4" label="TLS 1.1"/>
+  <int value="5" label="TLS 1.2"/>
+  <int value="7" label="QUIC"/>
+</enum>
+
 <enum name="SSLProtocolNegotiation" type="int">
   <int value="1" label="ALPN, HTTP/1.1"/>
   <int value="100" label="ALPN, SPDY 2.0"/>
@@ -59774,6 +60442,21 @@
   <int value="6" label="L2TP/IPSec Username/Password"/>
 </enum>
 
+<enum name="VTVDAInitializationFailureType" type="int">
+  <int value="0" label="Successfully Initialized"/>
+  <int value="1" label="Framework Load Error"/>
+  <int value="2" label="Hardware Session Error"/>
+  <int value="3" label="Software Session Error"/>
+</enum>
+
+<enum name="VTVDASessionFailureType" type="int">
+  <int value="0" label="Successfully Initialized"/>
+  <int value="1" label="Platform Error"/>
+  <int value="2" label="Invalid Stream"/>
+  <int value="3" label="Unsupported Stream"/>
+  <int value="4" label="Decode Error"/>
+</enum>
+
 <enum name="WakeOnWiFiFeaturesEnabledState" type="int">
   <summary>
     The wake on WiFi features enabled in shill, which come from
@@ -60177,7 +60860,7 @@
   <affected-histogram name="Renderer4.StartToFinish"/>
 </histogram_suffixes>
 
-<histogram_suffixes name="AutofillDataAvailability">
+<histogram_suffixes name="AutofillDataAvailability" separator=".">
   <suffix name="WithNoData" label="no autofill data"/>
   <suffix name="WithOnlyServerData" label="only server autofill data"/>
   <suffix name="WithOnlyLocalData" label="only local autofill data"/>
@@ -63800,6 +64483,13 @@
   <affected-histogram name="SB2.MalwareInterstitialTimeTakeMeBack"/>
 </histogram_suffixes>
 
+<histogram_suffixes name="ScrollUpdateHandledThread">
+  <affected-histogram name="Event.Latency.ScrollUpdate.HandledToRendererSwap"/>
+  <suffix name="Main" label="ScrollUpdate handled on main thread"/>
+  <suffix name="Impl" label="ScrollUpdate handled on impl thread"/>
+  <affected-histogram name="Event.Latency.ScrollUpdate.TouchToHandled"/>
+</histogram_suffixes>
+
 <histogram_suffixes name="SecurityInterstitialType" separator="."
     ordering="prefix">
   <suffix name="bad_clock"/>
diff --git a/tools/metrics/rappor/rappor.xml b/tools/metrics/rappor/rappor.xml
index 8e52f4a..62ae0d5 100644
--- a/tools/metrics/rappor/rappor.xml
+++ b/tools/metrics/rappor/rappor.xml
@@ -43,6 +43,52 @@
 <rappor-metrics>
 <!-- Rappor metric definitions -->
 
+<rappor-metric name="AppBanner.NativeApp.Dismissed" type="ETLD_PLUS_ONE">
+  <owner>dfalcantara@chromium.org</owner>
+  <summary>
+    The eTLD+1 of a URL that displayed a native app banner, which was explicitly
+    dismissed by the user.
+  </summary>
+</rappor-metric>
+
+<rappor-metric name="AppBanner.NativeApp.Installed" type="ETLD_PLUS_ONE">
+  <owner>dfalcantara@chromium.org</owner>
+  <summary>
+    The eTLD+1 of a URL that displayed a native app banner, which resulted in
+    the user installing the promoted application.
+  </summary>
+</rappor-metric>
+
+<rappor-metric name="AppBanner.NativeApp.Shown" type="ETLD_PLUS_ONE">
+  <owner>dfalcantara@chromium.org</owner>
+  <summary>
+    The eTLD+1 of a URL that displayed a native app banner.
+  </summary>
+</rappor-metric>
+
+<rappor-metric name="AppBanner.WebApp.Dismissed" type="ETLD_PLUS_ONE">
+  <owner>dfalcantara@chromium.org</owner>
+  <summary>
+    The eTLD+1 of a URL that displayed a web app banner, which was explicitly
+    dismissed by the user.
+  </summary>
+</rappor-metric>
+
+<rappor-metric name="AppBanner.WebApp.Installed" type="ETLD_PLUS_ONE">
+  <owner>dfalcantara@chromium.org</owner>
+  <summary>
+    The eTLD+1 of a URL that displayed a web app banner, which resulted in the
+    user installing the promoted application.
+  </summary>
+</rappor-metric>
+
+<rappor-metric name="AppBanner.WebApp.Shown" type="ETLD_PLUS_ONE">
+  <owner>dfalcantara@chromium.org</owner>
+  <summary>
+    The eTLD+1 of a URL that displayed a web app banner.
+  </summary>
+</rappor-metric>
+
 <rappor-metric name="ContentSettings.MixedScript.DisplayedShield"
     type="ETLD_PLUS_ONE">
   <owner>lgarron@chromium.org</owner>
diff --git a/tools/perf/OWNERS b/tools/perf/OWNERS
index 89bd9b6..f3784cc 100644
--- a/tools/perf/OWNERS
+++ b/tools/perf/OWNERS
@@ -2,11 +2,13 @@
 achuith@chromium.org
 dtu@chromium.org
 epenner@chromium.org
+nednguyen@google.com
+skyostil@chromium.org
+sullivan@chromium.org
+
+# emeritus:
 ernstm@chromium.org
 marja@chromium.org
 nduca@chromium.org
-nednguyen@google.com
 qyearsley@chromium.org
-skyostil@chromium.org
-sullivan@chromium.org
-tonyg@chromium.org
+tonyg@chromium.org
\ No newline at end of file
diff --git a/tools/perf/benchmarks/memory.py b/tools/perf/benchmarks/memory.py
index 301c8d01..244edad 100644
--- a/tools/perf/benchmarks/memory.py
+++ b/tools/perf/benchmarks/memory.py
@@ -17,7 +17,7 @@
     return 'memory.mobile_memory'
 
 
-@benchmark.Disabled  # http://crbug.com/440305
+@benchmark.Disabled  # http://crbug.com/459646
 class MemoryTop7Stress(benchmark.Benchmark):
   """Use (recorded) real world web sites and measure memory consumption."""
   test = memory.Memory
@@ -26,40 +26,3 @@
   @classmethod
   def Name(cls):
     return 'memory.top_7_stress'
-
-
-@benchmark.Disabled
-class Reload2012Q3(benchmark.Benchmark):
-  """Memory consumption for a set of top pages from 2012.
-
-  Performs reloading and garbage collecting on each page load."""
-  tag = 'reload'
-  test = memory.Memory
-  page_set = page_sets.Top2012Q3StressPageSet
-
-  @classmethod
-  def Name(cls):
-    return 'memory.reload.top_desktop_sites_2012Q3_stress'
-
-
-@benchmark.Disabled  # crbug.com/371153
-class MemoryToughDomMemoryCases(benchmark.Benchmark):
-  test = memory.Memory
-  page_set = page_sets.ToughDomMemoryCasesPageSet
-
-  @classmethod
-  def Name(cls):
-    return 'memory.tough_dom_memory_cases'
-
-
-@benchmark.Disabled('chromeos', 'linux', 'mac', 'win')
-@benchmark.Enabled('has tabs')
-class TypicalMobileSites(benchmark.Benchmark):
-  """Long memory test."""
-  test = memory.Memory
-  page_set = page_sets.TypicalMobileSitesPageSet
-  options = {'pageset_repeat': 15}
-  @classmethod
-  def Name(cls):
-    return 'memory.typical_mobile_sites'
-
diff --git a/tools/perf/benchmarks/startup.py b/tools/perf/benchmarks/startup.py
index 3fa3e34..b68ae4c 100644
--- a/tools/perf/benchmarks/startup.py
+++ b/tools/perf/benchmarks/startup.py
@@ -52,47 +52,3 @@
   @classmethod
   def Name(cls):
     return 'startup.warm.blank_page'
-
-
-@benchmark.Disabled  # crbug.com/336913
-class StartupColdTheme(_StartupCold):
-  tag = 'theme_cold'
-  page_set = page_sets.BlankPageSet
-  generated_profile_archive = 'theme_profile.zip'
-
-  @classmethod
-  def Name(cls):
-    return 'startup.theme_cold.blank_page'
-
-
-@benchmark.Disabled
-class StartupWarmTheme(_StartupWarm):
-  tag = 'theme_warm'
-  page_set = page_sets.BlankPageSet
-  generated_profile_archive = 'theme_profile.zip'
-
-  @classmethod
-  def Name(cls):
-    return 'startup.theme_warm.blank_page'
-
-
-@benchmark.Disabled  # crbug.com/336913
-class StartupColdManyExtensions(_StartupCold):
-  tag = 'many_extensions_cold'
-  page_set = page_sets.BlankPageSet
-  generated_profile_archive = 'many_extensions_profile.zip'
-
-  @classmethod
-  def Name(cls):
-    return 'startup.many_extensions_cold.blank_page'
-
-
-@benchmark.Disabled
-class StartupWarmManyExtensions(_StartupWarm):
-  tag = 'many_extensions_warm'
-  page_set = page_sets.BlankPageSet
-  generated_profile_archive = 'many_extensions_profile.zip'
-  @classmethod
-  def Name(cls):
-    return 'startup.many_extensions_warm.blank_page'
-
diff --git a/tools/perf/benchmarks/tab_switching.py b/tools/perf/benchmarks/tab_switching.py
index c9109b1..313d11f 100644
--- a/tools/perf/benchmarks/tab_switching.py
+++ b/tools/perf/benchmarks/tab_switching.py
@@ -9,6 +9,7 @@
 
 
 @benchmark.Enabled('has tabs')
+@benchmark.Disabled('android')  # http://crbug.com/460084
 class TabSwitchingTop10(benchmark.Benchmark):
   """This test records the MPArch.RWH_TabSwitchPaintDuration histogram.
 
@@ -26,6 +27,7 @@
 
 
 @benchmark.Enabled('has tabs')
+@benchmark.Disabled('android')  # http://crbug.com/460084
 class TabSwitchingTypical25(benchmark.Benchmark):
   """This test records the MPArch.RWH_TabSwitchPaintDuration histogram.
 
@@ -44,7 +46,7 @@
     return 'tab_switching.typical_25'
 
 
-@benchmark.Disabled('mac')  # http://crbug.com/455349
+@benchmark.Disabled('android')  # http://crbug.com/460084
 @benchmark.Enabled('has tabs')
 class TabSwitchingFiveBlankTabs(benchmark.Benchmark):
   """This test records the MPArch.RWH_TabSwitchPaintDuration histogram.
@@ -64,6 +66,7 @@
 
 
 @benchmark.Enabled('has tabs')
+@benchmark.Disabled('android')  # http://crbug.com/460084
 class TabSwitchingToughEnergyCases(benchmark.Benchmark):
   """This test records the MPArch.RWH_TabSwitchPaintDuration histogram.
 
diff --git a/tools/perf/generated_profiles/darwin/many_extensions_profile.zip.sha1 b/tools/perf/generated_profiles/darwin/many_extensions_profile.zip.sha1
deleted file mode 100644
index b345807..0000000
--- a/tools/perf/generated_profiles/darwin/many_extensions_profile.zip.sha1
+++ /dev/null
@@ -1 +0,0 @@
-87f83fecf7b0838cf5f492e1977f6b32ef4b6a71
\ No newline at end of file
diff --git a/tools/perf/generated_profiles/darwin/theme_profile.zip.sha1 b/tools/perf/generated_profiles/darwin/theme_profile.zip.sha1
deleted file mode 100644
index 5801f43..0000000
--- a/tools/perf/generated_profiles/darwin/theme_profile.zip.sha1
+++ /dev/null
@@ -1 +0,0 @@
-157712d1b7aa71d08e8f807239840f153f36df2a
\ No newline at end of file
diff --git a/tools/perf/generated_profiles/linux/many_extensions_profile.zip.sha1 b/tools/perf/generated_profiles/linux/many_extensions_profile.zip.sha1
deleted file mode 100644
index 1652c2d..0000000
--- a/tools/perf/generated_profiles/linux/many_extensions_profile.zip.sha1
+++ /dev/null
@@ -1 +0,0 @@
-8d4186c01ede2f94b33edb37b38a069f25fe3718
\ No newline at end of file
diff --git a/tools/perf/generated_profiles/linux/theme_profile.zip.sha1 b/tools/perf/generated_profiles/linux/theme_profile.zip.sha1
deleted file mode 100644
index f7e5987..0000000
--- a/tools/perf/generated_profiles/linux/theme_profile.zip.sha1
+++ /dev/null
@@ -1 +0,0 @@
-86215e7d02356bc5c13c33a330029937804f8b2c
\ No newline at end of file
diff --git a/tools/perf/page_sets/data/tough_dom_memory_cases.json b/tools/perf/page_sets/data/tough_dom_memory_cases.json
deleted file mode 100644
index 1f443c5..0000000
--- a/tools/perf/page_sets/data/tough_dom_memory_cases.json
+++ /dev/null
@@ -1,15 +0,0 @@
-{
-    "description": "Describes the Web Page Replay archives for a page set. Don't edit by hand! Use record_wpr for updating.", 
-    "archives": {
-        "tough_dom_memory_cases_000.wpr": [
-            "http://en.blog.wordpress.com/2012/09/04/freshly-pressed-editors-picks-for-august-2012/", 
-            "http://en.blog.wordpress.com/2012/09/06/new-themes-gridspace-and-ascetica/", 
-            "http://en.blog.wordpress.com/2012/09/20/new-themes-gigawatt-and-pinboard/", 
-            "http://en.blog.wordpress.com/2012/10/01/freshly-pressed-editors-picks-for-september-2012/", 
-            "https://twitter.com/katyperry", 
-            "https://twitter.com/justinbieber", 
-            "https://twitter.com/BarackObama", 
-            "https://twitter.com/ladygaga"
-        ]
-    }
-}
\ No newline at end of file
diff --git a/tools/perf/page_sets/data/tough_dom_memory_cases_000.wpr.sha1 b/tools/perf/page_sets/data/tough_dom_memory_cases_000.wpr.sha1
deleted file mode 100644
index 413a30cf..0000000
--- a/tools/perf/page_sets/data/tough_dom_memory_cases_000.wpr.sha1
+++ /dev/null
@@ -1 +0,0 @@
-23b9df19b810b3cd2c4296de841bcbf51f91e12d
\ No newline at end of file
diff --git a/tools/perf/page_sets/data/typical_mobile_sites.json b/tools/perf/page_sets/data/typical_mobile_sites.json
deleted file mode 100644
index bf59fe1..0000000
--- a/tools/perf/page_sets/data/typical_mobile_sites.json
+++ /dev/null
@@ -1,26 +0,0 @@
-{
-    "description": "Describes the Web Page Replay archives for a page set. Don't edit by hand! Use record_wpr for updating.", 
-    "archives": {
-        "typical_mobile_sites_000.wpr": [
-            "http://baidu.com", 
-            "http://sfbay.craigslist.org", 
-            "http://cnn.com", 
-            "http://amazon.co.jp", 
-            "http://latimes.com", 
-            "http://yandex.ru", 
-            "http://wikipedia.com", 
-            "http://yahoo.co.jp", 
-            "http://apple.com", 
-            "http://nytimes.com", 
-            "http://amazon.com", 
-            "http://weibo.com", 
-            "http://ebay.com", 
-            "http://google.com", 
-            "http://bing.com", 
-            "http://bbc.co.uk", 
-            "http://sina.com.cn", 
-            "http://yahoo.com", 
-            "http://chicagotribune.com"
-        ]
-    }
-}
diff --git a/tools/perf/page_sets/data/typical_mobile_sites_000.wpr.sha1 b/tools/perf/page_sets/data/typical_mobile_sites_000.wpr.sha1
deleted file mode 100644
index abf283a..0000000
--- a/tools/perf/page_sets/data/typical_mobile_sites_000.wpr.sha1
+++ /dev/null
@@ -1 +0,0 @@
-bc8ff193859f5aacaf1215a9b2c0a1a538f8ed72
\ No newline at end of file
diff --git a/tools/perf/page_sets/polymer.py b/tools/perf/page_sets/polymer.py
index db022e2..8afe9aa1 100644
--- a/tools/perf/page_sets/polymer.py
+++ b/tools/perf/page_sets/polymer.py
@@ -23,9 +23,9 @@
     '''
     self._run_no_page_interactions = run_no_page_interactions
 
-  def RunPageInteraction(self, action_runner):
-    # If a polymer page wants to customize it actions, it should
-    # overrides the PerformPageInteractions method instead of this method.
+  def RunPageInteractions(self, action_runner):
+    # If a polymer page wants to customize its actions, it should
+    # override the PerformPageInteractions method instead of this method.
     if self._run_no_page_interactions:
       return
     self.PerformPageInteractions(action_runner)
diff --git a/tools/perf/page_sets/top_desktop_sites_2012Q3_stress.py b/tools/perf/page_sets/top_desktop_sites_2012Q3_stress.py
deleted file mode 100644
index b94fca87..0000000
--- a/tools/perf/page_sets/top_desktop_sites_2012Q3_stress.py
+++ /dev/null
@@ -1,34 +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.
-
-from page_sets import top_desktop_sites_2012Q3
-
-from telemetry.page import page_set
-from telemetry.page import page
-
-
-class Top2012Q3StressPage(page.Page):
-
-  def __init__(self, url, ps):
-    super(Top2012Q3StressPage, self).__init__(
-        url=url, page_set=ps, credentials_path='data/credentials.json')
-    self.archive_data_file = 'data/2012Q3.json'
-
-  def RunPageInteractions(self, action_runner):
-    for _ in xrange(3):
-      action_runner.ReloadPage()
-      action_runner.Wait(1)
-      action_runner.ForceGarbageCollection()
-
-
-class Top2012Q3StressPageSet(page_set.PageSet):
-  """ Pages hand-picked from top-lists in Q32012. """
-
-  def __init__(self):
-    super(Top2012Q3StressPageSet, self).__init__(
-      archive_data_file='data/2012Q3.json',
-      bucket=page_set.PARTNER_BUCKET)
-
-    for url in top_desktop_sites_2012Q3.TOP_2013_URLS:
-      self.AddUserStory(Top2012Q3StressPage(url, self))
diff --git a/tools/perf/page_sets/tough_dom_memory_cases.py b/tools/perf/page_sets/tough_dom_memory_cases.py
deleted file mode 100644
index c697d97..0000000
--- a/tools/perf/page_sets/tough_dom_memory_cases.py
+++ /dev/null
@@ -1,49 +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.
-from telemetry.page import page as page_module
-from telemetry.page import page_set as page_set_module
-
-
-class ToughDomMemoryCasesPage(page_module.Page):
-
-  def __init__(self, url, page_set):
-    super(ToughDomMemoryCasesPage, self).__init__(url=url, page_set=page_set)
-    self.user_agent_type = 'desktop'
-    self.archive_data_file = 'data/tough_dom_memory_cases.json'
-
-  def RunPageInteractions(self, action_runner):
-    interaction = action_runner.BeginGestureInteraction(
-        'ScrollAction', is_smooth=True)
-    action_runner.ScrollPage()
-    interaction.End()
-
-class ToughDomMemoryCasesPageSet(page_set_module.PageSet):
-
-  """
-  Websites in top 25 where some DOM objects seem to remain even
-  after closing the page.
-  """
-
-  def __init__(self):
-    super(ToughDomMemoryCasesPageSet, self).__init__(
-      user_agent_type='desktop',
-      archive_data_file='data/tough_dom_memory_cases.json',
-      bucket=page_set_module.PARTNER_BUCKET)
-
-    urls_list = [
-      # pylint: disable=C0301
-      'http://en.blog.wordpress.com/2012/09/04/freshly-pressed-editors-picks-for-august-2012/',
-      # pylint: disable=C0301
-      'http://en.blog.wordpress.com/2012/09/06/new-themes-gridspace-and-ascetica/',
-      # pylint: disable=C0301
-      'http://en.blog.wordpress.com/2012/09/20/new-themes-gigawatt-and-pinboard/',
-      # pylint: disable=C0301
-      'https://twitter.com/katyperry',
-      'https://twitter.com/justinbieber',
-      'https://twitter.com/BarackObama',
-      'https://twitter.com/ladygaga'
-    ]
-
-    for url in urls_list:
-      self.AddUserStory(ToughDomMemoryCasesPage(url, self))
diff --git a/tools/perf/page_sets/typical_mobile_sites.py b/tools/perf/page_sets/typical_mobile_sites.py
deleted file mode 100644
index 0654f48..0000000
--- a/tools/perf/page_sets/typical_mobile_sites.py
+++ /dev/null
@@ -1,53 +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.
-
-from telemetry.page import page as page_module
-from telemetry.page import page_set as page_set_module
-
-
-class TypicalMobileSitesPage(page_module.Page):
-
-  def __init__(self, url, page_set):
-    super(TypicalMobileSitesPage, self).__init__(
-        url=url, page_set=page_set, credentials_path = 'data/credentials.json')
-    self.user_agent_type = 'mobile'
-    self.archive_data_file = 'data/typical_mobile_sites.json'
-
-  def RunPageInteractions(self, action_runner):
-    action_runner.Wait(5)
-    action_runner.ScrollPage()
-
-
-class TypicalMobileSitesPageSet(page_set_module.PageSet):
-
-  def __init__(self):
-    super(TypicalMobileSitesPageSet, self).__init__(
-        user_agent_type='mobile',
-        archive_data_file='data/typical_mobile_sites.json',
-        bucket=page_set_module.PARTNER_BUCKET)
-
-    urls_list = [
-       'http://google.com',
-       'http://yahoo.com',
-       'http://baidu.com',
-       'http://cnn.com',
-       'http://yandex.ru',
-       'http://yahoo.co.jp',
-       'http://amazon.com',
-       'http://ebay.com',
-       'http://bing.com',
-       'http://nytimes.com',
-       'http://latimes.com',
-       'http://chicagotribune.com',
-       'http://wikipedia.com',
-       'http://sfbay.craigslist.org',
-       'http://bbc.co.uk',
-       'http://apple.com',
-       'http://amazon.co.jp',
-       'http://sina.com.cn',
-       'http://weibo.com',
-    ]
-
-    for url in urls_list:
-      self.AddUserStory(TypicalMobileSitesPage(url, self))
diff --git a/tools/perf/profile_creators/cookie_profile_extender.py b/tools/perf/profile_creators/cookie_profile_extender.py
new file mode 100644
index 0000000..854e969
--- /dev/null
+++ b/tools/perf/profile_creators/cookie_profile_extender.py
@@ -0,0 +1,68 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+import multiprocessing
+import os
+import sqlite3
+
+from profile_creators import fast_navigation_profile_extender
+from profile_creators import profile_safe_url_list
+
+class CookieProfileExtender(
+    fast_navigation_profile_extender.FastNavigationProfileExtender):
+  """This extender performs a large number of navigations (up to 500), with the
+  goal of filling out the cookie database.
+
+  By default, Chrome purges the cookie DB down to 3300 cookies. However, it
+  won't purge cookies accessed in the last month. This means the extender needs
+  to be careful not to create an artificially high number of cookies.
+  """
+  _COOKIE_DB_EXPECTED_SIZE = 3300
+
+  def __init__(self):
+    # The rate limiting factors are fetching network resources and executing
+    # javascript. There's not much to be done about the former, and having one
+    # tab per logical core appears close to optimum for the latter.
+    maximum_batch_size = multiprocessing.cpu_count()
+    super(CookieProfileExtender, self).__init__(maximum_batch_size)
+
+    # A list of urls that have not yet been navigated to. This list will shrink
+    # over time. Each navigation will add a diminishing number of new cookies,
+    # since there's a high probability that the cookie is already present. If
+    # the cookie DB isn't full by 500 navigations, just give up.
+    self._navigation_urls = profile_safe_url_list.GetShuffledSafeUrls()[0:500]
+
+  def GetUrlIterator(self):
+    """Superclass override."""
+    return iter(self._navigation_urls)
+
+  def ShouldExitAfterBatchNavigation(self):
+    """Superclass override."""
+    return self._IsCookieDBFull()
+
+  @staticmethod
+  def _CookieCountInDB(db_path):
+    """The number of cookies in the db at |db_path|."""
+    connection = sqlite3.connect(db_path)
+    try:
+      cursor = connection.cursor()
+      cursor.execute("select count(*) from cookies")
+      cookie_count = cursor.fetchone()[0]
+    except:
+      raise
+    finally:
+      connection.close()
+    return cookie_count
+
+  def _IsCookieDBFull(self):
+    """Chrome does not immediately flush cookies to its database. It's possible
+    that this method will return a false negative."""
+    cookie_db_path = os.path.join(self.profile_path, "Default", "Cookies")
+    try:
+      cookie_count = CookieProfileExtender._CookieCountInDB(cookie_db_path)
+    except sqlite3.OperationalError:
+      # There will occasionally be contention for the SQLite database. This
+      # shouldn't happen often, so ignore the errors.
+      return False
+
+    return cookie_count > CookieProfileExtender._COOKIE_DB_EXPECTED_SIZE
diff --git a/tools/perf/profile_creators/cookie_profile_extender_unittest.py b/tools/perf/profile_creators/cookie_profile_extender_unittest.py
new file mode 100644
index 0000000..0dd4d010
--- /dev/null
+++ b/tools/perf/profile_creators/cookie_profile_extender_unittest.py
@@ -0,0 +1,48 @@
+# 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.
+import os
+import sqlite3
+import tempfile
+import unittest
+
+from profile_creators.cookie_profile_extender import CookieProfileExtender
+
+
+# Testing private method.
+# pylint: disable=protected-access
+class CookieProfileExtenderTest(unittest.TestCase):
+  def _CreateCookieTable(self, path):
+    connection = sqlite3.connect(path)
+    cursor = connection.cursor()
+    cursor.execute("CREATE TABLE cookies (url text)")
+    connection.commit()
+    connection.close()
+
+  def _AddCookiesToTable(self, path, count):
+    connection = sqlite3.connect(path)
+    cursor = connection.cursor()
+    for i in range(count):
+      cursor.execute("INSERT INTO cookies VALUES ('%s')" % i)
+    connection.commit()
+    connection.close()
+
+  def testCookieCount(self):
+    # Neither tempfile.TemporaryFile() nor tempfile.NamedTemporaryFile() work
+    # well here. The former doesn't work at all, since it doesn't gaurantee a
+    # file-system visible path. The latter doesn't work well, since the
+    # returned file cannot be opened a second time on Windows. The returned
+    # file would have to be closed, and the method would need to be called with
+    # Delete=False, which makes its functionality no simpler than
+    # tempfile.mkstemp().
+    handle, path = tempfile.mkstemp()
+    try:
+      os.close(handle)
+
+      self._CreateCookieTable(path)
+      self.assertEquals(CookieProfileExtender._CookieCountInDB(path), 0)
+
+      self._AddCookiesToTable(path, 100)
+      self.assertEquals(CookieProfileExtender._CookieCountInDB(path), 100)
+    finally:
+      os.remove(path)
diff --git a/tools/perf/profile_creators/fast_navigation_profile_extender.py b/tools/perf/profile_creators/fast_navigation_profile_extender.py
new file mode 100644
index 0000000..ec2cb7c
--- /dev/null
+++ b/tools/perf/profile_creators/fast_navigation_profile_extender.py
@@ -0,0 +1,251 @@
+# 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.
+import time
+
+from telemetry.core import browser_finder
+from telemetry.core import browser_finder_exceptions
+from telemetry.core import exceptions
+from telemetry.core import platform
+from telemetry.core import util
+
+
+class FastNavigationProfileExtender(object):
+  """Extends a Chrome profile.
+
+  This class creates or extends an existing profile by performing a set of tab
+  navigations in large batches. This is accomplished by opening a large number
+  of tabs, simultaneously navigating all the tabs, and then waiting for all the
+  tabs to load. This provides two benefits:
+    - Takes advantage of the high number of logical cores on modern CPUs.
+    - The total time spent waiting for navigations to time out scales linearly
+      with the number of batches, but does not scale with the size of the
+      batch.
+  """
+  def __init__(self, maximum_batch_size):
+    """Initializer.
+
+    Args:
+      maximum_batch_size: A positive integer indicating the number of tabs to
+      simultaneously perform navigations.
+    """
+    super(FastNavigationProfileExtender, self).__init__()
+
+    # The path of the profile that the browser will use while it's running.
+    # This member is initialized during SetUp().
+    self._profile_path = None
+
+    # A reference to the browser that will be performing all of the tab
+    # navigations.
+    # This member is initialized during SetUp().
+    self._browser = None
+
+    # The number of tabs to use.
+    self._NUM_TABS = maximum_batch_size
+
+    # The amount of time to wait for a batch of pages to finish loading.
+    self._BATCH_PAGE_LOAD_TIMEOUT_IN_SECONDS = 10
+
+    # The default amount of time to wait for the retrieval of the URL of a tab.
+    self._TAB_URL_RETRIEVAL_TIMEOUT_IN_SECONDS = 1
+
+  def Run(self, finder_options):
+    """Extends the profile.
+
+    Args:
+      finder_options: An instance of BrowserFinderOptions that contains the
+      directory of the input profile, the directory to place the output
+      profile, and sufficient information to choose a specific browser binary.
+    """
+    try:
+      self.SetUp(finder_options)
+      self._PerformNavigations()
+    finally:
+      self.TearDown()
+
+  def GetUrlIterator(self):
+    """Gets URLs for the browser to navigate to.
+
+    Intended for subclass override.
+
+    Returns:
+      An iterator whose elements are urls to be navigated to.
+    """
+    raise NotImplementedError()
+
+  def ShouldExitAfterBatchNavigation(self):
+    """Returns a boolean indicating whether profile extension is finished.
+
+    Intended for subclass override.
+    """
+    raise NotImplementedError()
+
+  def SetUp(self, finder_options):
+    """Finds the browser, starts the browser, and opens the requisite number of
+    tabs.
+
+    Can be overridden by subclasses. Subclasses must call the super class
+    implementation.
+    """
+    self._profile_path = finder_options.output_profile_path
+    possible_browser = self._GetPossibleBrowser(finder_options)
+
+    assert possible_browser.supports_tab_control
+    assert (platform.GetHostPlatform().GetOSName() in
+        ["win", "mac", "linux"])
+    self._browser = possible_browser.Create(finder_options)
+
+    while(len(self._browser.tabs) < self._NUM_TABS):
+      self._browser.tabs.New()
+
+  def TearDown(self):
+    """Teardown that is guaranteed to be executed before the instance is
+    destroyed.
+
+    Can be overridden by subclasses. Subclasses must call the super class
+    implementation.
+    """
+    if self._browser:
+      self._browser.Close()
+      self._browser = None
+
+  def CleanUpAfterBatchNavigation(self):
+    """A hook for subclasses to perform cleanup after each batch of
+    navigations.
+
+    Can be overridden by subclasses.
+    """
+    pass
+
+  @property
+  def profile_path(self):
+    return self._profile_path
+
+  def _GetPossibleBrowser(self, finder_options):
+    """Return a possible_browser with the given options."""
+    possible_browser = browser_finder.FindBrowser(finder_options)
+    if not possible_browser:
+      raise browser_finder_exceptions.BrowserFinderException(
+          'No browser found.\n\nAvailable browsers:\n%s\n' %
+          '\n'.join(browser_finder.GetAllAvailableBrowserTypes(finder_options)))
+    finder_options.browser_options.browser_type = (
+        possible_browser.browser_type)
+
+    return possible_browser
+
+  def _RetrieveTabUrl(self, tab, timeout):
+    """Retrives the URL of the tab."""
+    try:
+      return tab.EvaluateJavaScript('document.URL', timeout)
+    except exceptions.DevtoolsTargetCrashException:
+      return None
+
+  def _WaitForUrlToChange(self, tab, initial_url, timeout):
+    """Waits for the tab to navigate away from its initial url."""
+    end_time = time.time() + timeout
+    while True:
+      seconds_to_wait = end_time - time.time()
+      seconds_to_wait = max(0, seconds_to_wait)
+
+      if seconds_to_wait == 0:
+        break
+
+      current_url = self._RetrieveTabUrl(tab, seconds_to_wait)
+      if current_url != initial_url:
+        break
+
+      # Retrieving the current url is a non-trivial operation. Add a small
+      # sleep here to prevent this method from contending with the actual
+      # navigation.
+      time.sleep(0.01)
+
+  def _BatchNavigateTabs(self, batch):
+    """Performs a batch of tab navigations with minimal delay.
+
+    Args:
+      batch: A list of tuples (tab, url).
+
+    Returns:
+      A list of tuples (tab, initial_url). |initial_url| is the url of the
+      |tab| prior to a navigation command being sent to it.
+    """
+    timeout_in_seconds = 0
+
+    queued_tabs = []
+    for tab, url in batch:
+      initial_url = self._RetrieveTabUrl(tab,
+          self._TAB_URL_RETRIEVAL_TIMEOUT_IN_SECONDS)
+
+      try:
+        tab.Navigate(url, None, timeout_in_seconds)
+      except exceptions.DevtoolsTargetCrashException:
+        # We expect a time out, and don't mind if the webpage crashes. Ignore
+        # both exceptions.
+        pass
+
+      queued_tabs.append((tab, initial_url))
+    return queued_tabs
+
+  def _WaitForQueuedTabsToLoad(self, queued_tabs):
+    """Waits for all the batch navigated tabs to finish loading.
+
+    Args:
+      queued_tabs: A list of tuples (tab, initial_url). Each tab is guaranteed
+      to have already been sent a navigation command.
+    """
+    end_time = time.time() + self._BATCH_PAGE_LOAD_TIMEOUT_IN_SECONDS
+    for tab, initial_url in queued_tabs:
+      seconds_to_wait = end_time - time.time()
+      seconds_to_wait = max(0, seconds_to_wait)
+
+      if seconds_to_wait == 0:
+        break
+
+      # Since we don't wait any time for the tab url navigation to commit, it's
+      # possible that the tab hasn't started navigating yet.
+      self._WaitForUrlToChange(tab, initial_url, seconds_to_wait)
+
+      seconds_to_wait = end_time - time.time()
+      seconds_to_wait = max(0, seconds_to_wait)
+
+      try:
+        tab.WaitForDocumentReadyStateToBeComplete(seconds_to_wait)
+      except (util.TimeoutException, exceptions.DevtoolsTargetCrashException):
+        # Ignore time outs and web page crashes.
+        pass
+
+  def _GetUrlsToNavigate(self, url_iterator):
+    """Returns an array of urls to navigate to, given a url_iterator."""
+    urls = []
+    for _ in xrange(self._NUM_TABS):
+      try:
+        urls.append(url_iterator.next())
+      except StopIteration:
+        break
+    return urls
+
+  def _PerformNavigations(self):
+    """Repeatedly fetches a batch of urls, and navigates to those urls. This
+    will run until an empty batch is returned, or
+    ShouldExitAfterBatchNavigation() returns True.
+    """
+    url_iterator = self.GetUrlIterator()
+    while True:
+      urls = self._GetUrlsToNavigate(url_iterator)
+
+      if len(urls) == 0:
+        break
+
+      batch = []
+      for i in range(len(urls)):
+        url = urls[i]
+        tab = self._browser.tabs[i]
+        batch.append((tab, url))
+
+      queued_tabs = self._BatchNavigateTabs(batch)
+      self._WaitForQueuedTabsToLoad(queued_tabs)
+
+      self.CleanUpAfterBatchNavigation()
+
+      if self.ShouldExitAfterBatchNavigation():
+        break
diff --git a/tools/perf/profile_creators/fast_navigation_profile_extender_unittest.py b/tools/perf/profile_creators/fast_navigation_profile_extender_unittest.py
new file mode 100644
index 0000000..2a2af9d
--- /dev/null
+++ b/tools/perf/profile_creators/fast_navigation_profile_extender_unittest.py
@@ -0,0 +1,78 @@
+# 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.
+import unittest
+
+from profile_creators.fast_navigation_profile_extender import (
+    FastNavigationProfileExtender)
+from telemetry.core import util
+
+util.AddDirToPythonPath(util.GetTelemetryDir(), 'third_party', 'mock')
+import mock
+
+
+class FakeTab(object):
+  pass
+
+
+class FakeBrowser(object):
+  def __init__(self, tab_count):
+    self.tabs = []
+    for _ in range(tab_count):
+      self.tabs.append(FakeTab())
+
+
+# Testing private method.
+# pylint: disable=protected-access
+class FastNavigationProfileExtenderTest(unittest.TestCase):
+  def testPerformNavigations(self):
+    maximum_batch_size = 15
+    extender = FastNavigationProfileExtender(maximum_batch_size)
+
+    navigation_urls = []
+    for i in range(extender._NUM_TABS):
+      navigation_urls.append('http://test%s.com' % i)
+    batch_size = 5
+    navigation_urls_batch = navigation_urls[3:3 + batch_size]
+
+    extender.GetUrlIterator = mock.MagicMock(
+        return_value=iter(navigation_urls_batch))
+    extender.ShouldExitAfterBatchNavigation = mock.MagicMock(return_value=True)
+    extender._WaitForQueuedTabsToLoad = mock.MagicMock()
+
+    extender._browser = FakeBrowser(extender._NUM_TABS)
+    extender._BatchNavigateTabs = mock.MagicMock()
+
+    # Set up a callback to record the tabs and urls in each navigation.
+    callback_tabs_batch = []
+    callback_urls_batch = []
+    def SideEffect(*args, **_):
+      batch = args[0]
+      for tab, url in batch:
+        callback_tabs_batch.append(tab)
+        callback_urls_batch.append(url)
+    extender._BatchNavigateTabs.side_effect = SideEffect
+
+    # Perform the navigations.
+    extender._PerformNavigations()
+
+    # Each url in the batch should have been navigated to exactly once.
+    self.assertEqual(set(callback_urls_batch), set(navigation_urls_batch))
+
+    # The other urls should not have been navigated to.
+    navigation_urls_remaining = (set(navigation_urls) -
+        set(navigation_urls_batch))
+    self.assertFalse(navigation_urls_remaining & set(callback_urls_batch))
+
+    # The first couple of tabs should have been navigated once. The remaining
+    # tabs should not have been navigated.
+    for i in range(len(extender._browser.tabs)):
+      tab = extender._browser.tabs[i]
+
+      if i < batch_size:
+        expected_tab_navigation_count = 1
+      else:
+        expected_tab_navigation_count = 0
+
+      count = callback_tabs_batch.count(tab)
+      self.assertEqual(count, expected_tab_navigation_count)
diff --git a/tools/perf/profile_creators/history_profile_extender.py b/tools/perf/profile_creators/history_profile_extender.py
new file mode 100644
index 0000000..94cd636
--- /dev/null
+++ b/tools/perf/profile_creators/history_profile_extender.py
@@ -0,0 +1,90 @@
+# 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.
+import multiprocessing
+import tempfile
+import os
+
+from profile_creators import fast_navigation_profile_extender
+
+
+class HistoryProfileExtender(
+    fast_navigation_profile_extender.FastNavigationProfileExtender):
+  """This extender navigates Chrome to a large number of URIs pointing to local
+  files. It continues running until the history DB becomes full."""
+  _HISTORY_DB_MAX_SIZE_IN_MB = 10
+
+  def __init__(self):
+    # The rate limiting factors are the speed of page navigation, and the speed
+    # of python bindings. The former is larger than the latter, so having a
+    # large batch size skews the amortized average time per page load towards
+    # the latter.
+    maximum_batch_size = multiprocessing.cpu_count() * 2
+    super(HistoryProfileExtender, self).__init__(maximum_batch_size)
+
+    # A list of paths of temporary files. The instance is responsible for
+    # making sure that the files are deleted before they are removed from this
+    # list.
+    self._generated_temp_files = []
+
+  def _MakeTemporaryFile(self):
+    """Makes a temporary file and returns a URI to the file.
+
+    This method has the side effect of appending the temporary file to
+    self._generated_temp_files. The instance is responsible for deleting the
+    file at a later point in time.
+    """
+    # Adding a long suffix to the name of the file fills up the history
+    # database faster. The suffix can't be too long, since some file systems
+    # have a 256 character limit on the length of the path. While we could
+    # dynamically vary the length of the path, that would generate different
+    # profiles on different OSes, which is not ideal.
+    suffix = "reallylongsuffixintendedtoartificiallyincreasethelengthoftheurl"
+
+    # Neither tempfile.TemporaryFile() nor tempfile.NamedTemporaryFile() work
+    # well here. The former doesn't work at all, since it doesn't gaurantee a
+    # file-system visible path. The latter doesn't work well, since the
+    # returned file cannot be opened a second time on Windows. The returned
+    # file would have to be closed, and the method would need to be called with
+    # Delete=False, which makes its functionality no simpler than
+    # tempfile.mkstemp().
+    handle, path = tempfile.mkstemp(suffix=suffix)
+    os.close(handle)
+    self._generated_temp_files.append(path)
+
+    file_url = "file://" + path
+    return file_url
+
+  def GetUrlIterator(self):
+    """Superclass override."""
+    while True:
+      yield self._MakeTemporaryFile()
+
+  def ShouldExitAfterBatchNavigation(self):
+    """Superclass override."""
+    return self._IsHistoryDBAtMaxSize()
+
+  def TearDown(self):
+    """Superclass override."""
+    super(HistoryProfileExtender, self).TearDown()
+    for path in self._generated_temp_files:
+      os.remove(path)
+    self._generated_temp_files = []
+
+  def CleanUpAfterBatchNavigation(self):
+    """Superclass override."""
+    for path in self._generated_temp_files:
+      os.remove(path)
+    self._generated_temp_files = []
+
+  def _IsHistoryDBAtMaxSize(self):
+    """Whether the history DB has reached its maximum size."""
+    history_db_path = os.path.join(self.profile_path, "Default", "History")
+    stat_info = os.stat(history_db_path)
+    size = stat_info.st_size
+
+    max_size_threshold = 0.95
+    bytes_in_megabyte = 2**20
+    max_size = (bytes_in_megabyte *
+        HistoryProfileExtender._HISTORY_DB_MAX_SIZE_IN_MB * max_size_threshold)
+    return size > max_size
diff --git a/tools/perf/profile_creators/history_profile_extender_unittest.py b/tools/perf/profile_creators/history_profile_extender_unittest.py
new file mode 100644
index 0000000..4f9cc867
--- /dev/null
+++ b/tools/perf/profile_creators/history_profile_extender_unittest.py
@@ -0,0 +1,44 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+import os
+import shutil
+import tempfile
+import unittest
+
+from profile_creators.history_profile_extender import HistoryProfileExtender
+from telemetry import decorators
+from telemetry.core import util
+from telemetry.unittest_util import options_for_unittests
+
+util.AddDirToPythonPath(util.GetTelemetryDir(), 'third_party', 'mock')
+import mock
+
+
+# Testing private method.
+# pylint: disable=protected-access
+class HistoryProfileExtenderTest(unittest.TestCase):
+  # The profile extender does not work on Android or ChromeOS.
+  @decorators.Disabled('android', 'chromeos')
+  def testFullFunctionality(self):
+    extender = HistoryProfileExtender()
+
+    # Stop the extender at the earliest possible opportunity.
+    extender.ShouldExitAfterBatchNavigation = mock.MagicMock(return_value=True)
+    # Normally, the number of tabs depends on the number of cores. Use a
+    # static, small number to increase the speed of the test.
+    extender._NUM_TABS = 3
+
+    options = options_for_unittests.GetCopy()
+    options.output_profile_path = tempfile.mkdtemp()
+
+    try:
+      extender.Run(options)
+      self.assertEquals(extender.profile_path, options.output_profile_path)
+      self.assertTrue(os.path.exists(extender.profile_path))
+      history_db_path = os.path.join(extender.profile_path, "Default",
+          "History")
+      stat_info = os.stat(history_db_path)
+      self.assertGreater(stat_info.st_size, 1000)
+    finally:
+      shutil.rmtree(options.output_profile_path)
diff --git a/tools/perf/profile_creators/large_profile_creator.py b/tools/perf/profile_creators/large_profile_creator.py
new file mode 100644
index 0000000..394b54f
--- /dev/null
+++ b/tools/perf/profile_creators/large_profile_creator.py
@@ -0,0 +1,17 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from profile_creators import cookie_profile_extender
+from profile_creators import history_profile_extender
+from telemetry.page import profile_creator
+
+class LargeProfileCreator(profile_creator.ProfileCreator):
+  """This class creates a large profile by performing a large number of url
+  navigations."""
+  def Run(self, options):
+    extender = history_profile_extender.HistoryProfileExtender()
+    extender.Run(options)
+
+    extender = cookie_profile_extender.CookieProfileExtender()
+    extender.Run(options)
diff --git a/tools/perf_expectations/perf_expectations.json b/tools/perf_expectations/perf_expectations.json
index c11eec8..b920570 100644
--- a/tools/perf_expectations/perf_expectations.json
+++ b/tools/perf_expectations/perf_expectations.json
@@ -367,14 +367,14 @@
  "linux-release/sizes/chrome-si/initializers": {"reva": 281717, "revb": 281717, "type": "absolute", "better": "lower", "improve": 8, "regress": 8, "tolerance": 0, "sha1": "b639bbc4"},
  "linux-release/sizes/chrome-text/text": {"reva": 314576, "revb": 314590, "type": "absolute", "better": "lower", "improve": 102025091, "regress": 112765474, "sha1": "323b5a75"},
  "linux-release/sizes/chrome-textrel/textrel": {"reva": 234134, "revb": 234142, "type": "absolute", "better": "lower", "improve": 0, "regress": 0, "sha1": "61db9eaf"},
- "linux-release/sizes/chrome/chrome": {"reva": 314576, "revb": 314590, "type": "absolute", "better": "lower", "improve": 138826666, "regress": 153444353, "sha1": "943c4b9d"},
+ "linux-release/sizes/chrome/chrome": {"reva": 315765, "revb": 316804, "type": "absolute", "better": "lower", "improve": 145744151, "regress": 161295763, "sha1": "5bdd2fa3"},
  "linux-release/sizes/libffmpegsumo.so-textrel/textrel": {"reva": 200467, "revb": 203456, "type": "absolute", "better": "lower", "improve": 1075, "regress": 1189, "sha1": "a10d4ea4"},
  "linux-release/sizes/nacl_helper-bss/bss": {"reva": 282247, "revb": 282247, "type": "absolute", "better": "lower", "improve": 143640, "regress": 158760, "sha1": "95c4c516"},
  "linux-release/sizes/nacl_helper-data/data": {"reva": 315014, "revb": 315022, "type": "absolute", "better": "lower", "improve": 113042, "regress": 124942, "sha1": "d4f92fae"},
  "linux-release/sizes/nacl_helper-si/initializers": {"reva": 271321, "revb": 271321, "type": "absolute", "better": "lower", "improve": 6, "regress": 8, "sha1": "3394be7f"},
  "linux-release/sizes/nacl_helper-text/text": {"reva": 314720, "revb": 314730, "type": "absolute", "better": "lower", "improve": 5490160, "regress": 6068328, "sha1": "d97ac1ae"},
  "linux-release/sizes/nacl_helper-textrel/textrel": {"reva": 264283, "revb": 264283, "type": "absolute", "better": "lower", "improve": 0, "regress": 0, "sha1": "0efaa99f"},
- "linux-release/sizes/nacl_helper/nacl_helper": {"reva": 314720, "revb": 314730, "type": "absolute", "better": "lower", "improve": 7294860, "regress": 8062740, "sha1": "804bb0b4"},
+ "linux-release/sizes/nacl_helper/nacl_helper": {"reva": 316558, "revb": 316807, "type": "absolute", "better": "lower", "improve": 7712027, "regress": 8529465, "sha1": "72d6bac9"},
  "linux-release/sizes/nacl_helper_bootstrap-bss/bss": {"reva": 114822, "revb": 115019, "type": "absolute", "better": "lower", "improve": 1019980807, "regress": 1127347209, "sha1": "a4ff54ab"},
  "linux-release/sizes/nacl_helper_bootstrap-data/data": {"reva": 114822, "revb": 115019, "type": "absolute", "better": "lower", "improve": 19, "regress": 21, "sha1": "6a3e92f4"},
  "linux-release/sizes/nacl_helper_bootstrap-si/initializers": {"reva": 114822, "revb": 115019, "type": "absolute", "better": "lower", "improve": 0, "regress": 0, "sha1": "dd908f29"},
diff --git a/tools/relocation_packer/BUILD.gn b/tools/relocation_packer/BUILD.gn
index 0b29c91..e95dbcf 100644
--- a/tools/relocation_packer/BUILD.gn
+++ b/tools/relocation_packer/BUILD.gn
@@ -7,9 +7,9 @@
 
 assert(relocation_packing_supported)
 
-if (target_arch == "arm") {
+if (target_cpu == "arm") {
   target_define = "TARGET_ARM"
-} else if (target_arch == "arm64") {
+} else if (target_cpu == "arm64") {
   target_define = "TARGET_ARM64"
 }
 
@@ -78,7 +78,7 @@
 }
 
 if (current_toolchain == default_toolchain &&
-    (target_arch == "arm" || target_arch == "arm64")) {
+    (target_cpu == "arm" || target_cpu == "arm64")) {
   # Targets to build test data.  These participate only in building test
   # data for use with elf_file_unittest.cc, and are not part of the main
   # relocation packer build.  Unit test data files are checked in to the
@@ -102,11 +102,11 @@
   action("relocation_packer_unittests_test_data") {
     script = "test_data/generate_elf_file_unittest_relocs.py"
     test_file = "$root_build_dir/librelocation_packer_test_data.so"
-    if (target_arch == "arm") {
+    if (target_cpu == "arm") {
       added_section = ".android.rel.dyn"
       packed_output = "elf_file_unittest_relocs_arm32_packed.so"
       unpacked_output = "elf_file_unittest_relocs_arm32.so"
-    } else if (target_arch == "arm64") {
+    } else if (target_cpu == "arm64") {
       added_section = ".android.rela.dyn"
       packed_output = "elf_file_unittest_relocs_arm64_packed.so"
       unpacked_output = "elf_file_unittest_relocs_arm64.so"
diff --git a/tools/relocation_packer/config.gni b/tools/relocation_packer/config.gni
index 90e3979..4cdd9455 100644
--- a/tools/relocation_packer/config.gni
+++ b/tools/relocation_packer/config.gni
@@ -2,7 +2,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-relocation_packing_supported = target_arch == "arm" || target_arch == "arm64"
+relocation_packing_supported = target_cpu == "arm" || target_cpu == "arm64"
 
 if (relocation_packing_supported) {
   relocation_packer_target = "//tools/relocation_packer($host_toolchain)"
@@ -10,9 +10,9 @@
       get_label_info("$relocation_packer_target", "root_out_dir")
   relocation_packer_exe = "${relocation_packer_dir}/relocation_packer"
 
-  if (target_arch == "arm") {
+  if (target_cpu == "arm") {
     relocations_have_addends = 0
-  } else if (target_arch == "arm64") {
+  } else if (target_cpu == "arm64") {
     relocations_have_addends = 1
   }
 } else {
diff --git a/tools/sort_sources.py b/tools/sort_sources.py
index 63f1afd..bcbdbf6 100755
--- a/tools/sort_sources.py
+++ b/tools/sort_sources.py
@@ -7,6 +7,83 @@
 
 Shows a diff and prompts for confirmation before doing the deed.
 Works great with tools/git/for-all-touched-files.py.
+
+Limitations:
+
+1) Comments used as section headers
+
+If a comment (1+ lines starting with #) appears in a source list without a
+preceding blank line, the tool assumes that the comment is about the next
+line. For example, given the following source list,
+
+  sources = [
+    "b.cc",
+    # Comment.
+    "a.cc",
+    "c.cc",
+  ]
+
+the tool will produce the following output:
+
+  sources = [
+    # Comment.
+    "a.cc",
+    "b.cc",
+    "c.cc",
+  ]
+
+This is not correct if the comment is for starting a new section like:
+
+  sources = [
+    "b.cc",
+    # These are for Linux.
+    "a.cc",
+    "c.cc",
+  ]
+
+The tool cannot disambiguate the two types of comments. The problem can be
+worked around by inserting a blank line before the comment because the tool
+interprets a blank line as the end of a source list.
+
+2) Sources commented out
+
+Sometimes sources are commented out with their positions kept in the
+alphabetical order, but what if the list is not sorted correctly? For
+example, given the following source list,
+
+  sources = [
+    "a.cc",
+    # "b.cc",
+    "d.cc",
+    "c.cc",
+  ]
+
+the tool will produce the following output:
+
+  sources = [
+    "a.cc",
+    "c.cc",
+    # "b.cc",
+    "d.cc",
+  ]
+
+This is because the tool assumes that the comment (# "b.cc",) is about the
+next line ("d.cc",). This kind of errors should be fixed manually, or the
+commented-out code should be deleted.
+
+3) " and ' are used both used in the same source list (GYP only problem)
+
+If both " and ' are used in the same source list, sources quoted with " will
+appear first in the output. The problem is rare enough so the tool does not
+attempt to normalize them. Hence this kind of errors should be fixed
+manually.
+
+4) Spaces and tabs used in the same source list
+
+Similarly, if spaces and tabs are both used in the same source list, sources
+indented with tabs will appear first in the output. This kind of errors
+should be fixed manually.
+
 """
 
 import difflib
@@ -18,9 +95,10 @@
 
 SUFFIXES = ['c', 'cc', 'cpp', 'h', 'mm', 'rc', 'rc.version', 'ico', 'def',
             'release']
-SOURCE_PATTERN = re.compile('^\s+[\'"].*\.(%s)[\'"],$' %
+SOURCE_PATTERN = re.compile(r'^\s+[\'"].*\.(%s)[\'"],$' %
                             '|'.join([re.escape(x) for x in SUFFIXES]))
-COMMENT_PATTERN = re.compile('^\s+#')
+COMMENT_PATTERN = re.compile(r'^\s+#')
+
 
 def SortSources(original_lines):
   """Sort source file names in |original_lines|.
@@ -33,9 +111,8 @@
 
   The algorithm is fairly naive. The code tries to find a list of C-ish
   source file names by a simple regex, then sort them. The code does not try
-  to understand the syntax of the build files, hence there are some cases
-  that the code cannot handle correctly (ex. blank lines within a list of
-  source file names).
+  to understand the syntax of the build files. See the file comment above for
+  details.
   """
 
   output_lines = []
@@ -45,7 +122,7 @@
     if re.search(COMMENT_PATTERN, line):
       comments.append(line)
     elif re.search(SOURCE_PATTERN, line):
-      # Associate the line with the preceeding comments.
+      # Associate the line with the preceding comments.
       sources.append([line, comments])
       comments = []
     else:
diff --git a/tools/telemetry/telemetry/benchmark.py b/tools/telemetry/telemetry/benchmark.py
index 2ef5fa4b..6a34ec2 100644
--- a/tools/telemetry/telemetry/benchmark.py
+++ b/tools/telemetry/telemetry/benchmark.py
@@ -138,6 +138,20 @@
   def ProcessCommandLineArgs(cls, parser, args):
     pass
 
+  @classmethod
+  def ValueCanBeAddedPredicate(cls, value):  # pylint: disable=unused-argument
+    """ Returns whether |value| can be added to the test results.
+    Override this method to customize the logic of adding values to test
+    results.
+
+    Args:
+        value: a value.Value instance.
+
+    Returns: a boolean. True if value should be added to the test results and
+        False otherwise.
+    """
+    return True
+
   def CustomizeBrowserOptions(self, options):
     """Add browser options that are required by this benchmark."""
 
@@ -175,21 +189,22 @@
     self._DownloadGeneratedProfileArchive(finder_options)
 
     benchmark_metadata = self.GetMetadata()
-    results = results_options.CreateResults(benchmark_metadata, finder_options)
-    try:
-      user_story_runner.Run(pt, us, expectations, finder_options, results,
-                            max_failures=self._max_failures)
-      return_code = min(254, len(results.failures))
-    except Exception:
-      exception_formatter.PrintFormattedException()
-      return_code = 255
+    with results_options.CreateResults(benchmark_metadata,
+                                       finder_options) as results:
+      try:
+        user_story_runner.Run(pt, us, expectations, finder_options, results,
+                              max_failures=self._max_failures)
+        return_code = min(254, len(results.failures))
+      except Exception:
+        exception_formatter.PrintFormattedException()
+        return_code = 255
 
-    bucket = cloud_storage.BUCKET_ALIASES[finder_options.upload_bucket]
-    if finder_options.upload_results:
-      results.UploadTraceFilesToCloud(bucket)
-      results.UploadProfilingFilesToCloud(bucket)
+      bucket = cloud_storage.BUCKET_ALIASES[finder_options.upload_bucket]
+      if finder_options.upload_results:
+        results.UploadTraceFilesToCloud(bucket)
+        results.UploadProfilingFilesToCloud(bucket)
 
-    results.PrintSummary()
+      results.PrintSummary()
     return return_code
 
   def _DownloadGeneratedProfileArchive(self, options):
diff --git a/tools/telemetry/telemetry/core/backends/chrome/desktop_browser_backend.py b/tools/telemetry/telemetry/core/backends/chrome/desktop_browser_backend.py
index d58a1a75..0b4d5e2 100644
--- a/tools/telemetry/telemetry/core/backends/chrome/desktop_browser_backend.py
+++ b/tools/telemetry/telemetry/core/backends/chrome/desktop_browser_backend.py
@@ -102,8 +102,13 @@
     if os_name != 'win':
       return None
     arch_name = self.browser.platform.GetArchName()
+    command = support_binaries.FindPath('crash_service', arch_name, os_name)
+    if not command:
+      logging.warning('crash_service.exe not found for %s %s',
+                      arch_name, os_name)
+      return None
     return subprocess.Popen([
-        support_binaries.FindPath('crash_service', arch_name, os_name),
+        command,
         '--no-window',
         '--dumps-dir=%s' % self._tmp_minidump_dir,
         '--pipe-name=%s' % self._GetCrashServicePipeName()])
diff --git a/tools/telemetry/telemetry/core/backends/chrome_inspector/inspector_backend.py b/tools/telemetry/telemetry/core/backends/chrome_inspector/inspector_backend.py
index 05b95bdd..968f395 100644
--- a/tools/telemetry/telemetry/core/backends/chrome_inspector/inspector_backend.py
+++ b/tools/telemetry/telemetry/core/backends/chrome_inspector/inspector_backend.py
@@ -76,6 +76,10 @@
   def debugger_url(self):
     return self._context['webSocketDebuggerUrl']
 
+  def IsInspectable(self):
+    contexts = self._devtools_client.ListInspectableContexts()
+    return self._context['id'] in [c['id'] for c in contexts]
+
   # Public methods implemented in JavaScript.
 
   @property
@@ -159,10 +163,6 @@
 
   # Methods used internally by other backends.
 
-  def _IsInspectable(self):
-    contexts = self._devtools_client.ListInspectableContexts()
-    return self._context['id'] in [c['id'] for c in contexts]
-
   def _HandleInspectorDomainNotification(self, res):
     if (res['method'] == 'Inspector.detached' and
         res.get('params', {}).get('reason', '') == 'replaced_with_devtools'):
@@ -172,7 +172,7 @@
       raise exceptions.DevtoolsTargetCrashException(self.app)
 
   def _HandleError(self, elapsed_time):
-    if self._IsInspectable():
+    if self.IsInspectable():
       raise exceptions.DevtoolsTargetCrashException(self.app,
           'Received a socket error in the browser connection and the tab '
           'still exists, assuming it timed out. '
@@ -188,7 +188,7 @@
     self._websocket._socket.close()
     self._websocket._socket = None
     def IsBack():
-      if not self._IsInspectable():
+      if not self.IsInspectable():
         return False
       try:
         self._websocket.Connect(self.debugger_url)
diff --git a/tools/telemetry/telemetry/core/backends/chrome_inspector/inspector_page.py b/tools/telemetry/telemetry/core/backends/chrome_inspector/inspector_page.py
index b28a986..be9a21b 100644
--- a/tools/telemetry/telemetry/core/backends/chrome_inspector/inspector_page.py
+++ b/tools/telemetry/telemetry/core/backends/chrome_inspector/inspector_page.py
@@ -155,6 +155,7 @@
 
   def CollectGarbage(self, timeout=60):
     request = {
-        'method': 'HeapProfiler.CollectGarbage'
+        'method': 'HeapProfiler.collectGarbage'
         }
-    self._inspector_websocket.SyncRequest(request, timeout)
+    res = self._inspector_websocket.SyncRequest(request, timeout)
+    assert 'result' in res
diff --git a/tools/telemetry/telemetry/core/tab_unittest.py b/tools/telemetry/telemetry/core/tab_unittest.py
index 7109156..c8f1c3f 100644
--- a/tools/telemetry/telemetry/core/tab_unittest.py
+++ b/tools/telemetry/telemetry/core/tab_unittest.py
@@ -164,6 +164,17 @@
     # renderer_thread corresponding to it in the the trace.
     self.assertIs(None, timeline_model.GetRendererThreadFromTabId(third_tab.id))
 
+  def testTabIsAlive(self):
+    self.assertEquals(self._tab.url, 'about:blank')
+    self.assertTrue(self._tab.IsAlive())
+
+    self._tab.Navigate(self.UrlOfUnittestFile('blank.html'))
+    self.assertTrue(self._tab.IsAlive())
+
+    self.assertRaises(exceptions.DevtoolsTargetCrashException,
+        lambda: self._tab.Navigate(self.UrlOfUnittestFile('chrome://crash')))
+    self.assertFalse(self._tab.IsAlive())
+
 
 class GpuTabTest(tab_test_case.TabTestCase):
   @classmethod
diff --git a/tools/telemetry/telemetry/core/web_contents.py b/tools/telemetry/telemetry/core/web_contents.py
index fe790ad1..ae8f1cf 100644
--- a/tools/telemetry/telemetry/core/web_contents.py
+++ b/tools/telemetry/telemetry/core/web_contents.py
@@ -175,3 +175,14 @@
 
   def TakeJSHeapSnapshot(self, timeout=120):
     return self._inspector_backend.TakeJSHeapSnapshot(timeout)
+
+  def IsAlive(self):
+    """Whether the WebContents is still operating normally.
+
+    Since WebContents function asynchronously, this method does not guarantee
+    that the WebContents will still be alive at any point in the future.
+
+    Returns:
+      A boolean indicating whether the WebContents is opearting normally.
+    """
+    return self._inspector_backend.IsInspectable()
diff --git a/tools/telemetry/telemetry/results/chart_json_output_formatter.py b/tools/telemetry/telemetry/results/chart_json_output_formatter.py
index 43af846..753a840a 100644
--- a/tools/telemetry/telemetry/results/chart_json_output_formatter.py
+++ b/tools/telemetry/telemetry/results/chart_json_output_formatter.py
@@ -54,7 +54,7 @@
     'format_version': '0.1',
     'benchmark_name': benchmark_metadata.name,
     'benchmark_description': benchmark_metadata.description,
-    'benchmark_rerun_options': benchmark_metadata.rerun_options,
+    'trace_rerun_options': benchmark_metadata.rerun_options,
     'charts': charts,
   }
 
@@ -71,5 +71,5 @@
         self._benchmark_metadata,
         page_test_results.all_page_specific_values,
         page_test_results.all_summary_values),
-              self.output_stream)
+              self.output_stream, indent=2)
     self.output_stream.write('\n')
diff --git a/tools/telemetry/telemetry/results/json_output_formatter.py b/tools/telemetry/telemetry/results/json_output_formatter.py
index a65840f..db7b13b 100644
--- a/tools/telemetry/telemetry/results/json_output_formatter.py
+++ b/tools/telemetry/telemetry/results/json_output_formatter.py
@@ -53,5 +53,5 @@
   def Format(self, page_test_results):
     json.dump(
         ResultsAsDict(page_test_results, self.benchmark_metadata),
-        self.output_stream)
+        self.output_stream, indent=2)
     self.output_stream.write('\n')
diff --git a/tools/telemetry/telemetry/results/page_test_results.py b/tools/telemetry/telemetry/results/page_test_results.py
index 36866ec..819684f 100644
--- a/tools/telemetry/telemetry/results/page_test_results.py
+++ b/tools/telemetry/telemetry/results/page_test_results.py
@@ -22,7 +22,8 @@
 
 class PageTestResults(object):
   def __init__(self, output_stream=None, output_formatters=None,
-               progress_reporter=None, trace_tag='', output_dir=None):
+               progress_reporter=None, trace_tag='', output_dir=None,
+               value_can_be_added_predicate=lambda v: True):
     """
     Args:
       output_stream: The output stream to use to write test results.
@@ -31,8 +32,13 @@
           as CsvOutputFormatter, which output the test results as CSV.
       progress_reporter: An instance of progress_reporter.ProgressReporter,
           to be used to output test status/results progressively.
-      trace_tag: A string to append to the buildbot trace
-      name. Currently only used for buildbot.
+      trace_tag: A string to append to the buildbot trace name. Currently only
+          used for buildbot.
+      output_dir: A string specified the directory where to store the test
+          artifacts, e.g: trace, videos,...
+      value_can_be_added_predicate: A function that takes an value.Value
+          instance as input. It returns True if the value can be added to the
+          test results and False otherwise.
     """
     # TODO(chrishenry): Figure out if trace_tag is still necessary.
 
@@ -45,6 +51,7 @@
         output_formatters if output_formatters is not None else [])
     self._trace_tag = trace_tag
     self._output_dir = output_dir
+    self._value_can_be_added_predicate = value_can_be_added_predicate
 
     self._current_page_run = None
     self._all_page_runs = []
@@ -127,6 +134,20 @@
   def _GetStringFromExcInfo(self, err):
     return ''.join(traceback.format_exception(*err))
 
+  def CleanUp(self):
+    """Clean up any TraceValues contained within this results object."""
+    for run in self._all_page_runs:
+      for v in run.values:
+        if isinstance(v, trace.TraceValue):
+          v.CleanUp()
+          run.values.remove(v)
+
+  def __enter__(self):
+    return self
+
+  def __exit__(self, _, __, ___):
+    self.CleanUp()
+
   def WillRunPage(self, page):
     assert not self._current_page_run, 'Did not call DidRunPage.'
     self._current_page_run = page_run.PageRun(page)
@@ -148,6 +169,8 @@
   def AddValue(self, value):
     assert self._current_page_run, 'Not currently running test.'
     self._ValidateValue(value)
+    if not self._value_can_be_added_predicate(value):
+      return
     # TODO(eakuefner/chrishenry): Add only one skip per pagerun assert here
     self._current_page_run.AddValue(value)
     self._progress_reporter.DidAddValue(value)
diff --git a/tools/telemetry/telemetry/results/page_test_results_unittest.py b/tools/telemetry/telemetry/results/page_test_results_unittest.py
index 536cdc8..21006a1 100644
--- a/tools/telemetry/telemetry/results/page_test_results_unittest.py
+++ b/tools/telemetry/telemetry/results/page_test_results_unittest.py
@@ -15,7 +15,6 @@
 from telemetry.value import skip
 from telemetry.value import trace
 
-
 class PageTestResultsTest(base_test_results_unittest.BaseTestResultsUnittest):
   def setUp(self):
     ps = page_set.PageSet(file_path=os.path.dirname(__file__))
@@ -45,6 +44,7 @@
     self.assertTrue(results.all_page_runs[0].failed)
     self.assertTrue(results.all_page_runs[1].ok)
 
+
   def testSkips(self):
     results = page_test_results.PageTestResults()
     results.WillRunPage(self.pages[0])
@@ -63,7 +63,7 @@
     self.assertTrue(results.all_page_runs[0].skipped)
     self.assertTrue(results.all_page_runs[1].ok)
 
-  def test_basic(self):
+  def testBasic(self):
     results = page_test_results.PageTestResults()
     results.WillRunPage(self.pages[0])
     results.AddValue(scalar.ScalarValue(self.pages[0], 'a', 'seconds', 3))
@@ -84,7 +84,39 @@
     values = results.FindAllPageSpecificValuesNamed('a')
     assert len(values) == 2
 
-  def test_url_is_invalid_value(self):
+  def testResultsFiltering(self):
+    def AcceptValueNamed_a(value):
+      return value.name == 'a'
+    results = page_test_results.PageTestResults(
+        value_can_be_added_predicate=AcceptValueNamed_a)
+    results.WillRunPage(self.pages[0])
+    results.AddValue(scalar.ScalarValue(self.pages[0], 'a', 'seconds', 3))
+    results.AddValue(scalar.ScalarValue(self.pages[0], 'b', 'seconds', 3))
+    results.DidRunPage(self.pages[0])
+
+    results.WillRunPage(self.pages[1])
+    results.AddValue(scalar.ScalarValue(self.pages[1], 'a', 'seconds', 3))
+    results.AddValue(scalar.ScalarValue(self.pages[1], 'd', 'seconds', 3))
+    results.DidRunPage(self.pages[1])
+
+    results.PrintSummary()
+
+    values = results.FindPageSpecificValuesForPage(self.pages[0], 'a')
+    self.assertEquals(1, len(values))
+    v = values[0]
+    self.assertEquals(v.name, 'a')
+    self.assertEquals(v.page, self.pages[0])
+
+    values = results.FindPageSpecificValuesForPage(self.pages[0], 'b')
+    self.assertEquals(0, len(values))
+
+    values = results.FindAllPageSpecificValuesNamed('a')
+    self.assertEquals(len(values), 2)
+
+    values = results.all_page_specific_values
+    self.assertEquals(len(values), 2)
+
+  def testUrlIsInvalidValue(self):
     results = page_test_results.PageTestResults()
     results.WillRunPage(self.pages[0])
     self.assertRaises(
@@ -92,7 +124,7 @@
       lambda: results.AddValue(scalar.ScalarValue(
           self.pages[0], 'url', 'string', 'foo')))
 
-  def test_add_summary_value_with_page_specified(self):
+  def testAddSummaryValueWithPageSpecified(self):
     results = page_test_results.PageTestResults()
     results.WillRunPage(self.pages[0])
     self.assertRaises(
@@ -100,7 +132,7 @@
       lambda: results.AddSummaryValue(scalar.ScalarValue(self.pages[0],
                                                          'a', 'units', 3)))
 
-  def test_unit_change(self):
+  def testUnitChange(self):
     results = page_test_results.PageTestResults()
     results.WillRunPage(self.pages[0])
     results.AddValue(scalar.ScalarValue(self.pages[0], 'a', 'seconds', 3))
@@ -112,7 +144,7 @@
       lambda: results.AddValue(scalar.ScalarValue(
           self.pages[1], 'a', 'foobgrobbers', 3)))
 
-  def test_type_change(self):
+  def testTypeChange(self):
     results = page_test_results.PageTestResults()
     results.WillRunPage(self.pages[0])
     results.AddValue(scalar.ScalarValue(self.pages[0], 'a', 'seconds', 3))
@@ -125,7 +157,7 @@
           self.pages[1], 'a', 'seconds',
           raw_value_json='{"buckets": [{"low": 1, "high": 2, "count": 1}]}')))
 
-  def test_get_pages_that_succeeded_all_pages_fail(self):
+  def testGetPagesThatSucceededAllPagesFail(self):
     results = page_test_results.PageTestResults()
     results.WillRunPage(self.pages[0])
     results.AddValue(scalar.ScalarValue(self.pages[0], 'a', 'seconds', 3))
@@ -140,14 +172,14 @@
     results.PrintSummary()
     self.assertEquals(0, len(results.pages_that_succeeded))
 
-  def test_get_successful_page_values_merged_no_failures(self):
+  def testGetSuccessfulPageValuesMergedNoFailures(self):
     results = page_test_results.PageTestResults()
     results.WillRunPage(self.pages[0])
     results.AddValue(scalar.ScalarValue(self.pages[0], 'a', 'seconds', 3))
     self.assertEquals(1, len(results.all_page_specific_values))
     results.DidRunPage(self.pages[0])
 
-  def test_get_all_values_for_successful_pages(self):
+  def testGetAllValuesForSuccessfulPages(self):
     results = page_test_results.PageTestResults()
     results.WillRunPage(self.pages[0])
     value1 = scalar.ScalarValue(self.pages[0], 'a', 'seconds', 3)
@@ -167,7 +199,7 @@
     self.assertEquals(
         [value1, value2, value3], results.all_page_specific_values)
 
-  def test_get_all_values_for_successful_pages_one_page_fails(self):
+  def testGetAllValuesForSuccessfulPagesOnePageFails(self):
     results = page_test_results.PageTestResults()
     results.WillRunPage(self.pages[0])
     value1 = scalar.ScalarValue(self.pages[0], 'a', 'seconds', 3)
@@ -201,3 +233,36 @@
 
     values = results.FindAllTraceValues()
     self.assertEquals(2, len(values))
+
+  def testCleanUpCleansUpTraceValues(self):
+    results = page_test_results.PageTestResults()
+    v0 = trace.TraceValue(None, trace_data.TraceData({'test': 1}))
+    v1 = trace.TraceValue(None, trace_data.TraceData({'test': 2}))
+
+    results.WillRunPage(self.pages[0])
+    results.AddValue(v0)
+    results.DidRunPage(self.pages[0])
+
+    results.WillRunPage(self.pages[1])
+    results.AddValue(v1)
+    results.DidRunPage(self.pages[1])
+
+    results.CleanUp()
+    self.assertTrue(v0.cleaned_up)
+    self.assertTrue(v1.cleaned_up)
+
+  def testNoTracesLeftAfterCleanUp(self):
+    results = page_test_results.PageTestResults()
+    v0 = trace.TraceValue(None, trace_data.TraceData({'test': 1}))
+    v1 = trace.TraceValue(None, trace_data.TraceData({'test': 2}))
+
+    results.WillRunPage(self.pages[0])
+    results.AddValue(v0)
+    results.DidRunPage(self.pages[0])
+
+    results.WillRunPage(self.pages[1])
+    results.AddValue(v1)
+    results.DidRunPage(self.pages[1])
+
+    results.CleanUp()
+    self.assertFalse(results.FindAllTraceValues())
diff --git a/tools/telemetry/telemetry/results/results_options.py b/tools/telemetry/telemetry/results/results_options.py
index d70759a..461d074 100644
--- a/tools/telemetry/telemetry/results/results_options.py
+++ b/tools/telemetry/telemetry/results/results_options.py
@@ -107,7 +107,8 @@
       sys.stdout, output_skipped_tests_summary=output_skipped_tests_summary)
 
 
-def CreateResults(benchmark_metadata, options):
+def CreateResults(benchmark_metadata, options,
+                  value_can_be_added_predicate=lambda v: True):
   """
   Args:
     options: Contains the options specified in AddResultsOptions.
@@ -166,4 +167,5 @@
                                   options.suppress_gtest_report)
   return page_test_results.PageTestResults(
       output_formatters=output_formatters, progress_reporter=reporter,
-      output_dir=options.output_dir)
+      output_dir=options.output_dir,
+      value_can_be_added_predicate=value_can_be_added_predicate)
diff --git a/tools/telemetry/telemetry/value/histogram.py b/tools/telemetry/telemetry/value/histogram.py
index 64fadb0..877e135 100644
--- a/tools/telemetry/telemetry/value/histogram.py
+++ b/tools/telemetry/telemetry/value/histogram.py
@@ -37,10 +37,8 @@
              'Don\'t specify both raw_value and raw_value_json'
       raw_value = json.loads(raw_value_json)
     if raw_value:
-      assert 'buckets' in raw_value
-      assert isinstance(raw_value['buckets'], list)
       self.buckets = []
-      for bucket in raw_value['buckets']:
+      for bucket in histogram_util.GetHistogramBucketsFromRawValue(raw_value):
         self.buckets.append(HistogramValueBucket(
           low=bucket['low'],
           high=bucket['high'],
diff --git a/tools/telemetry/telemetry/value/histogram_util.py b/tools/telemetry/telemetry/value/histogram_util.py
index bb6ff36..99542be 100644
--- a/tools/telemetry/telemetry/value/histogram_util.py
+++ b/tools/telemetry/telemetry/value/histogram_util.py
@@ -16,6 +16,21 @@
 RENDERER_HISTOGRAM = 'renderer_histogram'
 
 
+def GetHistogramBucketsFromJson(histogram_json):
+  return GetHistogramBucketsFromRawValue(json.loads(histogram_json))
+
+
+def GetHistogramBucketsFromRawValue(raw_value):
+  buckets = raw_value.get('buckets', [])
+  if buckets:
+    # If there are values greater than the maximum allowable for the histogram,
+    # the highest bucket will have a 'low': maxvalue entry in the dict but no
+    # 'high' entry. Code often assumes the 'high' value will always be present,
+    # and uses it to get bucket mean. So default it to the same value as low.
+    buckets[-1].setdefault('high', buckets[-1]['low'])
+  return buckets
+
+
 def CustomizeBrowserOptions(options):
   """Allows histogram collection."""
   options.AppendExtraBrowserArgs(['--enable-stats-collection-bindings'])
@@ -27,9 +42,10 @@
   Both parameters and the returned result are json serializations.
   """
   start_histogram = json.loads(start_histogram_json)
+  start_histogram_buckets = GetHistogramBucketsFromRawValue(start_histogram)
   # It's ok if the start histogram is empty (we had no data, maybe even no
   # histogram at all, at the start of the test).
-  if 'buckets' not in start_histogram:
+  if not start_histogram_buckets:
     return histogram_json
 
   histogram = json.loads(histogram_json)
@@ -39,16 +55,16 @@
         'Trying to compare histograms from different processes (%d and %d)'
         % (start_histogram['pid'], histogram['pid']))
 
-  start_histogram_buckets = dict()
-  for b in start_histogram['buckets']:
-    start_histogram_buckets[b['low']] = b['count']
+  start_histogram_bucket_counts = dict()
+  for b in start_histogram_buckets:
+    start_histogram_bucket_counts[b['low']] = b['count']
 
   new_buckets = []
-  for b in histogram['buckets']:
+  for b in GetHistogramBucketsFromRawValue(histogram):
     new_bucket = b
     low = b['low']
-    if low in start_histogram_buckets:
-      new_bucket['count'] = b['count'] - start_histogram_buckets[low]
+    if low in start_histogram_bucket_counts:
+      new_bucket['count'] = b['count'] - start_histogram_bucket_counts[low]
       if new_bucket['count'] < 0:
         logging.error('Histogram subtraction error, starting histogram most '
                       'probably invalid.')
@@ -72,8 +88,7 @@
 
   buckets = collections.defaultdict(int)
   for histogram_json in histogram_jsons:
-    h = json.loads(histogram_json)
-    for b in h['buckets']:
+    for b in GetHistogramBucketsFromJson(histogram_json):
       key = (b['low'], b['high'])
       buckets[key] += b['count']
 
diff --git a/tools/telemetry/telemetry/value/histogram_util_unittest.py b/tools/telemetry/telemetry/value/histogram_util_unittest.py
index a91d0f9..d20caca 100644
--- a/tools/telemetry/telemetry/value/histogram_util_unittest.py
+++ b/tools/telemetry/telemetry/value/histogram_util_unittest.py
@@ -50,3 +50,31 @@
     self.assertEquals(5, new_buckets[1])
     self.assertEquals(12, new_buckets[2])
     self.assertEquals(21, new_buckets[3])
+
+
+  def testGetHistogramBucketsFromRawValue_Max(self):
+    raw_value = {'buckets': [
+      {'count': 4, 'low': 10, 'high': 15,},
+      {'count': 6, 'low': 16, 'high': 18,},
+      {'count': 8, 'low': 19},
+    ]}
+    buckets = histogram_util.GetHistogramBucketsFromRawValue(raw_value)
+    self.assertEquals([
+      {'count': 4, 'low': 10, 'high': 15,},
+      {'count': 6, 'low': 16, 'high': 18,},
+      {'count': 8, 'low': 19, 'high': 19},],
+      buckets)
+
+
+  def testGetHistogramBucketsFromJson(self):
+    json_value = json.dumps({'buckets': [
+      {'count': 4, 'low': 10, 'high': 15,},
+      {'count': 6, 'low': 16, 'high': 18,},
+      {'count': 8, 'low': 19, 'high': 25},
+    ]})
+    buckets = histogram_util.GetHistogramBucketsFromJson(json_value)
+    self.assertEquals([
+      {'count': 4, 'low': 10, 'high': 15,},
+      {'count': 6, 'low': 16, 'high': 18,},
+      {'count': 8, 'low': 19, 'high': 25},],
+      buckets)
diff --git a/tools/telemetry/telemetry/value/trace.py b/tools/telemetry/telemetry/value/trace.py
index ba73e7b7..a148bbb 100644
--- a/tools/telemetry/telemetry/value/trace.py
+++ b/tools/telemetry/telemetry/value/trace.py
@@ -18,8 +18,7 @@
 from trace_viewer.build import trace2html
 
 class TraceValue(value_module.Value):
-  def __init__(self, page, trace_data, important=False,
-               description=None):
+  def __init__(self, page, trace_data, important=False, description=None):
     """A value that contains a TraceData object and knows how to
     output it.
 
@@ -30,18 +29,18 @@
     super(TraceValue, self).__init__(
         page, name='trace', units='', important=important,
         description=description)
-    self._trace_data = trace_data
+    self._temp_file = self._GetTempFileHandle(trace_data)
     self._cloud_url = None
     self._serialized_file_handle = None
 
-  def _GetTempFileHandle(self):
+  def _GetTempFileHandle(self, trace_data):
     tf = tempfile.NamedTemporaryFile(delete=False, suffix='.html')
     if self.page:
       title = self.page.display_name
     else:
       title = ''
     trace2html.WriteHTMLForTraceDataToFile(
-        [self._trace_data.GetEventsFor(trace_data_module.CHROME_TRACE_PART)],
+        [trace_data.GetEventsFor(trace_data_module.CHROME_TRACE_PART)],
         title,
         tf)
     tf.close()
@@ -54,6 +53,27 @@
       page_name = None
     return 'TraceValue(%s, %s)' % (page_name, self.name)
 
+  def CleanUp(self):
+    """Cleans up tempfile after it is no longer needed.
+
+    A cleaned up TraceValue cannot be used for further operations. CleanUp()
+    may be called more than once without error.
+    """
+    if self._temp_file is None:
+      return
+    os.remove(self._temp_file.GetAbsPath())
+    self._temp_file = None
+
+  def __enter__(self):
+    return self
+
+  def __exit__(self, _, __, ___):
+    self.CleanUp()
+
+  @property
+  def cleaned_up(self):
+    return self._temp_file is None
+
   def GetBuildbotDataType(self, output_context):
     return None
 
@@ -84,6 +104,8 @@
     return None
 
   def AsDict(self):
+    if self._temp_file is None:
+      raise ValueError('Tried to serialize TraceValue without tempfile.')
     d = super(TraceValue, self).AsDict()
     if self._serialized_file_handle:
       d['file_id'] = self._serialized_file_handle.id
@@ -92,21 +114,22 @@
     return d
 
   def Serialize(self, dir_path):
-    fh = self._GetTempFileHandle()
-    file_name = str(fh.id) + fh.extension
+    if self._temp_file is None:
+      raise ValueError('Tried to serialize nonexistent trace.')
+    file_name = str(self._temp_file.id) + self._temp_file.extension
     file_path = os.path.abspath(os.path.join(dir_path, file_name))
-    shutil.move(fh.GetAbsPath(), file_path)
+    shutil.copy(self._temp_file.GetAbsPath(), file_path)
     self._serialized_file_handle = file_handle.FromFilePath(file_path)
     return self._serialized_file_handle
 
   def UploadToCloud(self, bucket):
-    temp_fh = None
+    if self._temp_file is None:
+      raise ValueError('Tried to upload nonexistent trace to Cloud Storage.')
     try:
       if self._serialized_file_handle:
         fh = self._serialized_file_handle
       else:
-        temp_fh = self._GetTempFileHandle()
-        fh = temp_fh
+        fh = self._temp_file
       remote_path = ('trace-file-id_%s-%s-%d%s' % (
           fh.id,
           datetime.datetime.now().strftime('%Y-%m-%d_%H-%M-%S'),
@@ -121,6 +144,3 @@
     except cloud_storage.PermissionError as e:
       logging.error('Cannot upload trace files to cloud storage due to '
                     ' permission error: %s' % e.message)
-    finally:
-      if temp_fh:
-        os.remove(temp_fh.GetAbsPath())
diff --git a/tools/telemetry/telemetry/value/trace_unittest.py b/tools/telemetry/telemetry/value/trace_unittest.py
index a197a9c..8f03b8aa 100644
--- a/tools/telemetry/telemetry/value/trace_unittest.py
+++ b/tools/telemetry/telemetry/value/trace_unittest.py
@@ -106,23 +106,14 @@
     self.actual_tempdir = trace.tempfile.tempdir
     trace.tempfile.tempdir = self.temp_test_dir
 
-  def testNoLeakedTempFileWhenTraceSerialize(self):
-    tempdir = tempfile.mkdtemp()
-    v = trace.TraceValue(None, trace_data.TraceData({'test': 1}))
-    fh = v.Serialize(tempdir)
-    try:
-      shutil.rmtree(fh.GetAbsPath(), ignore_errors=True)
-      self.assertTrue(tempdir)
-    finally:
-      shutil.rmtree(tempdir)
-      self.assertTrue(_IsEmptyDir(self.temp_test_dir))
+  def testNoLeakedTempFileOnImplicitCleanUp(self):
+    with trace.TraceValue(None, trace_data.TraceData({'test': 1})):
+      pass
+    self.assertTrue(_IsEmptyDir(self.temp_test_dir))
 
   def testNoLeakedTempFileWhenUploadingTrace(self):
     v = trace.TraceValue(None, trace_data.TraceData({'test': 1}))
-    trace.cloud_storage.SetCalculatedHashesForTesting(
-        TestDefaultDict(123))
-    bucket = trace.cloud_storage.PUBLIC_BUCKET
-    v.UploadToCloud(bucket)
+    v.CleanUp()
     self.assertTrue(_IsEmptyDir(self.temp_test_dir))
 
   def tearDown(self):
diff --git a/tools/valgrind/drmemory/suppressions_full.txt b/tools/valgrind/drmemory/suppressions_full.txt
index d2db1bf..eae85e5 100644
--- a/tools/valgrind/drmemory/suppressions_full.txt
+++ b/tools/valgrind/drmemory/suppressions_full.txt
@@ -1103,13 +1103,6 @@
 # at the peak set of tests we plan to run and remove the unused ones.
 
 UNINITIALIZED READ
-name=bug_99307
-*!modp_b64_encode
-*!base::Base64Encode*
-*!web_ui_util::GetImageDataUrl
-*!::NetworkInfoDictionary::set_icon
-
-UNINITIALIZED READ
 name=bug_101781
 *!encode_one_block
 *!encode_mcu_huff
diff --git a/tools/valgrind/gtest_exclude/cc_unittests.gtest-drmemory_win32.txt b/tools/valgrind/gtest_exclude/cc_unittests.gtest-drmemory_win32.txt
index bdc0e84..e7f3488 100644
--- a/tools/valgrind/gtest_exclude/cc_unittests.gtest-drmemory_win32.txt
+++ b/tools/valgrind/gtest_exclude/cc_unittests.gtest-drmemory_win32.txt
@@ -26,3 +26,6 @@
 PixelResourceTest/LayerTreeHostMasksPixelTest.MaskOfReplica/6
 PixelResourceTest/LayerTreeHostMasksPixelTest.MaskOfReplicaOfClippedLayer/5
 PixelResourceTest/LayerTreeHostMasksPixelTest.MaskOfReplicaOfClippedLayer/6
+
+# https://crbug.com/460581
+LayerTreeHostPictureTestRSLLMembershipWithScale.RunMultiThread_DirectRenderer_ImplSidePaint
diff --git a/tools/valgrind/gtest_exclude/content_unittests.gtest.txt b/tools/valgrind/gtest_exclude/content_unittests.gtest.txt
index d47ad75..60e98f2e 100644
--- a/tools/valgrind/gtest_exclude/content_unittests.gtest.txt
+++ b/tools/valgrind/gtest_exclude/content_unittests.gtest.txt
@@ -10,3 +10,7 @@
 
 # https://crbug.com/449103
 WebInputEventAuraTest.TestMakeWebKeyboardEventWindowsKeyCode
+
+# Flaky: https://crbug.com/460578
+DesktopCaptureDeviceTest.InvertedFrame
+DesktopCaptureDeviceTest.UnpackedFrame
diff --git a/tools/valgrind/gtest_exclude/installer_util_unittests.gtest-drmemory_win32.txt b/tools/valgrind/gtest_exclude/installer_util_unittests.gtest-drmemory_win32.txt
new file mode 100644
index 0000000..742862828
--- /dev/null
+++ b/tools/valgrind/gtest_exclude/installer_util_unittests.gtest-drmemory_win32.txt
@@ -0,0 +1,2 @@
+# https://crbug.com/460584
+CopyTreeWorkItemTest.NewNameAndCopyTest
diff --git a/tools/valgrind/gtest_exclude/remoting_unittests.gtest.txt b/tools/valgrind/gtest_exclude/remoting_unittests.gtest.txt
index 6491f45..c322014 100644
--- a/tools/valgrind/gtest_exclude/remoting_unittests.gtest.txt
+++ b/tools/valgrind/gtest_exclude/remoting_unittests.gtest.txt
@@ -1,5 +1,2 @@
 # http://crbug.com/241856
 VideoSchedulerTest.StartAndStop
-
-# crbug.com/458691
-ClientSessionTest.ClipboardStubFilter
diff --git a/tools/valgrind/memcheck/suppressions.txt b/tools/valgrind/memcheck/suppressions.txt
index 67243d14..9ea9570 100644
--- a/tools/valgrind/memcheck/suppressions.txt
+++ b/tools/valgrind/memcheck/suppressions.txt
@@ -1245,14 +1245,6 @@
    obj:*
 }
 {
-   bug_99307
-   Memcheck:Uninitialized
-   fun:modp_b64_encode
-   fun:_ZN4base12Base64Encode*
-   fun:_ZN11web_ui_util15GetImageDataUrlERK8SkBitmap
-   fun:_ZN12_GLOBAL__N_121NetworkInfoDictionary8set_iconERK8SkBitmap
-}
-{
    bug_100982
    Memcheck:Leak
    fun:_Znw*
@@ -3653,3 +3645,4 @@
    fun:_ZN4base12_GLOBAL__N_112WorkerThread10ThreadMainEv
    fun:_ZN4base12_GLOBAL__N_110ThreadFuncEPv
 }
+
diff --git a/ui/accessibility/ax_generated_tree_unittest.cc b/ui/accessibility/ax_generated_tree_unittest.cc
index 3845886..b0bd21c 100644
--- a/ui/accessibility/ax_generated_tree_unittest.cc
+++ b/ui/accessibility/ax_generated_tree_unittest.cc
@@ -41,7 +41,7 @@
 
 std::string TreeToString(const AXTree& tree) {
   std::string result;
-  TreeToStringHelper(tree.GetRoot(), &result);
+  TreeToStringHelper(tree.root(), &result);
   return "(" + result + ")";
 }
 
@@ -125,7 +125,7 @@
             tree0.CreateTreeSource());
         AXTreeSerializer<const AXNode*> serializer(tree0_source.get());
         AXTreeUpdate update0;
-        serializer.SerializeChanges(tree0.GetRoot(), &update0);
+        serializer.SerializeChanges(tree0.root(), &update0);
 
         AXTree dst_tree;
         ASSERT_TRUE(dst_tree.Unserialize(update0));
diff --git a/ui/accessibility/ax_node.h b/ui/accessibility/ax_node.h
index 5da7340..6fd766c5 100644
--- a/ui/accessibility/ax_node.h
+++ b/ui/accessibility/ax_node.h
@@ -5,8 +5,14 @@
 #ifndef UI_ACCESSIBILITY_AX_NODE_H_
 #define UI_ACCESSIBILITY_AX_NODE_H_
 
+#include <vector>
+
 #include "ui/accessibility/ax_node_data.h"
 
+namespace gfx {
+class Rect;
+}
+
 namespace ui {
 
 // One node in an AXTree.
@@ -62,7 +68,6 @@
   AXNodeData data_;
 };
 
-
 }  // namespace ui
 
 #endif  // UI_ACCESSIBILITY_AX_NODE_H_
diff --git a/ui/accessibility/ax_serializable_tree.cc b/ui/accessibility/ax_serializable_tree.cc
index 750b3b0..606dd843 100644
--- a/ui/accessibility/ax_serializable_tree.cc
+++ b/ui/accessibility/ax_serializable_tree.cc
@@ -20,7 +20,7 @@
   ~AXTreeSourceAdapter() override {}
 
   // AXTreeSource implementation.
-  AXNode* GetRoot() const override { return tree_->GetRoot(); }
+  AXNode* GetRoot() const override { return tree_->root(); }
 
   AXNode* GetFromId(int32 id) const override { return tree_->GetFromId(id); }
 
diff --git a/ui/accessibility/ax_tree.cc b/ui/accessibility/ax_tree.cc
index bb67a47..73b5c6d 100644
--- a/ui/accessibility/ax_tree.cc
+++ b/ui/accessibility/ax_tree.cc
@@ -22,7 +22,7 @@
   return result;
 }
 
-}  // anonymous namespace
+}  // namespace
 
 // Intermediate state to keep track of during a tree update.
 struct AXTreeUpdateState {
@@ -67,13 +67,9 @@
   delegate_ = delegate;
 }
 
-AXNode* AXTree::GetRoot() const {
-  return root_;
-}
-
 AXNode* AXTree::GetFromId(int32 id) const {
   base::hash_map<int32, AXNode*>::const_iterator iter = id_map_.find(id);
-  return iter != id_map_.end() ? (iter->second) : NULL;
+  return iter != id_map_.end() ? iter->second : NULL;
 }
 
 bool AXTree::Unserialize(const AXTreeUpdate& update) {
@@ -142,8 +138,7 @@
   return TreeToStringHelper(root_, 0);
 }
 
-AXNode* AXTree::CreateNode(
-    AXNode* parent, int32 id, int32 index_in_parent) {
+AXNode* AXTree::CreateNode(AXNode* parent, int32 id, int32 index_in_parent) {
   AXNode* new_node = new AXNode(parent, id, index_in_parent);
   id_map_[new_node->id()] = new_node;
   if (delegate_)
@@ -151,8 +146,8 @@
   return new_node;
 }
 
-bool AXTree::UpdateNode(
-    const AXNodeData& src, AXTreeUpdateState* update_state) {
+bool AXTree::UpdateNode(const AXNodeData& src,
+                        AXTreeUpdateState* update_state) {
   // This method updates one node in the tree based on serialized data
   // received in an AXTreeUpdate. See AXTreeUpdate for pre and post
   // conditions.
@@ -222,7 +217,7 @@
 }
 
 bool AXTree::DeleteOldChildren(AXNode* node,
-                               const std::vector<int32> new_child_ids) {
+                               const std::vector<int32>& new_child_ids) {
   // Create a set of child ids in |src| for fast lookup, and return false
   // if a duplicate is found;
   std::set<int32> new_child_id_set;
@@ -247,7 +242,7 @@
 }
 
 bool AXTree::CreateNewChildVector(AXNode* node,
-                                  const std::vector<int32> new_child_ids,
+                                  const std::vector<int32>& new_child_ids,
                                   std::vector<AXNode*>* new_children,
                                   AXTreeUpdateState* update_state) {
   bool success = true;
diff --git a/ui/accessibility/ax_tree.h b/ui/accessibility/ax_tree.h
index c8470c0..3027125 100644
--- a/ui/accessibility/ax_tree.h
+++ b/ui/accessibility/ax_tree.h
@@ -65,7 +65,7 @@
       this->node = node;
       this->type = type;
     }
-    AXNode *node;
+    AXNode* node;
     ChangeType type;
   };
 
@@ -91,8 +91,10 @@
 
   virtual void SetDelegate(AXTreeDelegate* delegate);
 
-  virtual AXNode* GetRoot() const;
-  virtual AXNode* GetFromId(int32 id) const;
+  AXNode* root() const { return root_; }
+
+  // Returns the AXNode with the given |id| if it is part of this AXTree.
+  AXNode* GetFromId(int32 id) const;
 
   // Returns true on success. If it returns false, it's a fatal error
   // and this tree should be destroyed, and the source of the tree update
@@ -104,7 +106,7 @@
 
   // A string describing the error from an unsuccessful Unserialize,
   // for testing and debugging.
-  const std::string& error() { return error_; }
+  const std::string& error() const { return error_; }
 
  private:
   AXNode* CreateNode(AXNode* parent, int32 id, int32 index_in_parent);
@@ -126,7 +128,7 @@
   // child and its subtree if its id is not in |new_child_ids|. Returns
   // true on success, false on fatal error.
   bool DeleteOldChildren(AXNode* node,
-                         const std::vector<int32> new_child_ids);
+                         const std::vector<int32>& new_child_ids);
 
   // Iterate over |new_child_ids| and populate |new_children| with
   // pointers to child nodes, reusing existing nodes already in the tree
@@ -134,7 +136,7 @@
   // if the id already exists as the child of another node, that's an
   // error. Returns true on success, false on fatal error.
   bool CreateNewChildVector(AXNode* node,
-                            const std::vector<int32> new_child_ids,
+                            const std::vector<int32>& new_child_ids,
                             std::vector<AXNode*>* new_children,
                             AXTreeUpdateState* update_state);
 
diff --git a/ui/accessibility/ax_tree_serializer_unittest.cc b/ui/accessibility/ax_tree_serializer_unittest.cc
index 691c0fb..733e289 100644
--- a/ui/accessibility/ax_tree_serializer_unittest.cc
+++ b/ui/accessibility/ax_tree_serializer_unittest.cc
@@ -50,7 +50,7 @@
   tree0_source_.reset(tree0_->CreateTreeSource());
   serializer_.reset(new AXTreeSerializer<const AXNode*>(tree0_source_.get()));
   AXTreeUpdate unused_update;
-  serializer_->SerializeChanges(tree0_->GetRoot(), &unused_update);
+  serializer_->SerializeChanges(tree0_->root(), &unused_update);
 
   // Pretend that tree0_ turned into tree1_. The next call to
   // AXTreeSerializer will force it to consider these changes to
diff --git a/ui/accessibility/ax_tree_unittest.cc b/ui/accessibility/ax_tree_unittest.cc
index 8be0f3901..83a8ae5 100644
--- a/ui/accessibility/ax_tree_unittest.cc
+++ b/ui/accessibility/ax_tree_unittest.cc
@@ -114,12 +114,12 @@
       src_tree.CreateTreeSource());
   AXTreeSerializer<const AXNode*> serializer(tree_source.get());
   AXTreeUpdate update;
-  serializer.SerializeChanges(src_tree.GetRoot(), &update);
+  serializer.SerializeChanges(src_tree.root(), &update);
 
   AXTree dst_tree;
   ASSERT_TRUE(dst_tree.Unserialize(update));
 
-  const AXNode* root_node = dst_tree.GetRoot();
+  const AXNode* root_node = dst_tree.root();
   ASSERT_TRUE(root_node != NULL);
   EXPECT_EQ(root.id, root_node->id());
   EXPECT_EQ(root.role, root_node->data().role);
diff --git a/ui/accessibility/ax_tree_update.h b/ui/accessibility/ax_tree_update.h
index 314a78f..5a92c165 100644
--- a/ui/accessibility/ax_tree_update.h
+++ b/ui/accessibility/ax_tree_update.h
@@ -5,6 +5,7 @@
 #ifndef UI_ACCESSIBILITY_AX_TREE_UPDATE_H_
 #define UI_ACCESSIBILITY_AX_TREE_UPDATE_H_
 
+#include <string>
 #include <vector>
 
 #include "ui/accessibility/ax_node_data.h"
diff --git a/ui/android/java/res/drawable-hdpi/verify_checkmark.png b/ui/android/java/res/drawable-hdpi/verify_checkmark.png
index 6c5f96b..4fa0cab 100644
--- a/ui/android/java/res/drawable-hdpi/verify_checkmark.png
+++ b/ui/android/java/res/drawable-hdpi/verify_checkmark.png
Binary files differ
diff --git a/ui/android/java/res/drawable-mdpi/verify_checkmark.png b/ui/android/java/res/drawable-mdpi/verify_checkmark.png
index 81cda89..24d308f 100644
--- a/ui/android/java/res/drawable-mdpi/verify_checkmark.png
+++ b/ui/android/java/res/drawable-mdpi/verify_checkmark.png
Binary files differ
diff --git a/ui/android/java/res/drawable-xhdpi/verify_checkmark.png b/ui/android/java/res/drawable-xhdpi/verify_checkmark.png
index e2b3d17..e50091c 100644
--- a/ui/android/java/res/drawable-xhdpi/verify_checkmark.png
+++ b/ui/android/java/res/drawable-xhdpi/verify_checkmark.png
Binary files differ
diff --git a/ui/android/java/res/drawable-xxhdpi/verify_checkmark.png b/ui/android/java/res/drawable-xxhdpi/verify_checkmark.png
index 1d3f804..b7093fff 100644
--- a/ui/android/java/res/drawable-xxhdpi/verify_checkmark.png
+++ b/ui/android/java/res/drawable-xxhdpi/verify_checkmark.png
Binary files differ
diff --git a/ui/android/java/res/drawable-xxxhdpi/verify_checkmark.png b/ui/android/java/res/drawable-xxxhdpi/verify_checkmark.png
index 5069d7b..bba25ca 100644
--- a/ui/android/java/res/drawable-xxxhdpi/verify_checkmark.png
+++ b/ui/android/java/res/drawable-xxxhdpi/verify_checkmark.png
Binary files differ
diff --git a/ui/android/java/res/values/dimens.xml b/ui/android/java/res/values/dimens.xml
index 5a7a227..c4c2491 100644
--- a/ui/android/java/res/values/dimens.xml
+++ b/ui/android/java/res/values/dimens.xml
@@ -8,7 +8,7 @@
 
     <!--
          14.5 = Seekbar thumb width - border width, but it depends on the width
-	     of the seek bar icon.
+         of the seek bar icon.
     -->
     <dimen name="color_picker_gradient_margin">14.5dp</dimen>
     <dimen name="color_button_height">60dp</dimen>
@@ -22,5 +22,4 @@
     -->
     <dimen name="config_min_scaling_span">27.0mm</dimen>
     <dimen name="config_min_scaling_touch_major">48.0dp</dimen>
-
 </resources>
diff --git a/ui/app_list/BUILD.gn b/ui/app_list/BUILD.gn
index 7e05648..f1fae35 100644
--- a/ui/app_list/BUILD.gn
+++ b/ui/app_list/BUILD.gn
@@ -234,6 +234,8 @@
       "test/run_all_unittests.cc",
     ]
 
+    configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
+
     deps = [
       ":app_list",
       ":test_support",
diff --git a/ui/app_list/app_list_constants.cc b/ui/app_list/app_list_constants.cc
index f30016d2..2916c44b 100644
--- a/ui/app_list/app_list_constants.cc
+++ b/ui/app_list/app_list_constants.cc
@@ -9,6 +9,8 @@
 const SkColor kContentsBackgroundColor = SkColorSetRGB(0xF2, 0xF2, 0xF2);
 const SkColor kSearchBoxBackground = SK_ColorWHITE;
 
+const SkColor kSearchTextColor = SkColorSetRGB(0x33, 0x33, 0x33);
+
 // In Windows, transparent background color will cause ugly text rendering,
 // therefore kContentsBackgroundColor should be used. See crbug.com/406989
 #if defined(OS_CHROMEOS)
@@ -23,10 +25,18 @@
 // The color of the separator used inside dialogs in the app list.
 const SkColor kDialogSeparatorColor = SkColorSetRGB(0xD1, 0xD1, 0xD1);
 
+// Non-views Mac requires opaque colors to paint correctly.
+#if defined(OS_MACOSX) && !defined(TOOLKIT_VIEWS)
 // The mouse hover colour (3% black over kContentsBackgroundColor).
 const SkColor kHighlightedColor = SkColorSetRGB(0xEE, 0xEE, 0xEE);
 // The keyboard select colour (6% black over kContentsBackgroundColor).
 const SkColor kSelectedColor = SkColorSetRGB(0xE6, 0xE6, 0xE6);
+#else
+// The mouse hover colour (3% black).
+const SkColor kHighlightedColor = SkColorSetARGB(8, 0, 0, 0);
+// The keyboard select colour (6% black).
+const SkColor kSelectedColor = SkColorSetARGB(15, 0, 0, 0);
+#endif
 
 const SkColor kPagerHoverColor = SkColorSetRGB(0xB4, 0xB4, 0xB4);
 const SkColor kPagerNormalColor = SkColorSetRGB(0xE2, 0xE2, 0xE2);
@@ -34,7 +44,7 @@
 
 const SkColor kResultBorderColor = SkColorSetRGB(0xE5, 0xE5, 0xE5);
 const SkColor kResultDefaultTextColor = SkColorSetRGB(0x33, 0x33, 0x33);
-const SkColor kResultDimmedTextColor = SkColorSetRGB(0x96, 0x96, 0x96);
+const SkColor kResultDimmedTextColor = SkColorSetRGB(0x84, 0x84, 0x84);
 const SkColor kResultURLTextColor = SkColorSetRGB(0x00, 0x99, 0x33);
 
 const SkColor kGridTitleColor = SkColorSetRGB(0x33, 0x33, 0x33);
@@ -84,7 +94,7 @@
 const int kGridIconDimension = 48;
 
 // Preferred search result icon sizes.
-const int kListIconSize = 32;
+const int kListIconSize = 24;
 const int kTileIconSize = 48;
 
 // Preferred number of columns and rows in the centered app list apps grid.
@@ -118,7 +128,14 @@
 
 // Font style for app item labels.
 const ui::ResourceBundle::FontStyle kItemTextFontStyle =
-    ui::ResourceBundle::SmallBoldFont;
+    ui::ResourceBundle::SmallFont;
+
+// The UMA histogram that logs which page gets opened by the user.
+const char kPageOpenedHistogram[] = "Apps.AppListPageOpened";
+
+// The UMA histogram that logs the type of search result opened.
+const char kSearchResultOpenDisplayTypeHistogram[] =
+    "Apps.AppListSearchResultOpenDisplayType";
 
 #if defined(OS_LINUX)
 #if defined(GOOGLE_CHROME_BUILD)
diff --git a/ui/app_list/app_list_constants.h b/ui/app_list/app_list_constants.h
index f3b4f33..eee4272b 100644
--- a/ui/app_list/app_list_constants.h
+++ b/ui/app_list/app_list_constants.h
@@ -15,6 +15,9 @@
 
 APP_LIST_EXPORT extern const SkColor kContentsBackgroundColor;
 APP_LIST_EXPORT extern const SkColor kSearchBoxBackground;
+
+APP_LIST_EXPORT extern const SkColor kSearchTextColor;
+
 APP_LIST_EXPORT extern const SkColor kLabelBackgroundColor;
 APP_LIST_EXPORT extern const SkColor kTopSeparatorColor;
 APP_LIST_EXPORT extern const SkColor kBottomSeparatorColor;
@@ -78,6 +81,9 @@
 
 APP_LIST_EXPORT extern const ui::ResourceBundle::FontStyle kItemTextFontStyle;
 
+APP_LIST_EXPORT extern const char kPageOpenedHistogram[];
+APP_LIST_EXPORT extern const char kSearchResultOpenDisplayTypeHistogram[];
+
 #if defined(OS_LINUX)
 // The WM_CLASS name for the app launcher window on Linux.
 APP_LIST_EXPORT extern const char kAppListWMClass[];
diff --git a/ui/app_list/app_list_model.cc b/ui/app_list/app_list_model.cc
index 491087e..0a8e0cd 100644
--- a/ui/app_list/app_list_model.cc
+++ b/ui/app_list/app_list_model.cc
@@ -20,7 +20,8 @@
       status_(STATUS_NORMAL),
       state_(INVALID_STATE),
       folders_enabled_(false),
-      custom_launcher_page_enabled_(true) {
+      custom_launcher_page_enabled_(true),
+      search_engine_is_google_(false) {
   top_level_item_list_->AddObserver(this);
 }
 
@@ -347,6 +348,12 @@
   custom_launcher_page_subpage_depth_ = 0;
 }
 
+void AppListModel::SetSearchEngineIsGoogle(bool is_google) {
+  search_engine_is_google_ = is_google;
+  FOR_EACH_OBSERVER(AppListModelObserver, observers_,
+                    OnSearchEngineIsGoogleChanged(is_google));
+}
+
 // Private methods
 
 void AppListModel::OnListItemMoved(size_t from_index,
diff --git a/ui/app_list/app_list_model.h b/ui/app_list/app_list_model.h
index edaf20e..6c61e71 100644
--- a/ui/app_list/app_list_model.h
+++ b/ui/app_list/app_list_model.h
@@ -39,13 +39,16 @@
     STATUS_SYNCING,  // Syncing apps or installing synced apps.
   };
 
+  // Do not change the order of these as they are used for metrics.
   enum State {
-    STATE_APPS,
+    STATE_APPS = 0,
     STATE_SEARCH_RESULTS,
     STATE_START,
     STATE_CUSTOM_LAUNCHER_PAGE,
+    // Add new values here.
 
     INVALID_STATE,
+    STATE_LAST = INVALID_STATE,
   };
 
   typedef ui::ListModel<SearchResult> SearchResults;
@@ -131,7 +134,17 @@
 
   // Sets whether or not the custom launcher page should be enabled.
   void SetCustomLauncherPageEnabled(bool enabled);
-  bool custom_launcher_page_enabled() { return custom_launcher_page_enabled_; }
+  bool custom_launcher_page_enabled() const {
+    return custom_launcher_page_enabled_;
+  }
+
+  void set_custom_launcher_page_name(const std::string& name) {
+    custom_launcher_page_name_ = name;
+  }
+
+  const std::string& custom_launcher_page_name() const {
+    return custom_launcher_page_name_;
+  }
 
   // Pushes a custom launcher page's subpage into the state stack in the model.
   void PushCustomLauncherPageSubpage();
@@ -147,6 +160,9 @@
     return custom_launcher_page_subpage_depth_;
   }
 
+  void SetSearchEngineIsGoogle(bool is_google);
+  bool search_engine_is_google() const { return search_engine_is_google_; }
+
   // Filters the given |results| by |display_type|. The returned list is
   // truncated to |max_results|.
   static std::vector<SearchResult*> FilterSearchResultsByDisplayType(
@@ -203,6 +219,8 @@
   ObserverList<AppListModelObserver, true> observers_;
   bool folders_enabled_;
   bool custom_launcher_page_enabled_;
+  std::string custom_launcher_page_name_;
+  bool search_engine_is_google_;
 
   // The current number of subpages the custom launcher page has pushed.
   int custom_launcher_page_subpage_depth_;
diff --git a/ui/app_list/app_list_model_observer.h b/ui/app_list/app_list_model_observer.h
index 13953131..09cccc0 100644
--- a/ui/app_list/app_list_model_observer.h
+++ b/ui/app_list/app_list_model_observer.h
@@ -36,6 +36,9 @@
   // Triggered when the custom launcher page enabled state is changed.
   virtual void OnCustomLauncherPageEnabledStateChanged(bool enabled) {}
 
+  // Triggered when the search engine is changed to and from Google.
+  virtual void OnSearchEngineIsGoogleChanged(bool is_google) {}
+
  protected:
   virtual ~AppListModelObserver() {}
 };
diff --git a/ui/app_list/search/mixer.cc b/ui/app_list/search/mixer.cc
index eac9fd7..9ee9d77 100644
--- a/ui/app_list/search/mixer.cc
+++ b/ui/app_list/search/mixer.cc
@@ -209,6 +209,7 @@
       // Update and use the old result if it exists.
       SearchResult* ui_result = ui_result_it->second;
       UpdateResult(new_result, ui_result);
+      ui_result->set_relevance(sort_data.score);
 
       // |ui_results| takes back ownership from |ui_results_map| here.
       ui_results->Add(ui_result);
@@ -217,8 +218,10 @@
       // results.
       ui_results_map.erase(ui_result->id());
     } else {
+      scoped_ptr<SearchResult> result_copy = new_result.Duplicate();
+      result_copy->set_relevance(sort_data.score);
       // Copy the result from |new_results| otherwise.
-      ui_results->Add(new_result.Duplicate().release());
+      ui_results->Add(result_copy.release());
     }
   }
 
diff --git a/ui/app_list/search/mixer_unittest.cc b/ui/app_list/search/mixer_unittest.cc
index 78c233e..7c56159d 100644
--- a/ui/app_list/search/mixer_unittest.cc
+++ b/ui/app_list/search/mixer_unittest.cc
@@ -35,7 +35,7 @@
   void Open(int event_flags) override {}
   void InvokeAction(int action_index, int event_flags) override {}
   scoped_ptr<SearchResult> Duplicate() const override {
-    return scoped_ptr<SearchResult>(new TestSearchResult(id(), relevance()));
+    return make_scoped_ptr(new TestSearchResult(id(), relevance()));
   }
 
   // For reference equality testing. (Addresses cannot be used to test reference
diff --git a/ui/app_list/search_box_model.cc b/ui/app_list/search_box_model.cc
index 006c4b3f..87632d5f 100644
--- a/ui/app_list/search_box_model.cc
+++ b/ui/app_list/search_box_model.cc
@@ -13,11 +13,13 @@
     const gfx::ImageSkia& on_icon,
     const base::string16& on_tooltip,
     const gfx::ImageSkia& off_icon,
-    const base::string16& off_tooltip)
+    const base::string16& off_tooltip,
+    const base::string16& accessible_name)
     : on_icon(on_icon),
       on_tooltip(on_tooltip),
       off_icon(off_icon),
-      off_tooltip(off_tooltip) {
+      off_tooltip(off_tooltip),
+      accessible_name(accessible_name) {
 }
 
 SearchBoxModel::SpeechButtonProperty::~SpeechButtonProperty() {
@@ -50,6 +52,14 @@
   FOR_EACH_OBSERVER(SearchBoxModelObserver, observers_, HintTextChanged());
 }
 
+void SearchBoxModel::SetAccessibleName(const base::string16& accessible_name) {
+  if (accessible_name_ == accessible_name)
+    return;
+
+  accessible_name_ = accessible_name;
+  FOR_EACH_OBSERVER(SearchBoxModelObserver, observers_, HintTextChanged());
+}
+
 void SearchBoxModel::SetSelectionModel(const gfx::SelectionModel& sel) {
   if (selection_model_ == sel)
     return;
diff --git a/ui/app_list/search_box_model.h b/ui/app_list/search_box_model.h
index 0512c2e..9faaab28 100644
--- a/ui/app_list/search_box_model.h
+++ b/ui/app_list/search_box_model.h
@@ -28,7 +28,8 @@
     SpeechButtonProperty(const gfx::ImageSkia& on_icon,
                          const base::string16& on_tooltip,
                          const gfx::ImageSkia& off_icon,
-                         const base::string16& off_tooltip);
+                         const base::string16& off_tooltip,
+                         const base::string16& accessible_name);
     ~SpeechButtonProperty();
 
     // The icon/tooltip when the hotword is on.
@@ -38,6 +39,9 @@
     // The icon/tooltip when the hotword is off.
     gfx::ImageSkia off_icon;
     base::string16 off_tooltip;
+
+    // The accessibility name of the button.
+    base::string16 accessible_name;
   };
 
   SearchBoxModel();
@@ -58,6 +62,10 @@
   void SetHintText(const base::string16& hint_text);
   const base::string16& hint_text() const { return hint_text_; }
 
+  // Sets/gets the text for screen readers on the search box.
+  void SetAccessibleName(const base::string16& accessible_name);
+  const base::string16& accessible_name() const { return accessible_name_; }
+
   // Sets/gets the selection model for the search box's Textfield.
   void SetSelectionModel(const gfx::SelectionModel& sel);
   const gfx::SelectionModel& selection_model() const {
@@ -75,6 +83,7 @@
   gfx::ImageSkia icon_;
   scoped_ptr<SpeechButtonProperty> speech_button_;
   base::string16 hint_text_;
+  base::string16 accessible_name_;
   gfx::SelectionModel selection_model_;
   base::string16 text_;
 
diff --git a/ui/app_list/search_controller.cc b/ui/app_list/search_controller.cc
index 66d7593c..0b1639e 100644
--- a/ui/app_list/search_controller.cc
+++ b/ui/app_list/search_controller.cc
@@ -9,9 +9,11 @@
 
 #include "base/bind.h"
 #include "base/memory/scoped_ptr.h"
+#include "base/metrics/histogram_macros.h"
 #include "base/metrics/user_metrics.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
+#include "ui/app_list/app_list_constants.h"
 #include "ui/app_list/search/history.h"
 #include "ui/app_list/search_box_model.h"
 #include "ui/app_list/search_provider.h"
@@ -77,6 +79,10 @@
   // Count AppList.Search here because it is composed of search + action.
   base::RecordAction(base::UserMetricsAction("AppList_Search"));
 
+  UMA_HISTOGRAM_ENUMERATION(kSearchResultOpenDisplayTypeHistogram,
+                            result->display_type(),
+                            SearchResult::DISPLAY_TYPE_LAST);
+
   result->Open(event_flags);
 
   if (history_ && history_->IsReady()) {
diff --git a/ui/app_list/search_result.cc b/ui/app_list/search_result.cc
index 1559bb91..87bea21 100644
--- a/ui/app_list/search_result.cc
+++ b/ui/app_list/search_result.cc
@@ -80,6 +80,8 @@
       return kListIconSize;
     case DISPLAY_NONE:
       return 0;
+    case DISPLAY_TYPE_LAST:
+      break;
   }
   NOTREACHED();
   return 0;
diff --git a/ui/app_list/search_result.h b/ui/app_list/search_result.h
index 5f8e371..4d5300c 100644
--- a/ui/app_list/search_result.h
+++ b/ui/app_list/search_result.h
@@ -28,12 +28,16 @@
 // default style.
 class APP_LIST_EXPORT SearchResult {
  public:
-  // How the result should be displayed.
+  // How the result should be displayed. Do not change the order of these as
+  // they are used for metrics.
   enum DisplayType {
+    DISPLAY_NONE = 0,
     DISPLAY_LIST,
     DISPLAY_TILE,
     DISPLAY_RECOMMENDATION,
-    DISPLAY_NONE,
+    // Add new values here.
+
+    DISPLAY_TYPE_LAST,
   };
 
   // A tagged range in search result text.
@@ -98,7 +102,9 @@
   void set_details_tags(const Tags& tags) { details_tags_ = tags; }
 
   const std::string& id() const { return id_; }
+
   double relevance() const { return relevance_; }
+  void set_relevance(double relevance) { relevance_ = relevance; }
 
   DisplayType display_type() const { return display_type_; }
   void set_display_type(DisplayType display_type) {
@@ -146,7 +152,6 @@
 
  protected:
   void set_id(const std::string& id) { id_ = id; }
-  void set_relevance(double relevance) { relevance_ = relevance; }
   void set_voice_result(bool voice_result) { voice_result_ = voice_result; }
 
  private:
diff --git a/ui/app_list/test/test_search_result.cc b/ui/app_list/test/test_search_result.cc
index a93229e..cdb16962 100644
--- a/ui/app_list/test/test_search_result.cc
+++ b/ui/app_list/test/test_search_result.cc
@@ -12,10 +12,6 @@
 TestSearchResult::~TestSearchResult() {
 }
 
-void TestSearchResult::SetDisplayType(SearchResult::DisplayType type) {
-  set_display_type(type);
-}
-
 scoped_ptr<SearchResult> TestSearchResult::Duplicate() const {
   NOTREACHED();
   return nullptr;
diff --git a/ui/app_list/test/test_search_result.h b/ui/app_list/test/test_search_result.h
index 5f18d86..dcf2e46 100644
--- a/ui/app_list/test/test_search_result.h
+++ b/ui/app_list/test/test_search_result.h
@@ -18,8 +18,6 @@
   // SearchResult:
   scoped_ptr<SearchResult> Duplicate() const override;
 
-  void SetDisplayType(SearchResult::DisplayType type);
-
  private:
   DISALLOW_COPY_AND_ASSIGN(TestSearchResult);
 };
diff --git a/ui/app_list/views/all_apps_tile_item_view.cc b/ui/app_list/views/all_apps_tile_item_view.cc
index c6e0ab51..c7e10f7 100644
--- a/ui/app_list/views/all_apps_tile_item_view.cc
+++ b/ui/app_list/views/all_apps_tile_item_view.cc
@@ -4,6 +4,8 @@
 
 #include "ui/app_list/views/all_apps_tile_item_view.h"
 
+#include "base/metrics/histogram_macros.h"
+#include "ui/app_list/app_list_constants.h"
 #include "ui/app_list/views/contents_view.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/strings/grit/ui_strings.h"
@@ -27,6 +29,9 @@
 
 void AllAppsTileItemView::ButtonPressed(views::Button* sender,
                                         const ui::Event& event) {
+  UMA_HISTOGRAM_ENUMERATION(kPageOpenedHistogram, AppListModel::STATE_APPS,
+                            AppListModel::STATE_LAST);
+
   contents_view_->SetActivePage(
       contents_view_->GetPageIndexForState(AppListModel::STATE_APPS));
 }
diff --git a/ui/app_list/views/app_list_folder_view.cc b/ui/app_list/views/app_list_folder_view.cc
index da765a2..8e2bb8a7 100644
--- a/ui/app_list/views/app_list_folder_view.cc
+++ b/ui/app_list/views/app_list_folder_view.cc
@@ -127,6 +127,23 @@
 }
 
 bool AppListFolderView::OnKeyPressed(const ui::KeyEvent& event) {
+  // Process TAB if focus should go to header; otherwise, AppsGridView will do
+  // the right thing.
+  if (event.key_code() == ui::VKEY_TAB) {
+    if (items_grid_view_->has_selected_view() == event.IsShiftDown() &&
+        !folder_header_view_->HasTextFocus()) {
+      folder_header_view_->SetTextFocus();
+      items_grid_view_->ClearAnySelectedView();
+      return true;
+    } else {
+      GiveBackFocusToSearchBox();
+    }
+  }
+
+  // This will select an app in the list, so we need to relinquish focus.
+  if (event.key_code() == ui::VKEY_DOWN)
+    GiveBackFocusToSearchBox();
+
   return items_grid_view_->OnKeyPressed(event);
 }
 
diff --git a/ui/app_list/views/app_list_main_view.cc b/ui/app_list/views/app_list_main_view.cc
index 436641c7..df9f32b 100644
--- a/ui/app_list/views/app_list_main_view.cc
+++ b/ui/app_list/views/app_list_main_view.cc
@@ -26,6 +26,7 @@
 #include "ui/app_list/views/apps_grid_view.h"
 #include "ui/app_list/views/contents_view.h"
 #include "ui/app_list/views/search_box_view.h"
+#include "ui/app_list/views/search_result_page_view.h"
 #include "ui/app_list/views/start_page_view.h"
 #include "ui/views/border.h"
 #include "ui/views/controls/button/button.h"
@@ -106,6 +107,8 @@
 
   // Starts icon loading early.
   PreloadIcons(parent);
+
+  OnSearchEngineIsGoogleChanged(model_->search_engine_is_google());
 }
 
 void AppListMainView::AddContentsViews() {
@@ -241,8 +244,14 @@
     parent()->SchedulePaint();
 }
 
-void AppListMainView::OnCustomLauncherPageEnabledStateChanged(bool enabled) {
-  if (enabled) {
+bool AppListMainView::ShouldShowCustomLauncherPage() const {
+  return contents_view_->custom_page_view() &&
+         model_->custom_launcher_page_enabled() &&
+         model_->search_engine_is_google();
+}
+
+void AppListMainView::UpdateCustomLauncherPageVisibility() {
+  if (ShouldShowCustomLauncherPage()) {
     // Make the custom page view visible again.
     contents_view_->custom_page_view()->SetVisible(true);
   } else if (contents_view_->IsStateActive(
@@ -257,6 +266,36 @@
   }
 }
 
+void AppListMainView::OnCustomLauncherPageEnabledStateChanged(bool enabled) {
+  views::View* custom_page = contents_view_->custom_page_view();
+  if (!custom_page)
+    return;
+
+  if (enabled) {
+    // Make the custom page view visible again.
+    custom_page->SetVisible(true);
+  } else if (contents_view_->IsStateActive(
+                 AppListModel::STATE_CUSTOM_LAUNCHER_PAGE)) {
+    // Animate to the start page if currently on the custom page view. The view
+    // will hide on animation completion.
+    contents_view_->SetActivePage(
+        contents_view_->GetPageIndexForState(AppListModel::STATE_START));
+  } else {
+    // Hide the view immediately otherwise.
+    custom_page->SetVisible(false);
+  }
+}
+
+void AppListMainView::OnSearchEngineIsGoogleChanged(bool is_google) {
+  if (contents_view_->custom_page_view())
+    OnCustomLauncherPageEnabledStateChanged(is_google);
+
+  if (contents_view_->start_page_view()) {
+    contents_view_->start_page_view()->instant_container()->SetVisible(
+        is_google);
+  }
+}
+
 void AppListMainView::ActivateApp(AppListItem* item, int event_flags) {
   // TODO(jennyz): Activate the folder via AppListModel notification.
   if (item->GetItemType() == AppListFolderItem::kItemType)
@@ -291,6 +330,11 @@
   contents_view_->Back();
 }
 
+void AppListMainView::SetSearchResultSelection(bool select) {
+  if (contents_view_->GetActiveState() == AppListModel::STATE_SEARCH_RESULTS)
+    contents_view_->search_results_page_view()->SetSelection(select);
+}
+
 void AppListMainView::OnResultInstalled(SearchResult* result) {
   // Clears the search to show the apps grid. The last installed app
   // should be highlighted and made visible already.
diff --git a/ui/app_list/views/app_list_main_view.h b/ui/app_list/views/app_list_main_view.h
index be70a96..5e517756 100644
--- a/ui/app_list/views/app_list_main_view.h
+++ b/ui/app_list/views/app_list_main_view.h
@@ -73,8 +73,12 @@
   // Called when the search box's visibility is changed.
   void NotifySearchBoxVisibilityChanged();
 
+  bool ShouldShowCustomLauncherPage() const;
+  void UpdateCustomLauncherPageVisibility();
+
   // Overridden from AppListModelObserver:
   void OnCustomLauncherPageEnabledStateChanged(bool enabled) override;
+  void OnSearchEngineIsGoogleChanged(bool is_google) override;
 
  private:
   class IconLoader;
@@ -105,6 +109,7 @@
   // Overridden from SearchBoxViewDelegate:
   void QueryChanged(SearchBoxView* sender) override;
   void BackButtonPressed() override;
+  void SetSearchResultSelection(bool select) override;
 
   // Overridden from SearchResultListViewDelegate:
   void OnResultInstalled(SearchResult* result) override;
diff --git a/ui/app_list/views/app_list_main_view_unittest.cc b/ui/app_list/views/app_list_main_view_unittest.cc
index 23f9079..accc731 100644
--- a/ui/app_list/views/app_list_main_view_unittest.cc
+++ b/ui/app_list/views/app_list_main_view_unittest.cc
@@ -19,6 +19,7 @@
 #include "ui/app_list/views/contents_view.h"
 #include "ui/app_list/views/search_box_view.h"
 #include "ui/app_list/views/test/apps_grid_view_test_api.h"
+#include "ui/events/event_utils.h"
 #include "ui/events/test/event_generator.h"
 #include "ui/views/test/views_test_base.h"
 #include "ui/views/view_model.h"
@@ -127,16 +128,12 @@
 
   void SimulateClick(views::View* view) {
     gfx::Point center = view->GetLocalBounds().CenterPoint();
-    view->OnMousePressed(ui::MouseEvent(ui::ET_MOUSE_PRESSED,
-                                        center,
-                                        center,
-                                        ui::EF_LEFT_MOUSE_BUTTON,
-                                        ui::EF_LEFT_MOUSE_BUTTON));
-    view->OnMouseReleased(ui::MouseEvent(ui::ET_MOUSE_RELEASED,
-                                         center,
-                                         center,
-                                         ui::EF_LEFT_MOUSE_BUTTON,
-                                         ui::EF_LEFT_MOUSE_BUTTON));
+    view->OnMousePressed(ui::MouseEvent(
+        ui::ET_MOUSE_PRESSED, center, center, ui::EventTimeForNow(),
+        ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON));
+    view->OnMouseReleased(ui::MouseEvent(
+        ui::ET_MOUSE_RELEASED, center, center, ui::EventTimeForNow(),
+        ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON));
   }
 
   // |point| is in |grid_view|'s coordinates.
@@ -148,7 +145,8 @@
 
     gfx::Point translated =
         gfx::PointAtOffsetFromOrigin(point - view->bounds().origin());
-    ui::MouseEvent pressed_event(ui::ET_MOUSE_PRESSED, translated, point, 0, 0);
+    ui::MouseEvent pressed_event(ui::ET_MOUSE_PRESSED, translated, point,
+                                 ui::EventTimeForNow(), 0, 0);
     grid_view->InitiateDrag(view, pointer, pressed_event);
     return view;
   }
@@ -161,7 +159,8 @@
     DCHECK(drag_view);
     gfx::Point translated =
         gfx::PointAtOffsetFromOrigin(point - drag_view->bounds().origin());
-    ui::MouseEvent drag_event(ui::ET_MOUSE_DRAGGED, translated, point, 0, 0);
+    ui::MouseEvent drag_event(ui::ET_MOUSE_DRAGGED, translated, point,
+                              ui::EventTimeForNow(), 0, 0);
     grid_view->UpdateDragFromItem(pointer, drag_event);
   }
 
diff --git a/ui/app_list/views/app_list_view_unittest.cc b/ui/app_list/views/app_list_view_unittest.cc
index 5e30e66..81dc2cf 100644
--- a/ui/app_list/views/app_list_view_unittest.cc
+++ b/ui/app_list/views/app_list_view_unittest.cc
@@ -30,6 +30,7 @@
 #include "ui/app_list/views/start_page_view.h"
 #include "ui/app_list/views/test/apps_grid_view_test_api.h"
 #include "ui/app_list/views/tile_item_view.h"
+#include "ui/events/event_utils.h"
 #include "ui/views/controls/textfield/textfield.h"
 #include "ui/views/test/views_test_base.h"
 #include "ui/views/views_delegate.h"
@@ -60,16 +61,12 @@
 
 void SimulateClick(views::View* view) {
   gfx::Point center = view->GetLocalBounds().CenterPoint();
-  view->OnMousePressed(ui::MouseEvent(ui::ET_MOUSE_PRESSED,
-                                      center,
-                                      center,
-                                      ui::EF_LEFT_MOUSE_BUTTON,
-                                      ui::EF_LEFT_MOUSE_BUTTON));
-  view->OnMouseReleased(ui::MouseEvent(ui::ET_MOUSE_RELEASED,
-                                       center,
-                                       center,
-                                       ui::EF_LEFT_MOUSE_BUTTON,
-                                       ui::EF_LEFT_MOUSE_BUTTON));
+  view->OnMousePressed(ui::MouseEvent(
+      ui::ET_MOUSE_PRESSED, center, center, ui::EventTimeForNow(),
+      ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON));
+  view->OnMouseReleased(ui::MouseEvent(
+      ui::ET_MOUSE_RELEASED, center, center, ui::EventTimeForNow(),
+      ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON));
 }
 
 // Choose a set that is 3 regular app list pages and 2 landscape app list pages.
diff --git a/ui/app_list/views/apps_container_view.cc b/ui/app_list/views/apps_container_view.cc
index a53cb609..6cc0fbca 100644
--- a/ui/app_list/views/apps_container_view.cc
+++ b/ui/app_list/views/apps_container_view.cc
@@ -69,6 +69,8 @@
   SetShowState(SHOW_ACTIVE_FOLDER, false);
 
   CreateViewsForFolderTopItemsAnimation(folder_item, true);
+
+  apps_grid_view_->ClearAnySelectedView();
 }
 
 void AppsContainerView::ShowApps(AppListFolderItem* folder_item) {
diff --git a/ui/app_list/views/apps_grid_view.cc b/ui/app_list/views/apps_grid_view.cc
index 465c73d..1c6f193c 100644
--- a/ui/app_list/views/apps_grid_view.cc
+++ b/ui/app_list/views/apps_grid_view.cc
@@ -944,7 +944,8 @@
         MoveSelected(0, forward_dir, 0);
         return true;
       case ui::VKEY_UP:
-        MoveSelected(0, 0, -1);
+        if (selected_view_)  // Don't initiate selection with UP
+          MoveSelected(0, 0, -1);
         return true;
       case ui::VKEY_DOWN:
         MoveSelected(0, 0, 1);
@@ -957,6 +958,15 @@
         MoveSelected(1, 0, 0);
         return true;
       }
+      case ui::VKEY_TAB: {
+        if (event.IsShiftDown()) {
+          ClearAnySelectedView();  // ContentsView will move focus back.
+        } else {
+          MoveSelected(0, 0, 0);  // Ensure but don't change selection.
+          handled = true;         // TABing internally doesn't move focus.
+        }
+        break;
+      }
       default:
         break;
     }
diff --git a/ui/app_list/views/apps_grid_view.h b/ui/app_list/views/apps_grid_view.h
index 1fff6f1..d8e2ede 100644
--- a/ui/app_list/views/apps_grid_view.h
+++ b/ui/app_list/views/apps_grid_view.h
@@ -97,6 +97,7 @@
   void ClearSelectedView(AppListItemView* view);
   void ClearAnySelectedView();
   bool IsSelectedView(const AppListItemView* view) const;
+  bool has_selected_view() const { return selected_view_ != nullptr; }
 
   void InitiateDrag(AppListItemView* view,
                     Pointer pointer,
@@ -124,7 +125,7 @@
   // Return true if the |bounds_animator_| is animating |view|.
   bool IsAnimatingView(AppListItemView* view);
 
-  bool has_dragged_view() const { return drag_view_ != NULL; }
+  bool has_dragged_view() const { return drag_view_ != nullptr; }
   bool dragging() const { return drag_pointer_ != NONE; }
 
   // Gets the PaginationModel used for the grid view.
@@ -202,7 +203,9 @@
   }
 
   // For test: Return if the drag and drop handler was set.
-  bool has_drag_and_drop_host_for_test() { return NULL != drag_and_drop_host_; }
+  bool has_drag_and_drop_host_for_test() {
+    return nullptr != drag_and_drop_host_;
+  }
 
   // For test: Return if the drag and drop operation gets dispatched.
   bool forward_events_to_drag_and_drop_host_for_test() {
diff --git a/ui/app_list/views/apps_grid_view_unittest.cc b/ui/app_list/views/apps_grid_view_unittest.cc
index 076f4f22..a5f9926 100644
--- a/ui/app_list/views/apps_grid_view_unittest.cc
+++ b/ui/app_list/views/apps_grid_view_unittest.cc
@@ -24,6 +24,7 @@
 #include "ui/app_list/views/app_list_item_view.h"
 #include "ui/app_list/views/apps_grid_view_folder_delegate.h"
 #include "ui/app_list/views/test/apps_grid_view_test_api.h"
+#include "ui/events/event_utils.h"
 #include "ui/views/test/views_test_base.h"
 
 namespace app_list {
@@ -154,12 +155,12 @@
     gfx::Point translated_to = gfx::PointAtOffsetFromOrigin(
         to - view->bounds().origin());
 
-    ui::MouseEvent pressed_event(ui::ET_MOUSE_PRESSED,
-                                 translated_from, from, 0, 0);
+    ui::MouseEvent pressed_event(ui::ET_MOUSE_PRESSED, translated_from, from,
+                                 ui::EventTimeForNow(), 0, 0);
     apps_grid_view_->InitiateDrag(view, pointer, pressed_event);
 
-    ui::MouseEvent drag_event(ui::ET_MOUSE_DRAGGED,
-                              translated_to, to, 0, 0);
+    ui::MouseEvent drag_event(ui::ET_MOUSE_DRAGGED, translated_to, to,
+                              ui::EventTimeForNow(), 0, 0);
     apps_grid_view_->UpdateDragFromItem(pointer, drag_event);
     return view;
   }
@@ -305,8 +306,8 @@
   EXPECT_FALSE(apps_grid_view_->has_dragged_view());
   // Even though cancelled, mouse move events can still arrive via the item
   // view. Ensure that behaves sanely, and doesn't start a new drag.
-  ui::MouseEvent drag_event(
-      ui::ET_MOUSE_DRAGGED, gfx::Point(1, 1), gfx::Point(2, 2), 0, 0);
+  ui::MouseEvent drag_event(ui::ET_MOUSE_DRAGGED, gfx::Point(1, 1),
+                            gfx::Point(2, 2), ui::EventTimeForNow(), 0, 0);
   apps_grid_view_->UpdateDragFromItem(AppsGridView::MOUSE, drag_event);
   EXPECT_FALSE(apps_grid_view_->has_dragged_view());
 
@@ -450,7 +451,8 @@
   to = GetItemTileRectAt(0, 1).CenterPoint();
   gfx::Point translated_to =
       gfx::PointAtOffsetFromOrigin(to - dragged_view->bounds().origin());
-  ui::MouseEvent drag_event(ui::ET_MOUSE_DRAGGED, translated_to, to, 0, 0);
+  ui::MouseEvent drag_event(ui::ET_MOUSE_DRAGGED, translated_to, to,
+                            ui::EventTimeForNow(), 0, 0);
   apps_grid_view_->UpdateDragFromItem(AppsGridView::MOUSE, drag_event);
   apps_grid_view_->EndDrag(false);
 
@@ -734,7 +736,7 @@
   EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(
       first_index_on_page2)));
 
-  // Moving onto a a page with too few apps to support the expected index snaps
+  // Moving onto a page with too few apps to support the expected index snaps
   // to the last available index.
   apps_grid_view_->SetSelectedView(GetItemViewAt(last_index_on_page2_last_row));
   SimulateKeyPress(ui::VKEY_RIGHT);
@@ -745,12 +747,10 @@
   EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(
       last_index)));
 
-
-
   // After page switch, arrow keys select first item on current page.
   apps_grid_view_->SetSelectedView(GetItemViewAt(first_index));
   GetPaginationModel()->SelectPage(1, false);
-  SimulateKeyPress(ui::VKEY_UP);
+  SimulateKeyPress(ui::VKEY_LEFT);
   EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(
       first_index_on_page2)));
 }
diff --git a/ui/app_list/views/contents_view.cc b/ui/app_list/views/contents_view.cc
index a076519..fe2a7fa 100644
--- a/ui/app_list/views/contents_view.cc
+++ b/ui/app_list/views/contents_view.cc
@@ -183,6 +183,9 @@
 void ContentsView::SetActivePageInternal(int page_index,
                                          bool show_search_results,
                                          bool animate) {
+  if (!GetPageView(page_index)->visible())
+    return;
+
   if (!show_search_results)
     page_before_search_ = page_index;
   // Start animating to the new page.
@@ -219,6 +222,11 @@
     app_list_main_view_->model()->ClearCustomLauncherPageSubpages();
   }
 
+  app_list_main_view_->search_box_view()->ResetTabFocus(false);
+  apps_container_view_->apps_grid_view()->ClearAnySelectedView();
+  apps_container_view_->app_list_folder_view()->items_grid_view()
+      ->ClearAnySelectedView();
+
   if (custom_page_view_) {
     custom_page_view_->SetFocusable(state ==
                                     AppListModel::STATE_CUSTOM_LAUNCHER_PAGE);
@@ -414,11 +422,6 @@
   return bounds;
 }
 
-bool ContentsView::ShouldShowCustomPageClickzone() const {
-  return custom_page_view_ && IsStateActive(AppListModel::STATE_START) &&
-         app_list_main_view_->model()->custom_launcher_page_enabled();
-}
-
 bool ContentsView::Back() {
   AppListModel::State state = view_to_state_[GetActivePageIndex()];
   switch (state) {
@@ -441,7 +444,7 @@
       GetSearchBoxView()->ClearSearch();
       ShowSearchResults(false);
       break;
-    case AppListModel::INVALID_STATE:
+    case AppListModel::INVALID_STATE:  // Falls through.
       NOTREACHED();
       break;
   }
@@ -511,7 +514,17 @@
 }
 
 bool ContentsView::OnKeyPressed(const ui::KeyEvent& event) {
-  return view_model_->view_at(GetActivePageIndex())->OnKeyPressed(event);
+  bool handled =
+      view_model_->view_at(GetActivePageIndex())->OnKeyPressed(event);
+
+  if (!handled) {
+    if (event.key_code() == ui::VKEY_TAB && event.IsShiftDown()) {
+      GetSearchBoxView()->MoveTabFocus(true);
+      handled = true;
+    }
+  }
+
+  return handled;
 }
 
 void ContentsView::TotalPagesChanged() {
@@ -528,7 +541,7 @@
     // Show or hide the custom page view, based on whether it is enabled.
     if (custom_page_view_) {
       custom_page_view_->SetVisible(
-          app_list_main_view_->model()->custom_launcher_page_enabled());
+          app_list_main_view_->ShouldShowCustomLauncherPage());
     }
   }
 }
diff --git a/ui/app_list/views/contents_view.h b/ui/app_list/views/contents_view.h
index 55e55f1..89d8d697 100644
--- a/ui/app_list/views/contents_view.h
+++ b/ui/app_list/views/contents_view.h
@@ -148,9 +148,6 @@
     return GetAnimatorForTransition(from_page, to_page, reverse);
   }
 
-  // Determines whether the custom page clickzone widget should be displayed.
-  bool ShouldShowCustomPageClickzone() const;
-
   // Performs the 'back' action for the active page. Returns whether the action
   // was handled.
   bool Back();
diff --git a/ui/app_list/views/folder_header_view.cc b/ui/app_list/views/folder_header_view.cc
index a6c6905..a123815 100644
--- a/ui/app_list/views/folder_header_view.cc
+++ b/ui/app_list/views/folder_header_view.cc
@@ -18,6 +18,7 @@
 #include "ui/views/border.h"
 #include "ui/views/controls/button/image_button.h"
 #include "ui/views/controls/textfield/textfield.h"
+#include "ui/views/focus/focus_manager.h"
 #include "ui/views/painter.h"
 
 namespace app_list {
@@ -116,6 +117,18 @@
   folder_item_ = NULL;
 }
 
+void FolderHeaderView::SetTextFocus() {
+  if (!folder_name_view_->HasFocus()) {
+    views::FocusManager* focus_manager = GetFocusManager();
+    if (focus_manager)
+      focus_manager->SetFocusedView(folder_name_view_);
+  }
+}
+
+bool FolderHeaderView::HasTextFocus() const {
+  return folder_name_view_->HasFocus();
+}
+
 void FolderHeaderView::Update() {
   if (!folder_item_)
     return;
diff --git a/ui/app_list/views/folder_header_view.h b/ui/app_list/views/folder_header_view.h
index 8129fef..81c998a 100644
--- a/ui/app_list/views/folder_header_view.h
+++ b/ui/app_list/views/folder_header_view.h
@@ -39,6 +39,8 @@
   void SetFolderItem(AppListFolderItem* folder_item);
   void UpdateFolderNameVisibility(bool visible);
   void OnFolderItemRemoved();
+  bool HasTextFocus() const;
+  void SetTextFocus();
 
   // Overridden from views::View:
   gfx::Size GetPreferredSize() const override;
diff --git a/ui/app_list/views/search_box_view.cc b/ui/app_list/views/search_box_view.cc
index a590fb9..14763823 100644
--- a/ui/app_list/views/search_box_view.cc
+++ b/ui/app_list/views/search_box_view.cc
@@ -13,12 +13,15 @@
 #include "ui/app_list/search_box_model.h"
 #include "ui/app_list/speech_ui_model.h"
 #include "ui/app_list/views/app_list_menu_views.h"
+#include "ui/app_list/views/contents_view.h"
 #include "ui/app_list/views/search_box_view_delegate.h"
+#include "ui/base/l10n/l10n_util.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/events/event.h"
 #include "ui/gfx/canvas.h"
 #include "ui/gfx/shadow_value.h"
 #include "ui/resources/grit/ui_resources.h"
+#include "ui/strings/grit/ui_strings.h"
 #include "ui/views/border.h"
 #include "ui/views/controls/button/image_button.h"
 #include "ui/views/controls/button/menu_button.h"
@@ -32,7 +35,8 @@
 
 namespace {
 
-const int kPadding = 14;
+const int kPadding = 16;
+const int kInnerPadding = 24;
 const int kPreferredWidth = 360;
 const int kPreferredHeight = 48;
 
@@ -66,6 +70,47 @@
 
 }  // namespace
 
+// To paint grey background on mic and back buttons
+class SearchBoxImageButton : public views::ImageButton {
+ public:
+  explicit SearchBoxImageButton(views::ButtonListener* listener)
+      : ImageButton(listener), selected_(false) {}
+  ~SearchBoxImageButton() override {}
+
+  bool selected() { return selected_; }
+  void SetSelected(bool selected) {
+    if (selected_ == selected)
+      return;
+
+    selected_ = selected;
+    SchedulePaint();
+    if (selected)
+      NotifyAccessibilityEvent(ui::AX_EVENT_FOCUS, true);
+  }
+
+  bool OnKeyPressed(const ui::KeyEvent& event) override {
+    // Disable space key to press the button. The keyboard events received
+    // by this view are forwarded from a Textfield (SearchBoxView) and key
+    // released events are not forwarded. This leaves the button in pressed
+    // state.
+    if (event.key_code() == ui::VKEY_SPACE)
+      return false;
+
+    return CustomButton::OnKeyPressed(event);
+  }
+
+ private:
+  // views::View overrides:
+  void OnPaintBackground(gfx::Canvas* canvas) override {
+    if (state_ == STATE_HOVERED || state_ == STATE_PRESSED || selected_)
+      canvas->FillRect(gfx::Rect(size()), kSelectedColor);
+  }
+
+  bool selected_;
+
+  DISALLOW_COPY_AND_ASSIGN(SearchBoxImageButton);
+};
+
 SearchBoxView::SearchBoxView(SearchBoxViewDelegate* delegate,
                              AppListViewDelegate* view_delegate)
     : delegate_(delegate),
@@ -77,19 +122,23 @@
       speech_button_(NULL),
       menu_button_(NULL),
       search_box_(new views::Textfield),
-      contents_view_(NULL) {
+      contents_view_(NULL),
+      focused_view_(FOCUS_SEARCH_BOX) {
   SetLayoutManager(new views::FillLayout);
   AddChildView(content_container_);
 
   if (switches::IsExperimentalAppListEnabled()) {
-    SetShadow(GetShadowForZHeight(1));
-    back_button_ = new views::ImageButton(this);
+    SetShadow(GetShadowForZHeight(2));
+    back_button_ = new SearchBoxImageButton(this);
     ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
     back_button_->SetImage(
         views::ImageButton::STATE_NORMAL,
         rb.GetImageSkiaNamed(IDR_APP_LIST_FOLDER_BACK_NORMAL));
     back_button_->SetImageAlignment(views::ImageButton::ALIGN_CENTER,
                                     views::ImageButton::ALIGN_MIDDLE);
+    base::string16 back_title(l10n_util::GetStringUTF16(IDS_APP_LIST_BACK));
+    back_button_->SetAccessibleName(back_title);
+    back_button_->SetTooltipText(back_title);
     content_container_->AddChildView(back_button_);
 
     content_container_->set_background(new ExperimentalSearchBoxBackground());
@@ -103,25 +152,22 @@
   }
 
   views::BoxLayout* layout =
-      new views::BoxLayout(views::BoxLayout::kHorizontal,
-                           kPadding,
-                           0,
-                           kPadding - views::Textfield::kTextPadding);
+      new views::BoxLayout(views::BoxLayout::kHorizontal, kPadding, 0,
+                           kInnerPadding - views::Textfield::kTextPadding);
   content_container_->SetLayoutManager(layout);
   layout->set_cross_axis_alignment(
       views::BoxLayout::CROSS_AXIS_ALIGNMENT_CENTER);
   layout->set_minimum_cross_axis_size(kPreferredHeight);
 
-  ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
-
   search_box_->SetBorder(views::Border::NullBorder());
-  search_box_->SetFontList(rb.GetFontList(ui::ResourceBundle::MediumFont));
+  search_box_->SetTextColor(kSearchTextColor);
   search_box_->set_placeholder_text_color(kHintTextColor);
   search_box_->set_controller(this);
   content_container_->AddChildView(search_box_);
   layout->SetFlexForView(search_box_, 1);
 
 #if !defined(OS_CHROMEOS)
+  ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
   menu_button_ = new views::MenuButton(NULL, base::string16(), this, false);
   menu_button_->SetBorder(views::Border::NullBorder());
   menu_button_->SetImage(views::Button::STATE_NORMAL,
@@ -183,6 +229,81 @@
   return view_bounds;
 }
 
+views::ImageButton* SearchBoxView::back_button() {
+  return static_cast<views::ImageButton*>(back_button_);
+}
+
+// Returns true if set internally, i.e. if focused_view_ != CONTENTS_VIEW.
+// Note: because we always want to be able to type in the edit box, this is only
+// a faux-focus so that buttons can respond to the ENTER key.
+bool SearchBoxView::MoveTabFocus(bool move_backwards) {
+  if (back_button_)
+    back_button_->SetSelected(false);
+  if (speech_button_)
+    speech_button_->SetSelected(false);
+
+  switch (focused_view_) {
+    case FOCUS_BACK_BUTTON:
+      focused_view_ = move_backwards ? FOCUS_BACK_BUTTON : FOCUS_SEARCH_BOX;
+      break;
+    case FOCUS_SEARCH_BOX:
+      if (move_backwards) {
+        focused_view_ = back_button_ && back_button_->visible()
+            ? FOCUS_BACK_BUTTON : FOCUS_SEARCH_BOX;
+      } else {
+        focused_view_ = speech_button_ && speech_button_->visible()
+            ? FOCUS_MIC_BUTTON : FOCUS_CONTENTS_VIEW;
+      }
+      break;
+    case FOCUS_MIC_BUTTON:
+      focused_view_ = move_backwards ? FOCUS_SEARCH_BOX : FOCUS_CONTENTS_VIEW;
+      break;
+    case FOCUS_CONTENTS_VIEW:
+      focused_view_ = move_backwards
+          ? (speech_button_ && speech_button_->visible() ?
+              FOCUS_MIC_BUTTON : FOCUS_SEARCH_BOX)
+          : FOCUS_CONTENTS_VIEW;
+      break;
+    default:
+      DCHECK(false);
+  }
+
+  switch (focused_view_) {
+    case FOCUS_BACK_BUTTON:
+      if (back_button_)
+        back_button_->SetSelected(true);
+      break;
+    case FOCUS_SEARCH_BOX:
+      // Set the ChromeVox focus to the search box. However, DO NOT do this if
+      // we are in the search results state (i.e., if the search box has text in
+      // it), because the focus is about to be shifted to the first search
+      // result and we do not want to read out the name of the search box as
+      // well.
+      if (search_box_->text().empty())
+        search_box_->NotifyAccessibilityEvent(ui::AX_EVENT_FOCUS, true);
+      break;
+    case FOCUS_MIC_BUTTON:
+      if (speech_button_)
+        speech_button_->SetSelected(true);
+      break;
+    default:
+      break;
+  }
+
+  if (focused_view_ < FOCUS_CONTENTS_VIEW)
+    delegate_->SetSearchResultSelection(focused_view_ == FOCUS_SEARCH_BOX);
+
+  return (focused_view_ < FOCUS_CONTENTS_VIEW);
+}
+
+void SearchBoxView::ResetTabFocus(bool on_contents) {
+  if (back_button_)
+    back_button_->SetSelected(false);
+  if (speech_button_)
+    speech_button_->SetSelected(false);
+  focused_view_ = on_contents ? FOCUS_CONTENTS_VIEW : FOCUS_SEARCH_BOX;
+}
+
 gfx::Size SearchBoxView::GetPreferredSize() const {
   return gfx::Size(kPreferredWidth, kPreferredHeight);
 }
@@ -225,9 +346,36 @@
 bool SearchBoxView::HandleKeyEvent(views::Textfield* sender,
                                    const ui::KeyEvent& key_event) {
   bool handled = false;
+  if (key_event.key_code() == ui::VKEY_TAB) {
+    if (focused_view_ != FOCUS_CONTENTS_VIEW &&
+        MoveTabFocus(key_event.IsShiftDown()))
+      return true;
+  }
+
+  if (focused_view_ == FOCUS_BACK_BUTTON && back_button_ &&
+      back_button_->OnKeyPressed(key_event))
+    return true;
+
+  if (focused_view_ == FOCUS_MIC_BUTTON && speech_button_ &&
+      speech_button_->OnKeyPressed(key_event))
+    return true;
+
   if (contents_view_ && contents_view_->visible())
     handled = contents_view_->OnKeyPressed(key_event);
 
+  // Arrow keys may have selected an item.  If they did, move focus off buttons.
+  // If they didn't, we still select the first search item, in case they're
+  // moving the caret through typed search text.  The UP arrow never moves
+  // focus from text/buttons to app list/results, so ignore it.
+  if (focused_view_ < FOCUS_CONTENTS_VIEW &&
+      (key_event.key_code() == ui::VKEY_LEFT ||
+       key_event.key_code() == ui::VKEY_RIGHT ||
+       key_event.key_code() == ui::VKEY_DOWN)) {
+    if (!handled)
+      delegate_->SetSearchResultSelection(true);
+    ResetTabFocus(handled);
+  }
+
   return handled;
 }
 
@@ -261,10 +409,11 @@
       model_->search_box()->speech_button();
   if (speech_button_prop) {
     if (!speech_button_) {
-      speech_button_ = new views::ImageButton(this);
+      speech_button_ = new SearchBoxImageButton(this);
       content_container_->AddChildView(speech_button_);
     }
 
+    speech_button_->SetAccessibleName(speech_button_prop->accessible_name);
     if (view_delegate_->GetSpeechUI()->state() ==
         SPEECH_RECOGNITION_HOTWORD_LISTENING) {
       speech_button_->SetImage(
@@ -286,7 +435,9 @@
 }
 
 void SearchBoxView::HintTextChanged() {
-  search_box_->set_placeholder_text(model_->search_box()->hint_text());
+  const app_list::SearchBoxModel* search_box = model_->search_box();
+  search_box_->set_placeholder_text(search_box->hint_text());
+  search_box_->SetAccessibleName(search_box->accessible_name());
 }
 
 void SearchBoxView::SelectionModelChanged() {
diff --git a/ui/app_list/views/search_box_view.h b/ui/app_list/views/search_box_view.h
index f32ec041..82fc162 100644
--- a/ui/app_list/views/search_box_view.h
+++ b/ui/app_list/views/search_box_view.h
@@ -23,11 +23,21 @@
 
 namespace app_list {
 
+// Possible locations for partial keyboard focus (but note that the search
+// box always handles typing).
+enum SearchBoxFocus {
+  FOCUS_BACK_BUTTON,    // Back button, only responds to ENTER
+  FOCUS_SEARCH_BOX,     // Nothing else has partial focus
+  FOCUS_MIC_BUTTON,     // Mic button, only responds to ENTER
+  FOCUS_CONTENTS_VIEW,  // Something outside the SearchBox is selected
+};
+
 class AppListMenuViews;
 class AppListModel;
 class AppListViewDelegate;
 class SearchBoxModel;
 class SearchBoxViewDelegate;
+class SearchBoxImageButton;
 
 // SearchBoxView consists of an icon and a Textfield. SearchBoxModel is its data
 // model that controls what icon to display, what placeholder text to use for
@@ -57,13 +67,19 @@
   gfx::Rect GetViewBoundsForSearchBoxContentsBounds(
       const gfx::Rect& rect) const;
 
-  views::ImageButton* back_button() { return back_button_; }
+  views::ImageButton* back_button();
   views::Textfield* search_box() { return search_box_; }
 
   void set_contents_view(views::View* contents_view) {
     contents_view_ = contents_view;
   }
 
+  // Moves focus forward/backwards in response to TAB.
+  bool MoveTabFocus(bool move_backwards);
+
+  // Moves focus to contents or SearchBox and unselects buttons.
+  void ResetTabFocus(bool on_contents);
+
   // Overridden from views::View:
   gfx::Size GetPreferredSize() const override;
   bool OnMouseWheel(const ui::MouseWheelEvent& event) override;
@@ -107,12 +123,14 @@
 
   views::View* content_container_;     // Owned by views hierarchy.
   views::ImageView* icon_view_;  // Owned by views hierarchy.
-  views::ImageButton* back_button_;    // Owned by views hierarchy.
-  views::ImageButton* speech_button_;  // Owned by views hierarchy.
+  SearchBoxImageButton* back_button_;    // Owned by views hierarchy.
+  SearchBoxImageButton* speech_button_;  // Owned by views hierarchy.
   views::MenuButton* menu_button_;  // Owned by views hierarchy.
   views::Textfield* search_box_;  // Owned by views hierarchy.
   views::View* contents_view_;  // Owned by views hierarchy.
 
+  SearchBoxFocus focused_view_;  // Which element has TAB'd focus.
+
   DISALLOW_COPY_AND_ASSIGN(SearchBoxView);
 };
 
diff --git a/ui/app_list/views/search_box_view_delegate.h b/ui/app_list/views/search_box_view_delegate.h
index 3de08683..e0e27c5 100644
--- a/ui/app_list/views/search_box_view_delegate.h
+++ b/ui/app_list/views/search_box_view_delegate.h
@@ -19,6 +19,9 @@
   // Invoked when the back button has been pressed.
   virtual void BackButtonPressed() = 0;
 
+  // Selects first item or clears selection from search list.
+  virtual void SetSearchResultSelection(bool select) = 0;
+
  protected:
   virtual ~SearchBoxViewDelegate() {}
 };
diff --git a/ui/app_list/views/search_box_view_unittest.cc b/ui/app_list/views/search_box_view_unittest.cc
index 843adfe5..48c769e1 100644
--- a/ui/app_list/views/search_box_view_unittest.cc
+++ b/ui/app_list/views/search_box_view_unittest.cc
@@ -114,6 +114,8 @@
 
   void BackButtonPressed() override {}
 
+  void SetSearchResultSelection(bool select) override {}
+
   AppListTestViewDelegate view_delegate_;
   views::Widget* widget_;
   SearchBoxView* view_;
diff --git a/ui/app_list/views/search_result_container_view.h b/ui/app_list/views/search_result_container_view.h
index 8390eb6c..c2fafe32 100644
--- a/ui/app_list/views/search_result_container_view.h
+++ b/ui/app_list/views/search_result_container_view.h
@@ -40,6 +40,9 @@
 
   int num_results() const { return num_results_; }
 
+  void set_container_score(double score) { container_score_ = score; }
+  double container_score() const { return container_score_; }
+
   // Schedules an Update call using |update_factory_|. Do nothing if there is a
   // pending call.
   void ScheduleUpdate();
@@ -52,8 +55,11 @@
 
   // Updates the container for being selected. |from_bottom| is true if the view
   // was entered into from a selected view below it; false if entered into from
-  // above.
-  virtual void OnContainerSelected(bool from_bottom) = 0;
+  // above. |directional_movement| is true if the navigation was caused by
+  // directional controls (eg, arrow keys), as opposed to linear controls (eg,
+  // Tab).
+  virtual void OnContainerSelected(bool from_bottom,
+                                   bool directional_movement) = 0;
 
  private:
   // Updates UI with model. Returns the number of visible results.
@@ -68,6 +74,8 @@
   int selected_index_;
   int num_results_;
 
+  double container_score_;
+
   AppListModel::SearchResults* results_;  // Owned by AppListModel.
 
   // The factory that consolidates multiple Update calls into one.
diff --git a/ui/app_list/views/search_result_list_view.cc b/ui/app_list/views/search_result_list_view.cc
index 18e630e..74be9f5 100644
--- a/ui/app_list/views/search_result_list_view.cc
+++ b/ui/app_list/views/search_result_list_view.cc
@@ -136,7 +136,8 @@
   SearchResultContainerView::ListItemsRemoved(start, count);
 }
 
-void SearchResultListView::OnContainerSelected(bool from_bottom) {
+void SearchResultListView::OnContainerSelected(bool from_bottom,
+                                               bool /*directional_movement*/) {
   if (num_results() == 0)
     return;
 
@@ -153,6 +154,7 @@
   for (size_t i = 0; i < static_cast<size_t>(results_container_->child_count());
        ++i) {
     SearchResultView* result_view = GetResultViewAt(i);
+    result_view->set_is_last_result(i == display_results.size() - 1);
     if (i < display_results.size()) {
       result_view->SetResult(display_results[i]);
       result_view->SetVisible(true);
@@ -163,6 +165,9 @@
   }
   UpdateAutoLaunchState();
 
+  set_container_score(
+      display_results.empty() ? 0 : display_results.front()->relevance());
+
   return display_results.size();
 }
 
diff --git a/ui/app_list/views/search_result_list_view.h b/ui/app_list/views/search_result_list_view.h
index 7acf75e..df02647a 100644
--- a/ui/app_list/views/search_result_list_view.h
+++ b/ui/app_list/views/search_result_list_view.h
@@ -53,7 +53,8 @@
   void ListItemsRemoved(size_t start, size_t count) override;
 
   // Overridden from SearchResultContainerView:
-  void OnContainerSelected(bool from_bottom) override;
+  void OnContainerSelected(bool from_bottom,
+                           bool directional_movement) override;
 
  private:
   friend class test::SearchResultListViewTest;
diff --git a/ui/app_list/views/search_result_list_view_unittest.cc b/ui/app_list/views/search_result_list_view_unittest.cc
index f5beab6..3d857ec 100644
--- a/ui/app_list/views/search_result_list_view_unittest.cc
+++ b/ui/app_list/views/search_result_list_view_unittest.cc
@@ -6,6 +6,7 @@
 
 #include <map>
 
+#include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
 #include "ui/app_list/app_list_model.h"
 #include "ui/app_list/test/app_list_test_view_delegate.h"
@@ -38,6 +39,10 @@
  protected:
   SearchResultListView* view() { return view_.get(); }
 
+  SearchResultView* GetResultViewAt(int index) {
+    return view_->GetResultViewAt(index);
+  }
+
   AppListModel::SearchResults* GetResults() {
     return view_delegate_.GetModel()->results();
   }
@@ -55,13 +60,16 @@
     AppListModel::SearchResults* results = GetResults();
     for (int i = 0; i < kDefaultSearchItems; ++i) {
       TestSearchResult* result = new TestSearchResult();
-      result->SetDisplayType(SearchResult::DISPLAY_LIST);
+      result->set_display_type(SearchResult::DISPLAY_LIST);
+      result->set_title(base::UTF8ToUTF16(base::StringPrintf("Result %d", i)));
+      if (i < 2)
+        result->set_details(base::ASCIIToUTF16("Detail"));
       results->Add(result);
     }
 
     // Adding results will schedule Update().
     RunPendingMessages();
-    view_->OnContainerSelected(false);
+    view_->OnContainerSelected(false, false);
   }
 
   int GetOpenResultCountAndReset(int ranking) {
@@ -103,12 +111,12 @@
 
     AppListModel::SearchResults* results = GetResults();
     for (size_t i = 0; i < results->item_count(); ++i) {
-      EXPECT_EQ(results->GetItemAt(i), view_->GetResultViewAt(i)->result());
+      EXPECT_EQ(results->GetItemAt(i), GetResultViewAt(i)->result());
     }
   }
 
   ProgressBarView* GetProgressBarAt(size_t index) {
-    return view()->GetResultViewAt(index)->progress_bar_;
+    return GetResultViewAt(index)->progress_bar_;
   }
 
  private:
@@ -194,6 +202,19 @@
   EXPECT_TRUE(IsAutoLaunching());
 }
 
+TEST_F(SearchResultListViewTest, SpokenFeedback) {
+  SetUpSearchResults();
+
+  // Result 0 has a detail text. Expect that the detail is appended to the
+  // accessibility name.
+  EXPECT_EQ(base::ASCIIToUTF16("Result 0, Detail"),
+            GetResultViewAt(0)->ComputeAccessibleName());
+
+  // Result 2 has no detail text.
+  EXPECT_EQ(base::ASCIIToUTF16("Result 2"),
+            GetResultViewAt(2)->ComputeAccessibleName());
+}
+
 TEST_F(SearchResultListViewTest, ModelObservers) {
   SetUpSearchResults();
   ExpectConsistent();
diff --git a/ui/app_list/views/search_result_page_view.cc b/ui/app_list/views/search_result_page_view.cc
index c575635c..04c9443 100644
--- a/ui/app_list/views/search_result_page_view.cc
+++ b/ui/app_list/views/search_result_page_view.cc
@@ -21,8 +21,8 @@
 
 namespace {
 
-const int kGroupSpacing = 20;
-const int kTopPadding = 5;
+const int kGroupSpacing = 6;
+const int kTopPadding = 8;
 
 // A container view that ensures the card background and the shadow are painted
 // in the correct order.
@@ -68,6 +68,13 @@
 SearchResultPageView::~SearchResultPageView() {
 }
 
+void SearchResultPageView::SetSelection(bool select) {
+  if (select)
+    SetSelectedIndex(0, false);
+  else
+    result_container_views_[selected_index_]->ClearSelectedIndex();
+}
+
 void SearchResultPageView::AddSearchResultContainerView(
     AppListModel::SearchResults* results_model,
     SearchResultContainerView* result_container) {
@@ -85,15 +92,18 @@
     return true;
 
   int dir = 0;
+  bool directional_movement = false;
   switch (event.key_code()) {
     case ui::VKEY_TAB:
       dir = event.IsShiftDown() ? -1 : 1;
       break;
     case ui::VKEY_UP:
       dir = -1;
+      directional_movement = true;
       break;
     case ui::VKEY_DOWN:
       dir = 1;
+      directional_movement = true;
       break;
     default:
       return false;
@@ -107,22 +117,23 @@
            result_container_views_[new_selected]->num_results() == 0);
 
   if (IsValidSelectionIndex(new_selected)) {
-    SetSelectedIndex(new_selected);
+    SetSelectedIndex(new_selected, directional_movement);
     return true;
   }
 
-  // Capture the Tab key to prevent defocusing of the search box.
-  return event.key_code() == ui::VKEY_TAB;
+  return false;
 }
 
-void SearchResultPageView::SetSelectedIndex(int index) {
+void SearchResultPageView::SetSelectedIndex(int index,
+                                            bool directional_movement) {
   bool from_bottom = index < selected_index_;
 
   // Reset the old selected view's selection.
   result_container_views_[selected_index_]->ClearSelectedIndex();
   selected_index_ = index;
   // Set the new selected view's selection to its first result.
-  result_container_views_[selected_index_]->OnContainerSelected(from_bottom);
+  result_container_views_[selected_index_]->OnContainerSelected(
+      from_bottom, directional_movement);
 }
 
 bool SearchResultPageView::IsValidSelectionIndex(int index) {
@@ -131,8 +142,23 @@
 
 void SearchResultPageView::ChildPreferredSizeChanged(views::View* child) {
   DCHECK(!result_container_views_.empty());
+
+  if (switches::IsExperimentalAppListEnabled()) {
+    // Sort the result container views by their score.
+    std::sort(result_container_views_.begin(), result_container_views_.end(),
+              [](const SearchResultContainerView* a,
+                 const SearchResultContainerView* b) -> bool {
+                return a->container_score() > b->container_score();
+              });
+
+    for (size_t i = 0; i < result_container_views_.size(); ++i) {
+      result_container_views_[i]->ClearSelectedIndex();
+      ReorderChildView(result_container_views_[i]->parent(), i);
+    }
+  }
+
   Layout();
-  SetSelectedIndex(0);
+  SetSelectedIndex(0, false);
 }
 
 }  // namespace app_list
diff --git a/ui/app_list/views/search_result_page_view.h b/ui/app_list/views/search_result_page_view.h
index 42d9054..d01c0a43 100644
--- a/ui/app_list/views/search_result_page_view.h
+++ b/ui/app_list/views/search_result_page_view.h
@@ -26,17 +26,24 @@
   ~SearchResultPageView() override;
 
   int selected_index() { return selected_index_; }
+  void SetSelection(bool select);  // Set or unset result selection.
 
   void AddSearchResultContainerView(
       AppListModel::SearchResults* result_model,
       SearchResultContainerView* result_container);
 
+  const std::vector<SearchResultContainerView*>& result_container_views() {
+    return result_container_views_;
+  }
+
   // Overridden from views::View:
   bool OnKeyPressed(const ui::KeyEvent& event) override;
   void ChildPreferredSizeChanged(views::View* child) override;
 
  private:
-  void SetSelectedIndex(int index);
+  // |directional_movement| is true if the navigation was caused by directional
+  // controls (eg, arrow keys), as opposed to linear controls (eg, Tab).
+  void SetSelectedIndex(int index, bool directional_movement);
   bool IsValidSelectionIndex(int index);
 
   // The SearchResultContainerViews that compose the search page. All owned by
diff --git a/ui/app_list/views/search_result_page_view_unittest.cc b/ui/app_list/views/search_result_page_view_unittest.cc
index c9087b7..2cce788 100644
--- a/ui/app_list/views/search_result_page_view_unittest.cc
+++ b/ui/app_list/views/search_result_page_view_unittest.cc
@@ -4,9 +4,12 @@
 
 #include "ui/app_list/views/search_result_page_view.h"
 
-#include <map>
+#include <utility>
+#include <vector>
 
+#include "base/command_line.h"
 #include "ui/app_list/app_list_model.h"
+#include "ui/app_list/app_list_switches.h"
 #include "ui/app_list/test/app_list_test_view_delegate.h"
 #include "ui/app_list/test/test_search_result.h"
 #include "ui/app_list/views/search_result_list_view.h"
@@ -22,7 +25,10 @@
 class SearchResultPageViewTest : public views::ViewsTestBase,
                                  public SearchResultListViewDelegate {
  public:
-  SearchResultPageViewTest() {}
+  SearchResultPageViewTest() {
+    base::CommandLine::ForCurrentProcess()->AppendSwitch(
+        switches::kEnableExperimentalAppList);
+  }
   ~SearchResultPageViewTest() override {}
 
   // Overridden from testing::Test:
@@ -46,13 +52,18 @@
     return view_delegate_.GetModel()->results();
   }
 
-  void SetUpSearchResults(
-      const std::map<SearchResult::DisplayType, int> result_types) {
+  void SetUpSearchResults(const std::vector<
+      std::pair<SearchResult::DisplayType, int>> result_types) {
     AppListModel::SearchResults* results = GetResults();
+    double relevance = result_types.size();
     for (const auto& data : result_types) {
+      // Set the relevance of the results in each group in decreasing order (so
+      // the earlier groups have higher relevance, and therefore appear first).
+      relevance -= 1.0;
       for (int i = 0; i < data.second; ++i) {
         TestSearchResult* result = new TestSearchResult();
-        result->SetDisplayType(data.first);
+        result->set_display_type(data.first);
+        result->set_relevance(relevance);
         results->Add(result);
       }
     }
@@ -63,8 +74,13 @@
 
   int GetSelectedIndex() { return view_->selected_index(); }
 
-  bool KeyPress(ui::KeyboardCode key_code) {
-    ui::KeyEvent event(ui::ET_KEY_PRESSED, key_code, ui::EF_NONE);
+  bool KeyPress(ui::KeyboardCode key_code) { return KeyPress(key_code, false); }
+
+  bool KeyPress(ui::KeyboardCode key_code, bool shift_down) {
+    int flags = ui::EF_NONE;
+    if (shift_down)
+      flags |= ui::EF_SHIFT_DOWN;
+    ui::KeyEvent event(ui::ET_KEY_PRESSED, key_code, flags);
     return view_->OnKeyPressed(event);
   }
 
@@ -81,47 +97,158 @@
   DISALLOW_COPY_AND_ASSIGN(SearchResultPageViewTest);
 };
 
-TEST_F(SearchResultPageViewTest, Basic) {
-  std::map<SearchResult::DisplayType, int> result_types;
+TEST_F(SearchResultPageViewTest, DirectionalMovement) {
+  std::vector<std::pair<SearchResult::DisplayType, int>> result_types;
+  // 3 tile results, followed by 2 list results.
+  const int kTileResults = 3;
   const int kListResults = 2;
-  const int kTileResults = 1;
   const int kNoneResults = 3;
-  result_types[SearchResult::DISPLAY_LIST] = kListResults;
-  result_types[SearchResult::DISPLAY_TILE] = kTileResults;
-  result_types[SearchResult::DISPLAY_NONE] = kNoneResults;
+  result_types.push_back(
+      std::make_pair(SearchResult::DISPLAY_TILE, kTileResults));
+  result_types.push_back(
+      std::make_pair(SearchResult::DISPLAY_LIST, kListResults));
+  result_types.push_back(
+      std::make_pair(SearchResult::DISPLAY_NONE, kNoneResults));
 
   SetUpSearchResults(result_types);
   EXPECT_EQ(0, GetSelectedIndex());
+  EXPECT_EQ(0, tile_list_view()->selected_index());
+
+  // Navigate to the second tile in the tile group.
+  EXPECT_TRUE(KeyPress(ui::VKEY_RIGHT));
+  EXPECT_EQ(0, GetSelectedIndex());
+  EXPECT_EQ(1, tile_list_view()->selected_index());
+  EXPECT_EQ(-1, list_view()->selected_index());
+
+  // Navigate to the list group.
+  EXPECT_TRUE(KeyPress(ui::VKEY_DOWN));
+  EXPECT_EQ(1, GetSelectedIndex());
+  EXPECT_EQ(-1, tile_list_view()->selected_index());
   EXPECT_EQ(0, list_view()->selected_index());
 
   // Navigate to the second result in the list view.
   EXPECT_TRUE(KeyPress(ui::VKEY_DOWN));
-  EXPECT_EQ(0, GetSelectedIndex());
+  EXPECT_EQ(1, GetSelectedIndex());
   EXPECT_EQ(1, list_view()->selected_index());
 
-  // Navigate to the tile group.
-  EXPECT_TRUE(KeyPress(ui::VKEY_DOWN));
-  EXPECT_EQ(1, GetSelectedIndex());
-  EXPECT_EQ(-1, list_view()->selected_index());
-  EXPECT_EQ(0, tile_list_view()->selected_index());
-
-  // Navigate off bottom of tile items.
+  // Attempt to navigate off bottom of list items.
   EXPECT_FALSE(KeyPress(ui::VKEY_DOWN));
   EXPECT_EQ(1, GetSelectedIndex());
-  EXPECT_EQ(0, tile_list_view()->selected_index());
+  EXPECT_EQ(1, list_view()->selected_index());
 
-  // Navigate back to the list group.
+  // Navigate back to the tile group (should select the first tile result).
+  EXPECT_TRUE(KeyPress(ui::VKEY_UP));
+  EXPECT_EQ(1, GetSelectedIndex());
+  EXPECT_EQ(0, list_view()->selected_index());
   EXPECT_TRUE(KeyPress(ui::VKEY_UP));
   EXPECT_EQ(0, GetSelectedIndex());
-  EXPECT_EQ(1, list_view()->selected_index());
-  EXPECT_EQ(-1, tile_list_view()->selected_index());
+  EXPECT_EQ(0, tile_list_view()->selected_index());
+  EXPECT_EQ(-1, list_view()->selected_index());
 
   // Navigate off top of list.
-  EXPECT_TRUE(KeyPress(ui::VKEY_UP));
-  EXPECT_EQ(0, list_view()->selected_index());
   EXPECT_FALSE(KeyPress(ui::VKEY_UP));
-  EXPECT_EQ(0, list_view()->selected_index());
   EXPECT_EQ(0, GetSelectedIndex());
+  EXPECT_EQ(0, tile_list_view()->selected_index());
+}
+
+TEST_F(SearchResultPageViewTest, TabMovement) {
+  std::vector<std::pair<SearchResult::DisplayType, int>> result_types;
+  // 3 tile results, followed by 2 list results.
+  const int kTileResults = 3;
+  const int kListResults = 2;
+  const int kNoneResults = 3;
+  result_types.push_back(
+      std::make_pair(SearchResult::DISPLAY_TILE, kTileResults));
+  result_types.push_back(
+      std::make_pair(SearchResult::DISPLAY_LIST, kListResults));
+  result_types.push_back(
+      std::make_pair(SearchResult::DISPLAY_NONE, kNoneResults));
+
+  SetUpSearchResults(result_types);
+  EXPECT_EQ(0, GetSelectedIndex());
+  EXPECT_EQ(0, tile_list_view()->selected_index());
+
+  // Navigate to the second tile in the tile group.
+  EXPECT_TRUE(KeyPress(ui::VKEY_TAB));
+  EXPECT_EQ(0, GetSelectedIndex());
+  EXPECT_EQ(1, tile_list_view()->selected_index());
+  EXPECT_EQ(-1, list_view()->selected_index());
+
+  // Navigate to the list group.
+  EXPECT_TRUE(KeyPress(ui::VKEY_TAB));
+  EXPECT_EQ(0, GetSelectedIndex());
+  EXPECT_EQ(2, tile_list_view()->selected_index());
+  EXPECT_TRUE(KeyPress(ui::VKEY_TAB));
+  EXPECT_EQ(1, GetSelectedIndex());
+  EXPECT_EQ(-1, tile_list_view()->selected_index());
+  EXPECT_EQ(0, list_view()->selected_index());
+
+  // Navigate to the second result in the list view.
+  EXPECT_TRUE(KeyPress(ui::VKEY_TAB));
+  EXPECT_EQ(1, GetSelectedIndex());
+  EXPECT_EQ(1, list_view()->selected_index());
+
+  // Attempt to navigate off bottom of list items.
+  EXPECT_FALSE(KeyPress(ui::VKEY_TAB));
+  EXPECT_EQ(1, GetSelectedIndex());
+  EXPECT_EQ(1, list_view()->selected_index());
+
+  // Navigate back to the tile group (should select the last tile result).
+  EXPECT_TRUE(KeyPress(ui::VKEY_TAB, true));
+  EXPECT_EQ(1, GetSelectedIndex());
+  EXPECT_EQ(0, list_view()->selected_index());
+  EXPECT_TRUE(KeyPress(ui::VKEY_TAB, true));
+  EXPECT_EQ(0, GetSelectedIndex());
+  EXPECT_EQ(2, tile_list_view()->selected_index());
+  EXPECT_EQ(-1, list_view()->selected_index());
+
+  // Navigate off top of list.
+  EXPECT_TRUE(KeyPress(ui::VKEY_TAB, true));
+  EXPECT_EQ(1, tile_list_view()->selected_index());
+  EXPECT_TRUE(KeyPress(ui::VKEY_TAB, true));
+  EXPECT_EQ(0, tile_list_view()->selected_index());
+  EXPECT_FALSE(KeyPress(ui::VKEY_TAB, true));
+  EXPECT_EQ(0, GetSelectedIndex());
+  EXPECT_EQ(0, tile_list_view()->selected_index());
+}
+
+TEST_F(SearchResultPageViewTest, ResultsSorted) {
+  AppListModel::SearchResults* results = GetResults();
+
+  // Add 3 results and expect the tile list view to be the first result
+  // container view.
+  TestSearchResult* tile_result = new TestSearchResult();
+  tile_result->set_display_type(SearchResult::DISPLAY_TILE);
+  tile_result->set_relevance(1.0);
+  results->Add(tile_result);
+  {
+    TestSearchResult* list_result = new TestSearchResult();
+    list_result->set_display_type(SearchResult::DISPLAY_LIST);
+    list_result->set_relevance(0.5);
+    results->Add(list_result);
+  }
+  {
+    TestSearchResult* list_result = new TestSearchResult();
+    list_result->set_display_type(SearchResult::DISPLAY_LIST);
+    list_result->set_relevance(0.3);
+    results->Add(list_result);
+  }
+
+  // Adding results will schedule Update().
+  RunPendingMessages();
+
+  EXPECT_EQ(tile_list_view(), view()->result_container_views()[0]);
+  EXPECT_EQ(list_view(), view()->result_container_views()[1]);
+
+  // Change the relevance of the tile result and expect the list results to be
+  // displayed first.
+  tile_result->set_relevance(0.4);
+
+  results->NotifyItemsChanged(0, 1);
+  RunPendingMessages();
+
+  EXPECT_EQ(list_view(), view()->result_container_views()[0]);
+  EXPECT_EQ(tile_list_view(), view()->result_container_views()[1]);
 }
 
 }  // namespace test
diff --git a/ui/app_list/views/search_result_tile_item_list_view.cc b/ui/app_list/views/search_result_tile_item_list_view.cc
index b6b680c..935eff89 100644
--- a/ui/app_list/views/search_result_tile_item_list_view.cc
+++ b/ui/app_list/views/search_result_tile_item_list_view.cc
@@ -4,11 +4,13 @@
 
 #include "ui/app_list/views/search_result_tile_item_list_view.h"
 
+#include "base/i18n/rtl.h"
 #include "ui/app_list/app_list_constants.h"
 #include "ui/app_list/app_list_view_delegate.h"
 #include "ui/app_list/search_result.h"
 #include "ui/app_list/views/search_result_tile_item_view.h"
 #include "ui/views/background.h"
+#include "ui/views/border.h"
 #include "ui/views/controls/textfield/textfield.h"
 #include "ui/views/layout/box_layout.h"
 
@@ -16,7 +18,8 @@
 
 // Layout constants.
 const size_t kNumSearchResultTiles = 5;
-const int kTileSpacing = 10;
+const int kTileSpacing = 7;
+const int kTopBottomPadding = 8;
 
 }  // namespace
 
@@ -30,6 +33,8 @@
   for (size_t i = 0; i < kNumSearchResultTiles; ++i) {
     SearchResultTileItemView* tile_item = new SearchResultTileItemView(this);
     tile_item->SetParentBackgroundColor(kCardBackgroundColor);
+    tile_item->SetBorder(views::Border::CreateEmptyBorder(
+        kTopBottomPadding, 0, kTopBottomPadding, 0));
     tile_views_.push_back(tile_item);
     AddChildView(tile_item);
   }
@@ -38,12 +43,17 @@
 SearchResultTileItemListView::~SearchResultTileItemListView() {
 }
 
-void SearchResultTileItemListView::OnContainerSelected(bool from_bottom) {
+void SearchResultTileItemListView::OnContainerSelected(
+    bool from_bottom,
+    bool directional_movement) {
   if (num_results() == 0)
     return;
 
-  // TODO(calamity): come in from the back when tab navigating backwards.
-  SetSelectedIndex(0);
+  // If the user came from below using linear controls (eg, Tab, as opposed to
+  // directional controls such as Up), select the right-most result. Otherwise,
+  // select the left-most result even if coming from below.
+  bool select_last = from_bottom && !directional_movement;
+  SetSelectedIndex(select_last ? num_results() - 1 : 0);
 }
 
 int SearchResultTileItemListView::Update() {
@@ -55,6 +65,10 @@
         i < display_results.size() ? display_results[i] : nullptr;
     tile_views_[i]->SetSearchResult(item);
   }
+
+  set_container_score(
+      display_results.empty() ? 0 : display_results.front()->relevance());
+
   return display_results.size();
 }
 
@@ -74,6 +88,7 @@
   int dir = 0;
   bool cursor_at_end_of_searchbox =
       search_box_->GetCursorPosition() == search_box_->text().length();
+  const int forward_dir = base::i18n::IsRTL() ? -1 : 1;
   switch (event.key_code()) {
     case ui::VKEY_TAB:
       if (event.IsShiftDown())
@@ -88,13 +103,13 @@
       // ignore the keypress if the user has clicked somewhere in the middle of
       // the searchbox.
       if (cursor_at_end_of_searchbox)
-        dir = -1;
+        dir = -forward_dir;
       break;
     case ui::VKEY_RIGHT:
       // Only move right if the search box text cursor is at the end of the
       // text.
       if (cursor_at_end_of_searchbox)
-        dir = 1;
+        dir = forward_dir;
       break;
     default:
       break;
diff --git a/ui/app_list/views/search_result_tile_item_list_view.h b/ui/app_list/views/search_result_tile_item_list_view.h
index 6d9d1800..d387c701 100644
--- a/ui/app_list/views/search_result_tile_item_list_view.h
+++ b/ui/app_list/views/search_result_tile_item_list_view.h
@@ -26,7 +26,8 @@
   ~SearchResultTileItemListView() override;
 
   // Overridden from SearchResultContainerView:
-  void OnContainerSelected(bool from_bottom) override;
+  void OnContainerSelected(bool from_bottom,
+                           bool directional_movement) override;
 
   // Overridden from views::View:
   bool OnKeyPressed(const ui::KeyEvent& event) override;
diff --git a/ui/app_list/views/search_result_view.cc b/ui/app_list/views/search_result_view.cc
index 1a73c14..3bfd656 100644
--- a/ui/app_list/views/search_result_view.cc
+++ b/ui/app_list/views/search_result_view.cc
@@ -6,6 +6,7 @@
 
 #include <algorithm>
 
+#include "base/strings/utf_string_conversions.h"
 #include "ui/app_list/app_list_constants.h"
 #include "ui/app_list/app_list_switches.h"
 #include "ui/app_list/search_result.h"
@@ -25,16 +26,19 @@
 namespace {
 
 const int kPreferredWidth = 300;
-const int kPreferredHeight = 52;
-const int kIconPadding = 14;
-const int kTextTrailPadding = kIconPadding;
+const int kPreferredHeight = 56;
+const int kIconLeftPadding = 16;
+const int kIconRightPadding = 24;
+const int kTextTrailPadding = 16;
+const int kSeparatorPadding = 62;
 const int kBorderSize = 1;
+const SkColor kSeparatorColor = SkColorSetRGB(0xE1, 0xE1, 0xE1);
 
 // Extra margin at the right of the rightmost action icon.
 const int kActionButtonRightMargin = 8;
 
 int GetIconViewWidth() {
-  return kListIconSize + 2 * kIconPadding;
+  return kListIconSize + kIconLeftPadding + kIconRightPadding;
 }
 
 // Creates a RenderText of given |text| and |styles|. Caller takes ownership
@@ -71,6 +75,7 @@
 SearchResultView::SearchResultView(SearchResultListView* list_view)
     : views::CustomButton(this),
       result_(NULL),
+      is_last_result_(false),
       list_view_(list_view),
       icon_(new views::ImageView),
       actions_view_(new SearchResultActionsView(this)),
@@ -116,12 +121,12 @@
 void SearchResultView::UpdateTitleText() {
   if (!result_ || result_->title().empty()) {
     title_text_.reset();
-    SetAccessibleName(base::string16());
   } else {
     title_text_.reset(CreateRenderText(result_->title(),
                                        result_->title_tags()));
-    SetAccessibleName(result_->title());
   }
+
+  UpdateAccessibleName();
 }
 
 void SearchResultView::UpdateDetailsText() {
@@ -131,6 +136,24 @@
     details_text_.reset(CreateRenderText(result_->details(),
                                          result_->details_tags()));
   }
+
+  UpdateAccessibleName();
+}
+
+base::string16 SearchResultView::ComputeAccessibleName() const {
+  if (!result_)
+    return base::string16();
+
+  base::string16 accessible_name = result_->title();
+  if (!result_->title().empty() && !result_->details().empty())
+    accessible_name += base::ASCIIToUTF16(", ");
+  accessible_name += result_->details();
+
+  return accessible_name;
+}
+
+void SearchResultView::UpdateAccessibleName() {
+  SetAccessibleName(ComputeAccessibleName());
 }
 
 const char* SearchResultView::GetClassName() const {
@@ -148,7 +171,9 @@
 
   gfx::Rect icon_bounds(rect);
   icon_bounds.set_width(GetIconViewWidth());
-  icon_bounds.Inset(kIconPadding, (rect.height() - kListIconSize) / 2);
+  const int top_bottom_padding = (rect.height() - kListIconSize) / 2;
+  icon_bounds.Inset(kIconLeftPadding, top_bottom_padding, kIconRightPadding,
+                    top_bottom_padding);
   icon_bounds.Intersect(rect);
   icon_->SetBoundsRect(icon_bounds);
 
@@ -215,12 +240,25 @@
 
   const bool selected = list_view_->IsResultViewSelected(this);
   const bool hover = state() == STATE_HOVERED || state() == STATE_PRESSED;
+
+  canvas->FillRect(content_rect, switches::IsExperimentalAppListEnabled()
+                                     ? kCardBackgroundColor
+                                     : kContentsBackgroundColor);
+
+  // Possibly call FillRect a second time (these colours are partially
+  // transparent, so the previous FillRect is not redundant).
   if (selected)
     canvas->FillRect(content_rect, kSelectedColor);
   else if (hover)
     canvas->FillRect(content_rect, kHighlightedColor);
-  else if (!switches::IsExperimentalAppListEnabled())
-    canvas->FillRect(content_rect, kContentsBackgroundColor);
+
+  if (switches::IsExperimentalAppListEnabled() && !is_last_result_) {
+    gfx::Rect line_rect = content_rect;
+    line_rect.set_height(kBorderSize);
+    line_rect.set_y(content_rect.bottom() - kBorderSize);
+    line_rect.set_x(kSeparatorPadding);
+    canvas->FillRect(line_rect, kSeparatorColor);
+  }
 
   gfx::Rect border_bottom = gfx::SubtractRects(rect, content_rect);
   canvas->FillRect(border_bottom, kResultBorderColor);
diff --git a/ui/app_list/views/search_result_view.h b/ui/app_list/views/search_result_view.h
index f84f7af..279a2a3e 100644
--- a/ui/app_list/views/search_result_view.h
+++ b/ui/app_list/views/search_result_view.h
@@ -9,7 +9,9 @@
 #include <vector>
 
 #include "base/basictypes.h"
+#include "base/compiler_specific.h"
 #include "base/memory/scoped_ptr.h"
+#include "base/strings/string16.h"
 #include "ui/app_list/search_result_observer.h"
 #include "ui/app_list/views/search_result_actions_view_delegate.h"
 #include "ui/views/context_menu_controller.h"
@@ -37,11 +39,12 @@
 class SearchResultActionsView;
 
 // SearchResultView displays a SearchResult.
-class SearchResultView : public views::CustomButton,
-                         public views::ButtonListener,
-                         public views::ContextMenuController,
-                         public SearchResultObserver,
-                         public SearchResultActionsViewDelegate {
+class APP_LIST_EXPORT SearchResultView
+    : public views::CustomButton,
+      public views::ButtonListener,
+      public views::ContextMenuController,
+      public SearchResultObserver,
+      NON_EXPORTED_BASE(public SearchResultActionsViewDelegate) {
  public:
   // Internal class name.
   static const char kViewClassName[];
@@ -59,11 +62,17 @@
   // Clears the selected action.
   void ClearSelectedAction();
 
+  // Computes the button's spoken feedback name.
+  base::string16 ComputeAccessibleName() const;
+
+  void set_is_last_result(bool is_last) { is_last_result_ = is_last; }
+
  private:
   friend class app_list::test::SearchResultListViewTest;
 
   void UpdateTitleText();
   void UpdateDetailsText();
+  void UpdateAccessibleName();
 
   // views::View overrides:
   const char* GetClassName() const override;
@@ -93,6 +102,8 @@
 
   SearchResult* result_;  // Owned by AppListModel::SearchResults.
 
+  bool is_last_result_;
+
   // Parent list view. Owned by views hierarchy.
   SearchResultListView* list_view_;
 
diff --git a/ui/app_list/views/speech_view_unittest.cc b/ui/app_list/views/speech_view_unittest.cc
index 319eca6..de190fb 100644
--- a/ui/app_list/views/speech_view_unittest.cc
+++ b/ui/app_list/views/speech_view_unittest.cc
@@ -5,6 +5,7 @@
 #include "ui/app_list/views/speech_view.h"
 
 #include "ui/app_list/test/app_list_test_view_delegate.h"
+#include "ui/events/event_utils.h"
 #include "ui/views/controls/button/image_button.h"
 #include "ui/views/test/widget_test.h"
 
@@ -56,16 +57,12 @@
   gfx::Rect screen_bounds(view()->mic_button()->GetBoundsInScreen());
 
   // Simulate a mouse click in the center of the MicButton.
-  ui::MouseEvent press(ui::ET_MOUSE_PRESSED,
-                       screen_bounds.CenterPoint(),
-                       screen_bounds.CenterPoint(),
-                       ui::EF_LEFT_MOUSE_BUTTON,
-                       0);
-  ui::MouseEvent release(ui::ET_MOUSE_RELEASED,
-                         screen_bounds.CenterPoint(),
-                         screen_bounds.CenterPoint(),
-                         ui::EF_LEFT_MOUSE_BUTTON,
-                         0);
+  ui::MouseEvent press(ui::ET_MOUSE_PRESSED, screen_bounds.CenterPoint(),
+                       screen_bounds.CenterPoint(), ui::EventTimeForNow(),
+                       ui::EF_LEFT_MOUSE_BUTTON, 0);
+  ui::MouseEvent release(ui::ET_MOUSE_RELEASED, screen_bounds.CenterPoint(),
+                         screen_bounds.CenterPoint(), ui::EventTimeForNow(),
+                         ui::EF_LEFT_MOUSE_BUTTON, 0);
   widget()->OnMouseEvent(&press);
   widget()->OnMouseEvent(&release);
   EXPECT_EQ(1, GetToggleSpeechRecognitionCountAndReset());
@@ -75,16 +72,10 @@
   // circular hit-test mask).
   gfx::Point bottom_right(screen_bounds.right() - 1,
                           screen_bounds.bottom() - 2);
-  press = ui::MouseEvent(ui::ET_MOUSE_PRESSED,
-                         bottom_right,
-                         bottom_right,
-                         ui::EF_LEFT_MOUSE_BUTTON,
-                         0);
-  release = ui::MouseEvent(ui::ET_MOUSE_RELEASED,
-                           bottom_right,
-                           bottom_right,
-                           ui::EF_LEFT_MOUSE_BUTTON,
-                           0);
+  press = ui::MouseEvent(ui::ET_MOUSE_PRESSED, bottom_right, bottom_right,
+                         ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON, 0);
+  release = ui::MouseEvent(ui::ET_MOUSE_RELEASED, bottom_right, bottom_right,
+                           ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON, 0);
   widget()->OnMouseEvent(&press);
   widget()->OnMouseEvent(&release);
   EXPECT_EQ(0, GetToggleSpeechRecognitionCountAndReset());
diff --git a/ui/app_list/views/start_page_view.cc b/ui/app_list/views/start_page_view.cc
index 2dcc30e0..c546b2d 100644
--- a/ui/app_list/views/start_page_view.cc
+++ b/ui/app_list/views/start_page_view.cc
@@ -4,7 +4,10 @@
 
 #include "ui/app_list/views/start_page_view.h"
 
+#include "base/i18n/rtl.h"
+#include "base/metrics/histogram_macros.h"
 #include "base/strings/utf_string_conversions.h"
+#include "ui/accessibility/ax_view_state.h"
 #include "ui/app_list/app_list_constants.h"
 #include "ui/app_list/app_list_item.h"
 #include "ui/app_list/app_list_model.h"
@@ -42,6 +45,8 @@
 const size_t kNumStartPageTiles = 4;
 const int kTileSpacing = 7;
 
+const int kLauncherPageBackgroundWidth = 400;
+
 // An invisible placeholder view which fills the space for the search box view
 // in a box layout. The search box view itself is a child of the AppListView
 // (because it is visible on many different pages).
@@ -63,6 +68,38 @@
 
 }  // namespace
 
+class CustomLauncherPageBackgroundView : public views::View {
+ public:
+  explicit CustomLauncherPageBackgroundView(
+      const std::string& custom_launcher_page_name)
+      : selected_(false),
+        custom_launcher_page_name_(custom_launcher_page_name) {
+    set_background(views::Background::CreateSolidBackground(kSelectedColor));
+  }
+  ~CustomLauncherPageBackgroundView() override {}
+
+  void SetSelected(bool selected) {
+    selected_ = selected;
+    SetVisible(selected);
+    if (selected)
+      NotifyAccessibilityEvent(ui::AX_EVENT_FOCUS, true);
+  }
+
+  bool selected() { return selected_; }
+
+  // Overridden from views::View:
+  void GetAccessibleState(ui::AXViewState* state) override {
+    state->role = ui::AX_ROLE_BUTTON;
+    state->name = base::UTF8ToUTF16(custom_launcher_page_name_);
+  }
+
+ private:
+  bool selected_;
+  std::string custom_launcher_page_name_;
+
+  DISALLOW_COPY_AND_ASSIGN(CustomLauncherPageBackgroundView);
+};
+
 // A container that holds the start page recommendation tiles and the all apps
 // tile.
 class StartPageView::StartPageTilesContainer
@@ -72,7 +109,7 @@
                           AllAppsTileItemView* all_apps_button);
   ~StartPageTilesContainer() override;
 
-  TileItemView* GetTileItemView(size_t index);
+  TileItemView* GetTileItemView(int index);
 
   const std::vector<SearchResultTileItemView*>& tile_views() const {
     return search_result_tile_views_;
@@ -83,7 +120,8 @@
   // Overridden from SearchResultContainerView:
   int Update() override;
   void UpdateSelectedIndex(int old_selected, int new_selected) override;
-  void OnContainerSelected(bool from_bottom) override;
+  void OnContainerSelected(bool from_bottom,
+                           bool directional_movement) override;
 
  private:
   ContentsView* contents_view_;
@@ -124,9 +162,9 @@
 }
 
 TileItemView* StartPageView::StartPageTilesContainer::GetTileItemView(
-    size_t index) {
-  DCHECK_GT(kNumStartPageTiles + 1, index);
-  if (index == kNumStartPageTiles)
+    int index) {
+  DCHECK_GT(num_results(), index);
+  if (index == num_results() - 1)
     return all_apps_button_;
 
   return search_result_tile_views_[index];
@@ -172,7 +210,8 @@
 }
 
 void StartPageView::StartPageTilesContainer::OnContainerSelected(
-    bool from_bottom) {
+    bool /*from_bottom*/,
+    bool /*directional_movement*/) {
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -184,6 +223,8 @@
       search_box_spacer_view_(new SearchBoxSpacerView(
           app_list_main_view->search_box_view()->GetPreferredSize())),
       instant_container_(new views::View),
+      custom_launcher_page_background_(new CustomLauncherPageBackgroundView(
+          view_delegate_->GetModel()->custom_launcher_page_name())),
       tiles_container_(new StartPageTilesContainer(
           app_list_main_view->contents_view(),
           new AllAppsTileItemView(
@@ -196,6 +237,8 @@
   // The view containing the start page tiles.
   AddChildView(tiles_container_);
 
+  AddChildView(custom_launcher_page_background_);
+
   tiles_container_->SetResults(view_delegate_->GetModel()->results());
   Reset();
 }
@@ -227,9 +270,13 @@
 void StartPageView::MaybeOpenCustomLauncherPage() {
   // Switch to the custom page.
   ContentsView* contents_view = app_list_main_view_->contents_view();
-  if (!contents_view->ShouldShowCustomPageClickzone())
+  if (!app_list_main_view_->ShouldShowCustomLauncherPage())
     return;
 
+  UMA_HISTOGRAM_ENUMERATION(kPageOpenedHistogram,
+                            AppListModel::STATE_CUSTOM_LAUNCHER_PAGE,
+                            AppListModel::STATE_LAST);
+
   int custom_page_index = contents_view->GetPageIndexForState(
       AppListModel::STATE_CUSTOM_LAUNCHER_PAGE);
   contents_view->SetActivePage(custom_page_index);
@@ -255,6 +302,7 @@
 void StartPageView::OnShow() {
   tiles_container_->Update();
   tiles_container_->ClearSelectedIndex();
+  custom_launcher_page_background_->SetSelected(false);
 }
 
 void StartPageView::Layout() {
@@ -266,26 +314,56 @@
   bounds.set_y(bounds.bottom());
   bounds.set_height(tiles_container_->GetHeightForWidth(bounds.width()));
   tiles_container_->SetBoundsRect(bounds);
+
+  bounds = app_list_main_view_->contents_view()->GetCustomPageCollapsedBounds();
+  bounds.Intersect(GetContentsBounds());
+  bounds.ClampToCenteredSize(
+      gfx::Size(kLauncherPageBackgroundWidth, bounds.height()));
+  custom_launcher_page_background_->SetBoundsRect(bounds);
 }
 
 bool StartPageView::OnKeyPressed(const ui::KeyEvent& event) {
+  const int forward_dir = base::i18n::IsRTL() ? -1 : 1;
   int selected_index = tiles_container_->selected_index();
-  if (selected_index >= 0 &&
-      tiles_container_->child_at(selected_index)->OnKeyPressed(event))
+
+  if (custom_launcher_page_background_->selected()) {
+    selected_index = tiles_container_->num_results();
+    switch (event.key_code()) {
+      case ui::VKEY_RETURN:
+        MaybeOpenCustomLauncherPage();
+        return true;
+      default:
+        break;
+    }
+  } else if (selected_index >= 0 &&
+             tiles_container_->GetTileItemView(selected_index)
+                 ->OnKeyPressed(event)) {
     return true;
+  }
 
   int dir = 0;
   switch (event.key_code()) {
     case ui::VKEY_LEFT:
-      dir = -1;
+      dir = -forward_dir;
       break;
     case ui::VKEY_RIGHT:
-      dir = 1;
+      // Don't go to the custom launcher page from the All apps tile.
+      if (selected_index != tiles_container_->num_results() - 1)
+        dir = forward_dir;
+      break;
+    case ui::VKEY_UP:
+      // Up selects the first tile if the custom launcher is selected.
+      if (custom_launcher_page_background_->selected()) {
+        selected_index = -1;
+        dir = 1;
+      }
       break;
     case ui::VKEY_DOWN:
       // Down selects the first tile if nothing is selected.
-      if (!tiles_container_->IsValidSelectionIndex(selected_index))
-        dir = 1;
+      dir = 1;
+      // If something is selected, select the custom launcher page.
+      if (tiles_container_->IsValidSelectionIndex(selected_index))
+        selected_index = tiles_container_->num_results() - 1;
       break;
     case ui::VKEY_TAB:
       dir = event.IsShiftDown() ? -1 : 1;
@@ -297,7 +375,8 @@
   if (dir == 0)
     return false;
 
-  if (!tiles_container_->IsValidSelectionIndex(selected_index)) {
+  if (selected_index == -1) {
+    custom_launcher_page_background_->SetSelected(false);
     tiles_container_->SetSelectedIndex(
         dir == -1 ? tiles_container_->num_results() - 1 : 0);
     return true;
@@ -305,10 +384,21 @@
 
   int selection_index = selected_index + dir;
   if (tiles_container_->IsValidSelectionIndex(selection_index)) {
+    custom_launcher_page_background_->SetSelected(false);
     tiles_container_->SetSelectedIndex(selection_index);
     return true;
   }
 
+  if (selection_index == tiles_container_->num_results() &&
+      app_list_main_view_->ShouldShowCustomLauncherPage()) {
+    custom_launcher_page_background_->SetSelected(true);
+    tiles_container_->ClearSelectedIndex();
+    return true;
+  }
+
+  if (event.key_code() == ui::VKEY_TAB && selection_index == -1)
+    tiles_container_->ClearSelectedIndex();  // ContentsView will handle focus.
+
   return false;
 }
 
@@ -322,7 +412,8 @@
 }
 
 bool StartPageView::OnMouseWheel(const ui::MouseWheelEvent& event) {
-  if (event.y_offset() > 0) {
+  // Negative y_offset is a downward scroll.
+  if (event.y_offset() < 0) {
     MaybeOpenCustomLauncherPage();
     return true;
   }
@@ -342,7 +433,9 @@
 }
 
 void StartPageView::OnScrollEvent(ui::ScrollEvent* event) {
-  if (event->type() == ui::ET_SCROLL && event->y_offset() > 0)
+  // Negative y_offset is a downward scroll (or upward, if Australian Scrolling
+  // is enabled).
+  if (event->type() == ui::ET_SCROLL && event->y_offset() < 0)
     MaybeOpenCustomLauncherPage();
 }
 
diff --git a/ui/app_list/views/start_page_view.h b/ui/app_list/views/start_page_view.h
index 8a6f0ba..fa97e80 100644
--- a/ui/app_list/views/start_page_view.h
+++ b/ui/app_list/views/start_page_view.h
@@ -16,6 +16,7 @@
 class AllAppsTileItemView;
 class AppListMainView;
 class AppListViewDelegate;
+class CustomLauncherPageBackgroundView;
 class SearchResultTileItemView;
 class TileItemView;
 
@@ -30,6 +31,7 @@
 
   void UpdateForTesting();
 
+  views::View* instant_container() const { return instant_container_; }
   const std::vector<SearchResultTileItemView*>& tile_views() const;
   TileItemView* all_apps_button() const;
 
@@ -54,6 +56,8 @@
   void InitInstantContainer();
   void MaybeOpenCustomLauncherPage();
 
+  void SetCustomLauncherPageSelected(bool selected);
+
   TileItemView* GetTileItemView(size_t index);
 
   // The parent view of ContentsView which is the parent of this view.
@@ -63,6 +67,8 @@
 
   views::View* search_box_spacer_view_;  // Owned by views hierarchy.
   views::View* instant_container_;  // Owned by views hierarchy.
+  CustomLauncherPageBackgroundView*
+      custom_launcher_page_background_;       // Owned by view hierarchy.
   StartPageTilesContainer* tiles_container_;  // Owned by views hierarchy.
 
   DISALLOW_COPY_AND_ASSIGN(StartPageView);
diff --git a/ui/app_list/views/tile_item_view.cc b/ui/app_list/views/tile_item_view.cc
index fcb10d2..af04fd6b 100644
--- a/ui/app_list/views/tile_item_view.cc
+++ b/ui/app_list/views/tile_item_view.cc
@@ -56,7 +56,9 @@
 
   selected_ = selected;
   UpdateBackgroundColor();
-  NotifyAccessibilityEvent(ui::AX_EVENT_FOCUS, true);
+
+  if (selected)
+    NotifyAccessibilityEvent(ui::AX_EVENT_FOCUS, true);
 }
 
 void TileItemView::SetParentBackgroundColor(SkColor color) {
diff --git a/ui/aura/BUILD.gn b/ui/aura/BUILD.gn
index e032d67..9a330fb 100644
--- a/ui/aura/BUILD.gn
+++ b/ui/aura/BUILD.gn
@@ -176,6 +176,9 @@
     "test/window_test_api.h",
   ]
 
+  # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+  configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
+
   public_deps = [
     ":aura",
   ]
@@ -194,8 +197,6 @@
 
   if (is_win) {
     sources += [ "test/ui_controls_factory_aurawin.cc" ]
-
-    cflags = [ "/wd4267" ]  # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
   }
 
   if (use_x11) {
diff --git a/ui/aura/gestures/gesture_recognizer_unittest.cc b/ui/aura/gestures/gesture_recognizer_unittest.cc
index 951aa44..01a8f57 100644
--- a/ui/aura/gestures/gesture_recognizer_unittest.cc
+++ b/ui/aura/gestures/gesture_recognizer_unittest.cc
@@ -337,15 +337,9 @@
   explicit QueueTouchEventDelegate(WindowEventDispatcher* dispatcher)
       : window_(NULL),
         dispatcher_(dispatcher),
-        queue_events_(true),
-        synchronous_ack_for_next_event_(AckState::PENDING) {
-  }
-  ~QueueTouchEventDelegate() override {
-    while(!queue_.empty()) {
-      delete queue_.front();
-      queue_.pop();
-    }
-  }
+        synchronous_ack_for_next_event_(AckState::PENDING) {}
+
+  ~QueueTouchEventDelegate() override {}
 
   void OnTouchEvent(ui::TouchEvent* event) override {
     event->DisableSynchronousHandling();
@@ -358,8 +352,6 @@
           window_);
       synchronous_ack_for_next_event_ = AckState::PENDING;
     }
-    if (queue_events_)
-      queue_.push(new ui::TouchEvent(*event, window_, window_));
   }
 
   void ReceivedAck() {
@@ -371,7 +363,6 @@
   }
 
   void set_window(Window* w) { window_ = w; }
-  void set_queue_events(bool queue) { queue_events_ = queue; }
   void set_synchronous_ack_for_next_event(bool consumed) {
     DCHECK(synchronous_ack_for_next_event_ == AckState::PENDING);
     synchronous_ack_for_next_event_ =
@@ -386,16 +377,12 @@
   };
 
   void ReceivedAckImpl(bool prevent_defaulted) {
-    scoped_ptr<ui::TouchEvent> event(queue_.front());
-    dispatcher_->ProcessedTouchEvent(event.get(), window_,
-        prevent_defaulted ? ui::ER_HANDLED : ui::ER_UNHANDLED);
-    queue_.pop();
+    dispatcher_->ProcessedTouchEvent(
+        window_, prevent_defaulted ? ui::ER_HANDLED : ui::ER_UNHANDLED);
   }
 
-  std::queue<ui::TouchEvent*> queue_;
   Window* window_;
   WindowEventDispatcher* dispatcher_;
-  bool queue_events_;
   AckState synchronous_ack_for_next_event_;
 
   DISALLOW_COPY_AND_ASSIGN(QueueTouchEventDelegate);
diff --git a/ui/aura/remote_window_tree_host_win.cc b/ui/aura/remote_window_tree_host_win.cc
index 3b34357..c44234c 100644
--- a/ui/aura/remote_window_tree_host_win.cc
+++ b/ui/aura/remote_window_tree_host_win.cc
@@ -311,16 +311,17 @@
     return;
 
   gfx::Point location = PointFromNativeEvent(x, y);
-  ui::MouseEvent event(ui::ET_MOUSE_MOVED, location, location, flags, 0);
+  ui::MouseEvent event(ui::ET_MOUSE_MOVED, location, location,
+                       ui::EventTimeForNow(), flags, 0);
   SendEventToProcessor(&event);
 }
 
 void RemoteWindowTreeHostWin::OnMouseButton(
     const MetroViewerHostMsg_MouseButtonParams& params) {
   gfx::Point location = PointFromNativeEvent(params.x, params.y);
-  ui::MouseEvent mouse_event(params.event_type, location, location,
-                             static_cast<int>(params.flags),
-                             static_cast<int>(params.changed_button));
+  ui::MouseEvent mouse_event(
+      params.event_type, location, location, ui::EventTimeForNow(),
+      static_cast<int>(params.flags), static_cast<int>(params.changed_button));
 
   SetEventFlags(params.flags | key_event_flags());
   if (params.event_type == ui::ET_MOUSEWHEEL) {
diff --git a/ui/aura/test/aura_test_utils.cc b/ui/aura/test/aura_test_utils.cc
index fe4120b..ce40df85 100644
--- a/ui/aura/test/aura_test_utils.cc
+++ b/ui/aura/test/aura_test_utils.cc
@@ -4,6 +4,7 @@
 
 #include "ui/aura/test/aura_test_utils.h"
 
+#include "ui/aura/window_event_dispatcher.h"
 #include "ui/aura/window_tree_host.h"
 
 namespace aura {
@@ -17,6 +18,10 @@
     return host_->last_cursor_request_position_in_host_;
   }
 
+  void set_dispatcher(scoped_ptr<WindowEventDispatcher> dispatcher) {
+    host_->dispatcher_ = dispatcher.Pass();
+  }
+
  private:
   WindowTreeHost* host_;
 
@@ -28,5 +33,11 @@
   return host_test_api.last_cursor_request_position_in_host();
 }
 
+void SetHostDispatcher(WindowTreeHost* host,
+                       scoped_ptr<WindowEventDispatcher> dispatcher) {
+  WindowTreeHostTestApi host_test_api(host);
+  host_test_api.set_dispatcher(dispatcher.Pass());
+}
+
 }  // namespace test
 }  // namespace aura
diff --git a/ui/aura/test/aura_test_utils.h b/ui/aura/test/aura_test_utils.h
index ec94b3c3..d33eb4e 100644
--- a/ui/aura/test/aura_test_utils.h
+++ b/ui/aura/test/aura_test_utils.h
@@ -6,17 +6,21 @@
 #define UI_AURA_TEST_AURA_TEST_UTILS_H_
 
 #include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
 
 namespace gfx {
 class Point;
 }
 
 namespace aura {
+class WindowEventDispatcher;
 class WindowTreeHost;
 
 namespace test {
 
 const gfx::Point& QueryLatestMousePositionRequestInHost(WindowTreeHost* host);
+void SetHostDispatcher(WindowTreeHost* host,
+                       scoped_ptr<WindowEventDispatcher> dispatcher);
 
 }  // namespace test
 }  // namespace aura
diff --git a/ui/aura/test/ui_controls_factory_ozone.cc b/ui/aura/test/ui_controls_factory_ozone.cc
index 2a256624..a928275 100644
--- a/ui/aura/test/ui_controls_factory_ozone.cc
+++ b/ui/aura/test/ui_controls_factory_ozone.cc
@@ -10,6 +10,7 @@
 #include "ui/aura/test/ui_controls_factory_aura.h"
 #include "ui/aura/window_tree_host.h"
 #include "ui/base/test/ui_controls_aura.h"
+#include "ui/events/event_utils.h"
 #include "ui/events/test/events_test_utils.h"
 
 namespace aura {
@@ -217,7 +218,8 @@
                           const gfx::PointF& host_location,
                           int flags,
                           int changed_button_flags) {
-    ui::MouseEvent mouse_event(type, host_location, host_location, flags,
+    ui::MouseEvent mouse_event(type, host_location, host_location,
+                               ui::EventTimeForNow(), flags,
                                changed_button_flags);
 
     // This hack is necessary to set the repeat count for clicks.
diff --git a/ui/aura/window_event_dispatcher.cc b/ui/aura/window_event_dispatcher.cc
index d0401ef..a21236a 100644
--- a/ui/aura/window_event_dispatcher.cc
+++ b/ui/aura/window_event_dispatcher.cc
@@ -22,6 +22,7 @@
 #include "ui/base/hit_test.h"
 #include "ui/compositor/dip_util.h"
 #include "ui/events/event.h"
+#include "ui/events/event_utils.h"
 #include "ui/events/gestures/gesture_recognizer.h"
 #include "ui/events/gestures/gesture_types.h"
 
@@ -152,19 +153,15 @@
 DispatchDetails WindowEventDispatcher::DispatchMouseExitAtPoint(
     Window* window,
     const gfx::Point& point) {
-  ui::MouseEvent event(ui::ET_MOUSE_EXITED, point, point, ui::EF_NONE,
-                       ui::EF_NONE);
+  ui::MouseEvent event(ui::ET_MOUSE_EXITED, point, point, ui::EventTimeForNow(),
+                       ui::EF_NONE, ui::EF_NONE);
   return DispatchMouseEnterOrExit(window, event, ui::ET_MOUSE_EXITED);
 }
 
-void WindowEventDispatcher::ProcessedTouchEvent(ui::TouchEvent* event,
-                                                Window* window,
+void WindowEventDispatcher::ProcessedTouchEvent(Window* window,
                                                 ui::EventResult result) {
-  // Once we've fully migrated to the eager gesture detector, we won't need to
-  // pass an event here.
   scoped_ptr<ui::GestureRecognizer::Gestures> gestures(
-      ui::GestureRecognizer::Get()->AckAsyncTouchEvent(
-          *event, result, window));
+      ui::GestureRecognizer::Get()->AckAsyncTouchEvent(result, window));
   DispatchDetails details = ProcessGestures(gestures.get());
   if (details.dispatcher_destroyed)
     return;
@@ -370,7 +367,7 @@
       old_capture->delegate()) {
     // Send a capture changed event with bogus location data.
     ui::MouseEvent event(ui::ET_MOUSE_CAPTURE_CHANGED, gfx::Point(),
-                         gfx::Point(), 0, 0);
+                         gfx::Point(), ui::EventTimeForNow(), 0, 0);
 
     DispatchDetails details = DispatchEvent(old_capture, &event);
     if (details.dispatcher_destroyed)
@@ -726,11 +723,9 @@
     return details;
   gfx::Point host_mouse_location = root_mouse_location;
   host_->ConvertPointToHost(&host_mouse_location);
-  ui::MouseEvent event(ui::ET_MOUSE_MOVED,
-                       host_mouse_location,
-                       host_mouse_location,
-                       ui::EF_IS_SYNTHESIZED,
-                       0);
+  ui::MouseEvent event(ui::ET_MOUSE_MOVED, host_mouse_location,
+                       host_mouse_location, ui::EventTimeForNow(),
+                       ui::EF_IS_SYNTHESIZED, 0);
   return OnEventFromSource(&event);
 }
 
diff --git a/ui/aura/window_event_dispatcher.h b/ui/aura/window_event_dispatcher.h
index 3136e83c..5bcccaf 100644
--- a/ui/aura/window_event_dispatcher.h
+++ b/ui/aura/window_event_dispatcher.h
@@ -92,9 +92,7 @@
   // event processing, so that gesture events can be properly created and
   // dispatched. |event|'s location should be in the dispatcher's coordinate
   // space, in DIPs.
-  void ProcessedTouchEvent(ui::TouchEvent* event,
-                           Window* window,
-                           ui::EventResult result);
+  virtual void ProcessedTouchEvent(Window* window, ui::EventResult result);
 
   // These methods are used to defer the processing of mouse/touch events
   // related to resize. A client (typically a RenderWidgetHostViewAura) can call
diff --git a/ui/aura/window_event_dispatcher_unittest.cc b/ui/aura/window_event_dispatcher_unittest.cc
index cfc08c2c..17c1fc8 100644
--- a/ui/aura/window_event_dispatcher_unittest.cc
+++ b/ui/aura/window_event_dispatcher_unittest.cc
@@ -117,9 +117,9 @@
 
   // Send a mouse event to window1.
   gfx::Point point(101, 201);
-  ui::MouseEvent event1(
-      ui::ET_MOUSE_PRESSED, point, point, ui::EF_LEFT_MOUSE_BUTTON,
-      ui::EF_LEFT_MOUSE_BUTTON);
+  ui::MouseEvent event1(ui::ET_MOUSE_PRESSED, point, point,
+                        ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
+                        ui::EF_LEFT_MOUSE_BUTTON);
   DispatchEventUsingWindowDispatcher(&event1);
 
   // Event was tested for non-client area for the target window.
@@ -140,9 +140,9 @@
   // Test RepostEvent in RootWindow. It only works for Mouse Press.
   EXPECT_FALSE(Env::GetInstance()->IsMouseButtonDown());
   gfx::Point point(10, 10);
-  ui::MouseEvent event(
-      ui::ET_MOUSE_PRESSED, point, point, ui::EF_LEFT_MOUSE_BUTTON,
-      ui::EF_LEFT_MOUSE_BUTTON);
+  ui::MouseEvent event(ui::ET_MOUSE_PRESSED, point, point,
+                       ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
+                       ui::EF_LEFT_MOUSE_BUTTON);
   host()->dispatcher()->RepostEvent(event);
   RunAllPendingInMessageLoop();
   EXPECT_TRUE(Env::GetInstance()->IsMouseButtonDown());
@@ -158,19 +158,14 @@
 
   // Press the left button.
   event.reset(new ui::MouseEvent(
-      ui::ET_MOUSE_PRESSED,
-      location,
-      location,
-      ui::EF_LEFT_MOUSE_BUTTON,
-      ui::EF_LEFT_MOUSE_BUTTON));
+      ui::ET_MOUSE_PRESSED, location, location, ui::EventTimeForNow(),
+      ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON));
   DispatchEventUsingWindowDispatcher(event.get());
   EXPECT_TRUE(Env::GetInstance()->IsMouseButtonDown());
 
   // Additionally press the right.
   event.reset(new ui::MouseEvent(
-      ui::ET_MOUSE_PRESSED,
-      location,
-      location,
+      ui::ET_MOUSE_PRESSED, location, location, ui::EventTimeForNow(),
       ui::EF_LEFT_MOUSE_BUTTON | ui::EF_RIGHT_MOUSE_BUTTON,
       ui::EF_RIGHT_MOUSE_BUTTON));
   DispatchEventUsingWindowDispatcher(event.get());
@@ -178,31 +173,22 @@
 
   // Release the left button.
   event.reset(new ui::MouseEvent(
-      ui::ET_MOUSE_RELEASED,
-      location,
-      location,
-      ui::EF_RIGHT_MOUSE_BUTTON,
-      ui::EF_LEFT_MOUSE_BUTTON));
+      ui::ET_MOUSE_RELEASED, location, location, ui::EventTimeForNow(),
+      ui::EF_RIGHT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON));
   DispatchEventUsingWindowDispatcher(event.get());
   EXPECT_TRUE(Env::GetInstance()->IsMouseButtonDown());
 
   // Release the right button.  We should ignore the Shift-is-down flag.
-  event.reset(new ui::MouseEvent(
-      ui::ET_MOUSE_RELEASED,
-      location,
-      location,
-      ui::EF_SHIFT_DOWN,
-      ui::EF_RIGHT_MOUSE_BUTTON));
+  event.reset(new ui::MouseEvent(ui::ET_MOUSE_RELEASED, location, location,
+                                 ui::EventTimeForNow(), ui::EF_SHIFT_DOWN,
+                                 ui::EF_RIGHT_MOUSE_BUTTON));
   DispatchEventUsingWindowDispatcher(event.get());
   EXPECT_FALSE(Env::GetInstance()->IsMouseButtonDown());
 
   // Press the middle button.
   event.reset(new ui::MouseEvent(
-      ui::ET_MOUSE_PRESSED,
-      location,
-      location,
-      ui::EF_MIDDLE_MOUSE_BUTTON,
-      ui::EF_MIDDLE_MOUSE_BUTTON));
+      ui::ET_MOUSE_PRESSED, location, location, ui::EventTimeForNow(),
+      ui::EF_MIDDLE_MOUSE_BUTTON, ui::EF_MIDDLE_MOUSE_BUTTON));
   DispatchEventUsingWindowDispatcher(event.get());
   EXPECT_TRUE(Env::GetInstance()->IsMouseButtonDown());
 }
@@ -212,7 +198,8 @@
       gfx::Rect(50, 50, 100, 100), root_window()));
 
   gfx::Point origin(100, 100);
-  ui::MouseEvent root(ui::ET_MOUSE_PRESSED, origin, origin, 0, 0);
+  ui::MouseEvent root(ui::ET_MOUSE_PRESSED, origin, origin,
+                      ui::EventTimeForNow(), 0, 0);
 
   EXPECT_EQ("100,100", root.location().ToString());
   EXPECT_EQ("100,100", root.root_location().ToString());
@@ -638,7 +625,7 @@
   window->AddPreTargetHandler(&recorder);
   window->SetCapture();
   const ui::MouseEvent press_event(
-      ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(),
+      ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(), ui::EventTimeForNow(),
       ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
   host()->dispatcher()->RepostEvent(press_event);
   RunAllPendingInMessageLoop();  // Necessitated by RepostEvent().
@@ -656,7 +643,8 @@
       &delegate, 1, gfx::Rect(0, 0, 100, 100), root_window()));
 
   ui::MouseEvent mouse_move_event(ui::ET_MOUSE_MOVED, gfx::Point(0, 0),
-                                  gfx::Point(0, 0), 0, 0);
+                                  gfx::Point(0, 0), ui::EventTimeForNow(), 0,
+                                  0);
   DispatchEventUsingWindowDispatcher(&mouse_move_event);
   // Discard MOUSE_ENTER.
   recorder.Reset();
@@ -665,14 +653,16 @@
 
   // Check that we don't immediately dispatch the MOUSE_DRAGGED event.
   ui::MouseEvent mouse_dragged_event(ui::ET_MOUSE_DRAGGED, gfx::Point(0, 0),
-                                     gfx::Point(0, 0), 0, 0);
+                                     gfx::Point(0, 0), ui::EventTimeForNow(), 0,
+                                     0);
   DispatchEventUsingWindowDispatcher(&mouse_dragged_event);
   EXPECT_TRUE(recorder.events().empty());
 
   // Check that we do dispatch the held MOUSE_DRAGGED event before another type
   // of event.
   ui::MouseEvent mouse_pressed_event(ui::ET_MOUSE_PRESSED, gfx::Point(0, 0),
-                                     gfx::Point(0, 0), 0, 0);
+                                     gfx::Point(0, 0), ui::EventTimeForNow(), 0,
+                                     0);
   DispatchEventUsingWindowDispatcher(&mouse_pressed_event);
   EXPECT_EQ("MOUSE_DRAGGED MOUSE_PRESSED",
             EventTypesToString(recorder.events()));
@@ -681,15 +671,18 @@
   // Check that we coalesce held MOUSE_DRAGGED events. Note that here (and
   // elsewhere in this test) we re-define each event prior to dispatch so that
   // it has the correct state (phase, handled, target, etc.).
-  mouse_dragged_event = ui::MouseEvent(
-      ui::ET_MOUSE_DRAGGED, gfx::Point(0, 0), gfx::Point(0, 0), 0, 0);
+  mouse_dragged_event =
+      ui::MouseEvent(ui::ET_MOUSE_DRAGGED, gfx::Point(0, 0), gfx::Point(0, 0),
+                     ui::EventTimeForNow(), 0, 0);
   ui::MouseEvent mouse_dragged_event2(ui::ET_MOUSE_DRAGGED, gfx::Point(10, 10),
-                                      gfx::Point(10, 10), 0, 0);
+                                      gfx::Point(10, 10), ui::EventTimeForNow(),
+                                      0, 0);
   DispatchEventUsingWindowDispatcher(&mouse_dragged_event);
   DispatchEventUsingWindowDispatcher(&mouse_dragged_event2);
   EXPECT_TRUE(recorder.events().empty());
-  mouse_pressed_event = ui::MouseEvent(
-      ui::ET_MOUSE_PRESSED, gfx::Point(0, 0), gfx::Point(0, 0), 0, 0);
+  mouse_pressed_event =
+      ui::MouseEvent(ui::ET_MOUSE_PRESSED, gfx::Point(0, 0), gfx::Point(0, 0),
+                     ui::EventTimeForNow(), 0, 0);
   DispatchEventUsingWindowDispatcher(&mouse_pressed_event);
   EXPECT_EQ("MOUSE_DRAGGED MOUSE_PRESSED",
             EventTypesToString(recorder.events()));
@@ -697,8 +690,9 @@
 
   // Check that on ReleasePointerMoves, held events are not dispatched
   // immediately, but posted instead.
-  mouse_dragged_event = ui::MouseEvent(
-      ui::ET_MOUSE_DRAGGED, gfx::Point(0, 0), gfx::Point(0, 0), 0, 0);
+  mouse_dragged_event =
+      ui::MouseEvent(ui::ET_MOUSE_DRAGGED, gfx::Point(0, 0), gfx::Point(0, 0),
+                     ui::EventTimeForNow(), 0, 0);
   DispatchEventUsingWindowDispatcher(&mouse_dragged_event);
   host()->dispatcher()->ReleasePointerMoves();
   EXPECT_TRUE(recorder.events().empty());
@@ -709,12 +703,14 @@
   // However if another message comes in before the dispatch of the posted
   // event, check that the posted event is dispatched before this new event.
   host()->dispatcher()->HoldPointerMoves();
-  mouse_dragged_event = ui::MouseEvent(
-      ui::ET_MOUSE_DRAGGED, gfx::Point(0, 0), gfx::Point(0, 0), 0, 0);
+  mouse_dragged_event =
+      ui::MouseEvent(ui::ET_MOUSE_DRAGGED, gfx::Point(0, 0), gfx::Point(0, 0),
+                     ui::EventTimeForNow(), 0, 0);
   DispatchEventUsingWindowDispatcher(&mouse_dragged_event);
   host()->dispatcher()->ReleasePointerMoves();
-  mouse_pressed_event = ui::MouseEvent(
-      ui::ET_MOUSE_PRESSED, gfx::Point(0, 0), gfx::Point(0, 0), 0, 0);
+  mouse_pressed_event =
+      ui::MouseEvent(ui::ET_MOUSE_PRESSED, gfx::Point(0, 0), gfx::Point(0, 0),
+                     ui::EventTimeForNow(), 0, 0);
   DispatchEventUsingWindowDispatcher(&mouse_pressed_event);
   EXPECT_EQ("MOUSE_DRAGGED MOUSE_PRESSED",
             EventTypesToString(recorder.events()));
@@ -725,12 +721,14 @@
   // Check that if the other message is another MOUSE_DRAGGED, we still coalesce
   // them.
   host()->dispatcher()->HoldPointerMoves();
-  mouse_dragged_event = ui::MouseEvent(
-      ui::ET_MOUSE_DRAGGED, gfx::Point(0, 0), gfx::Point(0, 0), 0, 0);
+  mouse_dragged_event =
+      ui::MouseEvent(ui::ET_MOUSE_DRAGGED, gfx::Point(0, 0), gfx::Point(0, 0),
+                     ui::EventTimeForNow(), 0, 0);
   DispatchEventUsingWindowDispatcher(&mouse_dragged_event);
   host()->dispatcher()->ReleasePointerMoves();
-  mouse_dragged_event2 = ui::MouseEvent(
-      ui::ET_MOUSE_DRAGGED, gfx::Point(10, 10), gfx::Point(10, 10), 0, 0);
+  mouse_dragged_event2 =
+      ui::MouseEvent(ui::ET_MOUSE_DRAGGED, gfx::Point(10, 10),
+                     gfx::Point(10, 10), ui::EventTimeForNow(), 0, 0);
   DispatchEventUsingWindowDispatcher(&mouse_dragged_event2);
   EXPECT_EQ("MOUSE_DRAGGED", EventTypesToString(recorder.events()));
   recorder.Reset();
@@ -739,12 +737,15 @@
 
   // Check that synthetic mouse move event has a right location when issued
   // while holding pointer moves.
-  mouse_dragged_event = ui::MouseEvent(
-      ui::ET_MOUSE_DRAGGED, gfx::Point(0, 0), gfx::Point(0, 0), 0, 0);
-  mouse_dragged_event2 = ui::MouseEvent(
-      ui::ET_MOUSE_DRAGGED, gfx::Point(10, 10), gfx::Point(10, 10), 0, 0);
+  mouse_dragged_event =
+      ui::MouseEvent(ui::ET_MOUSE_DRAGGED, gfx::Point(0, 0), gfx::Point(0, 0),
+                     ui::EventTimeForNow(), 0, 0);
+  mouse_dragged_event2 =
+      ui::MouseEvent(ui::ET_MOUSE_DRAGGED, gfx::Point(10, 10),
+                     gfx::Point(10, 10), ui::EventTimeForNow(), 0, 0);
   ui::MouseEvent mouse_dragged_event3(ui::ET_MOUSE_DRAGGED, gfx::Point(28, 28),
-                                      gfx::Point(28, 28), 0, 0);
+                                      gfx::Point(28, 28), ui::EventTimeForNow(),
+                                      0, 0);
   host()->dispatcher()->HoldPointerMoves();
   DispatchEventUsingWindowDispatcher(&mouse_dragged_event);
   DispatchEventUsingWindowDispatcher(&mouse_dragged_event2);
@@ -834,8 +835,8 @@
   window_second->AddPreTargetHandler(&recorder_second);
 
   const gfx::Point event_location(22, 33);
-  ui::MouseEvent mouse(ui::ET_MOUSE_MOVED, event_location, event_location, 0,
-                       0);
+  ui::MouseEvent mouse(ui::ET_MOUSE_MOVED, event_location, event_location,
+                       ui::EventTimeForNow(), 0, 0);
   DispatchEventUsingWindowDispatcher(&mouse);
 
   EXPECT_TRUE(recorder_first.events().empty());
@@ -846,17 +847,13 @@
             recorder_second.mouse_locations()[0].ToString());
 }
 
-// Verifies that a direct call to ProcessedTouchEvent() with a
-// TOUCH_PRESSED event does not cause a crash.
+// Verifies that a direct call to ProcessedTouchEvent() does not cause a crash.
 TEST_F(WindowEventDispatcherTest, CallToProcessedTouchEvent) {
   test::TestWindowDelegate delegate;
   scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate(
       &delegate, 1, gfx::Rect(50, 50, 100, 100), root_window()));
 
-  ui::TouchEvent touch(
-      ui::ET_TOUCH_PRESSED, gfx::Point(10, 10), 1, ui::EventTimeForNow());
-  host()->dispatcher()->ProcessedTouchEvent(
-      &touch, window.get(), ui::ER_UNHANDLED);
+  host()->dispatcher()->ProcessedTouchEvent(window.get(), ui::ER_UNHANDLED);
 }
 
 // This event handler requests the dispatcher to start holding pointer-move
@@ -973,14 +970,15 @@
 
   // Dispatch a non-synthetic mouse event when mouse events are enabled.
   ui::MouseEvent mouse1(ui::ET_MOUSE_MOVED, gfx::Point(10, 10),
-                        gfx::Point(10, 10), 0, 0);
+                        gfx::Point(10, 10), ui::EventTimeForNow(), 0, 0);
   DispatchEventUsingWindowDispatcher(&mouse1);
   EXPECT_FALSE(recorder.events().empty());
   recorder.Reset();
 
   // Dispatch a synthetic mouse event when mouse events are enabled.
   ui::MouseEvent mouse2(ui::ET_MOUSE_MOVED, gfx::Point(10, 10),
-                        gfx::Point(10, 10), ui::EF_IS_SYNTHESIZED, 0);
+                        gfx::Point(10, 10), ui::EventTimeForNow(),
+                        ui::EF_IS_SYNTHESIZED, 0);
   DispatchEventUsingWindowDispatcher(&mouse2);
   EXPECT_FALSE(recorder.events().empty());
   recorder.Reset();
@@ -1003,8 +1001,8 @@
   window->AddPreTargetHandler(&recorder);
   // Dispatch a non-synthetic mouse event when mouse events are enabled.
   ui::MouseEvent mouse1(ui::ET_MOUSE_PRESSED, gfx::Point(10, 10),
-                        gfx::Point(10, 10), ui::EF_LEFT_MOUSE_BUTTON,
-                        ui::EF_LEFT_MOUSE_BUTTON);
+                        gfx::Point(10, 10), ui::EventTimeForNow(),
+                        ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
   DispatchEventUsingWindowDispatcher(&mouse1);
   ASSERT_EQ(1u, recorder.events().size());
   EXPECT_EQ(ui::ET_MOUSE_PRESSED, recorder.events()[0]);
@@ -1043,7 +1041,7 @@
 
   // Dispatch a non-synthetic mouse event to place cursor inside window bounds.
   ui::MouseEvent mouse(ui::ET_MOUSE_MOVED, gfx::Point(10, 10),
-                       gfx::Point(10, 10), 0, 0);
+                       gfx::Point(10, 10), ui::EventTimeForNow(), 0, 0);
   DispatchEventUsingWindowDispatcher(&mouse);
   EXPECT_FALSE(recorder.events().empty());
   recorder.Reset();
@@ -1097,8 +1095,8 @@
 
   // Dispatch a mouse move event into the window.
   gfx::Point mouse_location(gfx::Point(15, 25));
-  ui::MouseEvent mouse1(ui::ET_MOUSE_MOVED, mouse_location,
-                        mouse_location, 0, 0);
+  ui::MouseEvent mouse1(ui::ET_MOUSE_MOVED, mouse_location, mouse_location,
+                        ui::EventTimeForNow(), 0, 0);
   EXPECT_TRUE(recorder.events().empty());
   DispatchEventUsingWindowDispatcher(&mouse1);
   EXPECT_FALSE(recorder.events().empty());
@@ -1134,8 +1132,8 @@
 
   // Dispatch a mouse move event into the window.
   gfx::Point mouse_location(gfx::Point(15, 25));
-  ui::MouseEvent mouse1(ui::ET_MOUSE_MOVED, mouse_location,
-                        mouse_location, 0, 0);
+  ui::MouseEvent mouse1(ui::ET_MOUSE_MOVED, mouse_location, mouse_location,
+                        ui::EventTimeForNow(), 0, 0);
   EXPECT_TRUE(recorder.events().empty());
   DispatchEventUsingWindowDispatcher(&mouse1);
   EXPECT_FALSE(recorder.events().empty());
@@ -1146,7 +1144,8 @@
 
   gfx::Point mouse_exit_location(gfx::Point(150, 150));
   ui::MouseEvent mouse2(ui::ET_MOUSE_EXITED, gfx::Point(150, 150),
-                        gfx::Point(150, 150), ui::EF_IS_SYNTHESIZED, 0);
+                        gfx::Point(150, 150), ui::EventTimeForNow(),
+                        ui::EF_IS_SYNTHESIZED, 0);
   DispatchEventUsingWindowDispatcher(&mouse2);
 
   EXPECT_FALSE(recorder.events().empty());
@@ -1629,8 +1628,8 @@
   void OnMouseEvent(ui::MouseEvent* event) override {
     if ((event->flags() & ui::EF_SHIFT_DOWN) != 0 &&
         mouse_event_count_++ == 0) {
-      ui::MouseEvent mouse_event(ui::ET_MOUSE_PRESSED,
-                                 gfx::Point(10, 10), gfx::Point(10, 10),
+      ui::MouseEvent mouse_event(ui::ET_MOUSE_PRESSED, gfx::Point(10, 10),
+                                 gfx::Point(10, 10), ui::EventTimeForNow(),
                                  ui::EF_SHIFT_DOWN, 0);
       root_->GetHost()->dispatcher()->RepostEvent(mouse_event);
     }
@@ -1654,12 +1653,12 @@
   DontResetHeldEventWindowDelegate delegate(root_window());
   scoped_ptr<Window> w1(CreateNormalWindow(1, root_window(), &delegate));
   w1->SetBounds(gfx::Rect(0, 0, 40, 40));
-  ui::MouseEvent pressed(ui::ET_MOUSE_PRESSED,
-                         gfx::Point(10, 10), gfx::Point(10, 10),
+  ui::MouseEvent pressed(ui::ET_MOUSE_PRESSED, gfx::Point(10, 10),
+                         gfx::Point(10, 10), ui::EventTimeForNow(),
                          ui::EF_SHIFT_DOWN, 0);
   root_window()->GetHost()->dispatcher()->RepostEvent(pressed);
-  ui::MouseEvent pressed2(ui::ET_MOUSE_PRESSED,
-                          gfx::Point(10, 10), gfx::Point(10, 10), 0, 0);
+  ui::MouseEvent pressed2(ui::ET_MOUSE_PRESSED, gfx::Point(10, 10),
+                          gfx::Point(10, 10), ui::EventTimeForNow(), 0, 0);
   // Dispatch an event to flush event scheduled by way of RepostEvent().
   DispatchEventUsingWindowDispatcher(&pressed2);
   // Delegate should have seen reposted event (identified by way of
@@ -1715,8 +1714,8 @@
   // Owned by |h2|.
   Window* w1 = CreateNormalWindow(1, h2->window(), &delegate);
   w1->SetBounds(gfx::Rect(0, 0, 40, 40));
-  ui::MouseEvent pressed(ui::ET_MOUSE_PRESSED,
-                         gfx::Point(10, 10), gfx::Point(10, 10),
+  ui::MouseEvent pressed(ui::ET_MOUSE_PRESSED, gfx::Point(10, 10),
+                         gfx::Point(10, 10), ui::EventTimeForNow(),
                          ui::EF_SHIFT_DOWN, 0);
   h2->dispatcher()->RepostEvent(pressed);
   // RunAllPendingInMessageLoop() to make sure the |pressed| is run.
@@ -1919,11 +1918,9 @@
     // Start a nested message-loop, post an event to be dispatched, and then
     // terminate the message-loop. When the message-loop unwinds and gets back,
     // the reposted event should not have fired.
-    scoped_ptr<ui::MouseEvent> mouse(new ui::MouseEvent(ui::ET_MOUSE_PRESSED,
-                                                        gfx::Point(10, 10),
-                                                        gfx::Point(10, 10),
-                                                        ui::EF_NONE,
-                                                        ui::EF_NONE));
+    scoped_ptr<ui::MouseEvent> mouse(new ui::MouseEvent(
+        ui::ET_MOUSE_PRESSED, gfx::Point(10, 10), gfx::Point(10, 10),
+        ui::EventTimeForNow(), ui::EF_NONE, ui::EF_NONE));
     message_loop()->PostTask(
         FROM_HERE,
         base::Bind(&WindowEventDispatcherTestWithMessageLoop::RepostEventHelper,
@@ -2008,18 +2005,18 @@
   child->AddPreTargetHandler(&handler_child);
 
   {
-    ui::MouseEvent move(ui::ET_MOUSE_MOVED,
-                        gfx::Point(30, 30), gfx::Point(30, 30),
-                        ui::EF_NONE, ui::EF_NONE);
+    ui::MouseEvent move(ui::ET_MOUSE_MOVED, gfx::Point(30, 30),
+                        gfx::Point(30, 30), ui::EventTimeForNow(), ui::EF_NONE,
+                        ui::EF_NONE);
     DispatchEventUsingWindowDispatcher(&move);
     EXPECT_EQ(0, handler_child.num_mouse_events());
     EXPECT_EQ(1, handler_root.num_mouse_events());
   }
 
   {
-    ui::MouseEvent move(ui::ET_MOUSE_MOVED,
-                        gfx::Point(50, 50), gfx::Point(50, 50),
-                        ui::EF_NONE, ui::EF_NONE);
+    ui::MouseEvent move(ui::ET_MOUSE_MOVED, gfx::Point(50, 50),
+                        gfx::Point(50, 50), ui::EventTimeForNow(), ui::EF_NONE,
+                        ui::EF_NONE);
     DispatchEventUsingWindowDispatcher(&move);
     // The child receives an ENTER, and a MOVED event.
     EXPECT_EQ(2, handler_child.num_mouse_events());
@@ -2108,16 +2105,18 @@
   RunAllPendingInMessageLoop();
 
   ui::MouseEvent mouse_move(ui::ET_MOUSE_MOVED, gfx::Point(80, 80),
-                            gfx::Point(80, 80), ui::EF_NONE, ui::EF_NONE);
+                            gfx::Point(80, 80), ui::EventTimeForNow(),
+                            ui::EF_NONE, ui::EF_NONE);
   const base::Closure callback_on_right_click = base::Bind(
       base::IgnoreResult(&WindowEventDispatcherTestInHighDPI::DispatchEvent),
       base::Unretained(this), base::Unretained(&mouse_move));
   TriggerNestedLoopOnRightMousePress handler(callback_on_right_click);
   window->AddPreTargetHandler(&handler);
 
-  scoped_ptr<ui::MouseEvent> mouse(new ui::MouseEvent(
-      ui::ET_MOUSE_PRESSED, gfx::Point(10, 10), gfx::Point(10, 10),
-      ui::EF_RIGHT_MOUSE_BUTTON, ui::EF_RIGHT_MOUSE_BUTTON));
+  scoped_ptr<ui::MouseEvent> mouse(
+      new ui::MouseEvent(ui::ET_MOUSE_PRESSED, gfx::Point(10, 10),
+                         gfx::Point(10, 10), ui::EventTimeForNow(),
+                         ui::EF_RIGHT_MOUSE_BUTTON, ui::EF_RIGHT_MOUSE_BUTTON));
   host()->dispatcher()->RepostEvent(*mouse);
   EXPECT_EQ(0, handler.num_mouse_events());
 
@@ -2157,7 +2156,7 @@
 
   // Synthesized event should not update the mouse location.
   ui::MouseEvent mouseev(ui::ET_MOUSE_MOVED, gfx::Point(), gfx::Point(),
-                         ui::EF_IS_SYNTHESIZED, 0);
+                         ui::EventTimeForNow(), ui::EF_IS_SYNTHESIZED, 0);
   generator.Dispatch(&mouseev);
   EXPECT_EQ("10,10",
             Env::GetInstance()->last_mouse_location().ToString());
@@ -2252,7 +2251,8 @@
   void OnMouseEvent(ui::MouseEvent* mouse) override {
     if (mouse->type() == ui::ET_MOUSE_MOVED) {
       ui::MouseEvent move(ui::ET_MOUSE_MOVED, target_->bounds().CenterPoint(),
-          target_->bounds().CenterPoint(), ui::EF_NONE, ui::EF_NONE);
+                          target_->bounds().CenterPoint(),
+                          ui::EventTimeForNow(), ui::EF_NONE, ui::EF_NONE);
       ui::EventDispatchDetails details =
           target_->GetHost()->dispatcher()->OnEventFromSource(&move);
       ASSERT_FALSE(details.dispatcher_destroyed);
@@ -2325,7 +2325,8 @@
 
   // Dispatch an event to |first|.
   ui::MouseEvent move(ui::ET_MOUSE_MOVED, first->bounds().CenterPoint(),
-                      first->bounds().CenterPoint(), ui::EF_NONE, ui::EF_NONE);
+                      first->bounds().CenterPoint(), ui::EventTimeForNow(),
+                      ui::EF_NONE, ui::EF_NONE);
   ui::EventDispatchDetails details =
       host()->dispatcher()->OnEventFromSource(&move);
   ASSERT_FALSE(details.dispatcher_destroyed);
@@ -2422,8 +2423,8 @@
   EventFilterRecorder recorder_second;
   window_second->AddPreTargetHandler(&recorder_second);
   const gfx::Point event_location(25, 15);
-  ui::MouseEvent mouse(ui::ET_MOUSE_PRESSED, event_location,
-                       event_location, ui::EF_LEFT_MOUSE_BUTTON,
+  ui::MouseEvent mouse(ui::ET_MOUSE_PRESSED, event_location, event_location,
+                       ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
                        ui::EF_LEFT_MOUSE_BUTTON);
   DispatchEventUsingWindowDispatcher(&mouse);
   EXPECT_TRUE(recorder_first.events().empty());
@@ -2446,7 +2447,7 @@
     // Convert touch event back to root window coordinates.
     event->ConvertLocationToTarget(window_, window_->GetRootWindow());
     event->DisableSynchronousHandling();
-    dispatcher_->ProcessedTouchEvent(event, window_, ui::ER_UNHANDLED);
+    dispatcher_->ProcessedTouchEvent(window_, ui::ER_UNHANDLED);
     event->StopPropagation();
   }
 
diff --git a/ui/aura/window_targeter_unittest.cc b/ui/aura/window_targeter_unittest.cc
index 922b021..02217271 100644
--- a/ui/aura/window_targeter_unittest.cc
+++ b/ui/aura/window_targeter_unittest.cc
@@ -8,6 +8,7 @@
 #include "ui/aura/test/aura_test_base.h"
 #include "ui/aura/test/test_window_delegate.h"
 #include "ui/aura/window.h"
+#include "ui/events/event_utils.h"
 #include "ui/events/test/test_event_handler.h"
 
 namespace aura {
@@ -66,10 +67,8 @@
   ui::test::TestEventHandler handler;
   one->AddPreTargetHandler(&handler);
 
-  ui::MouseEvent press(ui::ET_MOUSE_PRESSED,
-                       gfx::Point(20, 20),
-                       gfx::Point(20, 20),
-                       ui::EF_NONE,
+  ui::MouseEvent press(ui::ET_MOUSE_PRESSED, gfx::Point(20, 20),
+                       gfx::Point(20, 20), ui::EventTimeForNow(), ui::EF_NONE,
                        ui::EF_NONE);
   DispatchEventUsingWindowDispatcher(&press);
   EXPECT_EQ(1, handler.num_mouse_events());
@@ -96,7 +95,7 @@
   gfx::Point event_location(60, 60);
   {
     ui::MouseEvent mouse(ui::ET_MOUSE_MOVED, event_location, event_location,
-                         ui::EF_NONE, ui::EF_NONE);
+                         ui::EventTimeForNow(), ui::EF_NONE, ui::EF_NONE);
     EXPECT_EQ(child, targeter->FindTargetForEvent(root, &mouse));
   }
 
@@ -106,13 +105,13 @@
           new StaticWindowTargeter(window.get()))));
   {
     ui::MouseEvent mouse(ui::ET_MOUSE_MOVED, event_location, event_location,
-                         ui::EF_NONE, ui::EF_NONE);
+                         ui::EventTimeForNow(), ui::EF_NONE, ui::EF_NONE);
     EXPECT_EQ(window.get(), targeter->FindTargetForEvent(root, &mouse));
   }
   scoped_targeter.reset();
   {
     ui::MouseEvent mouse(ui::ET_MOUSE_MOVED, event_location, event_location,
-                         ui::EF_NONE, ui::EF_NONE);
+                         ui::EventTimeForNow(), ui::EF_NONE, ui::EF_NONE);
     EXPECT_EQ(child, targeter->FindTargetForEvent(root, &mouse));
   }
 }
@@ -146,7 +145,7 @@
   gfx::Point event_location(490, 50);
   {
     ui::MouseEvent mouse(ui::ET_MOUSE_MOVED, event_location, event_location,
-                         ui::EF_NONE, ui::EF_NONE);
+                         ui::EventTimeForNow(), ui::EF_NONE, ui::EF_NONE);
     EXPECT_EQ(window.get(), targeter->FindTargetForEvent(root_target, &mouse));
   }
 
@@ -159,7 +158,7 @@
             GetEffectiveVisibleBoundsInRootWindow(window.get()).ToString());
   {
     ui::MouseEvent mouse(ui::ET_MOUSE_MOVED, event_location, event_location,
-                         ui::EF_NONE, ui::EF_NONE);
+                         ui::EventTimeForNow(), ui::EF_NONE, ui::EF_NONE);
     EXPECT_EQ(root_window(), targeter->FindTargetForEvent(root_target, &mouse));
   }
 
@@ -171,7 +170,7 @@
             GetEffectiveVisibleBoundsInRootWindow(window.get()).ToString());
   {
     ui::MouseEvent mouse(ui::ET_MOUSE_MOVED, event_location, event_location,
-                         ui::EF_NONE, ui::EF_NONE);
+                         ui::EventTimeForNow(), ui::EF_NONE, ui::EF_NONE);
     EXPECT_EQ(window.get(), targeter->FindTargetForEvent(root_target, &mouse));
   }
 }
diff --git a/ui/aura/window_unittest.cc b/ui/aura/window_unittest.cc
index 9ae01ff..fd7ed66 100644
--- a/ui/aura/window_unittest.cc
+++ b/ui/aura/window_unittest.cc
@@ -1255,8 +1255,8 @@
   EXPECT_FALSE(d1.exited());
   d1.ResetExpectations();
 
-  ui::MouseEvent exit_event(
-      ui::ET_MOUSE_EXITED, gfx::Point(), gfx::Point(), 0, 0);
+  ui::MouseEvent exit_event(ui::ET_MOUSE_EXITED, gfx::Point(), gfx::Point(),
+                            ui::EventTimeForNow(), 0, 0);
   DispatchEventUsingWindowDispatcher(&exit_event);
   EXPECT_FALSE(d1.entered());
   EXPECT_TRUE(d1.exited());
diff --git a/ui/base/BUILD.gn b/ui/base/BUILD.gn
index c3eb671..a99a649 100644
--- a/ui/base/BUILD.gn
+++ b/ui/base/BUILD.gn
@@ -294,6 +294,9 @@
     sources += [ "touch/touch_device.cc" ]
   }
 
+  # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+  configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
+
   defines = [ "UI_BASE_IMPLEMENTATION" ]
 
   public_deps = [
@@ -414,7 +417,6 @@
     ]
     deps += [ "//third_party/wtl" ]
     cflags = [
-      "/wd4267",  # TODO(jschuh): C4267: http://crbug.com/167187 size_t -> int.
       "/wd4324",  # Structure was padded due to __declspec(align()), which is
                   # uninteresting.
     ]
@@ -701,6 +703,9 @@
     }
   }
 
+  # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+  configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
+
   deps = [
     "//base",
     "//base/allocator",
@@ -746,9 +751,6 @@
       "imm32.lib",
       "oleacc.lib",
     ]
-
-    # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
-    cflags = [ "/wd4267" ]
   }
 
   if (!is_win || !use_aura) {
diff --git a/ui/base/ime/BUILD.gn b/ui/base/ime/BUILD.gn
index 53b002e..e697402 100644
--- a/ui/base/ime/BUILD.gn
+++ b/ui/base/ime/BUILD.gn
@@ -99,6 +99,9 @@
     "win/tsf_input_scope.h",
   ]
 
+  # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+  configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
+
   defines = [ "UI_BASE_IME_IMPLEMENTATION" ]
 
   deps = [
@@ -164,7 +167,6 @@
 
   if (is_win) {
     cflags = [
-      "/wd4267",  # TODO(jschuh): C4267: http://crbug.com/167187 size_t -> int.
       "/wd4324",  # Structure was padded due to __declspec(align()), which is
                   # uninteresting.
     ]
diff --git a/ui/base/ime/dummy_text_input_client.cc b/ui/base/ime/dummy_text_input_client.cc
index 6e12ad7..2cd2dc7 100644
--- a/ui/base/ime/dummy_text_input_client.cc
+++ b/ui/base/ime/dummy_text_input_client.cc
@@ -115,11 +115,11 @@
 void DummyTextInputClient::OnCandidateWindowHidden() {
 }
 
-bool DummyTextInputClient::IsEditingCommandEnabled(int command_id) {
+bool DummyTextInputClient::IsEditCommandEnabled(int command_id) {
   return false;
 }
 
-void DummyTextInputClient::ExecuteEditingCommand(int command_id) {
+void DummyTextInputClient::SetEditCommandForNextKeyEvent(int command_id) {
 }
 
 }  // namespace ui
diff --git a/ui/base/ime/dummy_text_input_client.h b/ui/base/ime/dummy_text_input_client.h
index 32dd857..8be3296 100644
--- a/ui/base/ime/dummy_text_input_client.h
+++ b/ui/base/ime/dummy_text_input_client.h
@@ -46,8 +46,8 @@
   void OnCandidateWindowShown() override;
   void OnCandidateWindowUpdated() override;
   void OnCandidateWindowHidden() override;
-  bool IsEditingCommandEnabled(int command_id) override;
-  void ExecuteEditingCommand(int command_id) override;
+  bool IsEditCommandEnabled(int command_id) override;
+  void SetEditCommandForNextKeyEvent(int command_id) override;
 
   TextInputType text_input_type_;
 
diff --git a/ui/base/ime/input_method_chromeos_unittest.cc b/ui/base/ime/input_method_chromeos_unittest.cc
index 0f5ce75..4562eb4 100644
--- a/ui/base/ime/input_method_chromeos_unittest.cc
+++ b/ui/base/ime/input_method_chromeos_unittest.cc
@@ -20,6 +20,7 @@
 #include "ui/base/ime/chromeos/ime_bridge.h"
 #include "ui/base/ime/chromeos/mock_ime_candidate_window_handler.h"
 #include "ui/base/ime/chromeos/mock_ime_engine_handler.h"
+#include "ui/base/ime/dummy_text_input_client.h"
 #include "ui/base/ime/input_method_delegate.h"
 #include "ui/base/ime/text_input_client.h"
 #include "ui/base/ime/text_input_focus_manager.h"
@@ -201,7 +202,7 @@
 
 class InputMethodChromeOSTest : public internal::InputMethodDelegate,
                                 public testing::Test,
-                                public TextInputClient {
+                                public DummyTextInputClient {
  public:
   InputMethodChromeOSTest()
       : dispatched_key_event_(ui::ET_UNKNOWN, ui::VKEY_UNKNOWN, ui::EF_NONE) {
@@ -267,18 +268,10 @@
     inserted_char_ = ch;
     inserted_char_flags_ = flags;
   }
-  gfx::NativeWindow GetAttachedWindow() const override {
-    return static_cast<gfx::NativeWindow>(NULL);
-  }
   TextInputType GetTextInputType() const override { return input_type_; }
   TextInputMode GetTextInputMode() const override { return input_mode_; }
-  int GetTextInputFlags() const override { return 0; }
   bool CanComposeInline() const override { return can_compose_inline_; }
   gfx::Rect GetCaretBounds() const override { return caret_bounds_; }
-  bool GetCompositionCharacterBounds(uint32 index,
-                                     gfx::Rect* rect) const override {
-    return false;
-  }
   bool HasCompositionText() const override {
     CompositionText empty;
     return composition_text_ != empty;
@@ -287,16 +280,10 @@
     *range = text_range_;
     return true;
   }
-  bool GetCompositionTextRange(gfx::Range* range) const override {
-    return false;
-  }
   bool GetSelectionRange(gfx::Range* range) const override {
     *range = selection_range_;
     return true;
   }
-
-  bool SetSelectionRange(const gfx::Range& range) override { return false; }
-  bool DeleteRange(const gfx::Range& range) override { return false; }
   bool GetTextFromRange(const gfx::Range& range,
                         base::string16* text) const override {
     *text = surrounding_text_.substr(range.GetMin(), range.length());
@@ -305,17 +292,6 @@
   void OnInputMethodChanged() override {
     ++on_input_method_changed_call_count_;
   }
-  bool ChangeTextDirectionAndLayoutAlignment(
-      base::i18n::TextDirection direction) override {
-    return false;
-  }
-  void ExtendSelectionAndDelete(size_t before, size_t after) override {}
-  void EnsureCaretInRect(const gfx::Rect& rect) override {}
-  void OnCandidateWindowShown() override {}
-  void OnCandidateWindowUpdated() override {}
-  void OnCandidateWindowHidden() override {}
-  bool IsEditingCommandEnabled(int command_id) override { return false; }
-  void ExecuteEditingCommand(int command_id) override {}
 
   bool HasNativeEvent() const {
     return dispatched_key_event_.HasNativeEvent();
diff --git a/ui/base/ime/input_method_mac.mm b/ui/base/ime/input_method_mac.mm
index c4b55c9..04105fa 100644
--- a/ui/base/ime/input_method_mac.mm
+++ b/ui/base/ime/input_method_mac.mm
@@ -19,9 +19,8 @@
 }
 
 bool InputMethodMac::DispatchKeyEvent(const ui::KeyEvent& event) {
-  // IME processing on the Mac does not go through this path.
-  NOTREACHED();
-  return false;
+  // This is used on Mac only to dispatch events post-IME.
+  return DispatchKeyEventPostIME(event);
 }
 
 void InputMethodMac::OnCaretBoundsChanged(const TextInputClient* client) {
diff --git a/ui/base/ime/text_input_client.h b/ui/base/ime/text_input_client.h
index 19c602c..c36acf1 100644
--- a/ui/base/ime/text_input_client.h
+++ b/ui/base/ime/text_input_client.h
@@ -173,9 +173,14 @@
   virtual void OnCandidateWindowHidden() = 0;
 
   // Returns true if |command_id| is currently allowed to be executed.
-  virtual bool IsEditingCommandEnabled(int command_id) = 0;
-  // Execute the command specified by |command_id|.
-  virtual void ExecuteEditingCommand(int command_id) = 0;
+  virtual bool IsEditCommandEnabled(int command_id) = 0;
+
+  // Execute the command specified by |command_id| on the next key event.
+  // This allows a TextInputClient to be informed of a platform-independent edit
+  // command that has been derived from the key event currently being dispatched
+  // (but not yet sent to the TextInputClient). The edit command will take into
+  // account any OS-specific, or user-specified, keybindings that may be set up.
+  virtual void SetEditCommandForNextKeyEvent(int command_id) = 0;
 };
 
 }  // namespace ui
diff --git a/ui/base/l10n/l10n_util.cc b/ui/base/l10n/l10n_util.cc
index eb49b14..80afb84 100644
--- a/ui/base/l10n/l10n_util.cc
+++ b/ui/base/l10n/l10n_util.cc
@@ -191,7 +191,7 @@
   static const char* const kDuplicateNames[] = {
     "en",
     "en_001",
-    "pt",
+    "pt", // pt-BR and pt-PT are used.
     "zh",
     "zh_hans_cn",
     "zh_hant_hk",
@@ -200,11 +200,10 @@
     "zh_hant_tw"
   };
 
-  // Skip all 'es_RR'. Currently, we use 'es' for es-ES (Spanish in Spain).
-  // 'es-419' (Spanish in Latin America) is not available in ICU so that it
-  // has to be added manually in GetAvailableLocales().
-  if (LowerCaseEqualsASCII(locale_name.substr(0, 3),  "es_"))
-    return true;
+  // Skip all the es_Foo other than es_419 for now.
+  if (StartsWithASCII(locale_name, "es_", false))
+    return !EndsWith(locale_name, "419", true);
+
   for (size_t i = 0; i < arraysize(kDuplicateNames); ++i) {
     if (base::strcasecmp(kDuplicateNames[i], locale_name.c_str()) == 0)
       return true;
@@ -297,8 +296,6 @@
       locales->push_back(locale_name);
     }
 
-    // Manually add 'es-419' to the list. See the comment in IsDuplicateName().
-    locales->push_back("es-419");
     return locales;
   }
 };
diff --git a/ui/base/l10n/l10n_util_unittest.cc b/ui/base/l10n/l10n_util_unittest.cc
index cc25d36..6474740 100644
--- a/ui/base/l10n/l10n_util_unittest.cc
+++ b/ui/base/l10n/l10n_util_unittest.cc
@@ -459,6 +459,12 @@
   result = l10n_util::GetDisplayNameForLocale("xyz-xyz", "en", false);
   EXPECT_EQ(ASCIIToUTF16("xyz (XYZ)"), result);
 
+  // Make sure that en-GB locale has the corect display names.
+  result = l10n_util::GetDisplayNameForLocale("en", "en-GB", false);
+  EXPECT_EQ(ASCIIToUTF16("English"), result);
+  result = l10n_util::GetDisplayNameForLocale("es-419", "en-GB", false);
+  EXPECT_EQ(ASCIIToUTF16("Spanish (Latin America)"), result);
+
   // Check for directional markers when using RTL languages to ensure that
   // direction neutral characters such as parentheses are properly formatted.
 
diff --git a/ui/base/user_activity/user_activity_detector_unittest.cc b/ui/base/user_activity/user_activity_detector_unittest.cc
index de76313..dc4cbae 100644
--- a/ui/base/user_activity/user_activity_detector_unittest.cc
+++ b/ui/base/user_activity/user_activity_detector_unittest.cc
@@ -11,6 +11,7 @@
 #include "ui/base/user_activity/user_activity_observer.h"
 #include "ui/events/event.h"
 #include "ui/events/event_constants.h"
+#include "ui/events/event_utils.h"
 #include "ui/events/keycodes/keyboard_codes.h"
 #include "ui/gfx/geometry/point.h"
 
@@ -79,8 +80,8 @@
   base::TimeDelta advance_delta = base::TimeDelta::FromMilliseconds(
       UserActivityDetector::kNotifyIntervalMs);
   AdvanceTime(advance_delta);
-  ui::MouseEvent mouse_event(
-      ui::ET_MOUSE_MOVED, gfx::Point(), gfx::Point(), ui::EF_NONE, ui::EF_NONE);
+  ui::MouseEvent mouse_event(ui::ET_MOUSE_MOVED, gfx::Point(), gfx::Point(),
+                             ui::EventTimeForNow(), ui::EF_NONE, ui::EF_NONE);
   detector_->OnMouseEvent(&mouse_event);
   EXPECT_FALSE(mouse_event.handled());
   EXPECT_EQ(now_.ToInternalValue(),
@@ -181,9 +182,9 @@
 
 // Checks that the detector ignores synthetic mouse events.
 TEST_F(UserActivityDetectorTest, IgnoreSyntheticMouseEvents) {
-  ui::MouseEvent mouse_event(
-      ui::ET_MOUSE_MOVED, gfx::Point(), gfx::Point(), ui::EF_IS_SYNTHESIZED,
-      ui::EF_NONE);
+  ui::MouseEvent mouse_event(ui::ET_MOUSE_MOVED, gfx::Point(), gfx::Point(),
+                             ui::EventTimeForNow(), ui::EF_IS_SYNTHESIZED,
+                             ui::EF_NONE);
   detector_->OnMouseEvent(&mouse_event);
   EXPECT_FALSE(mouse_event.handled());
   EXPECT_EQ(base::TimeTicks().ToInternalValue(),
diff --git a/ui/chromeos/DEPS b/ui/chromeos/DEPS
index e89a792..c533fe6 100644
--- a/ui/chromeos/DEPS
+++ b/ui/chromeos/DEPS
@@ -1,5 +1,6 @@
 include_rules = [
   "+chromeos",
+  "+components/device_event_log",
   "+grit/ui_chromeos_resources.h",
   "+grit/ui_chromeos_strings.h",
   "+third_party/cros_system_api",
diff --git a/ui/chromeos/network/network_connect.cc b/ui/chromeos/network/network_connect.cc
index 0d9a3e9..bc9f02e 100644
--- a/ui/chromeos/network/network_connect.cc
+++ b/ui/chromeos/network/network_connect.cc
@@ -68,11 +68,11 @@
   void ActivateCellular(const std::string& service_path) override;
   void ShowMobileSetup(const std::string& service_path) override;
   void ConfigureNetworkAndConnect(const std::string& service_path,
-                                  const base::DictionaryValue& properties,
+                                  const base::DictionaryValue& shill_properties,
                                   bool shared) override;
-  void CreateConfigurationAndConnect(base::DictionaryValue* properties,
+  void CreateConfigurationAndConnect(base::DictionaryValue* shill_properties,
                                      bool shared) override;
-  void CreateConfiguration(base::DictionaryValue* properties,
+  void CreateConfiguration(base::DictionaryValue* shill_properties,
                            bool shared) override;
   base::string16 GetShillErrorString(const std::string& error,
                                      const std::string& service_path) override;
@@ -290,7 +290,7 @@
 }
 
 void NetworkConnectImpl::CallCreateConfiguration(
-    base::DictionaryValue* properties,
+    base::DictionaryValue* shill_properties,
     bool shared,
     bool connect_on_configure) {
   std::string profile_path;
@@ -299,14 +299,16 @@
         NetworkConnectionHandler::kErrorConfigureFailed, "");
     return;
   }
-  properties->SetStringWithoutPathExpansion(shill::kProfileProperty,
-                                            profile_path);
-  NetworkHandler::Get()->network_configuration_handler()->CreateConfiguration(
-      *properties, NetworkConfigurationObserver::SOURCE_USER_ACTION,
-      base::Bind(&NetworkConnectImpl::OnConfigureSucceeded,
-                 weak_factory_.GetWeakPtr(), connect_on_configure),
-      base::Bind(&NetworkConnectImpl::OnConfigureFailed,
-                 weak_factory_.GetWeakPtr()));
+  shill_properties->SetStringWithoutPathExpansion(shill::kProfileProperty,
+                                                  profile_path);
+  NetworkHandler::Get()
+      ->network_configuration_handler()
+      ->CreateShillConfiguration(
+          *shill_properties, NetworkConfigurationObserver::SOURCE_USER_ACTION,
+          base::Bind(&NetworkConnectImpl::OnConfigureSucceeded,
+                     weak_factory_.GetWeakPtr(), connect_on_configure),
+          base::Bind(&NetworkConnectImpl::OnConfigureFailed,
+                     weak_factory_.GetWeakPtr()));
 }
 
 void NetworkConnectImpl::SetPropertiesFailed(
@@ -342,7 +344,7 @@
   NET_LOG_USER("ClearPropertiesAndConnect", service_path);
   // After configuring a network, ignore any (possibly stale) error state.
   const bool check_error_state = false;
-  NetworkHandler::Get()->network_configuration_handler()->ClearProperties(
+  NetworkHandler::Get()->network_configuration_handler()->ClearShillProperties(
       service_path, properties_to_clear,
       base::Bind(&NetworkConnectImpl::CallConnectToNetwork,
                  weak_factory_.GetWeakPtr(), service_path, check_error_state),
@@ -355,7 +357,7 @@
     scoped_ptr<base::DictionaryValue> properties_to_set) {
   std::vector<std::string> properties_to_clear;
   SetPropertiesToClear(properties_to_set.get(), &properties_to_clear);
-  NetworkHandler::Get()->network_configuration_handler()->SetProperties(
+  NetworkHandler::Get()->network_configuration_handler()->SetShillProperties(
       service_path, *properties_to_set,
       NetworkConfigurationObserver::SOURCE_USER_ACTION,
       base::Bind(&NetworkConnectImpl::ClearPropertiesAndConnect,
diff --git a/ui/chromeos/network/network_connect.h b/ui/chromeos/network/network_connect.h
index 80ed818..540f1ff1 100644
--- a/ui/chromeos/network/network_connect.h
+++ b/ui/chromeos/network/network_connect.h
@@ -81,18 +81,19 @@
   // connect request. The profile is set according to 'shared' if allowed.
   virtual void ConfigureNetworkAndConnect(
       const std::string& service_path,
-      const base::DictionaryValue& properties,
+      const base::DictionaryValue& shill_properties,
       bool shared) = 0;
 
   // Requests a new network configuration to be created from a dictionary of
   // Shill properties and sends a connect request if the configuration succeeds.
   // The profile used is determined by |shared|.
-  virtual void CreateConfigurationAndConnect(base::DictionaryValue* properties,
-                                             bool shared) = 0;
+  virtual void CreateConfigurationAndConnect(
+      base::DictionaryValue* shill_properties,
+      bool shared) = 0;
 
   // Requests a new network configuration to be created from a dictionary of
   // Shill properties. The profile used is determined by |shared|.
-  virtual void CreateConfiguration(base::DictionaryValue* properties,
+  virtual void CreateConfiguration(base::DictionaryValue* shill_properties,
                                    bool shared) = 0;
 
   // Returns the localized string for shill error string |error|.
diff --git a/ui/chromeos/network/network_list.cc b/ui/chromeos/network/network_list.cc
index fb807e5..1fff907 100644
--- a/ui/chromeos/network/network_list.cc
+++ b/ui/chromeos/network/network_list.cc
@@ -7,10 +7,10 @@
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/power_manager/power_supply_properties.pb.h"
 #include "chromeos/dbus/power_manager_client.h"
-#include "chromeos/device_event_log.h"
 #include "chromeos/network/network_state.h"
 #include "chromeos/network/network_state_handler.h"
 #include "chromeos/network/network_state_handler_observer.h"
+#include "components/device_event_log/device_event_log.h"
 #include "grit/ui_chromeos_strings.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/chromeos/network/network_icon.h"
diff --git a/ui/chromeos/network/network_state_notifier.cc b/ui/chromeos/network/network_state_notifier.cc
index eb211c2..0cfde4aa 100644
--- a/ui/chromeos/network/network_state_notifier.cc
+++ b/ui/chromeos/network/network_state_notifier.cc
@@ -231,7 +231,7 @@
     return;
   }
   // Get the up-to-date properties for the network and display the error.
-  NetworkHandler::Get()->network_configuration_handler()->GetProperties(
+  NetworkHandler::Get()->network_configuration_handler()->GetShillProperties(
       service_path,
       base::Bind(&NetworkStateNotifier::ConnectErrorPropertiesSucceeded,
                  weak_ptr_factory_.GetWeakPtr(), error_name),
diff --git a/ui/chromeos/resources/default_100_percent/network/status_captive_portal_access.png b/ui/chromeos/resources/default_100_percent/network/status_captive_portal_access.png
new file mode 100644
index 0000000..0fed1057
--- /dev/null
+++ b/ui/chromeos/resources/default_100_percent/network/status_captive_portal_access.png
Binary files differ
diff --git a/ui/chromeos/resources/default_200_percent/network/status_captive_portal_access.png b/ui/chromeos/resources/default_200_percent/network/status_captive_portal_access.png
new file mode 100644
index 0000000..321d85c
--- /dev/null
+++ b/ui/chromeos/resources/default_200_percent/network/status_captive_portal_access.png
Binary files differ
diff --git a/ui/chromeos/resources/ui_chromeos_resources.grd b/ui/chromeos/resources/ui_chromeos_resources.grd
index 5683f9a..f557091 100644
--- a/ui/chromeos/resources/ui_chromeos_resources.grd
+++ b/ui/chromeos/resources/ui_chromeos_resources.grd
@@ -20,7 +20,7 @@
       <structure type="chrome_scaled_image" name="IDR_AURA_UBER_TRAY_NETWORK_ARCS_LIGHT" file="network/statusbar_network_arcs_light.png" />
       <structure type="chrome_scaled_image" name="IDR_AURA_UBER_TRAY_NETWORK_BARS_DARK" file="network/statusbar_network_bars_dark.png" />
       <structure type="chrome_scaled_image" name="IDR_AURA_UBER_TRAY_NETWORK_BARS_LIGHT" file="network/statusbar_network_bars_light.png" />
-      <structure type="chrome_scaled_image" name="IDR_AURA_UBER_TRAY_NETWORK_CONTROLLED" file="network/statusbar_network_roaming_dark.png" />
+      <structure type="chrome_scaled_image" name="IDR_AURA_UBER_TRAY_NETWORK_CONTROLLED" file="network/status_captive_portal_access.png" />
       <structure type="chrome_scaled_image" name="IDR_AURA_UBER_TRAY_NETWORK_EDGE_DARK" file="network/statusbar_network_edge_dark.png" />
       <structure type="chrome_scaled_image" name="IDR_AURA_UBER_TRAY_NETWORK_EDGE_LIGHT" file="network/statusbar_network_edge_light.png" />
       <structure type="chrome_scaled_image" name="IDR_AURA_UBER_TRAY_NETWORK_EVDO_DARK" file="network/statusbar_network_evdo_dark.png" />
diff --git a/ui/chromeos/touch_exploration_controller.cc b/ui/chromeos/touch_exploration_controller.cc
index 695142d..23ea740 100644
--- a/ui/chromeos/touch_exploration_controller.cc
+++ b/ui/chromeos/touch_exploration_controller.cc
@@ -986,8 +986,8 @@
   // event to the new ChromeVox background page via the automation api.
   flags |= ui::EF_COMMAND_DOWN;
 
-  return scoped_ptr<ui::Event>(
-      new ui::MouseEvent(ui::ET_MOUSE_MOVED, location, location, flags, 0));
+  return make_scoped_ptr(new ui::MouseEvent(
+      ui::ET_MOUSE_MOVED, location, location, ui::EventTimeForNow(), flags, 0));
 }
 
 void TouchExplorationController::EnterTouchToMouseMode() {
diff --git a/ui/chromeos/touch_exploration_controller_unittest.cc b/ui/chromeos/touch_exploration_controller_unittest.cc
index 052b527..1b7faf5 100644
--- a/ui/chromeos/touch_exploration_controller_unittest.cc
+++ b/ui/chromeos/touch_exploration_controller_unittest.cc
@@ -542,10 +542,8 @@
   generator_->MoveTouch(location_end);
 
   gfx::Point location_real_mouse_move(15, 16);
-  ui::MouseEvent mouse_move(ui::ET_MOUSE_MOVED,
-                            location_real_mouse_move,
-                            location_real_mouse_move,
-                            0,
+  ui::MouseEvent mouse_move(ui::ET_MOUSE_MOVED, location_real_mouse_move,
+                            location_real_mouse_move, ui::EventTimeForNow(), 0,
                             0);
   generator_->Dispatch(&mouse_move);
   generator_->ReleaseTouch();
diff --git a/ui/chromeos/ui_chromeos.gyp b/ui/chromeos/ui_chromeos.gyp
index 1b7ee6c..f55823e 100644
--- a/ui/chromeos/ui_chromeos.gyp
+++ b/ui/chromeos/ui_chromeos.gyp
@@ -49,6 +49,7 @@
         '../../base/third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations',
         '../../chromeos/chromeos.gyp:chromeos',
         '../../chromeos/chromeos.gyp:power_manager_proto',
+        '../../components/components.gyp:device_event_log_component',
         '../../skia/skia.gyp:skia',
         '../aura/aura.gyp:aura',
         '../base/ime/ui_base_ime.gyp:ui_base_ime',
diff --git a/ui/compositor/compositor.cc b/ui/compositor/compositor.cc
index 3592860..5f2a43b 100644
--- a/ui/compositor/compositor.cc
+++ b/ui/compositor/compositor.cc
@@ -224,6 +224,10 @@
   host_->SetNeedsCommit();
 }
 
+void Compositor::FinishAllRendering() {
+  host_->FinishAllRendering();
+}
+
 void Compositor::DisableSwapUntilResize() {
   host_->FinishAllRendering();
   context_factory_->ResizeDisplay(this, gfx::Size());
@@ -260,6 +264,10 @@
   host_->SetVisible(visible);
 }
 
+bool Compositor::IsVisible() {
+  return host_->visible();
+}
+
 scoped_refptr<CompositorVSyncManager> Compositor::vsync_manager() const {
   return vsync_manager_;
 }
@@ -299,6 +307,9 @@
     host_->SetNeedsAnimate();
 }
 
+void Compositor::BeginMainFrameNotExpectedSoon() {
+}
+
 void Compositor::Layout() {
   // We're sending damage that will be addressed during this composite
   // cycle, so we don't need to schedule another composite to address it.
diff --git a/ui/compositor/compositor.h b/ui/compositor/compositor.h
index 9bc7f884..3c99a16 100644
--- a/ui/compositor/compositor.h
+++ b/ui/compositor/compositor.h
@@ -182,6 +182,9 @@
   // from changes to layer properties.
   void ScheduleRedrawRect(const gfx::Rect& damage_rect);
 
+  // Finishes all outstanding rendering and disables swapping on this surface.
+  void FinishAllRendering();
+
   // Finishes all outstanding rendering and disables swapping on this surface
   // until it is resized.
   void DisableSwapUntilResize();
@@ -198,9 +201,12 @@
   // the |root_layer|.
   void SetBackgroundColor(SkColor color);
 
-  // Set the visibility of the underlying compositor.
+  // Sets the visibility of the underlying compositor.
   void SetVisible(bool visible);
 
+  // Gets the visibility of the underlying compositor.
+  bool IsVisible();
+
   // Returns the widget for this compositor.
   gfx::AcceleratedWidget widget() const { return widget_; }
 
@@ -243,6 +249,7 @@
   void WillBeginMainFrame() override {}
   void DidBeginMainFrame() override {}
   void BeginMainFrame(const cc::BeginFrameArgs& args) override;
+  void BeginMainFrameNotExpectedSoon() override;
   void Layout() override;
   void ApplyViewportDeltas(const gfx::Vector2dF& inner_delta,
                            const gfx::Vector2dF& outer_delta,
diff --git a/ui/compositor/layer_animation_element.cc b/ui/compositor/layer_animation_element.cc
index 3e667dfd..9a528e5 100644
--- a/ui/compositor/layer_animation_element.cc
+++ b/ui/compositor/layer_animation_element.cc
@@ -411,10 +411,8 @@
                                        target_,
                                        duration()));
     scoped_ptr<cc::Animation> animation(
-        cc::Animation::Create(animation_curve.Pass(),
-                              animation_id(),
-                              animation_group_id(),
-                              cc::Animation::Opacity));
+        cc::Animation::Create(animation_curve.Pass(), animation_id(),
+                              animation_group_id(), cc::Animation::OPACITY));
     return animation.Pass();
   }
 
@@ -466,10 +464,8 @@
                                            target_,
                                            duration()));
     scoped_ptr<cc::Animation> animation(
-        cc::Animation::Create(animation_curve.Pass(),
-                              animation_id(),
-                              animation_group_id(),
-                              cc::Animation::Transform));
+        cc::Animation::Create(animation_curve.Pass(), animation_id(),
+                              animation_group_id(), cc::Animation::TRANSFORM));
     return animation.Pass();
   }
 
@@ -540,10 +536,8 @@
 
   scoped_ptr<cc::Animation> CreateCCAnimation() override {
     scoped_ptr<cc::Animation> animation(
-        cc::Animation::Create(animation_curve_->Clone(),
-                              animation_id(),
-                              animation_group_id(),
-                              cc::Animation::Transform));
+        cc::Animation::Create(animation_curve_->Clone(), animation_id(),
+                              animation_group_id(), cc::Animation::TRANSFORM));
     return animation.Pass();
   }
 
@@ -738,9 +732,9 @@
 LayerAnimationElement::ToAnimatableProperty(
     cc::Animation::TargetProperty property) {
   switch (property) {
-    case cc::Animation::Transform:
+    case cc::Animation::TRANSFORM:
       return TRANSFORM;
-    case cc::Animation::Opacity:
+    case cc::Animation::OPACITY:
       return OPACITY;
     default:
       NOTREACHED();
diff --git a/ui/compositor/layer_animation_sequence_unittest.cc b/ui/compositor/layer_animation_sequence_unittest.cc
index 2d3b8be..c416fabc 100644
--- a/ui/compositor/layer_animation_sequence_unittest.cc
+++ b/ui/compositor/layer_animation_sequence_unittest.cc
@@ -92,12 +92,9 @@
     sequence.Progress(start_time, &delegate);
     EXPECT_FLOAT_EQ(start, sequence.last_progressed_fraction());
     effective_start = start_time + delta;
-    sequence.OnThreadedAnimationStarted(
-        cc::AnimationEvent(cc::AnimationEvent::Started,
-                           0,
-                           sequence.animation_group_id(),
-                           cc::Animation::Opacity,
-                           effective_start));
+    sequence.OnThreadedAnimationStarted(cc::AnimationEvent(
+        cc::AnimationEvent::STARTED, 0, sequence.animation_group_id(),
+        cc::Animation::OPACITY, effective_start));
     sequence.Progress(effective_start + delta/2, &delegate);
     EXPECT_FLOAT_EQ(middle, sequence.last_progressed_fraction());
     EXPECT_TRUE(sequence.IsFinished(effective_start + delta));
@@ -149,12 +146,9 @@
     EXPECT_FLOAT_EQ(0.0, sequence.last_progressed_fraction());
     opacity_effective_start = start_time + delta;
     EXPECT_EQ(starting_group_id, sequence.animation_group_id());
-    sequence.OnThreadedAnimationStarted(
-        cc::AnimationEvent(cc::AnimationEvent::Started,
-                           0,
-                           sequence.animation_group_id(),
-                           cc::Animation::Opacity,
-                           opacity_effective_start));
+    sequence.OnThreadedAnimationStarted(cc::AnimationEvent(
+        cc::AnimationEvent::STARTED, 0, sequence.animation_group_id(),
+        cc::Animation::OPACITY, opacity_effective_start));
     sequence.Progress(opacity_effective_start + delta/2, &delegate);
     EXPECT_FLOAT_EQ(0.5, sequence.last_progressed_fraction());
     sequence.Progress(opacity_effective_start + delta, &delegate);
@@ -180,12 +174,9 @@
     EXPECT_FLOAT_EQ(0.0, sequence.last_progressed_fraction());
     transform_effective_start = opacity_effective_start + 3 * delta;
     EXPECT_NE(starting_group_id, sequence.animation_group_id());
-    sequence.OnThreadedAnimationStarted(
-        cc::AnimationEvent(cc::AnimationEvent::Started,
-                           0,
-                           sequence.animation_group_id(),
-                           cc::Animation::Transform,
-                           transform_effective_start));
+    sequence.OnThreadedAnimationStarted(cc::AnimationEvent(
+        cc::AnimationEvent::STARTED, 0, sequence.animation_group_id(),
+        cc::Animation::TRANSFORM, transform_effective_start));
     sequence.Progress(transform_effective_start + delta/2, &delegate);
     EXPECT_FLOAT_EQ(0.5, sequence.last_progressed_fraction());
     EXPECT_TRUE(sequence.IsFinished(transform_effective_start + delta));
diff --git a/ui/compositor/layer_animator_unittest.cc b/ui/compositor/layer_animator_unittest.cc
index f6aff59d..613eb1c 100644
--- a/ui/compositor/layer_animator_unittest.cc
+++ b/ui/compositor/layer_animator_unittest.cc
@@ -340,12 +340,10 @@
   base::TimeTicks effective_start = start_time + delta;
 
   test_controller.animator()->OnThreadedAnimationStarted(cc::AnimationEvent(
-      cc::AnimationEvent::Started,
-      0,
+      cc::AnimationEvent::STARTED, 0,
       test_controller.GetRunningSequence(LayerAnimationElement::OPACITY)
           ->animation_group_id(),
-      cc::Animation::Opacity,
-      effective_start));
+      cc::Animation::OPACITY, effective_start));
 
   animator->Step(effective_start + delta / 2);
 
@@ -455,12 +453,10 @@
   base::TimeTicks effective_start = start_time + delta;
 
   test_controller.animator()->OnThreadedAnimationStarted(cc::AnimationEvent(
-      cc::AnimationEvent::Started,
-      0,
+      cc::AnimationEvent::STARTED, 0,
       test_controller.GetRunningSequence(LayerAnimationElement::OPACITY)
           ->animation_group_id(),
-      cc::Animation::Opacity,
-      effective_start));
+      cc::Animation::OPACITY, effective_start));
 
   animator->Step(effective_start + delta / 2);
 
@@ -729,12 +725,10 @@
   base::TimeTicks effective_start = start_time + delta;
 
   test_controller.animator()->OnThreadedAnimationStarted(cc::AnimationEvent(
-      cc::AnimationEvent::Started,
-      0,
+      cc::AnimationEvent::STARTED, 0,
       test_controller.GetRunningSequence(LayerAnimationElement::OPACITY)
           ->animation_group_id(),
-      cc::Animation::Opacity,
-      effective_start));
+      cc::Animation::OPACITY, effective_start));
 
   animator->Step(effective_start + delta / 2);
 
@@ -861,12 +855,10 @@
   base::TimeTicks effective_start = start_time + delta;
 
   test_controller.animator()->OnThreadedAnimationStarted(cc::AnimationEvent(
-      cc::AnimationEvent::Started,
-      0,
+      cc::AnimationEvent::STARTED, 0,
       test_controller.GetRunningSequence(LayerAnimationElement::OPACITY)
           ->animation_group_id(),
-      cc::Animation::Opacity,
-      effective_start));
+      cc::Animation::OPACITY, effective_start));
 
   animator->Step(effective_start + delta / 2);
 
@@ -886,12 +878,10 @@
   base::TimeTicks second_effective_start = effective_start + delta;
 
   test_controller.animator()->OnThreadedAnimationStarted(cc::AnimationEvent(
-      cc::AnimationEvent::Started,
-      0,
+      cc::AnimationEvent::STARTED, 0,
       test_controller.GetRunningSequence(LayerAnimationElement::OPACITY)
           ->animation_group_id(),
-      cc::Animation::Opacity,
-      second_effective_start));
+      cc::Animation::OPACITY, second_effective_start));
 
   animator->Step(second_effective_start + delta / 2);
 
@@ -1200,12 +1190,10 @@
   base::TimeTicks effective_start = start_time + delta;
 
   test_controller.animator()->OnThreadedAnimationStarted(cc::AnimationEvent(
-      cc::AnimationEvent::Started,
-      0,
+      cc::AnimationEvent::STARTED, 0,
       test_controller.GetRunningSequence(LayerAnimationElement::OPACITY)
           ->animation_group_id(),
-      cc::Animation::Opacity,
-      effective_start));
+      cc::Animation::OPACITY, effective_start));
 
   animator->Step(effective_start + delta / 2);
 
@@ -1230,12 +1218,10 @@
   base::TimeTicks second_effective_start = effective_start + delta;
 
   test_controller.animator()->OnThreadedAnimationStarted(cc::AnimationEvent(
-      cc::AnimationEvent::Started,
-      0,
+      cc::AnimationEvent::STARTED, 0,
       test_controller.GetRunningSequence(LayerAnimationElement::OPACITY)
           ->animation_group_id(),
-      cc::Animation::Opacity,
-      second_effective_start));
+      cc::Animation::OPACITY, second_effective_start));
 
   animator->Step(second_effective_start + delta / 2);
 
@@ -1484,12 +1470,10 @@
   base::TimeTicks effective_start = start_time + delta;
 
   test_controller.animator()->OnThreadedAnimationStarted(cc::AnimationEvent(
-      cc::AnimationEvent::Started,
-      0,
+      cc::AnimationEvent::STARTED, 0,
       test_controller.GetRunningSequence(LayerAnimationElement::OPACITY)
           ->animation_group_id(),
-      cc::Animation::Opacity,
-      effective_start));
+      cc::Animation::OPACITY, effective_start));
 
   animator->Step(effective_start + delta);
   EXPECT_TRUE(test_controller.animator()->is_animating());
@@ -1497,12 +1481,10 @@
 
   base::TimeTicks second_effective_start = effective_start + 2 * delta;
   test_controller.animator()->OnThreadedAnimationStarted(cc::AnimationEvent(
-      cc::AnimationEvent::Started,
-      0,
+      cc::AnimationEvent::STARTED, 0,
       test_controller.GetRunningSequence(LayerAnimationElement::OPACITY)
           ->animation_group_id(),
-      cc::Animation::Opacity,
-      second_effective_start));
+      cc::Animation::OPACITY, second_effective_start));
 
   animator->Step(second_effective_start + delta);
 
@@ -1511,12 +1493,10 @@
 
   base::TimeTicks third_effective_start = second_effective_start + 2 * delta;
   test_controller.animator()->OnThreadedAnimationStarted(cc::AnimationEvent(
-      cc::AnimationEvent::Started,
-      0,
+      cc::AnimationEvent::STARTED, 0,
       test_controller.GetRunningSequence(LayerAnimationElement::OPACITY)
           ->animation_group_id(),
-      cc::Animation::Opacity,
-      third_effective_start));
+      cc::Animation::OPACITY, third_effective_start));
 
   animator->Step(third_effective_start + delta);
   EXPECT_TRUE(test_controller.animator()->is_animating());
@@ -1524,12 +1504,10 @@
 
   base::TimeTicks fourth_effective_start = third_effective_start + 2 * delta;
   test_controller.animator()->OnThreadedAnimationStarted(cc::AnimationEvent(
-      cc::AnimationEvent::Started,
-      0,
+      cc::AnimationEvent::STARTED, 0,
       test_controller.GetRunningSequence(LayerAnimationElement::OPACITY)
           ->animation_group_id(),
-      cc::Animation::Opacity,
-      fourth_effective_start));
+      cc::Animation::OPACITY, fourth_effective_start));
 
   // Skip ahead by a lot.
   animator->Step(fourth_effective_start + 1000 * delta);
@@ -1539,12 +1517,10 @@
 
   base::TimeTicks fifth_effective_start = fourth_effective_start + 1001 * delta;
   test_controller.animator()->OnThreadedAnimationStarted(cc::AnimationEvent(
-      cc::AnimationEvent::Started,
-      0,
+      cc::AnimationEvent::STARTED, 0,
       test_controller.GetRunningSequence(LayerAnimationElement::OPACITY)
           ->animation_group_id(),
-      cc::Animation::Opacity,
-      fifth_effective_start));
+      cc::Animation::OPACITY, fifth_effective_start));
 
   // Skip ahead by a lot.
   animator->Step(fifth_effective_start + 999 * delta);
diff --git a/ui/compositor/test/layer_animator_test_controller.cc b/ui/compositor/test/layer_animator_test_controller.cc
index 121b8aff..860d903 100644
--- a/ui/compositor/test/layer_animator_test_controller.cc
+++ b/ui/compositor/test/layer_animator_test_controller.cc
@@ -30,8 +30,8 @@
 
 void LayerAnimatorTestController::StartThreadedAnimationsIfNeeded() {
   std::vector<cc::Animation::TargetProperty> threaded_properties;
-  threaded_properties.push_back(cc::Animation::Opacity);
-  threaded_properties.push_back(cc::Animation::Transform);
+  threaded_properties.push_back(cc::Animation::OPACITY);
+  threaded_properties.push_back(cc::Animation::TRANSFORM);
 
   for (size_t i = 0; i < threaded_properties.size(); i++) {
     LayerAnimationElement::AnimatableProperty animatable_property =
@@ -48,12 +48,9 @@
         element->effective_start_time() != base::TimeTicks())
       continue;
 
-    animator_->OnThreadedAnimationStarted(
-        cc::AnimationEvent(cc::AnimationEvent::Started,
-                           0,
-                           element->animation_group_id(),
-                           threaded_properties[i],
-                           gfx::FrameTime::Now()));
+    animator_->OnThreadedAnimationStarted(cc::AnimationEvent(
+        cc::AnimationEvent::STARTED, 0, element->animation_group_id(),
+        threaded_properties[i], gfx::FrameTime::Now()));
   }
 }
 
diff --git a/ui/display/chromeos/display_configurator.cc b/ui/display/chromeos/display_configurator.cc
index bcfc46a..a6eb35f43 100644
--- a/ui/display/chromeos/display_configurator.cc
+++ b/ui/display/chromeos/display_configurator.cc
@@ -814,6 +814,14 @@
 }
 
 void DisplayConfigurator::OnConfigurationChanged() {
+  // Don't do anything if the displays are currently suspended.  Instead we will
+  // probe and reconfigure the displays if necessary in ResumeDisplays().
+  if (displays_suspended_) {
+    VLOG(1) << "Displays are currently suspended.  Not attempting to "
+            << "reconfigure them.";
+    return;
+  }
+
   // Configure displays with |kConfigureDelayMs| delay,
   // so that time-consuming ConfigureDisplays() won't be called multiple times.
   if (configure_timer_.IsRunning()) {
@@ -859,6 +867,10 @@
   }
 
   displays_suspended_ = true;
+
+  // Stop |configure_timer_| because we will force probe and configure all the
+  // displays at resume time anyway.
+  configure_timer_.Stop();
 }
 
 void DisplayConfigurator::ResumeDisplays() {
@@ -880,14 +892,6 @@
 }
 
 void DisplayConfigurator::RunPendingConfiguration() {
-  // Don't do anything if the displays are currently suspended.  Instead we will
-  // probe and reconfigure the displays if necessary in ResumeDisplays().
-  if (displays_suspended_) {
-    VLOG(1) << "Displays are currently suspended.  Not attempting to "
-            << "reconfigure them.";
-    return;
-  }
-
   // Configuration task is currently running. Do not start a second
   // configuration.
   if (configuration_task_)
diff --git a/ui/display/chromeos/display_configurator_unittest.cc b/ui/display/chromeos/display_configurator_unittest.cc
index 86b1473..9e40fda 100644
--- a/ui/display/chromeos/display_configurator_unittest.cc
+++ b/ui/display/chromeos/display_configurator_unittest.cc
@@ -975,10 +975,59 @@
   configurator_.SuspendDisplays();
   EXPECT_EQ(kNoActions, log_->GetActionsAndClear());
 
+  // The configuration timer should not be started when the displays
+  // are suspended.
   configurator_.OnConfigurationChanged();
-  EXPECT_TRUE(test_api_.TriggerConfigureTimeout());
+  EXPECT_FALSE(test_api_.TriggerConfigureTimeout());
   EXPECT_EQ(kNoActions, log_->GetActionsAndClear());
 
+  // Calls to SetDisplayPower and SetDisplayMode should be successful.
+  configurator_.SetDisplayPower(
+      chromeos::DISPLAY_POWER_ALL_OFF,
+      DisplayConfigurator::kSetDisplayPowerNoFlags,
+      base::Bind(&DisplayConfiguratorTest::OnConfiguredCallback,
+                 base::Unretained(this)));
+  EXPECT_EQ(CALLBACK_SUCCESS, PopCallbackResult());
+  EXPECT_EQ(
+      JoinActions(
+          kGrab,
+          GetFramebufferAction(small_mode_.size(), &outputs_[0], NULL).c_str(),
+          GetCrtcAction(outputs_[0], NULL, gfx::Point(0, 0)).c_str(),
+          kUngrab,
+          NULL),
+      log_->GetActionsAndClear());
+  configurator_.SetDisplayPower(
+      chromeos::DISPLAY_POWER_ALL_ON,
+      DisplayConfigurator::kSetDisplayPowerNoFlags,
+      base::Bind(&DisplayConfiguratorTest::OnConfiguredCallback,
+                 base::Unretained(this)));
+  EXPECT_EQ(CALLBACK_SUCCESS, PopCallbackResult());
+  EXPECT_EQ(
+      JoinActions(
+          kGrab,
+          GetFramebufferAction(small_mode_.size(), &outputs_[0], NULL).c_str(),
+          GetCrtcAction(outputs_[0], &small_mode_, gfx::Point(0, 0)).c_str(),
+          kForceDPMS,
+          kUngrab,
+          NULL),
+      log_->GetActionsAndClear());
+
+  UpdateOutputs(2, false);
+  configurator_.SetDisplayMode(MULTIPLE_DISPLAY_STATE_DUAL_MIRROR);
+  EXPECT_EQ(
+      JoinActions(
+          kGrab,
+          GetFramebufferAction(small_mode_.size(), &outputs_[0], &outputs_[1])
+              .c_str(),
+          GetCrtcAction(outputs_[0], &small_mode_, gfx::Point(0, 0)).c_str(),
+          GetCrtcAction(outputs_[1], &small_mode_, gfx::Point(0, 0)).c_str(),
+          kUngrab,
+          NULL),
+      log_->GetActionsAndClear());
+
+  // The DisplayConfigurator should force a probe and reconfiguration at resume
+  // time.
+  UpdateOutputs(1, false);
   configurator_.ResumeDisplays();
   EXPECT_TRUE(test_api_.TriggerConfigureTimeout());
   EXPECT_EQ(
@@ -992,12 +1041,12 @@
       log_->GetActionsAndClear());
 
   // If a configuration task is pending when the displays are suspended, that
-  // task should not run either.
+  // task should not run either and the timer should be stopped.
   configurator_.OnConfigurationChanged();
   configurator_.SuspendDisplays();
   EXPECT_EQ(kNoActions, log_->GetActionsAndClear());
 
-  EXPECT_TRUE(test_api_.TriggerConfigureTimeout());
+  EXPECT_FALSE(test_api_.TriggerConfigureTimeout());
   EXPECT_EQ(kNoActions, log_->GetActionsAndClear());
 
   configurator_.ResumeDisplays();
diff --git a/ui/display/chromeos/x11/display_configurator_x11.cc b/ui/display/chromeos/x11/display_configurator_x11.cc
index 93c056c3..6e4786e 100644
--- a/ui/display/chromeos/x11/display_configurator_x11.cc
+++ b/ui/display/chromeos/x11/display_configurator_x11.cc
@@ -10,7 +10,7 @@
 
 scoped_ptr<NativeDisplayDelegate>
 DisplayConfigurator::CreatePlatformNativeDisplayDelegate() {
-  return scoped_ptr<NativeDisplayDelegate>(new NativeDisplayDelegateX11());
+  return make_scoped_ptr(new NativeDisplayDelegateX11());
 }
 
 }  // namespace ui
diff --git a/ui/events/event.cc b/ui/events/event.cc
index c26b9c9..5e7d8c8 100644
--- a/ui/events/event.cc
+++ b/ui/events/event.cc
@@ -125,35 +125,35 @@
 // static
 scoped_ptr<Event> Event::Clone(const Event& event) {
   if (event.IsKeyEvent()) {
-    return scoped_ptr<Event>(new KeyEvent(static_cast<const KeyEvent&>(event)));
+    return make_scoped_ptr(new KeyEvent(static_cast<const KeyEvent&>(event)));
   }
 
   if (event.IsMouseEvent()) {
     if (event.IsMouseWheelEvent()) {
-      return scoped_ptr<Event>(
+      return make_scoped_ptr(
           new MouseWheelEvent(static_cast<const MouseWheelEvent&>(event)));
     }
 
-    return scoped_ptr<Event>(
+    return make_scoped_ptr(
         new MouseEvent(static_cast<const MouseEvent&>(event)));
   }
 
   if (event.IsTouchEvent()) {
-    return scoped_ptr<Event>(
+    return make_scoped_ptr(
         new TouchEvent(static_cast<const TouchEvent&>(event)));
   }
 
   if (event.IsGestureEvent()) {
-    return scoped_ptr<Event>(
+    return make_scoped_ptr(
         new GestureEvent(static_cast<const GestureEvent&>(event)));
   }
 
   if (event.IsScrollEvent()) {
-    return scoped_ptr<Event>(
+    return make_scoped_ptr(
         new ScrollEvent(static_cast<const ScrollEvent&>(event)));
   }
 
-  return scoped_ptr<Event>(new Event(event));
+  return make_scoped_ptr(new Event(event));
 }
 
 Event::~Event() {
@@ -330,9 +330,10 @@
 MouseEvent::MouseEvent(EventType type,
                        const gfx::PointF& location,
                        const gfx::PointF& root_location,
+                       base::TimeDelta time_stamp,
                        int flags,
                        int changed_button_flags)
-    : LocatedEvent(type, location, root_location, EventTimeForNow(), flags),
+    : LocatedEvent(type, location, root_location, time_stamp, flags),
       changed_button_flags_(changed_button_flags) {
   if (this->type() == ET_MOUSE_MOVED && IsAnyButton())
     SetType(ET_MOUSE_DRAGGED);
@@ -483,9 +484,14 @@
 MouseWheelEvent::MouseWheelEvent(const gfx::Vector2d& offset,
                                  const gfx::PointF& location,
                                  const gfx::PointF& root_location,
+                                 base::TimeDelta time_stamp,
                                  int flags,
                                  int changed_button_flags)
-    : MouseEvent(ui::ET_MOUSEWHEEL, location, root_location, flags,
+    : MouseEvent(ui::ET_MOUSEWHEEL,
+                 location,
+                 root_location,
+                 time_stamp,
+                 flags,
                  changed_button_flags),
       offset_(offset) {
 }
@@ -946,13 +952,12 @@
                          float x_offset_ordinal,
                          float y_offset_ordinal,
                          int finger_count)
-    : MouseEvent(type, location, location, flags, 0),
+    : MouseEvent(type, location, location, time_stamp, flags, 0),
       x_offset_(x_offset),
       y_offset_(y_offset),
       x_offset_ordinal_(x_offset_ordinal),
       y_offset_ordinal_(y_offset_ordinal),
       finger_count_(finger_count) {
-  set_time_stamp(time_stamp);
   CHECK(IsScrollEvent());
 }
 
diff --git a/ui/events/event.h b/ui/events/event.h
index ac2e5b59..c698dec 100644
--- a/ui/events/event.h
+++ b/ui/events/event.h
@@ -351,10 +351,11 @@
     set_flags(flags);
   }
 
-  // Used for synthetic events in testing and by the gesture recognizer.
+  // Used for synthetic events in testing, gesture recognizer and Ozone
   MouseEvent(EventType type,
              const gfx::PointF& location,
              const gfx::PointF& root_location,
+             base::TimeDelta time_stamp,
              int flags,
              int changed_button_flags);
 
@@ -459,6 +460,7 @@
   MouseWheelEvent(const gfx::Vector2d& offset,
                   const gfx::PointF& location,
                   const gfx::PointF& root_location,
+                  base::TimeDelta time_stamp,
                   int flags,
                   int changed_button_flags);
 
diff --git a/ui/events/event_dispatcher_unittest.cc b/ui/events/event_dispatcher_unittest.cc
index 95b45a30..0bae34e8 100644
--- a/ui/events/event_dispatcher_unittest.cc
+++ b/ui/events/event_dispatcher_unittest.cc
@@ -247,8 +247,8 @@
   h7.set_expect_post_target(true);
   h8.set_expect_post_target(true);
 
-  MouseEvent mouse(ui::ET_MOUSE_MOVED, gfx::Point(3, 4),
-                   gfx::Point(3, 4), 0, 0);
+  MouseEvent mouse(ui::ET_MOUSE_MOVED, gfx::Point(3, 4), gfx::Point(3, 4),
+                   ui::EventTimeForNow(), 0, 0);
   Event::DispatcherApi event_mod(&mouse);
   dispatcher.ProcessEvent(&child, &mouse);
   EXPECT_FALSE(mouse.stopped_propagation());
@@ -321,8 +321,8 @@
   handler.set_expect_pre_target(true);
   handler.set_expect_post_target(true);
 
-  MouseEvent mouse(ui::ET_MOUSE_MOVED, gfx::Point(3, 4),
-                   gfx::Point(3, 4), 0, 0);
+  MouseEvent mouse(ui::ET_MOUSE_MOVED, gfx::Point(3, 4), gfx::Point(3, 4),
+                   ui::EventTimeForNow(), 0, 0);
   Event::DispatcherApi event_mod(&mouse);
   dispatcher.ProcessEvent(&target, &mouse);
   EXPECT_EQ(ER_UNHANDLED, mouse.result());
@@ -353,8 +353,8 @@
     // destroyed the dispatcher.
     h2.set_expect_pre_target(false);
 
-    MouseEvent mouse(ui::ET_MOUSE_MOVED, gfx::Point(3, 4),
-                     gfx::Point(3, 4), 0, 0);
+    MouseEvent mouse(ui::ET_MOUSE_MOVED, gfx::Point(3, 4), gfx::Point(3, 4),
+                     ui::EventTimeForNow(), 0, 0);
     EventDispatchDetails details = dispatcher->ProcessEvent(&target, &mouse);
     EXPECT_TRUE(details.dispatcher_destroyed);
     EXPECT_EQ(ER_CONSUMED, mouse.result());
@@ -405,8 +405,8 @@
     // destroyed the dispatcher.
     h2.set_expect_post_target(false);
 
-    MouseEvent mouse(ui::ET_MOUSE_MOVED, gfx::Point(3, 4),
-                     gfx::Point(3, 4), 0, 0);
+    MouseEvent mouse(ui::ET_MOUSE_MOVED, gfx::Point(3, 4), gfx::Point(3, 4),
+                     ui::EventTimeForNow(), 0, 0);
     EventDispatchDetails details = dispatcher->ProcessEvent(&target, &mouse);
     EXPECT_TRUE(details.dispatcher_destroyed);
     EXPECT_EQ(ER_CONSUMED, mouse.result());
@@ -459,8 +459,8 @@
   // |h3| should not receive events as the target will be invalidated.
   h3.set_expect_pre_target(false);
 
-  MouseEvent mouse(ui::ET_MOUSE_MOVED, gfx::Point(3, 4), gfx::Point(3, 4), 0,
-                   0);
+  MouseEvent mouse(ui::ET_MOUSE_MOVED, gfx::Point(3, 4), gfx::Point(3, 4),
+                   ui::EventTimeForNow(), 0, 0);
   EventDispatchDetails details = dispatcher.ProcessEvent(&target, &mouse);
   EXPECT_FALSE(details.dispatcher_destroyed);
   EXPECT_TRUE(details.target_destroyed);
@@ -502,8 +502,8 @@
     // destroyed it.
     h3->set_expect_pre_target(false);
 
-    MouseEvent mouse(ui::ET_MOUSE_MOVED, gfx::Point(3, 4), gfx::Point(3, 4), 0,
-                     0);
+    MouseEvent mouse(ui::ET_MOUSE_MOVED, gfx::Point(3, 4), gfx::Point(3, 4),
+                     ui::EventTimeForNow(), 0, 0);
     EventDispatchDetails details = dispatcher.ProcessEvent(&target, &mouse);
     EXPECT_FALSE(details.dispatcher_destroyed);
     EXPECT_FALSE(details.target_destroyed);
@@ -560,8 +560,8 @@
     // it.
     h3->set_expect_pre_target(false);
 
-    MouseEvent mouse(ui::ET_MOUSE_MOVED, gfx::Point(3, 4), gfx::Point(3, 4), 0,
-                     0);
+    MouseEvent mouse(ui::ET_MOUSE_MOVED, gfx::Point(3, 4), gfx::Point(3, 4),
+                     ui::EventTimeForNow(), 0, 0);
     EventDispatchDetails details = dispatcher->ProcessEvent(&target, &mouse);
     EXPECT_TRUE(details.dispatcher_destroyed);
     EXPECT_TRUE(mouse.stopped_propagation());
diff --git a/ui/events/event_processor_unittest.cc b/ui/events/event_processor_unittest.cc
index 7170aa0..56c205cd 100644
--- a/ui/events/event_processor_unittest.cc
+++ b/ui/events/event_processor_unittest.cc
@@ -7,6 +7,7 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/events/event.h"
 #include "ui/events/event_targeter.h"
+#include "ui/events/event_utils.h"
 #include "ui/events/test/events_test_utils.h"
 #include "ui/events/test/test_event_handler.h"
 #include "ui/events/test/test_event_processor.h"
@@ -52,7 +53,7 @@
   root()->AddChild(child.Pass());
 
   MouseEvent mouse(ET_MOUSE_MOVED, gfx::Point(10, 10), gfx::Point(10, 10),
-                   EF_NONE, EF_NONE);
+                   EventTimeForNow(), EF_NONE, EF_NONE);
   DispatchEvent(&mouse);
   EXPECT_TRUE(root()->child_at(0)->DidReceiveEvent(ET_MOUSE_MOVED));
   EXPECT_FALSE(root()->DidReceiveEvent(ET_MOUSE_MOVED));
@@ -140,8 +141,8 @@
   // Dispatch a mouse event that falls on the parent, but not on the child. When
   // the default event-targeter used, the event will still reach |grandchild|,
   // because the default targeter does not look at the bounds.
-  MouseEvent mouse(ET_MOUSE_MOVED, gfx::Point(1, 1), gfx::Point(1, 1), EF_NONE,
-                   EF_NONE);
+  MouseEvent mouse(ET_MOUSE_MOVED, gfx::Point(1, 1), gfx::Point(1, 1),
+                   EventTimeForNow(), EF_NONE, EF_NONE);
   DispatchEvent(&mouse);
   EXPECT_FALSE(root()->DidReceiveEvent(ET_MOUSE_MOVED));
   EXPECT_FALSE(parent_r->DidReceiveEvent(ET_MOUSE_MOVED));
@@ -152,8 +153,8 @@
   // Now install a targeter on the parent that looks at the bounds and makes
   // sure the event reaches the target only if the location of the event within
   // the bounds of the target.
-  MouseEvent mouse2(ET_MOUSE_MOVED, gfx::Point(1, 1), gfx::Point(1, 1), EF_NONE,
-                    EF_NONE);
+  MouseEvent mouse2(ET_MOUSE_MOVED, gfx::Point(1, 1), gfx::Point(1, 1),
+                    EventTimeForNow(), EF_NONE, EF_NONE);
   parent_r->SetEventTargeter(scoped_ptr<EventTargeter>(
       new BoundsEventTargeter<BoundsTestTarget>()));
   DispatchEvent(&mouse2);
@@ -164,7 +165,7 @@
   parent_r->ResetReceivedEvents();
 
   MouseEvent second(ET_MOUSE_MOVED, gfx::Point(12, 12), gfx::Point(12, 12),
-                    EF_NONE, EF_NONE);
+                    EventTimeForNow(), EF_NONE, EF_NONE);
   DispatchEvent(&second);
   EXPECT_FALSE(root()->DidReceiveEvent(ET_MOUSE_MOVED));
   EXPECT_FALSE(parent_r->DidReceiveEvent(ET_MOUSE_MOVED));
@@ -238,8 +239,8 @@
   // Dispatch a mouse event to the tree of event targets owned by the first
   // event processor, checking in ReDispatchEventHandler that the phase and
   // target information of the event is correct.
-  MouseEvent mouse(
-      ET_MOUSE_MOVED, gfx::Point(10, 10), gfx::Point(10, 10), EF_NONE, EF_NONE);
+  MouseEvent mouse(ET_MOUSE_MOVED, gfx::Point(10, 10), gfx::Point(10, 10),
+                   EventTimeForNow(), EF_NONE, EF_NONE);
   DispatchEvent(&mouse);
 
   // Verify also that |mouse| was seen by the child nodes contained in both
@@ -253,8 +254,8 @@
   // Indicate that the child of the second root should handle events, and
   // dispatch another mouse event to verify that it is marked as handled.
   second_root->child_at(0)->set_mark_events_as_handled(true);
-  MouseEvent mouse2(
-      ET_MOUSE_MOVED, gfx::Point(10, 10), gfx::Point(10, 10), EF_NONE, EF_NONE);
+  MouseEvent mouse2(ET_MOUSE_MOVED, gfx::Point(10, 10), gfx::Point(10, 10),
+                    EventTimeForNow(), EF_NONE, EF_NONE);
   DispatchEvent(&mouse2);
   EXPECT_TRUE(root()->child_at(0)->DidReceiveEvent(ET_MOUSE_MOVED));
   EXPECT_TRUE(second_root->child_at(0)->DidReceiveEvent(ET_MOUSE_MOVED));
@@ -271,7 +272,7 @@
   // Dispatch a mouse event. We expect the event to be seen by the target,
   // handled, and we expect OnEventProcessingFinished() to be invoked once.
   MouseEvent mouse(ET_MOUSE_MOVED, gfx::Point(10, 10), gfx::Point(10, 10),
-                   EF_NONE, EF_NONE);
+                   EventTimeForNow(), EF_NONE, EF_NONE);
   DispatchEvent(&mouse);
   EXPECT_TRUE(root()->child_at(0)->DidReceiveEvent(ET_MOUSE_MOVED));
   EXPECT_FALSE(root()->DidReceiveEvent(ET_MOUSE_MOVED));
@@ -291,8 +292,8 @@
   // OnEventProcessingStarted() should be called once, and
   // OnEventProcessingFinished() should be called once. The event should
   // remain unhandled.
-  MouseEvent mouse(
-      ET_MOUSE_MOVED, gfx::Point(10, 10), gfx::Point(10, 10), EF_NONE, EF_NONE);
+  MouseEvent mouse(ET_MOUSE_MOVED, gfx::Point(10, 10), gfx::Point(10, 10),
+                   EventTimeForNow(), EF_NONE, EF_NONE);
   DispatchEvent(&mouse);
   EXPECT_TRUE(root()->child_at(0)->DidReceiveEvent(ET_MOUSE_MOVED));
   EXPECT_FALSE(root()->DidReceiveEvent(ET_MOUSE_MOVED));
@@ -308,8 +309,8 @@
   // seen by the target this time, but OnEventProcessingStarted() and
   // OnEventProcessingFinished() should both still be called once.
   processor()->set_should_processing_occur(false);
-  MouseEvent mouse2(
-      ET_MOUSE_MOVED, gfx::Point(10, 10), gfx::Point(10, 10), EF_NONE, EF_NONE);
+  MouseEvent mouse2(ET_MOUSE_MOVED, gfx::Point(10, 10), gfx::Point(10, 10),
+                    EventTimeForNow(), EF_NONE, EF_NONE);
   DispatchEvent(&mouse2);
   EXPECT_FALSE(root()->child_at(0)->DidReceiveEvent(ET_MOUSE_MOVED));
   EXPECT_FALSE(root()->DidReceiveEvent(ET_MOUSE_MOVED));
@@ -338,7 +339,7 @@
   root()->AddChild(child.Pass());
 
   MouseEvent mouse(ET_MOUSE_MOVED, gfx::Point(10, 10), gfx::Point(10, 10),
-                   EF_NONE, EF_NONE);
+                   EventTimeForNow(), EF_NONE, EF_NONE);
   DispatchEvent(&mouse);
   EXPECT_TRUE(root()->child_at(0)->DidReceiveEvent(ET_MOUSE_MOVED));
   EXPECT_FALSE(root()->DidReceiveEvent(ET_MOUSE_MOVED));
@@ -349,7 +350,7 @@
   root()->child_at(0)->SetEventTargeter(
       scoped_ptr<EventTargeter>(new IgnoreEventTargeter()));
   MouseEvent mouse2(ET_MOUSE_MOVED, gfx::Point(10, 10), gfx::Point(10, 10),
-                    EF_NONE, EF_NONE);
+                    EventTimeForNow(), EF_NONE, EF_NONE);
   DispatchEvent(&mouse2);
   EXPECT_FALSE(root()->child_at(0)->DidReceiveEvent(ET_MOUSE_MOVED));
   EXPECT_TRUE(root()->DidReceiveEvent(ET_MOUSE_MOVED));
@@ -510,7 +511,7 @@
   grandchild_r->AddPostTargetHandler(&post_grandchild);
 
   MouseEvent mouse(ET_MOUSE_MOVED, gfx::Point(10, 10), gfx::Point(10, 10),
-                   EF_NONE, EF_NONE);
+                   EventTimeForNow(), EF_NONE, EF_NONE);
   DispatchEvent(&mouse);
 
   std::string expected[] = { "PreR", "PreC", "PreG", "G", "PostG", "PostC",
diff --git a/ui/events/event_unittest.cc b/ui/events/event_unittest.cc
index 7e92aa0..1b110fe 100644
--- a/ui/events/event_unittest.cc
+++ b/ui/events/event_unittest.cc
@@ -59,7 +59,7 @@
 
 TEST(EventTest, ClickCount) {
   const gfx::Point origin(0, 0);
-  MouseEvent mouseev(ET_MOUSE_PRESSED, origin, origin, 0, 0);
+  MouseEvent mouseev(ET_MOUSE_PRESSED, origin, origin, EventTimeForNow(), 0, 0);
   for (int i = 1; i <=3 ; ++i) {
     mouseev.SetClickCount(i);
     EXPECT_EQ(i, mouseev.GetClickCount());
@@ -68,8 +68,10 @@
 
 TEST(EventTest, RepeatedClick) {
   const gfx::Point origin(0, 0);
-  MouseEvent mouse_ev1(ET_MOUSE_PRESSED, origin, origin, 0, 0);
-  MouseEvent mouse_ev2(ET_MOUSE_PRESSED, origin, origin, 0, 0);
+  MouseEvent mouse_ev1(ET_MOUSE_PRESSED, origin, origin, EventTimeForNow(), 0,
+                       0);
+  MouseEvent mouse_ev2(ET_MOUSE_PRESSED, origin, origin, EventTimeForNow(), 0,
+                       0);
   LocatedEventTestApi test_ev1(&mouse_ev1);
   LocatedEventTestApi test_ev2(&mouse_ev2);
 
@@ -109,23 +111,29 @@
   scoped_ptr<MouseEvent> ev;
   base::TimeDelta start = base::TimeDelta::FromMilliseconds(0);
 
-  ev.reset(new MouseEvent(ET_MOUSE_PRESSED, origin1, origin1, 0, 0));
+  ev.reset(new MouseEvent(ET_MOUSE_PRESSED, origin1, origin1, EventTimeForNow(),
+                          0, 0));
   ev->set_time_stamp(start);
   EXPECT_EQ(1, MouseEvent::GetRepeatCount(*ev));
-  ev.reset(new MouseEvent(ET_MOUSE_PRESSED, origin1, origin1, 0, 0));
+  ev.reset(new MouseEvent(ET_MOUSE_PRESSED, origin1, origin1, EventTimeForNow(),
+                          0, 0));
   ev->set_time_stamp(start);
   EXPECT_EQ(1, MouseEvent::GetRepeatCount(*ev));
 
-  ev.reset(new MouseEvent(ET_MOUSE_PRESSED, origin2, origin2, 0, 0));
+  ev.reset(new MouseEvent(ET_MOUSE_PRESSED, origin2, origin2, EventTimeForNow(),
+                          0, 0));
   ev->set_time_stamp(start);
   EXPECT_EQ(1, MouseEvent::GetRepeatCount(*ev));
-  ev.reset(new MouseEvent(ET_MOUSE_RELEASED, origin2, origin2, 0, 0));
+  ev.reset(new MouseEvent(ET_MOUSE_RELEASED, origin2, origin2,
+                          EventTimeForNow(), 0, 0));
   ev->set_time_stamp(start);
   EXPECT_EQ(1, MouseEvent::GetRepeatCount(*ev));
-  ev.reset(new MouseEvent(ET_MOUSE_PRESSED, origin2, origin2, 0, 0));
+  ev.reset(new MouseEvent(ET_MOUSE_PRESSED, origin2, origin2, EventTimeForNow(),
+                          0, 0));
   ev->set_time_stamp(start);
   EXPECT_EQ(2, MouseEvent::GetRepeatCount(*ev));
-  ev.reset(new MouseEvent(ET_MOUSE_RELEASED, origin2, origin2, 0, 0));
+  ev.reset(new MouseEvent(ET_MOUSE_RELEASED, origin2, origin2,
+                          EventTimeForNow(), 0, 0));
   ev->set_time_stamp(start);
   EXPECT_EQ(2, MouseEvent::GetRepeatCount(*ev));
   MouseEvent::ResetLastClickForTest();
@@ -138,24 +146,21 @@
   scoped_ptr<MouseEvent> ev;
   base::TimeDelta start = base::TimeDelta::FromMilliseconds(0);
 
-  ev.reset(new MouseEvent(ET_MOUSE_PRESSED, origin, origin,
+  ev.reset(new MouseEvent(ET_MOUSE_PRESSED, origin, origin, EventTimeForNow(),
                           ui::EF_RIGHT_MOUSE_BUTTON,
                           ui::EF_RIGHT_MOUSE_BUTTON));
   ev->set_time_stamp(start);
   EXPECT_EQ(1, MouseEvent::GetRepeatCount(*ev));
-  ev.reset(new MouseEvent(ET_MOUSE_PRESSED, origin, origin,
-                          ui::EF_LEFT_MOUSE_BUTTON,
-                          ui::EF_LEFT_MOUSE_BUTTON));
+  ev.reset(new MouseEvent(ET_MOUSE_PRESSED, origin, origin, EventTimeForNow(),
+                          ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON));
   ev->set_time_stamp(start);
   EXPECT_EQ(1, MouseEvent::GetRepeatCount(*ev));
-  ev.reset(new MouseEvent(ET_MOUSE_RELEASED, origin, origin,
-                          ui::EF_LEFT_MOUSE_BUTTON,
-                          ui::EF_LEFT_MOUSE_BUTTON));
+  ev.reset(new MouseEvent(ET_MOUSE_RELEASED, origin, origin, EventTimeForNow(),
+                          ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON));
   ev->set_time_stamp(start);
   EXPECT_EQ(1, MouseEvent::GetRepeatCount(*ev));
-  ev.reset(new MouseEvent(ET_MOUSE_PRESSED, origin, origin,
-                          ui::EF_LEFT_MOUSE_BUTTON,
-                          ui::EF_LEFT_MOUSE_BUTTON));
+  ev.reset(new MouseEvent(ET_MOUSE_PRESSED, origin, origin, EventTimeForNow(),
+                          ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON));
   ev->set_time_stamp(start);
   EXPECT_EQ(2, MouseEvent::GetRepeatCount(*ev));
   MouseEvent::ResetLastClickForTest();
diff --git a/ui/events/gesture_detection/touch_disposition_gesture_filter.cc b/ui/events/gesture_detection/touch_disposition_gesture_filter.cc
index 0fa42bd..b4c25cad 100644
--- a/ui/events/gesture_detection/touch_disposition_gesture_filter.cc
+++ b/ui/events/gesture_detection/touch_disposition_gesture_filter.cc
@@ -172,7 +172,7 @@
 
 void TouchDispositionGestureFilter::OnTouchEventAckForQueueFront(
     bool event_consumed) {
-  // Spurious touch acks from the renderer should not trigger a crash.
+  // Spurious asynchronous acks should not trigger a crash.
   if (IsEmpty() || (Head().empty() && sequences_.size() == 1))
     return;
 
diff --git a/ui/events/gestures/gesture_recognizer.h b/ui/events/gestures/gesture_recognizer.h
index 9ed07049..325efa2 100644
--- a/ui/events/gestures/gesture_recognizer.h
+++ b/ui/events/gestures/gesture_recognizer.h
@@ -34,10 +34,8 @@
 
   // Returns a list of zero or more GestureEvents. The caller is responsible for
   // freeing the returned events. Acks the first gesture packet in the queue.
-  virtual Gestures* AckAsyncTouchEvent(
-      const TouchEvent& event,
-      ui::EventResult result,
-      GestureConsumer* consumer) = 0;
+  virtual Gestures* AckAsyncTouchEvent(ui::EventResult result,
+                                       GestureConsumer* consumer) = 0;
 
   // Returns a list of zero or more GestureEvents. The caller is responsible for
   // freeing the returned events. Acks the last gesture packet in the queue.
diff --git a/ui/events/gestures/gesture_recognizer_impl.cc b/ui/events/gestures/gesture_recognizer_impl.cc
index 7250bf22..7743503 100644
--- a/ui/events/gestures/gesture_recognizer_impl.cc
+++ b/ui/events/gestures/gesture_recognizer_impl.cc
@@ -251,9 +251,11 @@
   return gesture_provider->OnTouchEvent(event);
 }
 
+// TODO(tdresser): we should take a unique_event_id here, and validate
+// that the correct event is being acked. See crbug.com/457669 for
+// details.
 GestureRecognizer::Gestures*
 GestureRecognizerImpl::AckAsyncTouchEvent(
-    const TouchEvent& event,
     ui::EventResult result,
     GestureConsumer* consumer) {
   GestureProviderAura* gesture_provider =
diff --git a/ui/events/gestures/gesture_recognizer_impl.h b/ui/events/gestures/gesture_recognizer_impl.h
index 45903ae..2f4c9fe 100644
--- a/ui/events/gestures/gesture_recognizer_impl.h
+++ b/ui/events/gestures/gesture_recognizer_impl.h
@@ -60,8 +60,7 @@
   bool ProcessTouchEventPreDispatch(TouchEvent* event,
                                     GestureConsumer* consumer) override;
 
-  Gestures* AckAsyncTouchEvent(const TouchEvent& event,
-                               ui::EventResult result,
+  Gestures* AckAsyncTouchEvent(ui::EventResult result,
                                GestureConsumer* consumer) override;
 
   Gestures* AckSyncTouchEvent(const uint64 unique_event_id,
diff --git a/ui/events/gestures/gesture_recognizer_impl_mac.cc b/ui/events/gestures/gesture_recognizer_impl_mac.cc
index 3a80e5d..fac7935 100644
--- a/ui/events/gestures/gesture_recognizer_impl_mac.cc
+++ b/ui/events/gestures/gesture_recognizer_impl_mac.cc
@@ -22,8 +22,7 @@
     return false;
   }
 
-  Gestures* AckAsyncTouchEvent(const TouchEvent& event,
-                               ui::EventResult result,
+  Gestures* AckAsyncTouchEvent(ui::EventResult result,
                                GestureConsumer* consumer) override {
     return NULL;
   }
diff --git a/ui/events/latency_info.cc b/ui/events/latency_info.cc
index 1bcd43f..02586d69 100644
--- a/ui/events/latency_info.cc
+++ b/ui/events/latency_info.cc
@@ -24,11 +24,14 @@
     CASE_TYPE(INPUT_EVENT_LATENCY_FIRST_SCROLL_UPDATE_ORIGINAL_COMPONENT);
     CASE_TYPE(INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT);
     CASE_TYPE(INPUT_EVENT_LATENCY_UI_COMPONENT);
-    CASE_TYPE(INPUT_EVENT_LATENCY_RENDERING_SCHEDULED_COMPONENT);
+    CASE_TYPE(INPUT_EVENT_LATENCY_RENDERING_SCHEDULED_MAIN_COMPONENT);
+    CASE_TYPE(INPUT_EVENT_LATENCY_RENDERING_SCHEDULED_IMPL_COMPONENT);
     CASE_TYPE(INPUT_EVENT_LATENCY_FORWARD_SCROLL_UPDATE_TO_MAIN_COMPONENT);
     CASE_TYPE(INPUT_EVENT_LATENCY_ACK_RWH_COMPONENT);
     CASE_TYPE(WINDOW_SNAPSHOT_FRAME_NUMBER_COMPONENT);
     CASE_TYPE(WINDOW_OLD_SNAPSHOT_FRAME_NUMBER_COMPONENT);
+    CASE_TYPE(TAB_SHOW_COMPONENT);
+    CASE_TYPE(INPUT_EVENT_LATENCY_RENDERER_SWAP_COMPONENT);
     CASE_TYPE(INPUT_EVENT_BROWSER_RECEIVED_RENDERER_SWAP_COMPONENT);
     CASE_TYPE(INPUT_EVENT_GPU_SWAP_BUFFER_COMPONENT);
     CASE_TYPE(INPUT_EVENT_LATENCY_TERMINATED_MOUSE_COMPONENT);
diff --git a/ui/events/latency_info.h b/ui/events/latency_info.h
index c3a52b8..b6724b8 100644
--- a/ui/events/latency_info.h
+++ b/ui/events/latency_info.h
@@ -35,8 +35,11 @@
   // Timestamp when the UI event is created.
   INPUT_EVENT_LATENCY_UI_COMPONENT,
   // This is special component indicating there is rendering scheduled for
-  // the event associated with this LatencyInfo.
-  INPUT_EVENT_LATENCY_RENDERING_SCHEDULED_COMPONENT,
+  // the event associated with this LatencyInfo on main thread.
+  INPUT_EVENT_LATENCY_RENDERING_SCHEDULED_MAIN_COMPONENT,
+  // This is special component indicating there is rendering scheduled for
+  // the event associated with this LatencyInfo on impl thread.
+  INPUT_EVENT_LATENCY_RENDERING_SCHEDULED_IMPL_COMPONENT,
   // Timestamp when a scroll update is forwarded to the main thread.
   INPUT_EVENT_LATENCY_FORWARD_SCROLL_UPDATE_TO_MAIN_COMPONENT,
   // Timestamp when the event's ack is received by the RWH.
@@ -50,6 +53,8 @@
   WINDOW_OLD_SNAPSHOT_FRAME_NUMBER_COMPONENT,
   // Timestamp when a tab is requested to be shown.
   TAB_SHOW_COMPONENT,
+  // Timestamp when the frame is swapped in renderer.
+  INPUT_EVENT_LATENCY_RENDERER_SWAP_COMPONENT,
   // Timestamp of when the browser process receives a buffer swap notification
   // from the renderer.
   INPUT_EVENT_BROWSER_RECEIVED_RENDERER_SWAP_COMPONENT,
@@ -106,7 +111,7 @@
   };
 
   // Empirically determined constant based on a typical scroll sequence.
-  enum { kTypicalMaxComponentsPerLatencyInfo = 9 };
+  enum { kTypicalMaxComponentsPerLatencyInfo = 10 };
 
   enum { kMaxInputCoordinates = 2 };
 
diff --git a/ui/events/ozone/device/device_manager.cc b/ui/events/ozone/device/device_manager.cc
index bbd5d80..a52123f 100644
--- a/ui/events/ozone/device/device_manager.cc
+++ b/ui/events/ozone/device/device_manager.cc
@@ -14,9 +14,9 @@
 
 scoped_ptr<DeviceManager> CreateDeviceManager() {
 #if defined(USE_UDEV)
-  return scoped_ptr<DeviceManager>(new DeviceManagerUdev());
+  return make_scoped_ptr(new DeviceManagerUdev());
 #else
-  return scoped_ptr<DeviceManager>(new DeviceManagerManual());
+  return make_scoped_ptr(new DeviceManagerManual());
 #endif
 }
 
diff --git a/ui/events/ozone/evdev/device_event_dispatcher_evdev.cc b/ui/events/ozone/evdev/device_event_dispatcher_evdev.cc
index d8be2c42..2743846e 100644
--- a/ui/events/ozone/evdev/device_event_dispatcher_evdev.cc
+++ b/ui/events/ozone/evdev/device_event_dispatcher_evdev.cc
@@ -19,8 +19,9 @@
 }
 
 MouseMoveEventParams::MouseMoveEventParams(int device_id,
-                                           const gfx::PointF& location)
-    : device_id(device_id), location(location) {
+                                           const gfx::PointF& location,
+                                           base::TimeDelta timestamp)
+    : device_id(device_id), location(location), timestamp(timestamp) {
 }
 
 MouseMoveEventParams::MouseMoveEventParams(const MouseMoveEventParams& other) =
@@ -33,12 +34,14 @@
                                                const gfx::PointF& location,
                                                unsigned int button,
                                                bool down,
-                                               bool allow_remap)
+                                               bool allow_remap,
+                                               base::TimeDelta timestamp)
     : device_id(device_id),
       location(location),
       button(button),
       down(down),
-      allow_remap(allow_remap) {
+      allow_remap(allow_remap),
+      timestamp(timestamp) {
 }
 
 MouseButtonEventParams::MouseButtonEventParams(
@@ -49,8 +52,12 @@
 
 MouseWheelEventParams::MouseWheelEventParams(int device_id,
                                              const gfx::PointF& location,
-                                             const gfx::Vector2d& delta)
-    : device_id(device_id), location(location), delta(delta) {
+                                             const gfx::Vector2d& delta,
+                                             base::TimeDelta timestamp)
+    : device_id(device_id),
+      location(location),
+      delta(delta),
+      timestamp(timestamp) {
 }
 
 MouseWheelEventParams::MouseWheelEventParams(
diff --git a/ui/events/ozone/evdev/device_event_dispatcher_evdev.h b/ui/events/ozone/evdev/device_event_dispatcher_evdev.h
index fe2bee19f..a7dddce 100644
--- a/ui/events/ozone/evdev/device_event_dispatcher_evdev.h
+++ b/ui/events/ozone/evdev/device_event_dispatcher_evdev.h
@@ -34,12 +34,15 @@
 };
 
 struct EVENTS_OZONE_EVDEV_EXPORT MouseMoveEventParams {
-  MouseMoveEventParams(int device_id, const gfx::PointF& location);
+  MouseMoveEventParams(int device_id,
+                       const gfx::PointF& location,
+                       base::TimeDelta timestamp);
   MouseMoveEventParams(const MouseMoveEventParams& other);
   ~MouseMoveEventParams();
 
   int device_id;
   gfx::PointF location;
+  base::TimeDelta timestamp;
 };
 
 struct EVENTS_OZONE_EVDEV_EXPORT MouseButtonEventParams {
@@ -47,7 +50,8 @@
                          const gfx::PointF& location,
                          unsigned int button,
                          bool down,
-                         bool allow_remap);
+                         bool allow_remap,
+                         base::TimeDelta timestamp);
   MouseButtonEventParams(const MouseButtonEventParams& other);
   ~MouseButtonEventParams();
 
@@ -56,18 +60,21 @@
   unsigned int button;
   bool down;
   bool allow_remap;
+  base::TimeDelta timestamp;
 };
 
 struct EVENTS_OZONE_EVDEV_EXPORT MouseWheelEventParams {
   MouseWheelEventParams(int device_id,
                         const gfx::PointF& location,
-                        const gfx::Vector2d& delta);
+                        const gfx::Vector2d& delta,
+                        base::TimeDelta timestamp);
   MouseWheelEventParams(const MouseWheelEventParams& other);
   ~MouseWheelEventParams();
 
   int device_id;
   gfx::PointF location;
   gfx::Vector2d delta;
+  base::TimeDelta timestamp;
 };
 
 struct EVENTS_OZONE_EVDEV_EXPORT ScrollEventParams {
diff --git a/ui/events/ozone/evdev/event_converter_evdev_impl.cc b/ui/events/ozone/evdev/event_converter_evdev_impl.cc
index 30d1ccf5..ef1a4c5 100644
--- a/ui/events/ozone/evdev/event_converter_evdev_impl.cc
+++ b/ui/events/ozone/evdev/event_converter_evdev_impl.cc
@@ -98,7 +98,7 @@
       case EV_SYN:
         if (input.code == SYN_DROPPED)
           LOG(WARNING) << "kernel dropped input events";
-        FlushEvents();
+        FlushEvents(input);
         break;
     }
   }
@@ -142,19 +142,19 @@
   if (!cursor_)
     return;
 
-  dispatcher_->DispatchMouseButtonEvent(
-      MouseButtonEventParams(id_, cursor_->GetLocation(), input.code,
-                             input.value, /* allow_remap */ true));
+  dispatcher_->DispatchMouseButtonEvent(MouseButtonEventParams(
+      id_, cursor_->GetLocation(), input.code, input.value,
+      /* allow_remap */ true, TimeDeltaFromInputEvent(input)));
 }
 
-void EventConverterEvdevImpl::FlushEvents() {
+void EventConverterEvdevImpl::FlushEvents(const input_event& input) {
   if (!cursor_ || (x_offset_ == 0 && y_offset_ == 0))
     return;
 
   cursor_->MoveCursor(gfx::Vector2dF(x_offset_, y_offset_));
 
-  dispatcher_->DispatchMouseMoveEvent(
-      MouseMoveEventParams(id_, cursor_->GetLocation()));
+  dispatcher_->DispatchMouseMoveEvent(MouseMoveEventParams(
+      id_, cursor_->GetLocation(), TimeDeltaFromInputEvent(input)));
 
   x_offset_ = 0;
   y_offset_ = 0;
diff --git a/ui/events/ozone/evdev/event_converter_evdev_impl.h b/ui/events/ozone/evdev/event_converter_evdev_impl.h
index eef569e8..b04a07d 100644
--- a/ui/events/ozone/evdev/event_converter_evdev_impl.h
+++ b/ui/events/ozone/evdev/event_converter_evdev_impl.h
@@ -55,7 +55,7 @@
 
   // Flush events delimited by EV_SYN. This is useful for handling
   // non-axis-aligned movement properly.
-  void FlushEvents();
+  void FlushEvents(const input_event& input);
 
   // Input modalities for this device.
   bool has_keyboard_;
diff --git a/ui/events/ozone/evdev/event_factory_evdev.cc b/ui/events/ozone/evdev/event_factory_evdev.cc
index 75620ad..858f4635 100644
--- a/ui/events/ozone/evdev/event_factory_evdev.cc
+++ b/ui/events/ozone/evdev/event_factory_evdev.cc
@@ -12,6 +12,7 @@
 #include "base/trace_event/trace_event.h"
 #include "ui/events/devices/device_data_manager.h"
 #include "ui/events/devices/input_device.h"
+#include "ui/events/event_utils.h"
 #include "ui/events/ozone/device/device_event.h"
 #include "ui/events/ozone/device/device_manager.h"
 #include "ui/events/ozone/evdev/cursor_delegate_evdev.h"
@@ -153,7 +154,7 @@
 void EventFactoryEvdev::DispatchMouseMoveEvent(
     const MouseMoveEventParams& params) {
   MouseEvent event(ui::ET_MOUSE_MOVED, params.location, params.location,
-                   modifiers_.GetModifierFlags(),
+                   params.timestamp, modifiers_.GetModifierFlags(),
                    /* changed_button_flags */ 0);
   event.set_source_device_id(params.device_id);
   DispatchUiEvent(&event);
@@ -185,7 +186,7 @@
   modifiers_.UpdateModifier(modifier, params.down);
 
   MouseEvent event(params.down ? ui::ET_MOUSE_PRESSED : ui::ET_MOUSE_RELEASED,
-                   params.location, params.location,
+                   params.location, params.location, params.timestamp,
                    modifiers_.GetModifierFlags() | flag,
                    /* changed_button_flags */ flag);
   event.set_source_device_id(params.device_id);
@@ -195,7 +196,7 @@
 void EventFactoryEvdev::DispatchMouseWheelEvent(
     const MouseWheelEventParams& params) {
   MouseWheelEvent event(params.delta, params.location, params.location,
-                        modifiers_.GetModifierFlags(),
+                        params.timestamp, modifiers_.GetModifierFlags(),
                         0 /* changed_button_flags */);
   event.set_source_device_id(params.device_id);
   DispatchUiEvent(&event);
@@ -302,7 +303,8 @@
       FROM_HERE, base::Bind(&EventFactoryEvdev::DispatchMouseMoveEvent,
                             weak_ptr_factory_.GetWeakPtr(),
                             MouseMoveEventParams(-1 /* device_id */,
-                                                 cursor_->GetLocation())));
+                                                 cursor_->GetLocation(),
+                                                 EventTimeForNow())));
 }
 
 int EventFactoryEvdev::NextDeviceId() {
diff --git a/ui/events/ozone/evdev/input_injector_evdev.cc b/ui/events/ozone/evdev/input_injector_evdev.cc
index 843a990..fdb8e1f8 100644
--- a/ui/events/ozone/evdev/input_injector_evdev.cc
+++ b/ui/events/ozone/evdev/input_injector_evdev.cc
@@ -45,15 +45,15 @@
       return;
   }
 
-  dispatcher_->DispatchMouseButtonEvent(
-      MouseButtonEventParams(kDeviceIdForInjection, cursor_->GetLocation(),
-                             code, down, false /* allow_remap */));
+  dispatcher_->DispatchMouseButtonEvent(MouseButtonEventParams(
+      kDeviceIdForInjection, cursor_->GetLocation(), code, down,
+      false /* allow_remap */, EventTimeForNow()));
 }
 
 void InputInjectorEvdev::InjectMouseWheel(int delta_x, int delta_y) {
-  dispatcher_->DispatchMouseWheelEvent(
-      MouseWheelEventParams(kDeviceIdForInjection, cursor_->GetLocation(),
-                            gfx::Vector2d(delta_x, delta_y)));
+  dispatcher_->DispatchMouseWheelEvent(MouseWheelEventParams(
+      kDeviceIdForInjection, cursor_->GetLocation(),
+      gfx::Vector2d(delta_x, delta_y), EventTimeForNow()));
 }
 
 void InputInjectorEvdev::MoveCursorTo(const gfx::PointF& location) {
@@ -62,8 +62,8 @@
 
   cursor_->MoveCursorTo(location);
 
-  dispatcher_->DispatchMouseMoveEvent(
-      MouseMoveEventParams(kDeviceIdForInjection, cursor_->GetLocation()));
+  dispatcher_->DispatchMouseMoveEvent(MouseMoveEventParams(
+      kDeviceIdForInjection, cursor_->GetLocation(), EventTimeForNow()));
 }
 
 void InputInjectorEvdev::InjectKeyPress(DomCode physical_key, bool down) {
diff --git a/ui/events/ozone/evdev/libgestures_glue/gesture_interpreter_libevdev_cros.cc b/ui/events/ozone/evdev/libgestures_glue/gesture_interpreter_libevdev_cros.cc
index 143f4ce..58b38a0a 100644
--- a/ui/events/ozone/evdev/libgestures_glue/gesture_interpreter_libevdev_cros.cc
+++ b/ui/events/ozone/evdev/libgestures_glue/gesture_interpreter_libevdev_cros.cc
@@ -274,9 +274,8 @@
 
   cursor_->MoveCursor(gfx::Vector2dF(move->dx, move->dy));
   // TODO(spang): Use move->ordinal_dx, move->ordinal_dy
-  // TODO(spang): Use move->start_time, move->end_time
-  dispatcher_->DispatchMouseMoveEvent(
-      MouseMoveEventParams(id_, cursor_->GetLocation()));
+  dispatcher_->DispatchMouseMoveEvent(MouseMoveEventParams(
+      id_, cursor_->GetLocation(), StimeToTimedelta(gesture->end_time)));
 }
 
 void GestureInterpreterLibevdevCros::OnGestureScroll(
@@ -290,10 +289,10 @@
   if (!cursor_)
     return;  // No cursor!
 
-  // TODO(spang): Use scroll->start_time
   if (is_mouse_) {
     dispatcher_->DispatchMouseWheelEvent(MouseWheelEventParams(
-        id_, cursor_->GetLocation(), gfx::Vector2d(scroll->dx, scroll->dy)));
+        id_, cursor_->GetLocation(), gfx::Vector2d(scroll->dx, scroll->dy),
+        StimeToTimedelta(gesture->end_time)));
   } else {
     dispatcher_->DispatchScrollEvent(ScrollEventParams(
         id_, ET_SCROLL, cursor_->GetLocation(),
@@ -313,19 +312,18 @@
   if (!cursor_)
     return;  // No cursor!
 
-  // TODO(spang): Use buttons->start_time, buttons->end_time
   if (buttons->down & GESTURES_BUTTON_LEFT)
-    DispatchMouseButton(BTN_LEFT, true);
+    DispatchMouseButton(BTN_LEFT, true, gesture->end_time);
   if (buttons->down & GESTURES_BUTTON_MIDDLE)
-    DispatchMouseButton(BTN_MIDDLE, true);
+    DispatchMouseButton(BTN_MIDDLE, true, gesture->end_time);
   if (buttons->down & GESTURES_BUTTON_RIGHT)
-    DispatchMouseButton(BTN_RIGHT, true);
+    DispatchMouseButton(BTN_RIGHT, true, gesture->end_time);
   if (buttons->up & GESTURES_BUTTON_LEFT)
-    DispatchMouseButton(BTN_LEFT, false);
+    DispatchMouseButton(BTN_LEFT, false, gesture->end_time);
   if (buttons->up & GESTURES_BUTTON_MIDDLE)
-    DispatchMouseButton(BTN_MIDDLE, false);
+    DispatchMouseButton(BTN_MIDDLE, false, gesture->end_time);
   if (buttons->up & GESTURES_BUTTON_RIGHT)
-    DispatchMouseButton(BTN_RIGHT, false);
+    DispatchMouseButton(BTN_RIGHT, false, gesture->end_time);
 }
 
 void GestureInterpreterLibevdevCros::OnGestureContactInitiated(
@@ -415,10 +413,12 @@
 }
 
 void GestureInterpreterLibevdevCros::DispatchMouseButton(unsigned int button,
-                                                         bool down) {
+                                                         bool down,
+                                                         stime_t time) {
   bool allow_remap = is_mouse_;
-  dispatcher_->DispatchMouseButtonEvent(MouseButtonEventParams(
-      id_, cursor_->GetLocation(), button, down, allow_remap));
+  dispatcher_->DispatchMouseButtonEvent(
+      MouseButtonEventParams(id_, cursor_->GetLocation(), button, down,
+                             allow_remap, StimeToTimedelta(time)));
 }
 
 void GestureInterpreterLibevdevCros::DispatchChangedKeys(Evdev* evdev,
diff --git a/ui/events/ozone/evdev/libgestures_glue/gesture_interpreter_libevdev_cros.h b/ui/events/ozone/evdev/libgestures_glue/gesture_interpreter_libevdev_cros.h
index 1300ec4..6b0276a 100644
--- a/ui/events/ozone/evdev/libgestures_glue/gesture_interpreter_libevdev_cros.h
+++ b/ui/events/ozone/evdev/libgestures_glue/gesture_interpreter_libevdev_cros.h
@@ -76,7 +76,9 @@
   void OnGesturePinch(const Gesture* gesture, const GesturePinch* pinch);
   void OnGestureMetrics(const Gesture* gesture, const GestureMetrics* metrics);
 
-  void DispatchMouseButton(unsigned int modifier, bool down);
+  void DispatchMouseButton(unsigned int modifier,
+                           bool down,
+                           stime_t time);
   void DispatchChangedKeys(Evdev* evdev, const timeval& time);
 
   // The unique device id.
diff --git a/ui/events/ozone/evdev/tablet_event_converter_evdev.cc b/ui/events/ozone/evdev/tablet_event_converter_evdev.cc
index 4978876..24b01fcf 100644
--- a/ui/events/ozone/evdev/tablet_event_converter_evdev.cc
+++ b/ui/events/ozone/evdev/tablet_event_converter_evdev.cc
@@ -68,7 +68,7 @@
         ConvertAbsEvent(input);
         break;
       case EV_SYN:
-        FlushEvents();
+        FlushEvents(input);
         break;
     }
   }
@@ -145,10 +145,11 @@
   bool down = input.value;
 
   dispatcher_->DispatchMouseButtonEvent(MouseButtonEventParams(
-      id_, cursor_->GetLocation(), button, down, false /* allow_remap */));
+      id_, cursor_->GetLocation(), button, down, false /* allow_remap */,
+      TimeDeltaFromInputEvent(input)));
 }
 
-void TabletEventConverterEvdev::FlushEvents() {
+void TabletEventConverterEvdev::FlushEvents(const input_event& input) {
   if (!cursor_)
     return;
 
@@ -163,8 +164,8 @@
 
   UpdateCursor();
 
-  dispatcher_->DispatchMouseMoveEvent(
-      MouseMoveEventParams(id_, cursor_->GetLocation()));
+  dispatcher_->DispatchMouseMoveEvent(MouseMoveEventParams(
+      id_, cursor_->GetLocation(), TimeDeltaFromInputEvent(input)));
 
   abs_value_dirty_ = false;
 }
diff --git a/ui/events/ozone/evdev/tablet_event_converter_evdev.h b/ui/events/ozone/evdev/tablet_event_converter_evdev.h
index 02ca08e..1c3c930 100644
--- a/ui/events/ozone/evdev/tablet_event_converter_evdev.h
+++ b/ui/events/ozone/evdev/tablet_event_converter_evdev.h
@@ -46,7 +46,7 @@
 
   // Flush events delimited by EV_SYN. This is useful for handling
   // non-axis-aligned movement properly.
-  void FlushEvents();
+  void FlushEvents(const input_event& input);
 
   // Controller for watching the input fd.
   base::MessagePumpLibevent::FileDescriptorWatcher controller_;
diff --git a/ui/events/platform/x11/x11_event_source_glib.cc b/ui/events/platform/x11/x11_event_source_glib.cc
index f9f938d..f616cdd 100644
--- a/ui/events/platform/x11/x11_event_source_glib.cc
+++ b/ui/events/platform/x11/x11_event_source_glib.cc
@@ -94,8 +94,7 @@
 }  // namespace
 
 scoped_ptr<PlatformEventSource> PlatformEventSource::CreateDefault() {
-  return scoped_ptr<PlatformEventSource>(
-      new X11EventSourceGlib(gfx::GetXDisplay()));
+  return make_scoped_ptr(new X11EventSourceGlib(gfx::GetXDisplay()));
 }
 
 }  // namespace ui
diff --git a/ui/events/platform/x11/x11_event_source_libevent.cc b/ui/events/platform/x11/x11_event_source_libevent.cc
index 0df1977..ff01d2e 100644
--- a/ui/events/platform/x11/x11_event_source_libevent.cc
+++ b/ui/events/platform/x11/x11_event_source_libevent.cc
@@ -61,8 +61,7 @@
 }  // namespace
 
 scoped_ptr<PlatformEventSource> PlatformEventSource::CreateDefault() {
-  return scoped_ptr<PlatformEventSource>(
-      new X11EventSourceLibevent(gfx::GetXDisplay()));
+  return make_scoped_ptr(new X11EventSourceLibevent(gfx::GetXDisplay()));
 }
 
 }  // namespace ui
diff --git a/ui/events/platform/x11/x11_hotplug_event_handler.cc b/ui/events/platform/x11/x11_hotplug_event_handler.cc
index 5efe25d..ddd3aec 100644
--- a/ui/events/platform/x11/x11_hotplug_event_handler.cc
+++ b/ui/events/platform/x11/x11_hotplug_event_handler.cc
@@ -245,8 +245,9 @@
 
   std::set<int> no_match_touchscreen;
   for (const DeviceInfo& device_info : device_infos) {
-    if (!device_info.enabled || device_info.use != XIFloatingSlave)
-      continue;  // Assume all touchscreens are floating slaves
+    if (!device_info.enabled || (device_info.use != XIFloatingSlave
+        && device_info.use != XISlavePointer))
+      continue;
 
     double max_x = -1.0;
     double max_y = -1.0;
diff --git a/ui/events/test/event_generator.cc b/ui/events/test/event_generator.cc
index 9142d5c..f743ba1 100644
--- a/ui/events/test/event_generator.cc
+++ b/ui/events/test/event_generator.cc
@@ -144,7 +144,8 @@
 
 void EventGenerator::MoveMouseWheel(int delta_x, int delta_y) {
   gfx::Point location = GetLocationInCurrentRoot();
-  ui::MouseEvent mouseev(ui::ET_MOUSEWHEEL, location, location, flags_, 0);
+  ui::MouseEvent mouseev(ui::ET_MOUSEWHEEL, location, location,
+                         ui::EventTimeForNow(), flags_, 0);
   ui::MouseWheelEvent wheelev(mouseev, delta_x, delta_y);
   Dispatch(&wheelev);
 }
@@ -153,14 +154,15 @@
   gfx::Point exit_location(current_location_);
   delegate()->ConvertPointToTarget(current_target_, &exit_location);
   ui::MouseEvent mouseev(ui::ET_MOUSE_EXITED, exit_location, exit_location,
-                         flags_, 0);
+                         ui::EventTimeForNow(), flags_, 0);
   Dispatch(&mouseev);
 }
 
 void EventGenerator::MoveMouseToInHost(const gfx::Point& point_in_host) {
   const ui::EventType event_type = (flags_ & ui::EF_LEFT_MOUSE_BUTTON) ?
       ui::ET_MOUSE_DRAGGED : ui::ET_MOUSE_MOVED;
-  ui::MouseEvent mouseev(event_type, point_in_host, point_in_host, flags_, 0);
+  ui::MouseEvent mouseev(event_type, point_in_host, point_in_host,
+                         ui::EventTimeForNow(), flags_, 0);
   Dispatch(&mouseev);
 
   current_location_ = point_in_host;
@@ -181,7 +183,8 @@
     if (!grab_)
       UpdateCurrentDispatcher(move_point);
     delegate()->ConvertPointToTarget(current_target_, &move_point);
-    ui::MouseEvent mouseev(event_type, move_point, move_point, flags_, 0);
+    ui::MouseEvent mouseev(event_type, move_point, move_point,
+                           ui::EventTimeForNow(), flags_, 0);
     Dispatch(&mouseev);
   }
   current_location_ = point_in_screen;
@@ -557,8 +560,8 @@
     flags_ |= flag;
     grab_ = (flags_ & kAllButtonMask) != 0;
     gfx::Point location = GetLocationInCurrentRoot();
-    ui::MouseEvent mouseev(ui::ET_MOUSE_PRESSED, location, location, flags_,
-                           flag);
+    ui::MouseEvent mouseev(ui::ET_MOUSE_PRESSED, location, location,
+                           ui::EventTimeForNow(), flags_, flag);
     Dispatch(&mouseev);
   }
 }
@@ -566,8 +569,8 @@
 void EventGenerator::ReleaseButton(int flag) {
   if (flags_ & flag) {
     gfx::Point location = GetLocationInCurrentRoot();
-    ui::MouseEvent mouseev(ui::ET_MOUSE_RELEASED, location,
-                           location, flags_, flag);
+    ui::MouseEvent mouseev(ui::ET_MOUSE_RELEASED, location, location,
+                           ui::EventTimeForNow(), flags_, flag);
     Dispatch(&mouseev);
     flags_ ^= flag;
   }
diff --git a/ui/events/test/test_event_target.cc b/ui/events/test/test_event_target.cc
index dc0e80d..8ea7438d 100644
--- a/ui/events/test/test_event_target.cc
+++ b/ui/events/test/test_event_target.cc
@@ -35,7 +35,7 @@
   if (iter != children_.end()) {
     children_.weak_erase(iter);
     c->set_parent(NULL);
-    return scoped_ptr<TestEventTarget>(c);
+    return make_scoped_ptr(c);
   }
   return nullptr;
 }
@@ -64,7 +64,7 @@
 }
 
 scoped_ptr<EventTargetIterator> TestEventTarget::GetChildIterator() const {
-  return scoped_ptr<EventTargetIterator>(
+  return make_scoped_ptr(
       new EventTargetIteratorImpl<TestEventTarget>(children_.get()));
 }
 
diff --git a/ui/file_manager/audio_player/audio_player.html b/ui/file_manager/audio_player/audio_player.html
index 80971cf7..3d95596 100644
--- a/ui/file_manager/audio_player/audio_player.html
+++ b/ui/file_manager/audio_player/audio_player.html
@@ -24,19 +24,30 @@
     <!-- Keep the list in sync with audio_player_scripts.js. -->
     <script src="../../webui//resources/js/cr.js"></script>
     <script src="../../webui/resources/js/cr/event_target.js"></script>
-    <script src="../../webui/resources/js/cr/ui/array_data_model.js">
-    </script>
+    <script src="../../webui/resources/js/cr/ui/array_data_model.js"></script>
 
     <script src="../../../third_party/polymer/polymer/polymer.js"></script>
 
+    <!-- Base classes. -->
+    <script src="../file_manager/foreground/js/metadata/metadata_cache_set.js"></script>
+    <script src="../file_manager/foreground/js/metadata/new_metadata_provider.js"></script>
+
     <script src="../file_manager/common/js/async_util.js"></script>
     <script src="../file_manager/common/js/file_type.js"></script>
     <script src="../file_manager/common/js/util.js"></script>
     <script src="../file_manager/common/js/volume_manager_common.js"></script>
     <script src="../file_manager/foreground/js/volume_manager_wrapper.js"></script>
-    <script src="../file_manager/foreground/js/metadata/metadata_cache.js"></script>
+
+    <script src="../file_manager/foreground/js/metadata/content_metadata_provider.js"></script>
+    <script src="../file_manager/foreground/js/metadata/external_metadata_provider.js"></script>
+    <script src="../file_manager/foreground/js/metadata/file_system_metadata.js"></script>
+    <script src="../file_manager/foreground/js/metadata/file_system_metadata_provider.js"></script>
+    <script src="../file_manager/foreground/js/metadata/metadata_cache_item.js"></script>
+    <script src="../file_manager/foreground/js/metadata/metadata_item.js"></script>
+    <script src="../file_manager/foreground/js/metadata/thumbnail_model.js"></script>
 
     <script src="js/audio_player.js"></script>
+    <script src="js/audio_player_model.js"></script>
 
     <script src="elements/track_list.js"></script>
     <script src="elements/control_panel.js"></script>
diff --git a/ui/file_manager/audio_player/js/audio_player.js b/ui/file_manager/audio_player/js/audio_player.js
index a10c93b5..f5e172d 100644
--- a/ui/file_manager/audio_player/js/audio_player.js
+++ b/ui/file_manager/audio_player/js/audio_player.js
@@ -7,7 +7,7 @@
  * @type {string}
  * @const
  */
-ContentProvider.WORKER_SCRIPT = '/js/metadata_worker.js';
+ContentMetadataProvider.WORKER_SCRIPT = '/js/metadata_worker.js';
 
 /**
  * @param {HTMLElement} container Container element.
@@ -17,7 +17,8 @@
   this.container_ = container;
   this.volumeManager_ = new VolumeManagerWrapper(
       VolumeManagerWrapper.DriveEnabledStatus.DRIVE_ENABLED);
-  this.metadataCache_ = MetadataCache.createFull(this.volumeManager_);
+  this.fileSystemMetadata_ = FileSystemMetadata.create(
+      new MetadataProviderCache(), this.volumeManager_);
   this.selectedEntry_ = null;
 
   this.model_ = new AudioPlayerModel();
@@ -72,7 +73,7 @@
  * Initial load method (static).
  */
 AudioPlayer.load = function() {
-  document.ondragstart = function(e) { e.preventDefault() };
+  document.ondragstart = function(e) { e.preventDefault(); };
 
   AudioPlayer.instance =
       new AudioPlayer(document.querySelector('.audio-player'));
@@ -237,11 +238,12 @@
  * @private
  */
 AudioPlayer.prototype.fetchMetadata_ = function(entry, callback) {
-  this.metadataCache_.getOne(entry, 'thumbnail|media|external',
+  this.fileSystemMetadata_.get(
+      [entry], ['mediaTitle', 'mediaArtist', 'present']).then(
       function(generation, metadata) {
         // Do nothing if another load happened since the metadata request.
         if (this.playlistGeneration_ == generation)
-          callback(metadata);
+          callback(metadata[0]);
       }.bind(this, this.playlistGeneration_));
 };
 
@@ -257,7 +259,7 @@
   this.fetchMetadata_(
       this.entries_[track],
       function(metadata) {
-        var error = (!navigator.onLine && !metadata.external.present) ?
+        var error = (!navigator.onLine && !metadata.present) ?
             this.offlineString_ : this.errorString_;
         this.displayMetadata_(track, metadata, error);
         this.scheduleAutoAdvance_();
@@ -389,7 +391,7 @@
 /**
  * @return {HTMLDivElement} The wrapper element for the track.
  */
-AudioPlayer.TrackInfo.prototype.getBox = function() { return this.box_ };
+AudioPlayer.TrackInfo.prototype.getBox = function() { return this.box_; };
 
 /**
  * @return {string} Default track title (file name extracted from the url).
@@ -422,10 +424,8 @@
     metadata, error) {
   // TODO(yoshiki): Handle error in better way.
   // TODO(yoshiki): implement artwork (metadata.thumbnail)
-  this.title = (metadata.media && metadata.media.title) ||
-      this.getDefaultTitle();
-  this.artist = error ||
-      (metadata.media && metadata.media.artist) || this.getDefaultArtist();
+  this.title = metadata.mediaTitle || this.getDefaultTitle();
+  this.artist = error || metadata.mediaArtist || this.getDefaultArtist();
 };
 
 // Starts loading the audio player.
diff --git a/ui/file_manager/audio_player/js/audio_player_scripts.js b/ui/file_manager/audio_player/js/audio_player_scripts.js
index 2da94ba4..a4d5a978 100644
--- a/ui/file_manager/audio_player/js/audio_player_scripts.js
+++ b/ui/file_manager/audio_player/js/audio_player_scripts.js
@@ -23,12 +23,23 @@
 // 'strict mode' is invoked for this scope.
 'use strict';
 
+// Base classes.
+<include src="../../file_manager/foreground/js/metadata/metadata_cache_set.js">
+<include src="../../file_manager/foreground/js/metadata/new_metadata_provider.js">
+
 <include src="../../file_manager/common/js/async_util.js"/>
 <include src="../../file_manager/common/js/file_type.js"/>
 <include src="../../file_manager/common/js/util.js"/>
 <include src="../../file_manager/common/js/volume_manager_common.js"/>
 <include src="../../file_manager/foreground/js/volume_manager_wrapper.js">
-<include src="../../file_manager/foreground/js/metadata/metadata_cache.js"/>
+
+<include src="../../file_manager/foreground/js/metadata/content_metadata_provider.js">
+<include src="../../file_manager/foreground/js/metadata/external_metadata_provider.js">
+<include src="../../file_manager/foreground/js/metadata/file_system_metadata.js">
+<include src="../../file_manager/foreground/js/metadata/file_system_metadata_provider.js">
+<include src="../../file_manager/foreground/js/metadata/metadata_cache_item.js">
+<include src="../../file_manager/foreground/js/metadata/metadata_item.js">
+<include src="../../file_manager/foreground/js/metadata/thumbnail_model.js">
 
 <include src="audio_player.js"/>
 <include src="audio_player_model.js"/>
diff --git a/ui/file_manager/externs/gallery_background.js b/ui/file_manager/externs/gallery_background.js
index 79fc857..099e8b4 100644
--- a/ui/file_manager/externs/gallery_background.js
+++ b/ui/file_manager/externs/gallery_background.js
@@ -11,14 +11,9 @@
 function GalleryWindow() {}
 
 /**
- * @param {!BackgroundComponents} backgroundComponents Background components.
+ * @type {Promise}
  */
-window.initialize = function(backgroundComponents) {};
-
-/**
- * @param {!Array.<!Entry>} entries Array of entries.
- */
-window.loadEntries = function(entries) {};
+window.initializePromise;
 
 /**
  * This definition is required by
diff --git a/ui/file_manager/file_manager/background/js/app_window_wrapper.js b/ui/file_manager/file_manager/background/js/app_window_wrapper.js
index de19acf..d3dc479 100644
--- a/ui/file_manager/file_manager/background/js/app_window_wrapper.js
+++ b/ui/file_manager/file_manager/background/js/app_window_wrapper.js
@@ -23,7 +23,8 @@
   this.url_ = url;
   this.id_ = id;
   // Do deep copy for the template of options to assign customized params later.
-  this.options_ = JSON.parse(JSON.stringify(options));
+  this.options_ = /** @type chrome.app.window.CreateWindowOptions */(
+      JSON.parse(JSON.stringify(options)));
   this.window_ = null;
   this.appState_ = null;
   this.openingOrOpened_ = false;
@@ -224,8 +225,6 @@
 
   // Remove the window from the set.
   delete window.background.appWindows[this.id_];
-
-  window.background.tryClose();
 };
 
 /**
@@ -306,7 +305,7 @@
     }
 
     try {
-      var appState = JSON.parse(value);
+      var appState = assertInstanceof(JSON.parse(value), Object);
     } catch (e) {
       console.error('Corrupt launch data for ' + this.id_, value);
       opt_callback && opt_callback();
diff --git a/ui/file_manager/file_manager/background/js/background.js b/ui/file_manager/file_manager/background/js/background.js
index f520ba4..b5c4144 100644
--- a/ui/file_manager/file_manager/background/js/background.js
+++ b/ui/file_manager/file_manager/background/js/background.js
@@ -21,6 +21,9 @@
 function FileBrowserBackground() {
   BackgroundBase.call(this);
 
+  /** @type {!analytics.Tracker} */
+  this.tracker = metrics.getTracker();
+
   /**
    * Synchronous queue for asynchronous calls.
    * @type {!AsyncUtil.Queue}
@@ -69,9 +72,6 @@
    * @type {!DriveSyncHandler}
    */
   this.driveSyncHandler = new DriveSyncHandler(this.progressCenter);
-  this.driveSyncHandler.addEventListener(
-      DriveSyncHandler.COMPLETED_EVENT,
-      function() { this.tryClose(); }.bind(this));
 
   /**
    * Provides support for scaning media devices as part of Cloud Import.
@@ -91,7 +91,8 @@
       new importer.MediaImportHandler(
           this.progressCenter,
           this.historyLoader,
-          new importer.DriveDuplicateFinder());
+          new importer.DriveDuplicateFinder.Factory(),
+          this.tracker);
 
   /**
    * Promise of string data.
@@ -105,17 +106,12 @@
    */
   this.stringData = null;
 
-  /**
-   * Last time when the background page can close.
-   *
-   * @private {?number}
-   */
-  this.lastTimeCanClose_ = null;
-
   // Initialize handlers.
   chrome.fileBrowserHandler.onExecute.addListener(this.onExecute_.bind(this));
   chrome.app.runtime.onLaunched.addListener(this.onLaunched_.bind(this));
   chrome.app.runtime.onRestarted.addListener(this.onRestarted_.bind(this));
+  chrome.runtime.onMessageExternal.addListener(
+      this.onExternalMessageReceived_.bind(this));
   chrome.contextMenus.onClicked.addListener(
       this.onContextMenuClicked_.bind(this));
 
@@ -129,14 +125,6 @@
   }.bind(this));
 }
 
-/**
- * A number of delay milliseconds from the first call of tryClose to the actual
- * close action.
- * @const {number}
- * @private
- */
-FileBrowserBackground.CLOSE_DELAY_MS_ = 5000;
-
 FileBrowserBackground.prototype.__proto__ = BackgroundBase.prototype;
 
 /**
@@ -175,47 +163,6 @@
 };
 
 /**
- * Checks the current condition of background page.
- * @return {boolean} True if the background page is closable, false if not.
- */
-FileBrowserBackground.prototype.canClose = function() {
-  // If the file operation is going, the background page cannot close.
-  if ((this.fileOperationManager &&
-       this.fileOperationManager.hasQueuedTasks()) ||
-      this.driveSyncHandler.syncing) {
-    this.lastTimeCanClose_ = null;
-    return false;
-  }
-
-  var views = chrome.extension.getViews();
-  var closing = false;
-  for (var i = 0; i < views.length; i++) {
-    // If the window that is not the background page itself and it is not
-    // closing, the background page cannot close.
-    if (views[i] !== window && !views[i].closing) {
-      this.lastTimeCanClose_ = null;
-      return false;
-    }
-    closing = closing || views[i].closing;
-  }
-
-  // If some windows are closing, or the background page can close but could not
-  // 5 seconds ago, We need more time for sure.
-  if (closing ||
-      this.lastTimeCanClose_ === null ||
-      (Date.now() - this.lastTimeCanClose_ <
-           FileBrowserBackground.CLOSE_DELAY_MS_)) {
-    if (this.lastTimeCanClose_ === null)
-      this.lastTimeCanClose_ = Date.now();
-    setTimeout(this.tryClose.bind(this), FileBrowserBackground.CLOSE_DELAY_MS_);
-    return false;
-  }
-
-  // Otherwise we can close the background page.
-  return true;
-};
-
-/**
  * Opens the volume root (or opt directoryPath) in main UI.
  *
  * @param {!Event} event An event with the volumeId or
@@ -241,7 +188,7 @@
                 console.error('Got view event with invalid volume id.');
               }
             } else if (event.volumeId) {
-              this.navigateToVolumeWhenReady_(event.volumeId);
+              this.navigateToVolumeWhenReady_(event.volumeId, event.filePath);
             } else {
               console.error('Got view event with no actionable destination.');
             }
@@ -545,6 +492,22 @@
   launchFileManager(null, undefined, LaunchType.FOCUS_ANY_OR_CREATE);
 };
 
+/** @const {string} */
+var GPLUS_PHOTOS_APP_ID = 'efjnaogkjbogokcnohkmnjdojkikgobo';
+
+/**
+ * Handles a message received via chrome.runtime.sendMessageExternal.
+ *
+ * @param {*} message
+ * @param {MessageSender} sender
+ */
+FileBrowserBackground.prototype.onExternalMessageReceived_ =
+    function(message, sender) {
+  if ('id' in sender && sender.id === GPLUS_PHOTOS_APP_ID) {
+    importer.handlePhotosAppMessage(message);
+  }
+};
+
 /**
  * Restarted the app, restore windows.
  * @private
diff --git a/ui/file_manager/file_manager/background/js/background_base.js b/ui/file_manager/file_manager/background/js/background_base.js
index 6815a77..340cbb6 100644
--- a/ui/file_manager/file_manager/background/js/background_base.js
+++ b/ui/file_manager/file_manager/background/js/background_base.js
@@ -22,22 +22,6 @@
 }
 
 /**
- * Checks the current condition of background page.
- * @return {boolean} True if the background page can be closed. False if not.
- */
-BackgroundBase.prototype.canClose = function() {
-  return true;
-};
-
-/**
- * Checks the current condition of background page and closes it if possible.
- */
-BackgroundBase.prototype.tryClose = function() {
-  if (this.canClose())
-    window.close();
-};
-
-/**
  * Gets similar windows, it means with the same initial url.
  * @param {string} url URL that the obtained windows have.
  * @return {Array.<chrome.app.window.AppWindow>} List of similar windows.
diff --git a/ui/file_manager/file_manager/background/js/compiled_resources.gyp b/ui/file_manager/file_manager/background/js/compiled_resources.gyp
index 17f2974..7134604 100644
--- a/ui/file_manager/file_manager/background/js/compiled_resources.gyp
+++ b/ui/file_manager/file_manager/background/js/compiled_resources.gyp
@@ -19,6 +19,9 @@
           '../../common/js/error_util.js',
           '../../common/js/async_util.js',
           '../../common/js/file_type.js',
+          '../../common/js/metrics_base.js',
+          '../../common/js/metrics_events.js',
+          '../../common/js/metrics.js',
           '../../common/js/progress_center_common.js',
           '../../common/js/util.js',
           '../../common/js/volume_manager_common.js',
diff --git a/ui/file_manager/file_manager/background/js/device_handler.js b/ui/file_manager/file_manager/background/js/device_handler.js
index f5e03795..5bd92016 100644
--- a/ui/file_manager/file_manager/background/js/device_handler.js
+++ b/ui/file_manager/file_manager/background/js/device_handler.js
@@ -407,7 +407,6 @@
   // If this is remounting, which happens when resuming Chrome OS, the device
   // has already inserted to the computer. So we suppress the notification.
   var metadata = event.volumeMetadata;
-
   VolumeManager.getInstance()
       .then(
           /**
@@ -461,21 +460,47 @@
           })
       .then(
           /**
-           * @param {!Array.<DirectoryEntry>} results, where index 0 is for
+           * @param {!Array.<DirectoryEntry>} results where index 0 is for
            *     'DCIM' and 1 is for 'dcim'.
+           * @this {DeviceHandler}
            */
           function(results) {
             if (!!results[0] && results[0].isDirectory) {
-              // It's a "DCIM"!
-              this.openMediaDirectory_(metadata.volumeId, results[0].fullPath);
-              return Promise.resolve();
-            } else if(!!results[1] && results[1].isDirectory) {
-              // It's a "dcim"!
-              this.openMediaDirectory_(metadata.volumeId, results[1].fullPath);
-              return Promise.resolve();
+              return results[0];  // It's a "DCIM"!
+            } else if (!!results[1] && results[1].isDirectory) {
+              return results[1];  // It's a "dcim"!
             }
             return Promise.reject('Unable to local DCIM or dcim directory.');
           }.bind(this))
+      .then(
+          /**
+           * @param {!DirectoryEntry} directory
+           * @this {DeviceHandler}
+           */
+          function(directory) {
+            return importer.isPhotosAppImportEnabled()
+                .then(
+                    /**
+                     * @param {boolean} appEnabled
+                     * @this {DeviceHandler}
+                     */
+                    function(appEnabled) {
+                      // We don't want to auto-open two windows
+                      // when a user inserts a removable device.
+                      // If Photos App is enabled only show a notification.
+                      if (appEnabled) {
+                        if (metadata.deviceType && metadata.devicePath) {
+                          DeviceHandler.Notification.DEVICE_IMPORT.show(
+                              /** @type {string} */ (
+                                  metadata.devicePath));
+                        }
+                      } else {
+                        this.openMediaDirectory_(
+                            metadata.volumeId,
+                            directory.fullPath);
+                      }
+                    }.bind(this));
+          }.bind(this))
       .catch(
         function(error) {
           if (metadata.deviceType && metadata.devicePath) {
diff --git a/ui/file_manager/file_manager/background/js/device_handler_unittest.js b/ui/file_manager/file_manager/background/js/device_handler_unittest.js
index f1cba78..a5266085 100644
--- a/ui/file_manager/file_manager/background/js/device_handler_unittest.js
+++ b/ui/file_manager/file_manager/background/js/device_handler_unittest.js
@@ -27,10 +27,13 @@
   REMOVABLE_DEVICE_NAVIGATION_BUTTON_LABEL: '',
   REMOVABLE_DEVICE_IMPORT_MESSAGE: 'DEVICE_IMPORT',
   REMOVABLE_DEVICE_IMPORT_BUTTON_LABEL: '',
+  DEVICE_UNKNOWN_BUTTON_LABEL: 'DEVICE_UNKNOWN_BUTTON_LABEL',
   DEVICE_UNKNOWN_MESSAGE: 'DEVICE_UNKNOWN: $1',
   DEVICE_UNSUPPORTED_MESSAGE: 'DEVICE_UNSUPPORTED: $1',
   DEVICE_HARD_UNPLUGGED_TITLE: 'DEVICE_HARD_UNPLUGGED_TITLE',
   DEVICE_HARD_UNPLUGGED_MESSAGE: 'DEVICE_HARD_UNPLUGGED_MESSAGE',
+  DOWNLOADS_DIRECTORY_LABEL: 'DOWNLOADS_DIRECTORY_LABEL',
+  DRIVE_DIRECTORY_LABEL: 'DRIVE_DIRECTORY_LABEL',
   MULTIPART_DEVICE_UNSUPPORTED_MESSAGE: 'MULTIPART_DEVICE_UNSUPPORTED: $1',
   EXTERNAL_STORAGE_DISABLED_MESSAGE: 'EXTERNAL_STORAGE_DISABLED',
   FORMATTING_OF_DEVICE_PENDING_TITLE: 'FORMATTING_OF_DEVICE_PENDING_TITLE',
@@ -76,6 +79,8 @@
 }
 
 function testRemovableMediaDeviceWithImportEnabled(callback) {
+  var storage = new MockChromeStorageAPI();
+
   setupFileSystem(
       VolumeManagerCommon.VolumeType.REMOVABLE,
       'blabbity',
@@ -111,6 +116,8 @@
 }
 
 function testMtpMediaDeviceWithImportEnabled(callback) {
+  var storage = new MockChromeStorageAPI();
+
   setupFileSystem(
       VolumeManagerCommon.VolumeType.MTP,
       'blabbity',
@@ -145,6 +152,49 @@
   reportPromise(resolver.promise, callback);
 }
 
+function testMediaDeviceWithImportEnabledAndPhotosAppImportEnabled(callback) {
+  var storage = new MockChromeStorageAPI();
+  chrome.commandLinePrivate.cloudImportDisabled = false;
+
+  setupFileSystem(
+      VolumeManagerCommon.VolumeType.REMOVABLE,
+      'blabbity',
+      [
+        '/DCIM/',
+        '/DCIM/grandma.jpg'
+      ]);
+
+  var promise = importer.handlePhotosAppMessage(true)
+      .then(
+          function() {
+            chrome.fileManagerPrivate.onMountCompleted.dispatch({
+              eventType: 'mount',
+              status: 'success',
+              volumeMetadata: {
+                volumeId: 'blabbity',
+                isParentDevice: true,
+                deviceType: 'usb',
+                devicePath: '/device/path',
+                deviceLabel: 'label',
+                hasMedia: true
+              },
+              shouldNotify: true
+            });
+
+            return chrome.notifications.resolver.promise.then(
+                function(notifications) {
+                  assertEquals(1, Object.keys(notifications).length);
+                  assertEquals(
+                      'DEVICE_IMPORT',
+                      notifications[
+                          'deviceImport:/device/path'].message,
+                      'Device notification did not have the right message.');
+                });
+          });
+
+  reportPromise(promise, callback);
+}
+
 function testMediaDeviceWithImportDisabled(callback) {
   chrome.commandLinePrivate.cloudImportDisabled = true;
 
@@ -164,9 +214,6 @@
   var promise = chrome.notifications.resolver.promise.then(
       function(notifications) {
         assertEquals(1, Object.keys(notifications).length);
-        assertFalse(
-            'deviceImport:/device/path' in notifications,
-            'Unexpected import notification found in notifications queue.');
         assertEquals(
             'DEVICE_NAVIGATION',
             notifications[
diff --git a/ui/file_manager/file_manager/background/js/drive_sync_handler.js b/ui/file_manager/file_manager/background/js/drive_sync_handler.js
index 2c413d8f..a89a559f 100644
--- a/ui/file_manager/file_manager/background/js/drive_sync_handler.js
+++ b/ui/file_manager/file_manager/background/js/drive_sync_handler.js
@@ -174,8 +174,8 @@
       else
         this.item_.message = strf('SYNC_FILE_NAME', entry.name);
       this.item_.cancelCallback = this.requestCancel_.bind(this, entry);
-      this.item_.progressValue = status.processed;
-      this.item_.progressMax = status.total;
+      this.item_.progressValue = status.processed || 0;
+      this.item_.progressMax = status.total || 0;
       this.progressCenter_.updateItem(this.item_);
       callback();
     }.bind(this), function(error) {
diff --git a/ui/file_manager/file_manager/background/js/duplicate_finder.js b/ui/file_manager/file_manager/background/js/duplicate_finder.js
index 25ac1574..ce797e6 100644
--- a/ui/file_manager/file_manager/background/js/duplicate_finder.js
+++ b/ui/file_manager/file_manager/background/js/duplicate_finder.js
@@ -21,7 +21,17 @@
 importer.DuplicateFinder.prototype.checkDuplicate;
 
 /**
+ * A factory for producing duplicate finders.
+ * @interface
+ */
+importer.DuplicateFinder.Factory = function() {};
+
+/** @return {!importer.DuplicateFinder} */
+importer.DuplicateFinder.Factory.prototype.create;
+
+/**
  * A duplicate finder for Google Drive.
+ *
  * @constructor
  * @implements {importer.DuplicateFinder}
  * @struct
@@ -29,11 +39,31 @@
 importer.DriveDuplicateFinder = function() {
   /** @private {Promise<string>} */
   this.driveIdPromise_ = null;
+
+  /**
+   * Aggregate time spent computing content hashes (in ms).
+   * @private {number}
+   */
+  this.computeHashTime_ = 0;
+
+  /**
+   * Aggregate time spent performing content hash searches (in ms).
+   * @private {number}
+   */
+  this.searchHashTime_ = 0;
 };
 
+/**
+ * @typedef {{
+ *   computeHashTime: number,
+ *   searchHashTime: number
+ * }}
+ */
+importer.DriveDuplicateFinder.Statistics;
+
 /** @override */
 importer.DriveDuplicateFinder.prototype.checkDuplicate = function(entry) {
-  return importer.DriveDuplicateFinder.computeHash_(entry)
+  return this.computeHash_(entry)
       .then(this.findByHash_.bind(this))
       .then(
           /**
@@ -50,20 +80,24 @@
  * @param {!FileEntry} entry
  * @private
  */
-importer.DriveDuplicateFinder.computeHash_ = function(entry) {
+importer.DriveDuplicateFinder.prototype.computeHash_ = function(entry) {
   return new Promise(
+      /** @this {importer.DriveDuplicateFinder} */
       function(resolve, reject) {
+        var startTime = new Date().getTime();
         chrome.fileManagerPrivate.computeChecksum(
             entry.toURL(),
             /** @param {string} result The content hash. */
             function(result) {
+              var endTime = new Date().getTime();
+              this.searchHashTime_ += endTime - startTime;
               if (chrome.runtime.lastError) {
                 reject(chrome.runtime.lastError);
               } else {
                 resolve(result);
               }
             });
-      });
+      }.bind(this));
 };
 
 /**
@@ -75,7 +109,7 @@
  */
 importer.DriveDuplicateFinder.prototype.findByHash_ = function(hash) {
   return this.getDriveId_()
-      .then(importer.DriveDuplicateFinder.searchFilesByHash_.bind(null, hash));
+      .then(this.searchFilesByHash_.bind(this, hash));
 };
 
 /**
@@ -104,19 +138,46 @@
  * @param {string} volumeId The volume to search.
  * @return <!Promise<Array<string>>> A list of file URLs.
  */
-importer.DriveDuplicateFinder.searchFilesByHash_ = function(hash, volumeId) {
+importer.DriveDuplicateFinder.prototype.searchFilesByHash_ =
+    function(hash, volumeId) {
   return new Promise(
+      /** @this {importer.DriveDuplicateFinder} */
       function(resolve, reject) {
+        var startTime = new Date().getTime();
         chrome.fileManagerPrivate.searchFilesByHashes(
             volumeId,
             [hash],
-            /** @param {!Object<string, Array<string>>} urls */
+            /**
+             * @param {!Object<string, Array<string>>} urls
+             * @this {importer.DriveDuplicateFinder}
+             */
             function(urls) {
+              var endTime = new Date().getTime();
+              this.searchHashTime_ += endTime - startTime;
               if (chrome.runtime.lastError) {
                 reject(chrome.runtime.lastError);
               } else {
                 resolve(urls[hash]);
               }
-            });
-      });
+            }.bind(this));
+      }.bind(this));
+};
+
+/** @return {!importer.DriveDuplicateFinder.Statistics} */
+importer.DriveDuplicateFinder.prototype.getStatistics = function() {
+  return {
+    computeHashTime: this.computeHashTime_,
+    searchHashTime: this.searchHashTime_
+  };
+};
+
+/**
+ * @constructor
+ * @implements {importer.DuplicateFinder.Factory}
+ */
+importer.DriveDuplicateFinder.Factory = function() {};
+
+/** @override */
+importer.DriveDuplicateFinder.Factory.prototype.create = function() {
+  return new importer.DriveDuplicateFinder();
 };
diff --git a/ui/file_manager/file_manager/background/js/file_operation_handler.js b/ui/file_manager/file_manager/background/js/file_operation_handler.js
index d5730fce..b7126e2 100644
--- a/ui/file_manager/file_manager/background/js/file_operation_handler.js
+++ b/ui/file_manager/file_manager/background/js/file_operation_handler.js
@@ -163,9 +163,6 @@
 FileOperationHandler.prototype.onCopyProgress_ = function(event) {
   var EventType = fileOperationUtil.EventRouter.EventType;
   event = /** @type {FileOperationProgressEvent} */ (event);
-  // If the copy is finished, may be we can close the background page.
-  if (event.reason !== EventType.BEGIN && event.reason !== EventType.PROGRESS)
-    this.background_.tryClose();
 
   // Update progress center.
   var progressCenter = this.progressCenter_;
@@ -231,9 +228,6 @@
 FileOperationHandler.prototype.onDeleteProgress_ = function(event) {
   var EventType = fileOperationUtil.EventRouter.EventType;
   event = /** @type {FileOperationProgressEvent} */ (event);
-  // If the copy is finished, may be we can close the background page.
-  if (event.reason !== EventType.BEGIN && event.reason !== EventType.PROGRESS)
-    this.background_.tryClose();
 
   // Update progress center.
   var progressCenter = this.progressCenter_;
diff --git a/ui/file_manager/file_manager/background/js/file_operation_handler_unittest.js b/ui/file_manager/file_manager/background/js/file_operation_handler_unittest.js
index 93033d4..317efeb 100644
--- a/ui/file_manager/file_manager/background/js/file_operation_handler_unittest.js
+++ b/ui/file_manager/file_manager/background/js/file_operation_handler_unittest.js
@@ -68,7 +68,6 @@
   assertEquals('copy', item.type);
   assertEquals(true, item.single);
   assertEquals(100, item.progressRateInPercent);
-  assertEquals(1, background.closeRequestCount);
 }
 
 // Test for copy cancel.
@@ -113,7 +112,6 @@
   assertEquals('copy', item.type);
   assertEquals(true, item.single);
   assertEquals(0, item.progressRateInPercent);
-  assertEquals(1, background.closeRequestCount);
 }
 
 // Test for copy target exists error.
@@ -139,7 +137,6 @@
   assertEquals('copy', item.type);
   assertEquals(true, item.single);
   assertEquals(0, item.progressRateInPercent);
-  assertEquals(1, background.closeRequestCount);
 }
 
 // Test for copy file system error.
@@ -165,7 +162,6 @@
   assertEquals('copy', item.type);
   assertEquals(true, item.single);
   assertEquals(0, item.progressRateInPercent);
-  assertEquals(1, background.closeRequestCount);
 }
 
 // Test for copy unexpected error.
@@ -191,5 +187,4 @@
   assertEquals('copy', item.type);
   assertEquals(true, item.single);
   assertEquals(0, item.progressRateInPercent);
-  assertEquals(1, background.closeRequestCount);
 }
diff --git a/ui/file_manager/file_manager/background/js/file_operation_manager.js b/ui/file_manager/file_manager/background/js/file_operation_manager.js
index 6cf88d6e..b6fc719 100644
--- a/ui/file_manager/file_manager/background/js/file_operation_manager.js
+++ b/ui/file_manager/file_manager/background/js/file_operation_manager.js
@@ -351,7 +351,7 @@
 /**
  * Performs the deletion.
  *
- * @param {Object} task The delete task (see deleteEntries function).
+ * @param {!Object} task The delete task (see deleteEntries function).
  * @param {function()} callback Callback run on task end.
  * @private
  */
diff --git a/ui/file_manager/file_manager/background/js/file_operation_util.js b/ui/file_manager/file_manager/background/js/file_operation_util.js
index be018bb..a0e7c4ff 100644
--- a/ui/file_manager/file_manager/background/js/file_operation_util.js
+++ b/ui/file_manager/file_manager/background/js/file_operation_util.js
@@ -310,7 +310,7 @@
                 reader.readEntries(processSubEntries, maybeSettlePromise);
               },
               maybeSettlePromise);
-        }
+        };
 
         processEntry(entry);
       });
diff --git a/ui/file_manager/file_manager/background/js/import_history.js b/ui/file_manager/file_manager/background/js/import_history.js
index 17a9296..c9e87fe 100644
--- a/ui/file_manager/file_manager/background/js/import_history.js
+++ b/ui/file_manager/file_manager/background/js/import_history.js
@@ -1034,6 +1034,7 @@
         // in the background.
         chrome.fileManagerPrivate.getEntryProperties(
             [url],
+            ['dirty'],
             /**
              * @param {!Array.<Object>} propertiesList
              * @this {importer.DriveSyncWatcher}
@@ -1046,7 +1047,7 @@
                 reject(chrome.runtime.lastError);
               } else {
                 var data = propertiesList[0];
-                resolve(!data['isDirty']);
+                resolve(!data['dirty']);
               }
             }.bind(this));
       }.bind(this));
diff --git a/ui/file_manager/file_manager/background/js/media_import_handler.js b/ui/file_manager/file_manager/background/js/media_import_handler.js
index dffda98..9065e05 100644
--- a/ui/file_manager/file_manager/background/js/media_import_handler.js
+++ b/ui/file_manager/file_manager/background/js/media_import_handler.js
@@ -32,10 +32,11 @@
  *
  * @param {!ProgressCenter} progressCenter
  * @param {!importer.HistoryLoader} historyLoader
- * @param {!importer.DuplicateFinder} duplicateFinder
+ * @param {!importer.DuplicateFinder.Factory} duplicateFinderFactory
+ * @param {!analytics.Tracker} tracker
  */
 importer.MediaImportHandler =
-    function(progressCenter, historyLoader, duplicateFinder) {
+    function(progressCenter, historyLoader, duplicateFinderFactory, tracker) {
   /** @private {!ProgressCenter} */
   this.progressCenter_ = progressCenter;
 
@@ -45,8 +46,19 @@
   /** @private {!importer.TaskQueue} */
   this.queue_ = new importer.TaskQueue();
 
-  /** @private {!importer.DuplicateFinder} */
-  this.duplicateFinder_ = duplicateFinder;
+  // Prevent the system from sleeping while imports are active.
+  this.queue_.setActiveCallback(function() {
+    chrome.power.requestKeepAwake('system');
+  });
+  this.queue_.setIdleCallback(function() {
+    chrome.power.releaseKeepAwake();
+  });
+
+  /** @private {!importer.DuplicateFinder.Factory} */
+  this.duplicateFinderFactory_ = duplicateFinderFactory;
+
+  /** @private {!analytics.Tracker} */
+  this.tracker_ = tracker;
 
   /** @private {number} */
   this.nextTaskId_ = 0;
@@ -61,8 +73,9 @@
       this.historyLoader_,
       scanResult,
       directoryPromise,
-      this.duplicateFinder_,
-      destination);
+      this.duplicateFinderFactory_.create(),
+      destination,
+      this.tracker_);
 
   task.addObserver(this.onTaskProgress_.bind(this, task));
 
@@ -145,6 +158,7 @@
  * @param {!importer.DuplicateFinder} duplicateFinder A duplicate-finder linked
  *     to the import destination, that will be used to deduplicate imports.
  * @param {!importer.Destination} destination The logical destination.
+ * @param {!analytics.Tracker} tracker
  */
 importer.MediaImportHandler.ImportTask = function(
     taskId,
@@ -152,7 +166,8 @@
     scanResult,
     directoryPromise,
     duplicateFinder,
-    destination) {
+    destination,
+    tracker) {
 
   importer.TaskQueue.BaseTask.call(this, taskId);
   /** @private {string} */
@@ -173,6 +188,9 @@
   /** @private {!importer.HistoryLoader} */
   this.historyLoader_ = historyLoader;
 
+  /** @private {!analytics.Tracker} */
+  this.tracker_ = tracker;
+
   /** @private {number} */
   this.totalBytes_ = 0;
 
@@ -187,6 +205,12 @@
 
   /** @private {boolean} Indicates whether this task was canceled. */
   this.canceled_ = false;
+
+  /** @private {number} Number of files deduped by content dedupe. */
+  this.dedupeCount_ = 0;
+
+  /** @private {number} */
+  this.errorCount_ = 0;
 };
 
 /** @struct */
@@ -252,9 +276,15 @@
 
 /** @private */
 importer.MediaImportHandler.ImportTask.prototype.initialize_ = function() {
-  this.remainingFilesCount_ = this.scanResult_.getFileEntries().length;
-  this.totalBytes_ = this.scanResult_.getTotalBytes();
+  var stats = this.scanResult_.getStatistics();
+
+  this.remainingFilesCount_ = stats.newFileCount;
+  this.totalBytes_ = stats.sizeBytes;
   this.notify(importer.TaskQueue.UpdateType.PROGRESS);
+
+  this.tracker_.send(metrics.ImportEvents.STARTED);
+  this.tracker_.send(metrics.ImportEvents.HISTORY_DEDUPE_COUNT
+                     .value(stats.duplicateFileCount));
 };
 
 /**
@@ -286,6 +316,8 @@
     function(destinationDirectory, completionCallback, entry) {
   if (this.canceled_) {
     this.notify(importer.TaskQueue.UpdateType.CANCELED);
+    this.tracker_.send(metrics.ImportEvents.CANCELLED);
+    this.sendImportStats_();
     return;
   }
 
@@ -296,6 +328,7 @@
             if (isDuplicate) {
               // If the given file is a duplicate, don't import it again.  Just
               // update the progress indicator.
+              this.dedupeCount_++;
               this.markAsImported_(entry);
               this.processedBytes_ += entry.size;
               this.notify(importer.TaskQueue.UpdateType.PROGRESS);
@@ -372,14 +405,24 @@
     resolver.reject(error);
   };
 
-  this.cancelCallback_ = fileOperationUtil.copyTo(
-      entry,
-      destinationDirectory,
-      entry.name,  // TODO(kenobi): account for duplicate filenames
-      onEntryChanged.bind(this),
-      onProgress.bind(this),
-      onComplete.bind(this),
-      onError.bind(this));
+  fileOperationUtil.deduplicatePath(destinationDirectory, entry.name)
+      .then(
+          /**
+           * Performs the copy using the given deduped filename.
+           * @param {string} destinationFilename
+           * @this {importer.MediaImportHandler.ImportTask}
+           */
+          function(destinationFilename) {
+            this.cancelCallback_ = fileOperationUtil.copyTo(
+                entry,
+                destinationDirectory,
+                destinationFilename,
+                onEntryChanged.bind(this),
+                onProgress.bind(this),
+                onComplete.bind(this),
+                onError.bind(this));
+          }.bind(this),
+          resolver.reject);
 
   return resolver.promise;
 };
@@ -432,6 +475,8 @@
 /** @private */
 importer.MediaImportHandler.ImportTask.prototype.onSuccess_ = function() {
   this.notify(importer.TaskQueue.UpdateType.SUCCESS);
+  this.tracker_.send(metrics.ImportEvents.ENDED);
+  this.sendImportStats_();
 };
 
 /**
@@ -440,4 +485,36 @@
  */
 importer.MediaImportHandler.ImportTask.prototype.onError_ = function(error) {
   this.notify(importer.TaskQueue.UpdateType.ERROR);
+  this.errorCount_++;
+};
+
+/**
+ * Sends import statistics to analytics.
+ */
+importer.MediaImportHandler.ImportTask.prototype.sendImportStats_ = function() {
+  this.tracker_.send(
+      metrics.ImportEvents.CONTENT_DEDUPE_COUNT
+          .value(this.dedupeCount_));
+  // TODO(kenobi): Send correct import byte counts.
+  var importFileCount = this.scanResult_.getStatistics().newFileCount -
+      (this.dedupeCount_ + this.remainingFilesCount_);
+  this.tracker_.send(
+      metrics.ImportEvents.FILE_COUNT
+          .value(importFileCount));
+
+  this.tracker_.send(metrics.ImportEvents.ERROR.value(this.errorCount_));
+
+  // Send aggregate deduplication timings, to avoid flooding analytics with one
+  // timing per file.
+  var deduplicatorStats = this.deduplicator_.getStatistics();
+  this.tracker_.sendTiming(
+      metrics.Categories.ACQUISITION,
+      metrics.timing.Variables.COMPUTE_HASH,
+      deduplicatorStats.computeHashTime,
+      'In Place');
+  this.tracker_.sendTiming(
+      metrics.Categories.ACQUISITION,
+      metrics.timing.Variables.SEARCH_BY_HASH,
+      deduplicatorStats.searchHashTime);
+
 };
diff --git a/ui/file_manager/file_manager/background/js/media_import_handler_unittest.html b/ui/file_manager/file_manager/background/js/media_import_handler_unittest.html
index 834e3666..df07c05 100644
--- a/ui/file_manager/file_manager/background/js/media_import_handler_unittest.html
+++ b/ui/file_manager/file_manager/background/js/media_import_handler_unittest.html
@@ -11,8 +11,11 @@
   <script src="../../../../../ui/webui/resources/js/cr/event_target.js"></script>
   <script src="../../../../../ui/webui/resources/js/cr/ui/array_data_model.js"></script>
   <script src="../../../../../ui/webui/resources/js/load_time_data.js"></script>
-
+  <script src="../../../../../third_party/analytics/google-analytics-bundle.js"></script>
+  
   <script src="../../common/js/async_util.js"></script>
+  <script src="../../common/js/metrics_base.js"></script>
+  <script src="../../common/js/metrics_events.js"></script>
   <script src="../../common/js/mock_entry.js"></script>
   <script src="../../common/js/unittest_util.js"></script>
   <script src="../../common/js/util.js"></script>
@@ -20,6 +23,7 @@
   <script src="../../common/js/importer_common.js"></script>
   <script src="../../common/js/test_importer_common.js"></script>
   <script src="../../common/js/progress_center_common.js"></script>
+  <script src="../../common/js/test_tracker.js"></script>
 
   <script src="test_import_history.js"></script>
   <script src="mock_progress_center.js"></script>
diff --git a/ui/file_manager/file_manager/background/js/media_import_handler_unittest.js b/ui/file_manager/file_manager/background/js/media_import_handler_unittest.js
index 225c35f..35886a7 100644
--- a/ui/file_manager/file_manager/background/js/media_import_handler_unittest.js
+++ b/ui/file_manager/file_manager/background/js/media_import_handler_unittest.js
@@ -23,8 +23,8 @@
 /** @type {!MockFileSystem} */
 var destinationFileSystem;
 
-/** @type {!importer.DuplicateFinder} */
-var duplicateFinder;
+/** @type {!importer.TestDuplicateFinder.Factory} */
+var duplicateFinderFactory;
 
 /** @type {!Promise<!DirectoryEntry>} */
 var destinationFactory;
@@ -36,7 +36,24 @@
   DOWNLOADS_DIRECTORY_LABEL: 'Downloads'
 };
 
+var chrome;
+
 function setUp() {
+  // Set up mock chrome APIs.
+  chrome = {
+    power: {
+      requestKeepAwakeWasCalled: false,
+      requestKeepAwakeStatus: false,
+      requestKeepAwake: function() {
+        chrome.power.requestKeepAwakeWasCalled = true;
+        chrome.power.requestKeepAwakeStatus = true;
+      },
+      releaseKeepAwake: function() {
+        chrome.power.requestKeepAwakeStatus = false;
+      }
+    }
+  };
+
   importer.setupTestLogger();
 
   progressCenter = new MockProgressCenter();
@@ -65,10 +82,13 @@
           function(directory) {
             return directory;
           });
-  duplicateFinder = new importer.TestDuplicateFinder();
+  duplicateFinderFactory = new importer.TestDuplicateFinder.Factory();
 
   mediaImporter = new importer.MediaImportHandler(
-      progressCenter, importHistory, duplicateFinder);
+      progressCenter,
+      importHistory,
+      duplicateFinderFactory,
+      new TestTracker());
 }
 
 function testImportMedia(callback) {
@@ -116,6 +136,103 @@
   scanResult.finalize();
 }
 
+// Verifies that when files with duplicate names are imported, that they don't
+// overwrite one another.
+function testImportMediaWithDuplicateFilenames(callback) {
+  var media = setupFileSystem([
+    '/DCIM/photos0/IMG00001.jpg',
+    '/DCIM/photos0/IMG00002.jpg',
+    '/DCIM/photos0/IMG00003.jpg',
+    '/DCIM/photos1/IMG00001.jpg',
+    '/DCIM/photos1/IMG00002.jpg',
+    '/DCIM/photos1/IMG00003.jpg'
+  ]);
+
+  var scanResult = new TestScanResult(media);
+  var importTask = mediaImporter.importFromScanResult(
+        scanResult,
+        importer.Destination.GOOGLE_DRIVE,
+        destinationFactory);
+  var whenImportDone = new Promise(
+      function(resolve, reject) {
+        importTask.addObserver(
+            /**
+             * @param {!importer.TaskQueue.UpdateType} updateType
+             * @param {!importer.TaskQueue.Task} task
+             */
+            function(updateType, task) {
+              switch (updateType) {
+                case importer.TaskQueue.UpdateType.SUCCESS:
+                  resolve();
+                  break;
+                case importer.TaskQueue.UpdateType.ERROR:
+                  reject(new Error(importer.TaskQueue.UpdateType.ERROR));
+                  break;
+              }
+            });
+      });
+
+  // Verify that we end up with 6, and not 3, destination entries.
+  reportPromise(
+      whenImportDone.then(
+        function() {
+          var copiedEntries = destinationFileSystem.root.getAllChildren();
+          assertEquals(media.length, copiedEntries.length);
+        }),
+      callback);
+
+  scanResult.finalize();
+}
+
+function testKeepAwakeDuringImport(callback) {
+  var media = setupFileSystem([
+    '/DCIM/photos0/IMG00001.jpg',
+    '/DCIM/photos0/IMG00002.jpg',
+    '/DCIM/photos0/IMG00003.jpg',
+    '/DCIM/photos1/IMG00004.jpg',
+    '/DCIM/photos1/IMG00005.jpg',
+    '/DCIM/photos1/IMG00006.jpg'
+  ]);
+
+  var scanResult = new TestScanResult(media);
+  var importTask = mediaImporter.importFromScanResult(
+        scanResult,
+        importer.Destination.GOOGLE_DRIVE,
+        destinationFactory);
+  var whenImportDone = new Promise(
+      function(resolve, reject) {
+        importTask.addObserver(
+            /**
+             * @param {!importer.TaskQueue.UpdateType} updateType
+             * @param {!importer.TaskQueue.Task} task
+             */
+            function(updateType, task) {
+              // Assert that keepAwake is set while the task is active.
+              assertTrue(chrome.power.requestKeepAwakeStatus);
+              switch (updateType) {
+                case importer.TaskQueue.UpdateType.SUCCESS:
+                  resolve();
+                  break;
+                case importer.TaskQueue.UpdateType.ERROR:
+                  reject(new Error(importer.TaskQueue.UpdateType.ERROR));
+                  break;
+              }
+            });
+      });
+
+  reportPromise(
+      whenImportDone.then(
+        function() {
+          assertTrue(chrome.power.requestKeepAwakeWasCalled);
+          assertFalse(chrome.power.requestKeepAwakeStatus);
+          var copiedEntries = destinationFileSystem.root.getAllChildren();
+          assertEquals(media.length, copiedEntries.length);
+        }),
+      callback);
+
+  scanResult.finalize();
+}
+
 function testUpdatesHistoryAfterImport(callback) {
   var entries = setupFileSystem([
     '/DCIM/photos0/IMG00001.jpg',
@@ -262,7 +379,7 @@
         importer.MediaImportHandler.ImportTask.UpdateType.ENTRY_CHANGED) {
       copyCount++;
       if (copyCount === EXPECTED_COPY_COUNT) {
-        duplicateFinder.returnValue = true;
+        duplicateFinderFactory.instances[0].returnValue = true;
       }
     }
   });
diff --git a/ui/file_manager/file_manager/background/js/media_scanner.js b/ui/file_manager/file_manager/background/js/media_scanner.js
index b311fc6..86dd1bc 100644
--- a/ui/file_manager/file_manager/background/js/media_scanner.js
+++ b/ui/file_manager/file_manager/background/js/media_scanner.js
@@ -59,21 +59,6 @@
 importer.ScanResult.prototype.getFileEntries;
 
 /**
- * Returns the aggregate size, in bytes, of all FileEntries discovered
- * during scanning.
- *
- * @return {number}
- */
-importer.ScanResult.prototype.getTotalBytes;
-
-/**
- * Returns the scan duration in milliseconds.
- *
- * @return {number}
- */
-importer.ScanResult.prototype.getScanDurationMs;
-
-/**
  * Returns a promise that fires when scanning is complete.
  *
  * @return {!Promise.<!importer.ScanResult>}
@@ -81,6 +66,21 @@
 importer.ScanResult.prototype.whenFinal;
 
 /**
+ * @return {!importer.ScanResult.Statistics}
+ */
+importer.ScanResult.prototype.getStatistics;
+
+/**
+ * @typedef {{
+ *   scanDuration: number,
+ *   newFileCount: number,
+ *   duplicateFileCount: number,
+ *   sizeBytes: number
+ * }}
+ */
+importer.ScanResult.Statistics;
+
+/**
  * Recursively scans through a list of given files and directories, and creates
  * a list of media files.
  *
@@ -266,14 +266,14 @@
            * @this {importer.DefaultMediaScanner}
            */
           function(duplicate) {
-            if (!duplicate) {
-              return this.onUniqueFileFound_(scan, entry);
-            }
+            return duplicate ?
+                this.onDuplicateFileFound_(scan, entry) :
+                this.onUniqueFileFound_(scan, entry);
           }.bind(this));
 };
 
 /**
- * Finds all files beneath directory.
+ * Adds a newly discovered file to the given scan result.
  *
  * @param {!importer.DefaultScanResult} scan
  * @param {!FileEntry} entry
@@ -301,6 +301,21 @@
 };
 
 /**
+ * Adds a duplicate file to the given scan result.  This is to track the number
+ * of duplicates that are being encountered.
+ *
+ * @param {!importer.DefaultScanResult} scan
+ * @param {!FileEntry} entry
+ * @return {!Promise}
+ * @private
+ */
+importer.DefaultMediaScanner.prototype.onDuplicateFileFound_ =
+    function(scan, entry) {
+  scan.addDuplicateEntry(entry);
+  return Promise.resolve();
+};
+
+/**
  * @param {!FileEntry} entry
  * @return {!Promise.<boolean>} True if there is a history-entry-duplicate
  *     for the file.
@@ -366,6 +381,9 @@
   /** @private {number} */
   this.totalBytes_ = 0;
 
+  /** @private {number} */
+  this.duplicateFileCount_ = 0;
+
   /**
    * The point in time when the scan was started.
    * @type {Date}
@@ -412,16 +430,6 @@
 };
 
 /** @override */
-importer.DefaultScanResult.prototype.getTotalBytes = function() {
-  return this.totalBytes_;
-};
-
-/** @override */
-importer.DefaultScanResult.prototype.getScanDurationMs = function() {
-  return this.lastScanActivity_.getTime() - this.scanStarted_.getTime();
-};
-
-/** @override */
 importer.DefaultScanResult.prototype.whenFinal = function() {
   return this.resolver_.promise;
 };
@@ -461,11 +469,12 @@
                   this.lastScanActivity_ = new Date();
 
                   if (hashcode in this.fileHashcodes_) {
+                    this.addDuplicateEntry(entry);
                     return false;
                   }
 
                   entry.size = metadata.size;
-                  this.totalBytes_ += metadata['size'];
+                  this.totalBytes_ += metadata.size;
                   this.fileHashcodes_[hashcode] = entry;
                   this.fileEntries_.push(entry);
                   return true;
@@ -475,6 +484,25 @@
 };
 
 /**
+ * Logs the fact that a duplicate file entry was discovered during the scan.
+ * @param {!FileEntry} entry
+ */
+importer.DefaultScanResult.prototype.addDuplicateEntry = function(entry) {
+  this.duplicateFileCount_++;
+};
+
+/** @override */
+importer.DefaultScanResult.prototype.getStatistics = function() {
+  return {
+    scanDuration:
+        this.lastScanActivity_.getTime() - this.scanStarted_.getTime(),
+    newFileCount: this.fileEntries_.length,
+    duplicateFileCount: this.duplicateFileCount_,
+    sizeBytes: this.totalBytes_
+  };
+};
+
+/**
  * Watcher for directories.
  * @interface
  */
diff --git a/ui/file_manager/file_manager/background/js/media_scanner_unittest.js b/ui/file_manager/file_manager/background/js/media_scanner_unittest.js
index 77fe1c1..6e1630af 100644
--- a/ui/file_manager/file_manager/background/js/media_scanner_unittest.js
+++ b/ui/file_manager/file_manager/background/js/media_scanner_unittest.js
@@ -341,7 +341,7 @@
 /**
  * Verifies the results of the media scan are as expected.
  * @param {!Array.<string>} expected
- * @param {!impoter.ScanResults} results
+ * @param {!importer.ScanResults} results
  */
 function assertResults(expected, results) {
   assertFileEntryPathsEqual(expected, results.getFileEntries());
diff --git a/ui/file_manager/file_manager/background/js/mock_background.js b/ui/file_manager/file_manager/background/js/mock_background.js
index c9c605e..eddcf064 100644
--- a/ui/file_manager/file_manager/background/js/mock_background.js
+++ b/ui/file_manager/file_manager/background/js/mock_background.js
@@ -9,12 +9,4 @@
 function MockBackground() {
   this.fileOperationManager = new MockFileOperationManager();
   this.progressCenter = new MockProgressCenter();
-  this.closeRequestCount = 0;
 }
-
-/**
- * Increments the close request counter.
- */
-MockBackground.prototype.tryClose = function() {
-  this.closeRequestCount++;
-};
diff --git a/ui/file_manager/file_manager/background/js/mock_media_scanner.js b/ui/file_manager/file_manager/background/js/mock_media_scanner.js
index 407c02f..2019150 100644
--- a/ui/file_manager/file_manager/background/js/mock_media_scanner.js
+++ b/ui/file_manager/file_manager/background/js/mock_media_scanner.js
@@ -115,6 +115,9 @@
   /** @type {number} */
   this.scanDuration = 100;
 
+  /** @type {number} */
+  this.duplicateFileCount = 0;
+
   /** @type {function} */
   this.resolveResult_;
 
@@ -144,16 +147,6 @@
 };
 
 /** @override */
-TestScanResult.prototype.getTotalBytes = function() {
-  return this.totalBytes;
-};
-
-/** @override */
-TestScanResult.prototype.getScanDurationMs = function() {
-  return this.scanDuration;
-};
-
-/** @override */
 TestScanResult.prototype.finalize = function() {
   return this.resolveResult_(this);
 };
@@ -173,6 +166,16 @@
   return false;
 };
 
+/** @override */
+TestScanResult.prototype.getStatistics = function() {
+  return {
+    scanDuration: this.scanDuration,
+    newFileCount: this.fileEntries.length,
+    duplicateFileCount: this.duplicateFileCount,
+    sizeBytes: this.totalBytes
+  };
+};
+
 /**
  * @constructor
  * @implements {importer.DirectoryWatcher}
diff --git a/ui/file_manager/file_manager/background/js/task_queue.js b/ui/file_manager/file_manager/background/js/task_queue.js
index ae096251..26d1ac0 100644
--- a/ui/file_manager/file_manager/background/js/task_queue.js
+++ b/ui/file_manager/file_manager/background/js/task_queue.js
@@ -81,7 +81,7 @@
 
 /**
  * Sets a callback that is triggered each time the queue goes from an active to
- * an idle state.  Also see #onActive.
+ * an idle state.  Also see #setActiveCallback.
  * @param {function()} callback
  */
 importer.TaskQueue.prototype.setIdleCallback = function(callback) {
@@ -182,7 +182,7 @@
   /** @private {!Array<!importer.TaskQueue.Task.Observer>} */
   this.observers_ = [];
 
-  /** @private {!importer.Resolver} */
+  /** @private {!importer.Resolver<!importer.TaskQueue.UpdateType>} */
   this.finishedResolver_ = new importer.Resolver();
 };
 
@@ -191,7 +191,8 @@
   /** @return {string} The task ID. */
   get taskId() { return this.taskId_; },
 
-  /** @return {!Promise} Resolves when task is complete, rejects on error. */
+  /** @return {!Promise<!importer.TaskQueue.UpdateType>} Resolves when task
+      is complete, or cancelled, rejects on error. */
   get whenFinished() { return this.finishedResolver_.promise; }
 };
 
@@ -211,9 +212,8 @@
 importer.TaskQueue.BaseTask.prototype.notify = function(updateType, opt_data) {
   switch (updateType) {
     case importer.TaskQueue.UpdateType.CANCELED:
-    case importer.TaskQueue.UpdateType.ERROR:
     case importer.TaskQueue.UpdateType.SUCCESS:
-      this.finishedResolver_.resolve();
+      this.finishedResolver_.resolve(updateType);
   }
 
   this.observers_.forEach(
diff --git a/ui/file_manager/file_manager/background/js/test_duplicate_finder.js b/ui/file_manager/file_manager/background/js/test_duplicate_finder.js
index cd09f03..0b6982fe 100644
--- a/ui/file_manager/file_manager/background/js/test_duplicate_finder.js
+++ b/ui/file_manager/file_manager/background/js/test_duplicate_finder.js
@@ -20,3 +20,27 @@
 importer.TestDuplicateFinder.prototype.checkDuplicate = function(entry) {
   return Promise.resolve(this.returnValue);
 };
+
+/** @override */
+importer.TestDuplicateFinder.prototype.getStatistics = function() {
+  return {
+    computeHashTime: 0,
+    searchHashTime: 0
+  };
+};
+
+/**
+ * @constructor
+ * @implements {importer.DuplicateFinder.Factory}
+ */
+importer.TestDuplicateFinder.Factory = function() {
+  /** @private {!Array<!importer.DuplicateFinder>} */
+  this.instances = [];
+};
+
+/** @override */
+importer.TestDuplicateFinder.Factory.prototype.create = function() {
+  var newInstance = new importer.TestDuplicateFinder();
+  this.instances.push(newInstance);
+  return newInstance;
+};
diff --git a/ui/file_manager/file_manager/background/js/volume_manager.js b/ui/file_manager/file_manager/background/js/volume_manager.js
index 9c3850a..d7a0d5e 100644
--- a/ui/file_manager/file_manager/background/js/volume_manager.js
+++ b/ui/file_manager/file_manager/background/js/volume_manager.js
@@ -418,7 +418,7 @@
 
 /**
  * Searches the information of the volume that contains the passed entry.
- * @param {Entry|Object} entry Entry on the volume to be found.
+ * @param {!Entry|!Object} entry Entry on the volume to be found.
  * @return {VolumeInfo} The volume's information, or null if not found.
  */
 VolumeInfoList.prototype.findByEntry = function(entry) {
@@ -687,7 +687,7 @@
       case 'mount':
         var requestKey = this.makeRequestKey_(
             'mount',
-            event.volumeMetadata.sourcePath);
+            event.volumeMetadata.sourcePath || '');
 
         if (event.status === 'success' ||
             event.status ===
@@ -814,7 +814,7 @@
 /**
  * Obtains location information from an entry.
  *
- * @param {(Entry|Object)} entry File or directory entry. It can be a fake
+ * @param {(!Entry|!Object)} entry File or directory entry. It can be a fake
  *     entry.
  * @return {EntryLocation} Location information.
  */
diff --git a/ui/file_manager/file_manager/common/js/error_util.js b/ui/file_manager/file_manager/common/js/error_util.js
index 9fd8f11..4399f160 100644
--- a/ui/file_manager/file_manager/common/js/error_util.js
+++ b/ui/file_manager/file_manager/common/js/error_util.js
@@ -11,7 +11,13 @@
 /**
  * Count uncaught exceptions.
  */
-window.onerror = function() { window.JSErrorCount++; };
+window.onerror = function(message, url) {
+  // Analytics raises errors if the tracker instance is initiated in guest mode.
+  // crbug.com/459983
+  if (url === 'chrome://resources/js/analytics.js')
+    return;
+  window.JSErrorCount++;
+};
 
 // Overrides console.error() to count errors.
 /**
diff --git a/ui/file_manager/file_manager/common/js/externs.js b/ui/file_manager/file_manager/common/js/externs.js
index 3de10168..8569f652c 100644
--- a/ui/file_manager/file_manager/common/js/externs.js
+++ b/ui/file_manager/file_manager/common/js/externs.js
@@ -71,8 +71,3 @@
 
 /** @type {boolean} */
 DirectoryChangeEvent.prototype.volumeChanged;
-
-/**
- * @type {boolean}
- */
-Window.prototype.closing;
diff --git a/ui/file_manager/file_manager/common/js/importer_common.js b/ui/file_manager/file_manager/common/js/importer_common.js
index 4a6a559..6634ad3 100644
--- a/ui/file_manager/file_manager/common/js/importer_common.js
+++ b/ui/file_manager/file_manager/common/js/importer_common.js
@@ -13,6 +13,16 @@
 };
 
 /**
+ * Storage keys for settings saved by importer.
+ * @enum {string}
+ */
+importer.Setting = {
+  HAS_COMPLETED_IMPORT: 'importer-has-completed-import',
+  MACHINE_ID: 'importer-machine-id',
+  PHOTOS_APP_ENABLED: 'importer-photo-app-enabled'
+};
+
+/**
  * @typedef {function(
  *     !importer.ScanEvent, importer.ScanResult)}
  */
@@ -135,6 +145,37 @@
 };
 
 /**
+ * Handles a message from Pulsar...in which we presume we are being
+ * informed of its "Automatically import stuff." state.
+ *
+ * While the runtime message system is loosey goosey about types,
+ * we fully expect message to be a boolean value.
+ *
+ * @param {*} message
+ *
+ * @return {!Promise} Resolves once the message has been handled.
+ */
+importer.handlePhotosAppMessage = function(message) {
+  if (typeof message !== 'boolean') {
+    console.error(
+        'Unrecognized message type received from photos app: ' + message);
+    return Promise.reject();
+  }
+
+  var storage = importer.ChromeLocalStorage.getInstance();
+  return storage.set(importer.Setting.PHOTOS_APP_ENABLED, message);
+};
+
+/**
+ * @return {!Promise.<boolean>} Resolves with true when Cloud Import feature
+ *     is enabled.
+ */
+importer.isPhotosAppImportEnabled = function() {
+  var storage = importer.ChromeLocalStorage.getInstance();
+  return storage.get(importer.Setting.PHOTOS_APP_ENABLED, false);
+};
+
+/**
  * @param {!Date} date
  * @return {string} The current date, in YYYY-MM-DD format.
  */
@@ -155,46 +196,24 @@
 };
 
 /**
- * Local storage key for machine id.
- * @const {string}
- */
-importer.MACHINE_ID_STORAGE_KEY_ = 'importer-machine-id';
-
-/**
  * @return {!Promise.<number>} Resolves with an integer that is probably
  *     relatively unique to this machine (among a users machines).
  */
 importer.getMachineId = function() {
-  return new Promise(
-      function(resolve, reject) {
-        chrome.storage.local.get(
-            importer.MACHINE_ID_STORAGE_KEY_,
-            /** @param {Object.<string, ?>} values */
-            function(values) {
-              if (chrome.runtime.lastError) {
-                reject(chrome.runtime.lastError);
-                return;
-              }
-
-              var machineId = values[importer.MACHINE_ID_STORAGE_KEY_];
-              if (!!machineId) {
-                resolve(machineId);
-              } else {
-                var machineId = importer.generateMachineId_();
-                var newValues = {};
-                newValues[importer.MACHINE_ID_STORAGE_KEY_] = machineId;
-                chrome.storage.local.set(
-                    newValues,
+  var storage = importer.ChromeLocalStorage.getInstance();
+  return storage.get(importer.Setting.MACHINE_ID)
+      .then(
+          function(id) {
+            if (id) {
+              return id;
+            }
+            var id = importer.generateMachineId_();
+            return storage.set(importer.Setting.MACHINE_ID, id)
+                .then(
                     function() {
-                      if (chrome.runtime.lastError) {
-                        reject(chrome.runtime.lastError);
-                      } else {
-                        resolve(machineId);
-                      }
+                      return id;
                     });
-              }
-            });
-      });
+          });
 };
 
 /**
@@ -715,3 +734,67 @@
   }
   return importer.logger_;
 };
+
+/**
+ * Friendly wrapper around chrome.storage.local.
+ *
+ * NOTE: If you want to use this in a test, install MockChromeStorageAPI.
+ *
+ * @constructor
+ */
+importer.ChromeLocalStorage = function() {};
+
+/**
+ * @param {string} key
+ * @param {string|number|boolean} value
+ * @return {!Promise} Resolves when operation is complete
+ */
+importer.ChromeLocalStorage.prototype.set = function(key, value) {
+  return new Promise(
+      function(resolve, reject) {
+        var values = {};
+        values[key] = value;
+        chrome.storage.local.set(
+            values,
+            function() {
+              if (chrome.runtime.lastError) {
+                reject(chrome.runtime.lastError);
+              } else {
+                resolve(undefined);
+              }
+            });
+      });
+};
+
+/**
+ * @param {string} key
+ * @param {T=} opt_default
+ * @return {!Promise.<T>} Resolves with the value, or {@code opt_default} when
+ *     no value entry existis, or {@code undefined}.
+ * @template T
+ */
+importer.ChromeLocalStorage.prototype.get = function(key, opt_default) {
+  return new Promise(
+      function(resolve, reject) {
+        chrome.storage.local.get(
+            key,
+            /** @param {Object.<string, ?>} values */
+            function(values) {
+              if (chrome.runtime.lastError) {
+                reject(chrome.runtime.lastError);
+              } else if (key in values) {
+                resolve(values[key]);
+              } else {
+                resolve(opt_default);
+              }
+            });
+      });
+};
+
+/** @private @const {!importer.ChromeLocalStorage} */
+importer.ChromeLocalStorage.INSTANCE_ = new importer.ChromeLocalStorage();
+
+/** @return {!importer.ChromeLocalStorage} */
+importer.ChromeLocalStorage.getInstance = function() {
+  return importer.ChromeLocalStorage.INSTANCE_;
+};
diff --git a/ui/file_manager/file_manager/common/js/importer_common_unittest.js b/ui/file_manager/file_manager/common/js/importer_common_unittest.js
index 33a8f80..c531c16 100644
--- a/ui/file_manager/file_manager/common/js/importer_common_unittest.js
+++ b/ui/file_manager/file_manager/common/js/importer_common_unittest.js
@@ -31,6 +31,9 @@
 
 // Set up the test components.
 function setUp() {
+
+  new MockChromeStorageAPI();
+
   var cameraFileSystem = new MockFileSystem(
       'camera-fs', 'filesystem:camera-123');
   var sdFileSystem = new MockFileSystem(
@@ -104,9 +107,7 @@
           });
 }
 
-function testGetMachineId(callback) {
-  var storage = new MockChromeStorageAPI();
-
+function testGetMachineId_Persisted(callback) {
   var promise = importer.getMachineId().then(
       function(firstMachineId) {
         assertTrue(100000 <= firstMachineId <= 9999999);
@@ -118,9 +119,31 @@
   reportPromise(promise, callback);
 }
 
-function testHistoryFilename(callback) {
-  var storage = new MockChromeStorageAPI();
+function testPhotosApp_DefaultDisabled(callback) {
+  var promise = importer.isPhotosAppImportEnabled().then(assertFalse);
 
+  reportPromise(promise, callback);
+}
+
+function testPhotosApp_ImportEnabled(callback) {
+  var promise = importer.handlePhotosAppMessage(true).then(
+      function() {
+        return importer.isPhotosAppImportEnabled().then(assertTrue);
+      });
+
+  reportPromise(promise, callback);
+}
+
+function testPhotosApp_ImportDisabled(callback) {
+  var promise = importer.handlePhotosAppMessage(false).then(
+      function() {
+        return importer.isPhotosAppImportEnabled().then(assertFalse);
+      });
+
+  reportPromise(promise, callback);
+}
+
+function testHistoryFilename(callback) {
   var promise = importer.getHistoryFilename().then(
       function(firstName) {
         assertTrue(!!firstName && firstName.length > 10);
@@ -133,6 +156,24 @@
   reportPromise(promise, callback);
 }
 
+function testLocalStorageWrapper(callback) {
+  var storage = new importer.ChromeLocalStorage();
+  var promise = Promise.all([
+    storage.set('lamb', 'chop'),
+    storage.set('isPoodle', true),
+    storage.set('age of grandma', 103)
+  ]).then(
+      function() {
+        return Promise.all([
+          storage.get('lamb').then(assertEquals.bind(null, 'chop')),
+          storage.get('isPoodle').then(assertEquals.bind(null, true)),
+          storage.get('age of grandma').then(assertEquals.bind(null, 103))
+        ]);
+      });
+
+  reportPromise(promise, callback);
+}
+
 /** @param {string} path */
 function assertIsMediaDir(path) {
   var dir = createDirectoryEntry(sdVolume, path);
diff --git a/ui/file_manager/file_manager/common/js/metrics.js b/ui/file_manager/file_manager/common/js/metrics.js
new file mode 100644
index 0000000..1e86f36
--- /dev/null
+++ b/ui/file_manager/file_manager/common/js/metrics.js
@@ -0,0 +1,72 @@
+// 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.
+
+/**
+ * @fileoverview Utility methods for accessing chrome.metricsPrivate API.
+ *
+ * To be included as a first script in main.html
+ */
+
+var metrics = metrics || metricsBase;
+
+/**
+ * Analytics tracking ID for Files app.
+ * @const {string}
+ */
+metrics.TRACKING_ID = 'UA-38248358-9';
+
+/**
+ * Convert a short metric name to the full format.
+ *
+ * @param {string} name Short metric name.
+ * @return {string} Full metric name.
+ * @override
+ * @private
+ */
+metrics.convertName_ = function(name) {
+  return 'FileBrowser.' + name;
+};
+
+/** @private {analytics.Tracker} */
+metrics.tracker_ = null;
+
+/** @private {boolean} */
+metrics.enabled_ = false;
+
+/** @return {!analytics.Tracker} */
+metrics.getTracker = function() {
+  if (!metrics.tracker_) {
+    metrics.createTracker_();
+  }
+  return /** @type {!analytics.Tracker} */ (metrics.tracker_);
+};
+
+/**
+ * Creates a new analytics tracker.
+ * @private
+ */
+metrics.createTracker_ = function() {
+  var analyticsService = analytics.getService('Files app');
+
+  // Create a tracker, add a filter that only enables analytics when UMA is
+  // enabled.
+  metrics.tracker_ = analyticsService.getTracker(metrics.TRACKING_ID);
+  metrics.tracker_.addFilter(metrics.umaEnabledFilter_);
+};
+
+/**
+ * Queries the chrome UMA enabled setting, and filters hits based on that.
+ * @param {!analytics.Tracker.Hit} hit
+ * @private
+ */
+metrics.umaEnabledFilter_ = function(hit) {
+  chrome.fileManagerPrivate.isUMAEnabled(
+      /** @param {boolean} enabled */
+      function(enabled) {
+        metrics.enabled_ = enabled;
+      });
+  if (!metrics.enabled_) {
+    hit.cancel();
+  }
+};
diff --git a/ui/file_manager/file_manager/foreground/js/metrics_base.js b/ui/file_manager/file_manager/common/js/metrics_base.js
similarity index 100%
rename from ui/file_manager/file_manager/foreground/js/metrics_base.js
rename to ui/file_manager/file_manager/common/js/metrics_base.js
diff --git a/ui/file_manager/file_manager/common/js/metrics_events.js b/ui/file_manager/file_manager/common/js/metrics_events.js
new file mode 100644
index 0000000..df0f848
--- /dev/null
+++ b/ui/file_manager/file_manager/common/js/metrics_events.js
@@ -0,0 +1,100 @@
+// 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.
+
+// namespace
+var metrics = metrics || metricsBase;
+
+/** @enum {string} */
+metrics.Categories = {
+  ACQUISITION: 'Acquisition'
+};
+
+/**
+ * The values of these enums come from the analytics console.
+ * @private @enum {number}
+ */
+metrics.Dimension_ = {
+  USER_TYPE: 1,
+  SESSION_TYPE: 2
+};
+
+/**
+ * @enum {!analytics.EventBuilder.Dimension}
+ */
+metrics.Dimensions = {
+  USER_TYPE_NON_IMPORT: {
+    index: metrics.Dimension_.USER_TYPE,
+    value: 'Non-import'
+  },
+  USER_TYPE_IMPORT: {
+    index: metrics.Dimension_.USER_TYPE,
+    value: 'Import'
+  },
+  SESSION_TYPE_NON_IMPORT: {
+    index: metrics.Dimension_.SESSION_TYPE,
+    value: 'Non-import'
+  },
+  SESSION_TYPE_IMPORT: {
+    index: metrics.Dimension_.SESSION_TYPE,
+    value: 'Import'
+  }
+};
+
+// namespace
+metrics.event = metrics.event || {};
+
+/**
+ * Base event builders for files app.
+ * @private @enum {!analytics.EventBuilder}
+ */
+metrics.event.Builders_ = {
+  IMPORT: analytics.EventBuilder.builder()
+      .category(metrics.Categories.ACQUISITION)
+};
+
+/**
+ * @enum {!analytics.EventBuilder}
+ */
+metrics.ImportEvents = {
+  STARTED: metrics.event.Builders_.IMPORT
+      .action('Import Started')
+      .dimension(metrics.Dimensions.SESSION_TYPE_IMPORT)
+      .dimension(metrics.Dimensions.USER_TYPE_IMPORT),
+
+  ENDED: metrics.event.Builders_.IMPORT
+      .action('Import Completed'),
+
+  CANCELLED: metrics.event.Builders_.IMPORT
+      .action('Import Cancelled'),
+
+  ERROR: metrics.event.Builders_.IMPORT
+      .action('Import Error'),
+
+  FILE_COUNT: metrics.event.Builders_.IMPORT
+      .action('Files Imported'),
+
+  BYTE_COUNT: metrics.event.Builders_.IMPORT
+      .action('Total Bytes Imported'),
+
+  DEVICE_YANKED: metrics.event.Builders_.IMPORT
+      .action('Device Yanked'),
+
+  HISTORY_DEDUPE_COUNT: metrics.event.Builders_.IMPORT
+      .action('Files Deduped By History'),
+
+  CONTENT_DEDUPE_COUNT: metrics.event.Builders_.IMPORT
+      .action('Files Deduped By Content'),
+
+  HISTORY_CHANGED: metrics.event.Builders_.IMPORT
+      .action('History Changed')
+};
+
+// namespace
+metrics.timing = metrics.timing || {};
+
+/** @enum {string} */
+metrics.timing.Variables = {
+  COMPUTE_HASH: 'Compute Content Hash',
+  SEARCH_BY_HASH: 'Search By Hash'
+};
diff --git a/ui/file_manager/file_manager/common/js/metrics_unittest.html b/ui/file_manager/file_manager/common/js/metrics_unittest.html
new file mode 100644
index 0000000..fd45566
--- /dev/null
+++ b/ui/file_manager/file_manager/common/js/metrics_unittest.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<!-- 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.
+  -->
+
+<html>
+<body>
+  <script src="../../../../../ui/webui/resources/js/cr.js"></script>
+  <script src="../../../../../ui/webui/resources/js/cr/event_target.js"></script>
+  <script src="../../../../../ui/webui/resources/js/cr/ui/array_data_model.js"></script>
+  <script src="../../../../../ui/webui/resources/js/load_time_data.js"></script>
+  
+  <script src="../../../../../third_party/analytics/google-analytics-bundle.js"></script>
+  
+  <script src="unittest_util.js"></script>
+  <script src="util.js"></script>
+  <script src="metrics_base.js"></script>
+  <script src="metrics.js"></script>
+  
+  
+  <script src="metrics_unittest.js"></script>
+</body>
+</html>
diff --git a/ui/file_manager/file_manager/common/js/metrics_unittest.js b/ui/file_manager/file_manager/common/js/metrics_unittest.js
new file mode 100644
index 0000000..36a9807
--- /dev/null
+++ b/ui/file_manager/file_manager/common/js/metrics_unittest.js
@@ -0,0 +1,84 @@
+// 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.
+
+// Fake APIs
+var chrome;
+
+/** @type {analytics.Tracker.Hit} The last hit received. */
+var lastHit = null;
+
+/** @type {!analytics.Tracker} */
+var tracker;
+
+function setUp() {
+  setupFakeChromeAPIs();
+
+  tracker  = metrics.getTracker();
+
+  // Make a filter that logs the last received hit.  Cancel the hit so that
+  // running tests doesn't actually send any analytics.
+  tracker.addFilter(
+      /** @param {!analytics.Tracker.Hit} hit */
+      function(hit) {
+        // Log the hit.
+        lastHit = hit;
+        hit.cancel();
+      });
+  // Reset the last logged hit.
+  lastHit = null;
+}
+
+// Verifies that analytics logging occurs when fileManagerPrivate.isUMAEnabled
+// returns true.
+function testBasicLogging(callback) {
+  // Simulate UMA enabled, then check that hits are sent.
+  chrome.fileManagerPrivate.umaEnabled = true;
+  reportPromise(
+      tracker.sendAppView('Test').addCallback(
+          function() {
+            assertTrue(!!lastHit);
+          }),
+      callback);
+}
+
+// Verifies that analytics logging does not occur when
+// fileManagerPrivate.isUMAEnabled returns false.
+function testUMADisabled(callback) {
+  // Simulate UMA disabled, and verify that hits aren't sent.
+  chrome.fileManagerPrivate.umaEnabled = false;
+  reportPromise(
+      tracker.sendAppView('Test').addCallback(
+          function() {
+            assertTrue(lastHit === null);
+          }),
+      callback);
+}
+
+function setupFakeChromeAPIs() {
+  chrome = {
+    runtime: {
+      getManifest: function() {
+        return {
+          version: 0.0
+        };
+      }
+    },
+    storage: {
+      local: {
+        // Analytics uses storage to store the enabled/disabled flag.  Hard-wire
+        // the get method to always return true so analytics is jammed on for
+        // the purposes of testing.
+        get: function(data, cb) { cb(true); },
+        set: function(data, cb) {}
+      },
+      onChanged: {
+        addListener: function(cb) {}
+      }
+    },
+    fileManagerPrivate: {
+      umaEnabled: false,
+      isUMAEnabled: function(cb) { cb(chrome.fileManagerPrivate.umaEnabled); }
+    }
+  };
+}
diff --git a/ui/file_manager/file_manager/common/js/test_tracker.js b/ui/file_manager/file_manager/common/js/test_tracker.js
new file mode 100644
index 0000000..a33afd78
--- /dev/null
+++ b/ui/file_manager/file_manager/common/js/test_tracker.js
@@ -0,0 +1,74 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * A tracker to substitute for analytics.Tracker in tests.
+ * @construtor
+ * @implements {analytics.Tracker}
+ */
+TestTracker = function() {};
+
+/**
+ * @param {!analytics.HitType|!analytics.EventBuilder} hitType
+ * @param {(!analytics.ParameterMap|
+ *     !Object.<string, !analytics.Value>)=} opt_extraParams
+ * @return {!goog.async.Deferred}
+ */
+TestTracker.prototype.send = function(hitType, opt_extraParams) {
+};
+
+/**
+ * @param {string} description
+ * @return {!goog.async.Deferred}
+ */
+TestTracker.prototype.sendAppView = function() {};
+
+/**
+ * @param {string} category
+ * @param {string} action
+ * @param {string=} opt_label
+ * @param {number=} opt_value
+ * @return {!goog.async.Deferred}
+ */
+TestTracker.prototype.sendEvent = function() {};
+
+/**
+ * @param {string} network Specifies the social network, for example Facebook
+ *     or Google Plus.
+ * @param {string} action Specifies the social interaction action.
+ *     For example on Google Plus when a user clicks the +1 button,
+ *     the social action is 'plus'.
+ * @param {string} target Specifies the target of a social interaction.
+ *     This value is typically a URL but can be any text.
+ * @return {!goog.async.Deferred}
+ */
+TestTracker.prototype.sendSocial = function() {};
+
+/**
+ * @param {string=} opt_description Specifies the description of an exception.
+ * @param {boolean=} opt_fatal Was the exception fatal.
+ * @return {!goog.async.Deferred}
+ */
+TestTracker.prototype.sendException = function() {};
+
+/**
+ * @param {string} category Specifies the category of the timing.
+ * @param {string} variable Specifies the variable name of the timing.
+ * @param {number} value Specifies the value of the timing.
+ * @param {string=} opt_label Specifies the optional label of the timing.
+ * @param {number=} opt_sampleRate
+ * @return {!goog.async.Deferred}
+ */
+TestTracker.prototype.sendTiming = function() {};
+
+TestTracker.prototype.forceSessionStart = function() {};
+
+/**
+ * @param {string} category
+ * @param {string} variable
+ * @param {string=} opt_label
+ * @param {number=} opt_sampleRate
+ * @return {!TestTracker.Timing}
+ */
+TestTracker.prototype.startTiming = function() {};
diff --git a/ui/file_manager/file_manager/common/js/util.js b/ui/file_manager/file_manager/common/js/util.js
index fcfd4b1a..a4d26f11 100644
--- a/ui/file_manager/file_manager/common/js/util.js
+++ b/ui/file_manager/file_manager/common/js/util.js
@@ -589,7 +589,7 @@
 
 /**
  * Obtains whether an entry is fake or not.
- * @param {(Entry|Object)} entry Entry or a fake entry.
+ * @param {(!Entry|!Object)} entry Entry or a fake entry.
  * @return {boolean} True if the given entry is fake.
  */
 util.isFakeEntry = function(entry) {
@@ -775,7 +775,7 @@
  * Converts array of URLs to an array of corresponding Entries.
  *
  * @param {Array.<string>} urls Input array of URLs.
- * @param {function(Array.<Entry>, Array.<URL>)=} opt_callback Completion
+ * @param {function(!Array.<!Entry>, !Array.<!URL>)=} opt_callback Completion
  *     callback with array of success Entries and failure URLs.
  * @return {Promise} Promise fulfilled with the object that has entries property
  *     and failureUrls property. The promise is never rejected.
diff --git a/ui/file_manager/file_manager/foreground/css/common.css b/ui/file_manager/file_manager/foreground/css/common.css
index e4c3c8a3..80134e4 100644
--- a/ui/file_manager/file_manager/foreground/css/common.css
+++ b/ui/file_manager/file_manager/foreground/css/common.css
@@ -115,39 +115,6 @@
   opacity: 0.7;
 }
 
-.buttonbar {
-  display: flex;
-  height: 31px;
-}
-
-.buttonbar button:active img {
-  opacity: 1.0;
-}
-
-.buttonbar button:hover img {
-  opacity: 0.72;
-}
-
-.buttonbar button[disabled] img {
-  opacity: 0.9;
-}
-
-.buttonbar button img {
-  display: inline-block;
-  margin: -3px 0 0;
-  opacity: 0.55;
-  vertical-align: middle;
-}
-
-.buttonbar button.menubutton span.disclosureindicator {
-  -webkit-transform: rotate(90deg);
-  float: right;
-  margin-left: 7px;
-  margin-top: 10px;
-  opacity: .8;
-  transition: none;
-}
-
 span.disclosureindicator {
   background-image: -webkit-image-set(
     url(../images/common/disclosure_arrow_dk_grey.png) 1x,
@@ -163,13 +130,13 @@
    menus in FileBrowser look like native ChromeOS menus. */
 
 cr-menu.chrome-menu {
-  background-color: rgb(250, 250, 250);
-  border-radius: 3px;
+  background-color: white;
+  border-radius: 2px;
   box-shadow: 0 1px 4px 0 rgba(0, 0, 0, .5);
-  color: rgb(34, 34, 34);
+  color: rgb(51, 51, 51);
   outline: none;
   overflow: hidden;
-  padding: 5px 0;
+  padding: 4px 0;
   transition: opacity 200ms ease-in;
   z-index: 600;  /* Must be below the overlay pane (1000). */
 }
@@ -189,9 +156,8 @@
 cr-menu.chrome-menu > :not(hr) {
   background-position: right 10px center;
   background-repeat: no-repeat;
-  line-height: 30px;
-  padding-left: 20px;
-  padding-right: 20px;
+  line-height: 32px;
+  padding: 0 12px;
 }
 
 html[dir='rtl'] cr-menu.chrome-menu > :not(hr) {
@@ -233,19 +199,19 @@
 }
 
 cr-menu.chrome-menu > cr-menu-item[disabled] {
-  color: rgb(153, 153, 153);
+  color: rgb(219, 219, 219);
 }
 
 cr-menu.chrome-menu > cr-menu-item:not([disabled])[selected],
 cr-menu.chrome-menu > cr-menu-item:not([disabled]):active {
-  background-color: rgb(66, 129, 244);
+  background-color: rgb(27, 168, 243);
   color: white;
 }
 
 cr-menu.chrome-menu > hr {
-  background: rgb(235, 235, 235);
+  background: rgb(219, 219, 219);
   height: 1px;
-  margin: 5px 0;
+  margin: 4px 0;
 }
 
 cr-menu.chrome-menu > cr-menu-item[checked] {
@@ -506,3 +472,29 @@
 .cr-dialog-close:active {
   background-image: url(chrome://theme/IDR_CLOSE_DIALOG_P);
 }
+
+/* Minor tweak of vertical position for texts which need to be vertically
+ * aligned with corresponding file-type icons. */
+.tree-row > .label,
+.table-row-cell .filename-label,
+.table-row-cell .size,
+.table-row-cell .type,
+.table-row-cell .date,
+.thumbnail-bottom .filename-label,
+.autocomplete-suggestions > li > .detail-text {
+  padding-top: 1px;
+}
+
+@media (-webkit-min-device-pixel-ratio: 1.5) {
+  .tree-row > .label,
+  .table-row-cell .filename-label,
+  .table-row-cell .size,
+  .table-row-cell .type,
+  .table-row-cell .date,
+  .thumbnail-bottom .filename-label,
+  .autocomplete-suggestions > li > .detail-text {
+    /* In HiDPI display, 13pt Roboto font is drawn upper than normal display,
+     * so add extra padding on top of it. */
+    padding-top: 3px;
+  }
+}
diff --git a/ui/file_manager/file_manager/foreground/css/file_manager.css b/ui/file_manager/file_manager/foreground/css/file_manager.css
index b9d6655..217c5f5 100644
--- a/ui/file_manager/file_manager/foreground/css/file_manager.css
+++ b/ui/file_manager/file_manager/foreground/css/file_manager.css
@@ -173,18 +173,18 @@
   color: rgb(90, 90, 90);
   cursor: pointer;
   display: flex;
-  line-height: 40px;
+  height: 40px;
   padding: 0 3px;
 }
 
 #directory-tree .tree-row > .expand-icon {
   flex: none;
-  height: 37px;
+  height: 36px;
   left: 3px;
-  margin: -12px -4px -13px;
+  margin: -12px -2px;
   right: 3px;
   top: 0;
-  width: 37px;
+  width: 36px;
 }
 
 #directory-tree .tree-row > .volume-icon {
@@ -213,12 +213,7 @@
   color: rgb(27, 168, 243);
 }
 
-/* Make Drive volume's expand-icon invisible */
-#directory-tree > .tree-item.drive-volume > .tree-row > .expand-icon {
-  visibility: hidden;
-}
-
-#directory-tree .tree-row > div.root-eject {
+#directory-tree .tree-row > .root-eject {
   background: -webkit-image-set(
       url(../images/files/ui/eject.png) 1x,
       url(../images/files/ui/2x/eject.png) 2x) no-repeat center;
@@ -226,14 +221,14 @@
   flex: none;
   height: 20px;
   margin-right: 6px;
-  opacity: 0.7;
-  transition: opacity 70ms linear;
   width: 20px;
   z-index: 1;  /* Make sure .root-eject is on upper layer than paper-ripple. */
 }
 
-#directory-tree:focus .tree-row[selected] > div.root-eject {
-  opacity: 1;
+#directory-tree .tree-row[selected] > .root-eject {
+  background-image: -webkit-image-set(
+      url(../images/files/ui/eject_active.png) 1x,
+      url(../images/files/ui/2x/eject_active.png) 2x);
 }
 
 #directory-tree .root-item[disabled] {
@@ -442,7 +437,7 @@
 }
 
 #cloud-import-details.hidden {
-  -webkit-transform: translateX(25px);
+  -webkit-transform: translateY(-10px);
   opacity: 0;
 }
 
@@ -578,12 +573,18 @@
 }
 
 #search-box paper-input-decorator /deep/ .focused-underline {
-  background-color: rgba(255, 255, 255, 0.5);
+  background-color: white;
+  height: 1px;
+}
+
+#search-box.has-cursor,
+#search-box.has-text {
+  -webkit-margin-end: 12px;
 }
 
 #search-box.has-cursor input,
 #search-box.has-text input {
-  width: 176px;
+  width: 218px;
 }
 
 #search-box .clear {
@@ -659,7 +660,105 @@
   flex: none;
   flex-direction: row;
   outline: none;
-  padding: 10px;
+  padding: 8px 4px;
+}
+
+.dialog-footer .buttonbar {
+  display: flex;
+  height: 32px;
+}
+
+/* Copy style from paper-button for buttons on the footer. */
+.dialog-footer button {
+  -webkit-user-select: none;
+  background: transparent;
+  border: 0;
+  border-image: none;
+  border-radius: 2px;
+  box-sizing: border-box;
+  cursor: pointer;
+  margin: 0 0.29em;
+  min-width: 5.14em;
+  outline: none;
+  text-align: center;
+  text-transform: uppercase;
+  z-index: 0;
+}
+
+.dialog-footer button:hover {
+  border-image: none;  /* Overrides the definition of common.css. */
+}
+
+.dialog-footer paper-button,
+.dialog-footer button {
+  height: 32px;
+  margin: 0 4px;
+  min-width: 92px;
+}
+
+.dialog-footer paper-button[disabled],
+.dialog-footer paper-button[disabled]:hover,
+.dialog-footer button[disabled],
+.dialog-footer button[disabled]:hover {
+  background-color: rgb(234, 234, 234);
+  color: rgb(168, 168, 168);
+}
+
+.dialog-footer .primary,
+.dialog-footer .primary:hover {
+  background-color: rgb(27, 168, 243);
+  color: white;
+}
+
+.dialog-footer .secondary,
+.dialog-footer .secondary:hover {
+  color: rgb(27, 168, 243);
+}
+
+.dialog-footer input {
+  color: red;
+  margin: 3px 0 !important;
+}
+
+.dialog-footer paper-input-decorator {
+  color: rgb(51, 51, 51);
+  padding: 4px 0;
+  width: 100%;
+}
+
+.dialog-footer paper-input-decorator /deep/ .unfocused-underline {
+  background-color: rgb(207, 207, 207);
+}
+
+.dialog-footer paper-input-decorator /deep/ .focused-underline {
+  background-color: rgb(27, 168, 243);
+}
+
+.dialog-footer select {
+  -webkit-appearance: none;
+  background: -webkit-image-set(
+     url(../images/common/disclosure_arrow_dk_grey_down.png) 1x,
+     url(../images/common/2x/disclosure_arrow_dk_grey_down.png) 2x) no-repeat
+     right transparent;
+  border: 0;
+  border-bottom: 1px solid rgb(207, 207, 207);
+  border-radius: 0;
+  color: rgb(51, 51, 51);
+  cursor: pointer;
+  margin: 0 32px 0 36px;
+  outline: none;
+  padding: 0 12px 0 0;
+}
+
+.dialog-footer select:hover {
+  border-image: none;
+}
+
+/* Draw outline using box-shadow to make them rounded. */
+.dialog-footer .primary:focus,
+.dialog-footer .secondary:focus,
+.dialog-footer select:focus {
+  box-shadow: 0 0 0 2px rgba(27, 168, 243, 0.5);
 }
 
 .progressable:not([progress]) .progress-bar,
@@ -731,14 +830,6 @@
   -webkit-transform: scaleX(-1);
 }
 
-#filename-input-box input {
-  border: 1px solid #c8c8c8;
-  border-radius: 1px;
-  box-sizing: border-box;
-  height: 31px;  /* border-box */
-  margin-right: 30px;
-}
-
 .filelist-panel {
   display: flex;
   flex: auto;
@@ -872,8 +963,14 @@
 
 /* The cr.ui.Grid representing the detailed file list. */
 .thumbnail-grid {
+  /* On the right side, we have less margin to pack items as long as they are
+     fully visible. */
+  -webkit-padding-end: 2px;
+  -webkit-padding-start: 7px;
+  box-sizing: border-box;
   overflow-y: auto;
-  padding: 7px;
+  padding-bottom: 7px;
+  padding-top: 7px;
   width: 100%;
 }
 
@@ -892,7 +989,6 @@
   cursor: auto;
   display: flex;
   flex-direction: row;
-  justify-content: center;
   left: 0;
   padding: 0 10px;
   position: absolute;
@@ -907,7 +1003,7 @@
 
 .thumbnail-bottom .filename-label {
   flex: auto;
-  padding: 12px;
+  padding: 0 12px;
 }
 
 /* Styles specific for the grid view. */
@@ -921,6 +1017,7 @@
   margin-top: 8px;
   overflow: hidden;
   position: relative;
+  transition: box-shadow 220ms ease;
   width: 180px;
 }
 
@@ -964,12 +1061,14 @@
 }
 
 .thumbnail-grid .img-container {
+  background-color: rgb(230, 230, 230);
   height: 100%;
   width: 100%;
 }
 
 .thumbnail-grid .img-container > .thumbnail {
   -webkit-user-drag: none;
+  background-color: rgb(230, 230, 230);
   background-position: center;
   background-size: cover;
   height: 100%;
@@ -1003,14 +1102,14 @@
 
 .copied .badge {
   background-image: -webkit-image-set(
-      url(../images/files/ui/copied_badge.png) 1x,
-      url(../images/files/ui/2x/copied_badge.png) 2x);
+      url(../images/files/ui/cloud_import_syncing.png) 1x,
+      url(../images/files/ui/2x/cloud_import_syncing.png) 2x);
 }
 
 .imported .badge {
   background-image: -webkit-image-set(
-      url(../images/files/ui/drive_badge.png) 1x,
-      url(../images/files/ui/2x/drive_badge.png) 2x);
+      url(../images/files/ui/service_drive.png) 1x,
+      url(../images/files/ui/2x/service_drive.png) 2x);
   height: 16px;
   position: absolute;
   right: 7px;
@@ -1108,17 +1207,15 @@
 }
 
 li[renaming=''] .filename-label,
-li[renaming=''] .detail-icon,
 li[renaming=''] .badge {
   display: none;
 }
 
 /* Text box used for renaming in the thumbnail list. */
 .thumbnail-grid input.rename {
-  -webkit-margin-start: -1px;
+  -webkit-margin-start: 10px;
   box-sizing: border-box;
-  height: 20px;
-  width: 156px;
+  width: 100%;
 }
 
 /* The cr.ui.Table representing the detailed file list. */
@@ -1134,6 +1231,7 @@
 }
 
 .dialog-footer > .right {
+  align-items: center;
   flex: none;
   justify-content: flex-end;
 }
@@ -1271,6 +1369,7 @@
 
 #list-container list li .detail-thumbnail {
   -webkit-user-drag: none;
+  background-color: rgb(245, 245, 245);
   background-position: center;
   background-size: cover;
   border-radius: 14px;
@@ -1312,18 +1411,6 @@
   flex: auto;
 }
 
-#filename-input-box input {
-  flex: auto;
-  padding: 1px 2px;
-}
-
-#filename-input-box .filename-label {
-  background-color: white;
-  color: #333;
-  flex: none;
-  padding-right: 4px;
-}
-
 body:not([type='saveas-file']) #filename-input-box {
   display: none;
 }
@@ -1378,10 +1465,16 @@
   white-space: nowrap;
 }
 
-cr-menu.file-context-menu {
+/* TODO(fukino): Gather menu-related definitions into one place. */
+cr-menu#file-context-menu {
+  min-width: 208px;
   z-index: 600;  /* Must be below the overlay pane (1000). */
 }
 
+cr-menu#file-context-menu > :not(hr) {
+  -webkit-padding-end: 8px;
+}
+
 cr-menu.chrome-menu hr {
   color: transparent;
   font-size: 0;
@@ -1526,34 +1619,35 @@
 }
 
 list.autocomplete-suggestions {
-  -webkit-margin-start: -38px;
-  background-color: rgb(250, 250, 250);
+  -webkit-margin-start: -36px;
+  background-color: white;
   border-radius: 3px;
   box-shadow: 0 1px 4px 0 rgba(0, 0, 0, .5);
   box-sizing: border-box;  /* To match the width with the search box's. */
-  color: rgb(34, 34, 34);
+  color: rgb(90, 90, 90);
   flex: none;
   margin-top: 10px;
   overflow: hidden;
-  padding: 5px 0;
+  padding: 0;
   position: fixed;
-  width: 300px !important; /* This overrides the value specified by script. */
+  width: 260px !important; /* This overrides the value specified by script. */
   z-index: 550;
 }
 
 list.autocomplete-suggestions > li {
   align-items: center;
   display: flex;
-  padding: 3px 0;
+  height: 40px;
 }
 
 list.autocomplete-suggestions > li > div.detail-icon {
-  -webkit-margin-end: 6px;
-  -webkit-margin-start: 6px;
+  margin: 0 3px;
+  flex: none;
 }
 
 list.autocomplete-suggestions > li > div.detail-text {
   flex: auto;
+  margin: 0 4px;
   overflow-x: hidden;
   text-overflow: ellipsis;
 }
@@ -1565,26 +1659,13 @@
 
 list.autocomplete-suggestions > li > div[search-icon] {
   background: -webkit-image-set(
-      url(../images/files/ui/search_icon_active.png) 1x,
-      url(../images/files/ui/2x/search_icon_active.png) 2x) center no-repeat;
-}
-
-list.autocomplete-suggestions > li[selected] > div[search-icon],
-list.autocomplete-suggestions > li[lead] > div[search-icon] {
-  background: -webkit-image-set(
-      url(../images/files/ui/search_icon_white.png) 1x,
-      url(../images/files/ui/2x/search_icon_white.png) 2x) center no-repeat;
+      url(../images/files/ui/search.png) 1x,
+      url(../images/files/ui/2x/search.png) 2x) center no-repeat;
 }
 
 list.autocomplete-suggestions > [selected],
 list.autocomplete-suggestions > [lead] {
-  background-color: rgb(66, 129, 244);
-  color: white;
-}
-
-list.autocomplete-suggestions > [selected] > div.detail-text em,
-list.autocomplete-suggestions > [lead] > div.detail-text em {
-  color: white;
+  background-color: rgb(222, 243, 254);
 }
 
 #gear-menu {
diff --git a/ui/file_manager/file_manager/foreground/css/file_status.css b/ui/file_manager/file_manager/foreground/css/file_status.css
index 7f31f9b..3c182bc 100644
--- a/ui/file_manager/file_manager/foreground/css/file_status.css
+++ b/ui/file_manager/file_manager/foreground/css/file_status.css
@@ -12,28 +12,12 @@
 
 [file-status-icon='copied'] {
   background-image: -webkit-image-set(
-      url(../images/files/ui/copied_badge_gray.png) 1x,
-      url(../images/files/ui/2x/copied_badge_gray.png) 2x);
-}
-
-tree:focus .tree-item[selected] > .tree-row > [file-status-icon='copied'],
-list:focus [selected] [file-status-icon='copied'],
-list.autocomplete-suggestions [selected] [file-status-icon='copied'] {
-  background-image: -webkit-image-set(
-      url(../images/files/ui/copied_badge.png) 1x,
-      url(../images/files/ui/2x/copied_badge.png) 2x);
+      url(../images/files/ui/cloud_import_syncing.png) 1x,
+      url(../images/files/ui/2x/cloud_import_syncing.png) 2x);
 }
 
 [file-status-icon='imported'] {
   background-image: -webkit-image-set(
-      url(../images/files/ui/drive_badge.png) 1x,
-      url(../images/files/ui/2x/drive_badge.png) 2x);
-}
-
-tree:focus .tree-item[selected] > .tree-row > [file-status-icon='imported'],
-list:focus [selected] [file-status-icon='imported'],
-list.autocomplete-suggestions [selected] [file-status-icon='imported'] {
-  background-image: -webkit-image-set(
-      url(../images/files/ui/drive_badge_white.png) 1x,
-      url(../images/files/ui/2x/drive_badge_white.png) 2x);
+      url(../images/files/ui/service_drive.png) 1x,
+      url(../images/files/ui/2x/service_drive.png) 2x);
 }
diff --git a/ui/file_manager/file_manager/foreground/css/file_types.css b/ui/file_manager/file_manager/foreground/css/file_types.css
index 3e0ef2e4..1f74079 100644
--- a/ui/file_manager/file_manager/foreground/css/file_types.css
+++ b/ui/file_manager/file_manager/foreground/css/file_types.css
@@ -5,269 +5,150 @@
 /* Small icons for file types, used in lists and menus. */
 [file-type-icon] {
   background-image: -webkit-image-set(
-      url(chrome://theme/IDR_FILETYPE_GENERIC) 1x,
-      url(chrome://theme/IDR_FILETYPE_GENERIC@2x) 2x);
+      url(../images/filetype/filetype_generic.png) 1x,
+      url(../images/filetype/2x/filetype_generic.png) 2x);
   background-position: center;
   background-repeat: no-repeat;
   background-size: 16px 16px;
 }
 
-list.autocomplete-suggestions [selected] [file-type-icon] {
-  background-image: -webkit-image-set(
-      url(chrome://theme/IDR_FILETYPE_GENERIC_WHITE) 1x,
-      url(chrome://theme/IDR_FILETYPE_GENERIC_WHITE@2x) 2x);
-}
-
 [file-type-icon='archive'] {
   background-image: -webkit-image-set(
-      url(chrome://theme/IDR_FILETYPE_ARCHIVE) 1x,
-      url(chrome://theme/IDR_FILETYPE_ARCHIVE@2x) 2x);
-}
-
-list.autocomplete-suggestions [selected] [file-type-icon='archive'] {
-  background-image: -webkit-image-set(
-      url(chrome://theme/IDR_FILETYPE_ARCHIVE_WHITE) 1x,
-      url(chrome://theme/IDR_FILETYPE_ARCHIVE_WHITE@2x) 2x);
+      url(../images/filetype/filetype_archive.png) 1x,
+      url(../images/filetype/2x/filetype_archive.png) 2x);
 }
 
 [file-type-icon='audio'] {
   background-image: -webkit-image-set(
-      url(chrome://theme/IDR_FILETYPE_AUDIO) 1x,
-      url(chrome://theme/IDR_FILETYPE_AUDIO@2x) 2x);
-}
-
-list.autocomplete-suggestions [selected] [file-type-icon='audio'] {
-  background-image: -webkit-image-set(
-      url(chrome://theme/IDR_FILETYPE_AUDIO_WHITE) 1x,
-      url(chrome://theme/IDR_FILETYPE_AUDIO_WHITE@2x) 2x);
+      url(../images/filetype/filetype_audio.png) 1x,
+      url(../images/filetype/2x/filetype_audio.png) 2x);
 }
 
 [file-type-icon='excel'] {
   background-image: -webkit-image-set(
-      url(chrome://theme/IDR_FILETYPE_EXCEL) 1x,
-      url(chrome://theme/IDR_FILETYPE_EXCEL@2x) 2x);
-}
-
-list.autocomplete-suggestions [selected] [file-type-icon='excel'] {
-  background-image: -webkit-image-set(
-      url(chrome://theme/IDR_FILETYPE_EXCEL_WHITE) 1x,
-      url(chrome://theme/IDR_FILETYPE_EXCEL_WHITE@2x) 2x);
+      url(../images/filetype/filetype_excel.png) 1x,
+      url(../images/filetype/2x/filetype_excel.png) 2x);
 }
 
 [file-type-icon='folder'] {
   background-image: -webkit-image-set(
-      url(chrome://theme/IDR_FILETYPE_FOLDER) 1x,
-      url(chrome://theme/IDR_FILETYPE_FOLDER@2x) 2x);
+      url(../images/filetype/filetype_folder.png) 1x,
+      url(../images/filetype/2x/filetype_folder.png) 2x);
 }
 
 .shared[file-type-icon='folder'] {
   background-image: -webkit-image-set(
-      url(chrome://theme/IDR_FILETYPE_FOLDER_SHARED) 1x,
-      url(chrome://theme/IDR_FILETYPE_FOLDER_SHARED@2x) 2x);
+      url(../images/filetype/filetype_folder_shared.png) 1x,
+      url(../images/filetype/2x/filetype_folder_shared.png) 2x);
 }
 
-list.autocomplete-suggestions [selected] .shared[file-type-icon='folder'] {
+tree .tree-item[selected] > .tree-row > [file-type-icon='folder'] {
   background-image: -webkit-image-set(
-      url(chrome://theme/IDR_FILETYPE_FOLDER_SHARED_WHITE) 1x,
-      url(chrome://theme/IDR_FILETYPE_FOLDER_SHARED_WHITE@2x) 2x);
+      url(../images/filetype/filetype_folder_active.png) 1x,
+      url(../images/filetype/2x/filetype_folder_active.png) 2x);
 }
 
-list.autocomplete-suggestions [selected] [file-type-icon='folder'] {
+tree .tree-item[selected] > .tree-row > .shared[file-type-icon='folder'] {
   background-image: -webkit-image-set(
-      url(chrome://theme/IDR_FILETYPE_FOLDER_WHITE) 1x,
-      url(chrome://theme/IDR_FILETYPE_FOLDER_WHITE@2x) 2x);
+      url(../images/filetype/filetype_folder_shared_active.png) 1x,
+      url(../images/filetype/2x/filetype_folder_shared_active.png) 2x);
 }
 
+/* TODO(fukino): Check if 'form' is in use, and remove this if possible. */
 [file-type-icon='form'] {
   background-image: -webkit-image-set(
-      url(chrome://theme/IDR_FILETYPE_FORM) 1x,
-      url(chrome://theme/IDR_FILETYPE_FORM@2x) 2x);
-}
-
-list.autocomplete-suggestions [selected] [file-type-icon='form'] {
-  background-image: -webkit-image-set(
-      url(chrome://theme/IDR_FILETYPE_FORM_WHITE) 1x,
-      url(chrome://theme/IDR_FILETYPE_FORM_WHITE@2x) 2x);
+      url(../images/filetype/filetype_form.png) 1x,
+      url(../images/filetype/2x/filetype_form.png) 2x);
 }
 
 [file-type-icon='gdoc'] {
   background-image: -webkit-image-set(
-      url(chrome://theme/IDR_FILETYPE_GDOC) 1x,
-      url(chrome://theme/IDR_FILETYPE_GDOC@2x) 2x);
-}
-
-list.autocomplete-suggestions [selected] [file-type-icon='gdoc'] {
-  background-image: -webkit-image-set(
-      url(chrome://theme/IDR_FILETYPE_GDOC_WHITE) 1x,
-      url(chrome://theme/IDR_FILETYPE_GDOC_WHITE@2x) 2x);
+      url(../images/filetype/filetype_gdoc.png) 1x,
+      url(../images/filetype/2x/filetype_gdoc.png) 2x);
 }
 
 [file-type-icon='gdraw'] {
   background-image: -webkit-image-set(
-      url(chrome://theme/IDR_FILETYPE_GDRAW) 1x,
-      url(chrome://theme/IDR_FILETYPE_GDRAW@2x) 2x);
-}
-
-list.autocomplete-suggestions [selected] [file-type-icon='gdraw'] {
-  background-image: -webkit-image-set(
-      url(chrome://theme/IDR_FILETYPE_GDRAW_WHITE) 1x,
-      url(chrome://theme/IDR_FILETYPE_GDRAW_WHITE@2x) 2x);
+      url(../images/filetype/filetype_gdraw.png) 1x,
+      url(../images/filetype/2x/filetype_gdraw.png) 2x);
 }
 
 [file-type-icon='glink'] {
   background-image: -webkit-image-set(
-      url(chrome://theme/IDR_FILETYPE_GLINK) 1x,
-      url(chrome://theme/IDR_FILETYPE_GLINK@2x) 2x);
-}
-
-list.autocomplete-suggestions [selected] [file-type-icon='glink'] {
-  background-image: -webkit-image-set(
-      url(chrome://theme/IDR_FILETYPE_GLINK_WHITE) 1x,
-      url(chrome://theme/IDR_FILETYPE_GLINK_WHITE@2x) 2x);
+      url(../images/filetype/filetype_generic.png) 1x,
+      url(../images/filetype/2x/filetype_generic.png) 2x);
 }
 
 [file-type-icon='gsheet'] {
   background-image: -webkit-image-set(
-      url(chrome://theme/IDR_FILETYPE_GSHEET) 1x,
-      url(chrome://theme/IDR_FILETYPE_GSHEET@2x) 2x);
-}
-
-list.autocomplete-suggestions [selected] [file-type-icon='gsheet'] {
-  background-image: -webkit-image-set(
-      url(chrome://theme/IDR_FILETYPE_GSHEET_WHITE) 1x,
-      url(chrome://theme/IDR_FILETYPE_GSHEET_WHITE@2x) 2x);
+      url(../images/filetype/filetype_gsheet.png) 1x,
+      url(../images/filetype/2x/filetype_gsheet.png) 2x);
 }
 
 [file-type-icon='gslides'] {
   background-image: -webkit-image-set(
-      url(chrome://theme/IDR_FILETYPE_GSLIDES) 1x,
-      url(chrome://theme/IDR_FILETYPE_GSLIDES@2x) 2x);
-}
-
-list.autocomplete-suggestions [selected] [file-type-icon='gslides'] {
-  background-image: -webkit-image-set(
-      url(chrome://theme/IDR_FILETYPE_GSLIDES_WHITE) 1x,
-      url(chrome://theme/IDR_FILETYPE_GSLIDES_WHITE@2x) 2x);
+      url(../images/filetype/filetype_gslides.png) 1x,
+      url(../images/filetype/2x/filetype_gslides.png) 2x);
 }
 
 [file-type-icon='gtable'] {
   background-image: -webkit-image-set(
-      url(chrome://theme/IDR_FILETYPE_GTABLE) 1x,
-      url(chrome://theme/IDR_FILETYPE_GTABLE@2x) 2x);
-}
-
-list.autocomplete-suggestions [selected] [file-type-icon='gtable'] {
-  background-image: -webkit-image-set(
-      url(chrome://theme/IDR_FILETYPE_GTABLE_WHITE) 1x,
-      url(chrome://theme/IDR_FILETYPE_GTABLE_WHITE@2x) 2x);
+      url(../images/filetype/filetype_gtable.png) 1x,
+      url(../images/filetype/2x/filetype_gtable.png) 2x);
 }
 
 [file-type-icon='gform'] {
   background-image: -webkit-image-set(
-      url(chrome://theme/IDR_FILETYPE_FORM) 1x,
-      url(chrome://theme/IDR_FILETYPE_FORM@2x) 2x);
-}
-
-list.autocomplete-suggestions [selected] [file-type-icon='gform'] {
-  background-image: -webkit-image-set(
-      url(chrome://theme/IDR_FILETYPE_FORM_WHITE) 1x,
-      url(chrome://theme/IDR_FILETYPE_FORM_WHITE@2x) 2x);
+      url(../images/filetype/filetype_form.png) 1x,
+      url(../images/filetype/2x/filetype_form.png) 2x);
 }
 
 [file-type-icon='gmap'] {
   background-image: -webkit-image-set(
-      url(chrome://theme/IDR_FILETYPE_GMAP) 1x,
-      url(chrome://theme/IDR_FILETYPE_GMAP@2x) 2x);
-}
-
-list.autocomplete-suggestions [selected] [file-type-icon='gmap'] {
-  background-image: -webkit-image-set(
-      url(chrome://theme/IDR_FILETYPE_GMAP_WHITE) 1x,
-      url(chrome://theme/IDR_FILETYPE_GMAP_WHITE@2x) 2x);
+      url(../images/filetype/filetype_map.png) 1x,
+      url(../images/filetype/2x/filetype_map.png) 2x);
 }
 
 [file-type-icon='image'] {
   background-image: -webkit-image-set(
-      url(chrome://theme/IDR_FILETYPE_IMAGE) 1x,
-      url(chrome://theme/IDR_FILETYPE_IMAGE@2x) 2x);
-}
-
-list.autocomplete-suggestions [selected] [file-type-icon='image'] {
-  background-image: -webkit-image-set(
-      url(chrome://theme/IDR_FILETYPE_IMAGE_WHITE) 1x,
-      url(chrome://theme/IDR_FILETYPE_IMAGE_WHITE@2x) 2x);
+      url(../images/filetype/filetype_image.png) 1x,
+      url(../images/filetype/2x/filetype_image.png) 2x);
 }
 
 [file-type-icon='pdf'] {
   background-image: -webkit-image-set(
-      url(chrome://theme/IDR_FILETYPE_PDF) 1x,
-      url(chrome://theme/IDR_FILETYPE_PDF@2x) 2x);
-}
-
-list.autocomplete-suggestions [selected] [file-type-icon='pdf'] {
-  background-image: -webkit-image-set(
-      url(chrome://theme/IDR_FILETYPE_PDF_WHITE) 1x,
-      url(chrome://theme/IDR_FILETYPE_PDF_WHITE@2x) 2x);
+      url(../images/filetype/filetype_pdf.png) 1x,
+      url(../images/filetype/2x/filetype_pdf.png) 2x);
 }
 
 [file-type-icon='ppt'] {
   background-image: -webkit-image-set(
-      url(chrome://theme/IDR_FILETYPE_PPT) 1x,
-      url(chrome://theme/IDR_FILETYPE_PPT@2x) 2x);
-}
-
-list.autocomplete-suggestions [selected] [file-type-icon='ppt'] {
-  background-image: -webkit-image-set(
-      url(chrome://theme/IDR_FILETYPE_PPT_WHITE) 1x,
-      url(chrome://theme/IDR_FILETYPE_PPT_WHITE@2x) 2x);
+      url(../images/filetype/filetype_ppt.png) 1x,
+      url(../images/filetype/2x/filetype_ppt.png) 2x);
 }
 
 [file-type-icon='script'] {
   background-image: -webkit-image-set(
-      url(chrome://theme/IDR_FILETYPE_SCRIPT) 1x,
-      url(chrome://theme/IDR_FILETYPE_SCRIPT@2x) 2x);
-}
-
-list.autocomplete-suggestions [selected] [file-type-icon='script'] {
-  background-image: -webkit-image-set(
-      url(chrome://theme/IDR_FILETYPE_SCRIPT_WHITE) 1x,
-      url(chrome://theme/IDR_FILETYPE_SCRIPT_WHITE@2x) 2x);
+      url(../images/filetype/filetype_script.png) 1x,
+      url(../images/filetype/2x/filetype_script.png) 2x);
 }
 
 [file-type-icon='sites'] {
   background-image: -webkit-image-set(
-      url(chrome://theme/IDR_FILETYPE_SITES) 1x,
-      url(chrome://theme/IDR_FILETYPE_SITES@2x) 2x);
-}
-
-list.autocomplete-suggestions [selected] [file-type-icon='sites'] {
-  background-image: -webkit-image-set(
-      url(chrome://theme/IDR_FILETYPE_SITES_WHITE) 1x,
-      url(chrome://theme/IDR_FILETYPE_SITES_WHITE@2x) 2x);
+      url(../images/filetype/filetype_sites.png) 1x,
+      url(../images/filetype/2x/filetype_sites.png) 2x);
 }
 
 [file-type-icon='video'] {
   background-image: -webkit-image-set(
-      url(chrome://theme/IDR_FILETYPE_VIDEO) 1x,
-      url(chrome://theme/IDR_FILETYPE_VIDEO@2x) 2x);
-}
-
-list.autocomplete-suggestions [selected] [file-type-icon='video'] {
-  background-image: -webkit-image-set(
-      url(chrome://theme/IDR_FILETYPE_VIDEO_WHITE) 1x,
-      url(chrome://theme/IDR_FILETYPE_VIDEO_WHITE@2x) 2x);
+      url(../images/filetype/filetype_video.png) 1x,
+      url(../images/filetype/2x/filetype_video.png) 2x);
 }
 
 [file-type-icon='word'] {
   background-image: -webkit-image-set(
-      url(chrome://theme/IDR_FILETYPE_WORD) 1x,
-      url(chrome://theme/IDR_FILETYPE_WORD@2x) 2x);
-}
-
-list.autocomplete-suggestions [selected] [file-type-icon='word'] {
-  background-image: -webkit-image-set(
-      url(chrome://theme/IDR_FILETYPE_WORD_WHITE) 1x,
-      url(chrome://theme/IDR_FILETYPE_WORD_WHITE@2x) 2x);
+      url(../images/filetype/filetype_word.png) 1x,
+      url(../images/filetype/2x/filetype_word.png) 2x);
 }
 
 [file-type-icon='drive'] {
@@ -279,40 +160,40 @@
 /* Large generic thumbnails, used when a file does not have a thumbnail. */
 [generic-thumbnail] {
   background-image: -webkit-image-set(
-      url(chrome://theme/IDR_FILETYPE_LARGE_GENERIC) 1x,
-      url(chrome://theme/IDR_FILETYPE_LARGE_GENERIC@2x) 2x);
-  background-position: center center;
+      url(../images/files/ui/filetype_placeholder_generic.png) 1x,
+      url(../images/files/ui/2x/filetype_placeholder_generic.png) 2x);
+  background-position: center 50px;
   background-repeat: no-repeat;
 }
 
 [generic-thumbnail='audio'] {
   background-image: -webkit-image-set(
-      url(chrome://theme/IDR_FILETYPE_LARGE_AUDIO) 1x,
-      url(chrome://theme/IDR_FILETYPE_LARGE_AUDIO@2x) 2x);
+      url(../images/files/ui/filetype_placeholder_audio.png) 1x,
+      url(../images/files/ui/2x/filetype_placeholder_audio.png) 2x);
 }
 
 [generic-thumbnail='folder'] {
   background-image: -webkit-image-set(
-      url(chrome://theme/IDR_FILETYPE_LARGE_FOLDER) 1x,
-      url(chrome://theme/IDR_FILETYPE_LARGE_FOLDER@2x) 2x);
+      url(../images/files/ui/filetype_placeholder_folder.png) 1x,
+      url(../images/files/ui/2x/filetype_placeholder_folder.png) 2x);
 }
 
 .shared[generic-thumbnail='folder'] {
   background-image: -webkit-image-set(
-      url(chrome://theme/IDR_FILETYPE_LARGE_FOLDER_SHARED) 1x,
-      url(chrome://theme/IDR_FILETYPE_LARGE_FOLDER_SHARED@2x) 2x);
+      url(../images/files/ui/filetype_placeholder_folder_shared.png) 1x,
+      url(../images/files/ui/2x/filetype_placeholder_folder_shared.png) 2x);
 }
 
 [generic-thumbnail='image'] {
   background-image: -webkit-image-set(
-      url(chrome://theme/IDR_FILETYPE_LARGE_IMAGE) 1x,
-      url(chrome://theme/IDR_FILETYPE_LARGE_IMAGE@2x) 2x);
+      url(../images/files/ui/filetype_placeholder_image.png) 1x,
+      url(../images/files/ui/2x/filetype_placeholder_image.png) 2x);
 }
 
 [generic-thumbnail='video'] {
   background-image: -webkit-image-set(
-      url(chrome://theme/IDR_FILETYPE_LARGE_VIDEO) 1x,
-      url(chrome://theme/IDR_FILETYPE_LARGE_VIDEO@2x) 2x);
+      url(../images/files/ui/filetype_placeholder_video.png) 1x,
+      url(../images/files/ui/2x/filetype_placeholder_video.png) 2x);
 }
 
 /* Icons for volume types. */
diff --git a/ui/file_manager/file_manager/foreground/css/menu.css b/ui/file_manager/file_manager/foreground/css/menu.css
index cde2c33..4045a62 100644
--- a/ui/file_manager/file_manager/foreground/css/menu.css
+++ b/ui/file_manager/file_manager/foreground/css/menu.css
@@ -32,7 +32,7 @@
 
 cr-menu > [shortcutText]::after {
   -webkit-padding-start: 30px;
-  color: #999;
+  color: rgba(51, 51, 51, 0.5);
   content: attr(shortcutText);
   float: right;
 }
diff --git a/ui/file_manager/file_manager/foreground/css/tree.css b/ui/file_manager/file_manager/foreground/css/tree.css
index 38891fc..a319a94e 100644
--- a/ui/file_manager/file_manager/foreground/css/tree.css
+++ b/ui/file_manager/file_manager/foreground/css/tree.css
@@ -18,24 +18,29 @@
 }
 
 .tree-row > .expand-icon {
+  -webkit-transform: rotate(-90deg);
+  -webkit-transition: all 150ms;
+  background-image: -webkit-image-set(
+      url(../images/files/ui/expand_more.png) 1x,
+      url(../images/files/ui/2x/expand_more.png) 2x);
+  background-position: 50% 50%;
+  background-repeat: no-repeat;
+  background-size: 16px 16px;
+  vertical-align: top;
   position: relative;
 }
 
-.tree-row > .expand-icon > core-icon {
-  -webkit-transform: rotate(-90deg);
-  -webkit-transition: all 150ms;
-  height: 16px;
-  left: 10px;
-  position: absolute;
-  top: 10px;
-  width: 16px;
+.tree-row[selected] > .expand-icon {
+  background-image: -webkit-image-set(
+      url(../images/files/ui/expand_more_active.png) 1x,
+      url(../images/files/ui/2x/expand_more_active.png) 2x);
 }
 
-html[dir=rtl] .tree-row > .expand-icon > core-icon {
+html[dir=rtl] .tree-row > .expand-icon {
   -webkit-transform: rotate(90deg);
 }
 
-.tree-item[expanded] > .tree-row > .expand-icon > core-icon {
+.tree-item[expanded] > .tree-row > .expand-icon {
   -webkit-transform: rotate(0);
 }
 
diff --git a/ui/file_manager/file_manager/foreground/images/common/2x/disclosure_arrow_dk_grey_down.png b/ui/file_manager/file_manager/foreground/images/common/2x/disclosure_arrow_dk_grey_down.png
new file mode 100644
index 0000000..e37ee476
--- /dev/null
+++ b/ui/file_manager/file_manager/foreground/images/common/2x/disclosure_arrow_dk_grey_down.png
Binary files differ
diff --git a/ui/file_manager/file_manager/foreground/images/common/disclosure_arrow_dk_grey_down.png b/ui/file_manager/file_manager/foreground/images/common/disclosure_arrow_dk_grey_down.png
new file mode 100644
index 0000000..a8e5c6a
--- /dev/null
+++ b/ui/file_manager/file_manager/foreground/images/common/disclosure_arrow_dk_grey_down.png
Binary files differ
diff --git a/ui/file_manager/file_manager/foreground/images/files/ui/2x/cloud_import_syncing.png b/ui/file_manager/file_manager/foreground/images/files/ui/2x/cloud_import_syncing.png
new file mode 100644
index 0000000..e502153
--- /dev/null
+++ b/ui/file_manager/file_manager/foreground/images/files/ui/2x/cloud_import_syncing.png
Binary files differ
diff --git a/ui/file_manager/file_manager/foreground/images/files/ui/2x/eject.png b/ui/file_manager/file_manager/foreground/images/files/ui/2x/eject.png
index f764c0c4..182c21e 100644
--- a/ui/file_manager/file_manager/foreground/images/files/ui/2x/eject.png
+++ b/ui/file_manager/file_manager/foreground/images/files/ui/2x/eject.png
Binary files differ
diff --git a/ui/file_manager/file_manager/foreground/images/files/ui/2x/eject_active.png b/ui/file_manager/file_manager/foreground/images/files/ui/2x/eject_active.png
new file mode 100644
index 0000000..ed88420
--- /dev/null
+++ b/ui/file_manager/file_manager/foreground/images/files/ui/2x/eject_active.png
Binary files differ
diff --git a/ui/file_manager/file_manager/foreground/images/files/ui/2x/expand_less.png b/ui/file_manager/file_manager/foreground/images/files/ui/2x/expand_less.png
new file mode 100644
index 0000000..b90ff2f
--- /dev/null
+++ b/ui/file_manager/file_manager/foreground/images/files/ui/2x/expand_less.png
Binary files differ
diff --git a/ui/file_manager/file_manager/foreground/images/files/ui/2x/expand_more.png b/ui/file_manager/file_manager/foreground/images/files/ui/2x/expand_more.png
index 05155a0..f7b8376b 100644
--- a/ui/file_manager/file_manager/foreground/images/files/ui/2x/expand_more.png
+++ b/ui/file_manager/file_manager/foreground/images/files/ui/2x/expand_more.png
Binary files differ
diff --git a/ui/file_manager/file_manager/foreground/images/files/ui/2x/expand_more_active.png b/ui/file_manager/file_manager/foreground/images/files/ui/2x/expand_more_active.png
new file mode 100644
index 0000000..a83516e
--- /dev/null
+++ b/ui/file_manager/file_manager/foreground/images/files/ui/2x/expand_more_active.png
Binary files differ
diff --git a/ui/file_manager/file_manager/foreground/images/files/ui/2x/filetype_placeholder_audio.png b/ui/file_manager/file_manager/foreground/images/files/ui/2x/filetype_placeholder_audio.png
new file mode 100644
index 0000000..d89e57d
--- /dev/null
+++ b/ui/file_manager/file_manager/foreground/images/files/ui/2x/filetype_placeholder_audio.png
Binary files differ
diff --git a/ui/file_manager/file_manager/foreground/images/files/ui/2x/filetype_placeholder_folder.png b/ui/file_manager/file_manager/foreground/images/files/ui/2x/filetype_placeholder_folder.png
new file mode 100644
index 0000000..e9122f28
--- /dev/null
+++ b/ui/file_manager/file_manager/foreground/images/files/ui/2x/filetype_placeholder_folder.png
Binary files differ
diff --git a/ui/file_manager/file_manager/foreground/images/files/ui/2x/filetype_placeholder_folder_shared.png b/ui/file_manager/file_manager/foreground/images/files/ui/2x/filetype_placeholder_folder_shared.png
new file mode 100644
index 0000000..9ec5308
--- /dev/null
+++ b/ui/file_manager/file_manager/foreground/images/files/ui/2x/filetype_placeholder_folder_shared.png
Binary files differ
diff --git a/ui/file_manager/file_manager/foreground/images/files/ui/2x/filetype_placeholder_generic.png b/ui/file_manager/file_manager/foreground/images/files/ui/2x/filetype_placeholder_generic.png
new file mode 100644
index 0000000..b3929f9
--- /dev/null
+++ b/ui/file_manager/file_manager/foreground/images/files/ui/2x/filetype_placeholder_generic.png
Binary files differ
diff --git a/ui/file_manager/file_manager/foreground/images/files/ui/2x/filetype_placeholder_image.png b/ui/file_manager/file_manager/foreground/images/files/ui/2x/filetype_placeholder_image.png
new file mode 100644
index 0000000..4bc9cf0
--- /dev/null
+++ b/ui/file_manager/file_manager/foreground/images/files/ui/2x/filetype_placeholder_image.png
Binary files differ
diff --git a/ui/file_manager/file_manager/foreground/images/files/ui/2x/filetype_placeholder_video.png b/ui/file_manager/file_manager/foreground/images/files/ui/2x/filetype_placeholder_video.png
new file mode 100644
index 0000000..93ffe41
--- /dev/null
+++ b/ui/file_manager/file_manager/foreground/images/files/ui/2x/filetype_placeholder_video.png
Binary files differ
diff --git a/ui/file_manager/file_manager/foreground/images/files/ui/2x/service_drive.png b/ui/file_manager/file_manager/foreground/images/files/ui/2x/service_drive.png
new file mode 100644
index 0000000..199629b
--- /dev/null
+++ b/ui/file_manager/file_manager/foreground/images/files/ui/2x/service_drive.png
Binary files differ
diff --git a/ui/file_manager/file_manager/foreground/images/files/ui/cloud_import_syncing.png b/ui/file_manager/file_manager/foreground/images/files/ui/cloud_import_syncing.png
new file mode 100644
index 0000000..2bd6c7b
--- /dev/null
+++ b/ui/file_manager/file_manager/foreground/images/files/ui/cloud_import_syncing.png
Binary files differ
diff --git a/ui/file_manager/file_manager/foreground/images/files/ui/eject.png b/ui/file_manager/file_manager/foreground/images/files/ui/eject.png
index dbbdb7f..373290f6 100644
--- a/ui/file_manager/file_manager/foreground/images/files/ui/eject.png
+++ b/ui/file_manager/file_manager/foreground/images/files/ui/eject.png
Binary files differ
diff --git a/ui/file_manager/file_manager/foreground/images/files/ui/eject_active.png b/ui/file_manager/file_manager/foreground/images/files/ui/eject_active.png
new file mode 100644
index 0000000..caa6f53
--- /dev/null
+++ b/ui/file_manager/file_manager/foreground/images/files/ui/eject_active.png
Binary files differ
diff --git a/ui/file_manager/file_manager/foreground/images/files/ui/expand_less.png b/ui/file_manager/file_manager/foreground/images/files/ui/expand_less.png
new file mode 100644
index 0000000..5baf9d3
--- /dev/null
+++ b/ui/file_manager/file_manager/foreground/images/files/ui/expand_less.png
Binary files differ
diff --git a/ui/file_manager/file_manager/foreground/images/files/ui/expand_more.png b/ui/file_manager/file_manager/foreground/images/files/ui/expand_more.png
index e7aa0be..774752ff 100644
--- a/ui/file_manager/file_manager/foreground/images/files/ui/expand_more.png
+++ b/ui/file_manager/file_manager/foreground/images/files/ui/expand_more.png
Binary files differ
diff --git a/ui/file_manager/file_manager/foreground/images/files/ui/expand_more_active.png b/ui/file_manager/file_manager/foreground/images/files/ui/expand_more_active.png
new file mode 100644
index 0000000..40fa0a4
--- /dev/null
+++ b/ui/file_manager/file_manager/foreground/images/files/ui/expand_more_active.png
Binary files differ
diff --git a/ui/file_manager/file_manager/foreground/images/files/ui/filetype_placeholder_audio.png b/ui/file_manager/file_manager/foreground/images/files/ui/filetype_placeholder_audio.png
new file mode 100644
index 0000000..ab804e8
--- /dev/null
+++ b/ui/file_manager/file_manager/foreground/images/files/ui/filetype_placeholder_audio.png
Binary files differ
diff --git a/ui/file_manager/file_manager/foreground/images/files/ui/filetype_placeholder_folder.png b/ui/file_manager/file_manager/foreground/images/files/ui/filetype_placeholder_folder.png
new file mode 100644
index 0000000..7ea2417
--- /dev/null
+++ b/ui/file_manager/file_manager/foreground/images/files/ui/filetype_placeholder_folder.png
Binary files differ
diff --git a/ui/file_manager/file_manager/foreground/images/files/ui/filetype_placeholder_folder_shared.png b/ui/file_manager/file_manager/foreground/images/files/ui/filetype_placeholder_folder_shared.png
new file mode 100644
index 0000000..afa9ac6
--- /dev/null
+++ b/ui/file_manager/file_manager/foreground/images/files/ui/filetype_placeholder_folder_shared.png
Binary files differ
diff --git a/ui/file_manager/file_manager/foreground/images/files/ui/filetype_placeholder_generic.png b/ui/file_manager/file_manager/foreground/images/files/ui/filetype_placeholder_generic.png
new file mode 100644
index 0000000..26e51167a
--- /dev/null
+++ b/ui/file_manager/file_manager/foreground/images/files/ui/filetype_placeholder_generic.png
Binary files differ
diff --git a/ui/file_manager/file_manager/foreground/images/files/ui/filetype_placeholder_image.png b/ui/file_manager/file_manager/foreground/images/files/ui/filetype_placeholder_image.png
new file mode 100644
index 0000000..9f55645
--- /dev/null
+++ b/ui/file_manager/file_manager/foreground/images/files/ui/filetype_placeholder_image.png
Binary files differ
diff --git a/ui/file_manager/file_manager/foreground/images/files/ui/filetype_placeholder_video.png b/ui/file_manager/file_manager/foreground/images/files/ui/filetype_placeholder_video.png
new file mode 100644
index 0000000..2287cefaf
--- /dev/null
+++ b/ui/file_manager/file_manager/foreground/images/files/ui/filetype_placeholder_video.png
Binary files differ
diff --git a/ui/file_manager/file_manager/foreground/images/files/ui/service_drive.png b/ui/file_manager/file_manager/foreground/images/files/ui/service_drive.png
new file mode 100644
index 0000000..e54508bd
--- /dev/null
+++ b/ui/file_manager/file_manager/foreground/images/files/ui/service_drive.png
Binary files differ
diff --git a/ui/file_manager/file_manager/foreground/images/filetype/2x/filetype_archive.png b/ui/file_manager/file_manager/foreground/images/filetype/2x/filetype_archive.png
new file mode 100644
index 0000000..eacd06e
--- /dev/null
+++ b/ui/file_manager/file_manager/foreground/images/filetype/2x/filetype_archive.png
Binary files differ
diff --git a/ui/file_manager/file_manager/foreground/images/filetype/2x/filetype_audio.png b/ui/file_manager/file_manager/foreground/images/filetype/2x/filetype_audio.png
new file mode 100644
index 0000000..fb28e22
--- /dev/null
+++ b/ui/file_manager/file_manager/foreground/images/filetype/2x/filetype_audio.png
Binary files differ
diff --git a/ui/file_manager/file_manager/foreground/images/filetype/2x/filetype_chart.png b/ui/file_manager/file_manager/foreground/images/filetype/2x/filetype_chart.png
new file mode 100644
index 0000000..e3e8d52
--- /dev/null
+++ b/ui/file_manager/file_manager/foreground/images/filetype/2x/filetype_chart.png
Binary files differ
diff --git a/ui/file_manager/file_manager/foreground/images/filetype/2x/filetype_excel.png b/ui/file_manager/file_manager/foreground/images/filetype/2x/filetype_excel.png
new file mode 100644
index 0000000..b95db329
--- /dev/null
+++ b/ui/file_manager/file_manager/foreground/images/filetype/2x/filetype_excel.png
Binary files differ
diff --git a/ui/file_manager/file_manager/foreground/images/filetype/2x/filetype_folder.png b/ui/file_manager/file_manager/foreground/images/filetype/2x/filetype_folder.png
new file mode 100644
index 0000000..b15e38b
--- /dev/null
+++ b/ui/file_manager/file_manager/foreground/images/filetype/2x/filetype_folder.png
Binary files differ
diff --git a/ui/file_manager/file_manager/foreground/images/filetype/2x/filetype_folder_active.png b/ui/file_manager/file_manager/foreground/images/filetype/2x/filetype_folder_active.png
new file mode 100644
index 0000000..81faa83
--- /dev/null
+++ b/ui/file_manager/file_manager/foreground/images/filetype/2x/filetype_folder_active.png
Binary files differ
diff --git a/ui/file_manager/file_manager/foreground/images/filetype/2x/filetype_folder_shared.png b/ui/file_manager/file_manager/foreground/images/filetype/2x/filetype_folder_shared.png
new file mode 100644
index 0000000..567a1972
--- /dev/null
+++ b/ui/file_manager/file_manager/foreground/images/filetype/2x/filetype_folder_shared.png
Binary files differ
diff --git a/ui/file_manager/file_manager/foreground/images/filetype/2x/filetype_folder_shared_active.png b/ui/file_manager/file_manager/foreground/images/filetype/2x/filetype_folder_shared_active.png
new file mode 100644
index 0000000..c9cdd2b6
--- /dev/null
+++ b/ui/file_manager/file_manager/foreground/images/filetype/2x/filetype_folder_shared_active.png
Binary files differ
diff --git a/ui/file_manager/file_manager/foreground/images/filetype/2x/filetype_form.png b/ui/file_manager/file_manager/foreground/images/filetype/2x/filetype_form.png
new file mode 100644
index 0000000..6cbd3e6
--- /dev/null
+++ b/ui/file_manager/file_manager/foreground/images/filetype/2x/filetype_form.png
Binary files differ
diff --git a/ui/file_manager/file_manager/foreground/images/filetype/2x/filetype_gdoc.png b/ui/file_manager/file_manager/foreground/images/filetype/2x/filetype_gdoc.png
new file mode 100644
index 0000000..00355c50
--- /dev/null
+++ b/ui/file_manager/file_manager/foreground/images/filetype/2x/filetype_gdoc.png
Binary files differ
diff --git a/ui/file_manager/file_manager/foreground/images/filetype/2x/filetype_gdraw.png b/ui/file_manager/file_manager/foreground/images/filetype/2x/filetype_gdraw.png
new file mode 100644
index 0000000..355c7ff
--- /dev/null
+++ b/ui/file_manager/file_manager/foreground/images/filetype/2x/filetype_gdraw.png
Binary files differ
diff --git a/ui/file_manager/file_manager/foreground/images/filetype/2x/filetype_generic.png b/ui/file_manager/file_manager/foreground/images/filetype/2x/filetype_generic.png
new file mode 100644
index 0000000..47752d5
--- /dev/null
+++ b/ui/file_manager/file_manager/foreground/images/filetype/2x/filetype_generic.png
Binary files differ
diff --git a/ui/file_manager/file_manager/foreground/images/filetype/2x/filetype_gsheet.png b/ui/file_manager/file_manager/foreground/images/filetype/2x/filetype_gsheet.png
new file mode 100644
index 0000000..d895503
--- /dev/null
+++ b/ui/file_manager/file_manager/foreground/images/filetype/2x/filetype_gsheet.png
Binary files differ
diff --git a/ui/file_manager/file_manager/foreground/images/filetype/2x/filetype_gslides.png b/ui/file_manager/file_manager/foreground/images/filetype/2x/filetype_gslides.png
new file mode 100644
index 0000000..1315e79c
--- /dev/null
+++ b/ui/file_manager/file_manager/foreground/images/filetype/2x/filetype_gslides.png
Binary files differ
diff --git a/ui/file_manager/file_manager/foreground/images/filetype/2x/filetype_gtable.png b/ui/file_manager/file_manager/foreground/images/filetype/2x/filetype_gtable.png
new file mode 100644
index 0000000..c213623c
--- /dev/null
+++ b/ui/file_manager/file_manager/foreground/images/filetype/2x/filetype_gtable.png
Binary files differ
diff --git a/ui/file_manager/file_manager/foreground/images/filetype/2x/filetype_image.png b/ui/file_manager/file_manager/foreground/images/filetype/2x/filetype_image.png
new file mode 100644
index 0000000..86f727cb
--- /dev/null
+++ b/ui/file_manager/file_manager/foreground/images/filetype/2x/filetype_image.png
Binary files differ
diff --git a/ui/file_manager/file_manager/foreground/images/filetype/2x/filetype_map.png b/ui/file_manager/file_manager/foreground/images/filetype/2x/filetype_map.png
new file mode 100644
index 0000000..14bba4f
--- /dev/null
+++ b/ui/file_manager/file_manager/foreground/images/filetype/2x/filetype_map.png
Binary files differ
diff --git a/ui/file_manager/file_manager/foreground/images/filetype/2x/filetype_pdf.png b/ui/file_manager/file_manager/foreground/images/filetype/2x/filetype_pdf.png
new file mode 100644
index 0000000..5e6d911
--- /dev/null
+++ b/ui/file_manager/file_manager/foreground/images/filetype/2x/filetype_pdf.png
Binary files differ
diff --git a/ui/file_manager/file_manager/foreground/images/filetype/2x/filetype_ppt.png b/ui/file_manager/file_manager/foreground/images/filetype/2x/filetype_ppt.png
new file mode 100644
index 0000000..7e15c0a
--- /dev/null
+++ b/ui/file_manager/file_manager/foreground/images/filetype/2x/filetype_ppt.png
Binary files differ
diff --git a/ui/file_manager/file_manager/foreground/images/filetype/2x/filetype_script.png b/ui/file_manager/file_manager/foreground/images/filetype/2x/filetype_script.png
new file mode 100644
index 0000000..26bf6b2
--- /dev/null
+++ b/ui/file_manager/file_manager/foreground/images/filetype/2x/filetype_script.png
Binary files differ
diff --git a/ui/file_manager/file_manager/foreground/images/filetype/2x/filetype_sites.png b/ui/file_manager/file_manager/foreground/images/filetype/2x/filetype_sites.png
new file mode 100644
index 0000000..a8e89f1
--- /dev/null
+++ b/ui/file_manager/file_manager/foreground/images/filetype/2x/filetype_sites.png
Binary files differ
diff --git a/ui/file_manager/file_manager/foreground/images/filetype/2x/filetype_video.png b/ui/file_manager/file_manager/foreground/images/filetype/2x/filetype_video.png
new file mode 100644
index 0000000..de8847e8
--- /dev/null
+++ b/ui/file_manager/file_manager/foreground/images/filetype/2x/filetype_video.png
Binary files differ
diff --git a/ui/file_manager/file_manager/foreground/images/filetype/2x/filetype_word.png b/ui/file_manager/file_manager/foreground/images/filetype/2x/filetype_word.png
new file mode 100644
index 0000000..ddb77c4
--- /dev/null
+++ b/ui/file_manager/file_manager/foreground/images/filetype/2x/filetype_word.png
Binary files differ
diff --git a/ui/file_manager/file_manager/foreground/images/filetype/filetype_archive.png b/ui/file_manager/file_manager/foreground/images/filetype/filetype_archive.png
new file mode 100644
index 0000000..6fb4998
--- /dev/null
+++ b/ui/file_manager/file_manager/foreground/images/filetype/filetype_archive.png
Binary files differ
diff --git a/ui/file_manager/file_manager/foreground/images/filetype/filetype_audio.png b/ui/file_manager/file_manager/foreground/images/filetype/filetype_audio.png
new file mode 100644
index 0000000..31aa4c7
--- /dev/null
+++ b/ui/file_manager/file_manager/foreground/images/filetype/filetype_audio.png
Binary files differ
diff --git a/ui/file_manager/file_manager/foreground/images/filetype/filetype_chart.png b/ui/file_manager/file_manager/foreground/images/filetype/filetype_chart.png
new file mode 100644
index 0000000..cdd05c4
--- /dev/null
+++ b/ui/file_manager/file_manager/foreground/images/filetype/filetype_chart.png
Binary files differ
diff --git a/ui/file_manager/file_manager/foreground/images/filetype/filetype_excel.png b/ui/file_manager/file_manager/foreground/images/filetype/filetype_excel.png
new file mode 100644
index 0000000..8df1935e
--- /dev/null
+++ b/ui/file_manager/file_manager/foreground/images/filetype/filetype_excel.png
Binary files differ
diff --git a/ui/file_manager/file_manager/foreground/images/filetype/filetype_folder.png b/ui/file_manager/file_manager/foreground/images/filetype/filetype_folder.png
new file mode 100644
index 0000000..831263f4
--- /dev/null
+++ b/ui/file_manager/file_manager/foreground/images/filetype/filetype_folder.png
Binary files differ
diff --git a/ui/file_manager/file_manager/foreground/images/filetype/filetype_folder_active.png b/ui/file_manager/file_manager/foreground/images/filetype/filetype_folder_active.png
new file mode 100644
index 0000000..d5355c6
--- /dev/null
+++ b/ui/file_manager/file_manager/foreground/images/filetype/filetype_folder_active.png
Binary files differ
diff --git a/ui/file_manager/file_manager/foreground/images/filetype/filetype_folder_shared.png b/ui/file_manager/file_manager/foreground/images/filetype/filetype_folder_shared.png
new file mode 100644
index 0000000..fea60e6d
--- /dev/null
+++ b/ui/file_manager/file_manager/foreground/images/filetype/filetype_folder_shared.png
Binary files differ
diff --git a/ui/file_manager/file_manager/foreground/images/filetype/filetype_folder_shared_active.png b/ui/file_manager/file_manager/foreground/images/filetype/filetype_folder_shared_active.png
new file mode 100644
index 0000000..3e5bf9e
--- /dev/null
+++ b/ui/file_manager/file_manager/foreground/images/filetype/filetype_folder_shared_active.png
Binary files differ
diff --git a/ui/file_manager/file_manager/foreground/images/filetype/filetype_form.png b/ui/file_manager/file_manager/foreground/images/filetype/filetype_form.png
new file mode 100644
index 0000000..c01a3443
--- /dev/null
+++ b/ui/file_manager/file_manager/foreground/images/filetype/filetype_form.png
Binary files differ
diff --git a/ui/file_manager/file_manager/foreground/images/filetype/filetype_gdoc.png b/ui/file_manager/file_manager/foreground/images/filetype/filetype_gdoc.png
new file mode 100644
index 0000000..da9d8c4
--- /dev/null
+++ b/ui/file_manager/file_manager/foreground/images/filetype/filetype_gdoc.png
Binary files differ
diff --git a/ui/file_manager/file_manager/foreground/images/filetype/filetype_gdraw.png b/ui/file_manager/file_manager/foreground/images/filetype/filetype_gdraw.png
new file mode 100644
index 0000000..448e4b9
--- /dev/null
+++ b/ui/file_manager/file_manager/foreground/images/filetype/filetype_gdraw.png
Binary files differ
diff --git a/ui/file_manager/file_manager/foreground/images/filetype/filetype_generic.png b/ui/file_manager/file_manager/foreground/images/filetype/filetype_generic.png
new file mode 100644
index 0000000..9ff4caf
--- /dev/null
+++ b/ui/file_manager/file_manager/foreground/images/filetype/filetype_generic.png
Binary files differ
diff --git a/ui/file_manager/file_manager/foreground/images/filetype/filetype_gsheet.png b/ui/file_manager/file_manager/foreground/images/filetype/filetype_gsheet.png
new file mode 100644
index 0000000..daa23e510
--- /dev/null
+++ b/ui/file_manager/file_manager/foreground/images/filetype/filetype_gsheet.png
Binary files differ
diff --git a/ui/file_manager/file_manager/foreground/images/filetype/filetype_gslides.png b/ui/file_manager/file_manager/foreground/images/filetype/filetype_gslides.png
new file mode 100644
index 0000000..b1235439
--- /dev/null
+++ b/ui/file_manager/file_manager/foreground/images/filetype/filetype_gslides.png
Binary files differ
diff --git a/ui/file_manager/file_manager/foreground/images/filetype/filetype_gtable.png b/ui/file_manager/file_manager/foreground/images/filetype/filetype_gtable.png
new file mode 100644
index 0000000..ff6188f1
--- /dev/null
+++ b/ui/file_manager/file_manager/foreground/images/filetype/filetype_gtable.png
Binary files differ
diff --git a/ui/file_manager/file_manager/foreground/images/filetype/filetype_image.png b/ui/file_manager/file_manager/foreground/images/filetype/filetype_image.png
new file mode 100644
index 0000000..bfc26bb3
--- /dev/null
+++ b/ui/file_manager/file_manager/foreground/images/filetype/filetype_image.png
Binary files differ
diff --git a/ui/file_manager/file_manager/foreground/images/filetype/filetype_map.png b/ui/file_manager/file_manager/foreground/images/filetype/filetype_map.png
new file mode 100644
index 0000000..09af631
--- /dev/null
+++ b/ui/file_manager/file_manager/foreground/images/filetype/filetype_map.png
Binary files differ
diff --git a/ui/file_manager/file_manager/foreground/images/filetype/filetype_pdf.png b/ui/file_manager/file_manager/foreground/images/filetype/filetype_pdf.png
new file mode 100644
index 0000000..cea5a41
--- /dev/null
+++ b/ui/file_manager/file_manager/foreground/images/filetype/filetype_pdf.png
Binary files differ
diff --git a/ui/file_manager/file_manager/foreground/images/filetype/filetype_ppt.png b/ui/file_manager/file_manager/foreground/images/filetype/filetype_ppt.png
new file mode 100644
index 0000000..0d03aea
--- /dev/null
+++ b/ui/file_manager/file_manager/foreground/images/filetype/filetype_ppt.png
Binary files differ
diff --git a/ui/file_manager/file_manager/foreground/images/filetype/filetype_script.png b/ui/file_manager/file_manager/foreground/images/filetype/filetype_script.png
new file mode 100644
index 0000000..8d77c40
--- /dev/null
+++ b/ui/file_manager/file_manager/foreground/images/filetype/filetype_script.png
Binary files differ
diff --git a/ui/file_manager/file_manager/foreground/images/filetype/filetype_sites.png b/ui/file_manager/file_manager/foreground/images/filetype/filetype_sites.png
new file mode 100644
index 0000000..a2e49c2
--- /dev/null
+++ b/ui/file_manager/file_manager/foreground/images/filetype/filetype_sites.png
Binary files differ
diff --git a/ui/file_manager/file_manager/foreground/images/filetype/filetype_video.png b/ui/file_manager/file_manager/foreground/images/filetype/filetype_video.png
new file mode 100644
index 0000000..4dbe2e0
--- /dev/null
+++ b/ui/file_manager/file_manager/foreground/images/filetype/filetype_video.png
Binary files differ
diff --git a/ui/file_manager/file_manager/foreground/images/filetype/filetype_word.png b/ui/file_manager/file_manager/foreground/images/filetype/filetype_word.png
new file mode 100644
index 0000000..390d09d4
--- /dev/null
+++ b/ui/file_manager/file_manager/foreground/images/filetype/filetype_word.png
Binary files differ
diff --git a/ui/file_manager/file_manager/foreground/js/app_installer.js b/ui/file_manager/file_manager/foreground/js/app_installer.js
index 39cf8627..a637c8d 100644
--- a/ui/file_manager/file_manager/foreground/js/app_installer.js
+++ b/ui/file_manager/file_manager/foreground/js/app_installer.js
@@ -54,7 +54,7 @@
 /**
  * Called when the installation is completed.
  *
- * @param {{message: string}?} error Null if the installation is success,
+ * @param {!Object|undefined} error Undefined if the installation is success,
  *     otherwise an object which contains error message.
  * @private
  */
diff --git a/ui/file_manager/file_manager/foreground/js/compiled_resources.gyp b/ui/file_manager/file_manager/foreground/js/compiled_resources.gyp
index a9933f91f..6a8b6ea 100644
--- a/ui/file_manager/file_manager/foreground/js/compiled_resources.gyp
+++ b/ui/file_manager/file_manager/foreground/js/compiled_resources.gyp
@@ -47,6 +47,9 @@
           '../../common/js/util.js',
           '../../common/js/progress_center_common.js',
           '../../common/js/lru_cache.js',
+          '../../common/js/metrics_base.js',
+          '../../common/js/metrics_events.js',
+          '../../common/js/metrics.js',
           '../../background/js/file_operation_manager.js',
           '../../background/js/file_operation_util.js',
           '../../background/js/file_operation_handler.js',
@@ -63,8 +66,7 @@
           '../../background/js/background_base.js',
           '../../background/js/background.js',
           '../../../image_loader/image_loader_client.js',
-          './metrics_base.js',
-          './metrics.js',
+          './metrics_start.js',
           './ui/combobutton.js',
           './ui/commandbutton.js',
           './ui/file_manager_dialog_base.js',
@@ -92,7 +94,9 @@
           './metadata/metadata_cache.js',
           './metadata/metadata_cache_item.js',
           './metadata/metadata_cache_set.js',
+          './metadata/metadata_item.js',
           './metadata/new_metadata_provider.js',
+          './metadata/thumbnail_model.js',
           './metadata_update_controller.js',
           './naming_controller.js',
           './navigation_list_model.js',
diff --git a/ui/file_manager/file_manager/foreground/js/directory_contents.js b/ui/file_manager/file_manager/foreground/js/directory_contents.js
index f60a80d..9fbab186 100644
--- a/ui/file_manager/file_manager/foreground/js/directory_contents.js
+++ b/ui/file_manager/file_manager/foreground/js/directory_contents.js
@@ -539,23 +539,16 @@
  * TODO(yoshiki): remove this. crbug.com/224869.
  *
  * @param {FileFilter} fileFilter The file-filter context.
- * @param {MetadataCache} metadataCache Metadata cache service.
  * @param {!FileSystemMetadata} fileSystemMetadata
  * @constructor
  */
-function FileListContext(
-    fileFilter, metadataCache, fileSystemMetadata) {
+function FileListContext(fileFilter, fileSystemMetadata) {
   /**
    * @type {FileListModel}
    */
   this.fileList = new FileListModel(fileSystemMetadata);
 
   /**
-   * @type {MetadataCache}
-   */
-  this.metadataCache = metadataCache;
-
-  /**
    * @public {!FileSystemMetadata}
    * @const
    */
@@ -621,8 +614,6 @@
   this.processNewEntriesQueue_ = new AsyncUtil.Queue();
   this.scanCancelled_ = false;
 
-  this.lastSpaceInMetadataCache_ = 0;
-
   /**
    * Metadata snapshot which is used to know which file is actually changed.
    * @type {Object}
@@ -637,7 +628,7 @@
 
 /**
  * Create the copy of the object, but without scan started.
- * @return {DirectoryContents} Object copy.
+ * @return {!DirectoryContents} Object copy.
  */
 DirectoryContents.prototype.clone = function() {
   return new DirectoryContents(
@@ -648,27 +639,6 @@
 };
 
 /**
- * Disposes the reserved metadata cache.
- */
-DirectoryContents.prototype.dispose = function() {
-  this.context_.metadataCache.resizeBy(-this.lastSpaceInMetadataCache_);
-  // Though the lastSpaceInMetadataCache_ is not supposed to be referred after
-  // dispose(), keep it synced with requested cache size just in case.
-  this.lastSpaceInMetadataCache_ = 0;
-};
-
-/**
- * Make a space for current directory size in the metadata cache.
- *
- * @param {number} size The cache size to be set.
- * @private
- */
-DirectoryContents.prototype.makeSpaceInMetadataCache_ = function(size) {
-  this.context_.metadataCache.resizeBy(size - this.lastSpaceInMetadataCache_);
-  this.lastSpaceInMetadataCache_ = size;
-};
-
-/**
  * Use a given fileList instead of the fileList from the context.
  * @param {(!Array|!cr.ui.ArrayDataModel)} fileList The new file list.
  */
@@ -677,7 +647,6 @@
     this.fileList_ = fileList;
   else
     this.fileList_ = new cr.ui.ArrayDataModel(fileList);
-  this.makeSpaceInMetadataCache_(this.fileList_.length);
 };
 
 /**
@@ -719,7 +688,6 @@
     spliceArgs.unshift(0, fileList.length);
     fileList.splice.apply(fileList, spliceArgs);
     this.fileList_ = fileList;
-    this.makeSpaceInMetadataCache_(this.fileList_.length);
 
     // Check updated files and dispatch change events.
     if (this.metadataSnapshot_) {
@@ -731,11 +699,15 @@
       for (var i = 0; i < entries.length; i++) {
         var url = entries[i].toURL();
         var newMetadata = newMetadatas[i];
-        if (this.metadataSnapshot_[url] &&
-            this.metadataSnapshot_[url].modificationTime &&
-            this.metadataSnapshot_[url].modificationTime.getTime() !==
-            newMetadata.modificationTime.getTime())
+        // If Files.app fails to obtain both old and new modificationTime,
+        // regard the entry as not updated.
+        if ((this.metadataSnapshot_[url] &&
+             this.metadataSnapshot_[url].modificationTime &&
+             this.metadataSnapshot_[url].modificationTime.getTime()) !==
+            (newMetadata.modificationTime &&
+             newMetadata.modificationTime.getTime())) {
           updatedIndexes.push(i);
+        }
       }
 
       if (updatedIndexes.length > 0)
@@ -837,10 +809,8 @@
     addedList.push(updatedMap[url]);
   }
 
-  if (removedUrls.length > 0) {
-    this.context_.metadataCache.clearByUrl(removedUrls, '*');
+  if (removedUrls.length > 0)
     this.context_.fileSystemMetadata.notifyEntriesRemoved(removedUrls);
-  }
 
   this.prefetchMetadata(updatedList, true, function() {
     this.onNewEntries_(true, addedList);
@@ -946,7 +916,6 @@
 
   // Enlarge the cache size into the new filelist size.
   var newListSize = this.fileList_.length + entriesFiltered.length;
-  this.makeSpaceInMetadataCache_(newListSize);
 
   this.processNewEntriesQueue_.run(function(callbackOuter) {
     var finish = function() {
@@ -1005,25 +974,10 @@
  */
 DirectoryContents.prototype.prefetchMetadata =
     function(entries, refresh, callback) {
-  var TYPES = 'filesystem|external';
-  if (refresh) {
+  if (refresh)
     this.context_.fileSystemMetadata.notifyEntriesChanged(entries);
-    Promise.all([
-        new Promise(function(resolve) {
-          this.context_.metadataCache.getLatest(entries, TYPES, resolve);
-        }.bind(this)),
-        this.context_.fileSystemMetadata.get(
-            entries, this.context_.prefetchPropertyNames)
-    ]).then(function(results) { callback(results[0]); });
-  } else {
-    Promise.all([
-        new Promise(function(resolve) {
-          this.context_.metadataCache.get(entries, TYPES, callback);
-        }.bind(this)),
-        this.context_.fileSystemMetadata.get(
-            entries, this.context_.prefetchPropertyNames)
-    ]).then(function(results) { callback(results[0]); });
-  }
+  this.context_.fileSystemMetadata.get(
+      entries, this.context_.prefetchPropertyNames).then(callback);
 };
 
 /**
diff --git a/ui/file_manager/file_manager/foreground/js/directory_model.js b/ui/file_manager/file_manager/foreground/js/directory_model.js
index b53085e..1bb990a 100644
--- a/ui/file_manager/file_manager/foreground/js/directory_model.js
+++ b/ui/file_manager/file_manager/foreground/js/directory_model.js
@@ -14,8 +14,6 @@
  * @param {boolean} singleSelection True if only one file could be selected
  *                                  at the time.
  * @param {FileFilter} fileFilter Instance of FileFilter.
- * @param {FileWatcher} fileWatcher Instance of FileWatcher.
- * @param {MetadataCache} metadataCache The metadata cache service.
  * @param {!MetadataProviderCache} metadataProviderCache Metadata cache.
  * @param {!FileSystemMetadata} fileSystemMetadata Metadata model.
  *     service.
@@ -24,7 +22,7 @@
  * @constructor
  * @extends {cr.EventTarget}
  */
-function DirectoryModel(singleSelection, fileFilter, fileWatcher, metadataCache,
+function DirectoryModel(singleSelection, fileFilter,
                         metadataProviderCache, fileSystemMetadata,
                         volumeManager, fileOperationManager) {
   this.fileListSelection_ = singleSelection ?
@@ -44,12 +42,11 @@
   this.fileFilter_.addEventListener('changed',
                                     this.onFilterChanged_.bind(this));
 
-  this.currentFileListContext_ = new FileListContext(
-      fileFilter, metadataCache, fileSystemMetadata);
+  this.currentFileListContext_ =
+      new FileListContext(fileFilter,  fileSystemMetadata);
   this.currentDirContents_ =
       DirectoryContents.createForDirectory(this.currentFileListContext_, null);
 
-  this.metadataCache_ = metadataCache;
   this.metadataProviderCache_ = metadataProviderCache;
   this.fileSystemMetadata_ = fileSystemMetadata;
 
@@ -57,7 +54,12 @@
   this.volumeManager_.volumeInfoList.addEventListener(
       'splice', this.onVolumeInfoListUpdated_.bind(this));
 
-  this.fileWatcher_ = fileWatcher;
+  /**
+   * File watcher.
+   * @private {!FileWatcher}
+   * @const
+   */
+  this.fileWatcher_ = new FileWatcher();
   this.fileWatcher_.addEventListener(
       'watcher-directory-changed',
       this.onWatcherDirectoryChanged_.bind(this));
@@ -439,7 +441,6 @@
                                                   callback) {
   if (this.currentDirContents_.isScanning())
     this.currentDirContents_.cancelScan();
-  this.currentDirContents_.dispose();
   this.currentDirContents_ = newDirContents;
   this.clearRescanTimeout_();
 
@@ -671,7 +672,6 @@
     var previousDirContents = this.currentDirContents_;
     this.currentDirContents_ = dirContents;
     this.currentDirContents_.replaceContextFileList();
-    previousDirContents.dispose();
 
     this.setSelectedEntries_(selectedEntries);
     this.fileListSelection_.leadIndex = leadIndex;
@@ -846,7 +846,6 @@
 
       then(function(newEntry) {
         // Refresh the cache.
-        this.metadataCache_.clear([newEntry], '*');
         this.fileSystemMetadata_.notifyEntriesCreated([newEntry]);
         return new Promise(function(onFulfilled, onRejected) {
           dirContents.prefetchMetadata(
@@ -1092,7 +1091,7 @@
  * Creates directory contents for the entry and query.
  *
  * @param {FileListContext} context File list context.
- * @param {DirectoryEntry} entry Current directory.
+ * @param {!DirectoryEntry} entry Current directory.
  * @param {string=} opt_query Search query string.
  * @return {DirectoryContents} Directory contents.
  * @private
diff --git a/ui/file_manager/file_manager/foreground/js/file_manager.js b/ui/file_manager/file_manager/foreground/js/file_manager.js
index 2035e99..18e244a 100644
--- a/ui/file_manager/file_manager/foreground/js/file_manager.js
+++ b/ui/file_manager/file_manager/foreground/js/file_manager.js
@@ -54,13 +54,6 @@
   this.mediaImportHandler_ = null;
 
   /**
-   * Metadata cache.
-   * @type {MetadataCache}
-   * @private
-   */
-  this.metadataCache_ = null;
-
-  /**
    * @private {!MetadataProviderCache}
    * @const
    */
@@ -72,6 +65,11 @@
   this.fileSystemMetadata_ = null;
 
   /**
+   * @private {ThumbnailModel}
+   */
+  this.thumbnailModel_ = null;
+
+  /**
    * File operation manager.
    * @type {FileOperationManager}
    * @private
@@ -80,17 +78,11 @@
 
   /**
    * File filter.
-   * @type {FileFilter}
-   * @private
+   * @private {!FileFilter}
+   * @const
    */
-  this.fileFilter_ = null;
-
-  /**
-   * File watcher.
-   * @type {FileWatcher}
-   * @private
-   */
-  this.fileWatcher_ = null;
+  this.fileFilter_ = new FileFilter(
+      false  /* Don't show dot files and *.crdownload by default. */);
 
   /**
    * Model of current directory.
@@ -126,6 +118,11 @@
    */
   this.ui_ = null;
 
+  /**
+   * @private {analytics.Tracker}
+   */
+  this.tracker_ = null;
+
   // --------------------------------------------------------------------------
   // Parameters determining the type of file manager.
 
@@ -351,12 +348,6 @@
     return this.mediaImportHandler_;
   },
   /**
-   * @return {MetadataCache}
-   */
-  get metadataCache() {
-    return this.metadataCache_;
-  },
-  /**
    * @return {FileManagerUI}
    */
   get ui() {
@@ -393,11 +384,6 @@
     this.initFileList_();
     this.setupCurrentDirectory_();
 
-    // PyAuto tests monitor this state by polling this variable
-    this.__defineGetter__('workerInitialized_', function() {
-      return this.metadataCache_.isInitialized();
-    }.bind(this));
-
     var self = this;
 
     var listBeingUpdated = null;
@@ -452,7 +438,8 @@
                     this.mediaScanner_),
                 /** @type {!importer.ImportRunner} */ (
                     this.mediaImportHandler_),
-                new importer.RuntimeCommandWidget());
+                new importer.RuntimeCommandWidget(),
+                assert(this.tracker_));
           }
         }.bind(this));
 
@@ -504,8 +491,8 @@
         this.ui_.multiProfileShareDialog,
         assert(this.backgroundPage_.background.progressCenter),
         assert(this.fileOperationManager_),
-        assert(this.metadataCache_),
         assert(this.fileSystemMetadata_),
+        assert(this.thumbnailModel_),
         assert(this.directoryModel_),
         assert(this.volumeManager_),
         assert(this.selectionHandler_));
@@ -653,6 +640,7 @@
             this.mediaScanner_ =
                 this.backgroundPage_.background.mediaScanner;
             this.historyLoader_ = this.backgroundPage_.background.historyLoader;
+            this.tracker_ = this.backgroundPage_.background.tracker;
             callback();
           }.bind(this));
         }.bind(this)));
@@ -707,12 +695,10 @@
 
     // Create the metadata cache.
     assert(this.volumeManager_);
-    this.metadataCache_ = MetadataCache.createFull(this.volumeManager_);
-    this.fileSystemMetadata_ = new FileSystemMetadata(
+    this.fileSystemMetadata_ = FileSystemMetadata.create(
         this.metadataProviderCache_,
-        new FileSystemMetadataProvider(this.metadataProviderCache_),
-        new ExternalMetadataProvider(this.metadataProviderCache_),
         this.volumeManager_);
+    this.thumbnailModel_ = new ThumbnailModel(this.fileSystemMetadata_);
 
     // Create the root view of FileManager.
     assert(this.dialogDom_);
@@ -737,7 +723,6 @@
    * @private
    */
   FileManager.prototype.initAdditionalUI_ = function(callback) {
-    assert(this.metadataCache_);
     assert(this.fileSystemMetadata_);
     assert(this.volumeManager_);
     assert(this.historyLoader_);
@@ -752,10 +737,11 @@
     FileManagerDialogBase.setFileManager(this);
 
     var table = queryRequiredElement(dom, '.detail-table');
-    table.importEnabled = this.importEnabled_;
+    // TODO(kenobi): See crbug/460135.  The import status column in table view
+    // is disabled until it works properly.
+    table.importEnabled = false;
     FileTable.decorate(
         table,
-        this.metadataCache_,
         this.fileSystemMetadata_,
         this.volumeManager_,
         this.historyLoader_,
@@ -763,7 +749,6 @@
     var grid = queryRequiredElement(dom, '.thumbnail-grid');
     FileGrid.decorate(
         grid,
-        this.metadataCache_,
         this.fileSystemMetadata_,
         this.volumeManager_,
         this.historyLoader_);
@@ -822,6 +807,8 @@
    * @private
    */
   FileManager.prototype.onHistoryChanged_ = function(event) {
+    this.tracker_.send(metrics.ImportEvents.HISTORY_CHANGED);
+
     // Ignore any entry that isn't an immediate child of the
     // current directory.
     util.isChildEntry(event.entry, this.getCurrentDirectoryEntry())
@@ -853,19 +840,12 @@
         this.dialogType == DialogType.SELECT_UPLOAD_FOLDER ||
         this.dialogType == DialogType.SELECT_SAVEAS_FILE;
 
-    assert(this.metadataCache_);
-    this.fileFilter_ = new FileFilter(
-        false  /* Don't show dot files and *.crdownload by default. */);
-    this.fileWatcher_ = new FileWatcher(this.metadataCache_);
-
     assert(this.volumeManager_);
     assert(this.fileOperationManager_);
     assert(this.fileSystemMetadata_);
     this.directoryModel_ = new DirectoryModel(
         singleSelection,
         this.fileFilter_,
-        this.fileWatcher_,
-        this.metadataCache_,
         this.metadataProviderCache_,
         this.fileSystemMetadata_,
         this.volumeManager_,
@@ -880,12 +860,26 @@
         this.selectionHandler_.onFileSelectionChanged.bind(
             this.selectionHandler_));
 
+    this.directoryModel_.addEventListener(
+        'directory-changed',
+        function(event) {
+          if (event.volumeChanged) {
+            var volumeInfo =
+                this.volumeManager_.getVolumeInfo(event.newDirEntry);
+            // NOTE: That dynamic values, like volume name MUST NOT
+            // be sent to GA as that value can contain PII.
+            // VolumeType is an enum.
+            this.tracker_.sendAppView(volumeInfo.volumeType);
+          }
+        }.bind(this));
+
     // TODO(mtomasz, yoshiki): Create navigation list earlier, and here just
     // attach the directory model.
     this.initDirectoryTree_();
 
     this.ui_.listContainer.listThumbnailLoader = new ListThumbnailLoader(
-        assert(this.directoryModel_.getFileList()), this.metadataCache_);
+        assert(this.directoryModel_.getFileList()),
+        assert(this.thumbnailModel_));
     this.ui_.listContainer.dataModel = this.directoryModel_.getFileList();
     this.ui_.listContainer.selectionModel =
         this.directoryModel_.getFileListSelection();
@@ -1267,9 +1261,6 @@
     }
     this.backgroundPage_.background.progressCenter.removePanel(
         this.ui_.progressCenterPanel);
-    window.closing = true;
-    if (this.backgroundPage_)
-      this.backgroundPage_.background.tryClose();
   };
 
   /**
diff --git a/ui/file_manager/file_manager/foreground/js/file_manager_commands.js b/ui/file_manager/file_manager/foreground/js/file_manager_commands.js
index 6a9e4765..5443c9fa 100644
--- a/ui/file_manager/file_manager/foreground/js/file_manager_commands.js
+++ b/ui/file_manager/file_manager/foreground/js/file_manager_commands.js
@@ -121,9 +121,13 @@
  * array to avoid confusing because pinning directory is not supported
  * currently.
  *
- * @return {Array.<Entry>} Target entries.
+ * @return {!Array<!Entry>} Target entries.
  */
 CommandUtil.getPinTargetEntries = function() {
+  // If current directory is not on drive, no entry can be pinned.
+  if (!fileManager.isOnDrive())
+    return [];
+
   var hasDirectory = false;
   var results = fileManager.getSelection().entries.filter(function(entry) {
     hasDirectory = hasDirectory || entry.isDirectory;
@@ -1013,7 +1017,7 @@
     }
     // Add the overlapped class to prevent the applicaiton window from
     // captureing mouse events.
-    fileManager.ui.shareDialog.show(entries[0], function(result) {
+    fileManager.ui.shareDialog.showEntry(entries[0], function(result) {
       if (result == ShareDialog.Result.NETWORK_ERROR)
         fileManager.ui.errorDialog.show(str('SHARE_ERROR'), null, null, null);
     }.bind(this));
diff --git a/ui/file_manager/file_manager/foreground/js/file_selection.js b/ui/file_manager/file_manager/foreground/js/file_selection.js
index c271376..edbf40e 100644
--- a/ui/file_manager/file_manager/foreground/js/file_selection.js
+++ b/ui/file_manager/file_manager/foreground/js/file_selection.js
@@ -109,24 +109,18 @@
  */
 FileSelection.prototype.completeInit = function() {
   if (!this.asyncInitPromise_) {
-    if (!this.fileManager_.isOnDrive()) {
-      this.asyncInitPromise_ = Promise.resolve();
-      this.allDriveFilesPresent = true;
-      return this.tasks.init(this.entries);
-    } else {
-      this.asyncInitPromise_ = this.fileManager_.getFileSystemMetadata().get(
-          this.entries,
-          ['availableOffline', 'contentMimeType']).then(function(props) {
-        var present = props.filter(function(p) { return p.availableOffline; });
-        this.allDriveFilesPresent = present.length == props.length;
-        // Collect all of the mime types and push that info into the
-        // selection.
-        this.mimeTypes = props.map(function(value) {
-          return value.contentMimeType || '';
-        });
-        return this.tasks.init(this.entries, this.mimeTypes);
-      }.bind(this));
-    }
+    this.asyncInitPromise_ = this.fileManager_.getFileSystemMetadata().get(
+        this.entries, ['availableOffline', 'contentMimeType']
+    ).then(function(props) {
+      var present = props.filter(function(p) { return p.availableOffline; });
+      this.allDriveFilesPresent = present.length == props.length;
+      // Collect all of the mime types and push that info into the
+      // selection.
+      this.mimeTypes = props.map(function(value) {
+        return value.contentMimeType || '';
+      });
+      return this.tasks.init(this.entries, this.mimeTypes);
+    }.bind(this));
   }
   return this.asyncInitPromise_;
 };
diff --git a/ui/file_manager/file_manager/foreground/js/file_transfer_controller.js b/ui/file_manager/file_manager/foreground/js/file_transfer_controller.js
index 19f9b66..4f2e278 100644
--- a/ui/file_manager/file_manager/foreground/js/file_transfer_controller.js
+++ b/ui/file_manager/file_manager/foreground/js/file_transfer_controller.js
@@ -24,8 +24,8 @@
  * @param {!ProgressCenter} progressCenter To notify starting copy operation.
  * @param {!FileOperationManager} fileOperationManager File operation manager
  *     instance.
- * @param {!MetadataCache} metadataCache Metadata cache service.
  * @param {!FileSystemMetadata} fileSystemMetadata Metadata cache service.
+ * @param {!ThumbnailModel} thumbnailModel
  * @param {!DirectoryModel} directoryModel Directory model instance.
  * @param {!VolumeManagerWrapper} volumeManager Volume manager instance.
  * @param {!FileSelectionHandler} selectionHandler Selection handler.
@@ -38,8 +38,8 @@
                                 multiProfileShareDialog,
                                 progressCenter,
                                 fileOperationManager,
-                                metadataCache,
                                 fileSystemMetadata,
+                                thumbnailModel,
                                 directoryModel,
                                 volumeManager,
                                 selectionHandler) {
@@ -65,13 +65,6 @@
   this.fileOperationManager_ = fileOperationManager;
 
   /**
-   * @type {!MetadataCache}
-   * @private
-   * @const
-   */
-  this.metadataCache_ = metadataCache;
-
-  /**
    * @type {!FileSystemMetadata}
    * @private
    * @const
@@ -79,6 +72,13 @@
   this.fileSystemMetadata_ = fileSystemMetadata;
 
   /**
+   * @type {!ThumbnailModel}
+   * @private
+   * @const
+   */
+  this.thumbnailModel_ = thumbnailModel;
+
+  /**
    * @type {!DirectoryModel}
    * @private
    * @const
@@ -399,11 +399,12 @@
       var urls = util.entriesToURLs(entries);
       // Do not use metadata cache here because the urls come from the different
       // profile.
-      chrome.fileManagerPrivate.getEntryProperties(urls, callback);
+      chrome.fileManagerPrivate.getEntryProperties(
+          urls, ['hosted', 'sharedWithMe'], callback);
     }).then(function(metadatas) {
       return entries.filter(function(entry, i) {
         var metadata = metadatas[i];
-        return metadata && metadata.isHosted && !metadata.sharedWithMe;
+        return metadata && metadata.hosted && !metadata.sharedWithMe;
       });
     });
   };
@@ -555,22 +556,10 @@
  * @private
  */
 FileTransferController.prototype.preloadThumbnailImage_ = function(entry) {
-  var metadataPromise = new Promise(function(fulfill, reject) {
-    this.metadataCache_.getOne(
-        entry,
-        'thumbnail|filesystem',
-        function(metadata) {
-          if (metadata)
-            fulfill(metadata);
-          else
-            reject('Failed to fetch metadata.');
-        });
-  }.bind(this));
-
-  var imagePromise = metadataPromise.then(function(metadata) {
+  var imagePromise = this.thumbnailModel_.get([entry]).then(function(metadata) {
     return new Promise(function(fulfill, reject) {
       var loader = new ThumbnailLoader(
-          entry, ThumbnailLoader.LoaderType.IMAGE, metadata);
+          entry, ThumbnailLoader.LoaderType.IMAGE, metadata[0]);
       loader.loadDetachedImage(function(result) {
         if (result)
           fulfill(loader.getImage());
@@ -581,8 +570,6 @@
   imagePromise.then(function(image) {
     // Store the image so that we can obtain the image synchronously.
     imagePromise.value = image;
-  }, function(error) {
-    console.error(error.stack || error);
   });
 
   this.preloadedThumbnailImagePromise_ = imagePromise;
diff --git a/ui/file_manager/file_manager/foreground/js/file_watcher.js b/ui/file_manager/file_manager/foreground/js/file_watcher.js
index ed43e41d..dfd36ba 100644
--- a/ui/file_manager/file_manager/foreground/js/file_watcher.js
+++ b/ui/file_manager/file_manager/foreground/js/file_watcher.js
@@ -3,25 +3,18 @@
 // found in the LICENSE file.
 
 /**
- * Watches for changes in the tracked directory, including local metadata
- * changes.
+ * Watches for changes in the tracked directory.
  *
- * @param {MetadataCache} metadataCache Instance of MetadataCache.
  * @extends {cr.EventTarget}
  * @constructor
  */
-function FileWatcher(metadataCache) {
+function FileWatcher() {
   this.queue_ = new AsyncUtil.Queue();
-  this.metadataCache_ = metadataCache;
   this.watchedDirectoryEntry_ = null;
 
   this.onDirectoryChangedBound_ = this.onDirectoryChanged_.bind(this);
   chrome.fileManagerPrivate.onDirectoryChanged.addListener(
       this.onDirectoryChangedBound_);
-
-  this.filesystemMetadataObserverId_ = null;
-  this.thumbnailMetadataObserverId_ = null;
-  this.externalMetadataObserverId_ = null;
 }
 
 /**
@@ -73,64 +66,6 @@
 };
 
 /**
- * Called when general metadata in the watched directory has been changed.
- *
- * @param {Array.<Entry>} entries Array of entries.
- * @param {Object.<string, Object>} properties Map from entry URLs to metadata
- *     properties.
- * @private
- */
-FileWatcher.prototype.onFilesystemMetadataChanged_ = function(
-    entries, properties) {
-  this.dispatchMetadataEvent_('filesystem', entries, properties);
-};
-
-/**
- * Called when thumbnail metadata in the watched directory has been changed.
- *
- * @param {Array.<Entry>} entries Array of entries.
- * @param {Object.<string, Object>} properties Map from entry URLs to metadata
- *     properties.
- * @private
- */
-FileWatcher.prototype.onThumbnailMetadataChanged_ = function(
-    entries, properties) {
-  this.dispatchMetadataEvent_('thumbnail', entries, properties);
-};
-
-/**
- * Called when external metadata in the watched directory has been changed.
- *
- * @param {Array.<Entry>} entries Array of entries.
- * @param {Object.<string, Object>} properties Map from entry URLs to metadata
- *     properties.
- * @private
- */
-FileWatcher.prototype.onExternalMetadataChanged_ = function(
-    entries, properties) {
-  this.dispatchMetadataEvent_('external', entries, properties);
-};
-
-/**
- * Dispatches an event about detected change in metadata within the tracked
- * directory.
- *
- * @param {string} type Type of the metadata change.
- * @param {Array.<Entry>} entries Array of entries.
- * @param {Object.<string, Object>} properties Map from entry URLs to metadata
- *     properties.
- * @private
- */
-FileWatcher.prototype.dispatchMetadataEvent_ = function(
-    type, entries, properties) {
-  var e = new Event('watcher-metadata-changed');
-  e.metadataType = type;
-  e.entries = entries;
-  e.properties = properties;
-  this.dispatchEvent(e);
-};
-
-/**
  * Changes the watched directory. In case of a fake entry, the watch is
  * just released, since there is no reason to track a fake directory.
  *
@@ -180,9 +115,6 @@
               onError();
             callback();
           }.bind(this));
-      this.metadataCache_.removeObserver(this.filesystemMetadataObserverId_);
-      this.metadataCache_.removeObserver(this.thumbnailMetadataObserverId_);
-      this.metadataCache_.removeObserver(this.externalMetadataObserverId_);
     } else {
       onSuccess();
       callback();
@@ -215,21 +147,6 @@
             }
             callback();
           }.bind(this));
-      this.filesystemMetadataObserverId_ = this.metadataCache_.addObserver(
-          entry,
-          MetadataCache.CHILDREN,
-          'filesystem',
-          this.onFilesystemMetadataChanged_.bind(this));
-      this.thumbnailMetadataObserverId_ = this.metadataCache_.addObserver(
-          entry,
-          MetadataCache.CHILDREN,
-          'thumbnail',
-          this.onThumbnailMetadataChanged_.bind(this));
-      this.externalMetadataObserverId_ = this.metadataCache_.addObserver(
-          entry,
-          MetadataCache.CHILDREN,
-          'external',
-          this.onExternalMetadataChanged_.bind(this));
     }.bind(this));
   }.bind(this);
 
diff --git a/ui/file_manager/file_manager/foreground/js/folder_shortcuts_data_model.js b/ui/file_manager/file_manager/foreground/js/folder_shortcuts_data_model.js
index 0e46532..bd5ef71 100644
--- a/ui/file_manager/file_manager/foreground/js/folder_shortcuts_data_model.js
+++ b/ui/file_manager/file_manager/foreground/js/folder_shortcuts_data_model.js
@@ -207,7 +207,7 @@
    * Reloads the model and loads the shortcuts.
    * @private
    */
-  reload_: function(ev) {
+  reload_: function() {
     var shortcutPaths;
     this.queue_.run(function(callback) {
       chrome.storage.sync.get(FolderShortcutsDataModel.NAME, function(value) {
diff --git a/ui/file_manager/file_manager/foreground/js/import_controller.js b/ui/file_manager/file_manager/foreground/js/import_controller.js
index 6b3f33f..547800d 100644
--- a/ui/file_manager/file_manager/foreground/js/import_controller.js
+++ b/ui/file_manager/file_manager/foreground/js/import_controller.js
@@ -28,13 +28,17 @@
  * @param {!importer.MediaScanner} scanner
  * @param {!importer.ImportRunner} importRunner
  * @param {!importer.CommandWidget} commandWidget
+ * @param {!analytics.Tracker} tracker
  */
 importer.ImportController =
-    function(environment, scanner, importRunner, commandWidget) {
+    function(environment, scanner, importRunner, commandWidget, tracker) {
 
   /** @private {!importer.ControllerEnvironment} */
   this.environment_ = environment;
 
+  /** @private {!importer.ChromeLocalStorage} */
+  this.storage_ = importer.ChromeLocalStorage.getInstance();
+
   /** @private {!importer.ImportRunner} */
   this.importRunner_ = importRunner;
 
@@ -47,6 +51,9 @@
   /** @type {!importer.ScanManager} */
   this.scanManager_ = new importer.ScanManager(environment, scanner);
 
+  /** @private {!analytics.Tracker} */
+  this.tracker_ = tracker;
+
   /**
    * The active import task, if any.
    * @private {?importer.TaskDetails_}
@@ -84,6 +91,16 @@
 
   this.commandWidget_.addClickListener(
       this.onClick_.bind(this));
+
+  this.storage_.get(importer.Setting.HAS_COMPLETED_IMPORT, false)
+      .then(
+          /**
+           * @param {boolean} importCompleted If so, we hide the banner
+           * @this {importer.ImportController}
+           */
+          function(importCompleted) {
+            this.commandWidget_.setDetailsBannerVisible(!importCompleted);
+          }.bind(this));
 };
 
 /**
@@ -124,6 +141,10 @@
  * @private
  */
 importer.ImportController.prototype.onVolumeUnmounted_ = function(volumeId) {
+  if (this.activeImport_) {
+    this.activeImport_.task.requestCancel();
+    this.tracker_.send(metrics.ImportEvents.DEVICE_YANKED);
+  }
   this.scanManager_.reset();
   this.checkState_();
 };
@@ -167,6 +188,8 @@
   this.previousImport_ = this.activeImport_;
   this.activeImport_ = null;
   this.scanManager_.reset();
+  this.storage_.set(importer.Setting.HAS_COMPLETED_IMPORT, true);
+  this.commandWidget_.setDetailsBannerVisible(false);
   this.checkState_();
 };
 
@@ -212,7 +235,6 @@
 importer.ImportController.prototype.execute = function() {
   console.assert(!this.activeImport_,
       'Cannot execute while an import task is already active.');
-  metrics.recordEnum('CloudImport.UserAction', 'IMPORT_INITIATED');
 
   var scan = this.scanManager_.getActiveScan();
   assert(scan != null);
@@ -310,7 +332,7 @@
  */
 importer.ImportController.prototype.updateUi_ =
     function(activityState, opt_scan) {
-  this.lastActivityState_ = activityState
+  this.lastActivityState_ = activityState;
   this.commandWidget_.update(activityState, opt_scan);
 };
 
@@ -340,7 +362,7 @@
         // storage in this calculation on the assumption that we
         // don't want to completely max out storage...even though
         // synced files will eventually be evicted from the cache.
-        return availableSpace > scanResult.getTotalBytes();
+        return availableSpace > scanResult.getStatistics().sizeBytes;
       });
 };
 
@@ -394,6 +416,11 @@
 importer.CommandWidget.prototype.toggleDetails;
 
 /**
+ * Sets the details banner visibility.
+ */
+importer.CommandWidget.prototype.setDetailsBannerVisible;
+
+/**
  * @enum {string}
  */
 importer.ClickSource = {
@@ -412,6 +439,24 @@
  */
 importer.RuntimeCommandWidget = function() {
 
+  /** @private {HTMLElement} */
+  this.detailsPanel_ = document.getElementById('cloud-import-details');
+  this.detailsPanel_.addEventListener(
+      'transitionend',
+      this.onDetailsTransitionEnd_.bind(this),
+      false);
+
+  // Any clicks on document outside of the details panel
+  // result in the panel being hidden.
+  document.onclick = this.onDetailsFocusLost_.bind(this);
+
+  // Stop further propagation of click events.
+  // This allows us to listen for *any other* clicks
+  // to hide the panel.
+  this.detailsPanel_.onclick = function(event) {
+    event.stopPropagation();
+  };
+
   /** @private {Element} */
   this.mainButton_ = document.getElementById('cloud-import-button');
   this.mainButton_.onclick = this.onButtonClicked_.bind(
@@ -435,22 +480,40 @@
       this, importer.ClickSource.DESTINATION);
 
   /** @private {Element} */
-  this.detailsPanel_ = document.getElementById('cloud-import-details');
-  this.detailsPanel_.addEventListener(
-      'transitionend',
-      this.onDetailsTransitionEnd_.bind(this),
-      false);
-
-  /** @private {Element} */
   this.toolbarIcon_ =
       document.querySelector('#cloud-import-button core-icon');
   this.statusIcon_ =
       document.querySelector('#cloud-import-details .status core-icon');
 
+  /** @private {Element} */
+  this.detailsBanner_ = document.querySelector('#cloud-import-details .banner');
+
   /** @private {function(!importer.ClickSource)} */
   this.clickListener_;
 };
 
+/**
+ * Ensures that a transitionend event gets sent out after a transition.  Similar
+ * to ensureTransitionEndEvent (see ui/webui/resources/js/util.js) but sends a
+ * standard-compliant rather than a webkit event.
+ *
+ * @param {!HTMLElement} element
+ * @param {number} timeout In milliseconds.
+ */
+importer.RuntimeCommandWidget.ensureTransitionEndEvent =
+    function(element, timeout) {
+    var fired = false;
+  element.addEventListener('transitionend', function f(e) {
+    element.removeEventListener('transitionend', f);
+    fired = true;
+  });
+  // Use a timeout of 400 ms.
+  window.setTimeout(function() {
+    if (!fired)
+      cr.dispatchSimpleEvent(element, 'transitionend', true);
+  }, timeout);
+};
+
 /** @override */
 importer.RuntimeCommandWidget.prototype.addClickListener =
     function(listener) {
@@ -485,6 +548,8 @@
     default:
       assertNotReached('Unhandled click source: ' + source);
   }
+
+  event.stopPropagation();
 };
 
 /** @override */
@@ -492,6 +557,12 @@
     this.setDetailsVisible_(this.detailsPanel_.className === 'hidden');
 };
 
+/** @override */
+importer.RuntimeCommandWidget.prototype.setDetailsBannerVisible =
+    function(visible) {
+  this.detailsBanner_.hidden = !visible;
+};
+
 /**
  * @param {boolean} visible
  * @private
@@ -499,9 +570,19 @@
 importer.RuntimeCommandWidget.prototype.setDetailsVisible_ = function(visible) {
   if (visible) {
     this.detailsPanel_.hidden = false;
+
+    // The following line is a hacky way to force the container to lay out
+    // contents so that the transition is triggered.
+    // This line MUST appear before clearing the classname.
+    this.detailsPanel_.scrollTop += 0;
+
     this.detailsPanel_.className = '';
   } else {
     this.detailsPanel_.className = 'hidden';
+    // transition duration is 200ms. Let's wait for 400ms.
+    importer.RuntimeCommandWidget.ensureTransitionEndEvent(
+        /** @type {!HTMLElement} */ (this.detailsPanel_),
+        400);
   }
 };
 
@@ -516,6 +597,12 @@
   }
 };
 
+/** @private */
+importer.RuntimeCommandWidget.prototype.onDetailsFocusLost_ =
+    function() {
+  this.setDetailsVisible_(false);
+};
+
 /** @override */
 importer.RuntimeCommandWidget.prototype.update =
     function(activityState, opt_scan) {
@@ -528,12 +615,6 @@
       this.mainButton_.hidden = true;
       this.sideButton_.hidden = true;
 
-      this.mainButton_.setAttribute(
-          'title',
-          '** SHOULD NOT BE VISIBLE **');
-      this.statusContent_.innerHTML =
-          '** SHOULD NOT BE VISIBLE **';
-
       this.toolbarIcon_.setAttribute('icon', 'cloud-off');
       this.statusIcon_.setAttribute('icon', 'cloud-off');
 
diff --git a/ui/file_manager/file_manager/foreground/js/import_controller_unittest.js b/ui/file_manager/file_manager/foreground/js/import_controller_unittest.js
index 7347b57..d78e0b9 100644
--- a/ui/file_manager/file_manager/foreground/js/import_controller_unittest.js
+++ b/ui/file_manager/file_manager/foreground/js/import_controller_unittest.js
@@ -45,6 +45,8 @@
     recordEnum: function() {}
   };
 
+  new MockChromeStorageAPI();
+
   widget = new importer.TestCommandWidget();
 
   volumeManager = new MockVolumeManager();
@@ -178,6 +180,7 @@
       new MockFileEntry(fileSystem, '/DCIM/photos0/IMG00001.jpg', {size: 0}));
 
   environment.directoryChangedListener_();  // initiates a scan.
+  widget.resetPromises();
   mediaScanner.finalizeScans();
 
   reportPromise(widget.updateResolver.promise, callback);
@@ -506,6 +509,12 @@
   this.toggleDetailsResolver.resolve();
 };
 
+/** @override */
+importer.TestCommandWidget.prototype.setDetailsBannerVisible =
+    function(visible) {
+  // TODO(smckay)
+};
+
 /**
  * @param {!VolumeManagerCommon.VolumeType} volumeType
  * @param {string} volumeId
diff --git a/ui/file_manager/file_manager/foreground/js/list_thumbnail_loader.js b/ui/file_manager/file_manager/foreground/js/list_thumbnail_loader.js
index 731f5ec..dde11c1 100644
--- a/ui/file_manager/file_manager/foreground/js/list_thumbnail_loader.js
+++ b/ui/file_manager/file_manager/foreground/js/list_thumbnail_loader.js
@@ -11,7 +11,7 @@
  * proper priority.
  *
  * @param {!FileListModel} dataModel A file list model.
- * @param {!MetadataCache} metadataCache Metadata cache.
+ * @param {!ThumbnailModel} thumbnailModel Thumbnail metadata model.
  * @param {Function=} opt_thumbnailLoaderConstructor A constructor of thumbnail
  *     loader. This argument is used for testing.
  * @struct
@@ -20,7 +20,7 @@
  * @suppress {checkStructDictInheritance}
  */
 function ListThumbnailLoader(
-    dataModel, metadataCache, opt_thumbnailLoaderConstructor) {
+    dataModel, thumbnailModel, opt_thumbnailLoaderConstructor) {
   /**
    * @type {!FileListModel}
    * @private
@@ -28,10 +28,10 @@
   this.dataModel_ = dataModel;
 
   /**
-   * @type {!MetadataCache}
+   * @type {!ThumbnailModel}
    * @private
    */
-  this.metadataCache_ = metadataCache;
+  this.thumbnailModel_ = thumbnailModel;
 
   /**
    * Constructor of thumbnail loader.
@@ -109,7 +109,7 @@
 ListThumbnailLoader.prototype.onSplice_ = function(event) {
   this.cursor_ = this.beginIndex_;
   this.continue_();
-}
+};
 
 /**
  * An event handler for sorted event of data model. When list is sorted, start
@@ -120,7 +120,7 @@
 ListThumbnailLoader.prototype.onSorted_ = function(event) {
   this.cursor_ = this.beginIndex_;
   this.continue_();
-}
+};
 
 /**
  * An event handler for change event of data model.
@@ -135,7 +135,7 @@
 
   this.cursor_ = this.beginIndex_;
   this.continue_();
-}
+};
 
 /**
  * Sets high priority range in the list.
@@ -153,7 +153,7 @@
   this.cursor_ = this.beginIndex_;
 
   this.continue_();
-}
+};
 
 /**
  * Returns a thumbnail of an entry if it is in cache.
@@ -164,7 +164,7 @@
   // Since we want to evict cache based on high priority range, we use peek here
   // instead of get.
   return this.cache_.peek(entry.toURL()) || null;
-}
+};
 
 /**
  * Enqueues tasks if available.
@@ -195,7 +195,7 @@
   this.enqueue_(this.cursor_, entry);
   this.cursor_++;
   this.continue_();
-}
+};
 
 /**
  * Enqueues a thumbnail fetch task for an entry.
@@ -205,7 +205,7 @@
  */
 ListThumbnailLoader.prototype.enqueue_ = function(index, entry) {
   var task = new ListThumbnailLoader.Task(
-      entry, this.metadataCache_, this.thumbnailLoaderConstructor_);
+      entry, this.thumbnailModel_, this.thumbnailLoaderConstructor_);
 
   var url = entry.toURL();
   this.active_[url] = task;
@@ -219,7 +219,7 @@
     delete this.active_[url];
     this.continue_();
   }.bind(this));
-}
+};
 
 /**
  * Dispatches thumbnail loaded event.
@@ -307,24 +307,24 @@
    * @const {number}
    */
   this.height = height;
-}
+};
 
 /**
  * A task to load thumbnail.
  *
  * @param {!Entry} entry An entry.
- * @param {!MetadataCache} metadataCache Metadata cache.
+ * @param {!ThumbnailModel} thumbnailModel Metadata cache.
  * @param {!Function} thumbnailLoaderConstructor A constructor of thumbnail
  *     loader.
  * @constructor
  * @struct
  */
 ListThumbnailLoader.Task = function(
-    entry, metadataCache, thumbnailLoaderConstructor) {
+    entry, thumbnailModel, thumbnailLoaderConstructor) {
   this.entry_ = entry;
-  this.metadataCache_ = metadataCache;
+  this.thumbnailModel_ = thumbnailModel;
   this.thumbnailLoaderConstructor_ = thumbnailLoaderConstructor;
-}
+};
 
 /**
  * Fetches thumbnail.
@@ -333,10 +333,7 @@
  *     resolved when thumbnail is fetched.
  */
 ListThumbnailLoader.Task.prototype.fetch = function() {
-  return new Promise(function(resolve, reject) {
-    this.metadataCache_.getLatest(
-        [this.entry_], 'thumbnail|filesystem|external|media', resolve);
-  }.bind(this)).then(function(metadatas) {
+  return this.thumbnailModel_.get([this.entry_]).then(function(metadatas) {
     return new this.thumbnailLoaderConstructor_(
         this.entry_, ThumbnailLoader.LoaderType.IMAGE, metadatas[0])
         .loadAsDataUrl();
@@ -344,4 +341,4 @@
     return new ListThumbnailLoader.ThumbnailData(
         this.entry_.toURL(), result.data, result.width, result.height);
   }.bind(this));
-}
+};
diff --git a/ui/file_manager/file_manager/foreground/js/list_thumbnail_loader_unittest.js b/ui/file_manager/file_manager/foreground/js/list_thumbnail_loader_unittest.js
index 49afbe2..5705173 100644
--- a/ui/file_manager/file_manager/foreground/js/list_thumbnail_loader_unittest.js
+++ b/ui/file_manager/file_manager/foreground/js/list_thumbnail_loader_unittest.js
@@ -42,7 +42,7 @@
 }
 
 var listThumbnailLoader;
-var getLatestCallbacks;
+var getCallbacks;
 var thumbnailLoadedEvents;
 var fileListModel;
 var fileSystem = new MockFileSystem('volume-id');
@@ -63,16 +63,18 @@
   MockThumbnailLoader.testImageWidth = 160;
   MockThumbnailLoader.testImageHeight = 160;
 
-  getLatestCallbacks = {};
-  var metadataCache = {
-    getLatest: function(entries, type, callback) {
-      getLatestCallbacks[getKeyOfGetLatestCallback_(entries)] = callback;
+  getCallbacks = {};
+  var thumbnailModel = {
+    get: function(entries) {
+      return new Promise(function(fulfill) {
+        getCallbacks[getKeyOfGetCallback_(entries)] = fulfill;
+      });
     }
   };
 
-  fileListModel = new FileListModel(metadataCache);
+  fileListModel = new FileListModel(thumbnailModel);
 
-  listThumbnailLoader = new ListThumbnailLoader(fileListModel, metadataCache,
+  listThumbnailLoader = new ListThumbnailLoader(fileListModel, thumbnailModel,
       MockThumbnailLoader);
 
   thumbnailLoadedEvents = [];
@@ -81,21 +83,21 @@
   });
 }
 
-function getKeyOfGetLatestCallback_(entries) {
+function getKeyOfGetCallback_(entries) {
   return entries.reduce(function(previous, current) {
     return previous + '|' + current.toURL();
   }, '');
 }
 
 function resolveGetLatestCallback(entries) {
-  var key = getKeyOfGetLatestCallback_(entries);
-  assert(getLatestCallbacks[key]);
-  getLatestCallbacks[key](entries.map(function() { return {}; }));
-  delete getLatestCallbacks[key];
+  var key = getKeyOfGetCallback_(entries);
+  assert(getCallbacks[key]);
+  getCallbacks[key](entries.map(function() { return {}; }));
+  delete getCallbacks[key];
 }
 
 function hasPendingGetLatestCallback(entries) {
-  return !!getLatestCallbacks[getKeyOfGetLatestCallback_(entries)];
+  return !!getCallbacks[getKeyOfGetCallback_(entries)];
 }
 
 function areEntriesInCache(entries) {
@@ -118,7 +120,7 @@
   // Assert that 2 fetch tasks are running.
   assertTrue(hasPendingGetLatestCallback([entry1]));
   assertTrue(hasPendingGetLatestCallback([entry2]));
-  assertEquals(2, Object.keys(getLatestCallbacks).length);
+  assertEquals(2, Object.keys(getCallbacks).length);
 
   // Fails to get thumbnail from cache for Test2.jpg.
   assertEquals(null, listThumbnailLoader.getThumbnailFromCache(entry2));
@@ -129,7 +131,7 @@
   // Assert that no new tasks are enqueued.
   assertTrue(hasPendingGetLatestCallback([entry1]));
   assertTrue(hasPendingGetLatestCallback([entry2]));
-  assertEquals(2, Object.keys(getLatestCallbacks).length);
+  assertEquals(2, Object.keys(getCallbacks).length);
 
   resolveGetLatestCallback([entry2]);
 
@@ -155,7 +157,7 @@
     return waitUntil(function() {
       return hasPendingGetLatestCallback([entry1]) &&
           hasPendingGetLatestCallback([entry4]) &&
-          Object.keys(getLatestCallbacks).length === 2;
+          Object.keys(getCallbacks).length === 2;
     });
   }).then(function() {
     // Set high priority range to 2 - 4.
@@ -167,7 +169,7 @@
     return waitUntil(function() {
       return hasPendingGetLatestCallback([entry3]) &&
           hasPendingGetLatestCallback([entry4]) &&
-          Object.keys(getLatestCallbacks).length === 2;
+          Object.keys(getCallbacks).length === 2;
     });
   }), callback);
 }
@@ -183,7 +185,7 @@
 
   // Assert that a task is enqueued for entry5.
   assertTrue(hasPendingGetLatestCallback([entry5]));
-  assertEquals(1, Object.keys(getLatestCallbacks).length);
+  assertEquals(1, Object.keys(getCallbacks).length);
 }
 
 function testCache(callback) {
@@ -197,7 +199,7 @@
   // In this test case, entry 3 is resolved earlier than entry 2.
   resolveGetLatestCallback([entry3]);
   resolveGetLatestCallback([entry2]);
-  assertEquals(0, Object.keys(getLatestCallbacks).length);
+  assertEquals(0, Object.keys(getCallbacks).length);
 
   reportPromise(waitUntil(function() {
     return areEntriesInCache([entry3, entry2, entry1]);
@@ -205,7 +207,7 @@
     // Move high priority range to 1 - 3.
     listThumbnailLoader.setHighPriorityRange(1, 3);
     resolveGetLatestCallback([entry4]);
-    assertEquals(0, Object.keys(getLatestCallbacks).length);
+    assertEquals(0, Object.keys(getCallbacks).length);
 
     return waitUntil(function() {
       return areEntriesInCache([entry4, entry3, entry2, entry1]);
@@ -215,7 +217,7 @@
     listThumbnailLoader.setHighPriorityRange(4, 6);
     resolveGetLatestCallback([entry5]);
     resolveGetLatestCallback([entry6]);
-    assertEquals(0, Object.keys(getLatestCallbacks).length);
+    assertEquals(0, Object.keys(getCallbacks).length);
 
     return waitUntil(function() {
       return areEntriesInCache([entry6, entry5, entry4, entry3, entry2]);
@@ -223,13 +225,13 @@
   }).then(function() {
     // Move high priority range to 3 - 5.
     listThumbnailLoader.setHighPriorityRange(3, 5);
-    assertEquals(0, Object.keys(getLatestCallbacks).length);
+    assertEquals(0, Object.keys(getCallbacks).length);
     assertTrue(areEntriesInCache([entry6, entry5, entry4, entry3, entry2]));
 
     // Move high priority range to 0 - 2.
     listThumbnailLoader.setHighPriorityRange(0, 2);
     resolveGetLatestCallback([entry1]);
-    assertEquals(0, Object.keys(getLatestCallbacks).length);
+    assertEquals(0, Object.keys(getCallbacks).length);
 
     return waitUntil(function() {
       return areEntriesInCache([entry3, entry2, entry1, entry6, entry5]);
@@ -264,7 +266,7 @@
 
   resolveGetLatestCallback([entry1]);
   resolveGetLatestCallback([entry2]);
-  assertEquals(0, Object.keys(getLatestCallbacks).length);
+  assertEquals(0, Object.keys(getCallbacks).length);
 
   // In order to assert that following task enqueues are fired by sorted event,
   // wait until all thumbnail loads are completed.
@@ -277,7 +279,7 @@
 
     return waitUntil(function() {
       return hasPendingGetLatestCallback([entry5]) &&
-          hasPendingGetLatestCallback([entry4])
+          hasPendingGetLatestCallback([entry4]);
     });
   }), callback);
 }
diff --git a/ui/file_manager/file_manager/foreground/js/main_scripts.js b/ui/file_manager/file_manager/foreground/js/main_scripts.js
index 9885084..197b007 100644
--- a/ui/file_manager/file_manager/foreground/js/main_scripts.js
+++ b/ui/file_manager/file_manager/foreground/js/main_scripts.js
@@ -22,8 +22,9 @@
 //
 // metrics_base.js and metrics.js initiates load performance tracking
 // so we want to parse it as early as possible.
-//<include src="metrics_base.js">
-//<include src="metrics.js">
+//<include src="../../common/js/metrics_base.js">
+//<include src="../../common/js/metrics.js">
+//<include src="metrics_start.js">
 //
 //<include src="../../common/js/lru_cache.js">
 //<include src="../../../image_loader/image_loader_client.js">
@@ -66,6 +67,8 @@
 //<include src="../../../../webui/resources/js/cr/ui/context_menu_handler.js">
 //
 //<include src="../../../../webui/resources/js/analytics.js">
+// metrics_events.js must be loaded after the analytics package.
+//<include src="../../common/js/metrics_events.js">
 
 (function() {
 // 'strict mode' is invoked for this scope.
@@ -104,11 +107,14 @@
 //<include src="gear_menu_controller.js">
 //<include src="import_controller.js">
 //<include src="launch_param.js">
+//<include src="metadata/content_metadata_provider.js">
 //<include src="metadata/external_metadata_provider.js">
 //<include src="metadata/file_system_metadata.js">
 //<include src="metadata/file_system_metadata_provider.js">
 //<include src="metadata/metadata_cache.js">
 //<include src="metadata/metadata_cache_item.js">
+//<include src="metadata/metadata_item.js">
+//<include src="metadata/thumbnail_model.js">
 //<include src="metadata_update_controller.js">
 //<include src="naming_controller.js">
 //<include src="navigation_list_model.js">
diff --git a/ui/file_manager/file_manager/foreground/js/metadata/content_metadata_provider.js b/ui/file_manager/file_manager/foreground/js/metadata/content_metadata_provider.js
index c624065..7e163ca 100644
--- a/ui/file_manager/file_manager/foreground/js/metadata/content_metadata_provider.js
+++ b/ui/file_manager/file_manager/foreground/js/metadata/content_metadata_provider.js
@@ -3,23 +3,18 @@
 // found in the LICENSE file.
 
 /**
- * @typedef {{
- *   thumbnailURL:(string|undefined),
- *   thumbnailTransform: (string|undefined)
- * }}
- */
-var ContentMetadata;
-
-/**
  * @param {!MetadataProviderCache} cache
  * @param {!MessagePort=} opt_messagePort Message port overriding the default
  *     worker port.
- * @extends {NewMetadataProvider<!ContentMetadata>}
+ * @extends {NewMetadataProvider}
  * @constructor
  * @struct
  */
 function ContentMetadataProvider(cache, opt_messagePort) {
-  NewMetadataProvider.call(this, cache, ['thumbnailURL', 'thumbnailTransform']);
+  NewMetadataProvider.call(
+      this,
+      cache,
+      ContentMetadataProvider.PROPERTY_NAMES);
 
   /**
    * Pass all URLs to the metadata reader until we have a correct filter.
@@ -55,6 +50,17 @@
 }
 
 /**
+ * @const {!Array<string>}
+ */
+ContentMetadataProvider.PROPERTY_NAMES = [
+  'contentThumbnailUrl',
+  'contentThumbnailTransform',
+  'contentImageTransform',
+  'mediaTitle',
+  'mediaArtist'
+];
+
+/**
  * Path of a worker script.
  * @const {string}
  */
@@ -65,13 +71,16 @@
 /**
  * Converts content metadata from parsers to the internal format.
  * @param {Object} metadata The content metadata.
- * @return {!ContentMetadata} Converted metadata.
+ * @return {!MetadataItem} Converted metadata.
  */
 ContentMetadataProvider.convertContentMetadata = function(metadata) {
-  return {
-    thumbnailURL: metadata.thumbnailURL,
-    thumbnailTransform: metadata.thumbnailTransform
-  };
+  var item = new MetadataItem();
+  item.contentThumbnailUrl = metadata['thumbnailURL'];
+  item.contentThumbnailTransform = metadata['thumbnailTransform'];
+  item.contentImageTransform = metadata['imageTransform'];
+  item.mediaTitle = metadata['title'];
+  item.mediaArtist = metadata['artist'];
+  return item;
 };
 
 ContentMetadataProvider.prototype.__proto__ = NewMetadataProvider.prototype;
@@ -82,13 +91,8 @@
 ContentMetadataProvider.prototype.getImpl = function(requests) {
   var promises = [];
   for (var i = 0; i < requests.length; i++) {
-    promises.push(new Promise(function(request, fulfill, reject) {
-      this.fetch(request.entry, request.names, function(metadata) {
-        if (metadata)
-          fulfill(metadata);
-        else
-          reject();
-      });
+    promises.push(new Promise(function(request, fulfill) {
+      this.fetch(request.entry, request.names, fulfill);
     }.bind(this, requests[i])));
   }
   return Promise.all(promises);
@@ -175,7 +179,8 @@
   for (var i = 0; i < callbacks.length; i++) {
     callbacks[i](
         metadata ?
-        ContentMetadataProvider.convertContentMetadata(metadata) : null);
+        ContentMetadataProvider.convertContentMetadata(metadata) :
+        new MetadataItem());
   }
 };
 
@@ -187,11 +192,12 @@
  * @param {Object?} metadata The metadata, if available.
  * @private
  */
-ContentMetadataProvider.prototype.onError_ =
-    function(url, step, error, metadata) {
-  if (MetadataCache.log)  // Avoid log spam by default.
-    console.warn('metadata: ' + url + ': ' + step + ': ' + error);
-  this.onResult_(url, null);
+ContentMetadataProvider.prototype.onError_ = function(
+    url, step, error, metadata) {
+  console.error(
+      'ContentMetadataProvider failed to obtain metadata: '+
+      url + ': ' + step + ': ' + error);
+  this.onResult_(url, new MetadataItem());
 };
 
 /**
@@ -200,6 +206,5 @@
  * @private
  */
 ContentMetadataProvider.prototype.onLog_ = function(arglist) {
-  if (MetadataCache.log)  // Avoid log spam by default.
-    console.log.apply(console, ['metadata:'].concat(arglist));
+  console.log.apply(console, ['ContentMetadataProvider log:'].concat(arglist));
 };
diff --git a/ui/file_manager/file_manager/foreground/js/metadata/content_metadata_provider_unittest.html b/ui/file_manager/file_manager/foreground/js/metadata/content_metadata_provider_unittest.html
index 1df0974..bfe2b5d 100644
--- a/ui/file_manager/file_manager/foreground/js/metadata/content_metadata_provider_unittest.html
+++ b/ui/file_manager/file_manager/foreground/js/metadata/content_metadata_provider_unittest.html
@@ -13,7 +13,8 @@
 <script src="../../../../../webui/resources/js/assert.js"></script>
 <script src="../../../common/js/lru_cache.js"></script>
 <script src="../../../common/js/unittest_util.js"></script>
-<script src="metadata_cache_item.js"></script>
 <script src="content_metadata_provider.js"></script>
+<script src="metadata_cache_item.js"></script>
+<script src="metadata_item.js"></script>
 
 <script src="content_metadata_provider_unittest.js"></script>
diff --git a/ui/file_manager/file_manager/foreground/js/metadata/content_metadata_provider_unittest.js b/ui/file_manager/file_manager/foreground/js/metadata/content_metadata_provider_unittest.js
index 06c9b3f..e60977b 100644
--- a/ui/file_manager/file_manager/foreground/js/metadata/content_metadata_provider_unittest.js
+++ b/ui/file_manager/file_manager/foreground/js/metadata/content_metadata_provider_unittest.js
@@ -35,14 +35,16 @@
   var provider = new ContentMetadataProvider(cache, port);
   reportPromise(provider.get(
       [entryA, entryB],
-      ['thumbnailURL', 'thumbnailTransform']).then(
+      ['contentThumbnailUrl', 'contentThumbnailTransform']).then(
           function(results) {
             assertEquals(2, results.length);
-            assertEquals('filesystem://A,url', results[0].thumbnailURL);
+            assertEquals('filesystem://A,url', results[0].contentThumbnailUrl);
             assertEquals(
-                'filesystem://A,transform', results[0].thumbnailTransform);
-            assertEquals('filesystem://B,url', results[1].thumbnailURL);
+                'filesystem://A,transform',
+                results[0].contentThumbnailTransform);
+            assertEquals('filesystem://B,url', results[1].contentThumbnailUrl);
             assertEquals(
-                'filesystem://B,transform', results[1].thumbnailTransform);
+                'filesystem://B,transform',
+                results[1].contentThumbnailTransform);
           }), callback);
 }
diff --git a/ui/file_manager/file_manager/foreground/js/metadata/external_metadata_provider.js b/ui/file_manager/file_manager/foreground/js/metadata/external_metadata_provider.js
index b8eb48b..f6cc007 100644
--- a/ui/file_manager/file_manager/foreground/js/metadata/external_metadata_provider.js
+++ b/ui/file_manager/file_manager/foreground/js/metadata/external_metadata_provider.js
@@ -3,76 +3,67 @@
 // found in the LICENSE file.
 
 /**
- * @typedef {{
- *   size: number,
- *   shared: (boolean|undefined),
- *   modificationTime: Date,
- *   thumbnailUrl: (string|undefined),
- *   externalFileUrl: (string|undefined),
- *   imageWidth: (number|undefined),
- *   imageHeight: (number|undefined),
- *   imageRotation: (number|undefined),
- *   pinned: (boolean|undefined),
- *   present: (boolean|undefined),
- *   hosted: (boolean|undefined),
- *   dirty: (boolean|undefined),
- *   availableOffline: (boolean|undefined),
- *   availableWhenMetered: (boolean|undefined),
- *   customIconUrl: string,
- *   contentMimeType: string,
- *   sharedWithMe: (boolean|undefined)
- * }}
- */
-var ExternalMetadataProperties;
-
-/**
  * Metadata provider for FileEntry#getMetadata.
+ * TODO(hirono): Rename thumbnailUrl with externalThumbnailUrl.
  *
  * @param {!MetadataProviderCache} cache
  * @constructor
- * @extends {NewMetadataProvider<!ExternalMetadataProperties>}
+ * @extends {NewMetadataProvider}
  * @struct
  */
 function ExternalMetadataProvider(cache) {
-  NewMetadataProvider.call(this, cache, [
-    'availableOffline',
-    'availableWhenMetered',
-    'contentMimeType',
-    'customIconUrl',
-    'dirty',
-    'externalFileUrl',
-    'hosted',
-    'imageHeight',
-    'imageRotation',
-    'imageWidth',
-    'modificationTime',
-    'pinned',
-    'present',
-    'shared',
-    'sharedWithMe',
-    'size',
-    'thumbnailUrl'
-  ]);
+  NewMetadataProvider.call(
+      this, cache, ExternalMetadataProvider.PROPERTY_NAMES);
 }
 
+/**
+ * @const {!Array<string>}
+ */
+ExternalMetadataProvider.PROPERTY_NAMES = [
+  'availableOffline',
+  'availableWhenMetered',
+  'contentMimeType',
+  'customIconUrl',
+  'dirty',
+  'externalFileUrl',
+  'hosted',
+  'imageHeight',
+  'imageRotation',
+  'imageWidth',
+  'modificationTime',
+  'pinned',
+  'present',
+  'shared',
+  'sharedWithMe',
+  'size',
+  'thumbnailUrl'
+];
+
 ExternalMetadataProvider.prototype.__proto__ = NewMetadataProvider.prototype;
 
 /**
  * @override
  */
 ExternalMetadataProvider.prototype.getImpl = function(requests) {
-  return new Promise(function(fulfill, reject) {
+  return new Promise(function(fulfill) {
     var urls = [];
     for (var i = 0; i < requests.length; i++) {
       urls.push(requests[i].entry.toURL());
     }
+    var nameMap = [];
+    for (var i = 0; i < requests.length; i++) {
+      for (var j = 0; j < requests[i].names.length; j++) {
+        nameMap[requests[i].names[j]] = true;
+      }
+    }
     chrome.fileManagerPrivate.getEntryProperties(
         urls,
+        Object.keys(nameMap),
         function(results) {
           if (!chrome.runtime.lastError)
             fulfill(this.convertResults_(requests, results));
           else
-            reject(chrome.runtime.lastError);
+            fulfill(requests.map(function() { return new MetadataItem(); }));
         }.bind(this));
   }.bind(this));
 };
@@ -80,32 +71,32 @@
 /**
  * @param {!Array<!MetadataRequest>} requests
  * @param {!Array<!EntryProperties>} propertiesList
- * @return {!Array<!ExternalMetadataProperties>}
+ * @return {!Array<!MetadataItem>}
  */
 ExternalMetadataProvider.prototype.convertResults_ =
     function(requests, propertiesList) {
   var results = [];
   for (var i = 0; i < propertiesList.length; i++) {
     var properties = propertiesList[i];
-    results.push({
-      availableOffline: properties.isAvailableOffline,
-      availableWhenMetered: properties.isAvailableWhenMetered,
-      contentMimeType: properties.contentMimeType || '',
-      customIconUrl: properties.customIconUrl || '',
-      dirty: properties.isDirty,
-      externalFileUrl: properties.externalFileUrl,
-      hosted: properties.isHosted,
-      imageHeight: properties.imageHeight,
-      imageRotation: properties.imageRotation,
-      imageWidth: properties.imageWidth,
-      modificationTime: new Date(properties.lastModifiedTime),
-      pinned: properties.isPinned,
-      present: properties.isPresent,
-      shared: properties.shared,
-      sharedWithMe: properties.sharedWithMe,
-      size: requests[i].entry.isFile ? (properties.fileSize || 0) : -1,
-      thumbnailUrl: properties.thumbnailUrl
-    });
+    var item = new MetadataItem();
+    item.availableOffline = properties.availableOffline;
+    item.availableWhenMetered = properties.availableWhenMetered;
+    item.contentMimeType = properties.contentMimeType || '';
+    item.customIconUrl = properties.customIconUrl || '';
+    item.dirty = properties.dirty;
+    item.externalFileUrl = properties.externalFileUrl;
+    item.hosted = properties.hosted;
+    item.imageHeight = properties.imageHeight;
+    item.imageRotation = properties.imageRotation;
+    item.imageWidth = properties.imageWidth;
+    item.modificationTime = new Date(properties.modificationTime);
+    item.pinned = properties.pinned;
+    item.present = properties.present;
+    item.shared = properties.shared;
+    item.sharedWithMe = properties.sharedWithMe;
+    item.size = requests[i].entry.isFile ? (properties.size || 0) : -1;
+    item.thumbnailUrl = properties.thumbnailUrl;
+    results.push(item);
   }
   return results;
 };
diff --git a/ui/file_manager/file_manager/foreground/js/metadata/external_metadata_provider_unittest.html b/ui/file_manager/file_manager/foreground/js/metadata/external_metadata_provider_unittest.html
index bc4926c..01a43ac 100644
--- a/ui/file_manager/file_manager/foreground/js/metadata/external_metadata_provider_unittest.html
+++ b/ui/file_manager/file_manager/foreground/js/metadata/external_metadata_provider_unittest.html
@@ -13,7 +13,8 @@
 <script src="../../../../../webui/resources/js/assert.js"></script>
 <script src="../../../common/js/lru_cache.js"></script>
 <script src="../../../common/js/unittest_util.js"></script>
-<script src="metadata_cache_item.js"></script>
 <script src="external_metadata_provider.js"></script>
+<script src="metadata_cache_item.js"></script>
+<script src="metadata_item.js"></script>
 
 <script src="external_metadata_provider_unittest.js"></script>
diff --git a/ui/file_manager/file_manager/foreground/js/metadata/external_metadata_provider_unittest.js b/ui/file_manager/file_manager/foreground/js/metadata/external_metadata_provider_unittest.js
index 2cb8e7a..b5a0509 100644
--- a/ui/file_manager/file_manager/foreground/js/metadata/external_metadata_provider_unittest.js
+++ b/ui/file_manager/file_manager/foreground/js/metadata/external_metadata_provider_unittest.js
@@ -16,15 +16,19 @@
   // Mocking chrome API.
   window.chrome = {
     fileManagerPrivate: {
-      getEntryProperties: function(urls, callback) {
+      getEntryProperties: function(urls, names, callback) {
+        assertEquals(2, urls.length);
         assertEquals('filesystem://A', urls[0]);
         assertEquals('filesystem://B', urls[1]);
+        assertEquals(2, names.length);
+        assertEquals('modificationTime', names[0]);
+        assertEquals('size', names[1]);
         callback([{
-          lastModifiedTime: new Date(2015, 0, 1).getTime(),
-          fileSize: 1024
+          modificationTime: new Date(2015, 0, 1).getTime(),
+          size: 1024
         }, {
-          lastModifiedTime: new Date(2015, 1, 2).getTime(),
-          fileSize: 2048
+          modificationTime: new Date(2015, 1, 2).getTime(),
+          size: 2048
         }]);
       }
     },
diff --git a/ui/file_manager/file_manager/foreground/js/metadata/file_system_metadata.js b/ui/file_manager/file_manager/foreground/js/metadata/file_system_metadata.js
index a50f156..ec3e05a 100644
--- a/ui/file_manager/file_manager/foreground/js/metadata/file_system_metadata.js
+++ b/ui/file_manager/file_manager/foreground/js/metadata/file_system_metadata.js
@@ -6,6 +6,7 @@
  * @param {!MetadataProviderCache} cache
  * @param {!FileSystemMetadataProvider} fileSystemMetadataProvider
  * @param {!ExternalMetadataProvider} externalMetadataProvider
+ * @param {!ContentMetadataProvider} contentMetadataProvider
  * @param {!VolumeManagerWrapper} volumeManager
  * @constructor
  * @struct
@@ -14,6 +15,7 @@
     cache,
     fileSystemMetadataProvider,
     externalMetadataProvider,
+    contentMetadataProvider,
     volumeManager) {
   /**
    * @private {!MetadataProviderCache}
@@ -34,6 +36,12 @@
   this.externalMetadataProvider_ = externalMetadataProvider;
 
   /**
+   * @private {!ContentMetadataProvider}
+   * @const
+   */
+  this.contentMetadataProvider_ = contentMetadataProvider;
+
+  /**
    * @private {!VolumeManagerWrapper}
    * @const
    */
@@ -41,48 +49,78 @@
 }
 
 /**
+ * @param {!MetadataProviderCache} cache
+ * @param {!VolumeManagerWrapper} volumeManager
+ * @return {!FileSystemMetadata}
+ */
+FileSystemMetadata.create = function(cache, volumeManager) {
+  return new FileSystemMetadata(
+      cache,
+      new FileSystemMetadataProvider(cache),
+      new ExternalMetadataProvider(cache),
+      new ContentMetadataProvider(cache),
+      volumeManager);
+};
+
+/**
  * Obtains metadata for entries.
  * @param {!Array<!Entry>} entries Entries.
  * @param {!Array<string>} names Metadata property names to be obtained.
- * @return {!Promise<!Array<!ExternalMetadataProperties>>}
+ * @return {!Promise<!Array<!MetadataItem>>}
  */
 FileSystemMetadata.prototype.get = function(entries, names) {
   var localEntries = [];
-  var localEntryIndexes = [];
   var externalEntries = [];
-  var externalEntryIndexes = [];
   for (var i = 0; i < entries.length; i++) {
     var volumeInfo = this.volumeManager_.getVolumeInfo(entries[i]);
     if (volumeInfo &&
         (volumeInfo.volumeType === VolumeManagerCommon.VolumeType.DRIVE ||
          volumeInfo.volumeType === VolumeManagerCommon.VolumeType.PROVIDED)) {
       externalEntries.push(entries[i]);
-      externalEntryIndexes.push(i);
     } else {
       localEntries.push(entries[i]);
-      localEntryIndexes.push(i);
     }
   }
 
-  // Correct property names that are valid for fileSystemMetadataProvider.
-  var fileSystemPropertyNames = names.filter(function(name) {
-    return name === 'size' || name === 'modificationTime';
-  });
-
+  var fileSystemPropertyNames = [];
+  var externalPropertyNames = [];
+  var contentPropertyNames = [];
+  for (var i = 0; i < names.length; i++) {
+    var validName = false;
+    if (FileSystemMetadataProvider.PROPERTY_NAMES.indexOf(names[i]) !== -1) {
+      fileSystemPropertyNames.push(names[i]);
+      validName = true;
+    }
+    if (ExternalMetadataProvider.PROPERTY_NAMES.indexOf(names[i]) !== -1) {
+      externalPropertyNames.push(names[i]);
+      validName = true;
+    }
+    if (ContentMetadataProvider.PROPERTY_NAMES.indexOf(names[i]) !== -1) {
+      assert(!validName);
+      contentPropertyNames.push(names[i]);
+      validName = true;
+    }
+    assert(validName);
+  }
   return Promise.all([
     this.fileSystemMetadataProvider_.get(localEntries, fileSystemPropertyNames),
-    this.externalMetadataProvider_.get(externalEntries, names)
+    this.externalMetadataProvider_.get(externalEntries, externalPropertyNames),
+    this.contentMetadataProvider_.get(entries, contentPropertyNames)
   ]).then(function(results) {
-    var integratedResults = [];
-    var localResults = results[0];
-    for (var i = 0; i < localResults.length; i++) {
-      integratedResults[localEntryIndexes[i]] = localResults[i];
+    var integratedResults = {};
+    for (var i = 0; i < 3; i++) {
+      var entryList = [localEntries, externalEntries, entries][i];
+      for (var j = 0; j < entryList.length; j++) {
+        var url = entryList[j].toURL();
+        integratedResults[url] = integratedResults[url] || new MetadataItem();
+        for (var name in results[i][j]) {
+          integratedResults[url][name] = results[i][j][name];
+        }
+      }
     }
-    var externalResults = results[1];
-    for (var i = 0; i < externalResults.length; i++) {
-      integratedResults[externalEntryIndexes[i]] = externalResults[i];
-    }
-    return integratedResults;
+    return entries.map(function(entry) {
+      return integratedResults[entry.toURL()];
+    });
   });
 };
 
@@ -90,7 +128,7 @@
  * Obtains metadata cache for entries.
  * @param {!Array<!Entry>} entries Entries.
  * @param {!Array<string>} names Metadata property names to be obtained.
- * @return {!Array<!ExternalMetadataProperties>}
+ * @return {!Array<!MetadataItem>}
  */
 FileSystemMetadata.prototype.getCache = function(entries, names) {
   return this.cache_.get(entries, names);
diff --git a/ui/file_manager/file_manager/foreground/js/metadata/file_system_metadata_provider.js b/ui/file_manager/file_manager/foreground/js/metadata/file_system_metadata_provider.js
index 548d22b..01c8519 100644
--- a/ui/file_manager/file_manager/foreground/js/metadata/file_system_metadata_provider.js
+++ b/ui/file_manager/file_manager/foreground/js/metadata/file_system_metadata_provider.js
@@ -3,22 +3,25 @@
 // found in the LICENSE file.
 
 /**
- * @typedef {{modificationTime:Date, size:number}}
- */
-var FileSystemMetadataProperties;
-
-/**
  * Metadata provider for FileEntry#getMetadata.
  *
  * @param {!MetadataProviderCache} cache
  * @constructor
- * @extends {NewMetadataProvider<!FileSystemMetadataProperties>}
+ * @extends {NewMetadataProvider}
  * @struct
  */
 function FileSystemMetadataProvider(cache) {
-  NewMetadataProvider.call(this, cache, ['modificationTime', 'size']);
+  NewMetadataProvider.call(
+      this, cache, FileSystemMetadataProvider.PROPERTY_NAMES);
 }
 
+/**
+ * @const {!Array<string>}
+ */
+FileSystemMetadataProvider.PROPERTY_NAMES = [
+  'modificationTime', 'size', 'present', 'availableOffline', 'contentMimeType'
+];
+
 FileSystemMetadataProvider.prototype.__proto__ = NewMetadataProvider.prototype;
 
 /**
@@ -26,13 +29,29 @@
  */
 FileSystemMetadataProvider.prototype.getImpl = function(requests) {
   return Promise.all(requests.map(function(request) {
-    return new Promise(function(fulfill, reject) {
-      request.entry.getMetadata(fulfill, reject);
-    }).then(function(properties) {
-      return {
-        modificationTime: properties.modificationTime,
-        size: request.entry.isDirectory ? -1 : properties.size
-      };
+    return Promise.all([
+        new Promise(function(fulfill, reject) {
+          request.entry.getMetadata(fulfill, reject);
+        }),
+        new Promise(function(fulfill) {
+          if (request.names.indexOf('contentMimeType') > -1) {
+            chrome.fileManagerPrivate.getMimeType(
+                request.entry.toURL(), fulfill);
+          } else {
+            fulfill(null);
+          }
+        })
+    ]).then(function(results) {
+      var item = new MetadataItem();
+      item.modificationTime = results[0].modificationTime;
+      item.size = request.entry.isDirectory ? -1 : results[0].size;
+      item.present = true;
+      item.availableOffline = true;
+      if (results[1] !== null)
+        item.contentMimeType = results[1];
+      return item;
+    }, function() {
+      return new MetadataItem();
     });
   }));
 };
diff --git a/ui/file_manager/file_manager/foreground/js/metadata/file_system_metadata_provider_unittest.html b/ui/file_manager/file_manager/foreground/js/metadata/file_system_metadata_provider_unittest.html
index c971788..a4b10f7 100644
--- a/ui/file_manager/file_manager/foreground/js/metadata/file_system_metadata_provider_unittest.html
+++ b/ui/file_manager/file_manager/foreground/js/metadata/file_system_metadata_provider_unittest.html
@@ -13,7 +13,8 @@
 <script src="../../../../../webui/resources/js/assert.js"></script>
 <script src="../../../common/js/lru_cache.js"></script>
 <script src="../../../common/js/unittest_util.js"></script>
-<script src="metadata_cache_item.js"></script>
 <script src="file_system_metadata_provider.js"></script>
+<script src="metadata_cache_item.js"></script>
+<script src="metadata_item.js"></script>
 
 <script src="file_system_metadata_provider_unittest.js"></script>
diff --git a/ui/file_manager/file_manager/foreground/js/metadata/file_system_metadata_provider_unittest.js b/ui/file_manager/file_manager/foreground/js/metadata/file_system_metadata_provider_unittest.js
index b8204e1..3f81abd 100644
--- a/ui/file_manager/file_manager/foreground/js/metadata/file_system_metadata_provider_unittest.js
+++ b/ui/file_manager/file_manager/foreground/js/metadata/file_system_metadata_provider_unittest.js
@@ -18,21 +18,67 @@
   }
 };
 
+function setUp() {
+  chrome.fileManagerPrivate = {
+    getMimeType: function(url, callback) {
+      chrome.fileManagerPrivate.isGetMimeTypeCalled_ = true;
+
+      switch (url) {
+        case 'filesystem://A':
+          callback('application/A');
+          break;
+        case 'filesystem://B':
+          callback('application/B');
+          break;
+        default:
+          callback('');
+          break;
+      }
+    },
+    isGetMimeTypeCalled_: false
+  };
+}
+
 function testFileSystemMetadataProviderBasic(callback) {
   var cache = new MetadataProviderCache();
   var provider = new FileSystemMetadataProvider(cache);
   reportPromise(provider.get(
       [entryA, entryB],
-      ['modificationTime', 'size']).then(
+      ['modificationTime', 'size', 'contentMimeType',
+       'present', 'availableOffline']).then(
           function(results) {
             assertEquals(2, results.length);
             assertEquals(
                 new Date(2015, 1, 1).toString(),
                 results[0].modificationTime.toString());
             assertEquals(1024, results[0].size);
+            assertEquals('application/A', results[0].contentMimeType);
+            assertTrue(results[0].present);
+            assertTrue(results[0].availableOffline);
             assertEquals(
                 new Date(2015, 2, 2).toString(),
                 results[1].modificationTime.toString());
             assertEquals(2048, results[1].size);
+            assertEquals('application/B', results[1].contentMimeType);
+            assertTrue(results[1].present);
+            assertTrue(results[1].availableOffline);
+          }), callback);
+}
+
+function testFileSystemMetadataProviderPartialRequest(callback) {
+  var cache = new MetadataProviderCache();
+  var provider = new FileSystemMetadataProvider(cache);
+  reportPromise(provider.get(
+      [entryA],
+      ['modificationTime', 'size']).then(
+          function(results) {
+            assertEquals(1, results.length);
+            assertEquals(
+                new Date(2015, 1, 1).toString(),
+                results[0].modificationTime.toString());
+            assertEquals(1024, results[0].size);
+            // When contentMimeType is not requested, this shouldn't try to get
+            // MIME type.
+            assertFalse(chrome.fileManagerPrivate.isGetMimeTypeCalled_);
           }), callback);
 }
diff --git a/ui/file_manager/file_manager/foreground/js/metadata/file_system_metadata_unittest.html b/ui/file_manager/file_manager/foreground/js/metadata/file_system_metadata_unittest.html
index 6be027b..7c23ebc7 100644
--- a/ui/file_manager/file_manager/foreground/js/metadata/file_system_metadata_unittest.html
+++ b/ui/file_manager/file_manager/foreground/js/metadata/file_system_metadata_unittest.html
@@ -14,9 +14,11 @@
 <script src="../../../common/js/lru_cache.js"></script>
 <script src="../../../common/js/unittest_util.js"></script>
 <script src="../../../common/js/volume_manager_common.js"></script>
+<script src="content_metadata_provider.js"></script>
 <script src="external_metadata_provider.js"></script>
 <script src="file_system_metadata.js"></script>
 <script src="file_system_metadata_provider.js"></script>
 <script src="metadata_cache_item.js"></script>
+<script src="metadata_item.js"></script>
 
 <script src="file_system_metadata_unittest.js"></script>
diff --git a/ui/file_manager/file_manager/foreground/js/metadata/file_system_metadata_unittest.js b/ui/file_manager/file_manager/foreground/js/metadata/file_system_metadata_unittest.js
index a9a4d3e..0b6e8f95 100644
--- a/ui/file_manager/file_manager/foreground/js/metadata/file_system_metadata_unittest.js
+++ b/ui/file_manager/file_manager/foreground/js/metadata/file_system_metadata_unittest.js
@@ -16,22 +16,34 @@
       cache,
       // Mocking FileSystemMetadataProvider.
       {
-        get: function(urls) {
-          assertEquals(1, urls.length);
-          assertEquals('filesystem://A', urls[0].toURL());
+        get: function(entries) {
+          assertEquals(1, entries.length);
+          assertEquals('filesystem://A', entries[0].toURL());
           return Promise.resolve(
               [{modificationTime: new Date(2015, 0, 1), size: 1024}]);
         }
       },
       // Mocking ExternalMetadataProvider.
       {
-        get: function(urls) {
-          assertEquals(1, urls.length);
-          assertEquals('filesystem://B', urls[0].toURL());
+        get: function(entries) {
+          assertEquals(1, entries.length);
+          assertEquals('filesystem://B', entries[0].toURL());
           return Promise.resolve(
               [{modificationTime: new Date(2015, 1, 2), size: 2048}]);
         }
       },
+      // Mocking ContentMetadataProvider.
+      {
+        get: function(entries) {
+          assertEquals(2, entries.length);
+          assertEquals('filesystem://A', entries[0].toURL());
+          assertEquals('filesystem://B', entries[1].toURL());
+          return Promise.resolve([
+            {contentThumbnailUrl: 'THUMBNAIL_URL_A'},
+            {contentThumbnailUrl: 'THUMBNAIL_URL_B'}
+          ]);
+        }
+      },
       // Mocking VolumeManagerWrapper.
       {
         getVolumeInfo: function(entry) {
@@ -48,16 +60,20 @@
         }
       });
   reportPromise(
-      model.get([entryA, entryB], ['size', 'modificationTime']).then(
+      model.get(
+          [entryA, entryB],
+          ['size', 'modificationTime', 'contentThumbnailUrl']).then(
           function(results) {
             assertEquals(2, results.length);
             assertEquals(
                 new Date(2015, 0, 1).toString(),
                 results[0].modificationTime.toString());
             assertEquals(1024, results[0].size);
+            assertEquals('THUMBNAIL_URL_A', results[0].contentThumbnailUrl);
             assertEquals(
                 new Date(2015, 1, 2).toString(),
                 results[1].modificationTime.toString());
             assertEquals(2048, results[1].size);
+            assertEquals('THUMBNAIL_URL_B', results[1].contentThumbnailUrl);
           }), callback);
 }
diff --git a/ui/file_manager/file_manager/foreground/js/metadata/metadata_cache.js b/ui/file_manager/file_manager/foreground/js/metadata/metadata_cache.js
index 1532f5ed..f92bc17a 100644
--- a/ui/file_manager/file_manager/foreground/js/metadata/metadata_cache.js
+++ b/ui/file_manager/file_manager/foreground/js/metadata/metadata_cache.js
@@ -859,6 +859,10 @@
   var entryURLs = util.entriesToURLs(entries);
   chrome.fileManagerPrivate.getEntryProperties(
       entryURLs,
+      ['size', 'modificationTime', 'thumbnailUrl', 'imageWidth', 'imageHeight',
+       'imageRotation', 'pinned', 'present', 'hosted', 'availableOffline',
+       'availableWhenMetered', 'dirty', 'customIconUrl', 'contentMimeType',
+       'sharedWithMe', 'shared', 'externalFileUrl'],
       function(propertiesList) {
         console.assert(propertiesList.length === callbacks.length);
         for (var i = 0; i < callbacks.length; i++) {
@@ -877,15 +881,15 @@
 ExternalProvider.prototype.convert_ = function(data, entry) {
   var result = {};
   result.external = {
-    present: data.isPresent,
-    pinned: data.isPinned,
-    hosted: data.isHosted,
-    dirty: data.isDirty,
+    present: data.present,
+    pinned: data.pinned,
+    hosted: data.hosted,
+    dirty: data.dirty,
     imageWidth: data.imageWidth,
     imageHeight: data.imageHeight,
     imageRotation: data.imageRotation,
-    availableOffline: data.isAvailableOffline,
-    availableWhenMetered: data.isAvailableWhenMetered,
+    availableOffline: data.availableOffline,
+    availableWhenMetered: data.availableWhenMetered,
     customIconUrl: data.customIconUrl || '',
     contentMimeType: data.contentMimeType || '',
     sharedWithMe: data.sharedWithMe,
@@ -895,14 +899,14 @@
   };
 
   result.filesystem = {
-    size: (entry.isFile ? (data.fileSize || 0) : -1),
-    modificationTime: new Date(data.lastModifiedTime)
+    size: (entry.isFile ? (data.size || 0) : -1),
+    modificationTime: new Date(data.modificationTime)
   };
 
   // TODO(mtomasz): Remove all of the if logic in the new metadata cache.
   // If the file is not present, then use the thumbnail url instead of
   // extracting the thumbnail from contents.
-  if (data.isPresent === false) {
+  if (data.present === false) {
     if ('thumbnailUrl' in data) {
       result.thumbnail = {
         url: data.thumbnailUrl,
@@ -916,7 +920,7 @@
 
   // If not present in cache, then do not allow to fetch media by next
   // providers.
-  if (data.isPresent === false)
+  if (data.present === false)
     result.media = {};
 
   return result;
diff --git a/ui/file_manager/file_manager/foreground/js/metadata/metadata_cache_item.js b/ui/file_manager/file_manager/foreground/js/metadata/metadata_cache_item.js
index 1efae04..5f0b8a2f4 100644
--- a/ui/file_manager/file_manager/foreground/js/metadata/metadata_cache_item.js
+++ b/ui/file_manager/file_manager/foreground/js/metadata/metadata_cache_item.js
@@ -54,11 +54,12 @@
 /**
  * Feeds the result of startRequests.
  * @param {number} requestId Request ID passed when calling startRequests.
- * @param {!Object} object Map of property name and value.
+ * @param {!MetadataItem} typedObject Map of property name and value.
  * @return {boolean} Whether at least one property is updated or not.
  */
-MetadataCacheItem.prototype.storeProperties = function(requestId, object) {
+MetadataCacheItem.prototype.storeProperties = function(requestId, typedObject) {
   var changed = false;
+  var object = /** @type {!Object} */(typedObject);
   for (var name in object) {
     if (!this.properties_[name])
       this.properties_[name] = new MetadataCacheItemProperty();
@@ -93,16 +94,16 @@
  * Obtains property for entries and names.
  * Note that it returns invalidated properties also.
  * @param {!Array<string>} names
- * @return {!Object}
+ * @return {!MetadataItem}
  */
 MetadataCacheItem.prototype.get = function(names) {
-  var result = {};
+  var result = /** @type {!Object} */(new MetadataItem());
   for (var i = 0; i < names.length; i++) {
     var name = names[i];
     if (this.properties_[name])
       result[name] = this.properties_[name].value;
   }
-  return result;
+  return /** @type {!MetadataItem} */(result);
 };
 
 /**
diff --git a/ui/file_manager/file_manager/foreground/js/metadata/metadata_cache_item_unittest.html b/ui/file_manager/file_manager/foreground/js/metadata/metadata_cache_item_unittest.html
index 06f6f7a..a08dff0 100644
--- a/ui/file_manager/file_manager/foreground/js/metadata/metadata_cache_item_unittest.html
+++ b/ui/file_manager/file_manager/foreground/js/metadata/metadata_cache_item_unittest.html
@@ -6,5 +6,6 @@
 <script src="../../../../../webui/resources/js/assert.js"></script>
 <script src="../../../common/js/unittest_util.js"></script>
 <script src="metadata_cache_item.js"></script>
+<script src="metadata_item.js"></script>
 
 <script src="metadata_cache_item_unittest.js"></script>
diff --git a/ui/file_manager/file_manager/foreground/js/metadata/metadata_cache_set.js b/ui/file_manager/file_manager/foreground/js/metadata/metadata_cache_set.js
index 6a0dc31..5d499509 100644
--- a/ui/file_manager/file_manager/foreground/js/metadata/metadata_cache_set.js
+++ b/ui/file_manager/file_manager/foreground/js/metadata/metadata_cache_set.js
@@ -63,7 +63,7 @@
  * @param {number} requestId Request ID. If a newer operation has already been
  *     done, the results must be ingored.
  * @param {!Array<!Entry>} entries
- * @param {!Array<!Object>} results
+ * @param {!Array<!MetadataItem>} results
  * @return {boolean} Whether at least one result is stored or not.
  */
 MetadataCacheSet.prototype.storeProperties = function(
diff --git a/ui/file_manager/file_manager/foreground/js/metadata/metadata_cache_set_unittest.html b/ui/file_manager/file_manager/foreground/js/metadata/metadata_cache_set_unittest.html
index a086524..b6f8d05 100644
--- a/ui/file_manager/file_manager/foreground/js/metadata/metadata_cache_set_unittest.html
+++ b/ui/file_manager/file_manager/foreground/js/metadata/metadata_cache_set_unittest.html
@@ -13,5 +13,6 @@
 <script src="../../../common/js/unittest_util.js"></script>
 <script src="metadata_cache_item.js"></script>
 <script src="metadata_cache_set.js"></script>
+<script src="metadata_item.js"></script>
 
 <script src="metadata_cache_set_unittest.js"></script>
diff --git a/ui/file_manager/file_manager/foreground/js/metadata/metadata_dispatcher.js b/ui/file_manager/file_manager/foreground/js/metadata/metadata_dispatcher.js
index c8ff67d..6389ed9 100644
--- a/ui/file_manager/file_manager/foreground/js/metadata/metadata_dispatcher.js
+++ b/ui/file_manager/file_manager/foreground/js/metadata/metadata_dispatcher.js
@@ -80,7 +80,7 @@
   // Inform our owner that we're done initializing.
   // If we need to pass more data back, we can add it to the param array.
   this.postMessage('initialized', [this.parserRegexp_]);
-  this.log('initialized with URL filter ' + this.parserRegexp_);
+  this.vlog('initialized with URL filter ' + this.parserRegexp_);
 };
 
 /**
@@ -199,13 +199,13 @@
     function getEntry(parser) {
       webkitResolveLocalFileSystemURL(
           fileURL,
-          function(entry) { nextStep(entry, parser) },
+          function(entry) { nextStep(entry, parser); },
           onError);
     },
 
     // Step three, turn the entry into a file.
     function getFile(entry, parser) {
-      entry.file(function(file) { nextStep(file, parser) }, onError);
+      entry.file(function(file) { nextStep(file, parser); }, onError);
     },
 
     // Step four, parse the file content.
diff --git a/ui/file_manager/file_manager/foreground/js/metadata/metadata_item.js b/ui/file_manager/file_manager/foreground/js/metadata/metadata_item.js
new file mode 100644
index 0000000..447ce56
--- /dev/null
+++ b/ui/file_manager/file_manager/foreground/js/metadata/metadata_item.js
@@ -0,0 +1,141 @@
+// 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.
+
+/**
+ * @typedef {{
+ *  scaleX: number,
+ *  scaleY: number,
+ *  rotate90: number
+ * }}
+ */
+var ImageTransformation;
+
+/**
+ * @constructor
+ * @struct
+ */
+function MetadataItem() {
+  /**
+   * Size of the file. -1 for directory.
+   * @public {number|undefined}
+   */
+  this.size;
+
+  /**
+   * @public {!Date|undefined}
+   */
+  this.modificationTime;
+
+  /**
+   * Thumbnail URL obtained from external provider.
+   * @public {string|undefined}
+   */
+  this.thumbnailUrl;
+
+  /**
+   * @public {number|undefined}
+   */
+  this.imageWidth;
+
+  /**
+   * @public {number|undefined}
+   */
+  this.imageHeight;
+
+  /**
+   * @public {number|undefined}
+   */
+  this.imageRotation;
+
+  /**
+   * Thumbnail obtained from content provider.
+   * @public {string|undefined}
+   */
+  this.contentThumbnailUrl;
+
+  /**
+   * Thumbnail transformation obtained from content provider.
+   * @public {!ImageTransformation|undefined}
+   */
+  this.contentThumbnailTransform;
+
+  /**
+   * Image transformation obtained from content provider.
+   * @public {!ImageTransformation|undefined}
+   */
+  this.contentImageTransform;
+
+  /**
+   * Whether the entry is pinned for ensuring it is available offline.
+   * @public {boolean|undefined}
+   */
+  this.pinned;
+
+  /**
+   * Whether the entry is cached locally.
+   * @public {boolean|undefined}
+   */
+  this.present;
+
+  /**
+   * Whether the entry is hosted document of google drive.
+   * @public {boolean|undefined}
+   */
+  this.hosted;
+
+  /**
+   * Whether the entry is modified locally and not synched yet.
+   * @public {boolean|undefined}
+   */
+  this.dirty;
+
+  /**
+   * Whether the entry is present or hosted;
+   * @public {boolean|undefined}
+   */
+  this.availableOffline;
+
+  /**
+   * @public {boolean|undefined}
+   */
+  this.availableWhenMetered;
+
+  /**
+   * @public {string|undefined}
+   */
+  this.customIconUrl;
+
+  /**
+   * @public {string|undefined}
+   */
+  this.contentMimeType;
+
+  /**
+   * Whether the entry is shared explicitly with me.
+   * @public {boolean|undefined}
+   */
+  this.sharedWithMe;
+
+  /**
+   * Whether the entry is shared publicly.
+   * @public {boolean|undefined}
+   */
+  this.shared;
+
+  /**
+   * URL for open a file in browser tab.
+   * @public {string|undefined}
+   */
+  this.externalFileUrl;
+
+  /**
+   * @public {string|undefined}
+   */
+  this.mediaTitle;
+
+  /**
+   * @public {string|undefined}
+   */
+  this.mediaArtist;
+}
diff --git a/ui/file_manager/file_manager/foreground/js/metadata/new_metadata_provider.js b/ui/file_manager/file_manager/foreground/js/metadata/new_metadata_provider.js
index 846fd02..fc088271 100644
--- a/ui/file_manager/file_manager/foreground/js/metadata/new_metadata_provider.js
+++ b/ui/file_manager/file_manager/foreground/js/metadata/new_metadata_provider.js
@@ -8,7 +8,6 @@
  * @param {!Array<string>} validPropertyNames
  * @constructor
  * @struct
- * @template T
  */
 function NewMetadataProvider(cache, validPropertyNames) {
   /**
@@ -38,7 +37,10 @@
 /**
  * Obtains the metadata for the request.
  * @param {!Array<!MetadataRequest>} requests
- * @return {!Promise<!Array<!T>>}
+ * @return {!Promise<!Array<!MetadataItem>>} Promise with obtained metadata. It
+ *     should not return rejected promise. Instead it should return undefined
+ *     property for property error, and should return empty MetadataItem for
+ *     entry error.
  * @protected
  */
 NewMetadataProvider.prototype.getImpl;
@@ -47,7 +49,7 @@
  * Obtains metadata for entries.
  * @param {!Array<!Entry>} entries Entries.
  * @param {!Array<string>} names Metadata property names to be obtained.
- * @return {!Promise<!Array<!T>>}
+ * @return {!Promise<!Array<!MetadataItem>>}
  */
 NewMetadataProvider.prototype.get = function(entries, names) {
   // Check if the property name is correct or not.
@@ -68,9 +70,9 @@
   this.cache_.startRequests(requestId, requests);
 
   // Register callback.
-  var promise = new Promise(function(fulfill, reject) {
+  var promise = new Promise(function(fulfill) {
     this.callbackRequests_.push(new MetadataProviderCallbackRequest(
-        entries, names, snapshot, fulfill, reject));
+        entries, names, snapshot, fulfill));
   }.bind(this));
 
   // If the requests are not empty, call the requests.
@@ -89,9 +91,7 @@
       }
 
       // Store cache.
-      if (this.cache_.storeProperties(requestId, requestedEntries, list)) {
-        // TODO(hirono): Dispatch metadata change event here.
-      }
+      this.cache_.storeProperties(requestId, requestedEntries, list);
 
       // Invoke callbacks.
       var i = 0;
@@ -104,11 +104,7 @@
           i++;
         }
       }
-    }.bind(this), function(error) {
-      // TODO(hirono): Handle rejection here and call rejection callback of
-      // MetadataProviderCallbackRequest.
-      console.error(error.stack);
-    });
+    }.bind(this));
   }
 
   return promise;
@@ -118,7 +114,7 @@
  * Obtains metadata cache for entries.
  * @param {!Array<!Entry>} entries Entries.
  * @param {!Array<string>} names Metadata property names to be obtained.
- * @return {!Array<!T>}
+ * @return {!Array<!MetadataItem>}
  */
 NewMetadataProvider.prototype.getCache = function(entries, names) {
   // Check if the property name is correct or not.
@@ -132,14 +128,11 @@
  * @param {!Array<!Entry>} entries
  * @param {!Array<string>} names
  * @param {!MetadataCacheSet} cache
- * @param {function(!T):undefined} fulfill
- * @param {function():undefined} reject
+ * @param {function(!MetadataItem):undefined} fulfill
  * @constructor
  * @struct
- * @template T
  */
-function MetadataProviderCallbackRequest(
-    entries, names, cache, fulfill, reject) {
+function MetadataProviderCallbackRequest(entries, names, cache, fulfill) {
   /**
    * @private {!Array<!Entry>}
    * @const
@@ -159,16 +152,10 @@
   this.cache_ = cache;
 
   /**
-   * @private {function(!T):undefined}
+   * @private {function(!MetadataItem):undefined}
    * @const
    */
   this.fulfill_ = fulfill;
-
-  /**
-   * @private {function():undefined}
-   * @const
-   */
-  this.reject_ = reject;
 }
 
 /**
@@ -176,7 +163,7 @@
  * If all the requested property are served, it invokes the callback.
  * @param {number} requestId
  * @param {!Array<!Entry>} entries
- * @param {!Array<!Object>} objects
+ * @param {!Array<!MetadataItem>} objects
  * @return {boolean} Whether the callback is invoked or not.
  */
 MetadataProviderCallbackRequest.prototype.storeProperties = function(
diff --git a/ui/file_manager/file_manager/foreground/js/metadata/new_metadata_provider_unittest.html b/ui/file_manager/file_manager/foreground/js/metadata/new_metadata_provider_unittest.html
index 10e32f7..476242cd 100644
--- a/ui/file_manager/file_manager/foreground/js/metadata/new_metadata_provider_unittest.html
+++ b/ui/file_manager/file_manager/foreground/js/metadata/new_metadata_provider_unittest.html
@@ -13,6 +13,7 @@
 <script src="../../../common/js/lru_cache.js"></script>
 <script src="../../../common/js/unittest_util.js"></script>
 <script src="metadata_cache_item.js"></script>
+<script src="metadata_item.js"></script>
 <script src="new_metadata_provider.js"></script>
 
 <script src="new_metadata_provider_unittest.js"></script>
diff --git a/ui/file_manager/file_manager/foreground/js/metadata/thumbnail_model.js b/ui/file_manager/file_manager/foreground/js/metadata/thumbnail_model.js
new file mode 100644
index 0000000..16f4a5f
--- /dev/null
+++ b/ui/file_manager/file_manager/foreground/js/metadata/thumbnail_model.js
@@ -0,0 +1,77 @@
+// 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.
+
+/**
+ * @param {!FileSystemMetadata} fileSystemMetadata
+ * @struct
+ * @constructor
+ */
+function ThumbnailModel(fileSystemMetadata) {
+  /**
+   * @private {!FileSystemMetadata}
+   * @const
+   */
+  this.fileSystemMetadata_ = fileSystemMetadata;
+}
+
+/**
+ * @param {!Array<!Entry>} entries
+ * @return {Promise} Promise fulfilled with old format metadata list.
+ */
+ThumbnailModel.prototype.get = function(entries) {
+  var results = {};
+  return this.fileSystemMetadata_.get(
+      entries,
+      [
+        'modificationTime',
+        'customIconUrl',
+        'thumbnailUrl',
+        'present',
+        'dirty'
+      ]).then(function(metadataList) {
+        var contentRequestEntries = [];
+        for (var i = 0; i < entries.length; i++) {
+          var url = entries[i].toURL();
+          // TODO(hirono): Use the provider results directly after removing code
+          // using old metadata format.
+          results[url] = {
+            filesystem: {
+              modificationTime: metadataList[i].modificationTime
+            },
+            external: {
+              thumbnailUrl: metadataList[i].thumbnailUrl,
+              customIconUrl: metadataList[i].customIconUrl,
+              dirty: metadataList[i].dirty
+            },
+            thumbnail: {},
+            media: {}
+          };
+          var canUseContentThumbnail =
+              metadataList[i].present && FileType.isImage(entries[i]);
+          if (canUseContentThumbnail)
+            contentRequestEntries.push(entries[i]);
+        }
+        if (contentRequestEntries.length) {
+          return this.fileSystemMetadata_.get(
+              contentRequestEntries,
+              [
+                'contentThumbnailUrl',
+                'contentThumbnailTransform',
+                'contentImageTransform'
+              ]).then(function(contentMetadataList) {
+                for (var i = 0; i < contentRequestEntries.length; i++) {
+                  var url = contentRequestEntries[i].toURL();
+                  results[url].thumbnail.url =
+                      contentMetadataList[i].contentThumbnailUrl;
+                  results[url].thumbnail.transform =
+                      contentMetadataList[i].contentThumbnailTransform;
+                  results[url].media.imageTransform =
+                      contentMetadataList[i].contentImageTransform;
+                }
+              });
+        }
+      }.bind(this)).then(function() {
+        return entries.map(function(entry) { return results[entry.toURL()]; });
+      });
+};
diff --git a/ui/file_manager/file_manager/foreground/js/metadata/thumbnail_model_unittest.html b/ui/file_manager/file_manager/foreground/js/metadata/thumbnail_model_unittest.html
new file mode 100644
index 0000000..bb10cdcb
--- /dev/null
+++ b/ui/file_manager/file_manager/foreground/js/metadata/thumbnail_model_unittest.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<!-- 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.
+  -->
+<script src="../../../common/js/file_type.js"></script>
+<script src="../../../common/js/unittest_util.js"></script>
+<script src="metadata_item.js"></script>
+<script src="thumbnail_model.js"></script>
+
+<script src="thumbnail_model_unittest.js"></script>
diff --git a/ui/file_manager/file_manager/foreground/js/metadata/thumbnail_model_unittest.js b/ui/file_manager/file_manager/foreground/js/metadata/thumbnail_model_unittest.js
new file mode 100644
index 0000000..ac8ca0a6
--- /dev/null
+++ b/ui/file_manager/file_manager/foreground/js/metadata/thumbnail_model_unittest.js
@@ -0,0 +1,85 @@
+// 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.
+
+var imageEntry = {
+  name: 'image.jpg',
+  toURL: function() { return 'filesystem://A'; }
+};
+
+var nonImageEntry = {
+  name: 'note.txt',
+  toURL: function() { return 'filesystem://B'; }
+};
+
+var metadata;
+var contentMetadata;
+var thumbnailModel;
+
+function setUp() {
+  metadata = new MetadataItem();
+  metadata.modificationTime = new Date(2015, 0, 1);
+  metadata.present = true;
+  metadata.dirty = false;
+  metadata.thumbnailUrl = 'EXTERNAL_THUMBNAIL_URL';
+  metadata.customIconUrl = 'CUSTOM_ICON_URL';
+  metadata.contentThumbnailUrl = 'CONTENT_THUMBNAIL_URL';
+  metadata.contentThumbnailTransform = 'CONTENT_THUMBNAIL_TRANSFORM';
+  metadata.contentImageTransform = 'CONTENT_IMAGE_TRANSFORM';
+
+  thumbnailModel = new ThumbnailModel({
+      get: function(entries, names) {
+        var result = new MetadataItem();
+        for (var i = 0; i < names.length; i++) {
+          var name = names[i];
+          result[name] = metadata[name];
+        }
+        return Promise.resolve([result]);
+      }});
+}
+
+function testThumbnailModelGetBasic(callback) {
+  reportPromise(thumbnailModel.get([imageEntry]).then(function(results) {
+    assertEquals(1, results.length);
+    assertEquals(
+        new Date(2015, 0, 1).toString(),
+        results[0].filesystem.modificationTime.toString());
+    assertEquals('EXTERNAL_THUMBNAIL_URL', results[0].external.thumbnailUrl);
+    assertEquals('CUSTOM_ICON_URL', results[0].external.customIconUrl);
+    assertFalse(results[0].external.dirty);
+    assertEquals('CONTENT_THUMBNAIL_URL', results[0].thumbnail.url);
+    assertEquals('CONTENT_THUMBNAIL_TRANSFORM', results[0].thumbnail.transform);
+    assertEquals('CONTENT_IMAGE_TRANSFORM', results[0].media.imageTransform);
+  }), callback);
+}
+
+function testThumbnailModelGetNotPresent(callback) {
+  metadata.present = false;
+  reportPromise(thumbnailModel.get([imageEntry]).then(function(results) {
+    assertEquals(1, results.length);
+    assertEquals(
+        new Date(2015, 0, 1).toString(),
+        results[0].filesystem.modificationTime.toString());
+    assertEquals('EXTERNAL_THUMBNAIL_URL', results[0].external.thumbnailUrl);
+    assertEquals('CUSTOM_ICON_URL', results[0].external.customIconUrl);
+    assertFalse(results[0].external.dirty);
+    assertEquals(undefined, results[0].thumbnail.url);
+    assertEquals(undefined, results[0].thumbnail.transform);
+    assertEquals(undefined, results[0].media.imageTransform);
+  }), callback);
+}
+
+function testThumbnailModelGetNonImage(callback) {
+  reportPromise(thumbnailModel.get([nonImageEntry]).then(function(results) {
+    assertEquals(1, results.length);
+    assertEquals(
+        new Date(2015, 0, 1).toString(),
+        results[0].filesystem.modificationTime.toString());
+    assertEquals('EXTERNAL_THUMBNAIL_URL', results[0].external.thumbnailUrl);
+    assertEquals('CUSTOM_ICON_URL', results[0].external.customIconUrl);
+    assertFalse(results[0].external.dirty);
+    assertEquals(undefined, results[0].thumbnail.url);
+    assertEquals(undefined, results[0].thumbnail.transform);
+    assertEquals(undefined, results[0].media.imageTransform);
+  }), callback);
+}
diff --git a/ui/file_manager/file_manager/foreground/js/metrics.js b/ui/file_manager/file_manager/foreground/js/metrics.js
deleted file mode 100644
index 001cea9..0000000
--- a/ui/file_manager/file_manager/foreground/js/metrics.js
+++ /dev/null
@@ -1,44 +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.
-
-/**
- * @fileoverview Utility methods for accessing chrome.metricsPrivate API.
- *
- * To be included as a first script in main.html
- */
-
-var metrics = metricsBase;
-
-metrics.startInterval('Load.Total');
-metrics.startInterval('Load.Script');
-
-/**
- * A mapping of enum names to valid values. This object is consulted
- * any time an enum value is being reported unaccompanied by a list
- * of valid values.
- *
- * <p>Values in this object should correspond exactly with values
- * in {@code tools/metrics/histograms/histograms.xml}.
- *
- * <p>NEVER REMOVE OR REORDER ITEMS IN THIS LIST!
- *
- * @private {!Object.<string, !Array.<*>|number>}
- */
-metrics.validEnumValues_ = {
-  'CloudImport.UserAction': [
-    'IMPORT_INITIATED'
-  ]
-};
-
-/**
- * Convert a short metric name to the full format.
- *
- * @param {string} name Short metric name.
- * @return {string} Full metric name.
- * @override
- * @private
- */
-metrics.convertName_ = function(name) {
-  return 'FileBrowser.' + name;
-};
diff --git a/ui/file_manager/file_manager/foreground/js/metrics_start.js b/ui/file_manager/file_manager/foreground/js/metrics_start.js
new file mode 100644
index 0000000..8695c097
--- /dev/null
+++ b/ui/file_manager/file_manager/foreground/js/metrics_start.js
@@ -0,0 +1,12 @@
+// Copyright (c) 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.
+
+/**
+ * @fileoverview Metrics calls to start measurement of script loading.  Include
+ * this as the first script in main.html (i.e. after the common scripts that
+ * define the metrics namespace).
+ */
+
+metrics.startInterval('Load.Total');
+metrics.startInterval('Load.Script');
diff --git a/ui/file_manager/file_manager/foreground/js/mouse_inactivity_watcher.js b/ui/file_manager/file_manager/foreground/js/mouse_inactivity_watcher.js
index d0dc178..ab7f333 100644
--- a/ui/file_manager/file_manager/foreground/js/mouse_inactivity_watcher.js
+++ b/ui/file_manager/file_manager/foreground/js/mouse_inactivity_watcher.js
@@ -54,7 +54,10 @@
   var initiateFading = this.activityStopped_.bind(this, this.timeout_);
   this.container_.addEventListener('touchend', initiateFading);
   this.container_.addEventListener('touchcancel', initiateFading);
-  this.container_.addEventListener('focusin', initiateFading);
+  this.container_.addEventListener('focusin', function() {
+    this.activityStarted_();
+    this.activityStopped_();
+  }.bind(this));
 }
 
 /**
diff --git a/ui/file_manager/file_manager/foreground/js/naming_controller.js b/ui/file_manager/file_manager/foreground/js/naming_controller.js
index 8fdda9b..50a6ff3 100644
--- a/ui/file_manager/file_manager/foreground/js/naming_controller.js
+++ b/ui/file_manager/file_manager/foreground/js/naming_controller.js
@@ -103,7 +103,7 @@
         if (!isValid)
           return Promise.reject('Invalid filename.');
 
-        if (util.isFakeEntry(directory)) {
+        if (directory && util.isFakeEntry(directory)) {
           // Can't save a file into a fake directory.
           return Promise.reject('Cannot save into fake entry.');
         }
diff --git a/ui/file_manager/file_manager/foreground/js/search_controller.js b/ui/file_manager/file_manager/foreground/js/search_controller.js
index 8713797..ae12d88 100644
--- a/ui/file_manager/file_manager/foreground/js/search_controller.js
+++ b/ui/file_manager/file_manager/foreground/js/search_controller.js
@@ -151,7 +151,7 @@
         // Discard results for previous requests and fire a new search
         // for the most recent query.
         if (searchString != this.lastAutocompleteQuery_) {
-          this.requestAutocompleteSuggestions_(this.lastAutocompleteQuery_);
+          this.requestAutocompleteSuggestions_();
           return;
         }
 
diff --git a/ui/file_manager/file_manager/foreground/js/ui/directory_tree.js b/ui/file_manager/file_manager/foreground/js/ui/directory_tree.js
index 724c1621..d254c49 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/directory_tree.js
+++ b/ui/file_manager/file_manager/foreground/js/ui/directory_tree.js
@@ -41,10 +41,8 @@
 var TREE_ITEM_INNTER_HTML =
     '<div class="tree-row">' +
     ' <paper-ripple fit class="recenteringTouch"></paper-ripple>' +
-    ' <span class="expand-icon">' +
-    '  <core-icon icon="expand-more" class="expand-icon"></core-icon>' +
-    ' </span>' +
-    ' <core-icon class="icon"></core-icon>' +
+    ' <span class="expand-icon"></span>' +
+    ' <span class="icon"></span>' +
     ' <span class="label entry-name"></span>' +
     '</div>' +
     '<div class="tree-children"></div>';
@@ -223,7 +221,7 @@
  */
 DirectoryItem.prototype.updateSubDirectories = function(
     recursive, opt_successCallback, opt_errorCallback) {
-  if (util.isFakeEntry(this.entry)) {
+  if (!this.entry || util.isFakeEntry(this.entry)) {
     if (opt_errorCallback)
       opt_errorCallback();
     return;
@@ -354,7 +352,7 @@
   if (location && location.rootType && location.isRootEntry) {
     icon.setAttribute('volume-type-icon', location.rootType);
   } else {
-    icon.icon = 'folder';
+    icon.setAttribute('file-type-icon', 'folder');
     item.updateSharedStatusIcon();
   }
 
@@ -397,14 +395,11 @@
  * @override
  */
 SubDirectoryItem.prototype.updateSharedStatusIcon = function() {
-  var iconElement = this.querySelector('.icon');
+  var icon = this.querySelector('.icon');
   this.parentTree_.fileSystemMetadata.notifyEntriesChanged([this.dirEntry_]);
   this.parentTree_.fileSystemMetadata.get([this.dirEntry_], ['shared']).then(
       function(metadata) {
-        if (metadata[0].shared)
-          iconElement.icon = 'folder-shared';
-        else
-          iconElement.icon = 'folder';
+        icon.classList.toggle('shared', metadata[0] && metadata[0].shared);
       });
 };
 
@@ -445,6 +440,11 @@
 
 VolumeItem.prototype = {
   __proto__: DirectoryItem.prototype,
+  /**
+   * Directory entry for the display root, whose initial value is null.
+   * @type {DirectoryEntry}
+   * @override
+   */
   get entry() {
     return this.volumeInfo_.displayRoot;
   },
@@ -535,6 +535,7 @@
     ejectButton.addEventListener(
         'mousedown', function(event) { event.stopPropagation() });
     ejectButton.className = 'root-eject';
+    ejectButton.setAttribute('aria-label', str('UNMOUNT_DEVICE_BUTTON_LABEL'));
     ejectButton.addEventListener('click', function(event) {
       event.stopPropagation();
       var unmountCommand = cr.doc.querySelector('command#unmount');
@@ -575,10 +576,13 @@
         cr.ui.TreeItem.prototype, 'expanded').get.call(this);
   },
   set expanded(b) {
-    if (!b)
-      return;
     Object.getOwnPropertyDescriptor(
         cr.ui.TreeItem.prototype, 'expanded').set.call(this, b);
+    // When Google Drive is expanded while it is selected, select the My Drive.
+    if (b) {
+      if (this.selected && this.entry)
+        this.selectByEntry(this.entry);
+    }
   }
 };
 
diff --git a/ui/file_manager/file_manager/foreground/js/ui/directory_tree_unittest.js b/ui/file_manager/file_manager/foreground/js/ui/directory_tree_unittest.js
index 650061cf..e19e5d2 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/directory_tree_unittest.js
+++ b/ui/file_manager/file_manager/foreground/js/ui/directory_tree_unittest.js
@@ -72,7 +72,6 @@
   // Create mocks.
   var directoryModel = new MockDirectoryModel();
   var volumeManager = new MockVolumeManagerWrapper();
-  var metadataCache = new MockMetadataCache();
 
   // Set entry which is returned by
   // window.webkitResolveLocalFileSystemURLResults.
@@ -81,7 +80,7 @@
       new MockDirectoryEntry(driveFileSystem, '/root');
 
   DirectoryTree.decorate(directoryTree, directoryModel, volumeManager,
-      metadataCache, true);
+      null, true);
   directoryTree.dataModel = new MockNavigationListModel(volumeManager);
   directoryTree.redraw(true);
 
@@ -122,7 +121,6 @@
   // Creates mocks.
   var directoryModel = new MockDirectoryModel();
   var volumeManager = new MockVolumeManagerWrapper();
-  var metadataCache = new MockMetadataCache();
 
   // Sets entry which is returned by
   // window.webkitResolveLocalFileSystemURLResults.
@@ -131,7 +129,7 @@
       new MockDirectoryEntry(driveFileSystem, '/root');
 
   DirectoryTree.decorate(directoryTree, directoryModel, volumeManager,
-      metadataCache, true);
+      null, true);
   directoryTree.dataModel = new MockNavigationListModel(volumeManager);
   directoryTree.updateSubElementsFromList(true);
 
diff --git a/ui/file_manager/file_manager/foreground/js/ui/file_grid.js b/ui/file_manager/file_manager/foreground/js/ui/file_grid.js
index e514257..2eb70ab 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/file_grid.js
+++ b/ui/file_manager/file_manager/foreground/js/ui/file_grid.js
@@ -22,16 +22,14 @@
 /**
  * Decorates an HTML element to be a FileGrid.
  * @param {!Element} self The grid to decorate.
- * @param {MetadataCache} metadataCache Metadata cache for thumbnails.
  * @param {!FileSystemMetadata} fileSystemMetadata File system metadata.
  * @param {VolumeManagerWrapper} volumeManager Volume manager instance.
  * @param {!importer.HistoryLoader} historyLoader
  */
 FileGrid.decorate = function(
-    self, metadataCache, fileSystemMetadata, volumeManager, historyLoader) {
+    self, fileSystemMetadata, volumeManager, historyLoader) {
   cr.ui.Grid.decorate(self);
   self.__proto__ = FileGrid.prototype;
-  self.metadataCache_ = metadataCache;
   self.fileSystemMetadata_ = fileSystemMetadata;
   self.volumeManager_ = volumeManager;
   self.historyLoader_ = historyLoader;
@@ -178,7 +176,6 @@
  * Decorates thumbnail.
  * @param {cr.ui.ListItem} li List item.
  * @param {!Entry} entry Entry to render a thumbnail for.
- * @param {MetadataCache} metadataCache To retrieve thumbnail.
  * @param {!FileSystemMetadata} fileSystemMetadata To retrieve metadata.
  * @param {VolumeManagerWrapper} volumeManager Volume manager instance.
  * @param {!importer.HistoryLoader} historyLoader
@@ -191,7 +188,6 @@
 FileGrid.decorateThumbnail_ = function(
     li,
     entry,
-    metadataCache,
     fileSystemMetadata,
     volumeManager,
     historyLoader,
@@ -406,7 +402,6 @@
   FileGrid.decorateThumbnail_(
       li,
       entry,
-      grid.metadataCache_,
       grid.fileSystemMetadata_,
       grid.volumeManager_,
       grid.historyLoader_,
diff --git a/ui/file_manager/file_manager/foreground/js/ui/file_manager_ui.js b/ui/file_manager/file_manager/foreground/js/ui/file_manager_ui.js
index 03e4fbd..83464b9 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/file_manager_ui.js
+++ b/ui/file_manager/file_manager/foreground/js/ui/file_manager_ui.js
@@ -369,8 +369,12 @@
 FileManagerUI.prototype.setCurrentListType = function(listType) {
   this.listContainer.setCurrentListType(listType);
 
-  this.toggleViewButton.classList.toggle(
-      'thumbnail', listType === ListContainer.ListType.DETAIL);
+  var isListView = (listType === ListContainer.ListType.DETAIL);
+  this.toggleViewButton.classList.toggle('thumbnail', isListView);
+
+  var label = isListView ? str('CHANGE_TO_THUMBNAILVIEW_BUTTON_LABEL') :
+                           str('CHANGE_TO_LISTVIEW_BUTTON_LABEL');
+  this.toggleViewButton.setAttribute('aria-label', label);
   this.relayout();
 };
 
diff --git a/ui/file_manager/file_manager/foreground/js/ui/file_table.js b/ui/file_manager/file_manager/foreground/js/ui/file_table.js
index d007e961..e8a574e 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/file_table.js
+++ b/ui/file_manager/file_manager/foreground/js/ui/file_table.js
@@ -224,7 +224,6 @@
 /**
  * Decorates the element.
  * @param {!Element} self Table to decorate.
- * @param {MetadataCache} metadataCache To retrieve thumbnails.
  * @param {!FileSystemMetadata} fileSystemMetadata To retrieve
  *     metadata.
  * @param {VolumeManagerWrapper} volumeManager To retrieve volume info.
@@ -233,12 +232,10 @@
  *                           False if a file open/save dialog.
  */
 FileTable.decorate = function(
-    self, metadataCache, fileSystemMetadata, volumeManager, historyLoader,
-    fullPage) {
+    self, fileSystemMetadata, volumeManager, historyLoader, fullPage) {
   cr.ui.Table.decorate(self);
   FileTableList.decorate(self.list);
   self.__proto__ = FileTable.prototype;
-  self.metadataCache_ = metadataCache;
   self.fileSystemMetadata_ = fileSystemMetadata;
   self.volumeManager_ = volumeManager;
   self.historyLoader_ = historyLoader;
@@ -779,7 +776,7 @@
       filelist.updateListItemExternalProps(
           listItem,
           this.fileSystemMetadata_.getCache(
-              [entry], ['availableOffline', 'customIconUrl'])[0]);
+              [entry], ['availableOffline', 'customIconUrl', 'shared'])[0]);
     });
   } else if (type === 'import-history') {
     forEachCell('.table-row-cell > .status', function(item, entry, unused) {
@@ -877,7 +874,7 @@
   // updated when the metadata is ready via updateListItemsMetadata. For files
   // not on an external backend, externalProps is not available.
   var externalProps = fileSystemMetadata.getCache(
-      [entry], ['hosted', 'availableOffline'])[0];
+      [entry], ['hosted', 'availableOffline', 'customIconUrl', 'shared'])[0];
   filelist.updateListItemExternalProps(li, externalProps);
 
   // Overriding the default role 'list' to 'listbox' for better
diff --git a/ui/file_manager/file_manager/foreground/js/ui/file_table_list.js b/ui/file_manager/file_manager/foreground/js/ui/file_table_list.js
index d7c2009..2623ceaf 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/file_table_list.js
+++ b/ui/file_manager/file_manager/foreground/js/ui/file_table_list.js
@@ -25,5 +25,17 @@
 FileTableList.prototype.mergeItems = function(beginIndex, endIndex) {
   cr.ui.table.TableList.prototype.mergeItems.call(this, beginIndex, endIndex);
 
+  // Make sure that list item's selected attribute is updated just after the
+  // mergeItems operation is done. This prevents checkmarks on selected items
+  // from being animated unintentinally by redraw.
+  for (var i = beginIndex; i < endIndex; i++) {
+    var item = this.getListItemByIndex(i);
+    if (!item)
+      continue;
+    var isSelected = this.selectionModel.getIndexSelected(i);
+    if (item.selected != isSelected)
+      item.selected = isSelected;
+  }
+
   this.table.updateHighPriorityRange(beginIndex, endIndex);
 }
diff --git a/ui/file_manager/file_manager/foreground/js/ui/search_box.js b/ui/file_manager/file_manager/foreground/js/ui/search_box.js
index eb3b6c7..3ed4f99 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/search_box.js
+++ b/ui/file_manager/file_manager/foreground/js/ui/search_box.js
@@ -45,12 +45,6 @@
   this.inputElement = /** @type {!HTMLInputElement} */ (
       element.querySelector('input'));
 
-  /**
-   * Clear button of the search box.
-   * @type {Element}
-   */
-  this.clearButton = element.querySelector('.clear');
-
   // Register events.
   this.inputElement.addEventListener('input', this.onInput_.bind(this));
   this.inputElement.addEventListener('keydown', this.onKeyDown_.bind(this));
@@ -66,9 +60,6 @@
   this.searchButton.addEventListener(
       'click',
       this.onSearchButtonClick_.bind(this));
-  this.clearButton.addEventListener(
-      'click',
-      this.onClearButtonClick_.bind(this));
   var dispatchItemSelect =
       cr.dispatchSimpleEvent.bind(cr, this, SearchBox.EventType.ITEM_SELECT);
   this.autocompleteList.handleEnterKeydown = dispatchItemSelect;
@@ -267,11 +258,3 @@
 SearchBox.prototype.onSearchButtonClick_ = function() {
   this.inputElement.focus();
 };
-
-/**
- * @private
- */
-SearchBox.prototype.onClearButtonClick_ = function() {
-  this.inputElement.value = '';
-  this.onInput_();
-};
diff --git a/ui/file_manager/file_manager/foreground/js/ui/share_dialog.js b/ui/file_manager/file_manager/foreground/js/ui/share_dialog.js
index d6f03813..4a4a9188 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/share_dialog.js
+++ b/ui/file_manager/file_manager/foreground/js/ui/share_dialog.js
@@ -229,7 +229,7 @@
  *     showing task is completed. The argument is whether to succeed or not.
  *     Note that cancel is regarded as success.
  */
-ShareDialog.prototype.show = function(entry, callback) {
+ShareDialog.prototype.showEntry = function(entry, callback) {
   // If the dialog is already showing, return the error.
   if (this.isShowing()) {
     callback(ShareDialog.Result.ALREADY_SHOWING);
diff --git a/ui/file_manager/file_manager/foreground/js/volume_manager_wrapper.js b/ui/file_manager/file_manager/foreground/js/volume_manager_wrapper.js
index 0b02421..6270d7c 100644
--- a/ui/file_manager/file_manager/foreground/js/volume_manager_wrapper.js
+++ b/ui/file_manager/file_manager/foreground/js/volume_manager_wrapper.js
@@ -265,7 +265,7 @@
 /**
  * Obtains location information from an entry.
  *
- * @param {(Entry|Object)} entry File or directory entry.
+ * @param {(!Entry|!Object)} entry File or directory entry.
  * @return {EntryLocation} Location information.
  */
 VolumeManagerWrapper.prototype.getLocationInfo = function(entry) {
diff --git a/ui/file_manager/file_manager/main.html b/ui/file_manager/file_manager/main.html
index 176c817..8cb4d82 100644
--- a/ui/file_manager/file_manager/main.html
+++ b/ui/file_manager/file_manager/main.html
@@ -47,8 +47,10 @@
 
       <!-- metrics.js initiates load performance tracking
            so we want to parse it as early as possible -->
-      <script src="foreground/js/metrics_base.js"></script>
-      <script src="foreground/js/metrics.js"></script>
+      <script src="common/js/metrics_base.js"></script>
+      <script src="common/js/metrics_events.js"></script>
+      <script src="common/js/metrics.js"></script>
+      <script src="foreground/js/metrics_start.js"></script>
 
       <!-- Loads the client of the image loader extension -->
       <script src="common/js/lru_cache.js"></script>
@@ -106,8 +108,9 @@
       <script src="foreground/js/ui/combobutton.js"></script>
       <script src="foreground/js/ui/commandbutton.js"></script>
       <script src="foreground/js/ui/file_manager_dialog_base.js"></script>
-      <script src="metadata/metadata_cache_set.js"></script>
-      <script src="metadata/new_metadata_provider.js"></script>
+      <script src="foreground/js/metadata/metadata_cache_set.js"></script>
+      <script src="foreground/js/metadata/new_metadata_provider.js"></script>
+      <script src="foreground/js/metadata/thumbnail_model.js"></script>
 
       <script src="foreground/js/app_installer.js"></script>
       <script src="foreground/js/app_state_controller.js"></script>
@@ -126,11 +129,13 @@
       <script src="foreground/js/gear_menu_controller.js"></script>
       <script src="foreground/js/import_controller.js"></script>
       <script src="foreground/js/launch_param.js"></script>
+      <script src="foreground/js/metadata/content_metadata_provider.js"></script>
       <script src="foreground/js/metadata/external_metadata_provider.js"></script>
       <script src="foreground/js/metadata/file_system_metadata.js"></script>
       <script src="foreground/js/metadata/file_system_metadata_provider.js"></script>
       <script src="foreground/js/metadata/metadata_cache.js"></script>
       <script src="foreground/js/metadata/metadata_cache_item.js"></script>
+      <script src="foreground/js/metadata/metadata_item.js"></script>
       <script src="foreground/js/metadata_update_controller.js"></script>
       <script src="foreground/js/naming_controller.js"></script>
       <script src="foreground/js/navigation_list_model.js"></script>
@@ -314,7 +319,8 @@
     <div class="dialog-header">
       <div id="location-breadcrumbs" class="breadcrumbs"></div>
       <div id="cancel-selection-button-wrapper">
-        <paper-button id="cancel-selection-button" tabindex="-1">
+        <paper-button id="cancel-selection-button" tabindex="-1"
+              i18n-values="aria-label:CANCEL_SELECTION_BUTTON_LABEL">
           <span class="icon-arrow-back"></span>
           <span i18n-content="CANCEL_SELECTION_BUTTON_LABEL"></span>
         </paper-button>
@@ -322,17 +328,19 @@
       <div id="files-selected-label"></div>
       <div class="spacer"></div>
       <button id="tasks" class="combobutton" menu="#tasks-menu"
-              tabindex="7">
+              tabindex="7"
+              i18n-values="aria-label:TASKS_BUTTON_LABEL">
         <paper-ripple fit></paper-ripple>
       </button>
-      <button id="search-button" class="icon-button" tabindex="8">
+      <button id="search-button" class="icon-button" tabindex="8"
+              i18n-values="aria-label:SEARCH_TEXT_LABEL">
         <paper-ripple fit class="recenteringTouch circle"></paper-ripple>
       </button>
       <div id="search-box">
         <paper-input-decorator i18n-values="label:SEARCH_TEXT_LABEL">
-          <input is="core-input" type="search" tabindex="-1" i18n-values="aria-label:SEARCH_TEXT_LABEL">
+          <input is="core-input" type="search" tabindex="-1"
+                 i18n-values="aria-label:SEARCH_TEXT_LABEL">
         </paper-input-decorator>
-        <button class="clear" tabindex="-1"></button>
       </div>
       <button id="share-button" class="icon-button" command="#share" tabindex="9"
               i18n-values="aria-label:SHARE_BUTTON_LABEL"
@@ -360,7 +368,8 @@
         <core-icon icon="arrow-drop-down"></core-icon>
         <paper-ripple fit class="recenteringTouch circle"></paper-ripple>
       </button>
-      <button id="view-button" class="icon-button" tabindex="13">
+      <button id="view-button" class="icon-button" tabindex="13"
+              i18n-values="aria-label:CHANGE_TO_THUMBNAILVIEW_BUTTON_LABEL">
         <paper-ripple fit class="recenteringTouch circle"></paper-ripple>
       </button>
       <button id="gear-button" class="icon-button" tabindex="14" menu="#gear-menu"
@@ -449,13 +458,16 @@
     <div class="dialog-footer progressable" tabindex="-1"
          visibleif="saveas-file open-file open-multi-file folder upload-folder">
       <div class="left">
-        <button id="new-folder-button" i18n-content="NEW_FOLDER_BUTTON_LABEL"
+        <!-- TODO(fukino): Turn this button into paper-button when the CommandButton supports paper-button. -->
+        <button id="new-folder-button" class="primary"
+                i18n-content="NEW_FOLDER_BUTTON_LABEL"
                 visibleif="saveas-file folder" command="#new-folder"
-                tabindex="4">
+                tabindex="4" disabled>
         </button>
         <div id="filename-input-box" visibleif="saveas-file">
-          <div class="filename-label" i18n-content="FILENAME_LABEL"></div>
-          <input class="entry-name" type="text" spellcheck="false" tabindex="1">
+          <paper-input-decorator i18n-values="label:FILENAME_LABEL">
+            <input is="core-input" class="entry-name" type="text" spellcheck="false" tabindex="1">
+          </paper-input-decorator>
         </div>
         <div class="preparing-label" i18n-content="PREPARING_LABEL"></div>
         <div class="progress-bar" visibleif="saveas-file">
@@ -464,8 +476,8 @@
       </div>
       <div class="right buttonbar">
         <select class="file-type" hidden></select>
-        <button class="ok" disabled tabindex="2"></button>
-        <button class="cancel" i18n-content="CANCEL_LABEL" tabindex="3"></button>
+        <paper-button class="cancel secondary" i18n-content="CANCEL_LABEL" tabindex="3"></paper-button>
+        <paper-button class="ok primary" disabled tabindex="2"></paper-button>
       </div>
     </div>
     <div id="drag-container"></div>
diff --git a/ui/file_manager/file_manager/manifest.json b/ui/file_manager/file_manager/manifest.json
index 9085b92c..36a9aa9 100644
--- a/ui/file_manager/file_manager/manifest.json
+++ b/ui/file_manager/file_manager/manifest.json
@@ -173,6 +173,9 @@
         "chrome://resources/js/analytics.js",
         "common/js/async_util.js",
         "common/js/file_type.js",
+        "common/js/metrics_base.js",
+        "common/js/metrics_events.js",
+        "common/js/metrics.js",
         "common/js/progress_center_common.js",
         "common/js/util.js",
         "common/js/volume_manager_common.js",
diff --git a/ui/file_manager/file_manager_resources.grd b/ui/file_manager/file_manager_resources.grd
index b05d637..5091f19 100644
--- a/ui/file_manager/file_manager_resources.grd
+++ b/ui/file_manager/file_manager_resources.grd
@@ -25,6 +25,9 @@
       <include name="IDR_FILE_MANAGER_UTIL_JS" file="file_manager/common/js/util.js" flattenhtml="false" type="BINDATA" />
       <include name="IDR_FILE_MANAGER_VOLUME_MANAGER_COMMON_JS" file="file_manager/common/js/volume_manager_common.js" flattenhtml="false" type="BINDATA" />
       <include name="IDR_FILE_MANAGER_IMPORTER_COMMON_JS" file="file_manager/common/js/importer_common.js" flattenhtml="false" type="BINDATA" />
+      <include name="IDR_FILE_MANAGER_METRICS_BASE_JS" file="file_manager/common/js/metrics_base.js" flattenhtml="false" type="BINDATA" />
+      <include name="IDR_FILE_MANAGER_METRICS_JS" file="file_manager/common/js/metrics.js" flattenhtml="false" type="BINDATA" />
+      <include name="IDR_FILE_MANAGER_METRICS_EVENTS_JS" file="file_manager/common/js/metrics_events.js" flattenhtml="false" type="BINDATA" />
 
       <!-- Scripts working in background page. -->
       <include name="IDR_FILE_MANAGER_DEVICE_APP_WINDOW_WRAPPER_JS" file="file_manager/background/js/app_window_wrapper.js" flattenhtml="false" type="BINDATA" />
@@ -101,6 +104,7 @@
       <!-- AudioPlayer.app pages and scripts. -->
       <include name="IDR_AUDIO_PLAYER_MANIFEST" file="audio_player/manifest.json" type="BINDATA" />
       <include name="IDR_AUDIO_PLAYER_BKGND_JS" file="audio_player/js/background.js" flattenhtml="true" type="BINDATA" />
+      <include name="IDR_AUDIO_PLAYER_METADATA_WORKER_JS" file="audio_player/js/metadata_worker.js" flattenhtml="true" type="BINDATA" />
       <include name="IDR_AUDIO_PLAYER" file="audio_player/audio_player.html" allowexternalscript="true" flattenhtml="true" type="BINDATA" />
       <include name="IDR_AUDIO_PLAYER_JS" file="audio_player/js/audio_player_scripts.js" flattenhtml="true" type="BINDATA" />
       <include name="IDR_AUDIO_PLAYER_ICON_FAVICON_16" file="audio_player/icons/audio-player-favicon-16.png" type="BINDATA" />
diff --git a/ui/file_manager/gallery/css/gallery.css b/ui/file_manager/gallery/css/gallery.css
index 993dbeb..fe04bbb2 100644
--- a/ui/file_manager/gallery/css/gallery.css
+++ b/ui/file_manager/gallery/css/gallery.css
@@ -61,43 +61,43 @@
 }
 
 .gallery[tools] .image-container[cursor='move'] {
-  cursor: -webkit-image-set(
-      url(../images/100/cursor_move.png) 1x,
-      url(../images/200/cursor_move.png) 2x) 15 15, auto;
+  /** TODO(hirono): Take book 2x assets for the mouse cursor.
+      crbug.com/460430 */
+  cursor: url(../images/100/cursor_move.png) 15 15, auto;
 }
 
 .gallery[tools] .image-container[cursor='crop'] {
-  cursor: -webkit-image-set(
-      url(../images/100/cursor_crop.png) 1x,
-      url(../images/200/cursor_crop.png) 2x) 15 15, auto;
+  /** TODO(hirono): Take book 2x assets for the mouse cursor.
+      crbug.com/460430 */
+  cursor: url(../images/100/cursor_crop.png) 15 15, auto;
 }
 
 .gallery[tools] .image-container[cursor='n-resize'],
 .gallery[tools] .image-container[cursor='s-resize'] {
-  cursor: -webkit-image-set(
-      url(../images/100/cursor_updown.png) 1x,
-      url(../images/200/cursor_updown.png) 2x) 15 15, auto;
+  /** TODO(hirono): Take book 2x assets for the mouse cursor.
+      crbug.com/460430 */
+  cursor: url(../images/100/cursor_updown.png) 15 15, auto;
 }
 
 .gallery[tools] .image-container[cursor='e-resize'],
 .gallery[tools] .image-container[cursor='w-resize'] {
-  cursor: -webkit-image-set(
-      url(../images/100/cursor_leftright.png) 1x,
-      url(../images/200/cursor_leftright.png) 2x) 15 15, auto;
+  /** TODO(hirono): Take book 2x assets for the mouse cursor.
+      crbug.com/460430 */
+  cursor: url(../images/100/cursor_leftright.png) 15 15, auto;
 }
 
 .gallery[tools] .image-container[cursor='nw-resize'],
 .gallery[tools] .image-container[cursor='se-resize'] {
-  cursor: -webkit-image-set(
-      url(../images/100/cursor_nwse.png) 1x,
-      url(../images/200/cursor_nwse.png) 2x) 15 15, auto;
+  /** TODO(hirono): Take book 2x assets for the mouse cursor.
+      crbug.com/460430 */
+  cursor: url(../images/100/cursor_nwse.png) 15 15, auto;
 }
 
 .gallery[tools] .image-container[cursor='ne-resize'],
 .gallery[tools] .image-container[cursor='sw-resize'] {
-  cursor: -webkit-image-set(
-      url(../images/100/cursor_swne.png) 1x,
-      url(../images/200/cursor_swne.png) 2x) 15 15, auto;
+  /** TODO(hirono): Take book 2x assets for the mouse cursor.
+      crbug.com/460430 */
+  cursor: url(../images/100/cursor_swne.png) 15 15, auto;
 }
 
 .gallery .image-container > .image {
diff --git a/ui/file_manager/gallery/gallery.html b/ui/file_manager/gallery/gallery.html
index 062f23d..a63759cf 100644
--- a/ui/file_manager/gallery/gallery.html
+++ b/ui/file_manager/gallery/gallery.html
@@ -21,8 +21,9 @@
     <!-- This section is used when the file manager is loaded with
          'filemgr-ext-path' command-line flag. -->
     <!-- Keep the list in sync with gallery_scripts.js. -->
-    <script src="../file_manager/foreground/js/metrics_base.js"></script>
-    <script src="../file_manager/foreground/js/metrics.js"></script>
+    <script src="../file_manager/common/js/metrics_base.js"></script>
+    <script src="../file_manager/common/js/metrics.js"></script>
+    <script src="../file_manager/foreground/js/metrics_start.js"></script>
 
     <!-- Loads the client of the image loader extension -->
     <script src="../file_manager/common/js/lru_cache.js"></script>
diff --git a/ui/file_manager/gallery/js/background.js b/ui/file_manager/gallery/js/background.js
index ae5ed80..3203297 100644
--- a/ui/file_manager/gallery/js/background.js
+++ b/ui/file_manager/gallery/js/background.js
@@ -3,172 +3,132 @@
 // found in the LICENSE file.
 
 /**
- * @param {!Object.<string, string>} stringData String data.
- * @param {!VolumeManager} volumeManager Volume manager.
- * @constructor
- * @struct
+ * Configuration of the Gallery window.
+ * @const
+ * @type {Object}
  */
-function BackgroundComponents(stringData, volumeManager) {
-  /**
-   * String data.
-   * @type {!Object.<string, string>}
-   */
-  this.stringData = stringData;
-
-  /**
-   * Volume manager.
-   * @type {!VolumeManager}
-   */
-  this.volumeManager = volumeManager;
-}
-
-/**
- * Loads background component.
- * @return {!Promise} Promise fulfilled with BackgroundComponents.
- */
-BackgroundComponents.load = function() {
-  var stringDataPromise = new Promise(function(fulfill) {
-    chrome.fileManagerPrivate.getStrings(function(stringData) {
-      loadTimeData.data = stringData;
-      fulfill(stringData);
-    });
-  });
-
-  // VolumeManager should be obtained after stringData initialized.
-  var volumeManagerPromise = stringDataPromise.then(function() {
-    return new Promise(function(fulfill) {
-      VolumeManager.getInstance(fulfill);
-    });
-  });
-
-  return Promise.all([stringDataPromise, volumeManagerPromise]).then(
-      function(args) {
-        return new BackgroundComponents(args[0], args[1]);
-      });
+var windowCreateOptions = {
+  id: 'gallery',
+  innerBounds: {
+    minWidth: 820,
+    minHeight: 554
+  },
+  frame: 'none'
 };
 
 /**
- * Resolves file system names and obtains entries.
- * @param {!Array.<!FileEntry>} entries Names of isolated file system.
- * @return {!Promise} Promise to be fulfilled with an entry array.
+ * Backgound object. This is necessary for AppWindowWrapper.
+ * @type {!BackgroundBase}
  */
-function resolveEntries(entries) {
-  return new Promise(function(fulfill, reject) {
-    chrome.fileManagerPrivate.resolveIsolatedEntries(
-        entries, function(externalEntries) {
-          if (!chrome.runtime.lastError)
-            fulfill(externalEntries);
-          else
-            reject(chrome.runtime.lastError);
-        });
+var background = new BackgroundBase();
+
+
+// Initializes the strings. This needs for the volume manager.
+var loadTimeDataPromise = new Promise(function(fulfill, reject) {
+  chrome.fileManagerPrivate.getStrings(function(stringData) {
+    loadTimeData.data = stringData;
+    fulfill(true);
   });
-}
+});
+
+// Initializes the volume manager. This needs for isolated entries.
+var volumeManagerPromise = new Promise(function(fulfill, reject) {
+  VolumeManager.getInstance(fulfill);
+});
 
 /**
- * Promise to be fulfilled with singleton instance of background components.
- * @type {Promise}
+ * Queue to serialize initialization.
+ * @type {!Promise}
  */
-var backgroundComponentsPromise = null;
+window.initializePromise = Promise.all([loadTimeDataPromise,
+                                     volumeManagerPromise]);
+
+// Registers the handlers.
+chrome.app.runtime.onLaunched.addListener(onLaunched);
 
 /**
- * Promise to be fulfilled with single application window.
- * This can be null when the window is not opened.
- * @type {Promise}
- */
-var appWindowPromise = null;
-
-/**
- * Promise to be fulfilled with entries that are used for reopening the
- * application window.
- * @type {Promise}
- */
-var reopenEntriesPromise = null;
-
-/**
- * Launches the application with entries.
+ * Called when an app is launched.
  *
- * @param {!Promise} selectedEntriesPromise Promise to be fulfilled with the
- *     entries that are stored in the external file system (not in the isolated
- *     file system).
- * @return {!Promise} Promise to be fulfilled after the application is launched.
+ * @param {!Object} launchData Launch data. See the manual of chrome.app.runtime
+ *     .onLaunched for detail.
  */
-function launch(selectedEntriesPromise) {
-  // If there is the previous window, close the window.
-  if (appWindowPromise) {
-    reopenEntriesPromise = selectedEntriesPromise;
-    appWindowPromise.then(function(appWindow) {
-      appWindow.close();
+function onLaunched(launchData) {
+  // Skip if files are not selected.
+  if (!launchData || !launchData.items || launchData.items.length == 0)
+    return;
+
+  window.initializePromise.then(function() {
+    var isolatedEntries = launchData.items.map(function(item) {
+      return item.entry;
     });
-    return Promise.reject('The window has already opened.');
-  }
-  reopenEntriesPromise = null;
-
-  // Create a new window.
-  appWindowPromise = new Promise(function(fulfill) {
-    chrome.app.window.create(
-        'gallery.html',
-        {
-          id: 'gallery',
-          innerBounds: {
-            minWidth: 820,
-            minHeight: 544
-          },
-          frame: 'none'
-        },
-        function(appWindow) {
-          appWindow.contentWindow.addEventListener(
-              'load', fulfill.bind(null, appWindow));
-          appWindow.onClosed.addListener(function() {
-            appWindowPromise = null;
-            if (reopenEntriesPromise) {
-              // TODO(hirono): This is workaround for crbug.com/442217. Remove
-              // this after fixing it.
-              setTimeout(function() {
-                if (reopenEntriesPromise)
-                  launch(reopenEntriesPromise);
-              }, 500);
-            }
-          });
-        });
-  });
-
-  // Initialize the window document.
-  return Promise.all([
-    appWindowPromise,
-    backgroundComponentsPromise
-  ]).then(function(args) {
-    var appWindow = /** @type {!chrome.app.window.AppWindow} */ (args[0]);
-    var galleryWindow = /** @type {!GalleryWindow} */ (appWindow.contentWindow);
-    galleryWindow.initialize(args[1]);
-    return selectedEntriesPromise.then(function(entries) {
-      galleryWindow.loadEntries(entries);
-    });
-  });
-}
-
-// If the script is loaded from unit test, chrome.app.runtime is not defined.
-// In this case, does not run the initialization code for the application.
-if (chrome.app.runtime) {
-  backgroundComponentsPromise = BackgroundComponents.load();
-  chrome.app.runtime.onLaunched.addListener(function(launchData) {
-    // Skip if files are not selected.
-    if (!launchData || !launchData.items || launchData.items.length === 0)
-      return;
 
     // Obtains entries in non-isolated file systems.
     // The entries in launchData are stored in the isolated file system.
     // We need to map the isolated entries to the normal entries to retrieve
     // their parent directory.
-    var isolatedEntries = launchData.items.map(function(item) {
-      return item.entry;
-    });
-    var selectedEntriesPromise = backgroundComponentsPromise.then(function() {
-      return resolveEntries(isolatedEntries);
-    });
+    chrome.fileManagerPrivate.resolveIsolatedEntries(
+        isolatedEntries,
+        function(externalEntries) {
+          var urls = util.entriesToURLs(externalEntries);
+          openGalleryWindow(urls, false);
+        });
+  });
+}
 
-    launch(selectedEntriesPromise).catch(function(error) {
-      console.error(error.stack || error);
+/**
+ * Returns a function to generate an ID for window.
+ * @type {function():string} Function which returns an unique id.
+ */
+var generateWindowId = (function() {
+  var seq = 0;
+  return function() {
+    return 'GALLERY_' + seq++;
+  };
+})();
+
+/**
+ * Opens gallery window.
+ * @param {!Array.<string>} urls List of URL to show.
+ * @param {boolean} reopen True if reopen, false otherwise.
+ * @return {!Promise} Promise to be fulfilled on success, or rejected on error.
+ */
+function openGalleryWindow(urls, reopen) {
+  return new Promise(function(fulfill, reject) {
+    util.URLsToEntries(urls).then(function(result) {
+      fulfill(util.entriesToURLs(result.entries));
+    }).catch(reject);
+  }).then(function(urls) {
+    if (urls.length === 0)
+      return Promise.reject('No file to open.');
+
+    var windowId = generateWindowId();
+
+    // Opens a window.
+    return new Promise(function(fulfill, reject) {
+      var gallery = new AppWindowWrapper('gallery.html',
+          windowId,
+          windowCreateOptions);
+
+      gallery.launch(
+          {urls: urls},
+          reopen,
+          fulfill.bind(null, gallery));
+    }).then(function(gallery) {
+      var galleryDocument = gallery.rawAppWindow.contentWindow.document;
+      if (galleryDocument.readyState == 'complete')
+        return gallery;
+
+      return new Promise(function(fulfill, reject) {
+        galleryDocument.addEventListener(
+            'DOMContentLoaded', fulfill.bind(null, gallery));
+      });
     });
+  }).then(function(gallery) {
+    gallery.rawAppWindow.focus();
+    return gallery.rawAppWindow;
+  }).catch(function(error) {
+    console.error('Launch failed' + error.stack || error);
+    return Promise.reject(error);
   });
 }
 
diff --git a/ui/file_manager/gallery/js/compiled_resources.gyp b/ui/file_manager/gallery/js/compiled_resources.gyp
index 22542de..59cce400 100644
--- a/ui/file_manager/gallery/js/compiled_resources.gyp
+++ b/ui/file_manager/gallery/js/compiled_resources.gyp
@@ -20,6 +20,8 @@
           '../../file_manager/common/js/volume_manager_common.js',
           '../../file_manager/common/js/error_util.js',
           '../../file_manager/common/js/file_type.js',
+          '../../file_manager/background/js/app_window_wrapper.js',
+          '../../file_manager/background/js/background_base.js',
           '../../file_manager/background/js/volume_manager.js',
         ],
         'externs': [
@@ -55,13 +57,15 @@
           '../../../webui/resources/js/cr/ui/list_selection_controller.js',
           '../../../webui/resources/js/cr/ui/list.js',
           '../../../webui/resources/js/cr/ui/grid.js',
+          '../../../webui/resources/js/i18n_template_no_process.js',
           '../../file_manager/common/js/volume_manager_common.js',
           '../../file_manager/common/js/lru_cache.js',
           '../../file_manager/common/js/async_util.js',
           '../../file_manager/common/js/file_type.js',
           '../../file_manager/common/js/util.js',
-          '../../file_manager/foreground/js/metrics_base.js',
-          '../../file_manager/foreground/js/metrics.js',
+          '../../file_manager/common/js/metrics_base.js',
+          '../../file_manager/common/js/metrics.js',
+          '../../file_manager/foreground/js/metrics_start.js',
           '../../file_manager/foreground/js/metadata/metadata_cache.js',
           '../../file_manager/foreground/js/metadata/exif_constants.js',
           '../../file_manager/foreground/js/mouse_inactivity_watcher.js',
@@ -97,6 +101,7 @@
           '<(CLOSURE_DIR)/externs/chrome_extensions.js',
           '<(CLOSURE_DIR)/externs/file_manager_private.js',
           '<(CLOSURE_DIR)/externs/metrics_private.js',
+          '../../../../third_party/analytics/externs.js',
           '../../externs/chrome_test.js',
           '../../externs/exif_entry.js',
           '../../externs/gallery_foreground.js',
diff --git a/ui/file_manager/gallery/js/gallery.js b/ui/file_manager/gallery/js/gallery.js
index c5b74d1..07891d4 100644
--- a/ui/file_manager/gallery/js/gallery.js
+++ b/ui/file_manager/gallery/js/gallery.js
@@ -283,7 +283,7 @@
 /**
  * Loads the content.
  *
- * @param {!Array.<Entry>} selectedEntries Array of selected entries.
+ * @param {!Array.<!Entry>} selectedEntries Array of selected entries.
  */
 Gallery.prototype.load = function(selectedEntries) {
   GalleryUtil.createEntrySet(selectedEntries).then(function(allEntries) {
@@ -294,8 +294,8 @@
 /**
  * Loads the content.
  *
- * @param {!Array.<Entry>} entries Array of entries.
- * @param {!Array.<Entry>} selectedEntries Array of selected entries.
+ * @param {!Array.<!Entry>} entries Array of entries.
+ * @param {!Array.<!Entry>} selectedEntries Array of selected entries.
  * @private
  */
 Gallery.prototype.loadInternal_ = function(entries, selectedEntries) {
@@ -863,7 +863,7 @@
   var item = this.getSingleSelectedItem();
   if (!item)
     return;
-  this.shareDialog_.show(item.getEntry(), function() {});
+  this.shareDialog_.showEntry(item.getEntry(), function() {});
 };
 
 /**
@@ -908,25 +908,56 @@
 var gallery = null;
 
 /**
- * Initialize the window.
- * @param {!BackgroundComponents} backgroundComponents Background components.
+ * Promise to initialize the load time data.
+ * @type {!Promise}
  */
-window.initialize = function(backgroundComponents) {
-  window.loadTimeData.data = backgroundComponents.stringData;
-  gallery = new Gallery(backgroundComponents.volumeManager);
-};
+var loadTimeDataPromise = new Promise(function(fulfill, reject) {
+  chrome.fileManagerPrivate.getStrings(function(strings) {
+    window.loadTimeData.data = strings;
+    i18nTemplate.process(document, loadTimeData);
+    fulfill(true);
+  });
+});
 
 /**
- * Loads entries.
- * @param {!Array.<Entry>} selectedEntries Array of selected entries.
+ * Promise to initialize volume manager.
+ * @type {!Promise}
  */
-window.loadEntries = function(selectedEntries) {
-  gallery.load(selectedEntries);
-};
+var volumeManagerPromise = new Promise(function(fulfill, reject) {
+  var volumeManager = new VolumeManagerWrapper(
+      VolumeManagerWrapper.DriveEnabledStatus.DRIVE_ENABLED);
+  volumeManager.ensureInitialized(fulfill.bind(null, volumeManager));
+});
+
+/**
+ * Promise to initialize both the volume manager and the load time data.
+ * @type {!Promise}
+ */
+var initializePromise =
+    Promise.all([loadTimeDataPromise, volumeManagerPromise]).
+    then(function(args) {
+      var volumeManager = args[1];
+      var gallery = new Gallery(volumeManager);
+      return gallery;
+    });
+
+// Loads entries.
+initializePromise.then(
+    /**
+     * Loads entries.
+     * @param {!Gallery} gallery The gallery instance.
+     */
+    function(gallery) {
+      util.URLsToEntries(window.appState.urls, function(entries) {
+        gallery.load(entries);
+      });
+    });
 
 /**
  * Enteres the debug mode.
  */
 window.debugMe = function() {
-  gallery.debugMe();
+  initializePromise.then(function(gallery) {
+    gallery.debugMe();
+  });
 };
diff --git a/ui/file_manager/gallery/js/gallery_scripts.js b/ui/file_manager/gallery/js/gallery_scripts.js
index 5e19ca9d..2c0c885 100644
--- a/ui/file_manager/gallery/js/gallery_scripts.js
+++ b/ui/file_manager/gallery/js/gallery_scripts.js
@@ -8,8 +8,9 @@
 // included file but that's all right since any javascript file should start
 // with a copyright comment anyway.
 
-//<include src="../../file_manager/foreground/js/metrics_base.js">
-//<include src="../../file_manager/foreground/js/metrics.js">
+//<include src="../../file_manager/common/js/metrics_base.js">
+//<include src="../../file_manager/common/js/metrics.js">
+//<include src="../../file_manager/foreground/js/metrics_start.js">
 
 //<include src="../../file_manager/common/js/lru_cache.js">
 //<include src="../../image_loader/image_loader_client.js">
@@ -18,6 +19,7 @@
 //<include src="../../../webui/resources/js/util.js">
 //<include src="../../../webui/resources/js/event_tracker.js">
 //<include src="../../../webui/resources/js/load_time_data.js">
+//<include src="../../../webui/resources/js/i18n_template_no_process.js">
 
 //<include src="../../../webui/resources/js/cr/ui.js">
 //<include src="../../../webui/resources/js/cr/event_target.js">
diff --git a/ui/file_manager/gallery/js/image_editor/image_editor.js b/ui/file_manager/gallery/js/image_editor/image_editor.js
index 07260079..4f323375 100644
--- a/ui/file_manager/gallery/js/image_editor/image_editor.js
+++ b/ui/file_manager/gallery/js/image_editor/image_editor.js
@@ -12,7 +12,7 @@
  * @param {!Object} DOMContainers Various DOM containers required for the
  *     editor.
  * @param {!Array.<!ImageEditor.Mode>} modes Available editor modes.
- * @param {function(string, ...[string])} displayStringFunction String
+ * @param {function(string, ...string)} displayStringFunction String
  *     formatting function.
  * @param {function()} onToolsVisibilityChanged Callback to be called, when
  *     some of the UI elements have been dimmed or revealed.
@@ -1200,7 +1200,7 @@
 /** A prompt panel for the editor.
  *
  * @param {!HTMLElement} container Container element.
- * @param {function(string, ...[string])} displayStringFunction A formatting
+ * @param {function(string, ...string)} displayStringFunction A formatting
  *     function.
  * @constructor
  * @struct
diff --git a/ui/file_manager/gallery/manifest.json b/ui/file_manager/gallery/manifest.json
index 2e08ee59..71bc1d74 100644
--- a/ui/file_manager/gallery/manifest.json
+++ b/ui/file_manager/gallery/manifest.json
@@ -52,10 +52,12 @@
         // util.js and async_util.js should be included before volume_manager.js.
         "chrome-extension://hhaomjibdihmijegdhdafkllkbggdgoj/common/js/util.js",
         "chrome-extension://hhaomjibdihmijegdhdafkllkbggdgoj/common/js/async_util.js",
-        "chrome-extension://hhaomjibdihmijegdhdafkllkbggdgoj/common/js/volume_manager_common.js",
-        "chrome-extension://hhaomjibdihmijegdhdafkllkbggdgoj/background/js/volume_manager.js",
-        "chrome-extension://hhaomjibdihmijegdhdafkllkbggdgoj/common/js/error_util.js",
         "chrome-extension://hhaomjibdihmijegdhdafkllkbggdgoj/common/js/file_type.js",
+        "chrome-extension://hhaomjibdihmijegdhdafkllkbggdgoj/common/js/volume_manager_common.js",
+        "chrome-extension://hhaomjibdihmijegdhdafkllkbggdgoj/background/js/app_window_wrapper.js",
+        "chrome-extension://hhaomjibdihmijegdhdafkllkbggdgoj/background/js/background_base.js",
+        "chrome-extension://hhaomjibdihmijegdhdafkllkbggdgoj/background/js/test_util_base.js",
+        "chrome-extension://hhaomjibdihmijegdhdafkllkbggdgoj/background/js/volume_manager.js",
         "js/background.js"
       ]
     },
diff --git a/ui/file_manager/image_loader/request.js b/ui/file_manager/image_loader/request.js
index 5e2e111..49fdd37 100644
--- a/ui/file_manager/image_loader/request.js
+++ b/ui/file_manager/image_loader/request.js
@@ -228,6 +228,9 @@
 
   // Fetch the image via authorized XHR and parse it.
   var parseImage = function(contentType, blob) {
+    if (contentType)
+      this.contentType_ = contentType;
+
     this.image_.src = URL.createObjectURL(blob);
   }.bind(this);
 
@@ -245,6 +248,19 @@
 }
 
 /**
+ * A map which is used to estimate content type from extension.
+ * @enum {string}
+ */
+AuthorizedXHR.ExtensionContentTypeMap = {
+  gif: 'image/gif',
+  png: 'image/png',
+  svg: 'image/svg',
+  bmp: 'image/bmp',
+  jpg: 'image/jpeg',
+  jpeg: 'image/jpeg'
+};
+
+/**
  * Aborts the current request (if running).
  */
 AuthorizedXHR.prototype.abort = function() {
@@ -268,6 +284,12 @@
   // Do not call any callbacks when aborting.
   var onMaybeSuccess = /** @type {function(string, Blob)} */ (
       function(contentType, response) {
+        // When content type is not available, try to estimate it from url.
+        if (!contentType) {
+          contentType = AuthorizedXHR.ExtensionContentTypeMap[
+              this.extractExtension_(url)];
+        }
+
         if (!this.aborted_)
           onSuccess(contentType, response);
       }.bind(this));
@@ -318,6 +340,16 @@
 };
 
 /**
+ * Extracts extension from url.
+ * @param {string} url Url.
+ * @return {string} Extracted extensiion, e.g. png.
+ */
+AuthorizedXHR.prototype.extractExtension_ = function(url) {
+  var result = (/\.([a-zA-Z]+)$/i).exec(url);
+  return result ? result[1] : '';
+};
+
+/**
  * Fetches data using authorized XmlHttpRequest with the provided OAuth2 token.
  * If the token is invalid, the request will fail.
  *
diff --git a/ui/file_manager/integration_tests/file_manager/file_dialog.js b/ui/file_manager/integration_tests/file_manager/file_dialog.js
index 0718180..8645a93 100644
--- a/ui/file_manager/integration_tests/file_manager/file_dialog.js
+++ b/ui/file_manager/integration_tests/file_manager/file_dialog.js
@@ -76,12 +76,12 @@
         expectedSet,
         function(windowId) {
           return remoteCall.waitForElement(windowId,
-                                           '.button-panel button.cancel').
+                                           '.button-panel paper-button.cancel').
               then(function() {
                 return remoteCall.callRemoteTestUtil(
                     'fakeEvent',
                     windowId,
-                    ['.button-panel button.cancel', 'click']);
+                    ['.button-panel paper-button.cancel', 'click']);
               });
         });
   }).then(function(result) {
@@ -109,12 +109,13 @@
         volumeName,
         expectedSet,
         function(windowId) {
-          return remoteCall.waitForElement(windowId, '.button-panel button.ok').
+          return remoteCall.waitForElement(windowId,
+                                           '.button-panel paper-button.ok').
               then(function() {
                 return remoteCall.callRemoteTestUtil(
                     'fakeEvent',
                     windowId,
-                    ['.button-panel button.ok', 'click']);
+                    ['.button-panel paper-button.ok', 'click']);
               });
         });
   }).then(function(result) {
diff --git a/ui/file_manager/integration_tests/gallery/test_util.js b/ui/file_manager/integration_tests/gallery/test_util.js
index 6e41b90..685df61 100644
--- a/ui/file_manager/integration_tests/gallery/test_util.js
+++ b/ui/file_manager/integration_tests/gallery/test_util.js
@@ -119,10 +119,13 @@
         selectedEntries.map(function(entry) { return entry.nameText; }));
   });
 
-  return launch(entriesPromise).then(function() {
-    var launchedPromise = Promise.all([appWindowPromise, entriesPromise]);
-    return launchedPromise.then(function(results) {
-      return {appWindow: results[0], entries: results[1]};
+  return entriesPromise.then(function(entries) {
+    return window.initializePromise.then(function() {
+      var urls = util.entriesToURLs(entries);
+      var launchedPromise = openGalleryWindow(urls, false);
+      return launchedPromise.then(function(appWindow) {
+        return {appWindow: appWindow, entries: entries};
+      });
     });
   });
 }
diff --git a/ui/file_manager/video_player/js/cast/media_manager.js b/ui/file_manager/video_player/js/cast/media_manager.js
index daba91f..224df99 100644
--- a/ui/file_manager/video_player/js/cast/media_manager.js
+++ b/ui/file_manager/video_player/js/cast/media_manager.js
@@ -110,7 +110,7 @@
 
   return new Promise(function(fulfill, reject) {
     chrome.fileManagerPrivate.getEntryProperties(
-        [this.entry_.toURL()], fulfill);
+        [this.entry_.toURL()], ['contentMimeType', 'thumbnailUrl'], fulfill);
   }.bind(this)).then(function(props) {
     if (!props || !props[0]) {
       return Promise.reject('Mime fetch failed.');
@@ -136,7 +136,9 @@
 
   return new Promise(function(fulfill, reject) {
     chrome.fileManagerPrivate.getEntryProperties(
-        [this.entry_.toURL()], fulfill);
+        [this.entry_.toURL()],
+        ['contentMimeType', 'thumbnailUrl'],
+        fulfill);
   }.bind(this)).then(function(props) {
     if (!props || !props[0]) {
       return Promise.reject('Thumbnail fetch failed.');
diff --git a/ui/file_manager/video_player/js/compiled_resources.gyp b/ui/file_manager/video_player/js/compiled_resources.gyp
index 5bc15cf..867e3c5e 100644
--- a/ui/file_manager/video_player/js/compiled_resources.gyp
+++ b/ui/file_manager/video_player/js/compiled_resources.gyp
@@ -45,7 +45,7 @@
         'depends': [
           '../../../../third_party/jstemplate/compiled_resources.gyp:jstemplate',
           'error_util.js',
-          '../../file_manager/foreground/js/metrics_base.js',
+          '../../file_manager/common/js/metrics_base.js',
           'video_player_metrics.js',
           '../../../webui/resources/js/cr.js',
           '../../../webui/resources/js/load_time_data.js',
diff --git a/ui/file_manager/video_player/js/video_player_scripts.js b/ui/file_manager/video_player/js/video_player_scripts.js
index 448f420..e32510c 100644
--- a/ui/file_manager/video_player/js/video_player_scripts.js
+++ b/ui/file_manager/video_player/js/video_player_scripts.js
@@ -10,7 +10,7 @@
 
 //<include src="error_util.js">
 
-//<include src="../../file_manager/foreground/js/metrics_base.js">
+//<include src="../../file_manager/common/js/metrics_base.js">
 //<include src="video_player_metrics.js">
 
 //<include src="../../../webui/resources/js/cr.js">
diff --git a/ui/gfx/BUILD.gn b/ui/gfx/BUILD.gn
index dbe133f..5485a05 100644
--- a/ui/gfx/BUILD.gn
+++ b/ui/gfx/BUILD.gn
@@ -162,6 +162,8 @@
     "platform_font_win.h",
     "range/range.cc",
     "range/range.h",
+    "range/range_f.cc",
+    "range/range_f.h",
     "range/range_mac.mm",
     "range/range_win.cc",
     "scoped_canvas.h",
@@ -222,6 +224,9 @@
     "win/window_impl.h",
   ]
 
+  # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+  configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
+
   defines = [ "GFX_IMPLEMENTATION" ]
 
   deps = [
@@ -305,7 +310,6 @@
   # Windows.
   if (is_win) {
     cflags = [
-      "/wd4267",  # TODO(jschuh): C4267: http://crbug.com/167187 size_t -> int.
       "/wd4324",  # Structure was padded due to __declspec(align()), which is
                   # uninteresting.
     ]
@@ -450,6 +454,9 @@
     ]
   }
 
+  # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+  configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
+
   deps = [
     ":gfx",
     ":test_support",
@@ -509,9 +516,6 @@
       "imm32.lib",
       "oleacc.lib",
     ]
-
-    # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
-    cflags = [ "/wd4267" ]
   }
 }
 
diff --git a/ui/gfx/canvas_skia.cc b/ui/gfx/canvas_skia.cc
index 73f5992..3de9b93 100644
--- a/ui/gfx/canvas_skia.cc
+++ b/ui/gfx/canvas_skia.cc
@@ -142,8 +142,8 @@
   else
     render_text->SetHorizontalAlignment(ALIGN_LEFT);
 
-  if (flags & Canvas::NO_SUBPIXEL_RENDERING)
-    render_text->set_background_is_transparent(true);
+  render_text->set_subpixel_rendering_suppressed(
+      (flags & Canvas::NO_SUBPIXEL_RENDERING) != 0);
 
   render_text->SetColor(color);
   const int font_style = font_list.GetFontStyle();
diff --git a/ui/gfx/gfx.gyp b/ui/gfx/gfx.gyp
index 02e14e7..d8da3fed 100644
--- a/ui/gfx/gfx.gyp
+++ b/ui/gfx/gfx.gyp
@@ -240,6 +240,8 @@
         'platform_font_win.h',
         'range/range.cc',
         'range/range.h',
+        'range/range_f.cc',
+        'range/range_f.h',
         'range/range_mac.mm',
         'range/range_win.cc',
         'render_text.cc',
diff --git a/ui/gfx/harfbuzz_font_skia.cc b/ui/gfx/harfbuzz_font_skia.cc
index 2eaa0b0f..d837533 100644
--- a/ui/gfx/harfbuzz_font_skia.cc
+++ b/ui/gfx/harfbuzz_font_skia.cc
@@ -258,7 +258,7 @@
 hb_font_t* CreateHarfBuzzFont(SkTypeface* skia_face,
                               SkScalar text_size,
                               const FontRenderParams& params,
-                              bool background_is_transparent) {
+                              bool subpixel_rendering_suppressed) {
   // TODO(ckocagil): This shouldn't grow indefinitely. Maybe use base::MRUCache?
   static std::map<SkFontID, FaceCache> face_caches;
 
@@ -273,7 +273,7 @@
   hb_font_data->paint_.setTypeface(skia_face);
   hb_font_data->paint_.setTextSize(text_size);
   // TODO(ckocagil): Do we need to update these params later?
-  internal::ApplyRenderParams(params, background_is_transparent,
+  internal::ApplyRenderParams(params, subpixel_rendering_suppressed,
                               &hb_font_data->paint_);
   hb_font_set_funcs(harfbuzz_font, g_font_funcs.Get().get(), hb_font_data,
                     DeleteByType<FontData>);
diff --git a/ui/gfx/harfbuzz_font_skia.h b/ui/gfx/harfbuzz_font_skia.h
index 8e2289c..df8a3312 100644
--- a/ui/gfx/harfbuzz_font_skia.h
+++ b/ui/gfx/harfbuzz_font_skia.h
@@ -16,7 +16,7 @@
 hb_font_t* CreateHarfBuzzFont(SkTypeface* skia_face,
                               SkScalar text_size,
                               const FontRenderParams& params,
-                              bool background_is_transparent);
+                              bool subpixel_rendering_suppressed);
 
 }  // namespace gfx
 
diff --git a/ui/gfx/range/range.h b/ui/gfx/range/range.h
index d0d2a3e..6007d754 100644
--- a/ui/gfx/range/range.h
+++ b/ui/gfx/range/range.h
@@ -54,7 +54,7 @@
   // Returns a range that is invalid, which is {size_t_max,size_t_max}.
   static const Range InvalidRange();
 
-  // Checks if the range is valid through comparision to InvalidRange().
+  // Checks if the range is valid through comparison to InvalidRange().
   bool IsValid() const;
 
   // Getters and setters.
diff --git a/ui/gfx/range/range_f.cc b/ui/gfx/range/range_f.cc
new file mode 100644
index 0000000..8af832d
--- /dev/null
+++ b/ui/gfx/range/range_f.cc
@@ -0,0 +1,88 @@
+// 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/gfx/range/range_f.h"
+
+#include <algorithm>
+#include <limits>
+
+#include "base/float_util.h"
+#include "base/format_macros.h"
+#include "base/strings/stringprintf.h"
+
+namespace gfx {
+
+RangeF::RangeF()
+    : start_(0.0f),
+      end_(0.0f) {
+}
+
+RangeF::RangeF(float start, float end)
+    : start_(start),
+      end_(end) {
+}
+
+RangeF::RangeF(float position)
+    : start_(position),
+      end_(position) {
+}
+
+// static
+const RangeF RangeF::InvalidRange() {
+  return RangeF(std::numeric_limits<float>::max());
+}
+
+bool RangeF::IsValid() const {
+  return *this != InvalidRange();
+}
+
+float RangeF::GetMin() const {
+  return std::min(start(), end());
+}
+
+float RangeF::GetMax() const {
+  return std::max(start(), end());
+}
+
+bool RangeF::operator==(const RangeF& other) const {
+  return start() == other.start() && end() == other.end();
+}
+
+bool RangeF::operator!=(const RangeF& other) const {
+  return !(*this == other);
+}
+
+bool RangeF::EqualsIgnoringDirection(const RangeF& other) const {
+  return GetMin() == other.GetMin() && GetMax() == other.GetMax();
+}
+
+bool RangeF::Intersects(const RangeF& range) const {
+  return IsValid() && range.IsValid() &&
+      !(range.GetMax() < GetMin() || range.GetMin() >= GetMax());
+}
+
+bool RangeF::Contains(const RangeF& range) const {
+  return IsValid() && range.IsValid() &&
+      GetMin() <= range.GetMin() && range.GetMax() <= GetMax();
+}
+
+RangeF RangeF::Intersect(const RangeF& range) const {
+  float min = std::max(GetMin(), range.GetMin());
+  float max = std::min(GetMax(), range.GetMax());
+
+  if (min >= max)  // No intersection.
+    return InvalidRange();
+
+  return RangeF(min, max);
+}
+
+std::string RangeF::ToString() const {
+  return base::StringPrintf("{%f,%f}", start(), end());
+}
+
+std::ostream& operator<<(std::ostream& os, const RangeF& range) {
+  return os << range.ToString();
+}
+
+}  // namespace gfx
diff --git a/ui/gfx/range/range_f.h b/ui/gfx/range/range_f.h
new file mode 100644
index 0000000..49bc760
--- /dev/null
+++ b/ui/gfx/range/range_f.h
@@ -0,0 +1,82 @@
+// 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_GFX_RANGE_RANGE_F_H_
+#define UI_GFX_RANGE_RANGE_F_H_
+
+#include <ostream>
+#include <string>
+
+#include "base/basictypes.h"
+#include "ui/gfx/gfx_export.h"
+
+namespace gfx {
+
+// A float version of Range. RangeF is made of a start and end position; when
+// they are the same, the range is empty. Note that |start_| can be greater
+// than |end_| to respect the directionality of the range.
+class GFX_EXPORT RangeF {
+ public:
+  // Creates an empty range {0,0}.
+  RangeF();
+
+  // Initializes the range with a start and end.
+  RangeF(float start, float end);
+
+  // Initializes the range with the same start and end positions.
+  explicit RangeF(float position);
+
+  // Returns a range that is invalid, which is {float_max,float_max}.
+  static const RangeF InvalidRange();
+
+  // Checks if the range is valid through comparison to InvalidRange().
+  bool IsValid() const;
+
+  // Getters and setters.
+  float start() const { return start_; }
+  void set_start(float start) { start_ = start; }
+
+  float end() const { return end_; }
+  void set_end(float end) { end_ = end; }
+
+  // Returns the absolute value of the length.
+  float length() const {
+    const float length = end() - start();
+    return length >= 0 ? length : -length;
+  }
+
+  bool is_reversed() const { return start() > end(); }
+  bool is_empty() const { return start() == end(); }
+
+  // Returns the minimum and maximum values.
+  float GetMin() const;
+  float GetMax() const;
+
+  bool operator==(const RangeF& other) const;
+  bool operator!=(const RangeF& other) const;
+  bool EqualsIgnoringDirection(const RangeF& other) const;
+
+  // Returns true if this range intersects the specified |range|.
+  bool Intersects(const RangeF& range) const;
+
+  // Returns true if this range contains the specified |range|.
+  bool Contains(const RangeF& range) const;
+
+  // Computes the intersection of this range with the given |range|.
+  // If they don't intersect, it returns an InvalidRange().
+  // The returned range is always empty or forward (never reversed).
+  RangeF Intersect(const RangeF& range) const;
+
+  std::string ToString() const;
+
+ private:
+  float start_;
+  float end_;
+};
+
+GFX_EXPORT std::ostream& operator<<(std::ostream& os, const RangeF& range);
+
+}  // namespace gfx
+
+#endif  // UI_GFX_RANGE_RANGE_F_H_
diff --git a/ui/gfx/range/range_unittest.cc b/ui/gfx/range/range_unittest.cc
index 0afaa40..54ca81c 100644
--- a/ui/gfx/range/range_unittest.cc
+++ b/ui/gfx/range/range_unittest.cc
@@ -4,9 +4,51 @@
 
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/gfx/range/range.h"
+#include "ui/gfx/range/range_f.h"
 
-TEST(RangeTest, EmptyInit) {
-  gfx::Range r;
+namespace {
+
+template <typename T>
+class RangeTest : public testing::Test {
+};
+
+typedef testing::Types<gfx::Range, gfx::RangeF> RangeTypes;
+TYPED_TEST_CASE(RangeTest, RangeTypes);
+
+template <typename T>
+void TestContainsAndIntersects(const T& r1,
+                               const T& r2,
+                               const T& r3) {
+  EXPECT_TRUE(r1.Intersects(r1));
+  EXPECT_TRUE(r1.Contains(r1));
+  EXPECT_EQ(T(10, 12), r1.Intersect(r1));
+
+  EXPECT_FALSE(r1.Intersects(r2));
+  EXPECT_FALSE(r1.Contains(r2));
+  EXPECT_TRUE(r1.Intersect(r2).is_empty());
+  EXPECT_FALSE(r2.Intersects(r1));
+  EXPECT_FALSE(r2.Contains(r1));
+  EXPECT_TRUE(r2.Intersect(r1).is_empty());
+
+  EXPECT_TRUE(r1.Intersects(r3));
+  EXPECT_TRUE(r3.Intersects(r1));
+  EXPECT_TRUE(r3.Contains(r1));
+  EXPECT_FALSE(r1.Contains(r3));
+  EXPECT_EQ(T(10, 12), r1.Intersect(r3));
+  EXPECT_EQ(T(10, 12), r3.Intersect(r1));
+
+  EXPECT_TRUE(r2.Intersects(r3));
+  EXPECT_TRUE(r3.Intersects(r2));
+  EXPECT_FALSE(r3.Contains(r2));
+  EXPECT_FALSE(r2.Contains(r3));
+  EXPECT_EQ(T(5, 8), r2.Intersect(r3));
+  EXPECT_EQ(T(5, 8), r3.Intersect(r2));
+}
+
+}  // namespace
+
+TYPED_TEST(RangeTest, EmptyInit) {
+  TypeParam r;
   EXPECT_EQ(0U, r.start());
   EXPECT_EQ(0U, r.end());
   EXPECT_EQ(0U, r.length());
@@ -17,8 +59,8 @@
   EXPECT_EQ(0U, r.GetMax());
 }
 
-TEST(RangeTest, StartEndInit) {
-  gfx::Range r(10, 15);
+TYPED_TEST(RangeTest, StartEndInit) {
+  TypeParam r(10, 15);
   EXPECT_EQ(10U, r.start());
   EXPECT_EQ(15U, r.end());
   EXPECT_EQ(5U, r.length());
@@ -29,8 +71,8 @@
   EXPECT_EQ(15U, r.GetMax());
 }
 
-TEST(RangeTest, StartEndReversedInit) {
-  gfx::Range r(10, 5);
+TYPED_TEST(RangeTest, StartEndReversedInit) {
+  TypeParam r(10, 5);
   EXPECT_EQ(10U, r.start());
   EXPECT_EQ(5U, r.end());
   EXPECT_EQ(5U, r.length());
@@ -41,8 +83,8 @@
   EXPECT_EQ(10U, r.GetMax());
 }
 
-TEST(RangeTest, PositionInit) {
-  gfx::Range r(12);
+TYPED_TEST(RangeTest, PositionInit) {
+  TypeParam r(12);
   EXPECT_EQ(12U, r.start());
   EXPECT_EQ(12U, r.end());
   EXPECT_EQ(0U, r.length());
@@ -53,42 +95,45 @@
   EXPECT_EQ(12U, r.GetMax());
 }
 
-TEST(RangeTest, InvalidRange) {
-  gfx::Range r(gfx::Range::InvalidRange());
+TYPED_TEST(RangeTest, InvalidRange) {
+  TypeParam r(TypeParam::InvalidRange());
   EXPECT_EQ(0U, r.length());
   EXPECT_EQ(r.start(), r.end());
+  EXPECT_EQ(r.GetMax(), r.GetMin());
   EXPECT_FALSE(r.is_reversed());
   EXPECT_TRUE(r.is_empty());
   EXPECT_FALSE(r.IsValid());
+  EXPECT_EQ(r, TypeParam::InvalidRange());
+  EXPECT_TRUE(r.EqualsIgnoringDirection(TypeParam::InvalidRange()));
 }
 
-TEST(RangeTest, Equality) {
-  gfx::Range r1(10, 4);
-  gfx::Range r2(10, 4);
-  gfx::Range r3(10, 2);
+TYPED_TEST(RangeTest, Equality) {
+  TypeParam r1(10, 4);
+  TypeParam r2(10, 4);
+  TypeParam r3(10, 2);
   EXPECT_EQ(r1, r2);
   EXPECT_NE(r1, r3);
   EXPECT_NE(r2, r3);
 
-  gfx::Range r4(11, 4);
+  TypeParam r4(11, 4);
   EXPECT_NE(r1, r4);
   EXPECT_NE(r2, r4);
   EXPECT_NE(r3, r4);
 
-  gfx::Range r5(12, 5);
+  TypeParam r5(12, 5);
   EXPECT_NE(r1, r5);
   EXPECT_NE(r2, r5);
   EXPECT_NE(r3, r5);
 }
 
-TEST(RangeTest, EqualsIgnoringDirection) {
-  gfx::Range r1(10, 5);
-  gfx::Range r2(5, 10);
+TYPED_TEST(RangeTest, EqualsIgnoringDirection) {
+  TypeParam r1(10, 5);
+  TypeParam r2(5, 10);
   EXPECT_TRUE(r1.EqualsIgnoringDirection(r2));
 }
 
-TEST(RangeTest, SetStart) {
-  gfx::Range r(10, 20);
+TYPED_TEST(RangeTest, SetStart) {
+  TypeParam r(10, 20);
   EXPECT_EQ(10U, r.start());
   EXPECT_EQ(10U, r.length());
 
@@ -99,8 +144,8 @@
   EXPECT_TRUE(r.is_reversed());
 }
 
-TEST(RangeTest, SetEnd) {
-  gfx::Range r(10, 13);
+TYPED_TEST(RangeTest, SetEnd) {
+  TypeParam r(10, 13);
   EXPECT_EQ(10U, r.start());
   EXPECT_EQ(3U, r.length());
 
@@ -110,8 +155,8 @@
   EXPECT_EQ(10U, r.length());
 }
 
-TEST(RangeTest, SetStartAndEnd) {
-  gfx::Range r;
+TYPED_TEST(RangeTest, SetStartAndEnd) {
+  TypeParam r;
   r.set_end(5);
   r.set_start(1);
   EXPECT_EQ(1U, r.start());
@@ -121,8 +166,8 @@
   EXPECT_EQ(5U, r.GetMax());
 }
 
-TEST(RangeTest, ReversedRange) {
-  gfx::Range r(10, 5);
+TYPED_TEST(RangeTest, ReversedRange) {
+  TypeParam r(10, 5);
   EXPECT_EQ(10U, r.start());
   EXPECT_EQ(5U, r.end());
   EXPECT_EQ(5U, r.length());
@@ -132,8 +177,8 @@
   EXPECT_EQ(10U, r.GetMax());
 }
 
-TEST(RangeTest, SetReversedRange) {
-  gfx::Range r(10, 20);
+TYPED_TEST(RangeTest, SetReversedRange) {
+  TypeParam r(10, 20);
   r.set_start(25);
   EXPECT_EQ(25U, r.start());
   EXPECT_EQ(20U, r.end());
@@ -150,54 +195,25 @@
   EXPECT_EQ(25U, r.GetMax());
 }
 
-void TestContainsAndIntersects(const gfx::Range& r1,
-                               const gfx::Range& r2,
-                               const gfx::Range& r3) {
-  EXPECT_TRUE(r1.Intersects(r1));
-  EXPECT_TRUE(r1.Contains(r1));
-  EXPECT_EQ(gfx::Range(10, 12), r1.Intersect(r1));
-
-  EXPECT_FALSE(r1.Intersects(r2));
-  EXPECT_FALSE(r1.Contains(r2));
-  EXPECT_TRUE(r1.Intersect(r2).is_empty());
-  EXPECT_FALSE(r2.Intersects(r1));
-  EXPECT_FALSE(r2.Contains(r1));
-  EXPECT_TRUE(r2.Intersect(r1).is_empty());
-
-  EXPECT_TRUE(r1.Intersects(r3));
-  EXPECT_TRUE(r3.Intersects(r1));
-  EXPECT_TRUE(r3.Contains(r1));
-  EXPECT_FALSE(r1.Contains(r3));
-  EXPECT_EQ(gfx::Range(10, 12), r1.Intersect(r3));
-  EXPECT_EQ(gfx::Range(10, 12), r3.Intersect(r1));
-
-  EXPECT_TRUE(r2.Intersects(r3));
-  EXPECT_TRUE(r3.Intersects(r2));
-  EXPECT_FALSE(r3.Contains(r2));
-  EXPECT_FALSE(r2.Contains(r3));
-  EXPECT_EQ(gfx::Range(5, 8), r2.Intersect(r3));
-  EXPECT_EQ(gfx::Range(5, 8), r3.Intersect(r2));
-}
-
-TEST(RangeTest, ContainAndIntersect) {
+TYPED_TEST(RangeTest, ContainAndIntersect) {
   {
     SCOPED_TRACE("contain and intersect");
-    gfx::Range r1(10, 12);
-    gfx::Range r2(1, 8);
-    gfx::Range r3(5, 12);
+    TypeParam r1(10, 12);
+    TypeParam r2(1, 8);
+    TypeParam r3(5, 12);
     TestContainsAndIntersects(r1, r2, r3);
   }
   {
     SCOPED_TRACE("contain and intersect: reversed");
-    gfx::Range r1(12, 10);
-    gfx::Range r2(8, 1);
-    gfx::Range r3(12, 5);
+    TypeParam r1(12, 10);
+    TypeParam r2(8, 1);
+    TypeParam r3(12, 5);
     TestContainsAndIntersects(r1, r2, r3);
   }
   // Invalid rect tests
-  gfx::Range r1(10, 12);
-  gfx::Range r2(8, 1);
-  gfx::Range invalid = r1.Intersect(r2);
+  TypeParam r1(10, 12);
+  TypeParam r2(8, 1);
+  TypeParam invalid = r1.Intersect(r2);
   EXPECT_FALSE(invalid.IsValid());
   EXPECT_FALSE(invalid.Contains(invalid));
   EXPECT_FALSE(invalid.Contains(r1));
diff --git a/ui/gfx/render_text.cc b/ui/gfx/render_text.cc
index 89acc94..e04fc71 100644
--- a/ui/gfx/render_text.cc
+++ b/ui/gfx/render_text.cc
@@ -80,8 +80,7 @@
   // the entire font height instead.
   const int space =
       display_height - ((internal_leading != 0) ? cap_height : font_height);
-  const int baseline_shift =
-      std::ceil(static_cast<float>(space) / 2) - internal_leading;
+  const int baseline_shift = space / 2 - internal_leading;
   return baseline + std::max(min_shift, std::min(max_shift, baseline_shift));
 }
 
@@ -205,8 +204,8 @@
 }
 
 void SkiaTextRenderer::SetFontRenderParams(const FontRenderParams& params,
-                                           bool background_is_transparent) {
-  ApplyRenderParams(params, background_is_transparent, &paint_);
+                                           bool subpixel_rendering_suppressed) {
+  ApplyRenderParams(params, subpixel_rendering_suppressed, &paint_);
 }
 
 void SkiaTextRenderer::SetTypeface(SkTypeface* typeface) {
@@ -388,10 +387,10 @@
 }
 
 void ApplyRenderParams(const FontRenderParams& params,
-                       bool background_is_transparent,
+                       bool subpixel_rendering_suppressed,
                        SkPaint* paint) {
   paint->setAntiAlias(params.antialiasing);
-  paint->setLCDRenderText(!background_is_transparent &&
+  paint->setLCDRenderText(!subpixel_rendering_suppressed &&
       params.subpixel_rendering != FontRenderParams::SUBPIXEL_RENDERING_NONE);
   paint->setSubpixelText(params.subpixel_positioning);
   paint->setAutohinted(params.autohinter);
@@ -500,7 +499,7 @@
     multiline_ = multiline;
     cached_bounds_and_offset_valid_ = false;
     lines_.clear();
-    OnDisplayTextAttributeChanged();
+    OnTextAttributeChanged();
   }
 }
 
@@ -920,7 +919,7 @@
       text_elided_(false),
       min_line_height_(0),
       multiline_(false),
-      background_is_transparent_(false),
+      subpixel_rendering_suppressed_(false),
       clip_to_display_rect_(true),
       baseline_(kInvalidBaseline),
       cached_bounds_and_offset_valid_(false) {
diff --git a/ui/gfx/render_text.h b/ui/gfx/render_text.h
index 5a18253..4d629a0 100644
--- a/ui/gfx/render_text.h
+++ b/ui/gfx/render_text.h
@@ -45,14 +45,14 @@
 namespace internal {
 
 // Internal helper class used by derived classes to draw text through Skia.
-class SkiaTextRenderer {
+class GFX_EXPORT SkiaTextRenderer {
  public:
   explicit SkiaTextRenderer(Canvas* canvas);
-  ~SkiaTextRenderer();
+  virtual ~SkiaTextRenderer();
 
   void SetDrawLooper(SkDrawLooper* draw_looper);
   void SetFontRenderParams(const FontRenderParams& params,
-                           bool background_is_transparent);
+                           bool subpixel_rendering_suppressed);
   void SetTypeface(SkTypeface* typeface);
   void SetTextSize(SkScalar size);
   void SetFontFamilyWithStyle(const std::string& family, int font_style);
@@ -63,14 +63,14 @@
   // two metrics must be set together.
   void SetUnderlineMetrics(SkScalar thickness, SkScalar position);
   void DrawSelection(const std::vector<Rect>& selection, SkColor color);
-  void DrawPosText(const SkPoint* pos,
-                   const uint16* glyphs,
-                   size_t glyph_count);
+  virtual void DrawPosText(const SkPoint* pos,
+                           const uint16* glyphs,
+                           size_t glyph_count);
   // Draw underline and strike-through text decorations.
   // Based on |SkCanvas::DrawTextDecorations()| and constants from:
   //   third_party/skia/src/core/SkTextFormatParams.h
-  void DrawDecorations(int x, int y, int width, bool underline, bool strike,
-                       bool diagonal_strike);
+  virtual void DrawDecorations(int x, int y, int width, bool underline,
+                               bool strike, bool diagonal_strike);
   // Finishes any ongoing diagonal strike run.
   void EndDiagonalStrike();
   void DrawUnderline(int x, int y, int width);
@@ -181,7 +181,7 @@
 
 // Applies the given FontRenderParams to a Skia |paint|.
 void ApplyRenderParams(const FontRenderParams& params,
-                       bool background_is_transparent,
+                       bool subpixel_rendering_suppressed,
                        SkPaint* paint);
 
 }  // namespace internal
@@ -273,9 +273,11 @@
   const Rect& display_rect() const { return display_rect_; }
   void SetDisplayRect(const Rect& r);
 
-  bool background_is_transparent() const { return background_is_transparent_; }
-  void set_background_is_transparent(bool transparent) {
-    background_is_transparent_ = transparent;
+  bool subpixel_rendering_suppressed() const {
+    return subpixel_rendering_suppressed_;
+  }
+  void set_subpixel_rendering_suppressed(bool suppressed) {
+    subpixel_rendering_suppressed_ = suppressed;
   }
 
   const SelectionModel& selection_model() const { return selection_model_; }
@@ -732,8 +734,9 @@
   // |display_rect_| as the width cap.
   bool multiline_;
 
-  // Is the background transparent (either partially or fully)?
-  bool background_is_transparent_;
+  // Set to true to suppress subpixel rendering due to non-font reasons (eg.
+  // if the background is transparent). The default value is false.
+  bool subpixel_rendering_suppressed_;
 
   // The local display area for rendering the text.
   Rect display_rect_;
diff --git a/ui/gfx/render_text_harfbuzz.cc b/ui/gfx/render_text_harfbuzz.cc
index 7e916ff3..0580abe 100644
--- a/ui/gfx/render_text_harfbuzz.cc
+++ b/ui/gfx/render_text_harfbuzz.cc
@@ -20,13 +20,13 @@
 #include "ui/gfx/font_fallback.h"
 #include "ui/gfx/font_render_params.h"
 #include "ui/gfx/harfbuzz_font_skia.h"
+#include "ui/gfx/range/range_f.h"
 #include "ui/gfx/utf16_indexing.h"
 
 #if defined(OS_WIN)
 #include "ui/gfx/font_fallback_win.h"
 #endif
 
-using gfx::internal::RangeF;
 using gfx::internal::RoundRangeF;
 
 namespace gfx {
@@ -223,14 +223,14 @@
                       bool multiline,
                       const base::string16& text,
                       const BreakList<size_t>* words,
-                      const ScopedVector<internal::TextRunHarfBuzz>& runs)
+                      const internal::TextRunList& run_list)
       : max_width_((max_width == 0) ? SK_ScalarMax : SkIntToScalar(max_width)),
         min_baseline_(min_baseline),
         min_height_(min_height),
         multiline_(multiline),
         text_(text),
         words_(words),
-        runs_(runs),
+        run_list_(run_list),
         text_x_(0),
         line_x_(0),
         max_descent_(0),
@@ -241,7 +241,7 @@
 
   // Breaks the run at given |run_index| into Line structs.
   void AddRun(int run_index) {
-    const internal::TextRunHarfBuzz* run = runs_[run_index];
+    const internal::TextRunHarfBuzz* run = run_list_.runs()[run_index];
     base::char16 first_char = text_[run->range.start()];
     if (multiline_ && first_char == '\n') {
       AdvanceLine();
@@ -275,7 +275,7 @@
   // them. Adds a new Line to the back of |lines_| whenever a new segment can't
   // be added without the Line's width exceeding |max_width_|.
   void BreakRun(int run_index) {
-    const internal::TextRunHarfBuzz& run = *runs_[run_index];
+    const internal::TextRunHarfBuzz& run = *(run_list_.runs()[run_index]);
     SkScalar width = 0;
     size_t next_char = run.range.start();
 
@@ -375,6 +375,12 @@
   void AdvanceLine() {
     if (!lines_.empty()) {
       internal::Line* line = &lines_.back();
+      std::sort(line->segments.begin(), line->segments.end(),
+                [this](const internal::LineSegment& s1,
+                       const internal::LineSegment& s2) -> bool {
+                  return run_list_.logical_to_visual(s1.run) <
+                         run_list_.logical_to_visual(s2.run);
+                });
       line->size.set_height(std::max(min_height_, max_descent_ + max_ascent_));
       line->baseline =
           std::max(min_baseline_, SkScalarRoundToInt(max_ascent_));
@@ -394,7 +400,7 @@
       DCHECK_EQ(0, width);
       return;
     }
-    const internal::TextRunHarfBuzz& run = *runs_[run_index];
+    const internal::TextRunHarfBuzz& run = *(run_list_.runs()[run_index]);
 
     internal::LineSegment segment;
     segment.run = run_index;
@@ -437,7 +443,7 @@
   const bool multiline_;
   const base::string16& text_;
   const BreakList<size_t>* const words_;
-  const ScopedVector<internal::TextRunHarfBuzz>& runs_;
+  const internal::TextRunList& run_list_;
 
   // Stores the resulting lines.
   std::vector<internal::Line> lines_;
@@ -463,8 +469,8 @@
 namespace internal {
 
 Range RoundRangeF(const RangeF& range_f) {
-  return Range(std::floor(range_f.first + 0.5f),
-               std::floor(range_f.second + 0.5f));
+  return Range(std::floor(range_f.start() + 0.5f),
+               std::floor(range_f.end() + 0.5f));
 }
 
 TextRunHarfBuzz::TextRunHarfBuzz()
@@ -625,7 +631,7 @@
 RenderTextHarfBuzz::~RenderTextHarfBuzz() {}
 
 scoped_ptr<RenderText> RenderTextHarfBuzz::CreateInstanceOfSameType() const {
-  return scoped_ptr<RenderTextHarfBuzz>(new RenderTextHarfBuzz);
+  return make_scoped_ptr(new RenderTextHarfBuzz);
 }
 
 const base::string16& RenderTextHarfBuzz::GetDisplayText() {
@@ -721,9 +727,9 @@
   // position since clients expect them to be contiguous.
   if (cursor_enabled() && run_index == run_list->size() - 1 &&
       index == (run->is_rtl ? run->range.start() : run->range.end() - 1))
-    bounds.second = std::ceil(bounds.second);
+    bounds.set_end(std::ceil(bounds.end()));
   return RoundRangeF(run->is_rtl ?
-      RangeF(bounds.second, bounds.first) : bounds);
+      RangeF(bounds.end(), bounds.start()) : bounds);
 }
 
 int RenderTextHarfBuzz::GetDisplayTextBaseline() {
@@ -970,8 +976,7 @@
     HarfBuzzLineBreaker line_breaker(
         display_rect().width(), font_list().GetBaseline(),
         std::max(font_list().GetHeight(), min_line_height()), multiline(),
-        GetDisplayText(), multiline() ? &GetLineBreaks() : nullptr,
-        run_list->runs());
+        GetDisplayText(), multiline() ? &GetLineBreaks() : nullptr, *run_list);
 
     // TODO(vadimt): Remove ScopedTracker below once crbug.com/431326 is fixed.
     tracked_objects::ScopedTracker tracking_profile3(
@@ -979,8 +984,7 @@
             "431326 RenderTextHarfBuzz::EnsureLayout3"));
 
     for (size_t i = 0; i < run_list->size(); ++i)
-      line_breaker.AddRun(run_list->visual_to_logical(i));
-
+      line_breaker.AddRun(i);
     std::vector<internal::Line> lines;
     line_breaker.Finalize(&lines, &total_size_);
     set_lines(&lines);
@@ -988,15 +992,20 @@
 }
 
 void RenderTextHarfBuzz::DrawVisualText(Canvas* canvas) {
+  internal::SkiaTextRenderer renderer(canvas);
+  DrawVisualTextInternal(&renderer);
+}
+
+void RenderTextHarfBuzz::DrawVisualTextInternal(
+    internal::SkiaTextRenderer* renderer) {
   DCHECK(!update_layout_run_list_);
   DCHECK(!update_display_run_list_);
   DCHECK(!update_display_text_);
   if (lines().empty())
     return;
 
-  internal::SkiaTextRenderer renderer(canvas);
-  ApplyFadeEffects(&renderer);
-  ApplyTextShadows(&renderer);
+  ApplyFadeEffects(renderer);
+  ApplyTextShadows(renderer);
   ApplyCompositionAndSelectionStyles();
 
   internal::TextRunList* run_list = GetRunList();
@@ -1006,14 +1015,16 @@
     SkScalar preceding_segment_widths = 0;
     for (const internal::LineSegment& segment : line.segments) {
       const internal::TextRunHarfBuzz& run = *run_list->runs()[segment.run];
-      renderer.SetTypeface(run.skia_face.get());
-      renderer.SetTextSize(SkIntToScalar(run.font_size));
-      renderer.SetFontRenderParams(run.render_params,
-                                   background_is_transparent());
+      renderer->SetTypeface(run.skia_face.get());
+      renderer->SetTextSize(SkIntToScalar(run.font_size));
+      renderer->SetFontRenderParams(run.render_params,
+                                    subpixel_rendering_suppressed());
       Range glyphs_range = run.CharRangeToGlyphRange(segment.char_range);
       scoped_ptr<SkPoint[]> positions(new SkPoint[glyphs_range.length()]);
-      SkScalar offset_x =
-          preceding_segment_widths - run.positions[glyphs_range.start()].x();
+      SkScalar offset_x = preceding_segment_widths -
+                          ((glyphs_range.GetMin() != 0)
+                               ? run.positions[glyphs_range.GetMin()].x()
+                               : 0);
       for (size_t j = 0; j < glyphs_range.length(); ++j) {
         positions[j] = run.positions[(glyphs_range.is_reversed()) ?
                                      (glyphs_range.start() - j) :
@@ -1036,8 +1047,8 @@
         if (colored_glyphs.is_empty())
           continue;
 
-        renderer.SetForegroundColor(it->second);
-        renderer.DrawPosText(
+        renderer->SetForegroundColor(it->second);
+        renderer->DrawPosText(
             &positions[colored_glyphs.start() - glyphs_range.start()],
             &run.glyphs[colored_glyphs.start()], colored_glyphs.length());
         int start_x = SkScalarRoundToInt(
@@ -1047,15 +1058,15 @@
                 ? (SkFloatToScalar(segment.width) + preceding_segment_widths +
                    SkIntToScalar(origin.x()))
                 : positions[colored_glyphs.end() - glyphs_range.start()].x());
-        renderer.DrawDecorations(start_x, origin.y(), end_x - start_x,
-                                 run.underline, run.strike,
-                                 run.diagonal_strike);
+        renderer->DrawDecorations(start_x, origin.y(), end_x - start_x,
+                                  run.underline, run.strike,
+                                  run.diagonal_strike);
       }
       preceding_segment_widths += SkFloatToScalar(segment.width);
     }
   }
 
-  renderer.EndDiagonalStrike();
+  renderer->EndDiagonalStrike();
 
   UndoCompositionAndSelectionStyles();
 }
@@ -1297,7 +1308,7 @@
 
   hb_font_t* harfbuzz_font = CreateHarfBuzzFont(
       run->skia_face.get(), SkIntToScalar(run->font_size), run->render_params,
-      background_is_transparent());
+      subpixel_rendering_suppressed());
 
   // TODO(vadimt): Remove ScopedTracker below once crbug.com/431326 is fixed.
   tracked_objects::ScopedTracker tracking_profile1(
@@ -1377,8 +1388,8 @@
     const SkScalar y_offset = SkFixedToScalar(hb_positions[i].y_offset);
     run->positions[i].set(run->width + x_offset, -y_offset);
     run->width += (glyph_width_for_test_ > 0)
-                      ? SkIntToScalar(glyph_width_for_test_)
-                      : SkFixedToScalar(hb_positions[i].x_advance);
+                      ? glyph_width_for_test_
+                      : SkFixedToFloat(hb_positions[i].x_advance);
     // Round run widths if subpixel positioning is off to match native behavior.
     if (!run->render_params.subpixel_positioning)
       run->width = std::floor(run->width + 0.5f);
diff --git a/ui/gfx/render_text_harfbuzz.h b/ui/gfx/render_text_harfbuzz.h
index 8fbf1d9..23c94995 100644
--- a/ui/gfx/render_text_harfbuzz.h
+++ b/ui/gfx/render_text_harfbuzz.h
@@ -20,13 +20,13 @@
 
 namespace gfx {
 
+class Range;
+class RangeF;
+
 namespace internal {
 
-// TODO(ckocagil): Make Range a template class and RangeF an instance of it.
-typedef std::pair<float, float> RangeF;
-
 // Applies std::round to the start and end values of the given RangeF.
-Range GFX_EXPORT RoundRangeF(const RangeF& range_f);
+GFX_EXPORT Range RoundRangeF(const RangeF& range_f);
 
 struct GFX_EXPORT TextRunHarfBuzz {
   TextRunHarfBuzz();
@@ -164,6 +164,9 @@
   friend class RenderTextTest;
   FRIEND_TEST_ALL_PREFIXES(RenderTextTest, Multiline_NormalWidth);
   FRIEND_TEST_ALL_PREFIXES(RenderTextTest, HarfBuzz_RunDirection);
+  FRIEND_TEST_ALL_PREFIXES(RenderTextTest, HarfBuzz_HorizontalPositions);
+  FRIEND_TEST_ALL_PREFIXES(RenderTextTest,
+                           HarfBuzz_TextPositionWithFractionalSize);
   FRIEND_TEST_ALL_PREFIXES(RenderTextTest, HarfBuzz_BreakRunsByUnicodeBlocks);
   FRIEND_TEST_ALL_PREFIXES(RenderTextTest, HarfBuzz_BreakRunsByEmoji);
   FRIEND_TEST_ALL_PREFIXES(RenderTextTest, HarfBuzz_SubglyphGraphemeCases);
@@ -174,10 +177,13 @@
   // Specify the width of a glyph for test. The width of glyphs is very
   // platform-dependent and environment-dependent. Otherwise multiline test
   // will become really flaky.
-  void set_glyph_width_for_test(uint8 test_width) {
+  void set_glyph_width_for_test(float test_width) {
     glyph_width_for_test_ = test_width;
   }
 
+  // The actual implementation of the text drawing.
+  void DrawVisualTextInternal(internal::SkiaTextRenderer* renderer);
+
   // Return the run index that contains the argument; or the length of the
   // |runs_| vector if argument exceeds the text length or width.
   size_t GetRunContainingCaret(const SelectionModel& caret);
@@ -256,7 +262,7 @@
   SizeF total_size_;
 
   // Fixed width of glyphs. This should only be set in test environments.
-  uint8 glyph_width_for_test_;
+  float glyph_width_for_test_;
 
   DISALLOW_COPY_AND_ASSIGN(RenderTextHarfBuzz);
 };
diff --git a/ui/gfx/render_text_mac.cc b/ui/gfx/render_text_mac.cc
index 32731d4..e60ecca6 100644
--- a/ui/gfx/render_text_mac.cc
+++ b/ui/gfx/render_text_mac.cc
@@ -25,7 +25,7 @@
 }
 
 scoped_ptr<RenderText> RenderTextMac::CreateInstanceOfSameType() const {
-  return scoped_ptr<RenderTextMac>(new RenderTextMac);
+  return make_scoped_ptr(new RenderTextMac);
 }
 
 const base::string16& RenderTextMac::GetDisplayText() {
diff --git a/ui/gfx/render_text_unittest.cc b/ui/gfx/render_text_unittest.cc
index 60cd5a7..e7829f6 100644
--- a/ui/gfx/render_text_unittest.cc
+++ b/ui/gfx/render_text_unittest.cc
@@ -18,6 +18,8 @@
 #include "ui/gfx/canvas.h"
 #include "ui/gfx/color_utils.h"
 #include "ui/gfx/font.h"
+#include "ui/gfx/range/range.h"
+#include "ui/gfx/range/range_f.h"
 #include "ui/gfx/render_text_harfbuzz.h"
 
 #if defined(OS_WIN)
@@ -90,6 +92,76 @@
     EXPECT_EQ(expected_range, segments[0].char_range);
 }
 
+// The class which records the drawing operations so that the test case can
+// verify where exactly the glyphs are drawn.
+class TestSkiaTextRenderer : public internal::SkiaTextRenderer {
+ public:
+  struct TextLog {
+    TextLog() : glyph_count(0u) {}
+    PointF origin;
+    size_t glyph_count;
+  };
+
+  struct DecorationLog {
+    DecorationLog(int x, int y, int width, bool underline, bool strike,
+                  bool diagonal_strike)
+        : x(x), y(y), width(width), underline(underline), strike(strike),
+          diagonal_strike(diagonal_strike) {}
+    int x;
+    int y;
+    int width;
+    bool underline;
+    bool strike;
+    bool diagonal_strike;
+  };
+
+  explicit TestSkiaTextRenderer(Canvas* canvas)
+      : internal::SkiaTextRenderer(canvas) {}
+  ~TestSkiaTextRenderer() override {}
+
+  void GetTextLogAndReset(std::vector<TextLog>* text_log) {
+    text_log_.swap(*text_log);
+    text_log_.clear();
+  }
+
+  void GetDecorationLogAndReset(std::vector<DecorationLog>* decoration_log) {
+    decoration_log_.swap(*decoration_log);
+    decoration_log_.clear();
+  }
+
+ private:
+  // internal::SkiaTextRenderer:
+  void DrawPosText(const SkPoint* pos,
+                   const uint16* glyphs,
+                   size_t glyph_count) override {
+    TextLog log_entry;
+    log_entry.glyph_count = glyph_count;
+    if (glyph_count > 0) {
+      log_entry.origin =
+          PointF(SkScalarToFloat(pos[0].x()), SkScalarToFloat(pos[0].y()));
+      for (size_t i = 1U; i < glyph_count; ++i) {
+        log_entry.origin.SetToMin(
+            PointF(SkScalarToFloat(pos[i].x()), SkScalarToFloat(pos[i].y())));
+      }
+    }
+    text_log_.push_back(log_entry);
+    internal::SkiaTextRenderer::DrawPosText(pos, glyphs, glyph_count);
+  }
+
+  void DrawDecorations(int x, int y, int width, bool underline, bool strike,
+                       bool diagonal_strike) override {
+    decoration_log_.push_back(
+        DecorationLog(x, y, width, underline, strike, diagonal_strike));
+    internal::SkiaTextRenderer::DrawDecorations(
+        x, y, width, underline, strike, diagonal_strike);
+  }
+
+  std::vector<TextLog> text_log_;
+  std::vector<DecorationLog> decoration_log_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestSkiaTextRenderer);
+};
+
 }  // namespace
 
 class RenderTextTest : public testing::Test {
@@ -1917,11 +1989,15 @@
     const wchar_t* const text;
     const Range first_line_char_range;
     const Range second_line_char_range;
+    bool is_ltr;
   } kTestStrings[] = {
-    { L"abc defg hijkl", Range(0, 9), Range(9, 14) },
-    { L"qwertyzxcvbn", Range(0, 10), Range(10, 12) },
+    { L"abc defg hijkl", Range(0, 9), Range(9, 14), true },
+    { L"qwertyzxcvbn", Range(0, 10), Range(10, 12), true },
+    { L"\x0627\x0644\x0644\x063A\x0629 "
+      L"\x0627\x0644\x0639\x0631\x0628\x064A\x0629",
+      Range(0, 6), Range(6, 13), false },
     { L"\x062A\x0641\x0627\x062D\x05EA\x05E4\x05D5\x05D6\x05D9"
-      L"\x05DA\x05DB\x05DD", Range(4, 12), Range(0, 4) }
+      L"\x05DA\x05DB\x05DD", Range(0, 4), Range(4, 12), false }
   };
 
   RenderTextHarfBuzz render_text;
@@ -1930,12 +2006,17 @@
   render_text.set_glyph_width_for_test(5);
   render_text.SetDisplayRect(Rect(50, 1000));
   render_text.SetMultiline(true);
+  render_text.SetHorizontalAlignment(ALIGN_TO_HEAD);
+
   Canvas canvas;
+  TestSkiaTextRenderer renderer(&canvas);
 
   for (size_t i = 0; i < arraysize(kTestStrings); ++i) {
     SCOPED_TRACE(base::StringPrintf("kTestStrings[%" PRIuS "]", i));
     render_text.SetText(WideToUTF16(kTestStrings[i].text));
-    render_text.Draw(&canvas);
+    render_text.EnsureLayout();
+    render_text.DrawVisualTextInternal(&renderer);
+
     ASSERT_EQ(2U, render_text.lines_.size());
     ASSERT_EQ(1U, render_text.lines_[0].segments.size());
     EXPECT_EQ(kTestStrings[i].first_line_char_range,
@@ -1943,6 +2024,26 @@
     ASSERT_EQ(1U, render_text.lines_[1].segments.size());
     EXPECT_EQ(kTestStrings[i].second_line_char_range,
               render_text.lines_[1].segments[0].char_range);
+
+    std::vector<TestSkiaTextRenderer::TextLog> text_log;
+    renderer.GetTextLogAndReset(&text_log);
+    ASSERT_EQ(2U, text_log.size());
+    // NOTE: this expectation compares the character length and glyph counts,
+    // which isn't always equal. This is okay only because all the test
+    // strings are simple (like, no compound characters nor UTF16-surrogate
+    // pairs). Be careful in case more complicated test strings are added.
+    EXPECT_EQ(kTestStrings[i].first_line_char_range.length(),
+              text_log[0].glyph_count);
+    EXPECT_EQ(kTestStrings[i].second_line_char_range.length(),
+              text_log[1].glyph_count);
+    EXPECT_LT(text_log[0].origin.y(), text_log[1].origin.y());
+    if (kTestStrings[i].is_ltr) {
+      EXPECT_EQ(0, text_log[0].origin.x());
+      EXPECT_EQ(0, text_log[1].origin.x());
+    } else {
+      EXPECT_LT(0, text_log[0].origin.x());
+      EXPECT_LT(0, text_log[1].origin.x());
+    }
   }
 }
 
@@ -2019,6 +2120,29 @@
             render_text.GetDisplayText().find(base::WideToUTF16(kEllipsis)));
 }
 
+TEST_F(RenderTextTest, Multiline_NewlineCharacterReplacement) {
+  const wchar_t* kTestStrings[] = {
+    L"abc\ndef", L"a \n b ", L"ab\n", L"a\n\nb", L"\nab", L"\n",
+  };
+
+  for (size_t i = 0; i < arraysize(kTestStrings); ++i) {
+    SCOPED_TRACE(base::StringPrintf("kTestStrings[%" PRIuS "]", i));
+    RenderTextHarfBuzz render_text;
+    render_text.SetDisplayRect(Rect(200, 1000));
+    render_text.SetText(WideToUTF16(kTestStrings[i]));
+
+    base::string16 display_text = render_text.GetDisplayText();
+    // If RenderText is not multiline, the newline characters are replaced
+    // by symbols, therefore the character should be changed.
+    EXPECT_NE(WideToUTF16(kTestStrings[i]), render_text.GetDisplayText());
+
+    // Setting multiline will fix this, the newline characters will be back
+    // to the original text.
+    render_text.SetMultiline(true);
+    EXPECT_EQ(WideToUTF16(kTestStrings[i]), render_text.GetDisplayText());
+  }
+}
+
 TEST_F(RenderTextTest, NewlineWithoutMultilineFlag) {
   const wchar_t* kTestStrings[] = {
     L"abc\ndef", L"a \n b ", L"ab\n", L"a\n\nb", L"\nab", L"\n",
@@ -2037,6 +2161,61 @@
   }
 }
 
+// Make sure the horizontal positions of runs in a line (left-to-right for
+// LTR languages and right-to-left for RTL languages).
+TEST_F(RenderTextTest, HarfBuzz_HorizontalPositions) {
+  const struct {
+    const wchar_t* const text;
+    const Range first_run_char_range;
+    const Range second_run_char_range;
+    bool is_rtl;
+  } kTestStrings[] = {
+    { L"abc\x3042\x3044\x3046\x3048\x304A", Range(0, 3), Range(3, 8), false },
+    { L"\x062A\x0641\x0627\x062D"
+      L"\x05EA\x05E4\x05D5\x05D6\x05D9\x05DA\x05DB\x05DD",
+      Range(0, 4), Range(4, 12), true },
+  };
+
+  RenderTextHarfBuzz render_text;
+  Canvas canvas;
+  TestSkiaTextRenderer renderer(&canvas);
+
+  for (size_t i = 0; i < arraysize(kTestStrings); ++i) {
+    SCOPED_TRACE(base::StringPrintf("kTestStrings[%" PRIuS "]", i));
+    render_text.SetText(WideToUTF16(kTestStrings[i].text));
+
+    render_text.EnsureLayout();
+    const internal::TextRunList* run_list = render_text.GetRunList();
+    ASSERT_EQ(2U, run_list->runs().size());
+    EXPECT_EQ(kTestStrings[i].first_run_char_range, run_list->runs()[0]->range);
+    EXPECT_EQ(kTestStrings[i].second_run_char_range,
+              run_list->runs()[1]->range);
+    // If it's RTL, the visual order is reversed.
+    if (kTestStrings[i].is_rtl) {
+      EXPECT_EQ(1U, run_list->logical_to_visual(0));
+      EXPECT_EQ(0U, run_list->logical_to_visual(1));
+    } else {
+      EXPECT_EQ(0U, run_list->logical_to_visual(0));
+      EXPECT_EQ(1U, run_list->logical_to_visual(1));
+    }
+
+    render_text.DrawVisualTextInternal(&renderer);
+
+    std::vector<TestSkiaTextRenderer::TextLog> text_log;
+    renderer.GetTextLogAndReset(&text_log);
+
+    EXPECT_EQ(2U, text_log.size());
+
+    // Verifies the DrawText happens in the visual order and left-to-right.
+    // If the text is RTL, the logically first run should be drawn at last.
+    EXPECT_EQ(run_list->runs()[run_list->logical_to_visual(0)]->glyph_count,
+              text_log[0].glyph_count);
+    EXPECT_EQ(run_list->runs()[run_list->logical_to_visual(1)]->glyph_count,
+              text_log[1].glyph_count);
+    EXPECT_LT(text_log[0].origin.x(), text_log[1].origin.x());
+  }
+}
+
 // Test TextRunHarfBuzz's cluster finding logic.
 TEST_F(RenderTextTest, HarfBuzz_Clusters) {
   struct {
@@ -2121,7 +2300,7 @@
     auto first_grapheme_bounds = run->GetGraphemeBounds(iter, 0);
     EXPECT_EQ(first_grapheme_bounds, run->GetGraphemeBounds(iter, 1));
     auto second_grapheme_bounds = run->GetGraphemeBounds(iter, 2);
-    EXPECT_EQ(first_grapheme_bounds.second, second_grapheme_bounds.first);
+    EXPECT_EQ(first_grapheme_bounds.end(), second_grapheme_bounds.start());
   }
 }
 
@@ -2379,37 +2558,4 @@
   }
 }
 
-// This test validates that the RenderText centering baseline is greater than
-// the font list baseline for a valid display rect.
-TEST_F(RenderTextTest, CenteringBaselineTest) {
-  FontList font_list("Arial, 12px");
-  const std::vector<Font>& fonts = font_list.GetFonts();
-  ASSERT_EQ(1U, fonts.size());
-  ASSERT_EQ("arial",
-            base::StringToLowerASCII(fonts[0].GetActualFontNameForTesting()));
-
-  scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
-  render_text->SetDisplayRect(Rect(0, 0, 25, 25));
-  render_text->SetFontList(font_list);
-  EXPECT_GT(render_text->GetBaseline(), font_list.GetBaseline());
-}
-
-#if defined(OS_WIN)
-// This test validates that the RenderText centering baseline for uniscribe
-// fonts like Devnagri fonts is greater than the font list baseline for a valid
-// display rect.
-TEST_F(RenderTextTest, CenteringBaselineTestForUniscribeFonts) {
-  FontList font_list("Madhav, 12px");
-  const std::vector<Font>& fonts = font_list.GetFonts();
-  ASSERT_EQ(1U, fonts.size());
-  ASSERT_EQ("madhav",
-            base::StringToLowerASCII(fonts[0].GetActualFontNameForTesting()));
-
-  scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
-  render_text->SetDisplayRect(Rect(0, 0, 25, 25));
-  render_text->SetFontList(font_list);
-  EXPECT_GT(render_text->GetBaseline(), font_list.GetBaseline());
-}
-#endif
-
 }  // namespace gfx
diff --git a/ui/gl/generate_bindings.py b/ui/gl/generate_bindings.py
index 639ec7e..3b91dd37 100755
--- a/ui/gl/generate_bindings.py
+++ b/ui/gl/generate_bindings.py
@@ -562,6 +562,9 @@
 { 'return_type': 'void',
   'names': ['glGetProgramiv'],
   'arguments': 'GLuint program, GLenum pname, GLint* params', },
+{ 'return_type': 'GLint',
+  'names': ['glGetProgramResourceLocation'],
+  'arguments': 'GLuint program, GLenum programInterface, const char* name', },
 { 'return_type': 'void',
   'versions': [{ 'name': 'glGetQueryiv' }],
   'arguments': 'GLenum target, GLenum pname, GLint* params', },
@@ -621,6 +624,9 @@
 { 'return_type': 'const GLubyte*',
   'names': ['glGetString'],
   'arguments': 'GLenum name', },
+{ 'return_type': 'const GLubyte*',
+  'names': ['glGetStringi'],
+  'arguments': 'GLenum name, GLuint index', },
 { 'return_type': 'void',
   'versions': [{ 'name': 'glGetSynciv',
                  'extensions': ['GL_ARB_sync'] }],
diff --git a/ui/gl/gl_bindings_api_autogen_gl.h b/ui/gl/gl_bindings_api_autogen_gl.h
index c886a23..9b0366c 100644
--- a/ui/gl/gl_bindings_api_autogen_gl.h
+++ b/ui/gl/gl_bindings_api_autogen_gl.h
@@ -343,6 +343,9 @@
                            GLsizei* length,
                            char* infolog) override;
 void glGetProgramivFn(GLuint program, GLenum pname, GLint* params) override;
+GLint glGetProgramResourceLocationFn(GLuint program,
+                                     GLenum programInterface,
+                                     const char* name) override;
 void glGetQueryivFn(GLenum target, GLenum pname, GLint* params) override;
 void glGetQueryivARBFn(GLenum target, GLenum pname, GLint* params) override;
 void glGetQueryObjecti64vFn(GLuint id, GLenum pname, GLint64* params) override;
@@ -376,6 +379,7 @@
                          GLsizei* length,
                          char* source) override;
 const GLubyte* glGetStringFn(GLenum name) override;
+const GLubyte* glGetStringiFn(GLenum name, GLuint index) override;
 void glGetSyncivFn(GLsync sync,
                    GLenum pname,
                    GLsizei bufSize,
diff --git a/ui/gl/gl_bindings_autogen_gl.cc b/ui/gl/gl_bindings_autogen_gl.cc
index f1100e8..a655717 100644
--- a/ui/gl/gl_bindings_autogen_gl.cc
+++ b/ui/gl/gl_bindings_autogen_gl.cc
@@ -212,6 +212,7 @@
       GetGLProcAddress("glGetProgramInfoLog"));
   fn.glGetProgramivFn =
       reinterpret_cast<glGetProgramivProc>(GetGLProcAddress("glGetProgramiv"));
+  fn.glGetProgramResourceLocationFn = 0;
   fn.glGetQueryivFn = 0;
   fn.glGetQueryivARBFn = 0;
   fn.glGetQueryObjecti64vFn = 0;
@@ -232,6 +233,7 @@
       GetGLProcAddress("glGetShaderSource"));
   fn.glGetStringFn =
       reinterpret_cast<glGetStringProc>(GetGLProcAddress("glGetString"));
+  fn.glGetStringiFn = 0;
   fn.glGetSyncivFn = 0;
   fn.glGetTexLevelParameterfvFn = 0;
   fn.glGetTexLevelParameterivFn = 0;
@@ -1272,6 +1274,14 @@
     DCHECK(fn.glGetProgramBinaryFn);
   }
 
+  debug_fn.glGetProgramResourceLocationFn = 0;
+  if (ver->IsAtLeastGL(4u, 3u) || ver->IsAtLeastGLES(3u, 1u)) {
+    fn.glGetProgramResourceLocationFn =
+        reinterpret_cast<glGetProgramResourceLocationProc>(
+            GetGLProcAddress("glGetProgramResourceLocation"));
+    DCHECK(fn.glGetProgramResourceLocationFn);
+  }
+
   debug_fn.glGetQueryivFn = 0;
   if (!ver->is_es || ver->IsAtLeastGLES(3u, 0u)) {
     fn.glGetQueryivFn =
@@ -1387,6 +1397,13 @@
     DCHECK(fn.glGetShaderPrecisionFormatFn);
   }
 
+  debug_fn.glGetStringiFn = 0;
+  if (ver->IsAtLeastGL(3u, 0u) || ver->IsAtLeastGLES(3u, 0u)) {
+    fn.glGetStringiFn =
+        reinterpret_cast<glGetStringiProc>(GetGLProcAddress("glGetStringi"));
+    DCHECK(fn.glGetStringiFn);
+  }
+
   debug_fn.glGetSyncivFn = 0;
   if (ver->IsAtLeastGL(3u, 2u) || ver->IsAtLeastGLES(3u, 0u) ||
       ext.b_GL_ARB_sync) {
@@ -3246,6 +3263,20 @@
   g_driver_gl.debug_fn.glGetProgramivFn(program, pname, params);
 }
 
+static GLint GL_BINDING_CALL
+Debug_glGetProgramResourceLocation(GLuint program,
+                                   GLenum programInterface,
+                                   const char* name) {
+  GL_SERVICE_LOG("glGetProgramResourceLocation"
+                 << "(" << program << ", "
+                 << GLEnums::GetStringEnum(programInterface) << ", " << name
+                 << ")");
+  GLint result = g_driver_gl.debug_fn.glGetProgramResourceLocationFn(
+      program, programInterface, name);
+  GL_SERVICE_LOG("GL_RESULT: " << result);
+  return result;
+}
+
 static void GL_BINDING_CALL
 Debug_glGetQueryiv(GLenum target, GLenum pname, GLint* params) {
   GL_SERVICE_LOG("glGetQueryiv"
@@ -3391,6 +3422,16 @@
   return result;
 }
 
+static const GLubyte* GL_BINDING_CALL
+Debug_glGetStringi(GLenum name, GLuint index) {
+  GL_SERVICE_LOG("glGetStringi"
+                 << "(" << GLEnums::GetStringEnum(name) << ", " << index
+                 << ")");
+  const GLubyte* result = g_driver_gl.debug_fn.glGetStringiFn(name, index);
+  GL_SERVICE_LOG("GL_RESULT: " << result);
+  return result;
+}
+
 static void GL_BINDING_CALL Debug_glGetSynciv(GLsync sync,
                                               GLenum pname,
                                               GLsizei bufSize,
@@ -5243,6 +5284,10 @@
     debug_fn.glGetProgramivFn = fn.glGetProgramivFn;
     fn.glGetProgramivFn = Debug_glGetProgramiv;
   }
+  if (!debug_fn.glGetProgramResourceLocationFn) {
+    debug_fn.glGetProgramResourceLocationFn = fn.glGetProgramResourceLocationFn;
+    fn.glGetProgramResourceLocationFn = Debug_glGetProgramResourceLocation;
+  }
   if (!debug_fn.glGetQueryivFn) {
     debug_fn.glGetQueryivFn = fn.glGetQueryivFn;
     fn.glGetQueryivFn = Debug_glGetQueryiv;
@@ -5309,6 +5354,10 @@
     debug_fn.glGetStringFn = fn.glGetStringFn;
     fn.glGetStringFn = Debug_glGetString;
   }
+  if (!debug_fn.glGetStringiFn) {
+    debug_fn.glGetStringiFn = fn.glGetStringiFn;
+    fn.glGetStringiFn = Debug_glGetStringi;
+  }
   if (!debug_fn.glGetSyncivFn) {
     debug_fn.glGetSyncivFn = fn.glGetSyncivFn;
     fn.glGetSyncivFn = Debug_glGetSynciv;
@@ -6652,6 +6701,13 @@
   driver_->fn.glGetProgramivFn(program, pname, params);
 }
 
+GLint GLApiBase::glGetProgramResourceLocationFn(GLuint program,
+                                                GLenum programInterface,
+                                                const char* name) {
+  return driver_->fn.glGetProgramResourceLocationFn(program, programInterface,
+                                                    name);
+}
+
 void GLApiBase::glGetQueryivFn(GLenum target, GLenum pname, GLint* params) {
   driver_->fn.glGetQueryivFn(target, pname, params);
 }
@@ -6740,6 +6796,10 @@
   return driver_->fn.glGetStringFn(name);
 }
 
+const GLubyte* GLApiBase::glGetStringiFn(GLenum name, GLuint index) {
+  return driver_->fn.glGetStringiFn(name, index);
+}
+
 void GLApiBase::glGetSyncivFn(GLsync sync,
                               GLenum pname,
                               GLsizei bufSize,
@@ -8452,6 +8512,15 @@
   gl_api_->glGetProgramivFn(program, pname, params);
 }
 
+GLint TraceGLApi::glGetProgramResourceLocationFn(GLuint program,
+                                                 GLenum programInterface,
+                                                 const char* name) {
+  TRACE_EVENT_BINARY_EFFICIENT0("gpu",
+                                "TraceGLAPI::glGetProgramResourceLocation")
+  return gl_api_->glGetProgramResourceLocationFn(program, programInterface,
+                                                 name);
+}
+
 void TraceGLApi::glGetQueryivFn(GLenum target, GLenum pname, GLint* params) {
   TRACE_EVENT_BINARY_EFFICIENT0("gpu", "TraceGLAPI::glGetQueryiv")
   gl_api_->glGetQueryivFn(target, pname, params);
@@ -8559,6 +8628,11 @@
   return gl_api_->glGetStringFn(name);
 }
 
+const GLubyte* TraceGLApi::glGetStringiFn(GLenum name, GLuint index) {
+  TRACE_EVENT_BINARY_EFFICIENT0("gpu", "TraceGLAPI::glGetStringi")
+  return gl_api_->glGetStringiFn(name, index);
+}
+
 void TraceGLApi::glGetSyncivFn(GLsync sync,
                                GLenum pname,
                                GLsizei bufSize,
@@ -10539,6 +10613,16 @@
   LOG(ERROR) << "Trying to call glGetProgramiv() without current GL context";
 }
 
+GLint NoContextGLApi::glGetProgramResourceLocationFn(GLuint program,
+                                                     GLenum programInterface,
+                                                     const char* name) {
+  NOTREACHED() << "Trying to call glGetProgramResourceLocation() without "
+                  "current GL context";
+  LOG(ERROR) << "Trying to call glGetProgramResourceLocation() without current "
+                "GL context";
+  return 0;
+}
+
 void NoContextGLApi::glGetQueryivFn(GLenum target,
                                     GLenum pname,
                                     GLint* params) {
@@ -10676,6 +10760,12 @@
   return NULL;
 }
 
+const GLubyte* NoContextGLApi::glGetStringiFn(GLenum name, GLuint index) {
+  NOTREACHED() << "Trying to call glGetStringi() without current GL context";
+  LOG(ERROR) << "Trying to call glGetStringi() without current GL context";
+  return NULL;
+}
+
 void NoContextGLApi::glGetSyncivFn(GLsync sync,
                                    GLenum pname,
                                    GLsizei bufSize,
diff --git a/ui/gl/gl_bindings_autogen_gl.h b/ui/gl/gl_bindings_autogen_gl.h
index b930484..643a236 100644
--- a/ui/gl/gl_bindings_autogen_gl.h
+++ b/ui/gl/gl_bindings_autogen_gl.h
@@ -406,6 +406,10 @@
 typedef void(GL_BINDING_CALL* glGetProgramivProc)(GLuint program,
                                                   GLenum pname,
                                                   GLint* params);
+typedef GLint(GL_BINDING_CALL* glGetProgramResourceLocationProc)(
+    GLuint program,
+    GLenum programInterface,
+    const char* name);
 typedef void(GL_BINDING_CALL* glGetQueryivProc)(GLenum target,
                                                 GLenum pname,
                                                 GLint* params);
@@ -457,6 +461,8 @@
                                                      GLsizei* length,
                                                      char* source);
 typedef const GLubyte*(GL_BINDING_CALL* glGetStringProc)(GLenum name);
+typedef const GLubyte*(GL_BINDING_CALL* glGetStringiProc)(GLenum name,
+                                                          GLuint index);
 typedef void(GL_BINDING_CALL* glGetSyncivProc)(GLsync sync,
                                                GLenum pname,
                                                GLsizei bufSize,
@@ -1072,6 +1078,7 @@
   glGetProgramBinaryProc glGetProgramBinaryFn;
   glGetProgramInfoLogProc glGetProgramInfoLogFn;
   glGetProgramivProc glGetProgramivFn;
+  glGetProgramResourceLocationProc glGetProgramResourceLocationFn;
   glGetQueryivProc glGetQueryivFn;
   glGetQueryivARBProc glGetQueryivARBFn;
   glGetQueryObjecti64vProc glGetQueryObjecti64vFn;
@@ -1088,6 +1095,7 @@
   glGetShaderPrecisionFormatProc glGetShaderPrecisionFormatFn;
   glGetShaderSourceProc glGetShaderSourceFn;
   glGetStringProc glGetStringFn;
+  glGetStringiProc glGetStringiFn;
   glGetSyncivProc glGetSyncivFn;
   glGetTexLevelParameterfvProc glGetTexLevelParameterfvFn;
   glGetTexLevelParameterivProc glGetTexLevelParameterivFn;
@@ -1579,6 +1587,9 @@
   virtual void glGetProgramivFn(GLuint program,
                                 GLenum pname,
                                 GLint* params) = 0;
+  virtual GLint glGetProgramResourceLocationFn(GLuint program,
+                                               GLenum programInterface,
+                                               const char* name) = 0;
   virtual void glGetQueryivFn(GLenum target, GLenum pname, GLint* params) = 0;
   virtual void glGetQueryivARBFn(GLenum target,
                                  GLenum pname,
@@ -1622,6 +1633,7 @@
                                    GLsizei* length,
                                    char* source) = 0;
   virtual const GLubyte* glGetStringFn(GLenum name) = 0;
+  virtual const GLubyte* glGetStringiFn(GLenum name, GLuint index) = 0;
   virtual void glGetSyncivFn(GLsync sync,
                              GLenum pname,
                              GLsizei bufSize,
@@ -2181,6 +2193,8 @@
 #define glGetProgramBinary ::gfx::g_current_gl_context->glGetProgramBinaryFn
 #define glGetProgramInfoLog ::gfx::g_current_gl_context->glGetProgramInfoLogFn
 #define glGetProgramiv ::gfx::g_current_gl_context->glGetProgramivFn
+#define glGetProgramResourceLocation \
+  ::gfx::g_current_gl_context->glGetProgramResourceLocationFn
 #define glGetQueryiv ::gfx::g_current_gl_context->glGetQueryivFn
 #define glGetQueryivARB ::gfx::g_current_gl_context->glGetQueryivARBFn
 #define glGetQueryObjecti64v ::gfx::g_current_gl_context->glGetQueryObjecti64vFn
@@ -2204,6 +2218,7 @@
   ::gfx::g_current_gl_context->glGetShaderPrecisionFormatFn
 #define glGetShaderSource ::gfx::g_current_gl_context->glGetShaderSourceFn
 #define glGetString ::gfx::g_current_gl_context->glGetStringFn
+#define glGetStringi ::gfx::g_current_gl_context->glGetStringiFn
 #define glGetSynciv ::gfx::g_current_gl_context->glGetSyncivFn
 #define glGetTexLevelParameterfv \
   ::gfx::g_current_gl_context->glGetTexLevelParameterfvFn
diff --git a/ui/gl/gl_bindings_autogen_mock.cc b/ui/gl/gl_bindings_autogen_mock.cc
index 8d3614b..bf0a857 100644
--- a/ui/gl/gl_bindings_autogen_mock.cc
+++ b/ui/gl/gl_bindings_autogen_mock.cc
@@ -1223,6 +1223,15 @@
   interface_->GetProgramInfoLog(program, bufsize, length, infolog);
 }
 
+GLint GL_BINDING_CALL
+MockGLInterface::Mock_glGetProgramResourceLocation(GLuint program,
+                                                   GLenum programInterface,
+                                                   const char* name) {
+  MakeFunctionUnique("glGetProgramResourceLocation");
+  return interface_->GetProgramResourceLocation(program, programInterface,
+                                                name);
+}
+
 void GL_BINDING_CALL MockGLInterface::Mock_glGetProgramiv(GLuint program,
                                                           GLenum pname,
                                                           GLint* params) {
@@ -1398,6 +1407,12 @@
   return interface_->GetString(name);
 }
 
+const GLubyte* GL_BINDING_CALL
+MockGLInterface::Mock_glGetStringi(GLenum name, GLuint index) {
+  MakeFunctionUnique("glGetStringi");
+  return interface_->GetStringi(name, index);
+}
+
 void GL_BINDING_CALL MockGLInterface::Mock_glGetSynciv(GLsync sync,
                                                        GLenum pname,
                                                        GLsizei bufSize,
@@ -2860,6 +2875,8 @@
     return reinterpret_cast<void*>(Mock_glGetProgramBinaryOES);
   if (strcmp(name, "glGetProgramInfoLog") == 0)
     return reinterpret_cast<void*>(Mock_glGetProgramInfoLog);
+  if (strcmp(name, "glGetProgramResourceLocation") == 0)
+    return reinterpret_cast<void*>(Mock_glGetProgramResourceLocation);
   if (strcmp(name, "glGetProgramiv") == 0)
     return reinterpret_cast<void*>(Mock_glGetProgramiv);
   if (strcmp(name, "glGetQueryObjecti64v") == 0)
@@ -2906,6 +2923,8 @@
     return reinterpret_cast<void*>(Mock_glGetShaderiv);
   if (strcmp(name, "glGetString") == 0)
     return reinterpret_cast<void*>(Mock_glGetString);
+  if (strcmp(name, "glGetStringi") == 0)
+    return reinterpret_cast<void*>(Mock_glGetStringi);
   if (strcmp(name, "glGetSynciv") == 0)
     return reinterpret_cast<void*>(Mock_glGetSynciv);
   if (strcmp(name, "glGetTexLevelParameterfv") == 0)
diff --git a/ui/gl/gl_bindings_autogen_mock.h b/ui/gl/gl_bindings_autogen_mock.h
index 52abdae0..54c89939 100644
--- a/ui/gl/gl_bindings_autogen_mock.h
+++ b/ui/gl/gl_bindings_autogen_mock.h
@@ -441,6 +441,10 @@
                                                      GLsizei bufsize,
                                                      GLsizei* length,
                                                      char* infolog);
+static GLint GL_BINDING_CALL
+Mock_glGetProgramResourceLocation(GLuint program,
+                                  GLenum programInterface,
+                                  const char* name);
 static void GL_BINDING_CALL
 Mock_glGetProgramiv(GLuint program, GLenum pname, GLint* params);
 static void GL_BINDING_CALL
@@ -494,6 +498,8 @@
 static void GL_BINDING_CALL
 Mock_glGetShaderiv(GLuint shader, GLenum pname, GLint* params);
 static const GLubyte* GL_BINDING_CALL Mock_glGetString(GLenum name);
+static const GLubyte* GL_BINDING_CALL
+Mock_glGetStringi(GLenum name, GLuint index);
 static void GL_BINDING_CALL Mock_glGetSynciv(GLsync sync,
                                              GLenum pname,
                                              GLsizei bufSize,
diff --git a/ui/gl/gl_bindings_skia_in_process.cc b/ui/gl/gl_bindings_skia_in_process.cc
index 91d0b0f..25dd698e 100644
--- a/ui/gl/gl_bindings_skia_in_process.cc
+++ b/ui/gl/gl_bindings_skia_in_process.cc
@@ -229,6 +229,11 @@
   glFlush();
 }
 
+GLvoid StubGLFlushMappedBufferRange(GLenum target, GLintptr offset,
+                                    GLsizeiptr length) {
+  glFlushMappedBufferRange(target, offset, length);
+}
+
 GLvoid StubGLFramebufferRenderbuffer(GLenum target, GLenum attachment,
                                      GLenum renderbuffertarget,
                                      GLuint renderbuffer) {
@@ -331,6 +336,10 @@
   return glGetString(name);
 }
 
+const GLubyte* StubGLGetStringi(GLenum name, GLuint index) {
+  return glGetStringi(name, index);
+}
+
 GLvoid StubGLGetQueryiv(GLenum target, GLenum pname, GLint* params) {
   glGetQueryiv(target, pname, params);
 }
@@ -364,6 +373,20 @@
   glInsertEventMarkerEXT(length, marker);
 }
 
+GLvoid StubGLInvalidateFramebuffer(GLenum target, GLsizei numAttachments,
+                                   const GLenum* attachments) {
+  glInvalidateFramebuffer(target, numAttachments, attachments);
+}
+
+GLvoid StubGLInvalidateSubFramebuffer(GLenum target,
+                                      GLsizei numAttachments,
+                                      const GLenum* attachments,
+                                      GLint x, GLint y,
+                                      GLsizei width, GLsizei height) {
+  glInvalidateSubFramebuffer(target, numAttachments, attachments,
+                             x, y, width, height);
+}
+
 GLvoid StubGLLineWidth(GLfloat width) {
   glLineWidth(width);
 }
@@ -376,6 +399,11 @@
   return glMapBuffer(target, access);
 }
 
+void* StubGLMapBufferRange(GLenum target, GLintptr offset, GLsizeiptr length,
+                           GLbitfield access) {
+  return glMapBufferRange(target, offset, length, access);
+}
+
 GLvoid StubGLPixelStorei(GLenum pname, GLint param) {
   glPixelStorei(pname, param);
 }
@@ -589,6 +617,13 @@
 GLvoid StubGLViewport(GLint x, GLint y, GLsizei width, GLsizei height) {
   glViewport(x, y, width, height);
 }
+
+GLint StubGLGetProgramResourceLocation(GLuint program,
+                                       GLenum programInterface,
+                                       const char* name) {
+  return glGetProgramResourceLocation(program, programInterface, name);
+}
+
 }  // extern "C"
 }  // namespace
 
@@ -622,7 +657,7 @@
   interface->fStandard = standard;
   interface->fExtensions.init(standard,
                               StubGLGetString,
-                              NULL,
+                              StubGLGetStringi,
                               StubGLGetIntegerv);
 
   GrGLInterface::Functions* functions = &interface->fFunctions;
@@ -667,6 +702,7 @@
   functions->fEndQuery = StubGLEndQuery;
   functions->fFinish = StubGLFinish;
   functions->fFlush = StubGLFlush;
+  functions->fFlushMappedBufferRange = StubGLFlushMappedBufferRange;
   functions->fFrontFace = StubGLFrontFace;
   functions->fGenBuffers = StubGLGenBuffers;
   functions->fGenQueries = StubGLGenQueries;
@@ -687,11 +723,15 @@
   functions->fGetShaderiv = StubGLGetShaderiv;
   functions->fGetShaderPrecisionFormat = StubGLGetShaderPrecisionFormat;
   functions->fGetString = StubGLGetString;
+  functions->fGetStringi = StubGLGetStringi;
   functions->fGetTexLevelParameteriv = StubGLGetTexLevelParameteriv;
   functions->fGetUniformLocation = StubGLGetUniformLocation;
   functions->fInsertEventMarker = StubGLInsertEventMarker;
+  functions->fInvalidateFramebuffer = StubGLInvalidateFramebuffer;
+  functions->fInvalidateSubFramebuffer = StubGLInvalidateSubFramebuffer;
   functions->fLineWidth = StubGLLineWidth;
   functions->fLinkProgram = StubGLLinkProgram;
+  functions->fMapBufferRange = StubGLMapBufferRange;
   functions->fPixelStorei = StubGLPixelStorei;
   functions->fPopGroupMarker = StubGLPopGroupMarker;
   functions->fPushGroupMarker = StubGLPushGroupMarker;
@@ -754,11 +794,14 @@
   functions->fRenderbufferStorage = StubGLRenderbufferStorage;
   functions->fRenderbufferStorageMultisample =
     StubGLRenderbufferStorageMultisample;
+  functions->fRenderbufferStorageMultisampleES2EXT =
+    StubGLRenderbufferStorageMultisample;
   functions->fBlitFramebuffer = StubGLBlitFramebuffer;
   functions->fMapBuffer = StubGLMapBuffer;
   functions->fUnmapBuffer = StubGLUnmapBuffer;
   functions->fBindFragDataLocationIndexed =
     StubGLBindFragDataLocationIndexed;
+  functions->fGetProgramResourceLocation = StubGLGetProgramResourceLocation;
 
   return interface;
 }
diff --git a/ui/gl/gl_context.cc b/ui/gl/gl_context.cc
index f89f1d2a..720e8e2 100644
--- a/ui/gl/gl_context.cc
+++ b/ui/gl/gl_context.cc
@@ -106,8 +106,8 @@
   if(!version_info_) {
     std::string version = GetGLVersion();
     std::string renderer = GetGLRenderer();
-    version_info_ = scoped_ptr<GLVersionInfo>(
-        new GLVersionInfo(version.c_str(), renderer.c_str()));
+    version_info_ =
+        make_scoped_ptr(new GLVersionInfo(version.c_str(), renderer.c_str()));
   }
   return version_info_.get();
 }
diff --git a/ui/gl/gl_context_cgl.cc b/ui/gl/gl_context_cgl.cc
index d4bc7805..102407c 100644
--- a/ui/gl/gl_context_cgl.cc
+++ b/ui/gl/gl_context_cgl.cc
@@ -122,12 +122,15 @@
 
 void GLContextCGL::Destroy() {
   if (discrete_pixelformat_) {
-    // Delay releasing the pixel format for 10 seconds to reduce the number of
-    // unnecessary GPU switches.
-    base::MessageLoop::current()->PostDelayedTask(
-        FROM_HERE,
-        base::Bind(&CGLReleasePixelFormat, discrete_pixelformat_),
-        base::TimeDelta::FromSeconds(10));
+    if (base::MessageLoop::current() != NULL) {
+      // Delay releasing the pixel format for 10 seconds to reduce the number of
+      // unnecessary GPU switches.
+      base::MessageLoop::current()->PostDelayedTask(
+          FROM_HERE, base::Bind(&CGLReleasePixelFormat, discrete_pixelformat_),
+          base::TimeDelta::FromSeconds(10));
+    } else {
+      CGLReleasePixelFormat(discrete_pixelformat_);
+    }
     discrete_pixelformat_ = NULL;
   }
   if (context_) {
diff --git a/ui/gl/gl_mock_autogen_gl.h b/ui/gl/gl_mock_autogen_gl.h
index 5b47058..7bec0192 100644
--- a/ui/gl/gl_mock_autogen_gl.h
+++ b/ui/gl/gl_mock_autogen_gl.h
@@ -346,6 +346,8 @@
     GetProgramInfoLog,
     void(GLuint program, GLsizei bufsize, GLsizei* length, char* infolog));
 MOCK_METHOD3(GetProgramiv, void(GLuint program, GLenum pname, GLint* params));
+MOCK_METHOD3(GetProgramResourceLocation,
+             GLint(GLuint program, GLenum programInterface, const char* name));
 MOCK_METHOD3(GetQueryiv, void(GLenum target, GLenum pname, GLint* params));
 MOCK_METHOD3(GetQueryivARB, void(GLenum target, GLenum pname, GLint* params));
 MOCK_METHOD3(GetQueryObjecti64v,
@@ -376,6 +378,7 @@
     GetShaderSource,
     void(GLuint shader, GLsizei bufsize, GLsizei* length, char* source));
 MOCK_METHOD1(GetString, const GLubyte*(GLenum name));
+MOCK_METHOD2(GetStringi, const GLubyte*(GLenum name, GLuint index));
 MOCK_METHOD5(GetSynciv,
              void(GLsync sync,
                   GLenum pname,
diff --git a/ui/keyboard/BUILD.gn b/ui/keyboard/BUILD.gn
index e997834..bdb0775d 100644
--- a/ui/keyboard/BUILD.gn
+++ b/ui/keyboard/BUILD.gn
@@ -76,12 +76,12 @@
     "keyboard_resources.rc",
   ]
 
-  inputview_dir = "//third_party/google_input_tools/src/chrome/os/inputview"
+  input_tools_root_dir = "//third_party/google_input_tools/src/chrome/os"
   inputview_gen_js = "$root_gen_dir/ui/keyboard/resources/inputview.js"
   keyboard_mojom_gen_js = "$root_gen_dir/ui/keyboard/webui/keyboard.mojom.js"
   grit_flags = [
     "-E",
-    "inputview_dir=" + rebase_path(inputview_dir, "."),
+    "input_tools_root_dir=" + rebase_path(input_tools_root_dir, "."),
     "-E",
     "inputview_gen_js=" + rebase_path(inputview_gen_js, root_build_dir),
     "-E",
diff --git a/ui/keyboard/keyboard.gyp b/ui/keyboard/keyboard.gyp
index 27a26e2..c2c28a5 100644
--- a/ui/keyboard/keyboard.gyp
+++ b/ui/keyboard/keyboard.gyp
@@ -6,7 +6,7 @@
   'variables': {
     'chromium_code': 1,
     'keyboard_mojom_gen_js': '<(SHARED_INTERMEDIATE_DIR)/ui/keyboard/webui/keyboard.mojom.js',
-    'inputview_dir': '../../third_party/google_input_tools/src/chrome/os/inputview',
+    'input_tools_root_dir': '../../third_party/google_input_tools/src/chrome/os',
     'inputview_gen_js': '<(SHARED_INTERMEDIATE_DIR)/ui/keyboard/resources/inputview.js',
   },
   'targets': [
@@ -36,7 +36,7 @@
             'grit_grd_file': 'keyboard_resources.grd',
             'grit_additional_defines': [
               '-E', 'keyboard_mojom_gen_js=<(keyboard_mojom_gen_js)',
-              '-E', 'inputview_dir=<(inputview_dir)',
+              '-E', 'input_tools_root_dir=<(input_tools_root_dir)',
               '-E', 'inputview_gen_js=<(inputview_gen_js)',
             ],
           },
diff --git a/ui/keyboard/keyboard_controller_unittest.cc b/ui/keyboard/keyboard_controller_unittest.cc
index 7cab3064..fd5b087 100644
--- a/ui/keyboard/keyboard_controller_unittest.cc
+++ b/ui/keyboard/keyboard_controller_unittest.cc
@@ -26,6 +26,7 @@
 #include "ui/compositor/scoped_animation_duration_scale_mode.h"
 #include "ui/compositor/test/context_factories_for_test.h"
 #include "ui/compositor/test/layer_animator_test_controller.h"
+#include "ui/events/event_utils.h"
 #include "ui/events/test/event_generator.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/keyboard/keyboard_controller_observer.h"
@@ -337,13 +338,13 @@
   ui::EventTarget* root = root_window();
   ui::EventTargeter* targeter = root->GetEventTargeter();
   gfx::Point location = keyboard_window->bounds().CenterPoint();
-  ui::MouseEvent mouse1(ui::ET_MOUSE_MOVED, location, location, ui::EF_NONE,
-                        ui::EF_NONE);
+  ui::MouseEvent mouse1(ui::ET_MOUSE_MOVED, location, location,
+                        ui::EventTimeForNow(), ui::EF_NONE, ui::EF_NONE);
   EXPECT_EQ(keyboard_window, targeter->FindTargetForEvent(root, &mouse1));
 
   location.set_y(keyboard_window->bounds().y() - 5);
-  ui::MouseEvent mouse2(ui::ET_MOUSE_MOVED, location, location, ui::EF_NONE,
-                        ui::EF_NONE);
+  ui::MouseEvent mouse2(ui::ET_MOUSE_MOVED, location, location,
+                        ui::EventTimeForNow(), ui::EF_NONE, ui::EF_NONE);
   EXPECT_EQ(window.get(), targeter->FindTargetForEvent(root, &mouse2));
 }
 
@@ -369,8 +370,8 @@
   ui::EventTargeter* targeter = root->GetEventTargeter();
   gfx::Point location(root_window()->bounds().width() / 2,
                       root_window()->bounds().height() - 10);
-  ui::MouseEvent mouse(
-      ui::ET_MOUSE_MOVED, location, location, ui::EF_NONE, ui::EF_NONE);
+  ui::MouseEvent mouse(ui::ET_MOUSE_MOVED, location, location,
+                       ui::EventTimeForNow(), ui::EF_NONE, ui::EF_NONE);
   EXPECT_EQ(window.get(), targeter->FindTargetForEvent(root, &mouse));
   EXPECT_FALSE(proxy()->HasKeyboardWindow());
 
diff --git a/ui/keyboard/keyboard_resources.grd b/ui/keyboard/keyboard_resources.grd
index f70ead56..616109e 100644
--- a/ui/keyboard/keyboard_resources.grd
+++ b/ui/keyboard/keyboard_resources.grd
@@ -15,56 +15,55 @@
   </outputs>
   <release seq="1">
     <includes>
-      <include name="IDR_KEYBOARD_CONFIG_EMOJI" file="${inputview_dir}/config/emoji_data.js" type="BINDATA" />
-      <include name="IDR_KEYBOARD_CONFIG_HWT" file="${inputview_dir}/config/hwt_data.js" type="BINDATA" />
-      <include name="IDR_KEYBOARD_CONFIG_US" file="${inputview_dir}/config/us_data.js" type="BINDATA" />
-      <include name="IDR_KEYBOARD_CSS_EMOJI" file="${inputview_dir}/emoji.css" type="BINDATA" />
-      <include name="IDR_KEYBOARD_IMAGES_BACKSPACE" file="${inputview_dir}/images/backspace.png" type="BINDATA" />
-      <include name="IDR_KEYBOARD_IMAGES_CAR" file="${inputview_dir}/images/car.png" type="BINDATA" />
-      <include name="IDR_KEYBOARD_IMAGES_CAT" file="${inputview_dir}/images/emoji_cat_items.png" type="BINDATA" />
-      <include name="IDR_KEYBOARD_IMAGES_CHECK" file="${inputview_dir}/images/check.png" type="BINDATA" />
-      <include name="IDR_KEYBOARD_IMAGES_COMPACT" file="${inputview_dir}/images/compact.png" type="BINDATA" />
-      <include name="IDR_KEYBOARD_IMAGES_DOWN" file="${inputview_dir}/images/down.png" type="BINDATA" />
-      <include name="IDR_KEYBOARD_IMAGES_EMOJI" file="${inputview_dir}/images/emoji.png" type="BINDATA" />
-      <include name="IDR_KEYBOARD_IMAGES_EMOTICON" file="${inputview_dir}/images/emoticon.png" type="BINDATA" />
-      <include name="IDR_KEYBOARD_IMAGES_ERROR" file="${inputview_dir}/images/error.png" type="BINDATA" />
-      <include name="IDR_KEYBOARD_IMAGES_FAVORITE" file="${inputview_dir}/images/favorit.png" type="BINDATA" />
-      <include name="IDR_KEYBOARD_IMAGES_FLOWER" file="${inputview_dir}/images/flower.png" type="BINDATA" />
-      <include name="IDR_KEYBOARD_IMAGES_FULLSIZE" file="${inputview_dir}/images/regular_size.png" type="BINDATA" />
-      <include name="IDR_KEYBOARD_IMAGES_GLOBE" file="${inputview_dir}/images/globe.png" type="BINDATA" />
-      <include name="IDR_KEYBOARD_IMAGES_HIDE_KEYBOARD" file="${inputview_dir}/images/hide.png" type="BINDATA" />
+      <include name="IDR_KEYBOARD_CONFIG_EMOJI" file="${input_tools_root_dir}/inputview/config/emoji_data.js" type="BINDATA" />
+      <include name="IDR_KEYBOARD_CONFIG_HWT" file="${input_tools_root_dir}/inputview/config/hwt_data.js" type="BINDATA" />
+      <include name="IDR_KEYBOARD_CONFIG_US" file="${input_tools_root_dir}/inputview/config/us_data.js" type="BINDATA" />
+      <include name="IDR_KEYBOARD_CSS_EMOJI" file="${input_tools_root_dir}/inputview/emoji.css" type="BINDATA" />
+      <include name="IDR_KEYBOARD_IMAGES_BACKSPACE" file="${input_tools_root_dir}/inputview/images/backspace.png" type="BINDATA" />
+      <include name="IDR_KEYBOARD_IMAGES_CAR" file="${input_tools_root_dir}/inputview/images/car.png" type="BINDATA" />
+      <include name="IDR_KEYBOARD_IMAGES_CAT" file="${input_tools_root_dir}/inputview/images/emoji_cat_items.png" type="BINDATA" />
+      <include name="IDR_KEYBOARD_IMAGES_CHECK" file="${input_tools_root_dir}/inputview/images/check.png" type="BINDATA" />
+      <include name="IDR_KEYBOARD_IMAGES_COMPACT" file="${input_tools_root_dir}/inputview/images/compact.png" type="BINDATA" />
+      <include name="IDR_KEYBOARD_IMAGES_DOWN" file="${input_tools_root_dir}/inputview/images/down.png" type="BINDATA" />
+      <include name="IDR_KEYBOARD_IMAGES_EMOJI" file="${input_tools_root_dir}/inputview/images/emoji.png" type="BINDATA" />
+      <include name="IDR_KEYBOARD_IMAGES_EMOTICON" file="${input_tools_root_dir}/inputview/images/emoticon.png" type="BINDATA" />
+      <include name="IDR_KEYBOARD_IMAGES_ERROR" file="${input_tools_root_dir}/inputview/images/error.png" type="BINDATA" />
+      <include name="IDR_KEYBOARD_IMAGES_FAVORITE" file="${input_tools_root_dir}/inputview/images/favorit.png" type="BINDATA" />
+      <include name="IDR_KEYBOARD_IMAGES_FLOWER" file="${input_tools_root_dir}/inputview/images/flower.png" type="BINDATA" />
+      <include name="IDR_KEYBOARD_IMAGES_FULLSIZE" file="${input_tools_root_dir}/inputview/images/regular_size.png" type="BINDATA" />
+      <include name="IDR_KEYBOARD_IMAGES_GLOBE" file="${input_tools_root_dir}/inputview/images/globe.png" type="BINDATA" />
+      <include name="IDR_KEYBOARD_IMAGES_HIDE_KEYBOARD" file="${input_tools_root_dir}/inputview/images/hide.png" type="BINDATA" />
       <include name="IDR_KEYBOARD_IMAGES_KEYBOARD" file="resources/images/keyboard.svg" type="BINDATA" />
-      <include name="IDR_KEYBOARD_IMAGES_LEFT" file="${inputview_dir}/images/left.png" type="BINDATA" />
-      <include name="IDR_KEYBOARD_IMAGES_MENU" file="${inputview_dir}/images/menu.png" type="BINDATA" />
-      <include name="IDR_KEYBOARD_IMAGES_PENCIL" file="${inputview_dir}/images/pencil.png" type="BINDATA" />
-      <include name="IDR_KEYBOARD_IMAGES_RECENT" file="${inputview_dir}/images/recent.png" type="BINDATA" />
-      <include name="IDR_KEYBOARD_IMAGES_RETURN" file="${inputview_dir}/images/enter.png" type="BINDATA" />
-      <include name="IDR_KEYBOARD_IMAGES_RIGHT" file="${inputview_dir}/images/right.png" type="BINDATA" />
-      <include name="IDR_KEYBOARD_IMAGES_SPECIAL_CHARACTERS" file="${inputview_dir}/images/special_characters.png" type="BINDATA" />
-      <include name="IDR_KEYBOARD_IMAGES_SEARCH" file="${inputview_dir}/images/search.png" type="BINDATA" />
-      <include name="IDR_KEYBOARD_IMAGES_SETTINGS" file="${inputview_dir}/images/setting.png" type="BINDATA" />
-      <include name="IDR_KEYBOARD_IMAGES_SHIFT" file="${inputview_dir}/images/shift.png" type="BINDATA" />
-      <include name="IDR_KEYBOARD_IMAGES_SPACE" file="${inputview_dir}/images/space.png" type="BINDATA" />
-      <include name="IDR_KEYBOARD_IMAGES_TAB" file="${inputview_dir}/images/tab.png" type="BINDATA" />
-      <include name="IDR_KEYBOARD_IMAGES_TRIANGLE" file="${inputview_dir}/images/triangle.png" type="BINDATA" />
-      <include name="IDR_KEYBOARD_IMAGES_UP" file="${inputview_dir}/images/up.png" type="BINDATA" />
+      <include name="IDR_KEYBOARD_IMAGES_LEFT" file="${input_tools_root_dir}/inputview/images/left.png" type="BINDATA" />
+      <include name="IDR_KEYBOARD_IMAGES_MENU" file="${input_tools_root_dir}/inputview/images/menu.png" type="BINDATA" />
+      <include name="IDR_KEYBOARD_IMAGES_PENCIL" file="${input_tools_root_dir}/inputview/images/pencil.png" type="BINDATA" />
+      <include name="IDR_KEYBOARD_IMAGES_RECENT" file="${input_tools_root_dir}/inputview/images/recent.png" type="BINDATA" />
+      <include name="IDR_KEYBOARD_IMAGES_RETURN" file="${input_tools_root_dir}/inputview/images/enter.png" type="BINDATA" />
+      <include name="IDR_KEYBOARD_IMAGES_RIGHT" file="${input_tools_root_dir}/inputview/images/right.png" type="BINDATA" />
+      <include name="IDR_KEYBOARD_IMAGES_SEARCH" file="${input_tools_root_dir}/inputview/images/search.png" type="BINDATA" />
+      <include name="IDR_KEYBOARD_IMAGES_SETTINGS" file="${input_tools_root_dir}/inputview/images/setting.png" type="BINDATA" />
+      <include name="IDR_KEYBOARD_IMAGES_SHIFT" file="${input_tools_root_dir}/inputview/images/shift.png" type="BINDATA" />
+      <include name="IDR_KEYBOARD_IMAGES_SPACE" file="${input_tools_root_dir}/inputview/images/space.png" type="BINDATA" />
+      <include name="IDR_KEYBOARD_IMAGES_TAB" file="${input_tools_root_dir}/inputview/images/tab.png" type="BINDATA" />
+      <include name="IDR_KEYBOARD_IMAGES_TRIANGLE" file="${input_tools_root_dir}/inputview/images/triangle.png" type="BINDATA" />
+      <include name="IDR_KEYBOARD_IMAGES_UP" file="${input_tools_root_dir}/inputview/images/up.png" type="BINDATA" />
       <include name="IDR_KEYBOARD_INDEX" file="resources/index.html" type="BINDATA" />
       <include name="IDR_KEYBOARD_INPUTVIEW_ADAPTER" file="resources/inputview_adapter.js" type="BINDATA" />
-      <include name="IDR_KEYBOARD_INPUTVIEW_CSS" file="${inputview_dir}/common.css" type="BINDATA" />
+      <include name="IDR_KEYBOARD_INPUTVIEW_CSS" file="${input_tools_root_dir}/inputview/common.css" type="BINDATA" />
       <include name="IDR_KEYBOARD_INPUTVIEW_JS" file="${inputview_gen_js}" use_base_dir="false" type="BINDATA" />
       <include name="IDR_KEYBOARD_MOJO_JS" file="resources/keyboard_mojo.js" flattenhtml="true" type="BINDATA" />
       <include name="IDR_KEYBOARD_MOJO_GEN_JS" file="${keyboard_mojom_gen_js}" use_base_dir="false" type="BINDATA" />
       <include name="IDR_KEYBOARD_LOCALES_EN" file="resources/locales/en.js" flattenhtml="true" type="BINDATA" />
-      <include name="IDR_KEYBOARD_LAYOUTS_101" file="${inputview_dir}/layouts/101kbd_layout.js" type="BINDATA"/>
-      <include name="IDR_KEYBOARD_LAYOUTS_COMPACT_QWERTY" file="${inputview_dir}/layouts/compactkbd_qwerty_layout.js" type="BINDATA" />
-      <include name="IDR_KEYBOARD_LAYOUTS_COMPACT_NUMBERPAD" file="${inputview_dir}/layouts/compactkbd_numberpad_layout.js" type="BINDATA" />
-      <include name="IDR_KEYBOARD_LAYOUTS_EMOJI" file="${inputview_dir}/layouts/emoji_layout.js" type="BINDATA" />
-      <include name="IDR_KEYBOARD_LAYOUTS_HWT" file="${inputview_dir}/layouts/handwriting_layout.js" type="BINDATA" />
+      <include name="IDR_KEYBOARD_LAYOUTS_101" file="${input_tools_root_dir}/inputview/layouts/101kbd_layout.js" type="BINDATA"/>
+      <include name="IDR_KEYBOARD_LAYOUTS_COMPACT_QWERTY" file="${input_tools_root_dir}/inputview/layouts/compactkbd_qwerty_layout.js" type="BINDATA" />
+      <include name="IDR_KEYBOARD_LAYOUTS_COMPACT_NUMBERPAD" file="${input_tools_root_dir}/inputview/layouts/compactkbd_numberpad_layout.js" type="BINDATA" />
+      <include name="IDR_KEYBOARD_LAYOUTS_EMOJI" file="${input_tools_root_dir}/inputview/layouts/emoji_layout.js" type="BINDATA" />
+      <include name="IDR_KEYBOARD_LAYOUTS_HWT" file="${input_tools_root_dir}/inputview/layouts/handwriting_layout.js" type="BINDATA" />
       <include name="IDR_KEYBOARD_MANIFEST" file="resources/manifest.json" type="BINDATA" />
-      <include name="IDR_KEYBOARD_SOUNDS_KEYPRESS_DELETE" file="${inputview_dir}/sounds/keypress-delete.wav" type="BINDATA" />
-      <include name="IDR_KEYBOARD_SOUNDS_KEYPRESS_RETURN" file="${inputview_dir}/sounds/keypress-return.wav" type="BINDATA" />
-      <include name="IDR_KEYBOARD_SOUNDS_KEYPRESS_SPACEBAR" file="${inputview_dir}/sounds/keypress-spacebar.wav" type="BINDATA" />
-      <include name="IDR_KEYBOARD_SOUNDS_KEYPRESS_STANDARD" file="${inputview_dir}/sounds/keypress-standard.wav" type="BINDATA" />
+      <include name="IDR_KEYBOARD_SOUNDS_KEYPRESS_DELETE" file="${input_tools_root_dir}/sounds/keypress-delete.wav" type="BINDATA" />
+      <include name="IDR_KEYBOARD_SOUNDS_KEYPRESS_RETURN" file="${input_tools_root_dir}/sounds/keypress-return.wav" type="BINDATA" />
+      <include name="IDR_KEYBOARD_SOUNDS_KEYPRESS_SPACEBAR" file="${input_tools_root_dir}/sounds/keypress-spacebar.wav" type="BINDATA" />
+      <include name="IDR_KEYBOARD_SOUNDS_KEYPRESS_STANDARD" file="${input_tools_root_dir}/sounds/keypress-standard.wav" type="BINDATA" />
     </includes>
   </release>
 </grit>
diff --git a/ui/keyboard/keyboard_util.cc b/ui/keyboard/keyboard_util.cc
index 3c126c3e..6ffeb34 100644
--- a/ui/keyboard/keyboard_util.cc
+++ b/ui/keyboard/keyboard_util.cc
@@ -346,8 +346,6 @@
       {"keyboard/images/setting.png", IDR_KEYBOARD_IMAGES_SETTINGS},
       {"keyboard/images/shift.png", IDR_KEYBOARD_IMAGES_SHIFT},
       {"keyboard/images/space.png", IDR_KEYBOARD_IMAGES_SPACE},
-      {"keyboard/images/special_characters.png",
-       IDR_KEYBOARD_IMAGES_SPECIAL_CHARACTERS},
       {"keyboard/images/tab.png", IDR_KEYBOARD_IMAGES_TAB},
       {"keyboard/images/triangle.png", IDR_KEYBOARD_IMAGES_TRIANGLE},
       {"keyboard/images/up.png", IDR_KEYBOARD_IMAGES_UP},
diff --git a/ui/login/account_picker/screen_account_picker.js b/ui/login/account_picker/screen_account_picker.js
index 41c6a0c..61e6f54 100644
--- a/ui/login/account_picker/screen_account_picker.js
+++ b/ui/login/account_picker/screen_account_picker.js
@@ -290,6 +290,7 @@
      * @param {string} username Username of pod to add button
      * @param {!{id: !string,
      *           hardlockOnClick: boolean,
+     *           isTrialRun: boolean,
      *           tooltip: ({text: string, autoshow: boolean} | undefined)}} icon
      *     The icon parameters.
      */
diff --git a/ui/login/account_picker/user_pod_row.js b/ui/login/account_picker/user_pod_row.js
index 7bc4538..8e1da8d 100644
--- a/ui/login/account_picker/user_pod_row.js
+++ b/ui/login/account_picker/user_pod_row.js
@@ -2427,6 +2427,7 @@
      * @param {string} username Username of pod to add button
      * @param {!{id: !string,
      *           hardlockOnClick: boolean,
+     *           isTrialRun: boolean,
      *           ariaLabel: string | undefined,
      *           tooltip: ({text: string, autoshow: boolean} | undefined)}} icon
      *     The icon parameters.
@@ -2445,7 +2446,10 @@
       if (icon.id)
         pod.customIconElement.setIcon(icon.id);
 
-      if (icon.hardlockOnClick) {
+      if (icon.isTrialRun) {
+        pod.customIconElement.setInteractive(
+            this.onDidClickLockIconDuringTrialRun_.bind(this, username));
+      } else if (icon.hardlockOnClick) {
         pod.customIconElement.setInteractive(
             this.hardlockUserPod_.bind(this, username));
       } else {
@@ -2478,6 +2482,16 @@
     },
 
     /**
+     * Records a metric indicating that the user clicked on the lock icon during
+     * the trial run for Easy Unlock.
+     * @param {!string} username The user's username.
+     * @private
+     */
+    onDidClickLockIconDuringTrialRun_: function(username) {
+      chrome.send('recordClickOnLockIcon', [username]);
+    },
+
+    /**
      * Hides the custom icon in the user pod added by showUserPodCustomIcon().
      * @param {string} username Username of pod to remove button
      */
diff --git a/ui/login/display_manager.js b/ui/login/display_manager.js
index f73a13f..d7e45cc 100644
--- a/ui/login/display_manager.js
+++ b/ui/login/display_manager.js
@@ -30,6 +30,7 @@
 /** @const */ var SCREEN_KIOSK_ENABLE = 'kiosk-enable';
 /** @const */ var SCREEN_TERMS_OF_SERVICE = 'terms-of-service';
 /** @const */ var SCREEN_WRONG_HWID = 'wrong-hwid';
+/** @const */ var SCREEN_DEVICE_DISABLED = 'device-disabled';
 
 /* Accelerator identifiers. Must be kept in sync with webui_login_view.cc. */
 /** @const */ var ACCELERATOR_CANCEL = 'cancel';
@@ -626,6 +627,10 @@
      * @param {Object} screen Screen params dict, e.g. {id: screenId, data: {}}.
      */
     showScreen: function(screen) {
+      // Do not allow any other screen to clobber the device disabled screen.
+      if (this.currentScreen.id == SCREEN_DEVICE_DISABLED)
+        return;
+
       var screenId = screen.id;
 
       // Make sure the screen is decorated.
diff --git a/ui/login/screen_container.css b/ui/login/screen_container.css
index c19983d3..dcecc38 100644
--- a/ui/login/screen_container.css
+++ b/ui/login/screen_container.css
@@ -105,6 +105,17 @@
   top: 15px;
 }
 
+#close-button-item {
+  -webkit-margin-start: 17px;
+  display: flex;
+  position: absolute;
+  right: 15px;
+  top: 15px;
+  width: 30px;
+  height: 30px;
+  z-index: 1;
+}
+
 #progress-dots {
   -webkit-box-pack: center;
   -webkit-transition: opacity 200ms ease-in-out,
diff --git a/ui/message_center/BUILD.gn b/ui/message_center/BUILD.gn
index c6482de..b295019 100644
--- a/ui/message_center/BUILD.gn
+++ b/ui/message_center/BUILD.gn
@@ -23,6 +23,9 @@
     "//url",
   ]
 
+  # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+  configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
+
   defines = [ "MESSAGE_CENTER_IMPLEMENTATION" ]
 
   if (enable_notifications && !is_android) {
@@ -74,8 +77,6 @@
     ]
 
     if (is_win) {
-      # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
-      cflags = [ "/wd4267" ]
       deps += [ "//ui/aura" ]
     }
 
diff --git a/ui/message_center/message_center_tray.cc b/ui/message_center/message_center_tray.cc
index a4fbb08..1ce4e8e9 100644
--- a/ui/message_center/message_center_tray.cc
+++ b/ui/message_center/message_center_tray.cc
@@ -198,8 +198,8 @@
 scoped_ptr<ui::MenuModel> MessageCenterTray::CreateNotificationMenuModel(
     const NotifierId& notifier_id,
     const base::string16& display_source) {
-  return scoped_ptr<ui::MenuModel>(new NotificationMenuModel(
-      this, notifier_id, display_source));
+  return make_scoped_ptr(
+      new NotificationMenuModel(this, notifier_id, display_source));
 }
 
 void MessageCenterTray::OnNotificationAdded(
diff --git a/ui/message_center/views/message_popup_collection_unittest.cc b/ui/message_center/views/message_popup_collection_unittest.cc
index a1e5594..466181b 100644
--- a/ui/message_center/views/message_popup_collection_unittest.cc
+++ b/ui/message_center/views/message_popup_collection_unittest.cc
@@ -12,6 +12,7 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/events/event.h"
 #include "ui/events/event_constants.h"
+#include "ui/events/event_utils.h"
 #include "ui/gfx/display.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/message_center/fake_message_center.h"
@@ -368,7 +369,8 @@
   views::WidgetDelegateView* toast1 = GetToast(id1);
   EXPECT_TRUE(toast1 != NULL);
 
-  ui::MouseEvent event(ui::ET_MOUSE_MOVED, gfx::Point(), gfx::Point(), 0, 0);
+  ui::MouseEvent event(ui::ET_MOUSE_MOVED, gfx::Point(), gfx::Point(),
+                       ui::EventTimeForNow(), 0, 0);
 
   // Test that mouse detection logic works in presence of out-of-order events.
   toast0->OnMouseEntered(event);
@@ -403,7 +405,8 @@
   views::WidgetDelegateView* toast1 = GetToast(id1);
   ASSERT_TRUE(toast1 != NULL);
 
-  ui::MouseEvent event(ui::ET_MOUSE_MOVED, gfx::Point(), gfx::Point(), 0, 0);
+  ui::MouseEvent event(ui::ET_MOUSE_MOVED, gfx::Point(), gfx::Point(),
+                       ui::EventTimeForNow(), 0, 0);
   toast1->OnMouseEntered(event);
   static_cast<MessageCenterObserver*>(collection())->OnNotificationRemoved(
       id1, true);
diff --git a/ui/message_center/views/notification_view_unittest.cc b/ui/message_center/views/notification_view_unittest.cc
index d4c155b..5fdc60f 100644
--- a/ui/message_center/views/notification_view_unittest.cc
+++ b/ui/message_center/views/notification_view_unittest.cc
@@ -8,6 +8,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/skia/include/core/SkBitmap.h"
+#include "ui/events/event_utils.h"
 #include "ui/gfx/image/image.h"
 #include "ui/message_center/notification.h"
 #include "ui/message_center/notification_list.h"
@@ -237,11 +238,8 @@
   gfx::Point cursor_location(1, 1);
   views::View::ConvertPointToWidget(notification_view()->action_buttons_[0],
                                     &cursor_location);
-  ui::MouseEvent move(ui::ET_MOUSE_MOVED,
-                      cursor_location,
-                      cursor_location,
-                      ui::EF_NONE,
-                      ui::EF_NONE);
+  ui::MouseEvent move(ui::ET_MOUSE_MOVED, cursor_location, cursor_location,
+                      ui::EventTimeForNow(), ui::EF_NONE, ui::EF_NONE);
   widget()->OnMouseEvent(&move);
 
   EXPECT_EQ(views::CustomButton::STATE_HOVERED,
@@ -255,11 +253,8 @@
   // Now construct a mouse move event 1 pixel outside the boundary of the
   // widget.
   cursor_location = gfx::Point(-1, -1);
-  move = ui::MouseEvent(ui::ET_MOUSE_MOVED,
-                        cursor_location,
-                        cursor_location,
-                        ui::EF_NONE,
-                        ui::EF_NONE);
+  move = ui::MouseEvent(ui::ET_MOUSE_MOVED, cursor_location, cursor_location,
+                        ui::EventTimeForNow(), ui::EF_NONE, ui::EF_NONE);
   widget()->OnMouseEvent(&move);
 
   EXPECT_EQ(views::CustomButton::STATE_NORMAL,
@@ -281,11 +276,8 @@
   gfx::Point cursor_location(1, 1);
   views::View::ConvertPointToWidget(notification_view()->action_buttons_[0],
                                     &cursor_location);
-  ui::MouseEvent move(ui::ET_MOUSE_MOVED,
-                      cursor_location,
-                      cursor_location,
-                      ui::EF_NONE,
-                      ui::EF_NONE);
+  ui::MouseEvent move(ui::ET_MOUSE_MOVED, cursor_location, cursor_location,
+                      ui::EventTimeForNow(), ui::EF_NONE, ui::EF_NONE);
   widget()->OnMouseEvent(&move);
 
   EXPECT_EQ(views::CustomButton::STATE_HOVERED,
@@ -303,11 +295,8 @@
   // Now construct a mouse move event 1 pixel outside the boundary of the
   // widget.
   cursor_location = gfx::Point(-1, -1);
-  move = ui::MouseEvent(ui::ET_MOUSE_MOVED,
-                        cursor_location,
-                        cursor_location,
-                        ui::EF_NONE,
-                        ui::EF_NONE);
+  move = ui::MouseEvent(ui::ET_MOUSE_MOVED, cursor_location, cursor_location,
+                        ui::EventTimeForNow(), ui::EF_NONE, ui::EF_NONE);
   widget()->OnMouseEvent(&move);
 
   EXPECT_EQ(views::CustomButton::STATE_NORMAL,
diff --git a/ui/message_center/views/notifier_settings_view.cc b/ui/message_center/views/notifier_settings_view.cc
index 21f3fdf..0ffc8fc 100644
--- a/ui/message_center/views/notifier_settings_view.cc
+++ b/ui/message_center/views/notifier_settings_view.cc
@@ -14,6 +14,7 @@
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/models/simple_menu_model.h"
 #include "ui/base/resource/resource_bundle.h"
+#include "ui/events/event_utils.h"
 #include "ui/events/keycodes/keyboard_codes.h"
 #include "ui/gfx/canvas.h"
 #include "ui/gfx/geometry/size.h"
@@ -371,9 +372,9 @@
   if (learn_more_ == NULL)
     return;
   gfx::Point point(110, 120);
-  ui::MouseEvent pressed(
-      ui::ET_MOUSE_PRESSED, point, point, ui::EF_LEFT_MOUSE_BUTTON,
-      ui::EF_LEFT_MOUSE_BUTTON);
+  ui::MouseEvent pressed(ui::ET_MOUSE_PRESSED, point, point,
+                         ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
+                         ui::EF_LEFT_MOUSE_BUTTON);
   ButtonPressed(learn_more_, pressed);
 }
 
diff --git a/ui/native_theme/common_theme.cc b/ui/native_theme/common_theme.cc
index 4e4221a8..a7a4c5a 100644
--- a/ui/native_theme/common_theme.cc
+++ b/ui/native_theme/common_theme.cc
@@ -207,7 +207,7 @@
   // scale factor from canvas scale.
   SkMatrix m = sk_canvas->getTotalMatrix();
   float device_scale = static_cast<float>(SkScalarAbs(m.getScaleX()));
-  return scoped_ptr<gfx::Canvas>(
+  return make_scoped_ptr(
       gfx::Canvas::CreateCanvasWithoutScaling(sk_canvas, device_scale));
 }
 
diff --git a/ui/ozone/BUILD.gn b/ui/ozone/BUILD.gn
index 852bef6..8a55211 100644
--- a/ui/ozone/BUILD.gn
+++ b/ui/ozone/BUILD.gn
@@ -96,12 +96,12 @@
     "platform_selection.h",
     "public/input_controller.cc",
     "public/input_controller.h",
+    "public/ozone_gpu_thread_helper.cc",
+    "public/ozone_gpu_thread_helper.h",
     "public/ozone_platform.cc",
     "public/ozone_platform.h",
     "public/ozone_switches.cc",
     "public/ozone_switches.h",
-    "public/ui_thread_gpu.cc",
-    "public/ui_thread_gpu.h",
   ]
 
   defines = [ "OZONE_IMPLEMENTATION" ]
diff --git a/ui/ozone/common/gpu/ozone_gpu_messages.h b/ui/ozone/common/gpu/ozone_gpu_messages.h
index 667782ee..6ff12603 100644
--- a/ui/ozone/common/gpu/ozone_gpu_messages.h
+++ b/ui/ozone/common/gpu/ozone_gpu_messages.h
@@ -7,6 +7,7 @@
 
 #include <vector>
 
+#include "base/file_descriptor_posix.h"
 #include "ipc/ipc_message_macros.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 #include "ui/gfx/geometry/point.h"
@@ -91,8 +92,9 @@
 IPC_MESSAGE_CONTROL1(OzoneGpuMsg_DisableNativeDisplay,
                      int64_t)  // display ID
 
-IPC_MESSAGE_CONTROL1(OzoneGpuMsg_AddGraphicsDevice,
-                     base::FilePath /* device_path */)
+IPC_MESSAGE_CONTROL2(OzoneGpuMsg_AddGraphicsDevice,
+                     base::FilePath /* device_path */,
+                     base::FileDescriptor /* device_fd */)
 
 IPC_MESSAGE_CONTROL1(OzoneGpuMsg_RemoveGraphicsDevice,
                      base::FilePath /* device_path */)
diff --git a/ui/ozone/demo/ozone_demo.cc b/ui/ozone/demo/ozone_demo.cc
index 4b44c255..616bd9f 100644
--- a/ui/ozone/demo/ozone_demo.cc
+++ b/ui/ozone/demo/ozone_demo.cc
@@ -23,9 +23,9 @@
 #include "ui/ozone/demo/software_renderer.h"
 #include "ui/ozone/demo/surfaceless_gl_renderer.h"
 #include "ui/ozone/gpu/gpu_memory_buffer_factory_ozone_native_buffer.h"
+#include "ui/ozone/public/ozone_gpu_test_helper.h"
 #include "ui/ozone/public/ozone_platform.h"
 #include "ui/ozone/public/ozone_switches.h"
-#include "ui/ozone/public/ui_thread_gpu.h"
 #include "ui/platform_window/platform_window.h"
 #include "ui/platform_window/platform_window_delegate.h"
 
@@ -59,7 +59,7 @@
   RendererType type_;
 
   // Helper for applications that do GL on main thread.
-  ui::UiThreadGpu ui_thread_gpu_;
+  ui::OzoneGpuTestHelper gpu_helper_;
 
   // Used by the surfaceless renderers to allocate buffers.
   ui::GpuMemoryBufferFactoryOzoneNativeBuffer buffer_factory_;
@@ -199,7 +199,9 @@
 bool RendererFactory::Initialize() {
   base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
   if (!command_line->HasSwitch(kDisableGpu) &&
-      gfx::GLSurface::InitializeOneOff() && ui_thread_gpu_.Initialize()) {
+      gfx::GLSurface::InitializeOneOff() &&
+      gpu_helper_.Initialize(base::ThreadTaskRunnerHandle::Get(),
+                             base::ThreadTaskRunnerHandle::Get())) {
     if (command_line->HasSwitch(switches::kOzoneUseSurfaceless)) {
       type_ = SURFACELESS_GL;
     } else {
@@ -217,12 +219,12 @@
     const gfx::Size& size) {
   switch (type_) {
     case GL:
-      return scoped_ptr<ui::Renderer>(new ui::GlRenderer(widget, size));
+      return make_scoped_ptr(new ui::GlRenderer(widget, size));
     case SURFACELESS_GL:
-      return scoped_ptr<ui::Renderer>(
+      return make_scoped_ptr(
           new ui::SurfacelessGlRenderer(widget, size, &buffer_factory_));
     case SOFTWARE:
-      return scoped_ptr<ui::Renderer>(new ui::SoftwareRenderer(widget, size));
+      return make_scoped_ptr(new ui::SoftwareRenderer(widget, size));
   }
 
   return nullptr;
diff --git a/ui/ozone/ozone.gyp b/ui/ozone/ozone.gyp
index 40fb78e..d9294dd3 100644
--- a/ui/ozone/ozone.gyp
+++ b/ui/ozone/ozone.gyp
@@ -106,12 +106,12 @@
         'platform_selection.h',
         'public/input_controller.cc',
         'public/input_controller.h',
+        'public/ozone_gpu_test_helper.cc',
+        'public/ozone_gpu_test_helper.h',
         'public/ozone_platform.cc',
         'public/ozone_platform.h',
         'public/ozone_switches.cc',
         'public/ozone_switches.h',
-        'public/ui_thread_gpu.cc',
-        'public/ui_thread_gpu.h',
         '<@(external_ozone_platform_files)',
       ],
       'actions': [
diff --git a/ui/ozone/platform/caca/ozone_platform_caca.cc b/ui/ozone/platform/caca/ozone_platform_caca.cc
index bc11340d..a0186a6 100644
--- a/ui/ozone/platform/caca/ozone_platform_caca.cc
+++ b/ui/ozone/platform/caca/ozone_platform_caca.cc
@@ -55,7 +55,7 @@
     return caca_window.Pass();
   }
   scoped_ptr<NativeDisplayDelegate> CreateNativeDisplayDelegate() override {
-    return scoped_ptr<NativeDisplayDelegate>(new NativeDisplayDelegateOzone());
+    return make_scoped_ptr(new NativeDisplayDelegateOzone());
   }
 
   void InitializeUI() override {
diff --git a/ui/ozone/platform/dri/BUILD.gn b/ui/ozone/platform/dri/BUILD.gn
index 1b00cc9..d796a605 100644
--- a/ui/ozone/platform/dri/BUILD.gn
+++ b/ui/ozone/platform/dri/BUILD.gn
@@ -54,6 +54,8 @@
     "dri_window_manager.h",
     "dri_wrapper.cc",
     "dri_wrapper.h",
+    "drm_device_generator.cc",
+    "drm_device_generator.h",
     "drm_device_manager.cc",
     "drm_device_manager.h",
     "hardware_display_controller.cc",
diff --git a/ui/ozone/platform/dri/dri.gypi b/ui/ozone/platform/dri/dri.gypi
index 452c3b9..77795af 100644
--- a/ui/ozone/platform/dri/dri.gypi
+++ b/ui/ozone/platform/dri/dri.gypi
@@ -76,6 +76,8 @@
         'dri_window_manager.h',
         'dri_wrapper.cc',
         'dri_wrapper.h',
+        'drm_device_generator.cc',
+        'drm_device_generator.h',
         'drm_device_manager.cc',
         'drm_device_manager.h',
         'hardware_display_controller.cc',
diff --git a/ui/ozone/platform/dri/dri_cursor.cc b/ui/ozone/platform/dri/dri_cursor.cc
index 021a034d5..e415cf5 100644
--- a/ui/ozone/platform/dri/dri_cursor.cc
+++ b/ui/ozone/platform/dri/dri_cursor.cc
@@ -166,6 +166,8 @@
 
   SetCursorLocationLocked(screen_location -
                           state_.display_bounds_in_screen.OffsetFromOrigin());
+
+  SendCursorMoveLocked();
 }
 
 void DriCursor::MoveCursor(const gfx::Vector2dF& delta) {
diff --git a/ui/ozone/platform/dri/dri_gpu_platform_support.cc b/ui/ozone/platform/dri/dri_gpu_platform_support.cc
index 3a6724f..cbe4b3af 100644
--- a/ui/ozone/platform/dri/dri_gpu_platform_support.cc
+++ b/ui/ozone/platform/dri/dri_gpu_platform_support.cc
@@ -229,8 +229,8 @@
 
 void DriGpuPlatformSupport::OnCreateWindowDelegate(
     gfx::AcceleratedWidget widget) {
-  scoped_ptr<DriWindowDelegate> delegate(new DriWindowDelegateImpl(
-      widget, drm_device_manager_, window_manager_, screen_manager_));
+  scoped_ptr<DriWindowDelegate> delegate(
+      new DriWindowDelegateImpl(widget, drm_device_manager_, screen_manager_));
   delegate->Initialize();
   window_manager_->AddWindowDelegate(widget, delegate.Pass());
 }
@@ -289,8 +289,10 @@
   ndd_->RelinquishDisplayControl();
 }
 
-void DriGpuPlatformSupport::OnAddGraphicsDevice(const base::FilePath& path) {
-  ndd_->AddGraphicsDevice(path);
+void DriGpuPlatformSupport::OnAddGraphicsDevice(
+    const base::FilePath& path,
+    const base::FileDescriptor& fd) {
+  ndd_->AddGraphicsDevice(path, fd);
 }
 
 void DriGpuPlatformSupport::OnRemoveGraphicsDevice(const base::FilePath& path) {
diff --git a/ui/ozone/platform/dri/dri_gpu_platform_support.h b/ui/ozone/platform/dri/dri_gpu_platform_support.h
index 0858fdc..4e082aa 100644
--- a/ui/ozone/platform/dri/dri_gpu_platform_support.h
+++ b/ui/ozone/platform/dri/dri_gpu_platform_support.h
@@ -17,6 +17,7 @@
 namespace base {
 class FilePath;
 class SingleThreadTaskRunner;
+struct FileDescriptor;
 }
 
 namespace gfx {
@@ -74,7 +75,8 @@
   void OnDisableNativeDisplay(int64_t id);
   void OnTakeDisplayControl();
   void OnRelinquishDisplayControl();
-  void OnAddGraphicsDevice(const base::FilePath& path);
+  void OnAddGraphicsDevice(const base::FilePath& path,
+                           const base::FileDescriptor& fd);
   void OnRemoveGraphicsDevice(const base::FilePath& path);
 
   void SetIOTaskRunner(
diff --git a/ui/ozone/platform/dri/dri_surface.cc b/ui/ozone/platform/dri/dri_surface.cc
index 92685ff02..406014b 100644
--- a/ui/ozone/platform/dri/dri_surface.cc
+++ b/ui/ozone/platform/dri/dri_surface.cc
@@ -79,7 +79,7 @@
 }
 
 scoped_ptr<gfx::VSyncProvider> DriSurface::CreateVSyncProvider() {
-  return scoped_ptr<gfx::VSyncProvider>(new DriVSyncProvider(window_delegate_));
+  return make_scoped_ptr(new DriVSyncProvider(window_delegate_));
 }
 
 void DriSurface::UpdateNativeSurface(const gfx::Rect& damage) {
diff --git a/ui/ozone/platform/dri/dri_surface_factory.cc b/ui/ozone/platform/dri/dri_surface_factory.cc
index 8001474c..c3713702 100644
--- a/ui/ozone/platform/dri/dri_surface_factory.cc
+++ b/ui/ozone/platform/dri/dri_surface_factory.cc
@@ -25,7 +25,7 @@
 
 scoped_ptr<ui::SurfaceOzoneCanvas> DriSurfaceFactory::CreateCanvasForWidget(
     gfx::AcceleratedWidget widget) {
-  return scoped_ptr<ui::SurfaceOzoneCanvas>(
+  return make_scoped_ptr(
       new DriSurface(window_manager_->GetWindowDelegate(widget)));
 }
 
diff --git a/ui/ozone/platform/dri/dri_window_delegate_impl.cc b/ui/ozone/platform/dri/dri_window_delegate_impl.cc
index 8eca58e..0ebd524b 100644
--- a/ui/ozone/platform/dri/dri_window_delegate_impl.cc
+++ b/ui/ozone/platform/dri/dri_window_delegate_impl.cc
@@ -9,7 +9,6 @@
 #include "third_party/skia/include/core/SkDevice.h"
 #include "third_party/skia/include/core/SkSurface.h"
 #include "ui/ozone/platform/dri/dri_buffer.h"
-#include "ui/ozone/platform/dri/dri_window_delegate_manager.h"
 #include "ui/ozone/platform/dri/dri_wrapper.h"
 #include "ui/ozone/platform/dri/drm_device_manager.h"
 #include "ui/ozone/platform/dri/screen_manager.h"
@@ -46,11 +45,9 @@
 DriWindowDelegateImpl::DriWindowDelegateImpl(
     gfx::AcceleratedWidget widget,
     DrmDeviceManager* device_manager,
-    DriWindowDelegateManager* window_manager,
     ScreenManager* screen_manager)
     : widget_(widget),
       device_manager_(device_manager),
-      window_manager_(window_manager),
       screen_manager_(screen_manager),
       controller_(NULL),
       cursor_frontbuffer_(0),
diff --git a/ui/ozone/platform/dri/dri_window_delegate_impl.h b/ui/ozone/platform/dri/dri_window_delegate_impl.h
index cfbcff8..0e1136b12 100644
--- a/ui/ozone/platform/dri/dri_window_delegate_impl.h
+++ b/ui/ozone/platform/dri/dri_window_delegate_impl.h
@@ -16,7 +16,6 @@
 namespace ui {
 
 class DriBuffer;
-class DriWindowDelegateManager;
 class DrmDeviceManager;
 class HardwareDisplayController;
 class ScreenManager;
@@ -26,7 +25,6 @@
  public:
   DriWindowDelegateImpl(gfx::AcceleratedWidget widget,
                         DrmDeviceManager* device_manager,
-                        DriWindowDelegateManager* window_manager,
                         ScreenManager* screen_manager);
   ~DriWindowDelegateImpl() override;
 
@@ -59,7 +57,6 @@
   gfx::AcceleratedWidget widget_;
 
   DrmDeviceManager* device_manager_;          // Not owned.
-  DriWindowDelegateManager* window_manager_;  // Not owned.
   ScreenManager* screen_manager_;             // Not owned.
 
   // The current bounds of the window.
diff --git a/ui/ozone/platform/dri/dri_window_delegate_impl_unittest.cc b/ui/ozone/platform/dri/dri_window_delegate_impl_unittest.cc
index 32d52af..f6c5570 100644
--- a/ui/ozone/platform/dri/dri_window_delegate_impl_unittest.cc
+++ b/ui/ozone/platform/dri/dri_window_delegate_impl_unittest.cc
@@ -66,9 +66,9 @@
   window_delegate_manager_.reset(new ui::DriWindowDelegateManager());
 
   scoped_ptr<ui::DriWindowDelegate> window_delegate(
-      new ui::DriWindowDelegateImpl(
-          kDefaultWidgetHandle, drm_device_manager_.get(),
-          window_delegate_manager_.get(), screen_manager_.get()));
+      new ui::DriWindowDelegateImpl(kDefaultWidgetHandle,
+                                    drm_device_manager_.get(),
+                                    screen_manager_.get()));
   window_delegate->Initialize();
   window_delegate_manager_->AddWindowDelegate(kDefaultWidgetHandle,
                                               window_delegate.Pass());
diff --git a/ui/ozone/platform/dri/drm_device_generator.cc b/ui/ozone/platform/dri/drm_device_generator.cc
new file mode 100644
index 0000000..7b4a568
--- /dev/null
+++ b/ui/ozone/platform/dri/drm_device_generator.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 "ui/ozone/platform/dri/drm_device_generator.h"
+
+#include "ui/ozone/platform/dri/dri_wrapper.h"
+
+namespace ui {
+
+DrmDeviceGenerator::DrmDeviceGenerator() {
+}
+
+DrmDeviceGenerator::~DrmDeviceGenerator() {
+}
+
+scoped_refptr<DriWrapper> DrmDeviceGenerator::CreateDevice(
+    const base::FilePath& device_path,
+    base::File file) {
+  scoped_refptr<DriWrapper> drm = new DriWrapper(device_path, file.Pass());
+  drm->Initialize();
+  return drm;
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/dri/drm_device_generator.h b/ui/ozone/platform/dri/drm_device_generator.h
new file mode 100644
index 0000000..acd25c02
--- /dev/null
+++ b/ui/ozone/platform/dri/drm_device_generator.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 UI_OZONE_PLATFORM_DRI_DRM_DEVICE_GENERATOR_H_
+#define UI_OZONE_PLATFORM_DRI_DRM_DEVICE_GENERATOR_H_
+
+#include "base/files/file.h"
+#include "base/memory/ref_counted.h"
+
+namespace ui {
+
+class DriWrapper;
+
+class DrmDeviceGenerator {
+ public:
+  DrmDeviceGenerator();
+  virtual ~DrmDeviceGenerator();
+
+  // Creates a DRM device for |file|. |device_path| describes the location of
+  // the DRM device.
+  virtual scoped_refptr<DriWrapper> CreateDevice(
+      const base::FilePath& device_path,
+      base::File file);
+
+ public:
+  DISALLOW_COPY_AND_ASSIGN(DrmDeviceGenerator);
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_DRI_DRM_DEVICE_GENERATOR_H_
diff --git a/ui/ozone/platform/dri/gbm_buffer.cc b/ui/ozone/platform/dri/gbm_buffer.cc
index ec25bd6..371e9ab 100644
--- a/ui/ozone/platform/dri/gbm_buffer.cc
+++ b/ui/ozone/platform/dri/gbm_buffer.cc
@@ -10,6 +10,7 @@
 #include <xf86drm.h>
 
 #include "base/logging.h"
+#include "base/trace_event/trace_event.h"
 #include "ui/ozone/platform/dri/gbm_wrapper.h"
 
 namespace ui {
@@ -47,6 +48,8 @@
     SurfaceFactoryOzone::BufferFormat format,
     const gfx::Size& size,
     bool scanout) {
+  TRACE_EVENT2("dri", "GbmBuffer::CreateBuffer", "device",
+               gbm->device_path().value(), "size", size.ToString());
   unsigned flags = GBM_BO_USE_RENDERING;
   if (scanout)
     flags |= GBM_BO_USE_SCANOUT;
diff --git a/ui/ozone/platform/dri/gbm_surface_factory.cc b/ui/ozone/platform/dri/gbm_surface_factory.cc
index 60e9010e..cc7260f 100644
--- a/ui/ozone/platform/dri/gbm_surface_factory.cc
+++ b/ui/ozone/platform/dri/gbm_surface_factory.cc
@@ -138,7 +138,7 @@
   if (!allow_surfaceless_)
     return nullptr;
 
-  return scoped_ptr<SurfaceOzoneEGL>(
+  return make_scoped_ptr(
       new GbmSurfaceless(window_manager_->GetWindowDelegate(widget)));
 }
 
diff --git a/ui/ozone/platform/dri/gbm_surfaceless.cc b/ui/ozone/platform/dri/gbm_surfaceless.cc
index 27dfff9..efa09797 100644
--- a/ui/ozone/platform/dri/gbm_surfaceless.cc
+++ b/ui/ozone/platform/dri/gbm_surfaceless.cc
@@ -43,7 +43,7 @@
 }
 
 scoped_ptr<gfx::VSyncProvider> GbmSurfaceless::CreateVSyncProvider() {
-  return scoped_ptr<gfx::VSyncProvider>(new DriVSyncProvider(window_delegate_));
+  return make_scoped_ptr(new DriVSyncProvider(window_delegate_));
 }
 
 }  // namespace ui
diff --git a/ui/ozone/platform/dri/gbm_wrapper.h b/ui/ozone/platform/dri/gbm_wrapper.h
index f7e4f3d..41b2b6a 100644
--- a/ui/ozone/platform/dri/gbm_wrapper.h
+++ b/ui/ozone/platform/dri/gbm_wrapper.h
@@ -15,7 +15,6 @@
  public:
   GbmWrapper(const base::FilePath& device_path);
   GbmWrapper(const base::FilePath& device_path, base::File file);
-  ~GbmWrapper() override;
 
   gbm_device* device() const { return device_; }
 
@@ -23,6 +22,8 @@
   void Initialize() override;
 
  private:
+  ~GbmWrapper() override;
+
   gbm_device* device_;
 
   DISALLOW_COPY_AND_ASSIGN(GbmWrapper);
diff --git a/ui/ozone/platform/dri/native_display_delegate_dri.cc b/ui/ozone/platform/dri/native_display_delegate_dri.cc
index ff55ef4a7..b242283 100644
--- a/ui/ozone/platform/dri/native_display_delegate_dri.cc
+++ b/ui/ozone/platform/dri/native_display_delegate_dri.cc
@@ -6,6 +6,8 @@
 
 #include "base/bind.h"
 #include "base/command_line.h"
+#include "base/file_descriptor_posix.h"
+#include "base/files/file.h"
 #include "base/single_thread_task_runner.h"
 #include "ui/display/types/native_display_observer.h"
 #include "ui/events/ozone/device/device_event.h"
@@ -14,6 +16,7 @@
 #include "ui/ozone/platform/dri/display_snapshot_dri.h"
 #include "ui/ozone/platform/dri/dri_util.h"
 #include "ui/ozone/platform/dri/dri_wrapper.h"
+#include "ui/ozone/platform/dri/drm_device_generator.h"
 #include "ui/ozone/platform/dri/screen_manager.h"
 #include "ui/ozone/public/ozone_switches.h"
 
@@ -74,12 +77,26 @@
   uint32_t connector_;
 };
 
+class FindByDevicePath {
+ public:
+  explicit FindByDevicePath(const base::FilePath& path) : path_(path) {}
+
+  bool operator()(const scoped_refptr<DriWrapper>& device) {
+    return device->device_path() == path_;
+  }
+
+ private:
+  base::FilePath path_;
+};
+
 }  // namespace
 
 NativeDisplayDelegateDri::NativeDisplayDelegateDri(
     ScreenManager* screen_manager,
-    const scoped_refptr<DriWrapper>& primary_device)
-    : screen_manager_(screen_manager) {
+    const scoped_refptr<DriWrapper>& primary_device,
+    scoped_ptr<DrmDeviceGenerator> drm_device_generator)
+    : screen_manager_(screen_manager),
+      drm_device_generator_(drm_device_generator.Pass()) {
   devices_.push_back(primary_device);
 }
 
@@ -195,13 +212,36 @@
   return true;
 }
 
-void NativeDisplayDelegateDri::AddGraphicsDevice(const base::FilePath& path) {
-  NOTIMPLEMENTED();
+void NativeDisplayDelegateDri::AddGraphicsDevice(
+    const base::FilePath& path,
+    const base::FileDescriptor& fd) {
+  base::File file(fd.fd);
+  auto it =
+      std::find_if(devices_.begin(), devices_.end(), FindByDevicePath(path));
+  if (it != devices_.end()) {
+    LOG(WARNING) << "Got request to add existing device '" << path.value()
+                 << "'";
+    return;
+  }
+
+  scoped_refptr<DriWrapper> device =
+      drm_device_generator_->CreateDevice(path, file.Pass());
+  devices_.push_back(device);
+  if (io_task_runner_)
+    device->InitializeTaskRunner(io_task_runner_);
 }
 
 void NativeDisplayDelegateDri::RemoveGraphicsDevice(
     const base::FilePath& path) {
-  NOTIMPLEMENTED();
+  auto it =
+      std::find_if(devices_.begin(), devices_.end(), FindByDevicePath(path));
+  if (it == devices_.end()) {
+    LOG(ERROR) << "Got request to remove non-existent device '" << path.value()
+               << "'";
+    return;
+  }
+
+  devices_.erase(it);
 }
 
 DisplaySnapshotDri* NativeDisplayDelegateDri::FindDisplaySnapshot(int64_t id) {
diff --git a/ui/ozone/platform/dri/native_display_delegate_dri.h b/ui/ozone/platform/dri/native_display_delegate_dri.h
index 5ddb07a..da890b0 100644
--- a/ui/ozone/platform/dri/native_display_delegate_dri.h
+++ b/ui/ozone/platform/dri/native_display_delegate_dri.h
@@ -13,6 +13,7 @@
 namespace base {
 class FilePath;
 class SingleThreadTaskRunner;
+struct FileDescriptor;
 }
 
 namespace ui {
@@ -22,12 +23,14 @@
 class DisplayMode;
 class DisplayModeDri;
 class DriWrapper;
+class DrmDeviceGenerator;
 class ScreenManager;
 
 class NativeDisplayDelegateDri {
  public:
   NativeDisplayDelegateDri(ScreenManager* screen_manager,
-                           const scoped_refptr<DriWrapper>& primary_device);
+                           const scoped_refptr<DriWrapper>& primary_device,
+                           scoped_ptr<DrmDeviceGenerator> device_generator);
   ~NativeDisplayDelegateDri();
 
   void InitializeIOTaskRunner(
@@ -49,7 +52,8 @@
   bool RelinquishDisplayControl();
 
   // Called on DRM hotplug events to add/remove a DRM device.
-  void AddGraphicsDevice(const base::FilePath& path);
+  void AddGraphicsDevice(const base::FilePath& path,
+                         const base::FileDescriptor& fd);
   void RemoveGraphicsDevice(const base::FilePath& path);
 
  private:
@@ -73,6 +77,7 @@
       const std::vector<DisplaySnapshotDri*>& old_displays) const;
 
   ScreenManager* screen_manager_;  // Not owned.
+  scoped_ptr<DrmDeviceGenerator> drm_device_generator_;
   scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;
   std::vector<scoped_refptr<DriWrapper>> devices_;
   // Modes can be shared between different displays, so we need to keep track
diff --git a/ui/ozone/platform/dri/native_display_delegate_proxy.cc b/ui/ozone/platform/dri/native_display_delegate_proxy.cc
index 8ad2c40..377daf92 100644
--- a/ui/ozone/platform/dri/native_display_delegate_proxy.cc
+++ b/ui/ozone/platform/dri/native_display_delegate_proxy.cc
@@ -7,7 +7,9 @@
 #include <stdio.h>
 
 #include "base/logging.h"
+#include "base/thread_task_runner_handle.h"
 #include "base/threading/thread_restrictions.h"
+#include "base/threading/worker_pool.h"
 #include "ui/display/types/display_snapshot.h"
 #include "ui/display/types/native_display_observer.h"
 #include "ui/events/ozone/device/device_event.h"
@@ -22,6 +24,30 @@
 
 namespace {
 
+const char kDefaultGraphicsCardPath[] = "/dev/dri/card0";
+
+typedef base::Callback<void(const base::FilePath&, base::File)>
+    OnOpenDeviceReplyCallback;
+
+void OpenDeviceOnWorkerThread(
+    const base::FilePath& path,
+    const scoped_refptr<base::TaskRunner>& reply_runner,
+    const OnOpenDeviceReplyCallback& callback) {
+  base::File file(path, base::File::FLAG_OPEN | base::File::FLAG_READ |
+                            base::File::FLAG_WRITE);
+
+  base::File::Info info;
+  file.GetInfo(&info);
+
+  CHECK(!info.is_directory);
+  CHECK(path.DirName() == base::FilePath("/dev/dri"));
+
+  if (file.IsValid()) {
+    reply_runner->PostTask(
+        FROM_HERE, base::Bind(callback, path, base::Passed(file.Pass())));
+  }
+}
+
 class DriDisplaySnapshotProxy : public DisplaySnapshotProxy {
  public:
   DriDisplaySnapshotProxy(const DisplaySnapshot_Params& params,
@@ -49,7 +75,8 @@
     : proxy_(proxy),
       device_manager_(device_manager),
       display_manager_(display_manager),
-      has_dummy_display_(false) {
+      has_dummy_display_(false),
+      weak_ptr_factory_(this) {
   proxy_->RegisterHandler(this);
 }
 
@@ -195,13 +222,31 @@
   switch (event.action_type()) {
     case DeviceEvent::ADD:
       VLOG(1) << "Got display added event for " << event.path().value();
-      proxy_->Send(new OzoneGpuMsg_AddGraphicsDevice(event.path()));
-      break;
+      // The default card is a special case since it needs to be opened early on
+      // the GPU process in order to initialize EGL. If it is opened here as
+      // well, it will cause a race with opening it in the GPU process and the
+      // GPU process may fail initialization.
+      // TODO(dnicoara) Remove this when EGL_DEFAULT_DISPLAY is the only native
+      // display we return in GbmSurfaceFactory.
+      if (event.path().value() == kDefaultGraphicsCardPath)
+        return;
+
+      base::WorkerPool::PostTask(
+          FROM_HERE,
+          base::Bind(
+              &OpenDeviceOnWorkerThread, event.path(),
+              base::ThreadTaskRunnerHandle::Get(),
+              base::Bind(&NativeDisplayDelegateProxy::OnNewGraphicsDevice,
+                         weak_ptr_factory_.GetWeakPtr())),
+          false /* task_is_slow */);
+      return;
     case DeviceEvent::CHANGE:
       VLOG(1) << "Got display changed event for " << event.path().value();
       break;
     case DeviceEvent::REMOVE:
       VLOG(1) << "Got display removed event for " << event.path().value();
+      // It shouldn't be possible to remove this device.
+      DCHECK_NE(kDefaultGraphicsCardPath, event.path().value());
       proxy_->Send(new OzoneGpuMsg_RemoveGraphicsDevice(event.path()));
       break;
   }
@@ -210,10 +255,21 @@
                     OnConfigurationChanged());
 }
 
+void NativeDisplayDelegateProxy::OnNewGraphicsDevice(const base::FilePath& path,
+                                                     base::File file) {
+  DCHECK(file.IsValid());
+  proxy_->Send(new OzoneGpuMsg_AddGraphicsDevice(
+      path, base::FileDescriptor(file.Pass())));
+
+  FOR_EACH_OBSERVER(NativeDisplayObserver, observers_,
+                    OnConfigurationChanged());
+}
+
 void NativeDisplayDelegateProxy::OnChannelEstablished(
     int host_id,
     scoped_refptr<base::SingleThreadTaskRunner> send_runner,
     const base::Callback<void(IPC::Message*)>& send_callback) {
+  device_manager_->ScanDevices(this);
   FOR_EACH_OBSERVER(NativeDisplayObserver, observers_,
                     OnConfigurationChanged());
 }
diff --git a/ui/ozone/platform/dri/native_display_delegate_proxy.h b/ui/ozone/platform/dri/native_display_delegate_proxy.h
index a4dbf06..70f1e8f 100644
--- a/ui/ozone/platform/dri/native_display_delegate_proxy.h
+++ b/ui/ozone/platform/dri/native_display_delegate_proxy.h
@@ -7,6 +7,7 @@
 
 #include <map>
 
+#include "base/files/file.h"
 #include "base/macros.h"
 #include "base/memory/scoped_vector.h"
 #include "base/observer_list.h"
@@ -74,6 +75,8 @@
       const std::vector<DisplaySnapshot_Params>& displays);
   void OnDisplayConfigured(int64_t display_id, bool status);
 
+  void OnNewGraphicsDevice(const base::FilePath& path, base::File file);
+
   DriGpuPlatformSupportHost* proxy_;  // Not owned.
   DeviceManager* device_manager_;     // Not owned.
   DisplayManager* display_manager_;   // Not owned.
@@ -90,6 +93,8 @@
   // Map between display_id and the configuration callback.
   std::map<int64_t, ConfigureCallback> configure_callback_map_;
 
+  base::WeakPtrFactory<NativeDisplayDelegateProxy> weak_ptr_factory_;
+
   DISALLOW_COPY_AND_ASSIGN(NativeDisplayDelegateProxy);
 };
 
diff --git a/ui/ozone/platform/dri/ozone_platform_dri.cc b/ui/ozone/platform/dri/ozone_platform_dri.cc
index 2574de0..905c9f4 100644
--- a/ui/ozone/platform/dri/ozone_platform_dri.cc
+++ b/ui/ozone/platform/dri/ozone_platform_dri.cc
@@ -5,6 +5,7 @@
 #include "ui/ozone/platform/dri/ozone_platform_dri.h"
 
 #include "base/at_exit.h"
+#include "base/thread_task_runner_handle.h"
 #include "ui/base/cursor/ozone/bitmap_cursor_factory_ozone.h"
 #include "ui/events/ozone/device/device_manager.h"
 #include "ui/events/ozone/evdev/cursor_delegate_evdev.h"
@@ -22,12 +23,13 @@
 #include "ui/ozone/platform/dri/dri_window_delegate_manager.h"
 #include "ui/ozone/platform/dri/dri_window_manager.h"
 #include "ui/ozone/platform/dri/dri_wrapper.h"
+#include "ui/ozone/platform/dri/drm_device_generator.h"
 #include "ui/ozone/platform/dri/drm_device_manager.h"
 #include "ui/ozone/platform/dri/native_display_delegate_dri.h"
 #include "ui/ozone/platform/dri/native_display_delegate_proxy.h"
 #include "ui/ozone/platform/dri/screen_manager.h"
+#include "ui/ozone/public/ozone_gpu_test_helper.h"
 #include "ui/ozone/public/ozone_platform.h"
-#include "ui/ozone/public/ui_thread_gpu.h"
 
 #if defined(USE_XKBCOMMON)
 #include "ui/events/ozone/layout/xkb/xkb_evdev_codes.h"
@@ -84,7 +86,7 @@
     return platform_window.Pass();
   }
   scoped_ptr<NativeDisplayDelegate> CreateNativeDisplayDelegate() override {
-    return scoped_ptr<NativeDisplayDelegate>(new NativeDisplayDelegateProxy(
+    return make_scoped_ptr(new NativeDisplayDelegateProxy(
         gpu_platform_support_host_.get(), device_manager_.get(),
         display_manager_.get()));
   }
@@ -97,8 +99,9 @@
     display_manager_.reset(new DisplayManager());
     surface_factory_ozone_.reset(
         new DriSurfaceFactory(&window_delegate_manager_));
-    scoped_ptr<NativeDisplayDelegateDri> ndd(
-        new NativeDisplayDelegateDri(screen_manager_.get(), dri_));
+    scoped_ptr<NativeDisplayDelegateDri> ndd(new NativeDisplayDelegateDri(
+        screen_manager_.get(), dri_,
+        scoped_ptr<DrmDeviceGenerator>(new DrmDeviceGenerator())));
     gpu_platform_support_.reset(new DriGpuPlatformSupport(
         drm_device_manager_.get(), &window_delegate_manager_,
         screen_manager_.get(), ndd.Pass()));
@@ -119,7 +122,8 @@
         cursor_.get(), device_manager_.get(),
         KeyboardLayoutEngineManager::GetKeyboardLayoutEngine()));
 
-    if (!ui_thread_gpu_.Initialize())
+    if (!gpu_helper_.Initialize(base::ThreadTaskRunnerHandle::Get(),
+                                base::ThreadTaskRunnerHandle::Get()))
       LOG(FATAL) << "Failed to initialize dummy channel.";
   }
 
@@ -145,7 +149,7 @@
 
   DriWindowDelegateManager window_delegate_manager_;
 
-  UiThreadGpu ui_thread_gpu_;
+  OzoneGpuTestHelper gpu_helper_;
 
 #if defined(USE_XKBCOMMON)
   XkbEvdevCodes xkb_evdev_code_converter_;
diff --git a/ui/ozone/platform/dri/ozone_platform_gbm.cc b/ui/ozone/platform/dri/ozone_platform_gbm.cc
index a5b6742..fe728f04 100644
--- a/ui/ozone/platform/dri/ozone_platform_gbm.cc
+++ b/ui/ozone/platform/dri/ozone_platform_gbm.cc
@@ -23,6 +23,7 @@
 #include "ui/ozone/platform/dri/dri_window.h"
 #include "ui/ozone/platform/dri/dri_window_delegate_manager.h"
 #include "ui/ozone/platform/dri/dri_window_manager.h"
+#include "ui/ozone/platform/dri/drm_device_generator.h"
 #include "ui/ozone/platform/dri/drm_device_manager.h"
 #include "ui/ozone/platform/dri/gbm_buffer.h"
 #include "ui/ozone/platform/dri/gbm_surface.h"
@@ -74,6 +75,7 @@
   GbmBufferGenerator() {}
   ~GbmBufferGenerator() override {}
 
+  // ScanoutBufferGenerator:
   scoped_refptr<ScanoutBuffer> Create(const scoped_refptr<DriWrapper>& drm,
                                       const gfx::Size& size) override {
     scoped_refptr<GbmWrapper> gbm(static_cast<GbmWrapper*>(drm.get()));
@@ -85,6 +87,23 @@
   DISALLOW_COPY_AND_ASSIGN(GbmBufferGenerator);
 };
 
+class GbmDeviceGenerator : public DrmDeviceGenerator {
+ public:
+  GbmDeviceGenerator() {}
+  ~GbmDeviceGenerator() override {}
+
+  // DrmDeviceGenerator:
+  scoped_refptr<DriWrapper> CreateDevice(const base::FilePath& path,
+                                         base::File file) override {
+    scoped_refptr<DriWrapper> drm = new GbmWrapper(path, file.Pass());
+    drm->Initialize();
+    return drm;
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(GbmDeviceGenerator);
+};
+
 class OzonePlatformGbm : public OzonePlatform {
  public:
   OzonePlatformGbm(bool use_surfaceless) : use_surfaceless_(use_surfaceless) {
@@ -121,7 +140,7 @@
     return platform_window.Pass();
   }
   scoped_ptr<NativeDisplayDelegate> CreateNativeDisplayDelegate() override {
-    return scoped_ptr<NativeDisplayDelegate>(new NativeDisplayDelegateProxy(
+    return make_scoped_ptr(new NativeDisplayDelegateProxy(
         gpu_platform_support_host_.get(), device_manager_.get(),
         display_manager_.get()));
   }
@@ -168,8 +187,9 @@
 
     surface_factory_ozone_->InitializeGpu(gbm_, drm_device_manager_.get(),
                                           window_delegate_manager_.get());
-    scoped_ptr<NativeDisplayDelegateDri> ndd(
-        new NativeDisplayDelegateDri(screen_manager_.get(), gbm_));
+    scoped_ptr<NativeDisplayDelegateDri> ndd(new NativeDisplayDelegateDri(
+        screen_manager_.get(), gbm_,
+        scoped_ptr<DrmDeviceGenerator>(new GbmDeviceGenerator())));
     gpu_platform_support_.reset(new DriGpuPlatformSupport(
         drm_device_manager_.get(), window_delegate_manager_.get(),
         screen_manager_.get(), ndd.Pass()));
diff --git a/ui/ozone/platform/egltest/ozone_platform_egltest.cc b/ui/ozone/platform/egltest/ozone_platform_egltest.cc
index becbb81c..1e8ca15 100644
--- a/ui/ozone/platform/egltest/ozone_platform_egltest.cc
+++ b/ui/ozone/platform/egltest/ozone_platform_egltest.cc
@@ -354,7 +354,7 @@
         delegate, &eglplatform_shim_, event_factory_ozone_.get(), bounds));
   }
   scoped_ptr<NativeDisplayDelegate> CreateNativeDisplayDelegate() override {
-    return scoped_ptr<NativeDisplayDelegate>(new NativeDisplayDelegateOzone());
+    return make_scoped_ptr(new NativeDisplayDelegateOzone());
   }
 
   void InitializeUI() override {
diff --git a/ui/ozone/platform/test/ozone_platform_test.cc b/ui/ozone/platform/test/ozone_platform_test.cc
index 5cdef6c2..fce3a9d 100644
--- a/ui/ozone/platform/test/ozone_platform_test.cc
+++ b/ui/ozone/platform/test/ozone_platform_test.cc
@@ -59,7 +59,7 @@
         new TestWindow(delegate, window_manager_.get(), bounds));
   }
   scoped_ptr<NativeDisplayDelegate> CreateNativeDisplayDelegate() override {
-    return scoped_ptr<NativeDisplayDelegate>(new NativeDisplayDelegateOzone());
+    return make_scoped_ptr(new NativeDisplayDelegateOzone());
   }
 
   void InitializeUI() override {
diff --git a/ui/ozone/public/ozone_gpu_test_helper.cc b/ui/ozone/public/ozone_gpu_test_helper.cc
new file mode 100644
index 0000000..1fa265c
--- /dev/null
+++ b/ui/ozone/public/ozone_gpu_test_helper.cc
@@ -0,0 +1,119 @@
+// 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 "ui/ozone/public/ozone_gpu_test_helper.h"
+
+#include "base/thread_task_runner_handle.h"
+#include "ipc/ipc_listener.h"
+#include "ipc/ipc_message.h"
+#include "ipc/ipc_sender.h"
+#include "ipc/message_filter.h"
+#include "ui/ozone/public/gpu_platform_support.h"
+#include "ui/ozone/public/gpu_platform_support_host.h"
+#include "ui/ozone/public/ozone_platform.h"
+
+namespace ui {
+
+namespace {
+
+const int kGpuProcessHostId = 1;
+
+}  // namespace
+
+class FakeGpuProcess : public IPC::Sender {
+ public:
+  FakeGpuProcess(
+      const scoped_refptr<base::SingleThreadTaskRunner>& ui_task_runner)
+      : ui_task_runner_(ui_task_runner), weak_factory_(this) {}
+  ~FakeGpuProcess() override {}
+
+  void Init() {
+    ui::OzonePlatform::GetInstance()
+        ->GetGpuPlatformSupport()
+        ->OnChannelEstablished(this);
+  }
+
+  void InitOnIO() {
+    ui::OzonePlatform::GetInstance()
+        ->GetGpuPlatformSupport()
+        ->GetMessageFilter()
+        ->OnFilterAdded(this);
+  }
+
+  bool Send(IPC::Message* msg) override {
+    ui_task_runner_->PostTask(
+        FROM_HERE,
+        base::Bind(&FakeGpuProcess::DispatchToGpuPlatformSupportHostTask,
+                   weak_factory_.GetWeakPtr(), msg));
+    return true;
+  }
+
+ private:
+  void DispatchToGpuPlatformSupportHostTask(IPC::Message* msg) {
+    ui::OzonePlatform::GetInstance()
+        ->GetGpuPlatformSupportHost()
+        ->OnMessageReceived(*msg);
+    delete msg;
+  }
+
+  scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner_;
+  base::WeakPtrFactory<FakeGpuProcess> weak_factory_;
+};
+
+class FakeGpuProcessHost {
+ public:
+  FakeGpuProcessHost(
+      const scoped_refptr<base::SingleThreadTaskRunner>& gpu_task_runner)
+      : gpu_task_runner_(gpu_task_runner), weak_factory_(this) {}
+  ~FakeGpuProcessHost() {}
+
+  void Init() {
+    base::Callback<void(IPC::Message*)> sender =
+        base::Bind(&FakeGpuProcessHost::DispatchToGpuPlatformSupportTask,
+                   weak_factory_.GetWeakPtr());
+
+    ui::OzonePlatform::GetInstance()
+        ->GetGpuPlatformSupportHost()
+        ->OnChannelEstablished(kGpuProcessHostId, gpu_task_runner_, sender);
+  }
+
+ private:
+  void DispatchToGpuPlatformSupportTask(IPC::Message* msg) {
+    ui::OzonePlatform::GetInstance()
+        ->GetGpuPlatformSupport()
+        ->OnMessageReceived(*msg);
+    delete msg;
+  }
+
+  scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner_;
+  base::WeakPtrFactory<FakeGpuProcessHost> weak_factory_;
+};
+
+OzoneGpuTestHelper::OzoneGpuTestHelper() {
+}
+
+OzoneGpuTestHelper::~OzoneGpuTestHelper() {
+}
+
+bool OzoneGpuTestHelper::Initialize(
+    const scoped_refptr<base::SingleThreadTaskRunner>& ui_task_runner,
+    const scoped_refptr<base::SingleThreadTaskRunner>& gpu_task_runner) {
+  io_helper_thread_.reset(new base::Thread("IOHelperThread"));
+  if (!io_helper_thread_->StartWithOptions(
+          base::Thread::Options(base::MessageLoop::TYPE_IO, 0)))
+    return false;
+
+  fake_gpu_process_.reset(new FakeGpuProcess(ui_task_runner));
+  io_helper_thread_->task_runner()->PostTask(
+      FROM_HERE, base::Bind(&FakeGpuProcess::InitOnIO,
+                            base::Unretained(fake_gpu_process_.get())));
+  fake_gpu_process_->Init();
+
+  fake_gpu_process_host_.reset(new FakeGpuProcessHost(gpu_task_runner));
+  fake_gpu_process_host_->Init();
+
+  return true;
+}
+
+}  // namespace ui
diff --git a/ui/ozone/public/ozone_gpu_test_helper.h b/ui/ozone/public/ozone_gpu_test_helper.h
new file mode 100644
index 0000000..e342865
--- /dev/null
+++ b/ui/ozone/public/ozone_gpu_test_helper.h
@@ -0,0 +1,48 @@
+// 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 UI_OZONE_PUBLIC_OZONE_GPU_THREAD_HELPER_H_
+#define UI_OZONE_PUBLIC_OZONE_GPU_THREAD_HELPER_H_
+
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "ui/ozone/ozone_export.h"
+
+namespace base {
+class SingleThreadTaskRunner;
+class Thread;
+}
+
+namespace ui {
+
+class FakeGpuProcess;
+class FakeGpuProcessHost;
+
+// Helper class for applications that do not have a dedicated GPU channel.
+//
+// This sets up message forwarding between the "gpu" and "ui" threads.
+class OZONE_EXPORT OzoneGpuTestHelper {
+ public:
+  OzoneGpuTestHelper();
+  virtual ~OzoneGpuTestHelper();
+
+  // Start processing gpu messages. The host process will be using the
+  // |gpu_task_runner| to post messages intended for the GPU thread. The "gpu"
+  // process will be using the |ui_task_runner| to post messages intended for
+  // the "ui" thread.
+  bool Initialize(
+      const scoped_refptr<base::SingleThreadTaskRunner>& ui_task_runner,
+      const scoped_refptr<base::SingleThreadTaskRunner>& gpu_task_runner);
+
+ private:
+  scoped_ptr<FakeGpuProcess> fake_gpu_process_;
+  scoped_ptr<FakeGpuProcessHost> fake_gpu_process_host_;
+  scoped_ptr<base::Thread> io_helper_thread_;
+
+  DISALLOW_COPY_AND_ASSIGN(OzoneGpuTestHelper);
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PUBLIC_OZONE_GPU_THREAD_HELPER_H_
diff --git a/ui/ozone/public/ui_thread_gpu.cc b/ui/ozone/public/ui_thread_gpu.cc
deleted file mode 100644
index aeb08b1..0000000
--- a/ui/ozone/public/ui_thread_gpu.cc
+++ /dev/null
@@ -1,119 +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 "ui/ozone/public/ui_thread_gpu.h"
-
-#include "base/thread_task_runner_handle.h"
-#include "ipc/ipc_listener.h"
-#include "ipc/ipc_message.h"
-#include "ipc/ipc_sender.h"
-#include "ipc/message_filter.h"
-#include "ui/ozone/public/gpu_platform_support.h"
-#include "ui/ozone/public/gpu_platform_support_host.h"
-#include "ui/ozone/public/ozone_platform.h"
-
-namespace ui {
-
-namespace {
-
-const int kGpuProcessHostId = 1;
-
-}  // namespace
-
-class FakeGpuProcess : public IPC::Sender {
- public:
-  FakeGpuProcess() : weak_factory_(this) {}
-  ~FakeGpuProcess() override {}
-
-  void Init() {
-    task_runner_ = base::ThreadTaskRunnerHandle::Get();
-
-    ui::OzonePlatform::GetInstance()
-        ->GetGpuPlatformSupport()
-        ->OnChannelEstablished(this);
-  }
-
-  void InitOnIO() {
-    ui::OzonePlatform::GetInstance()
-        ->GetGpuPlatformSupport()
-        ->GetMessageFilter()
-        ->OnFilterAdded(this);
-  }
-
-  bool Send(IPC::Message* msg) override {
-    DCHECK(task_runner_->BelongsToCurrentThread());
-    base::MessageLoop::current()->PostTask(
-        FROM_HERE,
-        base::Bind(&FakeGpuProcess::DispatchToGpuPlatformSupportHostTask,
-                   weak_factory_.GetWeakPtr(), msg));
-    return true;
-  }
-
- private:
-  void DispatchToGpuPlatformSupportHostTask(IPC::Message* msg) {
-    ui::OzonePlatform::GetInstance()
-        ->GetGpuPlatformSupportHost()
-        ->OnMessageReceived(*msg);
-    delete msg;
-  }
-
-  scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
-  base::WeakPtrFactory<FakeGpuProcess> weak_factory_;
-};
-
-class FakeGpuProcessHost {
- public:
-  FakeGpuProcessHost() : weak_factory_(this) {}
-  ~FakeGpuProcessHost() {}
-
-  void Init() {
-    task_runner_ = base::ThreadTaskRunnerHandle::Get();
-
-    base::Callback<void(IPC::Message*)> sender =
-        base::Bind(&FakeGpuProcessHost::DispatchToGpuPlatformSupportTask,
-                   weak_factory_.GetWeakPtr());
-
-    ui::OzonePlatform::GetInstance()
-        ->GetGpuPlatformSupportHost()
-        ->OnChannelEstablished(kGpuProcessHostId, task_runner_, sender);
-  }
-
- private:
-  void DispatchToGpuPlatformSupportTask(IPC::Message* msg) {
-    DCHECK(task_runner_->BelongsToCurrentThread());
-    ui::OzonePlatform::GetInstance()
-        ->GetGpuPlatformSupport()
-        ->OnMessageReceived(*msg);
-    delete msg;
-  }
-
-  scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
-  base::WeakPtrFactory<FakeGpuProcessHost> weak_factory_;
-};
-
-UiThreadGpu::UiThreadGpu() {
-}
-
-UiThreadGpu::~UiThreadGpu() {
-}
-
-bool UiThreadGpu::Initialize() {
-  io_helper_thread_.reset(new base::Thread("IOHelperThread"));
-  if (!io_helper_thread_->StartWithOptions(
-          base::Thread::Options(base::MessageLoop::TYPE_IO, 0)))
-    return false;
-
-  fake_gpu_process_.reset(new FakeGpuProcess);
-  io_helper_thread_->task_runner()->PostTask(
-      FROM_HERE, base::Bind(&FakeGpuProcess::InitOnIO,
-                            base::Unretained(fake_gpu_process_.get())));
-  fake_gpu_process_->Init();
-
-  fake_gpu_process_host_.reset(new FakeGpuProcessHost);
-  fake_gpu_process_host_->Init();
-
-  return true;
-}
-
-}  // namespace ui
diff --git a/ui/ozone/public/ui_thread_gpu.h b/ui/ozone/public/ui_thread_gpu.h
deleted file mode 100644
index 4e75dabf..0000000
--- a/ui/ozone/public/ui_thread_gpu.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 UI_OZONE_PUBLIC_UI_THREAD_GPU_H_
-#define UI_OZONE_PUBLIC_UI_THREAD_GPU_H_
-
-#include "base/memory/scoped_ptr.h"
-#include "ui/ozone/ozone_export.h"
-
-namespace base {
-class Thread;
-}
-
-namespace ui {
-
-class FakeGpuProcess;
-class FakeGpuProcessHost;
-
-// Helper class for applications that do GL on the UI thead.
-//
-// This sets up message forwarding between the "gpu" and "ui" threads,
-// for applications in which they are both the same thread.
-class OZONE_EXPORT UiThreadGpu {
- public:
-  UiThreadGpu();
-  virtual ~UiThreadGpu();
-
-  // Start processing gpu messages.
-  bool Initialize();
-
- private:
-  scoped_ptr<FakeGpuProcess> fake_gpu_process_;
-  scoped_ptr<FakeGpuProcessHost> fake_gpu_process_host_;
-  scoped_ptr<base::Thread> io_helper_thread_;
-
-  DISALLOW_COPY_AND_ASSIGN(UiThreadGpu);
-};
-
-}  // namespace ui
-
-#endif  // UI_OZONE_PUBLIC_UI_THREAD_GPU_H_
diff --git a/ui/resources/default_100_percent/common/app_list_experimental_icon.png b/ui/resources/default_100_percent/common/app_list_experimental_icon.png
deleted file mode 100644
index 3d6f702..0000000
--- a/ui/resources/default_100_percent/common/app_list_experimental_icon.png
+++ /dev/null
Binary files differ
diff --git a/ui/resources/default_100_percent/common/app_list_folder_back_normal.png b/ui/resources/default_100_percent/common/app_list_folder_back_normal.png
index 4f748cc..af346248 100644
--- a/ui/resources/default_100_percent/common/app_list_folder_back_normal.png
+++ b/ui/resources/default_100_percent/common/app_list_folder_back_normal.png
Binary files differ
diff --git a/ui/resources/default_100_percent/common/app_list_progress_bar_background.png b/ui/resources/default_100_percent/common/app_list_progress_bar_background.png
index 0a285c4..d4b6e41 100644
--- a/ui/resources/default_100_percent/common/app_list_progress_bar_background.png
+++ b/ui/resources/default_100_percent/common/app_list_progress_bar_background.png
Binary files differ
diff --git a/ui/resources/default_100_percent/common/app_list_progress_bar_center.png b/ui/resources/default_100_percent/common/app_list_progress_bar_center.png
index dc6e013b..b652768 100644
--- a/ui/resources/default_100_percent/common/app_list_progress_bar_center.png
+++ b/ui/resources/default_100_percent/common/app_list_progress_bar_center.png
Binary files differ
diff --git a/ui/resources/default_100_percent/common/app_list_progress_bar_left.png b/ui/resources/default_100_percent/common/app_list_progress_bar_left.png
index 50362d4..1669f921 100644
--- a/ui/resources/default_100_percent/common/app_list_progress_bar_left.png
+++ b/ui/resources/default_100_percent/common/app_list_progress_bar_left.png
Binary files differ
diff --git a/ui/resources/default_100_percent/common/app_list_progress_bar_right.png b/ui/resources/default_100_percent/common/app_list_progress_bar_right.png
index 25ac77e..23b0905 100644
--- a/ui/resources/default_100_percent/common/app_list_progress_bar_right.png
+++ b/ui/resources/default_100_percent/common/app_list_progress_bar_right.png
Binary files differ
diff --git a/ui/resources/default_200_percent/common/app_list_experimental_icon.png b/ui/resources/default_200_percent/common/app_list_experimental_icon.png
deleted file mode 100644
index 3c999d6..0000000
--- a/ui/resources/default_200_percent/common/app_list_experimental_icon.png
+++ /dev/null
Binary files differ
diff --git a/ui/resources/default_200_percent/common/app_list_folder_back_normal.png b/ui/resources/default_200_percent/common/app_list_folder_back_normal.png
index 7ad7206b..860215e8 100644
--- a/ui/resources/default_200_percent/common/app_list_folder_back_normal.png
+++ b/ui/resources/default_200_percent/common/app_list_folder_back_normal.png
Binary files differ
diff --git a/ui/resources/default_200_percent/common/app_list_progress_bar_background.png b/ui/resources/default_200_percent/common/app_list_progress_bar_background.png
index 7172e9b..ebe962b5 100644
--- a/ui/resources/default_200_percent/common/app_list_progress_bar_background.png
+++ b/ui/resources/default_200_percent/common/app_list_progress_bar_background.png
Binary files differ
diff --git a/ui/resources/default_200_percent/common/app_list_progress_bar_center.png b/ui/resources/default_200_percent/common/app_list_progress_bar_center.png
index 2c49c00..421d0c0 100644
--- a/ui/resources/default_200_percent/common/app_list_progress_bar_center.png
+++ b/ui/resources/default_200_percent/common/app_list_progress_bar_center.png
Binary files differ
diff --git a/ui/resources/default_200_percent/common/app_list_progress_bar_left.png b/ui/resources/default_200_percent/common/app_list_progress_bar_left.png
index 1e4c82e..cf28951 100644
--- a/ui/resources/default_200_percent/common/app_list_progress_bar_left.png
+++ b/ui/resources/default_200_percent/common/app_list_progress_bar_left.png
Binary files differ
diff --git a/ui/resources/default_200_percent/common/app_list_progress_bar_right.png b/ui/resources/default_200_percent/common/app_list_progress_bar_right.png
index 3e0b4b35..a1b7ecf 100644
--- a/ui/resources/default_200_percent/common/app_list_progress_bar_right.png
+++ b/ui/resources/default_200_percent/common/app_list_progress_bar_right.png
Binary files differ
diff --git a/ui/resources/ui_resources.grd b/ui/resources/ui_resources.grd
index bdd4c388..3ecb7773 100644
--- a/ui/resources/ui_resources.grd
+++ b/ui/resources/ui_resources.grd
@@ -16,7 +16,6 @@
            BECAUSE YOUR RESOURCES ARE FUNCTIONALLY RELATED OR FALL UNDER THE
            SAME CONDITIONALS. -->
       <if expr="enable_app_list">
-        <structure type="chrome_scaled_image" name="IDR_APP_LIST_EXPERIMENTAL_ICON" file="common/app_list_experimental_icon.png" />
         <structure type="chrome_scaled_image" name="IDR_APP_LIST_ITEM_PROGRESS_BACKGROUND" file="common/app_list_progress_bar_background.png" />
         <structure type="chrome_scaled_image" name="IDR_APP_LIST_ITEM_PROGRESS_LEFT" file="common/app_list_progress_bar_left.png" />
         <structure type="chrome_scaled_image" name="IDR_APP_LIST_ITEM_PROGRESS_CENTER" file="common/app_list_progress_bar_center.png" />
diff --git a/ui/strings/ui_strings.grd b/ui/strings/ui_strings.grd
index 34aa7e4..9fa99f9 100644
--- a/ui/strings/ui_strings.grd
+++ b/ui/strings/ui_strings.grd
@@ -2181,6 +2181,9 @@
       <message name="IDS_APP_LIST_OPEN_FEEDBACK" desc="The menu entry to show the feedback UI.">
         Send feedback
       </message>
+      <message name="IDS_APP_LIST_BACK" desc="The name of the button that goes to the previous screen in the launcher.">
+        Back
+      </message>
       <message name="IDS_APP_LIST_ALL_APPS" desc="The text on the button that opens the app grid view in the app launcher.">
         All Apps
       </message>
diff --git a/ui/touch_selection/touch_handle_unittest.cc b/ui/touch_selection/touch_handle_unittest.cc
index b266fdb..d90c94b 100644
--- a/ui/touch_selection/touch_handle_unittest.cc
+++ b/ui/touch_selection/touch_handle_unittest.cc
@@ -92,8 +92,7 @@
   void SetNeedsAnimate() override { needs_animate_ = true; }
 
   scoped_ptr<TouchHandleDrawable> CreateDrawable() override {
-    return scoped_ptr<TouchHandleDrawable>(
-        new MockTouchHandleDrawable(&drawable_data_));
+    return make_scoped_ptr(new MockTouchHandleDrawable(&drawable_data_));
   }
 
   base::TimeDelta GetTapTimeout() const override {
diff --git a/ui/touch_selection/touch_selection_controller_unittest.cc b/ui/touch_selection/touch_selection_controller_unittest.cc
index b10c1da..2281aeea 100644
--- a/ui/touch_selection/touch_selection_controller_unittest.cc
+++ b/ui/touch_selection/touch_selection_controller_unittest.cc
@@ -97,8 +97,7 @@
   }
 
   scoped_ptr<TouchHandleDrawable> CreateDrawable() override {
-    return scoped_ptr<TouchHandleDrawable>(
-        new MockTouchHandleDrawable(&dragging_enabled_));
+    return make_scoped_ptr(new MockTouchHandleDrawable(&dragging_enabled_));
   }
 
   void AllowShowingOnTapForEmptyEditable() {
diff --git a/ui/views/BUILD.gn b/ui/views/BUILD.gn
index 726288c..be6c0da 100644
--- a/ui/views/BUILD.gn
+++ b/ui/views/BUILD.gn
@@ -14,6 +14,9 @@
 component("views") {
   sources = gypi_values.views_sources
 
+  # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+  configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
+
   defines = [ "VIEWS_IMPLEMENTATION" ]
 
   deps = [
@@ -67,7 +70,6 @@
 
   if (is_win) {
     sources += gypi_values.views_win_sources
-    cflags = [ "/wd4267" ]  # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
     libs = [
       "imm32.lib",
       "oleacc.lib",
diff --git a/ui/views/accessibility/ax_aura_obj_cache.cc b/ui/views/accessibility/ax_aura_obj_cache.cc
index ae2df206..05f1868 100644
--- a/ui/views/accessibility/ax_aura_obj_cache.cc
+++ b/ui/views/accessibility/ax_aura_obj_cache.cc
@@ -76,10 +76,11 @@
   delete obj;
 }
 
-AXAuraObjCache::AXAuraObjCache() : current_id_(1) {
+AXAuraObjCache::AXAuraObjCache() : current_id_(1), is_destroying_(false) {
 }
 
 AXAuraObjCache::~AXAuraObjCache() {
+  is_destroying_ = true;
   STLDeleteContainerPairSecondPointers(cache_.begin(), cache_.end());
   cache_.clear();
 }
diff --git a/ui/views/accessibility/ax_aura_obj_cache.h b/ui/views/accessibility/ax_aura_obj_cache.h
index 6e5d9e8..a8316b5 100644
--- a/ui/views/accessibility/ax_aura_obj_cache.h
+++ b/ui/views/accessibility/ax_aura_obj_cache.h
@@ -52,6 +52,9 @@
   // Remove a cached entry based on an id.
   void Remove(int32 id);
 
+  // Indicates if this object's currently being destroyed.
+  bool is_destroying() { return is_destroying_; }
+
  private:
   friend struct DefaultSingletonTraits<AXAuraObjCache>;
 
@@ -75,6 +78,9 @@
   std::map<int32, AXAuraObjWrapper*> cache_;
   int32 current_id_;
 
+  // True immediately when entering this object's destructor.
+  bool is_destroying_;
+
   DISALLOW_COPY_AND_ASSIGN(AXAuraObjCache);
 };
 
diff --git a/ui/views/accessibility/ax_view_obj_wrapper.cc b/ui/views/accessibility/ax_view_obj_wrapper.cc
index 38876d5..8c9d3bc 100644
--- a/ui/views/accessibility/ax_view_obj_wrapper.cc
+++ b/ui/views/accessibility/ax_view_obj_wrapper.cc
@@ -7,6 +7,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "ui/accessibility/ax_node_data.h"
 #include "ui/accessibility/ax_view_state.h"
+#include "ui/events/event_utils.h"
 #include "ui/views/accessibility/ax_aura_obj_cache.h"
 #include "ui/views/view.h"
 #include "ui/views/widget/widget.h"
@@ -77,9 +78,9 @@
 void AXViewObjWrapper::DoDefault() {
   gfx::Rect rect = view_->GetBoundsInScreen();
   gfx::Point center = rect.CenterPoint();
-  view_->OnMousePressed(ui::MouseEvent(ui::ET_MOUSE_PRESSED, center, center,
-                                       ui::EF_LEFT_MOUSE_BUTTON,
-                                       ui::EF_LEFT_MOUSE_BUTTON));
+  view_->OnMousePressed(ui::MouseEvent(
+      ui::ET_MOUSE_PRESSED, center, center, ui::EventTimeForNow(),
+      ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON));
 }
 
 void AXViewObjWrapper::Focus() {
diff --git a/ui/views/accessibility/ax_widget_obj_wrapper.cc b/ui/views/accessibility/ax_widget_obj_wrapper.cc
index 36a3143e..44fcfa8 100644
--- a/ui/views/accessibility/ax_widget_obj_wrapper.cc
+++ b/ui/views/accessibility/ax_widget_obj_wrapper.cc
@@ -17,8 +17,10 @@
 }
 
 AXWidgetObjWrapper::~AXWidgetObjWrapper() {
-  widget_->RemoveObserver(this);
-  widget_->RemoveRemovalsObserver(this);
+  if (!AXAuraObjCache::GetInstance()->is_destroying()) {
+    widget_->RemoveObserver(this);
+    widget_->RemoveRemovalsObserver(this);
+  }
   widget_ = NULL;
 }
 
diff --git a/ui/views/bubble/bubble_window_targeter_unittest.cc b/ui/views/bubble/bubble_window_targeter_unittest.cc
index be39081f..103cc3d 100644
--- a/ui/views/bubble/bubble_window_targeter_unittest.cc
+++ b/ui/views/bubble/bubble_window_targeter_unittest.cc
@@ -6,6 +6,7 @@
 
 #include "ui/aura/window.h"
 #include "ui/aura/window_event_dispatcher.h"
+#include "ui/events/event_utils.h"
 #include "ui/views/bubble/bubble_border.h"
 #include "ui/views/bubble/bubble_delegate.h"
 #include "ui/views/test/views_test_base.h"
@@ -93,13 +94,15 @@
   {
     bubble_delegate()->set_margins(gfx::Insets());
     ui::MouseEvent move1(ui::ET_MOUSE_MOVED, bubble_bounds.origin(),
-                         bubble_bounds.origin(), ui::EF_NONE, ui::EF_NONE);
+                         bubble_bounds.origin(), ui::EventTimeForNow(),
+                         ui::EF_NONE, ui::EF_NONE);
     EXPECT_EQ(bubble_window, targeter->FindTargetForEvent(root, &move1));
   }
   {
     bubble_delegate()->set_margins(gfx::Insets(20, 20, 20, 20));
     ui::MouseEvent move1(ui::ET_MOUSE_MOVED, bubble_bounds.origin(),
-                         bubble_bounds.origin(), ui::EF_NONE, ui::EF_NONE);
+                         bubble_bounds.origin(), ui::EventTimeForNow(),
+                         ui::EF_NONE, ui::EF_NONE);
     EXPECT_EQ(bubble_window, targeter->FindTargetForEvent(root, &move1));
   }
 
@@ -108,7 +111,8 @@
   {
     bubble_delegate()->set_margins(gfx::Insets(20, 20, 20, 20));
     ui::MouseEvent move1(ui::ET_MOUSE_MOVED, bubble_bounds.origin(),
-                         bubble_bounds.origin(), ui::EF_NONE, ui::EF_NONE);
+                         bubble_bounds.origin(), ui::EventTimeForNow(),
+                         ui::EF_NONE, ui::EF_NONE);
     EXPECT_NE(bubble_window, targeter->FindTargetForEvent(root, &move1));
   }
 }
diff --git a/ui/views/cocoa/bridged_content_view.h b/ui/views/cocoa/bridged_content_view.h
index 81dbb1fd..0d84c77 100644
--- a/ui/views/cocoa/bridged_content_view.h
+++ b/ui/views/cocoa/bridged_content_view.h
@@ -32,6 +32,9 @@
 
   // A tracking area installed to enable mouseMoved events.
   ui::ScopedCrTrackingArea trackingArea_;
+
+  // Whether the view is reacting to a keyDown event on the view.
+  BOOL inKeyDown_;
 }
 
 @property(readonly, nonatomic) views::View* hostedView;
diff --git a/ui/views/cocoa/bridged_content_view.mm b/ui/views/cocoa/bridged_content_view.mm
index 3113b37..493ebdf4 100644
--- a/ui/views/cocoa/bridged_content_view.mm
+++ b/ui/views/cocoa/bridged_content_view.mm
@@ -15,6 +15,7 @@
 #include "ui/gfx/geometry/rect.h"
 #include "ui/strings/grit/ui_strings.h"
 #include "ui/views/controls/menu/menu_controller.h"
+#include "ui/views/ime/input_method.h"
 #include "ui/views/view.h"
 #include "ui/views/widget/widget.h"
 
@@ -66,6 +67,8 @@
           eventFlags:(int)eventFlags;
 
 // Menu action handlers.
+- (void)undo:(id)sender;
+- (void)redo:(id)sender;
 - (void)cut:(id)sender;
 - (void)copy:(id)sender;
 - (void)paste:(id)sender;
@@ -153,21 +156,40 @@
       return;
   }
 
-  // If there's an active TextInputClient, it ignores the key and processes the
-  // logical editing action.
+  // If there's an active TextInputClient, schedule the editing command to be
+  // performed.
   if (commandId && textInputClient_ &&
-      textInputClient_->IsEditingCommandEnabled(commandId)) {
-    textInputClient_->ExecuteEditingCommand(commandId);
-    return;
-  }
+      textInputClient_->IsEditCommandEnabled(commandId))
+    textInputClient_->SetEditCommandForNextKeyEvent(commandId);
 
-  // Otherwise, process the action as a regular key event.
+  // Generate a synthetic event with the keycode toolkit-views expects.
   ui::KeyEvent event(ui::ET_KEY_PRESSED, keyCode, domCode, eventFlags);
-  hostedView_->GetWidget()->OnKeyEvent(&event);
+  hostedView_->GetWidget()->GetInputMethod()->DispatchKeyEvent(event);
+}
+
+- (void)undo:(id)sender {
+  // This DCHECK is more strict than a similar check in handleAction:. It can be
+  // done here because the actors sending these actions should be calling
+  // validateUserInterfaceItem: before enabling UI that allows these messages to
+  // be sent. Checking it here would be too late to provide correct UI feedback
+  // (e.g. there will be no "beep").
+  DCHECK(textInputClient_->IsEditCommandEnabled(IDS_APP_UNDO));
+  [self handleAction:IDS_APP_UNDO
+             keyCode:ui::VKEY_Z
+             domCode:ui::DomCode::KEY_Z
+          eventFlags:ui::EF_CONTROL_DOWN];
+}
+
+- (void)redo:(id)sender {
+  DCHECK(textInputClient_->IsEditCommandEnabled(IDS_APP_REDO));
+  [self handleAction:IDS_APP_REDO
+             keyCode:ui::VKEY_Z
+             domCode:ui::DomCode::KEY_Z
+          eventFlags:ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN];
 }
 
 - (void)cut:(id)sender {
-  DCHECK(textInputClient_->IsEditingCommandEnabled(IDS_APP_CUT));
+  DCHECK(textInputClient_->IsEditCommandEnabled(IDS_APP_CUT));
   [self handleAction:IDS_APP_CUT
              keyCode:ui::VKEY_X
              domCode:ui::DomCode::KEY_X
@@ -175,7 +197,7 @@
 }
 
 - (void)copy:(id)sender {
-  DCHECK(textInputClient_->IsEditingCommandEnabled(IDS_APP_COPY));
+  DCHECK(textInputClient_->IsEditCommandEnabled(IDS_APP_COPY));
   [self handleAction:IDS_APP_COPY
              keyCode:ui::VKEY_C
              domCode:ui::DomCode::KEY_C
@@ -183,7 +205,7 @@
 }
 
 - (void)paste:(id)sender {
-  DCHECK(textInputClient_->IsEditingCommandEnabled(IDS_APP_PASTE));
+  DCHECK(textInputClient_->IsEditCommandEnabled(IDS_APP_PASTE));
   [self handleAction:IDS_APP_PASTE
              keyCode:ui::VKEY_V
              domCode:ui::DomCode::KEY_V
@@ -191,7 +213,7 @@
 }
 
 - (void)selectAll:(id)sender {
-  DCHECK(textInputClient_->IsEditingCommandEnabled(IDS_APP_SELECT_ALL));
+  DCHECK(textInputClient_->IsEditCommandEnabled(IDS_APP_SELECT_ALL));
   [self handleAction:IDS_APP_SELECT_ALL
              keyCode:ui::VKEY_A
              domCode:ui::DomCode::KEY_A
@@ -244,7 +266,9 @@
 
 - (void)keyDown:(NSEvent*)theEvent {
   // Convert the event into an action message, according to OSX key mappings.
+  inKeyDown_ = YES;
   [self interpretKeyEvents:@[ theEvent ]];
+  inKeyDown_ = NO;
 }
 
 - (void)mouseDown:(NSEvent*)theEvent {
@@ -569,7 +593,17 @@
     return;
 
   textInputClient_->DeleteRange(gfx::Range(replacementRange));
-  textInputClient_->InsertText(base::SysNSStringToUTF16(text));
+
+  // If a single character is inserted by keyDown's call to interpretKeyEvents:
+  // then use InsertChar() to allow editing events to be merged. The second
+  // argument is the key modifier, which interpretKeyEvents: will have already
+  // processed, so don't send it to InsertChar() as well. E.g. Alt+S puts 'ß' in
+  // |text| but sending 'Alt' to InsertChar would filter it out since it thinks
+  // it's a command. Actual commands (e.g. Cmd+S) won't go through insertText:.
+  if (inKeyDown_ && [text length] == 1)
+    textInputClient_->InsertChar([text characterAtIndex:0], 0);
+  else
+    textInputClient_->InsertText(base::SysNSStringToUTF16(text));
 }
 
 - (NSRange)markedRange {
@@ -621,14 +655,18 @@
 
   SEL action = [item action];
 
+  if (action == @selector(undo:))
+    return textInputClient_->IsEditCommandEnabled(IDS_APP_UNDO);
+  if (action == @selector(redo:))
+    return textInputClient_->IsEditCommandEnabled(IDS_APP_REDO);
   if (action == @selector(cut:))
-    return textInputClient_->IsEditingCommandEnabled(IDS_APP_CUT);
+    return textInputClient_->IsEditCommandEnabled(IDS_APP_CUT);
   if (action == @selector(copy:))
-    return textInputClient_->IsEditingCommandEnabled(IDS_APP_COPY);
+    return textInputClient_->IsEditCommandEnabled(IDS_APP_COPY);
   if (action == @selector(paste:))
-    return textInputClient_->IsEditingCommandEnabled(IDS_APP_PASTE);
+    return textInputClient_->IsEditCommandEnabled(IDS_APP_PASTE);
   if (action == @selector(selectAll:))
-    return textInputClient_->IsEditingCommandEnabled(IDS_APP_SELECT_ALL);
+    return textInputClient_->IsEditCommandEnabled(IDS_APP_SELECT_ALL);
 
   return NO;
 }
diff --git a/ui/views/cocoa/bridged_native_widget.mm b/ui/views/cocoa/bridged_native_widget.mm
index 96d0170..0d397bd 100644
--- a/ui/views/cocoa/bridged_native_widget.mm
+++ b/ui/views/cocoa/bridged_native_widget.mm
@@ -453,8 +453,6 @@
 // BridgedNativeWidget, internal::InputMethodDelegate:
 
 void BridgedNativeWidget::DispatchKeyEventPostIME(const ui::KeyEvent& key) {
-  // Mac key events don't go through this, but some unit tests that use
-  // MockInputMethod do.
   DCHECK(focus_manager_);
   native_widget_mac_->GetWidget()->OnKeyEvent(const_cast<ui::KeyEvent*>(&key));
   if (!key.handled())
diff --git a/ui/views/controls/button/custom_button.cc b/ui/views/controls/button/custom_button.cc
index d9db867..0fa08f09 100644
--- a/ui/views/controls/button/custom_button.cc
+++ b/ui/views/controls/button/custom_button.cc
@@ -6,6 +6,7 @@
 
 #include "ui/accessibility/ax_view_state.h"
 #include "ui/events/event.h"
+#include "ui/events/event_utils.h"
 #include "ui/events/keycodes/keyboard_codes.h"
 #include "ui/gfx/animation/throb_animation.h"
 #include "ui/gfx/screen.h"
@@ -192,9 +193,8 @@
   } else if (event.key_code() == ui::VKEY_RETURN) {
     SetState(STATE_NORMAL);
     // TODO(beng): remove once NotifyClick takes ui::Event.
-    ui::MouseEvent synthetic_event(ui::ET_MOUSE_RELEASED,
-                                   gfx::Point(),
-                                   gfx::Point(),
+    ui::MouseEvent synthetic_event(ui::ET_MOUSE_RELEASED, gfx::Point(),
+                                   gfx::Point(), ui::EventTimeForNow(),
                                    ui::EF_LEFT_MOUSE_BUTTON,
                                    ui::EF_LEFT_MOUSE_BUTTON);
     NotifyClick(synthetic_event);
@@ -210,11 +210,9 @@
 
   SetState(STATE_NORMAL);
   // TODO(beng): remove once NotifyClick takes ui::Event.
-  ui::MouseEvent synthetic_event(ui::ET_MOUSE_RELEASED,
-                                 gfx::Point(),
-                                 gfx::Point(),
-                                 ui::EF_LEFT_MOUSE_BUTTON,
-                                 ui::EF_LEFT_MOUSE_BUTTON);
+  ui::MouseEvent synthetic_event(
+      ui::ET_MOUSE_RELEASED, gfx::Point(), gfx::Point(), ui::EventTimeForNow(),
+      ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
   NotifyClick(synthetic_event);
   return true;
 }
@@ -251,11 +249,9 @@
 bool CustomButton::AcceleratorPressed(const ui::Accelerator& accelerator) {
   SetState(STATE_NORMAL);
   // TODO(beng): remove once NotifyClick takes ui::Event.
-  ui::MouseEvent synthetic_event(ui::ET_MOUSE_RELEASED,
-                                 gfx::Point(),
-                                 gfx::Point(),
-                                 ui::EF_LEFT_MOUSE_BUTTON,
-                                 ui::EF_LEFT_MOUSE_BUTTON);
+  ui::MouseEvent synthetic_event(
+      ui::ET_MOUSE_RELEASED, gfx::Point(), gfx::Point(), ui::EventTimeForNow(),
+      ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
   NotifyClick(synthetic_event);
   return true;
 }
diff --git a/ui/views/controls/button/custom_button_unittest.cc b/ui/views/controls/button/custom_button_unittest.cc
index 69d71873..70841f9 100644
--- a/ui/views/controls/button/custom_button_unittest.cc
+++ b/ui/views/controls/button/custom_button_unittest.cc
@@ -9,6 +9,7 @@
 #include "ui/aura/window.h"
 #include "ui/aura/window_event_dispatcher.h"
 #include "ui/base/layout.h"
+#include "ui/events/event_utils.h"
 #include "ui/gfx/screen.h"
 #include "ui/views/controls/button/checkbox.h"
 #include "ui/views/controls/button/image_button.h"
@@ -71,14 +72,14 @@
   widget->SetContentsView(button);
 
   gfx::Point center(10, 10);
-  button->OnMousePressed(ui::MouseEvent(ui::ET_MOUSE_PRESSED, center, center,
-                                        ui::EF_LEFT_MOUSE_BUTTON,
-                                        ui::EF_LEFT_MOUSE_BUTTON));
+  button->OnMousePressed(ui::MouseEvent(
+      ui::ET_MOUSE_PRESSED, center, center, ui::EventTimeForNow(),
+      ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON));
   EXPECT_EQ(CustomButton::STATE_PRESSED, button->state());
 
-  button->OnMouseReleased(ui::MouseEvent(ui::ET_MOUSE_RELEASED, center, center,
-                                         ui::EF_LEFT_MOUSE_BUTTON,
-                                         ui::EF_LEFT_MOUSE_BUTTON));
+  button->OnMouseReleased(ui::MouseEvent(
+      ui::ET_MOUSE_RELEASED, center, center, ui::EventTimeForNow(),
+      ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON));
   EXPECT_EQ(CustomButton::STATE_HOVERED, button->state());
 
   button->SetEnabled(false);
diff --git a/ui/views/controls/button/radio_button.cc b/ui/views/controls/button/radio_button.cc
index e92b06af..9ad665eb 100644
--- a/ui/views/controls/button/radio_button.cc
+++ b/ui/views/controls/button/radio_button.cc
@@ -7,6 +7,7 @@
 #include "base/logging.h"
 #include "ui/accessibility/ax_view_state.h"
 #include "ui/base/resource/resource_bundle.h"
+#include "ui/events/event_utils.h"
 #include "ui/resources/grit/ui_resources.h"
 #include "ui/views/widget/widget.h"
 
@@ -123,7 +124,8 @@
 void RadioButton::OnFocus() {
   Checkbox::OnFocus();
   SetChecked(true);
-  ui::MouseEvent event(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(), 0, 0);
+  ui::MouseEvent event(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(),
+                       ui::EventTimeForNow(), 0, 0);
   LabelButton::NotifyClick(event);
 }
 
diff --git a/ui/views/controls/combobox/combobox_unittest.cc b/ui/views/controls/combobox/combobox_unittest.cc
index 65ca154..c8116036 100644
--- a/ui/views/controls/combobox/combobox_unittest.cc
+++ b/ui/views/controls/combobox/combobox_unittest.cc
@@ -12,6 +12,7 @@
 #include "ui/base/models/combobox_model.h"
 #include "ui/events/event.h"
 #include "ui/events/event_constants.h"
+#include "ui/events/event_utils.h"
 #include "ui/events/keycodes/keyboard_codes.h"
 #include "ui/views/controls/combobox/combobox_listener.h"
 #include "ui/views/controls/menu/menu_runner.h"
@@ -248,15 +249,13 @@
   }
 
   void PerformClick(const gfx::Point& point) {
-    ui::MouseEvent pressed_event = ui::MouseEvent(ui::ET_MOUSE_PRESSED, point,
-                                                  point,
-                                                  ui::EF_LEFT_MOUSE_BUTTON,
-                                                  ui::EF_LEFT_MOUSE_BUTTON);
+    ui::MouseEvent pressed_event = ui::MouseEvent(
+        ui::ET_MOUSE_PRESSED, point, point, ui::EventTimeForNow(),
+        ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
     widget_->OnMouseEvent(&pressed_event);
-    ui::MouseEvent released_event = ui::MouseEvent(ui::ET_MOUSE_RELEASED, point,
-                                                   point,
-                                                   ui::EF_LEFT_MOUSE_BUTTON,
-                                                   ui::EF_LEFT_MOUSE_BUTTON);
+    ui::MouseEvent released_event = ui::MouseEvent(
+        ui::ET_MOUSE_RELEASED, point, point, ui::EventTimeForNow(),
+        ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
     widget_->OnMouseEvent(&released_event);
   }
 
diff --git a/ui/views/controls/menu/menu_controller.cc b/ui/views/controls/menu/menu_controller.cc
index b74ddce..7e77b13 100644
--- a/ui/views/controls/menu/menu_controller.cc
+++ b/ui/views/controls/menu/menu_controller.cc
@@ -2212,15 +2212,14 @@
       gfx::Point target_point(target_menu_loc);
       View::ConvertPointToTarget(
           target_menu, active_mouse_view, &target_point);
-      ui::MouseEvent mouse_entered_event(ui::ET_MOUSE_ENTERED,
-                                         target_point, target_point,
-                                         0, 0);
+      ui::MouseEvent mouse_entered_event(ui::ET_MOUSE_ENTERED, target_point,
+                                         target_point, ui::EventTimeForNow(), 0,
+                                         0);
       active_mouse_view->OnMouseEntered(mouse_entered_event);
 
-      ui::MouseEvent mouse_pressed_event(ui::ET_MOUSE_PRESSED,
-                                         target_point, target_point,
-                                         event.flags(),
-                                         event.changed_button_flags());
+      ui::MouseEvent mouse_pressed_event(
+          ui::ET_MOUSE_PRESSED, target_point, target_point,
+          ui::EventTimeForNow(), event.flags(), event.changed_button_flags());
       active_mouse_view->OnMousePressed(mouse_pressed_event);
     }
   }
@@ -2228,10 +2227,9 @@
   if (active_mouse_view) {
     gfx::Point target_point(target_menu_loc);
     View::ConvertPointToTarget(target_menu, active_mouse_view, &target_point);
-    ui::MouseEvent mouse_dragged_event(ui::ET_MOUSE_DRAGGED,
-                                       target_point, target_point,
-                                       event.flags(),
-                                       event.changed_button_flags());
+    ui::MouseEvent mouse_dragged_event(
+        ui::ET_MOUSE_DRAGGED, target_point, target_point, ui::EventTimeForNow(),
+        event.flags(), event.changed_button_flags());
     active_mouse_view->OnMouseDragged(mouse_dragged_event);
   }
 }
@@ -2247,7 +2245,8 @@
                              &target_loc);
   View::ConvertPointFromScreen(active_mouse_view, &target_loc);
   ui::MouseEvent release_event(ui::ET_MOUSE_RELEASED, target_loc, target_loc,
-                               event.flags(), event.changed_button_flags());
+                               ui::EventTimeForNow(), event.flags(),
+                               event.changed_button_flags());
   // Reset active mouse view before sending mouse released. That way if it calls
   // back to us, we aren't in a weird state.
   SetActiveMouseView(NULL);
diff --git a/ui/views/controls/native/native_view_host_aura_unittest.cc b/ui/views/controls/native/native_view_host_aura_unittest.cc
index 7f14bd47..5f03fc8 100644
--- a/ui/views/controls/native/native_view_host_aura_unittest.cc
+++ b/ui/views/controls/native/native_view_host_aura_unittest.cc
@@ -9,6 +9,7 @@
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/window.h"
 #include "ui/base/cursor/cursor.h"
+#include "ui/events/event_utils.h"
 #include "ui/views/controls/native/native_view_host.h"
 #include "ui/views/controls/native/native_view_host_test_base.h"
 #include "ui/views/view.h"
@@ -147,7 +148,7 @@
   toplevel()->SetCursor(ui::kCursorHand);
   child()->SetCursor(ui::kCursorWait);
   ui::MouseEvent move_event(ui::ET_MOUSE_MOVED, gfx::Point(0, 0),
-                            gfx::Point(0, 0), 0, 0);
+                            gfx::Point(0, 0), ui::EventTimeForNow(), 0, 0);
 
   EXPECT_EQ(ui::kCursorWait, host()->GetCursor(move_event).native_type());
 
diff --git a/ui/views/controls/prefix_selector.cc b/ui/views/controls/prefix_selector.cc
index 7e23aa9..c4e22402 100644
--- a/ui/views/controls/prefix_selector.cc
+++ b/ui/views/controls/prefix_selector.cc
@@ -147,11 +147,11 @@
 void PrefixSelector::OnCandidateWindowHidden() {
 }
 
-bool PrefixSelector::IsEditingCommandEnabled(int command_id) {
+bool PrefixSelector::IsEditCommandEnabled(int command_id) {
   return false;
 }
 
-void PrefixSelector::ExecuteEditingCommand(int command_id) {
+void PrefixSelector::SetEditCommandForNextKeyEvent(int command_id) {
 }
 
 void PrefixSelector::OnTextInput(const base::string16& text) {
diff --git a/ui/views/controls/prefix_selector.h b/ui/views/controls/prefix_selector.h
index b1af2ad..5986e83 100644
--- a/ui/views/controls/prefix_selector.h
+++ b/ui/views/controls/prefix_selector.h
@@ -55,8 +55,8 @@
   void OnCandidateWindowUpdated() override;
   void OnCandidateWindowHidden() override;
 
-  bool IsEditingCommandEnabled(int command_id) override;
-  void ExecuteEditingCommand(int command_id) override;
+  bool IsEditCommandEnabled(int command_id) override;
+  void SetEditCommandForNextKeyEvent(int command_id) override;
 
  private:
   // Invoked when text is typed. Tries to change the selection appropriately.
diff --git a/ui/views/controls/scrollbar/base_scroll_bar_button.cc b/ui/views/controls/scrollbar/base_scroll_bar_button.cc
index 0a765c4..54dbe75 100644
--- a/ui/views/controls/scrollbar/base_scroll_bar_button.cc
+++ b/ui/views/controls/scrollbar/base_scroll_bar_button.cc
@@ -6,6 +6,7 @@
 
 #include "base/bind.h"
 #include "base/bind_helpers.h"
+#include "ui/events/event_utils.h"
 #include "ui/gfx/screen.h"
 
 namespace views {
@@ -38,9 +39,8 @@
   // TODO(scottmg): Native is wrong: http://crbug.com/133312
   gfx::Point cursor_point =
       gfx::Screen::GetNativeScreen()->GetCursorScreenPoint();
-  ui::MouseEvent event(ui::ET_MOUSE_RELEASED,
-                       cursor_point, cursor_point,
-                       ui::EF_LEFT_MOUSE_BUTTON,
+  ui::MouseEvent event(ui::ET_MOUSE_RELEASED, cursor_point, cursor_point,
+                       ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
                        ui::EF_LEFT_MOUSE_BUTTON);
   Button::NotifyClick(event);
 }
diff --git a/ui/views/controls/single_split_view_unittest.cc b/ui/views/controls/single_split_view_unittest.cc
index e0840bb..f56e497 100644
--- a/ui/views/controls/single_split_view_unittest.cc
+++ b/ui/views/controls/single_split_view_unittest.cc
@@ -6,6 +6,7 @@
 
 #include "base/logging.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "ui/events/event_utils.h"
 #include "ui/views/controls/single_split_view_listener.h"
 
 namespace {
@@ -168,8 +169,8 @@
   split.Layout();
 
   gfx::Point press_point(7, kInitialDividerOffset + kMouseOffset);
-  ui::MouseEvent mouse_pressed(
-      ui::ET_MOUSE_PRESSED, press_point, press_point, 0, 0);
+  ui::MouseEvent mouse_pressed(ui::ET_MOUSE_PRESSED, press_point, press_point,
+                               ui::EventTimeForNow(), 0, 0);
   ASSERT_TRUE(split.OnMousePressed(mouse_pressed));
   EXPECT_EQ(kInitialDividerOffset, split.divider_offset());
   EXPECT_EQ(0, listener.count());
@@ -177,8 +178,8 @@
   // Drag divider to the bottom.
   gfx::Point drag_1_point(
       5, kInitialDividerOffset + kMouseOffset + kMouseMoveDelta);
-  ui::MouseEvent mouse_dragged_1(
-      ui::ET_MOUSE_DRAGGED, drag_1_point, drag_1_point, 0, 0);
+  ui::MouseEvent mouse_dragged_1(ui::ET_MOUSE_DRAGGED, drag_1_point,
+                                 drag_1_point, ui::EventTimeForNow(), 0, 0);
   ASSERT_TRUE(split.OnMouseDragged(mouse_dragged_1));
   EXPECT_EQ(kInitialDividerOffset + kMouseMoveDelta, split.divider_offset());
   EXPECT_EQ(1, listener.count());
@@ -186,8 +187,8 @@
   // Drag divider to the top, beyond first child minimum size.
   gfx::Point drag_2_point(
       7, kMinimumChildSize - 5);
-  ui::MouseEvent mouse_dragged_2(
-      ui::ET_MOUSE_DRAGGED, drag_2_point, drag_2_point, 0,0 );
+  ui::MouseEvent mouse_dragged_2(ui::ET_MOUSE_DRAGGED, drag_2_point,
+                                 drag_2_point, ui::EventTimeForNow(), 0, 0);
   ASSERT_TRUE(split.OnMouseDragged(mouse_dragged_2));
   EXPECT_EQ(kMinimumChildSize, split.divider_offset());
   EXPECT_EQ(2, listener.count());
@@ -195,8 +196,8 @@
   // Drag divider to the bottom, beyond second child minimum size.
   gfx::Point drag_3_point(
       7, kTotalSplitSize - kMinimumChildSize + 5);
-  ui::MouseEvent mouse_dragged_3(
-      ui::ET_MOUSE_DRAGGED, drag_3_point, drag_3_point, 0, 0);
+  ui::MouseEvent mouse_dragged_3(ui::ET_MOUSE_DRAGGED, drag_3_point,
+                                 drag_3_point, ui::EventTimeForNow(), 0, 0);
   ASSERT_TRUE(split.OnMouseDragged(mouse_dragged_3));
   EXPECT_EQ(kTotalSplitSize - kMinimumChildSize - split.GetDividerSize(),
             split.divider_offset());
@@ -205,8 +206,8 @@
   // Drag divider between childs' minimum sizes.
   gfx::Point drag_4_point(
       6, kInitialDividerOffset + kMouseOffset + kMouseMoveDelta * 2);
-  ui::MouseEvent mouse_dragged_4(
-      ui::ET_MOUSE_DRAGGED, drag_4_point, drag_4_point, 0, 0);
+  ui::MouseEvent mouse_dragged_4(ui::ET_MOUSE_DRAGGED, drag_4_point,
+                                 drag_4_point, ui::EventTimeForNow(), 0, 0);
   ASSERT_TRUE(split.OnMouseDragged(mouse_dragged_4));
   EXPECT_EQ(kInitialDividerOffset + kMouseMoveDelta * 2,
             split.divider_offset());
@@ -214,8 +215,8 @@
 
   gfx::Point release_point(
       7, kInitialDividerOffset + kMouseOffset + kMouseMoveDelta * 2);
-  ui::MouseEvent mouse_released(
-      ui::ET_MOUSE_RELEASED, release_point, release_point, 0, 0);
+  ui::MouseEvent mouse_released(ui::ET_MOUSE_RELEASED, release_point,
+                                release_point, ui::EventTimeForNow(), 0, 0);
   split.OnMouseReleased(mouse_released);
   EXPECT_EQ(kInitialDividerOffset + kMouseMoveDelta * 2,
             split.divider_offset());
diff --git a/ui/views/controls/table/table_view_unittest.cc b/ui/views/controls/table/table_view_unittest.cc
index 1c3c40d7..2b991b0 100644
--- a/ui/views/controls/table/table_view_unittest.cc
+++ b/ui/views/controls/table/table_view_unittest.cc
@@ -7,6 +7,7 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/utf_string_conversions.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "ui/events/event_utils.h"
 #include "ui/views/controls/table/table_grouper.h"
 #include "ui/views/controls/table/table_header.h"
 #include "ui/views/controls/table/table_view_observer.h"
@@ -190,7 +191,7 @@
   void ClickOnRow(int row, int flags) {
     const int y = row * table_->row_height();
     const ui::MouseEvent pressed(ui::ET_MOUSE_PRESSED, gfx::Point(0, y),
-                                 gfx::Point(0, y),
+                                 gfx::Point(0, y), ui::EventTimeForNow(),
                                  ui::EF_LEFT_MOUSE_BUTTON | flags,
                                  ui::EF_LEFT_MOUSE_BUTTON);
     table_->OnMousePressed(pressed);
@@ -288,12 +289,13 @@
   EXPECT_NE(0, x);
   // Drag the mouse 1 pixel to the left.
   const ui::MouseEvent pressed(ui::ET_MOUSE_PRESSED, gfx::Point(x, 0),
-                               gfx::Point(x, 0), ui::EF_LEFT_MOUSE_BUTTON,
+                               gfx::Point(x, 0), ui::EventTimeForNow(),
+                               ui::EF_LEFT_MOUSE_BUTTON,
                                ui::EF_LEFT_MOUSE_BUTTON);
   helper_->header()->OnMousePressed(pressed);
   const ui::MouseEvent dragged(ui::ET_MOUSE_DRAGGED, gfx::Point(x - 1, 0),
-                               gfx::Point(x - 1, 0), ui::EF_LEFT_MOUSE_BUTTON,
-                               0);
+                               gfx::Point(x - 1, 0), ui::EventTimeForNow(),
+                               ui::EF_LEFT_MOUSE_BUTTON, 0);
   helper_->header()->OnMouseDragged(dragged);
 
   // This should shrink the first column and pull the second column in.
@@ -385,12 +387,14 @@
   EXPECT_NE(0, x);
   // Press and release the mouse.
   const ui::MouseEvent pressed(ui::ET_MOUSE_PRESSED, gfx::Point(x, 0),
-                               gfx::Point(x, 0), ui::EF_LEFT_MOUSE_BUTTON,
+                               gfx::Point(x, 0), ui::EventTimeForNow(),
+                               ui::EF_LEFT_MOUSE_BUTTON,
                                ui::EF_LEFT_MOUSE_BUTTON);
   // The header must return true, else it won't normally get the release.
   EXPECT_TRUE(helper_->header()->OnMousePressed(pressed));
   const ui::MouseEvent release(ui::ET_MOUSE_RELEASED, gfx::Point(x, 0),
-                               gfx::Point(x, 0), ui::EF_LEFT_MOUSE_BUTTON,
+                               gfx::Point(x, 0), ui::EventTimeForNow(),
+                               ui::EF_LEFT_MOUSE_BUTTON,
                                ui::EF_LEFT_MOUSE_BUTTON);
   helper_->header()->OnMouseReleased(release);
 
diff --git a/ui/views/controls/textfield/textfield.cc b/ui/views/controls/textfield/textfield.cc
index 271f29a..3cef41a 100644
--- a/ui/views/controls/textfield/textfield.cc
+++ b/ui/views/controls/textfield/textfield.cc
@@ -255,6 +255,7 @@
 Textfield::Textfield()
     : model_(new TextfieldModel(this)),
       controller_(NULL),
+      scheduled_edit_command_(kNoCommand),
       read_only_(false),
       default_width_in_chars_(0),
       use_default_text_color_(true),
@@ -665,6 +666,9 @@
 }
 
 bool Textfield::OnKeyPressed(const ui::KeyEvent& event) {
+  int edit_command = scheduled_edit_command_;
+  scheduled_edit_command_ = kNoCommand;
+
   // Since HandleKeyEvent() might destroy |this|, get a weak pointer and verify
   // it isn't null before proceeding.
   base::WeakPtr<Textfield> textfield(weak_ptr_factory_.GetWeakPtr());
@@ -691,9 +695,11 @@
   }
 #endif
 
-  const int command = GetCommandForKeyEvent(event, HasSelection());
-  if (!handled && IsCommandIdEnabled(command)) {
-    ExecuteCommand(command);
+  if (edit_command == kNoCommand)
+    edit_command = GetCommandForKeyEvent(event, HasSelection());
+
+  if (!handled && IsCommandIdEnabled(edit_command)) {
+    ExecuteCommand(edit_command);
     handled = true;
   }
   return handled;
@@ -1606,12 +1612,13 @@
 
 void Textfield::OnCandidateWindowHidden() {}
 
-bool Textfield::IsEditingCommandEnabled(int command_id) {
+bool Textfield::IsEditCommandEnabled(int command_id) {
   return IsCommandIdEnabled(command_id);
 }
 
-void Textfield::ExecuteEditingCommand(int command_id) {
-  ExecuteCommand(command_id);
+void Textfield::SetEditCommandForNextKeyEvent(int command_id) {
+  DCHECK_EQ(kNoCommand, scheduled_edit_command_);
+  scheduled_edit_command_ = command_id;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -1641,7 +1648,11 @@
 void Textfield::UpdateBackgroundColor() {
   const SkColor color = GetBackgroundColor();
   set_background(Background::CreateSolidBackground(color));
-  GetRenderText()->set_background_is_transparent(SkColorGetA(color) != 0xFF);
+  // Disable subpixel rendering when the background color is transparent
+  // because it draws incorrect colors around the glyphs in that case.
+  // See crbug.com/115198
+  GetRenderText()->set_subpixel_rendering_suppressed(
+      SkColorGetA(color) != 0xFF);
   SchedulePaint();
 }
 
diff --git a/ui/views/controls/textfield/textfield.h b/ui/views/controls/textfield/textfield.h
index 677e916..a2da0604 100644
--- a/ui/views/controls/textfield/textfield.h
+++ b/ui/views/controls/textfield/textfield.h
@@ -304,8 +304,8 @@
   void OnCandidateWindowShown() override;
   void OnCandidateWindowUpdated() override;
   void OnCandidateWindowHidden() override;
-  bool IsEditingCommandEnabled(int command_id) override;
-  void ExecuteEditingCommand(int command_id) override;
+  bool IsEditCommandEnabled(int command_id) override;
+  void SetEditCommandForNextKeyEvent(int command_id) override;
 
  protected:
   // Returns the TextfieldModel's text/cursor/selection rendering model.
@@ -390,6 +390,12 @@
   // This is the current listener for events from this Textfield.
   TextfieldController* controller_;
 
+  // If non-zero, an edit command to execute on the next key event. When set,
+  // the key event is still passed to |controller_|, but otherwise ignored in
+  // favor of the edit command. Set via SetEditCommandForNextKeyEvent() during
+  // dispatch of that key event (see comment in TextInputClient).
+  int scheduled_edit_command_;
+
   // True if this Textfield cannot accept input and is read-only.
   bool read_only_;
 
diff --git a/ui/views/controls/textfield/textfield_unittest.cc b/ui/views/controls/textfield/textfield_unittest.cc
index 65f0e650..bd93f6a 100644
--- a/ui/views/controls/textfield/textfield_unittest.cc
+++ b/ui/views/controls/textfield/textfield_unittest.cc
@@ -21,6 +21,7 @@
 #include "ui/base/ui_base_switches.h"
 #include "ui/base/ui_base_switches_util.h"
 #include "ui/events/event.h"
+#include "ui/events/event_utils.h"
 #include "ui/events/keycodes/keyboard_codes.h"
 #include "ui/events/test/event_generator.h"
 #include "ui/gfx/render_text.h"
@@ -86,9 +87,20 @@
   bool OnKeyReleased(const ui::KeyEvent& e) override {
     key_received_ = true;
     key_handled_ = views::Textfield::OnKeyReleased(e);
+    EXPECT_FALSE(key_handled_);  // Textfield doesn't override OnKeyReleased.
     return key_handled_;
   }
 
+  // ui::TextInputClient overrides:
+  void InsertChar(base::char16 ch, int flags) override {
+    views::Textfield::InsertChar(ch, flags);
+#if defined(OS_MACOSX)
+    // On Mac, characters are inserted directly rather than attempting to get a
+    // unicode character from the ui::KeyEvent (which isn't always possible).
+    key_received_ = true;
+#endif
+  }
+
   bool key_handled() const { return key_handled_; }
   bool key_received() const { return key_received_; }
 
@@ -393,20 +405,24 @@
   void MouseClick(const gfx::Rect bound, int x_offset) {
     gfx::Point point(bound.x() + x_offset, bound.y() + bound.height() / 2);
     ui::MouseEvent click(ui::ET_MOUSE_PRESSED, point, point,
-                         ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
+                         ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
+                         ui::EF_LEFT_MOUSE_BUTTON);
     textfield_->OnMousePressed(click);
     ui::MouseEvent release(ui::ET_MOUSE_RELEASED, point, point,
-                           ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
+                           ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
+                           ui::EF_LEFT_MOUSE_BUTTON);
     textfield_->OnMouseReleased(release);
   }
 
   // This is to avoid double/triple click.
   void NonClientMouseClick() {
     ui::MouseEvent click(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(),
+                         ui::EventTimeForNow(),
                          ui::EF_LEFT_MOUSE_BUTTON | ui::EF_IS_NON_CLIENT,
                          ui::EF_LEFT_MOUSE_BUTTON);
     textfield_->OnMousePressed(click);
     ui::MouseEvent release(ui::ET_MOUSE_RELEASED, gfx::Point(), gfx::Point(),
+                           ui::EventTimeForNow(),
                            ui::EF_LEFT_MOUSE_BUTTON | ui::EF_IS_NON_CLIENT,
                            ui::EF_LEFT_MOUSE_BUTTON);
     textfield_->OnMouseReleased(release);
@@ -823,7 +839,8 @@
   widget_->GetFocusManager()->AdvanceFocus(true);
   EXPECT_EQ(3, GetFocusedView()->id());
   ui::MouseEvent click(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(),
-                       ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
+                       ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
+                       ui::EF_LEFT_MOUSE_BUTTON);
   textfield_->OnMousePressed(click);
   EXPECT_EQ(1, GetFocusedView()->id());
 }
@@ -855,13 +872,15 @@
   InitTextfield();
   textfield_->SetText(ASCIIToUTF16("hello world"));
   ui::MouseEvent click(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(),
-                       ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
+                       ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
+                       ui::EF_LEFT_MOUSE_BUTTON);
   ui::MouseEvent release(ui::ET_MOUSE_RELEASED, gfx::Point(), gfx::Point(),
-                         ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
-  ui::MouseEvent double_click(
-      ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(),
-      ui::EF_LEFT_MOUSE_BUTTON | ui::EF_IS_DOUBLE_CLICK,
-      ui::EF_LEFT_MOUSE_BUTTON);
+                         ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
+                         ui::EF_LEFT_MOUSE_BUTTON);
+  ui::MouseEvent double_click(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(),
+                              ui::EventTimeForNow(),
+                              ui::EF_LEFT_MOUSE_BUTTON | ui::EF_IS_DOUBLE_CLICK,
+                              ui::EF_LEFT_MOUSE_BUTTON);
 
   // Test for double click.
   textfield_->OnMousePressed(click);
@@ -890,15 +909,18 @@
   gfx::Point start_point(kStart, 0);
   gfx::Point end_point(kEnd, 0);
   ui::MouseEvent click_a(ui::ET_MOUSE_PRESSED, start_point, start_point,
-                         ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
+                         ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
+                         ui::EF_LEFT_MOUSE_BUTTON);
   ui::MouseEvent click_b(ui::ET_MOUSE_PRESSED, end_point, end_point,
-                         ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
+                         ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
+                         ui::EF_LEFT_MOUSE_BUTTON);
   ui::MouseEvent drag_left(ui::ET_MOUSE_DRAGGED, gfx::Point(), gfx::Point(),
-                           ui::EF_LEFT_MOUSE_BUTTON, 0);
+                           ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON, 0);
   ui::MouseEvent drag_right(ui::ET_MOUSE_DRAGGED, end_point, end_point,
-                            ui::EF_LEFT_MOUSE_BUTTON, 0);
+                            ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON, 0);
   ui::MouseEvent release(ui::ET_MOUSE_RELEASED, end_point, end_point,
-                         ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
+                         ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
+                         ui::EF_LEFT_MOUSE_BUTTON);
   textfield_->OnMousePressed(click_a);
   EXPECT_TRUE(textfield_->GetSelectedText().empty());
   // Check that dragging left selects the beginning of the string.
@@ -1003,7 +1025,7 @@
   textfield_->SetTextInputType(ui::TEXT_INPUT_TYPE_TEXT);
   // Ensure that textfields only initiate drag operations inside the selection.
   ui::MouseEvent press_event(ui::ET_MOUSE_PRESSED, kStringPoint, kStringPoint,
-                             ui::EF_LEFT_MOUSE_BUTTON,
+                             ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
                              ui::EF_LEFT_MOUSE_BUTTON);
   textfield_->OnMousePressed(press_event);
   EXPECT_EQ(ui::DragDropTypes::DRAG_NONE,
@@ -1033,7 +1055,8 @@
   textfield_->SelectRange(gfx::Range(1, 5));
   gfx::Point point(GetCursorPositionX(3), 0);
   ui::MouseEvent click_a(ui::ET_MOUSE_PRESSED, point, point,
-                         ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
+                         ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
+                         ui::EF_LEFT_MOUSE_BUTTON);
   textfield_->OnMousePressed(click_a);
   EXPECT_TRUE(textfield_->CanStartDragForView(textfield_, click_a.location(),
                                               gfx::Point()));
@@ -1064,11 +1087,11 @@
   EXPECT_STR_EQ("", textfield_->text());
   SendKeyEvent(ui::VKEY_Z, false, true);
   EXPECT_STR_EQ("", textfield_->text());
-  SendKeyEvent(ui::VKEY_Y, false, true);
+  SendKeyEvent(ui::VKEY_Z, true, true);
   EXPECT_STR_EQ("hello world", textfield_->text());
-  SendKeyEvent(ui::VKEY_Y, false, true);
+  SendKeyEvent(ui::VKEY_Z, true, true);
   EXPECT_STR_EQ("h welloorld", textfield_->text());
-  SendKeyEvent(ui::VKEY_Y, false, true);
+  SendKeyEvent(ui::VKEY_Z, true, true);
   EXPECT_STR_EQ("h welloorld", textfield_->text());
 }
 
@@ -1086,7 +1109,8 @@
   textfield_->SelectRange(gfx::Range(5, 10));
   gfx::Point point(GetCursorPositionX(7), 0);
   ui::MouseEvent click_a(ui::ET_MOUSE_PRESSED, point, point,
-                         ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
+                         ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
+                         ui::EF_LEFT_MOUSE_BUTTON);
   textfield_->OnMousePressed(click_a);
   EXPECT_TRUE(textfield_->CanStartDragForView(textfield_, click_a.location(),
                                               gfx::Point()));
@@ -1117,11 +1141,11 @@
   EXPECT_STR_EQ("", textfield_->text());
   SendKeyEvent(ui::VKEY_Z, false, true);
   EXPECT_STR_EQ("", textfield_->text());
-  SendKeyEvent(ui::VKEY_Y, false, true);
+  SendKeyEvent(ui::VKEY_Z, true, true);
   EXPECT_STR_EQ("hello world", textfield_->text());
-  SendKeyEvent(ui::VKEY_Y, false, true);
+  SendKeyEvent(ui::VKEY_Z, true, true);
   EXPECT_STR_EQ("h worlellod", textfield_->text());
-  SendKeyEvent(ui::VKEY_Y, false, true);
+  SendKeyEvent(ui::VKEY_Z, true, true);
   EXPECT_STR_EQ("h worlellod", textfield_->text());
 }
 
@@ -1133,7 +1157,8 @@
   textfield_->SelectRange(gfx::Range(6, 10));
   gfx::Point point(GetCursorPositionX(8), 0);
   ui::MouseEvent click(ui::ET_MOUSE_PRESSED, point, point,
-                       ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
+                       ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
+                       ui::EF_LEFT_MOUSE_BUTTON);
   textfield_->OnMousePressed(click);
   ui::OSExchangeData data;
   textfield_->WriteDragDataForView(NULL, click.location(), &data);
@@ -1146,9 +1171,10 @@
   // "Cancel" the drag, via move and release over the selection, and OnDragDone.
   gfx::Point drag_point(GetCursorPositionX(9), 0);
   ui::MouseEvent drag(ui::ET_MOUSE_DRAGGED, drag_point, drag_point,
-                      ui::EF_LEFT_MOUSE_BUTTON, 0);
+                      ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON, 0);
   ui::MouseEvent release(ui::ET_MOUSE_RELEASED, drag_point, drag_point,
-                         ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
+                         ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
+                         ui::EF_LEFT_MOUSE_BUTTON);
   textfield_->OnMouseDragged(drag);
   textfield_->OnMouseReleased(release);
   textfield_->OnDragDone();
@@ -1254,7 +1280,15 @@
   textfield_->clear();
 
   on_before_user_action_ = on_after_user_action_ = 0;
-  SendKeyEvent(ui::VKEY_A);
+
+  // Send a key to trigger MockInputMethod::DispatchKeyEvent(). Note the
+  // specific VKEY isn't used (MockInputMethod will mock a ui::VKEY_PROCESSKEY
+  // whenever it has a test composition). However, on Mac, it can't be a letter
+  // (e.g. VKEY_A) since all native character events on Mac are unicode events
+  // and don't have a meaningful ui::KeyEvent that would trigger
+  // DispatchKeyEvent().
+  SendKeyEvent(ui::VKEY_RETURN);
+
   EXPECT_TRUE(textfield_->key_received());
   EXPECT_FALSE(textfield_->key_handled());
   EXPECT_TRUE(client->HasCompositionText());
@@ -1267,7 +1301,7 @@
   input_method_->SetResultTextForNextKey(UTF8ToUTF16("123"));
   on_before_user_action_ = on_after_user_action_ = 0;
   textfield_->clear();
-  SendKeyEvent(ui::VKEY_A);
+  SendKeyEvent(ui::VKEY_RETURN);
   EXPECT_TRUE(textfield_->key_received());
   EXPECT_FALSE(textfield_->key_handled());
   EXPECT_FALSE(client->HasCompositionText());
@@ -1279,7 +1313,7 @@
   input_method_->Clear();
   input_method_->SetCompositionTextForNextKey(composition);
   textfield_->clear();
-  SendKeyEvent(ui::VKEY_A);
+  SendKeyEvent(ui::VKEY_RETURN);
   EXPECT_TRUE(client->HasCompositionText());
   EXPECT_STR_EQ("0123321456789", textfield_->text());
 
@@ -1330,9 +1364,9 @@
   EXPECT_STR_EQ("", textfield_->text());
   SendKeyEvent(ui::VKEY_Z, false, true);
   EXPECT_STR_EQ("", textfield_->text());
-  SendKeyEvent(ui::VKEY_Y, false, true);
+  SendKeyEvent(ui::VKEY_Z, true, true);
   EXPECT_STR_EQ("a", textfield_->text());
-  SendKeyEvent(ui::VKEY_Y, false, true);
+  SendKeyEvent(ui::VKEY_Z, true, true);
   EXPECT_STR_EQ("a", textfield_->text());
 
   // AppendText
@@ -1341,7 +1375,7 @@
   EXPECT_STR_EQ("ab", textfield_->text());
   SendKeyEvent(ui::VKEY_Z, false, true);
   EXPECT_STR_EQ("a", textfield_->text());
-  SendKeyEvent(ui::VKEY_Y, false, true);
+  SendKeyEvent(ui::VKEY_Z, true, true);
   EXPECT_STR_EQ("ab", textfield_->text());
 
   // SetText
@@ -1353,9 +1387,9 @@
   EXPECT_STR_EQ("abc", textfield_->text());
   SendKeyEvent(ui::VKEY_Z, false, true);
   EXPECT_STR_EQ("ab", textfield_->text());
-  SendKeyEvent(ui::VKEY_Y, false, true);
+  SendKeyEvent(ui::VKEY_Z, true, true);
   EXPECT_STR_EQ("abc", textfield_->text());
-  SendKeyEvent(ui::VKEY_Y, false, true);
+  SendKeyEvent(ui::VKEY_Z, true, true);
   EXPECT_STR_EQ("abc", textfield_->text());
   textfield_->SetText(ASCIIToUTF16("123"));
   textfield_->SetText(ASCIIToUTF16("123"));
@@ -1372,11 +1406,11 @@
   EXPECT_STR_EQ("ab", textfield_->text());
   SendKeyEvent(ui::VKEY_Z, false, true);
   EXPECT_STR_EQ("a", textfield_->text());
-  SendKeyEvent(ui::VKEY_Y, false, true);
+  SendKeyEvent(ui::VKEY_Z, true, true);
   EXPECT_STR_EQ("ab", textfield_->text());
-  SendKeyEvent(ui::VKEY_Y, false, true);
+  SendKeyEvent(ui::VKEY_Z, true, true);
   EXPECT_STR_EQ("123", textfield_->text());
-  SendKeyEvent(ui::VKEY_Y, false, true);
+  SendKeyEvent(ui::VKEY_Z, true, true);
   EXPECT_STR_EQ("1234", textfield_->text());
 
   // Undoing to the same text shouldn't call ContentsChanged.
@@ -1388,13 +1422,13 @@
   EXPECT_STR_EQ("abc", textfield_->text());
   SendKeyEvent(ui::VKEY_Z, false, true);
   EXPECT_STR_EQ("1234", textfield_->text());
-  SendKeyEvent(ui::VKEY_Y, false, true);
+  SendKeyEvent(ui::VKEY_Z, true, true);
   EXPECT_STR_EQ("abc", textfield_->text());
 
   // Delete/Backspace
   SendKeyEvent(ui::VKEY_BACK);
   EXPECT_STR_EQ("ab", textfield_->text());
-  SendKeyEvent(ui::VKEY_HOME);
+  SendHomeEvent(false);
   SendKeyEvent(ui::VKEY_DELETE);
   EXPECT_STR_EQ("b", textfield_->text());
   SendKeyEvent(ui::VKEY_A, false, true);
@@ -1406,16 +1440,38 @@
   EXPECT_STR_EQ("ab", textfield_->text());
   SendKeyEvent(ui::VKEY_Z, false, true);
   EXPECT_STR_EQ("abc", textfield_->text());
-  SendKeyEvent(ui::VKEY_Y, false, true);
+  SendKeyEvent(ui::VKEY_Z, true, true);
   EXPECT_STR_EQ("ab", textfield_->text());
-  SendKeyEvent(ui::VKEY_Y, false, true);
+  SendKeyEvent(ui::VKEY_Z, true, true);
   EXPECT_STR_EQ("b", textfield_->text());
-  SendKeyEvent(ui::VKEY_Y, false, true);
+  SendKeyEvent(ui::VKEY_Z, true, true);
   EXPECT_STR_EQ("", textfield_->text());
-  SendKeyEvent(ui::VKEY_Y, false, true);
+  SendKeyEvent(ui::VKEY_Z, true, true);
   EXPECT_STR_EQ("", textfield_->text());
 }
 
+// Most platforms support Ctrl+Y as an alternative to Ctrl+Shift+Z, but on Mac
+// that is bound to "Show full history", so is not mapped as an editing
+// command. So, on Mac, send Cmd+Shift+Z.
+#if !defined(OS_MACOSX)
+
+// Test that Ctrl+Y works for Redo, as well as Ctrl+Shift+Z.
+TEST_F(TextfieldTest, RedoWithCtrlY) {
+  InitTextfield();
+  SendKeyEvent(ui::VKEY_A);
+  EXPECT_STR_EQ("a", textfield_->text());
+  SendKeyEvent(ui::VKEY_Z, false, true);
+  EXPECT_STR_EQ("", textfield_->text());
+  SendKeyEvent(ui::VKEY_Y, false, true);
+  EXPECT_STR_EQ("a", textfield_->text());
+  SendKeyEvent(ui::VKEY_Z, false, true);
+  EXPECT_STR_EQ("", textfield_->text());
+  SendKeyEvent(ui::VKEY_Z, true, true);
+  EXPECT_STR_EQ("a", textfield_->text());
+}
+
+#endif  // !defined(OS_MACOSX)
+
 TEST_F(TextfieldTest, CutCopyPaste) {
   InitTextfield();
 
@@ -1881,14 +1937,14 @@
   const gfx::Point middle(middle_cursor.x(),
                           middle_cursor.y() + middle_cursor.height() / 2);
   ui::MouseEvent press_event(ui::ET_MOUSE_PRESSED, middle, middle,
-                             ui::EF_LEFT_MOUSE_BUTTON,
+                             ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
                              ui::EF_LEFT_MOUSE_BUTTON);
   textfield_->OnMousePressed(press_event);
   EXPECT_EQ(gfx::Range(4, 7), textfield_->GetSelectedRange());
 
   // Drag the mouse to the beginning of the textfield.
   ui::MouseEvent drag_event(ui::ET_MOUSE_DRAGGED, beginning, beginning,
-                            ui::EF_LEFT_MOUSE_BUTTON, 0);
+                            ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON, 0);
   textfield_->OnMouseDragged(drag_event);
   EXPECT_EQ(gfx::Range(7, 0), textfield_->GetSelectedRange());
 }
@@ -1905,13 +1961,16 @@
 
   // Text selected by the mouse should be placed on the selection clipboard.
   ui::MouseEvent press(ui::ET_MOUSE_PRESSED, point_1, point_1,
-                       ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
+                       ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
+                       ui::EF_LEFT_MOUSE_BUTTON);
   textfield_->OnMousePressed(press);
   ui::MouseEvent drag(ui::ET_MOUSE_DRAGGED, point_3, point_3,
-                      ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
+                      ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
+                      ui::EF_LEFT_MOUSE_BUTTON);
   textfield_->OnMouseDragged(drag);
   ui::MouseEvent release(ui::ET_MOUSE_RELEASED, point_3, point_3,
-                         ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
+                         ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
+                         ui::EF_LEFT_MOUSE_BUTTON);
   textfield_->OnMouseReleased(release);
   EXPECT_EQ(gfx::Range(1, 3), textfield_->GetSelectedRange());
   EXPECT_STR_EQ("12", GetClipboardText(ui::CLIPBOARD_TYPE_SELECTION));
@@ -1925,14 +1984,16 @@
   // Shift-click selection modifications should update the clipboard.
   NonClientMouseClick();
   ui::MouseEvent press_2(ui::ET_MOUSE_PRESSED, point_2, point_2,
-                         ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
+                         ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
+                         ui::EF_LEFT_MOUSE_BUTTON);
   press_2.set_flags(press_2.flags() | ui::EF_SHIFT_DOWN);
 #if defined(USE_X11)
   ui::UpdateX11EventForFlags(&press_2);
 #endif
   textfield_->OnMousePressed(press_2);
   ui::MouseEvent release_2(ui::ET_MOUSE_RELEASED, point_2, point_2,
-                           ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
+                           ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
+                           ui::EF_LEFT_MOUSE_BUTTON);
   textfield_->OnMouseReleased(release_2);
   EXPECT_EQ(gfx::Range(0, 2), textfield_->GetSelectedRange());
   EXPECT_STR_EQ("01", GetClipboardText(ui::CLIPBOARD_TYPE_SELECTION));
@@ -1958,7 +2019,8 @@
   // Middle clicking should paste at the mouse (not cursor) location.
   // The cursor should be placed at the end of the pasted text.
   ui::MouseEvent middle(ui::ET_MOUSE_PRESSED, point_4, point_4,
-                        ui::EF_MIDDLE_MOUSE_BUTTON, ui::EF_MIDDLE_MOUSE_BUTTON);
+                        ui::EventTimeForNow(), ui::EF_MIDDLE_MOUSE_BUTTON,
+                        ui::EF_MIDDLE_MOUSE_BUTTON);
   textfield_->OnMousePressed(middle);
   EXPECT_STR_EQ("01230123", textfield_->text());
   EXPECT_EQ(gfx::Range(8, 8), textfield_->GetSelectedRange());
@@ -1995,13 +2057,15 @@
   textfield_->SetText(ASCIIToUTF16("ab cd ef"));
   gfx::Point word(GetCursorPositionX(4), 0);
   ui::MouseEvent press_word(ui::ET_MOUSE_PRESSED, word, word,
-                            ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
+                            ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
+                            ui::EF_LEFT_MOUSE_BUTTON);
   textfield_->OnMousePressed(press_word);
   ui::MouseEvent release_word(ui::ET_MOUSE_RELEASED, word, word,
-                              ui::EF_LEFT_MOUSE_BUTTON,
+                              ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
                               ui::EF_LEFT_MOUSE_BUTTON);
   textfield_->OnMouseReleased(release_word);
   ui::MouseEvent double_click(ui::ET_MOUSE_PRESSED, word, word,
+                              ui::EventTimeForNow(),
                               ui::EF_LEFT_MOUSE_BUTTON | ui::EF_IS_DOUBLE_CLICK,
                               ui::EF_LEFT_MOUSE_BUTTON);
   textfield_->OnMousePressed(double_click);
@@ -2076,7 +2140,7 @@
   EXPECT_TRUE(controller.target());
 
   // Send a key to trigger OnKeyEvent().
-  SendKeyEvent('X');
+  SendKeyEvent(ui::VKEY_RETURN);
 
   EXPECT_FALSE(controller.target());
 }
diff --git a/ui/views/controls/webview/webview.cc b/ui/views/controls/webview/webview.cc
index 2e4ba84..e872a58b 100644
--- a/ui/views/controls/webview/webview.cc
+++ b/ui/views/controls/webview/webview.cc
@@ -78,7 +78,7 @@
     DCHECK(!is_embedding_fullscreen_widget_);
   }
   AttachWebContents();
-  NotifyMaybeTextInputClientChanged();
+  NotifyMaybeTextInputClientAndAccessibilityChanged();
 }
 
 void WebView::SetEmbedFullscreenWidgetMode(bool enable) {
@@ -266,7 +266,7 @@
 void WebView::RenderProcessExited(content::RenderProcessHost* host,
                                   base::TerminationStatus status,
                                   int exit_code) {
-  NotifyMaybeTextInputClientChanged();
+  NotifyMaybeTextInputClientAndAccessibilityChanged();
 }
 
 void WebView::RenderProcessHostDestroyed(content::RenderProcessHost* host) {
@@ -293,11 +293,11 @@
 // WebView, content::WebContentsObserver implementation:
 
 void WebView::RenderViewReady() {
-  NotifyMaybeTextInputClientChanged();
+  NotifyMaybeTextInputClientAndAccessibilityChanged();
 }
 
 void WebView::RenderViewDeleted(content::RenderViewHost* render_view_host) {
-  NotifyMaybeTextInputClientChanged();
+  NotifyMaybeTextInputClientAndAccessibilityChanged();
 }
 
 void WebView::RenderViewHostChanged(content::RenderViewHost* old_host,
@@ -305,7 +305,7 @@
   FocusManager* const focus_manager = GetFocusManager();
   if (focus_manager && focus_manager->GetFocusedView() == this)
     OnFocus();
-  NotifyMaybeTextInputClientChanged();
+  NotifyMaybeTextInputClientAndAccessibilityChanged();
 }
 
 void WebView::WebContentsDestroyed() {
@@ -313,7 +313,7 @@
     observing_render_process_host_->RemoveObserver(this);
     observing_render_process_host_ = nullptr;
   }
-  NotifyMaybeTextInputClientChanged();
+  NotifyMaybeTextInputClientAndAccessibilityChanged();
 }
 
 void WebView::DidShowFullscreenWidget(int routing_id) {
@@ -332,11 +332,11 @@
 }
 
 void WebView::DidAttachInterstitialPage() {
-  NotifyMaybeTextInputClientChanged();
+  NotifyMaybeTextInputClientAndAccessibilityChanged();
 }
 
 void WebView::DidDetachInterstitialPage() {
-  NotifyMaybeTextInputClientChanged();
+  NotifyMaybeTextInputClientAndAccessibilityChanged();
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -398,14 +398,19 @@
     // the same.  So, do not change attachment.
     OnBoundsChanged(bounds());
   }
-  NotifyMaybeTextInputClientChanged();
+  NotifyMaybeTextInputClientAndAccessibilityChanged();
 }
 
-void WebView::NotifyMaybeTextInputClientChanged() {
+void WebView::NotifyMaybeTextInputClientAndAccessibilityChanged() {
   // Update the TextInputClient as needed; see GetTextInputClient().
   FocusManager* const focus_manager = GetFocusManager();
   if (focus_manager)
     focus_manager->OnTextInputClientChanged(this);
+
+#if defined(OS_CHROMEOS)
+  if (web_contents())
+    NotifyAccessibilityEvent(ui::AX_EVENT_CHILDREN_CHANGED, false);
+#endif  // defined OS_CHROMEOS
 }
 
 content::WebContents* WebView::CreateWebContents(
diff --git a/ui/views/controls/webview/webview.h b/ui/views/controls/webview/webview.h
index af64b72..f97449b8 100644
--- a/ui/views/controls/webview/webview.h
+++ b/ui/views/controls/webview/webview.h
@@ -149,7 +149,7 @@
   void AttachWebContents();
   void DetachWebContents();
   void ReattachForFullscreenChange(bool enter_fullscreen);
-  void NotifyMaybeTextInputClientChanged();
+  void NotifyMaybeTextInputClientAndAccessibilityChanged();
 
   // Create a regular or test web contents (based on whether we're running
   // in a unit test or not).
diff --git a/ui/views/controls/webview/webview_unittest.cc b/ui/views/controls/webview/webview_unittest.cc
index 79479d0..349ad3f 100644
--- a/ui/views/controls/webview/webview_unittest.cc
+++ b/ui/views/controls/webview/webview_unittest.cc
@@ -13,6 +13,7 @@
 #include "content/test/test_content_browser_client.h"
 #include "ui/aura/window.h"
 #include "ui/events/event.h"
+#include "ui/events/event_utils.h"
 #include "ui/views/controls/native/native_view_host.h"
 #include "ui/views/test/test_views_delegate.h"
 #include "ui/views/test/widget_test.h"
@@ -402,11 +403,10 @@
 
   // Send mouse press event to WebView outside the bounds of the holder, and
   // confirm WebView took focus.
-  const ui::MouseEvent click_outside_holder(ui::ET_MOUSE_PRESSED,
-                                            gfx::Point(1, 1),
-                                            gfx::Point(),  // Immaterial.
-                                            ui::EF_LEFT_MOUSE_BUTTON,
-                                            0);
+  const ui::MouseEvent click_outside_holder(
+      ui::ET_MOUSE_PRESSED, gfx::Point(1, 1),
+      gfx::Point(),  // Immaterial.
+      ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON, 0);
   EXPECT_TRUE(static_cast<views::View*>(web_view())->
                   OnMousePressed(click_outside_holder));
   EXPECT_TRUE(web_view()->HasFocus());
@@ -425,11 +425,10 @@
   // WebContents native view to grab the focus instead.  In this test
   // environment, the WebContents native view doesn't include the implementation
   // needed to grab focus, so no focus change will occur.
-  const ui::MouseEvent click_inside_holder(ui::ET_MOUSE_PRESSED,
-                                           web_view()->bounds().CenterPoint(),
-                                           gfx::Point(),  // Immaterial.
-                                           ui::EF_LEFT_MOUSE_BUTTON,
-                                           0);
+  const ui::MouseEvent click_inside_holder(
+      ui::ET_MOUSE_PRESSED, web_view()->bounds().CenterPoint(),
+      gfx::Point(),  // Immaterial.
+      ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON, 0);
   EXPECT_FALSE(static_cast<views::View*>(web_view())->
                   OnMousePressed(click_inside_holder));
   EXPECT_FALSE(web_view()->HasFocus());
diff --git a/ui/views/corewm/tooltip.h b/ui/views/corewm/tooltip.h
index 7fb15bc..b8e6803 100644
--- a/ui/views/corewm/tooltip.h
+++ b/ui/views/corewm/tooltip.h
@@ -25,6 +25,10 @@
  public:
   virtual ~Tooltip() {}
 
+  // Returns the max width of the tooltip when shown at the specified location.
+  virtual int GetMaxWidth(const gfx::Point& location,
+                          aura::Window* context) const = 0;
+
   // Updates the text on the tooltip and resizes to fit.
   virtual void SetText(aura::Window* window,
                        const base::string16& tooltip_text,
diff --git a/ui/views/corewm/tooltip_aura.cc b/ui/views/corewm/tooltip_aura.cc
index ef69bbc..3e98cf16 100644
--- a/ui/views/corewm/tooltip_aura.cc
+++ b/ui/views/corewm/tooltip_aura.cc
@@ -47,9 +47,8 @@
 namespace views {
 namespace corewm {
 
-TooltipAura::TooltipAura(gfx::ScreenType screen_type)
-    : screen_type_(screen_type),
-      widget_(NULL),
+TooltipAura::TooltipAura()
+    : widget_(NULL),
       tooltip_window_(NULL) {
   label_.set_owned_by_client();
   label_.SetMultiLine(true);
@@ -142,19 +141,11 @@
   *text = result;
 }
 
-int TooltipAura::GetMaxWidth(const gfx::Point& location) const {
-  // TODO(varunjain): implementation duplicated in tooltip_manager_aura. Figure
-  // out a way to merge.
-  gfx::Screen* screen = gfx::Screen::GetScreenByType(screen_type_);
-  gfx::Rect display_bounds(screen->GetDisplayNearestPoint(location).bounds());
-  return (display_bounds.width() + 1) / 2;
-}
-
 void TooltipAura::SetTooltipBounds(const gfx::Point& mouse_pos,
                                    const gfx::Size& tooltip_size) {
   gfx::Rect tooltip_rect(mouse_pos, tooltip_size);
   tooltip_rect.Offset(kCursorOffsetX, kCursorOffsetY);
-  gfx::Screen* screen = gfx::Screen::GetScreenByType(screen_type_);
+  gfx::Screen* screen = gfx::Screen::GetScreenFor(tooltip_window_);
   gfx::Rect display_bounds(screen->GetDisplayNearestPoint(mouse_pos).bounds());
 
   // If tooltip is out of bounds on the x axis, we simply shift it
@@ -181,6 +172,13 @@
   }
 }
 
+int TooltipAura::GetMaxWidth(const gfx::Point& location,
+                             aura::Window* context) const {
+  gfx::Screen* screen = gfx::Screen::GetScreenFor(context);
+  gfx::Rect display_bounds(screen->GetDisplayNearestPoint(location).bounds());
+  return std::min(kTooltipMaxWidthPixels, (display_bounds.width() + 1) / 2);
+}
+
 void TooltipAura::SetText(aura::Window* window,
                           const base::string16& tooltip_text,
                           const gfx::Point& location) {
@@ -188,8 +186,8 @@
   int max_width = 0;
   int line_count = 0;
   base::string16 trimmed_text(tooltip_text);
-  TrimTooltipToFit(label_.font_list(), GetMaxWidth(location), &trimmed_text,
-                   &max_width, &line_count);
+  TrimTooltipToFit(label_.font_list(), GetMaxWidth(location, window),
+                   &trimmed_text, &max_width, &line_count);
   label_.SetText(trimmed_text);
 
   if (!widget_) {
diff --git a/ui/views/corewm/tooltip_aura.h b/ui/views/corewm/tooltip_aura.h
index 07952b66..ce92fa2 100644
--- a/ui/views/corewm/tooltip_aura.h
+++ b/ui/views/corewm/tooltip_aura.h
@@ -23,7 +23,7 @@
 // Implementation of Tooltip that shows the tooltip using a Widget and Label.
 class VIEWS_EXPORT TooltipAura : public Tooltip, public WidgetObserver {
  public:
-  explicit TooltipAura(gfx::ScreenType screen_type);
+  TooltipAura();
   ~TooltipAura() override;
 
   // Trims the tooltip to fit in the width |max_width|, setting |text| to the
@@ -37,9 +37,6 @@
                                int* line_count);
 
  private:
-  // Returns the max width of the tooltip when shown at the specified location.
-  int GetMaxWidth(const gfx::Point& location) const;
-
   // Adjusts the bounds given by the arguments to fit inside the desktop
   // and applies the adjusted bounds to the label_.
   void SetTooltipBounds(const gfx::Point& mouse_pos,
@@ -49,6 +46,8 @@
   void DestroyWidget();
 
   // Tooltip:
+  int GetMaxWidth(const gfx::Point& location,
+                  aura::Window* context) const override;
   void SetText(aura::Window* window,
                const base::string16& tooltip_text,
                const gfx::Point& location) override;
@@ -59,8 +58,6 @@
   // WidgetObserver:
   void OnWidgetDestroying(Widget* widget) override;
 
-  const gfx::ScreenType screen_type_;
-
   // The label showing the tooltip.
   Label label_;
 
diff --git a/ui/views/corewm/tooltip_controller.cc b/ui/views/corewm/tooltip_controller.cc
index 0408f5d3..8af7199e 100644
--- a/ui/views/corewm/tooltip_controller.cc
+++ b/ui/views/corewm/tooltip_controller.cc
@@ -17,6 +17,7 @@
 #include "ui/gfx/font.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/screen.h"
+#include "ui/gfx/text_elider.h"
 #include "ui/views/corewm/tooltip.h"
 #include "ui/views/widget/tooltip_manager.h"
 #include "ui/wm/public/drag_drop_client.h"
@@ -27,6 +28,7 @@
 
 const int kTooltipTimeoutMs = 500;
 const int kDefaultTooltipShownTimeoutMs = 10000;
+const size_t kMaxTooltipLength = 1024;
 
 // Returns true if |target| is a valid window to get the tooltip from.
 // |event_target| is the original target from the event and |target| the window
@@ -129,6 +131,11 @@
     tooltip_window_->RemoveObserver(this);
 }
 
+int TooltipController::GetMaxWidth(const gfx::Point& location,
+                                   gfx::NativeView context) const {
+  return tooltip_->GetMaxWidth(location, context);
+}
+
 void TooltipController::UpdateTooltip(aura::Window* target) {
   // If tooltip is visible, we may want to hide it. If it is not, we are ok.
   if (tooltip_window_ == target && tooltip_->IsVisible())
@@ -298,8 +305,8 @@
   if (tooltip_text_ != tooltip_text || !tooltip_->IsVisible() || ids_differ) {
     tooltip_shown_timer_.Stop();
     tooltip_text_ = tooltip_text;
-    base::string16 trimmed_text(tooltip_text_);
-    views::TooltipManager::TrimTooltipText(&trimmed_text);
+    base::string16 trimmed_text =
+        gfx::TruncateString(tooltip_text_, kMaxTooltipLength, gfx::WORD_BREAK);
     // If the string consists entirely of whitespace, then don't both showing it
     // (an empty tooltip is useless).
     base::string16 whitespace_removed_text;
diff --git a/ui/views/corewm/tooltip_controller.h b/ui/views/corewm/tooltip_controller.h
index 87500b347..2f8c7ef6 100644
--- a/ui/views/corewm/tooltip_controller.h
+++ b/ui/views/corewm/tooltip_controller.h
@@ -38,6 +38,8 @@
   ~TooltipController() override;
 
   // Overridden from aura::client::TooltipClient.
+  int GetMaxWidth(const gfx::Point& location,
+                  aura::Window* context) const override;
   void UpdateTooltip(aura::Window* target) override;
   void SetTooltipShownTimeout(aura::Window* target, int timeout_in_ms) override;
   void SetTooltipsEnabled(bool enable) override;
diff --git a/ui/views/corewm/tooltip_controller_unittest.cc b/ui/views/corewm/tooltip_controller_unittest.cc
index 0ce3fb4a..ad341d96 100644
--- a/ui/views/corewm/tooltip_controller_unittest.cc
+++ b/ui/views/corewm/tooltip_controller_unittest.cc
@@ -90,7 +90,7 @@
 #if defined(OS_CHROMEOS)
     controller_.reset(new TooltipController(
           scoped_ptr<views::corewm::Tooltip>(
-              new views::corewm::TooltipAura(gfx::SCREEN_TYPE_ALTERNATE))));
+              new views::corewm::TooltipAura)));
     root_window()->AddPreTargetHandler(controller_.get());
     SetTooltipClient(root_window(), controller_.get());
 #endif
@@ -532,6 +532,10 @@
   const base::string16& tooltip_text() const { return tooltip_text_; }
 
   // Tooltip:
+  int GetMaxWidth(const gfx::Point& location,
+                  aura::Window* context) const override {
+    return 100;
+  }
   void SetText(aura::Window* window,
                const base::string16& tooltip_text,
                const gfx::Point& location) override {
diff --git a/ui/views/corewm/tooltip_win.cc b/ui/views/corewm/tooltip_win.cc
index 15d90ac..46635d2 100644
--- a/ui/views/corewm/tooltip_win.cc
+++ b/ui/views/corewm/tooltip_win.cc
@@ -95,6 +95,16 @@
                0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
 }
 
+int TooltipWin::GetMaxWidth(const gfx::Point& location,
+                            aura::Window* context) const {
+  // This code only runs for non-metro, so GetNativeScreen() is fine.
+  const gfx::Point screen_point = gfx::win::DIPToScreenPoint(location);
+  gfx::Display display(
+      gfx::Screen::GetNativeScreen()->GetDisplayNearestPoint(screen_point));
+  const gfx::Rect monitor_bounds = display.bounds();
+  return (monitor_bounds.width() + 1) / 2;
+}
+
 void TooltipWin::SetText(aura::Window* window,
                          const base::string16& tooltip_text,
                          const gfx::Point& location) {
@@ -116,12 +126,7 @@
   SendMessage(tooltip_hwnd_, TTM_SETTOOLINFO, 0,
               reinterpret_cast<LPARAM>(&toolinfo_));
 
-  // This code only runs for non-metro, so GetNativeScreen() is fine.
-  const gfx::Point screen_point = gfx::win::DIPToScreenPoint(location_);
-  gfx::Display display(
-      gfx::Screen::GetNativeScreen()->GetDisplayNearestPoint(screen_point));
-  const gfx::Rect monitor_bounds = display.bounds();
-  int max_width = (monitor_bounds.width() + 1) / 2;
+  int max_width = GetMaxWidth(location_, window);
   SendMessage(tooltip_hwnd_, TTM_SETMAXTIPWIDTH, 0, max_width);
 }
 
diff --git a/ui/views/corewm/tooltip_win.h b/ui/views/corewm/tooltip_win.h
index 3f3e51bb..9fac064 100644
--- a/ui/views/corewm/tooltip_win.h
+++ b/ui/views/corewm/tooltip_win.h
@@ -22,7 +22,7 @@
 class VIEWS_EXPORT TooltipWin : public Tooltip {
  public:
   explicit TooltipWin(HWND parent);
-  virtual ~TooltipWin();
+  ~TooltipWin() override;
 
   // HandleNotify() is forwarded from DesktopWindowTreeHostWin to keep the
   // native tooltip in sync.
@@ -37,12 +37,14 @@
   void PositionTooltip();
 
   // Tooltip:
-  virtual void SetText(aura::Window* window,
-                       const base::string16& tooltip_text,
-                       const gfx::Point& location) override;
-  virtual void Show() override;
-  virtual void Hide() override;
-  virtual bool IsVisible() override;
+  int GetMaxWidth(const gfx::Point& location,
+                  aura::Window* context) const override;
+  void SetText(aura::Window* window,
+               const base::string16& tooltip_text,
+               const gfx::Point& location) override;
+  void Show() override;
+  void Hide() override;
+  bool IsVisible() override;
 
   // The window |tooltip_hwnd_| is parented to.
   HWND parent_hwnd_;
diff --git a/ui/views/examples/BUILD.gn b/ui/views/examples/BUILD.gn
index c4289d8..455b06c 100644
--- a/ui/views/examples/BUILD.gn
+++ b/ui/views/examples/BUILD.gn
@@ -61,6 +61,9 @@
     "widget_example.h",
   ]
 
+  # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+  configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
+
   defines = [ "VIEWS_EXAMPLES_IMPLEMENTATION" ]
 
   deps = [
@@ -78,9 +81,6 @@
 
   if (is_win) {
     deps += [ "//third_party/wtl" ]
-
-    # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
-    cflags = [ "/wd4267" ]
   }
 
   if (use_aura) {
diff --git a/ui/views/ime/input_method_bridge.cc b/ui/views/ime/input_method_bridge.cc
index 785714aa..9abb1e20 100644
--- a/ui/views/ime/input_method_bridge.cc
+++ b/ui/views/ime/input_method_bridge.cc
@@ -320,11 +320,15 @@
 void InputMethodBridge::OnCandidateWindowHidden() {
 }
 
-bool InputMethodBridge::IsEditingCommandEnabled(int command_id) {
-  return false;
+bool InputMethodBridge::IsEditCommandEnabled(int command_id) {
+  TextInputClient* client = GetTextInputClient();
+  return client ? client->IsEditCommandEnabled(command_id) : false;
 }
 
-void InputMethodBridge::ExecuteEditingCommand(int command_id) {
+void InputMethodBridge::SetEditCommandForNextKeyEvent(int command_id) {
+  TextInputClient* client = GetTextInputClient();
+  if (client)
+    client->SetEditCommandForNextKeyEvent(command_id);
 }
 
 // Overridden from FocusChangeListener.
diff --git a/ui/views/ime/input_method_bridge.h b/ui/views/ime/input_method_bridge.h
index 23f7eca..87f6179 100644
--- a/ui/views/ime/input_method_bridge.h
+++ b/ui/views/ime/input_method_bridge.h
@@ -79,8 +79,8 @@
   void OnCandidateWindowShown() override;
   void OnCandidateWindowUpdated() override;
   void OnCandidateWindowHidden() override;
-  bool IsEditingCommandEnabled(int command_id) override;
-  void ExecuteEditingCommand(int command_id) override;
+  bool IsEditCommandEnabled(int command_id) override;
+  void SetEditCommandForNextKeyEvent(int command_id) override;
 
   // Overridden from FocusChangeListener.
   void OnWillChangeFocus(View* focused_before, View* focused) override;
diff --git a/ui/views/test/widget_test_mac.mm b/ui/views/test/widget_test_mac.mm
index ae1497b..523e31d 100644
--- a/ui/views/test/widget_test_mac.mm
+++ b/ui/views/test/widget_test_mac.mm
@@ -79,7 +79,7 @@
 
 // static
 scoped_ptr<WidgetTest::FakeActivation> WidgetTest::FakeWidgetIsActiveAlways() {
-  return scoped_ptr<FakeActivation>(new FakeActivationMac);
+  return make_scoped_ptr(new FakeActivationMac);
 }
 
 }  // namespace test
diff --git a/ui/views/touchui/touch_selection_controller_impl_unittest.cc b/ui/views/touchui/touch_selection_controller_impl_unittest.cc
index 1e2254a..3c9a9cc 100644
--- a/ui/views/touchui/touch_selection_controller_impl_unittest.cc
+++ b/ui/views/touchui/touch_selection_controller_impl_unittest.cc
@@ -10,6 +10,7 @@
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/base/touch/touch_editing_controller.h"
 #include "ui/base/ui_base_switches.h"
+#include "ui/events/event_utils.h"
 #include "ui/events/test/event_generator.h"
 #include "ui/gfx/canvas.h"
 #include "ui/gfx/geometry/point.h"
@@ -736,7 +737,7 @@
   StartTouchEditing();
   gfx::Point test_point = GetCursorHandleDragPoint();
   ui::MouseEvent test_event1(ui::ET_MOUSE_MOVED, test_point, test_point,
-                             ui::EF_NONE, ui::EF_NONE);
+                             ui::EventTimeForNow(), ui::EF_NONE, ui::EF_NONE);
   EXPECT_EQ(GetCursorHandleNativeView(),
             targeter->FindTargetForEvent(root, &test_event1));
   EndTouchEditing();
@@ -749,14 +750,14 @@
   // above the second window.
   StartTouchEditing();
   ui::MouseEvent test_event2(ui::ET_MOUSE_MOVED, test_point, test_point,
-                             ui::EF_NONE, ui::EF_NONE);
+                             ui::EventTimeForNow(), ui::EF_NONE, ui::EF_NONE);
   EXPECT_EQ(window2, targeter->FindTargetForEvent(root, &test_event2));
 
   // Move the first window to top and check that the handle is kept above the
   // first window.
   window1->GetRootWindow()->StackChildAtTop(window1);
   ui::MouseEvent test_event3(ui::ET_MOUSE_MOVED, test_point, test_point,
-                             ui::EF_NONE, ui::EF_NONE);
+                             ui::EventTimeForNow(), ui::EF_NONE, ui::EF_NONE);
   EXPECT_EQ(GetCursorHandleNativeView(),
             targeter->FindTargetForEvent(root, &test_event3));
 }
diff --git a/ui/views/view_unittest.cc b/ui/views/view_unittest.cc
index 2ea481c..cc15602 100644
--- a/ui/views/view_unittest.cc
+++ b/ui/views/view_unittest.cc
@@ -17,6 +17,7 @@
 #include "ui/compositor/layer_animator.h"
 #include "ui/compositor/test/draw_waiter_for_test.h"
 #include "ui/events/event.h"
+#include "ui/events/event_utils.h"
 #include "ui/events/keycodes/keyboard_codes.h"
 #include "ui/gfx/canvas.h"
 #include "ui/gfx/path.h"
@@ -340,7 +341,7 @@
   v2->Reset();
 
   gfx::Point p1(110, 120);
-  ui::MouseEvent pressed(ui::ET_MOUSE_PRESSED, p1, p1,
+  ui::MouseEvent pressed(ui::ET_MOUSE_PRESSED, p1, p1, ui::EventTimeForNow(),
                          ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
   root->OnMousePressed(pressed);
   EXPECT_EQ(v2->last_mouse_event_type_, ui::ET_MOUSE_PRESSED);
@@ -353,7 +354,7 @@
   v1->Reset();
   v2->Reset();
   gfx::Point p2(50, 40);
-  ui::MouseEvent dragged(ui::ET_MOUSE_DRAGGED, p2, p2,
+  ui::MouseEvent dragged(ui::ET_MOUSE_DRAGGED, p2, p2, ui::EventTimeForNow(),
                          ui::EF_LEFT_MOUSE_BUTTON, 0);
   root->OnMouseDragged(dragged);
   EXPECT_EQ(v2->last_mouse_event_type_, ui::ET_MOUSE_DRAGGED);
@@ -365,8 +366,8 @@
   // Releasted event out of bounds. Should still go to v2
   v1->Reset();
   v2->Reset();
-  ui::MouseEvent released(ui::ET_MOUSE_RELEASED, gfx::Point(), gfx::Point(), 0,
-                          0);
+  ui::MouseEvent released(ui::ET_MOUSE_RELEASED, gfx::Point(), gfx::Point(),
+                          ui::EventTimeForNow(), 0, 0);
   root->OnMouseDragged(released);
   EXPECT_EQ(v2->last_mouse_event_type_, ui::ET_MOUSE_RELEASED);
   EXPECT_EQ(v2->location_.x(), -100);
@@ -401,7 +402,8 @@
   v2->delete_on_pressed_ = true;
   gfx::Point point(110, 120);
   ui::MouseEvent pressed(ui::ET_MOUSE_PRESSED, point, point,
-                         ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
+                         ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
+                         ui::EF_LEFT_MOUSE_BUTTON);
   root->OnMousePressed(pressed);
   EXPECT_EQ(0, v1->child_count());
 
@@ -1056,7 +1058,7 @@
 
   // Move the mouse in v111.
   gfx::Point p1(6, 6);
-  ui::MouseEvent move1(ui::ET_MOUSE_MOVED, p1, p1, 0, 0);
+  ui::MouseEvent move1(ui::ET_MOUSE_MOVED, p1, p1, ui::EventTimeForNow(), 0, 0);
   root_view->OnMouseMoved(move1);
   EXPECT_TRUE(v111->received_mouse_enter_);
   EXPECT_FALSE(v11->last_mouse_event_type_);
@@ -1067,7 +1069,7 @@
 
   // Now, move into v121.
   gfx::Point p2(65, 21);
-  ui::MouseEvent move2(ui::ET_MOUSE_MOVED, p2, p2, 0, 0);
+  ui::MouseEvent move2(ui::ET_MOUSE_MOVED, p2, p2, ui::EventTimeForNow(), 0, 0);
   root_view->OnMouseMoved(move2);
   EXPECT_TRUE(v111->received_mouse_exit_);
   EXPECT_TRUE(v121->received_mouse_enter_);
@@ -1078,7 +1080,7 @@
 
   // Now, move into v11.
   gfx::Point p3(1, 1);
-  ui::MouseEvent move3(ui::ET_MOUSE_MOVED, p3, p3, 0, 0);
+  ui::MouseEvent move3(ui::ET_MOUSE_MOVED, p3, p3, ui::EventTimeForNow(), 0, 0);
   root_view->OnMouseMoved(move3);
   EXPECT_TRUE(v121->received_mouse_exit_);
   EXPECT_TRUE(v11->received_mouse_enter_);
@@ -1089,7 +1091,7 @@
 
   // Move to v21.
   gfx::Point p4(121, 15);
-  ui::MouseEvent move4(ui::ET_MOUSE_MOVED, p4, p4, 0, 0);
+  ui::MouseEvent move4(ui::ET_MOUSE_MOVED, p4, p4, ui::EventTimeForNow(), 0, 0);
   root_view->OnMouseMoved(move4);
   EXPECT_TRUE(v21->received_mouse_enter_);
   EXPECT_FALSE(v2->last_mouse_event_type_);
@@ -1102,7 +1104,7 @@
 
   // Move to v1.
   gfx::Point p5(21, 0);
-  ui::MouseEvent move5(ui::ET_MOUSE_MOVED, p5, p5, 0, 0);
+  ui::MouseEvent move5(ui::ET_MOUSE_MOVED, p5, p5, ui::EventTimeForNow(), 0, 0);
   root_view->OnMouseMoved(move5);
   EXPECT_TRUE(v21->received_mouse_exit_);
   EXPECT_TRUE(v1->received_mouse_enter_);
@@ -1112,7 +1114,8 @@
 
   // Now, move into v11.
   gfx::Point p6(15, 15);
-  ui::MouseEvent mouse6(ui::ET_MOUSE_MOVED, p6, p6, 0, 0);
+  ui::MouseEvent mouse6(ui::ET_MOUSE_MOVED, p6, p6, ui::EventTimeForNow(), 0,
+                        0);
   root_view->OnMouseMoved(mouse6);
   EXPECT_TRUE(v11->received_mouse_enter_);
   EXPECT_FALSE(v1->last_mouse_event_type_);
@@ -1124,7 +1127,8 @@
   // and the mouse remains inside |v1| the whole time, it receives another ENTER
   // when the mouse leaves v11.
   gfx::Point p7(21, 0);
-  ui::MouseEvent mouse7(ui::ET_MOUSE_MOVED, p7, p7, 0, 0);
+  ui::MouseEvent mouse7(ui::ET_MOUSE_MOVED, p7, p7, ui::EventTimeForNow(), 0,
+                        0);
   root_view->OnMouseMoved(mouse7);
   EXPECT_TRUE(v11->received_mouse_exit_);
   EXPECT_FALSE(v1->received_mouse_enter_);
@@ -1646,7 +1650,7 @@
   v2->Reset();
 
   gfx::Point p1(110, 210);
-  ui::MouseEvent pressed(ui::ET_MOUSE_PRESSED, p1, p1,
+  ui::MouseEvent pressed(ui::ET_MOUSE_PRESSED, p1, p1, ui::EventTimeForNow(),
                          ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
   root->OnMousePressed(pressed);
   EXPECT_EQ(0, v1->last_mouse_event_type_);
@@ -1654,8 +1658,8 @@
   EXPECT_EQ(190, v2->location_.x());
   EXPECT_EQ(10, v2->location_.y());
 
-  ui::MouseEvent released(ui::ET_MOUSE_RELEASED, gfx::Point(), gfx::Point(), 0,
-                          0);
+  ui::MouseEvent released(ui::ET_MOUSE_RELEASED, gfx::Point(), gfx::Point(),
+                          ui::EventTimeForNow(), 0, 0);
   root->OnMouseReleased(released);
 
   // Now rotate |v2| inside |v1| clockwise.
@@ -1671,7 +1675,7 @@
   v2->Reset();
 
   gfx::Point point2(110, 320);
-  ui::MouseEvent p2(ui::ET_MOUSE_PRESSED, point2, point2,
+  ui::MouseEvent p2(ui::ET_MOUSE_PRESSED, point2, point2, ui::EventTimeForNow(),
                     ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
   root->OnMousePressed(p2);
   EXPECT_EQ(0, v1->last_mouse_event_type_);
@@ -1707,7 +1711,7 @@
   v3->Reset();
 
   gfx::Point point(112, 110);
-  ui::MouseEvent p3(ui::ET_MOUSE_PRESSED, point, point,
+  ui::MouseEvent p3(ui::ET_MOUSE_PRESSED, point, point, ui::EventTimeForNow(),
                     ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
   root->OnMousePressed(p3);
 
@@ -1746,7 +1750,7 @@
   // |v3| now occupies (120, 120) to (144, 130) in |root|.
 
   gfx::Point point3(124, 125);
-  ui::MouseEvent p4(ui::ET_MOUSE_PRESSED, point3, point3,
+  ui::MouseEvent p4(ui::ET_MOUSE_PRESSED, point3, point3, ui::EventTimeForNow(),
                     ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
   root->OnMousePressed(p4);
 
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.cc b/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.cc
index 3874e67..784446e2 100644
--- a/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.cc
+++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.cc
@@ -289,7 +289,7 @@
 }
 
 scoped_ptr<corewm::Tooltip> DesktopWindowTreeHostX11::CreateTooltip() {
-  return make_scoped_ptr(new corewm::TooltipAura(gfx::SCREEN_TYPE_NATIVE));
+  return make_scoped_ptr(new corewm::TooltipAura);
 }
 
 scoped_ptr<aura::client::DragDropClient>
diff --git a/ui/views/widget/root_view.cc b/ui/views/widget/root_view.cc
index e092d73..6e45f356 100644
--- a/ui/views/widget/root_view.cc
+++ b/ui/views/widget/root_view.cc
@@ -14,6 +14,7 @@
 #include "ui/base/ui_base_switches_util.h"
 #include "ui/compositor/layer.h"
 #include "ui/events/event.h"
+#include "ui/events/event_utils.h"
 #include "ui/events/keycodes/keyboard_codes.h"
 #include "ui/gfx/canvas.h"
 #include "ui/views/drag_controller.h"
@@ -454,10 +455,9 @@
     // Synthesize a release event for UpdateCursor.
     if (mouse_pressed_handler_) {
       gfx::Point last_point(last_mouse_event_x_, last_mouse_event_y_);
-      ui::MouseEvent release_event(ui::ET_MOUSE_RELEASED,
-                                   last_point, last_point,
-                                   last_mouse_event_flags_,
-                                   0);
+      ui::MouseEvent release_event(ui::ET_MOUSE_RELEASED, last_point,
+                                   last_point, ui::EventTimeForNow(),
+                                   last_mouse_event_flags_, 0);
       UpdateCursor(release_event);
     }
     // We allow the view to delete us from OnMouseCaptureLost. As such,
diff --git a/ui/views/widget/tooltip_manager.cc b/ui/views/widget/tooltip_manager.cc
index b2e68271..c5e641a9cc 100644
--- a/ui/views/widget/tooltip_manager.cc
+++ b/ui/views/widget/tooltip_manager.cc
@@ -4,33 +4,9 @@
 
 #include "ui/views/widget/tooltip_manager.h"
 
-#include "ui/gfx/geometry/rect.h"
-#include "ui/gfx/screen.h"
-#include "ui/gfx/text_elider.h"
-
 namespace views {
 
-const size_t kMaxTooltipLength = 1024;
-
 // static
 const char TooltipManager::kGroupingPropertyKey[] = "GroupingPropertyKey";
 
-// static
-int TooltipManager::GetMaxWidth(int x, int y, gfx::NativeView context) {
-  return GetMaxWidth(gfx::Screen::GetScreenFor(context)->GetDisplayNearestPoint(
-                         gfx::Point(x, y)));
-}
-
-// static
-int TooltipManager::GetMaxWidth(const gfx::Display& display) {
-  return (display.bounds().width() + 1) / 2;
-}
-
-// static
-void TooltipManager::TrimTooltipText(base::string16* text) {
-  // Clamp the tooltip length to kMaxTooltipLength so that we don't
-  // accidentally DOS the user with a mega tooltip.
-  *text = gfx::TruncateString(*text, kMaxTooltipLength, gfx::WORD_BREAK);
-}
-
 }  // namespace views
diff --git a/ui/views/widget/tooltip_manager.h b/ui/views/widget/tooltip_manager.h
index 828d4f48..4145db4d 100644
--- a/ui/views/widget/tooltip_manager.h
+++ b/ui/views/widget/tooltip_manager.h
@@ -15,6 +15,7 @@
 namespace gfx {
 class Display;
 class FontList;
+class Point;
 }  // namespace gfx
 
 namespace views {
@@ -38,17 +39,11 @@
   TooltipManager() {}
   virtual ~TooltipManager() {}
 
-  // Returns the maximum width of the tooltip. |x| and |y| give the location
+  // Returns the maximum width of the tooltip. |point| gives the location
   // the tooltip is to be displayed on in screen coordinates. |context| is
   // used to determine which gfx::Screen should be used.
-  static int GetMaxWidth(int x, int y, gfx::NativeView context);
-
-  // Same as GetMaxWidth(), but takes a Display.
-  static int GetMaxWidth(const gfx::Display& display);
-
-  // If necessary trims the text of a tooltip to ensure we don't try to display
-  // a mega-tooltip.
-  static void TrimTooltipText(base::string16* text);
+  virtual int GetMaxWidth(const gfx::Point& location,
+                          gfx::NativeView context) const = 0;
 
   // Returns the font list used for tooltips.
   virtual const gfx::FontList& GetFontList() const = 0;
diff --git a/ui/views/widget/tooltip_manager_aura.cc b/ui/views/widget/tooltip_manager_aura.cc
index bb4ead6..8f22401 100644
--- a/ui/views/widget/tooltip_manager_aura.cc
+++ b/ui/views/widget/tooltip_manager_aura.cc
@@ -81,6 +81,12 @@
   return GetDefaultFontList();
 }
 
+int TooltipManagerAura::GetMaxWidth(const gfx::Point& point,
+                                    aura::Window* context) const {
+  return aura::client::GetTooltipClient(context->GetRootWindow())->
+      GetMaxWidth(point, context);
+}
+
 void TooltipManagerAura::UpdateTooltip() {
   aura::Window* root_window = GetWindow()->GetRootWindow();
   if (aura::client::GetTooltipClient(root_window)) {
diff --git a/ui/views/widget/tooltip_manager_aura.h b/ui/views/widget/tooltip_manager_aura.h
index a7ac414..87c0f4e 100644
--- a/ui/views/widget/tooltip_manager_aura.h
+++ b/ui/views/widget/tooltip_manager_aura.h
@@ -38,6 +38,8 @@
   static const gfx::FontList& GetDefaultFontList();
 
   // TooltipManager:
+  int GetMaxWidth(const gfx::Point& location,
+                  aura::Window* context) const override;
   const gfx::FontList& GetFontList() const override;
   void UpdateTooltip() override;
   void TooltipTextChanged(View* view) override;
diff --git a/ui/views/widget/widget.cc b/ui/views/widget/widget.cc
index 76dc14a..ad97464 100644
--- a/ui/views/widget/widget.cc
+++ b/ui/views/widget/widget.cc
@@ -16,6 +16,7 @@
 #include "ui/compositor/compositor.h"
 #include "ui/compositor/layer.h"
 #include "ui/events/event.h"
+#include "ui/events/event_utils.h"
 #include "ui/gfx/image/image_skia.h"
 #include "ui/gfx/screen.h"
 #include "ui/views/controls/menu/menu_controller.h"
@@ -991,9 +992,8 @@
 
 void Widget::SynthesizeMouseMoveEvent() {
   last_mouse_event_was_move_ = false;
-  ui::MouseEvent mouse_event(ui::ET_MOUSE_MOVED,
-                             last_mouse_event_position_,
-                             last_mouse_event_position_,
+  ui::MouseEvent mouse_event(ui::ET_MOUSE_MOVED, last_mouse_event_position_,
+                             last_mouse_event_position_, ui::EventTimeForNow(),
                              ui::EF_IS_SYNTHESIZED, 0);
   root_view_->OnMouseMoved(mouse_event);
 }
diff --git a/ui/views/widget/widget_interactive_uitest.cc b/ui/views/widget/widget_interactive_uitest.cc
index 84536030..9a2381bd 100644
--- a/ui/views/widget/widget_interactive_uitest.cc
+++ b/ui/views/widget/widget_interactive_uitest.cc
@@ -13,6 +13,7 @@
 #include "ui/base/ui_base_paths.h"
 #include "ui/base/ui_base_switches.h"
 #include "ui/events/event_processor.h"
+#include "ui/events/event_utils.h"
 #include "ui/events/test/event_generator.h"
 #include "ui/gfx/native_widget_types.h"
 #include "ui/gl/gl_surface.h"
@@ -334,7 +335,8 @@
   // By default, mouse release removes capture.
   gfx::Point click_location(45, 15);
   ui::MouseEvent release(ui::ET_MOUSE_RELEASED, click_location, click_location,
-                         ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
+                         ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
+                         ui::EF_LEFT_MOUSE_BUTTON);
   toplevel->OnMouseEvent(&release);
   EXPECT_FALSE(toplevel->HasCapture());
 
@@ -385,9 +387,11 @@
   gfx::Point click_location(45, 15);
 
   ui::MouseEvent press(ui::ET_MOUSE_PRESSED, click_location, click_location,
-                       ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
+                       ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
+                       ui::EF_LEFT_MOUSE_BUTTON);
   ui::MouseEvent release(ui::ET_MOUSE_RELEASED, click_location, click_location,
-                         ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
+                         ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
+                         ui::EF_LEFT_MOUSE_BUTTON);
 
   EXPECT_TRUE(toplevel->HasCapture());
 
@@ -432,16 +436,15 @@
   first->Show();
 
   gfx::Point location(20, 20);
-  base::MessageLoop::current()->PostTask(FROM_HERE,
-      base::Bind(&Widget::OnMouseEvent,
-                 base::Unretained(second),
-                 base::Owned(new ui::MouseEvent(ui::ET_MOUSE_RELEASED,
-                                                location,
-                                                location,
-                                                ui::EF_LEFT_MOUSE_BUTTON,
-                                                ui::EF_LEFT_MOUSE_BUTTON))));
+  base::MessageLoop::current()->PostTask(
+      FROM_HERE, base::Bind(&Widget::OnMouseEvent, base::Unretained(second),
+                            base::Owned(new ui::MouseEvent(
+                                ui::ET_MOUSE_RELEASED, location, location,
+                                ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
+                                ui::EF_LEFT_MOUSE_BUTTON))));
   ui::MouseEvent press(ui::ET_MOUSE_PRESSED, location, location,
-                       ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
+                       ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
+                       ui::EF_LEFT_MOUSE_BUTTON);
   first->OnMouseEvent(&press);
   EXPECT_FALSE(first->HasCapture());
   first->Close();
@@ -472,7 +475,7 @@
 
   // Click on child1
   gfx::Point p1(45, 45);
-  ui::MouseEvent pressed(ui::ET_MOUSE_PRESSED, p1, p1,
+  ui::MouseEvent pressed(ui::ET_MOUSE_PRESSED, p1, p1, ui::EventTimeForNow(),
                          ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
   toplevel->OnMouseEvent(&pressed);
 
@@ -480,7 +483,7 @@
   EXPECT_TRUE(child1->HasCapture());
   EXPECT_FALSE(child2->HasCapture());
 
-  ui::MouseEvent released(ui::ET_MOUSE_RELEASED, p1, p1,
+  ui::MouseEvent released(ui::ET_MOUSE_RELEASED, p1, p1, ui::EventTimeForNow(),
                           ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
   toplevel->OnMouseEvent(&released);
 
@@ -492,7 +495,7 @@
 
   // Click on child2
   gfx::Point p2(315, 45);
-  ui::MouseEvent pressed2(ui::ET_MOUSE_PRESSED, p2, p2,
+  ui::MouseEvent pressed2(ui::ET_MOUSE_PRESSED, p2, p2, ui::EventTimeForNow(),
                           ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
   toplevel->OnMouseEvent(&pressed2);
   EXPECT_TRUE(pressed2.handled());
@@ -500,7 +503,7 @@
   EXPECT_TRUE(child2->HasCapture());
   EXPECT_FALSE(child1->HasCapture());
 
-  ui::MouseEvent released2(ui::ET_MOUSE_RELEASED, p2, p2,
+  ui::MouseEvent released2(ui::ET_MOUSE_RELEASED, p2, p2, ui::EventTimeForNow(),
                            ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
   toplevel->OnMouseEvent(&released2);
   EXPECT_FALSE(toplevel->HasCapture());
@@ -526,24 +529,24 @@
 
   // Move to an outside position.
   gfx::Point p1(200, 200);
-  ui::MouseEvent moved_out(ui::ET_MOUSE_MOVED, p1, p1, ui::EF_NONE,
-                           ui::EF_NONE);
+  ui::MouseEvent moved_out(ui::ET_MOUSE_MOVED, p1, p1, ui::EventTimeForNow(),
+                           ui::EF_NONE, ui::EF_NONE);
   toplevel->OnMouseEvent(&moved_out);
   EXPECT_EQ(0, view->EnteredCalls());
   EXPECT_EQ(0, view->ExitedCalls());
 
   // Move onto the active view.
   gfx::Point p2(95, 95);
-  ui::MouseEvent moved_over(ui::ET_MOUSE_MOVED, p2, p2, ui::EF_NONE,
-                            ui::EF_NONE);
+  ui::MouseEvent moved_over(ui::ET_MOUSE_MOVED, p2, p2, ui::EventTimeForNow(),
+                            ui::EF_NONE, ui::EF_NONE);
   toplevel->OnMouseEvent(&moved_over);
   EXPECT_EQ(1, view->EnteredCalls());
   EXPECT_EQ(0, view->ExitedCalls());
 
   // Move onto the outer resizing border.
   gfx::Point p3(102, 95);
-  ui::MouseEvent moved_resizer(ui::ET_MOUSE_MOVED, p3, p3, ui::EF_NONE,
-                               ui::EF_NONE);
+  ui::MouseEvent moved_resizer(ui::ET_MOUSE_MOVED, p3, p3,
+                               ui::EventTimeForNow(), ui::EF_NONE, ui::EF_NONE);
   toplevel->OnMouseEvent(&moved_resizer);
   EXPECT_EQ(0, view->EnteredCalls());
   EXPECT_EQ(1, view->ExitedCalls());
@@ -1375,7 +1378,7 @@
   // Send a mouse event to the RootWindow associated with |widget1|. Even though
   // |widget2| has capture, |widget1| should still get the event.
   ui::MouseEvent mouse_event(ui::ET_MOUSE_EXITED, gfx::Point(), gfx::Point(),
-                             ui::EF_NONE, ui::EF_NONE);
+                             ui::EventTimeForNow(), ui::EF_NONE, ui::EF_NONE);
   ui::EventDispatchDetails details = widget1.GetNativeWindow()->
       GetHost()->event_processor()->OnEventFromSource(&mouse_event);
   ASSERT_FALSE(details.dispatcher_destroyed);
diff --git a/ui/views/widget/widget_unittest.cc b/ui/views/widget/widget_unittest.cc
index 4ad12053..7c739d2b 100644
--- a/ui/views/widget/widget_unittest.cc
+++ b/ui/views/widget/widget_unittest.cc
@@ -1423,7 +1423,8 @@
 void GenerateMouseEvents(Widget* widget, ui::EventType last_event_type) {
   const gfx::Rect screen_bounds(widget->GetWindowBoundsInScreen());
   ui::MouseEvent move_event(ui::ET_MOUSE_MOVED, screen_bounds.CenterPoint(),
-                            screen_bounds.CenterPoint(), 0, 0);
+                            screen_bounds.CenterPoint(), ui::EventTimeForNow(),
+                            0, 0);
   ui::EventProcessor* dispatcher = WidgetTest::GetEventProcessor(widget);
   ui::EventDispatchDetails details = dispatcher->OnEventFromSource(&move_event);
   if (last_event_type == ui::ET_MOUSE_ENTERED || details.dispatcher_destroyed)
@@ -1433,20 +1434,22 @@
     return;
 
   ui::MouseEvent press_event(ui::ET_MOUSE_PRESSED, screen_bounds.CenterPoint(),
-                             screen_bounds.CenterPoint(), 0, 0);
+                             screen_bounds.CenterPoint(), ui::EventTimeForNow(),
+                             0, 0);
   details = dispatcher->OnEventFromSource(&press_event);
   if (last_event_type == ui::ET_MOUSE_PRESSED || details.dispatcher_destroyed)
     return;
 
   gfx::Point end_point(screen_bounds.CenterPoint());
   end_point.Offset(1, 1);
-  ui::MouseEvent drag_event(ui::ET_MOUSE_DRAGGED, end_point, end_point, 0, 0);
+  ui::MouseEvent drag_event(ui::ET_MOUSE_DRAGGED, end_point, end_point,
+                            ui::EventTimeForNow(), 0, 0);
   details = dispatcher->OnEventFromSource(&drag_event);
   if (last_event_type == ui::ET_MOUSE_DRAGGED || details.dispatcher_destroyed)
     return;
 
-  ui::MouseEvent release_event(ui::ET_MOUSE_RELEASED, end_point, end_point, 0,
-                               0);
+  ui::MouseEvent release_event(ui::ET_MOUSE_RELEASED, end_point, end_point,
+                               ui::EventTimeForNow(), 0, 0);
   details = dispatcher->OnEventFromSource(&release_event);
   if (details.dispatcher_destroyed)
     return;
@@ -1722,7 +1725,7 @@
 
   gfx::Point cursor_location(5, 5);
   ui::MouseEvent move(ui::ET_MOUSE_MOVED, cursor_location, cursor_location,
-                      ui::EF_NONE, ui::EF_NONE);
+                      ui::EventTimeForNow(), ui::EF_NONE, ui::EF_NONE);
   widget->OnMouseEvent(&move);
 
   EXPECT_EQ(1, v1->GetEventCount(ui::ET_MOUSE_ENTERED));
@@ -2142,7 +2145,8 @@
   EXPECT_EQ(NULL, GetMousePressedHandler(root_view));
   gfx::Point click_location(45, 15);
   ui::MouseEvent press(ui::ET_MOUSE_PRESSED, click_location, click_location,
-                       ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
+                       ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
+                       ui::EF_LEFT_MOUSE_BUTTON);
   widget->OnMouseEvent(&press);
   EXPECT_EQ(view, GetMousePressedHandler(root_view));
   widget->Hide();
@@ -2152,7 +2156,8 @@
   widget->Show();
   EXPECT_EQ(NULL, GetMouseMoveHandler(root_view));
   gfx::Point move_location(45, 15);
-  ui::MouseEvent move(ui::ET_MOUSE_MOVED, move_location, move_location, 0, 0);
+  ui::MouseEvent move(ui::ET_MOUSE_MOVED, move_location, move_location,
+                      ui::EventTimeForNow(), 0, 0);
   widget->OnMouseEvent(&move);
   EXPECT_EQ(view, GetMouseMoveHandler(root_view));
   widget->Hide();
@@ -3013,11 +3018,9 @@
   top_level_widget.GetRootView()->AddChildView(widget_view);
 
   gfx::Point cursor_location_main(5, 5);
-  ui::MouseEvent move_main(ui::ET_MOUSE_MOVED,
-                           cursor_location_main,
-                           cursor_location_main,
-                           ui::EF_NONE,
-                           ui::EF_NONE);
+  ui::MouseEvent move_main(ui::ET_MOUSE_MOVED, cursor_location_main,
+                           cursor_location_main, ui::EventTimeForNow(),
+                           ui::EF_NONE, ui::EF_NONE);
   ui::EventDispatchDetails details =
       GetEventProcessor(&top_level_widget)->OnEventFromSource(&move_main);
   ASSERT_FALSE(details.dispatcher_destroyed);
@@ -3041,11 +3044,9 @@
   EXPECT_TRUE(modal_dialog_widget->IsVisible());
 
   gfx::Point cursor_location_dialog(100, 100);
-  ui::MouseEvent mouse_down_dialog(ui::ET_MOUSE_PRESSED,
-                                   cursor_location_dialog,
-                                   cursor_location_dialog,
-                                   ui::EF_NONE,
-                                   ui::EF_NONE);
+  ui::MouseEvent mouse_down_dialog(
+      ui::ET_MOUSE_PRESSED, cursor_location_dialog, cursor_location_dialog,
+      ui::EventTimeForNow(), ui::EF_NONE, ui::EF_NONE);
   details = GetEventProcessor(&top_level_widget)->OnEventFromSource(
       &mouse_down_dialog);
   ASSERT_FALSE(details.dispatcher_destroyed);
@@ -3054,11 +3055,9 @@
   // Send a mouse move message to the main window. It should not be received by
   // the main window as the modal dialog is still active.
   gfx::Point cursor_location_main2(6, 6);
-  ui::MouseEvent mouse_down_main(ui::ET_MOUSE_MOVED,
-                                 cursor_location_main2,
-                                 cursor_location_main2,
-                                 ui::EF_NONE,
-                                 ui::EF_NONE);
+  ui::MouseEvent mouse_down_main(ui::ET_MOUSE_MOVED, cursor_location_main2,
+                                 cursor_location_main2, ui::EventTimeForNow(),
+                                 ui::EF_NONE, ui::EF_NONE);
   details = GetEventProcessor(&top_level_widget)->OnEventFromSource(
       &mouse_down_main);
   ASSERT_FALSE(details.dispatcher_destroyed);
diff --git a/ui/webui/OWNERS b/ui/webui/OWNERS
index a3e0e16..3621c2c0e 100644
--- a/ui/webui/OWNERS
+++ b/ui/webui/OWNERS
@@ -1,3 +1,4 @@
+# Please use more specific OWNERS when possible.
 arv@chromium.org
 bauerb@chromium.org
 dbeam@chromium.org
diff --git a/ui/webui/resources/OWNERS b/ui/webui/resources/OWNERS
new file mode 100644
index 0000000..710d44ed
--- /dev/null
+++ b/ui/webui/resources/OWNERS
@@ -0,0 +1,2 @@
+per-file polymer_resources.grdp=dzhioev@chromium.org
+per-file polymer_resources.grdp=michaelpg@chromium.org
diff --git a/ui/webui/resources/cr_elements/OWNERS b/ui/webui/resources/cr_elements/OWNERS
new file mode 100644
index 0000000..0dc1b469
--- /dev/null
+++ b/ui/webui/resources/cr_elements/OWNERS
@@ -0,0 +1,2 @@
+dzhioev@chromium.org
+michaelpg@chromium.org
diff --git a/ui/webui/resources/cr_elements/cr_checkbox/cr_checkbox.css b/ui/webui/resources/cr_elements/cr_checkbox/cr_checkbox.css
new file mode 100644
index 0000000..204e397c
--- /dev/null
+++ b/ui/webui/resources/cr_elements/cr_checkbox/cr_checkbox.css
@@ -0,0 +1,7 @@
+/* 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. */
+
+:host {
+  display: inline-block;
+}
diff --git a/ui/webui/resources/cr_elements/cr_checkbox/cr_checkbox.html b/ui/webui/resources/cr_elements/cr_checkbox/cr_checkbox.html
new file mode 100644
index 0000000..20ad7c2
--- /dev/null
+++ b/ui/webui/resources/cr_elements/cr_checkbox/cr_checkbox.html
@@ -0,0 +1,15 @@
+<link rel="import"
+    href="chrome://resources/polymer/paper-checkbox/paper-checkbox.html">
+<link rel="import"
+    href="chrome://resources/cr_elements/cr_events/cr_events.html">
+
+<polymer-element name="cr-checkbox">
+  <template>
+    <link rel="stylesheet" href="cr_checkbox.css">
+    <cr-events id="events"></cr-events>
+    <paper-checkbox id="checkbox"
+        checked?="{{checked}}" disabled?="{{disabled}}" label="{{label}}">
+    </paper-checkbox>
+  </template>
+  <script src="cr_checkbox.js"></script>
+</polymer-element>
diff --git a/ui/webui/resources/cr_elements/cr_checkbox/cr_checkbox.js b/ui/webui/resources/cr_elements/cr_checkbox/cr_checkbox.js
new file mode 100644
index 0000000..4011c01
--- /dev/null
+++ b/ui/webui/resources/cr_elements/cr_checkbox/cr_checkbox.js
@@ -0,0 +1,56 @@
+/* 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. */
+
+/**
+ * @fileoverview
+ * `cr-checkbox` is a button that can be either checked or unchecked. User
+ * can tap the checkbox to check or uncheck it. Usually you use checkboxes
+ * to allow user to select multiple options from a set. If you have a single
+ * ON/OFF option, avoid using a single checkbox and use `cr-toggle-button`
+ * instead.
+ *
+ * Example:
+ *     <cr-checkbox></cr-checkbox>
+ *     <cr-checkbox checked></cr-checkbox>
+ *
+ * @element cr-checkbox
+ */
+Polymer({
+  publish: {
+    /**
+     * Gets or sets the state. `true` is checked and `false` is unchecked.
+     *
+     * @attribute checked
+     * @type boolean
+     * @default false
+     */
+    checked: {value: false, reflect: true},
+
+    /**
+     * The label for the checkbox.
+     *
+     * @attribute label
+     * @type string
+     * @default ''
+     */
+    label: '',
+
+    /**
+     * If true, the user cannot interact with this element.
+     *
+     * @attribute disabled
+     * @type boolean
+     * @default false
+     */
+    disabled: {value: false, reflect: true},
+  },
+
+  toggle: function() {
+    this.$.checkbox.toggle();
+  },
+
+  ready: function() {
+    this.$.events.forward(this.$.checkbox, ['change']);
+  },
+});
diff --git a/ui/webui/resources/cr_elements/cr_dropdown_menu/cr_dropdown_menu.css b/ui/webui/resources/cr_elements/cr_dropdown_menu/cr_dropdown_menu.css
new file mode 100644
index 0000000..b989083
--- /dev/null
+++ b/ui/webui/resources/cr_elements/cr_dropdown_menu/cr_dropdown_menu.css
@@ -0,0 +1,7 @@
+/* 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. */
+
+:host {
+  display: inline-flex;
+}
diff --git a/ui/webui/resources/cr_elements/cr_dropdown_menu/cr_dropdown_menu.html b/ui/webui/resources/cr_elements/cr_dropdown_menu/cr_dropdown_menu.html
new file mode 100644
index 0000000..65d2da9
--- /dev/null
+++ b/ui/webui/resources/cr_elements/cr_dropdown_menu/cr_dropdown_menu.html
@@ -0,0 +1,16 @@
+<link rel="import" href="chrome://resources/polymer/polymer/polymer.html">
+<link rel="import" href="chrome://resources/polymer/paper-dropdown-menu/paper-dropdown-menu.html">
+<link rel="import" href="chrome://resources/polymer/paper-dropdown/paper-dropdown.html">
+<script src="chrome://resources/js/assert.js"></script>
+
+<polymer-element name="cr-dropdown-menu">
+  <template>
+    <link rel="stylesheet" href="cr_dropdown_menu.css">
+    <paper-dropdown-menu flex label="{{label}}" opened="{{opened}}">
+      <paper-dropdown class="dropdown">
+        <content></content>
+      </paper-dropdown>
+    </paper-dropdown-menu>
+  </template>
+  <script src="cr_dropdown_menu.js"></script>
+</polymer-element>
diff --git a/ui/webui/resources/cr_elements/cr_dropdown_menu/cr_dropdown_menu.js b/ui/webui/resources/cr_elements/cr_dropdown_menu/cr_dropdown_menu.js
new file mode 100644
index 0000000..70f3253e
--- /dev/null
+++ b/ui/webui/resources/cr_elements/cr_dropdown_menu/cr_dropdown_menu.js
@@ -0,0 +1,52 @@
+/* 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. */
+
+/**
+ * @fileoverview
+ * 'cr-dropdown-menu' is a Chrome-specific wrapper around paper-dropdown-menu.
+ * It includes a paper-dropdown so its content should just be a core-menu and
+ * items.
+ *
+ * Example:
+ *   <cr-dropdown-menu>
+ *     <core-menu>
+ *       <paper-item>Chrome</paper-item>
+ *       <paper-item>Firefox</paper-item>
+ *       <paper-item>IE</paper-item>
+ *       <paper-item>Opera</paper-item>
+ *     </core-menu>
+ *   </cr-dropdown-menu>
+ *
+ * @group Chrome Elements
+ * @element cr-dropdown-menu
+ */
+
+Polymer('cr-dropdown-menu', {
+  publish: {
+    /**
+     * True if the menu is open.
+     *
+     * @attribute opened
+     * @type boolean
+     * @default false
+     */
+    opened: false,
+
+    /**
+     * A label for the control. The label is displayed if no item is selected.
+     *
+     * @attribute label
+     * @type string
+     * @default '<Dropdown Menu Label>'
+     */
+    label: '<Dropdown Menu Label>',
+  },
+
+  /** @override */
+  domReady: function() {
+    assert(
+        this.querySelector('.menu'),
+        'cr-dropdown-menu must have a menu child with class="menu".');
+  },
+});
diff --git a/ui/webui/resources/cr_elements/cr_dropdown_menu/demo.html b/ui/webui/resources/cr_elements/cr_dropdown_menu/demo.html
new file mode 100644
index 0000000..d9dd47c
--- /dev/null
+++ b/ui/webui/resources/cr_elements/cr_dropdown_menu/demo.html
@@ -0,0 +1,16 @@
+<html>
+<head>
+  <link href="chrome://resources/polymer/core-menu/core-menu.html" rel="import">
+  <link href="chrome://resources/polymer/paper-item/paper-item.html" rel="import">
+  <link href="cr_dropdown_menu.html" rel="import">
+</head>
+<body unresolved>
+  <cr-dropdown-menu>
+    <core-menu class="menu">
+      <paper-item>Chrome</paper-item>
+      <paper-item>IE</paper-item>
+      <paper-item>Firefox</paper-item>
+    </core-menu>
+  </cr-dropdown-menu>
+</body>
+</html>
diff --git a/ui/webui/resources/cr_elements_resources.grdp b/ui/webui/resources/cr_elements_resources.grdp
index d63770b..422720f 100644
--- a/ui/webui/resources/cr_elements_resources.grdp
+++ b/ui/webui/resources/cr_elements_resources.grdp
@@ -1,5 +1,23 @@
 <?xml version="1.0" encoding="utf-8"?>
 <grit-part>
+  <structure name="IDR_CR_ELEMENTS_CR_CHECKBOX_CSS"
+             file="../../webui/resources/cr_elements/cr_checkbox/cr_checkbox.css"
+             type="chrome_html" />
+  <structure name="IDR_CR_ELEMENTS_CR_CHECKBOX_HTML"
+             file="../../webui/resources/cr_elements/cr_checkbox/cr_checkbox.html"
+             type="chrome_html" />
+  <structure name="IDR_CR_ELEMENTS_CR_CHECKBOX_JS"
+             file="../../webui/resources/cr_elements/cr_checkbox/cr_checkbox.js"
+             type="chrome_html" />
+  <structure name="IDR_CR_ELEMENTS_CR_DROPDOWN_MENU_CSS"
+             file="../../webui/resources/cr_elements/cr_dropdown_menu/cr_dropdown_menu.css"
+             type="chrome_html" />
+  <structure name="IDR_CR_ELEMENTS_CR_DROPDOWN_MENU_HTML"
+             file="../../webui/resources/cr_elements/cr_dropdown_menu/cr_dropdown_menu.html"
+             type="chrome_html" />
+  <structure name="IDR_CR_ELEMENTS_CR_DROPDOWN_MENU_JS"
+             file="../../webui/resources/cr_elements/cr_dropdown_menu/cr_dropdown_menu.js"
+             type="chrome_html" />
   <structure name="IDR_CR_ELEMENTS_CR_TOGGLE_BUTTON_CSS"
              file="../../webui/resources/cr_elements/cr_toggle_button/cr_toggle_button.css"
              type="chrome_html" />
diff --git a/ui/webui/resources/css/widgets.css b/ui/webui/resources/css/widgets.css
index f8e7aad1..2ab881a 100644
--- a/ui/webui/resources/css/widgets.css
+++ b/ui/webui/resources/css/widgets.css
@@ -309,3 +309,9 @@
 label > input:disabled:-webkit-any([type='checkbox'], [type='radio']) ~ span {
   color: #999;
 }
+
+extensionview {
+  display: inline-block;
+  height: 300px;
+  width: 300px;
+}
\ No newline at end of file
diff --git a/ui/webui/resources/js/cr/ui/dialogs.js b/ui/webui/resources/js/cr/ui/dialogs.js
index 5c9cc8c..eb37f1d7 100644
--- a/ui/webui/resources/js/cr/ui/dialogs.js
+++ b/ui/webui/resources/js/cr/ui/dialogs.js
@@ -35,6 +35,7 @@
    */
   BaseDialog.ANIMATE_STABLE_DURATION = 500;
 
+  /** @private */
   BaseDialog.prototype.initDom_ = function() {
     var doc = this.document_;
     this.container_ = doc.createElement('div');
@@ -88,9 +89,13 @@
     this.initialFocusElement_ = this.okButton_;
   };
 
+  /** @private {Function|undefined} */
   BaseDialog.prototype.onOk_ = null;
+
+  /** @private {Function|undefined} */
   BaseDialog.prototype.onCancel_ = null;
 
+  /** @private */
   BaseDialog.prototype.onContainerKeyDown_ = function(event) {
     // Handle Escape.
     if (event.keyCode == 27 && !this.cancelButton_.disabled) {
@@ -102,6 +107,7 @@
     }
   };
 
+  /** @private */
   BaseDialog.prototype.onContainerMouseDown_ = function(event) {
     if (event.target == this.container_) {
       var classList = this.frame_.classList;
@@ -112,22 +118,26 @@
     }
   };
 
+  /** @private */
   BaseDialog.prototype.onOkClick_ = function(event) {
     this.hide();
     if (this.onOk_)
       this.onOk_();
   };
 
+  /** @private */
   BaseDialog.prototype.onCancelClick_ = function(event) {
     this.hide();
     if (this.onCancel_)
       this.onCancel_();
   };
 
+  /** @param {string} label */
   BaseDialog.prototype.setOkLabel = function(label) {
     this.okButton_.textContent = label;
   };
 
+  /** @param {string} label */
   BaseDialog.prototype.setCancelLabel = function(label) {
     this.cancelButton_.textContent = label;
   };
@@ -136,16 +146,31 @@
     this.initialFocusElement_ = this.cancelButton_;
   };
 
-  BaseDialog.prototype.show = function(message, onOk, onCancel, onShow) {
-    this.showWithTitle(null, message, onOk, onCancel, onShow);
+  /**
+   * @param {string} message
+   * @param {Function=} opt_onOk
+   * @param {Function=} opt_onCancel
+   * @param {Function=} opt_onShow
+   */
+  BaseDialog.prototype.show = function(
+      message, opt_onOk, opt_onCancel, opt_onShow) {
+    this.showWithTitle('', message, opt_onOk, opt_onCancel, opt_onShow);
   };
 
+  /**
+   * @param {string} title
+   * @param {string} message
+   * @param {Function=} opt_onOk
+   * @param {Function=} opt_onCancel
+   * @param {Function=} opt_onShow
+   */
   BaseDialog.prototype.showHtml = function(title, message,
-      onOk, onCancel, onShow) {
+      opt_onOk, opt_onCancel, opt_onShow) {
     this.text_.innerHTML = message;
-    this.show_(title, onOk, onCancel, onShow);
+    this.show_(title, opt_onOk, opt_onCancel, opt_onShow);
   };
 
+  /** @private */
   BaseDialog.prototype.findFocusableElements_ = function(doc) {
     var elements = Array.prototype.filter.call(
         doc.querySelectorAll('*'),
@@ -166,13 +191,28 @@
     return elements;
   };
 
+  /**
+   * @param {string} title
+   * @param {string} message
+   * @param {Function=} opt_onOk
+   * @param {Function=} opt_onCancel
+   * @param {Function=} opt_onShow
+   */
   BaseDialog.prototype.showWithTitle = function(title, message,
-      onOk, onCancel, onShow) {
+      opt_onOk, opt_onCancel, opt_onShow) {
     this.text_.textContent = message;
-    this.show_(title, onOk, onCancel, onShow);
+    this.show_(title, opt_onOk, opt_onCancel, opt_onShow);
   };
 
-  BaseDialog.prototype.show_ = function(title, onOk, onCancel, onShow) {
+  /**
+   * @param {string} title
+   * @param {Function=} opt_onOk
+   * @param {Function=} opt_onCancel
+   * @param {Function=} opt_onShow
+   * @private
+   */
+  BaseDialog.prototype.show_ = function(
+      title, opt_onOk, opt_onCancel, opt_onShow) {
     // Make all outside nodes unfocusable while the dialog is active.
     this.deactivatedNodes_ = this.findFocusableElements_(this.document_);
     this.tabIndexes_ = this.deactivatedNodes_.map(
@@ -183,8 +223,8 @@
     this.previousActiveElement_ = this.document_.activeElement;
     this.parentNode_.appendChild(this.container_);
 
-    this.onOk_ = onOk;
-    this.onCancel_ = onCancel;
+    this.onOk_ = opt_onOk;
+    this.onCancel_ = opt_onCancel;
 
     if (title) {
       this.title_.textContent = title;
@@ -201,15 +241,13 @@
       self.container_.classList.add('shown');
       self.initialFocusElement_.focus();
       setTimeout(function() {
-        if (onShow)
-          onShow();
+        if (opt_onShow)
+          opt_onShow();
       }, BaseDialog.ANIMATE_STABLE_DURATION);
     }, 0);
   };
 
-  /**
-   * @param {Function=} opt_onHide
-   */
+  /** @param {Function=} opt_onHide */
   BaseDialog.prototype.hide = function(opt_onHide) {
     // Restore focusability.
     for (var i = 0; i < this.deactivatedNodes_.length; i++) {
@@ -248,14 +286,20 @@
    * @extends {cr.ui.dialogs.BaseDialog}
    */
   function AlertDialog(parentNode) {
-    BaseDialog.apply(this, [parentNode]);
+    BaseDialog.call(this, parentNode);
     this.cancelButton_.style.display = 'none';
   }
 
   AlertDialog.prototype = {__proto__: BaseDialog.prototype};
 
-  AlertDialog.prototype.show = function(message, onOk, onShow) {
-    return BaseDialog.prototype.show.apply(this, [message, onOk, onOk, onShow]);
+  /**
+   * @param {Function=} opt_onOk
+   * @param {Function=} opt_onShow
+   * @override
+   */
+  AlertDialog.prototype.show = function(message, opt_onOk, opt_onShow) {
+    return BaseDialog.prototype.show.call(
+        this, message, opt_onOk, opt_onOk, opt_onShow);
   };
 
   /**
@@ -264,7 +308,7 @@
    * @extends {cr.ui.dialogs.BaseDialog}
    */
   function ConfirmDialog(parentNode) {
-    BaseDialog.apply(this, [parentNode]);
+    BaseDialog.call(this, parentNode);
   }
 
   ConfirmDialog.prototype = {__proto__: BaseDialog.prototype};
@@ -276,7 +320,7 @@
    * @extends {cr.ui.dialogs.BaseDialog}
    */
   function PromptDialog(parentNode) {
-    BaseDialog.apply(this, [parentNode]);
+    BaseDialog.call(this, parentNode);
     this.input_ = this.document_.createElement('input');
     this.input_.setAttribute('type', 'text');
     this.input_.addEventListener('focus', this.onInputFocus.bind(this));
@@ -291,6 +335,7 @@
     this.input_.select();
   };
 
+  /** @private */
   PromptDialog.prototype.onKeyDown_ = function(event) {
     if (event.keyCode == 13) {  // Enter
       this.onOkClick_(event);
@@ -299,21 +344,27 @@
   };
 
   /**
+   * @param {string} message
+   * @param {?} defaultValue
+   * @param {Function=} opt_onOk
+   * @param {Function=} opt_onCancel
+   * @param {Function=} opt_onShow
    * @suppress {checkTypes}
    * TODO(fukino): remove suppression if there is a better way to avoid warning
    * about overriding method with different signature.
    */
-  PromptDialog.prototype.show = function(message, defaultValue, onOk, onCancel,
-                                        onShow) {
+  PromptDialog.prototype.show = function(
+      message, defaultValue, opt_onOk, opt_onCancel, opt_onShow) {
     this.input_.value = defaultValue || '';
-    return BaseDialog.prototype.show.apply(this, [message, onOk, onCancel,
-                                                  onShow]);
+    return BaseDialog.prototype.show.call(
+        this, message, opt_onOk, opt_onCancel, opt_onShow);
   };
 
   PromptDialog.prototype.getValue = function() {
     return this.input_.value;
   };
 
+  /** @private */
   PromptDialog.prototype.onOkClick_ = function(event) {
     this.hide();
     if (this.onOk_)
diff --git a/ui/webui/resources/js/cr/ui/grid.js b/ui/webui/resources/js/cr/ui/grid.js
index 3d7f25f8..6d7ba8c 100644
--- a/ui/webui/resources/js/cr/ui/grid.js
+++ b/ui/webui/resources/js/cr/ui/grid.js
@@ -106,15 +106,22 @@
       if (!itemCount)
         return 0;
 
-      var columns = Math.floor(this.clientWidthWithoutScrollbar_ / width);
+      var columns = Math.floor(
+          (this.clientWidthWithoutScrollbar_ - this.horizontalPadding_) /
+          width);
       if (!columns)
         return 0;
 
       var rows = Math.ceil(itemCount / columns);
-      if (rows * height <= this.clientHeight_)
+      if (rows * height <= this.clientHeight_) {
+        // Content fits within the client area (no scrollbar required).
         return columns;
+      }
 
-      return Math.floor(this.clientWidthWithScrollbar_ / width);
+      // If the content doesn't fit within the client area, the number of
+      // columns should be calculated with consideration for scrollbar's width.
+      return Math.floor(
+          (this.clientWidthWithScrollbar_ - this.horizontalPadding_) / width);
     },
 
     /**
@@ -125,10 +132,14 @@
       // Check changings that may affect number of columns.
       var offsetWidth = this.offsetWidth;
       var offsetHeight = this.offsetHeight;
-      var overflowY = window.getComputedStyle(this).overflowY;
+      var style = window.getComputedStyle(this);
+      var overflowY = style.overflowY;
+      var horizontalPadding =
+          parseFloat(style.paddingLeft) + parseFloat(style.paddingRight);
 
       if (this.lastOffsetWidth_ == offsetWidth &&
-          this.lastOverflowY == overflowY) {
+          this.lastOverflowY == overflowY &&
+          this.horizontalPadding_ == horizontalPadding) {
         this.lastOffsetHeight_ = offsetHeight;
         return;
       }
@@ -136,6 +147,7 @@
       this.lastOffsetWidth_ = offsetWidth;
       this.lastOffsetHeight_ = offsetHeight;
       this.lastOverflowY = overflowY;
+      this.horizontalPadding_ = horizontalPadding;
       this.columns_ = 0;
 
       if (overflowY == 'auto' && offsetWidth > 0) {
diff --git a/ui/webui/resources/polymer_resources.grdp b/ui/webui/resources/polymer_resources.grdp
index 6730d97..42c8441d 100644
--- a/ui/webui/resources/polymer_resources.grdp
+++ b/ui/webui/resources/polymer_resources.grdp
@@ -30,6 +30,27 @@
   <structure name="IDR_POLYMER_CORE_ANIMATED_PAGES_TRANSITIONS_HERO_TRANSITION_HTML"
              file="../../../third_party/polymer/components-chromium/core-animated-pages/transitions/hero-transition.html"
              type="chrome_html" />
+  <structure name="IDR_POLYMER_CORE_ANIMATION_CORE_ANIMATION_EXTRACTED_JS"
+             file="../../../third_party/polymer/components-chromium/core-animation/core-animation-extracted.js"
+             type="chrome_html" />
+  <structure name="IDR_POLYMER_CORE_ANIMATION_CORE_ANIMATION_HTML"
+             file="../../../third_party/polymer/components-chromium/core-animation/core-animation.html"
+             type="chrome_html" />
+  <structure name="IDR_POLYMER_CORE_ANIMATION_WEB_ANIMATIONS_HTML"
+             file="../../../third_party/polymer/components-chromium/core-animation/web-animations.html"
+             type="chrome_html" />
+  <structure name="IDR_POLYMER_CORE_DROPDOWN_CORE_DROPDOWN_EXTRACTED_JS"
+             file="../../../third_party/polymer/components-chromium/core-dropdown/core-dropdown-extracted.js"
+             type="chrome_html" />
+  <structure name="IDR_POLYMER_CORE_DROPDOWN_CORE_DROPDOWN_HTML"
+             file="../../../third_party/polymer/components-chromium/core-dropdown/core-dropdown.html"
+             type="chrome_html" />
+  <structure name="IDR_POLYMER_CORE_DROPDOWN_CORE_DROPDOWN_BASE_EXTRACTED_JS"
+             file="../../../third_party/polymer/components-chromium/core-dropdown/core-dropdown-base-extracted.js"
+             type="chrome_html" />
+  <structure name="IDR_POLYMER_CORE_DROPDOWN_CORE_DROPDOWN_BASE_HTML"
+             file="../../../third_party/polymer/components-chromium/core-dropdown/core-dropdown-base.html"
+             type="chrome_html" />
   <structure name="IDR_POLYMER_CORE_FOCUSABLE_CORE_FOCUSABLE_HTML"
              file="../../../third_party/polymer/components-chromium/core-focusable/core-focusable.html"
              type="chrome_html" />
@@ -105,6 +126,12 @@
   <structure name="IDR_POLYMER_CORE_LIST_CORE_LIST_HTML"
              file="../../../third_party/polymer/components-chromium/core-list/core-list.html"
              type="chrome_html" />
+  <structure name="IDR_POLYMER_CORE_MENU_CORE_MENU_CSS"
+             file="../../../third_party/polymer/components-chromium/core-menu/core-menu.css"
+             type="chrome_html" />
+  <structure name="IDR_POLYMER_CORE_MENU_CORE_MENU_HTML"
+             file="../../../third_party/polymer/components-chromium/core-menu/core-menu.html"
+             type="chrome_html" />
   <structure name="IDR_POLYMER_CORE_META_CORE_META_EXTRACTED_JS"
              file="../../../third_party/polymer/components-chromium/core-meta/core-meta-extracted.js"
              type="chrome_html" />
@@ -195,6 +222,15 @@
   <structure name="IDR_POLYMER_PAPER_BUTTON_PAPER_BUTTON_HTML"
              file="../../../third_party/polymer/components-chromium/paper-button/paper-button.html"
              type="chrome_html" />
+  <structure name="IDR_POLYMER_PAPER_CHECKBOX_PAPER_CHECKBOX_CSS"
+             file="../../../third_party/polymer/components-chromium/paper-checkbox/paper-checkbox.css"
+             type="chrome_html" />
+  <structure name="IDR_POLYMER_PAPER_CHECKBOX_PAPER_CHECKBOX_EXTRACTED_JS"
+             file="../../../third_party/polymer/components-chromium/paper-checkbox/paper-checkbox-extracted.js"
+             type="chrome_html" />
+  <structure name="IDR_POLYMER_PAPER_CHECKBOX_PAPER_CHECKBOX_HTML"
+             file="../../../third_party/polymer/components-chromium/paper-checkbox/paper-checkbox.html"
+             type="chrome_html" />
   <structure name="IDR_POLYMER_PAPER_DIALOG_PAPER_ACTION_DIALOG_EXTRACTED_JS"
              file="../../../third_party/polymer/components-chromium/paper-dialog/paper-action-dialog-extracted.js"
              type="chrome_html" />
@@ -225,6 +261,30 @@
   <structure name="IDR_POLYMER_PAPER_FAB_PAPER_FAB_HTML"
              file="../../../third_party/polymer/components-chromium/paper-fab/paper-fab.html"
              type="chrome_html" />
+  <structure name="IDR_POLYMER_PAPER_DROPDOWN_PAPER_DROPDOWN_EXTRACTED_JS"
+             file="../../../third_party/polymer/components-chromium/paper-dropdown/paper-dropdown-extracted.js"
+             type="chrome_html" />
+  <structure name="IDR_POLYMER_PAPER_DROPDOWN_PAPER_DROPDOWN_HTML"
+             file="../../../third_party/polymer/components-chromium/paper-dropdown/paper-dropdown.html"
+             type="chrome_html" />
+  <structure name="IDR_POLYMER_PAPER_DROPDOWN_PAPER_DROPDOWN_TRANSITION_CSS"
+             file="../../../third_party/polymer/components-chromium/paper-dropdown/paper-dropdown-transition.css"
+             type="chrome_html" />
+  <structure name="IDR_POLYMER_PAPER_DROPDOWN_PAPER_DROPDOWN_TRANSITION_EXTRACTED_JS"
+             file="../../../third_party/polymer/components-chromium/paper-dropdown/paper-dropdown-transition-extracted.js"
+             type="chrome_html" />
+  <structure name="IDR_POLYMER_PAPER_DROPDOWN_PAPER_DROPDOWN_TRANSITION_HTML"
+             file="../../../third_party/polymer/components-chromium/paper-dropdown/paper-dropdown-transition.html"
+             type="chrome_html" />
+  <structure name="IDR_POLYMER_PAPER_DROPDOWN_MENU_PAPER_DROPDOWN_MENU_CSS"
+             file="../../../third_party/polymer/components-chromium/paper-dropdown-menu/paper-dropdown-menu.css"
+             type="chrome_html" />
+  <structure name="IDR_POLYMER_PAPER_DROPDOWN_MENU_PAPER_DROPDOWN_MENU_EXTRACTED_JS"
+             file="../../../third_party/polymer/components-chromium/paper-dropdown-menu/paper-dropdown-menu-extracted.js"
+             type="chrome_html" />
+  <structure name="IDR_POLYMER_PAPER_DROPDOWN_MENU_PAPER_DROPDOWN_MENU_HTML"
+             file="../../../third_party/polymer/components-chromium/paper-dropdown-menu/paper-dropdown-menu.html"
+             type="chrome_html" />
   <structure name="IDR_POLYMER_PAPER_ICON_BUTTON_PAPER_ICON_BUTTON_EXTRACTED_JS"
              file="../../../third_party/polymer/components-chromium/paper-icon-button/paper-icon-button-extracted.js"
              type="chrome_html" />
@@ -318,4 +378,10 @@
   <structure name="IDR_POLYMER_POLYMER_POLYMER_JS"
              file="../../../third_party/polymer/components-chromium/polymer/polymer.js"
              type="chrome_html" />
+  <structure name="IDR_POLYMER_WEB_ANIMATIONS_JS_WEB_ANIMATIONS_NEXT_LITE_MIN_JS"
+             file="../../../third_party/web-animations-js/sources/web-animations-next-lite.min.js"
+             type="chrome_html" />
+  <structure name="IDR_POLYMER_WEB_ANIMATIONS_JS_WEB_ANIMATIONS_NEXT_LITE_MIN_JS_MAP"
+             file="../../../third_party/web-animations-js/sources/web-animations-next-lite.min.js.map"
+             type="chrome_html" />
 </grit-part>
diff --git a/ui/wm/core/capture_controller_unittest.cc b/ui/wm/core/capture_controller_unittest.cc
index de1a59c..c48997b 100644
--- a/ui/wm/core/capture_controller_unittest.cc
+++ b/ui/wm/core/capture_controller_unittest.cc
@@ -99,7 +99,8 @@
   // Make a synthesized mouse down event. Ensure that the WindowEventDispatcher
   // will dispatch further mouse events to |w1|.
   ui::MouseEvent mouse_pressed_event(ui::ET_MOUSE_PRESSED, gfx::Point(5, 5),
-                                     gfx::Point(5, 5), 0, 0);
+                                     gfx::Point(5, 5), ui::EventTimeForNow(), 0,
+                                     0);
   DispatchEventUsingWindowDispatcher(&mouse_pressed_event);
   EXPECT_EQ(w1.get(), host()->dispatcher()->mouse_pressed_handler());
 
diff --git a/ui/wm/core/compound_event_filter_unittest.cc b/ui/wm/core/compound_event_filter_unittest.cc
index 453e03f..4ffdd26 100644
--- a/ui/wm/core/compound_event_filter_unittest.cc
+++ b/ui/wm/core/compound_event_filter_unittest.cc
@@ -68,20 +68,20 @@
 
   // Synthesized mouse event should not show the cursor.
   ui::MouseEvent enter(ui::ET_MOUSE_ENTERED, gfx::Point(10, 10),
-                       gfx::Point(10, 10), 0, 0);
+                       gfx::Point(10, 10), ui::EventTimeForNow(), 0, 0);
   enter.set_flags(enter.flags() | ui::EF_IS_SYNTHESIZED);
   DispatchEventUsingWindowDispatcher(&enter);
   EXPECT_FALSE(cursor_client.IsCursorVisible());
 
   ui::MouseEvent move(ui::ET_MOUSE_MOVED, gfx::Point(10, 10),
-                      gfx::Point(10, 10), 0, 0);
+                      gfx::Point(10, 10), ui::EventTimeForNow(), 0, 0);
   move.set_flags(enter.flags() | ui::EF_IS_SYNTHESIZED);
   DispatchEventUsingWindowDispatcher(&move);
   EXPECT_FALSE(cursor_client.IsCursorVisible());
 
   // A real mouse event should show the cursor.
   ui::MouseEvent real_move(ui::ET_MOUSE_MOVED, gfx::Point(10, 10),
-                           gfx::Point(10, 10), 0, 0);
+                           gfx::Point(10, 10), ui::EventTimeForNow(), 0, 0);
   DispatchEventUsingWindowDispatcher(&real_move);
   EXPECT_TRUE(cursor_client.IsCursorVisible());
 
@@ -99,7 +99,7 @@
 
   // Mouse synthesized exit event should not show the cursor.
   ui::MouseEvent exit(ui::ET_MOUSE_EXITED, gfx::Point(10, 10),
-                      gfx::Point(10, 10), 0, 0);
+                      gfx::Point(10, 10), ui::EventTimeForNow(), 0, 0);
   exit.set_flags(enter.flags() | ui::EF_IS_SYNTHESIZED);
   DispatchEventUsingWindowDispatcher(&exit);
   EXPECT_FALSE(cursor_client.IsCursorVisible());
@@ -123,7 +123,7 @@
   aura::test::TestCursorClient cursor_client(root_window());
 
   ui::MouseEvent mouse0(ui::ET_MOUSE_MOVED, gfx::Point(10, 10),
-                        gfx::Point(10, 10), 0, 0);
+                        gfx::Point(10, 10), ui::EventTimeForNow(), 0, 0);
   DispatchEventUsingWindowDispatcher(&mouse0);
   EXPECT_TRUE(cursor_client.IsMouseEventsEnabled());
 
@@ -144,7 +144,7 @@
   EXPECT_FALSE(cursor_client.IsMouseEventsEnabled());
 
   ui::MouseEvent mouse1(ui::ET_MOUSE_MOVED, gfx::Point(10, 10),
-                        gfx::Point(10, 10), 0, 0);
+                        gfx::Point(10, 10), ui::EventTimeForNow(), 0, 0);
   // Move the cursor again. The cursor should be visible.
   DispatchEventUsingWindowDispatcher(&mouse1);
   EXPECT_TRUE(cursor_client.IsMouseEventsEnabled());
@@ -231,7 +231,7 @@
   EXPECT_FALSE(cursor_client.IsMouseEventsEnabled());
 
   ui::MouseEvent mouse0(ui::ET_MOUSE_MOVED, gfx::Point(10, 10),
-                        gfx::Point(10, 10), 0, 0);
+                        gfx::Point(10, 10), ui::EventTimeForNow(), 0, 0);
   mouse0.set_flags(mouse0.flags() | ui::EF_FROM_TOUCH);
 
   DispatchEventUsingWindowDispatcher(&mouse0);
diff --git a/ui/wm/core/focus_controller_unittest.cc b/ui/wm/core/focus_controller_unittest.cc
index a773821..84c691f 100644
--- a/ui/wm/core/focus_controller_unittest.cc
+++ b/ui/wm/core/focus_controller_unittest.cc
@@ -22,6 +22,7 @@
 #include "ui/events/event_handler.h"
 #include "ui/events/test/event_generator.h"
 #include "ui/wm/core/base_focus_rules.h"
+#include "ui/wm/core/window_util.h"
 #include "ui/wm/core/wm_state.h"
 #include "ui/wm/public/activation_change_observer.h"
 #include "ui/wm/public/activation_client.h"
@@ -1111,6 +1112,26 @@
  public:
   FocusControllerParentHideTest() : FocusControllerHideTest(true) {}
 
+  // The parent window's visibility change should not change its transient child
+  // window's modality property.
+  void TransientChildWindowActivationTest() {
+    aura::Window* w1 = root_window()->GetChildById(1);
+    aura::Window* w11 = root_window()->GetChildById(11);
+    ::wm::AddTransientChild(w1, w11);
+    w11->SetProperty(aura::client::kModalKey, ui::MODAL_TYPE_WINDOW);
+
+    EXPECT_EQ(ui::MODAL_TYPE_NONE, w1->GetProperty(aura::client::kModalKey));
+    EXPECT_EQ(ui::MODAL_TYPE_WINDOW, w11->GetProperty(aura::client::kModalKey));
+
+    // Hide the parent window w1 and show it again.
+    w1->Hide();
+    w1->Show();
+
+    // Test that child window w11 doesn't change its modality property.
+    EXPECT_EQ(ui::MODAL_TYPE_NONE, w1->GetProperty(aura::client::kModalKey));
+    EXPECT_EQ(ui::MODAL_TYPE_WINDOW, w11->GetProperty(aura::client::kModalKey));
+  }
+
  private:
   DISALLOW_COPY_AND_ASSIGN(FocusControllerParentHideTest);
 };
@@ -1275,6 +1296,10 @@
 // See description above DontPassDeletedWindow() for details.
 FOCUS_CONTROLLER_TEST(FocusControllerApiTest, DontPassDeletedWindow);
 
+// See description above TransientChildWindowActivationTest() for details.
+FOCUS_CONTROLLER_TEST(FocusControllerParentHideTest,
+                      TransientChildWindowActivationTest);
+
 // - Verifies that the focused text input client is cleard when the window focus
 //   changes.
 ALL_FOCUS_TESTS(FocusedTextInputClient);
diff --git a/ui/wm/public/tooltip_client.h b/ui/wm/public/tooltip_client.h
index f2b97cdd..f58a63e1 100644
--- a/ui/wm/public/tooltip_client.h
+++ b/ui/wm/public/tooltip_client.h
@@ -8,6 +8,10 @@
 #include "ui/aura/aura_export.h"
 #include "ui/gfx/font.h"
 
+namespace gfx {
+class Point;
+}
+
 namespace aura {
 class Window;
 namespace client {
@@ -16,6 +20,10 @@
 
 class AURA_EXPORT TooltipClient {
  public:
+  // Returns the max width of the tooltip when shown at the specified location.
+  virtual int GetMaxWidth(const gfx::Point& point,
+                          aura::Window* context) const = 0;
+
   // Informs the shell tooltip manager of change in tooltip for window |target|.
   virtual void UpdateTooltip(Window* target) = 0;
 
diff --git a/url/BUILD.gn b/url/BUILD.gn
index 03e90cb..964a08b 100644
--- a/url/BUILD.gn
+++ b/url/BUILD.gn
@@ -58,11 +58,11 @@
 
   defines = [ "URL_IMPLEMENTATION" ]
 
-  configs += [ ":url_icu_config" ]
-
-  if (is_win) {
-    cflags = [ "/wd4267" ]  # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
-  }
+  configs += [
+    ":url_icu_config",
+    # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+    "//build/config/compiler:no_size_t_to_int_warning",
+  ]
 
   deps = [
     "//base",
@@ -101,19 +101,15 @@
       "url_util_unittest.cc",
     ]
 
+    # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+    configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
+
     #if (is_posix && !is_mac && !is_ios) {
     #  if (use_allocator!="none") {
     #    deps += "//base/allocator"
     #  }
     #}
 
-    if (is_win) {
-      cflags = [
-        # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
-        "/wd4267",
-      ]
-    }
-
     deps = [
       ":url",
       "//base",
diff --git a/url/url_canon.h b/url/url_canon.h
index 89e350909..432f291 100644
--- a/url/url_canon.h
+++ b/url/url_canon.h
@@ -734,8 +734,8 @@
   // Returns a pointer to a static empty string that is used as a placeholder
   // to indicate a component should be deleted (see below).
   const CHAR* Placeholder() {
-    static const CHAR empty_string = 0;
-    return &empty_string;
+    static const CHAR empty_cstr = 0;
+    return &empty_cstr;
   }
 
   // We support three states:
diff --git a/webkit/common/gpu/BUILD.gn b/webkit/common/gpu/BUILD.gn
index 43e0a64..35b30bd4 100644
--- a/webkit/common/gpu/BUILD.gn
+++ b/webkit/common/gpu/BUILD.gn
@@ -15,11 +15,11 @@
   ]
 
   defines = [ "WEBKIT_GPU_IMPLEMENTATION" ]
-  configs += [ "//build/config/compiler:wexit_time_destructors" ]
-  if (is_win) {
+  configs += [
+    "//build/config/compiler:wexit_time_destructors",
     # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
-    cflags = [ "/wd4267" ]
-  }
+    "//build/config/compiler:no_size_t_to_int_warning",
+  ]
 
   deps = [
     "//base",
diff --git a/win8/BUILD.gn b/win8/BUILD.gn
index 84d7972..fe2a830 100644
--- a/win8/BUILD.gn
+++ b/win8/BUILD.gn
@@ -29,6 +29,10 @@
     "//base",
     ":test_registrar_constants",
   ]
+
+  # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+  configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
+
   sources = [
     "test/metro_registration_helper.cc",
     "test/metro_registration_helper.h",
@@ -39,9 +43,6 @@
     "test/ui_automation_client.cc",
     "test/ui_automation_client.h",
   ]
-
-  # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
-  cflags = [ "/wd4267" ]
 }
 
 source_set("test_registrar_constants") {
diff --git a/win8/delegate_execute/chrome_util.cc b/win8/delegate_execute/chrome_util.cc
index 0f85d6dd..489e9fb 100644
--- a/win8/delegate_execute/chrome_util.cc
+++ b/win8/delegate_execute/chrome_util.cc
@@ -147,9 +147,8 @@
   // Wait for the update to complete and report the results.
   if (process.IsValid()) {
     int exit_code = 0;
-    if (!base::WaitForExitCodeWithTimeout(
-            process.Handle(), &exit_code,
-            base::TimeDelta::FromMilliseconds(INFINITE))) {
+    if (!process.WaitForExitWithTimeout(
+             base::TimeDelta::FromMilliseconds(INFINITE), &exit_code)) {
       AtlTrace("%hs. Failed to get result when finalizing update.\n",
                __FUNCTION__);
     } else if (exit_code != installer::RENAME_SUCCESSFUL) {
diff --git a/win8/test/metro_registration_helper.cc b/win8/test/metro_registration_helper.cc
index 160f602..00f5b99 100644
--- a/win8/test/metro_registration_helper.cc
+++ b/win8/test/metro_registration_helper.cc
@@ -58,9 +58,8 @@
                           base::LaunchOptions());
   if (register_process.IsValid()) {
     int ret = 0;
-    if (base::WaitForExitCodeWithTimeout(
-            register_process.Handle(), &ret,
-            base::TimeDelta::FromSeconds(kRegistrationTimeoutSeconds))) {
+    if (register_process.WaitForExitWithTimeout(
+            base::TimeDelta::FromSeconds(kRegistrationTimeoutSeconds), &ret)) {
       if (ret == 0) {
         return true;
       } else {